use super::FixedPoint;
use super::FixedVector;
use super::FixedMatrix;
use super::linalg::compute_tier_dot_raw;
use super::decompose::{lu_decompose, qr_decompose, cholesky_decompose, svd_decompose};
use crate::fixed_point::universal::fasc::stack_evaluator::BinaryStorage;
use crate::fixed_point::core_types::errors::OverflowDetected;
pub fn frobenius_norm(a: &FixedMatrix) -> FixedPoint {
let data = a.data_slice();
let raw: Vec<BinaryStorage> = data.iter().map(|fp| fp.raw()).collect();
let sum_sq = FixedPoint::from_raw(compute_tier_dot_raw(&raw, &raw));
sum_sq.sqrt()
}
pub fn norm_1(a: &FixedMatrix) -> FixedPoint {
let ones: Vec<BinaryStorage> = vec![FixedPoint::one().raw(); a.rows()];
let mut max_sum = FixedPoint::ZERO;
for j in 0..a.cols() {
let abs_col: Vec<BinaryStorage> = (0..a.rows()).map(|i| a.get(i, j).abs().raw()).collect();
let col_sum = FixedPoint::from_raw(compute_tier_dot_raw(&abs_col, &ones));
if col_sum > max_sum {
max_sum = col_sum;
}
}
max_sum
}
pub fn norm_inf(a: &FixedMatrix) -> FixedPoint {
let ones: Vec<BinaryStorage> = vec![FixedPoint::one().raw(); a.cols()];
let mut max_sum = FixedPoint::ZERO;
for i in 0..a.rows() {
let abs_row: Vec<BinaryStorage> = (0..a.cols()).map(|j| a.get(i, j).abs().raw()).collect();
let row_sum = FixedPoint::from_raw(compute_tier_dot_raw(&abs_row, &ones));
if row_sum > max_sum {
max_sum = row_sum;
}
}
max_sum
}
pub fn least_squares(a: &FixedMatrix, b: &FixedVector) -> Result<FixedVector, OverflowDetected> {
let qr = qr_decompose(a)?;
qr.solve(b)
}
pub fn inverse_spd(a: &FixedMatrix) -> Result<FixedMatrix, OverflowDetected> {
let chol = cholesky_decompose(a)?;
let n = a.rows();
let mut inv = FixedMatrix::new(n, n);
for j in 0..n {
let mut e_j = FixedVector::new(n);
e_j[j] = FixedPoint::one();
let col = chol.solve(&e_j)?;
for i in 0..n {
inv.set(i, j, col[i]);
}
}
Ok(inv)
}
pub fn condition_number_1(a: &FixedMatrix) -> Result<FixedPoint, OverflowDetected> {
let lu = lu_decompose(a)?;
let inv = lu.inverse()?;
Ok(norm_1(a) * norm_1(&inv))
}
pub fn solve(a: &FixedMatrix, b: &FixedVector) -> Result<FixedVector, OverflowDetected> {
let lu = lu_decompose(a)?;
lu.solve(b)
}
pub fn solve_spd(a: &FixedMatrix, b: &FixedVector) -> Result<FixedVector, OverflowDetected> {
let chol = cholesky_decompose(a)?;
chol.solve(b)
}
pub fn determinant(a: &FixedMatrix) -> Result<FixedPoint, OverflowDetected> {
let lu = lu_decompose(a)?;
Ok(lu.determinant())
}
pub fn inverse(a: &FixedMatrix) -> Result<FixedMatrix, OverflowDetected> {
let lu = lu_decompose(a)?;
lu.inverse()
}
fn default_sv_threshold(sigma: &FixedVector, m: usize, n: usize) -> FixedPoint {
if sigma.len() == 0 {
return FixedPoint::one();
}
let sigma_max = sigma[0]; if sigma_max.is_zero() {
return FixedPoint::one();
}
use super::linalg::convergence_threshold;
let base = convergence_threshold(sigma_max);
let factor = FixedPoint::from_int(m.max(n) as i32);
factor * base
}
pub fn pseudoinverse(a: &FixedMatrix) -> Result<FixedMatrix, OverflowDetected> {
let svd = svd_decompose(a)?;
let (m, n) = (a.rows(), a.cols());
let k = svd.sigma.len();
let thresh = default_sv_threshold(&svd.sigma, m, n);
let mut result = FixedMatrix::new(n, m);
for i in 0..k {
if svd.sigma[i] <= thresh {
break; }
let inv_sigma = FixedPoint::one() / svd.sigma[i];
for r in 0..n {
for c in 0..m {
let v_ri = svd.vt.get(i, r); let u_ci = svd.u.get(c, i);
result.set(r, c, result.get(r, c) + inv_sigma * v_ri * u_ci);
}
}
}
Ok(result)
}
pub fn pseudoinverse_with_threshold(
a: &FixedMatrix,
threshold: FixedPoint,
) -> Result<FixedMatrix, OverflowDetected> {
let svd = svd_decompose(a)?;
let (m, n) = (a.rows(), a.cols());
let k = svd.sigma.len();
let mut result = FixedMatrix::new(n, m);
for i in 0..k {
if svd.sigma[i] <= threshold {
break;
}
let inv_sigma = FixedPoint::one() / svd.sigma[i];
for r in 0..n {
for c in 0..m {
let v_ri = svd.vt.get(i, r);
let u_ci = svd.u.get(c, i);
result.set(r, c, result.get(r, c) + inv_sigma * v_ri * u_ci);
}
}
}
Ok(result)
}
pub fn rank(a: &FixedMatrix) -> Result<usize, OverflowDetected> {
let svd = svd_decompose(a)?;
let thresh = default_sv_threshold(&svd.sigma, a.rows(), a.cols());
let mut r = 0;
for i in 0..svd.sigma.len() {
if svd.sigma[i] > thresh {
r += 1;
} else {
break; }
}
Ok(r)
}
pub fn condition_number_2(a: &FixedMatrix) -> Result<FixedPoint, OverflowDetected> {
let svd = svd_decompose(a)?;
let k = svd.sigma.len();
if k == 0 {
return Err(OverflowDetected::DivisionByZero);
}
let sigma_max = svd.sigma[0];
let sigma_min = svd.sigma[k - 1];
if sigma_min.is_zero() {
return Err(OverflowDetected::DivisionByZero);
}
Ok(sigma_max / sigma_min)
}
pub fn nullspace(a: &FixedMatrix) -> Result<FixedMatrix, OverflowDetected> {
let svd = svd_decompose(a)?;
let n = a.cols();
let thresh = default_sv_threshold(&svd.sigma, a.rows(), n);
let mut r = 0;
for i in 0..svd.sigma.len() {
if svd.sigma[i] > thresh {
r += 1;
} else {
break;
}
}
let null_dim = n - r;
if null_dim == 0 {
return Ok(FixedMatrix::new(n, 0));
}
let mut basis = FixedMatrix::new(n, null_dim);
for j in 0..null_dim {
for i in 0..n {
basis.set(i, j, svd.vt.get(r + j, i)); }
}
Ok(basis)
}