public void Epoch(KPoint p)
        {
            //get a random item
            SOMNeuron bmu = GetBestMatchingUnit(p);

            neighborhoodRadius = radius * Math.Exp(-(double)itterations / timeConstant);
            double neighborSqr = neighborhoodRadius * neighborhoodRadius;

            QuadTreePointStruct center = new QuadTreePointStruct()
            {
                Index = -1,
                X     = bmu.position[0],
                Y     = bmu.position[1]
            };
            QuadTreePointStruct halfLength = new QuadTreePointStruct()
            {
                Index = -1,
                X     = neighborhoodRadius,
                Y     = neighborhoodRadius
            };

            List <int> neuronsInArea =
                neuronQuadTree.QueryRange(new QuadTreeBoundingBox()
            {
                Center = center, HalfLength = halfLength
            });

            //Now we need to update each neuron
            foreach (int i in neuronsInArea)
            {
                SOMNeuron n         = neurons[i];
                double    distToBMU = n.position.elucideanDistance(bmu.position);
                if (distToBMU <= neighborhoodRadius)
                {
                    influence = Math.Exp(-(distToBMU * distToBMU) / (2 * neighborSqr));
                    n.updateWeights(p, learningRate, influence);
                }
            }

            learningRate = this.initialLearningRate * Math.Exp(-(double)itterations / (double)this.totalItterations);
            itterations++;
        }
        public HexagonalSelfOrganizingMap(PointSet data, int dimension, double learningRate)
        {
            _dimension = dimension;

            //Get initial learning rate
            this.initialLearningRate = learningRate;
            this.learningRate        = learningRate;
            influence   = 0;
            itterations = 1;

            this.data = data;
            rng       = new Random();

            int numNeurons = dimension * dimension;

            //Scale each attribute from [0,1] and store the conversion matrix
            conversionMatrix = data.GetMinMaxWeights().Max.Coordinates;

            foreach (KPoint d in data.PointList)
            {
                d.Normalize(conversionMatrix);
            }

            //Here We will initialize Our Neurons
            neurons = new List <SOMNeuron>();
            KPoint zero            = KPoint.Zero(data[0].Dimensions);
            KPoint one             = KPoint.One(data[0].Dimensions);
            double halfNeuronDelta = 1 / Math.Sqrt(3.0);

            //Initialize our Quadtree for reference
            double centerX             = (dimension - 0.5) * (2 * halfNeuronDelta) / 2.0;
            double centerY             = (dimension - 1.0) / 2.0;
            QuadTreePointStruct center = new QuadTreePointStruct()
            {
                Index = -1, X = centerX, Y = centerY
            };
            QuadTreePointStruct halfDistance = new QuadTreePointStruct()
            {
                Index = -1, X = centerX + halfNeuronDelta, Y = centerY + 0.5
            };

            neuronQuadTree = new QuadTree(new QuadTreeBoundingBox()
            {
                Center = center, HalfLength = halfDistance
            });

            int neuronIndex = 0;

            for (int r = 0; r < dimension; r++)
            {
                double xPos = (r % 2 == 1) ? halfNeuronDelta : 0.0;
                for (int c = 0; c < dimension; c++)
                {
                    //Generate our neurons
                    double[]            posNeuron   = { xPos, (double)r };
                    SOMNeuron           neuron      = new SOMNeuron(zero, one, rng, new KPoint(posNeuron), neuronIndex);
                    QuadTreePointStruct neuronQTPos = new QuadTreePointStruct()
                    {
                        Index = neuronIndex,
                        X     = posNeuron[0],
                        Y     = posNeuron[1]
                    };
                    neuronQuadTree.Insert(neuronQTPos);
                    neurons.Add(neuron);
                    xPos += 2 * halfNeuronDelta;
                    neuronIndex++;
                }
            }



            //Get the max radius
            SetRadius();

            timeConstant = (double)totalItterations / Math.Log(radius);
        }