using System; using System.Collections.Generic; using System.Linq; namespace ZeroLevel.HNSW { public class SmallWorld { private readonly NSWOptions _options; private readonly VectorSet _vectors; private readonly Layer[] _layers; private Layer EnterPointsLayer => _layers[_layers.Length - 1]; private Layer LastLayer => _layers[0]; public SmallWorld(NSWOptions options) { _options = options; _vectors = new VectorSet(); _layers = new Layer[_options.LayersCount]; for (int i = 0; i < _options.LayersCount; i++) { _layers[i] = new Layer(_options, _vectors); } } public IEnumerable<(int, TItem[])> Search(TItem vector, int k, HashSet activeNodes = null) { return Enumerable.Empty<(int, TItem[])>(); } public int[] AddItems(IEnumerable vectors) { var insert = vectors.ToArray(); var ids = new int[insert.Length]; for (int i = 0; i < insert.Length; i++) { var item = insert[i]; ids[i] = Insert(item); } return ids; } public int Insert(TItem item) { var id = _vectors.Append(item); INSERT(id); return id; } #region https://arxiv.org/ftp/arxiv/papers/1603/1603.09320.pdf /// /// Algorithm 1 /// /// new element public void INSERT(int q) { // W ← ∅ // list for the currently found nearest elements IDictionary W; // ep ← get enter point for hnsw var ep = EnterPointsLayer.GetEntryPointFor(q); // L ← level of ep // top layer for hnsw var L = _layers.Length - 1; // l ← ⌊-ln(unif(0..1))∙mL⌋ // new element’s level int l = DefaultRandomGenerator.Instance.Next(0, _options.LayersCount - 1); // for lc ← L … l+1 for (int lc = L; lc > l; lc--) { // W ← SEARCH-LAYER(q, ep, ef = 1, lc) W = _layers[lc].SEARCH_LAYER(q, ep, 1); // ep ← get the nearest element from W to q ep = W.OrderBy(p => p.Value).First().Key; } //for lc ← min(L, l) … 0 for (int lc = Math.Min(L, l); lc >= 0; lc--) { // W ← SEARCH - LAYER(q, ep, efConstruction, lc) W = _layers[lc].SEARCH_LAYER(q, ep, _options.EFConstruction); // neighbors ← SELECT-NEIGHBORS(q, W, M, lc) // alg. 3 or alg. 4 var neighbors = _layers[lc].SELECT_NEIGHBORS_SIMPLE(q, W); // add bidirectionall connectionts from neighbors to q at layer lc // for each e ∈ neighbors // shrink connections if needed foreach (var e in neighbors) { // eConn ← neighbourhood(e) at layer lc _layers[lc].AddBidirectionallConnectionts(q, e.Key, e.Value); } // ep ← W ep = W.OrderBy(p => p.Value).First().Key; } // if l > L // set enter point for hnsw to q } /// /// Algorithm 5 /// /// query element /// number of nearest neighbors to return /// : K nearest elements to q public IList K_NN_SEARCH(int q, int K) { // W ← ∅ // set for the current nearest elements IDictionary W; // ep ← get enter point for hnsw var ep = EnterPointsLayer.GetEntryPointFor(q); // L ← level of ep // top layer for hnsw var L = _options.LayersCount - 1; // for lc ← L … 1 for (var lc = L; lc > 0; lc--) { // W ← SEARCH-LAYER(q, ep, ef = 1, lc) W = _layers[lc].SEARCH_LAYER(q, ep, 1); // ep ← get nearest element from W to q ep = W.OrderBy(p => p.Value).First().Key; } // W ← SEARCH-LAYER(q, ep, ef, lc =0) W = LastLayer.SEARCH_LAYER(q, ep, _options.EF); // return K nearest elements from W to q return W.OrderBy(p => p.Value).Take(K).Select(p => p.Key).ToList(); } #endregion } }