private static SOMResult TrainSOM(SOMNode[] nodes, ISOMInput[] inputs, SOMRules rules, bool returnEmptyNodes = false) { double mapRadius = MathND.GetRadius(MathND.GetAABB(nodes.Select(o => o.Weights))); SOMNode[] returnNodes = nodes. Select(o => o.Clone()). ToArray(); double timeConstant = rules.NumIterations / Math.Log(mapRadius); int iteration = 0; int remainingIterations = rules.NumIterations; while (remainingIterations > 0) { foreach (ISOMInput input in UtilityCore.RandomOrder(inputs, Math.Min(remainingIterations, inputs.Length))) { // Find closest node SOMNode closest = GetClosest(returnNodes, input).Item1; // Find other affected nodes (a node and distance squared) double searchRadius = mapRadius * rules.InitialRadiusPercent * Math.Exp(-iteration / timeConstant); Tuple <SOMNode, double>[] neigbors = GetNeighbors(returnNodes, closest, searchRadius); double learningRate = rules.LearningRate * Math.Exp(-(double)iteration / (double)rules.NumIterations); // Adjust the matched node (full learning rate) AdjustNodeWeights(closest, input.Weights, learningRate); foreach (var node in neigbors) { double influence = GetInfluence(rules.AttractionFunction, node.Item2, searchRadius); // Adjust a neighbor AdjustNodeWeights(node.Item1, input.Weights, learningRate * influence); } iteration++; } remainingIterations -= inputs.Length; } // See which images go with which nodes ISOMInput[][] inputsByNode = GetInputsByNode(returnNodes, inputs); SOMResult retVal = new SOMResult(returnNodes, inputsByNode, true); if (!returnEmptyNodes) { retVal = RemoveZeroNodes(retVal); } return(retVal); }
public SOMNode Clone() { SOMNode retVal = new SOMNode(); if (this.Weights != null) { retVal.Weights = this.Weights.Clone(); } retVal.Position = this.Position.Clone(); return(retVal); }
public static Tuple <SOMNode, int> GetClosest(SOMNode[] nodes, ISOMInput input) { int closestIndex = -1; double closest = double.MaxValue; SOMNode retVal = null; for (int cntr = 0; cntr < nodes.Length; cntr++) { double distSquared = (nodes[cntr].Weights - input.Weights).LengthSquared; if (distSquared < closest) { closestIndex = cntr; closest = distSquared; retVal = nodes[cntr]; } } return(Tuple.Create(retVal, closestIndex)); }
private static Tuple <SOMNode, double>[] GetNeighbors(SOMNode[] nodes, SOMNode match, double maxDistance) { List <Tuple <SOMNode, double> > retVal = new List <Tuple <SOMNode, double> >(); double maxDistanceSquared = maxDistance * maxDistance; for (int cntr = 0; cntr < nodes.Length; cntr++) { if (nodes[cntr].Token == match.Token) { continue; } double distSquared = (nodes[cntr].Weights - match.Weights).LengthSquared; if (distSquared < maxDistanceSquared) { retVal.Add(Tuple.Create(nodes[cntr], distSquared)); // no need for a square root. The calling method needs it squared } } return(retVal.ToArray()); }
private static Brush GetTiledSamples(Point[] edgePoints, ISOMInput[] samples, SOMNode node, int tileWidth, int tileHeight, Action <DrawTileArgs> drawTile) { var dimensions = GetTileImagePositions(samples.Length); // The image tiles will be drawn spiraling out from the center. Order the list so that tiles closest to the node are first (so that // they are drawn closer to the center of the spiral) ISOMInput[] orderedSamples = samples. OrderBy(o => (o.Weights - node.Weights).LengthSquared). ToArray(); //int tilehalf_left = tileWidth / 2; //int tilehalf_top = tileHeight / 2; //int tilehalf_right = tileWidth - tilehalf_left; //int tilehalf_bot = tileHeight - tilehalf_top; int imageWidth = (dimensions.Item2.X - dimensions.Item1.X + 1) * tileWidth; int imageHeight = (dimensions.Item2.Y - dimensions.Item1.Y + 1) * tileHeight; //int offsetX = (Math.Abs(dimensions.Item1.X) * tileWidth) + tilehalf_left; //int offsetY = (Math.Abs(dimensions.Item1.Y) * tileHeight) + tilehalf_top; //TODO: Get the AABB of edgePoints. If the bitmap will be bigger than the aabb, then draw just enough to totally fill the polygon //NOTE: Copied from UtilityWPF.GetBitmap WriteableBitmap bitmap = new WriteableBitmap(imageWidth, imageHeight, UtilityWPF.DPI, UtilityWPF.DPI, PixelFormats.Pbgra32, null); // may want Bgra32 if performance is an issue int pixelWidth = bitmap.Format.BitsPerPixel / 8; int stride = bitmap.PixelWidth * pixelWidth; // this is the length of one row of pixels byte[] pixels = new byte[bitmap.PixelHeight * stride]; DrawingVisual dv = new DrawingVisual(); using (DrawingContext ctx = dv.RenderOpen()) { for (int cntr = 0; cntr < orderedSamples.Length; cntr++) { int x = (dimensions.Item3[cntr].X - dimensions.Item1.X) * tileWidth; int y = (dimensions.Item3[cntr].Y - dimensions.Item1.Y) * tileWidth; DrawTileArgs args = new DrawTileArgs(orderedSamples[cntr], tileWidth, tileHeight, pixels, x, y, stride, pixelWidth); drawTile(args); } } bitmap.WritePixels(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight), pixels, stride, 0); return(new ImageBrush(bitmap) { Stretch = Stretch.None, }); }
public static Color GetNodeColor(SOMNode node) { var raw = GetNodeColor_RGB(node.Weights); return(GetNodeColor_Finish(raw)); }
private static void AdjustNodeWeights(SOMNode node, VectorND position, double percent) { //W(t+1) = W(t) + (pos - W(t)) * % node.Weights += (position - node.Weights) * percent; }
/// <summary> /// This is a helper method to turn som node positions into points to pass to AddStaticItems() /// </summary> public static IEnumerable <Tuple <ISOMInput, Point3D> > GetSOMNodeStaticPositions(SOMNode node, SOMNode[] allNodes, double innerRadius) { // Convert to Point3D var initial = allNodes. Select(o => { Vector3D position = new Vector3D(); if (o.Position.Size >= 1) { position.X = o.Position[0]; } if (o.Position.Size >= 2) { position.Y = o.Position[1]; } if (o.Position.Size >= 3) { position.Z = o.Position[2]; } return(Tuple.Create(o, position, position.Length)); }). OrderBy(o => o.Item3). ToArray(); // Find the closest one (that isn't the one passed in) var firstNonZero = initial. Where(o => o.Item1.Token != node.Token). FirstOrDefault(o => !o.Item3.IsNearZero()); double scale = 1d; if (firstNonZero != null) { scale = (innerRadius * 1.25) / firstNonZero.Item3; } // Make sure they are spaced properly var scaled = initial. Select(o => Tuple.Create(o.Item1, (o.Item2 * scale).ToPoint())). ToArray(); // These need to be centered over the origin, because the points will try to drift to the center Point3D center = Math3D.GetCenter(scaled.Select(o => o.Item2)); Vector3D offset = new Vector3D(-center.X, -center.Y, -center.Z); return(scaled. Select(o => Tuple.Create((ISOMInput)o.Item1, o.Item2 + offset)). ToArray()); }