Skip to main content

oxilean_std/functional_analysis/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
7
8pub struct L2Sequence {
9    pub terms: Vec<f64>,
10    pub max_terms: usize,
11}
12impl L2Sequence {
13    pub fn new(terms: Vec<f64>) -> Self {
14        let max_terms = terms.len();
15        L2Sequence { terms, max_terms }
16    }
17    pub fn l2_norm(&self) -> f64 {
18        self.terms.iter().map(|x| x * x).sum::<f64>().sqrt()
19    }
20    pub fn is_in_l2(&self) -> bool {
21        self.l2_norm().is_finite()
22    }
23    pub fn inner_product(&self, other: &Self) -> f64 {
24        self.terms
25            .iter()
26            .zip(other.terms.iter())
27            .map(|(a, b)| a * b)
28            .sum()
29    }
30    pub fn shift_left(&self) -> Self {
31        let terms = if self.terms.is_empty() {
32            vec![]
33        } else {
34            self.terms[1..].to_vec()
35        };
36        L2Sequence {
37            max_terms: terms.len(),
38            terms,
39        }
40    }
41    pub fn shift_right(&self) -> Self {
42        let mut terms = vec![0.0];
43        terms.extend_from_slice(&self.terms);
44        L2Sequence {
45            max_terms: terms.len(),
46            terms,
47        }
48    }
49    pub fn seq_add(&self, other: &Self) -> Self {
50        let len = self.terms.len().max(other.terms.len());
51        let terms: Vec<f64> = (0..len)
52            .map(|i| {
53                let a = if i < self.terms.len() {
54                    self.terms[i]
55                } else {
56                    0.0
57                };
58                let b = if i < other.terms.len() {
59                    other.terms[i]
60                } else {
61                    0.0
62                };
63                a + b
64            })
65            .collect();
66        L2Sequence {
67            max_terms: terms.len(),
68            terms,
69        }
70    }
71    pub fn seq_scale(&self, c: f64) -> Self {
72        let terms: Vec<f64> = self.terms.iter().map(|x| x * c).collect();
73        L2Sequence {
74            max_terms: terms.len(),
75            terms,
76        }
77    }
78    pub fn convolve(&self, other: &Self) -> Self {
79        let (n, m) = (self.terms.len(), other.terms.len());
80        if n == 0 || m == 0 {
81            return L2Sequence::new(vec![]);
82        }
83        let mut result = vec![0.0; n + m - 1];
84        for i in 0..n {
85            for j in 0..m {
86                result[i + j] += self.terms[i] * other.terms[j];
87            }
88        }
89        L2Sequence::new(result)
90    }
91    pub fn parseval_residual(&self) -> f64 {
92        let norm_sq = self.l2_norm().powi(2);
93        let sum_sq: f64 = self.terms.iter().map(|x| x * x).sum();
94        (norm_sq - sum_sq).abs()
95    }
96}
97/// Data for Sobolev space W^{k,p}(Ω).
98#[allow(dead_code)]
99#[derive(Debug, Clone)]
100pub struct SobolevSpaceData {
101    /// Differentiability order k.
102    pub order: usize,
103    /// Integrability p (1 <= p <= ∞).
104    pub p: f64,
105    /// Domain dimension n.
106    pub domain_dim: usize,
107    /// Domain description.
108    pub domain: String,
109}
110#[allow(dead_code)]
111impl SobolevSpaceData {
112    /// Creates Sobolev space data.
113    pub fn new(order: usize, p: f64, domain_dim: usize, domain: &str) -> Self {
114        SobolevSpaceData {
115            order,
116            p,
117            domain_dim,
118            domain: domain.to_string(),
119        }
120    }
121    /// Returns H^k = W^{k,2} Sobolev space.
122    pub fn hilbert_sobolev(order: usize, domain_dim: usize, domain: &str) -> Self {
123        SobolevSpaceData::new(order, 2.0, domain_dim, domain)
124    }
125    /// Sobolev embedding: W^{k,p}(Ω) ↪ L^q(Ω) for 1/q = 1/p - k/n.
126    pub fn embedding_exponent(&self) -> Option<f64> {
127        if self.p < 1.0 {
128            return None;
129        }
130        let critical = 1.0 / self.p - (self.order as f64) / (self.domain_dim as f64);
131        if critical <= 0.0 {
132            None
133        } else {
134            Some(1.0 / critical)
135        }
136    }
137    /// Returns the critical Sobolev exponent p* = np/(n-kp).
138    pub fn critical_sobolev_exponent(&self) -> Option<f64> {
139        let kp = self.order as f64 * self.p;
140        let n = self.domain_dim as f64;
141        if kp >= n {
142            None
143        } else {
144            Some(n * self.p / (n - kp))
145        }
146    }
147    /// Checks the Rellich-Kondrachov theorem (compact embedding).
148    pub fn rellich_kondrachov_compact(&self) -> bool {
149        self.order >= 1 && self.domain_dim >= 1
150    }
151    /// Returns trace theorem statement.
152    pub fn trace_theorem(&self) -> String {
153        if self.order >= 1 {
154            format!(
155                "Trace: W^{{{},{}}}'(Ω) → W^{{{}−1/p,p}}(∂Ω)",
156                self.order, self.p, self.order
157            )
158        } else {
159            "No trace for W^{0,p}".to_string()
160        }
161    }
162}
163/// Data for interpolation between Banach spaces.
164#[allow(dead_code)]
165#[derive(Debug, Clone)]
166pub struct InterpolationData {
167    /// Space X_0 description.
168    pub space0: String,
169    /// Space X_1 description.
170    pub space1: String,
171    /// Interpolation parameter θ ∈ [0,1].
172    pub theta: f64,
173    /// Interpolation method.
174    pub method: InterpolationMethod,
175}
176#[allow(dead_code)]
177impl InterpolationData {
178    /// Creates interpolation data.
179    pub fn new(space0: &str, space1: &str, theta: f64, method: InterpolationMethod) -> Self {
180        InterpolationData {
181            space0: space0.to_string(),
182            space1: space1.to_string(),
183            theta,
184            method,
185        }
186    }
187    /// Returns the interpolation exponent for L^p spaces: 1/p = (1-θ)/p0 + θ/p1.
188    pub fn lp_exponent(&self, p0: f64, p1: f64) -> f64 {
189        (1.0 - self.theta) / p0 + self.theta / p1
190    }
191    /// Riesz-Thorin theorem: if T: X0→Y0 has norm M0 and T: X1→Y1 has norm M1,
192    /// then T: [X0,X1]_θ → [Y0,Y1]_θ has norm <= M0^{1-θ} M1^θ.
193    pub fn riesz_thorin_bound(&self, m0: f64, m1: f64) -> f64 {
194        m0.powf(1.0 - self.theta) * m1.powf(self.theta)
195    }
196    /// Returns the interpolation space description.
197    pub fn interpolation_space(&self) -> String {
198        format!("[{}, {}]_{}", self.space0, self.space1, self.theta)
199    }
200}
201#[derive(Debug, Clone)]
202pub struct RnVector {
203    pub components: Vec<f64>,
204}
205impl RnVector {
206    pub fn new(components: Vec<f64>) -> Self {
207        RnVector { components }
208    }
209    pub fn dim(&self) -> usize {
210        self.components.len()
211    }
212    pub fn zero(dim: usize) -> Self {
213        RnVector {
214            components: vec![0.0; dim],
215        }
216    }
217    pub fn basis(dim: usize, i: usize) -> Self {
218        let mut v = vec![0.0; dim];
219        if i < dim {
220            v[i] = 1.0;
221        }
222        RnVector { components: v }
223    }
224    pub fn norm(&self) -> f64 {
225        self.components.iter().map(|x| x * x).sum::<f64>().sqrt()
226    }
227    pub fn norm_p(&self, p: f64) -> f64 {
228        if p.is_infinite() {
229            self.components
230                .iter()
231                .map(|x| x.abs())
232                .fold(0.0_f64, f64::max)
233        } else {
234            self.components
235                .iter()
236                .map(|x| x.abs().powf(p))
237                .sum::<f64>()
238                .powf(1.0 / p)
239        }
240    }
241    pub fn inner(&self, other: &Self) -> f64 {
242        self.components
243            .iter()
244            .zip(other.components.iter())
245            .map(|(a, b)| a * b)
246            .sum()
247    }
248    pub fn normalized(&self) -> Option<Self> {
249        let n = self.norm();
250        if n == 0.0 {
251            None
252        } else {
253            Some(self.scale(1.0 / n))
254        }
255    }
256    pub fn add(&self, other: &Self) -> Self {
257        RnVector {
258            components: self
259                .components
260                .iter()
261                .zip(other.components.iter())
262                .map(|(a, b)| a + b)
263                .collect(),
264        }
265    }
266    pub fn sub(&self, other: &Self) -> Self {
267        RnVector {
268            components: self
269                .components
270                .iter()
271                .zip(other.components.iter())
272                .map(|(a, b)| a - b)
273                .collect(),
274        }
275    }
276    pub fn scale(&self, s: f64) -> Self {
277        RnVector {
278            components: self.components.iter().map(|x| x * s).collect(),
279        }
280    }
281    pub fn angle_with(&self, other: &Self) -> f64 {
282        let n1 = self.norm();
283        let n2 = other.norm();
284        if n1 == 0.0 || n2 == 0.0 {
285            return f64::NAN;
286        }
287        (self.inner(other) / (n1 * n2)).clamp(-1.0, 1.0).acos()
288    }
289    pub fn cross(&self, other: &Self) -> Option<Self> {
290        if self.dim() != 3 || other.dim() != 3 {
291            return None;
292        }
293        let (a, b) = (&self.components, &other.components);
294        Some(RnVector::new(vec![
295            a[1] * b[2] - a[2] * b[1],
296            a[2] * b[0] - a[0] * b[2],
297            a[0] * b[1] - a[1] * b[0],
298        ]))
299    }
300    pub fn project_onto(&self, other: &Self) -> Self {
301        let d = other.inner(other);
302        if d.abs() < 1e-15 {
303            return RnVector::zero(self.dim());
304        }
305        other.scale(self.inner(other) / d)
306    }
307}
308/// QR decomposition result: A = Q * R where Q is orthogonal and R is upper triangular.
309#[derive(Debug, Clone)]
310pub struct QrDecomposition {
311    pub q: BoundedOp,
312    pub r: BoundedOp,
313}
314/// Interpolation method.
315#[allow(dead_code)]
316#[derive(Debug, Clone, PartialEq)]
317pub enum InterpolationMethod {
318    /// Complex interpolation (Calderón-Lions).
319    Complex,
320    /// Real interpolation (K-functional).
321    Real,
322    /// Riesz-Thorin (L^p spaces).
323    RieszThorin,
324}
325#[derive(Debug, Clone)]
326pub struct BoundedOp {
327    pub matrix: Vec<Vec<f64>>,
328    pub domain_dim: usize,
329    pub range_dim: usize,
330}
331impl BoundedOp {
332    pub fn new(matrix: Vec<Vec<f64>>) -> Self {
333        let range_dim = matrix.len();
334        let domain_dim = if range_dim > 0 { matrix[0].len() } else { 0 };
335        BoundedOp {
336            matrix,
337            domain_dim,
338            range_dim,
339        }
340    }
341    pub fn identity(n: usize) -> Self {
342        let matrix: Vec<Vec<f64>> = (0..n)
343            .map(|i| (0..n).map(|j| if i == j { 1.0 } else { 0.0 }).collect())
344            .collect();
345        BoundedOp {
346            matrix,
347            domain_dim: n,
348            range_dim: n,
349        }
350    }
351    pub fn diagonal(entries: &[f64]) -> Self {
352        let n = entries.len();
353        let matrix: Vec<Vec<f64>> = (0..n)
354            .map(|i| {
355                let mut row = vec![0.0; n];
356                row[i] = entries[i];
357                row
358            })
359            .collect();
360        BoundedOp {
361            matrix,
362            domain_dim: n,
363            range_dim: n,
364        }
365    }
366    pub fn zero_op(m: usize, n: usize) -> Self {
367        BoundedOp {
368            matrix: vec![vec![0.0; n]; m],
369            domain_dim: n,
370            range_dim: m,
371        }
372    }
373    pub fn apply(&self, v: &RnVector) -> RnVector {
374        if v.dim() != self.domain_dim {
375            return RnVector::zero(self.range_dim);
376        }
377        let comps: Vec<f64> = self
378            .matrix
379            .iter()
380            .map(|row| {
381                row.iter()
382                    .zip(v.components.iter())
383                    .map(|(a, b)| a * b)
384                    .sum()
385            })
386            .collect();
387        RnVector { components: comps }
388    }
389    pub fn operator_norm(&self) -> f64 {
390        self.frobenius_norm()
391    }
392    pub fn operator_norm_power_iter(&self, iterations: usize) -> f64 {
393        let ata = match self.transpose().compose(self) {
394            Some(m) => m,
395            None => return 0.0,
396        };
397        if self.domain_dim == 0 {
398            return 0.0;
399        }
400        let mut v = RnVector::new(vec![1.0; self.domain_dim]);
401        let n = v.norm();
402        if n > 0.0 {
403            v = v.scale(1.0 / n);
404        }
405        let mut eigenvalue = 0.0;
406        for _ in 0..iterations {
407            let w = ata.apply(&v);
408            eigenvalue = w.norm();
409            if eigenvalue < 1e-15 {
410                return 0.0;
411            }
412            v = w.scale(1.0 / eigenvalue);
413        }
414        eigenvalue.sqrt()
415    }
416    pub fn transpose(&self) -> Self {
417        if self.range_dim == 0 || self.domain_dim == 0 {
418            return BoundedOp::new(vec![]);
419        }
420        let matrix: Vec<Vec<f64>> = (0..self.domain_dim)
421            .map(|j| (0..self.range_dim).map(|i| self.matrix[i][j]).collect())
422            .collect();
423        BoundedOp {
424            matrix,
425            domain_dim: self.range_dim,
426            range_dim: self.domain_dim,
427        }
428    }
429    pub fn compose(&self, other: &Self) -> Option<Self> {
430        if other.range_dim != self.domain_dim {
431            return None;
432        }
433        let (m, n, k) = (self.range_dim, other.domain_dim, self.domain_dim);
434        let matrix: Vec<Vec<f64>> = (0..m)
435            .map(|i| {
436                (0..n)
437                    .map(|j| (0..k).map(|l| self.matrix[i][l] * other.matrix[l][j]).sum())
438                    .collect()
439            })
440            .collect();
441        Some(BoundedOp {
442            matrix,
443            domain_dim: n,
444            range_dim: m,
445        })
446    }
447    pub fn op_add(&self, other: &Self) -> Option<Self> {
448        if self.domain_dim != other.domain_dim || self.range_dim != other.range_dim {
449            return None;
450        }
451        let matrix: Vec<Vec<f64>> = self
452            .matrix
453            .iter()
454            .zip(other.matrix.iter())
455            .map(|(r1, r2)| r1.iter().zip(r2.iter()).map(|(a, b)| a + b).collect())
456            .collect();
457        Some(BoundedOp {
458            matrix,
459            domain_dim: self.domain_dim,
460            range_dim: self.range_dim,
461        })
462    }
463    pub fn scalar_mul(&self, c: f64) -> Self {
464        let matrix: Vec<Vec<f64>> = self
465            .matrix
466            .iter()
467            .map(|row| row.iter().map(|x| x * c).collect())
468            .collect();
469        BoundedOp {
470            matrix,
471            domain_dim: self.domain_dim,
472            range_dim: self.range_dim,
473        }
474    }
475    pub fn is_symmetric(&self) -> bool {
476        if self.domain_dim != self.range_dim {
477            return false;
478        }
479        let n = self.domain_dim;
480        for i in 0..n {
481            for j in (i + 1)..n {
482                if (self.matrix[i][j] - self.matrix[j][i]).abs() > 1e-10 {
483                    return false;
484                }
485            }
486        }
487        true
488    }
489    pub fn is_positive_definite(&self) -> bool {
490        if !self.is_symmetric() {
491            return false;
492        }
493        let n = self.domain_dim;
494        let mut l = vec![vec![0.0; n]; n];
495        for i in 0..n {
496            for j in 0..=i {
497                let mut sum = 0.0;
498                for k in 0..j {
499                    sum += l[i][k] * l[j][k];
500                }
501                if i == j {
502                    let val = self.matrix[i][i] - sum;
503                    if val <= 0.0 {
504                        return false;
505                    }
506                    l[i][j] = val.sqrt();
507                } else {
508                    if l[j][j].abs() < 1e-15 {
509                        return false;
510                    }
511                    l[i][j] = (self.matrix[i][j] - sum) / l[j][j];
512                }
513            }
514        }
515        true
516    }
517    pub fn eigenvalues_2x2(&self) -> Option<(f64, f64)> {
518        if self.domain_dim != 2 || self.range_dim != 2 || !self.is_symmetric() {
519            return None;
520        }
521        let (a, b, d) = (self.matrix[0][0], self.matrix[0][1], self.matrix[1][1]);
522        let tr = a + d;
523        let det = a * d - b * b;
524        let disc = tr * tr - 4.0 * det;
525        if disc < 0.0 {
526            return None;
527        }
528        let s = disc.sqrt();
529        Some(((tr + s) / 2.0, (tr - s) / 2.0))
530    }
531    pub fn power_iteration(&self, iterations: usize) -> Option<(f64, RnVector)> {
532        if self.domain_dim != self.range_dim || self.domain_dim == 0 {
533            return None;
534        }
535        let n = self.domain_dim;
536        let mut v = RnVector::new(vec![1.0; n]);
537        let nrm = v.norm();
538        if nrm > 0.0 {
539            v = v.scale(1.0 / nrm);
540        }
541        let mut eigenvalue = 0.0;
542        for _ in 0..iterations {
543            let w = self.apply(&v);
544            eigenvalue = w.inner(&v);
545            let nrm = w.norm();
546            if nrm < 1e-15 {
547                return Some((0.0, v));
548            }
549            v = w.scale(1.0 / nrm);
550        }
551        Some((eigenvalue, v))
552    }
553    pub fn trace(&self) -> f64 {
554        let n = self.domain_dim.min(self.range_dim);
555        (0..n).map(|i| self.matrix[i][i]).sum()
556    }
557    pub fn frobenius_norm(&self) -> f64 {
558        self.matrix
559            .iter()
560            .flat_map(|row| row.iter())
561            .map(|x| x * x)
562            .sum::<f64>()
563            .sqrt()
564    }
565    pub fn determinant(&self) -> Option<f64> {
566        if self.domain_dim != self.range_dim {
567            return None;
568        }
569        let n = self.domain_dim;
570        if n == 0 {
571            return Some(1.0);
572        }
573        let mut a = self.matrix.clone();
574        let mut sign = 1.0;
575        for col in 0..n {
576            let mut max_row = col;
577            let mut max_val = a[col][col].abs();
578            for row in (col + 1)..n {
579                if a[row][col].abs() > max_val {
580                    max_val = a[row][col].abs();
581                    max_row = row;
582                }
583            }
584            if max_val < 1e-15 {
585                return Some(0.0);
586            }
587            if max_row != col {
588                a.swap(col, max_row);
589                sign *= -1.0;
590            }
591            let pivot = a[col][col];
592            for row in (col + 1)..n {
593                let factor = a[row][col] / pivot;
594                for j in col..n {
595                    let val = a[col][j];
596                    a[row][j] -= factor * val;
597                }
598            }
599        }
600        Some((0..n).map(|i| a[i][i]).product::<f64>() * sign)
601    }
602    pub fn solve(&self, b: &RnVector) -> Option<RnVector> {
603        if self.domain_dim != self.range_dim || b.dim() != self.range_dim {
604            return None;
605        }
606        let n = self.domain_dim;
607        let mut aug: Vec<Vec<f64>> = self
608            .matrix
609            .iter()
610            .enumerate()
611            .map(|(i, row)| {
612                let mut r = row.clone();
613                r.push(b.components[i]);
614                r
615            })
616            .collect();
617        for col in 0..n {
618            let mut max_row = col;
619            let mut max_val = aug[col][col].abs();
620            for row in (col + 1)..n {
621                if aug[row][col].abs() > max_val {
622                    max_val = aug[row][col].abs();
623                    max_row = row;
624                }
625            }
626            if max_val < 1e-15 {
627                return None;
628            }
629            if max_row != col {
630                aug.swap(col, max_row);
631            }
632            let pivot = aug[col][col];
633            for row in (col + 1)..n {
634                let factor = aug[row][col] / pivot;
635                for j in col..=n {
636                    let val = aug[col][j];
637                    aug[row][j] -= factor * val;
638                }
639            }
640        }
641        let mut x = vec![0.0; n];
642        for i in (0..n).rev() {
643            let mut sum = aug[i][n];
644            for j in (i + 1)..n {
645                sum -= aug[i][j] * x[j];
646            }
647            if aug[i][i].abs() < 1e-15 {
648                return None;
649            }
650            x[i] = sum / aug[i][i];
651        }
652        Some(RnVector::new(x))
653    }
654    pub fn rank(&self) -> usize {
655        let (m, n) = (self.range_dim, self.domain_dim);
656        let mut a = self.matrix.clone();
657        let mut rank = 0;
658        for col in 0..n {
659            let mut pivot_row = None;
660            for row in rank..m {
661                if a[row][col].abs() > 1e-12 {
662                    pivot_row = Some(row);
663                    break;
664                }
665            }
666            let pr = match pivot_row {
667                Some(r) => r,
668                None => continue,
669            };
670            a.swap(rank, pr);
671            let pivot = a[rank][col];
672            for row in (rank + 1)..m {
673                let factor = a[row][col] / pivot;
674                for j in col..n {
675                    let val = a[rank][j];
676                    a[row][j] -= factor * val;
677                }
678            }
679            rank += 1;
680        }
681        rank
682    }
683    pub fn nullity(&self) -> usize {
684        self.domain_dim - self.rank()
685    }
686    pub fn is_injective(&self) -> bool {
687        self.nullity() == 0
688    }
689    pub fn is_surjective(&self) -> bool {
690        self.rank() == self.range_dim
691    }
692}
693/// LU decomposition result with partial pivoting: P*A = L*U.
694#[derive(Debug, Clone)]
695pub struct LuDecomposition {
696    pub l: BoundedOp,
697    pub u: BoundedOp,
698    pub permutation: Vec<usize>,
699}
700/// Data representing weak convergence in a Banach space.
701#[allow(dead_code)]
702#[derive(Debug, Clone)]
703pub struct WeakConvergenceData {
704    /// Sequence of vectors (represented as finite-dimensional approximations).
705    pub sequence: Vec<Vec<f64>>,
706    /// Weak limit, if it exists.
707    pub weak_limit: Option<Vec<f64>>,
708    /// Whether the sequence is bounded (Banach-Alaoglu prerequisite).
709    pub is_bounded: bool,
710}
711#[allow(dead_code)]
712impl WeakConvergenceData {
713    /// Creates weak convergence data.
714    pub fn new(sequence: Vec<Vec<f64>>) -> Self {
715        let is_bounded = sequence.iter().all(|v| {
716            let norm: f64 = v.iter().map(|&x| x * x).sum::<f64>().sqrt();
717            norm <= 1e6
718        });
719        WeakConvergenceData {
720            sequence,
721            weak_limit: None,
722            is_bounded,
723        }
724    }
725    /// Checks weak convergence to a given vector (tests against standard basis functionals).
726    pub fn check_weak_convergence(&self, limit: &[f64], tol: f64) -> bool {
727        if self.sequence.is_empty() {
728            return true;
729        }
730        let last = &self.sequence[self.sequence.len().saturating_sub(5)..];
731        last.iter().all(|v| {
732            limit
733                .iter()
734                .zip(v.iter())
735                .map(|(&l, &x)| (l - x).abs())
736                .fold(0.0f64, f64::max)
737                < tol
738        })
739    }
740    /// Banach-Alaoglu: bounded sequences in reflexive spaces have weakly convergent subsequences.
741    pub fn banach_alaoglu_applies(&self) -> bool {
742        self.is_bounded
743    }
744    /// Returns the strong norm of the last element.
745    pub fn last_norm(&self) -> Option<f64> {
746        self.sequence
747            .last()
748            .map(|v| v.iter().map(|&x| x * x).sum::<f64>().sqrt())
749    }
750}
751/// Data for a Fredholm operator T: X → Y.
752#[allow(dead_code)]
753#[derive(Debug, Clone)]
754pub struct FredholmOperatorData {
755    /// Description of the operator.
756    pub name: String,
757    /// Dimension of kernel (null space).
758    pub kernel_dim: usize,
759    /// Dimension of cokernel (Y / Im T).
760    pub cokernel_dim: usize,
761    /// Whether T is Fredholm.
762    pub is_fredholm: bool,
763}
764#[allow(dead_code)]
765impl FredholmOperatorData {
766    /// Creates Fredholm operator data.
767    pub fn new(name: &str, kernel_dim: usize, cokernel_dim: usize) -> Self {
768        FredholmOperatorData {
769            name: name.to_string(),
770            kernel_dim,
771            cokernel_dim,
772            is_fredholm: true,
773        }
774    }
775    /// Fredholm index: ind(T) = dim(ker T) - dim(coker T).
776    pub fn index(&self) -> i64 {
777        self.kernel_dim as i64 - self.cokernel_dim as i64
778    }
779    /// Checks Atkinson's theorem: T is Fredholm iff it is invertible modulo compact operators.
780    pub fn atkinson_description(&self) -> String {
781        format!("T = {} is Fredholm with index {}", self.name, self.index())
782    }
783    /// Returns the stability of the index under compact perturbations.
784    pub fn index_stable_under_compact(&self) -> bool {
785        true
786    }
787    /// Checks if T is an isomorphism (index 0 and injective).
788    pub fn is_isomorphism(&self) -> bool {
789        self.kernel_dim == 0 && self.cokernel_dim == 0
790    }
791    /// Essential spectrum: the set of λ for which T - λ is NOT Fredholm.
792    pub fn essential_spectrum_description(&self) -> String {
793        format!("σ_ess({}): values λ making T-λI non-Fredholm", self.name)
794    }
795}
796/// Represents a distribution (generalized function) as a linear functional on test functions.
797#[allow(dead_code)]
798#[derive(Debug, Clone)]
799pub struct Distribution {
800    /// Name of the distribution.
801    pub name: String,
802    /// Order: distributions of finite order.
803    pub order: Option<usize>,
804    /// Support description.
805    pub support: String,
806    /// Whether the distribution is a regular distribution (given by integration).
807    pub is_regular: bool,
808}
809#[allow(dead_code)]
810impl Distribution {
811    /// Creates a distribution.
812    pub fn new(name: &str) -> Self {
813        Distribution {
814            name: name.to_string(),
815            order: None,
816            support: "unknown".to_string(),
817            is_regular: false,
818        }
819    }
820    /// Creates the Dirac delta distribution.
821    pub fn dirac_delta(point: f64) -> Self {
822        Distribution {
823            name: format!("δ_{{{point}}}"),
824            order: Some(0),
825            support: point.to_string(),
826            is_regular: false,
827        }
828    }
829    /// Creates a regular distribution from L^1_loc.
830    pub fn regular(name: &str, support: &str) -> Self {
831        Distribution {
832            name: name.to_string(),
833            order: Some(0),
834            support: support.to_string(),
835            is_regular: true,
836        }
837    }
838    /// Differentiation raises the order by 1.
839    pub fn differentiate(&self) -> Distribution {
840        Distribution {
841            name: format!("d/dx ({})", self.name),
842            order: self.order.map(|o| o + 1),
843            support: self.support.clone(),
844            is_regular: false,
845        }
846    }
847    /// Returns the Fourier transform description.
848    pub fn fourier_transform_description(&self) -> String {
849        if self.name.starts_with('δ') {
850            "F[δ_a](ξ) = e^{-2πiαξ} (constant modulus 1)".to_string()
851        } else {
852            format!("F[{}](ξ) (distributional Fourier transform)", self.name)
853        }
854    }
855    /// Checks if the distribution is tempered (in S').
856    pub fn is_tempered(&self) -> bool {
857        self.order.map(|o| o <= 100).unwrap_or(false)
858    }
859}