use crate::error::{SparseError, SparseResult};
use crate::linalg::interface::{AdjointOperator, LinearOperator};
use crate::parallel_vector_ops::*;
use scirs2_core::numeric::{Float, NumAssign};
use scirs2_core::SparseElement;
use std::fmt::Debug;
use std::iter::Sum;
use scirs2_core::simd_ops::SimdUnifiedOps;
#[derive(Debug, Clone)]
pub struct EnhancedOperatorOptions {
pub use_parallel: bool,
pub parallel_threshold: usize,
pub use_simd: bool,
pub simd_threshold: usize,
pub chunk_size: usize,
}
impl Default for EnhancedOperatorOptions {
fn default() -> Self {
Self {
use_parallel: true,
parallel_threshold: 10000,
use_simd: true,
simd_threshold: 32,
chunk_size: 1024,
}
}
}
#[derive(Clone)]
pub struct EnhancedDiagonalOperator<F> {
diagonal: Vec<F>,
#[allow(dead_code)]
options: EnhancedOperatorOptions,
}
impl<F: Float + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps> EnhancedDiagonalOperator<F> {
#[allow(dead_code)]
pub fn new(diagonal: Vec<F>) -> Self {
Self {
diagonal,
options: EnhancedOperatorOptions::default(),
}
}
#[allow(dead_code)]
pub fn with_options(diagonal: Vec<F>, options: EnhancedOperatorOptions) -> Self {
Self { diagonal, options }
}
pub fn diagonal(&self) -> &[F] {
&self.diagonal
}
}
impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
LinearOperator<F> for EnhancedDiagonalOperator<F>
{
fn shape(&self) -> (usize, usize) {
let n = self.diagonal.len();
(n, n)
}
fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
if x.len() != self.diagonal.len() {
return Err(SparseError::DimensionMismatch {
expected: self.diagonal.len(),
found: x.len(),
});
}
let mut result = vec![F::sparse_zero(); x.len()];
if self.options.use_parallel && x.len() >= self.options.parallel_threshold {
use scirs2_core::parallel_ops::*;
let indices: Vec<usize> = (0..x.len()).collect();
let values = parallel_map(&indices, |&i| self.diagonal[i] * x[i]);
result = values;
} else {
#[allow(clippy::needless_range_loop)]
for i in 0..x.len() {
result[i] = self.diagonal[i] * x[i];
}
}
Ok(result)
}
fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
self.matvec(x)
}
fn has_adjoint(&self) -> bool {
true
}
}
pub struct EnhancedSumOperator<F> {
a: Box<dyn LinearOperator<F>>,
b: Box<dyn LinearOperator<F>>,
#[allow(dead_code)]
options: EnhancedOperatorOptions,
}
impl<F: Float + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps> EnhancedSumOperator<F> {
#[allow(dead_code)]
pub fn new(a: Box<dyn LinearOperator<F>>, b: Box<dyn LinearOperator<F>>) -> SparseResult<Self> {
if a.shape() != b.shape() {
return Err(SparseError::ShapeMismatch {
expected: a.shape(),
found: b.shape(),
});
}
Ok(Self {
a,
b,
options: EnhancedOperatorOptions::default(),
})
}
#[allow(dead_code)]
pub fn with_options(
a: Box<dyn LinearOperator<F>>,
b: Box<dyn LinearOperator<F>>,
#[allow(dead_code)] options: EnhancedOperatorOptions,
) -> SparseResult<Self> {
if a.shape() != b.shape() {
return Err(SparseError::ShapeMismatch {
expected: a.shape(),
found: b.shape(),
});
}
Ok(Self { a, b, options })
}
}
impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
LinearOperator<F> for EnhancedSumOperator<F>
{
fn shape(&self) -> (usize, usize) {
self.a.shape()
}
fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
let a_result = self.a.matvec(x)?;
let b_result = self.b.matvec(x)?;
let mut result = vec![F::sparse_zero(); a_result.len()];
let parallel_opts = Some(ParallelVectorOptions {
use_parallel: self.options.use_parallel,
parallel_threshold: self.options.parallel_threshold,
chunk_size: self.options.chunk_size,
use_simd: self.options.use_simd,
simd_threshold: self.options.simd_threshold,
});
parallel_vector_add(&a_result, &b_result, &mut result, parallel_opts);
Ok(result)
}
fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
if !self.a.has_adjoint() || !self.b.has_adjoint() {
return Err(SparseError::OperationNotSupported(
"adjoint not supported for one or both operators".to_string(),
));
}
let a_result = self.a.rmatvec(x)?;
let b_result = self.b.rmatvec(x)?;
let mut result = vec![F::sparse_zero(); a_result.len()];
let parallel_opts = Some(ParallelVectorOptions {
use_parallel: self.options.use_parallel,
parallel_threshold: self.options.parallel_threshold,
chunk_size: self.options.chunk_size,
use_simd: self.options.use_simd,
simd_threshold: self.options.simd_threshold,
});
parallel_vector_add(&a_result, &b_result, &mut result, parallel_opts);
Ok(result)
}
fn has_adjoint(&self) -> bool {
self.a.has_adjoint() && self.b.has_adjoint()
}
}
pub struct EnhancedDifferenceOperator<F> {
a: Box<dyn LinearOperator<F>>,
b: Box<dyn LinearOperator<F>>,
#[allow(dead_code)]
options: EnhancedOperatorOptions,
}
impl<F: Float + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
EnhancedDifferenceOperator<F>
{
#[allow(dead_code)]
pub fn new(a: Box<dyn LinearOperator<F>>, b: Box<dyn LinearOperator<F>>) -> SparseResult<Self> {
if a.shape() != b.shape() {
return Err(SparseError::ShapeMismatch {
expected: a.shape(),
found: b.shape(),
});
}
Ok(Self {
a,
b,
options: EnhancedOperatorOptions::default(),
})
}
#[allow(dead_code)]
pub fn with_options(
a: Box<dyn LinearOperator<F>>,
b: Box<dyn LinearOperator<F>>,
#[allow(dead_code)] options: EnhancedOperatorOptions,
) -> SparseResult<Self> {
if a.shape() != b.shape() {
return Err(SparseError::ShapeMismatch {
expected: a.shape(),
found: b.shape(),
});
}
Ok(Self { a, b, options })
}
}
impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
LinearOperator<F> for EnhancedDifferenceOperator<F>
{
fn shape(&self) -> (usize, usize) {
self.a.shape()
}
fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
let a_result = self.a.matvec(x)?;
let b_result = self.b.matvec(x)?;
let mut result = vec![F::sparse_zero(); a_result.len()];
let parallel_opts = Some(ParallelVectorOptions {
use_parallel: self.options.use_parallel,
parallel_threshold: self.options.parallel_threshold,
chunk_size: self.options.chunk_size,
use_simd: self.options.use_simd,
simd_threshold: self.options.simd_threshold,
});
parallel_vector_sub(&a_result, &b_result, &mut result, parallel_opts);
Ok(result)
}
fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
if !self.a.has_adjoint() || !self.b.has_adjoint() {
return Err(SparseError::OperationNotSupported(
"adjoint not supported for one or both operators".to_string(),
));
}
let a_result = self.a.rmatvec(x)?;
let b_result = self.b.rmatvec(x)?;
let mut result = vec![F::sparse_zero(); a_result.len()];
let parallel_opts = Some(ParallelVectorOptions {
use_parallel: self.options.use_parallel,
parallel_threshold: self.options.parallel_threshold,
chunk_size: self.options.chunk_size,
use_simd: self.options.use_simd,
simd_threshold: self.options.simd_threshold,
});
parallel_vector_sub(&a_result, &b_result, &mut result, parallel_opts);
Ok(result)
}
fn has_adjoint(&self) -> bool {
self.a.has_adjoint() && self.b.has_adjoint()
}
}
pub struct EnhancedScaledOperator<F> {
alpha: F,
operator: Box<dyn LinearOperator<F>>,
#[allow(dead_code)]
options: EnhancedOperatorOptions,
}
impl<F: Float + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps> EnhancedScaledOperator<F> {
#[allow(dead_code)]
pub fn new(alpha: F, operator: Box<dyn LinearOperator<F>>) -> Self {
Self {
alpha,
operator,
options: EnhancedOperatorOptions::default(),
}
}
#[allow(dead_code)]
pub fn with_options(
alpha: F,
operator: Box<dyn LinearOperator<F>>,
#[allow(dead_code)] options: EnhancedOperatorOptions,
) -> Self {
Self {
alpha,
operator,
options,
}
}
}
impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
LinearOperator<F> for EnhancedScaledOperator<F>
{
fn shape(&self) -> (usize, usize) {
self.operator.shape()
}
fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
let result = self.operator.matvec(x)?;
let mut scaled_result = vec![F::sparse_zero(); result.len()];
let parallel_opts = Some(ParallelVectorOptions {
use_parallel: self.options.use_parallel,
parallel_threshold: self.options.parallel_threshold,
chunk_size: self.options.chunk_size,
use_simd: self.options.use_simd,
simd_threshold: self.options.simd_threshold,
});
parallel_vector_scale(self.alpha, &result, &mut scaled_result, parallel_opts);
Ok(scaled_result)
}
fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
if !self.operator.has_adjoint() {
return Err(SparseError::OperationNotSupported(
"adjoint not supported for underlying operator".to_string(),
));
}
let result = self.operator.rmatvec(x)?;
let mut scaled_result = vec![F::sparse_zero(); result.len()];
let parallel_opts = Some(ParallelVectorOptions {
use_parallel: self.options.use_parallel,
parallel_threshold: self.options.parallel_threshold,
chunk_size: self.options.chunk_size,
use_simd: self.options.use_simd,
simd_threshold: self.options.simd_threshold,
});
parallel_vector_scale(self.alpha, &result, &mut scaled_result, parallel_opts);
Ok(scaled_result)
}
fn has_adjoint(&self) -> bool {
self.operator.has_adjoint()
}
}
pub struct ConvolutionOperator<F> {
kernel: Vec<F>,
input_size: usize,
output_size: usize,
mode: ConvolutionMode,
#[allow(dead_code)]
options: EnhancedOperatorOptions,
}
#[derive(Debug, Clone, Copy)]
pub enum ConvolutionMode {
Full,
Same,
Valid,
}
impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
ConvolutionOperator<F>
{
#[allow(dead_code)]
pub fn new(kernel: Vec<F>, input_size: usize, mode: ConvolutionMode) -> Self {
let output_size = match mode {
ConvolutionMode::Full => input_size + kernel.len() - 1,
ConvolutionMode::Same => input_size,
ConvolutionMode::Valid => {
if input_size >= kernel.len() {
input_size - kernel.len() + 1
} else {
0
}
}
};
Self {
kernel,
input_size,
output_size,
mode,
options: EnhancedOperatorOptions::default(),
}
}
fn optimize_convolution_parallel(&self, result: &mut [F], x: &[F]) -> SparseResult<()> {
use scirs2_core::parallel_ops::*;
let chunk_size = self.options.chunk_size.min(self.output_size);
let indices: Vec<usize> = (0..self.output_size).collect();
let chunks: Vec<&[usize]> = indices.chunks(chunk_size).collect();
let parallel_results: Vec<Vec<F>> = parallel_map(&chunks, |chunk| {
let mut chunk_result = vec![F::sparse_zero(); chunk.len()];
for (local_i, &global_i) in chunk.iter().enumerate() {
chunk_result[local_i] = self.compute_convolution_at_index(global_i, x);
}
chunk_result
});
let mut result_idx = 0;
for chunk_result in parallel_results {
for val in chunk_result {
if result_idx < result.len() {
result[result_idx] = val;
result_idx += 1;
}
}
}
Ok(())
}
fn optimize_convolution_simd(&self, result: &mut [F], x: &[F]) -> SparseResult<()> {
#[allow(clippy::needless_range_loop)]
for i in 0..self.output_size {
let convolution_result = self.compute_convolution_at_index_simd(i, x);
result[i] = convolution_result;
}
Ok(())
}
fn compute_convolution_at_index(&self, i: usize, x: &[F]) -> F {
let mut sum = F::sparse_zero();
match self.mode {
ConvolutionMode::Full => {
for (j, &kernel_val) in self.kernel.iter().enumerate() {
if i >= j && (i - j) < x.len() {
sum += kernel_val * x[i - j];
}
}
}
ConvolutionMode::Same => {
let pad = self.kernel.len() / 2;
for (j, &kernel_val) in self.kernel.iter().enumerate() {
let idx = i + j;
if idx >= pad && (idx - pad) < x.len() {
sum += kernel_val * x[idx - pad];
}
}
}
ConvolutionMode::Valid => {
for (j, &kernel_val) in self.kernel.iter().enumerate() {
let idx = i + j;
if idx < x.len() {
sum += kernel_val * x[idx];
}
}
}
}
sum
}
fn compute_convolution_at_index_simd(&self, i: usize, x: &[F]) -> F {
let mut x_segment = Vec::new();
let mut kernel_segment = Vec::new();
match self.mode {
ConvolutionMode::Full => {
for (j, &kernel_val) in self.kernel.iter().enumerate() {
if i >= j && (i - j) < x.len() {
kernel_segment.push(kernel_val);
x_segment.push(x[i - j]);
}
}
}
ConvolutionMode::Same => {
let pad = self.kernel.len() / 2;
for (j, &kernel_val) in self.kernel.iter().enumerate() {
let idx = i + j;
if idx >= pad && (idx - pad) < x.len() {
kernel_segment.push(kernel_val);
x_segment.push(x[idx - pad]);
}
}
}
ConvolutionMode::Valid => {
for (j, &kernel_val) in self.kernel.iter().enumerate() {
let idx = i + j;
if idx < x.len() {
kernel_segment.push(kernel_val);
x_segment.push(x[idx]);
}
}
}
}
if kernel_segment.len() >= self.options.simd_threshold {
use scirs2_core::ndarray::ArrayView1;
let kernel_view = ArrayView1::from(&kernel_segment);
let x_view = ArrayView1::from(&x_segment);
F::simd_dot(&kernel_view, &x_view)
} else {
kernel_segment
.iter()
.zip(x_segment.iter())
.map(|(&k, &x)| k * x)
.sum()
}
}
#[allow(dead_code)]
pub fn with_options(
kernel: Vec<F>,
input_size: usize,
mode: ConvolutionMode,
#[allow(dead_code)] options: EnhancedOperatorOptions,
) -> Self {
let output_size = match mode {
ConvolutionMode::Full => input_size + kernel.len() - 1,
ConvolutionMode::Same => input_size,
ConvolutionMode::Valid => {
if input_size >= kernel.len() {
input_size - kernel.len() + 1
} else {
0
}
}
};
Self {
kernel,
input_size,
output_size,
mode,
options,
}
}
}
impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
LinearOperator<F> for ConvolutionOperator<F>
{
fn shape(&self) -> (usize, usize) {
(self.output_size, self.input_size)
}
fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
if x.len() != self.input_size {
return Err(SparseError::DimensionMismatch {
expected: self.input_size,
found: x.len(),
});
}
let mut result = vec![F::sparse_zero(); self.output_size];
match self.mode {
ConvolutionMode::Full => {
for i in 0..self.output_size {
let mut sum = F::sparse_zero();
for (j, &kernel_val) in self.kernel.iter().enumerate() {
if i >= j && (i - j) < x.len() {
sum += kernel_val * x[i - j];
}
}
result[i] = sum;
}
}
ConvolutionMode::Same => {
let pad = self.kernel.len() / 2;
#[allow(clippy::needless_range_loop)]
for i in 0..self.output_size {
let mut sum = F::sparse_zero();
for (j, &kernel_val) in self.kernel.iter().enumerate() {
let idx = i + j;
if idx >= pad && (idx - pad) < x.len() {
sum += kernel_val * x[idx - pad];
}
}
result[i] = sum;
}
}
ConvolutionMode::Valid =>
{
#[allow(clippy::needless_range_loop)]
for i in 0..self.output_size {
let mut sum = F::sparse_zero();
for (j, &kernel_val) in self.kernel.iter().enumerate() {
let idx = i + j;
if idx < x.len() {
sum += kernel_val * x[idx];
}
}
result[i] = sum;
}
}
}
if self.options.use_parallel && self.output_size >= self.options.parallel_threshold {
self.optimize_convolution_parallel(&mut result, x)?;
} else if self.options.use_simd && self.kernel.len() >= self.options.simd_threshold {
self.optimize_convolution_simd(&mut result, x)?;
}
Ok(result)
}
fn has_adjoint(&self) -> bool {
true
}
fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
if x.len() != self.output_size {
return Err(SparseError::DimensionMismatch {
expected: self.output_size,
found: x.len(),
});
}
let mut result = vec![F::sparse_zero(); self.input_size];
let flipped_kernel: Vec<F> = self.kernel.iter().rev().copied().collect();
match self.mode {
ConvolutionMode::Full =>
{
#[allow(clippy::needless_range_loop)]
for i in 0..self.input_size {
let mut sum = F::sparse_zero();
for (j, &kernel_val) in flipped_kernel.iter().enumerate() {
let idx = i + j;
if idx < x.len() {
sum += kernel_val * x[idx];
}
}
result[i] = sum;
}
}
ConvolutionMode::Same => {
let pad = flipped_kernel.len() / 2;
for i in 0..self.input_size {
let mut sum = F::sparse_zero();
for (j, &kernel_val) in flipped_kernel.iter().enumerate() {
if i + pad >= j && (i + pad - j) < x.len() {
sum += kernel_val * x[i + pad - j];
}
}
result[i] = sum;
}
}
ConvolutionMode::Valid => {
for i in 0..self.input_size {
let mut sum = F::sparse_zero();
for (j, &kernel_val) in flipped_kernel.iter().enumerate() {
if i >= j && (i - j) < x.len() {
sum += kernel_val * x[i - j];
}
}
result[i] = sum;
}
}
}
Ok(result)
}
}
pub struct FiniteDifferenceOperator<F> {
size: usize,
order: usize,
spacing: F,
boundary: BoundaryCondition,
#[allow(dead_code)]
options: EnhancedOperatorOptions,
}
#[derive(Debug, Clone, Copy)]
pub enum BoundaryCondition {
Neumann,
Dirichlet,
Periodic,
}
impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
FiniteDifferenceOperator<F>
{
#[allow(dead_code)]
pub fn new(size: usize, order: usize, spacing: F, boundary: BoundaryCondition) -> Self {
Self {
size,
order,
spacing,
boundary,
options: EnhancedOperatorOptions::default(),
}
}
#[allow(dead_code)]
pub fn with_options(
size: usize,
order: usize,
spacing: F,
boundary: BoundaryCondition,
#[allow(dead_code)] options: EnhancedOperatorOptions,
) -> Self {
Self {
size,
order,
spacing,
boundary,
options,
}
}
fn get_coefficients(&self) -> Vec<F> {
match self.order {
1 => {
vec![
-F::sparse_one()
/ (F::from(2.0).expect("Failed to convert constant to float")
* self.spacing),
F::sparse_zero(),
F::sparse_one()
/ (F::from(2.0).expect("Failed to convert constant to float")
* self.spacing),
]
}
2 => {
let h_sq = self.spacing * self.spacing;
vec![
F::sparse_one() / h_sq,
-F::from(2.0).expect("Failed to convert constant to float") / h_sq,
F::sparse_one() / h_sq,
]
}
_ => {
vec![F::sparse_zero(); 2 * self.order + 1]
}
}
}
}
impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
LinearOperator<F> for FiniteDifferenceOperator<F>
{
fn shape(&self) -> (usize, usize) {
(self.size, self.size)
}
fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
if x.len() != self.size {
return Err(SparseError::DimensionMismatch {
expected: self.size,
found: x.len(),
});
}
let mut result = vec![F::sparse_zero(); self.size];
let coeffs = self.get_coefficients();
let stencil_radius = coeffs.len() / 2;
#[allow(clippy::needless_range_loop)]
for i in 0..self.size {
let mut sum = F::sparse_zero();
for (j, &coeff) in coeffs.iter().enumerate() {
let idx = i as isize + j as isize - stencil_radius as isize;
let value = match self.boundary {
BoundaryCondition::Neumann => {
if idx < 0 {
x[0]
} else if idx >= self.size as isize {
x[self.size - 1]
} else {
x[idx as usize]
}
}
BoundaryCondition::Dirichlet => {
if idx < 0 || idx >= self.size as isize {
F::sparse_zero()
} else {
x[idx as usize]
}
}
BoundaryCondition::Periodic => {
let periodic_idx = ((idx % self.size as isize + self.size as isize)
% self.size as isize)
as usize;
x[periodic_idx]
}
};
sum += coeff * value;
}
result[i] = sum;
}
Ok(result)
}
fn has_adjoint(&self) -> bool {
true
}
fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
self.matvec(x)
}
}
#[allow(dead_code)]
pub fn enhanced_diagonal<
F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
>(
diagonal: Vec<F>,
) -> Box<dyn LinearOperator<F>> {
Box::new(EnhancedDiagonalOperator::new(diagonal))
}
#[allow(dead_code)]
pub fn enhanced_add<
F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
>(
left: Box<dyn LinearOperator<F>>,
right: Box<dyn LinearOperator<F>>,
) -> SparseResult<Box<dyn LinearOperator<F>>> {
Ok(Box::new(EnhancedSumOperator::new(left, right)?))
}
#[allow(dead_code)]
pub fn enhanced_subtract<
F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
>(
left: Box<dyn LinearOperator<F>>,
right: Box<dyn LinearOperator<F>>,
) -> SparseResult<Box<dyn LinearOperator<F>>> {
Ok(Box::new(EnhancedDifferenceOperator::new(left, right)?))
}
#[allow(dead_code)]
pub fn enhanced_scale<
F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
>(
alpha: F,
operator: Box<dyn LinearOperator<F>>,
) -> Box<dyn LinearOperator<F>> {
Box::new(EnhancedScaledOperator::new(alpha, operator))
}
#[allow(dead_code)]
pub fn convolution_operator<
F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
>(
kernel: Vec<F>,
input_size: usize,
mode: ConvolutionMode,
) -> Box<dyn LinearOperator<F>> {
Box::new(ConvolutionOperator::new(kernel, input_size, mode))
}
#[allow(dead_code)]
pub fn finite_difference_operator<
F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
>(
size: usize,
order: usize,
spacing: F,
boundary: BoundaryCondition,
) -> Box<dyn LinearOperator<F>> {
Box::new(FiniteDifferenceOperator::new(
size, order, spacing, boundary,
))
}
pub struct EnhancedCompositionOperator<F> {
left: Box<dyn LinearOperator<F>>, right: Box<dyn LinearOperator<F>>, #[allow(dead_code)]
options: EnhancedOperatorOptions,
}
impl<F: Float + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
EnhancedCompositionOperator<F>
{
#[allow(dead_code)]
pub fn new(
left: Box<dyn LinearOperator<F>>,
right: Box<dyn LinearOperator<F>>,
) -> SparseResult<Self> {
let (left_rows, left_cols) = left.shape();
let (right_rows, right_cols) = right.shape();
if left_cols != right_rows {
return Err(SparseError::ShapeMismatch {
expected: (left_rows, right_rows),
found: (left_rows, left_cols),
});
}
Ok(Self {
left,
right,
options: EnhancedOperatorOptions::default(),
})
}
#[allow(dead_code)]
pub fn with_options(
left: Box<dyn LinearOperator<F>>,
right: Box<dyn LinearOperator<F>>,
options: EnhancedOperatorOptions,
) -> SparseResult<Self> {
let (left_rows, left_cols) = left.shape();
let (right_rows, right_cols) = right.shape();
if left_cols != right_rows {
return Err(SparseError::ShapeMismatch {
expected: (left_rows, right_rows),
found: (left_rows, left_cols),
});
}
Ok(Self {
left,
right,
options,
})
}
}
impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
LinearOperator<F> for EnhancedCompositionOperator<F>
{
fn shape(&self) -> (usize, usize) {
let (left_rows, _) = self.left.shape();
let (_, right_cols) = self.right.shape();
(left_rows, right_cols)
}
fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
let intermediate = self.right.matvec(x)?;
self.left.matvec(&intermediate)
}
fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
if !self.left.has_adjoint() || !self.right.has_adjoint() {
return Err(SparseError::OperationNotSupported(
"adjoint not supported for one or both operators".to_string(),
));
}
let intermediate = self.left.rmatvec(x)?;
self.right.rmatvec(&intermediate)
}
fn has_adjoint(&self) -> bool {
self.left.has_adjoint() && self.right.has_adjoint()
}
}
pub struct ElementwiseFunctionOperator<F> {
function: Box<dyn Fn(F) -> F + Send + Sync>,
size: usize,
#[allow(dead_code)]
options: EnhancedOperatorOptions,
}
impl<F: Float + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
ElementwiseFunctionOperator<F>
{
pub fn new<Func>(function: Func, size: usize) -> Self
where
Func: Fn(F) -> F + Send + Sync + 'static,
{
Self {
function: Box::new(function),
size,
options: EnhancedOperatorOptions::default(),
}
}
#[allow(dead_code)]
pub fn with_options<Func>(function: Func, size: usize, options: EnhancedOperatorOptions) -> Self
where
Func: Fn(F) -> F + Send + Sync + 'static,
{
Self {
function: Box::new(function),
size,
options,
}
}
}
impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
LinearOperator<F> for ElementwiseFunctionOperator<F>
{
fn shape(&self) -> (usize, usize) {
(self.size, self.size)
}
fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
if x.len() != self.size {
return Err(SparseError::DimensionMismatch {
expected: self.size,
found: x.len(),
});
}
if self.options.use_parallel && x.len() >= self.options.parallel_threshold {
use scirs2_core::parallel_ops::*;
let result = parallel_map(x, |&val| (self.function)(val));
Ok(result)
} else {
let result: Vec<F> = x.iter().map(|&val| (self.function)(val)).collect();
Ok(result)
}
}
fn has_adjoint(&self) -> bool {
false }
fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
Err(SparseError::OperationNotSupported(
"adjoint not supported for general element-wise functions".to_string(),
))
}
}
pub struct KroneckerProductOperator<F> {
left: Box<dyn LinearOperator<F>>,
right: Box<dyn LinearOperator<F>>,
#[allow(dead_code)]
options: EnhancedOperatorOptions,
}
impl<F: Float + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps> KroneckerProductOperator<F> {
#[allow(dead_code)]
pub fn new(left: Box<dyn LinearOperator<F>>, right: Box<dyn LinearOperator<F>>) -> Self {
Self {
left,
right,
options: EnhancedOperatorOptions::default(),
}
}
#[allow(dead_code)]
pub fn with_options(
left: Box<dyn LinearOperator<F>>,
right: Box<dyn LinearOperator<F>>,
options: EnhancedOperatorOptions,
) -> Self {
Self {
left,
right,
options,
}
}
}
impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
LinearOperator<F> for KroneckerProductOperator<F>
{
fn shape(&self) -> (usize, usize) {
let (left_rows, left_cols) = self.left.shape();
let (right_rows, right_cols) = self.right.shape();
(left_rows * right_rows, left_cols * right_cols)
}
fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
let (left_rows, left_cols) = self.left.shape();
let (right_rows, right_cols) = self.right.shape();
if x.len() != left_cols * right_cols {
return Err(SparseError::DimensionMismatch {
expected: left_cols * right_cols,
found: x.len(),
});
}
let mut result = vec![F::sparse_zero(); left_rows * right_rows];
for j in 0..left_cols {
let mut col: Vec<F> = Vec::with_capacity(right_cols);
for i in 0..right_cols {
col.push(x[i * left_cols + j]);
}
let right_result = self.right.matvec(&col)?;
for i in 0..right_rows {
result[i * left_cols + j] = right_result[i];
}
}
let mut final_result = vec![F::sparse_zero(); left_rows * right_rows];
for i in 0..right_rows {
let mut row: Vec<F> = Vec::with_capacity(left_cols);
for j in 0..left_cols {
row.push(result[i * left_cols + j]);
}
let left_result = self.left.matvec(&row)?;
for k in 0..left_rows {
final_result[k * right_rows + i] = left_result[k];
}
}
Ok(final_result)
}
fn has_adjoint(&self) -> bool {
self.left.has_adjoint() && self.right.has_adjoint()
}
fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
if !self.has_adjoint() {
return Err(SparseError::OperationNotSupported(
"adjoint not supported for one or both operators".to_string(),
));
}
Err(SparseError::OperationNotSupported(
"Kronecker product adjoint requires operator cloning which is not currently supported"
.to_string(),
))
}
}
#[allow(dead_code)]
pub fn enhanced_compose<
F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
>(
left: Box<dyn LinearOperator<F>>,
right: Box<dyn LinearOperator<F>>,
) -> SparseResult<Box<dyn LinearOperator<F>>> {
Ok(Box::new(EnhancedCompositionOperator::new(left, right)?))
}
#[allow(dead_code)]
pub fn elementwisefunction<
F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
Func: Fn(F) -> F + Send + Sync + 'static,
>(
function: Func,
size: usize,
) -> Box<dyn LinearOperator<F>> {
Box::new(ElementwiseFunctionOperator::new(function, size))
}
#[allow(dead_code)]
pub fn kronecker_product<
F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
>(
left: Box<dyn LinearOperator<F>>,
right: Box<dyn LinearOperator<F>>,
) -> Box<dyn LinearOperator<F>> {
Box::new(KroneckerProductOperator::new(left, right))
}
#[allow(dead_code)]
pub fn adjoint_operator<
F: Float + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
>(
operator: Box<dyn LinearOperator<F>>,
) -> SparseResult<Box<dyn LinearOperator<F>>> {
Ok(Box::new(AdjointOperator::new(operator)?))
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_enhanced_diagonal_operator() {
let diag = vec![2.0, 3.0, 4.0];
let op = EnhancedDiagonalOperator::new(diag);
let x = vec![1.0, 2.0, 3.0];
let y = op.matvec(&x).expect("Operation failed");
assert_eq!(y, vec![2.0, 6.0, 12.0]);
}
#[test]
fn test_enhanced_sum_operator() {
let diag1 = enhanced_diagonal(vec![1.0, 2.0, 3.0]);
let diag2 = enhanced_diagonal(vec![2.0, 1.0, 1.0]);
let sum_op = EnhancedSumOperator::new(diag1, diag2).expect("Operation failed");
let x = vec![1.0, 1.0, 1.0];
let y = sum_op.matvec(&x).expect("Operation failed");
assert_eq!(y, vec![3.0, 3.0, 4.0]); }
#[test]
fn test_enhanced_difference_operator() {
let diag1 = enhanced_diagonal(vec![3.0, 4.0, 5.0]);
let diag2 = enhanced_diagonal(vec![1.0, 2.0, 1.0]);
let diff_op = EnhancedDifferenceOperator::new(diag1, diag2).expect("Operation failed");
let x = vec![1.0, 1.0, 1.0];
let y = diff_op.matvec(&x).expect("Operation failed");
assert_eq!(y, vec![2.0, 2.0, 4.0]); }
#[test]
fn test_enhanced_scaled_operator() {
let diag = enhanced_diagonal(vec![1.0, 2.0, 3.0]);
let scaled_op = EnhancedScaledOperator::new(2.0, diag);
let x = vec![1.0, 1.0, 1.0];
let y = scaled_op.matvec(&x).expect("Operation failed");
assert_eq!(y, vec![2.0, 4.0, 6.0]); }
#[test]
fn test_convolution_operator_full() {
let kernel = vec![1.0, 2.0, 3.0];
let conv_op = ConvolutionOperator::new(kernel, 3, ConvolutionMode::Full);
let x = vec![1.0, 0.0, 0.0];
let y = conv_op.matvec(&x).expect("Operation failed");
assert_eq!(y, vec![1.0, 2.0, 3.0, 0.0, 0.0]); }
#[test]
fn test_convolution_operator_same() {
let kernel = vec![0.0, 1.0, 0.0]; let conv_op = ConvolutionOperator::new(kernel, 3, ConvolutionMode::Same);
let x = vec![1.0, 2.0, 3.0];
let y = conv_op.matvec(&x).expect("Operation failed");
assert_relative_eq!(y[0], 1.0, epsilon = 1e-10);
assert_relative_eq!(y[1], 2.0, epsilon = 1e-10);
assert_relative_eq!(y[2], 3.0, epsilon = 1e-10);
}
#[test]
fn test_finite_difference_first_derivative() {
let fd_op = FiniteDifferenceOperator::new(5, 1, 1.0, BoundaryCondition::Dirichlet);
let x = vec![0.0, 1.0, 2.0, 3.0, 4.0];
let y = fd_op.matvec(&x).expect("Operation failed");
#[allow(clippy::needless_range_loop)]
for i in 1..y.len() - 1 {
assert_relative_eq!(y[i], 1.0, epsilon = 0.1);
}
}
#[test]
fn test_finite_difference_second_derivative() {
let fd_op = FiniteDifferenceOperator::new(5, 2, 1.0, BoundaryCondition::Dirichlet);
let x = vec![0.0, 1.0, 4.0, 9.0, 16.0];
let y = fd_op.matvec(&x).expect("Operation failed");
#[allow(clippy::needless_range_loop)]
for i in 1..y.len() - 1 {
assert_relative_eq!(y[i], 2.0, epsilon = 0.5);
}
}
#[test]
fn test_enhanced_operators_with_large_vectors() {
let large_size = 15000; let diag1: Vec<f64> = (0..large_size).map(|i| (i + 1) as f64).collect();
let diag2: Vec<f64> = vec![2.0; large_size];
let op1 = enhanced_diagonal(diag1);
let op2 = enhanced_diagonal(diag2);
let sum_op = EnhancedSumOperator::new(op1, op2).expect("Operation failed");
let x = vec![1.0; large_size];
let y = sum_op.matvec(&x).expect("Operation failed");
assert_relative_eq!(y[0], 3.0, epsilon = 1e-10); assert_relative_eq!(y[1], 4.0, epsilon = 1e-10); assert_relative_eq!(y[999], 1002.0, epsilon = 1e-10); }
}