use std::ptr::NonNull;
use super::check_bounds;
use faer::{MatMut, MatRef, RowMut, RowRef};
use crate::{
array::TensorMut,
array::TensorRef,
memory::{Memorable, MemoryBuffer, alloc_zeroed_memory},
};
#[inline(always)]
fn coords_to_index(i: usize, j: usize) -> usize {
if i <= j {
j * (j + 1) / 2 + i
} else {
i * (i + 1) / 2 + j
}
}
#[inline(always)]
fn calculate_flat_index<const D: usize>(indices: &[usize; D], strides: &[usize; D]) -> usize {
let mut flat_idx = coords_to_index(indices[0], indices[1]) * strides[1];
for i in 2..D {
flat_idx += indices[i] * strides[i];
}
flat_idx
}
pub struct SymSqTensor<C: Memorable, const D: usize> {
data: MemoryBuffer<C>,
dims: [usize; D],
strides: [usize; D],
}
impl<C: Memorable, const D: usize> SymSqTensor<C, D> {
pub fn new(data: MemoryBuffer<C>, dims: [usize; D], strides: [usize; D]) -> Self {
assert!(
D >= 2,
"Symmetric square tensors must have 2 or more dimensions."
);
assert!(
dims[0] == dims[1],
"Symmetric square tensors must be square in their two major dimensions."
);
assert!(
strides[0] == strides[1] * dims[1],
"Symmetric square tensors must be continuous across their two major dimensions."
);
assert!(
dims.iter().all(|&d| d != 0),
"Cannot have a zero-length dimension."
);
assert!(
strides.iter().all(|&d| d != 0),
"Cannot have a zero-length stride."
);
let mut max_element = [0; D];
for (i, d) in dims.iter().enumerate() {
max_element[i] = d - 1;
}
let max_flat_index = calculate_flat_index(&max_element, &strides);
assert!(
data.len() >= max_flat_index,
"Data buffer is not large enough."
);
Self {
data,
dims,
strides,
}
}
pub fn zeros(dims: [usize; D]) -> Self {
let strides = super::calc_continuous_strides(&dims);
let data = alloc_zeroed_memory::<C>(strides[0] * dims[0]);
Self::new(data, dims, strides)
}
pub fn dims(&self) -> &[usize; D] {
&self.dims
}
pub fn strides(&self) -> &[usize; D] {
&self.strides
}
pub fn rank(&self) -> usize {
D
}
pub fn num_elements(&self) -> usize {
self.dims.iter().product()
}
pub fn as_ptr(&self) -> *const C {
self.data.as_ptr()
}
pub fn as_ptr_mut(&mut self) -> *mut C {
self.data.as_mut_ptr()
}
pub fn as_ref(&self) -> SymSqTensorRef<'_, C, D> {
unsafe { SymSqTensorRef::from_raw_parts(self.data.as_ptr(), self.dims, self.strides) }
}
pub fn as_mut(&mut self) -> SymSqTensorMut<'_, C, D> {
unsafe { SymSqTensorMut::from_raw_parts(self.data.as_mut_ptr(), self.dims, self.strides) }
}
pub fn get(&self, indices: &[usize; D]) -> &C {
check_bounds(indices, &self.dims);
unsafe { self.get_unchecked(indices) }
}
pub fn get_mut(&mut self, indices: &[usize; D]) -> &mut C {
check_bounds(indices, &self.dims);
unsafe { self.get_mut_unchecked(indices) }
}
#[inline(always)]
pub unsafe fn get_unchecked(&self, indices: &[usize; D]) -> &C {
unsafe { &*self.ptr_at(indices) }
}
#[inline(always)]
pub unsafe fn get_mut_unchecked(&mut self, indices: &[usize; D]) -> &mut C {
unsafe { &mut *self.ptr_at_mut(indices) }
}
#[inline(always)]
pub unsafe fn ptr_at(&self, indices: &[usize; D]) -> *const C {
unsafe {
let flat_idx = calculate_flat_index(indices, &self.strides);
self.as_ptr().add(flat_idx)
}
}
#[inline(always)]
pub unsafe fn ptr_at_mut(&mut self, indices: &[usize; D]) -> *mut C {
unsafe {
let flat_idx = calculate_flat_index(indices, &self.strides);
self.as_ptr_mut().add(flat_idx)
}
}
}
impl<C: Memorable, const D: usize> std::ops::Index<[usize; D]> for SymSqTensor<C, D> {
type Output = C;
fn index(&self, indices: [usize; D]) -> &Self::Output {
self.get(&indices)
}
}
impl<C: Memorable, const D: usize> std::ops::IndexMut<[usize; D]> for SymSqTensor<C, D> {
fn index_mut(&mut self, indices: [usize; D]) -> &mut Self::Output {
self.get_mut(&indices)
}
}
#[derive(Clone, Copy)]
pub struct SymSqTensorRef<'a, C: Memorable, const D: usize> {
data: NonNull<C>,
dims: [usize; D],
strides: [usize; D],
__marker: std::marker::PhantomData<&'a C>,
}
impl<'a, C: Memorable, const D: usize> SymSqTensorRef<'a, C, D> {
pub unsafe fn from_raw_parts(data: *const C, dims: [usize; D], strides: [usize; D]) -> Self {
unsafe {
let mut_ptr = data as *mut C;
Self {
data: NonNull::new_unchecked(mut_ptr),
dims,
strides,
__marker: std::marker::PhantomData,
}
}
}
pub fn dims(&self) -> &[usize; D] {
&self.dims
}
pub fn strides(&self) -> &[usize; D] {
&self.strides
}
pub fn rank(&self) -> usize {
D
}
pub fn num_elements(&self) -> usize {
self.dims.iter().product()
}
pub fn as_ptr(&self) -> *const C {
self.data.as_ptr()
}
pub fn get(&self, indices: &[usize; D]) -> &C {
check_bounds(indices, &self.dims);
unsafe { self.get_unchecked(indices) }
}
#[inline(always)]
pub unsafe fn get_unchecked(&self, indices: &[usize; D]) -> &C {
unsafe { &*self.ptr_at(indices) }
}
#[inline(always)]
pub unsafe fn ptr_at(&self, indices: &[usize; D]) -> *const C {
unsafe {
let flat_idx = calculate_flat_index(indices, &self.strides);
self.as_ptr().add(flat_idx)
}
}
}
impl<'a, C: Memorable, const D: usize> std::ops::Index<[usize; D]> for SymSqTensorRef<'a, C, D> {
type Output = C;
fn index(&self, indices: [usize; D]) -> &Self::Output {
self.get(&indices)
}
}
pub struct SymSqTensorMut<'a, C: Memorable, const D: usize> {
data: NonNull<C>,
dims: [usize; D],
strides: [usize; D],
__marker: std::marker::PhantomData<&'a mut C>,
}
impl<'a, C: Memorable, const D: usize> SymSqTensorMut<'a, C, D> {
pub unsafe fn from_raw_parts(data: *mut C, dims: [usize; D], strides: [usize; D]) -> Self {
unsafe {
Self {
data: NonNull::new_unchecked(data),
dims,
strides,
__marker: std::marker::PhantomData,
}
}
}
pub fn dims(&self) -> &[usize; D] {
&self.dims
}
pub fn strides(&self) -> &[usize; D] {
&self.strides
}
pub fn rank(&self) -> usize {
D
}
pub fn num_elements(&self) -> usize {
self.dims.iter().product()
}
pub fn as_ptr(&self) -> *const C {
self.data.as_ptr() as *const C
}
pub fn as_ptr_mut(&mut self) -> *mut C {
self.data.as_ptr()
}
pub fn get(&self, indices: &[usize; D]) -> &C {
check_bounds(indices, &self.dims);
unsafe { self.get_unchecked(indices) }
}
pub fn get_mut(&mut self, indices: &[usize; D]) -> &mut C {
check_bounds(indices, &self.dims);
unsafe { self.get_mut_unchecked(indices) }
}
#[inline(always)]
pub unsafe fn get_unchecked(&self, indices: &[usize; D]) -> &C {
unsafe { &*self.ptr_at(indices) }
}
#[inline(always)]
pub unsafe fn get_mut_unchecked(&mut self, indices: &[usize; D]) -> &mut C {
unsafe { &mut *self.ptr_at_mut(indices) }
}
#[inline(always)]
pub unsafe fn ptr_at(&self, indices: &[usize; D]) -> *const C {
unsafe {
let flat_idx = calculate_flat_index(indices, &self.strides);
self.as_ptr().add(flat_idx)
}
}
#[inline(always)]
pub unsafe fn ptr_at_mut(&mut self, indices: &[usize; D]) -> *mut C {
unsafe {
let flat_idx = calculate_flat_index(indices, &self.strides);
self.as_ptr_mut().add(flat_idx)
}
}
}
impl<'a, C: Memorable, const D: usize> std::ops::Index<[usize; D]> for SymSqTensorMut<'a, C, D> {
type Output = C;
fn index(&self, indices: [usize; D]) -> &Self::Output {
self.get(&indices)
}
}
impl<'a, C: Memorable, const D: usize> std::ops::IndexMut<[usize; D]> for SymSqTensorMut<'a, C, D> {
fn index_mut(&mut self, indices: [usize; D]) -> &mut Self::Output {
self.get_mut(&indices)
}
}
impl<C: Memorable> SymSqTensor<C, 5> {
pub fn subtensor_ref(&self, m1: usize, m2: usize) -> TensorRef<'_, C, 3> {
check_bounds(&[m1, m2, 0, 0, 0], &self.dims);
unsafe { self.subtensor_ref_unchecked(m1, m2) }
}
pub fn subtensor_mut(&mut self, m1: usize, m2: usize) -> TensorMut<'_, C, 3> {
check_bounds(&[m1, m2, 0, 0, 0], &self.dims);
unsafe { self.subtensor_mut_unchecked(m1, m2) }
}
pub unsafe fn subtensor_ref_unchecked(&self, m1: usize, m2: usize) -> TensorRef<'_, C, 3> {
unsafe {
TensorRef::from_raw_parts(
self.ptr_at(&[m1, m2, 0, 0, 0]),
[self.dims[2], self.dims[3], self.dims[4]],
[self.strides[2], self.strides[3], self.strides[4]],
)
}
}
pub unsafe fn subtensor_mut_unchecked(&mut self, m1: usize, m2: usize) -> TensorMut<'_, C, 3> {
unsafe {
TensorMut::from_raw_parts(
self.ptr_at_mut(&[m1, m2, 0, 0, 0]),
[self.dims[2], self.dims[3], self.dims[4]],
[self.strides[2], self.strides[3], self.strides[4]],
)
}
}
}
impl<C: Memorable> SymSqTensor<C, 4> {
pub fn subtensor_ref(&self, m1: usize, m2: usize) -> MatRef<'_, C> {
check_bounds(&[m1, m2, 0, 0], &self.dims);
unsafe { self.subtensor_ref_unchecked(m1, m2) }
}
pub fn subtensor_mut(&mut self, m1: usize, m2: usize) -> MatMut<'_, C> {
check_bounds(&[m1, m2, 0, 0], &self.dims);
unsafe { self.subtensor_mut_unchecked(m1, m2) }
}
pub unsafe fn subtensor_ref_unchecked(&self, m1: usize, m2: usize) -> MatRef<'_, C> {
unsafe {
MatRef::from_raw_parts(
self.ptr_at(&[m1, m2, 0, 0]),
self.dims[2],
self.dims[3],
self.strides[2] as isize,
self.strides[3] as isize,
)
}
}
pub unsafe fn subtensor_mut_unchecked(&mut self, m1: usize, m2: usize) -> MatMut<'_, C> {
unsafe {
MatMut::from_raw_parts_mut(
self.ptr_at_mut(&[m1, m2, 0, 0]),
self.dims[2],
self.dims[3],
self.strides[2] as isize,
self.strides[3] as isize,
)
}
}
}
impl<C: Memorable> SymSqTensor<C, 3> {
pub fn subtensor_ref(&self, m1: usize, m2: usize) -> RowRef<'_, C> {
check_bounds(&[m1, m2, 0], &self.dims);
unsafe { self.subtensor_ref_unchecked(m1, m2) }
}
pub fn subtensor_mut(&mut self, m1: usize, m2: usize) -> RowMut<'_, C> {
check_bounds(&[m1, m2, 0], &self.dims);
unsafe { self.subtensor_mut_unchecked(m1, m2) }
}
pub unsafe fn subtensor_ref_unchecked(&self, m1: usize, m2: usize) -> RowRef<'_, C> {
unsafe {
RowRef::from_raw_parts(
self.ptr_at(&[m1, m2, 0]),
self.dims[2],
self.strides[2] as isize,
)
}
}
pub unsafe fn subtensor_mut_unchecked(&mut self, m1: usize, m2: usize) -> RowMut<'_, C> {
unsafe {
RowMut::from_raw_parts_mut(
self.ptr_at_mut(&[m1, m2, 0]),
self.dims[2],
self.strides[2] as isize,
)
}
}
}
impl<C: Memorable> SymSqTensor<C, 2> {
pub fn subtensor_ref(&self, m1: usize, m2: usize) -> &C {
self.get(&[m1, m2])
}
pub fn subtensor_mut(&mut self, m1: usize, m2: usize) -> &mut C {
self.get_mut(&[m1, m2])
}
pub unsafe fn subtensor_ref_unchecked(&self, m1: usize, m2: usize) -> &C {
unsafe { self.get_unchecked(&[m1, m2]) }
}
pub unsafe fn subtensor_mut_unchecked(&mut self, m1: usize, m2: usize) -> &mut C {
unsafe { self.get_mut_unchecked(&[m1, m2]) }
}
}
impl<'a, C: Memorable> SymSqTensorRef<'a, C, 5> {
pub fn subtensor_ref(&self, m1: usize, m2: usize) -> TensorRef<'a, C, 3> {
check_bounds(&[m1, m2, 0, 0, 0], &self.dims);
unsafe { self.subtensor_ref_unchecked(m1, m2) }
}
pub unsafe fn subtensor_ref_unchecked(&self, m1: usize, m2: usize) -> TensorRef<'a, C, 3> {
unsafe {
TensorRef::from_raw_parts(
self.ptr_at(&[m1, m2, 0, 0, 0]),
[self.dims[2], self.dims[3], self.dims[4]],
[self.strides[2], self.strides[3], self.strides[4]],
)
}
}
}
impl<'a, C: Memorable> SymSqTensorRef<'a, C, 4> {
pub fn subtensor_ref(&self, m1: usize, m2: usize) -> MatRef<'a, C> {
check_bounds(&[m1, m2, 0, 0], &self.dims);
unsafe { self.subtensor_ref_unchecked(m1, m2) }
}
pub unsafe fn subtensor_ref_unchecked(&self, m1: usize, m2: usize) -> MatRef<'a, C> {
unsafe {
MatRef::from_raw_parts(
self.ptr_at(&[m1, m2, 0, 0]),
self.dims[2],
self.dims[3],
self.strides[2] as isize,
self.strides[3] as isize,
)
}
}
}
impl<'a, C: Memorable> SymSqTensorRef<'a, C, 3> {
pub fn subtensor_ref(&self, m1: usize, m2: usize) -> RowRef<'a, C> {
check_bounds(&[m1, m2, 0], &self.dims);
unsafe { self.subtensor_ref_unchecked(m1, m2) }
}
pub unsafe fn subtensor_ref_unchecked(&self, m1: usize, m2: usize) -> RowRef<'a, C> {
unsafe {
RowRef::from_raw_parts(
self.ptr_at(&[m1, m2, 0]),
self.dims[2],
self.strides[2] as isize,
)
}
}
}
impl<'a, C: Memorable> SymSqTensorRef<'a, C, 2> {
pub fn subtensor_ref(&self, m1: usize, m2: usize) -> &C {
self.get(&[m1, m2])
}
pub unsafe fn subtensor_ref_unchecked(&self, m1: usize, m2: usize) -> &C {
unsafe { self.get_unchecked(&[m1, m2]) }
}
}
impl<'a, C: Memorable> SymSqTensorMut<'a, C, 5> {
pub fn subtensor_ref(&self, m1: usize, m2: usize) -> TensorRef<'a, C, 3> {
check_bounds(&[m1, m2, 0, 0, 0], &self.dims);
unsafe { self.subtensor_ref_unchecked(m1, m2) }
}
pub fn subtensor_mut(&mut self, m1: usize, m2: usize) -> TensorMut<'a, C, 3> {
check_bounds(&[m1, m2, 0, 0, 0], &self.dims);
unsafe { self.subtensor_mut_unchecked(m1, m2) }
}
pub unsafe fn subtensor_ref_unchecked(&self, m1: usize, m2: usize) -> TensorRef<'a, C, 3> {
unsafe {
TensorRef::from_raw_parts(
self.ptr_at(&[m1, m2, 0, 0, 0]),
[self.dims[2], self.dims[3], self.dims[4]],
[self.strides[2], self.strides[3], self.strides[4]],
)
}
}
pub unsafe fn subtensor_mut_unchecked(&mut self, m1: usize, m2: usize) -> TensorMut<'a, C, 3> {
unsafe {
TensorMut::from_raw_parts(
self.ptr_at_mut(&[m1, m2, 0, 0, 0]),
[self.dims[2], self.dims[3], self.dims[4]],
[self.strides[2], self.strides[3], self.strides[4]],
)
}
}
}
impl<'a, C: Memorable> SymSqTensorMut<'a, C, 4> {
pub fn subtensor_ref(&self, m1: usize, m2: usize) -> MatRef<'a, C> {
check_bounds(&[m1, m2, 0, 0], &self.dims);
unsafe { self.subtensor_ref_unchecked(m1, m2) }
}
pub fn subtensor_mut(&mut self, m1: usize, m2: usize) -> MatMut<'a, C> {
check_bounds(&[m1, m2, 0, 0], &self.dims);
unsafe { self.subtensor_mut_unchecked(m1, m2) }
}
pub unsafe fn subtensor_ref_unchecked(&self, m1: usize, m2: usize) -> MatRef<'a, C> {
unsafe {
MatRef::from_raw_parts(
self.ptr_at(&[m1, m2, 0, 0]),
self.dims[2],
self.dims[3],
self.strides[2] as isize,
self.strides[3] as isize,
)
}
}
pub unsafe fn subtensor_mut_unchecked(&mut self, m1: usize, m2: usize) -> MatMut<'a, C> {
unsafe {
MatMut::from_raw_parts_mut(
self.ptr_at_mut(&[m1, m2, 0, 0]),
self.dims[2],
self.dims[3],
self.strides[2] as isize,
self.strides[3] as isize,
)
}
}
}
impl<'a, C: Memorable> SymSqTensorMut<'a, C, 3> {
pub fn subtensor_ref(&self, m1: usize, m2: usize) -> RowRef<'a, C> {
check_bounds(&[m1, m2, 0], &self.dims);
unsafe { self.subtensor_ref_unchecked(m1, m2) }
}
pub fn subtensor_mut(&mut self, m1: usize, m2: usize) -> RowMut<'a, C> {
check_bounds(&[m1, m2, 0], &self.dims);
unsafe { self.subtensor_mut_unchecked(m1, m2) }
}
pub unsafe fn subtensor_ref_unchecked(&self, m1: usize, m2: usize) -> RowRef<'a, C> {
unsafe {
RowRef::from_raw_parts(
self.ptr_at(&[m1, m2, 0]),
self.dims[2],
self.strides[2] as isize,
)
}
}
pub unsafe fn subtensor_mut_unchecked(&mut self, m1: usize, m2: usize) -> RowMut<'a, C> {
unsafe {
RowMut::from_raw_parts_mut(
self.ptr_at_mut(&[m1, m2, 0]),
self.dims[2],
self.strides[2] as isize,
)
}
}
}
impl<'a, C: Memorable> SymSqTensorMut<'a, C, 2> {
pub fn subtensor_ref(&self, m1: usize, m2: usize) -> &C {
self.get(&[m1, m2])
}
pub fn subtensor_mut(&mut self, m1: usize, m2: usize) -> &mut C {
self.get_mut(&[m1, m2])
}
pub unsafe fn subtensor_ref_unchecked(&self, m1: usize, m2: usize) -> &C {
unsafe { self.get_unchecked(&[m1, m2]) }
}
pub unsafe fn subtensor_mut_unchecked(&mut self, m1: usize, m2: usize) -> &mut C {
unsafe { self.get_mut_unchecked(&[m1, m2]) }
}
}