11 - The Quantum Convolution Neural Network
[1]:
# This code is at:
# https://qiskit-community.github.io/qiskit-machine-learning/tutorials/11_quantum_convolutional_neural_networks.html
[2]:
import json
import matplotlib.pyplot as plt
import numpy as np
from IPython.display import clear_output
from qiskit import QuantumCircuit
from qiskit.circuit import ParameterVector
from qiskit.circuit.library import ZFeatureMap
from qiskit.quantum_info import SparsePauliOp
#from qiskit.primitives import StatevectorEstimator as Estimator
from quantumrings.toolkit.qiskit import QrEstimatorV1 as Estimator
from qiskit_machine_learning.optimizers import COBYLA
from qiskit_machine_learning.utils import algorithm_globals
from qiskit_machine_learning.algorithms.classifiers import NeuralNetworkClassifier
#from qiskit_machine_learning.neural_networks import EstimatorQNN
from quantumrings.toolkit.qiskit import QrEstimatorQNN as EstimatorQNN
from sklearn.model_selection import train_test_split
algorithm_globals.random_seed = 12345
estimator = Estimator()
[3]:
# We now define a two qubit unitary as defined in [3]
def conv_circuit(params):
target = QuantumCircuit(2)
target.rz(-np.pi / 2, 1)
target.cx(1, 0)
target.rz(params[0], 0)
target.ry(params[1], 1)
target.cx(0, 1)
target.ry(params[2], 1)
target.cx(1, 0)
target.rz(np.pi / 2, 0)
return target
# Let's draw this circuit and see what it looks like
params = ParameterVector("θ", length=3)
circuit = conv_circuit(params)
circuit.draw("mpl", style="clifford")
[3]:
[4]:
def conv_layer(num_qubits, param_prefix):
qc = QuantumCircuit(num_qubits, name="Convolutional Layer")
qubits = list(range(num_qubits))
param_index = 0
params = ParameterVector(param_prefix, length=num_qubits * 3)
for q1, q2 in zip(qubits[0::2], qubits[1::2]):
qc = qc.compose(conv_circuit(params[param_index : (param_index + 3)]), [q1, q2])
qc.barrier()
param_index += 3
for q1, q2 in zip(qubits[1::2], qubits[2::2] + [0]):
qc = qc.compose(conv_circuit(params[param_index : (param_index + 3)]), [q1, q2])
qc.barrier()
param_index += 3
qc_inst = qc.to_instruction()
qc = QuantumCircuit(num_qubits)
qc.append(qc_inst, qubits)
return qc
circuit = conv_layer(4, "θ")
circuit.decompose().draw("mpl", style="clifford")
[4]:
[5]:
def pool_circuit(params):
target = QuantumCircuit(2)
target.rz(-np.pi / 2, 1)
target.cx(1, 0)
target.rz(params[0], 0)
target.ry(params[1], 1)
target.cx(0, 1)
target.ry(params[2], 1)
return target
params = ParameterVector("θ", length=3)
circuit = pool_circuit(params)
circuit.draw("mpl", style="clifford")
[5]:
[6]:
def pool_layer(sources, sinks, param_prefix):
num_qubits = len(sources) + len(sinks)
qc = QuantumCircuit(num_qubits, name="Pooling Layer")
param_index = 0
params = ParameterVector(param_prefix, length=num_qubits // 2 * 3)
for source, sink in zip(sources, sinks):
qc = qc.compose(pool_circuit(params[param_index : (param_index + 3)]), [source, sink])
qc.barrier()
param_index += 3
qc_inst = qc.to_instruction()
qc = QuantumCircuit(num_qubits)
qc.append(qc_inst, range(num_qubits))
return qc
sources = [0, 1]
sinks = [2, 3]
circuit = pool_layer(sources, sinks, "θ")
circuit.decompose().draw("mpl", style="clifford")
[6]:
[7]:
def generate_dataset(num_images):
images = []
labels = []
hor_array = np.zeros((6, 8))
ver_array = np.zeros((4, 8))
j = 0
for i in range(0, 7):
if i != 3:
hor_array[j][i] = np.pi / 2
hor_array[j][i + 1] = np.pi / 2
j += 1
j = 0
for i in range(0, 4):
ver_array[j][i] = np.pi / 2
ver_array[j][i + 4] = np.pi / 2
j += 1
for n in range(num_images):
rng = algorithm_globals.random.integers(0, 2)
if rng == 0:
labels.append(-1)
random_image = algorithm_globals.random.integers(0, 6)
images.append(np.array(hor_array[random_image]))
elif rng == 1:
labels.append(1)
random_image = algorithm_globals.random.integers(0, 4)
images.append(np.array(ver_array[random_image]))
# Create noise
for i in range(8):
if images[-1][i] == 0:
images[-1][i] = algorithm_globals.random.uniform(0, np.pi / 4)
return images, labels
[8]:
images, labels = generate_dataset(50)
train_images, test_images, train_labels, test_labels = train_test_split(
images, labels, test_size=0.3, random_state=246
)
[9]:
fig, ax = plt.subplots(2, 2, figsize=(10, 6), subplot_kw={"xticks": [], "yticks": []})
for i in range(4):
ax[i // 2, i % 2].imshow(
train_images[i].reshape(2, 4), # Change back to 2 by 4
aspect="equal",
)
plt.subplots_adjust(wspace=0.1, hspace=0.025)
[10]:
feature_map = ZFeatureMap(8)
feature_map.decompose().draw("mpl", style="clifford")
[10]:
[11]:
feature_map = ZFeatureMap(8)
ansatz = QuantumCircuit(8, name="Ansatz")
# First Convolutional Layer
ansatz.compose(conv_layer(8, "c1"), list(range(8)), inplace=True)
# First Pooling Layer
ansatz.compose(pool_layer([0, 1, 2, 3], [4, 5, 6, 7], "p1"), list(range(8)), inplace=True)
# Second Convolutional Layer
ansatz.compose(conv_layer(4, "c2"), list(range(4, 8)), inplace=True)
# Second Pooling Layer
ansatz.compose(pool_layer([0, 1], [2, 3], "p2"), list(range(4, 8)), inplace=True)
# Third Convolutional Layer
ansatz.compose(conv_layer(2, "c3"), list(range(6, 8)), inplace=True)
# Third Pooling Layer
ansatz.compose(pool_layer([0], [1], "p3"), list(range(6, 8)), inplace=True)
# Combining the feature map and ansatz
circuit = QuantumCircuit(8)
circuit.compose(feature_map, range(8), inplace=True)
circuit.compose(ansatz, range(8), inplace=True)
observable = SparsePauliOp.from_list([("Z" + "I" * 7, 1)])
# we decompose the circuit for the QNN to avoid additional data copying
qnn = EstimatorQNN(
circuit=circuit.decompose(),
observables=observable,
input_params=feature_map.parameters,
weight_params=ansatz.parameters,
estimator=estimator,
)
[12]:
circuit.draw("mpl", style="clifford")
[12]:
[13]:
def callback_graph(weights, obj_func_eval):
clear_output(wait=True)
objective_func_vals.append(obj_func_eval)
plt.title("Objective function value against iteration")
plt.xlabel("Iteration")
plt.ylabel("Objective function value")
plt.plot(range(len(objective_func_vals)), objective_func_vals)
plt.show()
[14]:
with open("C:\\Users\\vkasi\\QRForQiskit-MachineLearning\\11_qcnn_initial_point.json", "r") as f:
initial_point = json.load(f)
classifier = NeuralNetworkClassifier(
qnn,
optimizer=COBYLA(maxiter=200), # Set max iterations here
callback=callback_graph,
initial_point=initial_point,
)
[15]:
x = np.asarray(train_images)
y = np.asarray(train_labels)
objective_func_vals = []
plt.rcParams["figure.figsize"] = (12, 6)
classifier.fit(x, y)
# score classifier
print(f"Accuracy from the train data : {np.round(100 * classifier.score(x, y), 2)}%")
Accuracy from the train data : 85.71%
[16]:
y_predict = classifier.predict(test_images)
x = np.asarray(test_images)
y = np.asarray(test_labels)
print(f"Accuracy from the test data : {np.round(100 * classifier.score(x, y), 2)}%")
# Let's see some examples in our dataset
fig, ax = plt.subplots(2, 2, figsize=(10, 6), subplot_kw={"xticks": [], "yticks": []})
for i in range(0, 4):
ax[i // 2, i % 2].imshow(test_images[i].reshape(2, 4), aspect="equal")
if y_predict[i] == -1:
ax[i // 2, i % 2].set_title("The QCNN predicts this is a Horizontal Line")
if y_predict[i] == +1:
ax[i // 2, i % 2].set_title("The QCNN predicts this is a Vertical Line")
plt.subplots_adjust(wspace=0.1, hspace=0.5)
Accuracy from the test data : 86.67%
[ ]: