Skip to content

Commit

Permalink
hnsw: add ability to configure ef search (#803)
Browse files Browse the repository at this point in the history
  • Loading branch information
dankinder committed Feb 23, 2024
1 parent 78c016b commit 04f8767
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 3 deletions.
21 changes: 19 additions & 2 deletions base/search/hnsw.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ type HNSW struct {
levelFactor float32
maxConnection int // maximum number of connections for each element per layer
maxConnection0 int
ef int
efConstruction int
numJobs int
}
Expand Down Expand Up @@ -78,6 +79,14 @@ func SetEFConstruction(efConstruction int) HNSWConfig {
}
}

// SetEF sets the EF search value in HNSW.
// By default ef for search is the same as efConstruction. To return it to this default behavior, set it to 0.
func SetEF(ef int) HNSWConfig {
return func(h *HNSW) {
h.ef = ef
}
}

// NewHNSW builds a vector index based on Hierarchical Navigable Small Worlds.
func NewHNSW(vectors []Vector, configs ...HNSWConfig) *HNSW {
h := &HNSW{
Expand All @@ -96,7 +105,7 @@ func NewHNSW(vectors []Vector, configs ...HNSWConfig) *HNSW {

// Search a vector in Hierarchical Navigable Small Worlds.
func (h *HNSW) Search(q Vector, n int, prune0 bool) (values []int32, scores []float32) {
w := h.knnSearch(q, n, mathutil.Max(h.efConstruction, n))
w := h.knnSearch(q, n, h.efSearchValue(n))
for w.Len() > 0 {
value, score := w.Pop()
if !prune0 || score < 0 {
Expand Down Expand Up @@ -418,7 +427,7 @@ func (h *HNSW) MultiSearch(q Vector, terms []string, n int, prune0 bool) (values
scores[term] = make([]float32, 0, n)
}

w := h.efSearch(q, mathutil.Max(h.efConstruction, n))
w := h.efSearch(q, h.efSearchValue(n))
for w.Len() > 0 {
value, score := w.Pop()
if !prune0 || score < 0 {
Expand Down Expand Up @@ -452,6 +461,14 @@ func (h *HNSW) efSearch(q Vector, ef int) *heap.PriorityQueue {
return w
}

// efSearchValue returns the efSearch value to use, given the current number of elements desired.
func (h *HNSW) efSearchValue(n int) int {
if h.ef > 0 {
return mathutil.Max(h.ef, n)
}
return mathutil.Max(h.efConstruction, n)
}

func EstimateHNSWBuilderComplexity(dataSize, trials int) int {
// build index
complexity := dataSize * dataSize
Expand Down
6 changes: 5 additions & 1 deletion base/search/hnsw_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
package search

import (
"github.com/stretchr/testify/assert"
"testing"

"github.com/stretchr/testify/assert"
)

func TestHNSWConfig(t *testing.T) {
Expand All @@ -30,4 +31,7 @@ func TestHNSWConfig(t *testing.T) {

SetEFConstruction(345)(hnsw)
assert.Equal(t, 345, hnsw.efConstruction)

SetEF(456)(hnsw)
assert.Equal(t, 456, hnsw.ef)
}

0 comments on commit 04f8767

Please sign in to comment.