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


object ConjugateTranspose {
 
  case class Complex(re: Double, im: Double) {
    def conjugate(): Complex = Complex(re, -im)
    def +(other: Complex) = Complex(re + other.re, im + other.im)
    def *(other: Complex) = Complex(re * other.re - im * other.im, re * other.im + im * other.re)
    override def toString(): String = {
      if (im < 0) {
        s"${re}${im}i"
      } else {
        s"${re}+${im}i"
      }
    }
  }
 
  case class Matrix(val entries: Vector[Vector[Complex]]) {
 
    def *(other: Matrix): Matrix = {
      new Matrix(
        Vector.tabulate(entries.size, other.entries(0).size)((r, c) => {
          val rightRow = entries(r)
          val leftCol = other.entries.map(_(c))
          rightRow.zip(leftCol)
            .map{ case (x, y) => x * y } // multiply pair-wise
            .foldLeft(new Complex(0,0)){ case (x, y) => x + y } // sum over all
        })
      )
    }
 
    def conjugateTranspose(): Matrix = {
      new Matrix(
        Vector.tabulate(entries(0).size, entries.size)((r, c) => entries(c)(r).conjugate)
      )
    }
 
    def isHermitian(): Boolean = {
      this == conjugateTranspose()
    }
 
    def isNormal(): Boolean = {
      val ct = conjugateTranspose()
      this * ct == ct * this
    }
 
    def isIdentity(): Boolean = {
      val entriesWithIndexes = for (r <- 0 until entries.size; c <- 0 until entries(r).size) yield (r, c, entries(r)(c))
      entriesWithIndexes.forall { case (r, c, x) =>
        if (r == c) {
          x == Complex(1.0, 0.0)
        } else {
          x == Complex(0.0, 0.0)
        }
      }
    }
 
    def isUnitary(): Boolean = {
      (this * conjugateTranspose()).isIdentity()
    }
 
    override def toString(): String = {
      entries.map("  " + _.mkString("[", ",", "]")).mkString("[\n", "\n", "\n]")
    }
 
  }
 
  def main(args: Array[String]): Unit = {
    val m = new Matrix(
      Vector.fill(3, 3)(new Complex(Math.random() * 2 - 1.0, Math.random() * 2 - 1.0))
    )
    println("Matrix: " + m)
    println("Conjugate Transpose: " + m.conjugateTranspose())
    println("Hermitian: " + m.isHermitian())
    println("Normal: " + m.isNormal())
    println("Unitary: " + m.isUnitary())
  }
 
}