mirror of
https://github.com/quantumjim/Quantum-Computation-course-Basel.git
synced 2026-09-30 09:38:22 +02:00
33 KiB
33 KiB
In [ ]:
!pip install qiskit
!pip install qiskit-aer
!pip install qiskit-ibm-runtimeIn [ ]:
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
from qiskit.visualization import plot_histogram
import numpy as np
from tests1 import *In [ ]:
def NOT(inp):
"""An NOT gate.
Parameters:
inp (str): Input, encoded in qubit 0.
Returns:
QuantumCircuit: Output NOT circuit.
str: Output value measured from qubit 0.
"""
qc = QuantumCircuit(1, 1) # A quantum circuit with a single qubit and a single classical bit
qc.reset(0)
# We encode '0' as the qubit state |0⟩, and '1' as |1⟩
# Since the qubit is initially |0⟩, we don't need to do anything for an input of '0'
# For an input of '1', we do an x to rotate the |0⟩ to |1⟩
if inp=='1':
qc.x(0)
# barrier between input state and gate operation
qc.barrier()
# Now we've encoded the input, we can do a NOT on it using x
qc.x(0)
#barrier between gate operation and measurement
qc.barrier()
# Finally, we extract the |0⟩/|1⟩ output of the qubit and encode it in the bit c[0]
qc.measure(0,0)
# We'll run the program on a simulator
backend = AerSimulator()
# Since the output will be deterministic, we can use just a single shot to get it
job = backend.run(qc, shots=1, memory=True)
output = job.result().get_memory()[0]
return qc, outputIn [ ]:
for inp in ['0', '1']:
qc, out = NOT(inp)
print('NOT with input',inp,'gives output',out)
display(qc.draw())
print('\n')In [ ]:
### BEGIN SOLUTION
def XOR(inp1,inp2):
"""An XOR gate.
Parameters:
inpt1 (str): Input 1, encoded in qubit 0.
inpt2 (str): Input 2, encoded in qubit 1.
Returns:
QuantumCircuit: Output XOR circuit.
str: Output value measured from qubit 1.
"""
qc = QuantumCircuit(2, 1)
qc.reset(range(2))
if inp1=='1':
qc.x(0)
if inp2=='1':
qc.x(1)
# barrier between input state and gate operation
qc.barrier()
# this is where your program for quantum XOR gate goes
# barrier between input state and gate operation
qc.barrier()
qc.measure(1,0) # output from qubit 1 is measured
#We'll run the program on a simulator
backend = AerSimulator()
#Since the output will be deterministic, we can use just a single shot to get it
job = backend.run(qc, shots=1, memory=True)
output = job.result().get_memory()[0]
return qc, output
### END SOLUTIONIn [ ]:
## Test the function
for inp1 in ['0', '1']:
for inp2 in ['0', '1']:
qc, output = XOR(inp1, inp2)
print('XOR with inputs',inp1,inp2,'gives output',output)
display(qc.draw())
print('\n')
test_xor(XOR) # DO NOT EDIT THIS LINEIn [ ]:
### BEGIN SOLUTION
def AND(inp1,inp2):
"""An AND gate.
Parameters:
inpt1 (str): Input 1, encoded in qubit 0.
inpt2 (str): Input 2, encoded in qubit 1.
Returns:
QuantumCircuit: Output XOR circuit.
str: Output value measured from qubit 2.
"""
qc = QuantumCircuit(3, 1)
qc.reset(range(2))
if inp1=='1':
qc.x(0)
if inp2=='1':
qc.x(1)
qc.barrier()
# this is where your program for quantum AND gate goes
qc.barrier()
qc.measure(2, 0) # output from qubit 2 is measured
# We'll run the program on a simulator
backend = AerSimulator()
#Since the output will be deterministic, we can use just a single shot to get it
job = backend.run(qc, shots=1, memory=True)
output = job.result().get_memory()[0]
return qc, output
# Test AND gate outputs using assert statements
### END SOLUTIONIn [ ]:
## Test the function
for inp1 in ['0', '1']:
for inp2 in ['0', '1']:
qc, output = AND(inp1, inp2)
print('AND with inputs',inp1,inp2,'gives output',output)
display(qc.draw())
print('\n')
test_and(AND) # DO NOT EDIT THIS LINEIn [ ]:
### BEGIN SOLUTION
def NAND(inp1,inp2):
"""An NAND gate.
Parameters:
inpt1 (str): Input 1, encoded in qubit 0.
inpt2 (str): Input 2, encoded in qubit 1.
Returns:
QuantumCircuit: Output NAND circuit.
str: Output value measured from qubit 2.
"""
qc = QuantumCircuit(3, 1)
qc.reset(range(3))
if inp1=='1':
qc.x(0)
if inp2=='1':
qc.x(1)
qc.barrier()
# this is where your program for quantum NAND gate goes
qc.barrier()
qc.measure(2, 0) # output from qubit 2 is measured
# We'll run the program on a simulator
backend = AerSimulator()
#Since the output will be deterministic, we can use just a single shot to get it
job = backend.run(qc, shots=1, memory=True)
output = job.result().get_memory()[0]
return qc, output
# Test NAND gate outputs using assert statements
### END SOLUTIONIn [ ]:
## Test the function
for inp1 in ['0', '1']:
for inp2 in ['0', '1']:
qc, output = NAND(inp1, inp2)
print('NAND with inputs',inp1,inp2,'gives output',output)
display(qc.draw())
print('\n')
test_nand(NAND) # DO NOT EDIT THIS LINEIn [ ]:
### BEGIN SOLUTION
def OR(inp1,inp2):
"""An OR gate.
Parameters:
inpt1 (str): Input 1, encoded in qubit 0.
inpt2 (str): Input 2, encoded in qubit 1.
Returns:
QuantumCircuit: Output XOR circuit.
str: Output value measured from qubit 2.
"""
qc = QuantumCircuit(3, 1)
qc.reset(range(3))
if inp1=='1':
qc.x(0)
if inp2=='1':
qc.x(1)
qc.barrier()
# this is where your program for quantum OR gate goes
qc.barrier()
qc.measure(2, 0) # output from qubit 2 is measured
# We'll run the program on a simulator
backend = AerSimulator()
#Since the output will be deterministic, we can use just a single shot to get it
job = backend.run(qc, shots=1, memory=True)
output = job.result().get_memory()[0]
return qc, output
### END SOLUTIONIn [ ]:
## Test the function
for inp1 in ['0', '1']:
for inp2 in ['0', '1']:
qc, output = OR(inp1, inp2)
print('OR with inputs',inp1,inp2,'gives output',output)
display(qc.draw())
print('\n')
test_or(OR) # DO NOT EDIT THIS LINEIn [ ]:
from qiskit_ibm_runtime.fake_provider import FakeYorktownV2
backend = FakeYorktownV2()In [ ]:
backend.configuration().coupling_mapIn [ ]:
qc_and = QuantumCircuit(3)
qc_and.ccx(0,1,2)
print('AND gate')
display(qc_and.draw())
print('\n\nTranspiled AND gate for hardware with the required connectiviy')
qc_and.decompose().draw()In [ ]:
# run the cell to define AND gate for real quantum system
def AND(inp1, inp2, backend, layout):
qc = QuantumCircuit(3, 1)
qc.reset(range(3))
if inp1=='1':
qc.x(0)
if inp2=='1':
qc.x(1)
qc.barrier()
qc.ccx(0, 1, 2)
qc.barrier()
qc.measure(2, 0)
qc_trans = transpile(qc, backend, initial_layout=layout, optimization_level=3)
return qc_transIn [ ]:
# Assign your choice of the initial_layout to the variable layout1 as a list
# For example
# layout = [0,2,4]
layout = [0,2,4]In [ ]:
for input1 in ['0','1']:
for input2 in ['0','1']:
qc_trans1 = AND(input1, input2, backend, layout)
print('For input '+input1+input2)
print('# of nonlocal gates =',qc_trans1.num_nonlocal_gates())
test_compilation(layout) # DO NOT EDIT THIS LINEIn [ ]:
ket_0 = np.array([[1], [0]], dtype=complex)
ket_1 = np.array([[0], [1]], dtype=complex)
### BEGIN SOLUTION
def find_orthogonal_state(ket_zero_bar, theta):
"""
Calculates the orthogonal state |ψ1⟩ for a given |ψ0⟩.
Args:
ket_zero_bar: A NumPy array representing the state |ψ0⟩.
theta: The angle theta in radians.
Returns:
A NumPy array representing the orthogonal state |ψ1⟩.
"""
# --- YOUR CODE HERE ---
# Hint: An orthogonal state can be found by swapping the amplitudes
# of the original state and changing the sign of one of them.
# For |ψ0⟩ = a|0⟩ + b|1⟩, an orthogonal state is |ψ1⟩ = b|0⟩ - a|1⟩.
# Another valid orthogonal state is |ψ1⟩ = -b|0⟩ + a|1⟩.
pass # Replace this with your implementation
def find_mutually_unbiased_basis_plus(ket_zero_bar, ket_one_bar):
"""
Calculates the |ψ+⟩ state for the mutually unbiased basis.
Args:
ket_zero_bar: A NumPy array representing the state |ψ0⟩.
ket_one_bar: A NumPy array representing the state |ψ1⟩.
Returns:
A NumPy array representing the |ψ+⟩ state.
"""
# --- YOUR CODE HERE ---
# Hint: The |+⟩ state in the standard basis is (|0⟩ + |1⟩) / sqrt(2).
# In a new basis, it will be a superposition of the new basis vectors.
pass # Replace this with your implementation
def find_mutually_unbiased_basis_minus(ket_zero_bar, ket_one_bar):
"""
Calculates the |ψ-⟩ state for the mutually unbiased basis.
Args:
ket_zero_bar: A NumPy array representing the state |ψ0⟩.
ket_one_bar: A NumPy array representing the state |ψ1⟩.
Returns:
A NumPy array representing the |ψ-⟩ state.
"""
# --- YOUR CODE HERE ---
# Hint: The |-⟩ state in the standard basis is (|0⟩ - |1⟩) / sqrt(2).
# In a new basis, it will also be a superposition of the new basis vectors.
pass # Replace this with your implementation
### END SOLTUIONIn [ ]:
# Test your solutions
test_alt(find_orthogonal_state,find_mutually_unbiased_basis_plus,find_mutually_unbiased_basis_minus) # DO NOT EDIT THIS LINEIn [ ]:
### BEGIN SOLUTION
# --- Part 1: Define the Matrices ---
# Define the Pauli matrices and the Identity matrix as NumPy arrays.
# Use dtype=complex for matrices with complex entries.
X = None # Replace None with the definition of the X matrix
Y = None # Replace None with the definition of the Y matrix
Z = None # Replace None with the definition of the Z matrix
I = None # Replace None with the definition of the Identity matrix
# This dictionary is used by the test functions.
paulis = {'X': X, 'Y': Y, 'Z': Z}
# --- Part 2: Implement the Test Functions ---
# Complete the following functions to test the properties of the Pauli matrices.
def test_square_to_identity(P):
"""
(a) Check if each Pauli matrix squares to the identity matrix.
Returns:
np.array: matrix of your results
"""
# Hint:
# calculate P @ P
pass # Your implementation here
def test_anticommutation(P1, P2):
"""
(b) Check if P1 @ P2 = -P2 @ P1 for any pair of distinct Paulis.
Returns:
[np.array1,np.array2]: list of the left and right side of the equation for one pair
"""
# Hint: You can check the pairs (X, Y), (Y, Z), and (Z, X).
# For each pair (P1, P2), check if P1 @ P2 is close to -(P2 @ P1).
pass # Your implementation here
def test_product_relation():
"""
(c) Check if the Pauli product relations hold (XY=iZ, YZ=iX, ZX=iY).
Returns:
list[np.array]: The 3 matrices resulting from your calculations in the order above
"""
# Hint: Check each of the three relations separately. For example,
# check if np.allclose(X @ Y, 1j * Z).
# All three must be correct for the function to pass.
pass # Your implementation here
def get_eigenvalues_and_eigenvectors():
"""
(d) Calculate the eigenvalues and eigenvectors for each Pauli matrix.
Returns:
tuple: A tuple containing two dictionaries:
(eigenvalues_dict, eigenvectors_dict)
- eigenvalues_dict: A dictionary where keys are the names
('X', 'Y', 'Z') and values are the corresponding eigenvalues.
- eigenvectors_dict: A dictionary where keys are the names
and values are the corresponding eigenvectors.
"""
# Hint: Use a loop and np.linalg.eig() for each Pauli matrix. This
# function returns a tuple of (eigenvalues, eigenvectors). Store these
# in the two dictionaries with the correct keys ('X', 'Y', 'Z').
eigenvalues_dict = {}
eigenvectors_dict = {}
# Your implementation here
return eigenvalues_dict, eigenvectors_dict
### END SOLUTION
In [ ]:
# Test your solutions
test_paulis(I,X,Y,Z,test_square_to_identity,test_anticommutation,test_product_relation,get_eigenvalues_and_eigenvectors) # DO NOT EDIT THIS LINEIn [ ]:
### BEGIN SOLUTION
# --- Part 1: Define the Matrices ---
# Define the Hadamard and Identity matrices as NumPy arrays.
H = None # Replace None with the definition of the Hadamard matrix
# --- Part 2: Implement the Test Functions ---
# Complete the following functions to test the properties of the Hadamard matrix.
def get_hadamard_eigen_system():
"""
(a) Find the eigenvectors and eigenvalues of the Hadamard matrix.
Returns:
tuple: A tuple of (eigenvalues, eigenvectors)
"""
# Hint: Use np.linalg.eig() on the Hadamard matrix H. Do it by hand first!
pass # Your implementation here
def test_hadamard_squares_to_identity():
"""
(b) Show that H squares to the identity matrix.
Returns:
np.array: Result of the calculation asked for
"""
# Hint: Calculate H @ H and compare the result with I using np.allclose().
# Do it by hand first!
pass # Your implementation here
def test_hadamard_pauli_transformation():
"""
(c) Show that conjugating a Pauli with H results in another Pauli.
Specifically, verify:
1. H X H† =?
2. H Y H† = ?
3. H Z H† =?
Returns:
[str1,str2,str3]: strings must be a X Y Z or I and must be written as
iP, -P, -iP or P, where P is the correct Pauli.
"""
# Hint: The dagger/conjugate transpose of a matrix M is M.T.conj().
# Do it by hand first!
pass # Your implementation here
### END SOLTUIONIn [ ]:
# Test your solutions
test_hadamard(H,get_hadamard_eigen_system,test_hadamard_squares_to_identity,test_hadamard_pauli_transformation) # DO NOT EDIT THIS LINE