/// <summary> /// The implementaiton of SEARCH-LAYER(q, ep, ef, lc) algorithm. /// Article: Section 4. Algorithm 2. /// </summary> /// <param name="entryPointId">The identifier of the entry point for the search.</param> /// <param name="targetCosts">The traveling costs for the search target.</param> /// <param name="resultList">The list of identifiers of the nearest neighbours at the level.</param> /// <param name="layer">The layer to perform search at.</param> /// <param name="k">The number of the nearest neighbours to get from the layer.</param> internal void RunKnnAtLayer(int entryPointId, TravelingCosts <int, TDistance> targetCosts, IList <int> resultList, int layer, int k) { /* * v ← ep // set of visited elements * C ← ep // set of candidates * W ← ep // dynamic list of found nearest neighbors * while │C│ > 0 * c ← extract nearest element from C to q * f ← get furthest element from W to q * if distance(c, q) > distance(f, q) * break // all elements in W are evaluated * for each e ∈ neighbourhood(c) at layer lc // update C and W * if e ∉ v * v ← v ⋃ e * f ← get furthest element from W to q * if distance(e, q) < distance(f, q) or │W│ < ef * C ← C ⋃ e * W ← W ⋃ e * if │W│ > ef * remove furthest element from W to q * return W */ // prepare tools IComparer <int> fartherIsOnTop = targetCosts; IComparer <int> closerIsOnTop = fartherIsOnTop.Reverse(); // prepare collections // TODO: Optimize by providing buffers var resultHeap = new BinaryHeap <int>(resultList, fartherIsOnTop); var expansionHeap = new BinaryHeap <int>(this.expansionBuffer, closerIsOnTop); resultHeap.Push(entryPointId); expansionHeap.Push(entryPointId); this.visitedSet.Add(entryPointId); // run bfs while (expansionHeap.Buffer.Count > 0) { // get next candidate to check and expand var toExpandId = expansionHeap.Pop(); var farthestResultId = resultHeap.Buffer[0]; if (DistanceUtils.Gt(targetCosts.From(toExpandId), targetCosts.From(farthestResultId))) { // the closest candidate is farther than farthest result break; } // expand candidate var neighboursIds = this.core.Nodes[toExpandId][layer]; for (int i = 0; i < neighboursIds.Count; ++i) { int neighbourId = neighboursIds[i]; if (!this.visitedSet.Contains(neighbourId)) { // enque perspective neighbours to expansion list farthestResultId = resultHeap.Buffer[0]; if (resultHeap.Buffer.Count < k || DistanceUtils.Lt(targetCosts.From(neighbourId), targetCosts.From(farthestResultId))) { expansionHeap.Push(neighbourId); resultHeap.Push(neighbourId); if (resultHeap.Buffer.Count > k) { resultHeap.Pop(); } } // update visited list this.visitedSet.Add(neighbourId); } } } this.expansionBuffer.Clear(); this.visitedSet.Clear(); }
/// <inheritdoc /> public override IList <Node> SelectBestForConnecting(IList <Node> candidates) { /* * q ← this * R ← ∅ // result * W ← C // working queue for the candidates * if expandCandidates // expand candidates * for each e ∈ C * for each eadj ∈ neighbourhood(e) at layer lc * if eadj ∉ W * W ← W ⋃ eadj * * Wd ← ∅ // queue for the discarded candidates * while │W│ gt 0 and │R│ lt M * e ← extract nearest element from W to q * if e is closer to q compared to any element from R * R ← R ⋃ e * else * Wd ← Wd ⋃ e * * if keepPrunedConnections // add some of the discarded connections from Wd * while │Wd│ gt 0 and │R│ lt M * R ← R ⋃ extract nearest element from Wd to q * * return R */ IComparer <Node> closerIsLess = this.TravelingCosts; IComparer <Node> fartherIsLess = closerIsLess.Reverse(); var resultHeap = new BinaryHeap <Node>(new List <Node>(GetM(this.Parameters.M, this.MaxLevel) + 1), closerIsLess); var candidatesHeap = new BinaryHeap <Node>(candidates, fartherIsLess); // expand candidates option is enabled if (this.Parameters.ExpandBestSelection) { var candidatesIds = new HashSet <int>(candidates.Select(c => c.Id)); foreach (var neighbour in this.GetConnections(this.MaxLevel)) { if (!candidatesIds.Contains(neighbour.Id)) { candidatesHeap.Push(neighbour); candidatesIds.Add(neighbour.Id); } } } // main stage of moving candidates to result var discardedHeap = new BinaryHeap <Node>(new List <Node>(candidatesHeap.Buffer.Count), fartherIsLess); while (candidatesHeap.Buffer.Any() && resultHeap.Buffer.Count < GetM(this.Parameters.M, this.MaxLevel)) { var candidate = candidatesHeap.Pop(); var farestResult = resultHeap.Buffer.FirstOrDefault(); if (farestResult == null || DLt(this.TravelingCosts.From(candidate), this.TravelingCosts.From(farestResult))) { resultHeap.Push(candidate); } else if (this.Parameters.KeepPrunedConnections) { discardedHeap.Push(candidate); } } // keep pruned option is enabled if (this.Parameters.KeepPrunedConnections) { while (discardedHeap.Buffer.Any() && resultHeap.Buffer.Count < GetM(this.Parameters.M, this.MaxLevel)) { resultHeap.Push(discardedHeap.Pop()); } } return(resultHeap.Buffer); }
/// <summary> /// The implementaiton of SEARCH-LAYER(q, ep, ef, lc) algorithm. /// Article: Section 4. Algorithm 2. /// </summary> /// <param name="entryPoint">The entry point for the search.</param> /// <param name="destination">The search target.</param> /// <param name="k">The number of the nearest neighbours to get from the layer.</param> /// <param name="level">Level of the layer.</param> /// <returns>The list of the nearest neighbours at the level.</returns> private static IList <Node> KNearestAtLevel(Node entryPoint, Node destination, int k, int level) { /* * v ← ep // set of visited elements * C ← ep // set of candidates * W ← ep // dynamic list of found nearest neighbors * while │C│ > 0 * c ← extract nearest element from C to q * f ← get furthest element from W to q * if distance(c, q) > distance(f, q) * break // all elements in W are evaluated * for each e ∈ neighbourhood(c) at layer lc // update C and W * if e ∉ v * v ← v ⋃ e * f ← get furthest element from W to q * if distance(e, q) < distance(f, q) or │W│ < ef * C ← C ⋃ e * W ← W ⋃ e * if │W│ > ef * remove furthest element from W to q * return W */ // prepare tools IComparer <Node> closerIsLess = destination.TravelingCosts; IComparer <Node> fartherIsLess = closerIsLess.Reverse(); // prepare heaps var resultHeap = new BinaryHeap <Node>(new List <Node>(k + 1) { entryPoint }, closerIsLess); var expansionHeap = new BinaryHeap <Node>(new List <Node>() { entryPoint }, fartherIsLess); // run bfs var visited = new HashSet <int>() { entryPoint.Id }; while (expansionHeap.Buffer.Any()) { // get next candidate to check and expand var toExpand = expansionHeap.Pop(); var farthestResult = resultHeap.Buffer.First(); if (DGt(destination.TravelingCosts.From(toExpand), destination.TravelingCosts.From(farthestResult))) { // the closest candidate is farther than farthest result break; } // expand candidate foreach (var neighbour in toExpand.GetConnections(level)) { if (!visited.Contains(neighbour.Id)) { // enque perspective neighbours to expansion list farthestResult = resultHeap.Buffer.First(); if (resultHeap.Buffer.Count < k || DLt(destination.TravelingCosts.From(neighbour), destination.TravelingCosts.From(farthestResult))) { expansionHeap.Push(neighbour); resultHeap.Push(neighbour); if (resultHeap.Buffer.Count > k) { resultHeap.Pop(); } } // update visited list visited.Add(neighbour.Id); } } } return(resultHeap.Buffer); }
/// <inheritdoc/> internal override List <int> SelectBestForConnecting(List <int> candidatesIds, TravelingCosts <int, TDistance> travelingCosts, int layer) { /* * q ← this * R ← ∅ // result * W ← C // working queue for the candidates * if expandCandidates // expand candidates * for each e ∈ C * for each eadj ∈ neighbourhood(e) at layer lc * if eadj ∉ W * W ← W ⋃ eadj * * Wd ← ∅ // queue for the discarded candidates * while │W│ gt 0 and │R│ lt M * e ← extract nearest element from W to q * if e is closer to q compared to any element from R * R ← R ⋃ e * else * Wd ← Wd ⋃ e * * if keepPrunedConnections // add some of the discarded connections from Wd * while │Wd│ gt 0 and │R│ lt M * R ← R ⋃ extract nearest element from Wd to q * * return R */ IComparer <int> fartherIsOnTop = travelingCosts; IComparer <int> closerIsOnTop = fartherIsOnTop.Reverse(); var layerM = GetM(layer); var resultHeap = new BinaryHeap <int>(new List <int>(layerM + 1), fartherIsOnTop); var candidatesHeap = new BinaryHeap <int>(candidatesIds, closerIsOnTop); // expand candidates option is enabled if (GraphCore.Parameters.ExpandBestSelection) { var visited = new HashSet <int>(candidatesHeap.Buffer); var toAdd = new HashSet <int>(); foreach (var candidateId in candidatesHeap.Buffer) { var candidateNeighborsIDs = GraphCore.Nodes[candidateId][layer]; foreach (var candidateNeighbourId in candidateNeighborsIDs) { if (!visited.Contains(candidateNeighbourId)) { toAdd.Add(candidateNeighbourId); visited.Add(candidateNeighbourId); } } } foreach (var id in toAdd) { candidatesHeap.Push(id); } } // main stage of moving candidates to result var discardedHeap = new BinaryHeap <int>(new List <int>(candidatesHeap.Buffer.Count), closerIsOnTop); while (candidatesHeap.Buffer.Any() && resultHeap.Buffer.Count < layerM) { var candidateId = candidatesHeap.Pop(); var farestResultId = resultHeap.Buffer.FirstOrDefault(); if (!resultHeap.Buffer.Any() || DistanceUtils.LowerThan(travelingCosts.From(candidateId), travelingCosts.From(farestResultId))) { resultHeap.Push(candidateId); } else if (GraphCore.Parameters.KeepPrunedConnections) { discardedHeap.Push(candidateId); } } // keep pruned option is enabled if (GraphCore.Parameters.KeepPrunedConnections) { while (discardedHeap.Buffer.Any() && resultHeap.Buffer.Count < layerM) { resultHeap.Push(discardedHeap.Pop()); } } return(resultHeap.Buffer); }