scirs2_sparse/
sym_sparray.rs

1//! Symmetric Sparse Array trait
2//!
3//! This module defines a trait for symmetric sparse array implementations.
4//! It extends the base SparseArray trait with methods specific to symmetric
5//! matrices.
6
7use crate::coo_array::CooArray;
8use crate::csr_array::CsrArray;
9use crate::error::{SparseError, SparseResult};
10use crate::sparray::{SparseArray, SparseSum};
11use crate::sym_coo::SymCooArray;
12use crate::sym_csr::SymCsrArray;
13use num_traits::Float;
14use std::fmt::Debug;
15use std::ops::{Add, Div, Mul, Sub};
16
17/// Trait for symmetric sparse arrays
18///
19/// Extends the base SparseArray trait with methods specific to
20/// symmetric matrices. Implementations guarantee that the matrix
21/// is kept symmetric throughout all operations.
22pub trait SymSparseArray<T>: SparseArray<T>
23where
24    T: Float + Debug + Copy + 'static,
25{
26    /// Get the number of stored non-zero elements
27    ///
28    /// For symmetric formats, this returns the number of elements
29    /// actually stored (typically lower or upper triangular part).
30    ///
31    /// # Returns
32    ///
33    /// Number of stored non-zero elements
34    fn nnz_stored(&self) -> usize;
35
36    /// Check if the matrix is guaranteed to be symmetric
37    ///
38    /// For implementations of this trait, this should always return true.
39    /// It's included for consistency with checking functions.
40    ///
41    /// # Returns
42    ///
43    /// Always true for SymSparseArray implementations
44    fn is_symmetric(&self) -> bool {
45        true
46    }
47
48    /// Convert to standard CSR array
49    ///
50    /// # Returns
51    ///
52    /// A standard CSR array with the full symmetric matrix
53    fn to_csr(&self) -> SparseResult<CsrArray<T>>;
54
55    /// Convert to standard COO array
56    ///
57    /// # Returns
58    ///
59    /// A standard COO array with the full symmetric matrix
60    fn to_coo(&self) -> SparseResult<CooArray<T>>;
61
62    /// Convert to symmetric CSR array
63    ///
64    /// # Returns
65    ///
66    /// A symmetric CSR array
67    fn to_sym_csr(&self) -> SparseResult<SymCsrArray<T>>;
68
69    /// Convert to symmetric COO array
70    ///
71    /// # Returns
72    ///
73    /// A symmetric COO array
74    fn to_sym_coo(&self) -> SparseResult<SymCooArray<T>>;
75}
76
77/// Implementation of SymSparseArray for SymCsrArray
78impl<T> SymSparseArray<T> for SymCsrArray<T>
79where
80    T: Float
81        + Debug
82        + Copy
83        + 'static
84        + Add<Output = T>
85        + Sub<Output = T>
86        + Mul<Output = T>
87        + Div<Output = T>,
88{
89    fn nnz_stored(&self) -> usize {
90        self.inner().nnz_stored()
91    }
92
93    fn to_csr(&self) -> SparseResult<CsrArray<T>> {
94        self.to_csr_array()
95    }
96
97    fn to_coo(&self) -> SparseResult<CooArray<T>> {
98        // Extract matrix data
99        let csr_inner = self.inner();
100        let shape = csr_inner.shape;
101
102        // Convert to triplets format for full symmetric matrix
103        let mut rows = Vec::new();
104        let mut cols = Vec::new();
105        let mut data = Vec::new();
106
107        for i in 0..shape.0 {
108            for j_ptr in csr_inner.indptr[i]..csr_inner.indptr[i + 1] {
109                let j = csr_inner.indices[j_ptr];
110                let val = csr_inner.data[j_ptr];
111
112                // Add the element itself
113                rows.push(i);
114                cols.push(j);
115                data.push(val);
116
117                // Add its symmetric pair (if not diagonal)
118                if i != j {
119                    rows.push(j);
120                    cols.push(i);
121                    data.push(val);
122                }
123            }
124        }
125
126        // Create a COO array from the triplets
127        CooArray::from_triplets(&rows, &cols, &data, shape, false)
128    }
129
130    fn to_sym_csr(&self) -> SparseResult<SymCsrArray<T>> {
131        // Already a SymCsrArray, return a clone
132        Ok(self.clone())
133    }
134
135    fn to_sym_coo(&self) -> SparseResult<SymCooArray<T>> {
136        // Convert the internal SymCsrMatrix to SymCooMatrix
137        let csr_inner = self.inner();
138
139        // Extract data from CSR format into COO format (lower triangular part only)
140        let mut data = Vec::new();
141        let mut rows = Vec::new();
142        let mut cols = Vec::new();
143
144        for i in 0..csr_inner.shape.0 {
145            for j in csr_inner.indptr[i]..csr_inner.indptr[i + 1] {
146                let col = csr_inner.indices[j];
147                let val = csr_inner.data[j];
148
149                data.push(val);
150                rows.push(i);
151                cols.push(col);
152            }
153        }
154
155        use crate::sym_coo::SymCooMatrix;
156        let sym_coo = SymCooMatrix::new(data, rows, cols, csr_inner.shape)?;
157
158        Ok(SymCooArray::new(sym_coo))
159    }
160}
161
162/// Implementation of SymSparseArray for SymCooArray
163impl<T> SymSparseArray<T> for SymCooArray<T>
164where
165    T: Float
166        + Debug
167        + Copy
168        + 'static
169        + Add<Output = T>
170        + Sub<Output = T>
171        + Mul<Output = T>
172        + Div<Output = T>,
173{
174    fn nnz_stored(&self) -> usize {
175        self.inner().nnz_stored()
176    }
177
178    fn to_csr(&self) -> SparseResult<CsrArray<T>> {
179        // Convert to full COO, then to CSR
180        let coo = self.to_coo_array()?;
181        match coo.to_csr() {
182            Ok(boxed_csr) => {
183                // Convert Box<dyn SparseArray<T>> to CsrArray<T>
184                match boxed_csr.as_any().downcast_ref::<CsrArray<T>>() {
185                    Some(csr_array) => Ok(csr_array.clone()),
186                    None => Err(SparseError::ConversionError(
187                        "Failed to downcast to CsrArray".to_string(),
188                    )),
189                }
190            }
191            Err(e) => Err(e),
192        }
193    }
194
195    fn to_coo(&self) -> SparseResult<CooArray<T>> {
196        self.to_coo_array()
197    }
198
199    fn to_sym_csr(&self) -> SparseResult<SymCsrArray<T>> {
200        // We already have a symmetric COO matrix with only the lower triangular part
201        // Let's create a CSR matrix directly from it
202        let coo_inner = self.inner();
203
204        // Extract the triplets data
205        let data = coo_inner.data.clone();
206        let rows = coo_inner.rows.clone();
207        let cols = coo_inner.cols.clone();
208        let shape = coo_inner.shape;
209
210        // Create a new CsrMatrix from these triplets
211        let csr = crate::csr::CsrMatrix::new(data, rows, cols, shape)?;
212
213        // Create a symmetric CSR matrix without checking symmetry (we know it's symmetric)
214        use crate::sym_csr::SymCsrMatrix;
215
216        // Extract the lower triangular part (already in the correct format)
217        let mut sym_data = Vec::new();
218        let mut sym_indices = Vec::new();
219        let mut sym_indptr = vec![0];
220        let n = shape.0;
221
222        for i in 0..n {
223            for j_ptr in csr.indptr[i]..csr.indptr[i + 1] {
224                let j = csr.indices[j_ptr];
225                let val = csr.data[j_ptr];
226
227                // Only include elements in lower triangular part (including diagonal)
228                if j <= i {
229                    sym_data.push(val);
230                    sym_indices.push(j);
231                }
232            }
233
234            sym_indptr.push(sym_data.len());
235        }
236
237        let sym_csr = SymCsrMatrix::new(sym_data, sym_indptr, sym_indices, shape)?;
238
239        Ok(SymCsrArray::new(sym_csr))
240    }
241
242    fn to_sym_coo(&self) -> SparseResult<SymCooArray<T>> {
243        // Already a SymCooArray, return a clone
244        Ok(self.clone())
245    }
246}
247
248/// Implementation of SparseArray for SymCsrArray
249impl<T> SparseArray<T> for SymCsrArray<T>
250where
251    T: Float
252        + Debug
253        + Copy
254        + 'static
255        + Add<Output = T>
256        + Sub<Output = T>
257        + Mul<Output = T>
258        + Div<Output = T>,
259{
260    fn to_coo(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
261        let coo_array = <Self as SymSparseArray<T>>::to_coo(self)?;
262        Ok(Box::new(coo_array))
263    }
264
265    fn to_csr(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
266        // Convert to CsrArray (full matrix)
267        let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
268        Ok(Box::new(csr))
269    }
270    fn shape(&self) -> (usize, usize) {
271        self.inner().shape()
272    }
273
274    fn nnz(&self) -> usize {
275        self.inner().nnz()
276    }
277
278    fn dtype(&self) -> &str {
279        if std::any::TypeId::of::<T>() == std::any::TypeId::of::<f32>() {
280            "f32"
281        } else {
282            "f64"
283        }
284    }
285
286    fn get(&self, row: usize, col: usize) -> T {
287        self.inner().get(row, col)
288    }
289
290    fn to_array(&self) -> ndarray::Array2<T> {
291        // Convert to dense vector of vectors, then to ndarray
292        let dense = self.inner().to_dense();
293        let mut array = ndarray::Array2::zeros(self.shape());
294
295        for i in 0..dense.len() {
296            for j in 0..dense[i].len() {
297                array[[i, j]] = dense[i][j];
298            }
299        }
300
301        array
302    }
303
304    fn toarray(&self) -> ndarray::Array2<T> {
305        self.to_array()
306    }
307
308    fn set(&mut self, _i: usize, _j: usize, _value: T) -> SparseResult<()> {
309        Err(SparseError::NotImplemented(
310            "Setting individual elements in SymCsrArray is not supported. Convert to another format first.".to_string()
311        ))
312    }
313
314    fn dot_vector(&self, other: &ndarray::ArrayView1<T>) -> SparseResult<ndarray::Array1<T>> {
315        // Use optimized symmetric matrix-vector product
316        crate::sym_ops::sym_csr_matvec(self.inner(), other)
317    }
318
319    fn copy(&self) -> Box<dyn SparseArray<T>> {
320        Box::new(self.clone())
321    }
322
323    fn sub(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
324        // Subtraction will preserve symmetry if other is also symmetric
325        // For simplicity, we'll convert to CSR, perform subtraction, and convert back
326        let self_csr = <Self as SymSparseArray<T>>::to_csr(self)?;
327        let result = self_csr.sub(other)?;
328
329        // For now, we return the CSR result without converting back
330        Ok(result)
331    }
332
333    fn div(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
334        // Division may not preserve symmetry, so we'll just return a CSR result
335        let self_csr = <Self as SymSparseArray<T>>::to_csr(self)?;
336        self_csr.div(other)
337    }
338
339    fn eliminate_zeros(&mut self) {
340        // No-op for SymCsrArray as it already maintains minimal storage
341    }
342
343    fn sort_indices(&mut self) {
344        // No-op for SymCsrArray as indices are already sorted
345    }
346
347    fn sorted_indices(&self) -> Box<dyn SparseArray<T>> {
348        // CSR format typically maintains sorted indices, so return a clone
349        Box::new(self.clone())
350    }
351
352    fn has_sorted_indices(&self) -> bool {
353        true
354    }
355
356    fn sum(&self, axis: Option<usize>) -> SparseResult<SparseSum<T>> {
357        // Convert to CSR and use its implementation
358        let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
359        SparseArray::<T>::sum(&csr, axis)
360    }
361
362    fn max(&self) -> T {
363        // Convert to CSR and find the maximum value
364        match <Self as SymSparseArray<T>>::to_csr(self) {
365            Ok(csr) => SparseArray::<T>::max(&csr),
366            Err(_) => T::nan(), // Return NaN if conversion fails
367        }
368    }
369
370    fn min(&self) -> T {
371        // Convert to CSR and find the minimum value
372        match <Self as SymSparseArray<T>>::to_csr(self) {
373            Ok(csr) => SparseArray::<T>::min(&csr),
374            Err(_) => T::nan(), // Return NaN if conversion fails
375        }
376    }
377
378    fn slice(
379        &self,
380        rows: (usize, usize),
381        cols: (usize, usize),
382    ) -> SparseResult<Box<dyn SparseArray<T>>> {
383        // Slicing a symmetric matrix may not preserve symmetry
384        // Convert to CSR and use its implementation
385        let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
386        csr.slice(rows, cols)
387    }
388
389    fn as_any(&self) -> &dyn std::any::Any {
390        self
391    }
392
393    fn find(
394        &self,
395    ) -> (
396        ndarray::Array1<usize>,
397        ndarray::Array1<usize>,
398        ndarray::Array1<T>,
399    ) {
400        // To get the full matrix coordinates and values, we need to convert to a full CSR matrix
401        match <Self as SymSparseArray<T>>::to_csr(self) {
402            Ok(csr) => csr.find(),
403            Err(_) => (
404                ndarray::Array1::from_vec(Vec::new()),
405                ndarray::Array1::from_vec(Vec::new()),
406                ndarray::Array1::from_vec(Vec::new()),
407            ),
408        }
409    }
410
411    fn add(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
412        // First check shapes are compatible
413        let self_shape = self.shape();
414        let other_shape = other.shape();
415
416        if self_shape != other_shape {
417            return Err(crate::error::SparseError::DimensionMismatch {
418                expected: self_shape.0,
419                found: other_shape.0,
420            });
421        }
422
423        // Convert both to CSR to perform addition
424        let self_csr = <Self as SymSparseArray<T>>::to_csr(self)?;
425        let other_csr_box = other.to_csr()?;
426
427        // We need to unbox the other_csr to use it directly
428        match other_csr_box.as_any().downcast_ref::<CsrArray<T>>() {
429            Some(other_csr) => {
430                // Add as standard CSR arrays
431                let result = self_csr.add(other_csr)?;
432
433                // Since we expect result to be symmetric (if the other was symmetric),
434                // we can try to convert it back to a SymCsrArray
435
436                // First convert the result to CSR Matrix
437                use crate::csr::CsrMatrix;
438
439                // We need to get the data from the result
440                let (rows, cols, data) = result.find();
441                let csr_matrix =
442                    CsrMatrix::new(data.to_vec(), rows.to_vec(), cols.to_vec(), result.shape())?;
443
444                // Convert to SymCsrMatrix
445                use crate::sym_csr::SymCsrMatrix;
446                let sym_csr = SymCsrMatrix::from_csr(&csr_matrix)?;
447
448                // Create and return SymCsrArray
449                let sym_csr_array = SymCsrArray::new(sym_csr);
450                Ok(Box::new(sym_csr_array) as Box<dyn SparseArray<T>>)
451            }
452            None => {
453                // If we can't downcast, convert both to dense arrays and add them
454                let self_dense = self.to_array();
455                let other_dense = other.to_array();
456
457                // Create the result dense array
458                let mut result_dense = ndarray::Array2::zeros(self_shape);
459                for i in 0..self_shape.0 {
460                    for j in 0..self_shape.1 {
461                        result_dense[[i, j]] = self_dense[[i, j]] + other_dense[[i, j]];
462                    }
463                }
464
465                // Convert back to CSR
466                // Convert result_dense to triplets
467                let mut rows = Vec::new();
468                let mut cols = Vec::new();
469                let mut values = Vec::new();
470
471                for i in 0..self_shape.0 {
472                    for j in 0..self_shape.1 {
473                        let val = result_dense[[i, j]];
474                        if val != T::zero() {
475                            rows.push(i);
476                            cols.push(j);
477                            values.push(val);
478                        }
479                    }
480                }
481
482                let csr = CsrArray::from_triplets(&rows, &cols, &values, self_shape, false)?;
483                Ok(Box::new(csr) as Box<dyn SparseArray<T>>)
484            }
485        }
486    }
487
488    fn mul(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
489        // First check shapes are compatible
490        let self_shape = self.shape();
491        let other_shape = other.shape();
492
493        if self_shape != other_shape {
494            return Err(crate::error::SparseError::DimensionMismatch {
495                expected: self_shape.0,
496                found: other_shape.0,
497            });
498        }
499
500        // Convert both to CSR to perform multiplication
501        let self_csr = <Self as SymSparseArray<T>>::to_csr(self)?;
502        let other_csr_box = other.to_csr()?;
503
504        // We need to unbox the other_csr to use it directly
505        match other_csr_box.as_any().downcast_ref::<CsrArray<T>>() {
506            Some(other_csr) => {
507                // Multiply as standard CSR arrays (element-wise)
508                let result = self_csr.mul(other_csr)?;
509
510                // Element-wise multiplication of symmetric matrices preserves symmetry,
511                // so we can convert back to SymCsrArray
512
513                // We need to get the data from the result
514                let (rows, cols, data) = result.find();
515
516                // Convert to CsrMatrix
517                use crate::csr::CsrMatrix;
518                let csr_matrix =
519                    CsrMatrix::new(data.to_vec(), rows.to_vec(), cols.to_vec(), result.shape())?;
520
521                // Convert to SymCsrMatrix
522                use crate::sym_csr::SymCsrMatrix;
523                let sym_csr = SymCsrMatrix::from_csr(&csr_matrix)?;
524
525                // Create and return SymCsrArray
526                let sym_csr_array = SymCsrArray::new(sym_csr);
527                Ok(Box::new(sym_csr_array) as Box<dyn SparseArray<T>>)
528            }
529            None => {
530                // If we can't downcast, convert both to dense arrays and multiply them
531                let self_dense = self.to_array();
532                let other_dense = other.to_array();
533
534                // Create the result dense array
535                let mut result_dense = ndarray::Array2::zeros(self_shape);
536                for i in 0..self_shape.0 {
537                    for j in 0..self_shape.1 {
538                        result_dense[[i, j]] = self_dense[[i, j]] * other_dense[[i, j]];
539                    }
540                }
541
542                // Convert back to CSR
543                // Convert result_dense to triplets
544                let mut rows = Vec::new();
545                let mut cols = Vec::new();
546                let mut values = Vec::new();
547
548                for i in 0..self_shape.0 {
549                    for j in 0..self_shape.1 {
550                        let val = result_dense[[i, j]];
551                        if val != T::zero() {
552                            rows.push(i);
553                            cols.push(j);
554                            values.push(val);
555                        }
556                    }
557                }
558
559                let csr = CsrArray::from_triplets(&rows, &cols, &values, self_shape, false)?;
560                Ok(Box::new(csr) as Box<dyn SparseArray<T>>)
561            }
562        }
563    }
564
565    fn dot(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
566        // For matrix multiplication, symmetry is not generally preserved
567        // So we just convert to standard CSR, perform the operation, and return a CSR array
568
569        let self_csr = <Self as SymSparseArray<T>>::to_csr(self)?;
570        let result = self_csr.dot(other)?;
571
572        // For dot product of a symmetric matrix with itself, the result is symmetric
573        // We could check for this case and return a SymCsrArray, but for now we'll
574        // keep it simple and just return the CSR array
575        Ok(result)
576    }
577
578    fn transpose(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
579        // For symmetric matrices, transpose is the same as the original
580        Ok(Box::new(self.clone()))
581    }
582
583    fn to_csc(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
584        // Convert to CSR, then to CSC
585        let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
586        let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
587        csr_box.to_csc()
588    }
589
590    fn to_dia(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
591        // Convert to CSR, then to DIA
592        let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
593        let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
594        csr_box.to_dia()
595    }
596
597    fn to_dok(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
598        // Convert to CSR, then to DOK
599        let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
600        let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
601        csr_box.to_dok()
602    }
603
604    fn to_lil(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
605        // Convert to CSR, then to LIL
606        let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
607        let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
608        csr_box.to_lil()
609    }
610
611    fn to_bsr(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
612        // Convert to CSR, then to BSR
613        let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
614        let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
615        csr_box.to_bsr()
616    }
617}
618
619/// Implementation of SparseArray for SymCooArray
620impl<T> SparseArray<T> for SymCooArray<T>
621where
622    T: Float
623        + Debug
624        + Copy
625        + 'static
626        + Add<Output = T>
627        + Sub<Output = T>
628        + Mul<Output = T>
629        + Div<Output = T>,
630{
631    fn to_coo(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
632        let coo_array = <Self as SymSparseArray<T>>::to_coo(self)?;
633        Ok(Box::new(coo_array))
634    }
635
636    fn to_csr(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
637        // Convert to CsrArray (full matrix)
638        let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
639        Ok(Box::new(csr))
640    }
641    fn shape(&self) -> (usize, usize) {
642        self.inner().shape()
643    }
644
645    fn nnz(&self) -> usize {
646        self.inner().nnz()
647    }
648
649    fn dtype(&self) -> &str {
650        if std::any::TypeId::of::<T>() == std::any::TypeId::of::<f32>() {
651            "f32"
652        } else {
653            "f64"
654        }
655    }
656
657    fn get(&self, row: usize, col: usize) -> T {
658        self.inner().get(row, col)
659    }
660
661    fn to_array(&self) -> ndarray::Array2<T> {
662        // Convert to dense vector of vectors, then to ndarray
663        let dense = self.inner().to_dense();
664        let mut array = ndarray::Array2::zeros(self.shape());
665
666        for i in 0..dense.len() {
667            for j in 0..dense[i].len() {
668                array[[i, j]] = dense[i][j];
669            }
670        }
671
672        array
673    }
674
675    fn toarray(&self) -> ndarray::Array2<T> {
676        self.to_array()
677    }
678
679    fn set(&mut self, _i: usize, _j: usize, _value: T) -> SparseResult<()> {
680        Err(SparseError::NotImplemented(
681            "Setting individual elements in SymCooArray is not supported. Convert to another format first.".to_string()
682        ))
683    }
684
685    fn dot_vector(&self, other: &ndarray::ArrayView1<T>) -> SparseResult<ndarray::Array1<T>> {
686        // Use optimized symmetric matrix-vector product
687        crate::sym_ops::sym_coo_matvec(self.inner(), other)
688    }
689
690    fn copy(&self) -> Box<dyn SparseArray<T>> {
691        Box::new(self.clone())
692    }
693
694    fn sub(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
695        // For simplicity, we'll use the CSR implementation
696        let self_csr = <Self as SymSparseArray<T>>::to_csr(self)?;
697        self_csr.sub(other)
698    }
699
700    fn div(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
701        // For simplicity, we'll use the CSR implementation
702        let self_csr = <Self as SymSparseArray<T>>::to_csr(self)?;
703        self_csr.div(other)
704    }
705
706    fn eliminate_zeros(&mut self) {
707        // Not implemented for SymCooArray
708        // Could be implemented by filtering out zero values in the future
709    }
710
711    fn sort_indices(&mut self) {
712        // Not implemented for SymCooArray
713        // Could be implemented by sorting indices by row, then column in the future
714    }
715
716    fn sorted_indices(&self) -> Box<dyn SparseArray<T>> {
717        // Convert to SymCsrArray which has sorted indices
718        match self.to_sym_csr() {
719            Ok(csr) => Box::new(csr),
720            Err(_) => Box::new(self.clone()), // Return self if conversion fails
721        }
722    }
723
724    fn has_sorted_indices(&self) -> bool {
725        false
726    }
727
728    fn sum(&self, axis: Option<usize>) -> SparseResult<SparseSum<T>> {
729        // Convert to CSR and use its implementation
730        let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
731        SparseArray::<T>::sum(&csr, axis)
732    }
733
734    fn max(&self) -> T {
735        // Convert to CSR and find the maximum value
736        match <Self as SymSparseArray<T>>::to_csr(self) {
737            Ok(csr) => SparseArray::<T>::max(&csr),
738            Err(_) => T::nan(), // Return NaN if conversion fails
739        }
740    }
741
742    fn min(&self) -> T {
743        // Convert to CSR and find the minimum value
744        match <Self as SymSparseArray<T>>::to_csr(self) {
745            Ok(csr) => SparseArray::<T>::min(&csr),
746            Err(_) => T::nan(), // Return NaN if conversion fails
747        }
748    }
749
750    fn slice(
751        &self,
752        rows: (usize, usize),
753        cols: (usize, usize),
754    ) -> SparseResult<Box<dyn SparseArray<T>>> {
755        // Convert to CSR and use its implementation
756        let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
757        csr.slice(rows, cols)
758    }
759
760    fn as_any(&self) -> &dyn std::any::Any {
761        self
762    }
763
764    fn find(
765        &self,
766    ) -> (
767        ndarray::Array1<usize>,
768        ndarray::Array1<usize>,
769        ndarray::Array1<T>,
770    ) {
771        // To get the full matrix coordinates and values, we need to convert to a full COO matrix
772        match <Self as SymSparseArray<T>>::to_coo(self) {
773            Ok(coo) => coo.find(),
774            Err(_) => (
775                ndarray::Array1::from_vec(Vec::new()),
776                ndarray::Array1::from_vec(Vec::new()),
777                ndarray::Array1::from_vec(Vec::new()),
778            ),
779        }
780    }
781
782    fn add(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
783        // Convert to SymCsrArray and use its implementation
784        self.to_sym_csr()?.add(other)
785    }
786
787    fn mul(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
788        // Convert to SymCsrArray and use its implementation
789        self.to_sym_csr()?.mul(other)
790    }
791
792    fn dot(&self, other: &dyn SparseArray<T>) -> SparseResult<Box<dyn SparseArray<T>>> {
793        // Convert to CSR for dot product
794        <Self as SymSparseArray<T>>::to_csr(self)?.dot(other)
795    }
796
797    fn transpose(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
798        // For symmetric matrices, transpose is the same as the original
799        Ok(Box::new(self.clone()))
800    }
801
802    fn to_csc(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
803        // Convert to CSR, then to CSC
804        let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
805        let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
806        csr_box.to_csc()
807    }
808
809    fn to_dia(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
810        // Convert to CSR, then to DIA
811        let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
812        let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
813        csr_box.to_dia()
814    }
815
816    fn to_dok(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
817        // Convert to CSR, then to DOK
818        let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
819        let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
820        csr_box.to_dok()
821    }
822
823    fn to_lil(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
824        // Convert to CSR, then to LIL
825        let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
826        let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
827        csr_box.to_lil()
828    }
829
830    fn to_bsr(&self) -> SparseResult<Box<dyn SparseArray<T>>> {
831        // Convert to CSR, then to BSR
832        let csr = <Self as SymSparseArray<T>>::to_csr(self)?;
833        let csr_box: Box<dyn SparseArray<T>> = Box::new(csr);
834        csr_box.to_bsr()
835    }
836}
837
838#[cfg(test)]
839mod tests {
840    use super::*;
841    use crate::sym_csr::{SymCsrArray, SymCsrMatrix};
842
843    // Create a simple symmetric matrix for testing
844    fn create_test_sym_csr() -> SymCsrArray<f64> {
845        // Create a simple symmetric matrix in CSR format
846        // [2 1 0]
847        // [1 2 3]
848        // [0 3 1]
849
850        // Lower triangular part only:
851        // [2 0 0]
852        // [1 2 0]
853        // [0 3 1]
854
855        let data = vec![2.0, 1.0, 2.0, 3.0, 1.0];
856        let indices = vec![0, 0, 1, 1, 2];
857        let indptr = vec![0, 1, 3, 5];
858
859        let sym_matrix = SymCsrMatrix::new(data, indptr, indices, (3, 3)).unwrap();
860        SymCsrArray::new(sym_matrix)
861    }
862
863    #[test]
864    fn test_sym_sparse_array_trait() {
865        let sym_csr = create_test_sym_csr();
866
867        // Test basic properties
868        assert_eq!(sym_csr.shape(), (3, 3));
869        assert!(sym_csr.is_symmetric());
870
871        // Test SparseArray methods
872        assert_eq!(sym_csr.get(0, 0), 2.0);
873        assert_eq!(sym_csr.get(0, 1), 1.0);
874        assert_eq!(sym_csr.get(1, 0), 1.0); // Through symmetry
875
876        // Test nnz_stored vs nnz
877        assert_eq!(sym_csr.nnz_stored(), 5); // Only stored elements
878        assert_eq!(sym_csr.nnz(), 7); // Including symmetric pairs
879
880        // Test conversion between formats
881        let sym_coo = sym_csr.to_sym_coo().unwrap();
882        assert_eq!(sym_coo.shape(), (3, 3));
883        assert!(sym_coo.is_symmetric());
884
885        let csr = SymSparseArray::<f64>::to_csr(&sym_csr).unwrap();
886        assert_eq!(csr.shape(), (3, 3));
887
888        let coo = SymSparseArray::<f64>::to_coo(&sym_csr).unwrap();
889        assert_eq!(coo.shape(), (3, 3));
890
891        // Test that find() returns the full matrix elements
892        let (rows, _cols, _data) = sym_csr.find();
893        assert!(rows.len() > sym_csr.nnz_stored()); // Should include symmetric pairs
894    }
895
896    #[test]
897    fn test_sym_sparse_array_operations() {
898        let sym_csr = create_test_sym_csr();
899
900        // Create another symmetric matrix for testing operations
901        let sym_csr2 = create_test_sym_csr();
902
903        // Test addition
904        let sum = sym_csr.add(&sym_csr2).unwrap();
905        assert_eq!(sum.shape(), (3, 3));
906        assert_eq!(sum.get(0, 0), 4.0); // 2 + 2
907        assert_eq!(sum.get(0, 1), 2.0); // 1 + 1
908        assert_eq!(sum.get(1, 0), 2.0); // 1 + 1 (symmetric)
909
910        // Test element-wise multiplication
911        let prod = sym_csr.mul(&sym_csr2).unwrap();
912        assert_eq!(prod.shape(), (3, 3));
913        assert_eq!(prod.get(0, 0), 4.0); // 2 * 2
914        assert_eq!(prod.get(0, 1), 1.0); // 1 * 1
915        assert_eq!(prod.get(1, 0), 1.0); // 1 * 1 (symmetric)
916
917        // Test matrix multiplication
918        let dot = sym_csr.dot(&sym_csr2).unwrap();
919        assert_eq!(dot.shape(), (3, 3));
920
921        // Test transpose (should be no change for symmetric matrices)
922        let trans = sym_csr.transpose().unwrap();
923        assert_eq!(trans.shape(), sym_csr.shape());
924        assert_eq!(SparseArray::get(&*trans, 0, 1), sym_csr.get(0, 1));
925        assert_eq!(SparseArray::get(&*trans, 1, 0), sym_csr.get(1, 0));
926    }
927}