Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 17 additions & 87 deletions diskann-providers/src/model/pq/fixed_chunk_pq_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ impl FixedChunkPQTable {
Ok(Self { table })
}

/// Wrap an already-validated basic PQ table.
pub fn from_basic_table(table: BasicTable) -> Self {
Self { table }
}

/// Get chunk number.
pub fn get_num_chunks(&self) -> usize {
self.table.nchunks()
Expand Down Expand Up @@ -675,9 +680,8 @@ pub fn compute_pq_distance_for_pq_coordinates(
mod fixed_chunk_pq_table_test {
use core::ops::Range;

use crate::storage::{StorageReadProvider, VirtualStorageProvider};
use crate::storage::{PQStorage, VirtualStorageProvider};
use approx::assert_relative_eq;
use diskann::error::ErrorContext;
use diskann_utils::test_data_root;
use diskann_vector::{
PureDistanceFunction,
Expand All @@ -686,9 +690,17 @@ mod fixed_chunk_pq_table_test {
use itertools::iproduct;

use super::*;
use crate::{model::NUM_PQ_CENTROIDS, utils::read_bin_from};
use crate::model::NUM_PQ_CENTROIDS;

const DIM: usize = 128;
const PQ_PIVOTS_PATH: &str = "/sift/siftsmall_learn_pq_pivots.bin";

fn load_test_pivots() -> FixedChunkPQTable {
let storage_provider = VirtualStorageProvider::new_overlay(test_data_root());
PQStorage::new(PQ_PIVOTS_PATH, "", None)
.load_pq_pivots_bin(PQ_PIVOTS_PATH, 1, &storage_provider)
.unwrap()
}

#[test]
fn constructor_errors() {
Expand Down Expand Up @@ -811,14 +823,8 @@ mod fixed_chunk_pq_table_test {

#[test]
fn load_pivot_test() {
let storage_provider = VirtualStorageProvider::new_overlay(test_data_root());
let pq_pivots_path: &str = "/sift/siftsmall_learn_pq_pivots.bin";
let (dim, pq_table, chunk_offsets) =
load_pq_pivots_bin(pq_pivots_path, &1, &storage_provider).unwrap();
let fixed_chunk_pq_table =
FixedChunkPQTable::new(dim, pq_table.into(), chunk_offsets.into()).unwrap();
let fixed_chunk_pq_table = load_test_pivots();

assert_eq!(dim, DIM);
assert_eq!(fixed_chunk_pq_table.table.dim(), DIM);
assert_eq!(fixed_chunk_pq_table.table.ncenters(), NUM_PQ_CENTROIDS);

Expand All @@ -838,14 +844,7 @@ mod fixed_chunk_pq_table_test {

#[test]
fn calculate_distances_tests() {
let storage_provider = VirtualStorageProvider::new_overlay(test_data_root());

let pq_pivots_path: &str = "/sift/siftsmall_learn_pq_pivots.bin";

let (dim, pq_table, chunk_offsets) =
load_pq_pivots_bin(pq_pivots_path, &1, &storage_provider).unwrap();
let fixed_chunk_pq_table =
FixedChunkPQTable::new(dim, pq_table.into(), chunk_offsets.into()).unwrap();
let fixed_chunk_pq_table = load_test_pivots();

let query_vec: Vec<f32> = vec![
32.39f32, 78.57f32, 50.32f32, 80.46f32, 6.47f32, 69.76f32, 94.2f32, 83.36f32, 5.8f32,
Expand Down Expand Up @@ -994,75 +993,6 @@ mod fixed_chunk_pq_table_test {
}
}

type LoadPQPivotResult = (usize, Vec<f32>, Vec<usize>);
fn load_pq_pivots_bin<StorageProvider: StorageReadProvider>(
pq_pivots_path: &str,
num_pq_chunks: &usize,
storage_provider: &StorageProvider,
) -> ANNResult<LoadPQPivotResult> {
let mut reader = storage_provider
.open_reader(pq_pivots_path)
.with_context(|| format!("ERROR: Opening PQ k-means pivot file {}", pq_pivots_path))?;

let offsets = read_bin_from::<u64>(&mut reader, 0)?;
if offsets.nrows() != 4 {
return Err(ANNError::message(format!(
"Error reading pq_pivots file {}. \
Offsets don't contain correct metadata, \
# offsets = {}, but expecting 4.",
pq_pivots_path,
offsets.nrows()
)));
}
let file_offset_data = offsets.map(|x| x.into_usize());

let mut pivots = read_bin_from::<f32>(&mut reader, file_offset_data[(0, 0)])?;

if pivots.nrows() != NUM_PQ_CENTROIDS {
return Err(ANNError::message(format!(
"Error reading pq_pivots file {}. file_num_centers = {}, but expecting {} centers.",
pq_pivots_path,
pivots.nrows(),
NUM_PQ_CENTROIDS
)));
}
let dim = pivots.ncols();

let centroids = read_bin_from::<f32>(&mut reader, file_offset_data[(1, 0)])?;
if centroids.nrows() != dim || centroids.ncols() != 1 {
return Err(ANNError::message(format!(
"Error reading pq_pivots file {}. file_dim = {}, \
file_cols = {} but expecting {} entries in 1 dimension.",
pq_pivots_path,
centroids.nrows(),
centroids.ncols(),
dim
)));
}

pivots.row_iter_mut().for_each(|row| {
std::iter::zip(row.iter_mut(), centroids.as_slice().iter()).for_each(|(p, c)| *p += *c);
});

let chunk_offsets_m = read_bin_from::<u32>(&mut reader, file_offset_data[(2, 0)])?;
if chunk_offsets_m.nrows() != num_pq_chunks + 1 || chunk_offsets_m.ncols() != 1 {
return Err(ANNError::message(format!(
"Error reading pq_pivots file at chunk offsets; \
file has nr={}, nc={} but expecting nr={} and nc=1.",
chunk_offsets_m.nrows(),
chunk_offsets_m.ncols(),
num_pq_chunks + 1
)));
}
let chunk_offsets = chunk_offsets_m.map(|x| x.into_usize());

Ok((
dim,
pivots.into_inner().into_vec(),
chunk_offsets.into_inner().into_vec(),
))
}

#[test]
fn test_populate_chunk_distances() {
let dim = 8;
Expand Down
60 changes: 32 additions & 28 deletions diskann-providers/src/model/pq/pq_construction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,29 +340,25 @@ where

let (num_points, dim) = Metadata::read(uncompressed_data_reader)?.into_dims();

let mut full_pivot_data: Vec<f32>;
let centroid: Vec<f32>;
let chunk_offsets: Vec<usize>;
let full_dim: usize;
let mut table;

if !pq_storage.pivot_data_exist(storage_provider) {
return Err(ANNError::message("ERROR: PQ k-means pivot file not found."));
} else {
(_, full_dim) = pq_storage.read_existing_pivot_metadata(storage_provider)?;
(full_pivot_data, centroid, chunk_offsets) = pq_storage.load_existing_pivot_data(
&num_pq_chunks,
&num_centers,
&full_dim,
let (loaded_table, centroid) = pq_storage.load_existing_pivot_table(
num_pq_chunks,
num_centers,
full_dim,
storage_provider,
)?;
}
table = loaded_table;

// Instead of subtracting the center from each data set component, we instead
// add it to each center.
let mut full_pivot_data_mat =
MutMatrixView::try_from(full_pivot_data.as_mut_slice(), num_centers, full_dim)
.bridge_err()?;
accum_row_inplace(full_pivot_data_mat.as_mut_view(), centroid.as_slice());
// Instead of subtracting the center from each data set component, we instead
// add it to each center.
accum_row_inplace(table.view_pivots_mut(), centroid.as_slice());
}

pq_storage.write_compressed_pivot_metadata::<Storage>(
num_points,
Expand All @@ -387,13 +383,8 @@ where
))?;

// The compression table.
let table = TransposedTable::from_parts(
full_pivot_data_mat.as_view(),
diskann_quantization::views::ChunkOffsetsView::new(&chunk_offsets)
.bridge_err()?
.to_owned(),
)
.map_err(ANNError::new)?;
let table = TransposedTable::from_parts(table.view_pivots(), table.view_offsets().to_owned())
.map_err(|err| ANNError::message(diskann_quantization::error::format(&err)))?;

let mut buffer = vec![0.0; full_dim * block_size];

Expand Down Expand Up @@ -503,6 +494,19 @@ pub fn generate_pq_data_from_pivots_from_membuf<T: Copy + Into<f32>>(
)
.map_err(ANNError::new)?;

generate_pq_data_from_pivots_table(vector_data, &table, pq_out)
}

fn generate_pq_data_from_pivots_table<T, U, V>(
vector_data: &[T],
table: &diskann_quantization::product::BasicTableBase<U, V>,
pq_out: &mut [u8],
) -> ANNResult<()>
where
T: Copy + Into<f32>,
U: diskann_utils::views::DenseData<Elem = f32>,
V: diskann_utils::views::DenseData<Elem = usize>,
{
let data = vector_data
.iter()
.map(|x| (*x).into())
Expand Down Expand Up @@ -546,17 +550,17 @@ pub fn generate_pq_data_from_pivots_from_membuf_batch<T: Copy + Sync + Into<f32>
return Err(ANNError::message("Error: Invalid PQ buffer input size."));
}

let table = BasicTableView::new(
MatrixView::try_from(pivot_data, parameters.num_centers(), dim).bridge_err()?,
ChunkOffsetsView::new(offsets).bridge_err()?,
)
.map_err(|err| ANNError::message(diskann_quantization::error::format(&err)))?;

pq_out
.par_chunks_mut(num_pq_chunks)
.zip(vector_data.par_chunks(dim))
.try_for_each_in_pool(pool, |(pq_slice, vector_slice)| {
generate_pq_data_from_pivots_from_membuf(
vector_slice,
pivot_data,
parameters.num_centers(),
offsets,
pq_slice,
)
generate_pq_data_from_pivots_table(vector_slice, &table, pq_slice)
})
}

Expand Down
Loading
Loading