use crate::CausalTensor;
use alloc::vec;
use alloc::vec::Vec;
use core::fmt;
use deep_causality_algebra::Semiring;
use deep_causality_algebra::iso::witness::Iso;
use deep_causality_linear::CsrMatrix;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CsrFromTensorError {
pub rank: usize,
}
impl fmt::Display for CsrFromTensorError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"CausalTensor -> CsrMatrix requires rank 2, got rank {}",
self.rank
)
}
}
impl core::error::Error for CsrFromTensorError {}
impl<F> TryFrom<CausalTensor<F>> for CsrMatrix<F>
where
F: Semiring + Copy + PartialEq,
{
type Error = CsrFromTensorError;
fn try_from(tensor: CausalTensor<F>) -> Result<Self, Self::Error> {
let shape = tensor.shape();
if shape.len() != 2 {
return Err(CsrFromTensorError { rank: shape.len() });
}
let rows = shape[0];
let cols = shape[1];
let data = tensor.data();
let mut triplets: Vec<(usize, usize, F)> = Vec::new();
for r in 0..rows {
for c in 0..cols {
let v = data[r * cols + c];
if v != F::zero() {
triplets.push((r, c, v));
}
}
}
Ok(CsrMatrix::from_triplets(rows, cols, &triplets)
.expect("triplets are within bounds by construction"))
}
}
impl<F> Iso<CsrMatrix<F>, CausalTensor<F>> for CsrMatrix<F>
where
F: Semiring + Copy + PartialEq,
{
fn to_target(s: CsrMatrix<F>) -> CausalTensor<F> {
let (rows, cols) = s.shape();
let row_ptr = s.row_indices();
let col_idx = s.col_indices();
let vals = s.values();
let mut dense = vec![F::zero(); rows * cols];
for r in 0..rows {
let start = row_ptr[r];
let end = row_ptr[r + 1];
for k in start..end {
let c = col_idx[k];
dense[r * cols + c] = vals[k];
}
}
CausalTensor::new(dense, vec![rows, cols])
.expect("rows * cols matches data length by construction")
}
fn to_source(t: CausalTensor<F>) -> CsrMatrix<F> {
CsrMatrix::try_from(t).expect(
"Iso::to_source requires a rank-2 CausalTensor; use TryFrom for graceful failure",
)
}
}
pub trait ToDenseTensor<F> {
fn to_dense(self) -> CausalTensor<F>;
}
impl<F> ToDenseTensor<F> for CsrMatrix<F>
where
F: Semiring + Copy + PartialEq,
{
fn to_dense(self) -> CausalTensor<F> {
<Self as Iso<CsrMatrix<F>, CausalTensor<F>>>::to_target(self)
}
}