/// <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;
            }
Beispiel #22
0
 public void Add2DItem(Item2D item2D)
 {
     item2DList.Add(item2D);
 }
 public Set2D(int index, Item2D[] all)
     : this(new[] { index }, all) { }