1use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
6
7use super::types::{
8 BoundedOp, Distribution, FredholmOperatorData, InterpolationData, InterpolationMethod,
9 L2Sequence, LuDecomposition, QrDecomposition, RnVector, SobolevSpaceData, WeakConvergenceData,
10};
11
12pub fn app(f: Expr, a: Expr) -> Expr {
13 Expr::App(Box::new(f), Box::new(a))
14}
15pub fn app2(f: Expr, a: Expr, b: Expr) -> Expr {
16 app(app(f, a), b)
17}
18pub fn cst(s: &str) -> Expr {
19 Expr::Const(Name::str(s), vec![])
20}
21pub fn prop() -> Expr {
22 Expr::Sort(Level::zero())
23}
24pub fn type0() -> Expr {
25 Expr::Sort(Level::succ(Level::zero()))
26}
27pub fn pi(bi: BinderInfo, name: &str, dom: Expr, body: Expr) -> Expr {
28 Expr::Pi(bi, Name::str(name), Box::new(dom), Box::new(body))
29}
30pub fn arrow(a: Expr, b: Expr) -> Expr {
31 pi(BinderInfo::Default, "_", a, b)
32}
33pub fn bvar(n: u32) -> Expr {
34 Expr::BVar(n)
35}
36pub fn nat_ty() -> Expr {
37 cst("Nat")
38}
39pub fn real_ty() -> Expr {
40 cst("Real")
41}
42pub fn impl_pi(name: &str, dom: Expr, body: Expr) -> Expr {
43 pi(BinderInfo::Implicit, name, dom, body)
44}
45pub fn _use_helpers() {
46 let _ = (
47 app(cst("a"), cst("b")),
48 app2(cst("a"), cst("b"), cst("c")),
49 prop(),
50 type0(),
51 arrow(prop(), prop()),
52 bvar(0),
53 nat_ty(),
54 real_ty(),
55 impl_pi("x", type0(), bvar(0)),
56 );
57}
58pub fn normed_space_ty() -> Expr {
59 arrow(type0(), prop())
60}
61pub fn banach_space_ty() -> Expr {
62 arrow(type0(), prop())
63}
64pub fn hilbert_space_ty() -> Expr {
65 arrow(type0(), prop())
66}
67pub fn bounded_linear_op_ty() -> Expr {
68 pi(
69 BinderInfo::Default,
70 "X",
71 type0(),
72 pi(BinderInfo::Default, "Y", type0(), type0()),
73 )
74}
75pub fn dual_space_ty() -> Expr {
76 arrow(type0(), type0())
77}
78pub fn spectrum_ty() -> Expr {
79 pi(
80 BinderInfo::Default,
81 "X",
82 type0(),
83 arrow(
84 app2(cst("BoundedLinearOp"), bvar(0), bvar(0)),
85 app(cst("Set"), real_ty()),
86 ),
87 )
88}
89pub fn fredholm_operator_ty() -> Expr {
90 pi(
91 BinderInfo::Default,
92 "X",
93 type0(),
94 pi(BinderInfo::Default, "Y", type0(), prop()),
95 )
96}
97pub fn fredholm_index_ty() -> Expr {
98 pi(
99 BinderInfo::Default,
100 "X",
101 type0(),
102 pi(
103 BinderInfo::Default,
104 "Y",
105 type0(),
106 arrow(app2(cst("BoundedLinearOp"), bvar(1), bvar(0)), cst("Int")),
107 ),
108 )
109}
110pub fn compact_operator_ty() -> Expr {
111 pi(
112 BinderInfo::Default,
113 "X",
114 type0(),
115 pi(
116 BinderInfo::Default,
117 "Y",
118 type0(),
119 arrow(app2(cst("BoundedLinearOp"), bvar(1), bvar(0)), prop()),
120 ),
121 )
122}
123pub fn hahn_banach_ty() -> Expr {
124 pi(
125 BinderInfo::Default,
126 "X",
127 type0(),
128 arrow(app(cst("NormedSpace"), bvar(0)), prop()),
129 )
130}
131pub fn open_mapping_ty() -> Expr {
132 pi(
133 BinderInfo::Default,
134 "X",
135 type0(),
136 pi(
137 BinderInfo::Default,
138 "Y",
139 type0(),
140 arrow(
141 app2(cst("BoundedLinearOp"), bvar(1), bvar(0)),
142 arrow(
143 app(cst("BanachSpace"), bvar(2)),
144 arrow(app(cst("BanachSpace"), bvar(1)), prop()),
145 ),
146 ),
147 ),
148 )
149}
150pub fn closed_graph_ty() -> Expr {
151 pi(
152 BinderInfo::Default,
153 "X",
154 type0(),
155 pi(
156 BinderInfo::Default,
157 "Y",
158 type0(),
159 arrow(
160 arrow(bvar(1), bvar(0)),
161 arrow(app(cst("BanachSpace"), bvar(2)), prop()),
162 ),
163 ),
164 )
165}
166pub fn uniform_boundedness_ty() -> Expr {
167 pi(
168 BinderInfo::Default,
169 "X",
170 type0(),
171 pi(
172 BinderInfo::Default,
173 "Y",
174 type0(),
175 arrow(
176 app(cst("BanachSpace"), bvar(1)),
177 arrow(app(cst("BanachSpace"), bvar(0)), prop()),
178 ),
179 ),
180 )
181}
182pub fn riesz_representation_ty() -> Expr {
183 pi(
184 BinderInfo::Default,
185 "H",
186 type0(),
187 arrow(app(cst("HilbertSpace"), bvar(0)), prop()),
188 )
189}
190pub fn spectral_theorem_ty() -> Expr {
191 pi(
192 BinderInfo::Default,
193 "H",
194 type0(),
195 arrow(
196 app(cst("HilbertSpace"), bvar(0)),
197 arrow(app2(cst("CompactSelfAdjoint"), bvar(1), bvar(0)), prop()),
198 ),
199 )
200}
201pub fn fredholm_alternative_ty() -> Expr {
202 pi(
203 BinderInfo::Default,
204 "X",
205 type0(),
206 arrow(
207 app(cst("BanachSpace"), bvar(0)),
208 arrow(app2(cst("BoundedLinearOp"), bvar(1), bvar(1)), prop()),
209 ),
210 )
211}
212pub fn banach_fixed_point_ty() -> Expr {
213 pi(
214 BinderInfo::Default,
215 "X",
216 type0(),
217 arrow(
218 app(cst("BanachSpace"), bvar(0)),
219 arrow(arrow(bvar(1), bvar(1)), prop()),
220 ),
221 )
222}
223pub fn build_functional_analysis_env(env: &mut Environment) {
224 let axioms: &[(&str, Expr)] = &[
225 ("NormedSpace", normed_space_ty()),
226 ("BanachSpace", banach_space_ty()),
227 ("HilbertSpace", hilbert_space_ty()),
228 ("BoundedLinearOp", bounded_linear_op_ty()),
229 ("DualSpace", dual_space_ty()),
230 ("Spectrum", spectrum_ty()),
231 ("FredholmOperator", fredholm_operator_ty()),
232 ("FredholmIndex", fredholm_index_ty()),
233 ("CompactOperator", compact_operator_ty()),
234 ("Set", arrow(type0(), type0())),
235 (
236 "CompactSelfAdjoint",
237 pi(BinderInfo::Default, "H", type0(), arrow(type0(), type0())),
238 ),
239 ("Int", type0()),
240 ("hahn_banach", hahn_banach_ty()),
241 ("open_mapping", open_mapping_ty()),
242 ("closed_graph", closed_graph_ty()),
243 ("uniform_boundedness", uniform_boundedness_ty()),
244 ("riesz_representation", riesz_representation_ty()),
245 ("spectral_theorem", spectral_theorem_ty()),
246 ("fredholm_alternative", fredholm_alternative_ty()),
247 ("banach_fixed_point", banach_fixed_point_ty()),
248 ];
249 for (name, ty) in axioms {
250 env.add(Declaration::Axiom {
251 name: Name::str(*name),
252 univ_params: vec![],
253 ty: ty.clone(),
254 })
255 .ok();
256 }
257}
258pub fn gram_schmidt(vectors: &[RnVector]) -> Vec<RnVector> {
259 let mut orthonormal = Vec::new();
260 for v in vectors {
261 let mut u = v.clone();
262 for q in &orthonormal {
263 let proj = u.project_onto(q);
264 u = u.sub(&proj);
265 }
266 let n = u.norm();
267 if n > 1e-12 {
268 orthonormal.push(u.scale(1.0 / n));
269 }
270 }
271 orthonormal
272}
273pub fn is_orthonormal(vectors: &[RnVector], tol: f64) -> bool {
274 for (i, vi) in vectors.iter().enumerate() {
275 if (vi.norm() - 1.0).abs() > tol {
276 return false;
277 }
278 for vj in vectors.iter().skip(i + 1) {
279 if vi.inner(vj).abs() > tol {
280 return false;
281 }
282 }
283 }
284 true
285}
286pub fn spans_full_space(vectors: &[RnVector], dim: usize) -> bool {
287 gram_schmidt(vectors).len() == dim
288}
289pub fn conjugate_gradient(a: &BoundedOp, b: &RnVector, max_iter: usize, tol: f64) -> RnVector {
290 let n = b.dim();
291 let mut x = RnVector::zero(n);
292 let mut r = b.sub(&a.apply(&x));
293 let mut p = r.clone();
294 let mut rs_old = r.inner(&r);
295 for _ in 0..max_iter {
296 if rs_old.sqrt() < tol {
297 break;
298 }
299 let ap = a.apply(&p);
300 let alpha = rs_old / p.inner(&ap);
301 x = x.add(&p.scale(alpha));
302 r = r.sub(&ap.scale(alpha));
303 let rs_new = r.inner(&r);
304 if rs_new.sqrt() < tol {
305 break;
306 }
307 p = r.add(&p.scale(rs_new / rs_old));
308 rs_old = rs_new;
309 }
310 x
311}
312pub fn l2_norm_approx(f: &dyn Fn(f64) -> f64, a: f64, b: f64, n: usize) -> f64 {
313 if n == 0 {
314 return 0.0;
315 }
316 let h = (b - a) / n as f64;
317 let mut sum = 0.5 * f(a).powi(2) + 0.5 * f(b).powi(2);
318 for i in 1..n {
319 sum += f(a + i as f64 * h).powi(2);
320 }
321 (sum * h).sqrt()
322}
323pub fn l2_inner_approx(
324 f: &dyn Fn(f64) -> f64,
325 g: &dyn Fn(f64) -> f64,
326 a: f64,
327 b: f64,
328 n: usize,
329) -> f64 {
330 if n == 0 {
331 return 0.0;
332 }
333 let h = (b - a) / n as f64;
334 let mut sum = 0.5 * f(a) * g(a) + 0.5 * f(b) * g(b);
335 for i in 1..n {
336 let x = a + i as f64 * h;
337 sum += f(x) * g(x);
338 }
339 sum * h
340}
341pub fn sup_norm_approx(f: &dyn Fn(f64) -> f64, a: f64, b: f64, n: usize) -> f64 {
342 if n == 0 {
343 return f(a).abs();
344 }
345 let h = (b - a) / n as f64;
346 let mut max_val = 0.0_f64;
347 for i in 0..=n {
348 max_val = max_val.max(f(a + i as f64 * h).abs());
349 }
350 max_val
351}
352pub fn banach_fixed_point_iter(
353 t: &dyn Fn(f64) -> f64,
354 x0: f64,
355 max_iter: usize,
356 tol: f64,
357) -> (f64, usize) {
358 let mut x = x0;
359 for i in 0..max_iter {
360 let x_next = t(x);
361 if (x_next - x).abs() < tol {
362 return (x_next, i + 1);
363 }
364 x = x_next;
365 }
366 (x, max_iter)
367}
368pub fn fourier_cosine_coefficients(
369 f: &dyn Fn(f64) -> f64,
370 period: f64,
371 num_coeffs: usize,
372 qp: usize,
373) -> Vec<f64> {
374 let h = period / qp as f64;
375 let mut coeffs = Vec::with_capacity(num_coeffs);
376 for k in 0..num_coeffs {
377 let mut sum =
378 0.5 * f(0.0) + 0.5 * f(period) * (2.0 * std::f64::consts::PI * k as f64).cos();
379 for i in 1..qp {
380 let x = i as f64 * h;
381 sum += f(x) * (2.0 * std::f64::consts::PI * k as f64 * x / period).cos();
382 }
383 let factor = if k == 0 { 1.0 / period } else { 2.0 / period };
384 coeffs.push(sum * h * factor);
385 }
386 coeffs
387}
388pub fn eval_fourier_cosine(coeffs: &[f64], period: f64, x: f64) -> f64 {
389 coeffs
390 .iter()
391 .enumerate()
392 .map(|(k, &c)| c * (2.0 * std::f64::consts::PI * k as f64 * x / period).cos())
393 .sum()
394}
395pub fn qr_decomposition(a: &BoundedOp) -> Option<QrDecomposition> {
397 let (m, n) = (a.range_dim, a.domain_dim);
398 if m == 0 || n == 0 {
399 return None;
400 }
401 let mut cols: Vec<RnVector> = (0..n)
402 .map(|j| RnVector::new((0..m).map(|i| a.matrix[i][j]).collect()))
403 .collect();
404 let mut q_cols: Vec<RnVector> = Vec::new();
405 let mut r_matrix = vec![vec![0.0; n]; n.min(m)];
406 for j in 0..n.min(m) {
407 for k in 0..q_cols.len() {
408 let rk = cols[j].inner(&q_cols[k]);
409 r_matrix[k][j] = rk;
410 cols[j] = cols[j].sub(&q_cols[k].scale(rk));
411 }
412 let nrm = cols[j].norm();
413 if nrm < 1e-14 {
414 r_matrix[j][j] = 0.0;
415 q_cols.push(RnVector::zero(m));
416 } else {
417 r_matrix[j][j] = nrm;
418 q_cols.push(cols[j].scale(1.0 / nrm));
419 }
420 }
421 let q_n = q_cols.len();
422 let q_matrix: Vec<Vec<f64>> = (0..m)
423 .map(|i| (0..q_n).map(|j| q_cols[j].components[i]).collect())
424 .collect();
425 let q = BoundedOp {
426 matrix: q_matrix,
427 domain_dim: q_n,
428 range_dim: m,
429 };
430 let r = BoundedOp {
431 matrix: r_matrix,
432 domain_dim: n,
433 range_dim: q_n,
434 };
435 Some(QrDecomposition { q, r })
436}
437pub fn lu_decomposition(a: &BoundedOp) -> Option<LuDecomposition> {
439 let n = a.domain_dim;
440 if n != a.range_dim || n == 0 {
441 return None;
442 }
443 let mut u = a.matrix.clone();
444 let mut l = vec![vec![0.0; n]; n];
445 let mut perm: Vec<usize> = (0..n).collect();
446 for k in 0..n {
447 let mut max_val = u[k][k].abs();
448 let mut max_row = k;
449 for row in (k + 1)..n {
450 if u[row][k].abs() > max_val {
451 max_val = u[row][k].abs();
452 max_row = row;
453 }
454 }
455 if max_val < 1e-15 {
456 return None;
457 }
458 if max_row != k {
459 u.swap(k, max_row);
460 l.swap(k, max_row);
461 perm.swap(k, max_row);
462 }
463 l[k][k] = 1.0;
464 for i in (k + 1)..n {
465 let factor = u[i][k] / u[k][k];
466 l[i][k] = factor;
467 for j in k..n {
468 let val = u[k][j];
469 u[i][j] -= factor * val;
470 }
471 }
472 }
473 Some(LuDecomposition {
474 l: BoundedOp {
475 matrix: l,
476 domain_dim: n,
477 range_dim: n,
478 },
479 u: BoundedOp {
480 matrix: u,
481 domain_dim: n,
482 range_dim: n,
483 },
484 permutation: perm,
485 })
486}
487pub fn lu_solve(lu: &LuDecomposition, b: &RnVector) -> RnVector {
489 let n = lu.l.domain_dim;
490 let mut pb = vec![0.0; n];
491 for (i, &pi) in lu.permutation.iter().enumerate() {
492 pb[i] = b.components[pi];
493 }
494 let mut y = vec![0.0; n];
495 for i in 0..n {
496 let mut sum = pb[i];
497 for j in 0..i {
498 sum -= lu.l.matrix[i][j] * y[j];
499 }
500 y[i] = sum;
501 }
502 let mut x = vec![0.0; n];
503 for i in (0..n).rev() {
504 let mut sum = y[i];
505 for j in (i + 1)..n {
506 sum -= lu.u.matrix[i][j] * x[j];
507 }
508 if lu.u.matrix[i][i].abs() < 1e-15 {
509 x[i] = 0.0;
510 } else {
511 x[i] = sum / lu.u.matrix[i][i];
512 }
513 }
514 RnVector::new(x)
515}
516pub fn singular_values(a: &BoundedOp, max_iter: usize) -> Vec<f64> {
518 let ata = match a.transpose().compose(a) {
519 Some(m) => m,
520 None => return vec![],
521 };
522 let n = ata.domain_dim;
523 if n == 0 {
524 return vec![];
525 }
526 let mut current = ata.clone();
527 for _ in 0..max_iter {
528 let qr = match qr_decomposition(¤t) {
529 Some(qr) => qr,
530 None => break,
531 };
532 current = match qr.r.compose(&qr.q) {
533 Some(m) => m,
534 None => break,
535 };
536 }
537 let mut eigenvalues: Vec<f64> = (0..n).map(|i| current.matrix[i][i].max(0.0)).collect();
538 eigenvalues.sort_by(|a, b| b.partial_cmp(a).unwrap_or(std::cmp::Ordering::Equal));
539 eigenvalues.iter().map(|&ev| ev.sqrt()).collect()
540}
541pub fn condition_number(a: &BoundedOp, max_iter: usize) -> f64 {
543 let svs = singular_values(a, max_iter);
544 if svs.is_empty() {
545 return f64::INFINITY;
546 }
547 let max_sv = svs[0];
548 let min_sv = *svs
549 .last()
550 .expect("svs is non-empty: checked by early return");
551 if min_sv < 1e-15 {
552 f64::INFINITY
553 } else {
554 max_sv / min_sv
555 }
556}
557pub fn spectral_radius(a: &BoundedOp, iterations: usize) -> f64 {
559 if a.domain_dim != a.range_dim || a.domain_dim == 0 {
560 return 0.0;
561 }
562 let n = a.domain_dim;
563 let mut v = RnVector::new(vec![1.0; n]);
564 let nrm = v.norm();
565 if nrm > 0.0 {
566 v = v.scale(1.0 / nrm);
567 }
568 let mut radius = 0.0;
569 for _ in 0..iterations {
570 let w = a.apply(&v);
571 let nrm = w.norm();
572 if nrm < 1e-15 {
573 return 0.0;
574 }
575 radius = nrm;
576 v = w.scale(1.0 / nrm);
577 }
578 radius
579}
580pub fn h1_norm_approx(f: &dyn Fn(f64) -> f64, a: f64, b: f64, n: usize) -> f64 {
583 let l2_sq = l2_norm_approx(f, a, b, n).powi(2);
584 if n < 2 {
585 return l2_sq.sqrt();
586 }
587 let h = (b - a) / n as f64;
588 let mut deriv_sq_sum = 0.0;
589 for i in 0..n {
590 let x = a + i as f64 * h;
591 let df = (f(x + h) - f(x)) / h;
592 deriv_sq_sum += df * df;
593 }
594 let deriv_l2_sq = deriv_sq_sum * h;
595 (l2_sq + deriv_l2_sq).sqrt()
596}
597pub fn h2_norm_approx(f: &dyn Fn(f64) -> f64, a: f64, b: f64, n: usize) -> f64 {
599 let h1_sq = h1_norm_approx(f, a, b, n).powi(2);
600 if n < 3 {
601 return h1_sq.sqrt();
602 }
603 let h = (b - a) / n as f64;
604 let mut d2_sq_sum = 0.0;
605 for i in 1..(n - 1) {
606 let x = a + i as f64 * h;
607 let d2f = (f(x + h) - 2.0 * f(x) + f(x - h)) / (h * h);
608 d2_sq_sum += d2f * d2f;
609 }
610 let d2_l2_sq = d2_sq_sum * h;
611 (h1_sq + d2_l2_sq).sqrt()
612}
613pub fn simpson_integrate(f: &dyn Fn(f64) -> f64, a: f64, b: f64, n: usize) -> f64 {
615 let n = if n % 2 == 0 { n } else { n + 1 };
616 if n == 0 {
617 return 0.0;
618 }
619 let h = (b - a) / n as f64;
620 let mut sum = f(a) + f(b);
621 for i in 1..n {
622 let x = a + i as f64 * h;
623 sum += if i % 2 == 0 { 2.0 * f(x) } else { 4.0 * f(x) };
624 }
625 sum * h / 3.0
626}
627pub fn gauss_legendre_3(f: &dyn Fn(f64) -> f64, a: f64, b: f64) -> f64 {
629 let mid = (a + b) / 2.0;
630 let half = (b - a) / 2.0;
631 let nodes = [-0.7745966692414834, 0.0, 0.7745966692414834];
632 let weights = [0.5555555555555556, 0.8888888888888888, 0.5555555555555556];
633 let mut sum = 0.0;
634 for i in 0..3 {
635 sum += weights[i] * f(mid + half * nodes[i]);
636 }
637 sum * half
638}
639pub fn gauss_legendre_composite(f: &dyn Fn(f64) -> f64, a: f64, b: f64, panels: usize) -> f64 {
641 if panels == 0 {
642 return 0.0;
643 }
644 let h = (b - a) / panels as f64;
645 let mut total = 0.0;
646 for i in 0..panels {
647 let lo = a + i as f64 * h;
648 let hi = lo + h;
649 total += gauss_legendre_3(f, lo, hi);
650 }
651 total
652}
653pub fn matrix_exponential(a: &BoundedOp, terms: usize) -> BoundedOp {
655 let n = a.domain_dim;
656 if n != a.range_dim {
657 return BoundedOp::zero_op(n, n);
658 }
659 let mut result = BoundedOp::identity(n);
660 let mut term = BoundedOp::identity(n);
661 for k in 1..terms {
662 term = match term.compose(a) {
663 Some(m) => m.scalar_mul(1.0 / k as f64),
664 None => break,
665 };
666 result = match result.op_add(&term) {
667 Some(m) => m,
668 None => break,
669 };
670 }
671 result
672}
673pub fn commutator(a: &BoundedOp, b: &BoundedOp) -> Option<BoundedOp> {
675 let ab = a.compose(b)?;
676 let ba = b.compose(a)?;
677 let neg_ba = ba.scalar_mul(-1.0);
678 ab.op_add(&neg_ba)
679}
680pub fn anti_commutator(a: &BoundedOp, b: &BoundedOp) -> Option<BoundedOp> {
682 let ab = a.compose(b)?;
683 let ba = b.compose(a)?;
684 ab.op_add(&ba)
685}
686pub fn fredholm_index_numerical(k: &BoundedOp) -> (usize, usize, i64) {
689 let n = k.domain_dim;
690 if n != k.range_dim {
691 return (0, 0, 0);
692 }
693 let i_minus_k = match BoundedOp::identity(n).op_add(&k.scalar_mul(-1.0)) {
694 Some(m) => m,
695 None => return (0, 0, 0),
696 };
697 let kernel_dim = i_minus_k.nullity();
698 let cokernel_dim = i_minus_k.transpose().nullity();
699 let index = kernel_dim as i64 - cokernel_dim as i64;
700 (kernel_dim, cokernel_dim, index)
701}
702pub fn jacobi_iteration(a: &BoundedOp, b: &RnVector, max_iter: usize, tol: f64) -> RnVector {
704 let n = a.domain_dim;
705 if n != a.range_dim || n != b.dim() {
706 return RnVector::zero(n);
707 }
708 let mut x = RnVector::zero(n);
709 for _ in 0..max_iter {
710 let mut x_new = vec![0.0; n];
711 for i in 0..n {
712 if a.matrix[i][i].abs() < 1e-15 {
713 x_new[i] = x.components[i];
714 continue;
715 }
716 let mut sum = b.components[i];
717 for j in 0..n {
718 if j != i {
719 sum -= a.matrix[i][j] * x.components[j];
720 }
721 }
722 x_new[i] = sum / a.matrix[i][i];
723 }
724 let new_x = RnVector::new(x_new);
725 if new_x.sub(&x).norm() < tol {
726 return new_x;
727 }
728 x = new_x;
729 }
730 x
731}
732pub fn gauss_seidel_iteration(a: &BoundedOp, b: &RnVector, max_iter: usize, tol: f64) -> RnVector {
734 let n = a.domain_dim;
735 if n != a.range_dim || n != b.dim() {
736 return RnVector::zero(n);
737 }
738 let mut x = vec![0.0; n];
739 for _ in 0..max_iter {
740 let old_x = x.clone();
741 for i in 0..n {
742 if a.matrix[i][i].abs() < 1e-15 {
743 continue;
744 }
745 let mut sum = b.components[i];
746 for j in 0..n {
747 if j != i {
748 sum -= a.matrix[i][j] * x[j];
749 }
750 }
751 x[i] = sum / a.matrix[i][i];
752 }
753 let diff: f64 = x
754 .iter()
755 .zip(old_x.iter())
756 .map(|(a, b)| (a - b).powi(2))
757 .sum::<f64>()
758 .sqrt();
759 if diff < tol {
760 break;
761 }
762 }
763 RnVector::new(x)
764}
765pub fn fourier_sine_coefficients(
767 f: &dyn Fn(f64) -> f64,
768 period: f64,
769 num_coeffs: usize,
770 qp: usize,
771) -> Vec<f64> {
772 let h = period / qp as f64;
773 let mut coeffs = Vec::with_capacity(num_coeffs);
774 for k in 0..num_coeffs {
775 let mut sum = 0.0;
776 for i in 0..=qp {
777 let x = i as f64 * h;
778 let w = if i == 0 || i == qp { 0.5 } else { 1.0 };
779 sum += w * f(x) * (std::f64::consts::PI * (k + 1) as f64 * x / period).sin();
780 }
781 coeffs.push(sum * h * 2.0 / period);
782 }
783 coeffs
784}
785pub fn eval_fourier_sine(coeffs: &[f64], period: f64, x: f64) -> f64 {
787 coeffs
788 .iter()
789 .enumerate()
790 .map(|(k, &c)| c * (std::f64::consts::PI * (k + 1) as f64 * x / period).sin())
791 .sum()
792}
793pub fn chebyshev_t(n: usize, x: f64) -> f64 {
795 if n == 0 {
796 return 1.0;
797 }
798 if n == 1 {
799 return x;
800 }
801 let (mut t0, mut t1) = (1.0, x);
802 for _ in 2..=n {
803 let t2 = 2.0 * x * t1 - t0;
804 t0 = t1;
805 t1 = t2;
806 }
807 t1
808}
809pub fn chebyshev_nodes(n: usize, a: f64, b: f64) -> Vec<f64> {
811 (0..n)
812 .map(|k| {
813 let theta = std::f64::consts::PI * (2 * k + 1) as f64 / (2 * n) as f64;
814 (a + b) / 2.0 + (b - a) / 2.0 * theta.cos()
815 })
816 .collect()
817}
818pub fn chebyshev_coefficients(f: &dyn Fn(f64) -> f64, n: usize, a: f64, b: f64) -> Vec<f64> {
820 let nodes = chebyshev_nodes(n, a, b);
821 let values: Vec<f64> = nodes.iter().map(|&x| f(x)).collect();
822 let mut coeffs = vec![0.0; n];
823 for j in 0..n {
824 let mut sum = 0.0;
825 for (k, &val) in values.iter().enumerate() {
826 let xk = 2.0 * (nodes[k] - a) / (b - a) - 1.0;
827 sum += val * chebyshev_t(j, xk);
828 }
829 coeffs[j] = sum
830 * if j == 0 {
831 1.0 / n as f64
832 } else {
833 2.0 / n as f64
834 };
835 }
836 coeffs
837}
838pub fn eval_chebyshev(coeffs: &[f64], a: f64, b: f64, x: f64) -> f64 {
840 let t = 2.0 * (x - a) / (b - a) - 1.0;
841 coeffs
842 .iter()
843 .enumerate()
844 .map(|(k, &c)| c * chebyshev_t(k, t))
845 .sum()
846}
847#[cfg(test)]
848mod tests {
849 use super::*;
850 #[test]
851 fn test_rn_vector_norm() {
852 let v = RnVector::new(vec![3.0, 4.0]);
853 assert!((v.norm() - 5.0).abs() < 1e-10);
854 }
855 #[test]
856 fn test_rn_vector_inner() {
857 let e1 = RnVector::new(vec![1.0, 0.0]);
858 let e2 = RnVector::new(vec![0.0, 1.0]);
859 assert!(e1.inner(&e2).abs() < 1e-10);
860 assert!((e1.inner(&e1) - 1.0).abs() < 1e-10);
861 }
862 #[test]
863 fn test_rn_vector_normalized() {
864 let v = RnVector::new(vec![3.0, 4.0]);
865 let u = v.normalized().expect("nonzero");
866 assert!((u.norm() - 1.0).abs() < 1e-10);
867 assert!(RnVector::zero(3).normalized().is_none());
868 }
869 #[test]
870 fn test_rn_vector_basis() {
871 let e0 = RnVector::basis(3, 0);
872 assert!((e0.components[0] - 1.0).abs() < 1e-10);
873 assert!(e0.components[1].abs() < 1e-10);
874 }
875 #[test]
876 fn test_rn_vector_sub() {
877 let c = RnVector::new(vec![5.0, 3.0]).sub(&RnVector::new(vec![2.0, 1.0]));
878 assert!((c.components[0] - 3.0).abs() < 1e-10);
879 assert!((c.components[1] - 2.0).abs() < 1e-10);
880 }
881 #[test]
882 fn test_rn_vector_cross() {
883 let k = RnVector::new(vec![1.0, 0.0, 0.0])
884 .cross(&RnVector::new(vec![0.0, 1.0, 0.0]))
885 .expect("cross should succeed");
886 assert!(k.components[0].abs() < 1e-10);
887 assert!(k.components[1].abs() < 1e-10);
888 assert!((k.components[2] - 1.0).abs() < 1e-10);
889 }
890 #[test]
891 fn test_rn_vector_project() {
892 let proj = RnVector::new(vec![3.0, 4.0]).project_onto(&RnVector::new(vec![1.0, 0.0]));
893 assert!((proj.components[0] - 3.0).abs() < 1e-10);
894 assert!(proj.components[1].abs() < 1e-10);
895 }
896 #[test]
897 fn test_gram_schmidt() {
898 let basis = gram_schmidt(&[
899 RnVector::new(vec![1.0, 1.0, 0.0]),
900 RnVector::new(vec![1.0, 0.0, 1.0]),
901 RnVector::new(vec![0.0, 1.0, 1.0]),
902 ]);
903 assert_eq!(basis.len(), 3);
904 assert!(is_orthonormal(&basis, 1e-10));
905 }
906 #[test]
907 fn test_gram_schmidt_dependent() {
908 let basis = gram_schmidt(&[
909 RnVector::new(vec![1.0, 0.0]),
910 RnVector::new(vec![2.0, 0.0]),
911 RnVector::new(vec![0.0, 1.0]),
912 ]);
913 assert_eq!(basis.len(), 2);
914 assert!(is_orthonormal(&basis, 1e-10));
915 }
916 #[test]
917 fn test_spans_full_space() {
918 assert!(spans_full_space(
919 &[RnVector::new(vec![1.0, 0.0]), RnVector::new(vec![0.0, 1.0])],
920 2
921 ));
922 assert!(!spans_full_space(
923 &[
924 RnVector::new(vec![1.0, 0.0, 0.0]),
925 RnVector::new(vec![0.0, 1.0, 0.0])
926 ],
927 3
928 ));
929 }
930 #[test]
931 fn test_bounded_op_apply() {
932 let id = BoundedOp::identity(3);
933 let v = RnVector::new(vec![1.0, 2.0, 3.0]);
934 let w = id.apply(&v);
935 for (a, b) in w.components.iter().zip(v.components.iter()) {
936 assert!((a - b).abs() < 1e-10);
937 }
938 }
939 #[test]
940 fn test_bounded_op_transpose() {
941 let a = BoundedOp::new(vec![vec![1.0, 2.0, 3.0], vec![4.0, 5.0, 6.0]]);
942 let at = a.transpose();
943 assert_eq!(at.range_dim, 3);
944 assert_eq!(at.domain_dim, 2);
945 let att = at.transpose();
946 for i in 0..2 {
947 for j in 0..3 {
948 assert!((att.matrix[i][j] - a.matrix[i][j]).abs() < 1e-10);
949 }
950 }
951 }
952 #[test]
953 fn test_bounded_op_symmetric() {
954 assert!(BoundedOp::new(vec![vec![2.0, 1.0], vec![1.0, 3.0]]).is_symmetric());
955 assert!(!BoundedOp::new(vec![vec![1.0, 2.0], vec![3.0, 4.0]]).is_symmetric());
956 }
957 #[test]
958 fn test_bounded_op_diagonal() {
959 let w = BoundedOp::diagonal(&[2.0, 3.0, 5.0]).apply(&RnVector::new(vec![1.0, 1.0, 1.0]));
960 assert!((w.components[0] - 2.0).abs() < 1e-10);
961 assert!((w.components[1] - 3.0).abs() < 1e-10);
962 assert!((w.components[2] - 5.0).abs() < 1e-10);
963 }
964 #[test]
965 fn test_bounded_op_add() {
966 let c = BoundedOp::identity(2)
967 .op_add(&BoundedOp::identity(2))
968 .expect("op_add should succeed");
969 assert!((c.matrix[0][0] - 2.0).abs() < 1e-10);
970 }
971 #[test]
972 fn test_bounded_op_determinant() {
973 assert!(
974 (BoundedOp::identity(3)
975 .determinant()
976 .expect("determinant should succeed")
977 - 1.0)
978 .abs()
979 < 1e-10
980 );
981 assert!(
982 (BoundedOp::new(vec![vec![1.0, 2.0], vec![3.0, 4.0]])
983 .determinant()
984 .expect("determinant should succeed")
985 - (-2.0))
986 .abs()
987 < 1e-10
988 );
989 }
990 #[test]
991 fn test_bounded_op_solve() {
992 let x = BoundedOp::new(vec![vec![2.0, 1.0], vec![1.0, 3.0]])
993 .solve(&RnVector::new(vec![5.0, 7.0]))
994 .expect("solve should succeed");
995 assert!((x.components[0] - 1.6).abs() < 1e-10);
996 assert!((x.components[1] - 1.8).abs() < 1e-10);
997 }
998 #[test]
999 fn test_bounded_op_rank_nullity() {
1000 let a = BoundedOp::new(vec![
1001 vec![1.0, 2.0, 3.0],
1002 vec![4.0, 5.0, 6.0],
1003 vec![7.0, 8.0, 9.0],
1004 ]);
1005 assert_eq!(a.rank(), 2);
1006 assert_eq!(a.nullity(), 1);
1007 }
1008 #[test]
1009 fn test_bounded_op_positive_definite() {
1010 assert!(BoundedOp::new(vec![vec![2.0, 1.0], vec![1.0, 2.0]]).is_positive_definite());
1011 assert!(!BoundedOp::new(vec![vec![1.0, 2.0], vec![2.0, 1.0]]).is_positive_definite());
1012 }
1013 #[test]
1014 fn test_power_iteration() {
1015 let (ev, _) = BoundedOp::diagonal(&[5.0, 1.0])
1016 .power_iteration(100)
1017 .expect("power_iteration should succeed");
1018 assert!((ev - 5.0).abs() < 1e-6, "got {ev}");
1019 }
1020 #[test]
1021 fn test_eigenvalues_2x2() {
1022 let (e1, e2) = BoundedOp::new(vec![vec![3.0, 1.0], vec![1.0, 3.0]])
1023 .eigenvalues_2x2()
1024 .expect("eigenvalues_2x2 should succeed");
1025 assert!((e1 - 4.0).abs() < 1e-10);
1026 assert!((e2 - 2.0).abs() < 1e-10);
1027 }
1028 #[test]
1029 fn test_conjugate_gradient() {
1030 let a = BoundedOp::new(vec![vec![4.0, 1.0], vec![1.0, 3.0]]);
1031 let x = conjugate_gradient(&a, &RnVector::new(vec![1.0, 2.0]), 100, 1e-12);
1032 let ax = a.apply(&x);
1033 assert!((ax.components[0] - 1.0).abs() < 1e-8);
1034 assert!((ax.components[1] - 2.0).abs() < 1e-8);
1035 }
1036 #[test]
1037 fn test_conjugate_gradient_identity() {
1038 let b = RnVector::new(vec![1.0, 2.0, 3.0]);
1039 let x = conjugate_gradient(&BoundedOp::identity(3), &b, 100, 1e-12);
1040 for i in 0..3 {
1041 assert!((x.components[i] - b.components[i]).abs() < 1e-8);
1042 }
1043 }
1044 #[test]
1045 fn test_l2_sequence_norm() {
1046 assert!((L2Sequence::new(vec![1.0, 0.0, 0.0]).l2_norm() - 1.0).abs() < 1e-10);
1047 let b = L2Sequence::new(vec![3.0, 4.0]);
1048 assert!((b.l2_norm() - 5.0).abs() < 1e-10);
1049 assert!(b.is_in_l2());
1050 }
1051 #[test]
1052 fn test_l2_sequence_shift() {
1053 let s = L2Sequence::new(vec![1.0, 2.0, 3.0]);
1054 assert_eq!(s.shift_left().terms, vec![2.0, 3.0]);
1055 assert_eq!(s.shift_right().terms, vec![0.0, 1.0, 2.0, 3.0]);
1056 }
1057 #[test]
1058 fn test_l2_sequence_convolve() {
1059 let c = L2Sequence::new(vec![1.0, 2.0]).convolve(&L2Sequence::new(vec![3.0, 4.0]));
1060 assert!((c.terms[0] - 3.0).abs() < 1e-10);
1061 assert!((c.terms[1] - 10.0).abs() < 1e-10);
1062 assert!((c.terms[2] - 8.0).abs() < 1e-10);
1063 }
1064 #[test]
1065 fn test_l2_sequence_parseval() {
1066 assert!(L2Sequence::new(vec![1.0, 2.0, 3.0, 4.0]).parseval_residual() < 1e-10);
1067 }
1068 #[test]
1069 fn test_l2_norm_approx() {
1070 assert!((l2_norm_approx(&|_| 1.0, 0.0, 1.0, 1000) - 1.0).abs() < 1e-3);
1071 }
1072 #[test]
1073 fn test_l2_inner_approx() {
1074 let ip = l2_inner_approx(
1075 &|x: f64| x.sin(),
1076 &|x: f64| x.cos(),
1077 0.0,
1078 2.0 * std::f64::consts::PI,
1079 10000,
1080 );
1081 assert!(ip.abs() < 1e-3, "got {ip}");
1082 }
1083 #[test]
1084 fn test_sup_norm_approx() {
1085 let n = sup_norm_approx(&|x: f64| x.sin(), 0.0, std::f64::consts::PI, 10000);
1086 assert!((n - 1.0).abs() < 1e-3, "got {n}");
1087 }
1088 #[test]
1089 fn test_banach_fixed_point_iter() {
1090 let (fp, iters) = banach_fixed_point_iter(&|x: f64| x.cos(), 0.5, 1000, 1e-10);
1091 assert!((fp - fp.cos()).abs() < 1e-8);
1092 assert!(iters < 1000);
1093 }
1094 #[test]
1095 fn test_fourier_cosine_coefficients() {
1096 let c = fourier_cosine_coefficients(&|_| 1.0, 1.0, 5, 1000);
1097 assert!((c[0] - 1.0).abs() < 1e-3);
1098 for k in 1..5 {
1099 assert!(c[k].abs() < 1e-3);
1100 }
1101 }
1102 #[test]
1103 fn test_eval_fourier_cosine() {
1104 assert!((eval_fourier_cosine(&[1.0, 0.0, 0.0], 1.0, 0.5) - 1.0).abs() < 1e-10);
1105 }
1106 #[test]
1107 fn test_bounded_op_operator_norm_power() {
1108 let n = BoundedOp::diagonal(&[3.0, 1.0, 2.0]).operator_norm_power_iter(200);
1109 assert!((n - 3.0).abs() < 1e-3, "got {n}");
1110 }
1111 #[test]
1112 fn test_bounded_op_frobenius_norm() {
1113 let frob = BoundedOp::identity(2).frobenius_norm();
1114 assert!((frob - 2.0_f64.sqrt()).abs() < 1e-10);
1115 assert!(BoundedOp::identity(2).operator_norm() > 0.0);
1116 }
1117 #[test]
1118 fn test_bounded_op_compose() {
1119 let a = BoundedOp::new(vec![vec![1.0, 2.0], vec![3.0, 4.0]]);
1120 let c = a
1121 .compose(&BoundedOp::identity(2))
1122 .expect("BoundedOp::new should succeed");
1123 for i in 0..2 {
1124 for j in 0..2 {
1125 assert!((c.matrix[i][j] - a.matrix[i][j]).abs() < 1e-10);
1126 }
1127 }
1128 }
1129 #[test]
1130 fn test_bounded_op_scalar_mul() {
1131 let b = BoundedOp::identity(2).scalar_mul(3.0);
1132 assert!((b.matrix[0][0] - 3.0).abs() < 1e-10);
1133 }
1134 #[test]
1135 fn test_rn_vector_lp_norms() {
1136 let v = RnVector::new(vec![3.0, 4.0]);
1137 assert!((v.norm_p(1.0) - 7.0).abs() < 1e-10);
1138 assert!((v.norm_p(f64::INFINITY) - 4.0).abs() < 1e-10);
1139 }
1140 #[test]
1141 fn test_build_functional_analysis_env() {
1142 let mut env = Environment::new();
1143 build_functional_analysis_env(&mut env);
1144 assert!(env.get(&Name::str("NormedSpace")).is_some());
1145 assert!(env.get(&Name::str("hahn_banach")).is_some());
1146 assert!(env.get(&Name::str("fredholm_alternative")).is_some());
1147 assert!(env.get(&Name::str("banach_fixed_point")).is_some());
1148 }
1149 #[test]
1150 fn test_bounded_op_injective_surjective() {
1151 assert!(BoundedOp::identity(3).is_injective());
1152 assert!(BoundedOp::identity(3).is_surjective());
1153 let s = BoundedOp::new(vec![vec![1.0, 0.0], vec![0.0, 0.0]]);
1154 assert!(!s.is_injective());
1155 assert!(!s.is_surjective());
1156 }
1157 #[test]
1158 fn test_rn_vector_angle() {
1159 let angle = RnVector::new(vec![1.0, 0.0]).angle_with(&RnVector::new(vec![0.0, 1.0]));
1160 assert!((angle - std::f64::consts::FRAC_PI_2).abs() < 1e-10);
1161 }
1162 #[test]
1163 fn test_qr_identity() {
1164 let qr = qr_decomposition(&BoundedOp::identity(3)).expect("operation should succeed");
1165 for i in 0..3 {
1166 for j in 0..3 {
1167 let expected = if i == j { 1.0 } else { 0.0 };
1168 assert!((qr.q.matrix[i][j] - expected).abs() < 1e-10);
1169 assert!((qr.r.matrix[i][j] - expected).abs() < 1e-10);
1170 }
1171 }
1172 }
1173 #[test]
1174 fn test_qr_reconstruction() {
1175 let a = BoundedOp::new(vec![vec![1.0, 2.0], vec![3.0, 4.0], vec![5.0, 6.0]]);
1176 let qr = qr_decomposition(&a).expect("BoundedOp::new should succeed");
1177 let prod = qr.q.compose(&qr.r).expect("compose should succeed");
1178 for i in 0..a.range_dim {
1179 for j in 0..a.domain_dim {
1180 assert!(
1181 (prod.matrix[i][j] - a.matrix[i][j]).abs() < 1e-10,
1182 "mismatch at ({i},{j}): {} vs {}",
1183 prod.matrix[i][j],
1184 a.matrix[i][j]
1185 );
1186 }
1187 }
1188 }
1189 #[test]
1190 fn test_qr_orthogonality() {
1191 let a = BoundedOp::new(vec![
1192 vec![12.0, -51.0, 4.0],
1193 vec![6.0, 167.0, -68.0],
1194 vec![-4.0, 24.0, -41.0],
1195 ]);
1196 let qr = qr_decomposition(&a).expect("operation should succeed");
1197 let qtq =
1198 qr.q.transpose()
1199 .compose(&qr.q)
1200 .expect("compose should succeed");
1201 for i in 0..3 {
1202 for j in 0..3 {
1203 let expected = if i == j { 1.0 } else { 0.0 };
1204 assert!((qtq.matrix[i][j] - expected).abs() < 1e-10);
1205 }
1206 }
1207 }
1208 #[test]
1209 fn test_lu_solve() {
1210 let a = BoundedOp::new(vec![
1211 vec![2.0, 1.0, 1.0],
1212 vec![4.0, 3.0, 3.0],
1213 vec![8.0, 7.0, 9.0],
1214 ]);
1215 let b = RnVector::new(vec![1.0, 1.0, 1.0]);
1216 let lu = lu_decomposition(&a).expect("RnVector::new should succeed");
1217 let x = lu_solve(&lu, &b);
1218 let ax = a.apply(&x);
1219 for i in 0..3 {
1220 assert!(
1221 (ax.components[i] - b.components[i]).abs() < 1e-8,
1222 "lu_solve error at {i}"
1223 );
1224 }
1225 }
1226 #[test]
1227 fn test_lu_identity() {
1228 let lu = lu_decomposition(&BoundedOp::identity(3)).expect("operation should succeed");
1229 for i in 0..3 {
1230 assert!((lu.l.matrix[i][i] - 1.0).abs() < 1e-10);
1231 assert!((lu.u.matrix[i][i] - 1.0).abs() < 1e-10);
1232 }
1233 }
1234 #[test]
1235 fn test_singular_values_diagonal() {
1236 let svs = singular_values(&BoundedOp::diagonal(&[3.0, 1.0, 5.0]), 50);
1237 assert!((svs[0] - 5.0).abs() < 0.1, "sv[0] = {}", svs[0]);
1238 assert!((svs[1] - 3.0).abs() < 0.1, "sv[1] = {}", svs[1]);
1239 assert!((svs[2] - 1.0).abs() < 0.1, "sv[2] = {}", svs[2]);
1240 }
1241 #[test]
1242 fn test_condition_number_identity() {
1243 let cn = condition_number(&BoundedOp::identity(3), 50);
1244 assert!((cn - 1.0).abs() < 0.1, "cond(I) = {cn}");
1245 }
1246 #[test]
1247 fn test_condition_number_ill_conditioned() {
1248 let cn = condition_number(&BoundedOp::diagonal(&[100.0, 0.01]), 50);
1249 assert!(cn > 1000.0, "expected ill-conditioned, got {cn}");
1250 }
1251 #[test]
1252 fn test_spectral_radius() {
1253 let rho = spectral_radius(&BoundedOp::diagonal(&[2.0, -3.0, 1.0]), 200);
1254 assert!((rho - 3.0).abs() < 0.1, "got {rho}");
1255 }
1256 #[test]
1257 fn test_h1_norm_constant() {
1258 let h1 = h1_norm_approx(&|_| 1.0, 0.0, 1.0, 1000);
1259 assert!((h1 - 1.0).abs() < 1e-2, "got {h1}");
1260 }
1261 #[test]
1262 fn test_h1_norm_linear() {
1263 let h1 = h1_norm_approx(&|x| x, 0.0, 1.0, 1000);
1264 let expected = (1.0 / 3.0 + 1.0_f64).sqrt();
1265 assert!(
1266 (h1 - expected).abs() < 0.05,
1267 "got {h1}, expected {expected}"
1268 );
1269 }
1270 #[test]
1271 fn test_h2_norm() {
1272 let h2 = h2_norm_approx(&|x: f64| x * x, 0.0, 1.0, 1000);
1273 assert!(h2 > 0.0);
1274 }
1275 #[test]
1276 fn test_simpson_integrate() {
1277 let result = simpson_integrate(&|x| x * x, 0.0, 1.0, 100);
1278 assert!((result - 1.0 / 3.0).abs() < 1e-6, "got {result}");
1279 }
1280 #[test]
1281 fn test_gauss_legendre_quadrature() {
1282 let result = gauss_legendre_composite(&|x| x * x, 0.0, 1.0, 10);
1283 assert!((result - 1.0 / 3.0).abs() < 1e-10, "got {result}");
1284 }
1285 #[test]
1286 fn test_matrix_exponential_zero() {
1287 let exp_zero = matrix_exponential(&BoundedOp::zero_op(3, 3), 20);
1288 for i in 0..3 {
1289 for j in 0..3 {
1290 let expected = if i == j { 1.0 } else { 0.0 };
1291 assert!((exp_zero.matrix[i][j] - expected).abs() < 1e-10);
1292 }
1293 }
1294 }
1295 #[test]
1296 fn test_matrix_exponential_identity() {
1297 let exp_i = matrix_exponential(&BoundedOp::identity(2), 20);
1298 let e = std::f64::consts::E;
1299 assert!(
1300 (exp_i.matrix[0][0] - e).abs() < 1e-6,
1301 "got {}",
1302 exp_i.matrix[0][0]
1303 );
1304 assert!((exp_i.matrix[1][1] - e).abs() < 1e-6);
1305 assert!(exp_i.matrix[0][1].abs() < 1e-6);
1306 }
1307 #[test]
1308 fn test_commutator_self() {
1309 let a = BoundedOp::new(vec![vec![1.0, 2.0], vec![3.0, 4.0]]);
1310 let c = commutator(&a, &a).expect("BoundedOp::new should succeed");
1311 for i in 0..2 {
1312 for j in 0..2 {
1313 assert!(c.matrix[i][j].abs() < 1e-10);
1314 }
1315 }
1316 }
1317 #[test]
1318 fn test_anti_commutator() {
1319 let a = BoundedOp::identity(2);
1320 let ac = anti_commutator(&a, &a).expect("BoundedOp::identity should succeed");
1321 assert!((ac.matrix[0][0] - 2.0).abs() < 1e-10);
1322 assert!((ac.matrix[1][1] - 2.0).abs() < 1e-10);
1323 }
1324 #[test]
1325 fn test_fredholm_index_zero() {
1326 let k = BoundedOp::zero_op(3, 3);
1327 let (kd, cd, idx) = fredholm_index_numerical(&k);
1328 assert_eq!(kd, 0);
1329 assert_eq!(cd, 0);
1330 assert_eq!(idx, 0);
1331 }
1332 #[test]
1333 fn test_fredholm_index_projection() {
1334 let k = BoundedOp::new(vec![vec![1.0, 0.0], vec![0.0, 0.0]]);
1335 let (kd, _cd, _idx) = fredholm_index_numerical(&k);
1336 assert_eq!(kd, 1);
1337 }
1338 #[test]
1339 fn test_jacobi_iteration() {
1340 let a = BoundedOp::new(vec![vec![4.0, 1.0], vec![1.0, 3.0]]);
1341 let b = RnVector::new(vec![1.0, 2.0]);
1342 let x = jacobi_iteration(&a, &b, 200, 1e-10);
1343 let ax = a.apply(&x);
1344 assert!((ax.components[0] - b.components[0]).abs() < 1e-6);
1345 assert!((ax.components[1] - b.components[1]).abs() < 1e-6);
1346 }
1347 #[test]
1348 fn test_gauss_seidel_iteration() {
1349 let a = BoundedOp::new(vec![vec![4.0, 1.0], vec![1.0, 3.0]]);
1350 let b = RnVector::new(vec![1.0, 2.0]);
1351 let x = gauss_seidel_iteration(&a, &b, 200, 1e-10);
1352 let ax = a.apply(&x);
1353 assert!((ax.components[0] - b.components[0]).abs() < 1e-6);
1354 assert!((ax.components[1] - b.components[1]).abs() < 1e-6);
1355 }
1356 #[test]
1357 fn test_fourier_sine_coefficients() {
1358 let coeffs =
1359 fourier_sine_coefficients(&|x: f64| (std::f64::consts::PI * x).sin(), 1.0, 5, 1000);
1360 assert!((coeffs[0] - 1.0).abs() < 1e-2, "got {}", coeffs[0]);
1361 for k in 1..5 {
1362 assert!(coeffs[k].abs() < 1e-2, "coeff[{k}] = {}", coeffs[k]);
1363 }
1364 }
1365 #[test]
1366 fn test_eval_fourier_sine() {
1367 assert!((eval_fourier_sine(&[1.0, 0.0, 0.0], 1.0, 0.5) - 1.0).abs() < 1e-10);
1368 }
1369 #[test]
1370 fn test_chebyshev_t_values() {
1371 assert!((chebyshev_t(0, 0.5) - 1.0).abs() < 1e-10);
1372 assert!((chebyshev_t(1, 0.5) - 0.5).abs() < 1e-10);
1373 assert!((chebyshev_t(2, 0.5) - (-0.5)).abs() < 1e-10);
1374 }
1375 #[test]
1376 fn test_chebyshev_nodes() {
1377 let nodes = chebyshev_nodes(4, -1.0, 1.0);
1378 assert_eq!(nodes.len(), 4);
1379 for &x in &nodes {
1380 assert!(x >= -1.0 && x <= 1.0);
1381 }
1382 }
1383 #[test]
1384 fn test_chebyshev_approximation() {
1385 let coeffs = chebyshev_coefficients(&|x: f64| x.sin(), 10, -1.0, 1.0);
1386 let approx = eval_chebyshev(&coeffs, -1.0, 1.0, 0.5);
1387 assert!(
1388 (approx - 0.5_f64.sin()).abs() < 0.1,
1389 "got {approx}, expected {}",
1390 0.5_f64.sin()
1391 );
1392 }
1393}
1394#[cfg(test)]
1395mod tests_functional_analysis_ext {
1396 use super::*;
1397 #[test]
1398 fn test_fredholm_operator() {
1399 let fo = FredholmOperatorData::new("D_A", 2, 1);
1400 assert_eq!(fo.index(), 1);
1401 assert!(!fo.is_isomorphism());
1402 assert!(fo.index_stable_under_compact());
1403 assert!(fo.atkinson_description().contains("index 1"));
1404 }
1405 #[test]
1406 fn test_fredholm_isomorphism() {
1407 let fo = FredholmOperatorData::new("I", 0, 0);
1408 assert_eq!(fo.index(), 0);
1409 assert!(fo.is_isomorphism());
1410 }
1411 #[test]
1412 fn test_sobolev_space() {
1413 let ss = SobolevSpaceData::new(1, 2.0, 3, "R^3");
1414 let p_star = ss
1415 .critical_sobolev_exponent()
1416 .expect("critical_sobolev_exponent should succeed");
1417 assert!((p_star - 6.0).abs() < 1e-10, "p* should be 6, got {p_star}");
1418 assert!(ss.rellich_kondrachov_compact());
1419 assert!(ss.trace_theorem().contains("Trace"));
1420 }
1421 #[test]
1422 fn test_sobolev_supercritical() {
1423 let ss = SobolevSpaceData::new(2, 2.0, 2, "R^2");
1424 assert!(ss.critical_sobolev_exponent().is_none());
1425 }
1426 #[test]
1427 fn test_interpolation() {
1428 let id = InterpolationData::new("L^1", "L^inf", 0.5, InterpolationMethod::Complex);
1429 let exp = id.lp_exponent(1.0, 1e10);
1430 assert!((exp - 0.5).abs() < 1e-9, "1/p should be ~0.5");
1431 let bound = id.riesz_thorin_bound(1.0, 1.0);
1432 assert!((bound - 1.0).abs() < 1e-10);
1433 }
1434}
1435#[cfg(test)]
1436mod tests_functional_analysis_ext2 {
1437 use super::*;
1438 #[test]
1439 fn test_weak_convergence() {
1440 let seq: Vec<Vec<f64>> = (1..=10).map(|n| vec![1.0 / n as f64, 0.0]).collect();
1441 let wcd = WeakConvergenceData::new(seq);
1442 assert!(wcd.is_bounded);
1443 assert!(wcd.banach_alaoglu_applies());
1444 assert!(wcd.check_weak_convergence(&[0.0, 0.0], 0.2));
1445 }
1446 #[test]
1447 fn test_distribution_dirac() {
1448 let delta = Distribution::dirac_delta(0.0);
1449 assert!(!delta.is_regular);
1450 assert_eq!(delta.order, Some(0));
1451 assert!(delta.is_tempered());
1452 let deriv = delta.differentiate();
1453 assert_eq!(deriv.order, Some(1));
1454 }
1455 #[test]
1456 fn test_distribution_regular() {
1457 let reg = Distribution::regular("f", "R").differentiate();
1458 assert!(!reg.is_regular);
1459 assert_eq!(reg.order, Some(1));
1460 assert!(reg
1461 .fourier_transform_description()
1462 .contains("distributional"));
1463 }
1464}