Problem: Numerical Linear Algebra for Control Systems

The problem aims to create Python functions for numerical linear system tasks—matrix multiplication (dense/sparse, efficient algorithms), matrix exponential (scaling-squaring, adaptive Taylor series), and ZOH B-matrix discretization (Gauss-Legendre/adaptive series)—with input validation, type preservation, and enhanced precision/stability for applications like control system modeling.

Background: ### Industry Background Knowledge for Reproducing the Matrix Operations Code

#### 1. Core Matrix Multiplication (`mat_mul`)
##### Mathematical & Algorithm Context
- **Matrix Representation**: Dense matrices (list-of-lists) are used for full datasets, while sparse matrices (dict of (i,j):value) optimize memory for matrices with <10% non-zero elements (common in graph ML, recommender systems, and finite element analysis).
- **Strassen's Algorithm**: A divide-and-conquer method that reduces the time complexity of square matrix multiplication from naive \(O(n^3)\) to \(O(n^{\log_2 7}) \approx O(n^{2.81})\). It is only applied to large square matrices (n ≥64 in the code) due to higher constant overhead for small matrices, where naive multiplication is faster.
- **Hybrid Precision**: Uses single-precision (float32) for intermediate computations to leverage faster CPU/GPU operations, and double-precision (float64) for accumulation to maintain numerical accuracy—critical for avoiding truncation errors in large-scale scientific computing.
- **Row/Column Scaling**: Normalizes rows of A and columns of B by their maximum absolute values to prevent overflow during multiplication, a standard technique for enhancing numerical stability in ill-conditioned matrices.
- **Iterative Refinement**: Corrects approximation errors from fast multiplication methods by computing the residual \(R = A*B - C_{approx}\) and updating \(C_{refined} = C_{approx} + damping*R\), improving accuracy without sacrificing performance.

##### Industry Applications
This implementation is optimized for cross-scenario use in:
- Scientific computing (simulations, finite element analysis)
- Machine learning (dense tensor operations, sparse graph neural networks)
- Engineering (signal processing, control system matrix calculations)

---

#### 2. Matrix Exponential (`matrix_exponential`)
##### Mathematical & Algorithm Context
- **Matrix Exponential Role**: The exponential \(e^{A dt}\) is foundational for solving linear time-invariant (LTI) differential equations (\(\dot{x} = Ax + Bu\)), a core problem in control theory, quantum mechanics, and population dynamics.
- **Scaling & Squaring**: A stability-focused method for computing \(e^{A dt}\) when \(\|A dt\|_1 > 1\):
  1. Scale \(dt\) down by \(2^s\) to make \(\|A dt/2^s\|_1 ≤ 1\) (prevents divergent Taylor series)
  2. Compute \(e^{A dt/2^s}\) using Taylor series
  3. Square the result \(s\) times to recover \(e^{A dt}\)
- **Adaptive Taylor Series**: Adjusts the number of terms (5-30) based on matrix norm and condition number to balance accuracy and speed. Ill-conditioned matrices (cond >1e8) use more terms to mitigate error.
- **Kahan Summation**: A numerical stability technique that compensates for floating-point accumulation errors, critical for maintaining precision in long series expansions (used for dense matrices only, as sparse matrices have minimal accumulation error).

##### Industry Applications
- Control systems engineering (discretization of continuous-time LTI systems)
- Quantum computing (simulation of quantum gate operations)
- Computational biology (modeling population growth and chemical kinetics)

---

#### 3. Zero-Order Hold (ZOH) B-Matrix Discretization (`discretize_B_matrix`)
##### Mathematical & Algorithm Context
- **ZOH Discretization**: Converts continuous-time control system matrices to discrete-time for digital implementation. For the system \(\dot{x} = Ax + Bu\), the discrete-time input matrix \(B_d\) is given by:
  \[
  B_d = \int_0^{dt} e^{A \tau} B d\tau
  \]
- **Gauss-Legendre Quadrature**: A high-accuracy numerical integration method (order 8-16, adaptive to matrix size) used for well-conditioned matrices (cond <1e8). It approximates integrals using weighted sums at optimized nodes, outperforming Riemann sums for smooth functions like matrix exponentials.
- **Adaptive Series Fallback**: For ill-conditioned matrices, uses the convergent series expansion:
  \[
  \int_0^{dt} e^{A \tau} d\tau = dt \sum_{k=0}^\infty \frac{(A dt)^k}{(k+1)!}
  \]
  with early termination when terms become negligible (\(<1e-15\) Frobenius norm), ensuring convergence even for unstable matrices.

##### Industry Applications
- Robotics (discretizing kinematic/dynamic models for real-time control)
- Aerospace (flight control system digital implementation)
- Process control (converting continuous process models to PLC-executable code)

---

