/// <summary> /// This gets the bounding box of all the input values, then creates random vectors within that box /// </summary> private static VectorND[] GetRandomNodeWeights(int count, ISOMInput[] inputs) { var aabb = MathND.GetAABB(inputs.Select(o => o.Weights)); aabb = MathND.ResizeAABB(aabb, 1.1); // allow the return vectors to be slightly outside the input box return(Enumerable.Range(0, count). Select(o => MathND.GetRandomVector(aabb.Item1, aabb.Item2)). ToArray()); }
/// <summary> /// This does a bounding box of inputs, and also makes sure all positions are closer to the desired node than /// other nodes /// </summary> private static VectorND[] GetRandomWeights_InsideCell(int count, ISOMInput[] inputs, SOMNode[] nodes, int nodeIndex) { var inputAABB = MathND.GetAABB(inputs.Select(o => o.Weights)); // This actually could happen. Detecting an infinite loop instead //if (!MathND.IsInside(inputAABB, nodes[nodeIndex].Weights)) //{ // throw new ArgumentException("The node sits outside the input's aabb"); //} List <VectorND> retVal = new List <VectorND>(); for (int cntr = 0; cntr < count; cntr++) { int infiniteLoopDetector = 0; while (true) { VectorND attempt = MathND.GetRandomVector(inputAABB.Item1, inputAABB.Item2); var closest = nodes. Select((o, i) => new { Index = i, DistSquared = (attempt - o.Weights).LengthSquared }). OrderBy(o => o.DistSquared). First(); if (closest.Index == nodeIndex) { retVal.Add(attempt); break; } infiniteLoopDetector++; if (infiniteLoopDetector > 1000) { // Instead of giving up, increase the range that the weights can exist in. When testing this other method, there is almost never an improved // node. It's just an infinite loop in the caller (keeps trying for an improvement, but it never happens) return(GetRandomWeights_InsideCell_LARGERBUTFAIL(count, inputs, nodes, nodeIndex)); } } } return(retVal.ToArray()); }
/// <summary> /// Only call this from the other overload. This uses a much larger bounding box (it still makes sure all returned points /// are closer to the desired node than other nodes) /// </summary> private static VectorND[] GetRandomWeights_InsideCell_LARGERBUTFAIL(int count, ISOMInput[] inputs, SOMNode[] nodes, int nodeIndex) { //TODO: May want to get a voronoi, then choose random points within the convex hull (more elegant, but may not be any faster) // This works, but the caller generally never finds a solution that's better then what it already has. So if you need // to use this overload, you're just going to spin the processor #region calculate rectangle to choose points from // Get some nearby nodes double?largestDistance = Enumerable.Range(0, nodes.Length). Where(o => o != nodeIndex). Select(o => (nodes[o].Weights - nodes[nodeIndex].Weights).LengthSquared). OrderByDescending(o => o). FirstOrDefault(); var inputAABB = MathND.GetAABB(inputs.Select(o => o.Weights)); if (largestDistance == null) { largestDistance = Math.Max( (inputAABB.Item1 - nodes[nodeIndex].Weights).LengthSquared, (inputAABB.Item2 - nodes[nodeIndex].Weights).LengthSquared); } largestDistance = Math.Sqrt(largestDistance.Value); var bounds = MathND.GetAABB(new[] { nodes[nodeIndex].Weights.Select(o => o - largestDistance.Value).ToArray().ToVectorND(), nodes[nodeIndex].Weights.Select(o => o + largestDistance.Value).ToArray().ToVectorND(), inputAABB.Item1, inputAABB.Item2, }); #endregion List <VectorND> retVal = new List <VectorND>(); //int largestIteration = 0; for (int cntr = 0; cntr < count; cntr++) { int infiniteLoopDetector = 0; while (true) { //if(infiniteLoopDetector > largestIteration) //{ // largestIteration = infiniteLoopDetector; // I tested several times, and averaged about 5-15 iterations //} VectorND attempt = MathND.GetRandomVector(bounds.Item1, bounds.Item2); var closest = nodes. Select((o, i) => new { Index = i, DistSquared = (attempt - o.Weights).LengthSquared }). OrderBy(o => o.DistSquared). First(); if (closest.Index == nodeIndex) { retVal.Add(attempt); break; } infiniteLoopDetector++; if (infiniteLoopDetector > 1000) { throw new ApplicationException("Infinite loop detected"); } } } return(retVal.ToArray()); }