Commit a0344aed by Valerio

### partial solution lecture 7

parent 3aa42443
 %% 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