# Task One : Fuandamentals of Qunatum Computation

## Basic Arithmatic Operations & Complex Numbers


```python
5+9
```




    14




```python
12*4
```




    48



Complex numbers are always of the form

\begin{align}
\alpha = a + bi
\end{align}



```python
1j * 1j
```




    (-1+0j)




```python
z = 3 + 4j
w = 1 - 2j
```


```python
import numpy as np
```


```python
print('z=', z)
print('Real(z)=', np.real(z))
print('Imag(z)=', np.imag(z))
```

    z= (3+4j)
    Real(z)= 3.0
    Imag(z)= 4.0



```python
z+w
```




    (4+2j)




```python
z-w
```




    (2+6j)




```python
z/w
```




    (-1+2j)



## Complex conjugate


```python
z
```




    (3+4j)




```python
np.conj(z)
```




    (3-4j)



## Norms/Absolute Values



\begin{align} ||z|| &= \sqrt{zz^*} = \sqrt{|z|^2},\\ ||w|| &= \sqrt{ww^*} = \sqrt{|w|^2}, \end{align} 


```python
print('||z||=', np.abs(z))
```

    ||z||= 5.0



```python
print('||w||=', np.abs(w))
```

    ||w||= 2.23606797749979


## Row Vectors, Column Vectors, and Bra-Ket Notation

\begin{align} \text{Column Vector:} \ \begin{pmatrix}
a_1 \\ a_2 \\ \vdots \\ a_n
\end{pmatrix} 
\quad \quad \text{Row Vector:} \ \begin{pmatrix}
a_1, & a_2, & \cdots, & a_n
\end{pmatrix} \end{align}




```python
# Create a vector as a row
row_vector = np.array([2-1j, 7j, -3])

# Create a vector as a column
column_vector = np.array([[2+1j],
                          [-5],
                          [2j]])
```


```python
row_vector
```




    array([ 2.-1.j,  0.+7.j, -3.+0.j])



Row vectors in quantum mechanics are also called **bra-vectors**, and are denoted as follows:

\begin{align} \langle A| = \begin{pmatrix}
a_1, & a_2, \cdots, & a_n
\end{pmatrix} \end{align}

Column vectors are also called **ket-vectors** in quantum mechanics denoted as follows:

\begin{align} |B\rangle = \begin{pmatrix}
b_1 \\ b_2 \\ \vdots \\ b_n
\end{pmatrix} \end{align}

In general, if we have a column vector, i.e. a ket-vector:

\begin{align} |A\rangle = \begin{pmatrix}
a_1 \\ a_2 \\ \vdots \\ a_n
\end{pmatrix} \end{align}

the corresponding bra-vector:

\begin{align} \langle A| = \begin{pmatrix}
a_1^*, & a_2^*, & \cdots, & a_n^*
\end{pmatrix} \end{align}



```python
ket_A = np.array([[2-1j],
                  [5]])
ket_B = np.array([[7], 
                  [-3j]])
```


```python
print(ket_A + ket_B)
```

    [[9.-1.j]
     [5.-3.j]]


## Inner Product

\begin{align} \langle A| = \begin{pmatrix}
a_1, & a_2, & \cdots, & a_n
\end{pmatrix}, \quad \quad
|B\rangle = \begin{pmatrix}
b_1 \\ b_2 \\ \vdots \\ b_n
\end{pmatrix} \end{align}

Taking the inner product of $\langle A|$ and $|B\rangle$ gives the following:

\begin{align} \langle A| B \rangle &= \begin{pmatrix} 
a_1, & a_2, & \cdots, & a_n
\end{pmatrix}
\begin{pmatrix}
b_1 \\ b_2 \\ \vdots \\ b_n
\end{pmatrix}\\
&= 
a_1b_1 + a_2b_2 + \cdots + a_nb_n\\
&= \sum_{i=1}^n a_ib_i
\end{align}


