1use crate::error::{SparseError, SparseResult};
7use crate::linalg::interface::{AdjointOperator, LinearOperator};
8use crate::parallel_vector_ops::*;
9use scirs2_core::numeric::{Float, NumAssign};
10use scirs2_core::SparseElement;
11use std::fmt::Debug;
12use std::iter::Sum;
13
14use scirs2_core::simd_ops::SimdUnifiedOps;
16
17#[derive(Debug, Clone)]
19pub struct EnhancedOperatorOptions {
20 pub use_parallel: bool,
22 pub parallel_threshold: usize,
24 pub use_simd: bool,
26 pub simd_threshold: usize,
28 pub chunk_size: usize,
30}
31
32impl Default for EnhancedOperatorOptions {
33 fn default() -> Self {
34 Self {
35 use_parallel: true,
36 parallel_threshold: 10000,
37 use_simd: true,
38 simd_threshold: 32,
39 chunk_size: 1024,
40 }
41 }
42}
43
44#[derive(Clone)]
46pub struct EnhancedDiagonalOperator<F> {
47 diagonal: Vec<F>,
48 #[allow(dead_code)]
49 options: EnhancedOperatorOptions,
50}
51
52impl<F: Float + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps> EnhancedDiagonalOperator<F> {
53 #[allow(dead_code)]
55 pub fn new(diagonal: Vec<F>) -> Self {
56 Self {
57 diagonal,
58 options: EnhancedOperatorOptions::default(),
59 }
60 }
61
62 #[allow(dead_code)]
64 pub fn with_options(diagonal: Vec<F>, options: EnhancedOperatorOptions) -> Self {
65 Self { diagonal, options }
66 }
67
68 pub fn diagonal(&self) -> &[F] {
70 &self.diagonal
71 }
72}
73
74impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
75 LinearOperator<F> for EnhancedDiagonalOperator<F>
76{
77 fn shape(&self) -> (usize, usize) {
78 let n = self.diagonal.len();
79 (n, n)
80 }
81
82 fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
83 if x.len() != self.diagonal.len() {
84 return Err(SparseError::DimensionMismatch {
85 expected: self.diagonal.len(),
86 found: x.len(),
87 });
88 }
89
90 let mut result = vec![F::sparse_zero(); x.len()];
91
92 if self.options.use_parallel && x.len() >= self.options.parallel_threshold {
96 use scirs2_core::parallel_ops::*;
98 let indices: Vec<usize> = (0..x.len()).collect();
99 let values = parallel_map(&indices, |&i| self.diagonal[i] * x[i]);
100 result = values;
101 } else {
102 #[allow(clippy::needless_range_loop)]
104 for i in 0..x.len() {
105 result[i] = self.diagonal[i] * x[i];
106 }
107 }
108
109 Ok(result)
110 }
111
112 fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
113 self.matvec(x)
115 }
116
117 fn has_adjoint(&self) -> bool {
118 true
119 }
120}
121
122pub struct EnhancedSumOperator<F> {
124 a: Box<dyn LinearOperator<F>>,
125 b: Box<dyn LinearOperator<F>>,
126 #[allow(dead_code)]
127 options: EnhancedOperatorOptions,
128}
129
130impl<F: Float + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps> EnhancedSumOperator<F> {
131 #[allow(dead_code)]
133 pub fn new(a: Box<dyn LinearOperator<F>>, b: Box<dyn LinearOperator<F>>) -> SparseResult<Self> {
134 if a.shape() != b.shape() {
135 return Err(SparseError::ShapeMismatch {
136 expected: a.shape(),
137 found: b.shape(),
138 });
139 }
140 Ok(Self {
141 a,
142 b,
143 options: EnhancedOperatorOptions::default(),
144 })
145 }
146
147 #[allow(dead_code)]
149 pub fn with_options(
150 a: Box<dyn LinearOperator<F>>,
151 b: Box<dyn LinearOperator<F>>,
152 #[allow(dead_code)] options: EnhancedOperatorOptions,
153 ) -> SparseResult<Self> {
154 if a.shape() != b.shape() {
155 return Err(SparseError::ShapeMismatch {
156 expected: a.shape(),
157 found: b.shape(),
158 });
159 }
160 Ok(Self { a, b, options })
161 }
162}
163
164impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
165 LinearOperator<F> for EnhancedSumOperator<F>
166{
167 fn shape(&self) -> (usize, usize) {
168 self.a.shape()
169 }
170
171 fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
172 let a_result = self.a.matvec(x)?;
173 let b_result = self.b.matvec(x)?;
174
175 let mut result = vec![F::sparse_zero(); a_result.len()];
176 let parallel_opts = Some(ParallelVectorOptions {
177 use_parallel: self.options.use_parallel,
178 parallel_threshold: self.options.parallel_threshold,
179 chunk_size: self.options.chunk_size,
180 use_simd: self.options.use_simd,
181 simd_threshold: self.options.simd_threshold,
182 });
183
184 parallel_vector_add(&a_result, &b_result, &mut result, parallel_opts);
186 Ok(result)
187 }
188
189 fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
190 if !self.a.has_adjoint() || !self.b.has_adjoint() {
191 return Err(SparseError::OperationNotSupported(
192 "adjoint not supported for one or both operators".to_string(),
193 ));
194 }
195 let a_result = self.a.rmatvec(x)?;
196 let b_result = self.b.rmatvec(x)?;
197
198 let mut result = vec![F::sparse_zero(); a_result.len()];
199 let parallel_opts = Some(ParallelVectorOptions {
200 use_parallel: self.options.use_parallel,
201 parallel_threshold: self.options.parallel_threshold,
202 chunk_size: self.options.chunk_size,
203 use_simd: self.options.use_simd,
204 simd_threshold: self.options.simd_threshold,
205 });
206
207 parallel_vector_add(&a_result, &b_result, &mut result, parallel_opts);
208 Ok(result)
209 }
210
211 fn has_adjoint(&self) -> bool {
212 self.a.has_adjoint() && self.b.has_adjoint()
213 }
214}
215
216pub struct EnhancedDifferenceOperator<F> {
218 a: Box<dyn LinearOperator<F>>,
219 b: Box<dyn LinearOperator<F>>,
220 #[allow(dead_code)]
221 options: EnhancedOperatorOptions,
222}
223
224impl<F: Float + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
225 EnhancedDifferenceOperator<F>
226{
227 #[allow(dead_code)]
229 pub fn new(a: Box<dyn LinearOperator<F>>, b: Box<dyn LinearOperator<F>>) -> SparseResult<Self> {
230 if a.shape() != b.shape() {
231 return Err(SparseError::ShapeMismatch {
232 expected: a.shape(),
233 found: b.shape(),
234 });
235 }
236 Ok(Self {
237 a,
238 b,
239 options: EnhancedOperatorOptions::default(),
240 })
241 }
242
243 #[allow(dead_code)]
245 pub fn with_options(
246 a: Box<dyn LinearOperator<F>>,
247 b: Box<dyn LinearOperator<F>>,
248 #[allow(dead_code)] options: EnhancedOperatorOptions,
249 ) -> SparseResult<Self> {
250 if a.shape() != b.shape() {
251 return Err(SparseError::ShapeMismatch {
252 expected: a.shape(),
253 found: b.shape(),
254 });
255 }
256 Ok(Self { a, b, options })
257 }
258}
259
260impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
261 LinearOperator<F> for EnhancedDifferenceOperator<F>
262{
263 fn shape(&self) -> (usize, usize) {
264 self.a.shape()
265 }
266
267 fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
268 let a_result = self.a.matvec(x)?;
269 let b_result = self.b.matvec(x)?;
270
271 let mut result = vec![F::sparse_zero(); a_result.len()];
272 let parallel_opts = Some(ParallelVectorOptions {
273 use_parallel: self.options.use_parallel,
274 parallel_threshold: self.options.parallel_threshold,
275 chunk_size: self.options.chunk_size,
276 use_simd: self.options.use_simd,
277 simd_threshold: self.options.simd_threshold,
278 });
279
280 parallel_vector_sub(&a_result, &b_result, &mut result, parallel_opts);
282 Ok(result)
283 }
284
285 fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
286 if !self.a.has_adjoint() || !self.b.has_adjoint() {
287 return Err(SparseError::OperationNotSupported(
288 "adjoint not supported for one or both operators".to_string(),
289 ));
290 }
291 let a_result = self.a.rmatvec(x)?;
292 let b_result = self.b.rmatvec(x)?;
293
294 let mut result = vec![F::sparse_zero(); a_result.len()];
295 let parallel_opts = Some(ParallelVectorOptions {
296 use_parallel: self.options.use_parallel,
297 parallel_threshold: self.options.parallel_threshold,
298 chunk_size: self.options.chunk_size,
299 use_simd: self.options.use_simd,
300 simd_threshold: self.options.simd_threshold,
301 });
302
303 parallel_vector_sub(&a_result, &b_result, &mut result, parallel_opts);
304 Ok(result)
305 }
306
307 fn has_adjoint(&self) -> bool {
308 self.a.has_adjoint() && self.b.has_adjoint()
309 }
310}
311
312pub struct EnhancedScaledOperator<F> {
314 alpha: F,
315 operator: Box<dyn LinearOperator<F>>,
316 #[allow(dead_code)]
317 options: EnhancedOperatorOptions,
318}
319
320impl<F: Float + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps> EnhancedScaledOperator<F> {
321 #[allow(dead_code)]
323 pub fn new(alpha: F, operator: Box<dyn LinearOperator<F>>) -> Self {
324 Self {
325 alpha,
326 operator,
327 options: EnhancedOperatorOptions::default(),
328 }
329 }
330
331 #[allow(dead_code)]
333 pub fn with_options(
334 alpha: F,
335 operator: Box<dyn LinearOperator<F>>,
336 #[allow(dead_code)] options: EnhancedOperatorOptions,
337 ) -> Self {
338 Self {
339 alpha,
340 operator,
341 options,
342 }
343 }
344}
345
346impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
347 LinearOperator<F> for EnhancedScaledOperator<F>
348{
349 fn shape(&self) -> (usize, usize) {
350 self.operator.shape()
351 }
352
353 fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
354 let result = self.operator.matvec(x)?;
355 let mut scaled_result = vec![F::sparse_zero(); result.len()];
356
357 let parallel_opts = Some(ParallelVectorOptions {
358 use_parallel: self.options.use_parallel,
359 parallel_threshold: self.options.parallel_threshold,
360 chunk_size: self.options.chunk_size,
361 use_simd: self.options.use_simd,
362 simd_threshold: self.options.simd_threshold,
363 });
364
365 parallel_vector_scale(self.alpha, &result, &mut scaled_result, parallel_opts);
367 Ok(scaled_result)
368 }
369
370 fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
371 if !self.operator.has_adjoint() {
372 return Err(SparseError::OperationNotSupported(
373 "adjoint not supported for underlying operator".to_string(),
374 ));
375 }
376 let result = self.operator.rmatvec(x)?;
377 let mut scaled_result = vec![F::sparse_zero(); result.len()];
378
379 let parallel_opts = Some(ParallelVectorOptions {
380 use_parallel: self.options.use_parallel,
381 parallel_threshold: self.options.parallel_threshold,
382 chunk_size: self.options.chunk_size,
383 use_simd: self.options.use_simd,
384 simd_threshold: self.options.simd_threshold,
385 });
386
387 parallel_vector_scale(self.alpha, &result, &mut scaled_result, parallel_opts);
388 Ok(scaled_result)
389 }
390
391 fn has_adjoint(&self) -> bool {
392 self.operator.has_adjoint()
393 }
394}
395
396pub struct ConvolutionOperator<F> {
398 kernel: Vec<F>,
399 input_size: usize,
400 output_size: usize,
401 mode: ConvolutionMode,
402 #[allow(dead_code)]
403 options: EnhancedOperatorOptions,
404}
405
406#[derive(Debug, Clone, Copy)]
407pub enum ConvolutionMode {
408 Full,
410 Same,
412 Valid,
414}
415
416impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
417 ConvolutionOperator<F>
418{
419 #[allow(dead_code)]
421 pub fn new(kernel: Vec<F>, input_size: usize, mode: ConvolutionMode) -> Self {
422 let output_size = match mode {
423 ConvolutionMode::Full => input_size + kernel.len() - 1,
424 ConvolutionMode::Same => input_size,
425 ConvolutionMode::Valid => {
426 if input_size >= kernel.len() {
427 input_size - kernel.len() + 1
428 } else {
429 0
430 }
431 }
432 };
433
434 Self {
435 kernel,
436 input_size,
437 output_size,
438 mode,
439 options: EnhancedOperatorOptions::default(),
440 }
441 }
442
443 fn optimize_convolution_parallel(&self, result: &mut [F], x: &[F]) -> SparseResult<()> {
445 use scirs2_core::parallel_ops::*;
446
447 let chunk_size = self.options.chunk_size.min(self.output_size);
449 let indices: Vec<usize> = (0..self.output_size).collect();
450 let chunks: Vec<&[usize]> = indices.chunks(chunk_size).collect();
451
452 let parallel_results: Vec<Vec<F>> = parallel_map(&chunks, |chunk| {
453 let mut chunk_result = vec![F::sparse_zero(); chunk.len()];
454
455 for (local_i, &global_i) in chunk.iter().enumerate() {
456 chunk_result[local_i] = self.compute_convolution_at_index(global_i, x);
457 }
458
459 chunk_result
460 });
461
462 let mut result_idx = 0;
464 for chunk_result in parallel_results {
465 for val in chunk_result {
466 if result_idx < result.len() {
467 result[result_idx] = val;
468 result_idx += 1;
469 }
470 }
471 }
472
473 Ok(())
474 }
475
476 fn optimize_convolution_simd(&self, result: &mut [F], x: &[F]) -> SparseResult<()> {
478 #[allow(clippy::needless_range_loop)]
482 for i in 0..self.output_size {
483 let convolution_result = self.compute_convolution_at_index_simd(i, x);
485 result[i] = convolution_result;
486 }
487
488 Ok(())
489 }
490
491 fn compute_convolution_at_index(&self, i: usize, x: &[F]) -> F {
493 let mut sum = F::sparse_zero();
494
495 match self.mode {
496 ConvolutionMode::Full => {
497 for (j, &kernel_val) in self.kernel.iter().enumerate() {
498 if i >= j && (i - j) < x.len() {
499 sum += kernel_val * x[i - j];
500 }
501 }
502 }
503 ConvolutionMode::Same => {
504 let pad = self.kernel.len() / 2;
505 for (j, &kernel_val) in self.kernel.iter().enumerate() {
506 let idx = i + j;
507 if idx >= pad && (idx - pad) < x.len() {
508 sum += kernel_val * x[idx - pad];
509 }
510 }
511 }
512 ConvolutionMode::Valid => {
513 for (j, &kernel_val) in self.kernel.iter().enumerate() {
514 let idx = i + j;
515 if idx < x.len() {
516 sum += kernel_val * x[idx];
517 }
518 }
519 }
520 }
521
522 sum
523 }
524
525 fn compute_convolution_at_index_simd(&self, i: usize, x: &[F]) -> F {
527 let mut x_segment = Vec::new();
529 let mut kernel_segment = Vec::new();
530
531 match self.mode {
532 ConvolutionMode::Full => {
533 for (j, &kernel_val) in self.kernel.iter().enumerate() {
534 if i >= j && (i - j) < x.len() {
535 kernel_segment.push(kernel_val);
536 x_segment.push(x[i - j]);
537 }
538 }
539 }
540 ConvolutionMode::Same => {
541 let pad = self.kernel.len() / 2;
542 for (j, &kernel_val) in self.kernel.iter().enumerate() {
543 let idx = i + j;
544 if idx >= pad && (idx - pad) < x.len() {
545 kernel_segment.push(kernel_val);
546 x_segment.push(x[idx - pad]);
547 }
548 }
549 }
550 ConvolutionMode::Valid => {
551 for (j, &kernel_val) in self.kernel.iter().enumerate() {
552 let idx = i + j;
553 if idx < x.len() {
554 kernel_segment.push(kernel_val);
555 x_segment.push(x[idx]);
556 }
557 }
558 }
559 }
560
561 if kernel_segment.len() >= self.options.simd_threshold {
563 use scirs2_core::ndarray::ArrayView1;
564 let kernel_view = ArrayView1::from(&kernel_segment);
565 let x_view = ArrayView1::from(&x_segment);
566 F::simd_dot(&kernel_view, &x_view)
567 } else {
568 kernel_segment
570 .iter()
571 .zip(x_segment.iter())
572 .map(|(&k, &x)| k * x)
573 .sum()
574 }
575 }
576
577 #[allow(dead_code)]
579 pub fn with_options(
580 kernel: Vec<F>,
581 input_size: usize,
582 mode: ConvolutionMode,
583 #[allow(dead_code)] options: EnhancedOperatorOptions,
584 ) -> Self {
585 let output_size = match mode {
586 ConvolutionMode::Full => input_size + kernel.len() - 1,
587 ConvolutionMode::Same => input_size,
588 ConvolutionMode::Valid => {
589 if input_size >= kernel.len() {
590 input_size - kernel.len() + 1
591 } else {
592 0
593 }
594 }
595 };
596
597 Self {
598 kernel,
599 input_size,
600 output_size,
601 mode,
602 options,
603 }
604 }
605}
606
607impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
608 LinearOperator<F> for ConvolutionOperator<F>
609{
610 fn shape(&self) -> (usize, usize) {
611 (self.output_size, self.input_size)
612 }
613
614 fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
615 if x.len() != self.input_size {
616 return Err(SparseError::DimensionMismatch {
617 expected: self.input_size,
618 found: x.len(),
619 });
620 }
621
622 let mut result = vec![F::sparse_zero(); self.output_size];
623
624 match self.mode {
626 ConvolutionMode::Full => {
627 for i in 0..self.output_size {
628 let mut sum = F::sparse_zero();
629 for (j, &kernel_val) in self.kernel.iter().enumerate() {
630 if i >= j && (i - j) < x.len() {
631 sum += kernel_val * x[i - j];
632 }
633 }
634 result[i] = sum;
635 }
636 }
637 ConvolutionMode::Same => {
638 let pad = self.kernel.len() / 2;
639 #[allow(clippy::needless_range_loop)]
640 for i in 0..self.output_size {
641 let mut sum = F::sparse_zero();
642 for (j, &kernel_val) in self.kernel.iter().enumerate() {
643 let idx = i + j;
644 if idx >= pad && (idx - pad) < x.len() {
645 sum += kernel_val * x[idx - pad];
646 }
647 }
648 result[i] = sum;
649 }
650 }
651 ConvolutionMode::Valid =>
652 {
653 #[allow(clippy::needless_range_loop)]
654 for i in 0..self.output_size {
655 let mut sum = F::sparse_zero();
656 for (j, &kernel_val) in self.kernel.iter().enumerate() {
657 let idx = i + j;
658 if idx < x.len() {
659 sum += kernel_val * x[idx];
660 }
661 }
662 result[i] = sum;
663 }
664 }
665 }
666
667 if self.options.use_parallel && self.output_size >= self.options.parallel_threshold {
669 self.optimize_convolution_parallel(&mut result, x)?;
670 } else if self.options.use_simd && self.kernel.len() >= self.options.simd_threshold {
671 self.optimize_convolution_simd(&mut result, x)?;
672 }
673
674 Ok(result)
675 }
676
677 fn has_adjoint(&self) -> bool {
678 true
679 }
680
681 fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
682 if x.len() != self.output_size {
683 return Err(SparseError::DimensionMismatch {
684 expected: self.output_size,
685 found: x.len(),
686 });
687 }
688
689 let mut result = vec![F::sparse_zero(); self.input_size];
691 let flipped_kernel: Vec<F> = self.kernel.iter().rev().copied().collect();
692
693 match self.mode {
695 ConvolutionMode::Full =>
696 {
697 #[allow(clippy::needless_range_loop)]
698 for i in 0..self.input_size {
699 let mut sum = F::sparse_zero();
700 for (j, &kernel_val) in flipped_kernel.iter().enumerate() {
701 let idx = i + j;
702 if idx < x.len() {
703 sum += kernel_val * x[idx];
704 }
705 }
706 result[i] = sum;
707 }
708 }
709 ConvolutionMode::Same => {
710 let pad = flipped_kernel.len() / 2;
711 for i in 0..self.input_size {
712 let mut sum = F::sparse_zero();
713 for (j, &kernel_val) in flipped_kernel.iter().enumerate() {
714 if i + pad >= j && (i + pad - j) < x.len() {
715 sum += kernel_val * x[i + pad - j];
716 }
717 }
718 result[i] = sum;
719 }
720 }
721 ConvolutionMode::Valid => {
722 for i in 0..self.input_size {
723 let mut sum = F::sparse_zero();
724 for (j, &kernel_val) in flipped_kernel.iter().enumerate() {
725 if i >= j && (i - j) < x.len() {
726 sum += kernel_val * x[i - j];
727 }
728 }
729 result[i] = sum;
730 }
731 }
732 }
733
734 Ok(result)
735 }
736}
737
738pub struct FiniteDifferenceOperator<F> {
740 size: usize,
741 order: usize,
742 spacing: F,
743 boundary: BoundaryCondition,
744 #[allow(dead_code)]
745 options: EnhancedOperatorOptions,
746}
747
748#[derive(Debug, Clone, Copy)]
749pub enum BoundaryCondition {
750 Neumann,
752 Dirichlet,
754 Periodic,
756}
757
758impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
759 FiniteDifferenceOperator<F>
760{
761 #[allow(dead_code)]
763 pub fn new(size: usize, order: usize, spacing: F, boundary: BoundaryCondition) -> Self {
764 Self {
765 size,
766 order,
767 spacing,
768 boundary,
769 options: EnhancedOperatorOptions::default(),
770 }
771 }
772
773 #[allow(dead_code)]
775 pub fn with_options(
776 size: usize,
777 order: usize,
778 spacing: F,
779 boundary: BoundaryCondition,
780 #[allow(dead_code)] options: EnhancedOperatorOptions,
781 ) -> Self {
782 Self {
783 size,
784 order,
785 spacing,
786 boundary,
787 options,
788 }
789 }
790
791 fn get_coefficients(&self) -> Vec<F> {
793 match self.order {
794 1 => {
795 vec![
797 -F::sparse_one()
798 / (F::from(2.0).expect("Failed to convert constant to float")
799 * self.spacing),
800 F::sparse_zero(),
801 F::sparse_one()
802 / (F::from(2.0).expect("Failed to convert constant to float")
803 * self.spacing),
804 ]
805 }
806 2 => {
807 let h_sq = self.spacing * self.spacing;
809 vec![
810 F::sparse_one() / h_sq,
811 -F::from(2.0).expect("Failed to convert constant to float") / h_sq,
812 F::sparse_one() / h_sq,
813 ]
814 }
815 _ => {
816 vec![F::sparse_zero(); 2 * self.order + 1]
819 }
820 }
821 }
822}
823
824impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
825 LinearOperator<F> for FiniteDifferenceOperator<F>
826{
827 fn shape(&self) -> (usize, usize) {
828 (self.size, self.size)
829 }
830
831 fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
832 if x.len() != self.size {
833 return Err(SparseError::DimensionMismatch {
834 expected: self.size,
835 found: x.len(),
836 });
837 }
838
839 let mut result = vec![F::sparse_zero(); self.size];
840 let coeffs = self.get_coefficients();
841 let stencil_radius = coeffs.len() / 2;
842
843 #[allow(clippy::needless_range_loop)]
844 for i in 0..self.size {
845 let mut sum = F::sparse_zero();
846 for (j, &coeff) in coeffs.iter().enumerate() {
847 let idx = i as isize + j as isize - stencil_radius as isize;
848
849 let value = match self.boundary {
850 BoundaryCondition::Neumann => {
851 if idx < 0 {
852 x[0]
853 } else if idx >= self.size as isize {
854 x[self.size - 1]
855 } else {
856 x[idx as usize]
857 }
858 }
859 BoundaryCondition::Dirichlet => {
860 if idx < 0 || idx >= self.size as isize {
861 F::sparse_zero()
862 } else {
863 x[idx as usize]
864 }
865 }
866 BoundaryCondition::Periodic => {
867 let periodic_idx = ((idx % self.size as isize + self.size as isize)
868 % self.size as isize)
869 as usize;
870 x[periodic_idx]
871 }
872 };
873
874 sum += coeff * value;
875 }
876 result[i] = sum;
877 }
878
879 Ok(result)
880 }
881
882 fn has_adjoint(&self) -> bool {
883 true
884 }
885
886 fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
887 self.matvec(x)
890 }
891}
892
893#[allow(dead_code)]
896pub fn enhanced_diagonal<
897 F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
898>(
899 diagonal: Vec<F>,
900) -> Box<dyn LinearOperator<F>> {
901 Box::new(EnhancedDiagonalOperator::new(diagonal))
902}
903
904#[allow(dead_code)]
906pub fn enhanced_add<
907 F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
908>(
909 left: Box<dyn LinearOperator<F>>,
910 right: Box<dyn LinearOperator<F>>,
911) -> SparseResult<Box<dyn LinearOperator<F>>> {
912 Ok(Box::new(EnhancedSumOperator::new(left, right)?))
913}
914
915#[allow(dead_code)]
917pub fn enhanced_subtract<
918 F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
919>(
920 left: Box<dyn LinearOperator<F>>,
921 right: Box<dyn LinearOperator<F>>,
922) -> SparseResult<Box<dyn LinearOperator<F>>> {
923 Ok(Box::new(EnhancedDifferenceOperator::new(left, right)?))
924}
925
926#[allow(dead_code)]
928pub fn enhanced_scale<
929 F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
930>(
931 alpha: F,
932 operator: Box<dyn LinearOperator<F>>,
933) -> Box<dyn LinearOperator<F>> {
934 Box::new(EnhancedScaledOperator::new(alpha, operator))
935}
936
937#[allow(dead_code)]
939pub fn convolution_operator<
940 F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
941>(
942 kernel: Vec<F>,
943 input_size: usize,
944 mode: ConvolutionMode,
945) -> Box<dyn LinearOperator<F>> {
946 Box::new(ConvolutionOperator::new(kernel, input_size, mode))
947}
948
949#[allow(dead_code)]
951pub fn finite_difference_operator<
952 F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
953>(
954 size: usize,
955 order: usize,
956 spacing: F,
957 boundary: BoundaryCondition,
958) -> Box<dyn LinearOperator<F>> {
959 Box::new(FiniteDifferenceOperator::new(
960 size, order, spacing, boundary,
961 ))
962}
963
964pub struct EnhancedCompositionOperator<F> {
966 left: Box<dyn LinearOperator<F>>, right: Box<dyn LinearOperator<F>>, #[allow(dead_code)]
969 options: EnhancedOperatorOptions,
970}
971
972impl<F: Float + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
973 EnhancedCompositionOperator<F>
974{
975 #[allow(dead_code)]
978 pub fn new(
979 left: Box<dyn LinearOperator<F>>,
980 right: Box<dyn LinearOperator<F>>,
981 ) -> SparseResult<Self> {
982 let (left_rows, left_cols) = left.shape();
983 let (right_rows, right_cols) = right.shape();
984
985 if left_cols != right_rows {
986 return Err(SparseError::ShapeMismatch {
987 expected: (left_rows, right_rows),
988 found: (left_rows, left_cols),
989 });
990 }
991
992 Ok(Self {
993 left,
994 right,
995 options: EnhancedOperatorOptions::default(),
996 })
997 }
998
999 #[allow(dead_code)]
1001 pub fn with_options(
1002 left: Box<dyn LinearOperator<F>>,
1003 right: Box<dyn LinearOperator<F>>,
1004 options: EnhancedOperatorOptions,
1005 ) -> SparseResult<Self> {
1006 let (left_rows, left_cols) = left.shape();
1007 let (right_rows, right_cols) = right.shape();
1008
1009 if left_cols != right_rows {
1010 return Err(SparseError::ShapeMismatch {
1011 expected: (left_rows, right_rows),
1012 found: (left_rows, left_cols),
1013 });
1014 }
1015
1016 Ok(Self {
1017 left,
1018 right,
1019 options,
1020 })
1021 }
1022}
1023
1024impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
1025 LinearOperator<F> for EnhancedCompositionOperator<F>
1026{
1027 fn shape(&self) -> (usize, usize) {
1028 let (left_rows, _) = self.left.shape();
1029 let (_, right_cols) = self.right.shape();
1030 (left_rows, right_cols)
1031 }
1032
1033 fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
1034 let intermediate = self.right.matvec(x)?;
1036 self.left.matvec(&intermediate)
1037 }
1038
1039 fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
1040 if !self.left.has_adjoint() || !self.right.has_adjoint() {
1041 return Err(SparseError::OperationNotSupported(
1042 "adjoint not supported for one or both operators".to_string(),
1043 ));
1044 }
1045
1046 let intermediate = self.left.rmatvec(x)?;
1048 self.right.rmatvec(&intermediate)
1049 }
1050
1051 fn has_adjoint(&self) -> bool {
1052 self.left.has_adjoint() && self.right.has_adjoint()
1053 }
1054}
1055
1056pub struct ElementwiseFunctionOperator<F> {
1058 function: Box<dyn Fn(F) -> F + Send + Sync>,
1059 size: usize,
1060 #[allow(dead_code)]
1061 options: EnhancedOperatorOptions,
1062}
1063
1064impl<F: Float + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
1065 ElementwiseFunctionOperator<F>
1066{
1067 pub fn new<Func>(function: Func, size: usize) -> Self
1069 where
1070 Func: Fn(F) -> F + Send + Sync + 'static,
1071 {
1072 Self {
1073 function: Box::new(function),
1074 size,
1075 options: EnhancedOperatorOptions::default(),
1076 }
1077 }
1078
1079 #[allow(dead_code)]
1081 pub fn with_options<Func>(function: Func, size: usize, options: EnhancedOperatorOptions) -> Self
1082 where
1083 Func: Fn(F) -> F + Send + Sync + 'static,
1084 {
1085 Self {
1086 function: Box::new(function),
1087 size,
1088 options,
1089 }
1090 }
1091}
1092
1093impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
1094 LinearOperator<F> for ElementwiseFunctionOperator<F>
1095{
1096 fn shape(&self) -> (usize, usize) {
1097 (self.size, self.size)
1098 }
1099
1100 fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
1101 if x.len() != self.size {
1102 return Err(SparseError::DimensionMismatch {
1103 expected: self.size,
1104 found: x.len(),
1105 });
1106 }
1107
1108 if self.options.use_parallel && x.len() >= self.options.parallel_threshold {
1109 use scirs2_core::parallel_ops::*;
1111 let result = parallel_map(x, |&val| (self.function)(val));
1112 Ok(result)
1113 } else {
1114 let result: Vec<F> = x.iter().map(|&val| (self.function)(val)).collect();
1116 Ok(result)
1117 }
1118 }
1119
1120 fn has_adjoint(&self) -> bool {
1121 false }
1123
1124 fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
1125 Err(SparseError::OperationNotSupported(
1126 "adjoint not supported for general element-wise functions".to_string(),
1127 ))
1128 }
1129}
1130
1131pub struct KroneckerProductOperator<F> {
1133 left: Box<dyn LinearOperator<F>>,
1134 right: Box<dyn LinearOperator<F>>,
1135 #[allow(dead_code)]
1136 options: EnhancedOperatorOptions,
1137}
1138
1139impl<F: Float + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps> KroneckerProductOperator<F> {
1140 #[allow(dead_code)]
1142 pub fn new(left: Box<dyn LinearOperator<F>>, right: Box<dyn LinearOperator<F>>) -> Self {
1143 Self {
1144 left,
1145 right,
1146 options: EnhancedOperatorOptions::default(),
1147 }
1148 }
1149
1150 #[allow(dead_code)]
1152 pub fn with_options(
1153 left: Box<dyn LinearOperator<F>>,
1154 right: Box<dyn LinearOperator<F>>,
1155 options: EnhancedOperatorOptions,
1156 ) -> Self {
1157 Self {
1158 left,
1159 right,
1160 options,
1161 }
1162 }
1163}
1164
1165impl<F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps>
1166 LinearOperator<F> for KroneckerProductOperator<F>
1167{
1168 fn shape(&self) -> (usize, usize) {
1169 let (left_rows, left_cols) = self.left.shape();
1170 let (right_rows, right_cols) = self.right.shape();
1171 (left_rows * right_rows, left_cols * right_cols)
1172 }
1173
1174 fn matvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
1175 let (left_rows, left_cols) = self.left.shape();
1176 let (right_rows, right_cols) = self.right.shape();
1177
1178 if x.len() != left_cols * right_cols {
1179 return Err(SparseError::DimensionMismatch {
1180 expected: left_cols * right_cols,
1181 found: x.len(),
1182 });
1183 }
1184
1185 let mut result = vec![F::sparse_zero(); left_rows * right_rows];
1188
1189 for j in 0..left_cols {
1190 let mut col: Vec<F> = Vec::with_capacity(right_cols);
1192 for i in 0..right_cols {
1193 col.push(x[i * left_cols + j]);
1194 }
1195
1196 let right_result = self.right.matvec(&col)?;
1198
1199 for i in 0..right_rows {
1201 result[i * left_cols + j] = right_result[i];
1202 }
1203 }
1204
1205 let mut final_result = vec![F::sparse_zero(); left_rows * right_rows];
1207 for i in 0..right_rows {
1208 let mut row: Vec<F> = Vec::with_capacity(left_cols);
1210 for j in 0..left_cols {
1211 row.push(result[i * left_cols + j]);
1212 }
1213
1214 let left_result = self.left.matvec(&row)?;
1216
1217 for k in 0..left_rows {
1219 final_result[k * right_rows + i] = left_result[k];
1220 }
1221 }
1222
1223 Ok(final_result)
1224 }
1225
1226 fn has_adjoint(&self) -> bool {
1227 self.left.has_adjoint() && self.right.has_adjoint()
1228 }
1229
1230 fn rmatvec(&self, x: &[F]) -> SparseResult<Vec<F>> {
1231 if !self.has_adjoint() {
1232 return Err(SparseError::OperationNotSupported(
1233 "adjoint not supported for one or both operators".to_string(),
1234 ));
1235 }
1236
1237 Err(SparseError::OperationNotSupported(
1241 "Kronecker product adjoint requires operator cloning which is not currently supported"
1242 .to_string(),
1243 ))
1244 }
1245}
1246
1247#[allow(dead_code)]
1249pub fn enhanced_compose<
1250 F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
1251>(
1252 left: Box<dyn LinearOperator<F>>,
1253 right: Box<dyn LinearOperator<F>>,
1254) -> SparseResult<Box<dyn LinearOperator<F>>> {
1255 Ok(Box::new(EnhancedCompositionOperator::new(left, right)?))
1256}
1257
1258#[allow(dead_code)]
1260pub fn elementwisefunction<
1261 F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
1262 Func: Fn(F) -> F + Send + Sync + 'static,
1263>(
1264 function: Func,
1265 size: usize,
1266) -> Box<dyn LinearOperator<F>> {
1267 Box::new(ElementwiseFunctionOperator::new(function, size))
1268}
1269
1270#[allow(dead_code)]
1272pub fn kronecker_product<
1273 F: Float + SparseElement + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
1274>(
1275 left: Box<dyn LinearOperator<F>>,
1276 right: Box<dyn LinearOperator<F>>,
1277) -> Box<dyn LinearOperator<F>> {
1278 Box::new(KroneckerProductOperator::new(left, right))
1279}
1280
1281#[allow(dead_code)]
1283pub fn adjoint_operator<
1284 F: Float + NumAssign + Sum + Copy + Send + Sync + SimdUnifiedOps + 'static,
1285>(
1286 operator: Box<dyn LinearOperator<F>>,
1287) -> SparseResult<Box<dyn LinearOperator<F>>> {
1288 Ok(Box::new(AdjointOperator::new(operator)?))
1289}
1290
1291#[cfg(test)]
1292mod tests {
1293 use super::*;
1294 use approx::assert_relative_eq;
1295
1296 #[test]
1297 fn test_enhanced_diagonal_operator() {
1298 let diag = vec![2.0, 3.0, 4.0];
1299 let op = EnhancedDiagonalOperator::new(diag);
1300 let x = vec![1.0, 2.0, 3.0];
1301 let y = op.matvec(&x).expect("Operation failed");
1302 assert_eq!(y, vec![2.0, 6.0, 12.0]);
1303 }
1304
1305 #[test]
1306 fn test_enhanced_sum_operator() {
1307 let diag1 = enhanced_diagonal(vec![1.0, 2.0, 3.0]);
1308 let diag2 = enhanced_diagonal(vec![2.0, 1.0, 1.0]);
1309 let sum_op = EnhancedSumOperator::new(diag1, diag2).expect("Operation failed");
1310
1311 let x = vec![1.0, 1.0, 1.0];
1312 let y = sum_op.matvec(&x).expect("Operation failed");
1313 assert_eq!(y, vec![3.0, 3.0, 4.0]); }
1315
1316 #[test]
1317 fn test_enhanced_difference_operator() {
1318 let diag1 = enhanced_diagonal(vec![3.0, 4.0, 5.0]);
1319 let diag2 = enhanced_diagonal(vec![1.0, 2.0, 1.0]);
1320 let diff_op = EnhancedDifferenceOperator::new(diag1, diag2).expect("Operation failed");
1321
1322 let x = vec![1.0, 1.0, 1.0];
1323 let y = diff_op.matvec(&x).expect("Operation failed");
1324 assert_eq!(y, vec![2.0, 2.0, 4.0]); }
1326
1327 #[test]
1328 fn test_enhanced_scaled_operator() {
1329 let diag = enhanced_diagonal(vec![1.0, 2.0, 3.0]);
1330 let scaled_op = EnhancedScaledOperator::new(2.0, diag);
1331
1332 let x = vec![1.0, 1.0, 1.0];
1333 let y = scaled_op.matvec(&x).expect("Operation failed");
1334 assert_eq!(y, vec![2.0, 4.0, 6.0]); }
1336
1337 #[test]
1338 fn test_convolution_operator_full() {
1339 let kernel = vec![1.0, 2.0, 3.0];
1340 let conv_op = ConvolutionOperator::new(kernel, 3, ConvolutionMode::Full);
1341
1342 let x = vec![1.0, 0.0, 0.0];
1343 let y = conv_op.matvec(&x).expect("Operation failed");
1344 assert_eq!(y, vec![1.0, 2.0, 3.0, 0.0, 0.0]); }
1346
1347 #[test]
1348 fn test_convolution_operator_same() {
1349 let kernel = vec![0.0, 1.0, 0.0]; let conv_op = ConvolutionOperator::new(kernel, 3, ConvolutionMode::Same);
1351
1352 let x = vec![1.0, 2.0, 3.0];
1353 let y = conv_op.matvec(&x).expect("Operation failed");
1354 assert_relative_eq!(y[0], 1.0, epsilon = 1e-10);
1355 assert_relative_eq!(y[1], 2.0, epsilon = 1e-10);
1356 assert_relative_eq!(y[2], 3.0, epsilon = 1e-10);
1357 }
1358
1359 #[test]
1360 fn test_finite_difference_first_derivative() {
1361 let fd_op = FiniteDifferenceOperator::new(5, 1, 1.0, BoundaryCondition::Dirichlet);
1362
1363 let x = vec![0.0, 1.0, 2.0, 3.0, 4.0];
1365 let y = fd_op.matvec(&x).expect("Operation failed");
1366
1367 #[allow(clippy::needless_range_loop)]
1369 for i in 1..y.len() - 1 {
1370 assert_relative_eq!(y[i], 1.0, epsilon = 0.1);
1371 }
1372 }
1373
1374 #[test]
1375 fn test_finite_difference_second_derivative() {
1376 let fd_op = FiniteDifferenceOperator::new(5, 2, 1.0, BoundaryCondition::Dirichlet);
1377
1378 let x = vec![0.0, 1.0, 4.0, 9.0, 16.0];
1380 let y = fd_op.matvec(&x).expect("Operation failed");
1381
1382 #[allow(clippy::needless_range_loop)]
1384 for i in 1..y.len() - 1 {
1385 assert_relative_eq!(y[i], 2.0, epsilon = 0.5);
1386 }
1387 }
1388
1389 #[test]
1390 fn test_enhanced_operators_with_large_vectors() {
1391 let large_size = 15000; let diag1: Vec<f64> = (0..large_size).map(|i| (i + 1) as f64).collect();
1394 let diag2: Vec<f64> = vec![2.0; large_size];
1395
1396 let op1 = enhanced_diagonal(diag1);
1397 let op2 = enhanced_diagonal(diag2);
1398 let sum_op = EnhancedSumOperator::new(op1, op2).expect("Operation failed");
1399
1400 let x = vec![1.0; large_size];
1401 let y = sum_op.matvec(&x).expect("Operation failed");
1402
1403 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); }
1408}