# Chapter 2 - Math Tools II (Matrices and Operators)

The previous chapter was concluded by describing how one could use a vector to describe a quantum state. This was followed by giving the example of the most basic form of quantum information, a qubit. However, given that a quantum computer's function is to manipulate and evolve a qubit's state according to certain instructions (also called algorithms), it would seem obvious that what we learned isn't sufficient enough to describe quantum information. In quantum mechanics, a system's state is evolved by what is called an operator, and similarly to how vectors are used to describe a system's state, matrices are used to describe how that state evolves once acted upon by an operator. <a href="https://www.numpy.org">Numpy</a> will also be used for Python implementations.

 ## 2.1 Matrices

In Chapter 1, specifically in the sections related to Dirac Notation, vectors were described by either a column or a row matrix, and matrix multiplication was used to describe inner products. This chapter aims at describing Matrices in more detail and aims at explaining everything you would need to know about matrices to complete this series (which is arguably a complete introduction to basic quantum information theory). A matrix is essentially a 2D, or more specifically, a rectangular array of numbers. A matrix $M$, which has dimensions $m$ and $n$, would look like the following:


   \begin{equation}
  M = \begin{pmatrix} M_{11} & M_{12} & \ldots & M_{1n} \\\ M_{21} & M_{22} & \ldots & M_{2n} \\\ \vdots & \vdots & \ddots &\vdots \\\ M_{m1} & M_{m2} & \ldots & M_{mn}\end{pmatrix}
   \end{equation}


Where the components of the matrix, $M_{ij}$ are numbers (for the sake of this series). If $M_{ij} \in R$, then the matrix is called a real matrix. Similarly, if $M_{ij} \in C$, the matrix is called a complex matrix.

For example, 

   \begin{equation}
  M = \begin{pmatrix} 4 & i & 1 - i \\\ 2 & 0 & 2i\end{pmatrix}
   \end{equation}
   
   is a 2x3 complex matrix. $M_{22}$ = 0, $M_{21} = 2$, etc. 
   
  ### Operations on matrices
  
  **Matrix Addition:**
  
  Condition: Two matrices, $A$ and $B$, with dimensions $m_A \times n_A$ and $m_B \times n_b$, can only be added if $m_A = m_B$ and $n_A = n_B$ (i.e if they have the same dimension).

  Given that they have the same dimensions, their sum would yield the following:
  
  \begin{equation}
  C = A + B \newline C = \begin{pmatrix} A_{11} & A_{12} & \ldots & A_{1n} \\\ A_{21} & A_{22} & \ldots & A_{2n} \\\ \vdots & \vdots & \ddots &\vdots \\\ A_{m1} & A_{m2} & \ldots & A_{mn}\end{pmatrix} + \begin{pmatrix} B_{11} & B_{12} & \ldots & B_{1n} \\\ B_{21} & B_{22} & \ldots & B_{2n} \\\ \vdots & \vdots & \ddots &\vdots \\\ B_{m1} & B_{m2} & \ldots & B_{mn}\end{pmatrix} \newline C = \begin{pmatrix} A_{11} + B_{11}  & A_{12} + B_{12} & \ldots & A_{1n} + B_{1n} \\\ A_{21} + B_{21} & A_{22} + B_{22} & \ldots & A_{2n} + B_{2n} \\\ \vdots & \vdots & \ddots &\vdots \\\ A_{m1} + B_{m1} & A_{m2} + B_{m2} & \ldots & A_{mn} + B_{mn}\end{pmatrix}
  \end{equation}
  
  Or in simpler terms, $C$ is a matrix where $C_{ij} = A_{ij} + B_{ij}$. 
  
  **Matrix Subtraction:**
  
  Condition: Similar to addition, the condition is that the dimensions of matrices $A$ and $B$ are equal
  
   Given that they have the same dimensions, their difference would yield the following:
  
  \begin{equation}
  C = A - B \newline C = \begin{pmatrix} A_{11} & A_{12} & \ldots & A_{1n} \\\ A_{21} & A_{22} & \ldots & A_{2n} \\\ \vdots & \vdots & \ddots &\vdots \\\ A_{m1} & A_{m2} & \ldots & A_{mn}\end{pmatrix} - \begin{pmatrix} B_{11} & B_{12} & \ldots & B_{1n} \\\ B_{21} & B_{22} & \ldots & B_{2n} \\\ \vdots & \vdots & \ddots &\vdots \\\ B_{m1} & B_{m2} & \ldots & B_{mn}\end{pmatrix} \newline C = \begin{pmatrix} A_{11} - B_{11}  & A_{12} - B_{12} & \ldots & A_{1n} - B_{1n} \\\ A_{21} - B_{21} & A_{22} - B_{22} & \ldots & A_{2n} - B_{2n} \\\ \vdots & \vdots & \ddots &\vdots \\\ A_{m1} - B_{m1} & A_{m2} - B_{m2} & \ldots & A_{mn} - B_{mn}\end{pmatrix}
  \end{equation}
  
  Or in simpler terms, $C$ is a matrix where $C_{ij} = A_{ij} - B_{ij}$
  
  
  **Scalar Multiplication:**
  
  Multiplying a scalar $c$ with a matrix $A$ would yield, as with vectors, the same matrix where the components undergo the following transformation: $A_{ij} \rightarrow c*A_{ij}$, or in other words:
  
  \begin{equation}
  B = c*A \newline B = c\begin{pmatrix} A_{11} & A_{12} & \ldots & A_{1n} \\\ A_{21} & A_{22} & \ldots & A_{2n} \\\ \vdots & \vdots & \ddots &\vdots \\\ A_{m1} & A_{m2} & \ldots & A_{mn}\end{pmatrix} \newline B = \begin{pmatrix} cA_{11} & cA_{12} & \ldots & cA_{1n} \\\ cA_{21} & cA_{22} & \ldots & cA_{2n} \\\ \vdots & \vdots & \ddots &\vdots \\\ cA_{m1} & cA_{m2} & \ldots & cA_{mn}\end{pmatrix}
  \end{equation}
  
  **Matrix Multiplication:**
  
  Condition: For two matrices $A$ and $B$ to be multipliable, the following condition must apply: $n_A = m_b$, or in other words, the number of columns in $A$ must be equal to the number of rows in $B$. The result will be an $m_A \times n_B$ matrix. For example, if one is multiplying a $2 \times 4$ matrix by a $4 \times 2$ matrix, it will work out to be a $2 \times 2$ matrix. 
  
  The result of multiplying an $m \times n$ with an $n \times p$ will be
  \begin{equation}
  C = AB \newline C = \begin{pmatrix} A_{11} & A_{12} & \ldots & A_{1n} \\\ A_{21} & A_{22} & \ldots & A_{2n} \\\ \vdots & \vdots & \ddots &\vdots \\\ A_{m1} & A_{m2} & \ldots & A_{mn}\end{pmatrix}\begin{pmatrix} B_{11} & B_{12} & \ldots & B_{1p} \\\ B_{21} & B_{22} & \ldots & B_{2p} \\\ \vdots & \vdots & \ddots &\vdots \\\ B_{n1} & B_{n2} & \ldots & B_{np}\end{pmatrix} \newline C = \begin{pmatrix} C_{11} & C_{12} & \ldots & C_{1p} \\\ C_{21} & C_{22} & \ldots & C_{2p} \\\ \vdots & \vdots & \ddots &\vdots \\\ C_{m1} & C_{m2} & \ldots & C_{mp}\end{pmatrix}
  \end{equation}
  
  where $C_{ij} = A_{i1}A_{1j} + A_{i2}A_{2j} + \ldots A_{in}A_{nj} = \sum_{k=1}^n A_{ik}A_{kj}$.
  
  Note that in practice, it is much easier to get the hang of matrix multiplication than to think of it in terms of a generalized formula, so I'd suggest solving a few matrix multiplication problems to understand what this actually means. It is also important to note that matrix multiplication is not commutative (i.e $AB \neq BA$)
  

  
  **Matrix Division:**
  
  There isn't such a thing. One could argue that the closest thing to matrix division is multiplying a matrix $A$ by a matrix $B$'s inverse, $B^{-1}$. But that will be spoken about in Section 2.2
  
  ### Python Implementation