```python
# Define the 4x1 matrix version of a column vector (instead of using the np.array() version):
A = np.matrix([[1-1j], 
               [3], 
               [2j], 
               [5+1j]])
A
```




    matrix([[1.-1.j],
            [3.+0.j],
            [0.+2.j],
            [5.+1.j]])




```python
# Define B as a 1x4 matrix
B = np.matrix([[-3j, 2+2j, -6j, -7]])

B
```




    matrix([[-0.-3.j,  2.+2.j, -0.-6.j, -7.+0.j]])




```python
# Compute <B|A>
np.dot(B,A)
```




    matrix([[-20.-4.j]])



## Matrices


\begin{align}
M = \begin{pmatrix}
2-i & -3 \\
-5i & 2
\end{pmatrix}
\end{align}


```python
M = np.array([[2-1j, -3],
              [-5j, 2]])

print(M)
```

    [[ 2.-1.j -3.+0.j]
     [-0.-5.j  2.+0.j]]



```python
M = np.matrix([[2-1j, -3],
               [-5j, 2]])

print(M)
```

    [[ 2.-1.j -3.+0.j]
     [-0.-5.j  2.+0.j]]


Hermitian conjugates are given by taking the conjugate transpose of the matrix


```python
print(M.H)
```

    [[ 2.+1.j -0.+5.j]
     [-3.-0.j  2.-0.j]]


## Tensor Products of Matrices

\begin{align}
\begin{pmatrix}
a & b \\
c & d
\end{pmatrix} \otimes 
\begin{pmatrix}
x & y \\
z & w
\end{pmatrix} = 
\begin{pmatrix}
a \begin{pmatrix}
x & y \\
z & w
\end{pmatrix} & b \begin{pmatrix}
x & y \\
z & w
\end{pmatrix} \\
c \begin{pmatrix}
x & y \\
z & w
\end{pmatrix} & d \begin{pmatrix}
x & y \\
z & w
\end{pmatrix}
\end{pmatrix} = 
\begin{pmatrix}
ax & ay & bx & by \\
az & aw & bz & bw \\
cx & cy & dx & dy \\
cz & cw & dz & dw
\end{pmatrix}
\end{align}


```python
A = np.matrix([[1j+8 , 2j-6], []])











```

# Task Two: Qubits, Bloch Sphere and Basis States


```python
import qiskit
qiskit.__qiskit_version__
```




    {'qiskit-terra': '0.14.2',
     'qiskit-aer': '0.5.2',
     'qiskit-ignis': '0.3.3',
     'qiskit-ibmq-provider': '0.7.2',
     'qiskit-aqua': '0.7.3',
     'qiskit': '0.19.6'}





\begin{align}
|\psi \rangle = \begin{pmatrix}
\alpha \\ \beta
\end{pmatrix}, \quad \text{where } \sqrt{\langle \psi | \psi \rangle} = 1. 
\end{align}

Think of Qubit as an Electron:

\begin{align}
\text{spin-up}: \ |0\rangle &= \begin{pmatrix} 1\\0 \end{pmatrix} \\
\text{spin-down}: \ |1\rangle & = \begin{pmatrix} 0\\1 \end{pmatrix}
\end{align}


```python
import numpy as np
from qiskit import *
from qiskit.visualization import plot_bloch_vector
%matplotlib inline
```

Another representation is via Bloch Sphere:


```python
plot_bloch_vector([0,0,1], title="Spin-Up")
```

## Spin + / - :

\begin{align}
\text{spin +}: \ |+\rangle &= \begin{pmatrix} 1/\sqrt{2} \\ 1/\sqrt{2} \end{pmatrix} = \frac{1}{\sqrt{2}} \left(|0\rangle + |1\rangle\right) \\
\text{spin -}: \ |-\rangle & = \begin{pmatrix} 1/\sqrt{2} \\ -1/\sqrt{2} \end{pmatrix} = \frac{1}{\sqrt{2}} \left(|0\rangle - |1\rangle\right)
\end{align}


```python
plot_bloch_vector([1,0,0], title="spin-plus")
```

## Basis States


