1use crate::phantom::HkRegularity;
22use crate::space::Domain;
23use amari_core::Multivector;
24use std::marker::PhantomData;
25use std::sync::Arc;
26
27#[derive(Clone)]
31pub struct SobolevFunction<const P: usize, const Q: usize, const R: usize, const K: usize> {
32 func: Arc<dyn Fn(&[f64]) -> Multivector<P, Q, R> + Send + Sync>,
34 derivatives: Vec<Arc<dyn Fn(&[f64]) -> Multivector<P, Q, R> + Send + Sync>>,
37 spatial_dim: usize,
39}
40
41impl<const P: usize, const Q: usize, const R: usize, const K: usize> std::fmt::Debug
42 for SobolevFunction<P, Q, R, K>
43{
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 f.debug_struct("SobolevFunction")
46 .field("regularity", &K)
47 .field("spatial_dim", &self.spatial_dim)
48 .finish()
49 }
50}
51
52impl<const P: usize, const Q: usize, const R: usize, const K: usize> SobolevFunction<P, Q, R, K> {
53 pub fn new<F>(
55 func: F,
56 derivatives: Vec<Arc<dyn Fn(&[f64]) -> Multivector<P, Q, R> + Send + Sync>>,
57 spatial_dim: usize,
58 ) -> Self
59 where
60 F: Fn(&[f64]) -> Multivector<P, Q, R> + Send + Sync + 'static,
61 {
62 Self {
63 func: Arc::new(func),
64 derivatives,
65 spatial_dim,
66 }
67 }
68
69 pub fn eval(&self, point: &[f64]) -> Multivector<P, Q, R> {
71 (self.func)(point)
72 }
73
74 pub fn eval_derivative(&self, i: usize, point: &[f64]) -> Option<Multivector<P, Q, R>> {
76 self.derivatives.get(i).map(|d| d(point))
77 }
78
79 pub fn derivative_count(&self) -> usize {
81 self.derivatives.len()
82 }
83
84 pub fn spatial_dimension(&self) -> usize {
86 self.spatial_dim
87 }
88}
89
90#[derive(Clone)]
95pub struct SobolevSpace<const P: usize, const Q: usize, const R: usize, const K: usize> {
96 domain: Domain<f64>,
98 quadrature_points: usize,
100 _phantom: PhantomData<HkRegularity<K>>,
101}
102
103impl<const P: usize, const Q: usize, const R: usize, const K: usize> std::fmt::Debug
104 for SobolevSpace<P, Q, R, K>
105{
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 f.debug_struct("SobolevSpace")
108 .field("order", &K)
109 .field("signature", &(P, Q, R))
110 .field("domain", &self.domain)
111 .finish()
112 }
113}
114
115impl<const P: usize, const Q: usize, const R: usize, const K: usize> SobolevSpace<P, Q, R, K> {
116 pub fn new(domain: Domain<f64>) -> Self {
118 Self {
119 domain,
120 quadrature_points: 32,
121 _phantom: PhantomData,
122 }
123 }
124
125 pub fn unit_interval() -> Self {
127 Self::new(Domain::interval(0.0, 1.0))
128 }
129
130 pub fn with_quadrature_points(mut self, n: usize) -> Self {
132 self.quadrature_points = n;
133 self
134 }
135
136 pub fn order(&self) -> usize {
138 K
139 }
140
141 pub fn domain(&self) -> &Domain<f64> {
143 &self.domain
144 }
145
146 pub fn hk_inner_product(
150 &self,
151 f: &SobolevFunction<P, Q, R, K>,
152 g: &SobolevFunction<P, Q, R, K>,
153 ) -> f64 {
154 let (a, b) = self.domain.bounds_1d().unwrap_or((0.0, 1.0));
155 let h = (b - a) / self.quadrature_points as f64;
156
157 let mut sum = 0.0;
158
159 for i in 0..self.quadrature_points {
160 let x = a + (i as f64 + 0.5) * h;
161
162 let fx = f.eval(&[x]);
164 let gx = g.eval(&[x]);
165 let fx_coeffs = fx.to_vec();
166 let gx_coeffs = gx.to_vec();
167 let l2_part: f64 = fx_coeffs
168 .iter()
169 .zip(gx_coeffs.iter())
170 .map(|(a, b)| a * b)
171 .sum();
172 sum += l2_part;
173
174 let num_derivs = f.derivative_count().min(g.derivative_count());
176 for j in 0..num_derivs {
177 if let (Some(df), Some(dg)) =
178 (f.eval_derivative(j, &[x]), g.eval_derivative(j, &[x]))
179 {
180 let df_coeffs = df.to_vec();
181 let dg_coeffs = dg.to_vec();
182 let deriv_part: f64 = df_coeffs
183 .iter()
184 .zip(dg_coeffs.iter())
185 .map(|(a, b)| a * b)
186 .sum();
187 sum += deriv_part;
188 }
189 }
190 }
191
192 sum * h
193 }
194
195 pub fn hk_norm(&self, f: &SobolevFunction<P, Q, R, K>) -> f64 {
197 self.hk_inner_product(f, f).sqrt()
198 }
199
200 pub fn hk_seminorm(&self, f: &SobolevFunction<P, Q, R, K>) -> f64 {
202 let (a, b) = self.domain.bounds_1d().unwrap_or((0.0, 1.0));
203 let h = (b - a) / self.quadrature_points as f64;
204
205 let mut sum = 0.0;
206
207 if f.derivative_count() > 0 {
209 let last_deriv_idx = f.derivative_count() - 1;
210 for i in 0..self.quadrature_points {
211 let x = a + (i as f64 + 0.5) * h;
212 if let Some(df) = f.eval_derivative(last_deriv_idx, &[x]) {
213 let df_coeffs = df.to_vec();
214 let norm_sq: f64 = df_coeffs.iter().map(|c| c * c).sum();
215 sum += norm_sq;
216 }
217 }
218 }
219
220 (sum * h).sqrt()
221 }
222}
223
224pub type H1Space<const P: usize, const Q: usize, const R: usize> = SobolevSpace<P, Q, R, 1>;
226
227pub type H2Space<const P: usize, const Q: usize, const R: usize> = SobolevSpace<P, Q, R, 2>;
229
230pub fn poincare_constant_estimate(domain: &Domain<f64>) -> f64 {
235 match domain {
236 Domain::Interval { a, b } => {
237 (b - a) / std::f64::consts::PI
239 }
240 Domain::Rectangle { bounds } => {
241 bounds
243 .iter()
244 .map(|(a, b)| (b - a) / std::f64::consts::PI)
245 .fold(f64::MAX, f64::min)
246 }
247 Domain::RealLine => f64::INFINITY,
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254
255 #[test]
256 fn test_sobolev_space_creation() {
257 let h1: H1Space<2, 0, 0> = H1Space::unit_interval();
258 assert_eq!(h1.order(), 1);
259 }
260
261 #[test]
262 fn test_sobolev_function() {
263 let f: SobolevFunction<2, 0, 0, 1> = SobolevFunction::new(
265 |x| Multivector::<2, 0, 0>::scalar(x[0]),
266 vec![Arc::new(|_x| Multivector::<2, 0, 0>::scalar(1.0))],
267 1,
268 );
269
270 let val = f.eval(&[0.5]);
271 assert!((val.scalar_part() - 0.5).abs() < 1e-10);
272
273 let deriv = f.eval_derivative(0, &[0.5]).unwrap();
274 assert!((deriv.scalar_part() - 1.0).abs() < 1e-10);
275 }
276
277 #[test]
278 fn test_hk_norm() {
279 let h1: H1Space<2, 0, 0> = H1Space::unit_interval().with_quadrature_points(64);
280
281 let zero: SobolevFunction<2, 0, 0, 1> = SobolevFunction::new(
283 |_| Multivector::<2, 0, 0>::zero(),
284 vec![Arc::new(|_| Multivector::<2, 0, 0>::zero())],
285 1,
286 );
287
288 let norm = h1.hk_norm(&zero);
289 assert!(norm < 1e-10);
290 }
291
292 #[test]
293 fn test_h1_inner_product() {
294 let h1: H1Space<2, 0, 0> = H1Space::unit_interval().with_quadrature_points(64);
295
296 let f: SobolevFunction<2, 0, 0, 1> = SobolevFunction::new(
298 |x| Multivector::<2, 0, 0>::scalar(x[0]),
299 vec![Arc::new(|_| Multivector::<2, 0, 0>::scalar(1.0))],
300 1,
301 );
302
303 let norm_sq = h1.hk_inner_product(&f, &f);
307 assert!((norm_sq - 4.0 / 3.0).abs() < 0.1);
308 }
309
310 #[test]
311 fn test_poincare_constant() {
312 let domain = Domain::interval(0.0, 1.0);
313 let c = poincare_constant_estimate(&domain);
314
315 assert!((c - 1.0 / std::f64::consts::PI).abs() < 1e-10);
317 }
318
319 #[test]
320 fn test_h2_space() {
321 let h2: H2Space<2, 0, 0> = H2Space::unit_interval();
322 assert_eq!(h2.order(), 2);
323 }
324
325 #[test]
326 fn test_seminorm() {
327 let h1: H1Space<2, 0, 0> = H1Space::unit_interval().with_quadrature_points(64);
328
329 let f: SobolevFunction<2, 0, 0, 1> = SobolevFunction::new(
331 |x| Multivector::<2, 0, 0>::scalar(x[0]),
332 vec![Arc::new(|_| Multivector::<2, 0, 0>::scalar(1.0))],
333 1,
334 );
335
336 let seminorm_sq = h1.hk_seminorm(&f).powi(2);
338 assert!((seminorm_sq - 1.0).abs() < 0.1);
339 }
340}