Commit a0344aed authored by Valerio's avatar Valerio
Browse files

partial solution lecture 7

parent 3aa42443
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Exercise Lecture 7"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import random\n",
"import copy"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"class QSimulator:\n",
"\n",
" def binrep(self, var):\n",
" return (\"{0:0\" + str(self.n_) + \"b}\").format(var)\n",
"\n",
" def __init__(self, n, verbose=False):\n",
" \"\"\"\n",
" Initialise quantum gate circuit simulator of n qubits, which can handle\n",
" up to 2^n input states.\n",
" \"\"\"\n",
" self.n_ = n\n",
" self.max_state = (1 << n) - 1\n",
" self.verbose = verbose\n",
"\n",
" def CNOT(self, iwf, cqid, qid):\n",
" \"\"\"\n",
" Apply a NOT gate controlled by qubit cqid to the qubit qid of the\n",
" input state vector.\n",
" \"\"\"\n",
" owf = copy.deepcopy(iwf)\n",
" for st in range(self.max_state + 1):\n",
" if np.absolute(iwf[st]) > 0.: # skip unused states\n",
" if st & (1 << cqid): # control qubit is set\n",
" flipst = st ^ (1 << qid) # state with flipped qid\n",
" owf[flipst] = iwf[st]\n",
" owf[st] = iwf[flipst]\n",
"\n",
" if self.verbose:\n",
" print(\"Wave function after CNOT gate:\\n\", owf, \"\\n\")\n",
"\n",
" return owf\n",
"\n",
" def H(self, iwf, qid):\n",
" \"\"\"\n",
" Apply Hadamard gate on qubit qid of the input state vector iwf.\n",
" \"\"\"\n",
" owf = copy.deepcopy(iwf)\n",
" for st in range(self.max_state + 1):\n",
" if st & (1 << qid): # qubit contributes to state\n",
" flipst = st ^ (1 << qid) # state with flipped qid\n",
" if np.absolute(iwf[st]) == 0 and np.absolute(iwf[flipst]) == 0:\n",
" continue\n",
" owf[flipst] = (iwf[flipst] + iwf[st]) / np.sqrt(2)\n",
" owf[st] = (iwf[flipst] - iwf[st]) / np.sqrt(2)\n",
"\n",
" if self.verbose:\n",
" print(\"Wave function after H gate:\\n\", owf, \"\\n\")\n",
"\n",
" return owf\n",
"\n",
" def X(self, iwf, qid):\n",
" \"\"\"\n",
" Apply Pauli-X gate on qubit qid of the input state vector iwf.\n",
" \"\"\"\n",
" owf = copy.deepcopy(iwf)\n",
" for st in range(self.max_state + 1):\n",
" flipped = st ^ (1 << qid)\n",
" owf[st] = iwf[flipped]\n",
" owf[flipped] = iwf[st]\n",
" return owf\n",
"\n",
" def Z(self, iwf, qid):\n",
" \"\"\"\n",
" Apply Pauli-Z gate on qubit qid of the input state vector iwf.\n",
" \"\"\"\n",
" owf = copy.deepcopy(iwf)\n",
" for st in range(self.max_state + 1):\n",
" if np.absolute(iwf[st]) > 0.0:\n",
" if st & (1 << qid):\n",
" owf[st] *= -1\n",
" return owf\n",
"\n",
" def U(self, iwf, cqid, qid, theta):\n",
" \"\"\"\n",
" Apply controlled time evolution operator U to qid.\n",
" \"\"\"\n",
" owf = copy.deepcopy(iwf)\n",
" for st in range(self.max_state + 1):\n",
" if st & (1 << cqid):\n",
" flipst = st ^ (1 << qid)\n",
" owf[st] += 1.j * np.sin(theta) * iwf[st]\n",
" owf[flipst] += (np.cos(theta) - 1) * iwf[st]\n",
"\n",
" if self.verbose:\n",
" print(\"Wave function after CU gate:\\n\", owf, \"\\n\")\n",
"\n",
" return owf\n",
"\n",
" def tomography(self, wf, qid):\n",
" \"\"\"\n",
" Return probability of qubit qid of interest and accumulated\n",
" probabilities of the other qubits.\n",
" \"\"\"\n",
" alpha = 0\n",
" beta = 0\n",
" for st in range(self.max_state + 1):\n",
" if st & (1 << qid):\n",
" beta += (np.conj(wf[st]) * wf[st]).real\n",
" else:\n",
" alpha += (np.conj(wf[st]) * wf[st]).real\n",
" return (alpha, beta)\n",
"\n",
" def measureQubit(self, iwf, qid):\n",
" \"\"\"\n",
" Measure qubit qid.\n",
" \"\"\"\n",
" probs_pair = self.tomography(iwf, qid)\n",
" rnd_num = random.uniform(0., 1.)\n",
" m = (rnd_num < probs_pair[1])\n",
"\n",
" if self.verbose:\n",
" print(\"The probability to measure |0>: \", probs_pair[0],\n",
" \", for |1>: \", probs_pair[1], \"\\n\",\n",
" \"Random number:\", rnd_num,\n",
" \", we measured 1: \", m, \"\\n\")\n",
"\n",
" for st in range(self.max_state + 1):\n",
" if np.abs(iwf[st]) != 0.0:\n",
" if st & (1 << qid):\n",
" if m:\n",
" iwf[st] /= np.sqrt(probs_pair[1])\n",
" else:\n",
" iwf[st] = 0\n",
" else:\n",
" if m:\n",
" iwf[st] = 0\n",
" else:\n",
" iwf[st] /= np.sqrt(probs_pair[0])\n",
"\n",
" if self.verbose:\n",
" print(\"wavefunction after measurement:\\n\", iwf, \"\\n\")\n",
"\n",
" return (iwf, m)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Sanity Check"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"def check_hadamard():\n",
" qsim = QSimulator(2)\n",
" iwf = np.zeros(qsim.max_state + 1, dtype=np.complex)\n",
" iwf[int('00', 2)] = 1. / np.sqrt(2)\n",
" iwf[int('10', 2)] = 1. / np.sqrt(2)\n",
" iwf = qsim.H(iwf, 0)\n",
" assert(np.allclose(iwf, 0.5 * np.ones(qsim.max_state + 1, dtype=np.complex)))\n",
"\n",
"\n",
"check_hadamard()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Phase Estimation"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Phase estimation\n",
"input: 0.62832 -> output: 0.62832\n"
]
}
],
"source": [
"def phase_estimate(alpha, beta, theta, verbose=False):\n",
"\n",
" qsim = QSimulator(2, verbose)\n",
" iwf = np.zeros(qsim.max_state + 1, dtype=np.complex)\n",
" iwf[int('00', 2)] = alpha # qubit 1 real part\n",
" iwf[int('10', 2)] = beta # qubit 1 imag part\n",
" if qsim.verbose:\n",
" print(\"Initial wave function:\", iwf, \"\\n\")\n",
"\n",
" iwf = qsim.H(iwf, 0)\n",
" iwf = qsim.U(iwf, 0, 1, theta)\n",
" iwf = qsim.H(iwf, 0)\n",
"\n",
" # p(|0>) = 1/2 * (1 + cos\\phi)\n",
" return np.arccos((2 * (qsim.tomography(iwf, 0)[0]) - 1))\n",
"\n",
"\n",
"alpha = 1. / np.sqrt(2)\n",
"beta = 1. / np.sqrt(2)\n",
"theta = np.pi / 5\n",
"\n",
"theta2 = phase_estimate(alpha, beta, theta)\n",
"\n",
"print(\"Phase estimation\\ninput: {:.5f} -> output: {:.5f}\"\n",
" .format(theta, theta2))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Quantum Teleportation"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Teleportation\n",
"State of Alices's qubit is 0.30810*|0> + 0.95135*|1>\n",
"State of Bob's qubit is 0.30810*|0> + 0.95135*|1>\n"
]
}
],
"source": [
"def teleport(alpha, beta, verbose=False):\n",
"\n",
" qsim = QSimulator(3, verbose)\n",
" iwf = np.zeros(qsim.max_state + 1, dtype=np.complex)\n",
" iwf[int('000', 2)] = alpha # qubit 0 real part\n",
" iwf[int('001', 2)] = beta # qubit 0 imag part\n",
" if qsim.verbose:\n",
" print(\"Initial wave function:\", iwf, \"\\n\")\n",
"\n",
" # Generate Bell state:\n",
" iwf = qsim.H(iwf, 1)\n",
" iwf = qsim.CNOT(iwf, 1, 2)\n",
"\n",
" # Bell basis measurement:\n",
" iwf = qsim.CNOT(iwf, 0, 1)\n",
" iwf = qsim.H(iwf, 0)\n",
" m0 = qsim.measureQubit(iwf, 0)\n",
" m1 = qsim.measureQubit(iwf, 1)\n",
"\n",
" # Apply knowledge:\n",
" if m1[1]:\n",
" iwf = qsim.X(iwf, 2)\n",
" if m0[1]:\n",
" iwf = qsim.Z(iwf, 2)\n",
"\n",
" prob_pair = qsim.tomography(iwf, 2)\n",
"\n",
" return np.sqrt(prob_pair[0]), np.sqrt(prob_pair[1])\n",
"\n",
"\n",
"alpha = random.uniform(0, 1)\n",
"beta = np.sqrt(1 - alpha**2) # ensure |a|^2 + |b|^2 == 1\n",
"print(\"\\nTeleportation\")\n",
"print(\"State of Alices's qubit is {:.5f}*|0> + {:.5f}*|1>\"\n",
" .format(alpha, beta))\n",
"\n",
"alpha2, beta2 = teleport(alpha, beta)\n",
"\n",
"print(\"State of Bob's qubit is {:.5f}*|0> + {:.5f}*|1>\"\n",
" .format(alpha2, beta2))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.0"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 1
}
%% Cell type:markdown id: tags:
# Exercise Lecture 7
%% Cell type:code id: tags:
``` python
import numpy as np
import random
import copy
```
%% Cell type:code id: tags:
``` python
class QSimulator:
def binrep(self, var):
return ("{0:0" + str(self.n_) + "b}").format(var)
def __init__(self, n, verbose=False):
"""
Initialise quantum gate circuit simulator of n qubits, which can handle
up to 2^n input states.
"""
self.n_ = n
self.max_state = (1 << n) - 1
self.verbose = verbose
def CNOT(self, iwf, cqid, qid):
"""
Apply a NOT gate controlled by qubit cqid to the qubit qid of the
input state vector.
"""
owf = copy.deepcopy(iwf)
for st in range(self.max_state + 1):
if np.absolute(iwf[st]) > 0.: # skip unused states
if st & (1 << cqid): # control qubit is set
flipst = st ^ (1 << qid) # state with flipped qid
owf[flipst] = iwf[st]
owf[st] = iwf[flipst]
if self.verbose:
print("Wave function after CNOT gate:\n", owf, "\n")
return owf
def H(self, iwf, qid):
"""
Apply Hadamard gate on qubit qid of the input state vector iwf.
"""
owf = copy.deepcopy(iwf)
for st in range(self.max_state + 1):
if st & (1 << qid): # qubit contributes to state
flipst = st ^ (1 << qid) # state with flipped qid
if np.absolute(iwf[st]) == 0 and np.absolute(iwf[flipst]) == 0:
continue
owf[flipst] = (iwf[flipst] + iwf[st]) / np.sqrt(2)
owf[st] = (iwf[flipst] - iwf[st]) / np.sqrt(2)
if self.verbose:
print("Wave function after H gate:\n", owf, "\n")
return owf
def X(self, iwf, qid):
"""
Apply Pauli-X gate on qubit qid of the input state vector iwf.
"""
owf = copy.deepcopy(iwf)
for st in range(self.max_state + 1):
flipped = st ^ (1 << qid)
owf[st] = iwf[flipped]
owf[flipped] = iwf[st]
return owf
def Z(self, iwf, qid):
"""
Apply Pauli-Z gate on qubit qid of the input state vector iwf.
"""
owf = copy.deepcopy(iwf)
for st in range(self.max_state + 1):
if np.absolute(iwf[st]) > 0.0:
if st & (1 << qid):
owf[st] *= -1
return owf
def U(self, iwf, cqid, qid, theta):
"""
Apply controlled time evolution operator U to qid.
"""
owf = copy.deepcopy(iwf)
for st in range(self.max_state + 1):
if st & (1 << cqid):
flipst = st ^ (1 << qid)
owf[st] += 1.j * np.sin(theta) * iwf[st]
owf[flipst] += (np.cos(theta) - 1) * iwf[st]
if self.verbose:
print("Wave function after CU gate:\n", owf, "\n")
return owf
def tomography(self, wf, qid):
"""
Return probability of qubit qid of interest and accumulated
probabilities of the other qubits.
"""
alpha = 0
beta = 0
for st in range(self.max_state + 1):
if st & (1 << qid):
beta += (np.conj(wf[st]) * wf[st]).real
else:
alpha += (np.conj(wf[st]) * wf[st]).real
return (alpha, beta)
def measureQubit(self, iwf, qid):
"""
Measure qubit qid.
"""
probs_pair = self.tomography(iwf, qid)
rnd_num = random.uniform(0., 1.)
m = (rnd_num < probs_pair[1])
if self.verbose:
print("The probability to measure |0>: ", probs_pair[0],
", for |1>: ", probs_pair[1], "\n",
"Random number:", rnd_num,
", we measured 1: ", m, "\n")
for st in range(self.max_state + 1):
if np.abs(iwf[st]) != 0.0:
if st & (1 << qid):
if m:
iwf[st] /= np.sqrt(probs_pair[1])
else:
iwf[st] = 0
else:
if m:
iwf[st] = 0
else:
iwf[st] /= np.sqrt(probs_pair[0])
if self.verbose:
print("wavefunction after measurement:\n", iwf, "\n")
return (iwf, m)
```
%% Cell type:markdown id: tags:
## Sanity Check
%% Cell type:code id: tags:
``` python
def check_hadamard():
qsim = QSimulator(2)
iwf = np.zeros(qsim.max_state + 1, dtype=np.complex)
iwf[int('00', 2)] = 1. / np.sqrt(2)
iwf[int('10', 2)] = 1. / np.sqrt(2)
iwf = qsim.H(iwf, 0)
assert(np.allclose(iwf, 0.5 * np.ones(qsim.max_state + 1, dtype=np.complex)))
check_hadamard()
```
%% Cell type:markdown id: tags:
## Phase Estimation
%% Cell type:code id: tags:
``` python
def phase_estimate(alpha, beta, theta, verbose=False):
qsim = QSimulator(2, verbose)
iwf = np.zeros(qsim.max_state + 1, dtype=np.complex)
iwf[int('00', 2)] = alpha # qubit 1 real part
iwf[int('10', 2)] = beta # qubit 1 imag part
if qsim.verbose:
print("Initial wave function:", iwf, "\n")
iwf = qsim.H(iwf, 0)
iwf = qsim.U(iwf, 0, 1, theta)
iwf = qsim.H(iwf, 0)
# p(|0>) = 1/2 * (1 + cos\phi)
return np.arccos((2 * (qsim.tomography(iwf, 0)[0]) - 1))
alpha = 1. / np.sqrt(2)
beta = 1. / np.sqrt(2)
theta = np.pi / 5
theta2 = phase_estimate(alpha, beta, theta)
print("Phase estimation\ninput: {:.5f} -> output: {:.5f}"
.format(theta, theta2))
```
%%%% Output: stream
Phase estimation
input: 0.62832 -> output: 0.62832
%% Cell type:markdown id: tags:
## Quantum Teleportation
%% Cell type:code id: tags:
``` python
def teleport(alpha, beta, verbose=False):
qsim = QSimulator(3, verbose)
iwf = np.zeros(qsim.max_state + 1, dtype=np.complex)
iwf[int('000', 2)] = alpha # qubit 0 real part
iwf[int('001', 2)] = beta # qubit 0 imag part
if qsim.verbose:
print("Initial wave function:", iwf, "\n")
# Generate Bell state:
iwf = qsim.H(iwf, 1)
iwf = qsim.CNOT(iwf, 1, 2)
# Bell basis measurement:
iwf = qsim.CNOT(iwf, 0, 1)
iwf = qsim.H(iwf, 0)
m0 = qsim.measureQubit(iwf, 0)
m1 = qsim.measureQubit(iwf, 1)
# Apply knowledge:
if m1[1]:
iwf = qsim.X(iwf, 2)
if m0[1]:
iwf = qsim.Z(iwf, 2)
prob_pair = qsim.tomography(iwf, 2)
return np.sqrt(prob_pair[0]), np.sqrt(prob_pair[1])
alpha = random.uniform(0, 1)
beta = np.sqrt(1 - alpha**2) # ensure |a|^2 + |b|^2 == 1
print("\nTeleportation")
print("State of Alices's qubit is {:.5f}*|0> + {:.5f}*|1>"
.format(alpha, beta))
alpha2, beta2 = teleport(alpha, beta)
print("State of Bob's qubit is {:.5f}*|0> + {:.5f}*|1>"
.format(alpha2, beta2))
```
%%%% Output: stream
Teleportation
State of Alices's qubit is 0.30810*|0> + 0.95135*|1>
State of Bob's qubit is 0.30810*|0> + 0.95135*|1>
%% Cell type:code id: tags:
``` python
```
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment