use core::{fmt::Debug, ops::Index};
use std::{
fs::File,
io::{BufWriter, Read, Write},
path::Path,
};
use distances::Number;
use rayon::prelude::*;
use crate::Dataset;
use super::Instance;
#[derive(Debug, Clone)]
pub struct VecDataset<I: Instance, U: Number, M: Instance> {
pub(crate) name: String,
pub(crate) data: Vec<I>,
pub(crate) metric: fn(&I, &I) -> U,
pub(crate) is_expensive: bool,
pub(crate) permuted_indices: Option<Vec<usize>>,
pub(crate) metadata: Vec<M>,
}
impl<I: Instance, U: Number> VecDataset<I, U, usize> {
pub fn new(name: String, data: Vec<I>, metric: fn(&I, &I) -> U, is_expensive: bool) -> Self {
let metadata = (0..data.len()).collect();
Self {
name,
data,
metric,
is_expensive,
permuted_indices: None,
metadata,
}
}
}
impl<I: Instance, U: Number, M: Instance> VecDataset<I, U, M> {
pub fn assign_metadata<Mn: Instance>(self, metadata: Vec<Mn>) -> Result<VecDataset<I, U, Mn>, String> {
if metadata.len() == self.data.len() {
let metadata = if let Some(permutation) = self.permuted_indices.as_ref() {
permutation.par_iter().map(|&index| metadata[index].clone()).collect()
} else {
metadata
};
Ok(VecDataset {
name: self.name,
data: self.data,
metric: self.metric,
is_expensive: self.is_expensive,
permuted_indices: self.permuted_indices,
metadata,
})
} else {
Err(format!(
"Invalid metadata. Expected metadata of length {}, got metadata of length {}",
self.cardinality(),
metadata.len()
))
}
}
#[must_use]
pub fn data(&self) -> &[I] {
&self.data
}
#[must_use]
pub fn data_owned(self) -> Vec<I> {
self.data
}
#[must_use]
pub fn metadata(&self) -> &[M] {
&self.metadata
}
#[must_use]
pub fn metadata_owned(self) -> Vec<M> {
self.metadata
}
#[must_use]
pub fn metadata_of(&self, index: usize) -> &M {
&self.metadata[index]
}
}
impl<I: Instance, U: Number, M: Instance> Index<usize> for VecDataset<I, U, M> {
type Output = I;
fn index(&self, index: usize) -> &Self::Output {
self.data.index(index)
}
}
impl<I: Instance, U: Number, M: Instance> Dataset<I, U> for VecDataset<I, U, M> {
fn type_name() -> String {
format!("VecDataset<{}, {}, {}>", I::type_name(), U::type_name(), M::type_name())
}
fn name(&self) -> &str {
&self.name
}
fn cardinality(&self) -> usize {
self.data.len()
}
fn is_metric_expensive(&self) -> bool {
self.is_expensive
}
fn metric(&self) -> fn(&I, &I) -> U {
self.metric
}
fn set_permuted_indices(&mut self, indices: Option<&[usize]>) {
self.permuted_indices = indices.map(<[usize]>::to_vec);
}
fn swap(&mut self, left: usize, right: usize) -> Result<(), String> {
self.data.swap(left, right);
self.metadata.swap(left, right);
Ok(())
}
fn permuted_indices(&self) -> Option<&[usize]> {
self.permuted_indices.as_deref()
}
fn permute_instances(&mut self, permutation: &[usize]) -> Result<(), String> {
if permutation.len() != self.data.len() {
return Err(format!(
"Invalid permutation. Expected permutation of length {}, got permutation of length {}",
self.cardinality(),
permutation.len()
));
}
self.data = permutation.par_iter().map(|&index| self.data[index].clone()).collect();
self.metadata = permutation
.par_iter()
.map(|&index| self.metadata[index].clone())
.collect();
self.set_permuted_indices(Some(permutation));
Ok(())
}
fn make_shards(mut self, max_cardinality: usize) -> Vec<Self> {
let mut shards = Vec::new();
let mut metadata = self.metadata.clone();
while self.data.len() > max_cardinality {
let name = format!("{}-shard-{}", self.name, shards.len());
let at = self.data.len() - max_cardinality;
let data = self.data.split_off(at);
shards.push(
VecDataset::new(name, data, self.metric, self.is_expensive)
.assign_metadata(metadata.split_off(at))
.unwrap_or_else(|_| unreachable!("We just split this dataset at the same indices.")),
);
}
self.name = format!("{}-shard-{}", self.name, shards.len());
shards.push(self);
shards
}
fn save(&self, path: &Path) -> Result<(), String> {
let mut handle = BufWriter::new(File::create(path).map_err(|e| e.to_string())?);
let type_name = Self::type_name();
handle
.write_all(&type_name.len().to_le_bytes())
.and_then(|()| handle.write_all(type_name.as_bytes()))
.map_err(|e| e.to_string())?;
let name = self.name.clone();
handle
.write_all(&name.len().to_le_bytes())
.and_then(|()| handle.write_all(name.as_bytes()))
.map_err(|e| e.to_string())?;
let cardinality_bytes = self.data.len().to_le_bytes();
handle.write_all(&cardinality_bytes).map_err(|e| e.to_string())?;
let permutation = self
.permuted_indices
.as_ref()
.map_or(Vec::new(), |p| p.iter().flat_map(|i| i.to_le_bytes()).collect());
let permutation_bytes = permutation.len().to_le_bytes();
handle
.write_all(&permutation_bytes)
.and_then(|()| handle.write_all(&permutation))
.map_err(|e| e.to_string())?;
for row in &self.data {
row.save(&mut handle)?;
}
handle.write_all(&cardinality_bytes).map_err(|e| e.to_string())?;
for meta in &self.metadata {
meta.save(&mut handle)?;
}
Ok(())
}
fn load(path: &Path, metric: fn(&I, &I) -> U, is_expensive: bool) -> Result<Self, String> {
let mut handle = File::open(path).map_err(|e| e.to_string())?;
{
let mut num_type_bytes = vec![0; usize::num_bytes()];
handle.read_exact(&mut num_type_bytes).map_err(|e| e.to_string())?;
let num_type_bytes = <usize as Number>::from_le_bytes(&num_type_bytes);
let mut type_buf = vec![0; num_type_bytes];
handle.read_exact(&mut type_buf).map_err(|e| e.to_string())?;
let type_name = String::from_utf8(type_buf).map_err(|e| e.to_string())?;
let actual_type_name = Self::type_name();
if type_name != actual_type_name {
return Err(format!(
"Invalid type. File has data of type {type_name} but dataset was constructed with type {actual_type_name}"
));
}
};
let name = {
let mut num_name_bytes = vec![0; usize::num_bytes()];
handle.read_exact(&mut num_name_bytes).map_err(|e| e.to_string())?;
let num_name_bytes = <usize as Number>::from_le_bytes(&num_name_bytes);
let mut name_buf = vec![0; num_name_bytes];
handle.read_exact(&mut name_buf).map_err(|e| e.to_string())?;
String::from_utf8(name_buf).map_err(|e| e.to_string())?
};
let cardinality = {
let mut cardinality_buf = vec![0; usize::num_bytes()];
handle.read_exact(&mut cardinality_buf).map_err(|e| e.to_string())?;
<usize as Number>::from_le_bytes(&cardinality_buf)
};
let permutation = {
let mut permutation_buf = vec![0; usize::num_bytes()];
handle.read_exact(&mut permutation_buf).map_err(|e| e.to_string())?;
if <usize as Number>::from_le_bytes(&permutation_buf) == 0 {
None
} else {
let mut permutation_buf = vec![0; 8 * cardinality];
handle.read_exact(&mut permutation_buf).map_err(|e| e.to_string())?;
let permutation = permutation_buf
.chunks(8)
.map(<usize as Number>::from_le_bytes)
.collect::<Vec<_>>();
Some(permutation)
}
};
let data = (0..cardinality)
.map(|_| I::load(&mut handle))
.collect::<Result<Vec<_>, _>>()?;
let num_metadata = {
let mut num_metadata_buf = vec![0; usize::num_bytes()];
handle.read_exact(&mut num_metadata_buf).map_err(|e| e.to_string())?;
<usize as Number>::from_le_bytes(&num_metadata_buf)
};
let metadata = (0..num_metadata)
.map(|_| M::load(&mut handle))
.collect::<Result<Vec<_>, _>>()?;
Ok(Self {
name,
data,
metric,
is_expensive,
permuted_indices: permutation,
metadata,
})
}
}