```python
#Importing necessary modules: numpy can be used to describe matrices in two ways: np.array and np.matrix
#Both were used in Chapter 1, but from now on, we will carry on with np.array
#We'll also import a sympy function so that we can visualize matrices in a clearer way. This does not affect any calculation

import numpy as np
from sympy import Matrix, init_printing

#This function uses SymPy to convert a numpy array into a clear matrix image

def view(mat):
    display(Matrix(mat))
```


```python
#Defining Matrices

A = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])
print('4x4 Matrix, A =\n', A)

print('\n')

print('Using SymPy, we can make this look clearer, like this:')
view(A)

#Getting individual components
#A very important thing to note is that python starts counting from 0, so A[2][3] would give us A_34

print('A_34 = ',A[2][3])


```

    4x4 Matrix, A =
     [[ 1  2  3  4]
     [ 5  6  7  8]
     [ 9 10 11 12]
     [13 14 15 16]]
    
    
    Using SymPy, we can make this look clearer, like this:



$\displaystyle \left[\begin{matrix}1 & 2 & 3 & 4\\5 & 6 & 7 & 8\\9 & 10 & 11 & 12\\13 & 14 & 15 & 16\end{matrix}\right]$


    A_34 =  12



```python
#Matrix Operation

#Defining two 2x3 matrices, A and B, and one scalar, c
A = np.array([[1,2,0],[1,3,4]])
B = np.array([[4,0,6],[3,0,1]])
C = np.array([[1,0],[9,4],[3,2]])
c = 5

#Addition
print('A+B =')
view(A+B)
print('\n')

#Subtraction
print('A-B = ')
view(A-B)
print('\n')

#Scalar Multiplication
print('cA = ')
view(c*A)
print('\n')
#Matrix Multiplication, where AB is written as A@B in python
print('AC = ')
view(A@C)
print('\n')
#Cocktail
print('-2A + 3B = ')
view(-2*A + 3*B)

#Excersise: Examine Multiplication for matrices A and B, where dim(A) =/= dim(B) 

```

    A+B =



$\displaystyle \left[\begin{matrix}5 & 2 & 6\\4 & 3 & 5\end{matrix}\right]$


    
    
    A-B = 



$\displaystyle \left[\begin{matrix}-3 & 2 & -6\\-2 & 3 & 3\end{matrix}\right]$


    
    
    cA = 



$\displaystyle \left[\begin{matrix}5 & 10 & 0\\5 & 15 & 20\end{matrix}\right]$


    
    
    AC = 



$\displaystyle \left[\begin{matrix}19 & 8\\40 & 20\end{matrix}\right]$


    
    
    -2A + 3B = 



$\displaystyle \left[\begin{matrix}10 & -4 & 18\\7 & -6 & -5\end{matrix}\right]$


## 2.2 Square Matrices

A matrix that has an equal number of rows and columns is called a square matrix. Its dimensions are described as $n \times n$, where $n$ is what is known as the order of the matrix. For instance, the following matrix is described as a square matrix of order 2:

\begin{equation}
\begin{pmatrix} 1 & 2i \\\ 3 & -1 \end{pmatrix}
\end{equation}
<br>
The diagonal of a square matrix is formed from by set of numbers $A_{ii}$. For example, in the order 2 matrix example above, the diagonal has elements 1 and -1 ($A_{11}$ and $A_{22}$) 
 
 
 Square matrices are very useful for describing linear transformations such as rotation, as well as getting the area formed by a set of vectors. In quantum information theory, a specific type of square matrix called a unitary matrix is used to describe the operators which act on a statevector to change the amplitudes. 
  ### Determinant and Trace
 <br>
 Determinants and traces are operations that are unique to square matrices. The result of a determinant is a number that affects how "scaled" an object is when transformed by a matrix. Similar to matrix multiplication, the general equation of a determinant is usually tedious to look at at first hand, but I'll present the specific cases for $2 \times 2$ and $3 \times 3$ matrices.
 
 For a $2 \times 2$ matrix, $A$, one would find the determinant to be:
 
 \begin{equation}
 det(A) = det\begin{pmatrix} a & b \\\ c & d \end{pmatrix} \newline = ad - bc
 \end{equation}
 
 For a $3 \times 3$ matrix, $A$, one would find the determinant to be:
 
  \begin{equation}
 det(A) = det\begin{pmatrix} a & b & c \\\ d & e & f \\\ g & h & i \end{pmatrix} \newline = a(ei - hf) - b(di -fg) + c(dh - eg)
 \end{equation}
 
 This is all the information I'll be giving on determinants because it is the only information required for this series. However, feel free to investigate this at your own speed. 
 
 <br>
 
 A trace of a square matrix is essentially the sum of all its diagonal's components. Formally, it is written as the following:
 
 \begin{equation}
 Tr(A) = \sum_{i=1}^n A_{ii}
 \end{equation}
  
  In Section 2.1, we described that matrix multiplication is not always commutative, and that in many cases, $AB \neq BA$. However, an interesting property is that $Tr(BA) = Tr(AB)$ regardless of whether the matrices commute or not. As the name suggests, matrices commute when $AB = BA$, and don't commute when $AB \neq BA$.
  
  
 ### Diagonal Matrices
 
 A diagonal matrix is a special case of a square matrix where every element in the matrix is zero except for its diagonal. The following is an example of a $3 \times 3$ diagonal matrix:
 
 \begin{equation}
 \begin{pmatrix} 1 & 0 & 0 \\\ 0 & 3 & 0 \\\ 0 & 0 & 2+i \end{pmatrix}
 \end{equation}
 
 ### Triangular Matrices
 It is said that the diagonal divides the matrix into two blocks. The upper block, consists of all elements, $A_{ij}$ where $i \leq j$. Similarly, the lower block consists of all $A_{ij}$ where $i \geq j$. An upper triangle matrix is any matrix where the upper block consists of any set of numbers and the lower block consits of only zeros. In other words, it is any matrix that takes the following form:
 
   \begin{equation}
  M = \begin{pmatrix} M_{11} & M_{12} & \ldots & M_{1n} \\\ 0 & M_{22} & \ldots & M_{2n} \\\ \vdots & \vdots & \ddots &\vdots \\\ 0 & 0 & \ldots & M_{nn}\end{pmatrix}
   \end{equation}
   
   Matrix $A$ is a $4 \times 4$ example:
   
   \begin{equation}
  A = \begin{pmatrix} M_{11} & M_{12}  & M_{13} & M_{14} \\\ 0 & M_{22} & M_{23} & M_{24} \\\ 0 & 0 & M_{33} & M_{34} \\\ 0 & 0 & 0 & M_{44}\end{pmatrix}
   \end{equation}
<br>
  On the other hand, we also have lower triangle matrices, where $A_{ij} = 0$ if $i>j$. It takes the following form:
  
 <br>
  
   \begin{equation}
  M = \begin{pmatrix} M_{11} &0 & \ldots & 0 \\\ M_{21} & M_{22} & \ldots & 0 \\\ \vdots & \vdots & \ddots &\vdots \\\ M_{m1} & M_{m2} & \ldots & M_{mn}\end{pmatrix}
   \end{equation}


   Matrix $B$ is a $4 \times 4$ example:
    
   \begin{equation}
  B = \begin{pmatrix} M_{11} & 0  & 0 & 0 \\\ M_{21} & M_{22} & 0 & 0 \\\ M_{31} & M_{31} & M_{33} & 0 \\\ M_{41} & M_{42} & M_{43} & M_{44}\end{pmatrix}
   \end{equation}

