{ "cells": [ { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "xLOXFOT5Q40E" }, "source": [ "##### Copyright 2020 The TensorFlow Authors." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "colab": {}, "colab_type": "code", "id": "iiQkM5ZgQ8r2", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", "# you may not use this file except in compliance with the License.\n", "# You may obtain a copy of the License at\n", "#\n", "# https://www.apache.org/licenses/LICENSE-2.0\n", "#\n", "# Unless required by applicable law or agreed to in writing, software\n", "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", "# See the License for the specific language governing permissions and\n", "# limitations under the License." ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "uLeF5Nmdef0V" }, "source": [ "# Quantum Convolutional Neural Network" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "i9Jcnb8bQQyd" }, "source": [ "
\n",
" ![]() | \n",
" \n",
" ![]() | \n",
" \n",
" ![]() | \n",
" \n",
" ![]() | \n",
"
cirq.GridQubit
s:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "9tZt0aAO4r4F",
"vscode": {
"languageId": "python"
}
},
"outputs": [],
"source": [
"SVGCircuit(cluster_state_circuit(cirq.GridQubit.rect(1, 4)))"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "4xElWnRf1ZC7"
},
"source": [
"#### 1.5.2 QCNN layers\n",
"\n",
"Define the layers that make up the model using the Cong and Lukin QCNN paper. There are a few prerequisites:\n",
"\n",
"* The one- and two-qubit parameterized unitary matrices from the Tucci paper.\n",
"* A general parameterized two-qubit pooling operation."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "oNRGOqky2exY",
"vscode": {
"languageId": "python"
}
},
"outputs": [],
"source": [
"def one_qubit_unitary(bit, symbols):\n",
" \"\"\"Make a Cirq circuit enacting a rotation of the bloch sphere about the X,\n",
" Y and Z axis, that depends on the values in `symbols`.\n",
" \"\"\"\n",
" return cirq.Circuit(\n",
" cirq.X(bit)**symbols[0],\n",
" cirq.Y(bit)**symbols[1],\n",
" cirq.Z(bit)**symbols[2])\n",
"\n",
"\n",
"def two_qubit_unitary(bits, symbols):\n",
" \"\"\"Make a Cirq circuit that creates an arbitrary two qubit unitary.\"\"\"\n",
" circuit = cirq.Circuit()\n",
" circuit += one_qubit_unitary(bits[0], symbols[0:3])\n",
" circuit += one_qubit_unitary(bits[1], symbols[3:6])\n",
" circuit += [cirq.ZZ(*bits)**symbols[6]]\n",
" circuit += [cirq.YY(*bits)**symbols[7]]\n",
" circuit += [cirq.XX(*bits)**symbols[8]]\n",
" circuit += one_qubit_unitary(bits[0], symbols[9:12])\n",
" circuit += one_qubit_unitary(bits[1], symbols[12:])\n",
" return circuit\n",
"\n",
"\n",
"def two_qubit_pool(source_qubit, sink_qubit, symbols):\n",
" \"\"\"Make a Cirq circuit to do a parameterized 'pooling' operation, which\n",
" attempts to reduce entanglement down from two qubits to just one.\"\"\"\n",
" pool_circuit = cirq.Circuit()\n",
" sink_basis_selector = one_qubit_unitary(sink_qubit, symbols[0:3])\n",
" source_basis_selector = one_qubit_unitary(source_qubit, symbols[3:6])\n",
" pool_circuit.append(sink_basis_selector)\n",
" pool_circuit.append(source_basis_selector)\n",
" pool_circuit.append(cirq.CNOT(source_qubit, sink_qubit))\n",
" pool_circuit.append(sink_basis_selector**-1)\n",
" return pool_circuit"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "LoG0a3U_2qGA"
},
"source": [
"To see what you created, print out the one-qubit unitary circuit:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "T5uhvF-g2rpZ",
"vscode": {
"languageId": "python"
}
},
"outputs": [],
"source": [
"SVGCircuit(one_qubit_unitary(cirq.GridQubit(0, 0), sympy.symbols('x0:3')))"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "NWuMb_Us8ar2"
},
"source": [
"And the two-qubit unitary circuit:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "aJTdRrfS2uIo",
"vscode": {
"languageId": "python"
}
},
"outputs": [],
"source": [
"SVGCircuit(two_qubit_unitary(cirq.GridQubit.rect(1, 2), sympy.symbols('x0:15')))"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "EXQD1R_V8jyk"
},
"source": [
"And the two-qubit pooling circuit:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "DOHRbkvH2xGK",
"vscode": {
"languageId": "python"
}
},
"outputs": [],
"source": [
"SVGCircuit(two_qubit_pool(*cirq.GridQubit.rect(1, 2), sympy.symbols('x0:6')))"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "AzVauXWD3v8C"
},
"source": [
"##### 1.5.2.1 Quantum convolution\n",
"\n",
"As in the Cong and Lukin paper, define the 1D quantum convolution as the application of a two-qubit parameterized unitary to every pair of adjacent qubits with a stride of one."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "1Fa19Lzb3wnR",
"vscode": {
"languageId": "python"
}
},
"outputs": [],
"source": [
"def quantum_conv_circuit(bits, symbols):\n",
" \"\"\"Quantum Convolution Layer following the above diagram.\n",
" Return a Cirq circuit with the cascade of `two_qubit_unitary` applied\n",
" to all pairs of qubits in `bits` as in the diagram above.\n",
" \"\"\"\n",
" circuit = cirq.Circuit()\n",
" for first, second in zip(bits[0::2], bits[1::2]):\n",
" circuit += two_qubit_unitary([first, second], symbols)\n",
" for first, second in zip(bits[1::2], bits[2::2] + [bits[0]]):\n",
" circuit += two_qubit_unitary([first, second], symbols)\n",
" return circuit"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "fTzOm_t394Gj"
},
"source": [
"Display the (very horizontal) circuit:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "Bi6q2nmY3z_U",
"vscode": {
"languageId": "python"
}
},
"outputs": [],
"source": [
"SVGCircuit(\n",
" quantum_conv_circuit(cirq.GridQubit.rect(1, 8), sympy.symbols('x0:15')))"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "3svBAfap4xhP"
},
"source": [
"##### 1.5.2.2 Quantum pooling\n",
"\n",
"A quantum pooling layer pools from $N$ qubits to $\\frac{N}{2}$ qubits using the two-qubit pool defined above."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "jD3fgcWO4yEU",
"vscode": {
"languageId": "python"
}
},
"outputs": [],
"source": [
"def quantum_pool_circuit(source_bits, sink_bits, symbols):\n",
" \"\"\"A layer that specifies a quantum pooling operation.\n",
" A Quantum pool tries to learn to pool the relevant information from two\n",
" qubits onto 1.\n",
" \"\"\"\n",
" circuit = cirq.Circuit()\n",
" for source, sink in zip(source_bits, sink_bits):\n",
" circuit += two_qubit_pool(source, sink, symbols)\n",
" return circuit"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "NX83NHDP_Q_Z"
},
"source": [
"Examine a pooling component circuit:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "pFXow2OX47O5",
"vscode": {
"languageId": "python"
}
},
"outputs": [],
"source": [
"test_bits = cirq.GridQubit.rect(1, 8)\n",
"\n",
"SVGCircuit(\n",
" quantum_pool_circuit(test_bits[:4], test_bits[4:], sympy.symbols('x0:6')))"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "23VcPLT45Lg7"
},
"source": [
"### 1.6 Model definition\n",
"\n",
"Now use the defined layers to construct a purely quantum CNN. Start with eight qubits, pool down to one, then measure $\\langle \\hat{Z} \\rangle$."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "vzEsY6-n5NR0",
"vscode": {
"languageId": "python"
}
},
"outputs": [],
"source": [
"def create_model_circuit(qubits):\n",
" \"\"\"Create sequence of alternating convolution and pooling operators \n",
" which gradually shrink over time.\"\"\"\n",
" model_circuit = cirq.Circuit()\n",
" symbols = sympy.symbols('qconv0:63')\n",
" # Cirq uses sympy.Symbols to map learnable variables. TensorFlow Quantum\n",
" # scans incoming circuits and replaces these with TensorFlow variables.\n",
" model_circuit += quantum_conv_circuit(qubits, symbols[0:15])\n",
" model_circuit += quantum_pool_circuit(qubits[:4], qubits[4:],\n",
" symbols[15:21])\n",
" model_circuit += quantum_conv_circuit(qubits[4:], symbols[21:36])\n",
" model_circuit += quantum_pool_circuit(qubits[4:6], qubits[6:],\n",
" symbols[36:42])\n",
" model_circuit += quantum_conv_circuit(qubits[6:], symbols[42:57])\n",
" model_circuit += quantum_pool_circuit([qubits[6]], [qubits[7]],\n",
" symbols[57:63])\n",
" return model_circuit\n",
"\n",
"\n",
"# Create our qubits and readout operators in Cirq.\n",
"cluster_state_bits = cirq.GridQubit.rect(1, 8)\n",
"readout_operators = cirq.Z(cluster_state_bits[-1])\n",
"\n",
"# Build a sequential model enacting the logic in 1.3 of this notebook.\n",
"# Here you are making the static cluster state prep as a part of the AddCircuit and the\n",
"# \"quantum datapoints\" are coming in the form of excitation\n",
"excitation_input = tf.keras.Input(shape=(), dtype=tf.dtypes.string)\n",
"cluster_state = tfq.layers.AddCircuit()(\n",
" excitation_input, prepend=cluster_state_circuit(cluster_state_bits))\n",
"\n",
"quantum_model = tfq.layers.PQC(create_model_circuit(cluster_state_bits),\n",
" readout_operators)(cluster_state)\n",
"\n",
"qcnn_model = tf.keras.Model(inputs=[excitation_input], outputs=[quantum_model])\n",
"\n",
"# Show the keras plot of the model\n",
"tf.keras.utils.plot_model(qcnn_model,\n",
" show_shapes=True,\n",
" show_layer_names=False,\n",
" dpi=70)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "9jqTEe5VSbug"
},
"source": [
"### 1.7 Train the model\n",
"\n",
"Train the model over the full batch to simplify this example."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "_TFkAm1sQZEN",
"vscode": {
"languageId": "python"
}
},
"outputs": [],
"source": [
"# Generate some training data.\n",
"train_excitations, train_labels, test_excitations, test_labels = generate_data(\n",
" cluster_state_bits)\n",
"\n",
"\n",
"# Custom accuracy metric.\n",
"@tf.function\n",
"def custom_accuracy(y_true, y_pred):\n",
" y_true = tf.squeeze(y_true)\n",
" y_pred = tf.map_fn(lambda x: 1.0 if x >= 0 else -1.0, y_pred)\n",
" return tf.keras.backend.mean(tf.keras.backend.equal(y_true, y_pred))\n",
"\n",
"\n",
"qcnn_model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.02),\n",
" loss=tf.losses.mse,\n",
" metrics=[custom_accuracy])\n",
"\n",
"history = qcnn_model.fit(x=train_excitations,\n",
" y=train_labels,\n",
" batch_size=16,\n",
" epochs=25,\n",
" verbose=1,\n",
" validation_data=(test_excitations, test_labels))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "2tiCJOb5Qzcr",
"vscode": {
"languageId": "python"
}
},
"outputs": [],
"source": [
"plt.plot(history.history['loss'][1:], label='Training')\n",
"plt.plot(history.history['val_loss'][1:], label='Validation')\n",
"plt.title('Training a Quantum CNN to Detect Excited Cluster States')\n",
"plt.xlabel('Epochs')\n",
"plt.ylabel('Loss')\n",
"plt.legend()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "GyrkcEReQ5Bc"
},
"source": [
"## 2. Hybrid models\n",
"\n",
"You don't have to go from eight qubits to one qubit using quantum convolution—you could have done one or two rounds of quantum convolution and fed the results into a classical neural network. This section explores quantum-classical hybrid models."
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "A2tOK22t7Kjm"
},
"source": [
"### 2.1 Hybrid model with a single quantum filter\n",
"\n",
"Apply one layer of quantum convolution, reading out $\\langle \\hat{Z}_n \\rangle$ on all bits, followed by a densely-connected neural network.\n",
"\n",
"