use crate::freq::MatsubaraFreq;
use crate::matsubara_sampling::{MatsubaraSampling, MatsubaraSamplingPositiveOnly};
use crate::test_utils::{ErrorNorm, generate_test_data_tau_and_matsubara};
use crate::traits::{Bosonic, Fermionic, StatisticsType};
use crate::{FiniteTempBasis, LogisticKernel, RegularizedBoseKernel};
use num_complex::Complex;
#[test]
fn test_matsubara_sampling_roundtrip_fermionic() {
test_matsubara_sampling_roundtrip_generic::<Fermionic>();
}
#[test]
fn test_matsubara_sampling_roundtrip_bosonic() {
test_matsubara_sampling_roundtrip_generic::<Bosonic>();
}
fn test_matsubara_sampling_roundtrip_generic<S: StatisticsType + 'static>() {
let beta = 10.0;
let wmax = 10.0;
let epsilon = 1e-6;
let kernel = LogisticKernel::new(wmax * beta);
let basis = FiniteTempBasis::<_, S>::new(kernel, beta, Some(epsilon), None);
let sampling_points = basis.default_matsubara_sampling_points(false);
let sampling = MatsubaraSampling::with_sampling_points(&basis, sampling_points.clone());
let (_coeffs_random, _gtau_values, giwn_values) =
generate_test_data_tau_and_matsubara::<Complex<f64>, S, _>(
&basis,
&[0.5 * beta], &sampling_points,
12345,
);
let coeffs_fitted = sampling.fit(&giwn_values);
let giwn_reconstructed = sampling.evaluate(&coeffs_fitted);
let max_error = giwn_values
.iter()
.zip(giwn_reconstructed.iter())
.map(|(a, b)| (*a - *b).error_norm())
.fold(0.0f64, f64::max);
println!(
"MatsubaraSampling {:?} roundtrip max error: {}",
S::STATISTICS,
max_error
);
assert!(max_error < 1e-7, "Roundtrip error too large: {}", max_error);
}
#[test]
fn test_matsubara_sampling_positive_only_roundtrip_fermionic() {
test_matsubara_sampling_positive_only_roundtrip_generic::<Fermionic>();
}
#[test]
fn test_matsubara_sampling_positive_only_roundtrip_bosonic() {
test_matsubara_sampling_positive_only_roundtrip_generic::<Bosonic>();
}
fn test_matsubara_sampling_positive_only_roundtrip_generic<S: StatisticsType + 'static>() {
let beta = 10.0;
let wmax = 10.0;
let epsilon = 1e-6;
let kernel = LogisticKernel::new(wmax * beta);
let basis = FiniteTempBasis::<_, S>::new(kernel, beta, Some(epsilon), None);
let sampling_points = basis.default_matsubara_sampling_points(true);
let _n_matsubara = sampling_points.len();
let sampling =
MatsubaraSamplingPositiveOnly::with_sampling_points(&basis, sampling_points.clone());
let (_coeffs_random, _gtau_values, giwn_values) =
generate_test_data_tau_and_matsubara::<f64, S, _>(
&basis,
&[0.5 * beta], &sampling_points,
12345,
);
let coeffs_fitted = sampling.fit(&giwn_values);
let giwn_reconstructed = sampling.evaluate(&coeffs_fitted);
let max_error = giwn_values
.iter()
.zip(giwn_reconstructed.iter())
.map(|(a, b)| (*a - *b).error_norm())
.fold(0.0f64, f64::max);
println!(
"MatsubaraSamplingPositiveOnly {:?} roundtrip max error: {}",
S::STATISTICS,
max_error
);
assert!(max_error < 1e-7, "Roundtrip error too large: {}", max_error);
}
#[test]
fn test_matsubara_sampling_dimensions() {
let beta = 10.0;
let wmax = 10.0;
let epsilon = 1e-6;
let kernel = LogisticKernel::new(wmax * beta);
let basis = FiniteTempBasis::<_, Fermionic>::new(kernel, beta, Some(epsilon), None);
let sampling_points = basis.default_matsubara_sampling_points(true);
let sampling =
MatsubaraSamplingPositiveOnly::with_sampling_points(&basis, sampling_points.clone());
assert_eq!(sampling.basis_size(), basis.size());
assert_eq!(sampling.n_sampling_points(), sampling_points.len());
}
#[test]
fn test_matsubara_sampling_nd_roundtrip_fermionic() {
test_matsubara_sampling_nd_roundtrip_generic::<Fermionic>();
}
#[test]
fn test_matsubara_sampling_nd_roundtrip_bosonic() {
test_matsubara_sampling_nd_roundtrip_generic::<Bosonic>();
}
fn test_matsubara_sampling_nd_roundtrip_generic<S: StatisticsType + 'static>() {
use crate::test_utils::generate_nd_test_data;
use num_complex::Complex;
let beta = 10.0;
let wmax = 10.0;
let epsilon = 1e-6;
let kernel = LogisticKernel::new(wmax * beta);
let basis = FiniteTempBasis::<_, S>::new(kernel, beta, Some(epsilon), None);
let sampling_points = basis.default_matsubara_sampling_points(false);
let sampling = MatsubaraSampling::with_sampling_points(&basis, sampling_points.clone());
let n_k = 4;
let n_omega = 5;
for dim in 0..3 {
let (coeffs_0, _gtau_0, _giwn_0) = generate_nd_test_data::<Complex<f64>, _, _>(
&basis,
&[],
&sampling_points,
42 + dim as u64,
&[n_k, n_omega],
);
let coeffs_dim = crate::test_utils::movedim(&coeffs_0, 0, dim);
let values_dim = sampling.evaluate_nd(None, &coeffs_dim, dim);
let coeffs_fitted_dim = sampling.fit_nd(None, &values_dim, dim);
let coeffs_fitted_0 = crate::test_utils::movedim(&coeffs_fitted_dim, dim, 0);
let max_error = coeffs_0
.iter()
.zip(coeffs_fitted_0.iter())
.map(|(a, b)| (*a - *b).norm())
.fold(0.0, f64::max);
println!(
"MatsubaraSampling {:?} dim={} roundtrip error: {}",
S::STATISTICS,
dim,
max_error
);
assert!(
max_error < 1e-10,
"ND roundtrip (dim={}) error too large: {}",
dim,
max_error
);
}
}
#[test]
fn test_matsubara_sampling_positive_only_nd_roundtrip_fermionic() {
test_matsubara_sampling_positive_only_nd_roundtrip_generic::<Fermionic>();
}
#[test]
fn test_matsubara_sampling_positive_only_nd_roundtrip_bosonic() {
test_matsubara_sampling_positive_only_nd_roundtrip_generic::<Bosonic>();
}
fn test_matsubara_sampling_positive_only_nd_roundtrip_generic<S: StatisticsType + 'static>() {
use crate::test_utils::generate_nd_test_data;
let beta = 10.0;
let wmax = 10.0;
let epsilon = 1e-6;
let kernel = LogisticKernel::new(wmax * beta);
let basis = FiniteTempBasis::<_, S>::new(kernel, beta, Some(epsilon), None);
let sampling_points = basis.default_matsubara_sampling_points(true);
let sampling =
MatsubaraSamplingPositiveOnly::with_sampling_points(&basis, sampling_points.clone());
let n_k = 4;
let n_omega = 5;
for dim in 0..3 {
let (coeffs_0, _gtau_0, _giwn_0) = generate_nd_test_data::<f64, _, _>(
&basis,
&[],
&sampling_points,
42 + dim as u64,
&[n_k, n_omega],
);
let coeffs_dim = crate::test_utils::movedim(&coeffs_0, 0, dim);
let values_dim = sampling.evaluate_nd(None, &coeffs_dim, dim);
let coeffs_fitted_dim = sampling.fit_nd(None, &values_dim, dim);
let coeffs_fitted_0 = crate::test_utils::movedim(&coeffs_fitted_dim, dim, 0);
let max_error = coeffs_0
.iter()
.zip(coeffs_fitted_0.iter())
.map(|(a, b)| (*a - *b).abs())
.fold(0.0, f64::max);
println!(
"MatsubaraSamplingPositiveOnly {:?} dim={} roundtrip error: {}",
S::STATISTICS,
dim,
max_error
);
assert!(
max_error < 1e-7,
"ND roundtrip (dim={}) error too large: {}",
dim,
max_error
);
}
}
fn test_regularized_bose_matsubara_sampling_roundtrip_generic() {
let beta = 10.0;
let wmax = 1.0; let epsilon = 1e-4;
let kernel = RegularizedBoseKernel::new(wmax * beta);
let basis = FiniteTempBasis::<_, Bosonic>::new(kernel, beta, Some(epsilon), None);
let basis_size = basis.size();
let sampling_points: Vec<MatsubaraFreq<Bosonic>> = (0..basis_size + 2)
.map(|n| MatsubaraFreq::new((2 * n) as i64).unwrap()) .collect();
let mut symmetric_points = sampling_points.clone();
for n in 1..basis_size + 2 {
symmetric_points.push(MatsubaraFreq::new(-((2 * n) as i64)).unwrap());
}
println!("\n=== RegularizedBose MatsubaraSampling Test ===");
println!("Basis size: {}", basis_size);
println!("Sampling points (symmetric): {}", symmetric_points.len());
let sampling = MatsubaraSampling::with_sampling_points(&basis, symmetric_points.clone());
let (_coeffs_random, _gtau_values, giwn_values) =
generate_test_data_tau_and_matsubara::<Complex<f64>, Bosonic, _>(
&basis,
&[0.5 * beta], &symmetric_points,
12345,
);
let coeffs_fitted = sampling.fit(&giwn_values);
let giwn_reconstructed = sampling.evaluate(&coeffs_fitted);
let max_error = giwn_values
.iter()
.zip(giwn_reconstructed.iter())
.map(|(a, b)| (*a - *b).error_norm())
.fold(0.0f64, f64::max);
println!("MatsubaraSampling roundtrip max error: {:.2e}", max_error);
assert!(
max_error < 2.0,
"RegularizedBose Matsubara roundtrip error too large: {}",
max_error
);
}
fn test_regularized_bose_matsubara_sampling_positive_only_roundtrip_generic() {
let beta = 10.0;
let wmax = 1.0; let epsilon = 1e-4;
let kernel = RegularizedBoseKernel::new(wmax * beta);
let basis = FiniteTempBasis::<_, Bosonic>::new(kernel, beta, Some(epsilon), None);
let basis_size = basis.size();
let sampling_points: Vec<MatsubaraFreq<Bosonic>> = (0..basis_size + 2)
.map(|n| MatsubaraFreq::new((2 * n) as i64).unwrap()) .collect();
println!("\n=== RegularizedBose MatsubaraSamplingPositiveOnly Test ===");
println!("Basis size: {}", basis_size);
println!("Sampling points (positive-only): {}", sampling_points.len());
let sampling =
MatsubaraSamplingPositiveOnly::with_sampling_points(&basis, sampling_points.clone());
let (_coeffs_random, _gtau_values, giwn_values) =
generate_test_data_tau_and_matsubara::<Complex<f64>, Bosonic, _>(
&basis,
&[0.5 * beta], &sampling_points,
54321,
);
let coeffs_fitted = sampling.fit(&giwn_values);
let giwn_reconstructed = sampling.evaluate(&coeffs_fitted);
let max_error = giwn_values
.iter()
.zip(giwn_reconstructed.iter())
.map(|(a, b)| (*a - *b).error_norm())
.fold(0.0f64, f64::max);
println!(
"MatsubaraSamplingPositiveOnly roundtrip max error: {:.2e}",
max_error
);
assert!(
max_error < 1e-2,
"RegularizedBose Matsubara positive-only roundtrip error too large: {}",
max_error
);
}
#[test]
fn test_regularized_bose_matsubara_sampling_roundtrip() {
test_regularized_bose_matsubara_sampling_roundtrip_generic();
}
#[test]
fn test_regularized_bose_matsubara_sampling_positive_only_roundtrip() {
test_regularized_bose_matsubara_sampling_positive_only_roundtrip_generic();
}
use mdarray::{Shape, Tensor};
#[test]
fn test_matsubara_sampling_evaluate_nd_to_matches() {
let beta = 10.0;
let wmax = 10.0;
let epsilon = 1e-6;
let kernel = LogisticKernel::new(wmax * beta);
let basis = FiniteTempBasis::<_, Fermionic>::new(kernel, beta, Some(epsilon), None);
let sampling = MatsubaraSampling::new(&basis);
let basis_size = basis.size();
let n_points = sampling.n_sampling_points();
let n_k = 3;
let n_omega = 4;
let coeffs =
Tensor::<Complex<f64>, crate::DynRank>::from_fn(&[basis_size, n_k, n_omega][..], |idx| {
Complex::new(
(idx[0] as f64 + 1.0) * (idx[1] as f64 + 0.5),
(idx[2] as f64) * 0.3,
)
});
let expected = sampling.evaluate_nd(None, &coeffs, 0);
let mut actual = Tensor::<Complex<f64>, crate::DynRank>::from_elem(
&[n_points, n_k, n_omega][..],
Complex::new(0.0, 0.0),
);
sampling.evaluate_nd_to(None, &coeffs, 0, &mut actual);
let expected_shape = expected.shape().with_dims(|d| d.to_vec());
let actual_shape = actual.shape().with_dims(|d| d.to_vec());
assert_eq!(expected_shape, actual_shape);
for i in 0..n_points {
for j in 0..n_k {
for k in 0..n_omega {
let e = expected[&[i, j, k][..]];
let a = actual[&[i, j, k][..]];
let diff = (e - a).norm();
assert!(
diff < 1e-14,
"Mismatch at [{}, {}, {}]: expected={:?}, actual={:?}",
i,
j,
k,
e,
a
);
}
}
}
}
#[test]
fn test_matsubara_sampling_fit_nd_to_matches() {
let beta = 10.0;
let wmax = 10.0;
let epsilon = 1e-6;
let kernel = LogisticKernel::new(wmax * beta);
let basis = FiniteTempBasis::<_, Fermionic>::new(kernel, beta, Some(epsilon), None);
let sampling = MatsubaraSampling::new(&basis);
let basis_size = basis.size();
let n_points = sampling.n_sampling_points();
let n_k = 3;
let n_omega = 4;
let values =
Tensor::<Complex<f64>, crate::DynRank>::from_fn(&[n_points, n_k, n_omega][..], |idx| {
Complex::new(
(idx[0] as f64 + 1.0) * (idx[1] as f64 + 0.5),
(idx[2] as f64) * 0.2,
)
});
let expected = sampling.fit_nd(None, &values, 0);
let mut actual = Tensor::<Complex<f64>, crate::DynRank>::from_elem(
&[basis_size, n_k, n_omega][..],
Complex::new(0.0, 0.0),
);
sampling.fit_nd_to(None, &values, 0, &mut actual);
let expected_shape = expected.shape().with_dims(|d| d.to_vec());
let actual_shape = actual.shape().with_dims(|d| d.to_vec());
assert_eq!(expected_shape, actual_shape);
for i in 0..basis_size {
for j in 0..n_k {
for k in 0..n_omega {
let e = expected[&[i, j, k][..]];
let a = actual[&[i, j, k][..]];
let diff = (e - a).norm();
assert!(
diff < 1e-14,
"Mismatch at [{}, {}, {}]: expected={:?}, actual={:?}",
i,
j,
k,
e,
a
);
}
}
}
}
#[test]
fn test_matsubara_sampling_positive_only_evaluate_nd_to_matches() {
let beta = 10.0;
let wmax = 10.0;
let epsilon = 1e-6;
let kernel = LogisticKernel::new(wmax * beta);
let basis = FiniteTempBasis::<_, Fermionic>::new(kernel, beta, Some(epsilon), None);
let sampling = MatsubaraSamplingPositiveOnly::new(&basis);
let basis_size = basis.size();
let n_points = sampling.n_sampling_points();
let n_k = 3;
let n_omega = 4;
let coeffs = Tensor::<f64, crate::DynRank>::from_fn(&[basis_size, n_k, n_omega][..], |idx| {
(idx[0] as f64 + 1.0) * (idx[1] as f64 + 0.5) * (idx[2] as f64 + 0.3)
});
let expected = sampling.evaluate_nd(None, &coeffs, 0);
let mut actual = Tensor::<Complex<f64>, crate::DynRank>::from_elem(
&[n_points, n_k, n_omega][..],
Complex::new(0.0, 0.0),
);
sampling.evaluate_nd_to(None, &coeffs, 0, &mut actual);
let expected_shape = expected.shape().with_dims(|d| d.to_vec());
let actual_shape = actual.shape().with_dims(|d| d.to_vec());
assert_eq!(expected_shape, actual_shape);
for i in 0..n_points {
for j in 0..n_k {
for k in 0..n_omega {
let e = expected[&[i, j, k][..]];
let a = actual[&[i, j, k][..]];
let diff = (e - a).norm();
assert!(
diff < 1e-14,
"Mismatch at [{}, {}, {}]: expected={:?}, actual={:?}",
i,
j,
k,
e,
a
);
}
}
}
}
#[test]
fn test_matsubara_sampling_positive_only_fit_nd_to_matches() {
let beta = 10.0;
let wmax = 10.0;
let epsilon = 1e-6;
let kernel = LogisticKernel::new(wmax * beta);
let basis = FiniteTempBasis::<_, Fermionic>::new(kernel, beta, Some(epsilon), None);
let sampling = MatsubaraSamplingPositiveOnly::new(&basis);
let basis_size = basis.size();
let n_points = sampling.n_sampling_points();
let n_k = 3;
let n_omega = 4;
let values =
Tensor::<Complex<f64>, crate::DynRank>::from_fn(&[n_points, n_k, n_omega][..], |idx| {
Complex::new(
(idx[0] as f64 + 1.0) * (idx[1] as f64 + 0.5),
(idx[2] as f64) * 0.2,
)
});
let expected = sampling.fit_nd(None, &values, 0);
let mut actual = Tensor::<f64, crate::DynRank>::from_elem(&[basis_size, n_k, n_omega][..], 0.0);
sampling.fit_nd_to(None, &values, 0, &mut actual);
let expected_shape = expected.shape().with_dims(|d| d.to_vec());
let actual_shape = actual.shape().with_dims(|d| d.to_vec());
assert_eq!(expected_shape, actual_shape);
for i in 0..basis_size {
for j in 0..n_k {
for k in 0..n_omega {
let e = expected[&[i, j, k][..]];
let a = actual[&[i, j, k][..]];
let diff = (e - a).abs();
assert!(
diff < 1e-14,
"Mismatch at [{}, {}, {}]: expected={}, actual={}",
i,
j,
k,
e,
a
);
}
}
}
}
#[test]
fn test_matsubara_sampling_debug_parameters() {
let t = 0.1;
let wmax = 1.0;
let beta = 1.0 / t;
let lambda_ = beta * wmax;
let kernel = LogisticKernel::new(lambda_);
let eps = f64::EPSILON;
let basisf: FiniteTempBasis<LogisticKernel, Fermionic> =
FiniteTempBasis::new(kernel, beta, Some(eps), None);
let matsf = MatsubaraSampling::new(&basisf);
assert_eq!(
basisf.size(),
19,
"Basis size should be 19 for T=0.1, wmax=1.0"
);
assert_eq!(
matsf.sampling_points().len(),
20,
"Number of Matsubara sampling points should be 20 for Fermionic basis with L=19"
);
let sampling_points = matsf.sampling_points();
for i in 1..sampling_points.len() {
assert!(
sampling_points[i - 1] <= sampling_points[i],
"Sampling points should be sorted"
);
}
assert_eq!(
matsf.basis_size(),
basisf.size(),
"MatsubaraSampling basis_size() should match basis.size()"
);
let matrix = matsf.matrix();
assert_eq!(
matrix.shape().0,
matsf.n_sampling_points(),
"Matrix rows should match number of sampling points"
);
assert_eq!(
matrix.shape().1,
basisf.size(),
"Matrix columns should match basis size"
);
}