Skip to main content

qualia_core_db/specialized_libs/
linear_algebra.rs

1//! Linear Algebra Library - High-Performance Mathematical Computing
2//!
3//! This module provides high-performance linear algebra operations leveraging Phase 2 enhancements:
4//! - Hardware-Sympathetic Storage (ZNS) for zero-copy matrix operations
5//! - NVMe Computational Storage (CSD) for hardware-accelerated computations
6//! - Zero-Knowledge Semantic Proofs for privacy-preserving linear algebra
7//! - Ambient Sub-Threshold Orchestration for mobile optimization
8pub mod computation;
9pub mod core_types;
10pub mod optimization;
11pub mod performance;
12pub mod privacy;
13pub mod storage;
14
15pub use computation::*;
16pub use core_types::*;
17pub use optimization::*;
18pub use performance::*;
19pub use privacy::*;
20pub use storage::*;
21
22#[cfg(test)]
23mod tests {
24    use super::*;
25
26    #[test]
27    fn test_linear_algebra_library_creation() {
28        let library = LinearAlgebraLibrary::new();
29        assert_eq!(library.list_matrices().len(), 0);
30    }
31
32    #[test]
33    fn test_matrix_creation() {
34        let mut library = LinearAlgebraLibrary::new();
35        library.initialize().unwrap();
36
37        let data = vec![1.0, 2.0, 3.0, 4.0];
38        let matrix = library
39            .create_matrix("test_matrix".to_string(), 2, 2, DataType::Float64, data)
40            .unwrap();
41
42        assert_eq!(matrix.rows, 2);
43        assert_eq!(matrix.cols, 2);
44        assert_eq!(matrix.data.len(), 4);
45    }
46
47    #[test]
48    fn test_matrix_multiplication() {
49        let mut library = LinearAlgebraLibrary::new();
50        library.initialize().unwrap();
51
52        let a_data = vec![1.0, 2.0, 3.0, 4.0];
53        let b_data = vec![5.0, 6.0, 7.0, 8.0];
54
55        library
56            .create_matrix("A".to_string(), 2, 2, DataType::Float64, a_data)
57            .unwrap();
58        library
59            .create_matrix("B".to_string(), 2, 2, DataType::Float64, b_data)
60            .unwrap();
61
62        let result = library.matrix_multiply("A", "B", "C", 1.0, 0.0).unwrap();
63
64        assert_eq!(result.result.rows, 2);
65        assert_eq!(result.result.cols, 2);
66        assert_eq!(result.result.data[0], 19.0); // 1*5 + 2*7
67        assert_eq!(result.result.data[1], 22.0); // 1*6 + 2*8
68        assert_eq!(result.result.data[2], 43.0); // 3*5 + 4*7
69        assert_eq!(result.result.data[3], 50.0); // 3*6 + 4*8
70    }
71
72    #[test]
73    fn test_matrix_transpose() {
74        let mut library = LinearAlgebraLibrary::new();
75        library.initialize().unwrap();
76
77        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
78        library
79            .create_matrix("A".to_string(), 2, 3, DataType::Float64, data)
80            .unwrap();
81
82        let result = library.matrix_transpose("A", "AT").unwrap();
83
84        assert_eq!(result.result.rows, 3);
85        assert_eq!(result.result.cols, 2);
86        assert_eq!(result.result.data[0], 1.0);
87        assert_eq!(result.result.data[1], 4.0);
88        assert_eq!(result.result.data[2], 2.0);
89        assert_eq!(result.result.data[3], 5.0);
90        assert_eq!(result.result.data[4], 3.0);
91        assert_eq!(result.result.data[5], 6.0);
92    }
93
94    #[test]
95    fn test_matrix_inverse() {
96        let mut library = LinearAlgebraLibrary::new();
97        library.initialize().unwrap();
98
99        let data = vec![2.0, 1.0, 1.0, 1.0]; // [[2,1],[1,1]]
100        library
101            .create_matrix("A".to_string(), 2, 2, DataType::Float64, data)
102            .unwrap();
103
104        let result = library.matrix_inverse("A", "A_inv").unwrap();
105
106        assert_eq!(result.result.rows, 2);
107        assert_eq!(result.result.cols, 2);
108        // Inverse of [[2,1],[1,1]] is [[1,-1],[-1,2]]
109        assert!((result.result.data[0] - 1.0).abs() < 1e-10);
110        assert!((result.result.data[1] + 1.0).abs() < 1e-10);
111        assert!((result.result.data[2] + 1.0).abs() < 1e-10);
112        assert!((result.result.data[3] - 2.0).abs() < 1e-10);
113    }
114
115    #[test]
116    fn test_solve_linear_system() {
117        let mut library = LinearAlgebraLibrary::new();
118        library.initialize().unwrap();
119
120        let matrix_data = vec![2.0, 1.0, 1.0, 1.0]; // [[2,1],[1,1]]
121        let rhs_data = vec![3.0, 2.0]; // [3,2]
122
123        library
124            .create_matrix("A".to_string(), 2, 2, DataType::Float64, matrix_data)
125            .unwrap();
126        library
127            .create_matrix("b".to_string(), 2, 1, DataType::Float64, rhs_data)
128            .unwrap();
129
130        let result = library.solve_linear_system("A", "b", "x").unwrap();
131
132        assert_eq!(result.result.rows, 2);
133        assert_eq!(result.result.cols, 1);
134        // Solution should be [1,1] for 2x + y = 3, x + y = 2
135        assert!((result.result.data[0] - 1.0).abs() < 1e-10);
136        assert!((result.result.data[1] - 1.0).abs() < 1e-10);
137    }
138
139    #[test]
140    fn test_solve_quadratic_two_real() {
141        // x² − 5x + 6 = 0 → {2, 3}
142        match solve_quadratic(1.0, -5.0, 6.0).unwrap() {
143            QuadraticRoots::TwoReal(lo, hi) => {
144                assert!((lo - 2.0).abs() < 1e-12);
145                assert!((hi - 3.0).abs() < 1e-12);
146            }
147            other => panic!("expected two real roots, got {:?}", other),
148        }
149    }
150
151    #[test]
152    fn test_solve_quadratic_double_and_complex_and_linear() {
153        // x² − 2x + 1 = 0 → double root 1
154        assert_eq!(
155            solve_quadratic(1.0, -2.0, 1.0).unwrap(),
156            QuadraticRoots::DoubleReal(1.0)
157        );
158        // x² + 1 = 0 → ±i
159        match solve_quadratic(1.0, 0.0, 1.0).unwrap() {
160            QuadraticRoots::ComplexPair { re, im } => {
161                assert!(re.abs() < 1e-12 && (im - 1.0).abs() < 1e-12);
162            }
163            other => panic!("expected complex pair, got {:?}", other),
164        }
165        // 0·x² + 2x − 4 = 0 → linear root 2
166        assert_eq!(
167            solve_quadratic(0.0, 2.0, -4.0).unwrap(),
168            QuadraticRoots::Linear(2.0)
169        );
170    }
171
172    #[test]
173    fn test_polynomial_roots_general() {
174        // (x−1)(x−2)(x−3) = x³ − 6x² + 11x − 6 → real roots {1,2,3}
175        let mut roots = polynomial_roots(&[1.0, -6.0, 11.0, -6.0]).unwrap();
176        assert_eq!(roots.len(), 3);
177        assert!(roots.iter().all(|r| r.is_real(1e-7)));
178        roots.sort_by(|a, b| a.re.partial_cmp(&b.re).unwrap());
179        for (got, want) in roots.iter().zip([1.0, 2.0, 3.0]) {
180            assert!((got.re - want).abs() < 1e-6, "root {:?} != {}", got, want);
181        }
182    }
183
184    #[test]
185    fn test_polynomial_roots_complex_quartic() {
186        // (x²+1)(x²−1) = x⁴ − 1 → roots {1, −1, i, −i}
187        let roots = polynomial_roots(&[1.0, 0.0, 0.0, 0.0, -1.0]).unwrap();
188        assert_eq!(roots.len(), 4);
189        let real_count = roots.iter().filter(|r| r.is_real(1e-7)).count();
190        let imag_count = roots.iter().filter(|r| !r.is_real(1e-7)).count();
191        assert_eq!(real_count, 2, "expected ±1 real");
192        assert_eq!(imag_count, 2, "expected ±i imaginary");
193        // every root satisfies r⁴ = 1 → |r| ≈ 1
194        assert!(roots.iter().all(|r| (r.abs() - 1.0).abs() < 1e-6));
195    }
196
197    #[test]
198    fn test_determinant() {
199        // [[1,2],[3,4]] → −2
200        assert!((determinant(2, &[1.0, 2.0, 3.0, 4.0]).unwrap() + 2.0).abs() < 1e-12);
201        // 3×3 with known det: [[6,1,1],[4,-2,5],[2,8,7]] → −306
202        let d = determinant(3, &[6.0, 1.0, 1.0, 4.0, -2.0, 5.0, 2.0, 8.0, 7.0]).unwrap();
203        assert!((d + 306.0).abs() < 1e-9, "det = {}", d);
204        // Singular matrix → 0
205        assert!(determinant(2, &[1.0, 2.0, 2.0, 4.0]).unwrap().abs() < 1e-12);
206    }
207
208    #[test]
209    fn test_eigen_symmetric() {
210        // [[2,1],[1,2]] → eigenvalues {1, 3}; check A·v = λ·v for each.
211        let a = [2.0, 1.0, 1.0, 2.0];
212        let (mut vals, vecs) = eigen_symmetric(2, &a).unwrap();
213        vals.sort_by(|x, y| x.partial_cmp(y).unwrap());
214        assert!(
215            (vals[0] - 1.0).abs() < 1e-9 && (vals[1] - 3.0).abs() < 1e-9,
216            "vals = {:?}",
217            vals
218        );
219
220        // Re-fetch unsorted to pair eigenvalue j with column j.
221        let (vals_u, vecs_u) = eigen_symmetric(2, &a).unwrap();
222        for j in 0..2 {
223            let (v0, v1) = (vecs_u[0 * 2 + j], vecs_u[1 * 2 + j]);
224            // A·v
225            let av0 = a[0] * v0 + a[1] * v1;
226            let av1 = a[2] * v0 + a[3] * v1;
227            // λ·v
228            assert!(
229                (av0 - vals_u[j] * v0).abs() < 1e-7,
230                "A·v != λ·v (row0, col{j})"
231            );
232            assert!(
233                (av1 - vals_u[j] * v1).abs() < 1e-7,
234                "A·v != λ·v (row1, col{j})"
235            );
236            // unit eigenvector
237            assert!(((v0 * v0 + v1 * v1).sqrt() - 1.0).abs() < 1e-9);
238        }
239        let _ = vecs;
240    }
241
242    #[test]
243    fn test_eigen_symmetric_rejects_asymmetric() {
244        assert!(eigen_symmetric(2, &[1.0, 2.0, 3.0, 4.0]).is_err());
245    }
246
247    #[test]
248    fn test_lu_decompose_reconstructs_and_dets() {
249        // P·A = L·U: reconstruct A from the factors (applying the pivot permutation).
250        let n = 3;
251        let a = [4.0, 3.0, 2.0, 2.0, 1.0, 3.0, 3.0, 2.0, 1.0];
252        let lu = lu_decompose(n, &a).unwrap();
253        assert!(!lu.singular);
254        // det agrees with the standalone determinant fn.
255        assert!((lu.determinant() - determinant(n, &a).unwrap()).abs() < 1e-9);
256
257        // Rebuild L and U, multiply, and compare to the row-permuted A.
258        let mut l = vec![0.0; n * n];
259        let mut u = vec![0.0; n * n];
260        for i in 0..n {
261            l[i * n + i] = 1.0;
262            for j in 0..n {
263                if j < i {
264                    l[i * n + j] = lu.lu[i * n + j];
265                } else {
266                    u[i * n + j] = lu.lu[i * n + j];
267                }
268            }
269        }
270        for i in 0..n {
271            for j in 0..n {
272                let mut acc = 0.0;
273                for k in 0..n {
274                    acc += l[i * n + k] * u[k * n + j];
275                }
276                // (L·U)[i][j] must equal A at the permuted original row.
277                let orig_row = lu.pivots[i];
278                assert!(
279                    (acc - a[orig_row * n + j]).abs() < 1e-9,
280                    "LU != P·A at {i},{j}"
281                );
282            }
283        }
284
285        // Singular matrix → flagged + det 0.
286        let sing = lu_decompose(2, &[1.0, 2.0, 2.0, 4.0]).unwrap();
287        assert!(sing.singular && sing.determinant() == 0.0);
288    }
289
290    #[test]
291    fn test_eigenvalues_general() {
292        // Upper-triangular [[1,2],[0,3]] → eigenvalues {1,3} (real).
293        let mut e = eigenvalues_general(2, &[1.0, 2.0, 0.0, 3.0]).unwrap();
294        e.sort_by(|a, b| a.re.partial_cmp(&b.re).unwrap());
295        assert!(e.iter().all(|z| z.is_real(1e-7)));
296        assert!((e[0].re - 1.0).abs() < 1e-6 && (e[1].re - 3.0).abs() < 1e-6);
297
298        // Rotation [[0,-1],[1,0]] → eigenvalues ±i (non-symmetric, complex).
299        let r = eigenvalues_general(2, &[0.0, -1.0, 1.0, 0.0]).unwrap();
300        assert_eq!(r.len(), 2);
301        assert!(r
302            .iter()
303            .all(|z| z.re.abs() < 1e-6 && (z.im.abs() - 1.0).abs() < 1e-6));
304    }
305
306    #[test]
307    fn test_characteristic_polynomial_determinant_link() {
308        // det(A) = (-1)ⁿ · cₙ for A = [[1,2],[3,4]] (det = −2, n = 2 → cₙ = −2).
309        let c = characteristic_polynomial(2, &[1.0, 2.0, 3.0, 4.0]).unwrap();
310        assert_eq!(c.len(), 3); // [1, c1, c2]
311        assert!((c[1] + 5.0).abs() < 1e-12); // -trace = -(1+4) = -5
312        let det_from_poly = c[2]; // (-1)^2 · c2 = c2
313        assert!((det_from_poly - determinant(2, &[1.0, 2.0, 3.0, 4.0]).unwrap()).abs() < 1e-9);
314    }
315
316    #[test]
317    fn test_svd_reconstruction() {
318        // A is 3×2; verify A ≈ U·Σ·Vᵀ and that singular values are descending ≥ 0.
319        let m = 3;
320        let n = 2;
321        let a = vec![1.0, 0.0, 0.0, 1.0, 1.0, 1.0];
322        let decomp = svd(m, n, &a).unwrap();
323
324        assert_eq!(decomp.singular_values.len(), n);
325        assert!(decomp.singular_values[0] >= decomp.singular_values[1] - 1e-12);
326        assert!(decomp.singular_values.iter().all(|&s| s >= -1e-12));
327
328        for i in 0..m {
329            for j in 0..n {
330                let mut recon = 0.0;
331                for k in 0..n {
332                    recon += decomp.u[i * n + k] * decomp.singular_values[k] * decomp.v[j * n + k];
333                }
334                assert!(
335                    (recon - a[i * n + j]).abs() < 1e-9,
336                    "reconstruction[{i}][{j}] = {recon} != {}",
337                    a[i * n + j]
338                );
339            }
340        }
341    }
342
343    #[test]
344    fn test_private_matrix_multiplication() {
345        let mut library = LinearAlgebraLibrary::new();
346        library.initialize().unwrap();
347
348        let a_data = vec![1.0, 2.0, 3.0, 4.0];
349        let b_data = vec![5.0, 6.0, 7.0, 8.0];
350
351        library
352            .create_matrix("A".to_string(), 2, 2, DataType::Float64, a_data)
353            .unwrap();
354        library
355            .create_matrix("B".to_string(), 2, 2, DataType::Float64, b_data)
356            .unwrap();
357
358        let result = library.private_matrix_multiply("A", "B", "C").unwrap();
359
360        assert!(
361            result.privacy_preserved,
362            "the Groth16 proof of A·B = C must verify"
363        );
364        assert_eq!(result.result.rows, 2);
365        assert_eq!(result.result.cols, 2);
366        // The returned matrix is exactly what the ZK circuit attested: A·B.
367        // [[1,2],[3,4]] · [[5,6],[7,8]] = [[19,22],[43,50]].
368        assert_eq!(result.result.data, vec![19.0, 22.0, 43.0, 50.0]);
369    }
370
371    #[test]
372    fn test_private_matrix_multiplication_rectangular() {
373        // Non-square, with a negative entry, to exercise general dimensions and the
374        // signed field encoding. A is 2x3, B is 3x2.
375        let mut library = LinearAlgebraLibrary::new();
376        library.initialize().unwrap();
377        library
378            .create_matrix(
379                "A".to_string(),
380                2,
381                3,
382                DataType::Float64,
383                vec![1.0, 2.0, 3.0, 4.0, -5.0, 6.0],
384            )
385            .unwrap();
386        library
387            .create_matrix(
388                "B".to_string(),
389                3,
390                2,
391                DataType::Float64,
392                vec![7.0, 8.0, 9.0, 10.0, 11.0, 12.0],
393            )
394            .unwrap();
395
396        let result = library.private_matrix_multiply("A", "B", "C").unwrap();
397
398        assert!(result.privacy_preserved);
399        // Row0: [1·7+2·9+3·11, 1·8+2·10+3·12] = [58, 64]
400        // Row1: [4·7-5·9+6·11, 4·8-5·10+6·12] = [49, 54]
401        for (got, want) in result.result.data.iter().zip([58.0, 64.0, 49.0, 54.0]) {
402            assert!((got - want).abs() < 1e-4);
403        }
404    }
405
406    #[test]
407    fn test_private_matrix_multiplication_fractional() {
408        // Real-valued (non-integer) matrices must now work via the fixed-point encoding.
409        // [[0.5,1.5],[2.5,0.5]] · [[1.0,0.0],[0.0,2.0]] = [[0.5,3.0],[2.5,1.0]]
410        let mut library = LinearAlgebraLibrary::new();
411        library.initialize().unwrap();
412        library
413            .create_matrix(
414                "A".to_string(),
415                2,
416                2,
417                DataType::Float64,
418                vec![0.5, 1.5, 2.5, 0.5],
419            )
420            .unwrap();
421        library
422            .create_matrix(
423                "B".to_string(),
424                2,
425                2,
426                DataType::Float64,
427                vec![1.0, 0.0, 0.0, 2.0],
428            )
429            .unwrap();
430
431        let result = library.private_matrix_multiply("A", "B", "C").unwrap();
432        assert!(result.privacy_preserved);
433        for (got, want) in result.result.data.iter().zip([0.5, 3.0, 2.5, 1.0]) {
434            assert!(
435                (got - want).abs() < 1e-4,
436                "fixed-point ZK result {got} != {want}"
437            );
438        }
439    }
440
441    // === Integration tests for cache, monitoring, and pattern recognition ===
442
443    #[test]
444    fn test_multiply_uses_cache_and_records_metrics() {
445        let mut library = LinearAlgebraLibrary::new();
446        library.initialize().unwrap();
447
448        let a_data = vec![1.0, 2.0, 3.0, 4.0];
449        let b_data = vec![5.0, 6.0, 7.0, 8.0];
450
451        library
452            .create_matrix("A".to_string(), 2, 2, DataType::Float64, a_data)
453            .unwrap();
454        library
455            .create_matrix("B".to_string(), 2, 2, DataType::Float64, b_data)
456            .unwrap();
457
458        // First multiply — should compute and cache the result
459        let result1 = library.matrix_multiply("A", "B", "C", 1.0, 0.0).unwrap();
460        assert_eq!(result1.result.data, vec![19.0, 22.0, 43.0, 50.0]);
461
462        // Verify performance metrics were recorded
463        let stats = library.get_performance_stats();
464        assert!(stats.total_operations > 0);
465
466        // Verify operation metrics were recorded
467        let op_metrics = library
468            .performance_monitor
469            .get_operation_metrics("matrix_multiply");
470        assert!(op_metrics.is_some());
471        assert!(op_metrics.unwrap().count > 0);
472
473        // Verify matrix access was recorded
474        let m_metrics = library.performance_monitor.get_matrix_metrics("A");
475        assert!(m_metrics.is_some());
476        assert!(m_metrics.unwrap().access_count > 0);
477    }
478
479    #[test]
480    fn test_cache_populated_after_operation() {
481        let mut library = LinearAlgebraLibrary::new();
482        library.initialize().unwrap();
483
484        library
485            .create_matrix(
486                "A".to_string(),
487                2,
488                2,
489                DataType::Float64,
490                vec![1.0, 2.0, 3.0, 4.0],
491            )
492            .unwrap();
493        library
494            .create_matrix(
495                "B".to_string(),
496                2,
497                2,
498                DataType::Float64,
499                vec![5.0, 6.0, 7.0, 8.0],
500            )
501            .unwrap();
502
503        // After multiply, the result "C" should be in the cache
504        library.matrix_multiply("A", "B", "C", 1.0, 0.0).unwrap();
505
506        // The cache should have entries (at least the result matrix)
507        assert!(library.matrix_storage.cache.cache_size() > 0);
508    }
509
510    #[test]
511    fn test_analyze_matrix_method() {
512        let mut library = LinearAlgebraLibrary::new();
513        library.initialize().unwrap();
514
515        // Create a symmetric positive definite matrix
516        library
517            .create_matrix(
518                "S".to_string(),
519                2,
520                2,
521                DataType::Float64,
522                vec![2.0, 1.0, 1.0, 2.0],
523            )
524            .unwrap();
525
526        let analysis = library.analyze_matrix("S").unwrap();
527        assert_eq!(analysis.matrix_id, "S");
528        assert!(analysis
529            .detected_patterns
530            .contains(&MatrixPattern::Symmetric));
531        assert!(analysis
532            .detected_patterns
533            .contains(&MatrixPattern::PositiveDefinite));
534        assert!(!analysis.recommended_algorithms.is_empty());
535    }
536
537    #[test]
538    fn test_analyze_diagonal_matrix() {
539        let mut library = LinearAlgebraLibrary::new();
540        library.initialize().unwrap();
541
542        library
543            .create_matrix(
544                "D".to_string(),
545                3,
546                3,
547                DataType::Float64,
548                vec![1.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 3.0],
549            )
550            .unwrap();
551
552        let analysis = library.analyze_matrix("D").unwrap();
553        assert!(analysis
554            .detected_patterns
555            .contains(&MatrixPattern::Diagonal));
556    }
557
558    #[test]
559    fn test_performance_summary() {
560        let mut library = LinearAlgebraLibrary::new();
561        library.initialize().unwrap();
562
563        library
564            .create_matrix(
565                "A".to_string(),
566                2,
567                2,
568                DataType::Float64,
569                vec![1.0, 2.0, 3.0, 4.0],
570            )
571            .unwrap();
572        library
573            .create_matrix(
574                "B".to_string(),
575                2,
576                2,
577                DataType::Float64,
578                vec![5.0, 6.0, 7.0, 8.0],
579            )
580            .unwrap();
581        library.matrix_multiply("A", "B", "C", 1.0, 0.0).unwrap();
582
583        let summary = library.performance_summary();
584        assert!(summary.contains("Linear Algebra Performance Summary"));
585        assert!(summary.contains("matrix_multiply"));
586    }
587
588    #[test]
589    fn test_cache_hit_rate_accessor() {
590        let mut library = LinearAlgebraLibrary::new();
591        library.initialize().unwrap();
592
593        // Initially, no cache accesses
594        assert_eq!(library.cache_hit_rate(), 0.0);
595
596        library
597            .create_matrix(
598                "A".to_string(),
599                2,
600                2,
601                DataType::Float64,
602                vec![1.0, 2.0, 3.0, 4.0],
603            )
604            .unwrap();
605        library
606            .create_matrix(
607                "B".to_string(),
608                2,
609                2,
610                DataType::Float64,
611                vec![5.0, 6.0, 7.0, 8.0],
612            )
613            .unwrap();
614        library.matrix_multiply("A", "B", "C", 1.0, 0.0).unwrap();
615
616        // After an operation, cache should have entries
617        assert!(library.cache_size() > 0);
618    }
619
620    #[test]
621    fn test_transpose_records_metrics() {
622        let mut library = LinearAlgebraLibrary::new();
623        library.initialize().unwrap();
624
625        library
626            .create_matrix(
627                "A".to_string(),
628                2,
629                3,
630                DataType::Float64,
631                vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0],
632            )
633            .unwrap();
634
635        library.matrix_transpose("A", "AT").unwrap();
636
637        let op_metrics = library
638            .performance_monitor
639            .get_operation_metrics("matrix_transpose");
640        assert!(op_metrics.is_some());
641        assert!(op_metrics.unwrap().count > 0);
642    }
643
644    #[test]
645    fn test_inverse_records_metrics() {
646        let mut library = LinearAlgebraLibrary::new();
647        library.initialize().unwrap();
648
649        library
650            .create_matrix(
651                "A".to_string(),
652                2,
653                2,
654                DataType::Float64,
655                vec![2.0, 1.0, 1.0, 1.0],
656            )
657            .unwrap();
658
659        library.matrix_inverse("A", "A_inv").unwrap();
660
661        let op_metrics = library
662            .performance_monitor
663            .get_operation_metrics("matrix_inverse");
664        assert!(op_metrics.is_some());
665        assert!(op_metrics.unwrap().count > 0);
666    }
667
668    #[test]
669    fn test_solve_records_metrics() {
670        let mut library = LinearAlgebraLibrary::new();
671        library.initialize().unwrap();
672
673        library
674            .create_matrix(
675                "A".to_string(),
676                2,
677                2,
678                DataType::Float64,
679                vec![2.0, 1.0, 1.0, 1.0],
680            )
681            .unwrap();
682        library
683            .create_matrix("b".to_string(), 2, 1, DataType::Float64, vec![3.0, 2.0])
684            .unwrap();
685
686        library.solve_linear_system("A", "b", "x").unwrap();
687
688        let op_metrics = library
689            .performance_monitor
690            .get_operation_metrics("solve_linear_system");
691        assert!(op_metrics.is_some());
692        assert!(op_metrics.unwrap().count > 0);
693    }
694}
695
696/// Linear Algebra Library Manager
697pub struct LinearAlgebraLibrary {
698    pub matrix_storage: MatrixStorage,
699    pub computation_engine: ComputationEngine,
700    pub optimization_engine: OptimizationEngine,
701    pub privacy_engine: PrivacyEngine,
702    pub performance_monitor: LAPerformanceMonitor,
703}
704
705impl LinearAlgebraLibrary {
706    /// Create new linear algebra library
707    pub fn new() -> Self {
708        Self {
709            matrix_storage: MatrixStorage::new(),
710            computation_engine: ComputationEngine::new(),
711            optimization_engine: OptimizationEngine::new(),
712            privacy_engine: PrivacyEngine::new(),
713            performance_monitor: LAPerformanceMonitor::new(),
714        }
715    }
716
717    /// Initialize the library
718    pub fn initialize(&mut self) -> Result<(), LinearAlgebraError> {
719        // Initialize storage
720        self.matrix_storage.initialize()?;
721
722        // Initialize computation engine
723        self.computation_engine.initialize()?;
724
725        // Initialize optimization engine
726        self.optimization_engine.initialize()?;
727
728        // Initialize privacy engine
729        self.privacy_engine.initialize()?;
730
731        Ok(())
732    }
733
734    /// Create a new matrix
735    pub fn create_matrix(
736        &mut self,
737        matrix_id: String,
738        rows: usize,
739        cols: usize,
740        data_type: DataType,
741        data: Vec<f64>,
742    ) -> Result<Matrix, LinearAlgebraError> {
743        // Validate input
744        if data.len() != rows * cols {
745            return Err(LinearAlgebraError::InvalidDimensions(
746                "Data size doesn't match dimensions".to_string(),
747            ));
748        }
749
750        // Create matrix metadata
751        let metadata = MatrixMetadata {
752            matrix_id: matrix_id.clone(),
753            rows,
754            cols,
755            data_type: data_type.clone(),
756            storage_format: StorageFormat::RowMajor,
757            compression: CompressionType::None,
758            created_at: std::time::SystemTime::now()
759                .duration_since(std::time::UNIX_EPOCH)
760                .unwrap()
761                .as_secs(),
762            last_accessed: 0,
763            access_count: 0,
764        };
765
766        // Store matrix
767        let matrix = Matrix {
768            matrix_id: matrix_id.clone(),
769            rows,
770            cols,
771            data_type,
772            data,
773            storage_format: StorageFormat::RowMajor,
774            metadata,
775        };
776
777        self.matrix_storage.store_matrix(matrix.clone())?;
778
779        Ok(matrix)
780    }
781
782    /// Matrix multiplication with hardware acceleration
783    pub fn matrix_multiply(
784        &mut self,
785        left_id: &str,
786        right_id: &str,
787        result_id: &str,
788        alpha: f64,
789        beta: f64,
790    ) -> Result<LinearAlgebraResult<Matrix>, LinearAlgebraError> {
791        let start_time = std::time::Instant::now();
792
793        // Check cache for the result matrix
794        let cache_key = format!(
795            "mul:{}:{}:{}:{}:{}",
796            left_id, right_id, result_id, alpha, beta
797        );
798        let cache_hit = self.matrix_storage.cache.get(&cache_key).is_some();
799        if cache_hit {
800            // Retrieve from cache
801            if let Some(cached) = self.matrix_storage.cache.get(&cache_key) {
802                let execution_time = start_time.elapsed().as_millis() as u64;
803                self.performance_monitor
804                    .record_operation("matrix_multiply", execution_time, 0);
805                self.performance_monitor
806                    .record_matrix_access(result_id, "matrix_multiply", true);
807                return Ok(LinearAlgebraResult {
808                    result: cached,
809                    execution_time,
810                    memory_usage: 0,
811                    operations_used: vec!["matrix_multiply".to_string(), "cache_hit".to_string()],
812                    privacy_preserved: false,
813                });
814            }
815        }
816
817        // Get matrices
818        let left = self.matrix_storage.get_matrix(left_id)?;
819        let right = self.matrix_storage.get_matrix(right_id)?;
820
821        // Record cache miss for input matrices
822        self.performance_monitor
823            .record_matrix_access(left_id, "matrix_multiply", false);
824        self.performance_monitor
825            .record_matrix_access(right_id, "matrix_multiply", false);
826
827        // Validate dimensions
828        if left.cols != right.rows {
829            return Err(LinearAlgebraError::InvalidDimensions(
830                "Matrix dimensions incompatible for multiplication".to_string(),
831            ));
832        }
833
834        // Optimize operation
835        let optimized_operation = self
836            .optimization_engine
837            .optimize_multiplication(&left, &right)?;
838
839        // Execute multiplication
840        let result_data =
841            self.computation_engine
842                .execute_multiplication(&optimized_operation, alpha, beta)?;
843
844        // Create result matrix
845        let result = self.create_matrix(
846            result_id.to_string(),
847            left.rows,
848            right.cols,
849            left.data_type.clone(),
850            result_data,
851        )?;
852
853        // Store result in cache
854        self.matrix_storage.cache.put(&result)?;
855
856        let execution_time = start_time.elapsed().as_millis() as u64;
857        let memory_usage = (left.rows * right.cols * 8) as u64;
858
859        // Update performance metrics with detailed info
860        self.performance_monitor.record_operation_detailed(
861            "matrix_multiply",
862            execution_time as f64,
863            (left.rows, right.cols),
864        );
865        self.performance_monitor
866            .record_operation("matrix_multiply", execution_time, memory_usage);
867        self.performance_monitor
868            .record_matrix_access(result_id, "matrix_multiply", false);
869
870        Ok(LinearAlgebraResult {
871            result,
872            execution_time,
873            memory_usage,
874            operations_used: vec!["matrix_multiply".to_string()],
875            privacy_preserved: false,
876        })
877    }
878
879    /// Matrix addition
880    pub fn matrix_add(
881        &mut self,
882        left_id: &str,
883        right_id: &str,
884        result_id: &str,
885        alpha: f64,
886    ) -> Result<LinearAlgebraResult<Matrix>, LinearAlgebraError> {
887        let start_time = std::time::Instant::now();
888
889        // Get matrices
890        let left = self.matrix_storage.get_matrix(left_id)?;
891        let right = self.matrix_storage.get_matrix(right_id)?;
892
893        // Record matrix access for monitoring
894        self.performance_monitor
895            .record_matrix_access(left_id, "matrix_add", false);
896        self.performance_monitor
897            .record_matrix_access(right_id, "matrix_add", false);
898
899        // Validate dimensions
900        if left.rows != right.rows || left.cols != right.cols {
901            return Err(LinearAlgebraError::InvalidDimensions(
902                "Matrix dimensions incompatible for addition".to_string(),
903            ));
904        }
905
906        // Execute addition
907        let mut result_data = Vec::with_capacity(left.data.len());
908        for i in 0..left.data.len() {
909            result_data.push(alpha * (left.data[i] + right.data[i]));
910        }
911
912        // Create result matrix
913        let result = self.create_matrix(
914            result_id.to_string(),
915            left.rows,
916            left.cols,
917            left.data_type.clone(),
918            result_data,
919        )?;
920
921        // Store result in cache
922        self.matrix_storage.cache.put(&result)?;
923
924        let execution_time = start_time.elapsed().as_millis() as u64;
925        let memory_usage = (left.rows * left.cols * 8) as u64;
926
927        // Update performance metrics
928        self.performance_monitor.record_operation_detailed(
929            "matrix_add",
930            execution_time as f64,
931            (left.rows, left.cols),
932        );
933        self.performance_monitor
934            .record_operation("matrix_add", execution_time, memory_usage);
935        self.performance_monitor
936            .record_matrix_access(result_id, "matrix_add", false);
937
938        Ok(LinearAlgebraResult {
939            result,
940            execution_time,
941            memory_usage,
942            operations_used: vec!["matrix_add".to_string()],
943            privacy_preserved: false,
944        })
945    }
946
947    /// Matrix transpose
948    pub fn matrix_transpose(
949        &mut self,
950        input_id: &str,
951        result_id: &str,
952    ) -> Result<LinearAlgebraResult<Matrix>, LinearAlgebraError> {
953        let start_time = std::time::Instant::now();
954
955        // Get matrix
956        let input = self.matrix_storage.get_matrix(input_id)?;
957
958        // Record matrix access for monitoring
959        self.performance_monitor
960            .record_matrix_access(input_id, "matrix_transpose", false);
961
962        // Execute transpose
963        let mut result_data = Vec::with_capacity(input.data.len());
964        for j in 0..input.cols {
965            for i in 0..input.rows {
966                result_data.push(input.data[i * input.cols + j]);
967            }
968        }
969
970        // Create result matrix
971        let result = self.create_matrix(
972            result_id.to_string(),
973            input.cols,
974            input.rows,
975            input.data_type.clone(),
976            result_data,
977        )?;
978
979        // Store result in cache
980        self.matrix_storage.cache.put(&result)?;
981
982        let execution_time = start_time.elapsed().as_millis() as u64;
983        let memory_usage = (input.rows * input.cols * 8) as u64;
984
985        // Update performance metrics
986        self.performance_monitor.record_operation_detailed(
987            "matrix_transpose",
988            execution_time as f64,
989            (input.rows, input.cols),
990        );
991        self.performance_monitor
992            .record_operation("matrix_transpose", execution_time, memory_usage);
993        self.performance_monitor
994            .record_matrix_access(result_id, "matrix_transpose", false);
995
996        Ok(LinearAlgebraResult {
997            result,
998            execution_time,
999            memory_usage,
1000            operations_used: vec!["matrix_transpose".to_string()],
1001            privacy_preserved: false,
1002        })
1003    }
1004
1005    /// Matrix inverse
1006    pub fn matrix_inverse(
1007        &mut self,
1008        input_id: &str,
1009        result_id: &str,
1010    ) -> Result<LinearAlgebraResult<Matrix>, LinearAlgebraError> {
1011        let start_time = std::time::Instant::now();
1012
1013        // Get matrix
1014        let input = self.matrix_storage.get_matrix(input_id)?;
1015
1016        // Record matrix access for monitoring
1017        self.performance_monitor
1018            .record_matrix_access(input_id, "matrix_inverse", false);
1019
1020        // Validate square matrix
1021        if input.rows != input.cols {
1022            return Err(LinearAlgebraError::InvalidDimensions(
1023                "Matrix must be square for inversion".to_string(),
1024            ));
1025        }
1026
1027        // Execute inverse (simplified Gaussian elimination)
1028        let n = input.rows;
1029        let mut augmented = Vec::with_capacity(n * 2 * n);
1030
1031        // Create augmented matrix [A|I]
1032        for i in 0..n {
1033            for j in 0..n {
1034                augmented.push(input.data[i * n + j]);
1035            }
1036            for j in 0..n {
1037                augmented.push(if i == j { 1.0 } else { 0.0 });
1038            }
1039        }
1040
1041        // Gaussian elimination (simplified)
1042        for i in 0..n {
1043            // Find pivot
1044            let mut pivot_row = i;
1045            for k in (i + 1)..n {
1046                if (augmented[k * 2 * n + i]).abs() > (augmented[pivot_row * 2 * n + i]).abs() {
1047                    pivot_row = k;
1048                }
1049            }
1050
1051            // Swap rows
1052            for j in 0..(2 * n) {
1053                augmented.swap(i * 2 * n + j, pivot_row * 2 * n + j);
1054            }
1055
1056            // Eliminate column
1057            let pivot = augmented[i * 2 * n + i];
1058            if pivot.abs() < 1e-10 {
1059                return Err(LinearAlgebraError::SingularMatrix(
1060                    "Matrix is singular".to_string(),
1061                ));
1062            }
1063
1064            for j in 0..(2 * n) {
1065                augmented[i * 2 * n + j] /= pivot;
1066            }
1067
1068            for k in 0..n {
1069                if k != i {
1070                    let factor = augmented[k * 2 * n + i];
1071                    for j in 0..(2 * n) {
1072                        augmented[k * 2 * n + j] -= factor * augmented[i * 2 * n + j];
1073                    }
1074                }
1075            }
1076        }
1077
1078        // Extract inverse
1079        let mut result_data = Vec::with_capacity(n * n);
1080        for i in 0..n {
1081            for j in 0..n {
1082                result_data.push(augmented[i * 2 * n + n + j]);
1083            }
1084        }
1085
1086        // Create result matrix
1087        let result = self.create_matrix(
1088            result_id.to_string(),
1089            n,
1090            n,
1091            input.data_type.clone(),
1092            result_data,
1093        )?;
1094
1095        // Store result in cache
1096        self.matrix_storage.cache.put(&result)?;
1097
1098        let execution_time = start_time.elapsed().as_millis() as u64;
1099        let memory_usage = (n * n * 8) as u64;
1100
1101        // Update performance metrics
1102        self.performance_monitor.record_operation_detailed(
1103            "matrix_inverse",
1104            execution_time as f64,
1105            (n, n),
1106        );
1107        self.performance_monitor
1108            .record_operation("matrix_inverse", execution_time, memory_usage);
1109        self.performance_monitor
1110            .record_matrix_access(result_id, "matrix_inverse", false);
1111
1112        Ok(LinearAlgebraResult {
1113            result,
1114            execution_time,
1115            memory_usage,
1116            operations_used: vec!["matrix_inverse".to_string()],
1117            privacy_preserved: false,
1118        })
1119    }
1120
1121    /// Solve linear system Ax = b
1122    pub fn solve_linear_system(
1123        &mut self,
1124        matrix_id: &str,
1125        rhs_id: &str,
1126        solution_id: &str,
1127    ) -> Result<LinearAlgebraResult<Matrix>, LinearAlgebraError> {
1128        let start_time = std::time::Instant::now();
1129
1130        // Get matrices
1131        let matrix = self.matrix_storage.get_matrix(matrix_id)?;
1132        let rhs = self.matrix_storage.get_matrix(rhs_id)?;
1133
1134        // Record matrix access for monitoring
1135        self.performance_monitor
1136            .record_matrix_access(matrix_id, "solve_linear_system", false);
1137        self.performance_monitor
1138            .record_matrix_access(rhs_id, "solve_linear_system", false);
1139
1140        // Validate dimensions
1141        if matrix.rows != matrix.cols {
1142            return Err(LinearAlgebraError::InvalidDimensions(
1143                "Matrix must be square".to_string(),
1144            ));
1145        }
1146        if matrix.rows != rhs.rows {
1147            return Err(LinearAlgebraError::InvalidDimensions(
1148                "Matrix and RHS dimensions incompatible".to_string(),
1149            ));
1150        }
1151
1152        // Composition boundary: marshal into caller-owned buffers and solve via
1153        // the engine's Householder QR (replaces an inline Gauss-Jordan duplicate).
1154        // QR is numerically stable for the square nonsingular case and fails
1155        // closed on a (near-)singular system.
1156        use crate::solvers::linear_algebra::qr;
1157        use crate::solvers::SolversError;
1158        let n = matrix.rows;
1159        let mut a = matrix.data.clone(); // QR overwrites with R + reflectors
1160        let mut tau = vec![0.0; n];
1161        let mut b = rhs.data.clone(); // overwritten with Qᵀ·b
1162        let mut solution_data = vec![0.0; n];
1163        let map_err = |e: SolversError| match e {
1164            SolversError::SingularMatrix => {
1165                LinearAlgebraError::SingularMatrix("System is singular".to_string())
1166            }
1167            _ => LinearAlgebraError::InvalidDimensions(
1168                "matrix/RHS dimensions incompatible".to_string(),
1169            ),
1170        };
1171        qr::qr_factor(n, n, &mut a, &mut tau).map_err(map_err)?;
1172        qr::qr_solve_least_squares(n, n, &a, &tau, &mut b, &mut solution_data).map_err(map_err)?;
1173
1174        // Create result matrix
1175        let result = self.create_matrix(
1176            solution_id.to_string(),
1177            n,
1178            1,
1179            matrix.data_type.clone(),
1180            solution_data,
1181        )?;
1182
1183        // Store result in cache
1184        self.matrix_storage.cache.put(&result)?;
1185
1186        let execution_time = start_time.elapsed().as_millis() as u64;
1187        let memory_usage = (n * n * 8) as u64;
1188
1189        // Update performance metrics
1190        self.performance_monitor.record_operation_detailed(
1191            "solve_linear_system",
1192            execution_time as f64,
1193            (n, n),
1194        );
1195        self.performance_monitor.record_operation(
1196            "solve_linear_system",
1197            execution_time,
1198            memory_usage,
1199        );
1200        self.performance_monitor
1201            .record_matrix_access(solution_id, "solve_linear_system", false);
1202
1203        Ok(LinearAlgebraResult {
1204            result,
1205            execution_time,
1206            memory_usage,
1207            operations_used: vec!["solve_linear_system".to_string()],
1208            privacy_preserved: false,
1209        })
1210    }
1211
1212    /// Privacy-preserving matrix multiplication
1213    /// Multiply two matrices and produce a zero-knowledge proof that the published
1214    /// result really is `A·B`, WITHOUT revealing `A` or `B`.
1215    ///
1216    /// The proof is over a real R1CS circuit (see `ZkProofSystem::prove_matrix_multiply`):
1217    /// the `A`/`B` entries are private witnesses, the result entries are public inputs,
1218    /// and the circuit enforces `Σ_k A[i][k]·B[k][j] = C[i][j]`. `privacy_preserved` is
1219    /// set only when that Groth16 proof actually verifies — it is now a genuine
1220    /// cryptographic attestation, not a structural check.
1221    ///
1222    /// The ZK circuit operates over a FIXED-POINT encoding: each entry is scaled by
1223    /// 1e6 and rounded to a field integer, so real-valued matrices are supported to
1224    /// ~1e-6 precision (integer matrices are encoded exactly). The proof attests the
1225    /// exact scaled-integer identity; the returned matrix is that result rescaled.
1226    pub fn private_matrix_multiply(
1227        &mut self,
1228        left_id: &str,
1229        right_id: &str,
1230        result_id: &str,
1231    ) -> Result<LinearAlgebraResult<Matrix>, LinearAlgebraError> {
1232        let start_time = std::time::Instant::now();
1233
1234        let left = self.matrix_storage.get_matrix(left_id)?;
1235        let right = self.matrix_storage.get_matrix(right_id)?;
1236        if left.cols != right.rows {
1237            return Err(LinearAlgebraError::InvalidDimensions(
1238                "Matrix dimensions incompatible for multiplication".to_string(),
1239            ));
1240        }
1241        let (m, k, n) = (left.rows, left.cols, right.cols);
1242
1243        // Fixed-point encoding so REAL-valued (not just integer) matrices get a ZK proof.
1244        // Each entry is scaled by FIXED_POINT_SCALE and rounded to a field integer, so the
1245        // circuit proves the exact integer identity Σ a'·b' = C' where a' = round(a·S),
1246        // b' = round(b·S). The product is then scaled by S², so the real result is C'/S².
1247        // Precision is ~1/S; for integer matrices the encoding is exact (S·int is integer).
1248        const FIXED_POINT_SCALE: f64 = 1_000_000.0;
1249        let a_int: Vec<i128> = left
1250            .data
1251            .iter()
1252            .map(|v| (v * FIXED_POINT_SCALE).round() as i128)
1253            .collect();
1254        let b_int: Vec<i128> = right
1255            .data
1256            .iter()
1257            .map(|v| (v * FIXED_POINT_SCALE).round() as i128)
1258            .collect();
1259
1260        // Build the real circuit, prove, and verify in zero knowledge.
1261        let (verified, c_int) = self
1262            .privacy_engine
1263            .zk_proofs
1264            .lock()
1265            .unwrap()
1266            .prove_matrix_multiply(m, k, n, &a_int, &b_int)
1267            .map_err(|e| LinearAlgebraError::PrivacyError(format!("{:?}", e)))?;
1268
1269        if !verified {
1270            return Err(LinearAlgebraError::PrivacyError(
1271                "zero-knowledge proof of A·B = C failed to verify".to_string(),
1272            ));
1273        }
1274
1275        // Recover the real-valued product from the attested fixed-point integers (÷ S²).
1276        let scale_sq = FIXED_POINT_SCALE * FIXED_POINT_SCALE;
1277        let result_data: Vec<f64> = c_int.iter().map(|&v| v as f64 / scale_sq).collect();
1278        let result =
1279            self.create_matrix(result_id.to_string(), m, n, left.data_type, result_data)?;
1280
1281        let execution_time = start_time.elapsed().as_millis() as u64;
1282        self.performance_monitor
1283            .record_operation("private_matrix_multiply", execution_time, 0);
1284
1285        Ok(LinearAlgebraResult {
1286            result,
1287            execution_time,
1288            memory_usage: 0,
1289            operations_used: vec![
1290                "private_matrix_multiply".to_string(),
1291                "groth16_zk_proof".to_string(),
1292            ],
1293            privacy_preserved: true,
1294        })
1295    }
1296
1297    /// Analyze a matrix: detect structural patterns and return optimization hints.
1298    /// Uses the MatrixAnalyzer to detect diagonal, triangular, symmetric, sparse,
1299    /// banded, block-diagonal, Toeplitz, orthogonal, circulant, Hankel, and
1300    /// positive-definite patterns.
1301    pub fn analyze_matrix(
1302        &mut self,
1303        matrix_id: &str,
1304    ) -> Result<MatrixAnalysis, LinearAlgebraError> {
1305        let matrix = self.matrix_storage.get_matrix(matrix_id)?;
1306        self.optimization_engine.analyzer.analyze_matrix(&matrix)
1307    }
1308
1309    /// Get performance statistics
1310    pub fn get_performance_stats(&self) -> SystemMetrics {
1311        self.performance_monitor.get_system_metrics()
1312    }
1313
1314    /// Get a human-readable performance summary
1315    pub fn performance_summary(&self) -> String {
1316        self.performance_monitor.summary()
1317    }
1318
1319    /// Get the cache hit rate (0.0 to 1.0)
1320    pub fn cache_hit_rate(&self) -> f64 {
1321        self.matrix_storage.cache.hit_rate()
1322    }
1323
1324    /// Get the current cache size in bytes
1325    pub fn cache_size(&self) -> usize {
1326        self.matrix_storage.cache.cache_size()
1327    }
1328
1329    /// List all matrices
1330    pub fn list_matrices(&self) -> Vec<String> {
1331        self.matrix_storage.list_matrices()
1332    }
1333
1334    /// Get matrix information
1335    pub fn get_matrix_info(&self, matrix_id: &str) -> Option<MatrixMetadata> {
1336        self.matrix_storage.get_matrix_metadata(matrix_id)
1337    }
1338}
1339
1340// ════════════════════════════════════════════════════════════════════════════════
1341//  Polynomial algebra (ALGEBRA_MANIFOLD_PLAN.md Phase 1)
1342//  Numeric root finding: quadratics in closed form (stable), general degree via the
1343//  dependency-free Durand–Kerner (Weierstrass) iteration. f64, dynamic degree.
1344// ════════════════════════════════════════════════════════════════════════════════
1345
1346/// Complex / quadratic / polynomial-root algebra now lives in the engine
1347/// (`solvers::polynomial`); re-exported here so the silo's API is unchanged.
1348pub use crate::solvers::polynomial::{Complex, QuadraticRoots};
1349
1350/// Solve `a·x² + b·x + c = 0` over the reals — thin facade over the engine
1351/// `solvers::polynomial::solve_quadratic` (maps the engine error to this lib's type).
1352pub fn solve_quadratic(a: f64, b: f64, c: f64) -> Result<QuadraticRoots, LinearAlgebraError> {
1353    crate::solvers::polynomial::solve_quadratic(a, b, c).map_err(|_| {
1354        LinearAlgebraError::ComputationError("invalid quadratic coefficients".to_string())
1355    })
1356}
1357
1358/// Find all complex roots of a real polynomial — thin facade over the engine
1359/// `solvers::polynomial::polynomial_roots` (Durand–Kerner).
1360pub fn polynomial_roots(coeffs: &[f64]) -> Result<Vec<Complex>, LinearAlgebraError> {
1361    crate::solvers::polynomial::polynomial_roots(coeffs)
1362        .map_err(|_| LinearAlgebraError::ComputationError("invalid polynomial".to_string()))
1363}
1364
1365// ════════════════════════════════════════════════════════════════════════════════
1366//  Determinant + eigenvalues (ALGEBRA_MANIFOLD_PLAN.md Phase 2)
1367//  Dependency-free: determinant via LU (partial pivoting); symmetric eigensystem via
1368//  cyclic Jacobi rotations. Inputs are row-major n×n `f64` slices.
1369// ════════════════════════════════════════════════════════════════════════════════
1370
1371/// LU decomposition with partial pivoting (`P·A = L·U`). The canonical dynamic LU now
1372/// lives in the engine (`solvers::linear_algebra::lu`); re-exported here so the silo's
1373/// existing API surface (and `Lu::determinant`) is unchanged.
1374pub use crate::solvers::linear_algebra::lu::Lu;
1375
1376/// LU-decompose a row-major `n×n` matrix with partial pivoting — thin facade over the
1377/// engine `lu_decompose` (maps the engine error to this lib's error type).
1378pub fn lu_decompose(n: usize, data: &[f64]) -> Result<Lu, LinearAlgebraError> {
1379    crate::solvers::linear_algebra::lu::lu_decompose(n, data).map_err(|_| {
1380        LinearAlgebraError::InvalidDimensions(
1381            "lu_decompose expects a non-empty square n×n matrix".to_string(),
1382        )
1383    })
1384}
1385
1386/// Determinant of a row-major `n×n` matrix via LU decomposition — thin facade over the
1387/// engine `determinant`. O(n³), numerically robust; returns 0.0 for a singular matrix.
1388pub fn determinant(n: usize, data: &[f64]) -> Result<f64, LinearAlgebraError> {
1389    crate::solvers::linear_algebra::lu::determinant(n, data).map_err(|_| {
1390        LinearAlgebraError::InvalidDimensions(
1391            "determinant expects a non-empty square n×n matrix".to_string(),
1392        )
1393    })
1394}
1395
1396/// Eigen-decomposition of a SYMMETRIC row-major `n×n` matrix via cyclic Jacobi
1397/// rotations. Returns `(eigenvalues, eigenvectors)` where `eigenvectors` is a row-major
1398/// `n×n` matrix whose COLUMN `j` is the unit eigenvector for `eigenvalues[j]`.
1399/// Errors if the input is not (within tolerance) symmetric.
1400pub fn eigen_symmetric(n: usize, data: &[f64]) -> Result<(Vec<f64>, Vec<f64>), LinearAlgebraError> {
1401    if n == 0 || data.len() != n * n {
1402        return Err(LinearAlgebraError::InvalidDimensions(
1403            "eigen_symmetric expects a non-empty square n×n matrix".to_string(),
1404        ));
1405    }
1406    // Composition boundary: marshal into caller-owned buffers and call the engine's
1407    // canonical symmetric eigensolver (replaces an inline cyclic-Jacobi duplicate).
1408    let mut a = data.to_vec();
1409    let mut v = vec![0.0_f64; n * n];
1410    crate::solvers::linear_algebra::eigen::symmetric_eigen(n, &mut a, &mut v).map_err(
1411        |e| match e {
1412            crate::solvers::SolversError::InvalidParameters => {
1413                LinearAlgebraError::ComputationError(
1414                    "eigen_symmetric requires a symmetric matrix".to_string(),
1415                )
1416            }
1417            _ => LinearAlgebraError::InvalidDimensions(
1418                "eigen_symmetric expects a non-empty square n×n matrix".to_string(),
1419            ),
1420        },
1421    )?;
1422    // Eigenvalues are the diagonal of the rotated matrix; v's column j is its eigenvector.
1423    let eigenvalues: Vec<f64> = (0..n).map(|i| a[i * n + i]).collect();
1424    Ok((eigenvalues, v))
1425}
1426
1427/// Characteristic polynomial — thin facade over the engine
1428/// `solvers::linear_algebra::spectral::characteristic_polynomial` (Faddeev–LeVerrier).
1429pub fn characteristic_polynomial(n: usize, data: &[f64]) -> Result<Vec<f64>, LinearAlgebraError> {
1430    crate::solvers::linear_algebra::spectral::characteristic_polynomial(n, data).map_err(|_| {
1431        LinearAlgebraError::InvalidDimensions(
1432            "characteristic_polynomial expects a non-empty square n×n matrix".to_string(),
1433        )
1434    })
1435}
1436
1437/// General (non-symmetric) eigenvalues — thin facade over the engine
1438/// `solvers::linear_algebra::spectral::eigenvalues_general`.
1439pub fn eigenvalues_general(n: usize, data: &[f64]) -> Result<Vec<Complex>, LinearAlgebraError> {
1440    crate::solvers::linear_algebra::spectral::eigenvalues_general(n, data)
1441        .map_err(|_| LinearAlgebraError::ComputationError("eigenvalues_general failed".to_string()))
1442}
1443
1444/// SVD `A = U·Σ·Vᵀ` — the canonical implementation now lives in the engine
1445/// (`solvers::linear_algebra::svd`); re-exported here so the silo's API is unchanged.
1446pub use crate::solvers::linear_algebra::svd::Svd;
1447
1448/// Singular value decomposition of a row-major `m×n` matrix — thin facade over the
1449/// engine `svd` (maps the engine error to this lib's error type). Singular values are
1450/// returned in descending order.
1451pub fn svd(m: usize, n: usize, data: &[f64]) -> Result<Svd, LinearAlgebraError> {
1452    crate::solvers::linear_algebra::svd::svd(m, n, data).map_err(|_| {
1453        LinearAlgebraError::InvalidDimensions("svd expects a non-empty m×n matrix".to_string())
1454    })
1455}