use crate::gauss::Rule;
use crate::interpolation1d::legendre_collocation_matrix;
use crate::kernel::SymmetryType;
use crate::numeric::CustomNumeric;
use crate::poly::{PiecewiseLegendrePoly, PiecewiseLegendrePolyVector};
use mdarray::DTensor;
pub fn remove_weights<T: CustomNumeric>(
matrix: &DTensor<T, 2>,
weights: &[T],
is_row: bool,
) -> DTensor<T, 2> {
let mut result = matrix.clone();
let shape = *result.shape();
if is_row {
for i in 0..shape.0 {
let sqrt_weight = weights[i].sqrt();
for j in 0..shape.1 {
result[[i, j]] = result[[i, j]] / sqrt_weight;
}
}
} else {
for j in 0..shape.1 {
let sqrt_weight = weights[j].sqrt();
for i in 0..shape.0 {
result[[i, j]] = result[[i, j]] / sqrt_weight;
}
}
}
result
}
pub fn extend_to_full_domain(
polys: Vec<PiecewiseLegendrePoly>,
symmetry: SymmetryType,
_xmax: f64,
) -> Vec<PiecewiseLegendrePoly> {
let sign = symmetry.sign() as f64;
let symm = symmetry.sign();
let n_poly_coeffs = if !polys.is_empty() {
polys[0].data.shape().0
} else {
return Vec::new();
};
let poly_flip_x: Vec<f64> = (0..n_poly_coeffs)
.map(|i| if i % 2 == 0 { 1.0 } else { -1.0 })
.collect();
polys
.into_iter()
.map(|poly| {
let knots_pos = &poly.knots;
let mut full_segments = Vec::new();
for i in (0..knots_pos.len()).rev() {
full_segments.push(-knots_pos[i]);
}
for i in 1..knots_pos.len() {
full_segments.push(knots_pos[i]);
}
let pos_data = DTensor::<f64, 2>::from_fn(*poly.data.shape(), |idx| {
poly.data[idx] / 2.0_f64.sqrt()
});
let pos_shape = *pos_data.shape();
let mut neg_data = DTensor::<f64, 2>::from_fn([pos_shape.0, pos_shape.1], |idx| {
let reversed_col = pos_shape.1 - 1 - idx[1];
pos_data[[idx[0], reversed_col]]
});
for (i, &flip_sign) in poly_flip_x.iter().enumerate() {
let coeff_sign = flip_sign * sign;
for j in 0..pos_shape.1 {
neg_data[[i, j]] *= coeff_sign;
}
}
let combined_data = DTensor::<f64, 2>::from_fn([pos_shape.0, pos_shape.1 * 2], |idx| {
if idx[1] < pos_shape.1 {
neg_data[[idx[0], idx[1]]]
} else {
pos_data[[idx[0], idx[1] - pos_shape.1]]
}
});
PiecewiseLegendrePoly::new(
combined_data,
full_segments,
poly.l,
None, symm, )
})
.collect()
}
pub fn svd_to_polynomials<T: CustomNumeric>(
u_or_v: &DTensor<T, 2>,
segments: &[T],
gauss_rule: &Rule<f64>,
n_gauss: usize,
) -> Vec<PiecewiseLegendrePoly> {
let n_segments = segments.len() - 1;
let n_svals = u_or_v.shape().1;
let n_rows = u_or_v.shape().0;
let mut tensor_3d = DTensor::<f64, 3>::zeros([n_gauss, n_segments, n_svals]);
for i in 0..n_gauss {
for j in 0..n_segments {
for k in 0..n_svals {
let row_idx = j * n_gauss + i;
if row_idx < n_rows {
tensor_3d[[i, j, k]] = u_or_v[[row_idx, k]].to_f64();
} else {
tensor_3d[[i, j, k]] = 0.0;
}
}
}
}
let cmat = legendre_collocation_matrix(gauss_rule);
let cmat_shape = *cmat.shape();
let mut u_data = DTensor::<f64, 3>::zeros([cmat_shape.0, n_segments, n_svals]);
for j in 0..n_segments {
for k in 0..n_svals {
for i in 0..cmat_shape.0 {
let mut sum = 0.0;
for l in 0..n_gauss {
sum += cmat[[i, l]] * tensor_3d[[l, j, k]];
}
u_data[[i, j, k]] = sum;
}
}
}
let mut dsegs = Vec::new();
for i in 0..segments.len() - 1 {
dsegs.push(segments[i + 1].to_f64() - segments[i].to_f64());
}
let u_data_shape = *u_data.shape();
for j in 0..n_segments {
let norm = (0.5 * dsegs[j]).sqrt();
for i in 0..u_data_shape.0 {
for k in 0..n_svals {
u_data[[i, j, k]] *= norm;
}
}
}
let mut polys = Vec::new();
let knots: Vec<f64> = segments.iter().map(|&x| x.to_f64()).collect();
let delta_x: Vec<f64> = knots.windows(2).map(|w| w[1] - w[0]).collect();
for k in 0..n_svals {
let u_data_shape = u_data.shape();
let mut data = DTensor::<f64, 2>::zeros([u_data_shape.0, n_segments]);
for i in 0..u_data_shape.0 {
for j in 0..n_segments {
data[[i, j]] = u_data[[i, j, k]];
}
}
polys.push(PiecewiseLegendrePoly::new(
data,
knots.clone(),
k as i32,
Some(delta_x.clone()),
0, ));
}
polys
}
fn canonicalize_signs(
u_polys: PiecewiseLegendrePolyVector,
v_polys: PiecewiseLegendrePolyVector,
xmax: f64,
) -> (PiecewiseLegendrePolyVector, PiecewiseLegendrePolyVector) {
let u_vec = u_polys.get_polys();
let v_vec = v_polys.get_polys();
let mut new_u_vec = Vec::new();
let mut new_v_vec = Vec::new();
for i in 0..u_vec.len().min(v_vec.len()) {
let u_at_xmax = u_vec[i].evaluate(xmax);
if u_at_xmax < 0.0 {
let u_data_flipped =
DTensor::<f64, 2>::from_fn(*u_vec[i].data.shape(), |idx| -u_vec[i].data[idx]);
let v_data_flipped =
DTensor::<f64, 2>::from_fn(*v_vec[i].data.shape(), |idx| -v_vec[i].data[idx]);
new_u_vec.push(PiecewiseLegendrePoly::new(
u_data_flipped,
u_vec[i].knots.clone(),
u_vec[i].l,
Some(u_vec[i].delta_x.clone()),
u_vec[i].symm,
));
new_v_vec.push(PiecewiseLegendrePoly::new(
v_data_flipped,
v_vec[i].knots.clone(),
v_vec[i].l,
Some(v_vec[i].delta_x.clone()),
v_vec[i].symm,
));
} else {
new_u_vec.push(u_vec[i].clone());
new_v_vec.push(v_vec[i].clone());
}
}
(
PiecewiseLegendrePolyVector::new(new_u_vec),
PiecewiseLegendrePolyVector::new(new_v_vec),
)
}
pub fn merge_results(
result_even: (
PiecewiseLegendrePolyVector,
Vec<f64>,
PiecewiseLegendrePolyVector,
),
result_odd: (
PiecewiseLegendrePolyVector,
Vec<f64>,
PiecewiseLegendrePolyVector,
),
epsilon: f64,
) -> crate::sve::SVEResult {
use crate::sve::SVEResult;
let (u_even, s_even, v_even) = result_even;
let (u_odd, s_odd, v_odd) = result_odd;
let mut indices: Vec<(usize, bool)> = Vec::new();
for i in 0..s_even.len() {
indices.push((i, true)); }
for i in 0..s_odd.len() {
indices.push((i, false)); }
indices.sort_by(|a, b| {
let s_a = if a.1 { s_even[a.0] } else { s_odd[a.0] };
let s_b = if b.1 { s_even[b.0] } else { s_odd[b.0] };
s_b.partial_cmp(&s_a).unwrap_or(std::cmp::Ordering::Equal)
});
let mut u_polys = Vec::new();
let mut v_polys = Vec::new();
let mut s_sorted = Vec::new();
for (idx, is_even) in indices {
if is_even {
u_polys.push(u_even.get_polys()[idx].clone());
v_polys.push(v_even.get_polys()[idx].clone());
s_sorted.push(s_even[idx]);
} else {
u_polys.push(u_odd.get_polys()[idx].clone());
v_polys.push(v_odd.get_polys()[idx].clone());
s_sorted.push(s_odd[idx]);
}
}
let (canonical_u, canonical_v) = canonicalize_signs(
PiecewiseLegendrePolyVector::new(u_polys),
PiecewiseLegendrePolyVector::new(v_polys),
1.0,
);
SVEResult::new(canonical_u, s_sorted, canonical_v, epsilon)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_remove_weights() {
let matrix = DTensor::<f64, 2>::from_fn([2, 2], |idx| (idx[0] * 2 + idx[1] + 1) as f64);
let weights = vec![1.0, 4.0];
let result = remove_weights(&matrix, &weights, true);
assert!((result[[0, 0]] - 1.0).abs() < 1e-10);
assert!((result[[0, 1]] - 2.0).abs() < 1e-10);
assert!((result[[1, 0]] - 1.5).abs() < 1e-10);
assert!((result[[1, 1]] - 2.0).abs() < 1e-10);
}
}