/// <summary> /// Missing mapping to P objects /// KdTree could be refactored to use P object instead of Math.Net /// /// O(k * log n) /// </summary> /// <param name="s"></param> /// <param name="origin"></param> /// <param name="k"></param> /// <param name="conf"></param> /// <returns></returns> public long UpdateKnn(IAlgorithm s, IP origin, KnnConfiguration conf) { if (conf == null) conf = new KnnConfiguration(); if (conf.SameTypeOnly) throw new NotImplementedException(); if (conf.MaxDistance.HasValue) throw new NotImplementedException(); var sw = new Stopwatch(); sw.Start(); var vector = new DenseVector(new[] { origin.X, origin.Y }); var nn = Tree.FindNearestNNeighbors(vector, conf.K).ToList(); s.Knn.Clear(); s.Knn.Origin = origin; s.Knn.K = conf.K; foreach (var i in nn) { var p = new P { X = i[0], Y = i[1] }; var dist = origin.Distance(p.X,p.Y); s.Knn.NNs.Add(new PDist {Point = p, Distance = dist}); } sw.Stop(); return sw.ElapsedMilliseconds; }
// K nearest neighbor protected void UpdateKnnGridStrategy(IAlgorithm s, int max, KnnConfiguration conf) { var g = s.GridContainer; var nn = s.Knn; var square = s.Rectangle.Square; var currRing = new List<IPDist>(); var nextRing = new List<IPDist>(); for (var i = 1; i <= max; i++) { var temp = new List<IPDist>(); foreach (var p in nextRing) { if (p.Distance < i * square) currRing.Add(p); else temp.Add(p); } nextRing.Clear(); nextRing.AddRange(temp); var list = g.GetRing(nn.Origin, i); // First 9 squares, dont include origin if (i == 1) list.AddRange(g.GetSet(nn.Origin).Where(a => !a.Equals(nn.Origin)).ToList()); // Only NN on same type if set if (conf.SameTypeOnly) list = list.Where(a => a.Type == nn.Origin.Type).ToList(); var dataWasAdded = false; foreach (var p in list) { var dist = nn.Origin.Distance(p.X, p.Y); if(dist >= conf.MaxDistance) continue; // not within max distance if (dist < i * square) currRing.Add(new PDist { Point = p, Distance = dist }); else nextRing.Add(new PDist { Point = p, Distance = dist }); dataWasAdded = true; } if(conf.MaxDistance.HasValue && !dataWasAdded) break; // max distance used and no new data was added, then we are done if (currRing.Count >= nn.K) break; // enough neighbors? then done } if (currRing.Count < nn.K) { // Only NN on same type if set currRing.AddRange(conf.SameTypeOnly ? nextRing.Where(a => a.Point.Type == nn.Origin.Type).ToList() : nextRing); if (conf.MaxDistance.HasValue) currRing = currRing.Where(i => i.Distance < conf.MaxDistance.Value).ToList(); } currRing.Sort(); nn.NNs = currRing.Count > nn.K ? currRing.Take(nn.K).ToList() : currRing.ToList(); }
static void Run() { // Config var rect = new Rectangle { XMin = -200, XMax = 200, YMin = -100, YMax = 100, MaxDistance = 20, }; rect.Validate(); var conf = new KnnConfiguration { K = 100 }; // Random points IPoints points = new Points(); var rand = new Random(); for (var i = 0; i < 500000; i++) { var x = rect.XMin + rand.NextDouble() * rect.Width; var y = rect.YMin + rand.NextDouble() * rect.Height; points.Data.Add(new P { X = x, Y = y, }); } points.Round(3); // Init algo IAlgorithm algo = new Algorithm(points, rect, StrategyType.Grid); // Use algo var origin = new P { X = 0, Y = 0 }; var duration = algo.UpdateKnn(origin, conf); // Print result WL(string.Format("{0} msec. {1}:", algo.Strategy.Name, duration)); WL("K Nearest Neighbors:"); WL(string.Format("Origin: {0}", origin)); WL(string.Format("Distance sum: {0}", algo.Knn.GetDistanceSum())); algo.Knn.NNs.OrderBy(i => i.Distance).ToList().ForEach(WL); // Update strategy algo.SetAlgorithmStrategy(new NaiveStrategy()); // Use algo duration = algo.UpdateKnn(origin, conf); // Print result WL(string.Format("\n{0} msec. {1}:", algo.Strategy.Name, duration)); WL("K Nearest Neighbors:"); WL(string.Format("Distance sum: {0}", algo.Knn.GetDistanceSum())); algo.Knn.NNs.OrderBy(i => i.Distance).ToList().ForEach(WL); }
// O(n * m) where m is grid cells public long UpdateKnn(IAlgorithm s, IP p, KnnConfiguration conf) { if (conf == null) conf = new KnnConfiguration(); var sw = new Stopwatch(); sw.Start(); var max = Math.Max(s.Rectangle.XGrid, s.Rectangle.YGrid); s.Knn.Clear(); s.Knn.Origin = p; s.Knn.K = conf.K; UpdateKnnGridStrategy(s, max, conf); sw.Stop(); return sw.ElapsedMilliseconds; }
/// <summary> /// O(n * k * logk) // much faster than O(n logn) for k << n /// </summary> /// <param name="s"></param> /// <param name="p"></param> /// <param name="conf"></param> /// <returns></returns> public override long UpdateKnn(IAlgorithm s, IP p, KnnConfiguration conf) { if (conf == null) conf = new KnnConfiguration(); var sw = new Stopwatch(); sw.Start(); s.Knn.Clear(); s.Knn.Origin = p; s.Knn.K = conf.K; //var all = new List<IPDist>(); var sortedList2 = new SortedList2(); var n = s.Points.Count; for (var i = 0; i < n; i++) { var p1 = s.Points[i]; if (p.Equals(p1)) continue; // don't include origin if (conf.SameTypeOnly && p.Type != p1.Type) continue; // only same type used var dist = p.Distance(p1.X, p1.Y); if (dist >= conf.MaxDistance) continue; var pdist = new PDist { Point = p1, Distance = dist }; //all.Add(pdist); sortedList2.Add(pdist, conf.K); } //s.Knn.NNs = all.OrderBy(i => i.Distance).Take(conf.K).ToList(); // O(n logn) s.Knn.NNs = sortedList2.GetAll(); // O(n * k * logk) sw.Stop(); return sw.ElapsedMilliseconds; }
public long UpdateKnn(IP p, KnnConfiguration configuration) { UpdateIndex(p); return Strategy.UpdateKnn(this, p, configuration); }
// Example of usage: // Get 3 nearest -> /AreaGMC/gmc.svc/Knn/lat=8_5;lon=10_25;k=3 // Get 3 nearest type 1 -> /AreaGMC/gmc.svc/Knn/lat=8_5;lon=10_25;k=3;type=1 // Get nearest within 1 km -> /AreaGMC/gmc.svc/Knn/lat=8_5;lon=10_25;k=1000000;dist=1 public JsonKnnReply Knn(string s) { var sw = new Stopwatch(); sw.Start(); var invalid = new JsonKnnReply {}; if (string.IsNullOrEmpty(s)) { invalid.EMsg = "param is empty"; return invalid; } var arr = s.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries); if (arr.Length < 3) { invalid.EMsg = string.Format("param length is not valid: {0}",arr.Length); return invalid; } var nvc = new NameValueCollection(); foreach (var a in arr) { var kv = a.Split(new[] { "=" }, StringSplitOptions.RemoveEmptyEntries); if (kv.Length != 2) continue; nvc.Add(kv[0], kv[1]); } foreach (var key in Ajax.KnnReq) { if (nvc[key] != null) continue; invalid.EMsg = string.Format("param {0} is missing", key); return invalid; } try { var y = nvc[Ajax.lat].Replace("_", ".").ToDouble(); var x = nvc[Ajax.lon].Replace("_", ".").ToDouble(); var k = int.Parse(nvc[Ajax.k]); var type = nvc[Ajax.type] == null ? -1 : int.Parse(nvc[Ajax.type]); double? dist = null; if(! string.IsNullOrEmpty(nvc[Ajax.dist])) dist = nvc[Ajax.dist].Replace("_", ".").ToDouble(); // knn algo var algo = MemoryDatabase.Data as IKnnAlgorithm; if (algo == null) { invalid.EMsg = "algorithm is not available"; return invalid; } // Use algo var origin = new SingleDetectLibrary.Code.Data.P { X = x, Y = y, Type = type }; var knnSameTypeOnly = type != -1; var conf = new KnnConfiguration {K = k, SameTypeOnly = knnSameTypeOnly, MaxDistance = dist}; var duration = algo.UpdateKnn(origin, conf); var nns = algo.Knn.NNs.Select(p => p as PDist).ToList(); var gmsNns = new List<GmcPDist>(); foreach (var i in nns) { //i.Distance = Math.Round(i.Distance, 7); var pdist = new GmcPDist { Id = i.Point.Uid.ToString(), Point = i.Point, Distance = Math.Round(i.Distance, 7) }; gmsNns.Add(pdist); } var result = new JsonKnnReply { Data = string.Format("Distance in km, x: {0}; y: {1}; k: {2}; sameTypeOnly: {3}, algo msec: {4}", x.DoubleToString(), y.DoubleToString(), k, knnSameTypeOnly, duration), Nns = gmsNns, // cannot be interface, thus casting Msec = Sw(sw), }; return result; } catch (Exception ex) { invalid.EMsg = string.Format("Parsing error param: {0}", ex.Message); return invalid; } }
public abstract long UpdateKnn(IAlgorithm s, IP p, KnnConfiguration configuration);