/// <summary> /// Compose an enumerable that encompasses a range of points starting at the given point and running for the given length. /// If the point is too close to the end of the list in sorted order, fewer items than rangeLength may be returned. /// </summary> /// <param name="p">Point where range starts.</param> /// <param name="rangeLength">Range length.</param> public IEnumerable <HilbertPoint> Range(HilbertPoint p, int rangeLength) { var position = SortedPosition(p); var rangeStart = Math.Min(Math.Max(0, position - rangeLength / 2), Count - rangeLength); return(SortedPoints.Skip(rangeStart).Take(rangeLength)); }
/// <summary> /// Finds an approximately closest pair of points (one of each color) by using the ordering found in SortedPoints. /// /// This compares points in two of the clusters, ignoring points in all other clusters. /// /// NOTE: This was a good idea, but yields results too poor to be used. /// </summary> /// <param name="color1">Label of the first cluster.</param> /// <param name="color2">Label of the second cluster.</param> /// <returns>The point with Color1, the point with Color2 and the square distance between them. /// </returns> public ClosestPair FindPairApproximately(TLabel color1, TLabel color2) { var shortest = new ClosestPair(); UnsignedPoint prevP = null; TLabel prevColor = default(TLabel); foreach (var pc in SortedPoints .Select(p => new { Point = p, Color = Clusters.GetClassLabel(p) }) .Where(pc => pc.Color.Equals(color1) || pc.Color.Equals(color2))) { if (prevP != null && !prevColor.Equals(pc.Color)) { var d = pc.Point.Measure(prevP); if (d < shortest.SquareDistance) { shortest.SquareDistance = d; shortest.Color1 = prevColor; shortest.Point1 = prevP; shortest.Color2 = pc.Color; shortest.Point2 = pc.Point; } } prevP = pc.Point; prevColor = pc.Color; } return(shortest.Swap(color1)); }
/// <summary> /// Find the points adjacent to the given point in the Hilbert ordering, then sort them by the cartesian distance, from nearest to farthest. /// </summary> /// <param name="point">Reference point to seek in the index.</param> /// <param name="rangeLength">Number of points to retrieve from the index. Half of these points will precede and half succeed the given point /// in the index, unless we are near the beginning or end of the index, in which case the range will be shifted.</param> /// <param name="includePointItself">If false, the reference point will not be present in the results. /// If true, the point will be present in the results.</param> /// <returns>The points nearest to the reference point in both Hilbert and Cartesian ordering, sorted from nearest to farthest.</returns> public IEnumerable <HilbertPoint> NearestFromRange(HilbertPoint point, int rangeLength, bool includePointItself = false) { rangeLength = includePointItself ? rangeLength : rangeLength + 1; var middlePosition = SortedPosition(point); var rangeStart = Math.Max(0, middlePosition - rangeLength / 2); return(SortedPoints .Skip(rangeStart) .Take(rangeLength) .Where(p => includePointItself || !p.Equals(point)) .OrderBy(p => p.Measure(point))); }
private void InitIndexing() { Index = new Dictionary <UnsignedPoint, Position>(); foreach (var pointWithIndex in UnsortedPoints.Select((p, i) => new { Point = p, OriginalPosition = i })) { Index[pointWithIndex.Point] = new Position(pointWithIndex.OriginalPosition, -1); } foreach (var pointWithIndex in SortedPoints.Select((p, i) => new { Point = p, SortedPosition = i })) { Index[pointWithIndex.Point].Sorted = pointWithIndex.SortedPosition; } IdsToPoints = new Dictionary <int, UnsignedPoint>(); foreach (var p in UnsortedPoints) { IdsToPoints[p.UniqueId] = p; } }
/// <summary> /// Check if the ids in the points in the Clusters match the ids in the SortedPoints. /// </summary>s private void ValidateIds() { if (SortedPoints.Count != Clusters.NumPoints) { throw new InvalidOperationException("Clusters holds more points than SortedPoints"); } var idRangeClusters = Clusters.Points().Aggregate( new { Min = int.MaxValue, Max = int.MinValue }, (accumulator, o) => new { Min = Math.Min(o.UniqueId, accumulator.Min), Max = Math.Max(o.UniqueId, accumulator.Max) } ); var idRangeSortedPoints = SortedPoints.Aggregate( new { Min = int.MaxValue, Max = int.MinValue }, (accumulator, o) => new { Min = Math.Min(o.UniqueId, accumulator.Min), Max = Math.Max(o.UniqueId, accumulator.Max) } ); if (idRangeClusters.Min != idRangeSortedPoints.Min) { throw new InvalidOperationException("The lowest Id among the points in SortedPoints and Clusters is not the same"); } if (idRangeClusters.Max != idRangeSortedPoints.Max) { throw new InvalidOperationException("The highest Id among the points in SortedPoints and Clusters is not the same"); } /* * // Exhaustive comparison of all ids. More costly. * var idsInClusters = new HashSet<int>(); * foreach (var point in Clusters.Points()) * idsInClusters.Add(point.UniqueId); * * foreach (var point in SortedPoints) * { * if (!idsInClusters.Contains(point.UniqueId)) * throw new InvalidOperationException("SortedPoints has a point whose Id does not match a point in Clusters"); * } */ }
/// <summary> /// Find the K-nearest neighbors of a given point according to the cartesian distance between the point and its neighbors. /// /// NOTE: This compares the point to all other points, hence is more costly than NearestFromRange but is guaranteed /// to find all near neighbors. /// </summary> /// <param name="point">Reference point whose neighbors are sought.</param> /// <param name="k">Number of nearest neighbors to find.</param> /// <param name="includePointItself">If false, the point is not considered its own neighbor and will not be present in the results. /// If true, the point is considered its own neighbor and will be present in the results, /// unless all the nearest neighbors are zero distance from this point, in which case it might not make the cut.</param> /// <returns>The nearest neighbors of the given point, sorted from nearest to farthest.</returns> public IEnumerable <HilbertPoint> Nearest(HilbertPoint point, int k, bool includePointItself = false) { return(SortedPoints .Where(p => includePointItself || !p.Equals(point)) .BottomN <HilbertPoint, long>(point, k)); }