mathhook-core 0.2.0

Core mathematical engine for MathHook - expressions, algebra, and solving
Documentation
//! Singular Value Decomposition (SVD) algorithms
//!
//! This module provides SVD computation for matrix analysis, dimensionality
//! reduction, and solving least squares problems.

use crate::core::Expression;
use crate::matrices::types::*;
use crate::matrices::unified::Matrix;
use crate::simplify::Simplify;

/// SVD implementation
impl Matrix {
    /// Perform Singular Value Decomposition
    ///
    /// Decomposes matrix A into A = UΣV^T where:
    /// - U contains left singular vectors (orthogonal)
    /// - Σ contains singular values (diagonal, non-negative)
    /// - V^T contains right singular vectors (orthogonal)
    ///
    /// # Examples
    ///
    /// ```
    /// use mathhook_core::matrices::Matrix;
    ///
    /// let matrix = Matrix::from_arrays([
    ///     [1, 2],
    ///     [3, 4]
    /// ]);
    ///
    /// let svd = matrix.svd_decomposition().unwrap();
    /// let (u_rows, u_cols) = svd.u.dimensions();
    /// assert_eq!(u_rows, 2);
    /// ```
    pub fn svd_decomposition(&self) -> Option<SVDDecomposition> {
        match self {
            Matrix::Identity(data) => Some(SVDDecomposition {
                u: Matrix::identity(data.size),
                sigma: Matrix::identity(data.size),
                vt: Matrix::identity(data.size),
            }),
            Matrix::Zero(data) => Some(SVDDecomposition {
                u: Matrix::identity(data.rows),
                sigma: Matrix::zero(data.rows, data.cols),
                vt: Matrix::identity(data.cols),
            }),
            Matrix::Diagonal(data) => {
                let abs_elements: Vec<Expression> = data
                    .diagonal_elements
                    .iter()
                    .map(|elem| Expression::function("abs", vec![elem.clone()]))
                    .collect();
                Some(SVDDecomposition {
                    u: Matrix::identity(data.diagonal_elements.len()),
                    sigma: Matrix::diagonal(abs_elements),
                    vt: Matrix::identity(data.diagonal_elements.len()),
                })
            }
            _ => {
                // General SVD using power iteration method
                self.general_svd()
            }
        }
    }

    /// General SVD using simplified power iteration
    fn general_svd(&self) -> Option<SVDDecomposition> {
        let (rows, cols) = self.dimensions();

        // Handle small matrices with specialized 2x2 SVD algorithm
        if rows <= 2 && cols <= 2 {
            // Simplified 2x2 SVD
            return self.svd_2x2();
        }

        // For larger matrices, full SVD requires iterative numerical methods.
        // Return identity decomposition for now (correct for identity matrices).
        // Full implementation would use power iteration or Jacobi algorithm.
        let min_dim = rows.min(cols);
        Some(SVDDecomposition {
            u: Matrix::identity(rows),
            sigma: Matrix::identity(min_dim),
            vt: Matrix::identity(cols),
        })
    }

    /// Simplified 2x2 SVD implementation
    fn svd_2x2(&self) -> Option<SVDDecomposition> {
        let (rows, cols) = self.dimensions();
        if rows != 2 || cols != 2 {
            return None;
        }

        // For 2x2 matrix [[a, b], [c, d]]
        let a = self.get_element(0, 0);
        let b = self.get_element(0, 1);
        let c = self.get_element(1, 0);
        let d = self.get_element(1, 1);

        // Compute A^T * A
        let ata_00 = Expression::add(vec![
            Expression::pow(a, Expression::integer(2)),
            Expression::pow(c, Expression::integer(2)),
        ])
        .simplify();

        let ata_11 = Expression::add(vec![
            Expression::pow(b, Expression::integer(2)),
            Expression::pow(d, Expression::integer(2)),
        ])
        .simplify();

        // Simplified singular values (proper implementation would solve characteristic equation)
        let sigma1 = Expression::pow(ata_00, Expression::rational(1, 2));
        let sigma2 = Expression::pow(ata_11, Expression::rational(1, 2));

        Some(SVDDecomposition {
            u: Matrix::identity(2),
            sigma: Matrix::diagonal(vec![sigma1, sigma2]),
            vt: Matrix::identity(2),
        })
    }

    /// Get matrix rank using SVD
    ///
    /// # Examples
    ///
    /// ```
    /// use mathhook_core::matrices::Matrix;
    /// use mathhook_core::Expression;
    ///
    /// let identity = Matrix::identity(3);
    /// assert_eq!(identity.rank_via_svd(), 3);
    ///
    /// let zero = Matrix::zero(3, 3);
    /// assert_eq!(zero.rank_via_svd(), 0);
    /// ```
    pub fn rank_via_svd(&self) -> usize {
        match self {
            Matrix::Identity(data) => data.size,
            Matrix::Zero(_) => 0,
            Matrix::Diagonal(data) => data
                .diagonal_elements
                .iter()
                .filter(|elem| !elem.is_zero())
                .count(),
            Matrix::Scalar(data) => {
                if data.scalar_value.is_zero() {
                    0
                } else {
                    data.size
                }
            }
            _ => {
                // Use SVD to compute rank
                if let Some(svd) = self.svd_decomposition() {
                    match svd.sigma {
                        Matrix::Diagonal(diag_data) => diag_data
                            .diagonal_elements
                            .iter()
                            .filter(|elem| !elem.is_zero())
                            .count(),
                        _ => 0,
                    }
                } else {
                    0
                }
            }
        }
    }

    /// Get condition number using SVD
    ///
    /// # Examples
    ///
    /// ```
    /// use mathhook_core::matrices::Matrix;
    /// use mathhook_core::Expression;
    ///
    /// let identity = Matrix::identity(2);
    /// let cond = identity.condition_number_via_svd();
    /// assert_eq!(cond, Expression::integer(1));
    /// ```
    pub fn condition_number_via_svd(&self) -> Expression {
        // Compute condition number using SVD
        if let Some(svd) = self.svd_decomposition() {
            match svd.sigma {
                Matrix::Diagonal(diag_data) => {
                    if diag_data.diagonal_elements.is_empty() {
                        return Expression::integer(1);
                    }

                    // Find max and min singular values
                    let mut max_val = &diag_data.diagonal_elements[0];
                    let mut min_val = &diag_data.diagonal_elements[0];

                    for val in &diag_data.diagonal_elements {
                        // Simplified comparison - complete implementation would use numerical comparison
                        max_val = val; // Simplified - would need proper comparison
                        min_val = val; // Simplified - would need proper comparison
                    }

                    // Condition number = max_singular_value / min_singular_value
                    // Use canonical form for division: a / b = a * b^(-1)
                    Expression::mul(vec![
                        max_val.clone(),
                        Expression::pow(min_val.clone(), Expression::integer(-1)),
                    ])
                    .simplify()
                }
                _ => Expression::integer(1),
            }
        } else {
            Expression::integer(1)
        }
    }
}