use std::panic::catch_unwind;
use sparse_ir::sve::{TworkType, compute_sve};
use crate::types::{spir_kernel, spir_sve_result};
use crate::{SPIR_COMPUTATION_SUCCESS, SPIR_INTERNAL_ERROR, SPIR_INVALID_ARGUMENT, StatusCode};
#[unsafe(no_mangle)]
pub extern "C" fn spir_sve_result_release(sve: *mut spir_sve_result) {
if !sve.is_null() {
unsafe {
let _ = Box::from_raw(sve);
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn spir_sve_result_clone(src: *const spir_sve_result) -> *mut spir_sve_result {
if src.is_null() {
return std::ptr::null_mut();
}
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| unsafe {
let src_ref = &*src;
let cloned = (*src_ref).clone();
Box::into_raw(Box::new(cloned))
}));
result.unwrap_or(std::ptr::null_mut())
}
#[unsafe(no_mangle)]
pub extern "C" fn spir_sve_result_is_assigned(obj: *const spir_sve_result) -> i32 {
if obj.is_null() { 0 } else { 1 }
}
#[unsafe(no_mangle)]
pub extern "C" fn spir_sve_result_new(
k: *const spir_kernel,
epsilon: f64,
_lmax: libc::c_int,
_n_gauss: libc::c_int,
twork: libc::c_int,
status: *mut StatusCode,
) -> *mut spir_sve_result {
if status.is_null() {
return std::ptr::null_mut();
}
if k.is_null() {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return std::ptr::null_mut();
}
if epsilon <= 0.0 {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return std::ptr::null_mut();
}
let twork_type = match twork {
0 => TworkType::Float64,
1 => TworkType::Float64X2,
-1 => TworkType::Auto,
_ => {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return std::ptr::null_mut();
}
};
if std::env::var("SPARSEIR_DEBUG").is_ok() {
eprintln!(
"[SPARSEIR DEBUG] spir_sve_result_new: called with epsilon={}, twork={}, kernel_ptr={:p}",
epsilon, twork, k
);
std::io::Write::flush(&mut std::io::stderr()).ok();
}
let result = catch_unwind(|| unsafe {
let kernel = &*k;
if std::env::var("SPARSEIR_DEBUG").is_ok() {
eprintln!("[SPARSEIR DEBUG] spir_sve_result_new: kernel type determined");
std::io::Write::flush(&mut std::io::stderr()).ok();
}
let sve_result = if let Some(logistic) = kernel.as_logistic() {
debug_println!("spir_sve_result_new: computing SVE for LogisticKernel");
compute_sve(
**logistic, epsilon, None,
None, twork_type,
)
} else if let Some(reg_bose) = kernel.as_regularized_bose() {
debug_println!("spir_sve_result_new: computing SVE for RegularizedBoseKernel");
compute_sve(
**reg_bose, epsilon, None,
None, twork_type,
)
} else {
debug_eprintln!("spir_sve_result_new: Unknown kernel type");
return Err("Unknown kernel type");
};
debug_println!("spir_sve_result_new: SVE computation completed, creating wrapper");
let sve_wrapper = spir_sve_result::new(sve_result);
debug_println!("spir_sve_result_new: wrapper created successfully");
Ok(Box::into_raw(Box::new(sve_wrapper)))
});
match result {
Ok(Ok(ptr)) => {
unsafe {
*status = SPIR_COMPUTATION_SUCCESS;
}
ptr
}
Ok(Err(msg)) => {
debug_eprintln!("Error in spir_sve_result_new: {}", msg);
unsafe {
*status = SPIR_INTERNAL_ERROR;
}
std::ptr::null_mut()
}
Err(panic_payload) => {
debug_eprintln!("Panic in spir_sve_result_new");
if let Some(s) = panic_payload.downcast_ref::<String>() {
debug_eprintln!("Panic message: {}", s);
} else if let Some(s) = panic_payload.downcast_ref::<&str>() {
debug_eprintln!("Panic message: {}", s);
} else {
debug_eprintln!("Panic occurred but message could not be extracted");
}
unsafe {
*status = SPIR_INTERNAL_ERROR;
}
std::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn spir_sve_result_get_size(
sve: *const spir_sve_result,
size: *mut libc::c_int,
) -> StatusCode {
if sve.is_null() || size.is_null() {
return SPIR_INVALID_ARGUMENT;
}
let result = catch_unwind(|| unsafe {
let s = &*sve;
*size = s.size() as libc::c_int;
SPIR_COMPUTATION_SUCCESS
});
result.unwrap_or(SPIR_INTERNAL_ERROR)
}
#[unsafe(no_mangle)]
pub extern "C" fn spir_sve_result_truncate(
sve: *const spir_sve_result,
epsilon: f64,
max_size: libc::c_int,
status: *mut StatusCode,
) -> *mut spir_sve_result {
if status.is_null() {
return std::ptr::null_mut();
}
if sve.is_null() {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return std::ptr::null_mut();
}
if epsilon < 0.0 || !epsilon.is_finite() {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return std::ptr::null_mut();
}
let result = catch_unwind(|| unsafe {
let sve_ref = &*sve;
let max_size_opt = if max_size < 0 {
None
} else {
Some(max_size as usize)
};
let (u_part, s_part, v_part) = sve_ref.inner().part(Some(epsilon), max_size_opt);
let sve_truncated = sparse_ir::sve::SVEResult::new(
u_part, s_part, v_part, epsilon, );
let sve_wrapper = spir_sve_result::new(sve_truncated);
Box::into_raw(Box::new(sve_wrapper))
});
match result {
Ok(ptr) => {
unsafe {
*status = SPIR_COMPUTATION_SUCCESS;
}
ptr
}
Err(_) => {
unsafe {
*status = SPIR_INTERNAL_ERROR;
}
std::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn spir_sve_result_get_svals(
sve: *const spir_sve_result,
svals: *mut f64,
) -> StatusCode {
if sve.is_null() || svals.is_null() {
return SPIR_INVALID_ARGUMENT;
}
let result = catch_unwind(|| unsafe {
let s = &*sve;
let sval_slice = s.svals();
std::ptr::copy_nonoverlapping(sval_slice.as_ptr(), svals, sval_slice.len());
SPIR_COMPUTATION_SUCCESS
});
result.unwrap_or(SPIR_INTERNAL_ERROR)
}
#[unsafe(no_mangle)]
pub extern "C" fn spir_sve_result_from_matrix(
#[allow(non_snake_case)] K_high: *const f64,
#[allow(non_snake_case)] K_low: *const f64,
nx: libc::c_int,
ny: libc::c_int,
order: libc::c_int,
segments_x: *const f64,
n_segments_x: libc::c_int,
segments_y: *const f64,
n_segments_y: libc::c_int,
n_gauss: libc::c_int,
epsilon: f64,
status: *mut StatusCode,
) -> *mut spir_sve_result {
use crate::utils::MemoryOrder;
use sparse_ir::gauss::legendre;
use sparse_ir::poly::PiecewiseLegendrePolyVector;
use sparse_ir::sve::SVEResult;
use sparse_ir::tsvd::compute_svd_dtensor;
use std::panic::catch_unwind;
if status.is_null() {
return std::ptr::null_mut();
}
if K_high.is_null() || segments_x.is_null() || segments_y.is_null() {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return std::ptr::null_mut();
}
if nx < 1 || ny < 1 || n_segments_x < 1 || n_segments_y < 1 || n_gauss < 1 {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return std::ptr::null_mut();
}
if epsilon <= 0.0 || !epsilon.is_finite() {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return std::ptr::null_mut();
}
let result = catch_unwind(|| {
let segs_x_slice =
unsafe { std::slice::from_raw_parts(segments_x, (n_segments_x + 1) as usize) };
let segs_y_slice =
unsafe { std::slice::from_raw_parts(segments_y, (n_segments_y + 1) as usize) };
for i in 1..=n_segments_x as usize {
if segs_x_slice[i] <= segs_x_slice[i - 1] {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return std::ptr::null_mut();
}
}
for i in 1..=n_segments_y as usize {
if segs_y_slice[i] <= segs_y_slice[i - 1] {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return std::ptr::null_mut();
}
}
let use_ddouble = !K_low.is_null();
let rule_base_dd = legendre::<sparse_ir::Df64>(n_gauss as usize);
if use_ddouble {
use sparse_ir::Df64;
use sparse_ir::numeric::CustomNumeric;
let segs_x_dd: Vec<Df64> = segs_x_slice.iter().map(|&x| Df64::from(x)).collect();
let segs_y_dd: Vec<Df64> = segs_y_slice.iter().map(|&y| Df64::from(y)).collect();
let gauss_x_dd = rule_base_dd.piecewise(&segs_x_dd);
let gauss_y_dd = rule_base_dd.piecewise(&segs_y_dd);
let memory_order = MemoryOrder::from_c_int(order).unwrap_or(MemoryOrder::RowMajor);
let mut matrix =
mdarray::DTensor::<Df64, 2>::from_elem([nx as usize, ny as usize], Df64::new(0.0));
let k_high_slice = unsafe { std::slice::from_raw_parts(K_high, (nx * ny) as usize) };
let k_low_slice = unsafe { std::slice::from_raw_parts(K_low, (nx * ny) as usize) };
match memory_order {
MemoryOrder::RowMajor => {
for i in 0..(nx as usize) {
for j in 0..(ny as usize) {
let idx = i * (ny as usize) + j;
matrix[[i, j]] =
unsafe { Df64::new_full(k_high_slice[idx], k_low_slice[idx]) };
}
}
}
MemoryOrder::ColumnMajor => {
for i in 0..(nx as usize) {
for j in 0..(ny as usize) {
let idx = j * (nx as usize) + i;
matrix[[i, j]] =
unsafe { Df64::new_full(k_high_slice[idx], k_low_slice[idx]) };
}
}
}
}
let gauss_rule_f64 = legendre::<f64>(n_gauss as usize);
let segs_x_f64: Vec<f64> = segs_x_slice.to_vec();
let segs_y_f64: Vec<f64> = segs_y_slice.to_vec();
let (u, s, v) = compute_svd_dtensor(&matrix);
use sparse_ir::sve::utils::remove_weights;
let u_unweighted = remove_weights(&u, gauss_x_dd.w.as_slice(), true);
let v_unweighted = remove_weights(&v, gauss_y_dd.w.as_slice(), true);
let u_f64 = mdarray::DTensor::<f64, 2>::from_fn(*u_unweighted.shape(), |idx| {
u_unweighted[idx].to_f64()
});
let v_f64 = mdarray::DTensor::<f64, 2>::from_fn(*v_unweighted.shape(), |idx| {
v_unweighted[idx].to_f64()
});
let u_polys = sparse_ir::sve::utils::svd_to_polynomials(
&u_f64,
&segs_x_f64,
&gauss_rule_f64,
n_gauss as usize,
);
let v_polys = sparse_ir::sve::utils::svd_to_polynomials(
&v_f64,
&segs_y_f64,
&gauss_rule_f64,
n_gauss as usize,
);
let s_f64: Vec<f64> = s.iter().map(|&sv| sv.to_f64()).collect();
let sve_result = SVEResult::new(
PiecewiseLegendrePolyVector::new(u_polys),
s_f64,
PiecewiseLegendrePolyVector::new(v_polys),
epsilon,
);
let sve_wrapper = spir_sve_result::new(sve_result);
Box::into_raw(Box::new(sve_wrapper))
} else {
let memory_order = MemoryOrder::from_c_int(order).unwrap_or(MemoryOrder::RowMajor);
let mut matrix = mdarray::DTensor::<f64, 2>::zeros([nx as usize, ny as usize]);
let k_high_slice = unsafe { std::slice::from_raw_parts(K_high, (nx * ny) as usize) };
match memory_order {
MemoryOrder::RowMajor => {
for i in 0..(nx as usize) {
for j in 0..(ny as usize) {
let idx = i * (ny as usize) + j;
matrix[[i, j]] = k_high_slice[idx];
}
}
}
MemoryOrder::ColumnMajor => {
for i in 0..(nx as usize) {
for j in 0..(ny as usize) {
let idx = j * (nx as usize) + i;
matrix[[i, j]] = k_high_slice[idx];
}
}
}
}
let gauss_rule_f64 = legendre::<f64>(n_gauss as usize);
let segs_x_f64: Vec<f64> = segs_x_slice.to_vec();
let segs_y_f64: Vec<f64> = segs_y_slice.to_vec();
let gauss_x = gauss_rule_f64.piecewise(&segs_x_f64);
let gauss_y = gauss_rule_f64.piecewise(&segs_y_f64);
let (u, s, v) = compute_svd_dtensor(&matrix);
use sparse_ir::sve::utils::remove_weights;
let u_unweighted = remove_weights(&u, gauss_x.w.as_slice(), true);
let v_unweighted = remove_weights(&v, gauss_y.w.as_slice(), true);
let u_polys = sparse_ir::sve::utils::svd_to_polynomials(
&u_unweighted,
&segs_x_f64,
&gauss_rule_f64,
n_gauss as usize,
);
let v_polys = sparse_ir::sve::utils::svd_to_polynomials(
&v_unweighted,
&segs_y_f64,
&gauss_rule_f64,
n_gauss as usize,
);
let s_f64: Vec<f64> = s;
let sve_result = SVEResult::new(
PiecewiseLegendrePolyVector::new(u_polys),
s_f64,
PiecewiseLegendrePolyVector::new(v_polys),
epsilon,
);
let sve_wrapper = spir_sve_result::new(sve_result);
Box::into_raw(Box::new(sve_wrapper))
}
});
match result {
Ok(ptr) => {
unsafe {
*status = SPIR_COMPUTATION_SUCCESS;
}
ptr
}
Err(_) => {
unsafe {
*status = SPIR_INTERNAL_ERROR;
}
std::ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn spir_sve_result_from_matrix_centrosymmetric(
#[allow(non_snake_case)] K_even_high: *const f64,
#[allow(non_snake_case)] K_even_low: *const f64,
#[allow(non_snake_case)] K_odd_high: *const f64,
#[allow(non_snake_case)] K_odd_low: *const f64,
nx: libc::c_int,
ny: libc::c_int,
order: libc::c_int,
segments_x: *const f64,
n_segments_x: libc::c_int,
segments_y: *const f64,
n_segments_y: libc::c_int,
n_gauss: libc::c_int,
epsilon: f64,
status: *mut StatusCode,
) -> *mut spir_sve_result {
use crate::utils::MemoryOrder;
use sparse_ir::gauss::legendre;
use sparse_ir::kernel::SymmetryType;
use sparse_ir::poly::PiecewiseLegendrePolyVector;
use sparse_ir::sve::utils::{extend_to_full_domain, merge_results};
use sparse_ir::tsvd::compute_svd_dtensor;
use std::panic::catch_unwind;
if status.is_null() {
return std::ptr::null_mut();
}
if K_even_high.is_null() || K_odd_high.is_null() || segments_x.is_null() || segments_y.is_null()
{
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return std::ptr::null_mut();
}
if nx < 1 || ny < 1 || n_segments_x < 1 || n_segments_y < 1 || n_gauss < 1 {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return std::ptr::null_mut();
}
if epsilon <= 0.0 || !epsilon.is_finite() {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return std::ptr::null_mut();
}
let result = catch_unwind(|| {
let segs_x_slice =
unsafe { std::slice::from_raw_parts(segments_x, (n_segments_x + 1) as usize) };
let segs_y_slice =
unsafe { std::slice::from_raw_parts(segments_y, (n_segments_y + 1) as usize) };
for i in 1..=n_segments_x as usize {
if segs_x_slice[i] <= segs_x_slice[i - 1] {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return std::ptr::null_mut();
}
}
for i in 1..=n_segments_y as usize {
if segs_y_slice[i] <= segs_y_slice[i - 1] {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return std::ptr::null_mut();
}
}
let use_ddouble = !K_even_low.is_null() && !K_odd_low.is_null();
let xmax = segs_x_slice[segs_x_slice.len() - 1];
let ymax = segs_y_slice[segs_y_slice.len() - 1];
let segs_x_f64: Vec<f64> = segs_x_slice.to_vec();
let segs_y_f64: Vec<f64> = segs_y_slice.to_vec();
let gauss_rule_f64 = legendre::<f64>(n_gauss as usize);
let gauss_x = gauss_rule_f64.piecewise(&segs_x_f64);
let gauss_y = gauss_rule_f64.piecewise(&segs_y_f64);
let compute_svd_for_symmetry = |k_high: *const f64,
k_low: *const f64|
-> Option<(
mdarray::DTensor<f64, 2>,
Vec<f64>,
mdarray::DTensor<f64, 2>,
)> {
let memory_order = MemoryOrder::from_c_int(order).unwrap_or(MemoryOrder::RowMajor);
let matrix = if use_ddouble {
use sparse_ir::Df64;
use sparse_ir::numeric::CustomNumeric;
let mut matrix_dd = mdarray::DTensor::<Df64, 2>::from_elem(
[nx as usize, ny as usize],
Df64::new(0.0),
);
let k_high_slice =
unsafe { std::slice::from_raw_parts(k_high, (nx * ny) as usize) };
let k_low_slice = unsafe { std::slice::from_raw_parts(k_low, (nx * ny) as usize) };
match memory_order {
MemoryOrder::RowMajor => {
for i in 0..(nx as usize) {
for j in 0..(ny as usize) {
let idx = i * (ny as usize) + j;
matrix_dd[[i, j]] =
unsafe { Df64::new_full(k_high_slice[idx], k_low_slice[idx]) };
}
}
}
MemoryOrder::ColumnMajor => {
for i in 0..(nx as usize) {
for j in 0..(ny as usize) {
let idx = j * (nx as usize) + i;
matrix_dd[[i, j]] =
unsafe { Df64::new_full(k_high_slice[idx], k_low_slice[idx]) };
}
}
}
}
mdarray::DTensor::<f64, 2>::from_fn(*matrix_dd.shape(), |idx| {
matrix_dd[idx].to_f64()
})
} else {
let mut matrix_f64 = mdarray::DTensor::<f64, 2>::zeros([nx as usize, ny as usize]);
let k_high_slice =
unsafe { std::slice::from_raw_parts(k_high, (nx * ny) as usize) };
match memory_order {
MemoryOrder::RowMajor => {
for i in 0..(nx as usize) {
for j in 0..(ny as usize) {
let idx = i * (ny as usize) + j;
matrix_f64[[i, j]] = k_high_slice[idx];
}
}
}
MemoryOrder::ColumnMajor => {
for i in 0..(nx as usize) {
for j in 0..(ny as usize) {
let idx = j * (nx as usize) + i;
matrix_f64[[i, j]] = k_high_slice[idx];
}
}
}
}
matrix_f64
};
let (u, s, v) = compute_svd_dtensor(&matrix);
use sparse_ir::sve::utils::remove_weights;
let u_unweighted = remove_weights(&u, gauss_x.w.as_slice(), true);
let v_unweighted = remove_weights(&v, gauss_y.w.as_slice(), true);
let s_f64: Vec<f64> = s;
Some((u_unweighted, s_f64, v_unweighted))
};
let (u_even, s_even, v_even) = match compute_svd_for_symmetry(K_even_high, K_even_low) {
Some(result) => result,
None => {
unsafe {
*status = SPIR_INTERNAL_ERROR;
}
return std::ptr::null_mut();
}
};
let (u_odd, s_odd, v_odd) = match compute_svd_for_symmetry(K_odd_high, K_odd_low) {
Some(result) => result,
None => {
unsafe {
*status = SPIR_INTERNAL_ERROR;
}
return std::ptr::null_mut();
}
};
let u_even_polys = sparse_ir::sve::utils::svd_to_polynomials(
&u_even,
&segs_x_f64,
&gauss_rule_f64,
n_gauss as usize,
);
let v_even_polys = sparse_ir::sve::utils::svd_to_polynomials(
&v_even,
&segs_y_f64,
&gauss_rule_f64,
n_gauss as usize,
);
let u_odd_polys = sparse_ir::sve::utils::svd_to_polynomials(
&u_odd,
&segs_x_f64,
&gauss_rule_f64,
n_gauss as usize,
);
let v_odd_polys = sparse_ir::sve::utils::svd_to_polynomials(
&v_odd,
&segs_y_f64,
&gauss_rule_f64,
n_gauss as usize,
);
let u_even_full = extend_to_full_domain(u_even_polys, SymmetryType::Even, xmax);
let v_even_full = extend_to_full_domain(v_even_polys, SymmetryType::Even, ymax);
let u_odd_full = extend_to_full_domain(u_odd_polys, SymmetryType::Odd, xmax);
let v_odd_full = extend_to_full_domain(v_odd_polys, SymmetryType::Odd, ymax);
let result_even = (
PiecewiseLegendrePolyVector::new(u_even_full),
s_even,
PiecewiseLegendrePolyVector::new(v_even_full),
);
let result_odd = (
PiecewiseLegendrePolyVector::new(u_odd_full),
s_odd,
PiecewiseLegendrePolyVector::new(v_odd_full),
);
let sve_result = merge_results(result_even, result_odd, epsilon);
let sve_wrapper = spir_sve_result::new(sve_result);
Box::into_raw(Box::new(sve_wrapper))
});
match result {
Ok(ptr) => {
unsafe {
*status = SPIR_COMPUTATION_SUCCESS;
}
ptr
}
Err(_) => {
unsafe {
*status = SPIR_INTERNAL_ERROR;
}
std::ptr::null_mut()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::kernel::*;
use crate::{
SPIR_COMPUTATION_SUCCESS, SPIR_INTERNAL_ERROR, SPIR_ORDER_COLUMN_MAJOR,
SPIR_ORDER_ROW_MAJOR, spir_gauss_legendre_rule_piecewise_double,
};
use std::ptr;
#[test]
fn test_sve_result_logistic() {
let mut kernel_status = SPIR_INTERNAL_ERROR;
let kernel = spir_logistic_kernel_new(10.0, &mut kernel_status);
assert_eq!(kernel_status, SPIR_COMPUTATION_SUCCESS);
assert!(!kernel.is_null());
let mut sve_status = SPIR_INTERNAL_ERROR;
let sve = spir_sve_result_new(
kernel,
1e-6, -1, -1, -1, &mut sve_status,
);
assert_eq!(sve_status, SPIR_COMPUTATION_SUCCESS);
assert!(!sve.is_null());
let mut size = 0;
let status = spir_sve_result_get_size(sve, &mut size);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
assert!(size > 0);
debug_println!("SVE size: {}", size);
let mut svals = vec![0.0; size as usize];
let status = spir_sve_result_get_svals(sve, svals.as_mut_ptr());
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
assert!(svals[0] > 0.0);
for i in 1..svals.len() {
assert!(svals[i] <= svals[i - 1]);
}
spir_sve_result_release(sve);
spir_kernel_release(kernel);
}
#[test]
fn test_sve_result_truncate() {
let mut kernel_status = SPIR_INTERNAL_ERROR;
let kernel = spir_logistic_kernel_new(10.0, &mut kernel_status);
let mut sve_status = SPIR_INTERNAL_ERROR;
let sve = spir_sve_result_new(kernel, 1e-6, -1, -1, -1, &mut sve_status);
let mut size = 0;
spir_sve_result_get_size(sve, &mut size);
let mut truncate_status = SPIR_INTERNAL_ERROR;
let sve_truncated = spir_sve_result_truncate(sve, 1e-4, size / 2, &mut truncate_status);
assert_eq!(truncate_status, SPIR_COMPUTATION_SUCCESS);
assert!(!sve_truncated.is_null());
let mut new_size = 0;
spir_sve_result_get_size(sve_truncated, &mut new_size);
assert!(new_size <= size / 2);
spir_sve_result_release(sve_truncated);
spir_sve_result_release(sve);
spir_kernel_release(kernel);
}
#[test]
fn test_sve_null_pointers() {
let mut status = SPIR_COMPUTATION_SUCCESS;
let sve = spir_sve_result_new(ptr::null(), 1e-6, -1, -1, -1, &mut status);
assert_eq!(status, SPIR_INVALID_ARGUMENT);
assert!(sve.is_null());
let mut kernel_status = SPIR_INTERNAL_ERROR;
let kernel = spir_logistic_kernel_new(10.0, &mut kernel_status);
let mut sve_status = SPIR_INTERNAL_ERROR;
let sve = spir_sve_result_new(kernel, 1e-6, -1, -1, -1, &mut sve_status);
let status = spir_sve_result_get_size(sve, ptr::null_mut());
assert_eq!(status, SPIR_INVALID_ARGUMENT);
spir_sve_result_release(sve);
spir_kernel_release(kernel);
}
#[test]
fn test_sve_result_from_matrix() {
let lambda = 10.0;
let epsilon = 1e-8;
let mut kernel_status = SPIR_INTERNAL_ERROR;
let kernel = spir_logistic_kernel_new(lambda, &mut kernel_status);
assert_eq!(kernel_status, SPIR_COMPUTATION_SUCCESS);
assert!(!kernel.is_null());
let mut n_gauss = 0;
let status = spir_kernel_get_sve_hints_ngauss(kernel, epsilon, &mut n_gauss);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
let mut n_segments_x = 0;
let status = spir_kernel_get_sve_hints_segments_x(
kernel,
epsilon,
ptr::null_mut(),
&mut n_segments_x,
);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
let mut n_segments_y = 0;
let status = spir_kernel_get_sve_hints_segments_y(
kernel,
epsilon,
ptr::null_mut(),
&mut n_segments_y,
);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
let mut segments_x = vec![0.0; (n_segments_x + 1) as usize];
let mut n_segments_x_out = n_segments_x + 1;
let status = spir_kernel_get_sve_hints_segments_x(
kernel,
epsilon,
segments_x.as_mut_ptr(),
&mut n_segments_x_out,
);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
let mut segments_y = vec![0.0; (n_segments_y + 1) as usize];
let mut n_segments_y_out = n_segments_y + 1;
let status = spir_kernel_get_sve_hints_segments_y(
kernel,
epsilon,
segments_y.as_mut_ptr(),
&mut n_segments_y_out,
);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
assert_eq!(segments_x.len(), (n_segments_x + 1) as usize);
assert_eq!(segments_y.len(), (n_segments_y + 1) as usize);
let nx = n_gauss * (n_segments_x); let ny = n_gauss * (n_segments_y); let mut x = vec![0.0; nx as usize];
let mut w_x = vec![0.0; nx as usize];
let mut y = vec![0.0; ny as usize];
let mut w_y = vec![0.0; ny as usize];
let mut status_gauss = SPIR_INTERNAL_ERROR;
let result = spir_gauss_legendre_rule_piecewise_double(
n_gauss,
segments_x.as_ptr(),
n_segments_x,
x.as_mut_ptr(),
w_x.as_mut_ptr(),
&mut status_gauss,
);
assert_eq!(result, SPIR_COMPUTATION_SUCCESS);
let mut status_gauss = SPIR_INTERNAL_ERROR;
let result = spir_gauss_legendre_rule_piecewise_double(
n_gauss,
segments_y.as_ptr(),
n_segments_y,
y.as_mut_ptr(),
w_y.as_mut_ptr(),
&mut status_gauss,
);
assert_eq!(result, SPIR_COMPUTATION_SUCCESS);
let mut k_high = vec![0.0; (nx * ny) as usize];
for i in 0..(nx as usize) {
for j in 0..(ny as usize) {
let k_val = if i == j {
(w_x[i] * w_y[j] as f64).sqrt()
} else {
0.0
};
k_high[i * ny as usize + j] = k_val;
}
}
let mut sve_status = SPIR_INTERNAL_ERROR;
let sve_from_matrix = spir_sve_result_from_matrix(
k_high.as_ptr(),
ptr::null(),
nx,
ny,
SPIR_ORDER_ROW_MAJOR,
segments_x.as_ptr(),
n_segments_x,
segments_y.as_ptr(),
n_segments_y,
n_gauss,
epsilon,
&mut sve_status,
);
if sve_status == SPIR_COMPUTATION_SUCCESS && !sve_from_matrix.is_null() {
let mut sve_size = 0;
let status = spir_sve_result_get_size(sve_from_matrix, &mut sve_size);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
spir_sve_result_release(sve_from_matrix);
}
{
let mut sve_status = SPIR_INTERNAL_ERROR;
let sve_err = spir_sve_result_from_matrix(
ptr::null(),
ptr::null(),
nx,
ny,
SPIR_ORDER_ROW_MAJOR,
segments_x.as_ptr(),
n_segments_x,
segments_y.as_ptr(),
n_segments_y,
n_gauss,
epsilon,
&mut sve_status,
);
assert_ne!(sve_status, SPIR_COMPUTATION_SUCCESS);
assert!(sve_err.is_null());
}
{
let mut k_col = vec![0.0; (nx * ny) as usize];
for j in 0..(ny as usize) {
for i in 0..(nx as usize) {
let k_val = if i == j {
(w_x[i] * w_y[j] as f64).sqrt()
} else {
0.0
};
k_col[j * nx as usize + i] = k_val;
}
}
let mut sve_status = SPIR_INTERNAL_ERROR;
let sve_col = spir_sve_result_from_matrix(
k_col.as_ptr(),
ptr::null(),
nx,
ny,
SPIR_ORDER_COLUMN_MAJOR,
segments_x.as_ptr(),
n_segments_x,
segments_y.as_ptr(),
n_segments_y,
n_gauss,
epsilon,
&mut sve_status,
);
if sve_status == SPIR_COMPUTATION_SUCCESS && !sve_col.is_null() {
spir_sve_result_release(sve_col);
}
}
spir_kernel_release(kernel);
}
#[test]
fn test_sve_result_from_matrix_centrosymmetric() {
let lambda = 10.0;
let epsilon = 1e-8;
let mut kernel_status = SPIR_INTERNAL_ERROR;
let kernel = spir_logistic_kernel_new(lambda, &mut kernel_status);
assert_eq!(kernel_status, SPIR_COMPUTATION_SUCCESS);
assert!(!kernel.is_null());
let mut n_gauss = 0;
let status = spir_kernel_get_sve_hints_ngauss(kernel, epsilon, &mut n_gauss);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
let mut n_segments_x = 0;
let status = spir_kernel_get_sve_hints_segments_x(
kernel,
epsilon,
ptr::null_mut(),
&mut n_segments_x,
);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
let mut n_segments_y = 0;
let status = spir_kernel_get_sve_hints_segments_y(
kernel,
epsilon,
ptr::null_mut(),
&mut n_segments_y,
);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
let mut segments_x = vec![0.0; (n_segments_x + 1) as usize];
let mut n_segments_x_out = n_segments_x + 1;
let status = spir_kernel_get_sve_hints_segments_x(
kernel,
epsilon,
segments_x.as_mut_ptr(),
&mut n_segments_x_out,
);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
let mut segments_y = vec![0.0; (n_segments_y + 1) as usize];
let mut n_segments_y_out = n_segments_y + 1;
let status = spir_kernel_get_sve_hints_segments_y(
kernel,
epsilon,
segments_y.as_mut_ptr(),
&mut n_segments_y_out,
);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
let nx = n_gauss * n_segments_x;
let ny = n_gauss * n_segments_y;
let mut x = vec![0.0; nx as usize];
let mut w_x = vec![0.0; nx as usize];
let mut y = vec![0.0; ny as usize];
let mut w_y = vec![0.0; ny as usize];
let mut status_gauss = SPIR_INTERNAL_ERROR;
let result = spir_gauss_legendre_rule_piecewise_double(
n_gauss,
segments_x.as_ptr(),
n_segments_x,
x.as_mut_ptr(),
w_x.as_mut_ptr(),
&mut status_gauss,
);
assert_eq!(result, SPIR_COMPUTATION_SUCCESS);
let mut status_gauss = SPIR_INTERNAL_ERROR;
let result = spir_gauss_legendre_rule_piecewise_double(
n_gauss,
segments_y.as_ptr(),
n_segments_y - 1,
y.as_mut_ptr(),
w_y.as_mut_ptr(),
&mut status_gauss,
);
assert_eq!(result, SPIR_COMPUTATION_SUCCESS);
let mut k_even_high = vec![0.0; (nx * ny) as usize];
let mut k_odd_high = vec![0.0; (nx * ny) as usize];
for i in 0..(nx as usize) {
for j in 0..(ny as usize) {
let k_val = if i == j {
(w_x[i] * w_y[j] as f64).sqrt()
} else {
0.0
};
k_even_high[i * ny as usize + j] = k_val;
k_odd_high[i * ny as usize + j] = k_val * 0.5; }
}
let mut sve_status = SPIR_INTERNAL_ERROR;
let sve_centrosymm = spir_sve_result_from_matrix_centrosymmetric(
k_even_high.as_ptr(),
ptr::null(),
k_odd_high.as_ptr(),
ptr::null(),
nx,
ny,
SPIR_ORDER_ROW_MAJOR,
segments_x.as_ptr(),
n_segments_x,
segments_y.as_ptr(),
n_segments_y,
n_gauss,
epsilon,
&mut sve_status,
);
if sve_status == SPIR_COMPUTATION_SUCCESS && !sve_centrosymm.is_null() {
let mut sve_size = 0;
let status = spir_sve_result_get_size(sve_centrosymm, &mut sve_size);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
spir_sve_result_release(sve_centrosymm);
}
{
let mut sve_status = SPIR_INTERNAL_ERROR;
let sve_err = spir_sve_result_from_matrix_centrosymmetric(
ptr::null(),
ptr::null(),
k_odd_high.as_ptr(),
ptr::null(),
nx,
ny,
SPIR_ORDER_ROW_MAJOR,
segments_x.as_ptr(),
n_segments_x,
segments_y.as_ptr(),
n_segments_y,
n_gauss,
epsilon,
&mut sve_status,
);
assert_ne!(sve_status, SPIR_COMPUTATION_SUCCESS);
assert!(sve_err.is_null());
}
spir_kernel_release(kernel);
}
#[test]
fn test_sve_result_from_matrix_centrosymmetric_vs_from_matrix() {
use sparse_ir::gauss::legendre;
use sparse_ir::kernel::{KernelProperties, LogisticKernel, SVEHints, SymmetryType};
use sparse_ir::kernelmatrix::{
matrix_from_gauss_noncentrosymmetric, matrix_from_gauss_with_segments,
};
let lambda = 10.0;
let epsilon = 1e-6;
let kernel = LogisticKernel::new(lambda);
let hints = kernel.sve_hints::<f64>(epsilon);
let segments_x = hints.segments_x();
let segments_y = hints.segments_y();
let n_gauss = hints.ngauss();
let gauss_rule = legendre::<f64>(n_gauss);
let gauss_x_reduced = gauss_rule.piecewise(&segments_x);
let gauss_y_reduced = gauss_rule.piecewise(&segments_y);
let discretized_even = matrix_from_gauss_with_segments(
&kernel,
&gauss_x_reduced,
&gauss_y_reduced,
SymmetryType::Even,
&hints,
);
let discretized_odd = matrix_from_gauss_with_segments(
&kernel,
&gauss_x_reduced,
&gauss_y_reduced,
SymmetryType::Odd,
&hints,
);
let k_even_weighted = discretized_even.apply_weights_for_sve();
let k_odd_weighted = discretized_odd.apply_weights_for_sve();
let mut segments_x_full = Vec::new();
for i in (0..segments_x.len()).rev() {
segments_x_full.push(-segments_x[i]);
}
for i in 1..segments_x.len() {
segments_x_full.push(segments_x[i]);
}
let mut segments_y_full = Vec::new();
for i in (0..segments_y.len()).rev() {
segments_y_full.push(-segments_y[i]);
}
for i in 1..segments_y.len() {
segments_y_full.push(segments_y[i]);
}
let gauss_x_full = gauss_rule.piecewise(&segments_x_full);
let gauss_y_full = gauss_rule.piecewise(&segments_y_full);
let discretized_full =
matrix_from_gauss_noncentrosymmetric(&kernel, &gauss_x_full, &gauss_y_full, &hints);
let k_full_weighted = discretized_full.apply_weights_for_sve();
let nx = k_even_weighted.shape().0;
let ny = k_even_weighted.shape().1;
let nx_full = k_full_weighted.shape().0;
let ny_full = k_full_weighted.shape().1;
let mut k_even_vec = vec![0.0; nx * ny];
let mut k_odd_vec = vec![0.0; nx * ny];
let mut k_full_vec = vec![0.0; nx_full * ny_full];
for i in 0..nx {
for j in 0..ny {
let idx = i * ny + j;
k_even_vec[idx] = k_even_weighted[[i, j]];
k_odd_vec[idx] = k_odd_weighted[[i, j]];
}
}
for i in 0..nx_full {
for j in 0..ny_full {
let idx = i * ny_full + j;
k_full_vec[idx] = k_full_weighted[[i, j]];
}
}
let segments_x_vec = segments_x.clone();
let segments_y_vec = segments_y.clone();
let segments_x_full_vec = segments_x_full.clone();
let segments_y_full_vec = segments_y_full.clone();
let mut status_centrosymm = SPIR_INTERNAL_ERROR;
let sve_centrosymm = spir_sve_result_from_matrix_centrosymmetric(
k_even_vec.as_ptr(),
ptr::null(), k_odd_vec.as_ptr(),
ptr::null(), nx as libc::c_int,
ny as libc::c_int,
SPIR_ORDER_ROW_MAJOR,
segments_x_vec.as_ptr(),
(segments_x_vec.len() - 1) as libc::c_int,
segments_y_vec.as_ptr(),
(segments_y_vec.len() - 1) as libc::c_int,
n_gauss as libc::c_int,
epsilon,
&mut status_centrosymm,
);
assert_eq!(status_centrosymm, SPIR_COMPUTATION_SUCCESS);
assert!(!sve_centrosymm.is_null());
let mut status_noncentrosymm = SPIR_INTERNAL_ERROR;
let sve_noncentrosymm = spir_sve_result_from_matrix(
k_full_vec.as_ptr(),
ptr::null(), nx_full as libc::c_int,
ny_full as libc::c_int,
SPIR_ORDER_ROW_MAJOR,
segments_x_full_vec.as_ptr(),
(segments_x_full_vec.len() - 1) as libc::c_int,
segments_y_full_vec.as_ptr(),
(segments_y_full_vec.len() - 1) as libc::c_int,
n_gauss as libc::c_int,
epsilon,
&mut status_noncentrosymm,
);
assert_eq!(status_noncentrosymm, SPIR_COMPUTATION_SUCCESS);
assert!(!sve_noncentrosymm.is_null());
let mut size_centrosymm = 0;
let mut size_noncentrosymm = 0;
spir_sve_result_get_size(sve_centrosymm, &mut size_centrosymm);
spir_sve_result_get_size(sve_noncentrosymm, &mut size_noncentrosymm);
assert!(
(size_centrosymm as i32 - size_noncentrosymm as i32).abs() <= 1,
"Size mismatch: centrosymmetric={}, noncentrosymmetric={}",
size_centrosymm,
size_noncentrosymm
);
let mut svals_centrosymm = vec![0.0; size_centrosymm as usize];
let mut svals_noncentrosymm = vec![0.0; size_noncentrosymm as usize];
spir_sve_result_get_svals(sve_centrosymm, svals_centrosymm.as_mut_ptr());
spir_sve_result_get_svals(sve_noncentrosymm, svals_noncentrosymm.as_mut_ptr());
let min_size = size_centrosymm.min(size_noncentrosymm) as usize;
let tolerance = 1e-10;
for i in 0..min_size {
let diff = (svals_centrosymm[i] - svals_noncentrosymm[i]).abs();
let rel_diff = diff / svals_centrosymm[i].max(svals_noncentrosymm[i]);
assert!(
diff < tolerance || rel_diff < tolerance,
"Singular value mismatch at index {}: centrosymmetric={}, noncentrosymmetric={}, diff={}, rel_diff={}",
i,
svals_centrosymm[i],
svals_noncentrosymm[i],
diff,
rel_diff
);
}
println!(
"Comparison successful: {} singular values match within tolerance",
min_size
);
spir_sve_result_release(sve_centrosymm);
spir_sve_result_release(sve_noncentrosymm);
}
}