A diagonal matrix can be viewed as either an upper or lower matrix
 
 ### Identity Matrix and Matrix Inversion
 
 An identity matrix, $I$, is any matrix where its diagonal elements are all equal to 1 and the rest of the elements are equal to 0. Formally, it could be represented by the Kronicker-Delta function described in Section 1.9 in the following way:
 
 \begin{equation}
 I_{ij} = \delta_{ij}
 \end{equation}
 
 where $\delta_{ij}$ is
 \begin{equation}
\delta_{ij} =
    \begin{cases}
            1, &         \text{if } i=j,\\
            0, &         \text{if } i\neq j.
    \end{cases}
\end{equation}

The following is a $3 \times 3$ identity matrix, $I_3$:


\begin{equation}
I_3 = \begin{pmatrix} 1 & 0 & 0 \\\ 0 & 1 & 0 \\\ 0 & 0 & 1\end{pmatrix}
\end{equation}

 
 One of the most notable properties of the identity matrix is that when any matrix $A$ is multiplied with it,  the results will be the same matrix. In other words, for an $m \times n$ matrix,
 
 \begin{equation}
 I_m A = AI_n = A
 \end{equation}
 
 and for a square matrix, the order of multiplication with the identity won't matter because $n = m$.
 
 
 In Section 1, an important point that was stated is that there is no such this as matrix division but that for square matrices, one can get matrix $A$'s inverse $A^{-1}$, where 
 
  \begin{equation}
  AA^{-1} = I
  \end{equation}
  
 
   ### Python Implementation


```python
#Defining square matrices

A = np.array([[3,0,2], [2,0,-2],[0,1,1]])

#Determinant and Trace

trA = A.trace()
detA = np.linalg.det(A)

print('A = ')
view(A)

print('Tr(A) = ', trA)
print('det(A)', detA)

print('')
#Identity and Inverse

Ainv = np.linalg.inv(A)

#Multiplying A with its inverse. The result should be the I_3

print('I = AA^-1 = ')
view(A@Ainv)

print('The result is the identity. I_21 is approximately zero and is not exactly zero because of some negligable numerical error')

#Excercise: Create a function that identifies whether a square matrix is a diagonal matrix, upper triangle or lower triangle
#Hint: Start by creating a kronDelta(i,j) function

```

    A = 



$\displaystyle \left[\begin{matrix}3 & 0 & 2\\2 & 0 & -2\\0 & 1 & 1\end{matrix}\right]$


    Tr(A) =  4
    det(A) 10.000000000000002
    
    I = AA^-1 = 



$\displaystyle \left[\begin{matrix}1.0 & 0.0 & 0.0\\-5.55111512312578 \cdot 10^{-17} & 1.0 & 0.0\\0.0 & 0.0 & 1.0\end{matrix}\right]$


    The result is the identity. I_21 is approximately zero and is not exactly zero because of some negligable numerical error


## 2.3 Operators

As you most likely know, a function is a mathematical object that transforms an input number into an output number. Similarly, an operator is a mathematical object which transforms a function into a whole other function. An example is the derivative, which yields the rate at which a function is changing. The definition of an operator could be extended to vectors. In other word, an operator maps a vector $|v_1>$ to a new vector $|v_2>$. Notationally, this could be written as follows:

\begin{equation}
A|v_1> = |v_2>
\end{equation}

Where $A$ is an operator, $|v_1>$ is the "input " vector and $|v_2>$ is the "output vector".

In a quantum system, an operator $A|\psi>$ can represent an observable, which is essentially a physical quantity such as momentum and position. More specifically, an observable must be a linear operator, which we'll speak about soon.

### Operator Algebra

Let us say we have operators $A$ and $B$ acting on vector $|\psi>$. The following rules must apply:

**Adding operators:**

\begin{equation}
(A+B)|\psi> = A|\psi> + B|\psi>
\end{equation}

**Multiplying two operators (on state):**

\begin{equation}
(AB)|\psi> = A[B|\psi>]
\end{equation}

**Multiplying three operators:**

\begin{equation}
A(BC) = (AB)C
\end{equation}


**Commuation:**
\begin{equation}
[A,B]|\psi> = AB|\psi> - BA|\psi>
\end{equation}


If $[A,B]|\psi> = 0$, then the operators commutate and if $[A,B]|\psi> \neq 0$, then the operators don't commutate. This is similar to what was mentioned in 2.2. Commutation has a very important physical implication in quantum mechanics that will be described in Chapter 3.


### Linear Operators

An operator, $A$, was defined to be an object that transforms a vector $|v_1>$ to $|v_2>$. If vectors $|v_1>$ and $|v_2>$ are in the same vector space **V**, then $A$ is said to be a linear operator. Notationally, linear operator $A$,  is represented as follows:

\begin{equation}
A|v_1> = |v_2> ; |v_1>, and |v_2> \in V
\end{equation}
<br>
If $A$ is a linear operator, than it must have the following properties:

      1. A(|u> + |v>) = A|u> + A|v>
      2. A(c|u>) = cA|u>
and finally, from 1 and 2:
      
      3. A(c|u> + d|v>) = cA|u> + dA|v> 
      
Where $c$ and $d$ are some constants and $c|u> + d|v>$ is a linear combination. In other words, a linear operator acting on a linear combination of vectors is equal to a linear combination of the vectors produced by the act of applying the operator on them. In concise terms, one can simply say that the linear combination is preserved


As mentioned before, linear operators are used to describe physical quantities (such as position and momentum)in quantum mechanics. In quantum computing, linear operators are used to describe the $\textit{quantum logic gates}$ which evolves the state of a qubit. There is also what is known as an antilinear operator, which has the following property:

\begin{equation}
A(c_1 |\psi> + c_2|\phi>) = c_1^* A|\psi> + c_2^* A|\phi>
\end{equation}


A final important quality of linear opertors is that given an operator $A$ and ket $|\psi>$, where $A|\psi> = |\phi>$,

\begin{equation}
BA|\psi> = B(A|\psi>) = B|\phi> = |\omega>
\end{equation}

This is just to assert that we start by evaluating the operator closest to $|\psi>$.

  ### Python Implementation


```python
#Operator of a function where O[f(x)] = f(x)*2 + 3
#If you don't know what a lambda function is in Python, check it out here: https://www.w3schools.com/python/python_lambda.asp

def operator(f):
    newFunc = lambda x: f(x)*2 + 3
    return newFunc

#g(x) = O[f(x)], where f(x) = 2*x. The result should be 4x + 3
#g(3) = 4*3 + 3 = 15


g = operator(lambda x: 2*x)

print('g(x) = O[f(x)] = O[2x] = 2*2x + 3 = 4x+3')
print('g(3) = 4*3 + 3 = ', g(3))

#Excercise: Create a derivative(f) operator that gets the derivative of an input function


```

    g(x) = O[f(x)] = O[2x] = 2*2x + 3 = 4x+3
    g(3) = 4*3 + 3 =  15



```python
#Operator of a vector where A|u> = |v>, where v_i = 2*u_i + 3

def A(vec):
    newVec = np.array([2*i + 3 for i in range(len(vec)+1)])
    return newVec
    

u = np.array([1,2,3,4])
v = A(u)

print('|u> = ', u)
print('')
print('|v> = A|u> = ', v)

#Excersise: Create linChecker(operator) function to check if an operator O is linear or not

```

    |u> =  [1 2 3 4]
    
    |v> = A|u> =  [ 3  5  7  9 11]



