use half::f16;
use ndarray::{Array1, Array2};
use crate::error::{Error, Result};
use crate::types::{EncodeResult, Multivector, SparseVector};
pub fn dense_array(result: &EncodeResult) -> Result<Array1<f32>> {
Ok(Array1::from(result.require_dense()?.to_vec()))
}
pub fn multivector_f32(multivector: &Multivector) -> Result<Array2<f32>> {
rows_to_array(multivector.to_f32())
}
pub fn multivector_f16(multivector: &Multivector) -> Option<Result<Array2<f16>>> {
match multivector {
Multivector::F16(rows) => Some(rows_to_array(rows.clone())),
Multivector::F32(_) => None,
}
}
pub fn sparse_to_dense(sparse: &SparseVector, dims: usize) -> Result<Array1<f32>> {
let mut dense = Array1::zeros(dims);
for (index, value) in sparse.indices.iter().zip(&sparse.values) {
let index = *index as usize;
if index >= dims {
return Err(Error::decode(format!(
"sparse index {index} is outside a {dims}-dimensional vector"
)));
}
dense[index] = *value;
}
Ok(dense)
}
fn rows_to_array<T>(rows: Vec<Vec<T>>) -> Result<Array2<T>> {
let height = rows.len();
let width = rows.first().map_or(0, Vec::len);
if rows.iter().any(|row| row.len() != width) {
return Err(Error::decode("multivector rows have inconsistent widths"));
}
Array2::from_shape_vec((height, width), rows.into_iter().flatten().collect())
.map_err(|err| Error::decode(format!("could not shape the multivector: {err}")))
}
#[cfg(test)]
mod tests {
#![allow(clippy::float_cmp)]
use super::*;
#[test]
fn dense_becomes_a_one_dimensional_array() {
let result = EncodeResult {
dense: Some(vec![0.5, -0.25, 1.0]),
..EncodeResult::default()
};
let array = dense_array(&result).unwrap();
assert_eq!(array.len(), 3);
assert_eq!(array[1], -0.25);
assert!(dense_array(&EncodeResult::default()).is_err());
}
#[test]
fn multivectors_keep_their_shape() {
let multivector = Multivector::F32(vec![vec![1.0, 2.0, 3.0], vec![4.0, 5.0, 6.0]]);
let array = multivector_f32(&multivector).unwrap();
assert_eq!(array.shape(), &[2, 3]);
assert_eq!(array[[1, 2]], 6.0);
}
#[test]
fn f16_multivectors_are_offered_without_narrowing_f32_ones() {
let narrow = Multivector::F16(vec![vec![f16::from_f32(1.5), f16::from_f32(0.5)]]);
let array = multivector_f16(&narrow).unwrap().unwrap();
assert_eq!(array.shape(), &[1, 2]);
assert_eq!(array[[0, 0]], f16::from_f32(1.5));
assert!(multivector_f16(&Multivector::F32(vec![vec![1.0]])).is_none());
}
#[test]
fn ragged_multivectors_are_rejected_rather_than_reshaped() {
let ragged = Multivector::F32(vec![vec![1.0, 2.0], vec![3.0]]);
assert!(multivector_f32(&ragged).is_err());
}
#[test]
fn sparse_expands_into_a_dense_vector() {
let sparse = SparseVector {
indices: vec![0, 3],
values: vec![1.0, 0.5],
};
let dense = sparse_to_dense(&sparse, 4).unwrap();
assert_eq!(dense.to_vec(), vec![1.0, 0.0, 0.0, 0.5]);
assert!(sparse_to_dense(&sparse, 3).is_err());
}
}