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();
FixedPoint::from_raw(super::linalg::compute_tier_sqrt_dot(&raw, &raw).expect("frobenius_norm exceeds storage"))
}
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> {
use super::compute_matrix::{compute_lu_decompose, ComputeMatrix};
use super::linalg::{compute_abs, compute_product, downscale_to_storage};
use crate::fixed_point::universal::fasc::stack_evaluator::compute::{compute_checked_add, make_compute_int};
assert!(a.is_square(), "condition_number_1: matrix must be square");
let ac = ComputeMatrix::from_fixed_matrix(a);
let inv = compute_lu_decompose(&ac)?.inverse()?;
let norm_1_compute = |m: &ComputeMatrix| -> Result<_, OverflowDetected> {
let mut max_sum = make_compute_int(0);
for j in 0..m.cols() {
let mut sum = make_compute_int(0);
for i in 0..m.rows() {
sum = compute_checked_add(sum, compute_abs(m.get(i, j)))?;
}
if sum > max_sum { max_sum = sum; }
}
Ok(max_sum)
};
let kappa = compute_product(norm_1_compute(&ac)?, norm_1_compute(&inv)?)?;
Ok(FixedPoint::from_raw(downscale_to_storage(kappa)?))
}
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);
base.mul_count(m.max(n))
}
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 kept = (0..k).take_while(|&i| svd.sigma[i] > thresh).count();
pseudoinverse_sum(&svd.u, &svd.sigma, &svd.vt, kept, n, m)
}
fn pseudoinverse_sum(
u: &FixedMatrix,
sigma: &FixedVector,
vt: &FixedMatrix,
kept: usize,
n: usize,
m: usize,
) -> Result<FixedMatrix, OverflowDetected> {
use super::linalg::upscale_to_compute;
use super::wide_acc::{divide_to_compute_nearest, narrow_triple_nearest, widen_product, widen_storage, Wide};
use crate::fixed_point::universal::fasc::stack_evaluator::compute::make_compute_int;
let one = make_compute_int(1);
let mut scaled = Vec::with_capacity(kept * n);
for i in 0..kept {
let s = upscale_to_compute(sigma[i].raw());
for r in 0..n {
scaled.push(divide_to_compute_nearest(widen_product(upscale_to_compute(vt.get(i, r).raw()), one), s)?);
}
}
let mut result = FixedMatrix::new(n, m);
for r in 0..n {
for c in 0..m {
let mut sum = <super::wide_acc::acc::Orient as Wide>::zero();
for i in 0..kept {
sum = sum.add_exact(widen_product(scaled[i * n + r], widen_storage(u.get(c, i).raw())))?;
}
result.set(r, c, FixedPoint::from_raw(narrow_triple_nearest(sum)?));
}
}
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 kept = (0..k).take_while(|&i| svd.sigma[i] > threshold).count();
pseudoinverse_sum(&svd.u, &svd.sigma, &svd.vt, kept, n, m)
}
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)
}