igraph/matrix.rs
1//! Owned, column-major igraph matrices.
2//!
3//! [`Matrix`] (`igraph_matrix_t`, reals), [`MatrixInt`] (`igraph_matrix_int_t`),
4//! [`MatrixBool`] (`igraph_matrix_bool_t`), [`MatrixChar`]
5//! (`igraph_matrix_char_t`) and [`MatrixComplex`] (`igraph_matrix_complex_t`)
6//! are the C structs themselves,
7//! enriched with Rusty behaviour: they own their storage (freed on [`Drop`]),
8//! are indexed with `m[(row, col)]`, and convert from and to row-major
9//! `Vec<Vec<T>>`. The elements are stored in **column-major** order, as in
10//! igraph, and [`as_slice`](Matrix::as_slice) exposes them in that order.
11//!
12//! ```
13//! use igraph::prelude::*;
14//!
15//! let mut m = Matrix::from_rows(&[vec![1.0, 2.0], vec![3.0, 4.0], vec![5.0, 6.0]]).unwrap();
16//! assert_eq!((m.nrow(), m.ncol()), (3, 2));
17//! assert_eq!(m[(2, 1)], 6.0);
18//! m[(0, 0)] = 10.0;
19//! assert_eq!(m.as_slice(), &[10.0, 3.0, 5.0, 2.0, 4.0, 6.0]); // column-major
20//! assert_eq!(m.row(1), vec![3.0, 4.0]);
21//! assert_eq!(m.column(1), &[2.0, 4.0, 6.0]);
22//! ```
23//!
24//! The module covers `igraph_matrix_pmt.h`:
25//!
26//! | Group | Methods | Types |
27//! |-------|---------|-------|
28//! | construction | `zeros`, `new`, `from_rows`, `from_row_major`, `from_column_major`, `view` (zero-copy input), `identity` (real) | all |
29//! | access | `[(i, j)]`, `get`, `row`, `column`, `rows`, `columns`, `diagonal`, `indexed_iter`, `to_rows`, `as_slice` | all |
30//! | structure | `set_row`, `set_col`, `select_rows`, `select_cols`, `select_rows_cols`, `swap_rows`, `swap_cols`, `transpose`, `transposed`, `rbind`, `cbind`, `add_rows`, `add_cols`, `remove_row`, `remove_col`, `resize` | all |
31//! | queries | `is_symmetric`, `contains`, `search`, `fill`, `capacity`, `shrink_to_fit` | all |
32//! | order | `min`, `max`, `which_min`, `which_max`, `minmax`, `maxdifference`, `all_l`, `all_g`, `all_le`, `all_ge` | real, int, char |
33//! | arithmetic | `add`, `sub`, `mul_elements`, `div_elements`, `scale`, `add_constant`, `sum`, `prod`, `rowsums`, `colsums` | real, int, complex |
34//! | real only | `identity`, `matmul`, `mul_vec`, `all_almost_e`, `zapsmall` | real |
35//! | complex only | `from_parts`, `from_polar`, `real`, `imag`, `realimag`, `all_almost_e`, `zapsmall` | complex |
36//! | bool only | `count_true` | bool |
37//!
38//! Integer arithmetic is done in Rust with wrapping semantics (signed
39//! overflow and division by zero are undefined behaviour in the C
40//! implementation); char matrices have no arithmetic. Every index is checked
41//! before reaching igraph, which does not check them.
42//!
43//! ```
44//! use igraph::prelude::*;
45//!
46//! // The transition matrix of a random walk on the path 0 - 1 - 2.
47//! let path = Graph::ring(3, false, false, false).unwrap();
48//! let adj = path.get_adjacency(GetAdjacency::Both, None, Loops::Twice).unwrap();
49//! assert_eq!(adj, Matrix::from_rows(&[[0.0, 1.0, 0.0], [1.0, 0.0, 1.0], [0.0, 1.0, 0.0]]).unwrap());
50//! assert!(adj.is_symmetric());
51//! // Row sums of the adjacency matrix are the degrees...
52//! let degrees = adj.rowsums();
53//! assert_eq!(degrees, vec![1.0, 2.0, 1.0]);
54//! // ...and dividing each row by them gives the row-stochastic matrix.
55//! let mut p = adj.clone();
56//! for (i, d) in degrees.iter().enumerate() {
57//! let row: Vec<f64> = p.row(i).iter().map(|x| x / d).collect();
58//! p.set_row(i, &row).unwrap();
59//! }
60//! assert_eq!(p, path.get_stochastic(false, None).unwrap());
61//! assert_eq!(p.rowsums(), vec![1.0, 1.0, 1.0]);
62//! // Two steps from the middle vertex bring the walker back with probability 1.
63//! assert_eq!(p.matmul(&p).unwrap()[(1, 1)], 1.0);
64//! ```
65//!
66//! See also [`Graph::get_adjacency`](crate::Graph::get_adjacency) and
67//! [`Graph::adjacency`](crate::Graph::adjacency) to convert between graphs
68//! and matrices, [`MatrixList`](crate::list::MatrixList) for lists of
69//! matrices, [`linalg`](crate::linalg) for eigenvalue problems, BLAS/LAPACK
70//! routines and sparse matrices, and [`layout`](crate::layout), whose
71//! layouts are `n × 2` (or `n × 3`) coordinate matrices.
72
73use crate::{
74 error::{Error, Result},
75 ffi::*,
76 vector::{Vector, VectorBool, VectorChar, VectorComplex, VectorInt},
77};
78use std::{
79 fmt,
80 mem::MaybeUninit,
81 ops::{Index, IndexMut},
82};
83
84/// Owned column-major matrix of reals (`igraph_matrix_t`), see the [module docs](self).
85pub type Matrix = igraph_matrix_t;
86/// Owned column-major matrix of integers (`igraph_matrix_int_t`), see the [module docs](self).
87pub type MatrixInt = igraph_matrix_int_t;
88/// Owned column-major matrix of booleans (`igraph_matrix_bool_t`), see the [module docs](self).
89pub type MatrixBool = igraph_matrix_bool_t;
90/// Owned column-major matrix of chars (`igraph_matrix_char_t`), see the [module docs](self).
91pub type MatrixChar = igraph_matrix_char_t;
92/// Owned column-major matrix of complex numbers (`igraph_matrix_complex_t`),
93/// see the [module docs](self); igraph returns such matrices e.g. as the
94/// eigenvectors of non-symmetric matrices.
95pub type MatrixComplex = igraph_matrix_complex_t;
96
97/// How a matrix element is written by the matrices' `Display`.
98trait Cell {
99 fn cell(&self) -> String;
100}
101
102macro_rules! debug_cell {
103 ($($t:ty),*) => {$(
104 impl Cell for $t {
105 fn cell(&self) -> String {
106 format!("{self:?}")
107 }
108 }
109 )*};
110}
111debug_cell!(igraph_real_t, igraph_int_t, igraph_bool_t, std::ffi::c_char);
112
113impl Cell for igraph_complex_t {
114 fn cell(&self) -> String {
115 self.to_string()
116 }
117}
118
119macro_rules! impl_matrix {
120 ($ty:ident, $elem:ty, init = $init:ident, init_copy = $init_copy:ident,
121 destroy = $destroy:ident, resize = $resize:ident) => {
122 impl $ty {
123 /// Creates a `nrow` × `ncol` matrix filled with zeros
124 /// ([`igraph_matrix_init`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_init)).
125 ///
126 /// # Panics
127 /// If igraph cannot allocate the matrix, or the size overflows,
128 /// like [`Vec::with_capacity`].
129 pub fn zeros(nrow: usize, ncol: usize) -> Self {
130 crate::error::ensure_init();
131 let mut raw = MaybeUninit::<Self>::uninit();
132 crate::error::check(unsafe {
133 $init(
134 raw.as_mut_ptr(),
135 crate::error::int_size(nrow),
136 crate::error::int_size(ncol),
137 )
138 })
139 .expect("igraph failed to allocate a matrix");
140 unsafe { raw.assume_init() }
141 }
142
143 /// Creates an empty 0 × 0 matrix.
144 pub fn new() -> Self {
145 Self::zeros(0, 0)
146 }
147
148 /// Builds a matrix from its elements given in column-major order
149 /// (igraph's storage order, see
150 /// [`igraph_matrix_init_array`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_init_array)).
151 ///
152 /// # Errors
153 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue)
154 /// if `data.len() != nrow * ncol`.
155 pub fn from_column_major(nrow: usize, ncol: usize, data: &[$elem]) -> Result<Self> {
156 if nrow.checked_mul(ncol) != Some(data.len()) {
157 return Err(Error::invalid(format!(
158 "{} elements given for a {nrow}x{ncol} matrix",
159 data.len()
160 )));
161 }
162 let mut m = Self::zeros(nrow, ncol);
163 m.as_mut_slice().copy_from_slice(data);
164 Ok(m)
165 }
166
167 /// Builds a matrix from its elements given in row-major order.
168 ///
169 /// # Errors
170 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue)
171 /// if `data.len() != nrow * ncol`.
172 pub fn from_row_major(nrow: usize, ncol: usize, data: &[$elem]) -> Result<Self> {
173 if nrow.checked_mul(ncol) != Some(data.len()) {
174 return Err(Error::invalid(format!(
175 "{} elements given for a {nrow}x{ncol} matrix",
176 data.len()
177 )));
178 }
179 let mut m = Self::zeros(nrow, ncol);
180 for i in 0..nrow {
181 for j in 0..ncol {
182 m[(i, j)] = data[i * ncol + j];
183 }
184 }
185 Ok(m)
186 }
187
188 /// Builds a matrix from a slice of rows (a `0 × 0` matrix for no
189 /// rows).
190 ///
191 /// # Errors
192 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue)
193 /// if the rows do not all have the same length.
194 pub fn from_rows<R: AsRef<[$elem]>>(rows: &[R]) -> Result<Self> {
195 let nrow = rows.len();
196 let ncol = rows.first().map_or(0, |r| r.as_ref().len());
197 let mut m = Self::zeros(nrow, ncol);
198 for (i, row) in rows.iter().enumerate() {
199 let row = row.as_ref();
200 if row.len() != ncol {
201 return Err(Error::invalid(format!(
202 "row {i} has {} elements, expected {ncol}",
203 row.len()
204 )));
205 }
206 for (j, &x) in row.iter().enumerate() {
207 m[(i, j)] = x;
208 }
209 }
210 Ok(m)
211 }
212
213 /// Number of rows ([`igraph_matrix_nrow`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_nrow)).
214 pub fn nrow(&self) -> usize {
215 self.nrow as usize
216 }
217
218 /// Number of columns ([`igraph_matrix_ncol`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_ncol)).
219 pub fn ncol(&self) -> usize {
220 self.ncol as usize
221 }
222
223 /// `(nrow, ncol)`.
224 pub fn shape(&self) -> (usize, usize) {
225 (self.nrow(), self.ncol())
226 }
227
228 /// Whether the matrix has no elements.
229 pub fn is_empty(&self) -> bool {
230 self.nrow() * self.ncol() == 0
231 }
232
233 /// All the elements, in column-major order.
234 pub fn as_slice(&self) -> &[$elem] {
235 self.data.as_slice()
236 }
237
238 /// All the elements, in column-major order, mutably.
239 pub fn as_mut_slice(&mut self) -> &mut [$elem] {
240 self.data.as_mut_slice()
241 }
242
243 /// The element at `(row, col)`, if in bounds.
244 pub fn get(&self, row: usize, col: usize) -> Option<&$elem> {
245 if row < self.nrow() && col < self.ncol() {
246 Some(&self.as_slice()[col * self.nrow() + row])
247 } else {
248 None
249 }
250 }
251
252 /// The `j`-th column, as a slice (columns are contiguous in
253 /// column-major storage).
254 ///
255 /// # Panics
256 /// If `j >= ncol`.
257 pub fn column(&self, j: usize) -> &[$elem] {
258 let n = self.nrow();
259 &self.as_slice()[j * n..(j + 1) * n]
260 }
261
262 /// A copy of the `i`-th row.
263 ///
264 /// # Panics
265 /// If `i >= nrow`.
266 pub fn row(&self, i: usize) -> Vec<$elem> {
267 assert!(i < self.nrow(), "row index {i} out of bounds");
268 (0..self.ncol()).map(|j| self[(i, j)]).collect()
269 }
270
271 /// Iterates over copies of the rows.
272 pub fn rows(&self) -> impl Iterator<Item = Vec<$elem>> + '_ {
273 (0..self.nrow()).map(move |i| self.row(i))
274 }
275
276 /// Iterates over the columns, as slices.
277 pub fn columns(&self) -> impl Iterator<Item = &[$elem]> + '_ {
278 (0..self.ncol()).map(move |j| self.column(j))
279 }
280
281 /// Converts into a row-major `Vec` of rows.
282 pub fn to_rows(&self) -> Vec<Vec<$elem>> {
283 self.rows().collect()
284 }
285
286 /// Returns the transposed matrix.
287 pub fn transposed(&self) -> Self {
288 let mut t = Self::zeros(self.ncol(), self.nrow());
289 for i in 0..self.nrow() {
290 for j in 0..self.ncol() {
291 t[(j, i)] = self[(i, j)];
292 }
293 }
294 t
295 }
296
297 /// Resizes to `nrow` × `ncol`
298 /// ([`igraph_matrix_resize`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_resize)); igraph
299 /// does not preserve the content meaningfully, so this wrapper
300 /// resets every element to zero. Use
301 /// [`add_rows`](Self::add_rows), [`add_cols`](Self::add_cols),
302 /// [`remove_row`](Self::remove_row) and
303 /// [`remove_col`](Self::remove_col) to keep the content.
304 pub fn resize(&mut self, nrow: usize, ncol: usize) {
305 crate::error::check(unsafe {
306 $resize(
307 self,
308 crate::error::int_size(nrow),
309 crate::error::int_size(ncol),
310 )
311 })
312 .expect("igraph failed to resize a matrix");
313 for x in self.as_mut_slice() {
314 *x = Default::default();
315 }
316 }
317 }
318
319 impl Drop for $ty {
320 fn drop(&mut self) {
321 if !self.data.stor_begin.is_null() {
322 unsafe { $destroy(self) };
323 // `data` has been destroyed by igraph: forget it.
324 self.data.stor_begin = std::ptr::null_mut();
325 }
326 }
327 }
328
329 impl Default for $ty {
330 fn default() -> Self {
331 Self::new()
332 }
333 }
334
335 impl Clone for $ty {
336 fn clone(&self) -> Self {
337 crate::error::ensure_init();
338 let mut raw = MaybeUninit::<Self>::uninit();
339 crate::error::check(unsafe { $init_copy(raw.as_mut_ptr(), self) })
340 .expect("igraph failed to copy a matrix");
341 unsafe { raw.assume_init() }
342 }
343 }
344
345 impl PartialEq for $ty {
346 fn eq(&self, other: &Self) -> bool {
347 self.shape() == other.shape() && self.as_slice() == other.as_slice()
348 }
349 }
350
351 impl Index<(usize, usize)> for $ty {
352 type Output = $elem;
353 fn index(&self, (i, j): (usize, usize)) -> &$elem {
354 assert!(
355 i < self.nrow() && j < self.ncol(),
356 "matrix index ({i}, {j}) out of bounds"
357 );
358 let n = self.nrow();
359 &self.as_slice()[j * n + i]
360 }
361 }
362
363 impl IndexMut<(usize, usize)> for $ty {
364 fn index_mut(&mut self, (i, j): (usize, usize)) -> &mut $elem {
365 assert!(
366 i < self.nrow() && j < self.ncol(),
367 "matrix index ({i}, {j}) out of bounds"
368 );
369 let n = self.nrow();
370 &mut self.as_mut_slice()[j * n + i]
371 }
372 }
373
374 impl fmt::Display for $ty {
375 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376 for i in 0..self.nrow() {
377 let row = self.row(i);
378 let cells: Vec<String> = row.iter().map(Cell::cell).collect();
379 writeln!(f, "[{}]", cells.join(", "))?;
380 }
381 Ok(())
382 }
383 }
384
385 impl From<$ty> for Vec<Vec<$elem>> {
386 fn from(m: $ty) -> Self {
387 m.to_rows()
388 }
389 }
390
391 unsafe impl Send for $ty {}
392 unsafe impl Sync for $ty {}
393 };
394}
395
396impl_matrix!(
397 igraph_matrix_t,
398 igraph_real_t,
399 init = igraph_matrix_init,
400 init_copy = igraph_matrix_init_copy,
401 destroy = igraph_matrix_destroy,
402 resize = igraph_matrix_resize
403);
404impl_matrix!(
405 igraph_matrix_int_t,
406 igraph_int_t,
407 init = igraph_matrix_int_init,
408 init_copy = igraph_matrix_int_init_copy,
409 destroy = igraph_matrix_int_destroy,
410 resize = igraph_matrix_int_resize
411);
412impl_matrix!(
413 igraph_matrix_bool_t,
414 igraph_bool_t,
415 init = igraph_matrix_bool_init,
416 init_copy = igraph_matrix_bool_init_copy,
417 destroy = igraph_matrix_bool_destroy,
418 resize = igraph_matrix_bool_resize
419);
420impl_matrix!(
421 igraph_matrix_char_t,
422 std::ffi::c_char,
423 init = igraph_matrix_char_init,
424 init_copy = igraph_matrix_char_init_copy,
425 destroy = igraph_matrix_char_destroy,
426 resize = igraph_matrix_char_resize
427);
428impl_matrix!(
429 igraph_matrix_complex_t,
430 igraph_complex_t,
431 init = igraph_matrix_complex_init,
432 init_copy = igraph_matrix_complex_init_copy,
433 destroy = igraph_matrix_complex_destroy,
434 resize = igraph_matrix_complex_resize
435);
436
437// ---------------------------------------------------------------------------
438// Structural operations, for every element type (`igraph_matrix_pmt.h`).
439// ---------------------------------------------------------------------------
440
441fn check_positions(index: &[igraph_int_t], len: usize, what: &str) -> Result<()> {
442 match index.iter().find(|&&i| i < 0 || i as usize >= len) {
443 Some(i) => Err(Error::invalid(format!(
444 "{what} index {i} out of bounds (there are {len})"
445 ))),
446 None => Ok(()),
447 }
448}
449
450macro_rules! impl_matrix_common {
451 (
452 $ty:ident, $elem:ty, $vec:ident,
453 view = $view:ident, fill = $fill:ident, set_row = $set_row:ident, set_col = $set_col:ident,
454 select_rows = $select_rows:ident, select_cols = $select_cols:ident,
455 select_rows_cols = $select_rows_cols:ident, swap_rows = $swap_rows:ident,
456 swap_cols = $swap_cols:ident, transpose = $transpose:ident,
457 rbind = $rbind:ident, cbind = $cbind:ident, add_rows = $add_rows:ident,
458 add_cols = $add_cols:ident, remove_row = $remove_row:ident, remove_col = $remove_col:ident,
459 is_symmetric = $is_symmetric:ident, contains = $contains:ident, search = $search:ident,
460 capacity = $capacity:ident, resize_min = $resize_min:ident
461 ) => {
462 impl $ty {
463 /// A read-only matrix *view* of column-major `data`, without
464 /// copying it; the view dereferences to the matrix type, so it
465 /// can be handed to igraph as a `const` matrix input
466 /// ([`igraph_matrix_view`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_view)).
467 ///
468 /// # Errors
469 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue)
470 /// if `data.len() != nrow * ncol`.
471 pub fn view(
472 data: &[$elem],
473 nrow: usize,
474 ncol: usize,
475 ) -> Result<crate::vector::View<'_, Self>> {
476 if nrow.checked_mul(ncol) != Some(data.len()) {
477 return Err(Error::invalid(format!(
478 "{} elements given for a {nrow}x{ncol} matrix",
479 data.len()
480 )));
481 }
482 let raw = unsafe {
483 $view(
484 data.as_ptr(),
485 crate::error::int_size(nrow),
486 crate::error::int_size(ncol),
487 )
488 };
489 // SAFETY: the view borrows `data` for its whole lifetime.
490 Ok(unsafe { crate::vector::View::from_raw(raw) })
491 }
492
493 /// Sets every element to `value`
494 /// ([`igraph_matrix_fill`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_fill)).
495 pub fn fill(&mut self, value: $elem) {
496 if !self.is_empty() {
497 unsafe { $fill(self, value) };
498 }
499 }
500
501 /// Replaces row `i` with `values`
502 /// ([`igraph_matrix_set_row`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_set_row)).
503 ///
504 /// # Errors
505 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue)
506 /// if `i` is out of bounds or `values.len() != ncol`.
507 pub fn set_row(&mut self, i: usize, values: &[$elem]) -> Result<()> {
508 if i >= self.nrow() || values.len() != self.ncol() {
509 return Err(Error::invalid(format!(
510 "cannot set row {i} of a {}x{} matrix with {} values",
511 self.nrow(),
512 self.ncol(),
513 values.len()
514 )));
515 }
516 let v = $vec::view(values);
517 crate::igraph_call!($set_row(self, v.as_ptr(), i as igraph_int_t))
518 }
519
520 /// Replaces column `j` with `values`
521 /// ([`igraph_matrix_set_col`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_set_col)).
522 ///
523 /// # Errors
524 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue)
525 /// if `j` is out of bounds or `values.len() != nrow`.
526 pub fn set_col(&mut self, j: usize, values: &[$elem]) -> Result<()> {
527 if j >= self.ncol() || values.len() != self.nrow() {
528 return Err(Error::invalid(format!(
529 "cannot set column {j} of a {}x{} matrix with {} values",
530 self.nrow(),
531 self.ncol(),
532 values.len()
533 )));
534 }
535 let v = $vec::view(values);
536 crate::igraph_call!($set_col(self, v.as_ptr(), j as igraph_int_t))
537 }
538
539 /// A new matrix made of the given rows, in the given order
540 /// (repetitions allowed)
541 /// ([`igraph_matrix_select_rows`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_select_rows)).
542 ///
543 /// # Errors
544 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) for out of bounds rows.
545 pub fn select_rows(&self, rows: &[igraph_int_t]) -> Result<Self> {
546 check_positions(rows, self.nrow(), "row")?;
547 let r = VectorInt::view(rows);
548 let mut res = Self::zeros(rows.len(), self.ncol());
549 crate::igraph_call!($select_rows(self, &mut res, r.as_ptr()))?;
550 Ok(res)
551 }
552
553 /// A new matrix made of the given columns, in the given order
554 /// ([`igraph_matrix_select_cols`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_select_cols)).
555 ///
556 /// # Errors
557 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) for out of bounds columns.
558 pub fn select_cols(&self, cols: &[igraph_int_t]) -> Result<Self> {
559 check_positions(cols, self.ncol(), "column")?;
560 let c = VectorInt::view(cols);
561 let mut res = Self::zeros(self.nrow(), cols.len());
562 crate::igraph_call!($select_cols(self, &mut res, c.as_ptr()))?;
563 Ok(res)
564 }
565
566 /// The submatrix at the intersection of the given rows and columns
567 /// ([`igraph_matrix_select_rows_cols`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_select_rows_cols)).
568 ///
569 /// # Errors
570 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) for out of bounds indices.
571 pub fn select_rows_cols(
572 &self,
573 rows: &[igraph_int_t],
574 cols: &[igraph_int_t],
575 ) -> Result<Self> {
576 check_positions(rows, self.nrow(), "row")?;
577 check_positions(cols, self.ncol(), "column")?;
578 let r = VectorInt::view(rows);
579 let c = VectorInt::view(cols);
580 let mut res = Self::zeros(rows.len(), cols.len());
581 crate::igraph_call!($select_rows_cols(self, &mut res, r.as_ptr(), c.as_ptr()))?;
582 Ok(res)
583 }
584
585 /// Swaps rows `i` and `j`
586 /// ([`igraph_matrix_swap_rows`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_swap_rows)).
587 ///
588 /// # Errors
589 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) for out of bounds rows.
590 pub fn swap_rows(&mut self, i: usize, j: usize) -> Result<()> {
591 check_positions(&[i as igraph_int_t, j as igraph_int_t], self.nrow(), "row")?;
592 crate::igraph_call!($swap_rows(self, i as igraph_int_t, j as igraph_int_t))
593 }
594
595 /// Swaps columns `i` and `j`
596 /// ([`igraph_matrix_swap_cols`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_swap_cols)).
597 ///
598 /// # Errors
599 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) for out of bounds columns.
600 pub fn swap_cols(&mut self, i: usize, j: usize) -> Result<()> {
601 check_positions(
602 &[i as igraph_int_t, j as igraph_int_t],
603 self.ncol(),
604 "column",
605 )?;
606 crate::igraph_call!($swap_cols(self, i as igraph_int_t, j as igraph_int_t))
607 }
608
609 /// Transposes the matrix in place
610 /// ([`igraph_matrix_transpose`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_transpose));
611 /// see also [`transposed`](Self::transposed).
612 pub fn transpose(&mut self) {
613 crate::error::check(unsafe { $transpose(self) })
614 .expect("igraph failed to transpose a matrix");
615 }
616
617 /// Appends the rows of `other` below this matrix
618 /// ([`igraph_matrix_rbind`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_rbind)).
619 ///
620 /// # Errors
621 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the column counts differ.
622 pub fn rbind(&mut self, other: &Self) -> Result<()> {
623 if self.ncol() != other.ncol() {
624 return Err(Error::invalid("rbind: the number of columns differ"));
625 }
626 crate::igraph_call!($rbind(self, other))
627 }
628
629 /// Appends the columns of `other` to the right of this matrix
630 /// ([`igraph_matrix_cbind`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_cbind)).
631 ///
632 /// # Errors
633 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the row counts differ.
634 pub fn cbind(&mut self, other: &Self) -> Result<()> {
635 if self.nrow() != other.nrow() {
636 return Err(Error::invalid("cbind: the number of rows differ"));
637 }
638 crate::igraph_call!($cbind(self, other))
639 }
640
641 /// Appends `n` rows of zeros
642 /// ([`igraph_matrix_add_rows`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_add_rows)).
643 pub fn add_rows(&mut self, n: usize) {
644 let old = self.nrow();
645 crate::error::check(unsafe { $add_rows(self, crate::error::int_size(n)) })
646 .expect("igraph failed to grow a matrix");
647 for j in 0..self.ncol() {
648 for i in old..self.nrow() {
649 self[(i, j)] = Default::default();
650 }
651 }
652 }
653
654 /// Appends `n` columns of zeros
655 /// ([`igraph_matrix_add_cols`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_add_cols)).
656 pub fn add_cols(&mut self, n: usize) {
657 let old = self.ncol();
658 crate::error::check(unsafe { $add_cols(self, crate::error::int_size(n)) })
659 .expect("igraph failed to grow a matrix");
660 let nrow = self.nrow();
661 for x in &mut self.as_mut_slice()[old * nrow..] {
662 *x = Default::default();
663 }
664 }
665
666 /// Removes row `i`
667 /// ([`igraph_matrix_remove_row`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_remove_row)).
668 ///
669 /// # Errors
670 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if out of bounds.
671 pub fn remove_row(&mut self, i: usize) -> Result<()> {
672 check_positions(&[i as igraph_int_t], self.nrow(), "row")?;
673 crate::igraph_call!($remove_row(self, i as igraph_int_t))
674 }
675
676 /// Removes column `j`
677 /// ([`igraph_matrix_remove_col`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_remove_col)).
678 ///
679 /// # Errors
680 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if out of bounds.
681 pub fn remove_col(&mut self, j: usize) -> Result<()> {
682 check_positions(&[j as igraph_int_t], self.ncol(), "column")?;
683 crate::igraph_call!($remove_col(self, j as igraph_int_t))
684 }
685
686 /// Whether the matrix is square and equal to its transpose
687 /// ([`igraph_matrix_is_symmetric`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_is_symmetric)).
688 pub fn is_symmetric(&self) -> bool {
689 unsafe { $is_symmetric(self) }
690 }
691
692 /// Whether some element equals `value`
693 /// ([`igraph_matrix_contains`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_contains)).
694 pub fn contains(&self, value: $elem) -> bool {
695 !self.is_empty() && unsafe { $contains(self, value) }
696 }
697
698 /// The `(row, col)` of the first occurrence of `value` at or after
699 /// the column-major position `from`, if any
700 /// ([`igraph_matrix_search`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_search)).
701 pub fn search(&self, from: usize, value: $elem) -> Option<(usize, usize)> {
702 if from >= self.as_slice().len() {
703 return None;
704 }
705 let (mut pos, mut row, mut col) = (0, 0, 0);
706 unsafe {
707 $search(
708 self,
709 from as igraph_int_t,
710 value,
711 &mut pos,
712 &mut row,
713 &mut col,
714 )
715 }
716 .then_some((row as usize, col as usize))
717 }
718
719 /// Number of elements the matrix can hold without reallocating
720 /// ([`igraph_matrix_capacity`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_capacity)).
721 pub fn capacity(&self) -> usize {
722 unsafe { $capacity(self) as usize }
723 }
724
725 /// Frees the unused storage
726 /// ([`igraph_matrix_resize_min`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_resize_min)).
727 pub fn shrink_to_fit(&mut self) {
728 unsafe { $resize_min(self) };
729 }
730
731 /// The main diagonal, `m[(i, i)]` for `i < min(nrow, ncol)`.
732 pub fn diagonal(&self) -> Vec<$elem> {
733 (0..self.nrow().min(self.ncol()))
734 .map(|i| self[(i, i)])
735 .collect()
736 }
737
738 /// Iterates over `((row, col), &value)` in column-major order.
739 pub fn indexed_iter(&self) -> impl Iterator<Item = ((usize, usize), &$elem)> + '_ {
740 let n = self.nrow().max(1);
741 self.as_slice()
742 .iter()
743 .enumerate()
744 .map(move |(k, x)| ((k % n, k / n), x))
745 }
746 }
747 };
748}
749
750impl_matrix_common!(
751 igraph_matrix_t,
752 igraph_real_t,
753 Vector,
754 view = igraph_matrix_view,
755 fill = igraph_matrix_fill,
756 set_row = igraph_matrix_set_row,
757 set_col = igraph_matrix_set_col,
758 select_rows = igraph_matrix_select_rows,
759 select_cols = igraph_matrix_select_cols,
760 select_rows_cols = igraph_matrix_select_rows_cols,
761 swap_rows = igraph_matrix_swap_rows,
762 swap_cols = igraph_matrix_swap_cols,
763 transpose = igraph_matrix_transpose,
764 rbind = igraph_matrix_rbind,
765 cbind = igraph_matrix_cbind,
766 add_rows = igraph_matrix_add_rows,
767 add_cols = igraph_matrix_add_cols,
768 remove_row = igraph_matrix_remove_row,
769 remove_col = igraph_matrix_remove_col,
770 is_symmetric = igraph_matrix_is_symmetric,
771 contains = igraph_matrix_contains,
772 search = igraph_matrix_search,
773 capacity = igraph_matrix_capacity,
774 resize_min = igraph_matrix_resize_min
775);
776impl_matrix_common!(
777 igraph_matrix_int_t,
778 igraph_int_t,
779 VectorInt,
780 view = igraph_matrix_int_view,
781 fill = igraph_matrix_int_fill,
782 set_row = igraph_matrix_int_set_row,
783 set_col = igraph_matrix_int_set_col,
784 select_rows = igraph_matrix_int_select_rows,
785 select_cols = igraph_matrix_int_select_cols,
786 select_rows_cols = igraph_matrix_int_select_rows_cols,
787 swap_rows = igraph_matrix_int_swap_rows,
788 swap_cols = igraph_matrix_int_swap_cols,
789 transpose = igraph_matrix_int_transpose,
790 rbind = igraph_matrix_int_rbind,
791 cbind = igraph_matrix_int_cbind,
792 add_rows = igraph_matrix_int_add_rows,
793 add_cols = igraph_matrix_int_add_cols,
794 remove_row = igraph_matrix_int_remove_row,
795 remove_col = igraph_matrix_int_remove_col,
796 is_symmetric = igraph_matrix_int_is_symmetric,
797 contains = igraph_matrix_int_contains,
798 search = igraph_matrix_int_search,
799 capacity = igraph_matrix_int_capacity,
800 resize_min = igraph_matrix_int_resize_min
801);
802impl_matrix_common!(
803 igraph_matrix_bool_t,
804 igraph_bool_t,
805 VectorBool,
806 view = igraph_matrix_bool_view,
807 fill = igraph_matrix_bool_fill,
808 set_row = igraph_matrix_bool_set_row,
809 set_col = igraph_matrix_bool_set_col,
810 select_rows = igraph_matrix_bool_select_rows,
811 select_cols = igraph_matrix_bool_select_cols,
812 select_rows_cols = igraph_matrix_bool_select_rows_cols,
813 swap_rows = igraph_matrix_bool_swap_rows,
814 swap_cols = igraph_matrix_bool_swap_cols,
815 transpose = igraph_matrix_bool_transpose,
816 rbind = igraph_matrix_bool_rbind,
817 cbind = igraph_matrix_bool_cbind,
818 add_rows = igraph_matrix_bool_add_rows,
819 add_cols = igraph_matrix_bool_add_cols,
820 remove_row = igraph_matrix_bool_remove_row,
821 remove_col = igraph_matrix_bool_remove_col,
822 is_symmetric = igraph_matrix_bool_is_symmetric,
823 contains = igraph_matrix_bool_contains,
824 search = igraph_matrix_bool_search,
825 capacity = igraph_matrix_bool_capacity,
826 resize_min = igraph_matrix_bool_resize_min
827);
828impl_matrix_common!(
829 igraph_matrix_char_t,
830 std::ffi::c_char,
831 VectorChar,
832 view = igraph_matrix_char_view,
833 fill = igraph_matrix_char_fill,
834 set_row = igraph_matrix_char_set_row,
835 set_col = igraph_matrix_char_set_col,
836 select_rows = igraph_matrix_char_select_rows,
837 select_cols = igraph_matrix_char_select_cols,
838 select_rows_cols = igraph_matrix_char_select_rows_cols,
839 swap_rows = igraph_matrix_char_swap_rows,
840 swap_cols = igraph_matrix_char_swap_cols,
841 transpose = igraph_matrix_char_transpose,
842 rbind = igraph_matrix_char_rbind,
843 cbind = igraph_matrix_char_cbind,
844 add_rows = igraph_matrix_char_add_rows,
845 add_cols = igraph_matrix_char_add_cols,
846 remove_row = igraph_matrix_char_remove_row,
847 remove_col = igraph_matrix_char_remove_col,
848 is_symmetric = igraph_matrix_char_is_symmetric,
849 contains = igraph_matrix_char_contains,
850 search = igraph_matrix_char_search,
851 capacity = igraph_matrix_char_capacity,
852 resize_min = igraph_matrix_char_resize_min
853);
854impl_matrix_common!(
855 igraph_matrix_complex_t,
856 igraph_complex_t,
857 VectorComplex,
858 view = igraph_matrix_complex_view,
859 fill = igraph_matrix_complex_fill,
860 set_row = igraph_matrix_complex_set_row,
861 set_col = igraph_matrix_complex_set_col,
862 select_rows = igraph_matrix_complex_select_rows,
863 select_cols = igraph_matrix_complex_select_cols,
864 select_rows_cols = igraph_matrix_complex_select_rows_cols,
865 swap_rows = igraph_matrix_complex_swap_rows,
866 swap_cols = igraph_matrix_complex_swap_cols,
867 transpose = igraph_matrix_complex_transpose,
868 rbind = igraph_matrix_complex_rbind,
869 cbind = igraph_matrix_complex_cbind,
870 add_rows = igraph_matrix_complex_add_rows,
871 add_cols = igraph_matrix_complex_add_cols,
872 remove_row = igraph_matrix_complex_remove_row,
873 remove_col = igraph_matrix_complex_remove_col,
874 is_symmetric = igraph_matrix_complex_is_symmetric,
875 contains = igraph_matrix_complex_contains,
876 search = igraph_matrix_complex_search,
877 capacity = igraph_matrix_complex_capacity,
878 resize_min = igraph_matrix_complex_resize_min
879);
880
881// ---------------------------------------------------------------------------
882// Ordered element types: reals and integers.
883// ---------------------------------------------------------------------------
884
885macro_rules! impl_matrix_ordered {
886 (
887 $ty:ident, $elem:ty,
888 which_min = $which_min:ident,
889 which_max = $which_max:ident, maxdifference = $maxdifference:ident,
890 all_l = $all_l:ident, all_g = $all_g:ident, all_le = $all_le:ident, all_ge = $all_ge:ident
891 ) => {
892 impl $ty {
893 /// The smallest element, or `None` if the matrix is empty
894 /// ([`igraph_matrix_min`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_min)).
895 pub fn min(&self) -> Option<$elem> {
896 self.which_min().map(|ij| self[ij])
897 }
898
899 /// The largest element, or `None` if the matrix is empty
900 /// ([`igraph_matrix_max`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_max)).
901 pub fn max(&self) -> Option<$elem> {
902 self.which_max().map(|ij| self[ij])
903 }
904
905 /// `(row, col)` of the smallest element, or `None` if empty
906 /// ([`igraph_matrix_which_min`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_which_min)).
907 pub fn which_min(&self) -> Option<(usize, usize)> {
908 if self.is_empty() {
909 return None;
910 }
911 let (mut i, mut j) = (0, 0);
912 unsafe { $which_min(self, &mut i, &mut j) };
913 Some((i as usize, j as usize))
914 }
915
916 /// `(row, col)` of the largest element, or `None` if empty
917 /// ([`igraph_matrix_which_max`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_which_max)).
918 pub fn which_max(&self) -> Option<(usize, usize)> {
919 if self.is_empty() {
920 return None;
921 }
922 let (mut i, mut j) = (0, 0);
923 unsafe { $which_max(self, &mut i, &mut j) };
924 Some((i as usize, j as usize))
925 }
926
927 /// `(min, max)`, or `None` if empty (as `igraph_matrix_minmax`).
928 pub fn minmax(&self) -> Option<($elem, $elem)> {
929 Some((self.min()?, self.max()?))
930 }
931
932 /// The largest absolute element-wise difference, comparing the
933 /// elements in column-major order
934 /// ([`igraph_matrix_maxdifference`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_maxdifference)).
935 pub fn maxdifference(&self, other: &Self) -> f64 {
936 if self.is_empty() || other.is_empty() {
937 return 0.0;
938 }
939 unsafe { $maxdifference(self, other) }
940 }
941
942 /// Whether the shapes agree and every element is `<` the other's
943 /// ([`igraph_matrix_all_l`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_all_l)).
944 pub fn all_l(&self, other: &Self) -> bool {
945 self.shape() == other.shape() && (self.is_empty() || unsafe { $all_l(self, other) })
946 }
947
948 /// Whether the shapes agree and every element is `>` the other's
949 /// ([`igraph_matrix_all_g`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_all_g)).
950 pub fn all_g(&self, other: &Self) -> bool {
951 self.shape() == other.shape() && (self.is_empty() || unsafe { $all_g(self, other) })
952 }
953
954 /// Whether the shapes agree and every element is `<=` the other's
955 /// ([`igraph_matrix_all_le`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_all_le)).
956 pub fn all_le(&self, other: &Self) -> bool {
957 self.shape() == other.shape()
958 && (self.is_empty() || unsafe { $all_le(self, other) })
959 }
960
961 /// Whether the shapes agree and every element is `>=` the other's
962 /// ([`igraph_matrix_all_ge`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_all_ge)).
963 pub fn all_ge(&self, other: &Self) -> bool {
964 self.shape() == other.shape()
965 && (self.is_empty() || unsafe { $all_ge(self, other) })
966 }
967 }
968 };
969}
970
971impl_matrix_ordered!(
972 igraph_matrix_t,
973 igraph_real_t,
974 which_min = igraph_matrix_which_min,
975 which_max = igraph_matrix_which_max,
976 maxdifference = igraph_matrix_maxdifference,
977 all_l = igraph_matrix_all_l,
978 all_g = igraph_matrix_all_g,
979 all_le = igraph_matrix_all_le,
980 all_ge = igraph_matrix_all_ge
981);
982impl_matrix_ordered!(
983 igraph_matrix_int_t,
984 igraph_int_t,
985 which_min = igraph_matrix_int_which_min,
986 which_max = igraph_matrix_int_which_max,
987 maxdifference = igraph_matrix_int_maxdifference,
988 all_l = igraph_matrix_int_all_l,
989 all_g = igraph_matrix_int_all_g,
990 all_le = igraph_matrix_int_all_le,
991 all_ge = igraph_matrix_int_all_ge
992);
993impl_matrix_ordered!(
994 igraph_matrix_char_t,
995 std::ffi::c_char,
996 which_min = igraph_matrix_char_which_min,
997 which_max = igraph_matrix_char_which_max,
998 maxdifference = igraph_matrix_char_maxdifference,
999 all_l = igraph_matrix_char_all_l,
1000 all_g = igraph_matrix_char_all_g,
1001 all_le = igraph_matrix_char_all_le,
1002 all_ge = igraph_matrix_char_all_ge
1003);
1004
1005// ---------------------------------------------------------------------------
1006// Arithmetic on real matrices (igraph) and integer matrices (Rust, wrapping).
1007// ---------------------------------------------------------------------------
1008
1009impl igraph_matrix_t {
1010 /// The `n` × `n` identity matrix.
1011 pub fn identity(n: usize) -> Self {
1012 let mut m = Self::zeros(n, n);
1013 for i in 0..n {
1014 m[(i, i)] = 1.0;
1015 }
1016 m
1017 }
1018
1019 fn check_same_shape(&self, other: &Self) -> Result<()> {
1020 if self.shape() != other.shape() {
1021 return Err(Error::invalid(format!(
1022 "matrices of different shapes ({}x{} and {}x{})",
1023 self.nrow(),
1024 self.ncol(),
1025 other.nrow(),
1026 other.ncol()
1027 )));
1028 }
1029 Ok(())
1030 }
1031
1032 /// Element-wise `self += other`
1033 /// ([`igraph_matrix_add`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_add)).
1034 ///
1035 /// # Errors
1036 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1037 pub fn add(&mut self, other: &Self) -> Result<()> {
1038 self.check_same_shape(other)?;
1039 crate::igraph_call!(igraph_matrix_add(self, other))
1040 }
1041
1042 /// Element-wise `self -= other`
1043 /// ([`igraph_matrix_sub`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_sub)).
1044 ///
1045 /// # Errors
1046 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1047 pub fn sub(&mut self, other: &Self) -> Result<()> {
1048 self.check_same_shape(other)?;
1049 crate::igraph_call!(igraph_matrix_sub(self, other))
1050 }
1051
1052 /// Element-wise (Hadamard) product `self[(i,j)] *= other[(i,j)]`
1053 /// ([`igraph_matrix_mul_elements`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_mul_elements)).
1054 ///
1055 /// # Errors
1056 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1057 pub fn mul_elements(&mut self, other: &Self) -> Result<()> {
1058 self.check_same_shape(other)?;
1059 crate::igraph_call!(igraph_matrix_mul_elements(self, other))
1060 }
1061
1062 /// Element-wise division `self[(i,j)] /= other[(i,j)]`
1063 /// ([`igraph_matrix_div_elements`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_div_elements)).
1064 ///
1065 /// # Errors
1066 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1067 pub fn div_elements(&mut self, other: &Self) -> Result<()> {
1068 self.check_same_shape(other)?;
1069 crate::igraph_call!(igraph_matrix_div_elements(self, other))
1070 }
1071
1072 /// Multiplies every element by `factor`
1073 /// ([`igraph_matrix_scale`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_scale)).
1074 pub fn scale(&mut self, factor: f64) {
1075 unsafe { igraph_matrix_scale(self, factor) }
1076 }
1077
1078 /// Adds `value` to every element
1079 /// ([`igraph_matrix_add_constant`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_add_constant)).
1080 pub fn add_constant(&mut self, value: f64) {
1081 unsafe { igraph_matrix_add_constant(self, value) }
1082 }
1083
1084 /// Sum of all elements
1085 /// ([`igraph_matrix_sum`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_sum)).
1086 pub fn sum(&self) -> f64 {
1087 unsafe { igraph_matrix_sum(self) }
1088 }
1089
1090 /// Product of all elements (one for an empty matrix)
1091 /// ([`igraph_matrix_prod`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_prod)).
1092 pub fn prod(&self) -> f64 {
1093 unsafe { igraph_matrix_prod(self) }
1094 }
1095
1096 /// The sum of each row
1097 /// ([`igraph_matrix_rowsum`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_rowsum)).
1098 pub fn rowsums(&self) -> Vec<f64> {
1099 let mut res = Vector::new();
1100 crate::error::check(unsafe { igraph_matrix_rowsum(self, &mut res) })
1101 .expect("igraph failed to allocate a vector");
1102 res.into()
1103 }
1104
1105 /// The sum of each column
1106 /// ([`igraph_matrix_colsum`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_colsum)).
1107 pub fn colsums(&self) -> Vec<f64> {
1108 let mut res = Vector::new();
1109 crate::error::check(unsafe { igraph_matrix_colsum(self, &mut res) })
1110 .expect("igraph failed to allocate a vector");
1111 res.into()
1112 }
1113
1114 /// Whether the shapes agree and all elements are equal up to the
1115 /// relative tolerance `eps`
1116 /// ([`igraph_matrix_all_almost_e`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_all_almost_e)).
1117 pub fn all_almost_e(&self, other: &Self, eps: f64) -> bool {
1118 self.shape() == other.shape() && unsafe { igraph_matrix_all_almost_e(self, other, eps) }
1119 }
1120
1121 /// Replaces the elements smaller in magnitude than the *absolute*
1122 /// tolerance `tol` by exact zeros; `tol = 0` uses igraph's default,
1123 /// `f64::EPSILON^(2/3)` (about `1e-10`)
1124 /// ([`igraph_matrix_zapsmall`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_zapsmall)).
1125 ///
1126 /// # Errors
1127 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if `tol < 0`.
1128 pub fn zapsmall(&mut self, tol: f64) -> Result<()> {
1129 crate::igraph_call!(igraph_matrix_zapsmall(self, tol))
1130 }
1131
1132 /// The matrix product `self · other` (a plain Rust triple loop; for
1133 /// BLAS-backed products see the linear algebra module).
1134 ///
1135 /// ```
1136 /// use igraph::prelude::*;
1137 /// let a = Matrix::from_rows(&[[1.0, 2.0], [3.0, 4.0]]).unwrap();
1138 /// let b = a.matmul(&Matrix::identity(2)).unwrap();
1139 /// assert_eq!(a, b);
1140 /// assert_eq!(a.matmul(&a).unwrap().to_rows(), vec![vec![7.0, 10.0], vec![15.0, 22.0]]);
1141 /// ```
1142 ///
1143 /// # Errors
1144 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if
1145 /// `self.ncol() != other.nrow()`.
1146 pub fn matmul(&self, other: &Self) -> Result<Self> {
1147 if self.ncol() != other.nrow() {
1148 return Err(Error::invalid(format!(
1149 "cannot multiply a {}x{} matrix by a {}x{} one",
1150 self.nrow(),
1151 self.ncol(),
1152 other.nrow(),
1153 other.ncol()
1154 )));
1155 }
1156 let mut res = Self::zeros(self.nrow(), other.ncol());
1157 for j in 0..other.ncol() {
1158 for k in 0..self.ncol() {
1159 let b = other[(k, j)];
1160 if b == 0.0 {
1161 continue;
1162 }
1163 for i in 0..self.nrow() {
1164 res[(i, j)] += self[(i, k)] * b;
1165 }
1166 }
1167 }
1168 Ok(res)
1169 }
1170
1171 /// The matrix-vector product `self · x`.
1172 ///
1173 /// # Errors
1174 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if `x.len() != ncol`.
1175 pub fn mul_vec(&self, x: &[f64]) -> Result<Vec<f64>> {
1176 if x.len() != self.ncol() {
1177 return Err(Error::invalid(
1178 "vector length does not match the number of columns",
1179 ));
1180 }
1181 let mut res = vec![0.0; self.nrow()];
1182 for (j, col) in self.columns().enumerate() {
1183 for (r, a) in res.iter_mut().zip(col) {
1184 *r += a * x[j];
1185 }
1186 }
1187 Ok(res)
1188 }
1189}
1190
1191impl igraph_matrix_int_t {
1192 fn zip_op(
1193 &mut self,
1194 other: &Self,
1195 f: impl Fn(igraph_int_t, igraph_int_t) -> igraph_int_t,
1196 ) -> Result<()> {
1197 if self.shape() != other.shape() {
1198 return Err(Error::invalid("matrices of different shapes"));
1199 }
1200 self.as_mut_slice()
1201 .iter_mut()
1202 .zip(other.as_slice())
1203 .for_each(|(a, &b)| *a = f(*a, b));
1204 Ok(())
1205 }
1206
1207 /// Element-wise `self += other`, wrapping on overflow (Rust counterpart
1208 /// of `igraph_matrix_int_add`).
1209 ///
1210 /// # Errors
1211 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1212 pub fn add(&mut self, other: &Self) -> Result<()> {
1213 self.zip_op(other, igraph_int_t::wrapping_add)
1214 }
1215
1216 /// Element-wise `self -= other`, wrapping on overflow (Rust counterpart
1217 /// of `igraph_matrix_int_sub`).
1218 ///
1219 /// # Errors
1220 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1221 pub fn sub(&mut self, other: &Self) -> Result<()> {
1222 self.zip_op(other, igraph_int_t::wrapping_sub)
1223 }
1224
1225 /// Element-wise product, wrapping on overflow (Rust counterpart of
1226 /// `igraph_matrix_int_mul_elements`).
1227 ///
1228 /// # Errors
1229 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1230 pub fn mul_elements(&mut self, other: &Self) -> Result<()> {
1231 self.zip_op(other, igraph_int_t::wrapping_mul)
1232 }
1233
1234 /// Multiplies every element by `factor`, wrapping on overflow (Rust
1235 /// counterpart of `igraph_matrix_int_scale`).
1236 pub fn scale(&mut self, factor: igraph_int_t) {
1237 self.as_mut_slice()
1238 .iter_mut()
1239 .for_each(|x| *x = x.wrapping_mul(factor));
1240 }
1241
1242 /// Adds `value` to every element, wrapping on overflow (Rust
1243 /// counterpart of `igraph_matrix_int_add_constant`).
1244 pub fn add_constant(&mut self, value: igraph_int_t) {
1245 self.as_mut_slice()
1246 .iter_mut()
1247 .for_each(|x| *x = x.wrapping_add(value));
1248 }
1249
1250 /// Element-wise integer division `self[(i,j)] /= other[(i,j)]`, rounding
1251 /// towards zero (Rust counterpart of `igraph_matrix_int_div_elements`,
1252 /// whose C version traps on a zero divisor).
1253 ///
1254 /// # Errors
1255 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if
1256 /// the shapes differ or some divisor is zero (nothing is modified then).
1257 pub fn div_elements(&mut self, other: &Self) -> Result<()> {
1258 if other.as_slice().contains(&0) {
1259 return Err(Error::invalid("integer division by zero"));
1260 }
1261 self.zip_op(other, igraph_int_t::wrapping_div)
1262 }
1263
1264 /// Product of all elements (one for an empty matrix), wrapping on
1265 /// overflow (Rust counterpart of `igraph_matrix_int_prod`).
1266 pub fn prod(&self) -> igraph_int_t {
1267 self.as_slice().iter().fold(1, |a, &b| a.wrapping_mul(b))
1268 }
1269
1270 /// Sum of all elements, wrapping on overflow (Rust counterpart of
1271 /// `igraph_matrix_int_sum`).
1272 pub fn sum(&self) -> igraph_int_t {
1273 self.as_slice().iter().fold(0, |a, &b| a.wrapping_add(b))
1274 }
1275
1276 /// The sum of each row (Rust counterpart of `igraph_matrix_int_rowsum`).
1277 pub fn rowsums(&self) -> Vec<igraph_int_t> {
1278 self.rows()
1279 .map(|r| r.iter().fold(0, |a: igraph_int_t, &b| a.wrapping_add(b)))
1280 .collect()
1281 }
1282
1283 /// The sum of each column (Rust counterpart of `igraph_matrix_int_colsum`).
1284 pub fn colsums(&self) -> Vec<igraph_int_t> {
1285 self.columns()
1286 .map(|c| c.iter().fold(0, |a: igraph_int_t, &b| a.wrapping_add(b)))
1287 .collect()
1288 }
1289}
1290
1291impl igraph_matrix_complex_t {
1292 fn check_same_shape(&self, other: &Self) -> Result<()> {
1293 if self.shape() != other.shape() {
1294 return Err(Error::invalid(format!(
1295 "matrices of different shapes ({}x{} and {}x{})",
1296 self.nrow(),
1297 self.ncol(),
1298 other.nrow(),
1299 other.ncol()
1300 )));
1301 }
1302 Ok(())
1303 }
1304
1305 /// Builds a complex matrix from the matrices of its real and imaginary
1306 /// parts ([`igraph_matrix_complex_create`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_complex_create)).
1307 ///
1308 /// ```
1309 /// use igraph::prelude::*;
1310 /// let re = Matrix::from_rows(&[[1.0, 0.0], [0.0, 1.0]]).unwrap();
1311 /// let im = Matrix::from_rows(&[[0.0, -1.0], [1.0, 0.0]]).unwrap();
1312 /// let z = MatrixComplex::from_parts(&re, &im).unwrap();
1313 /// assert_eq!(z[(1, 0)].im(), 1.0);
1314 /// let (r, i) = z.realimag();
1315 /// assert_eq!((r, i), (re, im));
1316 /// ```
1317 ///
1318 /// # Errors
1319 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1320 pub fn from_parts(re: &Matrix, im: &Matrix) -> Result<Self> {
1321 re.check_same_shape(im)?;
1322 // `igraph_matrix_complex_create` *initializes* its output.
1323 let mut res = MaybeUninit::<Self>::uninit();
1324 crate::igraph_call!(igraph_matrix_complex_create(res.as_mut_ptr(), re, im))?;
1325 Ok(unsafe { res.assume_init() })
1326 }
1327
1328 /// Builds a complex matrix from the moduli `r` and arguments `theta`
1329 /// (radians) of its elements
1330 /// ([`igraph_matrix_complex_create_polar`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_complex_create_polar)).
1331 ///
1332 /// # Errors
1333 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1334 pub fn from_polar(r: &Matrix, theta: &Matrix) -> Result<Self> {
1335 r.check_same_shape(theta)?;
1336 let mut res = MaybeUninit::<Self>::uninit();
1337 crate::igraph_call!(igraph_matrix_complex_create_polar(
1338 res.as_mut_ptr(),
1339 r,
1340 theta
1341 ))?;
1342 Ok(unsafe { res.assume_init() })
1343 }
1344
1345 /// The real parts
1346 /// ([`igraph_matrix_complex_real`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_complex_real)).
1347 pub fn real(&self) -> Matrix {
1348 let mut res = Matrix::new();
1349 crate::error::check(unsafe { igraph_matrix_complex_real(self, &mut res) })
1350 .expect("igraph failed to allocate a matrix");
1351 res
1352 }
1353
1354 /// The imaginary parts
1355 /// ([`igraph_matrix_complex_imag`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_complex_imag)).
1356 pub fn imag(&self) -> Matrix {
1357 let mut res = Matrix::new();
1358 crate::error::check(unsafe { igraph_matrix_complex_imag(self, &mut res) })
1359 .expect("igraph failed to allocate a matrix");
1360 res
1361 }
1362
1363 /// `(real parts, imaginary parts)` in one call
1364 /// ([`igraph_matrix_complex_realimag`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_complex_realimag)).
1365 pub fn realimag(&self) -> (Matrix, Matrix) {
1366 let (mut re, mut im) = (Matrix::new(), Matrix::new());
1367 crate::error::check(unsafe { igraph_matrix_complex_realimag(self, &mut re, &mut im) })
1368 .expect("igraph failed to allocate a matrix");
1369 (re, im)
1370 }
1371
1372 /// Element-wise `self += other`
1373 /// ([`igraph_matrix_complex_add`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_add)).
1374 ///
1375 /// # Errors
1376 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1377 pub fn add(&mut self, other: &Self) -> Result<()> {
1378 self.check_same_shape(other)?;
1379 crate::igraph_call!(igraph_matrix_complex_add(self, other))
1380 }
1381
1382 /// Element-wise `self -= other`
1383 /// ([`igraph_matrix_complex_sub`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_sub)).
1384 ///
1385 /// # Errors
1386 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1387 pub fn sub(&mut self, other: &Self) -> Result<()> {
1388 self.check_same_shape(other)?;
1389 crate::igraph_call!(igraph_matrix_complex_sub(self, other))
1390 }
1391
1392 /// Element-wise product
1393 /// ([`igraph_matrix_complex_mul_elements`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_mul_elements)).
1394 ///
1395 /// # Errors
1396 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1397 pub fn mul_elements(&mut self, other: &Self) -> Result<()> {
1398 self.check_same_shape(other)?;
1399 crate::igraph_call!(igraph_matrix_complex_mul_elements(self, other))
1400 }
1401
1402 /// Element-wise division
1403 /// ([`igraph_matrix_complex_div_elements`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_div_elements)).
1404 ///
1405 /// # Errors
1406 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if the shapes differ.
1407 pub fn div_elements(&mut self, other: &Self) -> Result<()> {
1408 self.check_same_shape(other)?;
1409 crate::igraph_call!(igraph_matrix_complex_div_elements(self, other))
1410 }
1411
1412 /// Multiplies every element by `factor`
1413 /// ([`igraph_matrix_complex_scale`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_scale)).
1414 pub fn scale(&mut self, factor: igraph_complex_t) {
1415 unsafe { igraph_matrix_complex_scale(self, factor) }
1416 }
1417
1418 /// Adds `value` to every element
1419 /// ([`igraph_matrix_complex_add_constant`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_add_constant)).
1420 pub fn add_constant(&mut self, value: igraph_complex_t) {
1421 unsafe { igraph_matrix_complex_add_constant(self, value) }
1422 }
1423
1424 /// Sum of all elements
1425 /// ([`igraph_matrix_complex_sum`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_sum)).
1426 pub fn sum(&self) -> igraph_complex_t {
1427 unsafe { igraph_matrix_complex_sum(self) }
1428 }
1429
1430 /// Product of all elements (one for an empty matrix)
1431 /// ([`igraph_matrix_complex_prod`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_prod)).
1432 pub fn prod(&self) -> igraph_complex_t {
1433 unsafe { igraph_matrix_complex_prod(self) }
1434 }
1435
1436 /// The sum of each row
1437 /// ([`igraph_matrix_complex_rowsum`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_rowsum)).
1438 pub fn rowsums(&self) -> Vec<igraph_complex_t> {
1439 let mut res = VectorComplex::new();
1440 crate::error::check(unsafe { igraph_matrix_complex_rowsum(self, &mut res) })
1441 .expect("igraph failed to allocate a vector");
1442 res.into()
1443 }
1444
1445 /// The sum of each column
1446 /// ([`igraph_matrix_complex_colsum`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_colsum)).
1447 pub fn colsums(&self) -> Vec<igraph_complex_t> {
1448 let mut res = VectorComplex::new();
1449 crate::error::check(unsafe { igraph_matrix_complex_colsum(self, &mut res) })
1450 .expect("igraph failed to allocate a vector");
1451 res.into()
1452 }
1453
1454 /// Whether the shapes agree and all elements are equal up to the
1455 /// relative tolerance `eps`
1456 /// ([`igraph_matrix_complex_all_almost_e`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_complex_all_almost_e)).
1457 pub fn all_almost_e(&self, other: &Self, eps: f64) -> bool {
1458 // igraph takes non-const pointers but only reads the matrices.
1459 self.shape() == other.shape()
1460 && unsafe {
1461 igraph_matrix_complex_all_almost_e(
1462 (self as *const Self).cast_mut(),
1463 (other as *const Self).cast_mut(),
1464 eps,
1465 )
1466 }
1467 }
1468
1469 /// Replaces the real and imaginary parts smaller in magnitude than the
1470 /// absolute tolerance `tol` by exact zeros (`tol = 0`: igraph's default,
1471 /// about `1e-10`)
1472 /// ([`igraph_matrix_complex_zapsmall`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_matrix_complex_zapsmall)).
1473 ///
1474 /// # Errors
1475 /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if `tol < 0`.
1476 pub fn zapsmall(&mut self, tol: f64) -> Result<()> {
1477 crate::igraph_call!(igraph_matrix_complex_zapsmall(self, tol))
1478 }
1479}
1480
1481impl igraph_matrix_bool_t {
1482 /// Number of `true` elements.
1483 pub fn count_true(&self) -> usize {
1484 self.as_slice().iter().filter(|&&b| b).count()
1485 }
1486}
1487
1488impl From<&igraph_matrix_int_t> for igraph_matrix_t {
1489 /// Converts an integer matrix to a real one.
1490 fn from(m: &igraph_matrix_int_t) -> Self {
1491 let mut res = Self::zeros(m.nrow(), m.ncol());
1492 for (r, &x) in res.as_mut_slice().iter_mut().zip(m.as_slice()) {
1493 *r = x as f64;
1494 }
1495 res
1496 }
1497}