alessandrobenedetti commented on code in PR #3385:
URL: https://github.com/apache/solr/pull/3385#discussion_r2244949751


##########
solr/solr-ref-guide/modules/query-guide/pages/dense-vector-search.adoc:
##########
@@ -240,6 +240,66 @@ client.add(Arrays.asList(d1, d2));
 ====
 ======
 
+=== ScalarQuantizedDenseVectorField
+Because dense vectors can have a costly storage footprint, it may be 
worthwhile to use a technique called "quantization"
+to reduce the stored representation size at the cost of some precision.
+
+This dense vector type uses a conversion that projects a 32 bit float 
precision feature down to an 8 bit int (or smaller)
+by linearly mapping the float range down to evenly sized "buckets" of values 
that fit into an int. A more detailed explanation
+can be found in this 
https://www.elastic.co/search-labs/blog/scalar-quantization-101[blog post].

Review Comment:
   I would encourage linking a resource that's not Elastic. I  don't think we 
want Elasticsearch reference in the Solr documentation, it would feel weird



##########
solr/solr-ref-guide/modules/query-guide/pages/dense-vector-search.adoc:
##########
@@ -240,6 +240,66 @@ client.add(Arrays.asList(d1, d2));
 ====
 ======
 
+=== ScalarQuantizedDenseVectorField
+Because dense vectors can have a costly storage footprint, it may be 
worthwhile to use a technique called "quantization"

Review Comment:
   I would specify that's not only strictly storage (because actually the 
storage footprint on disk increases with quantisation).
   
   I would stress memory/latency benefits above all.



##########
solr/solr-ref-guide/modules/query-guide/pages/dense-vector-search.adoc:
##########
@@ -240,6 +240,66 @@ client.add(Arrays.asList(d1, d2));
 ====
 ======
 
+=== ScalarQuantizedDenseVectorField
+Because dense vectors can have a costly storage footprint, it may be 
worthwhile to use a technique called "quantization"
+to reduce the stored representation size at the cost of some precision.
+
+This dense vector type uses a conversion that projects a 32 bit float 
precision feature down to an 8 bit int (or smaller)
+by linearly mapping the float range down to evenly sized "buckets" of values 
that fit into an int. A more detailed explanation
+can be found in this 
https://www.elastic.co/search-labs/blog/scalar-quantization-101[blog post].
+
+As a specific type of DenseVectorField, this field type supports all the same 
configurable properties outlined above as well
+as some additional ones.
+
+Here is how a ScalarQuantizedDenseVectorField can be defined in the schema:
+
+[source,xml]
+<fieldType name="scalar_quantized_vector" 
class="solr.ScalarQuantizedDenseVectorField" vectorDimension="4" 
similarityFunction="cosine"/>
+<field name="vector" type="scalar_quantized_vector" indexed="true" 
stored="true"/>
+
+`bits`::
++
+[%autowidth,frame=none]
+|===
+s|Optional |Default: `7`
+|===
++
+The number of bits to use for each quantized dimension value
++
+Accepted values: 4 (half byte) or 7 (unsigned byte).
+
+`confidenceInterval`::
++
+[%autowidth,frame=none]
+|===
+s|Optional |Default: `dimension-scaled`
+|===
++
+Statistically, outlier values are rarely meaningfully relevant to searches, so 
to increase the size of each bucket for
+quantization (and therefore information gain) we can scale the quantization 
intervals to the middle n % of values and place the remaining
+outliers in the outermost intervals.
++
+For example: 0.9 means scale interval sizes to the middle 90% of values
++
+If this param is omitted a default is used; scaled to the number of dimensions 
according to `1-1/(vector_dimensions + 1)`
++
+If `0` is provided, confidence intervals will be dynamically adjusted (at 
segment merge time) optimized by sampling values
++
+Accepted values: `FLOAT32`  (within 0.9 and 1.0) or 0 for dynamically adjusted 
confidence interval
+
+`compress`::
++
+[%autowidth,frame=none]
+|===
+s|Optional |Default: `false`
+|===
++
+If set to true, this will further pack multiple dimension values within a one 
byte alignment. This further decreases the storage size by 50% at some decode 
penalty.

Review Comment:
   is it related to the quantised vector storage (both memory and disk)?
   or only memory?
   Maybe we should clarify that on disk we still store also the full vector



