use crate::limits::MattenLimits;
use crate::{MattenError, Tensor};
impl Tensor {
#[must_use]
pub fn norm(&self) -> f64 {
#[cfg(feature = "dynamic")]
if self.is_dynamic() {
panic!(
"matten unsupported error in norm: not supported on dynamic tensors; \
call try_numeric() first to convert"
);
}
self.data.iter().map(|x| x * x).sum::<f64>().sqrt()
}
#[must_use]
pub fn trace(&self) -> f64 {
self.try_trace().unwrap_or_else(|e| panic!("{e}"))
}
pub fn try_trace(&self) -> Result<f64, MattenError> {
#[cfg(feature = "dynamic")]
if self.is_dynamic() {
return Err(MattenError::Unsupported {
operation: "trace",
message: "trace is not supported on dynamic tensors; call try_numeric() first"
.to_string(),
});
}
if self.shape.len() != 2 {
return Err(MattenError::Shape {
operation: "trace",
message: format!(
"trace requires a rank-2 tensor, got rank {}",
self.shape.len()
),
});
}
let rows = self.shape[0];
let cols = self.shape[1];
let k = rows.min(cols);
let mut acc = 0.0;
for i in 0..k {
acc += self.data[i * cols + i];
}
Ok(acc)
}
#[must_use]
pub fn outer(&self, other: &Tensor) -> Tensor {
self.try_outer(other).unwrap_or_else(|e| panic!("{e}"))
}
pub fn try_outer(&self, other: &Tensor) -> Result<Tensor, MattenError> {
#[cfg(feature = "dynamic")]
if self.is_dynamic() || other.is_dynamic() {
return Err(MattenError::Unsupported {
operation: "outer",
message:
"outer is not supported on dynamic tensors; call try_numeric() on each operand first"
.to_string(),
});
}
if self.shape.len() != 1 || other.shape.len() != 1 {
return Err(MattenError::Shape {
operation: "outer",
message: format!(
"outer requires two rank-1 tensors, got ranks {} and {}",
self.shape.len(),
other.shape.len()
),
});
}
let m = self.shape[0];
let n = other.shape[0];
let out_shape = vec![m, n];
let total = MattenLimits::default().check_shape(&out_shape, "outer")?;
let mut data = Vec::with_capacity(total);
for &a in &self.data {
for &b in &other.data {
data.push(a * b);
}
}
Ok(Tensor {
data,
shape: out_shape,
#[cfg(feature = "dynamic")]
dynamic: None,
})
}
}