amari_functional/operator/
traits.rs1use crate::error::Result;
7use crate::phantom::{Bounded, BoundednessProperty, SelfAdjoint, SymmetryProperty};
8
9pub trait LinearOperator<V, W = V> {
20 fn apply(&self, x: &V) -> Result<W>;
22
23 fn domain_dimension(&self) -> Option<usize>;
25
26 fn codomain_dimension(&self) -> Option<usize>;
28}
29
30pub trait BoundedOperator<V, W = V, B = Bounded>: LinearOperator<V, W>
43where
44 B: BoundednessProperty,
45{
46 fn operator_norm(&self) -> f64;
48
49 fn is_bounded_by(&self, bound: f64) -> bool {
51 self.operator_norm() <= bound
52 }
53}
54
55pub trait OperatorNorm {
57 fn norm(&self) -> f64;
59
60 fn frobenius_norm(&self) -> Option<f64> {
62 None
63 }
64}
65
66pub trait AdjointableOperator<V>: LinearOperator<V, V> {
75 type Adjoint: LinearOperator<V, V>;
77
78 fn adjoint(&self) -> Self::Adjoint;
80
81 fn is_self_adjoint(&self) -> bool;
83
84 fn is_normal(&self) -> bool;
86}
87
88pub trait SelfAdjointOperator<V, S = SelfAdjoint>: LinearOperator<V, V>
100where
101 S: SymmetryProperty,
102{
103 fn has_real_spectrum(&self) -> bool {
105 true
106 }
107
108 fn quadratic_form(&self, x: &V) -> Result<f64>;
110
111 fn is_positive(&self, x: &V) -> Result<bool> {
113 Ok(self.quadratic_form(x)? >= 0.0)
114 }
115
116 fn is_positive_definite(&self, x: &V, tolerance: f64) -> Result<bool> {
118 Ok(self.quadratic_form(x)? > tolerance)
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use amari_core::Multivector;
126
127 struct TestIdentity;
129
130 impl LinearOperator<Multivector<2, 0, 0>> for TestIdentity {
131 fn apply(&self, x: &Multivector<2, 0, 0>) -> Result<Multivector<2, 0, 0>> {
132 Ok(x.clone())
133 }
134
135 fn domain_dimension(&self) -> Option<usize> {
136 Some(4) }
138
139 fn codomain_dimension(&self) -> Option<usize> {
140 Some(4)
141 }
142 }
143
144 impl BoundedOperator<Multivector<2, 0, 0>> for TestIdentity {
145 fn operator_norm(&self) -> f64 {
146 1.0
147 }
148 }
149
150 #[test]
151 fn test_linear_operator_apply() {
152 let identity = TestIdentity;
153 let x = Multivector::<2, 0, 0>::from_slice(&[1.0, 2.0, 3.0, 4.0]);
154 let y = identity.apply(&x).unwrap();
155 assert_eq!(x.to_vec(), y.to_vec());
156 }
157
158 #[test]
159 fn test_bounded_operator_norm() {
160 let identity = TestIdentity;
161 assert!((identity.operator_norm() - 1.0).abs() < 1e-10);
162 assert!(identity.is_bounded_by(1.0));
163 assert!(identity.is_bounded_by(2.0));
164 assert!(!identity.is_bounded_by(0.5));
165 }
166
167 #[test]
168 fn test_operator_dimensions() {
169 let identity = TestIdentity;
170 assert_eq!(identity.domain_dimension(), Some(4));
171 assert_eq!(identity.codomain_dimension(), Some(4));
172 }
173}