1use crate::gauss::{Rule, legendre_generic};
4use crate::kernel::{AbstractKernel, CentrosymmKernel, KernelProperties, SVEHints, SymmetryType};
5use crate::kernelmatrix::{matrix_from_gauss_noncentrosymmetric, matrix_from_gauss_with_segments};
6use crate::numeric::CustomNumeric;
7use crate::poly::PiecewiseLegendrePolyVector;
8use mdarray::DTensor;
9
10use super::result::SVEResult;
11use super::utils::{extend_to_full_domain, merge_results, remove_weights, svd_to_polynomials};
12
13pub trait SVEStrategy<T: CustomNumeric> {
15 fn matrices(&self) -> Vec<DTensor<T, 2>>;
17
18 fn postprocess(
20 &self,
21 u_list: Vec<DTensor<T, 2>>,
22 s_list: Vec<Vec<T>>,
23 v_list: Vec<DTensor<T, 2>>,
24 ) -> SVEResult;
25}
26
27pub struct SamplingSVE<T>
38where
39 T: CustomNumeric + Send + Sync + 'static,
40{
41 segments_x: Vec<T>,
42 segments_y: Vec<T>,
43 gauss_x: Rule<T>,
44 gauss_y: Rule<T>,
45 #[allow(dead_code)]
46 epsilon: f64,
47 n_gauss: usize,
48}
49
50impl<T> SamplingSVE<T>
51where
52 T: CustomNumeric + Send + Sync + 'static,
53{
54 pub fn new(
59 segments_x: Vec<T>,
60 segments_y: Vec<T>,
61 gauss_x: Rule<T>,
62 gauss_y: Rule<T>,
63 epsilon: f64,
64 n_gauss: usize,
65 ) -> Self {
66 Self {
67 segments_x,
68 segments_y,
69 gauss_x,
70 gauss_y,
71 epsilon,
72 n_gauss,
73 }
74 }
75
76 pub fn postprocess_single(
81 &self,
82 u: &DTensor<T, 2>,
83 s: &[T],
84 v: &DTensor<T, 2>,
85 ) -> (
86 PiecewiseLegendrePolyVector,
87 Vec<f64>,
88 PiecewiseLegendrePolyVector,
89 ) {
90 let u_unweighted = remove_weights(u, self.gauss_x.w.as_slice(), true);
93 let v_unweighted = remove_weights(v, self.gauss_y.w.as_slice(), true);
94
95 let gauss_rule_f64 = legendre_generic::<f64>(self.n_gauss);
97 let u_polys = svd_to_polynomials(
98 &u_unweighted,
99 &self.segments_x,
100 &gauss_rule_f64,
101 self.n_gauss,
102 );
103 let v_polys = svd_to_polynomials(
104 &v_unweighted,
105 &self.segments_y,
106 &gauss_rule_f64,
107 self.n_gauss,
108 );
109
110 (
112 PiecewiseLegendrePolyVector::new(u_polys),
113 s.iter().map(|&x| x.to_f64()).collect(),
114 PiecewiseLegendrePolyVector::new(v_polys),
115 )
116 }
117}
118
119pub struct CentrosymmSVE<T, K>
124where
125 T: CustomNumeric + Send + Sync + 'static,
126 K: CentrosymmKernel + KernelProperties,
127{
128 kernel: K,
129 epsilon: f64,
130 hints: K::SVEHintsType<T>,
131 #[allow(dead_code)]
132 n_gauss: usize,
133
134 #[allow(dead_code)]
136 segments_x: Vec<T>,
137 #[allow(dead_code)]
138 segments_y: Vec<T>,
139 gauss_x: Rule<T>,
140 gauss_y: Rule<T>,
141
142 sampling_sve: SamplingSVE<T>,
144}
145
146impl<T, K> CentrosymmSVE<T, K>
147where
148 T: CustomNumeric + Send + Sync + Clone + 'static,
149 K: CentrosymmKernel + KernelProperties + Clone,
150 K::SVEHintsType<T>: SVEHints<T> + Clone,
151{
152 pub fn new(kernel: K, epsilon: f64) -> Self {
154 let hints = kernel.sve_hints::<T>(epsilon);
155
156 let segments_x = hints.segments_x();
158 let segments_y = hints.segments_y();
159 let n_gauss = hints.ngauss();
160
161 let rule = legendre_generic::<T>(n_gauss);
163 let gauss_x = rule.piecewise(&segments_x);
164 let gauss_y = rule.piecewise(&segments_y);
165
166 let sampling_sve = SamplingSVE::new(
168 segments_x.clone(),
169 segments_y.clone(),
170 gauss_x.clone(),
171 gauss_y.clone(),
172 epsilon,
173 n_gauss,
174 );
175
176 Self {
177 kernel,
178 epsilon,
179 hints,
180 n_gauss,
181 segments_x,
182 segments_y,
183 gauss_x,
184 gauss_y,
185 sampling_sve,
186 }
187 }
188
189 fn compute_reduced_matrix(&self, symmetry: SymmetryType) -> DTensor<T, 2> {
191 let discretized = matrix_from_gauss_with_segments(
194 &self.kernel,
195 &self.gauss_x,
196 &self.gauss_y,
197 symmetry,
198 &self.hints,
199 );
200
201 discretized.apply_weights_for_sve()
204 }
205
206 fn extend_result_to_full_domain(
208 &self,
209 result: (
210 PiecewiseLegendrePolyVector,
211 Vec<f64>,
212 PiecewiseLegendrePolyVector,
213 ),
214 symmetry: SymmetryType,
215 ) -> (
216 PiecewiseLegendrePolyVector,
217 Vec<f64>,
218 PiecewiseLegendrePolyVector,
219 ) {
220 let (u, s, v) = result;
221
222 let u_full = extend_to_full_domain(u.get_polys().to_vec(), symmetry, self.kernel.xmax());
224 let v_full = extend_to_full_domain(v.get_polys().to_vec(), symmetry, self.kernel.ymax());
225
226 (
227 PiecewiseLegendrePolyVector::new(u_full),
228 s,
229 PiecewiseLegendrePolyVector::new(v_full),
230 )
231 }
232}
233
234impl<T, K> SVEStrategy<T> for CentrosymmSVE<T, K>
235where
236 T: CustomNumeric + Send + Sync + Clone + 'static,
237 K: CentrosymmKernel + KernelProperties + Clone,
238 K::SVEHintsType<T>: SVEHints<T> + Clone,
239{
240 fn matrices(&self) -> Vec<DTensor<T, 2>> {
241 let even_matrix = self.compute_reduced_matrix(SymmetryType::Even);
243 let odd_matrix = self.compute_reduced_matrix(SymmetryType::Odd);
244
245 vec![even_matrix, odd_matrix]
246 }
247
248 fn postprocess(
249 &self,
250 u_list: Vec<DTensor<T, 2>>,
251 s_list: Vec<Vec<T>>,
252 v_list: Vec<DTensor<T, 2>>,
253 ) -> SVEResult {
254 let result_even = self
256 .sampling_sve
257 .postprocess_single(&u_list[0], &s_list[0], &v_list[0]);
258 let result_odd = self
259 .sampling_sve
260 .postprocess_single(&u_list[1], &s_list[1], &v_list[1]);
261
262 let result_even_full = self.extend_result_to_full_domain(result_even, SymmetryType::Even);
264 let result_odd_full = self.extend_result_to_full_domain(result_odd, SymmetryType::Odd);
265
266 merge_results(result_even_full, result_odd_full, self.epsilon)
268 }
269}
270
271#[allow(dead_code)]
277pub struct NonCentrosymmSVE<T, K>
278where
279 T: CustomNumeric + Send + Sync + 'static,
280 K: AbstractKernel + KernelProperties,
281{
282 kernel: K,
283 epsilon: f64,
284 hints: K::SVEHintsType<T>,
285 n_gauss: usize,
286
287 segments_x: Vec<T>,
289 segments_y: Vec<T>,
290 gauss_x: Rule<T>,
291 gauss_y: Rule<T>,
292
293 sampling_sve: SamplingSVE<T>,
295}
296
297impl<T, K> NonCentrosymmSVE<T, K>
298where
299 T: CustomNumeric + Send + Sync + Clone + 'static,
300 K: AbstractKernel + KernelProperties + Clone,
301 K::SVEHintsType<T>: SVEHints<T> + Clone,
302{
303 pub fn new(kernel: K, epsilon: f64) -> Self {
305 let hints = kernel.sve_hints::<T>(epsilon);
306
307 let segments_x = hints.segments_x();
309 let segments_y = hints.segments_y();
310 let n_gauss = hints.ngauss();
311
312 let rule = legendre_generic::<T>(n_gauss);
314 let gauss_x = rule.piecewise(&segments_x);
315 let gauss_y = rule.piecewise(&segments_y);
316
317 let sampling_sve = SamplingSVE::new(
319 segments_x.clone(),
320 segments_y.clone(),
321 gauss_x.clone(),
322 gauss_y.clone(),
323 epsilon,
324 n_gauss,
325 );
326
327 Self {
328 kernel,
329 epsilon,
330 hints,
331 n_gauss,
332 segments_x,
333 segments_y,
334 gauss_x,
335 gauss_y,
336 sampling_sve,
337 }
338 }
339
340 fn compute_matrix(&self) -> DTensor<T, 2> {
342 let discretized = matrix_from_gauss_noncentrosymmetric(
344 &self.kernel,
345 &self.gauss_x,
346 &self.gauss_y,
347 &self.hints,
348 );
349
350 discretized.apply_weights_for_sve()
352 }
353}
354
355impl<T, K> SVEStrategy<T> for NonCentrosymmSVE<T, K>
356where
357 T: CustomNumeric + Send + Sync + Clone + 'static,
358 K: AbstractKernel + KernelProperties + Clone,
359 K::SVEHintsType<T>: SVEHints<T> + Clone,
360{
361 fn matrices(&self) -> Vec<DTensor<T, 2>> {
362 vec![self.compute_matrix()]
364 }
365
366 fn postprocess(
367 &self,
368 u_list: Vec<DTensor<T, 2>>,
369 s_list: Vec<Vec<T>>,
370 v_list: Vec<DTensor<T, 2>>,
371 ) -> SVEResult {
372 let (u_polys, s, v_polys) = self
374 .sampling_sve
375 .postprocess_single(&u_list[0], &s_list[0], &v_list[0]);
376
377 SVEResult::new(u_polys, s, v_polys, self.epsilon)
379 }
380}