use crate::error::{Result, SimulatorError};
#[cfg(feature = "advanced_math")]
use scirs2_core::ndarray::ndarray_linalg::Norm;
use scirs2_core::ndarray::{
s, Array1, Array2, ArrayView1, ArrayView2, ArrayViewMut1, ArrayViewMut2,
};
use scirs2_core::parallel_ops::{
current_num_threads, IndexedParallelIterator, ParallelIterator, ThreadPool, ThreadPoolBuilder,
};
use scirs2_core::random::prelude::*;
use scirs2_core::Complex64;
use std::collections::HashMap;
use std::f64::consts::PI;
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone, Copy)]
pub enum OptimizationLevel {
Basic,
Aggressive,
Maximum,
}
#[cfg(feature = "advanced_math")]
#[derive(Debug, Clone)]
pub struct Matrix {
pub data: Array2<Complex64>,
}
#[cfg(feature = "advanced_math")]
impl Matrix {
pub fn from_array2(array: &ArrayView2<Complex64>, _pool: &MemoryPool) -> Result<Self> {
Ok(Self {
data: array.to_owned(),
})
}
pub fn zeros(shape: (usize, usize), _pool: &MemoryPool) -> Result<Self> {
Ok(Self {
data: Array2::zeros(shape),
})
}
pub fn to_array2(&self, result: &mut Array2<Complex64>) -> Result<()> {
result.assign(&self.data);
Ok(())
}
}
#[cfg(not(feature = "advanced_math"))]
#[derive(Debug)]
pub struct Matrix;
#[cfg(not(feature = "advanced_math"))]
impl Matrix {
pub fn from_array2(_array: &ArrayView2<Complex64>, _pool: &MemoryPool) -> Result<Self> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
pub fn zeros(_shape: (usize, usize), _pool: &MemoryPool) -> Result<Self> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
pub fn to_array2(&self, _result: &mut Array2<Complex64>) -> Result<()> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
}
#[cfg(feature = "advanced_math")]
#[derive(Clone)]
pub struct SparseMatrix {
pub indptr: Vec<usize>,
pub indices: Vec<usize>,
pub data: Vec<Complex64>,
rows: usize,
cols: usize,
}
#[cfg(feature = "advanced_math")]
impl std::fmt::Debug for SparseMatrix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SparseMatrix")
.field("rows", &self.rows)
.field("cols", &self.cols)
.field("nnz", &self.data.len())
.finish()
}
}
#[cfg(feature = "advanced_math")]
impl SparseMatrix {
pub fn from_triplets(
values: Vec<Complex64>,
row_indices: Vec<usize>,
col_indices: Vec<usize>,
shape: (usize, usize),
) -> Result<Self> {
let (num_rows, num_cols) = shape;
let mut indptr = vec![0usize; num_rows + 1];
for &r in &row_indices {
if r >= num_rows {
return Err(SimulatorError::ComputationError(format!(
"Row index {r} out of bounds for matrix with {num_rows} rows"
)));
}
indptr[r + 1] += 1;
}
for i in 1..=num_rows {
indptr[i] += indptr[i - 1];
}
let nnz = values.len();
let mut indices = vec![0usize; nnz];
let mut data = vec![Complex64::new(0.0, 0.0); nnz];
let mut offset = indptr.clone();
for (idx, (&r, &c)) in row_indices.iter().zip(col_indices.iter()).enumerate() {
let pos = offset[r];
indices[pos] = c;
data[pos] = values[idx];
offset[r] += 1;
}
Ok(Self {
indptr,
indices,
data,
rows: num_rows,
cols: num_cols,
})
}
pub fn from_csr(
values: &[Complex64],
col_indices: &[usize],
row_ptr: &[usize],
num_rows: usize,
num_cols: usize,
_pool: &MemoryPool,
) -> Result<Self> {
Ok(Self {
indptr: row_ptr.to_vec(),
indices: col_indices.to_vec(),
data: values.to_vec(),
rows: num_rows,
cols: num_cols,
})
}
#[must_use]
pub fn rows(&self) -> usize {
self.rows
}
#[must_use]
pub fn cols(&self) -> usize {
self.cols
}
pub fn matvec(&self, vector: &Vector, result: &mut Vector) -> Result<()> {
let x = vector.to_array1()?;
if x.len() != self.cols {
return Err(SimulatorError::DimensionMismatch(format!(
"Vector length {} does not match matrix columns {}",
x.len(),
self.cols
)));
}
let mut y = Array1::zeros(self.rows);
for row in 0..self.rows {
let start = self.indptr[row];
let end = self.indptr[row + 1];
let mut sum = Complex64::new(0.0, 0.0);
for j in start..end {
let col = self.indices[j];
sum += self.data[j] * x[col];
}
y[row] = sum;
}
let pool = MemoryPool::new();
*result = Vector::from_array1(&y.view(), &pool)?;
Ok(())
}
pub fn solve(&self, rhs: &Vector) -> Result<Vector> {
SparseSolvers::conjugate_gradient(self, rhs, None, 1e-10, 1000)
}
}
#[cfg(not(feature = "advanced_math"))]
#[derive(Debug)]
pub struct SparseMatrix;
#[cfg(not(feature = "advanced_math"))]
impl SparseMatrix {
pub fn from_csr(
_values: &[Complex64],
_col_indices: &[usize],
_row_ptr: &[usize],
_num_rows: usize,
_num_cols: usize,
_pool: &MemoryPool,
) -> Result<Self> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
pub fn matvec(&self, _vector: &Vector, _result: &mut Vector) -> Result<()> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
pub fn solve(&self, _rhs: &Vector) -> Result<Vector> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
}
#[cfg(feature = "advanced_math")]
#[derive(Debug, Clone)]
pub struct EigResult {
pub values: Vector,
pub vectors: Matrix,
}
#[cfg(feature = "advanced_math")]
impl EigResult {
pub fn to_array1(&self) -> Result<Array1<Complex64>> {
self.values.to_array1()
}
pub fn eigenvectors(&self) -> Array2<Complex64> {
self.vectors.data.clone()
}
}
#[cfg(not(feature = "advanced_math"))]
#[derive(Debug)]
pub struct EigResult;
#[cfg(not(feature = "advanced_math"))]
impl EigResult {
pub fn to_array1(&self) -> Result<Array1<Complex64>> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
}
#[derive(Debug, Clone)]
pub struct SciRS2SimdConfig {
pub force_instruction_set: Option<String>,
pub override_simd_lanes: Option<usize>,
pub enable_aggressive_simd: bool,
pub numa_aware_allocation: bool,
}
#[derive(Debug, Clone)]
pub struct SciRS2Vector {
data: Array1<Complex64>,
simd_aligned: bool,
}
impl SciRS2Vector {
pub fn zeros(len: usize, _allocator: &SciRS2MemoryAllocator) -> Result<Self> {
Ok(Self {
data: Array1::zeros(len),
simd_aligned: true,
})
}
#[must_use]
pub const fn from_array1(array: Array1<Complex64>) -> Self {
Self {
data: array,
simd_aligned: false,
}
}
#[must_use]
pub fn len(&self) -> usize {
self.data.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
#[must_use]
pub fn data_view(&self) -> ArrayView1<'_, Complex64> {
self.data.view()
}
pub fn data_view_mut(&mut self) -> ArrayViewMut1<'_, Complex64> {
self.data.view_mut()
}
pub fn to_array1(&self) -> Result<Array1<Complex64>> {
Ok(self.data.clone())
}
}
#[cfg(feature = "advanced_math")]
#[derive(Debug, Clone)]
pub struct SvdResult {
pub u: Matrix,
pub s: Vector,
pub vt: Matrix,
}
#[cfg(feature = "advanced_math")]
impl SvdResult {
pub fn to_array2(&self) -> Result<Array2<Complex64>> {
Ok(self.u.data.clone())
}
}
#[cfg(not(feature = "advanced_math"))]
#[derive(Debug)]
pub struct SvdResult;
#[cfg(not(feature = "advanced_math"))]
impl SvdResult {
pub fn to_array2(&self) -> Result<Array2<Complex64>> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
}
#[cfg(feature = "advanced_math")]
#[derive(Debug)]
pub struct AdvancedEigensolvers;
#[cfg(feature = "advanced_math")]
impl AdvancedEigensolvers {
pub fn lanczos(
matrix: &SparseMatrix,
num_eigenvalues: usize,
max_iterations: usize,
tolerance: f64,
) -> Result<EigResult> {
let n = matrix.rows();
let m = num_eigenvalues.min(max_iterations);
let mut q = Array1::from_vec(
(0..n)
.map(|_| {
Complex64::new(
thread_rng().random::<f64>() - 0.5,
thread_rng().random::<f64>() - 0.5,
)
})
.collect(),
);
let q_norm = q.norm_l2()?;
q = q.mapv(|x| x / Complex64::new(q_norm, 0.0));
let mut q_vectors = Vec::new();
q_vectors.push(q.clone());
let mut alpha = Vec::new();
let mut beta = Vec::new();
let mut q_prev = Array1::<Complex64>::zeros(n);
for j in 0..m {
let q_vec = Vector::from_array1(&q_vectors[j].view(), &MemoryPool::new())?;
let mut av_vec = Vector::from_array1(&Array1::zeros(n).view(), &MemoryPool::new())?;
matrix.matvec(&q_vec, &mut av_vec)?;
let mut av = av_vec.to_array1()?;
let alpha_j = q_vectors[j].dot(&av);
alpha.push(alpha_j);
av = av - alpha_j * &q_vectors[j];
if j > 0 {
av = av - Complex64::new(beta[j - 1], 0.0) * &q_prev;
}
let beta_j = av.norm_l2()?;
if beta_j.abs() < tolerance {
break;
}
beta.push(beta_j);
q_prev = q_vectors[j].clone();
if j + 1 < m {
q = av / beta_j;
q_vectors.push(q.clone());
}
}
let dim = alpha.len();
let mut tridiag = Array2::zeros((dim, dim));
for i in 0..dim {
tridiag[[i, i]] = alpha[i];
if i > 0 {
tridiag[[i - 1, i]] = Complex64::new(beta[i - 1], 0.0);
tridiag[[i, i - 1]] = Complex64::new(beta[i - 1], 0.0);
}
}
let mut eigenvalues = Array1::zeros(num_eigenvalues.min(dim));
for i in 0..eigenvalues.len() {
eigenvalues[i] = tridiag[[i, i]];
}
let mut eigenvectors = Array2::zeros((n, eigenvalues.len()));
for (i, mut col) in eigenvectors
.columns_mut()
.into_iter()
.enumerate()
.take(eigenvalues.len())
{
if i < q_vectors.len() {
col.assign(&q_vectors[i]);
}
}
let values = Vector::from_array1(&eigenvalues.view(), &MemoryPool::new())?;
let vectors = Matrix::from_array2(&eigenvectors.view(), &MemoryPool::new())?;
Ok(EigResult { values, vectors })
}
pub fn arnoldi(
matrix: &SparseMatrix,
num_eigenvalues: usize,
max_iterations: usize,
tolerance: f64,
) -> Result<EigResult> {
let n = matrix.rows();
let m = num_eigenvalues.min(max_iterations);
let mut q = Array1::from_vec(
(0..n)
.map(|_| {
Complex64::new(
thread_rng().random::<f64>() - 0.5,
thread_rng().random::<f64>() - 0.5,
)
})
.collect(),
);
let q_norm = q.norm_l2()?;
q = q.mapv(|x| x / Complex64::new(q_norm, 0.0));
let mut q_vectors = Vec::new();
q_vectors.push(q.clone());
let mut h = Array2::zeros((m + 1, m));
for j in 0..m {
let q_vec = Vector::from_array1(&q_vectors[j].view(), &MemoryPool::new())?;
let mut v_vec = Vector::from_array1(&Array1::zeros(n).view(), &MemoryPool::new())?;
matrix.matvec(&q_vec, &mut v_vec)?;
let mut v = v_vec.to_array1()?;
for i in 0..=j {
h[[i, j]] = q_vectors[i].dot(&v);
v = v - h[[i, j]] * &q_vectors[i];
}
h[[j + 1, j]] = Complex64::new(v.norm_l2()?, 0.0);
if h[[j + 1, j]].norm() < tolerance {
break;
}
if j + 1 < m {
q = v / h[[j + 1, j]];
q_vectors.push(q.clone());
}
}
let dim = q_vectors.len();
let mut eigenvalues = Array1::zeros(num_eigenvalues.min(dim));
for i in 0..eigenvalues.len() {
eigenvalues[i] = h[[i, i]];
}
let mut eigenvectors = Array2::zeros((n, eigenvalues.len()));
for (i, mut col) in eigenvectors
.columns_mut()
.into_iter()
.enumerate()
.take(eigenvalues.len())
{
if i < q_vectors.len() {
col.assign(&q_vectors[i]);
}
}
let values = Vector::from_array1(&eigenvalues.view(), &MemoryPool::new())?;
let vectors = Matrix::from_array2(&eigenvectors.view(), &MemoryPool::new())?;
Ok(EigResult { values, vectors })
}
}
#[cfg(feature = "advanced_math")]
#[derive(Debug)]
pub struct SparseSolvers;
#[cfg(feature = "advanced_math")]
impl SparseSolvers {
pub fn conjugate_gradient(
matrix: &SparseMatrix,
b: &Vector,
x0: Option<&Vector>,
tolerance: f64,
max_iterations: usize,
) -> Result<Vector> {
use nalgebra::{Complex, DVector};
let b_array = b.to_array1()?;
let b_vec = DVector::from_iterator(
b_array.len(),
b_array.iter().map(|&c| Complex::new(c.re, c.im)),
);
let mut x = if let Some(x0_vec) = x0 {
let x0_array = x0_vec.to_array1()?;
DVector::from_iterator(
x0_array.len(),
x0_array.iter().map(|&c| Complex::new(c.re, c.im)),
)
} else {
DVector::zeros(b_vec.len())
};
let pool = MemoryPool::new();
let x_vector = Vector::from_array1(
&Array1::from_vec(x.iter().map(|c| Complex64::new(c.re, c.im)).collect()).view(),
&pool,
)?;
let mut ax_vector = Vector::zeros(x.len(), &pool)?;
matrix.matvec(&x_vector, &mut ax_vector)?;
let ax_array = ax_vector.to_array1()?;
let ax = DVector::from_iterator(
ax_array.len(),
ax_array.iter().map(|&c| Complex::new(c.re, c.im)),
);
let mut r = &b_vec - &ax;
let mut p = r.clone();
let mut rsold = r.dot(&r).re;
for _ in 0..max_iterations {
let p_vec = Vector::from_array1(
&Array1::from_vec(p.iter().map(|c| Complex64::new(c.re, c.im)).collect()).view(),
&MemoryPool::new(),
)?;
let mut ap_vec =
Vector::from_array1(&Array1::zeros(p.len()).view(), &MemoryPool::new())?;
matrix.matvec(&p_vec, &mut ap_vec)?;
let ap_array = ap_vec.to_array1()?;
let ap = DVector::from_iterator(
ap_array.len(),
ap_array.iter().map(|&c| Complex::new(c.re, c.im)),
);
let alpha = rsold / p.dot(&ap).re;
let alpha_complex = Complex::new(alpha, 0.0);
x += &p * alpha_complex;
r -= &ap * alpha_complex;
let rsnew = r.dot(&r).re;
if rsnew.sqrt() < tolerance {
break;
}
let beta = rsnew / rsold;
let beta_complex = Complex::new(beta, 0.0);
p = &r + &p * beta_complex;
rsold = rsnew;
}
let result_array = Array1::from_vec(x.iter().map(|c| Complex64::new(c.re, c.im)).collect());
Vector::from_array1(&result_array.view(), &MemoryPool::new())
}
pub fn gmres(
matrix: &SparseMatrix,
b: &Vector,
x0: Option<&Vector>,
tolerance: f64,
max_iterations: usize,
restart: usize,
) -> Result<Vector> {
let b_array = b.to_array1()?;
let n = b_array.len();
let mut x = if let Some(x0_vec) = x0 {
x0_vec.to_array1()?.to_owned()
} else {
Array1::zeros(n)
};
for _restart_iter in 0..(max_iterations / restart) {
let mut ax = Array1::zeros(n);
let x_vec = Vector::from_array1(&x.view(), &MemoryPool::new())?;
let mut ax_vec = Vector::from_array1(&ax.view(), &MemoryPool::new())?;
matrix.matvec(&x_vec, &mut ax_vec)?;
ax = ax_vec.to_array1()?;
let mut r = &b_array - &ax;
let beta = r.norm_l2()?;
if beta < tolerance {
break;
}
r = r.mapv(|x| x / Complex64::new(beta, 0.0));
let mut v = Vec::new();
v.push(r.clone());
let mut h = Array2::zeros((restart + 1, restart));
for j in 0..restart.min(max_iterations) {
let v_vec = Vector::from_array1(&v[j].view(), &MemoryPool::new())?;
let mut av = Array1::zeros(n);
let mut av_vec = Vector::from_array1(&av.view(), &MemoryPool::new())?;
matrix.matvec(&v_vec, &mut av_vec)?;
av = av_vec.to_array1()?;
for i in 0..=j {
h[[i, j]] = v[i].dot(&av);
av = av - h[[i, j]] * &v[i];
}
h[[j + 1, j]] = Complex64::new(av.norm_l2()?, 0.0);
if h[[j + 1, j]].norm() < tolerance {
break;
}
av /= h[[j + 1, j]];
v.push(av);
}
let krylov_dim = v.len() - 1;
if krylov_dim > 0 {
let mut e1 = Array1::zeros(krylov_dim + 1);
e1[0] = Complex64::new(beta, 0.0);
let mut y = Array1::zeros(krylov_dim);
for i in (0..krylov_dim).rev() {
let mut sum = Complex64::new(0.0, 0.0);
for j in (i + 1)..krylov_dim {
sum += h[[i, j]] * y[j];
}
y[i] = (e1[i] - sum) / h[[i, i]];
}
for i in 0..krylov_dim {
x = x + y[i] * &v[i];
}
}
}
Vector::from_array1(&x.view(), &MemoryPool::new())
}
pub fn bicgstab(
matrix: &SparseMatrix,
b: &Vector,
x0: Option<&Vector>,
tolerance: f64,
max_iterations: usize,
) -> Result<Vector> {
let b_array = b.to_array1()?;
let n = b_array.len();
let mut x = if let Some(x0_vec) = x0 {
x0_vec.to_array1()?.to_owned()
} else {
Array1::zeros(n)
};
let mut ax = Array1::zeros(n);
let x_vec = Vector::from_array1(&x.view(), &MemoryPool::new())?;
let mut ax_vec = Vector::from_array1(&ax.view(), &MemoryPool::new())?;
matrix.matvec(&x_vec, &mut ax_vec)?;
ax = ax_vec.to_array1()?;
let mut r = &b_array - &ax;
let r0 = r.clone();
let mut rho = Complex64::new(1.0, 0.0);
let mut alpha = Complex64::new(1.0, 0.0);
let mut omega = Complex64::new(1.0, 0.0);
let mut p = Array1::zeros(n);
let mut v = Array1::zeros(n);
for _ in 0..max_iterations {
let rho_new = r0.dot(&r);
let beta = (rho_new / rho) * (alpha / omega);
p = &r + beta * (&p - omega * &v);
let p_vec = Vector::from_array1(&p.view(), &MemoryPool::new())?;
let mut v_vec = Vector::from_array1(&v.view(), &MemoryPool::new())?;
matrix.matvec(&p_vec, &mut v_vec)?;
v = v_vec.to_array1()?;
alpha = rho_new / r0.dot(&v);
let s = &r - alpha * &v;
if s.norm_l2()? < tolerance {
x = x + alpha * &p;
break;
}
let s_vec = Vector::from_array1(&s.view(), &MemoryPool::new())?;
let mut t_vec = Vector::from_array1(&Array1::zeros(n).view(), &MemoryPool::new())?;
matrix.matvec(&s_vec, &mut t_vec)?;
let t = t_vec.to_array1()?;
omega = t.dot(&s) / t.dot(&t);
x = x + alpha * &p + omega * &s;
r = s - omega * &t;
if r.norm_l2()? < tolerance {
break;
}
rho = rho_new;
}
Vector::from_array1(&x.view(), &MemoryPool::new())
}
}
#[derive(Debug, Default, Clone)]
pub struct BackendStats {
pub simd_vector_ops: usize,
pub simd_matrix_ops: usize,
pub complex_simd_ops: usize,
pub simd_time_ns: u64,
pub parallel_time_ns: u64,
pub memory_usage_bytes: usize,
pub peak_simd_throughput: f64,
pub simd_efficiency: f64,
pub vectorized_fft_ops: usize,
pub sparse_simd_ops: usize,
pub matrix_ops: usize,
pub lapack_time_ms: f64,
pub cache_hit_rate: f64,
}
#[cfg(feature = "advanced_math")]
#[derive(Debug)]
pub struct MemoryPool;
#[cfg(feature = "advanced_math")]
impl MemoryPool {
#[must_use]
pub const fn new() -> Self {
Self
}
}
#[cfg(not(feature = "advanced_math"))]
#[derive(Debug)]
pub struct MemoryPool;
#[cfg(not(feature = "advanced_math"))]
impl MemoryPool {
#[must_use]
pub const fn new() -> Self {
Self
}
}
#[cfg(feature = "advanced_math")]
#[derive(Debug, Clone)]
pub struct Vector {
pub data: Array1<Complex64>,
}
#[cfg(feature = "advanced_math")]
impl Vector {
pub fn from_array1(array: &ArrayView1<Complex64>, _pool: &MemoryPool) -> Result<Self> {
Ok(Self {
data: array.to_owned(),
})
}
pub fn zeros(len: usize, _pool: &MemoryPool) -> Result<Self> {
Ok(Self {
data: Array1::zeros(len),
})
}
pub fn to_array1(&self) -> Result<Array1<Complex64>> {
Ok(self.data.clone())
}
pub fn to_array1_mut(&self, result: &mut Array1<Complex64>) -> Result<()> {
result.assign(&self.data);
Ok(())
}
}
#[cfg(not(feature = "advanced_math"))]
#[derive(Debug)]
pub struct Vector;
#[cfg(not(feature = "advanced_math"))]
impl Vector {
pub fn from_array1(_array: &ArrayView1<Complex64>, _pool: &MemoryPool) -> Result<Self> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
pub fn zeros(_len: usize, _pool: &MemoryPool) -> Result<Self> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
pub fn to_array1(&self) -> Result<Array1<Complex64>> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
pub fn to_array1_mut(&self, _result: &mut Array1<Complex64>) -> Result<()> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
}
#[derive(Debug, Clone)]
pub struct FFTPlan {
pub size: usize,
pub twiddle_factors: Vec<Complex64>,
pub vectorization_strategy: VectorizationStrategy,
}
#[cfg(feature = "advanced_math")]
#[derive(Debug)]
pub struct BLAS;
#[cfg(feature = "advanced_math")]
impl BLAS {
pub fn gemm(
alpha: Complex64,
a: &Matrix,
b: &Matrix,
beta: Complex64,
c: &mut Matrix,
) -> Result<()> {
let result = a.data.dot(&b.data);
c.data = c.data.mapv(|x| x * beta) + result.mapv(|x| x * alpha);
Ok(())
}
pub fn gemv(
alpha: Complex64,
a: &Matrix,
x: &Vector,
beta: Complex64,
y: &mut Vector,
) -> Result<()> {
let result = a.data.dot(&x.data);
y.data = y.data.mapv(|v| v * beta) + result.mapv(|v| v * alpha);
Ok(())
}
}
#[cfg(not(feature = "advanced_math"))]
#[derive(Debug)]
pub struct BLAS;
#[cfg(not(feature = "advanced_math"))]
impl BLAS {
pub fn gemm(
_alpha: Complex64,
_a: &Matrix,
_b: &Matrix,
_beta: Complex64,
_c: &mut Matrix,
) -> Result<()> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
pub fn gemv(
_alpha: Complex64,
_a: &Matrix,
_x: &Vector,
_beta: Complex64,
_y: &mut Vector,
) -> Result<()> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
}
#[cfg(feature = "advanced_math")]
#[derive(Debug)]
pub struct AdvancedLinearAlgebra;
#[cfg(feature = "advanced_math")]
impl AdvancedLinearAlgebra {
pub fn qr_decomposition(matrix: &Matrix) -> Result<QRResult> {
use scirs2_core::ndarray::ndarray_linalg::QR;
let qr_result = matrix
.data
.qr()
.map_err(|_| SimulatorError::ComputationError("QR decomposition failed".to_string()))?;
let pool = MemoryPool::new();
let q = Matrix::from_array2(&qr_result.0.view(), &pool)?;
let r = Matrix::from_array2(&qr_result.1.view(), &pool)?;
Ok(QRResult { q, r })
}
pub fn cholesky_decomposition(matrix: &Matrix) -> Result<Matrix> {
use scirs2_core::ndarray::ndarray_linalg::{Cholesky, UPLO};
let chol_result = matrix.data.cholesky(UPLO::Lower).map_err(|_| {
SimulatorError::ComputationError("Cholesky decomposition failed".to_string())
})?;
Matrix::from_array2(&chol_result.view(), &MemoryPool::new())
}
pub fn matrix_exponential(matrix: &Matrix, t: f64) -> Result<Matrix> {
let scaled_matrix = &matrix.data * Complex64::new(0.0, -t);
let mut result = Array2::eye(scaled_matrix.nrows());
let mut term = Array2::eye(scaled_matrix.nrows());
for k in 1..20 {
term = term.dot(&scaled_matrix) / Complex64::new(k as f64, 0.0);
result += &term;
if term.norm_l2().unwrap_or_default() < 1e-12 {
break;
}
}
Matrix::from_array2(&result.view(), &MemoryPool::new())
}
pub fn pseudoinverse(matrix: &Matrix, tolerance: f64) -> Result<Matrix> {
let svd_result = LAPACK::svd(matrix)?;
let u = svd_result.u.data;
let s = svd_result.s.to_array1()?;
let vt = svd_result.vt.data;
let mut s_pinv = Array1::zeros(s.len());
for (i, &sigma) in s.iter().enumerate() {
if sigma.norm() > tolerance {
s_pinv[i] = Complex64::new(1.0, 0.0) / sigma;
}
}
let s_pinv_diag = Array2::from_diag(&s_pinv);
let result = vt.t().dot(&s_pinv_diag).dot(&u.t());
Matrix::from_array2(&result.view(), &MemoryPool::new())
}
pub fn condition_number(matrix: &Matrix) -> Result<f64> {
let svd_result = LAPACK::svd(matrix)?;
let s = svd_result.s.to_array1()?;
let mut min_singular = f64::INFINITY;
let mut max_singular: f64 = 0.0;
for &sigma in &s {
let sigma_norm = sigma.norm();
if sigma_norm > 1e-15 {
min_singular = min_singular.min(sigma_norm);
max_singular = max_singular.max(sigma_norm);
}
}
Ok(max_singular / min_singular)
}
}
#[derive(Debug)]
pub struct SciRS2Backend {
pub available: bool,
pub stats: Arc<Mutex<BackendStats>>,
pub simd_context: SciRS2SimdContext,
pub memory_allocator: SciRS2MemoryAllocator,
pub fft_engine: SciRS2VectorizedFFT,
pub parallel_context: SciRS2ParallelContext,
}
impl SciRS2Backend {
#[must_use]
pub fn new() -> Self {
let simd_context = SciRS2SimdContext::detect_capabilities();
let memory_allocator = SciRS2MemoryAllocator::default();
let fft_engine = SciRS2VectorizedFFT::default();
let parallel_context = SciRS2ParallelContext::default();
Self {
available: simd_context.supports_complex_simd,
stats: Arc::new(Mutex::new(BackendStats::default())),
simd_context,
memory_allocator,
fft_engine,
parallel_context,
}
}
pub fn with_config(simd_config: SciRS2SimdConfig) -> Result<Self> {
let mut backend = Self::new();
backend.simd_context = SciRS2SimdContext::from_config(&simd_config)?;
Ok(backend)
}
#[must_use]
pub const fn is_available(&self) -> bool {
self.available && self.simd_context.supports_complex_simd
}
#[must_use]
pub const fn get_simd_info(&self) -> &SciRS2SimdContext {
&self.simd_context
}
#[must_use]
pub fn get_stats(&self) -> BackendStats {
self.stats
.lock()
.map(|guard| guard.clone())
.unwrap_or_default()
}
pub fn reset_stats(&self) {
if let Ok(mut guard) = self.stats.lock() {
*guard = BackendStats::default();
}
}
pub fn matrix_multiply(&self, a: &SciRS2Matrix, b: &SciRS2Matrix) -> Result<SciRS2Matrix> {
let start_time = std::time::Instant::now();
if a.cols() != b.rows() {
return Err(SimulatorError::DimensionMismatch(format!(
"Cannot multiply {}x{} matrix with {}x{} matrix",
a.rows(),
a.cols(),
b.rows(),
b.cols()
)));
}
let result_shape = (a.rows(), b.cols());
let mut result = SciRS2Matrix::zeros(result_shape, &self.memory_allocator)?;
self.simd_gemm_complex(&a.data_view(), &b.data_view(), &mut result.data_view_mut())?;
if let Ok(mut stats) = self.stats.lock() {
stats.simd_matrix_ops += 1;
stats.simd_time_ns += start_time.elapsed().as_nanos() as u64;
}
Ok(result)
}
pub fn matrix_vector_multiply(
&self,
a: &SciRS2Matrix,
x: &SciRS2Vector,
) -> Result<SciRS2Vector> {
let start_time = std::time::Instant::now();
if a.cols() != x.len() {
return Err(SimulatorError::DimensionMismatch(format!(
"Cannot multiply {}x{} matrix with vector of length {}",
a.rows(),
a.cols(),
x.len()
)));
}
let mut result = SciRS2Vector::zeros(a.rows(), &self.memory_allocator)?;
self.simd_gemv_complex(&a.data_view(), &x.data_view(), &mut result.data_view_mut())?;
if let Ok(mut stats) = self.stats.lock() {
stats.simd_vector_ops += 1;
stats.simd_time_ns += start_time.elapsed().as_nanos() as u64;
}
Ok(result)
}
pub(super) fn simd_gemm_complex(
&self,
a: &ArrayView2<Complex64>,
b: &ArrayView2<Complex64>,
c: &mut ArrayViewMut2<Complex64>,
) -> Result<()> {
let (m, k) = a.dim();
let (k2, n) = b.dim();
assert_eq!(k, k2, "Inner dimensions must match");
assert_eq!(c.dim(), (m, n), "Output dimensions must match");
let a_real: Vec<f64> = a.iter().map(|z| z.re).collect();
let a_imag: Vec<f64> = a.iter().map(|z| z.im).collect();
let b_real: Vec<f64> = b.iter().map(|z| z.re).collect();
let b_imag: Vec<f64> = b.iter().map(|z| z.im).collect();
for i in 0..m {
for j in 0..n {
let mut real_sum = 0.0;
let mut imag_sum = 0.0;
let a_row_start = i * k;
if k >= self.simd_context.simd_lanes {
for l in 0..k {
let b_idx = l * n + j;
let ar = a_real[a_row_start + l];
let ai = a_imag[a_row_start + l];
let br = b_real[b_idx];
let bi = b_imag[b_idx];
real_sum += ar.mul_add(br, -(ai * bi));
imag_sum += ar.mul_add(bi, ai * br);
}
} else {
for l in 0..k {
let b_idx = l * n + j;
let ar = a_real[a_row_start + l];
let ai = a_imag[a_row_start + l];
let br = b_real[b_idx];
let bi = b_imag[b_idx];
real_sum += ar.mul_add(br, -(ai * bi));
imag_sum += ar.mul_add(bi, ai * br);
}
}
c[[i, j]] = Complex64::new(real_sum, imag_sum);
}
}
Ok(())
}
pub(super) fn simd_gemv_complex(
&self,
a: &ArrayView2<Complex64>,
x: &ArrayView1<Complex64>,
y: &mut ArrayViewMut1<Complex64>,
) -> Result<()> {
let (m, n) = a.dim();
assert_eq!(x.len(), n, "Vector length must match matrix columns");
assert_eq!(y.len(), m, "Output vector length must match matrix rows");
let a_real: Vec<f64> = a.iter().map(|z| z.re).collect();
let a_imag: Vec<f64> = a.iter().map(|z| z.im).collect();
let x_real: Vec<f64> = x.iter().map(|z| z.re).collect();
let x_imag: Vec<f64> = x.iter().map(|z| z.im).collect();
for i in 0..m {
let mut real_sum = 0.0;
let mut imag_sum = 0.0;
let row_start = i * n;
if n >= self.simd_context.simd_lanes {
let chunks = n / self.simd_context.simd_lanes;
for chunk in 0..chunks {
let start_idx = chunk * self.simd_context.simd_lanes;
let end_idx = start_idx + self.simd_context.simd_lanes;
for j in start_idx..end_idx {
let a_idx = row_start + j;
let ar = a_real[a_idx];
let ai = a_imag[a_idx];
let xr = x_real[j];
let xi = x_imag[j];
real_sum += ar.mul_add(xr, -(ai * xi));
imag_sum += ar.mul_add(xi, ai * xr);
}
}
for j in (chunks * self.simd_context.simd_lanes)..n {
let a_idx = row_start + j;
let ar = a_real[a_idx];
let ai = a_imag[a_idx];
let xr = x_real[j];
let xi = x_imag[j];
real_sum += ar.mul_add(xr, -(ai * xi));
imag_sum += ar.mul_add(xi, ai * xr);
}
} else {
for j in 0..n {
let a_idx = row_start + j;
let ar = a_real[a_idx];
let ai = a_imag[a_idx];
let xr = x_real[j];
let xi = x_imag[j];
real_sum += ar.mul_add(xr, -(ai * xi));
imag_sum += ar.mul_add(xi, ai * xr);
}
}
y[i] = Complex64::new(real_sum, imag_sum);
}
Ok(())
}
#[cfg(feature = "advanced_math")]
pub fn svd(&mut self, matrix: &Matrix) -> Result<SvdResult> {
let start_time = std::time::Instant::now();
let result = LAPACK::svd(matrix)?;
if let Ok(mut stats) = self.stats.lock() {
stats.simd_matrix_ops += 1;
stats.simd_time_ns += start_time.elapsed().as_nanos() as u64;
}
Ok(result)
}
#[cfg(feature = "advanced_math")]
pub fn eigendecomposition(&mut self, matrix: &Matrix) -> Result<EigResult> {
let start_time = std::time::Instant::now();
let result = LAPACK::eig(matrix)?;
if let Ok(mut stats) = self.stats.lock() {
stats.simd_matrix_ops += 1;
stats.simd_time_ns += start_time.elapsed().as_nanos() as u64;
}
Ok(result)
}
}
#[cfg(feature = "advanced_math")]
#[derive(Debug, Clone)]
pub struct QRResult {
pub q: Matrix,
pub r: Matrix,
}
#[cfg(feature = "advanced_math")]
#[derive(Debug)]
pub struct AdvancedFFT;
#[cfg(feature = "advanced_math")]
impl AdvancedFFT {
pub fn fft_nd(input: &Array2<Complex64>) -> Result<Array2<Complex64>> {
use scirs2_fft::fft::fft;
let (rows, cols) = input.dim();
let mut result = input.clone();
for i in 0..rows {
let row: Vec<Complex64> = input.row(i).iter().copied().collect();
let row_out = fft(&row, Some(cols))
.map_err(|e| SimulatorError::ComputationError(format!("FFT row error: {e}")))?;
let row_arr = Array1::from_vec(row_out);
result.row_mut(i).assign(&row_arr);
}
for j in 0..cols {
let col: Vec<Complex64> = result.column(j).iter().copied().collect();
let col_out = fft(&col, Some(rows))
.map_err(|e| SimulatorError::ComputationError(format!("FFT col error: {e}")))?;
let col_arr = Array1::from_vec(col_out);
result.column_mut(j).assign(&col_arr);
}
Ok(result)
}
pub fn windowed_fft(
input: &Vector,
window_size: usize,
overlap: usize,
) -> Result<Array2<Complex64>> {
let array = input.to_array1()?;
let step_size = window_size - overlap;
let num_windows = (array.len() - overlap) / step_size;
let mut result = Array2::zeros((num_windows, window_size));
for (i, mut row) in result.outer_iter_mut().enumerate() {
let start = i * step_size;
let end = (start + window_size).min(array.len());
if end - start == window_size {
let window = array.slice(s![start..end]);
let windowed: Array1<Complex64> = window
.iter()
.enumerate()
.map(|(j, &val)| {
let hann =
0.5 * (1.0 - (2.0 * PI * j as f64 / (window_size - 1) as f64).cos());
val * Complex64::new(hann, 0.0)
})
.collect();
use scirs2_fft::fft::fft;
let windowed_vec: Vec<Complex64> = windowed.iter().copied().collect();
let fft_result = fft(&windowed_vec, Some(window_size)).map_err(|e| {
SimulatorError::ComputationError(format!("FFT windowed error: {e}"))
})?;
let fft_arr = Array1::from_vec(fft_result);
row.assign(&fft_arr);
}
}
Ok(result)
}
pub fn convolution(a: &Vector, b: &Vector) -> Result<Vector> {
let a_array = a.to_array1()?;
let b_array = b.to_array1()?;
let n = a_array.len() + b_array.len() - 1;
let fft_size = n.next_power_of_two();
let mut a_padded = Array1::zeros(fft_size);
let mut b_padded = Array1::zeros(fft_size);
a_padded.slice_mut(s![..a_array.len()]).assign(&a_array);
b_padded.slice_mut(s![..b_array.len()]).assign(&b_array);
use scirs2_fft::fft::{fft, ifft};
let a_vec: Vec<Complex64> = a_padded.iter().copied().collect();
let b_vec: Vec<Complex64> = b_padded.iter().copied().collect();
let a_fft = fft(&a_vec, Some(fft_size))
.map_err(|e| SimulatorError::ComputationError(format!("FFT convolution error: {e}")))?;
let b_fft = fft(&b_vec, Some(fft_size))
.map_err(|e| SimulatorError::ComputationError(format!("FFT convolution error: {e}")))?;
let product: Vec<Complex64> = a_fft.iter().zip(b_fft.iter()).map(|(a, b)| a * b).collect();
let result_vec = ifft(&product, Some(fft_size)).map_err(|e| {
SimulatorError::ComputationError(format!("IFFT convolution error: {e}"))
})?;
let truncated = Array1::from_vec(result_vec[..n].to_vec());
Vector::from_array1(&truncated.view(), &MemoryPool::new())
}
}
#[derive(Debug)]
pub struct SciRS2VectorizedFFT {
pub(super) plans: HashMap<usize, FFTPlan>,
pub(super) optimization_level: OptimizationLevel,
}
impl SciRS2VectorizedFFT {
pub fn forward(&self, input: &SciRS2Vector) -> Result<SciRS2Vector> {
let data = input.data_view().to_owned();
#[cfg(feature = "advanced_math")]
{
use scirs2_fft::fft::fft;
let input_vec: Vec<Complex64> = data.iter().copied().collect();
let output_vec = fft(&input_vec, None)
.map_err(|e| SimulatorError::ComputationError(format!("FFT forward error: {e}")))?;
Ok(SciRS2Vector::from_array1(Array1::from_vec(output_vec)))
}
#[cfg(not(feature = "advanced_math"))]
{
let n = data.len();
let mut output = Array1::zeros(n);
for k in 0..n {
let mut sum = Complex64::new(0.0, 0.0);
for j in 0..n {
let angle = -2.0 * PI * (k * j) as f64 / n as f64;
let twiddle = Complex64::new(angle.cos(), angle.sin());
sum += data[j] * twiddle;
}
output[k] = sum;
}
Ok(SciRS2Vector::from_array1(output))
}
}
pub fn inverse(&self, input: &SciRS2Vector) -> Result<SciRS2Vector> {
let data = input.data_view().to_owned();
#[cfg(feature = "advanced_math")]
{
use scirs2_fft::fft::ifft;
let input_vec: Vec<Complex64> = data.iter().copied().collect();
let output_vec = ifft(&input_vec, None).map_err(|e| {
SimulatorError::ComputationError(format!("IFFT inverse error: {e}"))
})?;
Ok(SciRS2Vector::from_array1(Array1::from_vec(output_vec)))
}
#[cfg(not(feature = "advanced_math"))]
{
let n = data.len();
let mut output = Array1::zeros(n);
let scale = 1.0 / n as f64;
for k in 0..n {
let mut sum = Complex64::new(0.0, 0.0);
for j in 0..n {
let angle = 2.0 * PI * (k * j) as f64 / n as f64;
let twiddle = Complex64::new(angle.cos(), angle.sin());
sum += data[j] * twiddle;
}
output[k] = sum * scale;
}
Ok(SciRS2Vector::from_array1(output))
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum VectorizationStrategy {
SimdComplexSeparate,
SimdComplexInterleaved,
Adaptive,
}
#[derive(Debug)]
pub struct SciRS2ParallelContext {
pub num_threads: usize,
pub thread_pool: ThreadPool,
pub numa_aware: bool,
}
#[cfg(feature = "advanced_math")]
#[derive(Debug)]
pub struct LAPACK;
#[cfg(feature = "advanced_math")]
impl LAPACK {
pub fn svd(matrix: &Matrix) -> Result<SvdResult> {
use scirs2_core::ndarray::ndarray_linalg::SVD;
let (u, s_raw, vt) = matrix
.data
.svd(true, true)
.map_err(|e| SimulatorError::ComputationError(format!("SVD failed: {e}")))?;
let s_complex = s_raw.mapv(|v| Complex64::new(v, 0.0));
let pool = MemoryPool::new();
Ok(SvdResult {
u: Matrix::from_array2(&u.view(), &pool)?,
s: Vector::from_array1(&s_complex.view(), &pool)?,
vt: Matrix::from_array2(&vt.view(), &pool)?,
})
}
pub fn eig(matrix: &Matrix) -> Result<EigResult> {
use scirs2_core::ndarray::ndarray_linalg::Eig;
let (eigenvalues, eigenvectors) = matrix.data.eig().map_err(|e| {
SimulatorError::ComputationError(format!("Eigendecomposition failed: {e}"))
})?;
let pool = MemoryPool::new();
Ok(EigResult {
values: Vector::from_array1(&eigenvalues.view(), &pool)?,
vectors: Matrix::from_array2(&eigenvectors.view(), &pool)?,
})
}
}
#[cfg(not(feature = "advanced_math"))]
#[derive(Debug)]
pub struct LAPACK;
#[cfg(not(feature = "advanced_math"))]
impl LAPACK {
pub fn svd(_matrix: &Matrix) -> Result<SvdResult> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
pub fn eig(_matrix: &Matrix) -> Result<EigResult> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
}
#[derive(Debug, Clone)]
pub struct SciRS2Matrix {
data: Array2<Complex64>,
simd_aligned: bool,
}
impl SciRS2Matrix {
pub fn zeros(shape: (usize, usize), _allocator: &SciRS2MemoryAllocator) -> Result<Self> {
Ok(Self {
data: Array2::zeros(shape),
simd_aligned: true,
})
}
#[must_use]
pub const fn from_array2(array: Array2<Complex64>) -> Self {
Self {
data: array,
simd_aligned: false,
}
}
#[must_use]
pub fn shape(&self) -> (usize, usize) {
self.data.dim()
}
#[must_use]
pub fn rows(&self) -> usize {
self.data.nrows()
}
#[must_use]
pub fn cols(&self) -> usize {
self.data.ncols()
}
#[must_use]
pub fn data_view(&self) -> ArrayView2<'_, Complex64> {
self.data.view()
}
pub fn data_view_mut(&mut self) -> ArrayViewMut2<'_, Complex64> {
self.data.view_mut()
}
}
#[cfg(feature = "advanced_math")]
#[derive(Debug)]
pub struct FftEngine;
#[cfg(feature = "advanced_math")]
impl FftEngine {
#[must_use]
pub const fn new() -> Self {
Self
}
pub fn forward(&self, input: &Vector) -> Result<Vector> {
use scirs2_fft::fft::fft;
let data_vec: Vec<Complex64> = input.data.iter().copied().collect();
let result = fft(&data_vec, None)
.map_err(|e| SimulatorError::ComputationError(format!("FFT forward error: {e}")))?;
Ok(Vector {
data: Array1::from_vec(result),
})
}
pub fn inverse(&self, input: &Vector) -> Result<Vector> {
use scirs2_fft::fft::ifft;
let data_vec: Vec<Complex64> = input.data.iter().copied().collect();
let result = ifft(&data_vec, None)
.map_err(|e| SimulatorError::ComputationError(format!("IFFT error: {e}")))?;
Ok(Vector {
data: Array1::from_vec(result),
})
}
}
#[cfg(not(feature = "advanced_math"))]
#[derive(Debug)]
pub struct FftEngine;
#[cfg(not(feature = "advanced_math"))]
impl FftEngine {
#[must_use]
pub const fn new() -> Self {
Self
}
pub fn forward(&self, _input: &Vector) -> Result<Vector> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
pub fn inverse(&self, _input: &Vector) -> Result<Vector> {
Err(SimulatorError::UnsupportedOperation(
"SciRS2 integration requires 'advanced_math' feature".to_string(),
))
}
}
#[derive(Debug, Clone)]
pub struct SciRS2SimdContext {
pub simd_lanes: usize,
pub supports_complex_simd: bool,
pub instruction_set: String,
pub max_vector_width: usize,
}
impl SciRS2SimdContext {
#[must_use]
pub fn detect_capabilities() -> Self {
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx512f") {
Self {
simd_lanes: 16,
supports_complex_simd: true,
instruction_set: "AVX-512".to_string(),
max_vector_width: 64,
}
} else if is_x86_feature_detected!("avx2") {
Self {
simd_lanes: 8,
supports_complex_simd: true,
instruction_set: "AVX2".to_string(),
max_vector_width: 32,
}
} else if is_x86_feature_detected!("sse4.1") {
Self {
simd_lanes: 4,
supports_complex_simd: true,
instruction_set: "SSE4.1".to_string(),
max_vector_width: 16,
}
} else {
Self::fallback()
}
}
#[cfg(target_arch = "aarch64")]
{
Self {
simd_lanes: 4,
supports_complex_simd: true,
instruction_set: "NEON".to_string(),
max_vector_width: 16,
}
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
{
Self::fallback()
}
}
pub fn from_config(config: &SciRS2SimdConfig) -> Result<Self> {
let mut context = Self::detect_capabilities();
if let Some(ref instruction_set) = config.force_instruction_set {
context.instruction_set = instruction_set.clone();
}
if let Some(simd_lanes) = config.override_simd_lanes {
context.simd_lanes = simd_lanes;
}
Ok(context)
}
pub(super) fn fallback() -> Self {
Self {
simd_lanes: 1,
supports_complex_simd: false,
instruction_set: "Scalar".to_string(),
max_vector_width: 8,
}
}
}
#[derive(Debug)]
pub struct SciRS2MemoryAllocator {
pub total_allocated: usize,
pub alignment: usize,
pub(super) allocation_count: usize,
}
impl SciRS2MemoryAllocator {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}