```python
#Commutation: Creating a function that checks whether operators A and B commutate on some dummy function f(x) = 2*x
#[A,B](2x) =?= 0


'''
Apparently, trying to define such a function violates something called The Rice theorem, so the next best thing
one can do is check whether A(B(f(n))) == B(A(f(n))), where n is a number. n = 5 is chosen arbitrarly.

Be carefuly about which number you select, though.
'''

def commutate(A,B,n):
    f = lambda x: 2*x
    if A(B(f(n))) == B(A(f(n))):
        print('A and B commute!')
    else:
        print('A and B do not commute!')

    
        

A = lambda f: f**2
B = lambda f: np.log(f)



print('A = f(x)^2, B = ln(f(x)):')
commutate(A,B,5)

print('')

C = lambda f: 2*f
D = lambda f: 3*f

print('A = 2*f(x), B = 3*f(x):')
commutate(C,D,5)



```

    A = f(x)^2, B = ln(f(x)):
    A and B do not commute!
    
    A = 2*f(x), B = 3*f(x):
    A and B commute!


## 2.4 Matrix Representation of Linear Operators 

We have stated what a linear operator is and have presented some basic rules of operator algebra, but we still have not described how to properly represent them. For linear operators (i.e the operators we are concerned with), a very effective way to represent an operator which acts on a $n$-dimensional vector is by an $n \times n$ square matrix. 

For instance, if one were to transform a 3D vector $|u>$ to $|v>$, as follows

\begin{equation}
\begin{pmatrix} a \\\ b \\\ c\end{pmatrix} \rightarrow \begin{pmatrix} a+b \\\ a-b \\\ 3c\end{pmatrix}
\end{equation}

they can use an operator $A$, represented by a $3 \times 3$ matrix, or 

\begin{equation}
A|u> = |v>\newline \begin{pmatrix} 1 & 1 & 0 \\\ 1 & -1 & 0 \\\ 0 & 0 & 3 \end{pmatrix}
\begin{pmatrix} a \\\ b \\\ c\end{pmatrix} =\begin{pmatrix} a+b \\\ a-b \\\ 3c\end{pmatrix}
\end{equation}