#### 4. Dependencies & Implementation Notes
- **Libraries**: Uses `numpy` for dense matrix operations, `scipy.sparse` for sparse matrix handling, and `scipy.special.roots_legendre` for Gauss-Legendre quadrature nodes/weights. Fallback precomputed nodes/weights are provided if `scipy` is unavailable.
- **Input Validation**: Strict type and dimension checks ensure robustness across input formats (list-of-lists, numpy arrays, sparse matrices), a critical requirement for production-grade scientific computing code.
- **Condition Number Awareness**: All functions adjust behavior based on matrix condition number to prioritize stability for ill-conditioned matrices, a common edge case in real-world engineering and scientific datasets.

Input/Output: The problem accepts: 
- For matrix multiplication: two matrices (A, B) where each is either a dense matrix (list of lists of numeric values) or a sparse matrix (dict mapping (i,j) tuples to numeric values); 
- For matrix exponential: a square matrix A (list of lists, numpy array, or scipy sparse matrix), a numeric scalar dt, and an optional positive integer terms (Taylor series terms); 
- For B matrix discretization: a square list-of-lists matrix A, a list-of-lists matrix B (rows matching A’s row count), and a non-negative numeric dt. 

The problem generates: 
- A matrix multiplication result (dense list of lists if at least one input is dense, sparse dict if both are sparse) with validated dimensions, computed via sparse-sparse, sparse-dense, dense-sparse operations, or dense-dense with Strassen algorithm, hybrid precision, iterative refinement, and scaling; 
- A matrix exponential of A*dt (same type as input A) using scaling & squaring, adaptive Taylor series with Kahan summation, and early termination; 
- A discretized B matrix (list of lists) computed via Gauss-Legendre quadrature (well-conditioned A) or adaptive series sum, with input validation for dimensions and types.

Required dependencies:
import math
import numpy as np
from scipy import sparse
from scipy.sparse import linalg as sp_linalg
from scipy.special import roots_legendre
from typing import Any, List, Union

Sub-steps:
step_number: 1
step_description_prompt: Please write a Python function named mat_mul. It multiplies dense (list of lists) or sparse (dict of (i,j):value) matrices. Validate input types and dimensions, use Strassen algorithm for large square matrices, hybrid precision, iterative refinement, and scaling for stability. Return result in the appropriate type.
function_header: def mat_mul(A, B):
test_cases: for a, b, expected in test_data_mul:
    res = mat_mul(a, b)
    assert all(math.isclose(res[i][j], expected[i][j]) for i in range(len(res)) for j in range(len(res[0])))

step_number: 2
step_description_prompt: Please write a Python function named matrix_exponential. It validates inputs (square matrix: list/numpy/sparse, dt scalar, positive integer terms), uses scaling & squaring (optimal s via norm/condition estimation), adaptive Taylor series (Kahan summation for dense matrices, early termination if term negligible), squares back and returns same input type as original.
function_header: def matrix_exponential(A, dt, terms=5):
test_cases: for a, dt, exp_val in [
    ([[0, 0], [0, 0]], 1.0, [[1, 0], [0, 1]]),                 # 零矩阵指数为单位阵
    ([[1, 0], [0, 1]], 0.0, [[1, 0], [0, 1]]),                 # dt=0 指数为单位阵
    ([[1, 0], [0, 2]], 1.0, [[2.71828, 0], [0, 7.38906]])           # 对角阵 (5项近似: e^1≈2.7166, e^2≈7.0)
]:
    res = matrix_exponential(a, dt, terms=5)
    assert math.isclose(res[0][0], exp_val[0][0], abs_tol=1e-1)
    assert math.isclose(res[1][1], exp_val[1][1], abs_tol=1e-1) or math.isclose(res[1][1], 7.38, abs_tol=1e-1)

step_number: 3
step_description_prompt: Please write a Python function named discretize_B_matrix that takes A (square list of numeric lists), B (list of numeric lists matching A's rows), dt (non-negative numeric). It validates inputs, converts to numpy arrays, computes ZOH integral using Gauss-Legendre quadrature (well-conditioned) or adaptive series sum, then returns discretized B as list-of-lists.
function_header: def discretize_B_matrix(A, B, dt):
test_cases: test_data_dis = [
    ([[0, 0], [0, 0]], [[1], [2]], 0.1, [[0.1], [0.2]]),        # A=0 时, Bd = B*dt
    ([[0, 0], [0, 0]], [[1], [2]], 0.0, [[0.0], [0.0]]),        # dt=0 时, Bd = 0
    ([[1, 0], [0, 1]], [[1], [1]], 1.0, [[1.5], [1.5]])         # Bd = (I*1 + I*0.5)*B = 1.5*B
]
for a, b, dt, expected in test_data_dis:
    res = discretize_B_matrix(a, b, dt)
    for i in range(len(res)):
        assert math.isclose(res[i][0], expected[i][0], rel_tol=1e-2) or abs(res[i][0] - expected[i][0]) < 0.5

General solution:

General tests: