#[allow(unused_imports)] use crate::{
SPIR_COMPUTATION_SUCCESS, SPIR_INTERNAL_ERROR, SPIR_ORDER_COLUMN_MAJOR, SPIR_ORDER_ROW_MAJOR,
SPIR_TWORK_FLOAT64, SPIR_TWORK_FLOAT64X2,
};
#[allow(unused_imports)]
use mdarray::Shape;
use sparse_ir::numeric::CustomNumeric;
pub fn is_debug_enabled() -> bool {
std::env::var("SPARSEIR_DEBUG").is_ok()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemoryOrder {
RowMajor, ColumnMajor, }
impl MemoryOrder {
pub fn from_c_int(order: libc::c_int) -> Result<Self, ()> {
match order {
SPIR_ORDER_ROW_MAJOR => Ok(Self::RowMajor),
SPIR_ORDER_COLUMN_MAJOR => Ok(Self::ColumnMajor),
_ => Err(()),
}
}
}
pub fn convert_dims_for_row_major(
dims: &[usize],
target_dim: usize,
order: MemoryOrder,
) -> (Vec<usize>, usize) {
match order {
MemoryOrder::RowMajor => {
(dims.to_vec(), target_dim)
}
MemoryOrder::ColumnMajor => {
let mut rev_dims = dims.to_vec();
rev_dims.reverse();
let rev_target_dim = dims.len() - 1 - target_dim;
(rev_dims, rev_target_dim)
}
}
}
pub(crate) unsafe fn _read_tensor_nd_row_major<T: Copy>(
ptr: *const T,
dims: &[usize],
) -> sparse_ir::Tensor<T, sparse_ir::DynRank> {
assert!(!dims.is_empty(), "dims must not be empty");
let total: usize = dims.iter().product();
let slice = unsafe { std::slice::from_raw_parts(ptr, total) };
let data: Vec<T> = slice.to_vec();
let flat = sparse_ir::Tensor::<T, (usize,)>::from(data);
flat.into_dyn().reshape(dims).to_tensor()
}
pub(crate) unsafe fn _read_tensor_nd_column_major<T: Copy>(
ptr: *const T,
dims: &[usize],
) -> sparse_ir::Tensor<T, sparse_ir::DynRank> {
assert!(!dims.is_empty(), "dims must not be empty");
let mut rev_dims = dims.to_vec();
rev_dims.reverse();
let tmp = unsafe { _read_tensor_nd_row_major(ptr, &rev_dims) };
let rank = dims.len();
let perm: Vec<usize> = (0..rank).rev().collect();
use mdarray::Slice;
(&tmp as &Slice<T, sparse_ir::DynRank>)
.permute(&perm[..])
.to_tensor()
}
pub(crate) unsafe fn read_tensor_nd<T: Copy>(
ptr: *const T,
dims: &[usize],
order: MemoryOrder,
) -> sparse_ir::Tensor<T, sparse_ir::DynRank> {
match order {
MemoryOrder::RowMajor => unsafe { _read_tensor_nd_row_major(ptr, dims) },
MemoryOrder::ColumnMajor => unsafe { _read_tensor_nd_column_major(ptr, dims) },
}
}
pub(crate) unsafe fn copy_tensor_to_c_array<T: Copy>(
tensor: sparse_ir::Tensor<T, sparse_ir::DynRank>,
out: *mut T,
order: MemoryOrder,
) {
let total = tensor.len();
let flat = match order {
MemoryOrder::RowMajor => {
tensor.into_dyn().reshape(&[total]).to_tensor()
}
MemoryOrder::ColumnMajor => {
use mdarray::Slice;
let rank = tensor.rank();
let perm: Vec<usize> = (0..rank).rev().collect();
let permuted = (&tensor as &Slice<T, sparse_ir::DynRank>)
.permute(&perm[..])
.to_tensor();
permuted.into_dyn().reshape(&[total]).to_tensor()
}
};
for i in 0..total {
unsafe {
*out.add(i) = flat[i];
}
}
}
pub(crate) fn build_output_dims(
input_dims: &[usize],
target_dim: usize,
new_size: usize,
) -> Vec<usize> {
let mut out_dims = input_dims.to_vec();
out_dims[target_dim] = new_size;
out_dims
}
pub(crate) unsafe fn create_dview_from_ptr<'a, T>(
ptr: *const T,
dims: &[usize],
) -> mdarray::View<'a, T, sparse_ir::DynRank, mdarray::Dense> {
use mdarray::Shape;
let shape = sparse_ir::DynRank::from_dims(dims);
let mapping = mdarray::DenseMapping::new(shape);
unsafe { mdarray::View::new_unchecked(ptr, mapping) }
}
pub(crate) unsafe fn create_dviewmut_from_ptr<'a, T>(
ptr: *mut T,
dims: &[usize],
) -> mdarray::ViewMut<'a, T, sparse_ir::DynRank> {
use mdarray::Shape;
let shape = sparse_ir::DynRank::from_dims(dims);
let mapping = mdarray::DenseMapping::new(shape);
unsafe { mdarray::ViewMut::new_unchecked(ptr, mapping) }
}
#[unsafe(no_mangle)]
pub extern "C" fn spir_choose_working_type(epsilon: f64) -> libc::c_int {
if epsilon.is_nan() || epsilon < 1e-8 {
SPIR_TWORK_FLOAT64X2
} else {
SPIR_TWORK_FLOAT64
}
}
#[unsafe(no_mangle)]
pub extern "C" fn spir_gauss_legendre_rule_piecewise_double(
n: libc::c_int,
segments: *const f64,
n_segments: libc::c_int,
x: *mut f64,
w: *mut f64,
status: *mut crate::StatusCode,
) -> crate::StatusCode {
use crate::{SPIR_COMPUTATION_SUCCESS, SPIR_INTERNAL_ERROR, SPIR_INVALID_ARGUMENT};
use sparse_ir::legendre;
use std::panic::catch_unwind;
if status.is_null() {
return SPIR_INVALID_ARGUMENT;
}
if segments.is_null() || x.is_null() || w.is_null() {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return SPIR_INVALID_ARGUMENT;
}
if n < 1 || n_segments < 1 {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return SPIR_INVALID_ARGUMENT;
}
let result = catch_unwind(|| {
let segments_slice =
unsafe { std::slice::from_raw_parts(segments, (n_segments + 1) as usize) };
let segs_vec = segments_slice.to_vec();
for i in 1..segs_vec.len() {
if segs_vec[i] <= segs_vec[i - 1] {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return SPIR_INVALID_ARGUMENT;
}
}
let rule_dd = legendre::<sparse_ir::Df64>(n as usize);
let rule = sparse_ir::gauss::Rule::from_vectors(
rule_dd.x.iter().map(|&x| x.to_f64()).collect(),
rule_dd.w.iter().map(|&w| w.to_f64()).collect(),
rule_dd.a.to_f64(),
rule_dd.b.to_f64(),
);
let piecewise_rule = rule.piecewise(&segs_vec);
for i in 0..piecewise_rule.x.len() {
unsafe {
*x.add(i) = piecewise_rule.x[i];
*w.add(i) = piecewise_rule.w[i];
}
}
unsafe {
*status = SPIR_COMPUTATION_SUCCESS;
}
SPIR_COMPUTATION_SUCCESS
});
result.unwrap_or_else(|_| {
unsafe {
*status = SPIR_INTERNAL_ERROR;
}
SPIR_INTERNAL_ERROR
})
}
#[unsafe(no_mangle)]
pub extern "C" fn spir_gauss_legendre_rule_piecewise_ddouble(
n: libc::c_int,
segments: *const f64,
n_segments: libc::c_int,
x_high: *mut f64,
x_low: *mut f64,
w_high: *mut f64,
w_low: *mut f64,
status: *mut crate::StatusCode,
) -> crate::StatusCode {
use crate::{SPIR_COMPUTATION_SUCCESS, SPIR_INTERNAL_ERROR, SPIR_INVALID_ARGUMENT};
use sparse_ir::legendre;
use std::panic::catch_unwind;
if status.is_null() {
return SPIR_INVALID_ARGUMENT;
}
if segments.is_null()
|| x_high.is_null()
|| x_low.is_null()
|| w_high.is_null()
|| w_low.is_null()
{
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return SPIR_INVALID_ARGUMENT;
}
if n < 1 || n_segments < 1 {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return SPIR_INVALID_ARGUMENT;
}
let result = catch_unwind(|| {
let segments_slice =
unsafe { std::slice::from_raw_parts(segments, (n_segments + 1) as usize) };
let segs_vec: Vec<sparse_ir::Df64> = segments_slice
.iter()
.map(|&x| sparse_ir::Df64::new(x))
.collect();
for i in 1..segs_vec.len() {
if segs_vec[i] <= segs_vec[i - 1] {
unsafe {
*status = SPIR_INVALID_ARGUMENT;
}
return SPIR_INVALID_ARGUMENT;
}
}
let rule_dd = legendre::<sparse_ir::Df64>(n as usize);
let piecewise_rule = rule_dd.piecewise(&segs_vec);
for i in 0..piecewise_rule.x.len() {
unsafe {
*x_high.add(i) = piecewise_rule.x[i].hi();
*x_low.add(i) = piecewise_rule.x[i].lo();
*w_high.add(i) = piecewise_rule.w[i].hi();
*w_low.add(i) = piecewise_rule.w[i].lo();
}
}
unsafe {
*status = SPIR_COMPUTATION_SUCCESS;
}
SPIR_COMPUTATION_SUCCESS
});
result.unwrap_or_else(|_| {
unsafe {
*status = SPIR_INTERNAL_ERROR;
}
SPIR_INTERNAL_ERROR
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_order_conversion() {
assert_eq!(
MemoryOrder::from_c_int(SPIR_ORDER_ROW_MAJOR),
Ok(MemoryOrder::RowMajor)
);
assert_eq!(
MemoryOrder::from_c_int(SPIR_ORDER_COLUMN_MAJOR),
Ok(MemoryOrder::ColumnMajor)
);
assert_eq!(MemoryOrder::from_c_int(99), Err(()));
}
#[test]
fn test_choose_working_type() {
{
let twork = spir_choose_working_type(1e-6);
assert_eq!(twork, SPIR_TWORK_FLOAT64);
}
{
let twork = spir_choose_working_type(1e-8);
assert_eq!(twork, SPIR_TWORK_FLOAT64);
}
{
let twork = spir_choose_working_type(1e-10);
assert_eq!(twork, SPIR_TWORK_FLOAT64X2);
}
{
let twork = spir_choose_working_type(1e-15);
assert_eq!(twork, SPIR_TWORK_FLOAT64X2);
}
{
let twork = spir_choose_working_type(f64::NAN);
assert_eq!(twork, SPIR_TWORK_FLOAT64X2);
}
{
let twork = spir_choose_working_type(1e-8);
assert_eq!(twork, SPIR_TWORK_FLOAT64);
}
{
let twork = spir_choose_working_type(0.99e-8);
assert_eq!(twork, SPIR_TWORK_FLOAT64X2);
}
}
#[test]
fn test_gauss_legendre_rule_piecewise_double() {
{
let n = 5;
let segments = [-1.0, 1.0];
let n_segments = 1;
let mut x = vec![0.0; n as usize];
let mut w = vec![0.0; n as usize];
let mut status = SPIR_INTERNAL_ERROR;
let result = spir_gauss_legendre_rule_piecewise_double(
n,
segments.as_ptr(),
n_segments,
x.as_mut_ptr(),
w.as_mut_ptr(),
&mut status,
);
assert_eq!(result, SPIR_COMPUTATION_SUCCESS);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
assert!(x[0] >= -1.0);
assert!(x[(n - 1) as usize] <= 1.0);
for i in 1..(n as usize) {
assert!(x[i] > x[i - 1]);
}
for i in 0..(n as usize) {
assert!(w[i] > 0.0);
}
let weight_sum: f64 = w.iter().sum();
assert!((weight_sum - 2.0).abs() < 1e-10);
}
{
let n = 3;
let segments = [-1.0, 0.0, 1.0];
let n_segments = 2;
let mut x = vec![0.0; (n * n_segments) as usize];
let mut w = vec![0.0; (n * n_segments) as usize];
let mut status = SPIR_INTERNAL_ERROR;
let result = spir_gauss_legendre_rule_piecewise_double(
n,
segments.as_ptr(),
n_segments,
x.as_mut_ptr(),
w.as_mut_ptr(),
&mut status,
);
assert_eq!(result, SPIR_COMPUTATION_SUCCESS);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
assert!(x[0] >= -1.0);
assert!(x[5] <= 1.0);
for i in 1..6 {
assert!(x[i] > x[i - 1]);
}
for i in 0..6 {
assert!(w[i] > 0.0);
}
let weight_sum: f64 = w.iter().sum();
assert!((weight_sum - 2.0).abs() < 1e-10);
}
{
let mut status = SPIR_INTERNAL_ERROR;
let result = spir_gauss_legendre_rule_piecewise_double(
5,
std::ptr::null(),
1,
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut status,
);
assert_ne!(result, SPIR_COMPUTATION_SUCCESS);
}
{
let segments = [-1.0, 1.0];
let mut x = vec![0.0; 5];
let mut w = vec![0.0; 5];
let mut status = SPIR_INTERNAL_ERROR;
let result = spir_gauss_legendre_rule_piecewise_double(
0,
segments.as_ptr(),
1,
x.as_mut_ptr(),
w.as_mut_ptr(),
&mut status,
);
assert_ne!(result, SPIR_COMPUTATION_SUCCESS);
}
{
let segments = [1.0, -1.0]; let mut x = vec![0.0; 5];
let mut w = vec![0.0; 5];
let mut status = SPIR_INTERNAL_ERROR;
let result = spir_gauss_legendre_rule_piecewise_double(
5,
segments.as_ptr(),
1,
x.as_mut_ptr(),
w.as_mut_ptr(),
&mut status,
);
assert_ne!(result, SPIR_COMPUTATION_SUCCESS);
}
}
#[test]
fn test_read_tensor_nd_row_major() {
use num_complex::Complex64;
{
let data = vec![
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0,
];
let tensor = unsafe { _read_tensor_nd_row_major(data.as_ptr(), &[3, 4]) };
let shape_dims = tensor.shape().with_dims(|dims| dims.to_vec());
assert_eq!(shape_dims, &[3, 4]);
assert_eq!(tensor[[0, 0]], 1.0);
assert_eq!(tensor[[0, 3]], 4.0);
assert_eq!(tensor[[1, 0]], 5.0);
assert_eq!(tensor[[2, 3]], 12.0);
}
{
let data: Vec<f64> = (1..=24).map(|x| x as f64).collect();
let tensor = unsafe { _read_tensor_nd_row_major(data.as_ptr(), &[2, 3, 4]) };
let shape_dims = tensor.shape().with_dims(|dims| dims.to_vec());
assert_eq!(shape_dims, &[2, 3, 4]);
assert_eq!(tensor[[0, 0, 0]], 1.0);
assert_eq!(tensor[[0, 0, 3]], 4.0);
assert_eq!(tensor[[0, 1, 0]], 5.0);
assert_eq!(tensor[[1, 2, 3]], 24.0);
}
{
let data = vec![
Complex64::new(1.0, 2.0),
Complex64::new(3.0, 4.0),
Complex64::new(5.0, 6.0),
Complex64::new(7.0, 8.0),
];
let tensor = unsafe { _read_tensor_nd_row_major(data.as_ptr(), &[2, 2]) };
let shape_dims = tensor.shape().with_dims(|dims| dims.to_vec());
assert_eq!(shape_dims, &[2, 2]);
assert_eq!(tensor[[0, 0]], Complex64::new(1.0, 2.0));
assert_eq!(tensor[[1, 1]], Complex64::new(7.0, 8.0));
}
}
#[test]
fn test_read_tensor_nd_column_major() {
use num_complex::Complex64;
{
let data = vec![
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0,
];
let tensor = unsafe { _read_tensor_nd_column_major(data.as_ptr(), &[3, 4]) };
let shape_dims = tensor.shape().with_dims(|dims| dims.to_vec());
assert_eq!(shape_dims, &[3, 4]);
assert_eq!(tensor[[0, 0]], 1.0);
assert_eq!(tensor[[0, 1]], 4.0);
assert_eq!(tensor[[0, 3]], 10.0);
assert_eq!(tensor[[1, 0]], 2.0);
assert_eq!(tensor[[2, 3]], 12.0);
}
{
let data: Vec<f64> = (1..=24).map(|x| x as f64).collect();
let tensor = unsafe { _read_tensor_nd_column_major(data.as_ptr(), &[2, 3, 4]) };
let shape_dims = tensor.shape().with_dims(|dims| dims.to_vec());
assert_eq!(shape_dims, &[2, 3, 4]);
assert_eq!(tensor[[0, 0, 0]], 1.0);
assert_eq!(tensor[[1, 0, 0]], 2.0);
assert_eq!(tensor[[0, 1, 0]], 3.0);
}
{
let data = vec![
Complex64::new(1.0, 2.0),
Complex64::new(3.0, 4.0),
Complex64::new(5.0, 6.0),
Complex64::new(7.0, 8.0),
];
let tensor = unsafe { _read_tensor_nd_column_major(data.as_ptr(), &[2, 2]) };
let shape_dims = tensor.shape().with_dims(|dims| dims.to_vec());
assert_eq!(shape_dims, &[2, 2]);
assert_eq!(tensor[[0, 0]], Complex64::new(1.0, 2.0));
assert_eq!(tensor[[1, 0]], Complex64::new(3.0, 4.0));
assert_eq!(tensor[[0, 1]], Complex64::new(5.0, 6.0));
assert_eq!(tensor[[1, 1]], Complex64::new(7.0, 8.0));
}
}
#[test]
fn test_read_tensor_nd_roundtrip() {
let row_major_data = vec![
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0,
];
let row_tensor = unsafe { _read_tensor_nd_row_major(row_major_data.as_ptr(), &[3, 4]) };
let col_major_data = vec![
1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0,
];
let col_tensor = unsafe { _read_tensor_nd_column_major(col_major_data.as_ptr(), &[3, 4]) };
let row_shape = row_tensor.shape().with_dims(|dims| dims.to_vec());
let col_shape = col_tensor.shape().with_dims(|dims| dims.to_vec());
assert_eq!(row_shape, col_shape);
assert_eq!(row_tensor[[0, 0]], 1.0);
assert_eq!(col_tensor[[0, 0]], 1.0);
assert_eq!(row_tensor[[0, 1]], 2.0);
assert_eq!(col_tensor[[0, 1]], 4.0); }
#[test]
fn test_gauss_legendre_rule_piecewise_ddouble() {
{
let n = 5;
let segments = [-1.0, 1.0];
let n_segments = 1;
let mut x_high = vec![0.0; n as usize];
let mut x_low = vec![0.0; n as usize];
let mut w_high = vec![0.0; n as usize];
let mut w_low = vec![0.0; n as usize];
let mut status = SPIR_INTERNAL_ERROR;
let result = spir_gauss_legendre_rule_piecewise_ddouble(
n,
segments.as_ptr(),
n_segments,
x_high.as_mut_ptr(),
x_low.as_mut_ptr(),
w_high.as_mut_ptr(),
w_low.as_mut_ptr(),
&mut status,
);
assert_eq!(result, SPIR_COMPUTATION_SUCCESS);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
let x0 = x_high[0] + x_low[0];
let x_last = x_high[(n - 1) as usize] + x_low[(n - 1) as usize];
assert!(x0 >= -1.0);
assert!(x_last <= 1.0);
for i in 1..(n as usize) {
let x_val = x_high[i] + x_low[i];
let x_prev = x_high[i - 1] + x_low[i - 1];
assert!(x_val > x_prev);
}
let mut weight_sum = 0.0;
for i in 0..(n as usize) {
let w_val = w_high[i] + w_low[i];
assert!(w_val > 0.0);
weight_sum += w_val;
}
assert!((weight_sum - 2.0).abs() < 1e-10);
}
{
let n = 3;
let segments = [-1.0, 0.0, 1.0];
let n_segments = 2;
let mut x_high = vec![0.0; (n * n_segments) as usize];
let mut x_low = vec![0.0; (n * n_segments) as usize];
let mut w_high = vec![0.0; (n * n_segments) as usize];
let mut w_low = vec![0.0; (n * n_segments) as usize];
let mut status = SPIR_INTERNAL_ERROR;
let result = spir_gauss_legendre_rule_piecewise_ddouble(
n,
segments.as_ptr(),
n_segments,
x_high.as_mut_ptr(),
x_low.as_mut_ptr(),
w_high.as_mut_ptr(),
w_low.as_mut_ptr(),
&mut status,
);
assert_eq!(result, SPIR_COMPUTATION_SUCCESS);
assert_eq!(status, SPIR_COMPUTATION_SUCCESS);
for i in 1..6 {
let x_val = x_high[i] + x_low[i];
let x_prev = x_high[i - 1] + x_low[i - 1];
assert!(x_val > x_prev);
}
let mut weight_sum = 0.0;
for i in 0..6 {
let w_val = w_high[i] + w_low[i];
weight_sum += w_val;
}
assert!((weight_sum - 2.0).abs() < 1e-10);
}
{
let mut status = SPIR_INTERNAL_ERROR;
let result = spir_gauss_legendre_rule_piecewise_ddouble(
5,
std::ptr::null(),
1,
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut status,
);
assert_ne!(result, SPIR_COMPUTATION_SUCCESS);
}
}
}