Using the rules of matrix multiplication described in Section 1.1, one would find this to be true (in fact if you're new to this, I would encourage trying it out manually).


Generally, an $n \times n$ matrix transforms an $n$-vector where the $i$th column of the matrix is what the $i$th basis vector is mapped to. To demonstrate this, I will use new notation which makes it more obvious. Previously, an object in a matrix was described by $M_{ij}$, where $i$ is the row index and $j$ is the column index. Alternatively, a matrix component will now be written as $j_i$, where the column index is represented by a letter, not a number. For instance, the 3 $\times$ 3 example is

\begin{equation}
M|u> = |v> \newline
\begin{pmatrix} a_1 & b_1 & c_1 \\\ a_2 & b_2 & c_2 \\\ a_3 & b_3 & c_3 \end{pmatrix}\begin{pmatrix} x\\y\\z\end{pmatrix}  = \begin{pmatrix} a_1x + a_2y + a_3z \\\ b_1x + b_2y + b_3z \\\ c_1x + c_2y + c_3z \end{pmatrix}
\end{equation}
 
 As you can see, the $i$th components of the $|v>$ is the linear combination of the vector components of $|u>$, where the scalars are the components of the $i$th row of matrix (linear operator) $M$
 
 ### Python Implementation


```python
#Linear operator Examples
print('Example 1:')
#A|u> = |v>, where |u> = - |v>

u = np.array([1,2,3])

v = -1*u

A = np.array([[-1,0,0],[0,-1,0],[0,0,-1]])


print('A|u> = |v> = -|u>')
print('')

print('A|u>, where A is -I_3 = ')
view(A@u)
print('')

print('v = ')
view(v)

print('They are equal! \n')


print('Example 2:')

#AB|psi> = phi|> where |psi>, |phi> in R^4

A = np.array([[1,0,3,4],[1,1,1,1],[0,9,2,-1]])
B = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])
psi = np.array([1,2,3,4])
phi= A@B@psi

print('|phi> = ','AB|psi> = ')
view(phi)

```

    Example 1:
    A|u> = |v> = -|u>
    
    A|u>, where A is -I_3 = 



$\displaystyle \left[\begin{matrix}-1\\-2\\-3\end{matrix}\right]$


    
    v = 



$\displaystyle \left[\begin{matrix}-1\\-2\\-3\end{matrix}\right]$


    They are equal! 
    
    Example 2:
    |phi> =  AB|psi> = 



$\displaystyle \left[\begin{matrix}960\\360\\700\end{matrix}\right]$



```python
#Application to Quantum Computing

#X|0> = |1>, where, according to section 1.10, |0> = (1,0) and |1> = (0,1)

zero = np.array([1,0])
one = np.array([0,1])

X = np.array([[0,1],[1,0]])
print('X = ')
view(X)

print('')

#Checking to see if X|0>
XZero = X@zero
print('Is X|0> = |1>:')
#Prints true if all elements in both matrices are equal
print(XZero.all() == one.all())

print('')

print('X is a very important operator in quantum computing because it essentially acts as a quantum NOT gate. The implication of this tranformation is taking a qubit which is always 0 and making it always 1 and vice versa (X|1>=|0>).')
```

    X = 



$\displaystyle \left[\begin{matrix}0 & 1\\1 & 0\end{matrix}\right]$


    
    Is X|0> = |1>:
    True
    
    X is a very important operator in quantum computing because it essentially acts as a quantum NOT gate. The implication of this tranformation is taking a qubit which is always 0 and making it always 1 and vice versa (X|1>=|0>).


## 2.5 Operations on Matrices

In Section 2.2, we discussed certain operations such as determinants and traces that produce a scalar out of a matrix. This section aims at explaining three important operations which transform matrix $M \rightarrow M'$ using certain rules. These operations are important not only for defining which linear transformations are allowed in a quantum system, but for doing these transformations. Note that all of these were defined briefly in Section 1.5, but will be further elaborated here

### Complex Conjugate

The first operation that will be discussed is the complex conjugate of a matrix, which, as we've discussed several times, simple transforms the matrix in the following way: $M_{ij} \rightarrow M_{ij}^*$, or in other words, replaces every component in matrix $M$ with it's complex conjugate. Explicitly, 

\begin{equation}
  M^* = \begin{pmatrix} M_{11}^* & M_{12}^* & \ldots & M_{1n}^* \\\ M_{21}^* & M_{22}^* & \ldots & M_{2n}^* \\\ \vdots & \vdots & \ddots &\vdots \\\ M_{m1}^* & M_{m2}^* & \ldots & M_{mn}^*\end{pmatrix}
   \end{equation}

**Example:**

\begin{equation}
\begin{pmatrix} i+2 & -1 \\\ 2i & 5 \end{pmatrix}^* = \begin{pmatrix} -i+2 & -1 \\\ -2i & 5 \end{pmatrix}
\end{equation}

The complex conjugate of a real matrix is itself
### Transpose

A transpose of a matrix $M$ essentially performs the following transformation: $M_{ij} \rightarrow M_{ji}$. In other words it switches the row and column indices. It is easy to realize that all components in the matrix will be affected except for the diagonal compontents, who are affected by a transpose in the following way: $M_{ii} \rightarrow M_{ii}$. Explicitly, 

\begin{equation}
  M^T = \begin{pmatrix} M_{11} & M_{21} & \ldots & M_{n1} \\\ M_{12} & M_{22} & \ldots & M_{n2} \\\ \vdots & \vdots & \ddots &\vdots \\\ M_{1m} & M_{2m} & \ldots & M_{nm}\end{pmatrix}
   \end{equation}
   
   
 
**Example:**

\begin{equation}
\begin{pmatrix} 1 & 2 & 3 \\\ 4 & 5 & 6 \\\ 7 & 8 & 9 \end{pmatrix}^T = 
\begin{pmatrix} 1 & 4 & 7 \\\ 2 & 5 & 8 \\\ 3 & 6 & 9 \end{pmatrix}
\end{equation}

**Notable Relationships:**

    1.
   \begin{equation}
   (A^T)^T = (A^*)^* = A
   \end{equation}
     
    2.
   \begin{equation}
   (A \pm B)^T = A^T \pm B^T
   \end{equation}
   
    3.
   \begin{equation}
   (cA)^T = c(A^T)
   \end{equation}
    
    4.
   \begin{equation}
   (AB)^T = B^TA^T
   \end{equation}

### Hermitian Adjoint

A Hermitian Adjoint, also called a conjugate transpose, is essentially a combination of the two operators presented above. Its transformation on matrix $M$ would result in the following change in its components: $M_{ij} \rightarrow M_{ji}^*$. A Hermitian Adjoint on $M$ is denoted by $M^\dagger$. Explicitly, 

<br>

\begin{equation}
  M^\dagger = (M^T)^* \begin{pmatrix} M_{11}^* & M_{21}^* & \ldots & M_{n1}^* \\\ M_{12}^* & M_{22}^* & \ldots & M_{n2}^* \\\ \vdots & \vdots & \ddots &\vdots \\\ M_{1m}^* & M_{2m}^* & \ldots & M_{nm}^*\end{pmatrix}
   \end{equation}
   <br>

It is obvious to deduce that $M^\dagger = M^T$ if $M \in R^{m \times n}$ (i.e is real).

**Example:**


\begin{equation}
\begin{pmatrix} 1 + i & 2 \\\ 3-2i & 4 \end{pmatrix}^\dagger = \begin{pmatrix} 1 - i & 3+2i \\\ 2 & 4 \end{pmatrix}
\end{equation}

Recall that one of the most crucial points of Dirac notation is that a ket's dual (i.e a bra) is essentially its Hermitian conjugate. In other words $|\psi>^\dagger = <\psi|$ and vice versa. 

**Adjoint of $A$ in Dirac Notation:**

Let's say we have states $|\psi>$ and $\psi$ and operator $A$. The operator, $A$'s linear adjoint can be written as follows:

\begin{equation}
<A^\dagger \phi|\psi> = <\phi|A\psi>
\end{equation}

Note that according to the symmetry of an inner product, this could be rewritten as

\begin{equation}
<\psi|A^\dagger \phi>^* = <\phi|A\psi>
\end{equation}

By conjugating both sides, we get

\begin{equation}
<\psi|A^\dagger\phi> = (<\phi|A\psi>)^*
\end{equation}

Which, by changing the orders of bras and kets, gives us

\begin{equation}
<\psi|A^\dagger \phi> = <A\psi|\phi>
\end{equation}

Removing |\phi> from both sides, the following can be obtained as a result:

\begin{equation}
<|A^\dagger \equiv <A\psi|
\end{equation}

The right hand side is what is known as the adjoint of $A$. This is important because remember that a linear operator $A$ acting on a ket produces a new ket, so the effect it has on the ket's corresponding bra is equivalent to $<\psi|A^\dagger$.

**Notable Relationships:**

Using a combination of the notable relationships of the complex conjugate (presented in 1.2) and the notable relationships of the transpose (presented here in 2.5), one can derive the following:

    1.
   \begin{equation}
   (A^\dagger)^\dagger = A
   \end{equation}
     
    2.
   \begin{equation}
   (aA)^\dagger = a^*A^\dagger
   \end{equation}
   
    3.
   \begin{equation}
   (A + B)^\dagger = A^\dagger + B^\dagger
   \end{equation}
    
    4.
   \begin{equation}
   (AB)^\dagger = B^\dagger A^\dagger
   \end{equation}
   
    5.
   \begin{equation}
   (AB|\psi>)^\dagger = <\psi|B^\dagger A^\dagger
   \end{equation}


  ### Python Implementation


```python
#Basic Operation Applications 

A = np.array([[1j, 2+3j,4], [1,-2, 1j], [-2,3,0]])



AStar = np.conj(A)
ATrans = np.transpose(A)

#Defining hermitian conjugate as conjugate transpose
def hermitian(A):
    return np.conj(np.transpose(A))

ADagger = hermitian(A)

print('A = ')
view(A)

print('')

print('A* = ')
view(AStar)

print('')

print('AT = ')
view(ATrans)


print('A† = ')
view(ADagger)


```

    A = 



$\displaystyle \left[\begin{matrix}1.0 i & 2.0 + 3.0 i & 4.0\\1.0 & -2.0 & 1.0 i\\-2.0 & 3.0 & 0\end{matrix}\right]$


    
    A* = 



$\displaystyle \left[\begin{matrix}- 1.0 i & 2.0 - 3.0 i & 4.0\\1.0 & -2.0 & - 1.0 i\\-2.0 & 3.0 & 0\end{matrix}\right]$


    
    AT = 



$\displaystyle \left[\begin{matrix}1.0 i & 1.0 & -2.0\\2.0 + 3.0 i & -2.0 & 3.0\\4.0 & 1.0 i & 0\end{matrix}\right]$


    A† = 



$\displaystyle \left[\begin{matrix}- 1.0 i & 1.0 & -2.0\\2.0 - 3.0 i & -2.0 & 3.0\\4.0 & - 1.0 i & 0\end{matrix}\right]$



```python
#Testing The Five Notable Relationships of Hermitian Adjoints on matrices A, B, constant a, and vectors |psi>, <psi|

A = np.array([[1, 1j, 3], [1+2j, 0, 0], [3j, -1j,0]])
B = np.array([[3,-2j, 1j],[-3, 0, 0], [3j, -2j +3, 1]])
a = 5j
psi_ket = np.array([3j, 0, 2])
psi_bra = hermitian(psi_ket)


print('Is (A†)† = A:')
print(hermitian(hermitian(A)).all() == A.all())


print('')


print('Is (aA)† = a*A†:')
print(hermitian(a*A).all() == np.conj(a)*hermitian(A).all())


print('')

print('Is (A + B)† = A† + B†:')
print(hermitian(A+B).all() == (hermitian(A) + hermitian(B)).all())

print('')

print('Is (AB)† = B†A†:')
print(hermitian(A@B).all() == (hermitian(B)@hermitian(A)).any())


print('')

print('Is (AB|psi>)† = <psi|B†A†:')
print(hermitian(A@B@psi_ket).all() == (psi_bra@hermitian(B)@hermitian(A)).all())


#Excersise: Build a function that returns True if a matrix is Hermitian by testing it against the 5 relationships above 
```

    Is (A†)† = A:
    True
    
    Is (aA)† = a*A†:
    True
    
    Is (A + B)† = A† + B†:
    True
    
    Is (AB)† = B†A†:
    True
    
    Is (AB|psi>)† = <psi|B†A†:
    True


## 2.6 Symmetric Matrices

Symmetric Matrices are are any square matrices $M$ where $M = M^T$. The following is an example:

\begin{equation}
M = M^T = \begin{pmatrix} 1 & 2 & 3 \\\ 2 & 5 & 6 \\\ 3 & 6 & 2 \end{pmatrix}
\end{equation}

    1. The transpose of a symmetric matrix is also symmetric
    2. A scalar times a symmetric matrix is also symmetric
    3. The inverse of a symmetric matrix is equal to the inverse of the transpose
    
 \begin{equation}
 (A^T)^{-1} = A^{-1}
 \end{equation}
     
    4. If (A+B)T = AT + BT = A + B, then A + B is a symmetric matrix
    5. The product of two symmetric matrices is also symmetric if the matrices commute (AB = BA) 
    
A lot of the operators that we are concerned with in quantum mechanics are symmetrical. Namely, the two types of symmetrical matrices that we will deal with are called Hermitian operators (Section 2.9) and Unitary operators (Section 2.10)


```python
#Defining a function that tests whether a matrix is symmetrical or not

def symmCheck(a):
    return a.all() == np.transpose(a).any()

#A matrix we'd expect to be symmetrical
M = np.array([[1,2,3],[2,4,6],[3,6,2]])

#A matrix we'd expect not to be 
N = np.array([[33,-2,0],[4,5,6],[62,3,3]])

#Printing symmCheck

print('Is M Symmetrical? ', symmCheck(M))
print('Is N Symmetrical? ', symmCheck(N))

#Excercise: Create a function that generates a random matrix M, with a condition that M is symmetrical

```

    Is M Symmetrical?  True
    Is N Symmetrical?  False


## 2.7 Orthogonal Matrices

Another property that a square matrix might have is being orthogonal. A matrix $M$ is orthogonal given that its inverse is equal to its transpose. Notationally, 

\begin{equation}
A^T = A^{-1}
\end{equation}

Given the definition of a matrix's inverse (as stated in 2.2), one can see that the statement above is equivelent to the following:

\begin{equation}
A^T A = I
\end{equation}

<br>

**Example:**

\begin{equation}
A =\begin{pmatrix} 1 & 0 \\\ 0 & -1 \end{pmatrix}
\end{equation}

\begin{equation}
A^T =\begin{pmatrix} -1 & 0 \\\ 0 & 1 \end{pmatrix}
\end{equation}
Multiplying those $A$ with its transpose, 
\begin{equation}
AA^T = \begin{pmatrix} 1 & 0 \\\ 0 & -1 \end{pmatrix}\begin{pmatrix} -1 & 0 \\\ 0 & 1 \end{pmatrix} \newline = 
\begin{pmatrix} 1 & 0 \\\ 0 & 1 \end{pmatrix} \newline= I
\end{equation}

<br>
Therefore, we can deduce that $A^T = A^{-1}$, and that $A$ is orthogonal.

**Properties:**

    1. Orthogonal matrices are always symmetrical
    2. The inner product of two columns or two rows are always zero (i.e orthogonal, See Section 1.6)
    3. The product of two orthogonal matrices is also an orthogonal matrix


## 2.8 Identity Operator


A reasonable summary of everything that has been described in this section so far is the following: An operator is anything that transforms a vector into another. A linear operator, i.e an operator that preserves linear combination, can be represented by a matrix. 

As mentioned before, an identity matrix is an $n \times n$ orthogonal matrix that consists of 1s in its diagonal and 0s elsewhere,(i.e $I_{ij} = \delta_{ij}$). When multiplied with another matrix $A$, the Identity matrix produces the same matrix $A$. 

It can also be viewed as an operator that follows the same rules. Namely,

\begin{equation}
I_n|u> = |u>
\end{equation}

Where |u> is a vector, $n$ is the number of dimensions of the vector, and $I_n$ is an $n \times n$ Identity matrix. In other words, the Identity operator acting on a vector simply produces the same vector.


### Python Implementation


```python
#Identity operator function

def IOperator(vec):
    I = np.identity(len(vec))
    return I@vec

IOperator(np.array([1,2,3]))


#Excercise: Write code to show that the identity matrix is orthogonal, as I stated in 2.8

```




    array([1., 2., 3.])



## 2.9 Hermitian Operator

Section 2.3 started by explaining that the reason we're learning about operators is because they have very important applications to quantum mechanics. This is mainly because in QM, physical quantities which are usually described as functions of time now become linear operators, or more specifically, Hermitian operators. This is what is known as the third postulate of quantum mechanics. For instance, one may have heard of the Schrodinger Equation, which plays a key roll to modeling and understanding quantum systems as it can be solved to obtain the system's wavefunction ($\psi(x) \equiv <x|\psi>$). What one might not know is that it is essentially just an **eigienvector-eigenvalue** relationship of a Hermitian Operator called the Hamiltonian (more on that in chapter 3).

The topic of Hermitian operators and eigeinvalues will be revisited in Chapter 3, but for now, only a few basic properties will be stated. A Hermitian operator is represented by any matrix that is equivilent to its Hermitian Adjoint, or

\begin{equation}
H = H^{\dagger}
\end{equation}

where $H$ is a Hermitian operator. 

One can realize that in a real matrix, a Hermitian operator is equivilent to Symmetric Operators described in 2.6. Also, an important, but simple thing to realize is that for a matrix to be Hermitian,

    1. Its diagonal must consist of real numbers
    2. H_ij = H_ji*
    
 **Example:**
 
 \begin{equation}
 H = \begin{pmatrix} 1 & 1 + i & 3 + 2i \\\ 1 - i & 5 & -i \\\ 3 - 2i & i & 7\end{pmatrix}
 \end{equation}
 
 (Look at Python Implementation for showing that this is Hermitian)
 
As you can see, the diagonal is real and the image of $H_{ij}$ , $H_{ji}$ is its complex conjugate. For example, 

\begin{equation}
H_{12} = 1 + i, H_{21} = 1 - i \newline \dot{.\hspace{.095in}.}\hspace{.5in}H_{12} = H_{21}^*
\end{equation}

### Python Implementation


```python
#Defining the H used previously

H = np.array([[1,1+1j,3+2j],[1-1j,5,-1j],[3-2j,1j,7]])
HDagger = hermitian(H)

isHermitian = H.all() == HDagger.all()
HermOnVec = HDagger@np.array([1,1,1])

print('Hermitian Matrix, H = ')
view(H)

print('')

print('Hermitian Checker: ')
print(isHermitian)

print('')

print('H|u>, where |u> = [1,1,1]:')
view(HermOnVec)
```

    Hermitian Matrix, H = 



$\displaystyle \left[\begin{matrix}1.0 & 1.0 + 1.0 i & 3.0 + 2.0 i\\1.0 - 1.0 i & 5.0 & - 1.0 i\\3.0 - 2.0 i & 1.0 i & 7.0\end{matrix}\right]$


    
    Hermitian Checker: 
    True
    
    H|u>, where |u> = [1,1,1]:



$\displaystyle \left[\begin{matrix}5.0 + 3.0 i\\6.0 - 2.0 i\\10.0 - 1.0 i\end{matrix}\right]$


## 2.10 Unitary Operators 

Another important type of operator in quantum mechanics is what is called a unitary operator. A key feature of a unitary operator, $U$, is that when acted on a vector, it produces another vector with the same norm, or

\begin{equation}
U|v_1> = |v_2>
\end{equation}

where $<v_1|v_1> = <v_2|v_2>$. This is extremely crucial because recall that in Section 1.10, we stated that vectors in quantum mechanics and quantum information theory represent amplitudes that when squared, give the probabilities of obtaining a specific state. Meaning that the norm (i.e the sum of all probabilities) must always equal 1, no matter how the statevector evolves. This being given, one should note that any operator which evolves (i.e updates) the statevector should be unitary.

A unitary matrix is essentially any matrix $U$, where the following condition applies:

\begin{equation}
UU^\dagger = I
\end{equation}

In other words, $U^\dagger = U^{-1}$. Similarly to how a Hermition matrix is an complex extension to a symmetric matrix, a Unitary matrix is a complex extension to an orthogonal matrix.


**Example:**

The following is an example of a unitary matrix:

<br>


\begin{equation}
U = \frac{1}{2}\begin{pmatrix} 1 + i & 1 - i \\\ 1 - i & 1 + i \end{pmatrix}
\end{equation}

This can be proved by multiplying $U$ with $U^\dagger$, as done below:

\begin{equation}
U = \frac{1}{4}\begin{pmatrix} 1 + i & 1 - i \\\ 1 - i & 1 + i \end{pmatrix} \begin{pmatrix} 1 - i & 1 + i \\\ 1 + i & 1 - i \end{pmatrix} \newline = \begin{pmatrix} 1 & 0 \\\ 0 & 1 \end{pmatrix} \newline = I
\end{equation}


I will not do the full matrix multiplication but rather will leave it to you, and will also use it as an example in the Python Implementation.

**Inner Product Preservation:**

I previously said that a unitary operator is special because once applying on a vector, its norm stays the same. While this is true, it is not a general fact and is simply a concequence of the fact that a unitary acted on two vectors will not change their inner product (and the norm invariance is simply a case where the two vectors are the same). Inner product preservation can be derived as follows:

Say we have vectors $|u>$ and $|v>$ that are both operated by U to produce $|\psi>$ and $\phi$, respectively. Or notationally, 

\begin{equation}
|\psi> = U|u>
\end{equation}
and 
\begin{equation}
|\phi> = U|v>
\end{equation}

Now let's say we want to get the following inner-product:

\begin{equation}
<\psi|\phi>
\end{equation}

Notice that according to basic bra-ket notation, this is equivelent to

\begin{equation}
<U|u><U|v>
\end{equation}

or
\begin{equation}
<u|U^\dagger U|v>
\end{equation}

which, by the definition of a unitary matrix $U$, reduces down to

\begin{equation}
<u|v>
\end{equation}

We essentially just derived that $<\psi|\phi> = <u|v>$, or that the inner product is equal before and after the unitary transformation.

### Python Implementation


```python
#Example given above:

#Defining U and U†

U = 0.5*np.array([[1+1j, 1-1j],[1-1j, 1+1j]])
UDagger = hermitian(U)

#2x2 Identity matrix
I = np.identity(2)

#|v> and |psi> = U|v>
v = np.array([1,2])
psi = U@v

#Finding the norm of a vectors |v> and |psi>
normV = np.linalg.norm(v)
normPsi = np.linalg.norm(psi)

#UU† (Supposed to be I_2)

print('UU† = ')
view(U@UDagger)


print('')

#<v|v> vs <psi|psi>

print('Is <v|v> = <psi|psi>:')
normV == normPsi
```

    UU† = 



$\displaystyle \left[\begin{matrix}1.0 & 0\\0 & 1.0\end{matrix}\right]$


    
    Is <v|v> = <psi|psi>:





    True




```python
#Application to Quantum Computing

#Defining |0>
zero = np.array([1,0])

#Defining a unitary matrix H, which stands for Hadamard (more info in chapter 4)
H = 1/np.sqrt(2)*np.array([[1,1],[1,-1]])

print('Hadamard Matrix= ')
view(H)

print('')

#Let's define a state vector, which is initialized at |0> (i.e 100% probability of a qubit being 0)

statevector = zero

print('Statevector before Hadamard:', statevector)

statevector = H@zero

print('Statevector after Hadamard:', statevector)

print('')
```

    Hadamard Matrix= 



$\displaystyle \left[\begin{matrix}0.707106781186547 & 0.707106781186547\\0.707106781186547 & -0.707106781186547\end{matrix}\right]$


    
    Statevector before Hadamard: [1 0]
    Statevector after Hadamard: [0.70710678 0.70710678]
    


#### Interpretation:

                                    **WARNING!! MAY REQUIRE REFRESHER FROM SECTION 1.10**

<br>
<br>

This is a very crucial application to quantum computing which arguably summarizes everything we have learned so far! 

We essentially took a qubit's statevector which is initialized at

\begin{equation}
|\psi> = |0> = \begin{pmatrix}1 \\\ 0 \end{pmatrix}
\end{equation}

A unitary operator $U$ was then applied on the qubit's state which evolved it into the following state

\begin{equation}
|\psi> = \begin{pmatrix}0.7071067 \\\ 0.7071067 \end{pmatrix} \newline = 0.7071067|0> + 0.7071067|1>
\end{equation}

Remember that the two terms in the statevector represent $\alpha$ and $\beta$. Also remember that $\alpha^2$ and $\beta^2$ represent the probabilities of obtaining a 0 and 1, respectively.

This means that before applying unitary operator $H$:

\begin{equation}
\alpha^2 = 1 \newline
\beta^2 = 0
\end{equation}

And after applying unitary operator $H$:

\begin{equation}
\alpha^2 = 0.7071067^2 = 0.5 \newline
\beta^2 = 0.7071067^2 = 0.5
\end{equation}

In other words, the qubit went from having 100% chance to be measured at 0 to a 50/50 percent chance to be a 0 or 1. Physically, what has happened is that we changed the probability of the qubit ending up in either the two possible states it can be in. And this is not the equivilent to a coin flip because a the result of a coin flip is based on how much force was applied on the coin and can be predicted every time using Newton's Laws and a measurement device that is accurate enough, whilst in this case we are exploiting the intrinsically random nature of the universe to set up a qubit state to do what we want! 

Friendly advice: If you don't find this fascinating or mind-blowing, you might want to reconsider quantum computing as a whole.

## 2.11 Eigenvalues

One of the key takeaways of this chapter is that a matrix can be used as a "function" (more specifically, operator) in the sense where it can produce an output vector from an input one (by means of matrix multiplication). The topic of eigenvalues and eigenvectors arise when we consider only the linear transformations where the input and output vectors are in the same direction, as shown in the following figure:

  

Here, you can see that the only difference between $\vec{A}$ and $\vec{B}$ is the length, or in other words

\begin{equation}
\vec{B} = \lambda \vec{A}
\end{equation}

where $\lambda$ is a scalar that represents the ratio bewtween the length of $\vec{A}$ and the length of $\vec{B}$.

Now let us say we want to transform $\vec{A}$ to $\vec{B}$, which as described in Section 2.4, can be written out as follows:

\begin{equation}
O\vec{A} = \vec{B}
\end{equation}

where $O$ is a matrix.

But remember that $\vec{B}$ is simply a scalar product of $\vec{A}$, which means that we can write the linear transformation as 

\begin{equation}
O\vec{A} = \lambda \vec{A}  
\end{equation}


In other words, the effect of multiplying a vector by matrix $O$ is the same as multiplying it by constant $\lambda$. In such a case, $\lambda$ is what is called the eigenvalue of $O$. 

Let's say we have vector $|\psi>$ and and matrix $M$, where the following relationship applies:

\begin{equation}
M|\psi> = \lambda |\psi>
\end{equation}

A key thing to notice is that the relationship above can be re-written as

\begin{equation}
(M - \lambda I_n)|\psi> = 0
\end{equation}

where $I_n$ is an $n \times n$ identity matrix.

This could be used to find $\lambda$ because $M - I_n \lambda$ is a matrix that, by definition, must have a determinant of 0. Using the fact that $det(M - I_n \lambda) = 0$, one can get a polynomial of degree $n$, where its roots are the eigeinvalues of $M$, as done <a href="https://www.youtube.com/watch?v=j2B_vcp3tUQ&t=239s">here in a 3x3 case</a>. Using the fundamental theorem of algebra, we can deduce that an $n \times n$ matrix will have $n$ eigeinvalues. 

Another important relationship is that the sum of all eigenvalues is equal to the trace of $M$. Or, 

\begin{equation}
\sum_{i = 1}^n \lambda_i = Tr(M)
\end{equation}



### Eigenvalue of Unitary Matrix

In Section 2.10, a specific type of square matrix was introduced, a Unitary matrix, which preserves the length of a vector when operated on it. It was also said that in quantum mechanics, all operators must be unitary because they must preserve the length of the statevector it acts on (which must be 1, as it represents the total probability). 

The eigenvalue of a unitary matrix must always have a norm of 1. If $U \in R^{n \times n}$, then it's eigenvalue will be $\pm$ 1, but in the case of a complex matrix (i.e $U \in C^{n \times n}$), then its eigenvalues could be any value which lies in the unit circle in an argand diagram. This property is used in an imporant quantum algorithm called the Phase Estimation Algorithm (PEA) which will be discussed in a few chapters.

### Python Implementation


```python
#Finding the eigenvalues of 3x3 matrix M

M = np.array([[1,2,3],[1,5,0],[0,1,1]])
lambda_M = np.linalg.eigvals(M)

print('M = ')
view(M)

print('')
print('M has 3 eigenvalues:')

print(lambda_M)

print('')

#Checking to see whether the sum of eigenvalues is equal to the trace

if lambda_M.sum() == M.trace():
    print('Math works! Sum of eig(M) = Tr(M)')
else:
    print('Whoops.  math doesnt work... nothing does')


#Excercise: Construct a function that outputs the same result as np.linalg.eigvals. Use the method laid out above or
#any numerical method of your choice
```

    M = 



$\displaystyle \left[\begin{matrix}1 & 2 & 3\\1 & 5 & 0\\0 & 1 & 1\end{matrix}\right]$


    
    M has 3 eigenvalues:
    [5.5797401 +0.j         0.71012995+0.75566815j 0.71012995-0.75566815j]
    
    Math works! Sum of eig(M) = Tr(M)


## 2.12 Tensor Products

### Kronecker Product vs. Outer Product
Another important mathematical concept that one would very likely find themselves using in quantum computing and quantum information is the Tensor Product. Whilst the name may sound foreign to you, it is actually something that has already been discussed in this series. Specifically in Section 1.7, where the outer product was discussed. It was said that if one were to get the ket-bra of two vectors $|u>$ and $|v>$, say, both $\in R^3$, the following matrix would be the outcome:

   \begin{equation}
  |u><v| = \begin{pmatrix}v_1^*u_1 & v_2^*u_1 & v_3^*u_1 \\\ v_1^*u_2 & v_2^*u_2 & v_3^*u_2 \\\ v_1^*u_3 & v_2^*u_3& v_n^*u_3\end{pmatrix}
   \end{equation}
   
This is called the density matrix and it is result of the outer tensor product (also denoted by $\vec{u} \otimes_{outer} \vec{v}$). However, there is another type of tensor product called the Kronecker Product (denoted by $\vec{u} \otimes_{kron} \vec{v}$ or simply, $\vec{u} \otimes \vec{v}$), which is equivilent to the result obtained in the density matrix, but packaged in a different shape. When the word tensor product is used, it is most likely referring to the kronecker product

The outer product inputs two vectors of dimensions $n$ and $m$ and outputs an $n \times m$ matrix. The kronecker product, on the other hand, inputs two vectors of dimensions $n$ and $m$, and produces a new vector of dimensions $nm$. 

### Calculating Tensor Products

Like many topics in linear algebra, one would find that tensor products are much easier to compute than to describe formally. For this reason, I will take a more mechanical and practical approach to explaining how to evaluate a tensor product, as its crude utilization is the only thing that will be necessary in this series. 

To get the tensor product of vectors $\vec{u}$ and $\vec{v}$ ($\vec{u} \in R^n$ and $\vec{v} \in R^m$), all you have to do is scalar multiply the first component of $\vec{u}$ with vector $\vec{v}$, and repeat the process for all components of $\vec{u}$. You'll find that you have now made $n$ new vectors. Merge them. The result is the $\vec{u} \otimes \vec{v}$. In the case that the vectors are complex, do the same but with the complex conjugates of $u_i$.

If that description is not adequate enough for you to understand, hopefully this example with 2 and 3 dimensional complex vectors will work:

\begin{equation}
\vec{u} \otimes \vec{v} = \begin{pmatrix} u_1^* \\\ u_2^*\end{pmatrix} \otimes \begin{pmatrix} v_1 \\\ v_2 \\\ v_3\end{pmatrix} \newline  = \begin{pmatrix} u_1^* \begin{bmatrix} v_1 \\\ v_2 \\\ v_3\end{bmatrix} \\\ u_2^* \begin{bmatrix} v_1 \\\ v_2 \\\ v_3 \end{bmatrix} \end{pmatrix} = \begin{pmatrix} u_1^* v_1 \\\ u_1^* v_2 \\\ u_1^* v_3 \\\ u_2^* v_1 \\\ u_2^* v_2  \\\ u_2^* v_3\end{pmatrix}
\end{equation}

One can find that this is essentially stacking up the terms columns of the density matrix into one.

**Example:**


  
  
  The result is a 6-vector.
  

  
### Using Tensor Products to Extend to Multiple Qubit Systems

In Section 1.10, it was stated that one of the key differences between describing classical and quantum information is that classical information is described by a series of 1s and 0s, whilst quantum information is described by a vector which describes the probability of obtaining any possible series of 1s or 0s.

The basic unit of this quantum logic is called a qubit which is a linear combination of two basis vectors which represent the possible states: $|0>$ and $|1>$, where $|0> = \begin{pmatrix} 1 \\\ 0\end{pmatrix}$ and $|1> = \begin{pmatrix} 0 \\\ 1\end{pmatrix}$.

However, one cannot go very far with one qubit and nearly all quantum algorithms require multiple qubits, and this is where the tensor product comes in. Suppose one would want to describe the $|10>$ state (which is 2 in binary). To do this, one would have to take the tensor product of the two qubit states which make up |10>, |1> and |0>. So

\begin{equation}
|10> = |1> \otimes |0> \newline = \begin{pmatrix} 0 \\\ 1 \end{pmatrix} \otimes \begin{pmatrix} 1 \\\ 0 \end{pmatrix} \newline = \begin{pmatrix} 0 \begin{bmatrix} 1 \\\ 0\end{bmatrix} \\\ 1 \begin{bmatrix} 1 \\\ 0\end{bmatrix} \end{pmatrix} \newline = \begin{pmatrix}  0 \\\ 0 \\\ 1 \\\ 0 \end{pmatrix}
\end{equation}

The 4-dimensional statevector obtained above represents the basis vector |01>, where as you can see, all terms are zero except for the third component, which is 1. Generally, the result will be a $2^n$ dimensional statevector, where $n$ is the number of qubits and all the terms are zero except for the $i + 1$th index, where $i$ is the number in decimal. 

Note to Adam: Rewrite this completely!!!


```python
#Tensor Product 

u = np.array([1,2,3])
v = np.array([4,5])

outerProd = np.outer(u,v)
kronProd = np.kron(u,v)

print('u = [1 2 3], v = [4 5]')
print('u ⊗ v = ', kronProd)
print('|u><v| = ')
view(outerProd)

#Excercise: Create a function that does the same as np.kron() (Hint: Two for loops and np.append())
```

    u = [1 2 3], v = [4 5]
    u ⊗ v =  [ 4  5  8 10 12 15]
    |u><v| = 



$\displaystyle \left[\begin{matrix}4 & 5\\8 & 10\\12 & 15\end{matrix}\right]$



```python
# Go nuts here:



```

1df575054fa52664d5c21d5a82ce93d8b29ce1c4
ipynb
Jupyter Notebook
Chapter 2 - Math Tools II (Matrices and Operators).ipynb
theheavygluon/QuantumComputingSeries
76c005fa8280cd7eed619da9954a6399cc7855ad
MIT
2021-03-26T18:52:37.000Z
2021-05-18T18:06:20.000Z
Chapter 2 - Math Tools II (Matrices and Operators).ipynb
theheavygluon/QuantumComputingPrereqs
76c005fa8280cd7eed619da9954a6399cc7855ad
MIT
Chapter 2 - Math Tools II (Matrices and Operators).ipynb
theheavygluon/QuantumComputingPrereqs
76c005fa8280cd7eed619da9954a6399cc7855ad
MIT
2021-04-22T11:32:56.000Z
2021-04-22T11:32:56.000Z
Qwen/Qwen-72B
1. YES
2. YES