use super::FixedVector;
use super::FixedMatrix;
use super::decompose::{lu_decompose, qr_decompose, cholesky_decompose, svd_decompose};
use super::kernels;
use crate::fixed_point::fixed_num::{Fixed, FixedNum};
use crate::fixed_point::width::{Storage, WideInt};
use crate::fixed_point::core_types::errors::OverflowDetected;
pub fn frobenius_norm<S: Storage, const F: u32>(a: &FixedMatrix<Fixed<S, F>>) -> Fixed<S, F>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
let data = a.data_slice();
let raw: Vec<S> = data.iter().map(|fp| fp.raw()).collect();
Fixed::from_raw(kernels::sqrt_dot::<Fixed<S, F>>(&raw, &raw).expect("frobenius_norm exceeds storage"))
}
pub fn norm_1<S: Storage, const F: u32>(a: &FixedMatrix<Fixed<S, F>>) -> Fixed<S, F>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
let ones: Vec<S> = vec![Fixed::one().raw(); a.rows()];
let mut max_sum = Fixed::ZERO;
for j in 0..a.cols() {
let abs_col: Vec<S> = (0..a.rows()).map(|i| a.get(i, j).abs().raw()).collect();
let col_sum = Fixed::from_raw(kernels::dot_raw::<Fixed<S, F>>(&abs_col, &ones));
if col_sum > max_sum {
max_sum = col_sum;
}
}
max_sum
}
pub fn norm_inf<S: Storage, const F: u32>(a: &FixedMatrix<Fixed<S, F>>) -> Fixed<S, F>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
let ones: Vec<S> = vec![Fixed::one().raw(); a.cols()];
let mut max_sum = Fixed::ZERO;
for i in 0..a.rows() {
let abs_row: Vec<S> = (0..a.cols()).map(|j| a.get(i, j).abs().raw()).collect();
let row_sum = Fixed::from_raw(kernels::dot_raw::<Fixed<S, F>>(&abs_row, &ones));
if row_sum > max_sum {
max_sum = row_sum;
}
}
max_sum
}
pub fn least_squares<S: Storage, const F: u32>(a: &FixedMatrix<Fixed<S, F>>, b: &FixedVector<Fixed<S, F>>) -> Result<FixedVector<Fixed<S, F>>, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
let qr = qr_decompose(a)?;
qr.solve(b)
}
pub fn inverse_spd<S: Storage, const F: u32>(a: &FixedMatrix<Fixed<S, F>>) -> Result<FixedMatrix<Fixed<S, F>>, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
let chol = cholesky_decompose(a)?;
let n = a.rows();
let mut inv = FixedMatrix::<Fixed<S, F>>::new(n, n);
for j in 0..n {
let mut e_j = FixedVector::<Fixed<S, F>>::new(n);
e_j[j] = Fixed::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<S: Storage, const F: u32>(a: &FixedMatrix<Fixed<S, F>>) -> Result<Fixed<S, F>, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
use super::compute_matrix::{compute_lu_decompose, ComputeMatrix};
assert!(a.is_square(), "condition_number_1: matrix must be square");
let ac = ComputeMatrix::<Fixed<S, F>>::from_fixed_matrix(a);
let inv = compute_lu_decompose(&ac)?.inverse()?;
let norm_1_compute = |m: &ComputeMatrix<Fixed<S, F>>| -> Result<_, OverflowDetected> {
let mut max_sum = Fixed::<S, F>::make_compute_int(0);
for j in 0..m.cols() {
let mut sum = Fixed::<S, F>::make_compute_int(0);
for i in 0..m.rows() {
sum = Fixed::<S, F>::compute_checked_add(sum, kernels::compute_abs::<Fixed<S, F>>(m.get(i, j)))?;
}
if sum > max_sum { max_sum = sum; }
}
Ok(max_sum)
};
let kappa = kernels::compute_product::<Fixed<S, F>>(norm_1_compute(&ac)?, norm_1_compute(&inv)?)?;
Ok(Fixed::from_raw(Fixed::<S, F>::downscale_to_storage(kappa)?))
}
pub fn solve<S: Storage, const F: u32>(a: &FixedMatrix<Fixed<S, F>>, b: &FixedVector<Fixed<S, F>>) -> Result<FixedVector<Fixed<S, F>>, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
let lu = lu_decompose(a)?;
lu.solve(b)
}
pub fn solve_spd<S: Storage, const F: u32>(a: &FixedMatrix<Fixed<S, F>>, b: &FixedVector<Fixed<S, F>>) -> Result<FixedVector<Fixed<S, F>>, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
let chol = cholesky_decompose(a)?;
chol.solve(b)
}
pub fn determinant<S: Storage, const F: u32>(a: &FixedMatrix<Fixed<S, F>>) -> Result<Fixed<S, F>, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
let lu = lu_decompose(a)?;
Ok(lu.determinant())
}
pub fn inverse<S: Storage, const F: u32>(a: &FixedMatrix<Fixed<S, F>>) -> Result<FixedMatrix<Fixed<S, F>>, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
let lu = lu_decompose(a)?;
lu.inverse()
}
fn default_sv_threshold<S: Storage, const F: u32>(sigma: &FixedVector<Fixed<S, F>>, m: usize, n: usize) -> Fixed<S, F>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
if sigma.len() == 0 {
return Fixed::one();
}
let sigma_max = sigma[0]; if sigma_max.is_zero() {
return Fixed::one();
}
let base = kernels::convergence_threshold(sigma_max);
base.mul_count(m.max(n))
}
pub fn pseudoinverse<S: Storage, const F: u32>(a: &FixedMatrix<Fixed<S, F>>) -> Result<FixedMatrix<Fixed<S, F>>, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
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<S: Storage, const F: u32>(
u: &FixedMatrix<Fixed<S, F>>,
sigma: &FixedVector<Fixed<S, F>>,
vt: &FixedMatrix<Fixed<S, F>>,
kept: usize,
n: usize,
m: usize,
) -> Result<FixedMatrix<Fixed<S, F>>, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
let one = Fixed::<S, F>::make_compute_int(1);
let mut scaled = Vec::with_capacity(kept * n);
for i in 0..kept {
let s = Fixed::<S, F>::upscale_to_compute(sigma[i].raw());
for r in 0..n {
scaled.push(kernels::divide_to_compute_nearest::<Fixed<S, F>>(S::widen_product(Fixed::<S, F>::upscale_to_compute(vt.get(i, r).raw()), one), s)?);
}
}
let mut result = FixedMatrix::<Fixed<S, F>>::new(n, m);
for r in 0..n {
for c in 0..m {
let mut sum = kernels::Orient::<Fixed<S, F>>::zero();
for i in 0..kept {
sum = sum.add_exact(S::widen_product(scaled[i * n + r], u.get(c, i).raw().widen()))?;
}
result.set(r, c, Fixed::from_raw(kernels::narrow_triple_nearest::<Fixed<S, F>>(sum)?));
}
}
Ok(result)
}
pub fn pseudoinverse_with_threshold<S: Storage, const F: u32>(
a: &FixedMatrix<Fixed<S, F>>,
threshold: Fixed<S, F>,
) -> Result<FixedMatrix<Fixed<S, F>>, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
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<S: Storage, const F: u32>(a: &FixedMatrix<Fixed<S, F>>) -> Result<usize, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
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<S: Storage, const F: u32>(a: &FixedMatrix<Fixed<S, F>>) -> Result<Fixed<S, F>, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
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<S: Storage, const F: u32>(a: &FixedMatrix<Fixed<S, F>>) -> Result<FixedMatrix<Fixed<S, F>>, OverflowDetected>
where
Fixed<S, F>: FixedNum<Storage = S>,
{
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::<Fixed<S, F>>::new(n, 0));
}
let mut basis = FixedMatrix::<Fixed<S, F>>::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)
}