Suppose that a matrix 



M


{\displaystyle M}

 contains complex numbers. Then the conjugate transpose of 



M


{\displaystyle M}

 is a matrix 




M

H




{\displaystyle M^{H}}

 containing the complex conjugates of the matrix transposition of 



M


{\displaystyle M}

.

 



(

M

H



)

j
i


=



M

i
j


¯




{\displaystyle (M^{H})_{ji}={\overline {M_{ij}}}}




This means that row 



j


{\displaystyle j}

, column 



i


{\displaystyle i}

 of the conjugate transpose equals the

complex conjugate of row 



i


{\displaystyle i}

, column 



j


{\displaystyle j}

 of the original matrix.



In the next list, 



M


{\displaystyle M}

 must also be a square matrix.

 A Hermitian matrix equals its own conjugate transpose: 




M

H


=
M


{\displaystyle M^{H}=M}

.
 A normal matrix is commutative in multiplication with its conjugate transpose: 




M

H


M
=
M

M

H




{\displaystyle M^{H}M=MM^{H}}

.
 A unitary matrix has its inverse equal to its conjugate transpose: 




M

H


=

M

−
1




{\displaystyle M^{H}=M^{-1}}

. 
 This is true iff 




M

H


M
=

I

n




{\displaystyle M^{H}M=I_{n}}

 and iff 



M

M

H


=

I

n




{\displaystyle MM^{H}=I_{n}}

, where 




I

n




{\displaystyle I_{n}}

 is the identity matrix.


Task

Given some matrix of complex numbers, find its conjugate transpose.

Also determine if the matrix is a:

 Hermitian matrix,
 normal matrix, or 
 unitary matrix.


See also

 MathWorld entry: conjugate transpose
 MathWorld entry: Hermitian matrix
 MathWorld entry: normal matrix
 MathWorld entry: unitary matrix


 
extern crate num; // crate for complex numbers
 
use num::complex::Complex;
use std::ops::Mul;
use std::fmt;
 
 
#[derive(Debug, PartialEq)]
struct Matrix<f32> {
    grid: [[Complex<f32>; 2]; 2], // used to represent matrix
}
 
 
impl Matrix<f32> { // implements a method call for calculating the conjugate transpose
    fn conjugate_transpose(&self) -> Matrix<f32> {
        Matrix {grid: [[self.grid[0][0].conj(), self.grid[1][0].conj()],
        [self.grid[0][1].conj(), self.grid[1][1].conj()]]}
    }
}
 
impl Mul for Matrix<f32> { // implements '*' (multiplication) for the matrix
    type Output = Matrix<f32>;
 
    fn mul(self, other: Matrix<f32>) -> Matrix<f32> {
        Matrix {grid: [[self.grid[0][0]*other.grid[0][0] + self.grid[0][1]*other.grid[1][0],
            self.grid[0][0]*other.grid[0][1] + self.grid[0][1]*other.grid[1][1]],
            [self.grid[1][0]*other.grid[0][0] + self.grid[1][1]*other.grid[1][0],
            self.grid[1][0]*other.grid[1][0] + self.grid[1][1]*other.grid[1][1]]]}
    }
}
 
impl Copy for Matrix<f32> {} // implemented to prevent 'moved value' errors in if statements below
impl Clone for Matrix<f32> {
    fn clone(&self) -> Matrix<f32> {
        *self
    }
}
 
impl fmt::Display for Matrix<f32> { // implemented to make output nicer
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})\n({}, {})", self.grid[0][0], self.grid[0][1], self.grid[1][0], self.grid[1][1])
    }
}
 
fn main() {
    let a = Matrix {grid: [[Complex::new(3.0, 0.0), Complex::new(2.0, 1.0)],
        [Complex::new(2.0, -1.0), Complex::new(1.0, 0.0)]]};
 
    let b = Matrix {grid: [[Complex::new(0.5, 0.5), Complex::new(0.5, -0.5)],
        [Complex::new(0.5, -0.5), Complex::new(0.5, 0.5)]]};
 
    test_type(a);
    test_type(b);
}
 
fn test_type(mat: Matrix<f32>) {
    let identity = Matrix {grid: [[Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)],
        [Complex::new(0.0, 0.0), Complex::new(1.0, 0.0)]]};
    let mat_conj = mat.conjugate_transpose();
 
    println!("Matrix: \n{}\nConjugate transpose: \n{}", mat, mat_conj);
 
    if mat == mat_conj {
        println!("Hermitian?: TRUE");
    } else {
        println!("Hermitian?: FALSE");
    }
 
    if mat*mat_conj == mat_conj*mat {
        println!("Normal?: TRUE");
    } else {
        println!("Normal?: FALSE");
    }
 
    if mat*mat_conj == identity {
        println!("Unitary?: TRUE");
    } else {
        println!("Unitary?: FALSE");
    }
}