\begin{align}
 |0\rangle &= \begin{pmatrix} 1\\0 \end{pmatrix} \\
 |1\rangle & = \begin{pmatrix} 0\\1 \end{pmatrix}
\end{align}
Preapring other states from Basis States:
\begin{align}
|00 \rangle &= |0\rangle \otimes |0\rangle = \begin{pmatrix} 1\\0 \end{pmatrix} \otimes \begin{pmatrix} 1\\0 \end{pmatrix} = \begin{pmatrix} 1\\0\\0\\0 \end{pmatrix} \\
|01 \rangle &= |0\rangle \otimes |1\rangle = \begin{pmatrix} 1\\0 \end{pmatrix} \otimes \begin{pmatrix} 0\\1 \end{pmatrix} = \begin{pmatrix} 0\\1\\0\\0 \end{pmatrix} \\
|10 \rangle &= |1\rangle \otimes |0\rangle = \begin{pmatrix} 0\\1 \end{pmatrix} \otimes \begin{pmatrix} 1\\0 \end{pmatrix} = \begin{pmatrix} 0\\0\\1\\0 \end{pmatrix} \\
|11 \rangle &= |1\rangle \otimes |1\rangle = \begin{pmatrix} 0\\1 \end{pmatrix} \otimes \begin{pmatrix} 0\\1 \end{pmatrix} = \begin{pmatrix} 0\\0\\0\\1 \end{pmatrix}
\end{align}


```python
qubit1_state = np.matrix([[1],[0]]) 
qubit2_state = np.matrix([[0],[1]])

two_qubit_state =np.kron(qubit1_state, qubit2_state)
```


```python

```

# Task Three: Qunatum Gates and Circuits


```python
from qiskit import *
from qiskit.visualization import plot_bloch_multivector
```

## Pauli Matrices

\begin{align}
I = \begin{pmatrix} 1&0 \\ 0&1 \end{pmatrix}, \quad
X = \begin{pmatrix} 0&1 \\ 1&0 \end{pmatrix}, \quad
Y = \begin{pmatrix} 0&i \\ -i&0 \end{pmatrix}, \quad
Z = \begin{pmatrix} 1&0 \\ 0&-1 \end{pmatrix} \quad
\end{align}

## X-gate

The X-gate is represented by the Pauli-X matrix:

$$ X = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix} = |0\rangle\langle1| + |1\rangle\langle0| $$

Effect a gate has on a qubit: 

$$ X|0\rangle = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix}\begin{bmatrix} 1 \\ 0 \end{bmatrix} = \begin{bmatrix} 0 \\ 1 \end{bmatrix} = |1\rangle$$



```python
# Let's do an X-gate on a |0> qubit
qc = QuantumCircuit(1)
qc.x(0)
qc.draw('mpl')
```


```python
# Let's see the result
backend = Aer.get_backend('statevector_simulator')
out = execute(qc,backend).result().get_statevector()
plot_bloch_multivector(out)
```

## Z & Y-Gate



$$ Y = \begin{bmatrix} 0 & -i \\ i & 0 \end{bmatrix} \quad\quad\quad\quad Z = \begin{bmatrix} 1 & 0 \\ 0 & -1 \end{bmatrix} $$

$$ Y = -i|0\rangle\langle1| + i|1\rangle\langle0| \quad\quad Z = |0\rangle\langle0| - |1\rangle\langle1| $$





```python
qc.y(0) # Do Y-gate on qubit 0
qc.z(0) # Do Z-gate on qubit 0
qc.draw('mpl')
```

## Hadamard Gate



$$ H = \tfrac{1}{\sqrt{2}}\begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix} $$

We can see that this performs the transformations below:

$$ H|0\rangle = |+\rangle $$

$$ H|1\rangle = |-\rangle $$


```python
qc = QuantumCircuit(3)
# Apply H-gate to each qubit:
for qubit in range(3):
    qc.h(qubit)
# See the circuit:
qc.draw('mpl')
```

## Identity Gate



$$
I = \begin{bmatrix} 1 & 0 \\ 0 & 1\end{bmatrix}
$$



