use std::fmt;
use ariadnetor_core::Scalar;
use ariadnetor_core::backend::{ComputeBackend, MemoryOrder};
use num_traits::Zero;
use rand::RngExt;
use crate::capability::Host;
use crate::{
BlockCoord, BlockSparseLayout, BlockSparseStorage, BlockSparseTensorData, DenseLayout,
DenseStorage, DenseTensorData, QNIndex, Sector, Storage, StorageFor, TensorData, TensorLayout,
};
mod dense_ops;
mod block_sparse_ops;
#[cfg(test)]
mod tests;
fn host_order() -> MemoryOrder {
Host::shared().preferred_order()
}
pub struct Tensor<St, L>
where
St: Storage + StorageFor<L>,
L: TensorLayout,
{
data: TensorData<St, L>,
}
pub type DenseTensor<T = f64> = Tensor<DenseStorage<T>, DenseLayout>;
pub type BlockSparseTensor<T, S> = Tensor<BlockSparseStorage<T>, BlockSparseLayout<S>>;
impl<St, L> Clone for Tensor<St, L>
where
St: Storage + StorageFor<L> + Clone,
L: TensorLayout + Clone,
{
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
}
}
}
impl<St, L> fmt::Debug for Tensor<St, L>
where
St: Storage + StorageFor<L>,
L: TensorLayout,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Tensor")
.field("shape", &self.data.layout().shape())
.finish()
}
}
impl<St, L> Tensor<St, L>
where
St: Storage + StorageFor<L>,
L: TensorLayout,
{
pub fn from_data(data: TensorData<St, L>) -> Self {
Self { data }
}
pub fn data(&self) -> &TensorData<St, L> {
&self.data
}
pub fn data_mut(&mut self) -> &mut TensorData<St, L> {
&mut self.data
}
pub fn shape(&self) -> &[usize] {
self.data.layout().shape()
}
pub fn rank(&self) -> usize {
self.shape().len()
}
pub fn len(&self) -> usize {
self.shape().iter().product()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<S: Scalar> Tensor<DenseStorage<S>, DenseLayout> {
pub fn zeros(shape: Vec<usize>) -> Self {
Self::dense_filled(shape, S::zero())
}
pub fn ones(shape: Vec<usize>) -> Self {
Self::dense_filled(shape, S::one())
}
pub fn filled(shape: Vec<usize>, value: S) -> Self {
Self::dense_filled(shape, value)
}
pub fn eye(n: usize) -> Self {
let order = host_order();
let mut data = vec![S::zero(); n * n];
for i in 0..n {
data[i * n + i] = S::one();
}
let td = DenseTensorData::from_raw_parts(data, vec![n, n], order);
Self::from_data(td)
}
pub fn random<R: rand::Rng>(shape: Vec<usize>, rng: &mut R) -> Self
where
rand::distr::StandardUniform: rand::distr::Distribution<S>,
{
let order = host_order();
let total: usize = shape.iter().product();
let data: Vec<S> = (0..total).map(|_| rng.random()).collect();
let td = DenseTensorData::from_raw_parts(data, shape, order);
Self::from_data(td)
}
fn dense_filled(shape: Vec<usize>, value: S) -> Self {
let order = host_order();
let len: usize = shape.iter().product();
let data = DenseTensorData::from_raw_parts(vec![value; len], shape, order);
Self::from_data(data)
}
}
impl<T, S> Tensor<BlockSparseStorage<T>, BlockSparseLayout<S>>
where
T: Clone + Zero,
S: Sector,
{
pub fn zeros(indices: Vec<QNIndex<S>>, flux: S) -> Self {
let order = host_order();
let td = BlockSparseTensorData::zeros(indices, flux, order);
Self::from_data(td)
}
}
impl<T, S> Tensor<BlockSparseStorage<T>, BlockSparseLayout<S>>
where
T: Clone,
S: Sector,
rand::distr::StandardUniform: rand::distr::Distribution<T>,
{
pub fn random<R: rand::Rng>(indices: Vec<QNIndex<S>>, flux: S, rng: &mut R) -> Self {
let order = host_order();
let td = BlockSparseTensorData::random(indices, flux, order, rng);
Self::from_data(td)
}
}
impl<T, S> Tensor<BlockSparseStorage<T>, BlockSparseLayout<S>>
where
T: Clone + Zero,
S: Sector,
{
pub fn from_block_fn<F>(indices: Vec<QNIndex<S>>, flux: S, f: F) -> Self
where
F: FnMut(&BlockCoord, &[usize]) -> Vec<T>,
{
let order = host_order();
let td = BlockSparseTensorData::from_block_fn(indices, flux, order, f);
Self::from_data(td)
}
}