diff --git a/docs/modules/ROOT/pages/how-to/databases-and-queries.adoc b/docs/modules/ROOT/pages/how-to/databases-and-queries.adoc index ba9d927d..3413d0e8 100644 --- a/docs/modules/ROOT/pages/how-to/databases-and-queries.adoc +++ b/docs/modules/ROOT/pages/how-to/databases-and-queries.adoc @@ -226,7 +226,7 @@ The bindings' check counts the `?`, `$` and `:` characters of the SQL text, so i ==== A query vector -A query vector for `vectorNeighbors` can be a numpy array or a list. The conversion before the statement runs is the cost to watch: a query takes about 0.3 to 0.75 ms, and a vector of 1536 numbers took 0.5 to 0.6 ms to convert when it was a list. +A query vector for `vectorNeighbors` can be a numpy array or a list. This section is about statements: `VectorIndex.find_nearest` on the native package converts any numeric query to one `float32` buffer itself, see xref:how-to/vectors.adoc#find-nearest[find_nearest]. The conversion before the statement runs is the cost to watch: a query takes about 0.3 to 0.75 ms, and a vector of 1536 numbers took 0.5 to 0.6 ms to convert when it was a list. * A `float32` array crosses as one buffer on both builds: about 5 to 15 us. This is the fast path. `np.asarray(v, dtype=np.float32)` makes it from a list or from another array. * A list that holds only Python floats (no `int`, `bool`, `None`, `str` or numpy scalar) also crosses as one buffer. The statement reads the same list of doubles it always read: no value changes, and an `int` in a list stays an `int`. A list shorter than 32 numbers (JVM build) or 64 numbers (native build) is not worth a buffer and goes number by number. On the native build the list crosses as a buffer only when `float32` holds every number exactly, which is true of a list from a float32 model (`array.tolist()`); a list of other doubles, `NaN` or an infinity is sent as text, because a buffer would round it. diff --git a/docs/modules/ROOT/pages/how-to/vectors.adoc b/docs/modules/ROOT/pages/how-to/vectors.adoc index 8f55fa4c..df066b24 100644 --- a/docs/modules/ROOT/pages/how-to/vectors.adoc +++ b/docs/modules/ROOT/pages/how-to/vectors.adoc @@ -159,10 +159,34 @@ print([row["name"] for row in rows]) ['python-ish', 'python'] ---- +[[find-nearest]] === find_nearest `VectorIndex.find_nearest(query_vector, k=10, ef_search=None, allowed_rids=None)` searches without a statement. It takes a list or a NumPy array and returns a list of `(record, distance)` tuples, nearest first. Use it when you already hold the index object, or when you have a list of record ids to search within. +On the native package the query vector crosses as one `float32` buffer whatever its form (a list, a `float64` array, or a `float32` array), so you do not need to convert it first. A vector index stores `float32`, so the numbers are rounded the same way as before. The table gives the median `find_nearest` latency with this change over the median without it (95% interval, below 1 is faster), measured on the native wheel `26.11.1.dev0` on one Linux x86_64 laptop with 11 to 12 clean pairs per figure. The wheel built before this change was the baseline. + +[cols="3,2,2",options="header"] +|=== +| Query passed as | 1536 dimensions | 256 dimensions + +| `find_nearest`, a `float32` array +| 0.951 [0.945, 0.954] +| 0.952 [0.942, 0.96] + +| `find_nearest`, a `float64` array +| 0.616 [0.614, 0.621] +| 0.777 [0.774, 0.781] + +| `find_nearest`, a list of Python floats +| 0.631 [0.627, 0.635] +| 0.792 [0.787, 0.798] + +| `vectorNeighbors` statement with a `float32` array (not changed) +| 1.003 [0.995, 1.014] +| 1.004 [0.991, 1.006] +|=== + [source,python] ---- for record, distance in index.find_nearest(query, k=2): diff --git a/docs/modules/ROOT/pages/which-package.adoc b/docs/modules/ROOT/pages/which-package.adoc index a77d58ad..4688e525 100644 --- a/docs/modules/ROOT/pages/which-package.adoc +++ b/docs/modules/ROOT/pages/which-package.adoc @@ -73,7 +73,7 @@ The API is the same. The runtime is not. These are the differences you can plan | Measure on your own workload. | [[vector-search]]Vector search (`LSM_VECTOR`) -| Slower to build and to query, smaller in memory. Against the JVM build, index builds took 1.28 to 1.62 times as long and median queries 1.22 to 1.87 times as long, and peak memory was 0.51 to 0.67 of the JVM build's (medians, 100,000 vectors of 128, 768 and 1536 dimensions, `26.11.1.dev0`, one laptop, with the Highway kernels that are the default on Linux, see xref:how-to/vectors.adoc#native-provider[Distance kernels in the native package]). +| Slower to build and to query, smaller in memory. Against the JVM build, index builds took 1.28 to 1.62 times as long and median queries 1.22 to 1.87 times as long, and peak memory was 0.51 to 0.67 of the JVM build's (medians, 100,000 vectors of 128, 768 and 1536 dimensions, `26.11.1.dev0`, one laptop, measured before the change that makes native `find_nearest` send its query as one `float32` buffer (#203), with the Highway kernels that are the default on Linux, see xref:how-to/vectors.adoc#native-provider[Distance kernels in the native package]). | Faster to build and to query, larger in memory. | Wheel size diff --git a/src/arcadedb/vector.py b/src/arcadedb/vector.py index f2c69dc9..ddc8b32b 100644 --- a/src/arcadedb/vector.py +++ b/src/arcadedb/vector.py @@ -598,7 +598,8 @@ def find_nearest( option, use ``vectorNeighbors`` in SQL. Args: - query_vector: Query vector as Python list, NumPy array, or array-like + query_vector: Query vector as Python list, NumPy array, or array-like (on the + native build it crosses as one float32 buffer in any of these forms) k: Number of nearest neighbors to return (final k). Default is 10. ef_search: Optional search beam width override for exact graph search. None uses the Java index default/adaptive behavior.