Repository: antgroup/vsag (language: python)

Issue:
Add a new Layer "DataCell" to manage data access 
I think high level index should ignore the specific implement of quantization and io type, So the quantization and io components can be managed by new layer DataCell which provide different data access interface to the index layer. just like the following show

|Layer| Description|Implement|
|-------|--------|---------|
|Index Layer | ANN algorithm & architecture.          |                       Graph (HNSW,NNDescent) + Reorder,  IVF(PQ,SQ,fastscan), HNSW_IVF, MultiIndex ......|
|DataCell Layer| data access, use quantization, io and others    | FlattenDC, GraphNeighborhoodDC, MixtureDC .......|
|Operator Layer| quantizations, IO and other basic components                                                 | PQ,SQ,MemoryIO,SSDIO,......|

this abstract layered approach may facilitate scalability and code reuse
currently we only have index layer, so the issue will provide Datacell Layer and Operator Layer in this feature.
the related pull requests:

- #25 
- #34 
- #37 
- #41 
- #35 
- #59 
- #60 
- #61 
- #62 
- #79 
- #80 
- #96 
- #111 
- #114 
- #125 
todo tasks:
- support mixture DataCell
- adapt Graph DataCell for HNSW
- implement HNSW + SQ4 + SQ8 Index 




Discussion / hints:

Fix patch:
diff --git a/src/quantization/computer.h b/src/quantization/computer.h
index 0b67308b..3f75d36b 100644
--- a/src/quantization/computer.h
+++ b/src/quantization/computer.h
@@ -35,7 +35,7 @@ template <typename T>
 class Computer : public ComputerInterface {
 public:
     ~Computer() {
-        delete[] buf_;
+        quantizer_->ReleaseComputer(*this);
     }
 
     explicit Computer(const T* quantizer) : quantizer_(quantizer){};
@@ -52,6 +52,6 @@ class Computer : public ComputerInterface {
 
 public:
     const T* quantizer_{nullptr};
-    uint8_t* buf_{nullptr};  // TODO(LHT): Manage Alloc and Free
+    uint8_t* buf_{nullptr};
 };
 }  // namespace vsag
diff --git a/src/quantization/fp32_quantizer.h b/src/quantization/fp32_quantizer.h
index 95435dfc..2a131c1d 100644
--- a/src/quantization/fp32_quantizer.h
+++ b/src/quantization/fp32_quantizer.h
@@ -14,13 +14,13 @@
 // limitations under the License.
 
 #pragma once
-
 #include <cstdint>
 #include <cstring>
 
 #include "index/index_common_param.h"
 #include "nlohmann/json.hpp"
 #include "quantizer.h"
