/// <summary> /// This finds brains that are really close together, and turns them into sets (the rest of the brains become 1 item sets) /// </summary> internal static Set2D[] IOLinks_PreMergeBrains(Item2D[] brains, SortedList<Tuple<int, int>, double> links) { if (!IOPREMERGE_SHOULD) { return Enumerable.Range(0, brains.Length).Select(o => new Set2D(o, brains)).ToArray(); } // Get the AABB of the brains, and use the diagonal as the size var aabb = Math2D.GetAABB(brains.Select(o => o.Position)); double maxDistance = (aabb.Item2 - aabb.Item1).Length; // Figure out the max distance allowed for a merge double threshold = maxDistance * IOPREMERGE_DISTANCE; // Find links that should be merged var closePairs = links. Where(o => o.Value <= threshold). OrderBy(o => o.Value). ToArray(); if (closePairs.Length == 0) { return Enumerable.Range(0, brains.Length).Select(o => new Set2D(o, brains)).ToArray(); } // Combine any close pairs that share a point (turn pairs into triples) List<List<int>> sets = IOLinks_PreMergeBrains_Sets(closePairs); // Build the final list return IOLinks_PreMergeBrains_Centers(sets, brains, links); }
private static Set2D[] IOLinks_PreMergeBrains_Centers(List<List<int>> sets, Item2D[] brains, SortedList<Tuple<int, int>, double> links) { List<Set2D> retVal = new List<Set2D>(); // Multi brain sets retVal.AddRange(sets.Select(o => new Set2D(o.Distinct(), brains))); // Single brain sets foreach (Tuple<int, int> key in links.Keys) { if (!retVal.Any(o => o.Indices.Contains(key.Item1))) { retVal.Add(new Set2D(key.Item1, brains)); } if (!retVal.Any(o => o.Indices.Contains(key.Item2))) { retVal.Add(new Set2D(key.Item2, brains)); } } return retVal.ToArray(); }
public Set2D(IEnumerable<int> indices, Item2D[] all) { this.Indices = indices.ToArray(); this.Items = this.Indices.Select(o => all[o]).ToArray(); this.Positions = this.Items.Select(o => o.Position).ToArray(); if (this.Items.Length == 1) { this.Center = this.Items[0].Position; } else { this.Center = Math2D.GetCenter(this.Positions); } }
/// <summary> /// Link brains to io /// </summary> private static LinkIO[] IOLinks(Item2D[] brains, Item2D[] io, SortedList<Tuple<int, int>, double> allBrainLinks) { if (brains.Length < 2) { throw new ArgumentException("This method requires at least two brains: " + brains.Length.ToString()); } // Look for brains that are really close together, treat those as single brains Set2D[] brainSets = IOLinks_PreMergeBrains(brains, allBrainLinks); // Link IO to each brain set VoronoiResult2D voronoi = Math2D.GetVoronoi(brainSets.Select(o => o.Center).ToArray(), true); Tuple<int, int>[] initial = IOLinks_Initial(brainSets, io, voronoi); //TODO: If a brain set is saturated, and there is an under used brainset nearby, transfer some links Tuple<int, int>[] adjusted = IOLinks_PostAdjust(initial, brainSets, io); return adjusted.Select(o => new LinkIO(brainSets[o.Item1], o.Item2, io)).ToArray(); }
public BrainBurden(int index, Set2D[] brainSets, Item2D[] io) { this.Index = index; this.Brain = brainSets[index]; this.Size = this.Brain.Items.Sum(o => o.Size); _io = io; }
private static LinkIO[] IOLinks(Item2D[] brains, Item2D[] io, SortedList<Tuple<int, int>, double> allBrainLinks, IOLinkupPriority ioLinkupPriority, double brainLinkResistanceMult) { // Look for brains that are really close together, treat those as single brains Set2D[] brainSets = Worker2D_Voronoi.IOLinks_PreMergeBrains(brains, allBrainLinks); #region Brain-IO Distances // Figure out the distances between io and brains var distancesIO = Enumerable.Range(0, io.Length). Select(o => new { IOIndex = o, BrainDistances = Enumerable.Range(0, brainSets.Length). Select(p => Tuple.Create(p, (brainSets[p].Center - io[o].Position).Length)). //Item1=BrainIndex, Item2=DistanceToBrain OrderBy(p => p.Item2). // first brain needs to be the shortest distance ToArray() }); switch (ioLinkupPriority) { case IOLinkupPriority.ShortestDistFirst: distancesIO = distancesIO.OrderBy(o => o.BrainDistances.First().Item2); break; case IOLinkupPriority.LongestDistFirst: distancesIO = distancesIO.OrderByDescending(o => o.BrainDistances.First().Item2); break; case IOLinkupPriority.RandomOrder: //TODO: Come up with a less expensive way of doing this //.Select(o => new { Orig=o, Key=Guid.NewGuid }).OrderBy(o => o.Key).Select(o => o.Orig) Random rand = StaticRandom.GetRandomForThread(); distancesIO = distancesIO. Select(o => new { Orig = o, Key = rand.Next(int.MaxValue) }). OrderBy(o => o.Key). Select(o => o.Orig); break; default: throw new ApplicationException("Unknown IOLinkupPriority: " + ioLinkupPriority.ToString()); } distancesIO = distancesIO.ToArray(); #endregion // Brain-Brain Distances var distancesBrain = BrainBrainDistances(brainSets, brainLinkResistanceMult); // Link IO to brains BrainBurden[] links = Enumerable.Range(0, brainSets.Length).Select(o => new BrainBurden(o, brainSets, io)).ToArray(); foreach (var distanceIO in distancesIO) { int ioIndex = distanceIO.IOIndex; int closestBrainIndex = distanceIO.BrainDistances[0].Item1; AddIOLink(links, ioIndex, io[ioIndex].Size, closestBrainIndex, distancesBrain[closestBrainIndex]); } // Build the return List<LinkIO> retVal = new List<LinkIO>(); foreach (BrainBurden brain in links) { foreach (int ioIndex in brain.IOLinks) { retVal.Add(new LinkIO(brain.Brain, ioIndex, io)); } } return retVal.ToArray(); }
public void Recalc(Set2D[] brainSets, Item2D[] io) { this.BrainSize = brainSets[this.Index].Items.Sum(o => o.Size); this.Burden = CalculateBurden(UtilityCore.Iterate(this.LinksOrig, this.LinksMoved).Sum(o => io[o].Size), this.BrainSize); // size of the links / size of the brain }
private static bool IOLinks_PostAdjust_MoveNext(BrainBurden[] ioLinks, Tuple<int, double>[][] brainLinks, Set2D[] brainSets, Item2D[] io) { List<Tuple<BrainBurden, int, double, double>> candidates = new List<Tuple<BrainBurden, int, double, double>>(); // Find brains that have excess links to give away, and which ones to hand to foreach (BrainBurden ioLink in ioLinks.Where(o => o.LinksOrig.Count > 0)) { foreach (Tuple<int, double> brainLink in brainLinks[ioLink.Index]) { double otherBurden = ioLinks.First(o => o.Index == brainLink.Item1).Burden; if (ioLink.Burden > otherBurden + brainLink.Item2) { candidates.Add(Tuple.Create(ioLink, brainLink.Item1, ioLink.Burden - (otherBurden + brainLink.Item2), brainLink.Item2)); } } } // Iterate over them with the largest distance first (can't just pull the top, because an individual io link may represent too big of a burden // to be able to shift) foreach (var candidate in candidates.OrderByDescending(o => o.Item3)) { // Try to move a link if (IOLinks_PostAdjust_MoveNext_Brain(candidate.Item1, ioLinks.First(o => o.Index == candidate.Item2), candidate.Item4, brainSets, io)) { return true; } } return false; }
public LinkIO(IEnumerable<int> brains, int io, Item2D[] allBrains, Item2D[] allIO) : this(new Set2D(brains, allBrains), io, allIO) { }
public LinkIO(Set2D brains, int io, Item2D[] allIO) { this.Brains = brains; this.IOIndex = io; this.IO = allIO[io]; }
public LinkIO(int brain, int io, Item2D[] allBrains, Item2D[] allIO) : this(new Set2D(brain, allBrains), io, allIO) { }
public LinkBrain2D(IEnumerable<int> set1, IEnumerable<int> set2, Item2D[] all) : this(new Set2D(set1, all), new Set2D(set2, all)) { }
public LinkBrain2D(int item1, int item2, Item2D[] all) : this(new Set2D(item1, all), new Set2D(item2, all)) { }
private static Tuple<int, int>[] IOLinks_Initial(Set2D[] brains, Item2D[] io, VoronoiResult2D voronoi) { List<Tuple<int, int>> retVal = new List<Tuple<int, int>>(); List<int> remainingIO = Enumerable.Range(0, io.Length).ToList(); // store the remaining so that they can be removed as found (avoid unnecessary IsInside checks) for (int brainCntr = 0; brainCntr < brains.Length; brainCntr++) { Edge2D[] edges = voronoi.EdgesByControlPoint[brainCntr].Select(o => voronoi.Edges[o]).ToArray(); if (edges.Length == 1) { throw new ApplicationException("finish this"); } foreach (int ioIndex in remainingIO.ToArray()) { if (Edge2D.IsInside(edges, io[ioIndex].Position)) { retVal.Add(Tuple.Create(brainCntr, ioIndex)); remainingIO.Remove(ioIndex); } } } return retVal.ToArray(); }
public static void GetLinks(out LinkBrain2D[] brainLinks, out LinkIO[] ioLinks, Item2D[] brains, Item2D[] io) { if (brains == null || brains.Length == 0) { brainLinks = null; ioLinks = null; return; } else if (brains.Length == 1) { brainLinks = null; ioLinks = Enumerable.Range(0, io.Length).Select(o => new LinkIO(0, o, brains, io)).ToArray(); return; } // Link brains:brains SortedList<Tuple<int, int>, double> allBrainLinks; BrainLinks(out allBrainLinks, out brainLinks, brains); // Link brains:io ioLinks = IOLinks(brains, io, allBrainLinks); }
private static Tuple<int, int>[] IOLinks_PostAdjust(Tuple<int, int>[] initial, Set2D[] brainSets, Item2D[] io) { // Get the links and distances between brain sets Tuple<int, double>[][] brainLinks = IOLinks_PostAdjust_BrainBrainLinks(brainSets); // Turn the initial links into something more usable (list of links by brain) BrainBurden[] ioLinks = initial. GroupBy(o => o.Item1). Select(o => new BrainBurden(o.Key, o.Select(p => p.Item2))). ToArray(); // Make sure there is an element in ioLinks for each brainset (the groupby logic above only grabs brainsets that have links) int[] missing = Enumerable.Range(0, brainLinks.Length).Where(o => !ioLinks.Any(p => p.Index == o)).ToArray(); ioLinks = UtilityCore.Iterate(ioLinks, missing.Select(o => new BrainBurden(o, Enumerable.Empty<int>()))).ToArray(); // Move one link at a time while (true) { // Figure out how burdened each brain set is foreach (BrainBurden brain in ioLinks) { brain.Recalc(brainSets, io); } // Find the best link to move if (!IOLinks_PostAdjust_MoveNext(ioLinks, brainLinks, brainSets, io)) { break; } } // Just build it manually //return ioLinks.Select(o => new { BrainIndex = o.Index, UtilityCore.Iterate(o.LinksOrig, o.LinksMoved) //var test = ioLinks. // Select(o => new { BrainIndex = o.Index, Links = UtilityCore.Iterate(o.LinksOrig, o.LinksMoved).ToArray() }). // ToArray(); List<Tuple<int, int>> retVal = new List<Tuple<int, int>>(); foreach (BrainBurden brain in ioLinks) { foreach (int ioIndex in UtilityCore.Iterate(brain.LinksOrig, brain.LinksMoved)) { retVal.Add(Tuple.Create(brain.Index, ioIndex)); } } return retVal.ToArray(); //return initial; }
/// <summary> /// Link brains to each other (delaunay graph, then prune thin triangles) /// </summary> internal static void BrainLinks(out SortedList<Tuple<int, int>, double> all, out LinkBrain2D[] pruned, Item2D[] brains) { if (brains.Length < 2) { throw new ArgumentException("This method requires at least two brains: " + brains.Length.ToString()); } else if (brains.Length == 2) { all = GetLengths(new[] { Tuple.Create(0, 1) }, brains); pruned = all.Keys.Select(o => new LinkBrain2D(o.Item1, o.Item2, brains)).ToArray(); return; } TriangleIndexed[] triangles = Math2D.GetDelaunayTriangulation(brains.Select(o => o.Position).ToArray(), brains.Select(o => o.Position.ToPoint3D()).ToArray()); all = GetLengths(TriangleIndexed.GetUniqueLines(triangles), brains); // Prune links that don't make sense pruned = PruneBrainLinks(triangles, all, brains); }
private static bool IOLinks_PostAdjust_MoveNext_Brain(BrainBurden from, BrainBurden to, double linkBurden, Set2D[] brainSets, Item2D[] io) { // Cache the sizes of the current io links double fromIOSize = UtilityCore.Iterate(from.LinksOrig, from.LinksMoved).Sum(o => io[o].Size); double toIOSize = UtilityCore.Iterate(to.LinksOrig, to.LinksMoved).Sum(o => io[o].Size); // See what the new burdens would be for from and to for each io link var newBurdens = from.LinksOrig.Select(o => new { IOIndex = o, FromBurden = BrainBurden.CalculateBurden(fromIOSize - io[o].Size, from.BrainSize), ToBurden = BrainBurden.CalculateBurden(toIOSize + io[o].Size, to.BrainSize), }). Where(o => o.FromBurden >= linkBurden + o.ToBurden). // throw out any moves that cause a reverse in burden Select(o => new { IOIndex = o.IOIndex, FromBurden = o.FromBurden, ToBurden = o.ToBurden, Distance = (brainSets[to.Index].Center - io[o.IOIndex].Position).Length, //TODO: Other scoring criterea here }). ToArray(); if (newBurdens.Length == 0) { return false; } // Come up with the best io to move //TODO: Things to consider // Distance between io and to brain // Types of IO that from and to have (try to specialize) // // When combining scores, can't just score things linearly, then add up the score. Average performers of multiple categories // would win var best = newBurdens.OrderBy(o => o.Distance).First(); // Move the link to.LinksMoved.Add(best.IOIndex); from.LinksOrig.Remove(best.IOIndex); return true; }
private static LinkBrain2D[] PruneBrainLinks(TriangleIndexed[] triangles, SortedList<Tuple<int, int>, double> all, Item2D[] brains) { List<Tuple<int[], int[]>> retVal = all.Keys. Select(o => Tuple.Create(new[] { o.Item1 }, new[] { o.Item2 })). ToList(); foreach (TriangleIndexed triangle in triangles) { Tuple<bool, TriangleEdge> changeEdge = PruneBrainLinks_LongThin(triangle, all); if (changeEdge == null) { continue; } if (changeEdge.Item1) { PruneBrainLinks_Merge(triangle, changeEdge.Item2, retVal); } else { PruneBrainLinks_Remove(triangle, changeEdge.Item2, retVal); } } return retVal.Select(o => new LinkBrain2D(o.Item1, o.Item2, brains)).ToArray(); }
public static void GetLinks(out LinkBrain2D[] brainLinks, out LinkIO[] ioLinks, Item2D[] brains, Item2D[] io, IOLinkupPriority ioLinkupPriority = IOLinkupPriority.ShortestDistFirst, double brainLinkResistanceMult = 10) { if (brains == null || brains.Length == 0) { brainLinks = null; ioLinks = null; return; } else if (brains.Length == 1) { brainLinks = null; ioLinks = Enumerable.Range(0, io.Length).Select(o => new LinkIO(0, o, brains, io)).ToArray(); return; } // Link brains:brains SortedList<Tuple<int, int>, double> allBrainLinks; Worker2D_Voronoi.BrainLinks(out allBrainLinks, out brainLinks, brains); // Link brains:io ioLinks = IOLinks(brains, io, allBrainLinks, ioLinkupPriority, brainLinkResistanceMult); }
//NOTE: This makes sure that key.item1 is less than key.item2 private static SortedList<Tuple<int, int>, double> GetLengths(Tuple<int, int>[] keys, Item2D[] brains) { SortedList<Tuple<int, int>, double> retVal = new SortedList<Tuple<int, int>, double>(); foreach (Tuple<int, int> key in keys.Select(o => o.Item1 < o.Item2 ? o : Tuple.Create(o.Item2, o.Item1))) { double distance = (brains[key.Item1].Position - brains[key.Item2].Position).Length; retVal.Add(key, distance); } return retVal; }
public void Add2DItem(Item2D item2D) { item2DList.Add(item2D); }
public Set2D(int index, Item2D[] all) : this(new[] { index }, all) { }