$$ I = XX $$




```python
qc = QuantumCircuit(1)
qc.i(0)
qc.draw('mpl')
```

 ** Other Gates: S-gate , T-gate, U-gate

## Multiple Qubits, Entanglement

## Multiple Qubits

The state of two qubits :

$$ |psi\rangle = a_{00}|00\rangle + a_{01}|01\rangle + a_{10}|10\rangle + a_{11}|11\rangle = \begin{bmatrix} a_{00} \\ a_{01} \\ a_{10} \\ a_{11} \end{bmatrix} $$


```python
from qiskit import * 
```


```python
qc = QuantumCircuit(3)

for qubit in range(3):
    qc.h(qubit)

qc.draw('mpl')
```

Each qubit is in the state $|+\rangle$, so we should see the vector:

$$ 
|{+++}\rangle = \frac{1}{\sqrt{8}}\begin{bmatrix} 1 \\ 1 \\ 1 \\ 1 \\
                              1 \\ 1 \\ 1 \\ 1 \\
              \end{bmatrix}
$$


```python
backend = Aer.get_backend('statevector_simulator')
final_state= execute(qc, backend).result().get_statevector()
print(final_state)
```

    [0.35355339+0.j 0.35355339+0.j 0.35355339+0.j 0.35355339+0.j
     0.35355339+0.j 0.35355339+0.j 0.35355339+0.j 0.35355339+0.j]



```python
qc = QuantumCircuit(2)

qc.x(0)
qc.h(1)

qc.draw('mpl')
```



$$
X|q_1\rangle \otimes H|q_0\rangle = (X\otimes H)|q_1 q_0\rangle
$$

The operation looks like this:

$$
X\otimes H = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix} \otimes \tfrac{1}{\sqrt{2}}\begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix} = \frac{1}{\sqrt{2}}
\begin{bmatrix} 0 & 0 & 1 & 1 \\
                0 & 0 & 1 & -1 \\
                1 & 1 & 0 & 0 \\
                1 & -1 & 0 & 0 \\
\end{bmatrix}
$$

Which we can then apply to our 4D statevector $|q_1 q_0\rangle$. You will often see the clearer notation:

$$
X\otimes H = 
\begin{bmatrix} 0 & H \\
               H & 0\\
\end{bmatrix}
$$

## C-Not Gate


```python
qc = QuantumCircuit(2)

qc.cx(0, 1)

qc.draw('mpl')


```

Classical truth table of C-Not gate:

| Input (t,c) | Output (t,c) |
|:-----------:|:------------:|
| 00          | 00           |
| 01          | 11           |
| 10          | 10           |
| 11          | 01           |



## Entanglement


```python
qc = QuantumCircuit(2)
# Apply H-gate to the first:
qc.h(0)
qc.draw('mpl')
```


```python
# Let's see the result:
backend = Aer.get_backend('statevector_simulator')
final_state = execute(qc,backend).result().get_statevector()
print(final_state)
```

    [0.70710678+0.j 0.70710678+0.j 0.        +0.j 0.        +0.j]


Quantum System Sate is:

$$
|0{+}\rangle = \tfrac{1}{\sqrt{2}}(|00\rangle + |01\rangle)
$$



```python
qc = QuantumCircuit(2)
# Apply H-gate to the first:
qc.h(0)
# Apply a CNOT:
qc.cx(0,1)
qc.draw('mpl')
```


```python
# Let's see the result:
backend = Aer.get_backend('statevector_simulator')
final_state = execute(qc,backend).result().get_statevector()
print(final_state)
```

    [0.70710678+0.j 0.        +0.j 0.        +0.j 0.70710678+0.j]


We see we have this final state (Bell State):

$$
\text{CNOT}|0{+}\rangle = \tfrac{1}{\sqrt{2}}(|00\rangle + |11\rangle)
$$ 



Other Bell States:

## Bernstein-Vazirani Algorithm