##########
solr/core/src/java/org/apache/solr/schema/ScalarQuantizedDenseVectorField.java:
##########
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.schema;
+
+import static java.util.Optional.ofNullable;
+import static 
org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat.DEFAULT_NUM_MERGE_WORKER;
+
+import java.util.Map;
+import org.apache.lucene.codecs.KnnVectorsFormat;
+import 
org.apache.lucene.codecs.lucene99.Lucene99HnswScalarQuantizedVectorsFormat;
+import org.apache.lucene.codecs.lucene99.Lucene99ScalarQuantizedVectorsFormat;
+import org.apache.lucene.index.VectorEncoding;
+import org.apache.lucene.index.VectorSimilarityFunction;
+import org.apache.solr.common.SolrException;
+
+public class ScalarQuantizedDenseVectorField extends DenseVectorField {
+  public static final String BITS_PARAM = "bits"; //
+  public static final String CONFIDENCE_INTERVAL_PARAM = "confidenceInterval";
+  public static final String DYNAMIC_CONFIDENCE_INTERVAL_PARAM = 
"dynamicConfidenceInterval";
+  public static final String COMPRESS_PARAM =
+      "compress"; // can only be enabled when bits = 4 per Lucene codec spec
+
+  static final int DEFAULT_BITS = 7; // use signed byte as default when 
unspecified
+  static final Float DEFAULT_CONFIDENCE_INTERVAL = null; // use dimension 
scaled confidence interval
+
+  // lucene does not expose these so we are duplicating them here
+  static final Float MINIMUM_CONFIDENCE_INTERVAL = 0.9f;
+  static final Float MAXIMUM_CONFIDENCE_INTERVAL = 1f;
+
+  /**
+   * Number of bits to use for storage Must be 4 (half-byte) or 7 
(signed-byte) per Lucene codec
+   * spec
+   */
+  private int bits;
+
+  /**
+   * Confidence interval to use for scalar quantization Default is calculated 
as
+   * `1-1/(vector_dimensions + 1)`
+   */
+  private Float confidenceInterval;
+
+  /**
+   * When enabled, in conjunction with 4 bit size, will pair values into 
single bytes for 50%
+   * reduction in memory usage (comes at the cost of some decode speed penalty)
+   */
+  private boolean compress;
+
+  public ScalarQuantizedDenseVectorField() {
+    super();
+  }
+
+  public ScalarQuantizedDenseVectorField(
+      int dimension,
+      VectorSimilarityFunction similarityFunction,
+      VectorEncoding vectorEncoding,
+      int bits,
+      Float confidenceInterval,
+      boolean compress) {
+    super(dimension, similarityFunction, vectorEncoding);
+    this.bits = bits;
+    this.confidenceInterval = confidenceInterval;
+    this.compress = compress;
+  }
+
+  @Override
+  public void init(IndexSchema schema, Map<String, String> args) {
+    this.bits = 
ofNullable(args.remove(BITS_PARAM)).map(Integer::parseInt).orElse(DEFAULT_BITS);
+
+    this.compress =
+        
ofNullable(args.remove(COMPRESS_PARAM)).map(Boolean::parseBoolean).orElse(false);
+
+    this.confidenceInterval =
+        ofNullable(args.remove(CONFIDENCE_INTERVAL_PARAM))
+            .map(Float::parseFloat)
+            .orElse(DEFAULT_CONFIDENCE_INTERVAL);
+
+    if (ofNullable(args.remove(DYNAMIC_CONFIDENCE_INTERVAL_PARAM))
+        .map(Boolean::parseBoolean)
+        .orElse(false)) {
+      this.confidenceInterval = 
Lucene99ScalarQuantizedVectorsFormat.DYNAMIC_CONFIDENCE_INTERVAL;
+    }
+
+    super.init(schema, args);
+  }
+
+  @Override
+  public KnnVectorsFormat buildKnnVectorsFormat() {
+    return new Lucene99HnswScalarQuantizedVectorsFormat(
+        getHnswMaxConn(),
+        getHnswBeamWidth(),
+        DEFAULT_NUM_MERGE_WORKER,
+        getBits(),
+        useCompression(),
+        getConfidenceInterval(),
+        null);
+  }
+
+  @Override
+  public void checkSchemaField(final SchemaField field) throws SolrException {
+    super.checkSchemaField(field);
+
+    if (confidenceInterval != null
+        && confidenceInterval != 
Lucene99ScalarQuantizedVectorsFormat.DYNAMIC_CONFIDENCE_INTERVAL
+        && (confidenceInterval < MINIMUM_CONFIDENCE_INTERVAL
+            || confidenceInterval > MAXIMUM_CONFIDENCE_INTERVAL)) {
+      throw new SolrException(
+          SolrException.ErrorCode.SERVER_ERROR,
+          getClass().getSimpleName()
+              + " fields must have non-dynamic confidence interval between 0.9 
and 1.0 "
+              + field.getName());
+    }
+    if (getBits() != 4 && getBits() != 7) {

Review Comment:
   can't we rely on Lucene checks that throw the "if (bits < 1 || bits > 8 || 
(ALLOWED_BITS & (1 << bits)) == 0) {
         throw new IllegalArgumentException("bits must be one of: 4, 7; bits=" 
+ bits);
       }"



##########
solr/core/src/test-files/solr/collection1/conf/schema-densevector.xml:
##########
@@ -25,7 +25,7 @@
   <fieldType name="high_dimensional_float_knn_vector" 
class="solr.DenseVectorField" vectorDimension="2048" 
similarityFunction="cosine" vectorEncoding="FLOAT32"/>
   <fieldType name="high_dimensional_byte_knn_vector" 
class="solr.DenseVectorField" vectorDimension="2048" 
similarityFunction="cosine" vectorEncoding="BYTE"/>
   <fieldType name="plong" class="solr.LongPointField" 
useDocValuesAsStored="false"/>
-  
+

Review Comment:
   I guess this is unwanted



##########
solr/core/src/java/org/apache/solr/schema/ScalarQuantizedDenseVectorField.java:
##########
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.schema;
+
+import static java.util.Optional.ofNullable;
+import static 
org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat.DEFAULT_NUM_MERGE_WORKER;
+
+import java.util.Map;
+import org.apache.lucene.codecs.KnnVectorsFormat;
+import 
org.apache.lucene.codecs.lucene99.Lucene99HnswScalarQuantizedVectorsFormat;
+import org.apache.lucene.codecs.lucene99.Lucene99ScalarQuantizedVectorsFormat;
+import org.apache.lucene.index.VectorEncoding;
+import org.apache.lucene.index.VectorSimilarityFunction;
+import org.apache.solr.common.SolrException;
+
+public class ScalarQuantizedDenseVectorField extends DenseVectorField {
+  public static final String BITS_PARAM = "bits"; //
+  public static final String CONFIDENCE_INTERVAL_PARAM = "confidenceInterval";
+  public static final String DYNAMIC_CONFIDENCE_INTERVAL_PARAM = 
"dynamicConfidenceInterval";
+  public static final String COMPRESS_PARAM =
+      "compress"; // can only be enabled when bits = 4 per Lucene codec spec
+
+  static final int DEFAULT_BITS = 7; // use signed byte as default when 
unspecified
+  static final Float DEFAULT_CONFIDENCE_INTERVAL = null; // use dimension 
scaled confidence interval
+
+  // lucene does not expose these so we are duplicating them here
+  static final Float MINIMUM_CONFIDENCE_INTERVAL = 0.9f;

Review Comment:
   I'm not sure we need this here, looking at Lucene code, in case we go off 
the values we get an "llegalArgumentException(
             "confidenceInterval must be between "
                 + MINIMUM_CONFIDENCE_INTERVAL
                 + " and "
                 + MAXIMUM_CONFIDENCE_INTERVAL
                 + " or 0"
                 + "; confidenceInterval="
                 + confidenceInterval);"
                 
                 Can't we catch this exception and handle it rather than 
duplicating the constants?



##########
solr/core/src/java/org/apache/solr/schema/ScalarQuantizedDenseVectorField.java:
##########
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.schema;
+
+import static java.util.Optional.ofNullable;
+import static 
org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat.DEFAULT_NUM_MERGE_WORKER;
+
+import java.util.Map;
+import org.apache.lucene.codecs.KnnVectorsFormat;
+import 
org.apache.lucene.codecs.lucene99.Lucene99HnswScalarQuantizedVectorsFormat;
+import org.apache.lucene.codecs.lucene99.Lucene99ScalarQuantizedVectorsFormat;
+import org.apache.lucene.index.VectorEncoding;
+import org.apache.lucene.index.VectorSimilarityFunction;
+import org.apache.solr.common.SolrException;
+
+public class ScalarQuantizedDenseVectorField extends DenseVectorField {
+  public static final String BITS_PARAM = "bits"; //
+  public static final String CONFIDENCE_INTERVAL_PARAM = "confidenceInterval";
+  public static final String DYNAMIC_CONFIDENCE_INTERVAL_PARAM = 
"dynamicConfidenceInterval";
+  public static final String COMPRESS_PARAM =
+      "compress"; // can only be enabled when bits = 4 per Lucene codec spec
+
+  static final int DEFAULT_BITS = 7; // use signed byte as default when 
unspecified
+  static final Float DEFAULT_CONFIDENCE_INTERVAL = null; // use dimension 
scaled confidence interval
+
+  // lucene does not expose these so we are duplicating them here
+  static final Float MINIMUM_CONFIDENCE_INTERVAL = 0.9f;
+  static final Float MAXIMUM_CONFIDENCE_INTERVAL = 1f;
+
+  /**
+   * Number of bits to use for storage Must be 4 (half-byte) or 7 
(signed-byte) per Lucene codec
+   * spec
+   */
+  private int bits;
+
+  /**
+   * Confidence interval to use for scalar quantization Default is calculated 
as
+   * `1-1/(vector_dimensions + 1)`
+   */
+  private Float confidenceInterval;
+
+  /**
+   * When enabled, in conjunction with 4 bit size, will pair values into 
single bytes for 50%
+   * reduction in memory usage (comes at the cost of some decode speed penalty)
+   */
+  private boolean compress;
+
+  public ScalarQuantizedDenseVectorField() {
+    super();
+  }
+
+  public ScalarQuantizedDenseVectorField(
+      int dimension,
+      VectorSimilarityFunction similarityFunction,
+      VectorEncoding vectorEncoding,
+      int bits,
+      Float confidenceInterval,
+      boolean compress) {
+    super(dimension, similarityFunction, vectorEncoding);
+    this.bits = bits;
+    this.confidenceInterval = confidenceInterval;
+    this.compress = compress;
+  }
+
+  @Override
+  public void init(IndexSchema schema, Map<String, String> args) {
+    this.bits = 
ofNullable(args.remove(BITS_PARAM)).map(Integer::parseInt).orElse(DEFAULT_BITS);
+
+    this.compress =
+        
ofNullable(args.remove(COMPRESS_PARAM)).map(Boolean::parseBoolean).orElse(false);
+
+    this.confidenceInterval =
+        ofNullable(args.remove(CONFIDENCE_INTERVAL_PARAM))
+            .map(Float::parseFloat)
+            .orElse(DEFAULT_CONFIDENCE_INTERVAL);
+
+    if (ofNullable(args.remove(DYNAMIC_CONFIDENCE_INTERVAL_PARAM))
+        .map(Boolean::parseBoolean)
+        .orElse(false)) {
+      this.confidenceInterval = 
Lucene99ScalarQuantizedVectorsFormat.DYNAMIC_CONFIDENCE_INTERVAL;
+    }
+
+    super.init(schema, args);
+  }
+
+  @Override
+  public KnnVectorsFormat buildKnnVectorsFormat() {
+    return new Lucene99HnswScalarQuantizedVectorsFormat(
+        getHnswMaxConn(),
+        getHnswBeamWidth(),
+        DEFAULT_NUM_MERGE_WORKER,
+        getBits(),
+        useCompression(),
+        getConfidenceInterval(),
+        null);
+  }
+
+  @Override
+  public void checkSchemaField(final SchemaField field) throws SolrException {
+    super.checkSchemaField(field);
+
+    if (confidenceInterval != null
+        && confidenceInterval != 
Lucene99ScalarQuantizedVectorsFormat.DYNAMIC_CONFIDENCE_INTERVAL
+        && (confidenceInterval < MINIMUM_CONFIDENCE_INTERVAL
+            || confidenceInterval > MAXIMUM_CONFIDENCE_INTERVAL)) {
+      throw new SolrException(
+          SolrException.ErrorCode.SERVER_ERROR,
+          getClass().getSimpleName()
+              + " fields must have non-dynamic confidence interval between 0.9 
and 1.0 "
+              + field.getName());

Review Comment:
   as commented above, I'm wondering if we can use the Lucene exceptions here



##########
solr/core/src/java/org/apache/solr/schema/ScalarQuantizedDenseVectorField.java:
##########
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.schema;
+
+import static java.util.Optional.ofNullable;
+import static 
org.apache.lucene.codecs.lucene99.Lucene99HnswVectorsFormat.DEFAULT_NUM_MERGE_WORKER;
+
+import java.util.Map;
+import org.apache.lucene.codecs.KnnVectorsFormat;
+import 
org.apache.lucene.codecs.lucene99.Lucene99HnswScalarQuantizedVectorsFormat;
+import org.apache.lucene.codecs.lucene99.Lucene99ScalarQuantizedVectorsFormat;
+import org.apache.lucene.index.VectorEncoding;
+import org.apache.lucene.index.VectorSimilarityFunction;
+import org.apache.solr.common.SolrException;
+
+public class ScalarQuantizedDenseVectorField extends DenseVectorField {
+  public static final String BITS_PARAM = "bits"; //
+  public static final String CONFIDENCE_INTERVAL_PARAM = "confidenceInterval";
+  public static final String DYNAMIC_CONFIDENCE_INTERVAL_PARAM = 
"dynamicConfidenceInterval";
+  public static final String COMPRESS_PARAM =
+      "compress"; // can only be enabled when bits = 4 per Lucene codec spec
+
+  static final int DEFAULT_BITS = 7; // use signed byte as default when 
unspecified
+  static final Float DEFAULT_CONFIDENCE_INTERVAL = null; // use dimension 
scaled confidence interval
+
+  // lucene does not expose these so we are duplicating them here
+  static final Float MINIMUM_CONFIDENCE_INTERVAL = 0.9f;
+  static final Float MAXIMUM_CONFIDENCE_INTERVAL = 1f;
+
+  /**
+   * Number of bits to use for storage Must be 4 (half-byte) or 7 
(signed-byte) per Lucene codec
+   * spec
+   */
+  private int bits;
+
+  /**
+   * Confidence interval to use for scalar quantization Default is calculated 
as
+   * `1-1/(vector_dimensions + 1)`
+   */
+  private Float confidenceInterval;
+
+  /**
+   * When enabled, in conjunction with 4 bit size, will pair values into 
single bytes for 50%
+   * reduction in memory usage (comes at the cost of some decode speed penalty)
+   */
+  private boolean compress;
+
+  public ScalarQuantizedDenseVectorField() {
+    super();
+  }
+
+  public ScalarQuantizedDenseVectorField(
+      int dimension,
+      VectorSimilarityFunction similarityFunction,
+      VectorEncoding vectorEncoding,
+      int bits,
+      Float confidenceInterval,
+      boolean compress) {
+    super(dimension, similarityFunction, vectorEncoding);
+    this.bits = bits;
+    this.confidenceInterval = confidenceInterval;
+    this.compress = compress;
+  }
+
+  @Override
+  public void init(IndexSchema schema, Map<String, String> args) {
+    this.bits = 
ofNullable(args.remove(BITS_PARAM)).map(Integer::parseInt).orElse(DEFAULT_BITS);
+
+    this.compress =
+        
ofNullable(args.remove(COMPRESS_PARAM)).map(Boolean::parseBoolean).orElse(false);
+
+    this.confidenceInterval =
+        ofNullable(args.remove(CONFIDENCE_INTERVAL_PARAM))
+            .map(Float::parseFloat)
+            .orElse(DEFAULT_CONFIDENCE_INTERVAL);
+
+    if (ofNullable(args.remove(DYNAMIC_CONFIDENCE_INTERVAL_PARAM))
+        .map(Boolean::parseBoolean)
+        .orElse(false)) {
+      this.confidenceInterval = 
Lucene99ScalarQuantizedVectorsFormat.DYNAMIC_CONFIDENCE_INTERVAL;
+    }
+
+    super.init(schema, args);
+  }
+
+  @Override
+  public KnnVectorsFormat buildKnnVectorsFormat() {
+    return new Lucene99HnswScalarQuantizedVectorsFormat(
+        getHnswMaxConn(),
+        getHnswBeamWidth(),
+        DEFAULT_NUM_MERGE_WORKER,
+        getBits(),
+        useCompression(),
+        getConfidenceInterval(),
+        null);
+  }
+
+  @Override
+  public void checkSchemaField(final SchemaField field) throws SolrException {
+    super.checkSchemaField(field);
+
+    if (confidenceInterval != null
+        && confidenceInterval != 
Lucene99ScalarQuantizedVectorsFormat.DYNAMIC_CONFIDENCE_INTERVAL
+        && (confidenceInterval < MINIMUM_CONFIDENCE_INTERVAL
+            || confidenceInterval > MAXIMUM_CONFIDENCE_INTERVAL)) {
+      throw new SolrException(
+          SolrException.ErrorCode.SERVER_ERROR,
+          getClass().getSimpleName()
+              + " fields must have non-dynamic confidence interval between 0.9 
and 1.0 "
+              + field.getName());
+    }
+    if (getBits() != 4 && getBits() != 7) {
+      throw new SolrException(
+          SolrException.ErrorCode.SERVER_ERROR,
+          getClass().getSimpleName()
+              + " fields must have bit size of 4 (half-byte) or 7 
(signed-byte) "
+              + field.getName());
+    }
+
+    if (useCompression() && getBits() != 4) {

Review Comment:
   Can't we rely on "if (bits > 4 && compress) {
         // compress=true otherwise silently does nothing when bits=7?
         throw new IllegalArgumentException("compress=true only applies when 
bits=4");
       }" Lucene side?
       
       



##########
solr/core/src/test-files/solr/collection1/conf/schema-densevector-quantized.xml:
##########
@@ -0,0 +1,86 @@
+<?xml version="1.0" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Test schema file for DenseVectorField types -->
+
+<schema name="schema-densevector-quantized" version="1.0">
+  <fieldType name="string" class="solr.StrField" multiValued="true"/>  
+  <fieldType name="knn_vector" class="solr.DenseVectorField" 
vectorDimension="4" similarityFunction="cosine" />
+  <fieldType name="knn_vector_byte_encoding" class="solr.DenseVectorField" 
vectorDimension="4" similarityFunction="cosine" vectorEncoding="BYTE"/>
+  <fieldType name="high_dimensional_float_knn_vector" 
class="solr.DenseVectorField" vectorDimension="2048" 
similarityFunction="cosine" vectorEncoding="FLOAT32"/>
+  <fieldType name="high_dimensional_byte_knn_vector" 
class="solr.DenseVectorField" vectorDimension="2048" 
similarityFunction="cosine" vectorEncoding="BYTE"/>
+  <fieldType name="plong" class="solr.LongPointField" 
useDocValuesAsStored="false"/>
+
+  <!-- Scalar Quantized vectors -->
+  <fieldType name="knn_vector_scalar_quantized_default" 
class="solr.ScalarQuantizedDenseVectorField"
+             vectorDimension="4"
+             similarityFunction="cosine"
+  />
+  <fieldType name="knn_vector_scalar_quantized_half_byte" 
class="solr.ScalarQuantizedDenseVectorField"
+             vectorDimension="4"
+             similarityFunction="cosine"
+             bits="4"/>
+  <fieldType name="knn_vector_scalar_quantized_compressed" 
class="solr.ScalarQuantizedDenseVectorField"
+             vectorDimension="4"
+             similarityFunction="cosine"
+             bits="4"
+            compress="true"/>
+  <fieldType name="knn_vector_scalar_quantized_confidence_interval" 
class="solr.ScalarQuantizedDenseVectorField"
+             vectorDimension="4"
+             similarityFunction="cosine"
+             confidenceInterval="0.91"/>
+  <fieldType name="knn_vector_scalar_quantized_confidence_dynamic" 
class="solr.ScalarQuantizedDenseVectorField"
+             vectorDimension="4"
+             similarityFunction="cosine"
+             dynamicConfidenceInterval="true"/>
+
+
+  <field name="id" type="string" indexed="true" stored="true" 
multiValued="false" required="false"/>
+  <field name="vector" type="knn_vector" indexed="true" stored="true"/>
+  <field name="vector2" type="knn_vector" indexed="true" stored="true"/>
+  <field name="vector_byte_encoding" type="knn_vector_byte_encoding" 
indexed="true" stored="true" />
+  <field name="2048_byte_vector" type="high_dimensional_byte_knn_vector" 
indexed="true" stored="true" />
+  <field name="2048_float_vector" type="high_dimensional_float_knn_vector" 
indexed="true" stored="true" />
+
+  <field name="v_scalar_default" type="knn_vector_scalar_quantized_default" 
indexed="true" stored="true" />
+  <field name="v_scalar_half_byte" 
type="knn_vector_scalar_quantized_half_byte" indexed="true" stored="true" />
+  <field name="v_scalar_compressed" 
type="knn_vector_scalar_quantized_compressed" indexed="true" stored="true" />
+  <field name="v_scalar_confidence" 
type="knn_vector_scalar_quantized_confidence_interval" indexed="true" 
stored="true" />
+  <field name="v_scalar_dynamic" 
type="knn_vector_scalar_quantized_confidence_dynamic" indexed="true" 
stored="true" />
+
+  <field name="string_field" type="string" indexed="true" stored="true" 
multiValued="false" required="false"/>
+
+  <field name="_version_" type="plong" indexed="true" stored="true" 
multiValued="false" />
+  <field name="_text_" type="text_general" indexed="true" stored="false" 
multiValued="true"/>
+  <copyField source="*" dest="_text_"/>
+  <fieldType name="text_general" class="solr.TextField" 
positionIncrementGap="100" multiValued="true">
+    <analyzer type="index">
+      <tokenizer class="solr.StandardTokenizerFactory"/>

Review Comment:
   can't we use one single schema file, where you add the different fields and 
field types with the ad hoc incorrect parameters?



##########
solr/solr-ref-guide/modules/query-guide/pages/dense-vector-search.adoc:
##########
@@ -240,6 +240,66 @@ client.add(Arrays.asList(d1, d2));
 ====
 ======
 
+=== ScalarQuantizedDenseVectorField
+Because dense vectors can have a costly storage footprint, it may be 
worthwhile to use a technique called "quantization"
+to reduce the stored representation size at the cost of some precision.
+
+This dense vector type uses a conversion that projects a 32 bit float 
precision feature down to an 8 bit int (or smaller)
+by linearly mapping the float range down to evenly sized "buckets" of values 
that fit into an int. A more detailed explanation
+can be found in this 
https://www.elastic.co/search-labs/blog/scalar-quantization-101[blog post].
+
+As a specific type of DenseVectorField, this field type supports all the same 
configurable properties outlined above as well
+as some additional ones.
+
+Here is how a ScalarQuantizedDenseVectorField can be defined in the schema:
+
+[source,xml]
+<fieldType name="scalar_quantized_vector" 
class="solr.ScalarQuantizedDenseVectorField" vectorDimension="4" 
similarityFunction="cosine"/>
+<field name="vector" type="scalar_quantized_vector" indexed="true" 
stored="true"/>
+
+`bits`::
++
+[%autowidth,frame=none]
+|===
+s|Optional |Default: `7`
+|===
++
+The number of bits to use for each quantized dimension value
++
+Accepted values: 4 (half byte) or 7 (unsigned byte).
+
+`confidenceInterval`::
++
+[%autowidth,frame=none]
+|===
+s|Optional |Default: `dimension-scaled`
+|===
++
+Statistically, outlier values are rarely meaningfully relevant to searches, so 
to increase the size of each bucket for
+quantization (and therefore information gain) we can scale the quantization 
intervals to the middle n % of values and place the remaining
+outliers in the outermost intervals.
++
+For example: 0.9 means scale interval sizes to the middle 90% of values
++
+If this param is omitted a default is used; scaled to the number of dimensions 
according to `1-1/(vector_dimensions + 1)`
++
+If `0` is provided, confidence intervals will be dynamically adjusted (at 
segment merge time) optimized by sampling values
++
+Accepted values: `FLOAT32`  (within 0.9 and 1.0) or 0 for dynamically adjusted 
confidence interval

Review Comment:
   I'm not sure this is clear enough for someone not familiar with the topic.
   Can I suggest involving some additional readers here?
   @ilariapet , @aruggero , @liangkaiwen  can you involve some of your 
colleagues?
   We may need to rephrase a bit this chapter
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to