use std::ops::{DivAssign, MulAssign};
use itertools::izip;
use num::PrimInt;
use crate::dense::base_array::BaseArray;
use crate::dense::data_container::ArrayContainer;
use crate::fftw::{FftPlanInplace, R2RPlanKind};
use crate::{Array, fftw::R2RPlan};
use crate::{DynArray, MutableArrayImpl, RlstScalar, ValueArrayImpl};
use crate::{c32, c64};
pub trait ChebychevCoefficientsToData<Item, const NDIM: usize> {
fn chebychev_coeffs_from_data_second_kind(&self) -> DynArray<Item, NDIM>;
fn chebychev_data_from_coeffs_second_kind(&self) -> DynArray<Item, NDIM>;
}
pub trait EvaluateChebychevCoefficients1d<Item>
where
Item: RlstScalar,
{
fn evaluate_chebychev_series_second_kind<ArrayImplPoints, ArrayImplResult>(
&self,
eval_points: &Array<ArrayImplPoints, 1>,
result: &mut Array<ArrayImplResult, 1>,
) where
ArrayImplPoints: ValueArrayImpl<Item::Real, 1>,
ArrayImplResult: MutableArrayImpl<Item, 1>;
}
pub trait EvaluateChebychevCoefficients2d<Item>
where
Item: RlstScalar,
{
fn evaluate_chebychev_series_second_kind<ArrayImplPoints, ArrayImplResult>(
&self,
eval_points: &Array<ArrayImplPoints, 2>,
result: &mut Array<ArrayImplResult, 1>,
) where
ArrayImplPoints: ValueArrayImpl<Item::Real, 2>,
ArrayImplResult: MutableArrayImpl<Item, 1>;
fn tensor_evaluate_chebychev_series_second_kind<
ArrayImplPointsX,
ArrayImplPointsY,
ArrayImplResult,
>(
&self,
eval_points_x: &Array<ArrayImplPointsX, 1>,
eval_points_y: &Array<ArrayImplPointsY, 1>,
result: &mut Array<ArrayImplResult, 2>,
) where
ArrayImplPointsX: ValueArrayImpl<Item::Real, 1>,
ArrayImplPointsY: ValueArrayImpl<Item::Real, 1>,
ArrayImplResult: MutableArrayImpl<Item, 2>;
}
pub trait EvaluateChebychevCoefficients3d<Item>
where
Item: RlstScalar,
{
fn evaluate_chebychev_series_second_kind<ArrayImplPoints, ArrayImplResult>(
&self,
eval_points: &Array<ArrayImplPoints, 2>,
result: &mut Array<ArrayImplResult, 1>,
) where
ArrayImplPoints: ValueArrayImpl<Item::Real, 2>,
ArrayImplResult: MutableArrayImpl<Item, 1>;
fn tensor_evaluate_chebychev_series_second_kind<
ArrayImplPointsX,
ArrayImplPointsY,
ArrayImplPointsZ,
ArrayImplResult,
>(
&self,
eval_points_x: &Array<ArrayImplPointsX, 1>,
eval_points_y: &Array<ArrayImplPointsY, 1>,
eval_points_z: &Array<ArrayImplPointsZ, 1>,
result: &mut Array<ArrayImplResult, 3>,
) where
ArrayImplPointsX: ValueArrayImpl<Item::Real, 1>,
ArrayImplPointsY: ValueArrayImpl<Item::Real, 1>,
ArrayImplPointsZ: ValueArrayImpl<Item::Real, 1>,
ArrayImplResult: MutableArrayImpl<Item, 3>;
}
pub trait DerivativeOfChebychevSeries1d<Item> {
fn chebychev_derivative_second_kind(&mut self, order: usize);
}
pub trait DerivativeOfChebychevSeries2d<Item> {
fn chebychev_derivative_second_kind(&mut self, order: [usize; 2]);
}
pub trait DerivativeOfChebychevSeries3d<Item> {
fn chebychev_derivative_second_kind(&mut self, order: [usize; 3]);
}
impl<Item, ArrayImpl> DerivativeOfChebychevSeries1d<Item> for Array<ArrayImpl, 1>
where
Item: RlstScalar,
ArrayImpl: MutableArrayImpl<Item, 1>,
{
#[inline(always)]
fn chebychev_derivative_second_kind(&mut self, order: usize) {
let n = self.len();
if order == 0 {
return;
}
for _ in 0..order {
let mut bkp1 = Item::default(); let mut dp1 = Item::default(); let mut dp2 = Item::default(); let two = Item::from_f64(2.0).unwrap();
let half = two.recip();
for (index, d) in self
.r_mut()
.reverse_axis(0)
.iter_mut()
.take(n - 1)
.enumerate()
{
let kp1 = Item::from_usize(n - index).unwrap();
let tmp = *d;
*d = dp2 + two * kp1 * bkp1;
bkp1 = tmp;
(dp1, dp2) = (*d, dp1); }
self[[0]] = bkp1 + half * dp2;
}
}
}
impl<Item, ArrayImpl> DerivativeOfChebychevSeries2d<Item> for Array<ArrayImpl, 2>
where
Item: RlstScalar,
ArrayImpl: MutableArrayImpl<Item, 2>,
{
fn chebychev_derivative_second_kind(&mut self, order: [usize; 2]) {
for (_, mut slice) in self.multislice_iter_mut::<1, 1>([1]) {
slice.chebychev_derivative_second_kind(order[0]);
}
for (_, mut slice) in self.multislice_iter_mut::<1, 1>([0]) {
slice.chebychev_derivative_second_kind(order[1]);
}
}
}
impl<Item, ArrayImpl> DerivativeOfChebychevSeries3d<Item> for Array<ArrayImpl, 3>
where
Item: RlstScalar,
ArrayImpl: MutableArrayImpl<Item, 3>,
{
fn chebychev_derivative_second_kind(&mut self, order: [usize; 3]) {
for (_, mut slice) in self.multislice_iter_mut::<2, 1>([1, 2]) {
slice.chebychev_derivative_second_kind(order[0]);
}
for (_, mut slice) in self.multislice_iter_mut::<2, 1>([0, 2]) {
slice.chebychev_derivative_second_kind(order[1]);
}
for (_, mut slice) in self.multislice_iter_mut::<2, 1>([0, 1]) {
slice.chebychev_derivative_second_kind(order[2]);
}
}
}
impl<Item, ArrayImpl> EvaluateChebychevCoefficients1d<Item> for Array<ArrayImpl, 1>
where
Item: RlstScalar,
ArrayImpl: ValueArrayImpl<Item, 1>,
{
fn evaluate_chebychev_series_second_kind<ArrayImplPoints, ArrayImplResult>(
&self,
eval_points: &Array<ArrayImplPoints, 1>,
result: &mut Array<ArrayImplResult, 1>,
) where
ArrayImplPoints: ValueArrayImpl<Item::Real, 1>,
ArrayImplResult: MutableArrayImpl<Item, 1>,
{
assert!(!self.is_empty());
assert_eq!(eval_points.len(), result.len());
let n = self.len() - 1;
let mut bkp1;
let mut bkp2;
let a0 = self.get_value([0]).unwrap();
let two = Item::from_f64(2.0).unwrap();
for (x, r) in izip!(eval_points.iter_value(), result.iter_mut()) {
bkp1 = Item::default();
bkp2 = Item::default();
let x = Item::from_real(x);
for cheb_coeff in self.r().reverse_axis(0).iter_value().take(n) {
(bkp1, bkp2) = (cheb_coeff + two * x * bkp1 - bkp2, bkp1);
}
*r = a0 + x * bkp1 - bkp2;
}
}
}
impl<Item, ArrayImpl> EvaluateChebychevCoefficients2d<Item> for Array<ArrayImpl, 2>
where
Item: RlstScalar,
ArrayImpl: ValueArrayImpl<Item, 2>,
{
fn evaluate_chebychev_series_second_kind<ArrayImplPoints, ArrayImplResult>(
&self,
eval_points: &Array<ArrayImplPoints, 2>,
result: &mut Array<ArrayImplResult, 1>,
) where
ArrayImplPoints: ValueArrayImpl<Item::Real, 2>,
ArrayImplResult: MutableArrayImpl<Item, 1>,
{
let points_shape = eval_points.shape();
let npoints = points_shape[1];
let points_dim = points_shape[0];
assert_eq!(npoints, result.len());
assert_eq!(points_dim, 2);
let mut p0 =
Array::<_, 1>::new(BaseArray::new(ArrayContainer::<Item::Real, 1>::new(), [1]));
let mut p1 =
Array::<_, 1>::new(BaseArray::new(ArrayContainer::<Item::Real, 1>::new(), [1]));
let mut res = Array::<_, 2>::new(BaseArray::new(ArrayContainer::<Item, 1>::new(), [1, 1]));
for (point, r) in izip!(eval_points.col_iter(), result.iter_mut()) {
p0[[0]] = point.get_value([0]).unwrap();
p1[[0]] = point.get_value([1]).unwrap();
self.tensor_evaluate_chebychev_series_second_kind(&p0, &p1, &mut res);
*r = res[[0, 0]];
}
}
fn tensor_evaluate_chebychev_series_second_kind<
ArrayImplPointsX,
ArrayImplPointsY,
ArrayImplResult,
>(
&self,
eval_points_x: &Array<ArrayImplPointsX, 1>,
eval_points_y: &Array<ArrayImplPointsY, 1>,
result: &mut Array<ArrayImplResult, 2>,
) where
ArrayImplPointsX: ValueArrayImpl<Item::Real, 1>,
ArrayImplPointsY: ValueArrayImpl<Item::Real, 1>,
ArrayImplResult: MutableArrayImpl<Item, 2>,
{
let [mx, my] = result.shape();
let [_nx, ny] = self.shape();
assert_eq!(mx, eval_points_x.len());
assert_eq!(my, eval_points_y.len());
let mut tmp = DynArray::<Item, 2>::from_shape([mx, ny]);
for (col_in, mut col_tmp) in izip!(self.col_iter(), tmp.col_iter_mut()) {
col_in.evaluate_chebychev_series_second_kind(eval_points_x, &mut col_tmp);
}
for (row_tmp, mut row_res) in izip!(tmp.row_iter(), result.row_iter_mut()) {
row_tmp.evaluate_chebychev_series_second_kind(eval_points_y, &mut row_res);
}
}
}
impl<Item, ArrayImpl> EvaluateChebychevCoefficients3d<Item> for Array<ArrayImpl, 3>
where
Item: RlstScalar,
ArrayImpl: ValueArrayImpl<Item, 3>,
{
fn tensor_evaluate_chebychev_series_second_kind<
ArrayImplPointsX,
ArrayImplPointsY,
ArrayImplPointsZ,
ArrayImplResult,
>(
&self,
eval_points_x: &Array<ArrayImplPointsX, 1>,
eval_points_y: &Array<ArrayImplPointsY, 1>,
eval_points_z: &Array<ArrayImplPointsZ, 1>,
result: &mut Array<ArrayImplResult, 3>,
) where
ArrayImplPointsX: ValueArrayImpl<Item::Real, 1>,
ArrayImplPointsY: ValueArrayImpl<Item::Real, 1>,
ArrayImplPointsZ: ValueArrayImpl<Item::Real, 1>,
ArrayImplResult: MutableArrayImpl<Item, 3>,
{
let [mx, my, mz] = result.shape();
let [_nx, ny, nz] = self.shape();
assert_eq!(mx, eval_points_x.len());
assert_eq!(my, eval_points_y.len());
assert_eq!(mz, eval_points_z.len());
let mut tmp_x = DynArray::<Item, 3>::from_shape([mx, ny, nz]);
for (indices, slice) in self.r().multislice_iter::<_, 1>([1, 2]) {
slice.evaluate_chebychev_series_second_kind(
eval_points_x,
&mut tmp_x.r_mut().multislice([1, 2], indices),
);
}
let mut tmp_y = DynArray::<Item, 3>::from_shape([mx, my, nz]);
for (indices, slice) in tmp_x.r().multislice_iter::<_, 1>([0, 2]) {
slice.evaluate_chebychev_series_second_kind(
eval_points_y,
&mut tmp_y.r_mut().multislice([0, 2], indices),
);
}
for (indices, slice) in tmp_y.r().multislice_iter::<_, 1>([0, 1]) {
slice.evaluate_chebychev_series_second_kind(
eval_points_z,
&mut result.r_mut().multislice([0, 1], indices),
);
}
}
fn evaluate_chebychev_series_second_kind<ArrayImplPoints, ArrayImplResult>(
&self,
eval_points: &Array<ArrayImplPoints, 2>,
result: &mut Array<ArrayImplResult, 1>,
) where
ArrayImplPoints: ValueArrayImpl<<Item as RlstScalar>::Real, 2>,
ArrayImplResult: MutableArrayImpl<Item, 1>,
{
let points_shape = eval_points.shape();
let npoints = points_shape[1];
let points_dim = points_shape[0];
assert_eq!(npoints, result.len());
assert_eq!(points_dim, 3);
let mut p0 =
Array::<_, 1>::new(BaseArray::new(ArrayContainer::<Item::Real, 1>::new(), [1]));
let mut p1 =
Array::<_, 1>::new(BaseArray::new(ArrayContainer::<Item::Real, 1>::new(), [1]));
let mut p2 =
Array::<_, 1>::new(BaseArray::new(ArrayContainer::<Item::Real, 1>::new(), [1]));
let mut res =
Array::<_, 3>::new(BaseArray::new(ArrayContainer::<Item, 1>::new(), [1, 1, 1]));
for (point, r) in izip!(eval_points.col_iter(), result.iter_mut()) {
p0[[0]] = point.get_value([0]).unwrap();
p1[[0]] = point.get_value([1]).unwrap();
p2[[0]] = point.get_value([2]).unwrap();
self.tensor_evaluate_chebychev_series_second_kind(&p0, &p1, &p2, &mut res);
*r = res[[0, 0, 0]];
}
}
}
macro_rules! impl_cheb_coeffs_complex {
($dtype:ty) => {
impl<ArrayImpl, const NDIM: usize> ChebychevCoefficientsToData<$dtype, NDIM>
for Array<ArrayImpl, NDIM>
where
ArrayImpl: ValueArrayImpl<$dtype, NDIM>,
{
fn chebychev_coeffs_from_data_second_kind(&self) -> DynArray<$dtype, NDIM> {
let shape = self.shape();
let mut re = DynArray::<<$dtype as RlstScalar>::Real, NDIM>::from_shape(shape);
let mut im = DynArray::<<$dtype as RlstScalar>::Real, NDIM>::from_shape(shape);
re.fill_from(&self.r().unary_op(|elem: $dtype| elem.re()));
im.fill_from(&self.r().unary_op(|elem: $dtype| elem.im()));
let re_coeffs = re.chebychev_coeffs_from_data_second_kind();
let im_coeffs = im.chebychev_coeffs_from_data_second_kind();
let mut out = DynArray::<$dtype, NDIM>::from_shape(shape);
for (o, r, i) in izip!(
out.iter_mut(),
re_coeffs.iter_value(),
im_coeffs.iter_value()
) {
*o = <$dtype>::new(r, i);
}
out
}
fn chebychev_data_from_coeffs_second_kind(&self) -> DynArray<$dtype, NDIM> {
let shape = self.shape();
let mut re = DynArray::<<$dtype as RlstScalar>::Real, NDIM>::from_shape(shape);
let mut im = DynArray::<<$dtype as RlstScalar>::Real, NDIM>::from_shape(shape);
re.fill_from(&self.r().unary_op(|elem: $dtype| elem.re()));
im.fill_from(&self.r().unary_op(|elem: $dtype| elem.im()));
let re_data = re.chebychev_data_from_coeffs_second_kind();
let im_data = im.chebychev_data_from_coeffs_second_kind();
let mut out = DynArray::<$dtype, NDIM>::from_shape(shape);
for (o, r, i) in izip!(out.iter_mut(), re_data.iter_value(), im_data.iter_value()) {
*o = <$dtype>::new(r, i);
}
out
}
}
};
}
impl_cheb_coeffs_complex!(c64);
impl_cheb_coeffs_complex!(c32);
macro_rules! impl_cheb_coeffs {
($dtype:ty) => {
impl<ArrayImpl, const NDIM: usize> ChebychevCoefficientsToData<$dtype, NDIM>
for Array<ArrayImpl, NDIM>
where
ArrayImpl: ValueArrayImpl<$dtype, NDIM>,
{
fn chebychev_coeffs_from_data_second_kind(&self) -> DynArray<$dtype, NDIM> {
if NDIM > 3 {
unimplemented!();
}
let mut result = DynArray::<$dtype, NDIM>::from_shape(self.shape());
result.fill_from(self);
let shape = result.shape();
let kind = vec![R2RPlanKind::FFTW_REDFT00; NDIM];
result
.r_mut()
.into_fft()
.r2r_fft(crate::fftw::FftwPlanFlags::Estimate, &kind)
.unwrap()
.execute();
let n_recip =
(shape.iter().map(|elem| elem - 1).product::<usize>() as $dtype).recip();
result *= n_recip;
if NDIM == 1 {
*result.data_mut().unwrap().first_mut().unwrap() /= 2.0;
*result.data_mut().unwrap().last_mut().unwrap() /= 2.0;
} else if NDIM == 2 {
for (axis, dim) in shape.iter().enumerate() {
for index in [0, dim - 1] {
result
.r_mut()
.coerce_dim::<2>()
.unwrap()
.slice::<1>(axis, index)
.div_assign(2.0);
}
}
} else if NDIM == 3 {
for (axis, dim) in shape.iter().enumerate() {
for index in [0, dim - 1] {
result
.r_mut()
.coerce_dim::<3>()
.unwrap()
.slice::<2>(axis, index)
.div_assign(2.0);
}
}
}
result
}
fn chebychev_data_from_coeffs_second_kind(&self) -> DynArray<$dtype, NDIM> {
if NDIM > 3 {
unimplemented!();
}
let mut result = DynArray::<$dtype, NDIM>::from_shape(self.shape());
result.fill_from(self);
let shape = result.shape();
result *= (2.pow(NDIM as u32) as $dtype).recip();
if NDIM == 1 {
*result.data_mut().unwrap().first_mut().unwrap() *= 2.0;
*result.data_mut().unwrap().last_mut().unwrap() *= 2.0;
} else if NDIM == 2 {
for (axis, dim) in shape.iter().enumerate() {
for index in [0, dim - 1] {
result
.r_mut()
.coerce_dim::<2>()
.unwrap()
.slice::<1>(axis, index)
.mul_assign(2.0);
}
}
} else if NDIM == 3 {
for (axis, dim) in shape.iter().enumerate() {
for index in [0, dim - 1] {
result
.r_mut()
.coerce_dim::<3>()
.unwrap()
.slice::<2>(axis, index)
.mul_assign(2.0);
}
}
}
let kind = vec![R2RPlanKind::FFTW_REDFT00; NDIM];
result
.r_mut()
.into_fft()
.r2r_fft(crate::fftw::FftwPlanFlags::Estimate, &kind)
.unwrap()
.execute();
result
}
}
};
}
impl_cheb_coeffs!(f64);
impl_cheb_coeffs!(f32);
#[cfg(test)]
mod test {
use num::Complex;
use crate::{
EvaluateArray, Linspace, assert_array_relative_eq,
dense::tools::PrettyPrint,
interpolation::{
Kind, barycentric_chebychev_weights, barycentric_evaluate_2d, barycentric_evaluate_3d,
chebychev_points, tensor_barycentric_evaluate_2d, tensor_barycentric_evaluate_3d,
},
};
use super::*;
#[test]
pub fn test_data_to_coeffs_1d() {
let n = 3;
let points = chebychev_points::<f64>(Kind::Second, n);
let mut one = DynArray::<f64, _>::from_shape([n]);
one.fill_with_value(1.0);
let data = 2.0_f64 * points.r() * points.r() + points.r();
let coeffs = data.chebychev_coeffs_from_data_second_kind();
let data_out = coeffs.chebychev_data_from_coeffs_second_kind();
crate::assert_array_relative_eq!(coeffs, one, 1E-10);
crate::assert_array_relative_eq!(data, data_out, 1E-10);
}
#[test]
pub fn test_data_to_coeffs_1d_f32() {
let n = 3;
let points = chebychev_points::<f32>(Kind::Second, n);
let mut one = DynArray::<f32, _>::from_shape([n]);
one.fill_with_value(1.0);
let data = 2.0_f32 * points.r() * points.r() + points.r();
let coeffs = data.chebychev_coeffs_from_data_second_kind();
let data_out = coeffs.chebychev_data_from_coeffs_second_kind();
crate::assert_array_relative_eq!(coeffs, one, 1E-5);
crate::assert_array_relative_eq!(data, data_out, 1E-5);
}
#[test]
pub fn test_data_to_coeffs_2d() {
let n = 3;
let points = chebychev_points::<f64>(Kind::Second, n);
let mut data = DynArray::<f64, 2>::from_shape([n, n]);
for (i, pi) in points.iter_value().enumerate() {
for (j, pj) in points.iter_value().enumerate() {
data[[i, j]] = pi * pj + pi + pj + 2.0 * pi * pi + 2.0 * pj * pj - 1.0;
}
}
let mut coeffs_expected = DynArray::<f64, 2>::from_shape([3, 3]);
coeffs_expected[[0, 0]] = 1.0;
coeffs_expected[[1, 0]] = 1.0;
coeffs_expected[[2, 0]] = 1.0;
coeffs_expected[[0, 1]] = 1.0;
coeffs_expected[[1, 1]] = 1.0;
coeffs_expected[[0, 2]] = 1.0;
let coeffs = data.chebychev_coeffs_from_data_second_kind();
coeffs.pretty_print();
crate::assert_array_relative_eq!(coeffs_expected, coeffs, 1E-10);
let data_out = coeffs.chebychev_data_from_coeffs_second_kind();
crate::assert_array_abs_diff_eq!(data, data_out, 1E-10);
}
#[test]
pub fn test_data_to_coeffs_3d() {
let n = 3;
let points = chebychev_points::<f64>(Kind::Second, n);
let mut data = DynArray::<f64, 3>::from_shape([n, n, n]);
for (i, pi) in points.iter_value().enumerate() {
for (j, pj) in points.iter_value().enumerate() {
for (k, pk) in points.iter_value().enumerate() {
data[[i, j, k]] =
pi * pj + pi + pj + 2.0 * pi * pi + 2.0 * pj * pj - 1.0 + 3.0 * pk;
}
}
}
let mut coeffs_expected = DynArray::<f64, 3>::from_shape([3, 3, 3]);
coeffs_expected[[0, 0, 0]] = 1.0;
coeffs_expected[[1, 0, 0]] = 1.0;
coeffs_expected[[2, 0, 0]] = 1.0;
coeffs_expected[[0, 1, 0]] = 1.0;
coeffs_expected[[1, 1, 0]] = 1.0;
coeffs_expected[[0, 2, 0]] = 1.0;
coeffs_expected[[0, 0, 1]] = 3.0;
let coeffs = data.chebychev_coeffs_from_data_second_kind();
crate::assert_array_relative_eq!(coeffs_expected, coeffs, 1E-10);
let data_out = coeffs.chebychev_data_from_coeffs_second_kind();
crate::assert_array_abs_diff_eq!(data, data_out, 1E-10);
}
#[test]
pub fn test_random_data_to_coefficients_and_back_1d() {
let n = 11;
let mut expected = DynArray::<f64, _>::from_shape([n]);
expected.fill_from_seed_normally_distributed(0);
let coeffs = expected.chebychev_coeffs_from_data_second_kind();
let actual = coeffs.chebychev_data_from_coeffs_second_kind();
crate::assert_array_relative_eq!(actual, expected, 1E-10);
}
#[test]
pub fn test_complex_random_data_to_coefficients_and_back_1d() {
let n = 11;
let mut expected = DynArray::<c64, _>::from_shape([n]);
expected.fill_from_seed_normally_distributed(0);
let coeffs = expected.chebychev_coeffs_from_data_second_kind();
let actual = coeffs.chebychev_data_from_coeffs_second_kind();
crate::assert_array_relative_eq!(actual, expected, 1E-10);
}
#[test]
pub fn test_random_data_to_coefficients_and_back_2d() {
let n = 5;
let m = 7;
let mut expected = DynArray::<f64, _>::from_shape([m, n]);
expected.fill_from_seed_normally_distributed(0);
let coeffs = expected.chebychev_coeffs_from_data_second_kind();
let actual = coeffs.chebychev_data_from_coeffs_second_kind();
crate::assert_array_relative_eq!(actual, expected, 1E-10);
}
#[test]
pub fn test_random_data_to_coefficients_and_back_3d() {
let m = 7;
let n = 5;
let k = 8;
let mut expected = DynArray::<f64, _>::from_shape([m, n, k]);
expected.fill_from_seed_normally_distributed(0);
let coeffs = expected.chebychev_coeffs_from_data_second_kind();
let actual = coeffs.chebychev_data_from_coeffs_second_kind();
crate::assert_array_relative_eq!(actual, expected, 1E-10);
}
#[test]
pub fn test_eval_cheb_series_1d_second_kind() {
let n = 3;
let m = 11;
let mut eval_points = DynArray::<f64, 1>::from_shape([m]);
eval_points.linspace(-1.0, 1.0);
let mut coeffs = DynArray::<f64, 1>::from_shape([n]);
coeffs[[0]] = 1.0;
coeffs[[1]] = 2.0;
coeffs[[2]] = 2.5;
let mut result = DynArray::<f64, 1>::from_shape([m]);
coeffs.evaluate_chebychev_series_second_kind(&eval_points, &mut result);
let mut expected = eval_points.eval();
expected
.iter_mut()
.for_each(|x| *x = 5.0 * *x * *x + 2.0 * *x - 1.5);
crate::assert_array_relative_eq!(result, expected, 1E-13);
}
#[test]
pub fn test_tensor_eval_cheb_series_2d_second_kind() {
let nx = 5;
let ny = 10;
let mx = 60;
let my = 70;
let mut eval_points_x = DynArray::<f64, 1>::from_shape([mx]);
let mut eval_points_y = DynArray::<f64, 1>::from_shape([my]);
eval_points_x.linspace(-1.0, 1.0);
eval_points_y.linspace(-1.0, 1.0);
let mut coeffs = DynArray::<f64, 2>::from_shape([nx, ny]);
coeffs.fill_from_seed_equally_distributed(0);
let mut actual = DynArray::<f64, 2>::from_shape([mx, my]);
coeffs.tensor_evaluate_chebychev_series_second_kind(
&eval_points_x,
&eval_points_y,
&mut actual,
);
let data = coeffs.chebychev_data_from_coeffs_second_kind();
let mut expected = DynArray::<f64, 2>::from_shape([mx, my]);
let cheb_points_x = chebychev_points(Kind::Second, nx);
let cheb_points_y = chebychev_points(Kind::Second, ny);
let weights_x = barycentric_chebychev_weights::<f64>(Kind::Second, nx);
let weights_y = barycentric_chebychev_weights::<f64>(Kind::Second, ny);
tensor_barycentric_evaluate_2d(
&eval_points_x,
&eval_points_y,
&cheb_points_x,
&cheb_points_y,
&weights_x,
&weights_y,
&data,
&mut expected,
);
assert_array_relative_eq!(actual, expected, 1e-10);
}
#[test]
pub fn test_eval_cheb_series_2d_second_kind() {
let nx = 5;
let ny = 10;
let m = 60;
let mut eval_points = DynArray::<f64, 2>::from_shape([2, m]);
eval_points.fill_from_seed_equally_distributed(0);
for value in eval_points.iter_mut() {
*value = 2.0 * *value - 1.0
}
let mut coeffs = DynArray::<f64, 2>::from_shape([nx, ny]);
coeffs.fill_from_seed_normally_distributed(0);
let mut actual = DynArray::<f64, 1>::from_shape([m]);
coeffs.evaluate_chebychev_series_second_kind(&eval_points, &mut actual);
let data = coeffs.chebychev_data_from_coeffs_second_kind();
let mut expected = DynArray::<f64, 1>::from_shape([m]);
let cheb_points_x = chebychev_points(Kind::Second, nx);
let cheb_points_y = chebychev_points(Kind::Second, ny);
let weights_x = barycentric_chebychev_weights::<f64>(Kind::Second, nx);
let weights_y = barycentric_chebychev_weights::<f64>(Kind::Second, ny);
barycentric_evaluate_2d(
&eval_points,
&cheb_points_x,
&cheb_points_y,
&weights_x,
&weights_y,
&data,
&mut expected,
);
assert_array_relative_eq!(actual, expected, 1e-10);
}
#[test]
pub fn test_tensor_eval_cheb_series_3d_second_kind() {
let nx = 5;
let ny = 10;
let nz = 13;
let mx = 18;
let my = 19;
let mz = 14;
let mut eval_points_x = DynArray::<f64, 1>::from_shape([mx]);
let mut eval_points_y = DynArray::<f64, 1>::from_shape([my]);
let mut eval_points_z = DynArray::<f64, 1>::from_shape([mz]);
eval_points_x.linspace(-1.0, 1.0);
eval_points_y.linspace(-1.0, 1.0);
eval_points_z.linspace(-1.0, 1.0);
let mut coeffs = DynArray::<f64, 3>::from_shape([nx, ny, nz]);
coeffs.fill_from_seed_equally_distributed(0);
let mut actual = DynArray::<f64, 3>::from_shape([mx, my, mz]);
coeffs.tensor_evaluate_chebychev_series_second_kind(
&eval_points_x,
&eval_points_y,
&eval_points_z,
&mut actual,
);
let data = coeffs.chebychev_data_from_coeffs_second_kind();
let mut expected = DynArray::<f64, 3>::from_shape([mx, my, mz]);
let cheb_points_x = chebychev_points(Kind::Second, nx);
let cheb_points_y = chebychev_points(Kind::Second, ny);
let cheb_points_z = chebychev_points(Kind::Second, nz);
let weights_x = barycentric_chebychev_weights::<f64>(Kind::Second, nx);
let weights_y = barycentric_chebychev_weights::<f64>(Kind::Second, ny);
let weights_z = barycentric_chebychev_weights::<f64>(Kind::Second, nz);
tensor_barycentric_evaluate_3d(
&eval_points_x,
&eval_points_y,
&eval_points_z,
&cheb_points_x,
&cheb_points_y,
&cheb_points_z,
&weights_x,
&weights_y,
&weights_z,
&data,
&mut expected,
);
assert_array_relative_eq!(actual, expected, 1E-10);
}
#[test]
pub fn test_eval_cheb_series_3d_second_kind() {
let nx = 5;
let ny = 10;
let nz = 13;
let m = 60;
let mut eval_points = DynArray::<f64, 2>::from_shape([3, m]);
eval_points.fill_from_seed_equally_distributed(0);
for value in eval_points.iter_mut() {
*value = 2.0 * *value - 1.0
}
let mut coeffs = DynArray::<f64, 3>::from_shape([nx, ny, nz]);
coeffs.fill_from_seed_normally_distributed(0);
let mut actual = DynArray::<f64, 1>::from_shape([m]);
coeffs.evaluate_chebychev_series_second_kind(&eval_points, &mut actual);
let data = coeffs.chebychev_data_from_coeffs_second_kind();
let mut expected = DynArray::<f64, 1>::from_shape([m]);
let cheb_points_x = chebychev_points(Kind::Second, nx);
let cheb_points_y = chebychev_points(Kind::Second, ny);
let cheb_points_z = chebychev_points(Kind::Second, nz);
let weights_x = barycentric_chebychev_weights::<f64>(Kind::Second, nx);
let weights_y = barycentric_chebychev_weights::<f64>(Kind::Second, ny);
let weights_z = barycentric_chebychev_weights::<f64>(Kind::Second, nz);
barycentric_evaluate_3d(
&eval_points,
&cheb_points_x,
&cheb_points_y,
&cheb_points_z,
&weights_x,
&weights_y,
&weights_z,
&data,
&mut expected,
);
assert_array_relative_eq!(actual, expected, 1e-10);
}
#[test]
fn test_derivative_1d() {
let n = 20;
let points = chebychev_points::<f64>(Kind::Second, n);
let mut values = points.eval();
values.iter_mut().for_each(|elem| *elem = elem.sin());
let mut expected = points.eval();
expected.iter_mut().for_each(|elem| *elem = elem.cos());
let coeffs = values.chebychev_coeffs_from_data_second_kind();
let mut deriv = coeffs.eval();
deriv.chebychev_derivative_second_kind(1);
let values_deriv = deriv.chebychev_data_from_coeffs_second_kind();
crate::assert_array_relative_eq!(values_deriv, expected, 1E-10);
let mut deriv_2 = coeffs.eval();
deriv_2.chebychev_derivative_second_kind(2);
let values_deriv_2 = deriv_2.chebychev_data_from_coeffs_second_kind();
let mut expected = points.eval();
expected.iter_mut().for_each(|elem| *elem = -elem.sin());
crate::assert_array_relative_eq!(values_deriv_2, expected, 1E-10);
}
#[test]
fn test_derivative_complex_1d() {
let n = 30;
let points = chebychev_points::<f64>(Kind::Second, n);
let mut values = points.r().cast::<c64>().eval();
values
.iter_mut()
.for_each(|elem| *elem = Complex::new(elem.re().sin(), (5.0_f64 * elem.re()).cos()));
let mut expected = points.r().cast::<c64>().eval();
expected.iter_mut().for_each(|elem| {
*elem = Complex::new(elem.re().cos(), -5.0_f64 * (5.0_f64 * elem.re()).sin())
});
let coeffs = values.chebychev_coeffs_from_data_second_kind();
let mut deriv = coeffs.eval();
deriv.chebychev_derivative_second_kind(1);
let values_deriv = deriv.chebychev_data_from_coeffs_second_kind();
crate::assert_array_relative_eq!(values_deriv, expected, 1E-10);
}
#[test]
fn test_derivative_2d() {
let nx = 20; let ny = 30;
let points_x = chebychev_points::<f64>(Kind::Second, nx);
let points_y = chebychev_points::<f64>(Kind::Second, ny);
let mut values = DynArray::from_shape([nx, ny]);
for (indx, px) in points_x.iter_value().enumerate() {
for (indy, py) in points_y.iter_value().enumerate() {
values[[indx, indy]] = f64::sin(px * py);
}
}
let mut expected = DynArray::from_shape([nx, ny]);
for (indx, px) in points_x.iter_value().enumerate() {
for (indy, py) in points_y.iter_value().enumerate() {
expected[[indx, indy]] =
-px * px * py * f64::cos(px * py) - 2.0 * px * f64::sin(px * py);
}
}
let coeffs = values.chebychev_coeffs_from_data_second_kind();
let mut deriv = coeffs.eval();
deriv.chebychev_derivative_second_kind([1, 2]);
let values_deriv = deriv.chebychev_data_from_coeffs_second_kind();
crate::assert_array_relative_eq!(values_deriv, expected, 1E-8);
}
#[test]
fn test_derivative_3d() {
let nx = 14; let ny = 15; let nz = 16;
let points_x = chebychev_points::<f64>(Kind::Second, nx);
let points_y = chebychev_points::<f64>(Kind::Second, ny);
let points_z = chebychev_points::<f64>(Kind::Second, nz);
let mut values = DynArray::from_shape([nx, ny, nz]);
for (indx, px) in points_x.iter_value().enumerate() {
for (indy, py) in points_y.iter_value().enumerate() {
for (indz, pz) in points_z.iter_value().enumerate() {
values[[indx, indy, indz]] = f64::sin(px * py * pz);
}
}
}
let mut expected = DynArray::from_shape([nx, ny, nz]);
for (indx, px) in points_x.iter_value().enumerate() {
for (indy, py) in points_y.iter_value().enumerate() {
for (indz, pz) in points_z.iter_value().enumerate() {
expected[[indx, indy, indz]] = -18.0 * px * px * py * f64::cos(px * py * pz)
+ px * px
* px
* px
* py
* py
* py
* 11.0
* pz
* pz
* f64::cos(px * py * pz)
+ px * px * px * py * py * 30.0 * pz * f64::sin(px * py * pz)
- px * px
* px
* px
* px
* py
* py
* py
* py
* pz
* pz
* pz
* f64::sin(px * py * pz);
}
}
}
let coeffs = values.chebychev_coeffs_from_data_second_kind();
let mut deriv = coeffs.eval();
deriv.chebychev_derivative_second_kind([1, 2, 3]);
let values_deriv = deriv.chebychev_data_from_coeffs_second_kind();
crate::assert_array_abs_diff_eq!(values_deriv, expected, 1E-4);
}
}