1use crate::gauss::Rule;
4use crate::interpolation1d::legendre_collocation_matrix;
5use crate::kernel::SymmetryType;
6use crate::numeric::CustomNumeric;
7use crate::poly::{PiecewiseLegendrePoly, PiecewiseLegendrePolyVector};
8use mdarray::DTensor;
9
10pub fn remove_weights<T: CustomNumeric>(
26 matrix: &DTensor<T, 2>,
27 weights: &[T],
28 is_row: bool,
29) -> DTensor<T, 2> {
30 let mut result = matrix.clone();
31
32 let shape = *result.shape();
33 if is_row {
34 for i in 0..shape.0 {
36 let sqrt_weight = weights[i].sqrt();
37 for j in 0..shape.1 {
38 result[[i, j]] = result[[i, j]] / sqrt_weight;
39 }
40 }
41 } else {
42 for j in 0..shape.1 {
44 let sqrt_weight = weights[j].sqrt();
45 for i in 0..shape.0 {
46 result[[i, j]] = result[[i, j]] / sqrt_weight;
47 }
48 }
49 }
50
51 result
52}
53
54pub fn extend_to_full_domain(
75 polys: Vec<PiecewiseLegendrePoly>,
76 symmetry: SymmetryType,
77 _xmax: f64,
78) -> Vec<PiecewiseLegendrePoly> {
79 let sign = symmetry.sign() as f64;
80 let symm = symmetry.sign(); let n_poly_coeffs = if !polys.is_empty() {
85 polys[0].data.shape().0
86 } else {
87 return Vec::new();
88 };
89
90 let poly_flip_x: Vec<f64> = (0..n_poly_coeffs)
91 .map(|i| if i % 2 == 0 { 1.0 } else { -1.0 })
92 .collect();
93
94 polys
95 .into_iter()
96 .map(|poly| {
97 let knots_pos = &poly.knots;
99 let mut full_segments = Vec::new();
100 for i in (0..knots_pos.len()).rev() {
101 full_segments.push(-knots_pos[i]);
102 }
103 for i in 1..knots_pos.len() {
104 full_segments.push(knots_pos[i]);
105 }
106
107 let pos_data = DTensor::<f64, 2>::from_fn(*poly.data.shape(), |idx| {
109 poly.data[idx] / 2.0_f64.sqrt()
110 });
111
112 let pos_shape = *pos_data.shape();
114 let mut neg_data = DTensor::<f64, 2>::from_fn([pos_shape.0, pos_shape.1], |idx| {
115 let reversed_col = pos_shape.1 - 1 - idx[1];
117 pos_data[[idx[0], reversed_col]]
118 });
119
120 for (i, &flip_sign) in poly_flip_x.iter().enumerate() {
122 let coeff_sign = flip_sign * sign;
123 for j in 0..pos_shape.1 {
124 neg_data[[i, j]] *= coeff_sign;
125 }
126 }
127
128 let combined_data = DTensor::<f64, 2>::from_fn([pos_shape.0, pos_shape.1 * 2], |idx| {
130 if idx[1] < pos_shape.1 {
131 neg_data[[idx[0], idx[1]]]
132 } else {
133 pos_data[[idx[0], idx[1] - pos_shape.1]]
134 }
135 });
136
137 PiecewiseLegendrePoly::new(
140 combined_data,
141 full_segments,
142 poly.l,
143 None, symm, )
146 })
147 .collect()
148}
149
150pub fn svd_to_polynomials<T: CustomNumeric>(
166 u_or_v: &DTensor<T, 2>,
167 segments: &[T],
168 gauss_rule: &Rule<f64>,
169 n_gauss: usize,
170) -> Vec<PiecewiseLegendrePoly> {
171 let n_segments = segments.len() - 1;
172 let n_svals = u_or_v.shape().1;
173 let n_rows = u_or_v.shape().0;
174
175 let mut tensor_3d = DTensor::<f64, 3>::zeros([n_gauss, n_segments, n_svals]);
179 for i in 0..n_gauss {
180 for j in 0..n_segments {
181 for k in 0..n_svals {
182 let row_idx = j * n_gauss + i;
183 if row_idx < n_rows {
184 tensor_3d[[i, j, k]] = u_or_v[[row_idx, k]].to_f64();
185 } else {
186 tensor_3d[[i, j, k]] = 0.0;
188 }
189 }
190 }
191 }
192
193 let cmat = legendre_collocation_matrix(gauss_rule);
195
196 let cmat_shape = *cmat.shape();
198 let mut u_data = DTensor::<f64, 3>::zeros([cmat_shape.0, n_segments, n_svals]);
199 for j in 0..n_segments {
200 for k in 0..n_svals {
201 for i in 0..cmat_shape.0 {
202 let mut sum = 0.0;
203 for l in 0..n_gauss {
204 sum += cmat[[i, l]] * tensor_3d[[l, j, k]];
205 }
206 u_data[[i, j, k]] = sum;
207 }
208 }
209 }
210
211 let mut dsegs = Vec::new();
213 for i in 0..segments.len() - 1 {
214 dsegs.push(segments[i + 1].to_f64() - segments[i].to_f64());
215 }
216
217 let u_data_shape = *u_data.shape();
218 for j in 0..n_segments {
219 let norm = (0.5 * dsegs[j]).sqrt();
220 for i in 0..u_data_shape.0 {
221 for k in 0..n_svals {
222 u_data[[i, j, k]] *= norm;
223 }
224 }
225 }
226
227 let mut polys = Vec::new();
229 let knots: Vec<f64> = segments.iter().map(|&x| x.to_f64()).collect();
230 let delta_x: Vec<f64> = knots.windows(2).map(|w| w[1] - w[0]).collect();
231
232 for k in 0..n_svals {
233 let u_data_shape = u_data.shape();
235 let mut data = DTensor::<f64, 2>::zeros([u_data_shape.0, n_segments]);
236 for i in 0..u_data_shape.0 {
237 for j in 0..n_segments {
238 data[[i, j]] = u_data[[i, j, k]];
239 }
240 }
241
242 polys.push(PiecewiseLegendrePoly::new(
243 data,
244 knots.clone(),
245 k as i32,
246 Some(delta_x.clone()),
247 0, ));
249 }
250
251 polys
252}
253
254fn canonicalize_signs(
268 u_polys: PiecewiseLegendrePolyVector,
269 v_polys: PiecewiseLegendrePolyVector,
270 xmax: f64,
271) -> (PiecewiseLegendrePolyVector, PiecewiseLegendrePolyVector) {
272 let u_vec = u_polys.get_polys();
273 let v_vec = v_polys.get_polys();
274
275 let mut new_u_vec = Vec::new();
276 let mut new_v_vec = Vec::new();
277
278 for i in 0..u_vec.len().min(v_vec.len()) {
279 let u_at_xmax = u_vec[i].evaluate(xmax);
281
282 if u_at_xmax < 0.0 {
283 let u_data_flipped =
285 DTensor::<f64, 2>::from_fn(*u_vec[i].data.shape(), |idx| -u_vec[i].data[idx]);
286 let v_data_flipped =
287 DTensor::<f64, 2>::from_fn(*v_vec[i].data.shape(), |idx| -v_vec[i].data[idx]);
288
289 new_u_vec.push(PiecewiseLegendrePoly::new(
290 u_data_flipped,
291 u_vec[i].knots.clone(),
292 u_vec[i].l,
293 Some(u_vec[i].delta_x.clone()),
294 u_vec[i].symm,
295 ));
296 new_v_vec.push(PiecewiseLegendrePoly::new(
297 v_data_flipped,
298 v_vec[i].knots.clone(),
299 v_vec[i].l,
300 Some(v_vec[i].delta_x.clone()),
301 v_vec[i].symm,
302 ));
303 } else {
304 new_u_vec.push(u_vec[i].clone());
306 new_v_vec.push(v_vec[i].clone());
307 }
308 }
309
310 (
311 PiecewiseLegendrePolyVector::new(new_u_vec),
312 PiecewiseLegendrePolyVector::new(new_v_vec),
313 )
314}
315
316pub fn merge_results(
328 result_even: (
329 PiecewiseLegendrePolyVector,
330 Vec<f64>,
331 PiecewiseLegendrePolyVector,
332 ),
333 result_odd: (
334 PiecewiseLegendrePolyVector,
335 Vec<f64>,
336 PiecewiseLegendrePolyVector,
337 ),
338 epsilon: f64,
339) -> crate::sve::SVEResult {
340 use crate::sve::SVEResult;
341
342 let (u_even, s_even, v_even) = result_even;
343 let (u_odd, s_odd, v_odd) = result_odd;
344
345 let mut indices: Vec<(usize, bool)> = Vec::new();
348 for i in 0..s_even.len() {
349 indices.push((i, true)); }
351 for i in 0..s_odd.len() {
352 indices.push((i, false)); }
354
355 indices.sort_by(|a, b| {
357 let s_a = if a.1 { s_even[a.0] } else { s_odd[a.0] };
358 let s_b = if b.1 { s_even[b.0] } else { s_odd[b.0] };
359 s_b.partial_cmp(&s_a).unwrap_or(std::cmp::Ordering::Equal)
360 });
361
362 let mut u_polys = Vec::new();
364 let mut v_polys = Vec::new();
365 let mut s_sorted = Vec::new();
366
367 for (idx, is_even) in indices {
368 if is_even {
369 u_polys.push(u_even.get_polys()[idx].clone());
370 v_polys.push(v_even.get_polys()[idx].clone());
371 s_sorted.push(s_even[idx]);
372 } else {
373 u_polys.push(u_odd.get_polys()[idx].clone());
374 v_polys.push(v_odd.get_polys()[idx].clone());
375 s_sorted.push(s_odd[idx]);
376 }
377 }
378
379 let (canonical_u, canonical_v) = canonicalize_signs(
381 PiecewiseLegendrePolyVector::new(u_polys),
382 PiecewiseLegendrePolyVector::new(v_polys),
383 1.0,
384 );
385
386 SVEResult::new(canonical_u, s_sorted, canonical_v, epsilon)
387}
388
389#[cfg(test)]
390mod tests {
391 use super::*;
392
393 #[test]
394 fn test_remove_weights() {
395 let matrix = DTensor::<f64, 2>::from_fn([2, 2], |idx| (idx[0] * 2 + idx[1] + 1) as f64);
396 let weights = vec![1.0, 4.0];
397
398 let result = remove_weights(&matrix, &weights, true);
399
400 assert!((result[[0, 0]] - 1.0).abs() < 1e-10);
404 assert!((result[[0, 1]] - 2.0).abs() < 1e-10);
405 assert!((result[[1, 0]] - 1.5).abs() < 1e-10);
406 assert!((result[[1, 1]] - 2.0).abs() < 1e-10);
407 }
408}