+#include "simd/fp32_simd.h"
 #include "simd/simd.h"
 
 namespace vsag {
@@ -28,7 +28,7 @@ namespace vsag {
 template <MetricType metric = MetricType::METRIC_TYPE_L2SQR>
 class FP32Quantizer : public Quantizer<FP32Quantizer<metric>> {
 public:
-    explicit FP32Quantizer(int dim);
+    explicit FP32Quantizer(int dim, Allocator* allocator);
 
     FP32Quantizer(const nlohmann::json& quantization_obj, const IndexCommonParam& common_param);
 
@@ -65,17 +65,21 @@ class FP32Quantizer : public Quantizer<FP32Quantizer<metric>> {
     ComputeDistImpl(Computer<FP32Quantizer<metric>>& computer,
                     const uint8_t* codes,
                     float* dists) const;
+
+    inline void
+    ReleaseComputerImpl(Computer<FP32Quantizer<metric>>& computer) const;
 };
 
 template <MetricType metric>
 FP32Quantizer<metric>::FP32Quantizer(const nlohmann::json& quantization_obj,
                                      const IndexCommonParam& common_param)
-    : Quantizer<FP32Quantizer<metric>>(common_param.dim_) {
+    : Quantizer<FP32Quantizer<metric>>(common_param.dim_, common_param.allocator_) {
     this->code_size_ = common_param.dim_ * sizeof(float);
 }
 
 template <MetricType metric>
-FP32Quantizer<metric>::FP32Quantizer(int dim) : Quantizer<FP32Quantizer<metric>>(dim) {
+FP32Quantizer<metric>::FP32Quantizer(int dim, Allocator* allocator)
+    : Quantizer<FP32Quantizer<metric>>(dim, allocator) {
     this->code_size_ = dim * sizeof(float);
 }
 
@@ -122,11 +126,17 @@ template <MetricType metric>
 float
 FP32Quantizer<metric>::ComputeImpl(const uint8_t* codes1, const uint8_t* codes2) {
     if (metric == MetricType::METRIC_TYPE_IP) {
-        return InnerProduct(codes1, codes2, &this->dim_);
+        return 1 - FP32ComputeIP(reinterpret_cast<const float*>(codes1),
+                                 reinterpret_cast<const float*>(codes2),
+                                 this->dim_);
     } else if (metric == MetricType::METRIC_TYPE_L2SQR) {
-        return L2Sqr(codes1, codes2, &this->dim_);
+        return FP32ComputeL2Sqr(reinterpret_cast<const float*>(codes1),
+                                reinterpret_cast<const float*>(codes2),
+                                this->dim_);
     } else if (metric == MetricType::METRIC_TYPE_COSINE) {
-        return InnerProduct(codes1, codes2, &this->dim_);  // TODO
+        return 1 - FP32ComputeIP(reinterpret_cast<const float*>(codes1),
+                                 reinterpret_cast<const float*>(codes2),
+                                 this->dim_);  // TODO
     } else {
         return 0.0f;
     }
@@ -136,7 +146,13 @@ template <MetricType metric>
 void
 FP32Quantizer<metric>::ProcessQueryImpl(const DataType* query,
                                         Computer<FP32Quantizer<metric>>& computer) const {
-    computer.buf_ = new uint8_t[this->code_size_];
+    try {
+        computer.buf_ = reinterpret_cast<uint8_t*>(this->allocator_->Allocate(this->code_size_));
+    } catch (const std::bad_alloc& e) {
+        computer.buf_ = nullptr;
+        logger::error("bad alloc when init computer buf");
+        throw std::bad_alloc();
+    }
     memcpy(computer.buf_, query, this->code_size_);
 }
 
@@ -146,14 +162,26 @@ FP32Quantizer<metric>::ComputeDistImpl(Computer<FP32Quantizer<metric>>& computer
                                        const uint8_t* codes,
                                        float* dists) const {
     if (metric == MetricType::METRIC_TYPE_IP) {
-        *dists = InnerProduct(codes, computer.buf_, &this->dim_);
+        *dists = 1 - FP32ComputeIP(reinterpret_cast<const float*>(codes),
+                                   reinterpret_cast<const float*>(computer.buf_),
+                                   this->dim_);
     } else if (metric == MetricType::METRIC_TYPE_L2SQR) {
-        *dists = L2Sqr(codes, computer.buf_, &this->dim_);
+        *dists = FP32ComputeL2Sqr(reinterpret_cast<const float*>(codes),
+                                  reinterpret_cast<const float*>(computer.buf_),
+                                  this->dim_);
     } else if (metric == MetricType::METRIC_TYPE_COSINE) {
-        *dists = InnerProduct(codes, computer.buf_, &this->dim_);  // TODO
+        *dists = 1 - FP32ComputeIP(reinterpret_cast<const float*>(codes),
+                                   reinterpret_cast<const float*>(computer.buf_),
+                                   this->dim_);  // TODO
     } else {
         *dists = 0.0f;
     }
 }
 
+template <MetricType metric>
+void
+FP32Quantizer<metric>::ReleaseComputerImpl(Computer<FP32Quantizer<metric>>& computer) const {
+    this->allocator_->Deallocate(computer.buf_);
+}
+
 }  // namespace vsag
diff --git a/src/quantization/quantizer.h b/src/quantization/quantizer.h
index f615d46d..50b4534a 100644
--- a/src/quantization/quantizer.h
+++ b/src/quantization/quantizer.h
@@ -34,7 +34,8 @@ using DataType = float;
 template <typename T>
 class Quantizer {
 public:
-    explicit Quantizer<T>(int dim) : dim_(dim), code_size_(dim * sizeof(DataType)){};
+    explicit Quantizer<T>(int dim, Allocator* allocator)
+        : dim_(dim), code_size_(dim * sizeof(DataType)), allocator_(allocator){};
 
     ~Quantizer() = default;
 
@@ -166,6 +167,11 @@ class Quantizer {
         return dist;
     }
 
+    inline void
+    ReleaseComputer(Computer<T>& computer) const {
+        cast().ReleaseComputerImpl(computer);
+    }
+
     /**
      * @brief Get the size of the encoded code in bytes.
      *
@@ -204,6 +210,7 @@ class Quantizer {
     uint64_t code_size_{0};
     bool is_trained_{false};
     MetricType metric_{MetricType::METRIC_TYPE_L2SQR};
+    Allocator* const allocator_{nullptr};
 };
 
 }  // namespace vsag
diff --git a/src/quantization/sq4_quantizer.h b/src/quantization/sq4_quantizer.h
index 98f92354..dc4d0ddb 100644
--- a/src/quantization/sq4_quantizer.h
+++ b/src/quantization/sq4_quantizer.h
@@ -14,6 +14,7 @@
 // limitations under the License.
 
 #pragma once
+
 #include <cstring>
 #include <limits>
 #include <vector>
@@ -26,7 +27,7 @@ namespace vsag {
 template <MetricType metric = MetricType::METRIC_TYPE_L2SQR>
 class SQ4Quantizer : public Quantizer<SQ4Quantizer<metric>> {
 public:
-    explicit SQ4Quantizer(int dim);
+    explicit SQ4Quantizer(int dim, Allocator* allocator);
 
     bool
     TrainImpl(const DataType* data, uint64_t count);
@@ -52,13 +53,17 @@ class SQ4Quantizer : public Quantizer<SQ4Quantizer<metric>> {
     inline void
     ComputeDistImpl(Computer<SQ4Quantizer>& computer, const uint8_t* codes, float* dists) const;
 
+    inline void
+    ReleaseComputerImpl(Computer<SQ4Quantizer<metric>>& computer) const;
+
 private:
     std::vector<DataType> lower_bound_{};
     std::vector<DataType> diff_{};
 };
 
 template <MetricType metric>
-SQ4Quantizer<metric>::SQ4Quantizer(int dim) : Quantizer<SQ4Quantizer<metric>>(dim) {
+SQ4Quantizer<metric>::SQ4Quantizer(int dim, Allocator* allocator)
+    : Quantizer<SQ4Quantizer<metric>>(dim, allocator) {
     this->code_size_ = (dim + (1 << 6) - 1) >> 6 << 6;
     lower_bound_.resize(dim, std::numeric_limits<DataType>::max());
     diff_.resize(dim, std::numeric_limits<DataType>::lowest());
@@ -161,9 +166,9 @@ SQ4Quantizer<metric>::ComputeImpl(const uint8_t* codes1, const uint8_t* codes2)
     if constexpr (metric == MetricType::METRIC_TYPE_L2SQR) {
         return SQ4ComputeCodesL2Sqr(codes1, codes2, lower_bound_.data(), diff_.data(), this->dim_);
     } else if constexpr (metric == MetricType::METRIC_TYPE_IP) {
-        return SQ4ComputeCodesIP(codes1, codes2, lower_bound_.data(), diff_.data(), this->dim_);
+        return 1 - SQ4ComputeCodesIP(codes1, codes2, lower_bound_.data(), diff_.data(), this->dim_);
     } else if constexpr (metric == MetricType::METRIC_TYPE_COSINE) {
-        return SQ4ComputeCodesIP(codes1, codes2, lower_bound_.data(), diff_.data(), this->dim_);
+        return 1 - SQ4ComputeCodesIP(codes1, codes2, lower_bound_.data(), diff_.data(), this->dim_);
     } else {
         return 0;
     }
@@ -174,11 +179,12 @@ void
 SQ4Quantizer<metric>::ProcessQueryImpl(const DataType* query,
                                        Computer<SQ4Quantizer>& computer) const {
     try {
-        computer.buf_ = new uint8_t[this->code_size_];  // todo: replace with allocator
+        computer.buf_ = reinterpret_cast<uint8_t*>(this->allocator_->Allocate(this->code_size_));
         this->EncodeOneImpl(query, computer.buf_);
     } catch (const std::bad_alloc& e) {
         computer.buf_ = nullptr;
         logger::error("bad alloc when init computer buf");
+        throw std::bad_alloc();
     }
 }
 
@@ -191,15 +197,21 @@ SQ4Quantizer<metric>::ComputeDistImpl(Computer<SQ4Quantizer>& computer,
         dists[0] = SQ4ComputeCodesL2Sqr(
             computer.buf_, codes, lower_bound_.data(), diff_.data(), this->dim_);
     } else if constexpr (metric == MetricType::METRIC_TYPE_IP) {
-        dists[0] =
-            SQ4ComputeCodesIP(computer.buf_, codes, lower_bound_.data(), diff_.data(), this->dim_);
+        dists[0] = 1 - SQ4ComputeCodesIP(
+                           computer.buf_, codes, lower_bound_.data(), diff_.data(), this->dim_);
     } else if constexpr (metric == MetricType::METRIC_TYPE_COSINE) {
-        dists[0] =
-            SQ4ComputeCodesIP(computer.buf_, codes, lower_bound_.data(), diff_.data(), this->dim_);
+        dists[0] = 1 - SQ4ComputeCodesIP(
+                           computer.buf_, codes, lower_bound_.data(), diff_.data(), this->dim_);
     } else {
         logger::error("unsupported metric type");
         dists[0] = 0;
     }
 }
 
+template <MetricType metric>
+void
+SQ4Quantizer<metric>::ReleaseComputerImpl(Computer<SQ4Quantizer<metric>>& computer) const {
+    this->allocator_->Deallocate(computer.buf_);
+}
+
 }  // namespace vsag
diff --git a/src/quantization/sq4_uniform_quantizer.h b/src/quantization/sq4_uniform_quantizer.h
index 3fc0cf34..ab1577bf 100644
--- a/src/quantization/sq4_uniform_quantizer.h
+++ b/src/quantization/sq4_uniform_quantizer.h
@@ -14,8 +14,8 @@
 // limitations under the License.
 
 #pragma once
-#include <math.h>
 
+#include <cmath>
 #include <limits>
 #include <string>
 #include <unordered_map>
@@ -32,7 +32,7 @@ using sum_type = float;
 template <MetricType metric = MetricType::METRIC_TYPE_L2SQR>
 class SQ4UniformQuantizer : public Quantizer<SQ4UniformQuantizer<metric>> {
 public:
-    explicit SQ4UniformQuantizer(int dim);
+    explicit SQ4UniformQuantizer(int dim, Allocator* allocator);
 
     bool
     TrainImpl(const DataType* data, uint64_t count);
@@ -60,6 +60,9 @@ class SQ4UniformQuantizer : public Quantizer<SQ4UniformQuantizer<metric>> {
                     const uint8_t* codes,
                     float* dists) const;
 
+    inline void
+    ReleaseComputerImpl(Computer<SQ4UniformQuantizer<metric>>& computer) const;
+
 private:
     DataType lower_bound_{0};
     DataType diff_{0};
@@ -75,8 +78,8 @@ class SQ4UniformQuantizer : public Quantizer<SQ4UniformQuantizer<metric>> {
 };
 
 template <MetricType metric>
-SQ4UniformQuantizer<metric>::SQ4UniformQuantizer(int dim)
-    : Quantizer<SQ4UniformQuantizer<metric>>(dim) {
+SQ4UniformQuantizer<metric>::SQ4UniformQuantizer(int dim, Allocator* allocator)
+    : Quantizer<SQ4UniformQuantizer<metric>>(dim, allocator) {
     lower_bound_ = std::numeric_limits<DataType>::max();
     diff_ = std::numeric_limits<DataType>::lowest();
 
@@ -217,7 +220,7 @@ SQ4UniformQuantizer<metric>::DecodeBatchImpl(const uint8_t* codes, DataType* dat
 template <MetricType metric>
 inline float
 SQ4UniformQuantizer<metric>::ComputeImpl(const uint8_t* codes1, const uint8_t* codes2) const {
-    float result = 0;
+    float result = 0.0f;
     if constexpr (metric == MetricType::METRIC_TYPE_L2SQR) {
         result = SQ4UniformComputeCodesIP(codes1, codes2, this->dim_);
 
@@ -234,6 +237,8 @@ SQ4UniformQuantizer<metric>::ComputeImpl(const uint8_t* codes1, const uint8_t* c
         result = lower_bound_ * (sum1 + sum2) + (diff_ / 15.0) * (diff_ / 15.0) * result -
                  lower_bound_ * lower_bound_;
 
+        result = 1 - result;
+
     } else if constexpr (metric == MetricType::METRIC_TYPE_COSINE) {
         result = SQ4UniformComputeCodesIP(codes1, codes2, this->dim_);
 
@@ -242,6 +247,8 @@ SQ4UniformQuantizer<metric>::ComputeImpl(const uint8_t* codes1, const uint8_t* c
 
         result = lower_bound_ * (sum1 + sum2) + (diff_ / 15.0) * (diff_ / 15.0) * result -
                  lower_bound_ * lower_bound_;
+
+        result = 1 - result;
     } else {
         logger::error("unsupported metric type");
         result = 0;
@@ -254,11 +261,12 @@ void
 SQ4UniformQuantizer<metric>::ProcessQueryImpl(const DataType* query,
                                               Computer<SQ4UniformQuantizer>& computer) const {
     try {
-        computer.buf_ = new uint8_t[this->code_size_];  // todo: replace with allocator
+        computer.buf_ = reinterpret_cast<uint8_t*>(this->allocator_->Allocate(this->code_size_));
         this->EncodeOneImpl(query, computer.buf_);
     } catch (const std::bad_alloc& e) {
         computer.buf_ = nullptr;
         logger::error("bad alloc when init computer buf");
+        throw std::bad_alloc();
     }
 }
 
@@ -270,4 +278,11 @@ SQ4UniformQuantizer<metric>::ComputeDistImpl(Computer<SQ4UniformQuantizer>& comp
     dists[0] = this->ComputeImpl(computer.buf_, codes);
 }
 
+template <MetricType metric>
+void
+SQ4UniformQuantizer<metric>::ReleaseComputerImpl(
+    Computer<SQ4UniformQuantizer<metric>>& computer) const {
+    this->allocator_->Deallocate(computer.buf_);
+}
+
 }  // namespace vsag
diff --git a/src/quantization/sq8_quantizer.h b/src/quantization/sq8_quantizer.h
index 0905b6a0..36c59841 100644
--- a/src/quantization/sq8_quantizer.h
+++ b/src/quantization/sq8_quantizer.h
@@ -20,7 +20,6 @@
 #include <limits>
 #include <memory>
 #include <nlohmann/json.hpp>
-#include <vector>
 
 #include "index/index_common_param.h"
 #include "quantizer.h"
@@ -31,7 +30,7 @@ namespace vsag {
 template <MetricType metric = MetricType::METRIC_TYPE_L2SQR>
 class SQ8Quantizer : public Quantizer<SQ8Quantizer<metric>> {
 public:
-    explicit SQ8Quantizer(int dim);
+    explicit SQ8Quantizer(int dim, Allocator* allocator);
 
     SQ8Quantizer(const nlohmann::json& quantization_obj, const IndexCommonParam& common_param);
 
@@ -67,13 +66,17 @@ class SQ8Quantizer : public Quantizer<SQ8Quantizer<metric>> {
     inline void
     DeserializeImpl(StreamReader& reader);
 
+    inline void
+    ReleaseComputerImpl(Computer<SQ8Quantizer<metric>>& computer) const;
+
 public:
-    std::vector<DataType> diff_{};
-    std::vector<DataType> lower_bound_{};
+    Vector<DataType> diff_;
+    Vector<DataType> lower_bound_;
 };
 
 template <MetricType Metric>
-SQ8Quantizer<Metric>::SQ8Quantizer(int dim) : Quantizer<SQ8Quantizer<Metric>>(dim) {
+SQ8Quantizer<Metric>::SQ8Quantizer(int dim, Allocator* allocator)
+    : Quantizer<SQ8Quantizer<Metric>>(dim, allocator), diff_(allocator), lower_bound_(allocator) {
     // align 64 bytes (512 bits) to avoid illegal memory access in SIMD
     this->code_size_ = this->dim_;
     this->diff_.resize(dim, 0);
@@ -83,7 +86,9 @@ SQ8Quantizer<Metric>::SQ8Quantizer(int dim) : Quantizer<SQ8Quantizer<Metric>>(di
 template <MetricType metric>
 SQ8Quantizer<metric>::SQ8Quantizer(const nlohmann::json& quantization_obj,
                                    const IndexCommonParam& common_param)
-    : Quantizer<SQ8Quantizer<metric>>(common_param.dim_) {
+    : Quantizer<SQ8Quantizer<metric>>(common_param.dim_, common_param.allocator_),
+      diff_(common_param.allocator_),
+      lower_bound_(common_param.allocator_) {
     // align 64 bytes (512 bits) to avoid illegal memory access in SIMD
     this->code_size_ = this->dim_;
     this->diff_.resize(this->dim_, 0);
@@ -96,15 +101,16 @@ SQ8Quantizer<metric>::TrainImpl(const vsag::DataType* data, uint64_t count) {
     if (this->is_trained_) {
         return true;
     }
-    std::vector<DataType> upperBound(this->dim_, std::numeric_limits<DataType>::lowest());
+    Vector<DataType> upper_bound(
+        this->dim_, std::numeric_limits<DataType>::lowest(), this->allocator_);
     for (uint64_t i = 0; i < this->dim_; ++i) {
         for (uint64_t j = 0; j < count; ++j) {
-            upperBound[i] = std::max(upperBound[i], data[j * this->dim_ + i]);
+            upper_bound[i] = std::max(upper_bound[i], data[j * this->dim_ + i]);
             lower_bound_[i] = std::min(lower_bound_[i], data[j * this->dim_ + i]);
         }
     }
     for (uint64_t i = 0; i < this->dim_; ++i) {
-        this->diff_[i] = upperBound[i] - this->lower_bound_[i];
+        this->diff_[i] = upper_bound[i] - this->lower_bound_[i];
     }
     this->is_trained_ = true;
     return true;
@@ -164,11 +170,14 @@ SQ8Quantizer<metric>::ComputeImpl(const uint8_t* codes1, const uint8_t* codes2)
         return SQ8ComputeCodesL2Sqr(
             codes1, codes2, this->lower_bound_.data(), this->diff_.data(), this->dim_);
     } else if constexpr (metric == MetricType::METRIC_TYPE_IP) {
-        return SQ8ComputeCodesIP(
-            codes1, codes2, this->lower_bound_.data(), this->diff_.data(), this->dim_);
+        return 1 - SQ8ComputeCodesIP(
+                       codes1, codes2, this->lower_bound_.data(), this->diff_.data(), this->dim_);
     } else if constexpr (metric == MetricType::METRIC_TYPE_COSINE) {
-        return SQ8ComputeCodesIP(
-            codes1, codes2, this->lower_bound_.data(), this->diff_.data(), this->dim_);  // TODO
+        return 1 - SQ8ComputeCodesIP(codes1,
+                                     codes2,
+                                     this->lower_bound_.data(),
+                                     this->diff_.data(),
+                                     this->dim_);  // TODO
     } else {
         return 0.0f;
     }
@@ -180,7 +189,13 @@ SQ8Quantizer<metric>::ProcessQueryImpl(const DataType* query,
                                        Computer<SQ8Quantizer>& computer) const {
     // align 64 bytes (512 bits) to avoid illegal memory access in SIMD
     uint64_t aligned_size = (this->dim_ * sizeof(float) + (1 << 6) - 1) >> 6 << 6;
-    computer.buf_ = new uint8_t[aligned_size];
+    try {
+        computer.buf_ = reinterpret_cast<uint8_t*>(this->allocator_->Allocate(aligned_size));
+    } catch (const std::bad_alloc& e) {
+        computer.buf_ = nullptr;
+        logger::error("bad alloc when init computer buf");
+        throw std::bad_alloc();
+    }
     std::memcpy(computer.buf_, query, this->dim_ * sizeof(float));
 }
 
@@ -195,11 +210,13 @@ SQ8Quantizer<metric>::ComputeDistImpl(Computer<SQ8Quantizer>& computer,
         *dists = SQ8ComputeL2Sqr(
             query, codes, this->lower_bound_.data(), this->diff_.data(), this->dim_);
     } else if constexpr (metric == MetricType::METRIC_TYPE_IP) {
-        *dists =
-            SQ8ComputeIP(query, codes, this->lower_bound_.data(), this->diff_.data(), this->dim_);
+        *dists = 1 - SQ8ComputeIP(
+                         query, codes, this->lower_bound_.data(), this->diff_.data(), this->dim_);
     } else if constexpr (metric == MetricType::METRIC_TYPE_COSINE) {
-        *dists = SQ8ComputeIP(
-            query, codes, this->lower_bound_.data(), this->diff_.data(), this->dim_);  // TODO
+        *dists =
+            1 -
+            SQ8ComputeIP(
+                query, codes, this->lower_bound_.data(), this->diff_.data(), this->dim_);  // TODO
     } else {
         *dists = 0.0f;
     }
@@ -219,4 +236,10 @@ SQ8Quantizer<metric>::DeserializeImpl(StreamReader& reader) {
     StreamReader::ReadVector(reader, this->lower_bound_);
 }
 
+template <MetricType metric>
+void
+SQ8Quantizer<metric>::ReleaseComputerImpl(Computer<SQ8Quantizer<metric>>& computer) const {
+    this->allocator_->Deallocate(computer.buf_);
+}
+
 }  // namespace vsag


Test patch:
diff --git a/src/data_cell/flatten_datacell_test.cpp b/src/data_cell/flatten_datacell_test.cpp
index 894f0b2d..b12bdc6d 100644
--- a/src/data_cell/flatten_datacell_test.cpp
+++ b/src/data_cell/flatten_datacell_test.cpp
@@ -14,109 +14,96 @@
 // limitations under the License.
 
 #include "flatten_datacell.h"
-
+/*
 #include <algorithm>
-#include <random>
 
 #include "catch2/catch_template_test_macros.hpp"
 #include "default_allocator.h"
 #include "fixtures.h"
-#include "io/memory_io.h"
-#include "quantization/fp32_quantizer.h"
-#include "quantization/sq8_quantizer.h"
+#include "flatten_interface_test.h"
+#include "io/io_headers.h"
+#include "quantization/quantizer_headers.h"
 
 using namespace vsag;
 
+template <typename QuantTmpl, typename IOTmpl, MetricType metric>
 void
-TestFlattenDataCell(const std::shared_ptr<FlattenInterface>& data_cell,
-                    uint64_t dim,
-                    MetricType metric,
+TestFlattenDataCell(int dim,
+                    std::shared_ptr<Allocator> allocator,
+                    const nlohmann::json& quantizer_json,
+                    const nlohmann::json& io_json,
                     float error = 1e-5) {
-    int64_t base_size = 1000;
-    int64_t query_size = 10;
-    auto vectors = fixtures::generate_vectors(base_size, dim);
-    auto querys = fixtures::generate_vectors(query_size, dim);
-
-    data_cell->Train(vectors.data(), base_size);
-    data_cell->BatchInsertVector(vectors.data(), base_size);
-
-    auto func = [&](FlattenInterface* vs) {
-        std::vector<uint64_t> idx(base_size);
-        std::iota(idx.begin(), idx.end(), 0);
-        std::shuffle(idx.begin(), idx.end(), std::mt19937(std::random_device()()));
-        std::vector<float> dists(base_size);
-        for (auto i = 0; i < query_size; ++i) {
-            auto computer = vs->FactoryComputer(querys.data() + i * dim);
-            vs->Query(dists.data(), computer, idx.data(), base_size);
-            float gt;
-            for (auto j = 0; j < base_size; ++j) {
-                if (metric == vsag::MetricType::METRIC_TYPE_IP ||
-                    metric == vsag::MetricType::METRIC_TYPE_COSINE) {
-                    gt = InnerProduct(vectors.data() + idx[j] * dim, querys.data() + i * dim, &dim);
-                } else if (metric == vsag::MetricType::METRIC_TYPE_L2SQR) {
-                    gt = L2Sqr(vectors.data() + idx[j] * dim, querys.data() + i * dim, &dim);
-                }
-                REQUIRE(std::abs(gt - dists[j]) < error);
-            }
-        }
-    };
+    auto counts = {100, 1000};
+    IndexCommonParam common;
+    common.dim_ = dim;
+    common.allocator_ = allocator.get();
+    common.metric_ = metric;
+    for (auto count : counts) {
+        auto flatten =
+            std::make_shared<FlattenDataCell<QuantTmpl, IOTmpl>>(quantizer_json, io_json, common);
+        FlattenInterfaceTest test(flatten, metric);
+        test.BasicTest(dim, count, error);
+        auto other =
+            std::make_shared<FlattenDataCell<QuantTmpl, IOTmpl>>(quantizer_json, io_json, common);
+        test.TestSerializeAndDeserialize(dim, other, error);
+    }
+}
 
-    func(data_cell.get());
-    REQUIRE(data_cell->TotalCount() == base_size);
+template <typename IOTmpl>
+void
+TestFlattenDataCellFP32(int dim,
+                        std::shared_ptr<Allocator> allocator,
+                        const nlohmann::json& quantizer_json,
+                        const nlohmann::json& io_json,
+                        float error = 1e-5) {
+    constexpr MetricType metrics[3] = {
+        MetricType::METRIC_TYPE_L2SQR, MetricType::METRIC_TYPE_COSINE, MetricType::METRIC_TYPE_IP};
+    TestFlattenDataCell<FP32Quantizer<metrics[0]>, IOTmpl, metrics[0]>(
+        dim, allocator, quantizer_json, io_json, error);
+    TestFlattenDataCell<FP32Quantizer<metrics[1]>, IOTmpl, metrics[1]>(
+        dim, allocator, quantizer_json, io_json, error);
+    TestFlattenDataCell<FP32Quantizer<metrics[2]>, IOTmpl, metrics[2]>(
+        dim, allocator, quantizer_json, io_json, error);
 }
 
-TEST_CASE("fp32[ut][flatten_data_cell]") {
-    int dim = 32;
-    auto alloctor = new DefaultAllocator();
-    {
-        auto data_cell = std::make_shared<FlattenDataCell<FP32Quantizer<>, MemoryIO>>();
-        data_cell->SetQuantizer(std::make_shared<FP32Quantizer<>>(dim));
-        data_cell->SetIO(std::make_shared<MemoryIO>(alloctor));
-        TestFlattenDataCell(data_cell, dim, vsag::MetricType::METRIC_TYPE_L2SQR);
-    }
-    {
-        auto data_cell = std::make_shared<
-            FlattenDataCell<FP32Quantizer<vsag::MetricType::METRIC_TYPE_IP>, MemoryIO>>();
-        data_cell->SetQuantizer(
-            std::make_shared<FP32Quantizer<vsag::MetricType::METRIC_TYPE_IP>>(dim));
-        data_cell->SetIO(std::make_shared<MemoryIO>(alloctor));
-        TestFlattenDataCell(data_cell, dim, vsag::MetricType::METRIC_TYPE_IP);
+TEST_CASE("fp32 [ut][flatten_data_cell]") {
+    auto allocator = std::make_shared<DefaultAllocator>();
+    auto fp32_param = nlohmann::json::parse("{}");
+    auto io_param = nlohmann::json::parse("{}");
+    auto dims = {8, 64, 512};
+    float error = 1e-5;
+    for (auto dim : dims) {
+        TestFlattenDataCellFP32<MemoryIO>(dim, allocator, fp32_param, io_param, error);
+        TestFlattenDataCellFP32<MemoryBlockIO>(dim, allocator, fp32_param, io_param, error);
     }
-    {
-        auto data_cell = std::make_shared<
-            FlattenDataCell<FP32Quantizer<vsag::MetricType::METRIC_TYPE_COSINE>, MemoryIO>>();
-        data_cell->SetQuantizer(
-            std::make_shared<FP32Quantizer<vsag::MetricType::METRIC_TYPE_COSINE>>(dim));
-        data_cell->SetIO(std::make_shared<MemoryIO>(alloctor));
-        TestFlattenDataCell(data_cell, dim, vsag::MetricType::METRIC_TYPE_COSINE);
-    }
-    delete alloctor;
 }
 
-TEST_CASE("sq8[ut][flatten_data_cell]") {
-    int dim = 32;
-    auto alloctor = new DefaultAllocator();
-    {
-        auto data_cell = std::make_shared<FlattenDataCell<SQ8Quantizer<>, MemoryIO>>();
-        data_cell->SetQuantizer(std::make_shared<SQ8Quantizer<>>(dim));
-        data_cell->SetIO(std::make_shared<MemoryIO>(alloctor));
-        TestFlattenDataCell(data_cell, dim, vsag::MetricType::METRIC_TYPE_L2SQR, 0.01);
-    }
-    {
-        auto data_cell = std::make_shared<
-            FlattenDataCell<SQ8Quantizer<vsag::MetricType::METRIC_TYPE_IP>, MemoryIO>>();
-        data_cell->SetQuantizer(
-            std::make_shared<SQ8Quantizer<vsag::MetricType::METRIC_TYPE_IP>>(dim));
-        data_cell->SetIO(std::make_shared<MemoryIO>(alloctor));
-        TestFlattenDataCell(data_cell, dim, vsag::MetricType::METRIC_TYPE_IP, 0.01);
-    }
-    {
-        auto data_cell = std::make_shared<
-            FlattenDataCell<SQ8Quantizer<vsag::MetricType::METRIC_TYPE_COSINE>, MemoryIO>>();
-        data_cell->SetQuantizer(
-            std::make_shared<SQ8Quantizer<vsag::MetricType::METRIC_TYPE_COSINE>>(dim));
-        data_cell->SetIO(std::make_shared<MemoryIO>(alloctor));
-        TestFlattenDataCell(data_cell, dim, vsag::MetricType::METRIC_TYPE_COSINE, 0.01);
+template <typename IOTmpl>
+void
+TestFlattenDataCellSQ8(int dim,
+                       std::shared_ptr<Allocator> allocator,
+                       const nlohmann::json& quantizer_json,
+                       const nlohmann::json& io_json,
+                       float error = 1e-5) {
+    constexpr MetricType metrics[3] = {
+        MetricType::METRIC_TYPE_L2SQR, MetricType::METRIC_TYPE_COSINE, MetricType::METRIC_TYPE_IP};
+    TestFlattenDataCell<SQ8Quantizer<metrics[0]>, IOTmpl, metrics[0]>(
+        dim, allocator, quantizer_json, io_json, error);
+    TestFlattenDataCell<SQ8Quantizer<metrics[1]>, IOTmpl, metrics[1]>(
+        dim, allocator, quantizer_json, io_json, error);
+    TestFlattenDataCell<SQ8Quantizer<metrics[2]>, IOTmpl, metrics[2]>(
+        dim, allocator, quantizer_json, io_json, error);
+}
+
+TEST_CASE("sq8 [ut][flatten_data_cell]") {
+    auto allocator = std::make_shared<DefaultAllocator>();
+    auto sq8_param = nlohmann::json::parse("{}");
+    auto io_param = nlohmann::json::parse("{}");
+    auto dims = {32, 64, 512};
+    auto error = 2e-2f;
+    for (auto dim : dims) {
+        TestFlattenDataCellSQ8<MemoryIO>(dim, allocator, sq8_param, io_param, error);
+        TestFlattenDataCellSQ8<MemoryBlockIO>(dim, allocator, sq8_param, io_param, error);
     }
-    delete alloctor;
 }
+*/
\ No newline at end of file
diff --git a/src/quantization/fp32_quantizer_test.cpp b/src/quantization/fp32_quantizer_test.cpp
index 3eeb8ac3..e1384952 100644
--- a/src/quantization/fp32_quantizer_test.cpp
+++ b/src/quantization/fp32_quantizer_test.cpp
@@ -26,10 +26,11 @@ using namespace vsag;
 const auto dims = {64, 128};
 const auto counts = {10, 101};
 
-template <MetricType Metric>
+template <MetricType metric>
 void
 TestQuantizerEncodeDecodeMetricFP32(uint64_t dim, int count, float error = 1e-5) {
-    FP32Quantizer<Metric> quantizer(dim);
+    auto allocator = std::make_shared<DefaultAllocator>();
+    FP32Quantizer<metric> quantizer(dim, allocator.get());
     TestQuantizerEncodeDecode(quantizer, dim, count, error);
     TestQuantizerEncodeDecodeSame(quantizer, dim, count, 65536, error);
 }
@@ -47,13 +48,14 @@ TEST_CASE("encode&decode [ut][fp32_quantizer]") {
     }
 }
 
-template <MetricType Metric>
+template <MetricType metric>
 void
 TestComputeMetricFP32(uint64_t dim, int count, float error = 1e-5) {
-    FP32Quantizer<Metric> quantizer(dim);
-    TestComputeCodes<FP32Quantizer<Metric>, Metric>(quantizer, dim, count, error);
-    TestComputeCodesSame<FP32Quantizer<Metric>, Metric>(quantizer, dim, count, 65536);
-    TestComputer<FP32Quantizer<Metric>, Metric>(quantizer, dim, count, error);
+    auto allocator = std::make_shared<DefaultAllocator>();
+    FP32Quantizer<metric> quantizer(dim, allocator.get());
+    TestComputeCodes<FP32Quantizer<metric>, metric>(quantizer, dim, count, error);
+    TestComputeCodesSame<FP32Quantizer<metric>, metric>(quantizer, dim, count, 65536);
+    TestComputer<FP32Quantizer<metric>, metric>(quantizer, dim, count, error);
 }
 
 TEST_CASE("compute [ut][fp32_quantizer]") {
@@ -69,12 +71,13 @@ TEST_CASE("compute [ut][fp32_quantizer]") {
     }
 }
 
-template <MetricType Metric>
+template <MetricType metric>
 void
 TestSerializeAndDeserializeMetricFP32(uint64_t dim, int count, float error = 1e-5) {
-    FP32Quantizer<Metric> quantizer1(dim);
-    FP32Quantizer<Metric> quantizer2(0);
-    TestSerializeAndDeserialize<FP32Quantizer<Metric>, Metric>(
+    auto allocator = std::make_shared<DefaultAllocator>();
+    FP32Quantizer<metric> quantizer1(dim, allocator.get());
+    FP32Quantizer<metric> quantizer2(0, allocator.get());
+    TestSerializeAndDeserialize<FP32Quantizer<metric>, metric>(
         quantizer1, quantizer2, dim, count, error);
 }
 
diff --git a/src/quantization/quantizer_test.h b/src/quantization/quantizer_test.h
index 658114f2..e13978cb 100644
--- a/src/quantization/quantizer_test.h
+++ b/src/quantization/quantizer_test.h
@@ -111,7 +111,7 @@ TestComputeCodes(
         float value = quantizer.Compute(codes1.data(), codes2.data());
         if constexpr (metric == vsag::MetricType::METRIC_TYPE_IP ||
                       metric == vsag::MetricType::METRIC_TYPE_COSINE) {
-            gt = InnerProduct(vecs.data() + idx1 * dim, vecs.data() + idx2 * dim, &dim);
+            gt = 1 - InnerProduct(vecs.data() + idx1 * dim, vecs.data() + idx2 * dim, &dim);
         } else if constexpr (metric == vsag::MetricType::METRIC_TYPE_L2SQR) {
             gt = L2Sqr(vecs.data() + idx1 * dim, vecs.data() + idx2 * dim, &dim);
         }
@@ -145,7 +145,7 @@ TestComputeCodesSame(Quantizer<T>& quantizer,
         float value = quantizer.Compute(codes1.data(), codes2.data());
         if constexpr (metric == vsag::MetricType::METRIC_TYPE_IP ||
                       metric == vsag::MetricType::METRIC_TYPE_COSINE) {
-            gt = InnerProduct(data.data() + idx1 * dim, data.data() + idx2 * dim, &dim);
+            gt = 1 - InnerProduct(data.data() + idx1 * dim, data.data() + idx2 * dim, &dim);
         } else if constexpr (metric == vsag::MetricType::METRIC_TYPE_L2SQR) {
             gt = L2Sqr(data.data() + idx1 * dim, data.data() + idx2 * dim, &dim);
         }
@@ -177,7 +177,7 @@ TestComputer(
             REQUIRE(quant.ComputeDist(*computer, codes1) == value);
             if constexpr (metric == vsag::MetricType::METRIC_TYPE_IP ||
                           metric == vsag::MetricType::METRIC_TYPE_COSINE) {
-                gt = InnerProduct(vecs.data() + idx1 * dim, querys.data() + i * dim, &dim);
+                gt = 1 - InnerProduct(vecs.data() + idx1 * dim, querys.data() + i * dim, &dim);
             } else if constexpr (metric == vsag::MetricType::METRIC_TYPE_L2SQR) {
                 gt = L2Sqr(vecs.data() + idx1 * dim, querys.data() + i * dim, &dim);
             }
diff --git a/src/quantization/sq4_quantizer_test.cpp b/src/quantization/sq4_quantizer_test.cpp
index 2adddf6c..75d72942 100644
--- a/src/quantization/sq4_quantizer_test.cpp
+++ b/src/quantization/sq4_quantizer_test.cpp
@@ -25,13 +25,14 @@ using namespace vsag;
 const auto dims = fixtures::get_common_used_dims();
 const auto counts = {10, 101};
 
-template <MetricType Metric>
+template <MetricType metric>
 void
 TestQuantizerEncodeDecodeMetricSQ4(uint64_t dim,
                                    int count,
                                    float error = 1e-5,
                                    float error_same = 1e-2) {
-    SQ4Quantizer<Metric> quantizer(dim);
+    auto allocator = std::make_shared<DefaultAllocator>();
+    SQ4Quantizer<metric> quantizer(dim, allocator.get());
     TestQuantizerEncodeDecode(quantizer, dim, count, error);
     TestQuantizerEncodeDecodeSame(quantizer, dim, count, 15, error_same);
 }
@@ -50,20 +51,21 @@ TEST_CASE("Encode and Decode", "[ut][SQ4Quantizer]") {
     }
 }
 
-template <MetricType Metric>
+template <MetricType metric>
 void
 TestComputeMetricSQ4(uint64_t dim, int count, float error = 1e-5) {
-    SQ4Quantizer<Metric> quantizer(dim);
-    TestComputeCodes<SQ4Quantizer<Metric>, Metric>(quantizer, dim, count, error);
-    TestComputeCodesSame<SQ4Quantizer<Metric>, Metric>(quantizer, dim, count, error);
-    TestComputer<SQ4Quantizer<Metric>, Metric>(quantizer, dim, count, error);
+    auto allocator = std::make_shared<DefaultAllocator>();
+    SQ4Quantizer<metric> quantizer(dim, allocator.get());
+    TestComputeCodes<SQ4Quantizer<metric>, metric>(quantizer, dim, count, error);
+    TestComputer<SQ4Quantizer<metric>, metric>(quantizer, dim, count, error);
 }
 
 TEST_CASE("compute [ut][sq4_quantizer]") {
     constexpr MetricType metrics[3] = {
         MetricType::METRIC_TYPE_L2SQR, MetricType::METRIC_TYPE_COSINE, MetricType::METRIC_TYPE_IP};
-    float error = 0.6f;
+
     for (auto dim : dims) {
+        float error = 0.1f * dim;
         for (auto count : counts) {
             TestComputeMetricSQ4<metrics[0]>(dim, count, error);
             TestComputeMetricSQ4<metrics[1]>(dim, count, error);
diff --git a/src/quantization/sq4_uniform_quantizer_test.cpp b/src/quantization/sq4_uniform_quantizer_test.cpp
index c3742739..4787e97e 100644
--- a/src/quantization/sq4_uniform_quantizer_test.cpp
+++ b/src/quantization/sq4_uniform_quantizer_test.cpp
@@ -18,7 +18,7 @@
 #include <catch2/catch_test_macros.hpp>
 #include <vector>
 
-#include "../../tests/fixtures/fixtures.h"
+#include "fixtures.h"
 #include "quantizer_test.h"
 
 using namespace vsag;
@@ -26,13 +26,14 @@ using namespace vsag;
 const auto dims = fixtures::get_common_used_dims();
 const auto counts = {10, 101};
 
-template <MetricType Metric>
+template <MetricType metric>
 void
 TestQuantizerEncodeDecodeMetricSQ4Uniform(uint64_t dim,
                                           int count,
                                           float error = 1e-5,
                                           float error_same = 1e-2) {
-    SQ4UniformQuantizer<Metric> quantizer(dim);
+    auto allocator = std::make_shared<DefaultAllocator>();
+    SQ4UniformQuantizer<metric> quantizer(dim, allocator.get());
     TestQuantizerEncodeDecode(quantizer, dim, count, error);
     TestQuantizerEncodeDecodeSame(quantizer, dim, count, 15, error_same);
 }
@@ -51,11 +52,12 @@ TEST_CASE("SQ4 Uniform Encode and Decode", "[ut][SQ4UniformQuantizer]") {
     }
 }
 
-template <MetricType Metric>
+template <MetricType metric>
 void
 TestComputeMetricSQ4Uniform(uint64_t dim, int count, float error = 1e-5) {
-    SQ4UniformQuantizer<Metric> quantizer(dim);
-    TestComputeCodesSame<SQ4UniformQuantizer<Metric>, Metric>(quantizer, dim, count, error);
+    auto allocator = std::make_shared<DefaultAllocator>();
+    SQ4UniformQuantizer<metric> quantizer(dim, allocator.get());
+    TestComputeCodesSame<SQ4UniformQuantizer<metric>, metric>(quantizer, dim, count, error);
 }
 
 TEST_CASE("compute [ut][SQ4UniformQuantizer]") {
diff --git a/src/quantization/sq8_quantizer_test.cpp b/src/quantization/sq8_quantizer_test.cpp
index 2650765c..94dca6e7 100644
--- a/src/quantization/sq8_quantizer_test.cpp
+++ b/src/quantization/sq8_quantizer_test.cpp
@@ -25,13 +25,14 @@ using namespace vsag;
 
 const auto counts = {10, 101};
 
-template <MetricType Metric>
+template <MetricType metric>
 void
 TestQuantizerEncodeDecodeMetricSQ8(uint64_t dim,
                                    int count,
                                    float error = 1e-5,
                                    float error_same = 1e-2) {
-    SQ8Quantizer<Metric> quantizer(dim);
+    auto allocator = std::make_shared<DefaultAllocator>();
+    SQ8Quantizer<metric> quantizer(dim, allocator.get());
     TestQuantizerEncodeDecode(quantizer, dim, count, error);
     TestQuantizerEncodeDecodeSame(quantizer, dim, count, 255, error_same);
 }
@@ -51,12 +52,13 @@ TEST_CASE("encode&decode [SQ8Quantizer]") {
     }
 }
 
-template <MetricType Metric>
+template <MetricType metric>
 void
 TestComputeMetricSQ8(uint64_t dim, int count, float error = 1e-5) {
-    SQ8Quantizer<Metric> quantizer(dim);
-    TestComputeCodes<SQ8Quantizer<Metric>, Metric>(quantizer, dim, count, error);
-    TestComputer<SQ8Quantizer<Metric>, Metric>(quantizer, dim, count, error);
+    auto allocator = std::make_shared<DefaultAllocator>();
+    SQ8Quantizer<metric> quantizer(dim, allocator.get());
+    TestComputeCodes<SQ8Quantizer<metric>, metric>(quantizer, dim, count, error);
+    TestComputer<SQ8Quantizer<metric>, metric>(quantizer, dim, count, error);
 }
 
 TEST_CASE("compute [ut][sq8_quantizer]") {
@@ -73,12 +75,13 @@ TEST_CASE("compute [ut][sq8_quantizer]") {
     }
 }
 
-template <MetricType Metric>
+template <MetricType metric>
 void
 TestSerializeAndDeserializeMetricSQ8(uint64_t dim, int count, float error = 1e-5) {
-    SQ8Quantizer<Metric> quantizer1(dim);
-    SQ8Quantizer<Metric> quantizer2(0);
-    TestSerializeAndDeserialize<SQ8Quantizer<Metric>, Metric>(
+    auto allocator = std::make_shared<DefaultAllocator>();
+    SQ8Quantizer<metric> quantizer1(dim, allocator.get());
+    SQ8Quantizer<metric> quantizer2(0, allocator.get());
+    TestSerializeAndDeserialize<SQ8Quantizer<metric>, metric>(
         quantizer1, quantizer2, dim, count, error);
 }