Skip to content

Commit 523185d

Browse files
committed
IMPL: Add trace, Hermitian conjugate, real/imag extractors (#87)
Add the small ergonomic operations requested in #87: * Matrix::trace() and ComplexMatrix::trace(): sum of the diagonal, panics on non-square input (same contract as diag()). * ComplexMatrix::h(): Hermitian conjugate (conjugate transpose). * ComplexMatrix::real() / imag(): extract the real or imaginary part as a real Matrix, preserving the storage order. The remaining items from #87 (IntoIterator support and a true matrix exponential) are larger and stay tracked on the issue.
1 parent d3aa307 commit 523185d

4 files changed

Lines changed: 155 additions & 1 deletion

File tree

src/complex/matrix.rs

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rand_distr::num_traits::{One, Zero};
1212

1313
use crate::{
1414
complex::C64,
15-
structure::matrix::Shape,
15+
structure::matrix::{matrix, Matrix, Shape},
1616
traits::fp::{FPMatrix, FPVector},
1717
traits::general::Algorithm,
1818
traits::math::{InnerProduct, LinearOp, MatrixProduct, Norm, Normed, Vector},
@@ -74,6 +74,91 @@ impl ComplexMatrix {
7474
pub fn into_vec(self) -> Vec<C64> {
7575
self.data
7676
}
77+
78+
/// Trace of a square complex matrix (sum of the diagonal entries)
79+
///
80+
/// # Panics
81+
/// Panics if the matrix is not square.
82+
///
83+
/// # Examples
84+
/// ```rust
85+
/// use peroxide::fuga::*;
86+
/// use peroxide::complex::matrix::cmatrix;
87+
///
88+
/// let a = cmatrix(vec![
89+
/// C64::new(1f64, 1f64),
90+
/// C64::new(2f64, 2f64),
91+
/// C64::new(3f64, 3f64),
92+
/// C64::new(4f64, -1f64),
93+
/// ], 2, 2, Row);
94+
/// assert_eq!(a.trace(), C64::new(5f64, 0f64));
95+
/// ```
96+
pub fn trace(&self) -> C64 {
97+
self.diag().into_iter().sum()
98+
}
99+
100+
/// Hermitian conjugate (conjugate transpose)
101+
///
102+
/// # Examples
103+
/// ```rust
104+
/// use peroxide::fuga::*;
105+
/// use peroxide::complex::matrix::cmatrix;
106+
///
107+
/// let a = cmatrix(vec![
108+
/// C64::new(1f64, 1f64),
109+
/// C64::new(2f64, -1f64),
110+
/// C64::new(0f64, 2f64),
111+
/// C64::new(3f64, 0f64),
112+
/// ], 2, 2, Row);
113+
/// let ah = a.h();
114+
/// assert_eq!(ah[(0, 1)], C64::new(0f64, -2f64));
115+
/// assert_eq!(ah[(1, 0)], C64::new(2f64, 1f64));
116+
/// ```
117+
pub fn h(&self) -> Self {
118+
let mut m = self.transpose();
119+
for x in m.as_mut_slice() {
120+
*x = x.conj();
121+
}
122+
m
123+
}
124+
125+
/// Real part as a real matrix
126+
///
127+
/// # Examples
128+
/// ```rust
129+
/// use peroxide::fuga::*;
130+
/// use peroxide::complex::matrix::cmatrix;
131+
///
132+
/// let a = cmatrix(vec![C64::new(1f64, 2f64), C64::new(3f64, 4f64)], 1, 2, Row);
133+
/// assert_eq!(a.real()[(0, 1)], 3f64);
134+
/// ```
135+
pub fn real(&self) -> Matrix {
136+
matrix(
137+
self.data.iter().map(|c| c.re).collect::<Vec<f64>>(),
138+
self.row,
139+
self.col,
140+
self.shape,
141+
)
142+
}
143+
144+
/// Imaginary part as a real matrix
145+
///
146+
/// # Examples
147+
/// ```rust
148+
/// use peroxide::fuga::*;
149+
/// use peroxide::complex::matrix::cmatrix;
150+
///
151+
/// let a = cmatrix(vec![C64::new(1f64, 2f64), C64::new(3f64, 4f64)], 1, 2, Row);
152+
/// assert_eq!(a.imag()[(0, 1)], 4f64);
153+
/// ```
154+
pub fn imag(&self) -> Matrix {
155+
matrix(
156+
self.data.iter().map(|c| c.im).collect::<Vec<f64>>(),
157+
self.row,
158+
self.col,
159+
self.shape,
160+
)
161+
}
77162
}
78163

79164
// =============================================================================

src/structure/matrix.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,22 @@ impl Matrix {
728728
pub fn into_vec(self) -> Vec<f64> {
729729
self.data
730730
}
731+
732+
/// Trace of a square matrix (sum of the diagonal entries)
733+
///
734+
/// # Panics
735+
/// Panics if the matrix is not square.
736+
///
737+
/// # Examples
738+
/// ```
739+
/// use peroxide::fuga::*;
740+
///
741+
/// let a = ml_matrix("1 2; 3 4");
742+
/// assert_eq!(a.trace(), 5f64);
743+
/// ```
744+
pub fn trace(&self) -> f64 {
745+
self.diag().iter().sum()
746+
}
731747
}
732748

733749
// =============================================================================

tests/complex_matrix.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,48 @@ fn test_seq() {
2121
);
2222
assert_eq!(v1.as_slice()[0], Complex64::new(1f64, 1f64));
2323
}
24+
25+
#[cfg(feature = "complex")]
26+
#[test]
27+
fn test_trace_hermitian_real_imag() {
28+
use num_complex::Complex64;
29+
use peroxide::complex::matrix::cmatrix;
30+
31+
let a = cmatrix(
32+
vec![
33+
Complex64::new(1f64, 1f64),
34+
Complex64::new(2f64, -1f64),
35+
Complex64::new(0f64, 2f64),
36+
Complex64::new(4f64, -1f64),
37+
],
38+
2,
39+
2,
40+
Row,
41+
);
42+
43+
// trace = (1+i) + (4-i) = 5
44+
assert_eq!(a.trace(), Complex64::new(5f64, 0f64));
45+
46+
// hermitian conjugate: transpose + conjugate
47+
let ah = a.h();
48+
assert_eq!(ah[(0, 0)], Complex64::new(1f64, -1f64));
49+
assert_eq!(ah[(0, 1)], Complex64::new(0f64, -2f64));
50+
assert_eq!(ah[(1, 0)], Complex64::new(2f64, 1f64));
51+
assert_eq!(ah[(1, 1)], Complex64::new(4f64, 1f64));
52+
53+
// (A^H)^H == A
54+
let ahh = ah.h();
55+
for i in 0..2 {
56+
for j in 0..2 {
57+
assert_eq!(ahh[(i, j)], a[(i, j)]);
58+
}
59+
}
60+
61+
// real / imag parts
62+
let re = a.real();
63+
let im = a.imag();
64+
assert_eq!(re[(0, 1)], 2f64);
65+
assert_eq!(im[(0, 1)], -1f64);
66+
assert_eq!(re[(1, 0)], 0f64);
67+
assert_eq!(im[(1, 0)], 2f64);
68+
}

tests/matrix.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,11 @@ fn test_matrix_rejects_length_mismatch() {
170170
// row * col must be impossible to construct from safe code.
171171
let _ = matrix(vec![1, 2, 3], 2, 2, Row);
172172
}
173+
174+
#[test]
175+
fn test_trace() {
176+
let a = ml_matrix("1 2; 3 4");
177+
assert_eq!(a.trace(), 5f64);
178+
// layout independence
179+
assert_eq!(a.change_shape().trace(), 5f64);
180+
}

0 commit comments

Comments
 (0)