Skip to content

Commit 41f4a36

Browse files
authored
remove empty layer after delete or update value (#11)
1. don't panic when update data or search and and remove empty layer after 2. add new search function return distance
1 parent ff889c9 commit 41f4a36

File tree

4 files changed

+70
-13
lines changed

4 files changed

+70
-13
lines changed

go.mod

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,19 @@ module github.com/coder/hnsw
22

33
go 1.21.4
44

5-
require github.com/stretchr/testify v1.9.0
6-
7-
require github.com/google/renameio v1.0.1
8-
95
require (
10-
github.com/chewxy/math32 v1.10.1 // indirect
11-
github.com/viterin/partial v1.1.0 // indirect
12-
github.com/viterin/vek v0.4.2 // indirect
13-
golang.org/x/sys v0.11.0 // indirect
6+
github.com/google/renameio v1.0.1
7+
github.com/stretchr/testify v1.9.0
148
)
159

1610
require (
11+
github.com/chewxy/math32 v1.10.1 // indirect
1712
github.com/davecgh/go-spew v1.1.1 // indirect
13+
github.com/kr/text v0.2.0 // indirect
1814
github.com/pmezard/go-difflib v1.0.0 // indirect
15+
github.com/viterin/partial v1.1.0 // indirect
16+
github.com/viterin/vek v0.4.2
1917
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
18+
golang.org/x/sys v0.11.0 // indirect
2019
gopkg.in/yaml.v3 v3.0.1 // indirect
2120
)

go.sum

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
github.com/chewxy/math32 v1.10.1 h1:LFpeY0SLJXeaiej/eIp2L40VYfscTvKh/FSEZ68uMkU=
22
github.com/chewxy/math32 v1.10.1/go.mod h1:dOB2rcuFrCn6UHrze36WSLVPKtzPMRAQvBvUwkSsLqs=
3+
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
34
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
45
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
56
github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU=
67
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
8+
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
9+
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
10+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
11+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
712
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
813
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
914
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
@@ -16,7 +21,8 @@ golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJ
1621
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
1722
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
1823
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
19-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2024
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
25+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
26+
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2127
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
2228
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

graph.go

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -402,13 +402,35 @@ func (g *Graph[K]) Add(nodes ...Node[K]) {
402402

403403
// Invariant check: the node should have been added to the graph.
404404
if g.Len() != preLen+1 {
405-
panic("node not added")
405+
if len(g.layers) > 0 && g.layers[len(g.layers)-1].entry() == nil {
406+
g.layers = g.layers[:len(g.layers)-1]
407+
}
406408
}
407409
}
408410
}
409411

410412
// Search finds the k nearest neighbors from the target node.
411413
func (h *Graph[K]) Search(near Vector, k int) []Node[K] {
414+
sr := h.search(near, k)
415+
out := make([]Node[K], len(sr))
416+
for i, node := range sr {
417+
out[i] = node.Node
418+
}
419+
return out
420+
}
421+
422+
// SearchWithDistance finds the k nearest neighbors from the target node
423+
// and returns the distance.
424+
func (h *Graph[K]) SearchWithDistance(near Vector, k int) []SearchResult[K] {
425+
return h.search(near, k)
426+
}
427+
428+
type SearchResult[T cmp.Ordered] struct {
429+
Node[T]
430+
Distance float32
431+
}
432+
433+
func (h *Graph[K]) search(near Vector, k int) []SearchResult[K] {
412434
h.assertDims(near)
413435
if len(h.layers) == 0 {
414436
return nil
@@ -434,10 +456,13 @@ func (h *Graph[K]) Search(near Vector, k int) []Node[K] {
434456
}
435457

436458
nodes := searchPoint.search(k, efSearch, near, h.Distance)
437-
out := make([]Node[K], 0, len(nodes))
459+
out := make([]SearchResult[K], 0, len(nodes))
438460

439461
for _, node := range nodes {
440-
out = append(out, node.node.Node)
462+
out = append(out, SearchResult[K]{
463+
Node: node.node.Node,
464+
Distance: node.dist,
465+
})
441466
}
442467

443468
return out
@@ -462,17 +487,33 @@ func (h *Graph[K]) Delete(key K) bool {
462487
return false
463488
}
464489

490+
var deleteLayer = map[int]struct{}{}
465491
var deleted bool
466-
for _, layer := range h.layers {
492+
for i, layer := range h.layers {
467493
node, ok := layer.nodes[key]
468494
if !ok {
469495
continue
470496
}
471497
delete(layer.nodes, key)
498+
if len(layer.nodes) == 0 {
499+
deleteLayer[i] = struct{}{}
500+
}
472501
node.isolate(h.M)
473502
deleted = true
474503
}
475504

505+
if len(deleteLayer) > 0 {
506+
var newLayers = make([]*layer[K], 0, len(h.layers)-len(deleteLayer))
507+
for i, layer := range h.layers {
508+
if _, ok := deleteLayer[i]; ok {
509+
continue
510+
}
511+
newLayers = append(newLayers, layer)
512+
}
513+
514+
h.layers = newLayers
515+
}
516+
476517
return deleted
477518
}
478519

graph_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,14 @@ func TestGraph_DefaultCosine(t *testing.T) {
248248
neighbors,
249249
)
250250
}
251+
252+
func TestGraph_RemoveAllNodes(t *testing.T) {
253+
var vec = []float32{1}
254+
255+
for i := 0; i < 10; i++ {
256+
g := NewGraph[int]()
257+
g.Add(MakeNode(1, vec))
258+
g.Delete(1)
259+
g.Add(MakeNode(1, vec))
260+
}
261+
}

0 commit comments

Comments
 (0)