1use crate::error::{FunctionalError, Result};
7use crate::phantom::Complete;
8use crate::space::traits::{
9 BanachSpace, HilbertSpace, InnerProductSpace, NormedSpace, VectorSpace,
10};
11use amari_core::Multivector;
12use core::marker::PhantomData;
13
14#[derive(Debug, Clone)]
34pub struct MultivectorHilbertSpace<const P: usize, const Q: usize, const R: usize> {
35 _phantom: PhantomData<(Multivector<P, Q, R>,)>,
36}
37
38impl<const P: usize, const Q: usize, const R: usize> Default for MultivectorHilbertSpace<P, Q, R> {
39 fn default() -> Self {
40 Self::new()
41 }
42}
43
44impl<const P: usize, const Q: usize, const R: usize> MultivectorHilbertSpace<P, Q, R> {
45 pub const DIM: usize = 1 << (P + Q + R);
47
48 pub fn new() -> Self {
50 Self {
51 _phantom: PhantomData,
52 }
53 }
54
55 pub fn signature(&self) -> (usize, usize, usize) {
57 (P, Q, R)
58 }
59
60 pub fn algebra_dimension(&self) -> usize {
62 Self::DIM
63 }
64
65 pub fn from_coefficients(&self, coefficients: &[f64]) -> Result<Multivector<P, Q, R>> {
67 if coefficients.len() != Self::DIM {
68 return Err(FunctionalError::dimension_mismatch(
69 Self::DIM,
70 coefficients.len(),
71 ));
72 }
73
74 Ok(Multivector::<P, Q, R>::from_slice(coefficients))
75 }
76
77 pub fn to_coefficients(&self, mv: &Multivector<P, Q, R>) -> Vec<f64> {
79 mv.to_vec()
80 }
81
82 pub fn basis_vector(&self, index: usize) -> Result<Multivector<P, Q, R>> {
84 if index >= Self::DIM {
85 return Err(FunctionalError::invalid_parameters(format!(
86 "Basis index {} out of range [0, {})",
87 index,
88 Self::DIM
89 )));
90 }
91
92 let mut coeffs = vec![0.0; Self::DIM];
93 coeffs[index] = 1.0;
94 self.from_coefficients(&coeffs)
95 }
96
97 pub fn basis(&self) -> Vec<Multivector<P, Q, R>> {
99 (0..Self::DIM)
100 .map(|i| self.basis_vector(i).unwrap())
101 .collect()
102 }
103}
104
105impl<const P: usize, const Q: usize, const R: usize> VectorSpace<Multivector<P, Q, R>, f64>
106 for MultivectorHilbertSpace<P, Q, R>
107{
108 fn add(&self, x: &Multivector<P, Q, R>, y: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
109 x.add(y)
110 }
111
112 fn sub(&self, x: &Multivector<P, Q, R>, y: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
113 x - y
114 }
115
116 fn scale(&self, scalar: f64, x: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
117 x * scalar
118 }
119
120 fn zero(&self) -> Multivector<P, Q, R> {
121 Multivector::<P, Q, R>::zero()
122 }
123
124 fn dimension(&self) -> Option<usize> {
125 Some(Self::DIM)
126 }
127}
128
129impl<const P: usize, const Q: usize, const R: usize> NormedSpace<Multivector<P, Q, R>, f64>
130 for MultivectorHilbertSpace<P, Q, R>
131{
132 fn norm(&self, x: &Multivector<P, Q, R>) -> f64 {
133 let coeffs = x.to_vec();
135 coeffs.iter().map(|c| c * c).sum::<f64>().sqrt()
136 }
137
138 fn normalize(&self, x: &Multivector<P, Q, R>) -> Option<Multivector<P, Q, R>> {
139 let n = self.norm(x);
140 if n < 1e-15 {
141 None
142 } else {
143 Some(self.scale(1.0 / n, x))
144 }
145 }
146}
147
148impl<const P: usize, const Q: usize, const R: usize> InnerProductSpace<Multivector<P, Q, R>, f64>
149 for MultivectorHilbertSpace<P, Q, R>
150{
151 fn inner_product(&self, x: &Multivector<P, Q, R>, y: &Multivector<P, Q, R>) -> f64 {
152 let x_coeffs = x.to_vec();
153 let y_coeffs = y.to_vec();
154 x_coeffs
155 .iter()
156 .zip(y_coeffs.iter())
157 .map(|(a, b)| a * b)
158 .sum()
159 }
160
161 fn project(&self, x: &Multivector<P, Q, R>, y: &Multivector<P, Q, R>) -> Multivector<P, Q, R> {
162 let ip_xy = self.inner_product(x, y);
163 let ip_yy = self.inner_product(y, y);
164 if ip_yy.abs() < 1e-15 {
165 self.zero()
166 } else {
167 self.scale(ip_xy / ip_yy, y)
168 }
169 }
170
171 fn gram_schmidt(&self, vectors: &[Multivector<P, Q, R>]) -> Vec<Multivector<P, Q, R>> {
172 let mut orthonormal = Vec::new();
173 for v in vectors {
174 let mut u = v.clone();
175 for q in &orthonormal {
176 let proj = self.project(&u, q);
177 u = self.sub(&u, &proj);
178 }
179 if let Some(normalized) = self.normalize(&u) {
180 orthonormal.push(normalized);
181 }
182 }
183 orthonormal
184 }
185}
186
187impl<const P: usize, const Q: usize, const R: usize>
188 BanachSpace<Multivector<P, Q, R>, f64, Complete> for MultivectorHilbertSpace<P, Q, R>
189{
190 fn is_cauchy_sequence(&self, sequence: &[Multivector<P, Q, R>], tolerance: f64) -> bool {
191 if sequence.len() < 2 {
192 return true;
193 }
194
195 let n = sequence.len();
197 for i in (n.saturating_sub(5))..n {
198 for j in (i + 1)..n {
199 if self.distance(&sequence[i], &sequence[j]) > tolerance {
200 return false;
201 }
202 }
203 }
204 true
205 }
206
207 fn sequence_limit(
208 &self,
209 sequence: &[Multivector<P, Q, R>],
210 tolerance: f64,
211 ) -> Result<Multivector<P, Q, R>> {
212 if sequence.is_empty() {
213 return Err(FunctionalError::convergence_error(
214 0,
215 "Empty sequence has no limit",
216 ));
217 }
218
219 if !self.is_cauchy_sequence(sequence, tolerance) {
220 return Err(FunctionalError::convergence_error(
221 sequence.len(),
222 "Sequence is not Cauchy",
223 ));
224 }
225
226 Ok(sequence.last().unwrap().clone())
229 }
230}
231
232impl<const P: usize, const Q: usize, const R: usize>
233 HilbertSpace<Multivector<P, Q, R>, f64, Complete> for MultivectorHilbertSpace<P, Q, R>
234{
235 fn riesz_representative<F>(&self, functional: F) -> Result<Multivector<P, Q, R>>
236 where
237 F: Fn(&Multivector<P, Q, R>) -> f64,
238 {
239 let mut coeffs = Vec::with_capacity(Self::DIM);
242 for i in 0..Self::DIM {
243 let basis_i = self.basis_vector(i)?;
244 coeffs.push(functional(&basis_i));
245 }
246 self.from_coefficients(&coeffs)
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253
254 #[test]
255 fn test_hilbert_space_creation() {
256 let space: MultivectorHilbertSpace<3, 0, 0> = MultivectorHilbertSpace::new();
257 assert_eq!(space.algebra_dimension(), 8);
258 assert_eq!(space.signature(), (3, 0, 0));
259 }
260
261 #[test]
262 fn test_basis_vectors() {
263 let space: MultivectorHilbertSpace<2, 0, 0> = MultivectorHilbertSpace::new();
264 let basis = space.basis();
265 assert_eq!(basis.len(), 4); for (i, bi) in basis.iter().enumerate() {
269 assert!((space.norm(bi) - 1.0).abs() < 1e-10);
270 for (j, bj) in basis.iter().enumerate() {
271 if i != j {
272 assert!(space.inner_product(bi, bj).abs() < 1e-10);
273 }
274 }
275 }
276 }
277
278 #[test]
279 fn test_vector_space_operations() {
280 let space: MultivectorHilbertSpace<2, 0, 0> = MultivectorHilbertSpace::new();
281
282 let x = space.from_coefficients(&[1.0, 2.0, 3.0, 4.0]).unwrap();
283 let y = space.from_coefficients(&[5.0, 6.0, 7.0, 8.0]).unwrap();
284
285 let sum = space.add(&x, &y);
286 let sum_coeffs = space.to_coefficients(&sum);
287 assert_eq!(sum_coeffs, vec![6.0, 8.0, 10.0, 12.0]);
288
289 let scaled = space.scale(2.0, &x);
290 let scaled_coeffs = space.to_coefficients(&scaled);
291 assert_eq!(scaled_coeffs, vec![2.0, 4.0, 6.0, 8.0]);
292 }
293
294 #[test]
295 fn test_inner_product() {
296 let space: MultivectorHilbertSpace<2, 0, 0> = MultivectorHilbertSpace::new();
297
298 let x = space.from_coefficients(&[1.0, 0.0, 0.0, 0.0]).unwrap();
299 let y = space.from_coefficients(&[0.0, 1.0, 0.0, 0.0]).unwrap();
300 let z = space.from_coefficients(&[1.0, 1.0, 0.0, 0.0]).unwrap();
301
302 assert!(space.inner_product(&x, &y).abs() < 1e-10);
304 assert!(!space.are_orthogonal(&x, &z, 1e-10));
305
306 assert!((space.inner_product(&z, &z) - 2.0).abs() < 1e-10);
308 assert!((space.norm(&z) - 2.0_f64.sqrt()).abs() < 1e-10);
309 }
310
311 #[test]
312 fn test_gram_schmidt() {
313 let space: MultivectorHilbertSpace<2, 0, 0> = MultivectorHilbertSpace::new();
314
315 let v1 = space.from_coefficients(&[1.0, 1.0, 0.0, 0.0]).unwrap();
316 let v2 = space.from_coefficients(&[1.0, 0.0, 1.0, 0.0]).unwrap();
317 let v3 = space.from_coefficients(&[0.0, 1.0, 1.0, 0.0]).unwrap();
318
319 let orthonormal = space.gram_schmidt(&[v1, v2, v3]);
320 assert_eq!(orthonormal.len(), 3);
321
322 assert!(space.is_orthonormal(&orthonormal, 1e-10));
324 }
325
326 #[test]
327 fn test_riesz_representation() {
328 let space: MultivectorHilbertSpace<2, 0, 0> = MultivectorHilbertSpace::new();
329
330 let functional = |x: &Multivector<2, 0, 0>| {
332 let coeffs = x.to_vec();
333 coeffs[0] + 2.0 * coeffs[1]
334 };
335
336 let repr = space.riesz_representative(functional).unwrap();
337 let repr_coeffs = space.to_coefficients(&repr);
338
339 assert!((repr_coeffs[0] - 1.0).abs() < 1e-10);
341 assert!((repr_coeffs[1] - 2.0).abs() < 1e-10);
342 assert!(repr_coeffs[2].abs() < 1e-10);
343 assert!(repr_coeffs[3].abs() < 1e-10);
344 }
345
346 #[test]
347 fn test_projection() {
348 let space: MultivectorHilbertSpace<2, 0, 0> = MultivectorHilbertSpace::new();
349
350 let x = space.from_coefficients(&[3.0, 4.0, 0.0, 0.0]).unwrap();
351 let y = space.from_coefficients(&[1.0, 0.0, 0.0, 0.0]).unwrap();
352
353 let proj = space.project(&x, &y);
354 let proj_coeffs = space.to_coefficients(&proj);
355
356 assert!((proj_coeffs[0] - 3.0).abs() < 1e-10);
358 assert!(proj_coeffs[1].abs() < 1e-10);
359 }
360
361 #[test]
362 fn test_best_approximation() {
363 let space: MultivectorHilbertSpace<2, 0, 0> = MultivectorHilbertSpace::new();
364
365 let x = space.from_coefficients(&[1.0, 2.0, 3.0, 4.0]).unwrap();
366
367 let e0 = space.basis_vector(0).unwrap();
369 let e1 = space.basis_vector(1).unwrap();
370
371 let approx = space.best_approximation(&x, &[e0, e1]);
372 let approx_coeffs = space.to_coefficients(&approx);
373
374 assert!((approx_coeffs[0] - 1.0).abs() < 1e-10);
376 assert!((approx_coeffs[1] - 2.0).abs() < 1e-10);
377 assert!(approx_coeffs[2].abs() < 1e-10);
378 assert!(approx_coeffs[3].abs() < 1e-10);
379 }
380}