A black-box function $f$, which takes as input a string of bits ($x$), and returns either $0$ or $1$, that is:

$$f(\{x_0,x_1,x_2,...\}) \rightarrow 0 \textrm{ or } 1 \textrm{ where } x_n \textrm{ is }0 \textrm{ or } 1  $$ 

The function is guaranteed to return the bitwise product of the input with some string, $s$. 


In other words, given an input $x$, $f(x) = s \cdot x \, \text{(mod 2)} =\ x_0 * s_0+x_1*s_1+x_2*s_2+...\ $ mod 2

# Classical-solution:
    
|Input(x)|
|:-----:|
|100...0|
|010...0|
|001...0|
|000...1|

Where each query reveals a different bit of $s$ (the bit $s_i$)

# The quantum Bernstein-Vazirani Oracle:
    
1. Initialise the inputs qubits to the $|0\rangle^{\otimes n}$ state, and output qubit to $|{-}\rangle$.
2. Apply Hadamard gates to the input register
3. Query the oracle
4. Apply Hadamard gates to the input register
5. Measure

## Example Two Qubits:

<ol>
    <li> The register of two qubits is initialized to zero:
    

$$\lvert \psi_0 \rangle = \lvert 0 0 \rangle$$

 
   </li>

   <li> Apply a Hadamard gate to both qubits:
    

$$\lvert \psi_1 \rangle = \frac{1}{2} \left( \lvert 0 0 \rangle + \lvert 0 1 \rangle + \lvert 1 0 \rangle + \lvert 1 1 \rangle \right) $$

 
   </li>

   <li> For the string $s=11$, the quantum oracle performs the operation:
$$
|x \rangle \xrightarrow{f_s} (-1)^{x\cdot 11} |x \rangle. 
$$

$$\lvert \psi_2 \rangle = \frac{1}{2} \left( (-1)^{00\cdot 11}|00\rangle + (-1)^{01\cdot 11}|01\rangle + (-1)^{10\cdot 11}|10\rangle + (-1)^{11\cdot 11}|11\rangle \right)$$

$$\lvert \psi_2 \rangle = \frac{1}{2} \left( \lvert 0 0 \rangle - \lvert 0 1 \rangle - \lvert 1 0 \rangle + \lvert 1 1 \rangle \right)$$

 
   </li>

   <li> Apply a Hadamard gate to both qubits:
    

$$\lvert \psi_3 \rangle = \lvert 1 1 \rangle$$

 
   </li>

   <li> Measure to find the secret string $s=11$
   </li>


</ol>



```python
from qiskit import *
%matplotlib inline
from qiskit.tools.visualization import plot_histogram
```


```python
secret_num = '101001'
```


```python
circuit = QuantumCircuit(6+1,6)
circuit.h([0,1,2,3,4,5])

circuit.x(6)
circuit.h(6)

circuit.barrier()


circuit.cx(5,6)
circuit.cx(3,6)
circuit.cx(0,6)

circuit.barrier()
circuit.h([0,1,2,3,4,5])

circuit.measure([0,1,2,3,4,5],[0,1,2,3,4,5])
```




    <qiskit.circuit.instructionset.InstructionSet at 0x277a2388>




```python
circuit.draw(output= 'mpl')
```


```python
simulator = Aer.get_backend('qasm_simulator')
result = execute(circuit, backend= simulator, shots = 1).result()
counts= result.get_counts()
print(counts)
```

    {'101001': 1}



```python

```

cfef1a236a4153f94595622c81d68e76e8a34576
ipynb
Jupyter Notebook
Qiskit/Guided_Project.ipynb
brij2804/quantum-computing
925d08b2281f49b9398ca04b3949a755b9592f28
MIT
Qiskit/Guided_Project.ipynb
brij2804/quantum-computing
925d08b2281f49b9398ca04b3949a755b9592f28
MIT
Qiskit/Guided_Project.ipynb
brij2804/quantum-computing
925d08b2281f49b9398ca04b3949a755b9592f28
MIT
Qwen/Qwen-72B
1. YES
2. YES