.\"\"\"\n",
+ "\n",
+ " def __init__(self):\n",
+ " pass\n",
+ "\n",
+ " @tf.function\n",
+ " def _compute_gradient(self, symbol_values):\n",
+ " \"\"\"Compute the gradient based on symbol_values.\"\"\"\n",
+ "\n",
+ " # f(x) = sin(pi * x)\n",
+ " # f'(x) = pi * cos(pi * x)\n",
+ " return tf.cast(tf.cos(symbol_values * np.pi) * np.pi, tf.float32)\n",
+ "\n",
+ " @tf.function\n",
+ " def differentiate_analytic(self, programs, symbol_names, symbol_values,\n",
+ " pauli_sums, forward_pass_vals, grad):\n",
+ " \"\"\"Specify how to differentiate a circuit with analytical expectation.\n",
+ "\n",
+ " This is called at graph runtime by TensorFlow. `differentiate_analytic`\n",
+ " should calculate the gradient of a batch of circuits and return it\n",
+ " formatted as indicated below. See\n",
+ " `tfq.differentiators.ForwardDifference` for an example.\n",
+ "\n",
+ " Args:\n",
+ " programs: `tf.Tensor` of strings with shape [batch_size] containing\n",
+ " the string representations of the circuits to be executed.\n",
+ " symbol_names: `tf.Tensor` of strings with shape [n_params], which\n",
+ " is used to specify the order in which the values in\n",
+ " `symbol_values` should be placed inside of the circuits in\n",
+ " `programs`.\n",
+ " symbol_values: `tf.Tensor` of real numbers with shape\n",
+ " [batch_size, n_params] specifying parameter values to resolve\n",
+ " into the circuits specified by programs, following the ordering\n",
+ " dictated by `symbol_names`.\n",
+ " pauli_sums: `tf.Tensor` of strings with shape [batch_size, n_ops]\n",
+ " containing the string representation of the operators that will\n",
+ " be used on all of the circuits in the expectation calculations.\n",
+ " forward_pass_vals: `tf.Tensor` of real numbers with shape\n",
+ " [batch_size, n_ops] containing the output of the forward pass\n",
+ " through the op you are differentiating.\n",
+ " grad: `tf.Tensor` of real numbers with shape [batch_size, n_ops]\n",
+ " representing the gradient backpropagated to the output of the\n",
+ " op you are differentiating through.\n",
+ "\n",
+ " Returns:\n",
+ " A `tf.Tensor` with the same shape as `symbol_values` representing\n",
+ " the gradient backpropagated to the `symbol_values` input of the op\n",
+ " you are differentiating through.\n",
+ " \"\"\"\n",
+ "\n",
+ " # Computing gradients just based off of symbol_values.\n",
+ " return self._compute_gradient(symbol_values) * grad\n",
+ "\n",
+ " @tf.function\n",
+ " def differentiate_sampled(self, programs, symbol_names, symbol_values,\n",
+ " pauli_sums, num_samples, forward_pass_vals, grad):\n",
+ " \"\"\"Specify how to differentiate a circuit with sampled expectation.\n",
+ "\n",
+ " This is called at graph runtime by TensorFlow. `differentiate_sampled`\n",
+ " should calculate the gradient of a batch of circuits and return it\n",
+ " formatted as indicated below. See\n",
+ " `tfq.differentiators.ForwardDifference` for an example.\n",
+ "\n",
+ " Args:\n",
+ " programs: `tf.Tensor` of strings with shape [batch_size] containing\n",
+ " the string representations of the circuits to be executed.\n",
+ " symbol_names: `tf.Tensor` of strings with shape [n_params], which\n",
+ " is used to specify the order in which the values in\n",
+ " `symbol_values` should be placed inside of the circuits in\n",
+ " `programs`.\n",
+ " symbol_values: `tf.Tensor` of real numbers with shape\n",
+ " [batch_size, n_params] specifying parameter values to resolve\n",
+ " into the circuits specified by programs, following the ordering\n",
+ " dictated by `symbol_names`.\n",
+ " pauli_sums: `tf.Tensor` of strings with shape [batch_size, n_ops]\n",
+ " containing the string representation of the operators that will\n",
+ " be used on all of the circuits in the expectation calculations.\n",
+ " num_samples: `tf.Tensor` of positive integers representing the\n",
+ " number of samples per term in each term of pauli_sums used\n",
+ " during the forward pass.\n",
+ " forward_pass_vals: `tf.Tensor` of real numbers with shape\n",
+ " [batch_size, n_ops] containing the output of the forward pass\n",
+ " through the op you are differentiating.\n",
+ " grad: `tf.Tensor` of real numbers with shape [batch_size, n_ops]\n",
+ " representing the gradient backpropagated to the output of the\n",
+ " op you are differentiating through.\n",
+ "\n",
+ " Returns:\n",
+ " A `tf.Tensor` with the same shape as `symbol_values` representing\n",
+ " the gradient backpropagated to the `symbol_values` input of the op\n",
+ " you are differentiating through.\n",
+ " \"\"\"\n",
+ " return self._compute_gradient(symbol_values) * grad"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "bvEgw2m6NUAI",
+ "colab_type": "text"
+ },
+ "source": [
+ "This new differentiator can now be used with existing `tfq.layer` objects:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "QrKnkWswNUAJ",
+ "colab_type": "code",
+ "colab": {}
+ },
+ "source": [
+ "custom_dif = MyDifferentiator()\n",
+ "custom_grad_expectation = tfq.layers.Expectation(differentiator=custom_dif)\n",
+ "\n",
+ "# Now let's get the gradients with finite diff.\n",
+ "with tf.GradientTape() as g:\n",
+ " g.watch(values_tensor)\n",
+ " exact_outputs = expectation_calculation(\n",
+ " my_circuit,\n",
+ " operators=[pauli_x],\n",
+ " symbol_names=['alpha'],\n",
+ " symbol_values=values_tensor)\n",
+ "\n",
+ "analytic_finite_diff_gradients = g.gradient(exact_outputs, values_tensor)\n",
+ "\n",
+ "# Now let's get the gradients with custom diff.\n",
+ "with tf.GradientTape() as g:\n",
+ " g.watch(values_tensor)\n",
+ " my_outputs = custom_grad_expectation(\n",
+ " my_circuit,\n",
+ " operators=[pauli_x],\n",
+ " symbol_names=['alpha'],\n",
+ " symbol_values=values_tensor)\n",
+ "\n",
+ "my_gradients = g.gradient(my_outputs, values_tensor)\n",
+ "\n",
+ "plt.subplot(1, 2, 1)\n",
+ "plt.title('Exact Gradient')\n",
+ "plt.plot(input_points, analytic_finite_diff_gradients.numpy())\n",
+ "plt.xlabel('x')\n",
+ "plt.ylabel('f(x)')\n",
+ "plt.subplot(1, 2, 2)\n",
+ "plt.title('My Gradient')\n",
+ "plt.plot(input_points, my_gradients.numpy())\n",
+ "plt.xlabel('x')"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "oXqcJWigNUAL",
+ "colab_type": "text"
+ },
+ "source": [
+ "This new differentiator can now be used to generate differentiable ops.\n",
+ "\n",
+ "Key Point: A differentiator that has been previously attached to an op must be refreshed before attaching to a new op, because a differentiator may only be attached to one op at a time."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "F_WHcj3bNUAM",
+ "colab_type": "code",
+ "colab": {}
+ },
+ "source": [
+ "# Create a noisy sample based expectation op.\n",
+ "expectation_sampled = tfq.get_sampled_expectation_op(\n",
+ " cirq.DensityMatrixSimulator(noise=cirq.depolarize(0.01)))\n",
+ "\n",
+ "# Make it differentiable with your differentiator:\n",
+ "# Remember to refresh the differentiator before attaching the new op\n",
+ "custom_dif.refresh()\n",
+ "differentiable_op = custom_dif.generate_differentiable_op(\n",
+ " sampled_op=expectation_sampled)\n",
+ "\n",
+ "# Prep op inputs.\n",
+ "circuit_tensor = tfq.convert_to_tensor([my_circuit])\n",
+ "op_tensor = tfq.convert_to_tensor([[pauli_x]])\n",
+ "single_value = tf.convert_to_tensor([[my_alpha]])\n",
+ "num_samples_tensor = tf.convert_to_tensor([[1000]])\n",
+ "\n",
+ "with tf.GradientTape() as g:\n",
+ " g.watch(single_value)\n",
+ " forward_output = differentiable_op(circuit_tensor, ['alpha'], single_value,\n",
+ " op_tensor, num_samples_tensor)\n",
+ "\n",
+ "my_gradients = g.gradient(forward_output, single_value)\n",
+ "\n",
+ "print('---TFQ---')\n",
+ "print('Foward: ', forward_output.numpy())\n",
+ "print('Gradient:', my_gradients.numpy())\n",
+ "print('---Original---')\n",
+ "print('Forward: ', my_expectation(pauli_x, my_alpha))\n",
+ "print('Gradient:', my_grad(pauli_x, my_alpha))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "OGWcpqzDNUAP",
+ "colab_type": "text"
+ },
+ "source": [
+ "Success: Now you can use all the differentiators that TensorFlow Quantum has to offer—and define your own."
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/tutorials/hello_many_worlds.ipynb b/docs/tutorials/hello_many_worlds.ipynb
index 82f2f509f..9befd1fef 100644
--- a/docs/tutorials/hello_many_worlds.ipynb
+++ b/docs/tutorials/hello_many_worlds.ipynb
@@ -1,870 +1,864 @@
{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "xLOXFOT5Q40E"
- },
- "source": [
- "##### Copyright 2020 The TensorFlow Authors."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "cellView": "form",
- "colab": {},
- "colab_type": "code",
- "id": "iiQkM5ZgQ8r2"
- },
- "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": "j6331ZSsQGY3"
- },
- "source": [
- "# Hello, many worlds"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "i9Jcnb8bQQyd"
- },
- "source": [
- ""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "6tYn2HaAUgH0"
- },
- "source": [
- "This tutorial shows how a classical neural network can learn to control a qubit in an highly simplified setting. It introduces [Cirq](https://github.com/quantumlib/Cirq), a Python framework to create, edit, and invoke Noisy Intermediate Scale Quantum (NISQ) circuits."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "sPZoNKvpUaqa"
- },
- "source": [
- "## Setup\n",
- "\n",
- "Download and install the required packages:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "fAVlg7rxkvUw"
- },
- "outputs": [],
- "source": [
- "%%capture\n",
- "!pip install --upgrade pip\n",
- "!pip install cirq==0.6.0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "TorxE5tnkvb2"
- },
- "outputs": [],
- "source": [
- "%%capture\n",
- "!pip install --upgrade tensorflow==2.1.0"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "FxkQA6oblNqI"
- },
- "source": [
- "Note: If the following code cell fails, execute the first code cells and then restart the Colab runtime (*Runtime > Restart Runtime*)."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "saFHsRDpkvkH"
- },
- "outputs": [],
- "source": [
- "h = \"2dfcfceb9726fa73c40381c037dc01facd3d061e\"\n",
- "!cd ~/\n",
- "!rm -r -f TFQuantum/\n",
- "!git clone https://{h}:{h}@github.com/quantumlib/TFQuantum.git;\n",
- "!pip install --upgrade ./TFQuantum/wheels/tfquantum-0.2.0-cp36-cp36m-manylinux1_x86_64.whl"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "F1L8h1YKUvIO"
- },
- "source": [
- "Now import TensorFlow and the module dependencies:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "enZ300Bflq80"
- },
- "outputs": [],
- "source": [
- "import tensorflow as tf\n",
- "import tensorflow_quantum as tfq\n",
- "\n",
- "import cirq\n",
- "import sympy\n",
- "import numpy as np\n",
- "\n",
- "# visualization tools\n",
- "%matplotlib inline\n",
- "import matplotlib.pyplot as plt\n",
- "from cirq.contrib.svg import SVGCircuit"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "b08Mmbs8lr81"
- },
- "source": [
- "## 1. The Basics"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "y31qSRCczI-L"
- },
- "source": [
- "### 1.1 Cirq and parameterized quantum circuits\n",
- "\n",
- "Before exploring TensorFlow Quantum (TFQ), let's look at some [Cirq](https://github.com/quantumlib/Cirq) basics. Cirq is a Python library for quantum computing from Google. You use it to define circuits, including static and parameterized gates. The following code creates a two-qubit parameterized circuit. Cirq uses `sympy` symbols to specify gate parameters:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "2yQdmhQLCrzQ"
- },
- "outputs": [],
- "source": [
- "circuit_parameters = sympy.symbols('a b')\n",
- "circuit_parameters"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "itUlpbKmDYNW"
- },
- "source": [
- "Then you can create a parameterized circuit with these parameters:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "Ps-pd2mndXs7"
- },
- "outputs": [],
- "source": [
- "# Create a qubit in cirq.\n",
- "cirq_qubits = cirq.GridQubit.rect(1, 2)\n",
- "\n",
- "# Create a circuit using that qubit and the parameters we created previously.\n",
- "parameterized_circuit = cirq.Circuit(\n",
- " cirq.Rx(circuit_parameters[0]).on(cirq_qubits[0]),\n",
- " cirq.Ry(circuit_parameters[1]).on(cirq_qubits[1]),\n",
- " cirq.CNOT(control=cirq_qubits[0], target=cirq_qubits[1]))\n",
- "\n",
- "SVGCircuit(parameterized_circuit)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "zcCX109cJUaz"
- },
- "source": [
- "These parameterized circuits can be evaluated with different parameter values using the `cirq.Simulator` and the `cirq.ParamResolver` interface."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "VMq7EayNRyQb"
- },
- "outputs": [],
- "source": [
- "# Calculate a state vector with a=0.5 and b=-0.5.\n",
- "param_resolver = cirq.ParamResolver({\n",
- " circuit_parameters[0]: 0.5,\n",
- " circuit_parameters[1]: -0.5\n",
- "})\n",
- "output_wavefunction = cirq.Simulator().simulate(parameterized_circuit,\n",
- " param_resolver)\n",
- "\n",
- "output_wavefunction.final_state"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "-SUlLpXBeicF"
- },
- "source": [
- "Cirq can also specify Pauli operators. The following creates $\\hat{Z}$ and $\\hat{Z} + \\hat{X}$:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "hrSnOCi3ehr_"
- },
- "outputs": [],
- "source": [
- "pauli_z = cirq.Z(cirq_qubits[0])\n",
- "pauli_z_pauli_x = pauli_z + cirq.X(cirq_qubits[1])\n",
- "\n",
- "print(pauli_z)\n",
- "print(pauli_z_pauli_x)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "bkC-yjIolDNr"
- },
- "source": [
- "### 1.2 Quantum circuits in TensorFlow\n",
- "\n",
- "TensorFlow Quantum (TFQ) provides `tfq.convert_to_tensor()`, a function that converts lists/arrays of Cirq circuits to tensors:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "1gLQjA02mIyy"
- },
- "outputs": [],
- "source": [
- "# List of 1 circuit.\n",
- "circuit_tensor = tfq.convert_to_tensor([parameterized_circuit])\n",
- "\n",
- "# Rank 1 tensor containing 1 circuit.\n",
- "circuit_tensor.shape"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "zsaOWR6Fmcc8"
- },
- "source": [
- "Similarly, you can create a tensor representation of Cirq Pauli operators:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "aX_vEmCKmpQS"
- },
- "outputs": [],
- "source": [
- "# List of two pauli operators.\n",
- "pauli_tensor = tfq.convert_to_tensor([pauli_z, pauli_z_pauli_x])\n",
- "\n",
- "# Rank 1 tensor containing 2 Pauli operators.\n",
- "pauli_tensor.shape"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "FI1JLWe6m8JF"
- },
- "source": [
- "### 1.3 Circuit execution in TensorFlow\n",
- "\n",
- "TFQ provides methods for computing expectation values, samples, and state vectors. For now, let's focus on *expectation values*, as samples and state vectors are not as common and, by definition, not differentiable.\n",
- "\n",
- "The highest-level interface for calculating expectation values is the `tfq.layers.Expectation` layer, which is a `tf.keras.Layer`. In its simplest form, this layer is equivalent to running a batch of `cirq.ParamResolvers` in Cirq, but the circuits are run in efficient C++ code instead of Python.\n",
- "\n",
- "In the following example, create a circuit parameterized by `x` to go along with our Z operator:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "X8gT3qksohIP"
- },
- "outputs": [],
- "source": [
- "qubit = cirq.GridQubit(0, 0)\n",
- "parameter = sympy.Symbol('a')\n",
- "parameterized_circuit = cirq.Circuit(cirq.Rx(parameter).on(qubit))\n",
- "SVGCircuit(parameterized_circuit)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "NBMCp3HtfWbW"
- },
- "source": [
- "Create the batch of `a` values to input into the layer:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "1fsVZhF5lIXp"
- },
- "outputs": [],
- "source": [
- "batch_x_vals = np.array(np.random.uniform(0, np.pi, (5, 1)), dtype=np.float32)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "Ip7jlGXIf22u"
- },
- "source": [
- "Display a batch of expectation values in Cirq:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "RsfF53UCJtr9"
- },
- "outputs": [],
- "source": [
- "cirq_batch = []\n",
- "cirq_simulator = cirq.Simulator()\n",
- "\n",
- "for inp in batch_x_vals:\n",
- " param_resolver = cirq.ParamResolver({parameter: inp[0]})\n",
- " final_state = cirq_simulator.simulate(parameterized_circuit,\n",
- " param_resolver).final_state\n",
- " cirq_batch.append([\n",
- " np.real(pauli_z.expectation_from_wavefunction(final_state, {qubit: 0}))\n",
- " ])\n",
- "\n",
- "print('cirq batch results: \\n {}'.format(np.array(cirq_batch)))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "W0JlZEu-f9Ac"
- },
- "source": [
- "Display a batch of expectation values in TensorFlow:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "kGZVdcZ6y9lC"
- },
- "outputs": [],
- "source": [
- "# Create the layer.\n",
- "expectation_layer = tfq.layers.Expectation()\n",
- "\n",
- "# Run the layer and display the ouputs.\n",
- "expectation_batch = expectation_layer(parameterized_circuit,\n",
- " symbol_names=[parameter],\n",
- " symbol_values=batch_x_vals,\n",
- " operators=pauli_z)\n",
- "\n",
- "print('tfq batch results: \\n {}'.format(expectation_batch))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "2zDGtlrYLJ1G"
- },
- "source": [
- "This layer also supports more complicated runtime inputs. These are explored later in this tutorial and in other example notebooks."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "wppQ3TJ23mWC"
- },
- "source": [
- "## 2. Hybrid quantum-classical optimization\n",
- "\n",
- "Now that you've seen the basics, let's use TensorFlow Quantum to construct a *hybrid quantum-classical neural net*. You will train a classical neural net to control a single qubit. The output of the classical neural network determines the control parameters of the quantum circuit that is applied to the qubit. This is measured to produce the expectation values of a measurement operator. From a datapoint perspective, the input pairs are the real number values for `command` and the initial qubit rotation circuits, and the output pairs are the epxectation values.\n",
- "\n",
- "More specifically, this example trains a classical neural network to prepare a given quantum state determined by the input to the classical neural network. Starting from a random initial quantum state, you will prepare the qubit in either the 0 or 1 state by learning an arbitrary rotation ($Rx$, $Ry$, and $Rz$). This figure shows the architecture:\n",
- "\n",
- "
\n",
- "\n",
- "Even without a neural network this is a straightforward problem to solve, but the theme is similar to the real quantum control problems you might solve using TFQ. It demonstrates an end-to-end example of a quantum-classical computation using the `tfq.layers.ControlledPQC` layer inside of a `tf.keras.Model`."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "VjDf-nTM6ZSs"
- },
- "source": [
- "### 2.1 Circuit preparation\n",
- "\n",
- "Define a learnable single bit rotation, as indicated in the figure above. This will correspond to our model control circuit."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "N-j7SCl-51-q"
- },
- "outputs": [],
- "source": [
- "# Parameters that the classical NN will feed values into.\n",
- "control_params = sympy.symbols('theta_1 theta_2 theta_3')\n",
- "\n",
- "# Create the parameterized circuit.\n",
- "model_circuit = cirq.Circuit(\n",
- " cirq.Rz(control_params[2])(qubit),\n",
- " cirq.Ry(control_params[1])(qubit),\n",
- " cirq.Rx(control_params[0])(qubit))\n",
- "\n",
- "SVGCircuit(model_circuit)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "wfjSbsvb7g9f"
- },
- "source": [
- "### 2.2 Model definition\n",
- "\n",
- "Now define your model. The network architecture is indicated by the plot of the model below, which is compared to the figure above to verify correctness."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "Lod5uhHo7gXH"
- },
- "outputs": [],
- "source": [
- "circuit_inputs = tf.keras.Input(shape=(),\n",
- " dtype=tf.dtypes.string,\n",
- " name='circuit_input')\n",
- "command_inputs = tf.keras.Input(dtype=tf.dtypes.float32,\n",
- " shape=(1,),\n",
- " name='command_input')\n",
- "d1 = tf.keras.layers.Dense(10)(command_inputs)\n",
- "d2 = tf.keras.layers.Dense(3)(d1)\n",
- "expectation = tfq.layers.ControlledPQC(model_circuit,\n",
- " pauli_z)([circuit_inputs, d2])\n",
- "model = tf.keras.Model(inputs=[circuit_inputs, command_inputs],\n",
- " outputs=expectation)\n",
- "tf.keras.utils.plot_model(model,\n",
- " show_shapes=True,\n",
- " show_layer_names=True,\n",
- " dpi=70)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "bbiVHvSYVW4H"
- },
- "source": [
- "### 2.3 Data definition\n",
- "\n",
- "Define two possible inputs and two possible corresponding outputs:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "_VYfzHffWo7n"
- },
- "outputs": [],
- "source": [
- "# Input values to the classical NN.\n",
- "commands = np.array([[0], [1]], dtype=np.float32)\n",
- "\n",
- "# Desired Z expectation value at output of quantum circuit.\n",
- "expected_outputs = np.array([[1], [-1]], dtype=np.float32)\n",
- "\n",
- "# These rotations define the random state to start in.\n",
- "random_rotations = np.random.uniform(0, 2 * np.pi, 3)\n",
- "\n",
- "# Create datapoint circuits. (They're the same for each command.)\n",
- "datapoint_circuits = tfq.convert_to_tensor([\n",
- " cirq.Circuit(\n",
- " cirq.Rx(random_rotations[0])(qubit),\n",
- " cirq.Ry(random_rotations[1])(qubit),\n",
- " cirq.Rz(random_rotations[2])(qubit))\n",
- "] * 2)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "gB--UhZZYgVY"
- },
- "source": [
- "### 2.4 Training"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "dtPYqbNi8zeZ"
- },
- "outputs": [],
- "source": [
- "optimizer = tf.keras.optimizers.Adam(learning_rate=0.05)\n",
- "loss = tf.keras.losses.mean_squared_error\n",
- "model.compile(optimizer=optimizer, loss=loss)\n",
- "\n",
- "# Note that the only changing input is the command input.\n",
- "# datapoint_circuit is the same for the different parameter pairs.\n",
- "history = model.fit(x=[datapoint_circuits, commands],\n",
- " y=expected_outputs,\n",
- " epochs=30,\n",
- " verbose=0)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "azE-qV0OaC1o"
- },
- "outputs": [],
- "source": [
- "plt.plot(history.history['loss'])\n",
- "plt.title(\"Learning to Control a Qubit\")\n",
- "plt.xlabel(\"Iterations\")\n",
- "plt.ylabel(\"Error in Control\")\n",
- "plt.show()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "GTd5DGcRmmOK"
- },
- "source": [
- "From this plot you can see that the neural network appears to have learned to return the qubit back to the initial state. Quickly verify this by examining the parameters of the model."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "jNrW0NXR-lDC"
- },
- "source": [
- "### 2.5 Learning to prepare eigenstates of different operators\n",
- "\n",
- "The choice of the $\\pm \\hat{Z}$ eigenstates corresponding to 1 and 0 was arbitrary. You could have just as easily wanted 1 to correspond to the $+ \\hat{Z}$ eigenstate and 0 to correspond to the $-\\hat{X}$ eigenstate. One way (of many) to accomplish this is by specifying a different measurement operator for each command, as indicated in the figure below:\n",
- "\n",
- "
\n",
- "\n",
- "This requires a slightly different approach involving new usage of the `tfq.layers.Expectation` from earlier. Now your input, output pairs have grown to be input: (circuit, command, operator). The output is still the expectation value."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "Ci3WMZ9CjEM1"
- },
- "source": [
- "#### 2.5.1 New model definition\n",
- "\n",
- "Lets take a look at the model to accomplish this task:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "n_aTG4g3-y0F"
- },
- "outputs": [],
- "source": [
- "# Define inputs.\n",
- "command_inputs = tf.keras.layers.Input(shape=(1),\n",
- " dtype=tf.dtypes.float32,\n",
- " name='command_input')\n",
- "circuit_inputs = tf.keras.Input(shape=(),\n",
- " dtype=tf.dtypes.string,\n",
- " name='circuit_input')\n",
- "operator_inputs = tf.keras.Input(shape=(1,),\n",
- " dtype=tf.dtypes.string,\n",
- " name='operaotr_input')\n",
- "\n",
- "# Define classical NN.\n",
- "dense_1 = tf.keras.layers.Dense(10)(command_inputs)\n",
- "dense_2 = tf.keras.layers.Dense(3)(dense_1)\n",
- "\n",
- "# Since you aren't using a PQC or ControlledPQC you must append\n",
- "# you model circuit onto the datapoint circuit tensor manually.\n",
- "full_circuit = tfq.layers.AddCircuit()(circuit_inputs, append=model_circuit)\n",
- "\n",
- "expectation_output = tfq.layers.Expectation()(full_circuit,\n",
- " symbol_names=control_params,\n",
- " symbol_values=dense_2,\n",
- " operators=operator_inputs)\n",
- "two_axis_control_model = tf.keras.Model(\n",
- " inputs=[circuit_inputs, command_inputs, operator_inputs],\n",
- " outputs=[expectation_output])\n",
- "\n",
- "tf.keras.utils.plot_model(two_axis_control_model, show_shapes=True, dpi=70)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "VQTM6CCiD4gU"
- },
- "source": [
- "#### 2.5.2 Adding to datapoints\n",
- "\n",
- "Now you will include the operators you wish to measure for each datapoint you supply for `model_circuit`:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "4gw_L3JG0_G0"
- },
- "outputs": [],
- "source": [
- "operator_data = tfq.convert_to_tensor([[cirq.X(qubit)], [cirq.Z(qubit)]])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "ALCKSvwh0_G2"
- },
- "source": [
- "#### 2.5.3 Training\n",
- "\n",
- "Now that you have your new input outputs pairs you can train once again using keras."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "nFuGA73MAA4p"
- },
- "outputs": [],
- "source": [
- "optimizer = tf.keras.optimizers.Adam(learning_rate=0.05)\n",
- "loss = tf.keras.losses.mean_squared_error\n",
- "\n",
- "two_axis_control_model.compile(optimizer=optimizer, loss=loss)\n",
- "\n",
- "history = two_axis_control_model.fit(\n",
- " x=[datapoint_circuits, commands, operator_data],\n",
- " y=expected_outputs,\n",
- " epochs=30,\n",
- " verbose=1)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "Cf_G-GdturLL"
- },
- "outputs": [],
- "source": [
- "plt.plot(history.history['loss'])\n",
- "plt.title(\"Learning to Control a Qubit\")\n",
- "plt.xlabel(\"Iterations\")\n",
- "plt.ylabel(\"Error in Control\")\n",
- "plt.show()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "sdCPDH9NlJBl"
- },
- "source": [
- "The loss function has dropped to zero.\n",
- "\n",
- "If you inspect the parameters of the model, you'll see that the parameters have been recovered to control the qubit correctly with these new measurement operators."
- ]
- }
- ],
- "metadata": {
- "colab": {
- "collapsed_sections": [],
- "name": "hello_many_worlds.ipynb",
- "private_outputs": true,
- "provenance": [],
- "toc_visible": true
- },
- "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.5rc1"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
+ "nbformat": 4,
+ "nbformat_minor": 0,
+ "metadata": {
+ "colab": {
+ "name": "hello_many_worlds.ipynb",
+ "provenance": [],
+ "private_outputs": true,
+ "collapsed_sections": [],
+ "toc_visible": true
+ },
+ "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.5rc1"
+ }
+ },
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "xLOXFOT5Q40E"
+ },
+ "source": [
+ "##### Copyright 2020 The TensorFlow Authors."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "cellView": "form",
+ "colab_type": "code",
+ "id": "iiQkM5ZgQ8r2",
+ "colab": {}
+ },
+ "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."
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "j6331ZSsQGY3"
+ },
+ "source": [
+ "# Hello, many worlds"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "i9Jcnb8bQQyd"
+ },
+ "source": [
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "6tYn2HaAUgH0"
+ },
+ "source": [
+ "This tutorial shows how a classical neural network can learn to control a qubit in an highly simplified setting. It introduces [Cirq](https://github.com/quantumlib/Cirq), a Python framework to create, edit, and invoke Noisy Intermediate Scale Quantum (NISQ) circuits."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "sPZoNKvpUaqa"
+ },
+ "source": [
+ "## Setup\n",
+ "\n",
+ "Download and install the required packages:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "fAVlg7rxkvUw",
+ "colab": {}
+ },
+ "source": [
+ "%%capture\n",
+ "!pip install --upgrade pip\n",
+ "!pip install cirq==0.7.0"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "TorxE5tnkvb2",
+ "colab": {}
+ },
+ "source": [
+ "%%capture\n",
+ "!pip install --upgrade tensorflow==2.1.0"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "FxkQA6oblNqI"
+ },
+ "source": [
+ "Note: If the following code cell fails, execute the first code cells and then restart the Colab runtime (*Runtime > Restart Runtime*)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "saFHsRDpkvkH",
+ "colab": {}
+ },
+ "source": [
+ "h = \"2dfcfceb9726fa73c40381c037dc01facd3d061e\"\n",
+ "!cd ~/\n",
+ "!rm -r -f TFQuantum/\n",
+ "!git clone https://{h}:{h}@github.com/quantumlib/TFQuantum.git;\n",
+ "!pip install --upgrade ./TFQuantum/wheels/tfquantum-0.2.0-cp36-cp36m-manylinux1_x86_64.whl"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "F1L8h1YKUvIO"
+ },
+ "source": [
+ "Now import TensorFlow and the module dependencies:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "enZ300Bflq80",
+ "colab": {}
+ },
+ "source": [
+ "import tensorflow as tf\n",
+ "import tensorflow_quantum as tfq\n",
+ "\n",
+ "import cirq\n",
+ "import sympy\n",
+ "import numpy as np\n",
+ "\n",
+ "# visualization tools\n",
+ "%matplotlib inline\n",
+ "import matplotlib.pyplot as plt\n",
+ "from cirq.contrib.svg import SVGCircuit"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "b08Mmbs8lr81"
+ },
+ "source": [
+ "## 1. The Basics"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "y31qSRCczI-L"
+ },
+ "source": [
+ "### 1.1 Cirq and parameterized quantum circuits\n",
+ "\n",
+ "Before exploring TensorFlow Quantum (TFQ), let's look at some [Cirq](https://github.com/quantumlib/Cirq) basics. Cirq is a Python library for quantum computing from Google. You use it to define circuits, including static and parameterized gates. The following code creates a two-qubit parameterized circuit. Cirq uses `sympy` symbols to specify gate parameters:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "2yQdmhQLCrzQ",
+ "colab": {}
+ },
+ "source": [
+ "circuit_parameters = sympy.symbols('a b')\n",
+ "circuit_parameters"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "itUlpbKmDYNW"
+ },
+ "source": [
+ "Then you can create a parameterized circuit with these parameters:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "Ps-pd2mndXs7",
+ "colab": {}
+ },
+ "source": [
+ "# Create a qubit in cirq.\n",
+ "cirq_qubits = cirq.GridQubit.rect(1, 2)\n",
+ "\n",
+ "# Create a circuit using that qubit and the parameters we created previously.\n",
+ "parameterized_circuit = cirq.Circuit(\n",
+ " cirq.Rx(circuit_parameters[0]).on(cirq_qubits[0]),\n",
+ " cirq.Ry(circuit_parameters[1]).on(cirq_qubits[1]),\n",
+ " cirq.CNOT(control=cirq_qubits[0], target=cirq_qubits[1])\n",
+ ")\n",
+ "\n",
+ "SVGCircuit(parameterized_circuit)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "zcCX109cJUaz"
+ },
+ "source": [
+ "These parameterized circuits can be evaluated with different parameter values using the `cirq.Simulator` and the `cirq.ParamResolver` interface."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "VMq7EayNRyQb",
+ "colab": {}
+ },
+ "source": [
+ "# Calculate a state vector with a=0.5 and b=-0.5.\n",
+ "param_resolver = cirq.ParamResolver({\n",
+ " circuit_parameters[0]: 0.5,\n",
+ " circuit_parameters[1]: -0.5\n",
+ "})\n",
+ "output_wavefunction = cirq.Simulator().simulate(parameterized_circuit,\n",
+ " param_resolver)\n",
+ "\n",
+ "output_wavefunction.final_state"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "-SUlLpXBeicF"
+ },
+ "source": [
+ "Cirq can also specify Pauli operators. The following creates $\\hat{Z}$ and $\\hat{Z} + \\hat{X}$:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "hrSnOCi3ehr_",
+ "colab": {}
+ },
+ "source": [
+ "pauli_z = cirq.Z(cirq_qubits[0])\n",
+ "pauli_z_pauli_x = pauli_z + cirq.X(cirq_qubits[1])\n",
+ "\n",
+ "print(pauli_z)\n",
+ "print(pauli_z_pauli_x)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "bkC-yjIolDNr"
+ },
+ "source": [
+ "### 1.2 Quantum circuits in TensorFlow\n",
+ "\n",
+ "TensorFlow Quantum (TFQ) provides `tfq.convert_to_tensor()`, a function that converts lists/arrays of Cirq circuits to tensors:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "1gLQjA02mIyy",
+ "colab": {}
+ },
+ "source": [
+ "# List of 1 circuit.\n",
+ "circuit_tensor = tfq.convert_to_tensor([parameterized_circuit])\n",
+ "\n",
+ "# Rank 1 tensor containing 1 circuit.\n",
+ "circuit_tensor.shape"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "zsaOWR6Fmcc8"
+ },
+ "source": [
+ "Similarly, you can create a tensor representation of Cirq Pauli operators:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "aX_vEmCKmpQS",
+ "colab": {}
+ },
+ "source": [
+ "# List of two pauli operators.\n",
+ "pauli_tensor = tfq.convert_to_tensor([pauli_z, pauli_z_pauli_x])\n",
+ "\n",
+ "# Rank 1 tensor containing 2 Pauli operators.\n",
+ "pauli_tensor.shape"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "FI1JLWe6m8JF"
+ },
+ "source": [
+ "### 1.3 Circuit execution in TensorFlow\n",
+ "\n",
+ "TFQ provides methods for computing expectation values, samples, and state vectors. For now, let's focus on *expectation values*, as samples and state vectors are not as common and, by definition, not differentiable.\n",
+ "\n",
+ "The highest-level interface for calculating expectation values is the `tfq.layers.Expectation` layer, which is a `tf.keras.Layer`. In its simplest form, this layer is equivalent to running a batch of `cirq.ParamResolvers` in Cirq, but the circuits are run in efficient C++ code instead of Python.\n",
+ "\n",
+ "In the following example, create a circuit parameterized by `x` to go along with our Z operator:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "X8gT3qksohIP",
+ "colab": {}
+ },
+ "source": [
+ "qubit = cirq.GridQubit(0, 0)\n",
+ "parameter = sympy.Symbol('a')\n",
+ "parameterized_circuit = cirq.Circuit(cirq.Rx(parameter).on(qubit))\n",
+ "SVGCircuit(parameterized_circuit)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "NBMCp3HtfWbW"
+ },
+ "source": [
+ "Create the batch of `a` values to input into the layer:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "1fsVZhF5lIXp",
+ "colab": {}
+ },
+ "source": [
+ "batch_x_vals = np.array(np.random.uniform(0, np.pi, (5, 1)), dtype=np.float32)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "Ip7jlGXIf22u"
+ },
+ "source": [
+ "Display a batch of expectation values in Cirq:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "RsfF53UCJtr9",
+ "colab": {}
+ },
+ "source": [
+ "cirq_batch = []\n",
+ "cirq_simulator = cirq.Simulator()\n",
+ "\n",
+ "for inp in batch_x_vals:\n",
+ " param_resolver = cirq.ParamResolver({parameter: inp[0]})\n",
+ " final_state = cirq_simulator.simulate(parameterized_circuit,\n",
+ " param_resolver).final_state\n",
+ " cirq_batch.append([\n",
+ " np.real(pauli_z.expectation_from_wavefunction(final_state, {qubit: 0}))\n",
+ " ])\n",
+ "\n",
+ "print('cirq batch results: \\n {}'.format(np.array(cirq_batch)))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "W0JlZEu-f9Ac"
+ },
+ "source": [
+ "Display a batch of expectation values in TensorFlow:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "kGZVdcZ6y9lC",
+ "colab": {}
+ },
+ "source": [
+ "# Create the layer.\n",
+ "expectation_layer = tfq.layers.Expectation()\n",
+ "\n",
+ "# Run the layer and display the ouputs.\n",
+ "expectation_batch = expectation_layer(parameterized_circuit,\n",
+ " symbol_names=[parameter],\n",
+ " symbol_values=batch_x_vals,\n",
+ " operators=pauli_z)\n",
+ "\n",
+ "print('tfq batch results: \\n {}'.format(expectation_batch))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "2zDGtlrYLJ1G"
+ },
+ "source": [
+ "This layer also supports more complicated runtime inputs. These are explored later in this tutorial and in other example notebooks."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "wppQ3TJ23mWC"
+ },
+ "source": [
+ "## 2. Hybrid quantum-classical optimization\n",
+ "\n",
+ "Now that you've seen the basics, let's use TensorFlow Quantum to construct a *hybrid quantum-classical neural net*. You will train a classical neural net to control a single qubit. The output of the classical neural network determines the control parameters of the quantum circuit that is applied to the qubit. This is measured to produce the expectation values of a measurement operator. From a datapoint perspective, the input pairs are the real number values for `command` and the initial qubit rotation circuits, and the output pairs are the epxectation values.\n",
+ "\n",
+ "More specifically, this example trains a classical neural network to prepare a given quantum state determined by the input to the classical neural network. Starting from a random initial quantum state, you will prepare the qubit in either the 0 or 1 state by learning an arbitrary rotation ($Rx$, $Ry$, and $Rz$). This figure shows the architecture:\n",
+ "\n",
+ "
\n",
+ "\n",
+ "Even without a neural network this is a straightforward problem to solve, but the theme is similar to the real quantum control problems you might solve using TFQ. It demonstrates an end-to-end example of a quantum-classical computation using the `tfq.layers.ControlledPQC` layer inside of a `tf.keras.Model`."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "VjDf-nTM6ZSs"
+ },
+ "source": [
+ "### 2.1 Circuit preparation\n",
+ "\n",
+ "Define a learnable single bit rotation, as indicated in the figure above. This will correspond to our model control circuit."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "N-j7SCl-51-q",
+ "colab": {}
+ },
+ "source": [
+ "# Parameters that the classical NN will feed values into.\n",
+ "control_params = sympy.symbols('theta_1 theta_2 theta_3')\n",
+ "\n",
+ "# Create the parameterized circuit.\n",
+ "model_circuit = cirq.Circuit(\n",
+ " cirq.Rz(control_params[2])(qubit),\n",
+ " cirq.Ry(control_params[1])(qubit),\n",
+ " cirq.Rx(control_params[0])(qubit)\n",
+ ")\n",
+ "\n",
+ "SVGCircuit(model_circuit)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "wfjSbsvb7g9f"
+ },
+ "source": [
+ "### 2.2 Model definition\n",
+ "\n",
+ "Now define your model. The network architecture is indicated by the plot of the model below, which is compared to the figure above to verify correctness."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "Lod5uhHo7gXH",
+ "colab": {}
+ },
+ "source": [
+ "circuit_inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string, name='circuit_input')\n",
+ "command_inputs = tf.keras.Input(dtype=tf.dtypes.float32,\n",
+ " shape=(1,),\n",
+ " name='command_input')\n",
+ "d1 = tf.keras.layers.Dense(10)(command_inputs)\n",
+ "d2 = tf.keras.layers.Dense(3)(d1)\n",
+ "expectation = tfq.layers.ControlledPQC(model_circuit, pauli_z)([circuit_inputs, d2])\n",
+ "model = tf.keras.Model(inputs=[circuit_inputs, command_inputs],\n",
+ " outputs=expectation)\n",
+ "tf.keras.utils.plot_model(model,\n",
+ " show_shapes=True,\n",
+ " show_layer_names=True,\n",
+ " dpi=70)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "bbiVHvSYVW4H"
+ },
+ "source": [
+ "### 2.3 Data definition\n",
+ "\n",
+ "Define two possible inputs and two possible corresponding outputs:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "_VYfzHffWo7n",
+ "colab": {}
+ },
+ "source": [
+ "# Input values to the classical NN.\n",
+ "commands = np.array([[0], [1]], dtype=np.float32)\n",
+ "\n",
+ "# Desired Z expectation value at output of quantum circuit.\n",
+ "expected_outputs = np.array([[1], [-1]], dtype=np.float32)\n",
+ "\n",
+ "# These rotations define the random state to start in.\n",
+ "random_rotations = np.random.uniform(0, 2 * np.pi, 3)\n",
+ "\n",
+ "# Create datapoint circuits. (They're the same for each command.)\n",
+ "datapoint_circuits = tfq.convert_to_tensor([cirq.Circuit(\n",
+ " cirq.Rx(random_rotations[0])(qubit),\n",
+ " cirq.Ry(random_rotations[1])(qubit),\n",
+ " cirq.Rz(random_rotations[2])(qubit))\n",
+ "] * 2)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "gB--UhZZYgVY"
+ },
+ "source": [
+ "### 2.4 Training"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "dtPYqbNi8zeZ",
+ "colab": {}
+ },
+ "source": [
+ "optimizer = tf.keras.optimizers.Adam(learning_rate=0.05)\n",
+ "loss = tf.keras.losses.mean_squared_error\n",
+ "model.compile(optimizer=optimizer, loss=loss)\n",
+ "\n",
+ "# Note that the only changing input is the command input.\n",
+ "# datapoint_circuit is the same for the different parameter pairs.\n",
+ "history = model.fit(x=[datapoint_circuits, commands],\n",
+ " y=expected_outputs,\n",
+ " epochs=30,\n",
+ " verbose=0)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "azE-qV0OaC1o",
+ "colab": {}
+ },
+ "source": [
+ "plt.plot(history.history['loss'])\n",
+ "plt.title(\"Learning to Control a Qubit\")\n",
+ "plt.xlabel(\"Iterations\")\n",
+ "plt.ylabel(\"Error in Control\")\n",
+ "plt.show()"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "GTd5DGcRmmOK"
+ },
+ "source": [
+ "From this plot you can see that the neural network appears to have learned to return the qubit back to the initial state. Quickly verify this by examining the parameters of the model."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "jNrW0NXR-lDC"
+ },
+ "source": [
+ "### 2.5 Learning to prepare eigenstates of different operators\n",
+ "\n",
+ "The choice of the $\\pm \\hat{Z}$ eigenstates corresponding to 1 and 0 was arbitrary. You could have just as easily wanted 1 to correspond to the $+ \\hat{Z}$ eigenstate and 0 to correspond to the $-\\hat{X}$ eigenstate. One way (of many) to accomplish this is by specifying a different measurement operator for each command, as indicated in the figure below:\n",
+ "\n",
+ "
\n",
+ "\n",
+ "This requires a slightly different approach involving new usage of the `tfq.layers.Expectation` from earlier. Now your input, output pairs have grown to be input: (circuit, command, operator). The output is still the expectation value."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "Ci3WMZ9CjEM1"
+ },
+ "source": [
+ "#### 2.5.1 New model definition\n",
+ "\n",
+ "Lets take a look at the model to accomplish this task:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "n_aTG4g3-y0F",
+ "colab": {}
+ },
+ "source": [
+ "# Define inputs.\n",
+ "command_inputs = tf.keras.layers.Input(shape=(1),\n",
+ " dtype=tf.dtypes.float32,\n",
+ " name='command_input')\n",
+ "circuit_inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string, name='circuit_input')\n",
+ "operator_inputs = tf.keras.Input(shape=(1,), dtype=tf.dtypes.string, name='operaotr_input')\n",
+ "\n",
+ "# Define classical NN.\n",
+ "dense_1 = tf.keras.layers.Dense(10)(command_inputs)\n",
+ "dense_2 = tf.keras.layers.Dense(3)(dense_1)\n",
+ "\n",
+ "\n",
+ "# Since you aren't using a PQC or ControlledPQC you must append\n",
+ "# you model circuit onto the datapoint circuit tensor manually.\n",
+ "full_circuit = tfq.layers.AddCircuit()(circuit_inputs, append=model_circuit)\n",
+ "\n",
+ "expectation_output = tfq.layers.Expectation()(full_circuit,\n",
+ " symbol_names=control_params,\n",
+ " symbol_values=dense_2,\n",
+ " operators=operator_inputs)\n",
+ "two_axis_control_model = tf.keras.Model(\n",
+ " inputs=[circuit_inputs, command_inputs, operator_inputs], outputs=[expectation_output])\n",
+ "\n",
+ "tf.keras.utils.plot_model(two_axis_control_model, show_shapes=True, dpi=70)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "VQTM6CCiD4gU"
+ },
+ "source": [
+ "#### 2.5.2 Adding to datapoints\n",
+ "\n",
+ "Now you will include the operators you wish to measure for each datapoint you supply for `model_circuit`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "4gw_L3JG0_G0",
+ "colab_type": "code",
+ "colab": {}
+ },
+ "source": [
+ "operator_data = tfq.convert_to_tensor([[cirq.X(qubit)], [cirq.Z(qubit)]])"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "ALCKSvwh0_G2",
+ "colab_type": "text"
+ },
+ "source": [
+ "#### 2.5.3 Training\n",
+ "\n",
+ "Now that you have your new input outputs pairs you can train once again using keras."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "nFuGA73MAA4p",
+ "colab": {}
+ },
+ "source": [
+ "optimizer = tf.keras.optimizers.Adam(learning_rate=0.05)\n",
+ "loss = tf.keras.losses.mean_squared_error\n",
+ "\n",
+ "two_axis_control_model.compile(optimizer=optimizer, loss=loss)\n",
+ "\n",
+ "history = two_axis_control_model.fit(\n",
+ " x=[datapoint_circuits, commands, operator_data],\n",
+ " y=expected_outputs,\n",
+ " epochs=30,\n",
+ " verbose=1)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "Cf_G-GdturLL",
+ "colab": {}
+ },
+ "source": [
+ "plt.plot(history.history['loss'])\n",
+ "plt.title(\"Learning to Control a Qubit\")\n",
+ "plt.xlabel(\"Iterations\")\n",
+ "plt.ylabel(\"Error in Control\")\n",
+ "plt.show()"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "sdCPDH9NlJBl"
+ },
+ "source": [
+ "The loss function has dropped to zero.\n",
+ "\n",
+ "If you inspect the parameters of the model, you'll see that the parameters have been recovered to control the qubit correctly with these new measurement operators."
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/tutorials/mnist.ipynb b/docs/tutorials/mnist.ipynb
index cc1a185e1..940298deb 100644
--- a/docs/tutorials/mnist.ipynb
+++ b/docs/tutorials/mnist.ipynb
@@ -1,644 +1,642 @@
{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "xLOXFOT5Q40E"
- },
- "source": [
- "##### Copyright 2020 The TensorFlow Authors."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "cellView": "form",
- "colab": {},
- "colab_type": "code",
- "id": "iiQkM5ZgQ8r2"
- },
- "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": "j6331ZSsQGY3"
- },
- "source": [
- "# MNIST classification"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "i9Jcnb8bQQyd"
- },
- "source": [
- ""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "udLObUVeGfTs"
- },
- "source": [
- "This tutorial builds a quantum neural network (QNN) to classify a simplified version of MNIST, similar to the approach used in Farhi et al. The performance of the quantum neural network on this classical data problem is compared with a classical neural network."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "X35qHdh5Gzqg"
- },
- "source": [
- "## Setup\n",
- "\n",
- "Download and install the required packages:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "fAVlg7rxkvUw"
- },
- "outputs": [],
- "source": [
- "%%capture\n",
- "!pip install --upgrade pip\n",
- "!pip install cirq==0.6.0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "TorxE5tnkvb2"
- },
- "outputs": [],
- "source": [
- "%%capture\n",
- "!pip install --upgrade tensorflow==2.0.0"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "FxkQA6oblNqI"
- },
- "source": [
- "Note: If the following code cell fails, execute the first code cells and then restart the Colab runtime (*Runtime > Restart Runtime*)."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "saFHsRDpkvkH"
- },
- "outputs": [],
- "source": [
- "%%capture\n",
- "h = \"2dfcfceb9726fa73c40381c037dc01facd3d061e\"\n",
- "!cd ~/\n",
- "!rm -r -f TFQuantum/\n",
- "!git clone https://{h}:{h}@github.com/quantumlib/TFQuantum.git;cd TFQuantum/\n",
- "!pip install --upgrade ./TFQuantum/wheels/tfquantum-0.2.0-cp36-cp36m-linux_x86_64.whl"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "hdgMMZEBGqyl"
- },
- "source": [
- "Now import TensorFlow and the module dependencies:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "enZ300Bflq80"
- },
- "outputs": [],
- "source": [
- "import tensorflow as tf\n",
- "import tensorflow_quantum as tfq\n",
- "\n",
- "import cirq\n",
- "import sympy\n",
- "import numpy as np\n",
- "import seaborn\n",
- "import collections\n",
- "\n",
- "# visualization tools\n",
- "%matplotlib inline\n",
- "import matplotlib.pyplot as plt\n",
- "from cirq.contrib.svg import SVGCircuit"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "b08Mmbs8lr81"
- },
- "source": [
- "## 1. Load the data\n",
- "\n",
- "Load the MNIST data distributed with Keras. Since this tutorial demonstrates a binary classification problem for the numbers 3 and 6, remove the other numbers. Then display the first training example:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "p-XEU8egGL6q"
- },
- "outputs": [],
- "source": [
- "(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()\n",
- "\n",
- "print(\"Number of original training examples:\", len(x_train))\n",
- "print(\"Number of original test examples:\", len(x_train))\n",
- "\n",
- "# Keep the 3s and 6s and remove the other numbers.\n",
- "x_train, y_train = zip(*(\n",
- " (x, y) for x, y in zip(x_train, y_train) if y in [3, 6]))\n",
- "x_test, y_test = zip(*((x, y) for x, y in zip(x_test, y_test) if y in [3, 6]))\n",
- "\n",
- "print(\"Number of filtered training examples:\", len(x_train))\n",
- "print(\"Number of filtered test examples:\", len(x_test))\n",
- "\n",
- "print(y_train[0])\n",
- "seaborn.heatmap(x_train[0])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "fmmtplIFGL6t"
- },
- "source": [
- "But an image size of 28x28 is much too large for current quantum computers. Resize the image down to 4x4 and scale the numbers between 0 and 1:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "lR4tKxLOGL6u"
- },
- "outputs": [],
- "source": [
- "def reduce_image(x):\n",
- " x = tf.reshape(x, [1, 28, 28, 1])\n",
- " x = tf.image.resize(x, [4, 4])\n",
- " x = tf.reshape(x, [4, 4])\n",
- " x = x / 255\n",
- " return x.numpy()\n",
- "\n",
- "\n",
- "x_train = [reduce_image(x) for x in x_train]\n",
- "x_test = [reduce_image(x) for x in x_test]\n",
- "\n",
- "\n",
- "# Remove examples where the same input has multiple labels.\n",
- "def remove_contradicting(xs, ys):\n",
- " mapping = collections.defaultdict(set)\n",
- " for x, y in zip(xs, ys):\n",
- " mapping[str(x)].add(y)\n",
- " return zip(*((x, y) for x, y in zip(xs, ys) if len(mapping[str(x)]) == 1))\n",
- "\n",
- "\n",
- "x_train, y_train = remove_contradicting(x_train, y_train)\n",
- "x_test, y_test = remove_contradicting(x_test, y_test)\n",
- "\n",
- "print(\"Number of non-contradicting training examples: \", len(x_train))\n",
- "print(\"Number of non-contradicting test examples: \", len(x_test))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "pOMd7zIjGL6x"
- },
- "source": [
- "Again, display the first training example—after resize: "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "YIYOtCRIGL6y"
- },
- "outputs": [],
- "source": [
- "print(y_train[0])\n",
- "seaborn.heatmap(x_train[0])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "_3kBLeodGL61"
- },
- "source": [
- "To process images using a quantum computer, Farhi et al. proposed representing each pixel with a qubit, with the state depending on the value of the pixel.\n",
- "\n",
- "To classify these images, Farhi et al. proposed taking the expectation of a readout qubit in a parameterized circuit. The expectation returns a value between 1 and -1, so choosing 1 and -1 as the targets is natural."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "aOu_3-3ZGL61"
- },
- "outputs": [],
- "source": [
- "def convert_to_circuit(image):\n",
- " \"\"\"Encode truncated classical image into quantum datapoint.\"\"\"\n",
- " values = np.ndarray.flatten(image)\n",
- " qubits = cirq.GridQubit.rect(4, 4)\n",
- " circuit = cirq.Circuit()\n",
- " for i, value in enumerate(values):\n",
- " if value > 0.5:\n",
- " circuit.append(cirq.X(qubits[i]))\n",
- " return circuit\n",
- "\n",
- "\n",
- "x_train = [convert_to_circuit(x) for x in x_train]\n",
- "x_test = [convert_to_circuit(x) for x in x_test]\n",
- "\n",
- "\n",
- "def convert_label(y):\n",
- " if y == 3:\n",
- " return 1.0\n",
- " else:\n",
- " return -1.0\n",
- "\n",
- "\n",
- "y_train = [convert_label(y) for y in y_train]\n",
- "y_test = [convert_label(y) for y in y_test]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "Zkr6jVDgGL64"
- },
- "source": [
- "And display the circuit diagram for the first example:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "qP-wmm-0GL65"
- },
- "outputs": [],
- "source": [
- "print(y_train[0])\n",
- "print(x_train[0])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "4USiqeOqGL67"
- },
- "source": [
- "## 2. Quantum neural network\n",
- "\n",
- "There is little guidance for a quantum circuit structure that classifies images. Since the classification is based on the expectation of the readout qubit, Farhi et al. propose using two qubit gates, with the readout qubit always acted upon.\n",
- "\n",
- "This following example shows this layer approach. It uses *n* instances of the same gate, with each of the data qubits acting on the readout qubit:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "JiALbpwRGL69"
- },
- "outputs": [],
- "source": [
- "def create_quantum_model():\n",
- " \"\"\"Create a QNN model circuit and readout operation to go along with it.\"\"\"\n",
- " data_qubits = cirq.GridQubit.rect(4, 4)\n",
- " readout = cirq.GridQubit(16, 0)\n",
- "\n",
- " symbols = []\n",
- " circuit = cirq.Circuit()\n",
- "\n",
- " # Generates a layer of the gate type.\n",
- " def layer(gate, prefix):\n",
- " for i, qubit in enumerate(data_qubits):\n",
- " symbol = sympy.Symbol(prefix + '-' + str(i))\n",
- " circuit.append(gate(qubit, readout)**symbol)\n",
- " symbols.append(symbol)\n",
- "\n",
- " # Prepare the readout qubit.\n",
- " circuit.append(cirq.X(readout))\n",
- " circuit.append(cirq.H(readout))\n",
- "\n",
- " # Then add layers (experiment by adding more).\n",
- " layer(cirq.XX, \"xx-1\")\n",
- " layer(cirq.ZZ, \"zz-1\")\n",
- "\n",
- " # Finally, prepare the readout qubit.\n",
- " circuit.append(cirq.H(readout))\n",
- "\n",
- " return circuit, cirq.Z(readout)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "LY7vbY6yfABE"
- },
- "source": [
- "Build the Keras model with the quantum components. This model is fed the \"quantum data\" that encodes the classical data."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "x2xDsYpFGL7A"
- },
- "outputs": [],
- "source": [
- "# Get the quantum components.\n",
- "model_circuit, model_readout = create_quantum_model()\n",
- "\n",
- "# Build the Keras model.\n",
- "model = tf.keras.Sequential()\n",
- "model.add(tf.keras.layers.Input(shape=(), dtype=tf.dtypes.string))\n",
- "model.add(tfq.layers.PQC(model_circuit, model_readout))\n",
- "\n",
- "\n",
- "# Define a custom accuracy that equals the sign of the output.\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",
- "print(model.summary())\n",
- "\n",
- "model.compile(loss=tf.keras.losses.hinge,\n",
- " optimizer=tf.keras.optimizers.Adam(),\n",
- " metrics=[custom_accuracy])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "lsuOzDYblA9s"
- },
- "source": [
- "Reduce the dataset size for faster training. For better results, ignore this code cell:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "n8vuQpSLlBV2"
- },
- "outputs": [],
- "source": [
- "# Comment out for increased accuracy.\n",
- "NUM_EXAMPLES = 500\n",
- "x_train = x_train[:NUM_EXAMPLES]\n",
- "y_train = y_train[:NUM_EXAMPLES]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "QMSdgGC1GL7D"
- },
- "source": [
- "Using the full dataset, training this model should achieve >85% accuracy on the test set. Here, only `NUM_EXAMPLES` datapoints are used to save time."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "nl0aQ-w7GL7H"
- },
- "outputs": [],
- "source": [
- "# Wrap the training and test sets so Keras can handle them.\n",
- "x_train = tfq.convert_to_tensor(x_train)\n",
- "x_test = tfq.convert_to_tensor(x_test)\n",
- "y_train = np.array(y_train)\n",
- "y_test = np.array(y_test)\n",
- "\n",
- "model.fit(x_train,\n",
- " y_train,\n",
- " batch_size=32,\n",
- " epochs=3,\n",
- " verbose=1,\n",
- " validation_data=(x_test, y_test))\n",
- "\n",
- "qnn_results = model.evaluate(x_test, y_test)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "8952YvuWGL7J"
- },
- "source": [
- "## 3. Classical neural network\n",
- "\n",
- "While the quantum neural network works for this simplified MNIST problem, a basic classical neural network can easily outperform a QNN on this task. After a single epoch, a classical neural network can achieve >98% accuracy on the holdout set.\n",
- "\n",
- "In the following example, a classical neural network is used for a 10-class classification problem (instead of the 2-class problem for the QNN), and uses the entire 28x28 image instead of subsampling the image."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "pZofEHhLGL7L"
- },
- "outputs": [],
- "source": [
- "(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()\n",
- "\n",
- "y_train = tf.keras.utils.to_categorical(y_train, 10)\n",
- "y_test = tf.keras.utils.to_categorical(y_test, 10)\n",
- "\n",
- "x_train = x_train.astype('float32') / 255\n",
- "x_test = x_test.astype('float32') / 255\n",
- "\n",
- "\n",
- "def create_classical_model():\n",
- " # A simple model based off LeNet from https://keras.io/examples/mnist_cnn/\n",
- " model = tf.keras.Sequential()\n",
- " model.add(tf.keras.layers.Reshape([28, 28, 1]))\n",
- " model.add(tf.keras.layers.Conv2D(32, [3, 3], activation='relu'))\n",
- " model.add(tf.keras.layers.Conv2D(64, [3, 3], activation='relu'))\n",
- " model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))\n",
- " model.add(tf.keras.layers.Dropout(0.25))\n",
- " model.add(tf.keras.layers.Flatten())\n",
- " model.add(tf.keras.layers.Dense(128, activation='relu'))\n",
- " model.add(tf.keras.layers.Dropout(0.5))\n",
- " model.add(tf.keras.layers.Dense(10, activation='softmax'))\n",
- " return model\n",
- "\n",
- "\n",
- "model = create_classical_model()\n",
- "model.compile(loss=tf.keras.losses.categorical_crossentropy,\n",
- " optimizer=tf.keras.optimizers.Adam(),\n",
- " metrics=['accuracy'])\n",
- "\n",
- "model.fit(x_train,\n",
- " y_train,\n",
- " batch_size=128,\n",
- " epochs=1,\n",
- " verbose=1,\n",
- " validation_data=(x_test, y_test))\n",
- "\n",
- "cnn_results = model.evaluate(x_test, y_test)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "RH3mam7EGL7N"
- },
- "source": [
- "## 4. Comparison\n",
- "\n",
- "Despite a more difficult problem, the classical neural network easily outperforms the quantum neural network. For classical data, it is difficult to beat a classical neural network."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "NOMeN7pMGL7P"
- },
- "outputs": [],
- "source": [
- "qnn_accuracy = qnn_results[1]\n",
- "cnn_accuracy = cnn_results[1]\n",
- "\n",
- "seaborn.barplot([\"quantum\", \"classical\"], [qnn_accuracy, cnn_accuracy])"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "collapsed_sections": [],
- "name": "mnist.ipynb",
- "private_outputs": true,
- "provenance": [],
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "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.5rc1"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
+ "nbformat": 4,
+ "nbformat_minor": 0,
+ "metadata": {
+ "colab": {
+ "name": "mnist.ipynb",
+ "provenance": [],
+ "private_outputs": true,
+ "collapsed_sections": [],
+ "toc_visible": true
+ },
+ "kernelspec": {
+ "name": "python3",
+ "display_name": "Python 3"
+ },
+ "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.5rc1"
+ }
+ },
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "xLOXFOT5Q40E"
+ },
+ "source": [
+ "##### Copyright 2020 The TensorFlow Authors."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "cellView": "form",
+ "colab_type": "code",
+ "id": "iiQkM5ZgQ8r2",
+ "colab": {}
+ },
+ "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."
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "j6331ZSsQGY3"
+ },
+ "source": [
+ "# MNIST classification"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "i9Jcnb8bQQyd"
+ },
+ "source": [
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "udLObUVeGfTs",
+ "colab_type": "text"
+ },
+ "source": [
+ "This tutorial builds a quantum neural network (QNN) to classify a simplified version of MNIST, similar to the approach used in Farhi et al. The performance of the quantum neural network on this classical data problem is compared with a classical neural network."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "X35qHdh5Gzqg",
+ "colab_type": "text"
+ },
+ "source": [
+ "## Setup\n",
+ "\n",
+ "Download and install the required packages:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "fAVlg7rxkvUw",
+ "colab": {}
+ },
+ "source": [
+ "%%capture\n",
+ "!pip install --upgrade pip\n",
+ "!pip install cirq==0.7.0"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "TorxE5tnkvb2",
+ "colab": {}
+ },
+ "source": [
+ "%%capture\n",
+ "!pip install --upgrade tensorflow==2.0.0"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "FxkQA6oblNqI"
+ },
+ "source": [
+ "Note: If the following code cell fails, execute the first code cells and then restart the Colab runtime (*Runtime > Restart Runtime*)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "saFHsRDpkvkH",
+ "colab": {}
+ },
+ "source": [
+ "%%capture\n",
+ "h = \"2dfcfceb9726fa73c40381c037dc01facd3d061e\"\n",
+ "!cd ~/\n",
+ "!rm -r -f TFQuantum/\n",
+ "!git clone https://{h}:{h}@github.com/quantumlib/TFQuantum.git;cd TFQuantum/\n",
+ "!pip install --upgrade ./TFQuantum/wheels/tfquantum-0.2.0-cp36-cp36m-linux_x86_64.whl"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "hdgMMZEBGqyl",
+ "colab_type": "text"
+ },
+ "source": [
+ "Now import TensorFlow and the module dependencies:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "enZ300Bflq80",
+ "colab": {}
+ },
+ "source": [
+ "import tensorflow as tf\n",
+ "import tensorflow_quantum as tfq\n",
+ "\n",
+ "import cirq\n",
+ "import sympy\n",
+ "import numpy as np\n",
+ "import seaborn\n",
+ "import collections\n",
+ "\n",
+ "# visualization tools\n",
+ "%matplotlib inline\n",
+ "import matplotlib.pyplot as plt\n",
+ "from cirq.contrib.svg import SVGCircuit"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "b08Mmbs8lr81"
+ },
+ "source": [
+ "## 1. Load the data\n",
+ "\n",
+ "Load the MNIST data distributed with Keras. Since this tutorial demonstrates a binary classification problem for the numbers 3 and 6, remove the other numbers. Then display the first training example:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "p-XEU8egGL6q",
+ "colab_type": "code",
+ "colab": {}
+ },
+ "source": [
+ "(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()\n",
+ "\n",
+ "print(\"Number of original training examples:\", len(x_train))\n",
+ "print(\"Number of original test examples:\", len(x_train))\n",
+ "\n",
+ "# Keep the 3s and 6s and remove the other numbers.\n",
+ "x_train, y_train = zip(*((x, y) for x, y in zip(x_train, y_train) if y in [3, 6]))\n",
+ "x_test, y_test = zip(*((x, y) for x, y in zip(x_test, y_test) if y in [3, 6]))\n",
+ "\n",
+ "print(\"Number of filtered training examples:\", len(x_train))\n",
+ "print(\"Number of filtered test examples:\", len(x_test))\n",
+ "\n",
+ "print(y_train[0])\n",
+ "seaborn.heatmap(x_train[0])"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "fmmtplIFGL6t",
+ "colab_type": "text"
+ },
+ "source": [
+ "But an image size of 28x28 is much too large for current quantum computers. Resize the image down to 4x4 and scale the numbers between 0 and 1:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "lR4tKxLOGL6u",
+ "colab_type": "code",
+ "colab": {}
+ },
+ "source": [
+ "def reduce_image(x):\n",
+ " x = tf.reshape(x, [1, 28, 28, 1])\n",
+ " x = tf.image.resize(x, [4, 4])\n",
+ " x = tf.reshape(x, [4, 4])\n",
+ " x = x / 255\n",
+ " return x.numpy()\n",
+ "\n",
+ "\n",
+ "x_train = [reduce_image(x) for x in x_train]\n",
+ "x_test = [reduce_image(x) for x in x_test]\n",
+ "\n",
+ "\n",
+ "# Remove examples where the same input has multiple labels.\n",
+ "def remove_contradicting(xs, ys):\n",
+ " mapping = collections.defaultdict(set)\n",
+ " for x, y in zip(xs, ys):\n",
+ " mapping[str(x)].add(y)\n",
+ " return zip(*((x, y) for x, y in zip(xs, ys) if len(mapping[str(x)]) == 1))\n",
+ "\n",
+ "\n",
+ "x_train, y_train = remove_contradicting(x_train, y_train)\n",
+ "x_test, y_test = remove_contradicting(x_test, y_test)\n",
+ "\n",
+ "print(\"Number of non-contradicting training examples: \", len(x_train))\n",
+ "print(\"Number of non-contradicting test examples: \", len(x_test))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "pOMd7zIjGL6x",
+ "colab_type": "text"
+ },
+ "source": [
+ "Again, display the first training example—after resize: "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "YIYOtCRIGL6y",
+ "colab_type": "code",
+ "colab": {}
+ },
+ "source": [
+ "print(y_train[0])\n",
+ "seaborn.heatmap(x_train[0])"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "_3kBLeodGL61",
+ "colab_type": "text"
+ },
+ "source": [
+ "To process images using a quantum computer, Farhi et al. proposed representing each pixel with a qubit, with the state depending on the value of the pixel.\n",
+ "\n",
+ "To classify these images, Farhi et al. proposed taking the expectation of a readout qubit in a parameterized circuit. The expectation returns a value between 1 and -1, so choosing 1 and -1 as the targets is natural."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "aOu_3-3ZGL61",
+ "colab_type": "code",
+ "colab": {}
+ },
+ "source": [
+ "def convert_to_circuit(image):\n",
+ " \"\"\"Encode truncated classical image into quantum datapoint.\"\"\"\n",
+ " values = np.ndarray.flatten(image)\n",
+ " qubits = cirq.GridQubit.rect(4, 4)\n",
+ " circuit = cirq.Circuit()\n",
+ " for i, value in enumerate(values):\n",
+ " if value > 0.5:\n",
+ " circuit.append(cirq.X(qubits[i]))\n",
+ " return circuit\n",
+ "\n",
+ "\n",
+ "x_train = [convert_to_circuit(x) for x in x_train]\n",
+ "x_test = [convert_to_circuit(x) for x in x_test]\n",
+ "\n",
+ "\n",
+ "def convert_label(y):\n",
+ " if y == 3:\n",
+ " return 1.0\n",
+ " else:\n",
+ " return -1.0\n",
+ "\n",
+ "\n",
+ "y_train = [convert_label(y) for y in y_train]\n",
+ "y_test = [convert_label(y) for y in y_test]"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Zkr6jVDgGL64",
+ "colab_type": "text"
+ },
+ "source": [
+ "And display the circuit diagram for the first example:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "qP-wmm-0GL65",
+ "colab_type": "code",
+ "colab": {}
+ },
+ "source": [
+ "print(y_train[0])\n",
+ "print(x_train[0])"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "4USiqeOqGL67",
+ "colab_type": "text"
+ },
+ "source": [
+ "## 2. Quantum neural network\n",
+ "\n",
+ "There is little guidance for a quantum circuit structure that classifies images. Since the classification is based on the expectation of the readout qubit, Farhi et al. propose using two qubit gates, with the readout qubit always acted upon.\n",
+ "\n",
+ "This following example shows this layer approach. It uses *n* instances of the same gate, with each of the data qubits acting on the readout qubit:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "JiALbpwRGL69",
+ "colab_type": "code",
+ "colab": {}
+ },
+ "source": [
+ "def create_quantum_model():\n",
+ " \"\"\"Create a QNN model circuit and readout operation to go along with it.\"\"\"\n",
+ " data_qubits = cirq.GridQubit.rect(4, 4)\n",
+ " readout = cirq.GridQubit(16, 0)\n",
+ "\n",
+ " symbols = []\n",
+ " circuit = cirq.Circuit()\n",
+ "\n",
+ " # Generates a layer of the gate type.\n",
+ " def layer(gate, prefix):\n",
+ " for i, qubit in enumerate(data_qubits):\n",
+ " symbol = sympy.Symbol(prefix + '-' + str(i))\n",
+ " circuit.append(gate(qubit, readout)**symbol)\n",
+ " symbols.append(symbol)\n",
+ "\n",
+ " # Prepare the readout qubit.\n",
+ " circuit.append(cirq.X(readout))\n",
+ " circuit.append(cirq.H(readout))\n",
+ "\n",
+ " # Then add layers (experiment by adding more).\n",
+ " layer(cirq.XX, \"xx-1\")\n",
+ " layer(cirq.ZZ, \"zz-1\")\n",
+ "\n",
+ " # Finally, prepare the readout qubit.\n",
+ " circuit.append(cirq.H(readout))\n",
+ "\n",
+ " return circuit, cirq.Z(readout)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "LY7vbY6yfABE",
+ "colab_type": "text"
+ },
+ "source": [
+ "Build the Keras model with the quantum components. This model is fed the \"quantum data\" that encodes the classical data."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "x2xDsYpFGL7A",
+ "colab_type": "code",
+ "colab": {}
+ },
+ "source": [
+ "# Get the quantum components.\n",
+ "model_circuit, model_readout = create_quantum_model()\n",
+ "\n",
+ "# Build the Keras model.\n",
+ "model = tf.keras.Sequential()\n",
+ "model.add(tf.keras.layers.Input(shape=(), dtype=tf.dtypes.string))\n",
+ "model.add(tfq.layers.PQC(model_circuit, model_readout))\n",
+ "\n",
+ "# Define a custom accuracy that equals the sign of the output.\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",
+ "print(model.summary())\n",
+ "\n",
+ "model.compile(loss=tf.keras.losses.hinge,\n",
+ " optimizer=tf.keras.optimizers.Adam(),\n",
+ " metrics=[custom_accuracy])"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "lsuOzDYblA9s",
+ "colab_type": "text"
+ },
+ "source": [
+ "Reduce the dataset size for faster training. For better results, ignore this code cell:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "n8vuQpSLlBV2",
+ "colab_type": "code",
+ "colab": {}
+ },
+ "source": [
+ "# Comment out for increased accuracy.\n",
+ "NUM_EXAMPLES = 500\n",
+ "x_train = x_train[:NUM_EXAMPLES]\n",
+ "y_train = y_train[:NUM_EXAMPLES]"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "QMSdgGC1GL7D",
+ "colab_type": "text"
+ },
+ "source": [
+ "Using the full dataset, training this model should achieve >85% accuracy on the test set. Here, only `NUM_EXAMPLES` datapoints are used to save time."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "nl0aQ-w7GL7H",
+ "colab_type": "code",
+ "colab": {}
+ },
+ "source": [
+ "# Wrap the training and test sets so Keras can handle them.\n",
+ "x_train = tfq.convert_to_tensor(x_train)\n",
+ "x_test = tfq.convert_to_tensor(x_test)\n",
+ "y_train = np.array(y_train)\n",
+ "y_test = np.array(y_test)\n",
+ "\n",
+ "model.fit(x_train,\n",
+ " y_train,\n",
+ " batch_size=32,\n",
+ " epochs=3,\n",
+ " verbose=1,\n",
+ " validation_data=(x_test, y_test))\n",
+ "\n",
+ "qnn_results = model.evaluate(x_test, y_test)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "8952YvuWGL7J",
+ "colab_type": "text"
+ },
+ "source": [
+ "## 3. Classical neural network\n",
+ "\n",
+ "While the quantum neural network works for this simplified MNIST problem, a basic classical neural network can easily outperform a QNN on this task. After a single epoch, a classical neural network can achieve >98% accuracy on the holdout set.\n",
+ "\n",
+ "In the following example, a classical neural network is used for a 10-class classification problem (instead of the 2-class problem for the QNN), and uses the entire 28x28 image instead of subsampling the image."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "pZofEHhLGL7L",
+ "colab_type": "code",
+ "colab": {}
+ },
+ "source": [
+ "(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()\n",
+ "\n",
+ "y_train = tf.keras.utils.to_categorical(y_train, 10)\n",
+ "y_test = tf.keras.utils.to_categorical(y_test, 10)\n",
+ "\n",
+ "x_train = x_train.astype('float32') / 255\n",
+ "x_test = x_test.astype('float32') / 255\n",
+ "\n",
+ "\n",
+ "def create_classical_model():\n",
+ " # A simple model based off LeNet from https://keras.io/examples/mnist_cnn/\n",
+ " model = tf.keras.Sequential()\n",
+ " model.add(tf.keras.layers.Reshape([28, 28, 1]))\n",
+ " model.add(tf.keras.layers.Conv2D(32, [3, 3], activation='relu'))\n",
+ " model.add(tf.keras.layers.Conv2D(64, [3, 3], activation='relu'))\n",
+ " model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))\n",
+ " model.add(tf.keras.layers.Dropout(0.25))\n",
+ " model.add(tf.keras.layers.Flatten())\n",
+ " model.add(tf.keras.layers.Dense(128, activation='relu'))\n",
+ " model.add(tf.keras.layers.Dropout(0.5))\n",
+ " model.add(tf.keras.layers.Dense(10, activation='softmax'))\n",
+ " return model\n",
+ "\n",
+ "\n",
+ "model = create_classical_model()\n",
+ "model.compile(loss=tf.keras.losses.categorical_crossentropy,\n",
+ " optimizer=tf.keras.optimizers.Adam(),\n",
+ " metrics=['accuracy'])\n",
+ "\n",
+ "model.fit(x_train,\n",
+ " y_train,\n",
+ " batch_size=128,\n",
+ " epochs=1,\n",
+ " verbose=1,\n",
+ " validation_data=(x_test, y_test))\n",
+ "\n",
+ "cnn_results = model.evaluate(x_test, y_test)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "RH3mam7EGL7N",
+ "colab_type": "text"
+ },
+ "source": [
+ "## 4. Comparison\n",
+ "\n",
+ "Despite a more difficult problem, the classical neural network easily outperforms the quantum neural network. For classical data, it is difficult to beat a classical neural network."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "NOMeN7pMGL7P",
+ "colab_type": "code",
+ "colab": {}
+ },
+ "source": [
+ "qnn_accuracy = qnn_results[1]\n",
+ "cnn_accuracy = cnn_results[1]\n",
+ "\n",
+ "seaborn.barplot([\"quantum\", \"classical\"], [qnn_accuracy, cnn_accuracy])"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ }
+ ]
}
diff --git a/docs/tutorials/model.png b/docs/tutorials/model.png
index 437bd490e..c8d88bb83 100644
Binary files a/docs/tutorials/model.png and b/docs/tutorials/model.png differ
diff --git a/docs/tutorials/qcnn.ipynb b/docs/tutorials/qcnn.ipynb
index ea1b81a59..10563a09a 100644
--- a/docs/tutorials/qcnn.ipynb
+++ b/docs/tutorials/qcnn.ipynb
@@ -1,1107 +1,1131 @@
{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "xLOXFOT5Q40E"
- },
- "source": [
- "##### Copyright 2020 The TensorFlow Authors."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "cellView": "form",
- "colab": {},
- "colab_type": "code",
- "id": "iiQkM5ZgQ8r2"
- },
- "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": [
- ""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "4D3xaWBHOIVg"
- },
- "source": [
- "This tutorial implements a simplified Quantum Convolutional Neural Network (QCNN), a proposed quantum analogue to a classical convolutional neural network that is also *translationally invariant*.\n",
- "\n",
- "This example demonstrates how to detect certain properties of a quantum data source, such as a quantum sensor or a complex simulation from a device. The quantum data source being a cluster state that may or may not have an excitation—what the QCNN will learn to detect."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "FnjolLuz8o5C"
- },
- "source": [
- "## Setup\n",
- "\n",
- "Download and install the required packages:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "evObd2BXr5hs"
- },
- "outputs": [],
- "source": [
- "%%capture\n",
- "!pip install --upgrade pip\n",
- "!pip install cirq==0.6.0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "Aquwcz-0aHqz"
- },
- "outputs": [],
- "source": [
- "%%capture\n",
- "!pip install --upgrade tensorflow==2.1.0"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "e_ZuLN_N8yhT"
- },
- "source": [
- "Note: If the following code cell fails, execute the first code cells and then restart the Colab runtime (*Runtime > Restart Runtime*)."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "3Pl5PW-ACO9J"
- },
- "outputs": [],
- "source": [
- "%%capture\n",
- "h = \"2dfcfceb9726fa73c40381c037dc01facd3d061e\"\n",
- "!cd ~/\n",
- "!rm -r -f TFQuantum/\n",
- "!git clone https://{h}:{h}@github.com/quantumlib/TFQuantum.git;cd TFQuantum/\n",
- "!pip install --upgrade ./TFQuantum/wheels/tfquantum-0.2.0-cp36-cp36m-linux_x86_64.whl"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "TL_LvHXzPNjW"
- },
- "source": [
- "Now import TensorFlow and the module dependencies:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "QytLEAtoejW5"
- },
- "outputs": [],
- "source": [
- "import tensorflow as tf\n",
- "import tensorflow_quantum as tfq\n",
- "\n",
- "import cirq\n",
- "import sympy\n",
- "import numpy as np\n",
- "\n",
- "# visualization tools\n",
- "%matplotlib inline\n",
- "import matplotlib.pyplot as plt\n",
- "from cirq.contrib.svg import SVGCircuit"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "j6331ZSsQGY3"
- },
- "source": [
- "## 1. Build a QCNN"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "Qg85u3G--CGq"
- },
- "source": [
- "### 1.1 Assemble circuits in a TensorFlow graph\n",
- "\n",
- "TensorFlow Quantum (TFQ) provides layer classes designed for in-graph circuit construction. One example is the `tfq.layers.AddCircuit` layer that inherits from `tf.keras.Layer`. This layer can either prepend or append to the input batch of circuits, as shown in the following figure.\n",
- "\n",
- "
\n",
- "\n",
- "The following snippet uses this layer:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "FhNf0G_OPLqZ"
- },
- "outputs": [],
- "source": [
- "qubit = cirq.GridQubit(0, 0)\n",
- "\n",
- "# Define some circuits.\n",
- "circuit1 = cirq.Circuit(cirq.X(qubit))\n",
- "circuit2 = cirq.Circuit(cirq.H(qubit))\n",
- "\n",
- "# Convert to a tensor.\n",
- "input_circuit_tensor = tfq.convert_to_tensor([circuit1, circuit2])\n",
- "\n",
- "# Define a circuit that we want to append\n",
- "y_circuit = cirq.Circuit(cirq.Y(qubit))\n",
- "\n",
- "# Instantiate our layer\n",
- "y_appender = tfq.layers.AddCircuit()\n",
- "\n",
- "# Run our circuit tensor through the layer and save the output.\n",
- "output_circuit_tensor = y_appender(input_circuit_tensor, append=y_circuit)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "ShZbRZCXkvk5"
- },
- "source": [
- "Examine the input tensor:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "ImRynsUN4BSG"
- },
- "outputs": [],
- "source": [
- "print(tfq.from_tensor(input_circuit_tensor))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "xkGU4ZTUk4gf"
- },
- "source": [
- "And examine the output tensor:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "tfff6dJp39Fg"
- },
- "outputs": [],
- "source": [
- "print(tfq.from_tensor(output_circuit_tensor))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "23JeZ7Ns5qy5"
- },
- "source": [
- "While it is possible to run the examples below without using `tfq.layers.AddCircuit`, it's a good opportunity to understand how complex functionality can be embedded into TensorFlow compute graphs."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "GcVplt9455Hi"
- },
- "source": [
- "### 1.2 Problem overview\n",
- "\n",
- "You will prepare a *cluster state* and train a quantum classifier to detect if it is \"excited\" or not. The cluster state is highly entangled but not necessarily difficult for a classical computer. For clarity, this is a simpler dataset than the one used in the paper.\n",
- "\n",
- "For this classification task you will implement a deep MERA-like QCNN architecture since:\n",
- "\n",
- "1. Like the QCNN, the cluster state on a ring is translationally invariant.\n",
- "2. The cluster state is highly entangled.\n",
- "\n",
- "This architecture should be effective at reducing entanglement, obtaining the classification by reading out a single qubit.\n",
- "\n",
- "
\n",
- "\n",
- "An \"excited\" cluster state is defined as a cluster state that had an X gate applied to any of its qubits. Qconv and QPool are discussed later in this tutorial."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "jpqtsGJH_I1d"
- },
- "source": [
- "### 1.3 Building blocks for TensorFlow\n",
- "\n",
- "
\n",
- "\n",
- "One way to solve this problem with TensorFlow Quantum is to implement the following:\n",
- "\n",
- "1. The input to the model is a circuit tensor—either an empty circuit or an X gate on a particular qubit indicating an excitation.\n",
- "2. The rest of the model's quantum components are constructed with `tfq.layers.AddCircuit` layers.\n",
- "3. For inference a `tfq.layers.PQC` layer is used. This reads $\\langle \\hat{Z} \\rangle$ and compares it to a label of 1 for an excited state, or -1 for a non-excited state."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "sFiRlDt_0-DL"
- },
- "source": [
- "### 1.4 Define layers\n",
- "\n",
- "Now define the layers shown in the figure above in TensorFlow."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "s2B9geIqLWHK"
- },
- "source": [
- "#### 1.4.1 Cluster state\n",
- "\n",
- "The first step is to define the cluster state using Cirq, a Google-provided framework for programming quantum circuits. Since this is a static part of the model, embed it using the `tfq.layers.AddCircuit` functionality."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "qpQwVWKazU8g"
- },
- "outputs": [],
- "source": [
- "def cluster_state_circuit(bits):\n",
- " \"\"\"Return a cluster state on the qubits in `bits`.\"\"\"\n",
- " circuit = cirq.Circuit()\n",
- " circuit.append(cirq.H.on_each(bits))\n",
- " for this_bit, next_bit in zip(bits, bits[1:] + [bits[0]]):\n",
- " circuit.append(cirq.CZ(this_bit, next_bit))\n",
- " return circuit"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "e9qX1uN740vJ"
- },
- "source": [
- "Display a cluster state circuit for a rectangle of cirq.GridQubit
s:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "9tZt0aAO4r4F"
- },
- "outputs": [],
- "source": [
- "SVGCircuit(cluster_state_circuit(cirq.GridQubit.rect(1, 4)))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "4xElWnRf1ZC7"
- },
- "source": [
- "#### 1.4.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": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "oNRGOqky2exY"
- },
- "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[7]]\n",
- " circuit += [cirq.YY(*bits)**symbols[8]]\n",
- " circuit += [cirq.XX(*bits)**symbols[9]]\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(control=source_qubit, target=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": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "T5uhvF-g2rpZ"
- },
- "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": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "aJTdRrfS2uIo"
- },
- "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": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "DOHRbkvH2xGK"
- },
- "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.4.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.\n",
- "\n",
- "
"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "1Fa19Lzb3wnR"
- },
- "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": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "Bi6q2nmY3z_U"
- },
- "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.4.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": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "jD3fgcWO4yEU"
- },
- "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": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "pFXow2OX47O5"
- },
- "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.5 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": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "vzEsY6-n5NR0"
- },
- "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": "FLMs5rBGR218"
- },
- "source": [
- "### 1.6 Data definition\n",
- "\n",
- "In this example, the data is the excitations of the cluster state, which is represented with X-gates. An excitation is labeled `1` and no excitation is labeled `-1`."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "oc5qTX6zSEpH"
- },
- "outputs": [],
- "source": [
- "excitations = []\n",
- "labels = []\n",
- "weights = []\n",
- "\n",
- "for bit in cluster_state_bits:\n",
- " excitations.append(cirq.Circuit(cirq.X(bit)))\n",
- " labels.append(1)\n",
- " weights.append(1)\n",
- "\n",
- "excitations.append(cirq.Circuit())\n",
- "\n",
- "# Labels[i] is 1 if excitations[i] contains an excitation circuit\n",
- "# and is -1 if excitations[i] contains a non-excitation circuit (empty)\n",
- "labels.append(-1)\n",
- "\n",
- "# Since we only have one non-excitation circuit, we will weigh it's label\n",
- "# higher to offset all of the excitation circuits.\n",
- "weights.append(len(cluster_state_bits))\n",
- "\n",
- "# Excitations is now a tensor of circuits with an excitation on each qubit\n",
- "# along with a circuit representing no excitation (empty).\n",
- "excitations = tfq.convert_to_tensor(excitations)"
- ]
- },
- {
- "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": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "_TFkAm1sQZEN"
- },
- "outputs": [],
- "source": [
- "qcnn_model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.05),\n",
- " loss=tf.losses.mse)\n",
- "\n",
- "history = qcnn_model.fit(x=excitations,\n",
- " y=np.array(labels),\n",
- " sample_weight=np.array(weights),\n",
- " batch_size=np.size(weights),\n",
- " epochs=25,\n",
- " verbose=1)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "2tiCJOb5Qzcr"
- },
- "outputs": [],
- "source": [
- "plt.plot(history.history['loss'][1:])\n",
- "plt.title(\"Training a Quantum CNN to Detect Excited Cluster States\")\n",
- "plt.xlabel(\"Epochs\")\n",
- "plt.ylabel(\"Mean-Square Classification Error\")\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",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "lKXuOApgWYFa"
- },
- "source": [
- "#### 2.1.1 Model definition"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "Ut-U1hBkQ8Fs"
- },
- "outputs": [],
- "source": [
- "# 1-local operators to read out\n",
- "readouts = [cirq.Z(bit) for bit in cluster_state_bits[4:]]\n",
- "\n",
- "\n",
- "def multi_readout_model_circuit(qubits):\n",
- " \"\"\"Make a model circuit with less quantum pool and conv operations.\"\"\"\n",
- " model_circuit = cirq.Circuit()\n",
- " symbols = sympy.symbols('qconv0:21')\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",
- " return model_circuit\n",
- "\n",
- "\n",
- "# Build a model enacting the logic in 2.1 of this notebook.\n",
- "excitation_input_dual = tf.keras.Input(shape=(), dtype=tf.dtypes.string)\n",
- "\n",
- "cluster_state_dual = tfq.layers.AddCircuit()(\n",
- " excitation_input_dual, prepend=cluster_state_circuit(cluster_state_bits))\n",
- "\n",
- "quantum_model_dual = tfq.layers.PQC(\n",
- " multi_readout_model_circuit(cluster_state_bits),\n",
- " readouts)(cluster_state_dual)\n",
- "\n",
- "d1_dual = tf.keras.layers.Dense(8)(quantum_model_dual)\n",
- "\n",
- "d2_dual = tf.keras.layers.Dense(1)(d1_dual)\n",
- "\n",
- "hybrid_model = tf.keras.Model(inputs=[excitation_input_dual], outputs=[d2_dual])\n",
- "\n",
- "# Display the model architecture\n",
- "tf.keras.utils.plot_model(hybrid_model,\n",
- " show_shapes=True,\n",
- " show_layer_names=False,\n",
- " dpi=70)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "qDqoLZJuWcgH"
- },
- "source": [
- "#### 2.1.2 Train the model"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "EyYw9kYIRCE7"
- },
- "outputs": [],
- "source": [
- "hybrid_model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.05),\n",
- " loss=tf.losses.mse)\n",
- "\n",
- "hybrid_history = hybrid_model.fit(x=excitations,\n",
- " y=np.array(labels),\n",
- " sample_weight=np.array(weights),\n",
- " batch_size=np.size(weights),\n",
- " epochs=25,\n",
- " verbose=1)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "yL3jhGiBRJHt"
- },
- "outputs": [],
- "source": [
- "plt.plot(history.history['loss'], label='QCNN')\n",
- "plt.plot(hybrid_history.history['loss'], label='Hybrid CNN')\n",
- "plt.title(\"Quantum vs Hybrid CNN performance\")\n",
- "plt.xlabel(\"Epochs\")\n",
- "plt.legend()\n",
- "plt.ylabel(\"Mean-Square Classification Error\")\n",
- "plt.show()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "NCNiNvheRNzq"
- },
- "source": [
- "As you can see, with very modest classical assistance, the hybrid model out-performs the purely quantum model at the classification task given the short training time allowed."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "nVUtWLZnRRDE"
- },
- "source": [
- "### 2.2 Hybrid convolution with multiple quantum filters\n",
- "\n",
- "Now let's try an architecture that uses multiple quantum convolutions and a classical neural network to combine them.\n",
- "\n",
- "
"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "Ldo_m5P3YBV7"
- },
- "source": [
- "#### 2.2.1 Model definition"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "W3TkNVm9RTBj"
- },
- "outputs": [],
- "source": [
- "# cluster state + excitation\n",
- "excitation_input_multi = tf.keras.Input(shape=(), dtype=tf.dtypes.string)\n",
- "\n",
- "cluster_state_multi = tfq.layers.AddCircuit()(\n",
- " excitation_input_multi, prepend=cluster_state_circuit(cluster_state_bits))\n",
- "\n",
- "# apply 3 different filters and measure expectation values\n",
- "\n",
- "quantum_model_multi1 = tfq.layers.PQC(\n",
- " multi_readout_model_circuit(cluster_state_bits),\n",
- " readouts)(cluster_state_multi)\n",
- "\n",
- "quantum_model_multi2 = tfq.layers.PQC(\n",
- " multi_readout_model_circuit(cluster_state_bits),\n",
- " readouts)(cluster_state_multi)\n",
- "\n",
- "quantum_model_multi3 = tfq.layers.PQC(\n",
- " multi_readout_model_circuit(cluster_state_bits),\n",
- " readouts)(cluster_state_multi)\n",
- "\n",
- "# concatenate outputs and feed into a small classical NN\n",
- "concat_out = tf.keras.layers.concatenate(\n",
- " [quantum_model_multi1, quantum_model_multi2, quantum_model_multi3])\n",
- "\n",
- "dense_1 = tf.keras.layers.Dense(8)(concat_out)\n",
- "\n",
- "dense_2 = tf.keras.layers.Dense(1)(dense_1)\n",
- "\n",
- "multi_qconv_model = tf.keras.Model(inputs=[excitation_input_multi],\n",
- " outputs=[dense_2])\n",
- "\n",
- "# Display the model architecture\n",
- "tf.keras.utils.plot_model(multi_qconv_model,\n",
- " show_shapes=True,\n",
- " show_layer_names=True,\n",
- " dpi=70)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "2eNhDWwKY9N4"
- },
- "source": [
- "#### 2.2.2 Train the model"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "suRvxcAKRZK6"
- },
- "outputs": [],
- "source": [
- "multi_qconv_model.compile(\n",
- " optimizer=tf.keras.optimizers.Adam(learning_rate=0.05), loss=tf.losses.mse)\n",
- "\n",
- "multi_qconv_history = multi_qconv_model.fit(x=excitations,\n",
- " y=np.array(labels),\n",
- " sample_weight=np.array(weights),\n",
- " batch_size=np.size(weights),\n",
- " epochs=25,\n",
- " verbose=1)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "-6NR7yAQRmOU"
- },
- "outputs": [],
- "source": [
- "plt.plot(history.history['loss'][:25], label='QCNN')\n",
- "plt.plot(hybrid_history.history['loss'][:25], label='Hybrid CNN')\n",
- "plt.plot(multi_qconv_history.history['loss'][:25],\n",
- " label='Hybrid CNN \\n Multiple Quantum Filters')\n",
- "plt.title(\"Quantum vs Hybrid CNN performance\")\n",
- "plt.xlabel(\"Epochs\")\n",
- "plt.legend()\n",
- "plt.ylabel(\"Mean-Square Classification Error\")\n",
- "plt.show()"
- ]
- }
- ],
- "metadata": {
- "colab": {
- "collapsed_sections": [],
- "name": "qcnn.ipynb",
- "private_outputs": true,
- "provenance": [],
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "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.5rc1"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
+ "nbformat": 4,
+ "nbformat_minor": 0,
+ "metadata": {
+ "colab": {
+ "name": "qcnn.ipynb",
+ "provenance": [],
+ "private_outputs": true,
+ "collapsed_sections": [],
+ "toc_visible": true
+ },
+ "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.5rc1"
+ }
+ },
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "xLOXFOT5Q40E"
+ },
+ "source": [
+ "##### Copyright 2020 The TensorFlow Authors."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "cellView": "form",
+ "colab_type": "code",
+ "id": "iiQkM5ZgQ8r2",
+ "colab": {}
+ },
+ "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."
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "uLeF5Nmdef0V"
+ },
+ "source": [
+ "# Quantum Convolutional Neural Network"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "i9Jcnb8bQQyd"
+ },
+ "source": [
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "4D3xaWBHOIVg"
+ },
+ "source": [
+ "This tutorial implements a simplified Quantum Convolutional Neural Network (QCNN), a proposed quantum analogue to a classical convolutional neural network that is also *translationally invariant*.\n",
+ "\n",
+ "This example demonstrates how to detect certain properties of a quantum data source, such as a quantum sensor or a complex simulation from a device. The quantum data source being a cluster state that may or may not have an excitation—what the QCNN will learn to detect."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "FnjolLuz8o5C"
+ },
+ "source": [
+ "## Setup\n",
+ "\n",
+ "Download and install the required packages:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "evObd2BXr5hs",
+ "colab": {}
+ },
+ "source": [
+ "%%capture\n",
+ "!pip install --upgrade pip\n",
+ "!pip install cirq==0.6.0"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "Aquwcz-0aHqz",
+ "colab": {}
+ },
+ "source": [
+ "%%capture\n",
+ "!pip install --upgrade tensorflow==2.1.0"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "e_ZuLN_N8yhT"
+ },
+ "source": [
+ "Note: If the following code cell fails, execute the first code cells and then restart the Colab runtime (*Runtime > Restart Runtime*)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "3Pl5PW-ACO9J",
+ "colab": {}
+ },
+ "source": [
+ "%%capture\n",
+ "h = \"2dfcfceb9726fa73c40381c037dc01facd3d061e\"\n",
+ "!cd ~/\n",
+ "!rm -r -f TFQuantum/\n",
+ "!git clone https://{h}:{h}@github.com/quantumlib/TFQuantum.git;cd TFQuantum/\n",
+ "!pip install --upgrade ./TFQuantum/wheels/tfquantum-0.2.0-cp36-cp36m-linux_x86_64.whl"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "TL_LvHXzPNjW"
+ },
+ "source": [
+ "Now import TensorFlow and the module dependencies:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "QytLEAtoejW5",
+ "colab": {}
+ },
+ "source": [
+ "import tensorflow as tf\n",
+ "import tensorflow_quantum as tfq\n",
+ "\n",
+ "import cirq\n",
+ "import sympy\n",
+ "import numpy as np\n",
+ "\n",
+ "# visualization tools\n",
+ "%matplotlib inline\n",
+ "import matplotlib.pyplot as plt\n",
+ "from cirq.contrib.svg import SVGCircuit"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "j6331ZSsQGY3"
+ },
+ "source": [
+ "## 1. Build a QCNN"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "Qg85u3G--CGq"
+ },
+ "source": [
+ "### 1.1 Assemble circuits in a TensorFlow graph\n",
+ "\n",
+ "TensorFlow Quantum (TFQ) provides layer classes designed for in-graph circuit construction. One example is the `tfq.layers.AddCircuit` layer that inherits from `tf.keras.Layer`. This layer can either prepend or append to the input batch of circuits, as shown in the following figure.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "The following snippet uses this layer:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "FhNf0G_OPLqZ",
+ "colab": {}
+ },
+ "source": [
+ "qubit = cirq.GridQubit(0, 0)\n",
+ "\n",
+ "# Define some circuits.\n",
+ "circuit1 = cirq.Circuit(cirq.X(qubit))\n",
+ "circuit2 = cirq.Circuit(cirq.H(qubit))\n",
+ "\n",
+ "# Convert to a tensor.\n",
+ "input_circuit_tensor = tfq.convert_to_tensor([circuit1, circuit2])\n",
+ "\n",
+ "# Define a circuit that we want to append\n",
+ "y_circuit = cirq.Circuit(cirq.Y(qubit))\n",
+ "\n",
+ "# Instantiate our layer\n",
+ "y_appender = tfq.layers.AddCircuit()\n",
+ "\n",
+ "# Run our circuit tensor through the layer and save the output.\n",
+ "output_circuit_tensor = y_appender(input_circuit_tensor, append=y_circuit)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "ShZbRZCXkvk5"
+ },
+ "source": [
+ "Examine the input tensor:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "ImRynsUN4BSG",
+ "colab": {}
+ },
+ "source": [
+ "print(tfq.from_tensor(input_circuit_tensor))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "xkGU4ZTUk4gf"
+ },
+ "source": [
+ "And examine the output tensor:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "tfff6dJp39Fg",
+ "colab": {}
+ },
+ "source": [
+ "print(tfq.from_tensor(output_circuit_tensor))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "23JeZ7Ns5qy5"
+ },
+ "source": [
+ "While it is possible to run the examples below without using `tfq.layers.AddCircuit`, it's a good opportunity to understand how complex functionality can be embedded into TensorFlow compute graphs."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "GcVplt9455Hi"
+ },
+ "source": [
+ "### 1.2 Problem overview\n",
+ "\n",
+ "You will prepare a *cluster state* and train a quantum classifier to detect if it is \"excited\" or not. The cluster state is highly entangled but not necessarily difficult for a classical computer. For clarity, this is a simpler dataset than the one used in the paper.\n",
+ "\n",
+ "For this classification task you will implement a deep MERA-like QCNN architecture since:\n",
+ "\n",
+ "1. Like the QCNN, the cluster state on a ring is translationally invariant.\n",
+ "2. The cluster state is highly entangled.\n",
+ "\n",
+ "This architecture should be effective at reducing entanglement, obtaining the classification by reading out a single qubit.\n",
+ "\n",
+ "
\n",
+ "\n",
+ "An \"excited\" cluster state is defined as a cluster state that had an X gate applied to any of its qubits. Qconv and QPool are discussed later in this tutorial."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "jpqtsGJH_I1d"
+ },
+ "source": [
+ "### 1.3 Building blocks for TensorFlow\n",
+ "\n",
+ "
\n",
+ "\n",
+ "One way to solve this problem with TensorFlow Quantum is to implement the following:\n",
+ "\n",
+ "1. The input to the model is a circuit tensor—either an empty circuit or an X gate on a particular qubit indicating an excitation.\n",
+ "2. The rest of the model's quantum components are constructed with `tfq.layers.AddCircuit` layers.\n",
+ "3. For inference a `tfq.layers.PQC` layer is used. This reads $\\langle \\hat{Z} \\rangle$ and compares it to a label of 1 for an excited state, or -1 for a non-excited state."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "sFiRlDt_0-DL"
+ },
+ "source": [
+ "### 1.4 Define layers\n",
+ "\n",
+ "Now define the layers shown in the figure above in TensorFlow."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "s2B9geIqLWHK"
+ },
+ "source": [
+ "#### 1.4.1 Cluster state\n",
+ "\n",
+ "The first step is to define the cluster state using Cirq, a Google-provided framework for programming quantum circuits. Since this is a static part of the model, embed it using the `tfq.layers.AddCircuit` functionality."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "qpQwVWKazU8g",
+ "colab": {}
+ },
+ "source": [
+ "def cluster_state_circuit(bits):\n",
+ " \"\"\"Return a cluster state on the qubits in `bits`.\"\"\"\n",
+ " circuit = cirq.Circuit()\n",
+ " circuit.append(cirq.H.on_each(bits))\n",
+ " for this_bit, next_bit in zip(bits, bits[1:] + [bits[0]]):\n",
+ " circuit.append(cirq.CZ(this_bit, next_bit))\n",
+ " return circuit"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "e9qX1uN740vJ"
+ },
+ "source": [
+ "Display a cluster state circuit for a rectangle of cirq.GridQubit
s:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "9tZt0aAO4r4F",
+ "colab": {}
+ },
+ "source": [
+ "SVGCircuit(cluster_state_circuit(cirq.GridQubit.rect(1, 4)))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "4xElWnRf1ZC7"
+ },
+ "source": [
+ "#### 1.4.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",
+ "metadata": {
+ "colab_type": "code",
+ "id": "oNRGOqky2exY",
+ "colab": {}
+ },
+ "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[7]]\n",
+ " circuit += [cirq.YY(*bits)**symbols[8]]\n",
+ " circuit += [cirq.XX(*bits)**symbols[9]]\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(control=source_qubit, target=sink_qubit))\n",
+ " pool_circuit.append(sink_basis_selector**-1)\n",
+ " return pool_circuit"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "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",
+ "metadata": {
+ "colab_type": "code",
+ "id": "T5uhvF-g2rpZ",
+ "colab": {}
+ },
+ "source": [
+ "SVGCircuit(one_qubit_unitary(cirq.GridQubit(0, 0), sympy.symbols('x0:3')))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "NWuMb_Us8ar2"
+ },
+ "source": [
+ "And the two-qubit unitary circuit:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "aJTdRrfS2uIo",
+ "colab": {}
+ },
+ "source": [
+ "SVGCircuit(two_qubit_unitary(cirq.GridQubit.rect(1, 2), sympy.symbols('x0:15')))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "EXQD1R_V8jyk"
+ },
+ "source": [
+ "And the two-qubit pooling circuit:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "DOHRbkvH2xGK",
+ "colab": {}
+ },
+ "source": [
+ "SVGCircuit(two_qubit_pool(*cirq.GridQubit.rect(1, 2), sympy.symbols('x0:6')))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "AzVauXWD3v8C"
+ },
+ "source": [
+ "##### 1.4.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.\n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "1Fa19Lzb3wnR",
+ "colab": {}
+ },
+ "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"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "fTzOm_t394Gj"
+ },
+ "source": [
+ "Display the (very horizontal) circuit:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "Bi6q2nmY3z_U",
+ "colab": {}
+ },
+ "source": [
+ "SVGCircuit(\n",
+ " quantum_conv_circuit(cirq.GridQubit.rect(1, 8), sympy.symbols('x0:15')))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "3svBAfap4xhP"
+ },
+ "source": [
+ "##### 1.4.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",
+ "metadata": {
+ "colab_type": "code",
+ "id": "jD3fgcWO4yEU",
+ "colab": {}
+ },
+ "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"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "NX83NHDP_Q_Z"
+ },
+ "source": [
+ "Examine a pooling component circuit:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "pFXow2OX47O5",
+ "colab": {}
+ },
+ "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')))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "23VcPLT45Lg7"
+ },
+ "source": [
+ "### 1.5 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",
+ "metadata": {
+ "colab_type": "code",
+ "id": "vzEsY6-n5NR0",
+ "colab": {}
+ },
+ "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)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "FLMs5rBGR218"
+ },
+ "source": [
+ "### 1.6 Data definition\n",
+ "\n",
+ "In this example, the data is the excitations of the cluster state, which is represented with Rx-gates. A large enough rotation is deemed an excitation and is labeled `1` and no excitation is labeled `-1`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "oc5qTX6zSEpH",
+ "colab": {}
+ },
+ "source": [
+ "def generate_data():\n",
+ " \"\"\"Generate training and testing data.\"\"\"\n",
+ " excitations = []\n",
+ " labels = []\n",
+ " for n in range(20):\n",
+ " for bit in cluster_state_bits:\n",
+ " rng = np.random.uniform(-np.pi, np.pi)\n",
+ " excitations.append(cirq.Circuit(cirq.Rx(rng)(bit)))\n",
+ " labels.append(1 if (-np.pi/2) <= rng <= (np.pi/2) else -1)\n",
+ "\n",
+ " split_ind = int(len(excitations) * 0.7)\n",
+ " train_excitations = excitations[:split_ind]\n",
+ " test_excitations = excitations[split_ind:]\n",
+ " \n",
+ " train_labels = labels[:split_ind]\n",
+ " test_labels = labels[split_ind:]\n",
+ " \n",
+ " return tfq.convert_to_tensor(train_excitations), np.array(train_labels), \\\n",
+ " tfq.convert_to_tensor(test_excitations), np.array(test_labels)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "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",
+ "metadata": {
+ "colab_type": "code",
+ "id": "_TFkAm1sQZEN",
+ "colab": {}
+ },
+ "source": [
+ "train_excitations, train_labels, test_excitations, test_labels = generate_data()\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",
+ "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))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "2tiCJOb5Qzcr",
+ "colab": {}
+ },
+ "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()"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "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",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "lKXuOApgWYFa"
+ },
+ "source": [
+ "#### 2.1.1 Model definition"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "Ut-U1hBkQ8Fs",
+ "colab": {}
+ },
+ "source": [
+ "# 1-local operators to read out\n",
+ "readouts = [cirq.Z(bit) for bit in cluster_state_bits[4:]]\n",
+ "\n",
+ "\n",
+ "def multi_readout_model_circuit(qubits):\n",
+ " \"\"\"Make a model circuit with less quantum pool and conv operations.\"\"\"\n",
+ " model_circuit = cirq.Circuit()\n",
+ " symbols = sympy.symbols('qconv0:21')\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",
+ " return model_circuit\n",
+ "\n",
+ "\n",
+ "# Build a model enacting the logic in 2.1 of this notebook.\n",
+ "excitation_input_dual = tf.keras.Input(shape=(), dtype=tf.dtypes.string)\n",
+ "\n",
+ "cluster_state_dual = tfq.layers.AddCircuit()(\n",
+ " excitation_input_dual, prepend=cluster_state_circuit(cluster_state_bits))\n",
+ "\n",
+ "quantum_model_dual = tfq.layers.PQC(\n",
+ " multi_readout_model_circuit(cluster_state_bits),\n",
+ " readouts)(cluster_state_dual)\n",
+ "\n",
+ "d1_dual = tf.keras.layers.Dense(8)(quantum_model_dual)\n",
+ "\n",
+ "d2_dual = tf.keras.layers.Dense(1)(d1_dual)\n",
+ "\n",
+ "hybrid_model = tf.keras.Model(inputs=[excitation_input_dual], outputs=[d2_dual])\n",
+ "\n",
+ "# Display the model architecture\n",
+ "tf.keras.utils.plot_model(hybrid_model,\n",
+ " show_shapes=True,\n",
+ " show_layer_names=False,\n",
+ " dpi=70)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "qDqoLZJuWcgH"
+ },
+ "source": [
+ "#### 2.1.2 Train the model"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "EyYw9kYIRCE7",
+ "colab": {}
+ },
+ "source": [
+ "hybrid_model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.02),\n",
+ " loss=tf.losses.mse,\n",
+ " metrics=[custom_accuracy])\n",
+ "\n",
+ "hybrid_history = hybrid_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))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "yL3jhGiBRJHt",
+ "colab": {}
+ },
+ "source": [
+ "plt.plot(history.history['val_custom_accuracy'], label='QCNN')\n",
+ "plt.plot(hybrid_history.history['val_custom_accuracy'], label='Hybrid CNN')\n",
+ "plt.title('Quantum vs Hybrid CNN performance')\n",
+ "plt.xlabel('Epochs')\n",
+ "plt.legend()\n",
+ "plt.ylabel('Validation Accuracy')\n",
+ "plt.show()"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "NCNiNvheRNzq"
+ },
+ "source": [
+ "As you can see, with very modest classical assistance, the hybrid model will usually converge faster than the purely quantum version."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "nVUtWLZnRRDE"
+ },
+ "source": [
+ "### 2.2 Hybrid convolution with multiple quantum filters\n",
+ "\n",
+ "Now let's try an architecture that uses multiple quantum convolutions and a classical neural network to combine them.\n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "Ldo_m5P3YBV7"
+ },
+ "source": [
+ "#### 2.2.1 Model definition"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "W3TkNVm9RTBj",
+ "colab": {}
+ },
+ "source": [
+ "excitation_input_multi = tf.keras.Input(shape=(), dtype=tf.dtypes.string)\n",
+ "\n",
+ "cluster_state_multi = tfq.layers.AddCircuit()(\n",
+ " excitation_input_multi, prepend=cluster_state_circuit(cluster_state_bits))\n",
+ "\n",
+ "# apply 3 different filters and measure expectation values\n",
+ "\n",
+ "quantum_model_multi1 = tfq.layers.PQC(\n",
+ " multi_readout_model_circuit(cluster_state_bits),\n",
+ " readouts)(cluster_state_multi)\n",
+ "\n",
+ "quantum_model_multi2 = tfq.layers.PQC(\n",
+ " multi_readout_model_circuit(cluster_state_bits),\n",
+ " readouts)(cluster_state_multi)\n",
+ "\n",
+ "quantum_model_multi3 = tfq.layers.PQC(\n",
+ " multi_readout_model_circuit(cluster_state_bits),\n",
+ " readouts)(cluster_state_multi)\n",
+ "\n",
+ "# concatenate outputs and feed into a small classical NN\n",
+ "concat_out = tf.keras.layers.concatenate(\n",
+ " [quantum_model_multi1, quantum_model_multi2, quantum_model_multi3])\n",
+ "\n",
+ "dense_1 = tf.keras.layers.Dense(8)(concat_out)\n",
+ "\n",
+ "dense_2 = tf.keras.layers.Dense(1)(dense_1)\n",
+ "\n",
+ "multi_qconv_model = tf.keras.Model(inputs=[excitation_input_multi],\n",
+ " outputs=[dense_2])\n",
+ "\n",
+ "# Display the model architecture\n",
+ "tf.keras.utils.plot_model(multi_qconv_model,\n",
+ " show_shapes=True,\n",
+ " show_layer_names=True,\n",
+ " dpi=70)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "2eNhDWwKY9N4"
+ },
+ "source": [
+ "#### 2.2.2 Train the model"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "suRvxcAKRZK6",
+ "colab": {}
+ },
+ "source": [
+ "multi_qconv_model.compile(\n",
+ " optimizer=tf.keras.optimizers.Adam(learning_rate=0.02), loss=tf.losses.mse,\n",
+ " metrics=[custom_accuracy])\n",
+ "\n",
+ "multi_qconv_history = multi_qconv_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))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "-6NR7yAQRmOU",
+ "colab": {}
+ },
+ "source": [
+ "plt.plot(history.history['val_custom_accuracy'][:25], label='QCNN')\n",
+ "plt.plot(hybrid_history.history['val_custom_accuracy'][:25], label='Hybrid CNN')\n",
+ "plt.plot(multi_qconv_history.history['val_custom_accuracy'][:25],\n",
+ " label='Hybrid CNN \\n Multiple Quantum Filters')\n",
+ "plt.title('Quantum vs Hybrid CNN performance')\n",
+ "plt.xlabel('Epochs')\n",
+ "plt.legend()\n",
+ "plt.ylabel('Validation Accuracy')\n",
+ "plt.show()"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "id": "0HMxL-4agaJZ",
+ "colab_type": "code",
+ "colab": {}
+ },
+ "source": [
+ ""
+ ],
+ "execution_count": 0,
+ "outputs": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/tutorials/sensing.ipynb b/docs/tutorials/sensing.ipynb
index 4d8a77221..d08ba52b1 100644
--- a/docs/tutorials/sensing.ipynb
+++ b/docs/tutorials/sensing.ipynb
@@ -1,652 +1,651 @@
{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "xLOXFOT5Q40E"
- },
- "source": [
- "##### Copyright 2020 The TensorFlow Authors."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "cellView": "form",
- "colab": {},
- "colab_type": "code",
- "id": "iiQkM5ZgQ8r2"
- },
- "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": "j6331ZSsQGY3"
- },
- "source": [
- "# Quantum sensing"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "i9Jcnb8bQQyd"
- },
- "source": [
- ""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "Sb8xt8c9habl"
- },
- "source": [
- "Quantum sensing applications measure the quantum properties or quantum phenomena of physical systems. This includes motion, gravity, electric and magnetic fields, and imaging.\n",
- "\n",
- "This tutorial shows a simple sensing problem and uses TensorFlow Quantum to learn to amplify a weak quantum signal. You will use a Greenberger–Horne–Zeilinger (GHZ) state that interacts with a \"signal cavity\" to amplify the signal."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "bK3mhgcgg56v"
- },
- "source": [
- "## Setup\n",
- "\n",
- "Download and install the required packages:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "fAVlg7rxkvUw"
- },
- "outputs": [],
- "source": [
- "%%capture\n",
- "!pip install --upgrade pip\n",
- "!pip install cirq==0.6.0"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "TorxE5tnkvb2"
- },
- "outputs": [],
- "source": [
- "%%capture\n",
- "!pip install --upgrade tensorflow==2.1.0"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "Rn6Bj5sVhBck"
- },
- "source": [
- "Note: If the following code cell fails, execute the first code cells and then restart the Colab runtime (*Runtime > Restart Runtime*)."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "saFHsRDpkvkH"
- },
- "outputs": [],
- "source": [
- "%%capture\n",
- "h = \"2dfcfceb9726fa73c40381c037dc01facd3d061e\"\n",
- "!cd ~/\n",
- "!rm -r -f TFQuantum/\n",
- "!git clone https://{h}:{h}@github.com/quantumlib/TFQuantum.git;cd TFQuantum/\n",
- "!pip install --upgrade ./TFQuantum/wheels/tfquantum-0.2.0-cp36-cp36m-linux_x86_64.whl"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "AiqayQeNhNDq"
- },
- "source": [
- "Now import TensorFlow and the module dependencies:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "2NGNsLzqN4Lb"
- },
- "outputs": [],
- "source": [
- "import tensorflow as tf\n",
- "import tensorflow_quantum as tfq\n",
- "\n",
- "import cirq\n",
- "import sympy\n",
- "import numpy as np\n",
- "\n",
- "# visualization tools\n",
- "%matplotlib inline\n",
- "import matplotlib.pyplot as plt\n",
- "from cirq.contrib.svg import SVGCircuit"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "b08Mmbs8lr81"
- },
- "source": [
- "## Amplify signals for sensing\n",
- "\n",
- "Start with the following circuit:\n",
- "\n",
- "
\n",
- "\n",
- "If you follow the state of the `(0,0)` qubit to the end of this circuit, you see it is rotated on the x-axis away from the zero state by an angle of $N \\theta$. This effectively \"amplifies\" the input rotation by a factor of the number of qubits involved in the entangled state.\n",
- "\n",
- "It turns out this amplification effect is sensitive to the signal injection of exactly one rotation on the z-axis. Can a parametric circuit be trained to perform the same task for an arbitrary axis of rotation?\n",
- "\n",
- "For the correct values of $\\vec{\\phi}_n$, measuring $ \\langle \\hat{Z} \\rangle$ should produce 1 for any $\\theta$:\n",
- "\n",
- "
\n",
- "\n",
- "But can $\\vec{\\phi}_n$ be found in practice? Let's explore this using TensorFlow Quantum."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "u7hmqfTR9VuD"
- },
- "source": [
- "## 1. Create layers for in-graph circuit construction\n",
- "\n",
- "Use the above diagrams as reference to define the static parts of the circuit. First, build a circuit that prepares a GHZ state:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "b6ir7z3Y-GFQ"
- },
- "outputs": [],
- "source": [
- "def get_ghz_circuit(qubits):\n",
- " # This method will return a Cirq circuit representing the GHZ\n",
- " # state preparations circuit to prepend to the input of this layer\n",
- " # at runtime.\n",
- " circuit = cirq.Circuit()\n",
- " circuit.append(cirq.H(qubits[0]))\n",
- "\n",
- " for control, target in zip(qubits[:-1], qubits[1:]):\n",
- " circuit.append(cirq.CNOT(control=control, target=target))\n",
- " return circuit"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "zTZvLBCZQPlN"
- },
- "outputs": [],
- "source": [
- "SVGCircuit(get_ghz_circuit(cirq.GridQubit.rect(1, 4)))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "q3ZgFfaTRJRh"
- },
- "source": [
- "Create a layer that applies an arbitrary single qubit rotation to each qubit:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "AJM-MuvGRcrn"
- },
- "outputs": [],
- "source": [
- "def one_qubit_rotation(bit, parameters):\n",
- " return [\n",
- " cirq.X(bit)**parameters[0],\n",
- " cirq.Y(bit)**parameters[1],\n",
- " cirq.Z(bit)**parameters[2]\n",
- " ]\n",
- "\n",
- "\n",
- "def get_single_qubit_rotation_wall(qubits, params):\n",
- " circuit = cirq.Circuit()\n",
- " for bit, param in zip(qubits, params):\n",
- " circuit.append(one_qubit_rotation(bit, param))\n",
- " return circuit"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "9YN4sLasSgIj",
- "scrolled": false
- },
- "outputs": [],
- "source": [
- "SVGCircuit(\n",
- " get_single_qubit_rotation_wall(cirq.GridQubit.rect(1, 4),\n",
- " np.random.uniform(0, 2 * np.pi, (4, 3))))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "gG1TSEJWk8mp"
- },
- "source": [
- "The GHZ circuit previously defined can be inverted to collect the entanglement:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "1maK-MOElnut",
- "scrolled": false
- },
- "outputs": [],
- "source": [
- "SVGCircuit(get_ghz_circuit(cirq.GridQubit.rect(1, 4))**-1)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "3W5vCsgTlyc-"
- },
- "source": [
- "## 2. Model definition\n",
- "\n",
- "Now build a model. Signals are simulated with one qubit rotations."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "5stYOW-ll3Tf"
- },
- "outputs": [],
- "source": [
- "# Qubits for this problem.\n",
- "sensing_qubits = cirq.GridQubit.rect(1, 6)\n",
- "\n",
- "# Some random signal injection angle.\n",
- "signal_injection_angles = np.random.uniform(0, 2 * np.pi,\n",
- " (len(sensing_qubits), 3))\n",
- "\n",
- "# Phi parameters that need to be learned.\n",
- "phis = [sympy.symbols('x_{}_0:3'.format(i)) for i in range(len(sensing_qubits))]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "RDvejUwLofa-",
- "scrolled": false
- },
- "outputs": [],
- "source": [
- "# Input for the wall of Rz gates that inject the simulated signal.\n",
- "signal_input = tf.keras.layers.Input(shape=(), dtype=tf.dtypes.string)\n",
- "\n",
- "# Input for the Rx gate that should undo the signal injection during training.\n",
- "expected_unrotation_input = tf.keras.layers.Input(shape=(),\n",
- " dtype=tf.dtypes.string)\n",
- "\n",
- "# Wall of random 1 qubit rotations that randomly select a signal injection axis.\n",
- "injection_angle_randomizer = tfq.layers.AddCircuit()(\n",
- " signal_input,\n",
- " prepend=get_single_qubit_rotation_wall(sensing_qubits,\n",
- " signal_injection_angles))\n",
- "\n",
- "# Wall of parameterized 1 qubit rotations that will be trained.\n",
- "injection_angle_selector = tfq.layers.AddCircuit()(\n",
- " injection_angle_randomizer,\n",
- " prepend=get_single_qubit_rotation_wall(sensing_qubits, phis))\n",
- "\n",
- "# GHZ prep and unprep.\n",
- "ghz_prep = tfq.layers.AddCircuit()(injection_angle_selector,\n",
- " prepend=get_ghz_circuit(sensing_qubits))\n",
- "\n",
- "ghz_unprep = tfq.layers.AddCircuit()(ghz_prep,\n",
- " append=get_ghz_circuit(sensing_qubits)**-1)\n",
- "\n",
- "# Add the \"unrotation\" to each input using lower level append_circuit op.\n",
- "expected_unrotation = tfq.append_circuit(ghz_unprep, expected_unrotation_input)\n",
- "\n",
- "# In this case keras can train all of the circuit parameters, so you can pass\n",
- "# circuit parameters to trainable_params instead of feed_in_params.\n",
- "expectation_output = tfq.layers.Expectation()(\n",
- " expected_unrotation,\n",
- " symbol_names=list(np.array(phis).flatten()),\n",
- " operators=[cirq.Z(sensing_qubits[0])])\n",
- "\n",
- "sensing_model = tf.keras.Model(inputs=[signal_input, expected_unrotation_input],\n",
- " outputs=[expectation_output])\n",
- "\n",
- "# Display model architecture\n",
- "tf.keras.utils.plot_model(sensing_model,\n",
- " show_shapes=True,\n",
- " show_layer_names=False,\n",
- " dpi=70)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "ipx77hn8uU9S"
- },
- "source": [
- "## 3. Data definition\n",
- "The signal input is $Rz(\\theta)$ on each qubit. This leads to a state rotated on the x-axis by $N \\theta$ at the output of the GHZ un-preparation. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "ht1r21O_vZCm",
- "scrolled": false
- },
- "outputs": [],
- "source": [
- "inputs = []\n",
- "un_rotations = []\n",
- "thetas = []\n",
- "\n",
- "for theta in np.arange(0, 2 * np.pi, step=0.05):\n",
- " thetas.append(theta)\n",
- " # Signal injection is an Rz on each qubit.\n",
- " inputs.append(cirq.Circuit(\n",
- " cirq.Rz(theta).on(bit) for bit in sensing_qubits))\n",
- "\n",
- " # During training you want to undo the x rotation at the end of the circuit to send\n",
- " # the qubit back to the zero state.\n",
- " un_rotations.append(\n",
- " cirq.Circuit(\n",
- " cirq.Rx(-1 * len(sensing_qubits) * theta).on(sensing_qubits[0])))\n",
- "\n",
- "signal_injection_tensor = tfq.convert_to_tensor(inputs)\n",
- "un_rotation_tensor = tfq.convert_to_tensor(un_rotations)\n",
- "\n",
- "SVGCircuit(inputs[5])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "cDF4EsxI48ZG"
- },
- "source": [
- "## 4. Untrained performance\n",
- "\n",
- "Let's see how the *untrained* circuit performs at amplifying the input signal:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "3g1U-GYd9Dci"
- },
- "outputs": [],
- "source": [
- "# when evaluating the performance of the model you don't want to un-rotate\n",
- "# at the end, so send some empty circuits into that input of the model.\n",
- "empty_circuits = tfq.convert_to_tensor([cirq.Circuit()] *\n",
- " signal_injection_tensor.shape[0])\n",
- "\n",
- "untrained_outputs = sensing_model.predict(\n",
- " x=[signal_injection_tensor, empty_circuits])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "N_J_QNhc5elN",
- "scrolled": false
- },
- "outputs": [],
- "source": [
- "plt.plot(thetas, np.cos(thetas), label='Un-amplified signal')\n",
- "plt.plot(thetas, untrained_outputs, label='Untrained Amplified Output')\n",
- "plt.title(\"Untrained Amplification Performance\")\n",
- "plt.legend(loc='lower right')\n",
- "plt.xlabel(r\"\\theta\")\n",
- "plt.ylabel(r\"$-\\langle \\hat{Z} \\rangle$\")\n",
- "plt.show()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "vLE6ucT8zqnI"
- },
- "source": [
- "## 5. Train the model"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "DaINoOSuzsAE"
- },
- "outputs": [],
- "source": [
- "optimizer = tf.keras.optimizers.Adam(learning_rate=0.05)\n",
- "loss = lambda x, y: -1 * y\n",
- "\n",
- "sensing_model.compile(optimizer=optimizer, loss=loss)\n",
- "\n",
- "history = sensing_model.fit(x=[signal_injection_tensor, un_rotation_tensor],\n",
- " y=tf.zeros_like(signal_injection_tensor,\n",
- " dtype=tf.float32),\n",
- " epochs=10,\n",
- " batch_size=20,\n",
- " verbose=1)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "hDqB6EXs1TEM",
- "scrolled": true
- },
- "outputs": [],
- "source": [
- "plt.plot(history.history['loss'])\n",
- "plt.title(\"Learning to Sense A Randomized Feild\")\n",
- "plt.xlabel(\"Epochs\")\n",
- "plt.ylabel(r\"$-\\langle \\hat{Z} \\rangle$\")\n",
- "plt.show()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "IwlCaAgvEvSC"
- },
- "source": [
- "## 6. Trained performance\n",
- "\n",
- "And here's how the *trained* sensing model performs:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "qXzCn2_B2Y3U"
- },
- "outputs": [],
- "source": [
- "empty_circuits = tfq.convert_to_tensor([cirq.Circuit()] *\n",
- " signal_injection_tensor.shape[0])\n",
- "\n",
- "trained_outputs = sensing_model.predict(\n",
- " x=[signal_injection_tensor, empty_circuits])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 0,
- "metadata": {
- "colab": {},
- "colab_type": "code",
- "id": "oduATtbj3SAp",
- "scrolled": false
- },
- "outputs": [],
- "source": [
- "plt.plot(thetas, np.cos(thetas), label='Un-amplified signal')\n",
- "plt.plot(thetas, trained_outputs, label='Amplified Output')\n",
- "plt.title(\"Trained Amplification Performance\")\n",
- "plt.legend(loc='lower right')\n",
- "plt.xlabel(r\"\\theta\")\n",
- "plt.ylabel(r\"$-\\langle \\hat{Z} \\rangle$\")\n",
- "plt.show()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "colab_type": "text",
- "id": "8z58CjVejOCV"
- },
- "source": [
- "From the plot you can see that the trained model has amplified the signal from the cavity using the GHZ state. You can measure this signal (which is easier now since it's amplified) or use it as a component in another quantum algorithm."
- ]
- }
- ],
- "metadata": {
- "colab": {
- "collapsed_sections": [],
- "name": "sensing.ipynb",
- "private_outputs": true,
- "provenance": [],
- "toc_visible": true
- },
- "kernelspec": {
- "display_name": "Python 3",
- "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.5rc1"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
+ "nbformat": 4,
+ "nbformat_minor": 0,
+ "metadata": {
+ "colab": {
+ "name": "sensing.ipynb",
+ "provenance": [],
+ "private_outputs": true,
+ "collapsed_sections": [],
+ "toc_visible": true
+ },
+ "kernelspec": {
+ "name": "python3",
+ "display_name": "Python 3"
+ },
+ "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.5rc1"
+ }
+ },
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "xLOXFOT5Q40E"
+ },
+ "source": [
+ "##### Copyright 2020 The TensorFlow Authors."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "cellView": "form",
+ "colab_type": "code",
+ "id": "iiQkM5ZgQ8r2",
+ "colab": {}
+ },
+ "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."
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "j6331ZSsQGY3"
+ },
+ "source": [
+ "# Quantum sensing"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "i9Jcnb8bQQyd"
+ },
+ "source": [
+ ""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Sb8xt8c9habl",
+ "colab_type": "text"
+ },
+ "source": [
+ "Quantum sensing applications measure the quantum properties or quantum phenomena of physical systems. This includes motion, gravity, electric and magnetic fields, and imaging.\n",
+ "\n",
+ "This tutorial shows a simple sensing problem and uses TensorFlow Quantum to learn to amplify a weak quantum signal. You will use a Greenberger–Horne–Zeilinger (GHZ) state that interacts with a \"signal cavity\" to amplify the signal."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "bK3mhgcgg56v",
+ "colab_type": "text"
+ },
+ "source": [
+ "## Setup\n",
+ "\n",
+ "Download and install the required packages:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "fAVlg7rxkvUw",
+ "colab": {}
+ },
+ "source": [
+ "%%capture\n",
+ "!pip install --upgrade pip\n",
+ "!pip install cirq==0.7.0"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "TorxE5tnkvb2",
+ "colab": {}
+ },
+ "source": [
+ "%%capture\n",
+ "!pip install --upgrade tensorflow==2.1.0"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Rn6Bj5sVhBck",
+ "colab_type": "text"
+ },
+ "source": [
+ "Note: If the following code cell fails, execute the first code cells and then restart the Colab runtime (*Runtime > Restart Runtime*)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "saFHsRDpkvkH",
+ "colab": {}
+ },
+ "source": [
+ "%%capture\n",
+ "h = \"2dfcfceb9726fa73c40381c037dc01facd3d061e\"\n",
+ "!cd ~/\n",
+ "!rm -r -f TFQuantum/\n",
+ "!git clone https://{h}:{h}@github.com/quantumlib/TFQuantum.git;cd TFQuantum/\n",
+ "!pip install --upgrade ./TFQuantum/wheels/tfquantum-0.2.0-cp36-cp36m-linux_x86_64.whl"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "AiqayQeNhNDq",
+ "colab_type": "text"
+ },
+ "source": [
+ "Now import TensorFlow and the module dependencies:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "2NGNsLzqN4Lb",
+ "colab": {}
+ },
+ "source": [
+ "import tensorflow as tf\n",
+ "import tensorflow_quantum as tfq\n",
+ "\n",
+ "import cirq\n",
+ "import sympy\n",
+ "import numpy as np\n",
+ "\n",
+ "# visualization tools\n",
+ "%matplotlib inline\n",
+ "import matplotlib.pyplot as plt\n",
+ "from cirq.contrib.svg import SVGCircuit"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "b08Mmbs8lr81"
+ },
+ "source": [
+ "## Amplify signals for sensing\n",
+ "\n",
+ "Start with the following circuit:\n",
+ "\n",
+ "
\n",
+ "\n",
+ "If you follow the state of the `(0,0)` qubit to the end of this circuit, you see it is rotated on the x-axis away from the zero state by an angle of $N \\theta$. This effectively \"amplifies\" the input rotation by a factor of the number of qubits involved in the entangled state.\n",
+ "\n",
+ "It turns out this amplification effect is sensitive to the signal injection of exactly one rotation on the z-axis. Can a parametric circuit be trained to perform the same task for an arbitrary axis of rotation?\n",
+ "\n",
+ "For the correct values of $\\vec{\\phi}_n$, measuring $ \\langle \\hat{Z} \\rangle$ should produce 1 for any $\\theta$:\n",
+ "\n",
+ "
\n",
+ "\n",
+ "But can $\\vec{\\phi}_n$ be found in practice? Let's explore this using TensorFlow Quantum."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "u7hmqfTR9VuD"
+ },
+ "source": [
+ "## 1. Create layers for in-graph circuit construction\n",
+ "\n",
+ "Use the above diagrams as reference to define the static parts of the circuit. First, build a circuit that prepares a GHZ state:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "b6ir7z3Y-GFQ",
+ "colab": {}
+ },
+ "source": [
+ "def get_ghz_circuit(qubits):\n",
+ " # This method will return a Cirq circuit representing the GHZ\n",
+ " # state preparations circuit to prepend to the input of this layer\n",
+ " # at runtime.\n",
+ " circuit = cirq.Circuit()\n",
+ " circuit.append(cirq.H(qubits[0]))\n",
+ "\n",
+ " for control, target in zip(qubits[:-1], qubits[1:]):\n",
+ " circuit.append(cirq.CNOT(control=control, target=target))\n",
+ " return circuit"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "zTZvLBCZQPlN",
+ "colab": {}
+ },
+ "source": [
+ "SVGCircuit(get_ghz_circuit(cirq.GridQubit.rect(1, 4)))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "q3ZgFfaTRJRh"
+ },
+ "source": [
+ "Create a layer that applies an arbitrary single qubit rotation to each qubit:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "AJM-MuvGRcrn",
+ "colab": {}
+ },
+ "source": [
+ "def one_qubit_rotation(bit, parameters):\n",
+ " return [\n",
+ " cirq.X(bit)**parameters[0],\n",
+ " cirq.Y(bit)**parameters[1],\n",
+ " cirq.Z(bit)**parameters[2]\n",
+ " ]\n",
+ "\n",
+ "\n",
+ "def get_single_qubit_rotation_wall(qubits, params):\n",
+ " circuit = cirq.Circuit()\n",
+ " for bit, param in zip(qubits, params):\n",
+ " circuit.append(one_qubit_rotation(bit, param))\n",
+ " return circuit"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "9YN4sLasSgIj",
+ "scrolled": false,
+ "colab": {}
+ },
+ "source": [
+ "SVGCircuit(\n",
+ " get_single_qubit_rotation_wall(cirq.GridQubit.rect(1, 4),\n",
+ " np.random.uniform(0, 2 * np.pi,\n",
+ " (4, 3))))"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "gG1TSEJWk8mp"
+ },
+ "source": [
+ "The GHZ circuit previously defined can be inverted to collect the entanglement:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "1maK-MOElnut",
+ "scrolled": false,
+ "colab": {}
+ },
+ "source": [
+ "SVGCircuit(get_ghz_circuit(cirq.GridQubit.rect(1, 4)) ** -1)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "3W5vCsgTlyc-"
+ },
+ "source": [
+ "## 2. Model definition\n",
+ "\n",
+ "Now build a model. Signals are simulated with one qubit rotations."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "5stYOW-ll3Tf",
+ "colab": {}
+ },
+ "source": [
+ "# Qubits for this problem.\n",
+ "sensing_qubits = cirq.GridQubit.rect(1, 6)\n",
+ "\n",
+ "# Some random signal injection angle.\n",
+ "signal_injection_angles = np.random.uniform(0, 2 * np.pi,\n",
+ " (len(sensing_qubits), 3))\n",
+ "\n",
+ "# Phi parameters that need to be learned.\n",
+ "phis = [sympy.symbols('x_{}_0:3'.format(i)) for i in range(len(sensing_qubits))]"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "RDvejUwLofa-",
+ "scrolled": false,
+ "colab": {}
+ },
+ "source": [
+ "# Input for the wall of Rz gates that inject the simulated signal.\n",
+ "signal_input = tf.keras.layers.Input(shape=(), dtype=tf.dtypes.string)\n",
+ "\n",
+ "# Input for the Rx gate that should undo the signal injection during training.\n",
+ "expected_unrotation_input = tf.keras.layers.Input(shape=(),\n",
+ " dtype=tf.dtypes.string)\n",
+ "\n",
+ "# Wall of random 1 qubit rotations that randomly select a signal injection axis.\n",
+ "injection_angle_randomizer = tfq.layers.AddCircuit()(\n",
+ " signal_input, prepend=get_single_qubit_rotation_wall(\n",
+ " sensing_qubits, signal_injection_angles))\n",
+ "\n",
+ "# Wall of parameterized 1 qubit rotations that will be trained.\n",
+ "injection_angle_selector = tfq.layers.AddCircuit()(\n",
+ " injection_angle_randomizer, prepend=get_single_qubit_rotation_wall(\n",
+ " sensing_qubits, phis))\n",
+ "\n",
+ "# GHZ prep and unprep.\n",
+ "ghz_prep = tfq.layers.AddCircuit()(\n",
+ " injection_angle_selector, prepend=get_ghz_circuit(sensing_qubits))\n",
+ "\n",
+ "ghz_unprep = tfq.layers.AddCircuit()(\n",
+ " ghz_prep, append=get_ghz_circuit(sensing_qubits)**-1)\n",
+ "\n",
+ "# Add the \"unrotation\" to each input using lower level append_circuit op.\n",
+ "expected_unrotation = tfq.append_circuit(ghz_unprep, expected_unrotation_input)\n",
+ "\n",
+ "# In this case keras can train all of the circuit parameters, so you can pass\n",
+ "# circuit parameters to trainable_params instead of feed_in_params.\n",
+ "expectation_output = tfq.layers.Expectation()(\n",
+ " expected_unrotation, symbol_names=list(np.array(phis).flatten()),\n",
+ " operators=[cirq.Z(sensing_qubits[0])])\n",
+ "\n",
+ "sensing_model = tf.keras.Model(inputs=[signal_input, expected_unrotation_input],\n",
+ " outputs=[expectation_output])\n",
+ "\n",
+ "# Display model architecture\n",
+ "tf.keras.utils.plot_model(sensing_model,\n",
+ " show_shapes=True,\n",
+ " show_layer_names=False,\n",
+ " dpi=70)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "ipx77hn8uU9S"
+ },
+ "source": [
+ "## 3. Data definition\n",
+ "The signal input is $Rz(\\theta)$ on each qubit. This leads to a state rotated on the x-axis by $N \\theta$ at the output of the GHZ un-preparation. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "ht1r21O_vZCm",
+ "scrolled": false,
+ "colab": {}
+ },
+ "source": [
+ "inputs = []\n",
+ "un_rotations = []\n",
+ "thetas = []\n",
+ "\n",
+ "for theta in np.arange(0, 2 * np.pi, step=0.05):\n",
+ " thetas.append(theta)\n",
+ " # Signal injection is an Rz on each qubit.\n",
+ " inputs.append(cirq.Circuit(\n",
+ " cirq.Rz(theta).on(bit) for bit in sensing_qubits))\n",
+ "\n",
+ " # During training you want to undo the x rotation at the end of the circuit to send\n",
+ " # the qubit back to the zero state.\n",
+ " un_rotations.append(\n",
+ " cirq.Circuit(\n",
+ " cirq.Rx(-1 * len(sensing_qubits) * theta).on(sensing_qubits[0])))\n",
+ "\n",
+ "signal_injection_tensor = tfq.convert_to_tensor(inputs)\n",
+ "un_rotation_tensor = tfq.convert_to_tensor(un_rotations)\n",
+ "\n",
+ "SVGCircuit(inputs[5])"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "cDF4EsxI48ZG"
+ },
+ "source": [
+ "## 4. Untrained performance\n",
+ "\n",
+ "Let's see how the *untrained* circuit performs at amplifying the input signal:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "3g1U-GYd9Dci",
+ "colab": {}
+ },
+ "source": [
+ "# when evaluating the performance of the model you don't want to un-rotate\n",
+ "# at the end, so send some empty circuits into that input of the model.\n",
+ "empty_circuits = tfq.convert_to_tensor([cirq.Circuit()] *\n",
+ " signal_injection_tensor.shape[0])\n",
+ "\n",
+ "untrained_outputs = sensing_model.predict(\n",
+ " x=[signal_injection_tensor, empty_circuits])"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "N_J_QNhc5elN",
+ "scrolled": false,
+ "colab": {}
+ },
+ "source": [
+ "plt.plot(thetas, np.cos(thetas), label='Un-amplified signal')\n",
+ "plt.plot(thetas, untrained_outputs, label='Untrained Amplified Output')\n",
+ "plt.title(\"Untrained Amplification Performance\")\n",
+ "plt.legend(loc='lower right')\n",
+ "plt.xlabel(r\"\\theta\")\n",
+ "plt.ylabel(r\"$-\\langle \\hat{Z} \\rangle$\")\n",
+ "plt.show()"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "vLE6ucT8zqnI"
+ },
+ "source": [
+ "## 5. Train the model"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "DaINoOSuzsAE",
+ "colab": {}
+ },
+ "source": [
+ "optimizer = tf.keras.optimizers.Adam(learning_rate=0.05)\n",
+ "loss = lambda x, y: -1 * y\n",
+ "\n",
+ "sensing_model.compile(optimizer=optimizer, loss=loss)\n",
+ "\n",
+ "history = sensing_model.fit(x=[signal_injection_tensor, un_rotation_tensor],\n",
+ " y=tf.zeros_like(signal_injection_tensor,\n",
+ " dtype=tf.float32),\n",
+ " epochs=10,\n",
+ " batch_size=20,\n",
+ " verbose=1)"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "hDqB6EXs1TEM",
+ "scrolled": true,
+ "colab": {}
+ },
+ "source": [
+ "plt.plot(history.history['loss'])\n",
+ "plt.title(\"Learning to Sense A Randomized Feild\")\n",
+ "plt.xlabel(\"Epochs\")\n",
+ "plt.ylabel(r\"$-\\langle \\hat{Z} \\rangle$\")\n",
+ "plt.show()"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "IwlCaAgvEvSC"
+ },
+ "source": [
+ "## 6. Trained performance\n",
+ "\n",
+ "And here's how the *trained* sensing model performs:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "qXzCn2_B2Y3U",
+ "colab": {}
+ },
+ "source": [
+ "empty_circuits = tfq.convert_to_tensor([cirq.Circuit()] *\n",
+ " signal_injection_tensor.shape[0])\n",
+ "\n",
+ "trained_outputs = sensing_model.predict(\n",
+ " x=[signal_injection_tensor, empty_circuits])"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "metadata": {
+ "colab_type": "code",
+ "id": "oduATtbj3SAp",
+ "scrolled": false,
+ "colab": {}
+ },
+ "source": [
+ "plt.plot(thetas, np.cos(thetas), label='Un-amplified signal')\n",
+ "plt.plot(thetas, trained_outputs, label='Amplified Output')\n",
+ "plt.title(\"Trained Amplification Performance\")\n",
+ "plt.legend(loc='lower right')\n",
+ "plt.xlabel(r\"\\theta\")\n",
+ "plt.ylabel(r\"$-\\langle \\hat{Z} \\rangle$\")\n",
+ "plt.show()"
+ ],
+ "execution_count": 0,
+ "outputs": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "colab_type": "text",
+ "id": "8z58CjVejOCV"
+ },
+ "source": [
+ "From the plot you can see that the trained model has amplified the signal from the cavity using the GHZ state. You can measure this signal (which is easier now since it's amplified) or use it as a component in another quantum algorithm."
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/release/setup.py b/release/setup.py
index 9d1ee30af..c8b562c92 100644
--- a/release/setup.py
+++ b/release/setup.py
@@ -25,7 +25,7 @@
REQUIRED_PACKAGES = [
#'tensorflow = 2.0.0b1',
- 'cirq == 0.6.0',
+ 'cirq == 0.7.0',
'tensorflow == 2.1.0'
]
diff --git a/scripts/build_pip_package_test.sh b/scripts/build_pip_package_test.sh
index dd0ff036b..6fa7ce134 100755
--- a/scripts/build_pip_package_test.sh
+++ b/scripts/build_pip_package_test.sh
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
-pip install tensorflow==2.1.0 cirq==0.6.0
+pip install tensorflow==2.1.0 cirq==0.7.0
cd TFQuantum
diff --git a/scripts/ci_install.sh b/scripts/ci_install.sh
index 77b74ed87..afe3cbde6 100755
--- a/scripts/ci_install.sh
+++ b/scripts/ci_install.sh
@@ -15,4 +15,4 @@
# ==============================================================================
wget https://github.com/bazelbuild/bazel/releases/download/0.26.0/bazel_0.26.0-linux-x86_64.deb
sudo dpkg -i bazel_0.26.0-linux-x86_64.deb
-pip install --upgrade pip setuptools wheel pylint yapf cirq==0.6.0 tensorflow==2.1.0
\ No newline at end of file
+pip install --upgrade pip setuptools wheel pylint yapf cirq==0.7.0 tensorflow==2.1.0
\ No newline at end of file
diff --git a/tensorflow_quantum/core/ops/cirq_ops.py b/tensorflow_quantum/core/ops/cirq_ops.py
index 8a9206ad2..5be8c4faf 100644
--- a/tensorflow_quantum/core/ops/cirq_ops.py
+++ b/tensorflow_quantum/core/ops/cirq_ops.py
@@ -108,7 +108,7 @@ def _batch_deserialize_helper(programs, symbol_names, symbol_values):
program = program.numpy()
values = values.numpy().astype(float)
- circuit_proto = cirq.api.google.v2.program_pb2.Program()
+ circuit_proto = cirq.google.api.v2.program_pb2.Program()
circuit_proto.ParseFromString(program)
circuit = serializer.deserialize_circuit(circuit_proto)
diff --git a/tensorflow_quantum/core/ops/parse_context.cc b/tensorflow_quantum/core/ops/parse_context.cc
index 7616f857c..ca0effd96 100644
--- a/tensorflow_quantum/core/ops/parse_context.cc
+++ b/tensorflow_quantum/core/ops/parse_context.cc
@@ -20,7 +20,7 @@ limitations under the License.
#include
#include
-#include "cirq/api/google/v2/program.pb.h"
+#include "cirq/google/api/v2/program.pb.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/lib/core/error_codes.pb.h"
#include "tensorflow/core/lib/core/status.h"
@@ -32,7 +32,7 @@ limitations under the License.
namespace tfq {
namespace {
-using ::cirq::api::google::v2::Program;
+using ::cirq::google::api::v2::Program;
using ::tensorflow::OpKernelContext;
using ::tensorflow::Status;
using ::tensorflow::Tensor;
diff --git a/tensorflow_quantum/core/ops/parse_context.h b/tensorflow_quantum/core/ops/parse_context.h
index 2eea41069..cba72bf07 100644
--- a/tensorflow_quantum/core/ops/parse_context.h
+++ b/tensorflow_quantum/core/ops/parse_context.h
@@ -20,7 +20,7 @@ limitations under the License.
#include
#include "absl/container/flat_hash_map.h"
-#include "cirq/api/google/v2/program.pb.h"
+#include "cirq/google/api/v2/program.pb.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow_quantum/core/proto/pauli_sum.pb.h"
@@ -30,13 +30,13 @@ namespace tfq {
// Simplest Program proto parsing
tensorflow::Status ParsePrograms(
tensorflow::OpKernelContext* context, const std::string& input_name,
- std::vector* programs);
+ std::vector* programs);
// Parses a vector of programs along with another vector of programs to append
tensorflow::Status GetProgramsAndProgramsToAppend(
tensorflow::OpKernelContext* context,
- std::vector* programs,
- std::vector* programs_to_append);
+ std::vector* programs,
+ std::vector* programs_to_append);
// A parameter map is a mapping from the name of the parameter to the index in
// the input parameter value tensor (for gradient computations) and the value
@@ -49,7 +49,7 @@ typedef absl::flat_hash_map> SymbolMap;
// and correct with the original programs.
tensorflow::Status GetProgramsAndNumQubits(
tensorflow::OpKernelContext* context,
- std::vector* programs,
+ std::vector* programs,
std::vector* num_qubits,
std::vector>* p_sums = nullptr);
diff --git a/tensorflow_quantum/core/ops/tfq_circuit_append_op.cc b/tensorflow_quantum/core/ops/tfq_circuit_append_op.cc
index 777a8ce45..6b248d24a 100644
--- a/tensorflow_quantum/core/ops/tfq_circuit_append_op.cc
+++ b/tensorflow_quantum/core/ops/tfq_circuit_append_op.cc
@@ -15,7 +15,7 @@ limitations under the License.
#include
-#include "cirq/api/google/v2/program.pb.h"
+#include "cirq/google/api/v2/program.pb.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/shape_inference.h"
#include "tensorflow/core/framework/tensor_shape.h"
@@ -26,9 +26,9 @@ limitations under the License.
namespace tfq {
-using ::cirq::api::google::v2::Circuit;
-using ::cirq::api::google::v2::Moment;
-using ::cirq::api::google::v2::Program;
+using ::cirq::google::api::v2::Circuit;
+using ::cirq::google::api::v2::Moment;
+using ::cirq::google::api::v2::Program;
class TfqCircuitAppendOp : public tensorflow::OpKernel {
public:
diff --git a/tensorflow_quantum/core/ops/tfq_ps_decompose_op.cc b/tensorflow_quantum/core/ops/tfq_ps_decompose_op.cc
index b80992103..86274ad44 100644
--- a/tensorflow_quantum/core/ops/tfq_ps_decompose_op.cc
+++ b/tensorflow_quantum/core/ops/tfq_ps_decompose_op.cc
@@ -16,7 +16,7 @@ limitations under the License.
#include
#include
-#include "cirq/api/google/v2/program.pb.h"
+#include "cirq/google/api/v2/program.pb.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/shape_inference.h"
#include "tensorflow/core/framework/tensor_shape.h"
@@ -27,12 +27,12 @@ limitations under the License.
namespace tfq {
-using ::cirq::api::google::v2::Arg;
-using ::cirq::api::google::v2::Circuit;
-using ::cirq::api::google::v2::Moment;
-using ::cirq::api::google::v2::Operation;
-using ::cirq::api::google::v2::Program;
-using ::cirq::api::google::v2::Qubit;
+using ::cirq::google::api::v2::Arg;
+using ::cirq::google::api::v2::Circuit;
+using ::cirq::google::api::v2::Moment;
+using ::cirq::google::api::v2::Operation;
+using ::cirq::google::api::v2::Program;
+using ::cirq::google::api::v2::Qubit;
using ::tensorflow::Status;
using ::tensorflow::Tensor;
diff --git a/tensorflow_quantum/core/ops/tfq_ps_symbol_replace_op.cc b/tensorflow_quantum/core/ops/tfq_ps_symbol_replace_op.cc
index 0269bf88a..7ad0c8ca6 100644
--- a/tensorflow_quantum/core/ops/tfq_ps_symbol_replace_op.cc
+++ b/tensorflow_quantum/core/ops/tfq_ps_symbol_replace_op.cc
@@ -15,7 +15,7 @@ limitations under the License.
#include
-#include "cirq/api/google/v2/program.pb.h"
+#include "cirq/google/api/v2/program.pb.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/shape_inference.h"
#include "tensorflow/core/framework/tensor_shape.h"
@@ -26,11 +26,11 @@ limitations under the License.
namespace tfq {
-using ::cirq::api::google::v2::Arg;
-using ::cirq::api::google::v2::Circuit;
-using ::cirq::api::google::v2::Moment;
-using ::cirq::api::google::v2::Operation;
-using ::cirq::api::google::v2::Program;
+using ::cirq::google::api::v2::Arg;
+using ::cirq::google::api::v2::Circuit;
+using ::cirq::google::api::v2::Moment;
+using ::cirq::google::api::v2::Operation;
+using ::cirq::google::api::v2::Program;
using ::tensorflow::Status;
using ::tensorflow::Tensor;
diff --git a/tensorflow_quantum/core/ops/tfq_ps_weights_from_symbols_op.cc b/tensorflow_quantum/core/ops/tfq_ps_weights_from_symbols_op.cc
index 7c19bbed9..bc96d822b 100644
--- a/tensorflow_quantum/core/ops/tfq_ps_weights_from_symbols_op.cc
+++ b/tensorflow_quantum/core/ops/tfq_ps_weights_from_symbols_op.cc
@@ -18,7 +18,7 @@ limitations under the License.
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/numbers.h"
-#include "cirq/api/google/v2/program.pb.h"
+#include "cirq/google/api/v2/program.pb.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/shape_inference.h"
#include "tensorflow/core/framework/tensor_shape.h"
@@ -29,10 +29,10 @@ limitations under the License.
namespace tfq {
-using ::cirq::api::google::v2::Arg;
-using ::cirq::api::google::v2::Moment;
-using ::cirq::api::google::v2::Operation;
-using ::cirq::api::google::v2::Program;
+using ::cirq::google::api::v2::Arg;
+using ::cirq::google::api::v2::Moment;
+using ::cirq::google::api::v2::Operation;
+using ::cirq::google::api::v2::Program;
using ::tensorflow::Tensor;
class TfqPsWeightsFromSymbolOp : public tensorflow::OpKernel {
diff --git a/tensorflow_quantum/core/ops/tfq_simulate_expectation_op.cc b/tensorflow_quantum/core/ops/tfq_simulate_expectation_op.cc
index 43933c6fe..0897c7c47 100644
--- a/tensorflow_quantum/core/ops/tfq_simulate_expectation_op.cc
+++ b/tensorflow_quantum/core/ops/tfq_simulate_expectation_op.cc
@@ -16,7 +16,7 @@ limitations under the License.
#include
#include
-#include "cirq/api/google/v2/program.pb.h"
+#include "cirq/google/api/v2/program.pb.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/shape_inference.h"
#include "tensorflow/core/framework/tensor_shape.h"
@@ -33,7 +33,7 @@ limitations under the License.
namespace tfq {
-using ::cirq::api::google::v2::Program;
+using ::cirq::google::api::v2::Program;
using ::tensorflow::Status;
using ::tfq::proto::PauliSum;
using ::tfq::qsim::QState;
diff --git a/tensorflow_quantum/core/ops/tfq_simulate_state_op.cc b/tensorflow_quantum/core/ops/tfq_simulate_state_op.cc
index cf0b39b0f..486d1042c 100644
--- a/tensorflow_quantum/core/ops/tfq_simulate_state_op.cc
+++ b/tensorflow_quantum/core/ops/tfq_simulate_state_op.cc
@@ -15,7 +15,7 @@ limitations under the License.
#include
-#include "cirq/api/google/v2/program.pb.h"
+#include "cirq/google/api/v2/program.pb.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/shape_inference.h"
#include "tensorflow/core/framework/tensor_shape.h"
@@ -30,7 +30,7 @@ limitations under the License.
namespace tfq {
-using ::cirq::api::google::v2::Program;
+using ::cirq::google::api::v2::Program;
using ::tensorflow::Status;
using ::tfq::Circuit;
using ::tfq::CircuitFromProgram;
diff --git a/tensorflow_quantum/core/proto/BUILD b/tensorflow_quantum/core/proto/BUILD
index c321bd2eb..a934cd8d1 100644
--- a/tensorflow_quantum/core/proto/BUILD
+++ b/tensorflow_quantum/core/proto/BUILD
@@ -12,7 +12,7 @@ exports_files(["__init__.py"])
cc_proto_library(
name = "program_cc_proto",
deps = [
- "@cirq//cirq/api/google/v2:program_proto",
+ "@cirq//cirq/google/api/v2:program_proto",
],
)
diff --git a/tensorflow_quantum/core/qsim/q_state_test.cc b/tensorflow_quantum/core/qsim/q_state_test.cc
index 5c1a2867d..5a5999940 100644
--- a/tensorflow_quantum/core/qsim/q_state_test.cc
+++ b/tensorflow_quantum/core/qsim/q_state_test.cc
@@ -31,7 +31,7 @@ limitations under the License.
#include
#include "absl/memory/memory.h"
-#include "cirq/api/google/v2/program.pb.h"
+#include "cirq/google/api/v2/program.pb.h"
#include "gtest/gtest.h"
#include "tensorflow_quantum/core/qsim/q_state.h"
#include "tensorflow_quantum/core/qsim/simulator.h"
@@ -45,7 +45,7 @@ namespace tfq {
namespace qsim {
namespace {
-using ::cirq::api::google::v2::Program;
+using ::cirq::google::api::v2::Program;
void BasicTest(QState* state) {
const std::string text = R"(
diff --git a/tensorflow_quantum/core/serialize/serializer.py b/tensorflow_quantum/core/serialize/serializer.py
index d287d9ed3..f1e17aba8 100644
--- a/tensorflow_quantum/core/serialize/serializer.py
+++ b/tensorflow_quantum/core/serialize/serializer.py
@@ -323,7 +323,7 @@ def serialize_circuit(circuit):
circuit: A `cirq.Circuit`.
Returns:
- A `cirq.api.google.v2.Program` proto.
+ A `cirq.google.api.v2.Program` proto.
"""
if not isinstance(circuit, cirq.Circuit):
raise TypeError("serialize requires cirq.Circuit objects."
@@ -385,14 +385,14 @@ def deserialize_circuit(proto):
Note that the proto must use gates valid in the tfq_gate_set.
Args:
- proto: A `cirq.api.google.v2.Program` proto
+ proto: A `cirq.google.api.v2.Program` proto
Returns:
A `cirq.Circuit`.
"""
- if not isinstance(proto, cirq.api.google.v2.program_pb2.Program):
+ if not isinstance(proto, cirq.google.api.v2.program_pb2.Program):
raise TypeError("deserialize requires "
- "cirq.api.google.v2.program_pb2.Program object."
+ "cirq.google.api.v2.program_pb2.Program object."
" Given: " + str(type(proto)))
return SERIALIZER.deserialize(proto)
diff --git a/tensorflow_quantum/core/serialize/serializer_test.py b/tensorflow_quantum/core/serialize/serializer_test.py
index 8b95b70b8..8975bd1c8 100644
--- a/tensorflow_quantum/core/serialize/serializer_test.py
+++ b/tensorflow_quantum/core/serialize/serializer_test.py
@@ -18,7 +18,7 @@
import tensorflow as tf
import cirq
-from cirq.api.google.v2 import program_pb2
+from cirq.google.api.v2 import program_pb2
from absl.testing import parameterized
from tensorflow_quantum.core.proto import pauli_sum_pb2
from tensorflow_quantum.core.serialize import serializer
@@ -78,7 +78,7 @@ def _build_gate_proto(gate_id, arg_names, arg_vals, qubit_ids):
args = {arg_names[i]: (program_pb2.Arg(symbol=arg_vals[i]) \
if isinstance(arg_vals[i], str) else \
program_pb2.Arg(
- arg_value=cirq.api.google.v2.program_pb2.ArgValue(
+ arg_value=cirq.google.api.v2.program_pb2.ArgValue(
float_value=arg_vals[i]))) for i in range(len(arg_vals))},
qubits=[program_pb2.Qubit(
id=q_id) for q_id in qubit_ids])])
diff --git a/tensorflow_quantum/core/src/circuit_parser.cc b/tensorflow_quantum/core/src/circuit_parser.cc
index c2ee00a3e..86185eca2 100644
--- a/tensorflow_quantum/core/src/circuit_parser.cc
+++ b/tensorflow_quantum/core/src/circuit_parser.cc
@@ -23,7 +23,7 @@ limitations under the License.
#include "absl/container/flat_hash_map.h"
#include "absl/strings/numbers.h"
-#include "cirq/api/google/v2/program.pb.h"
+#include "cirq/google/api/v2/program.pb.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow_quantum/core/proto/pauli_sum.pb.h"
#include "tensorflow_quantum/core/src/circuit.h"
@@ -32,11 +32,11 @@ limitations under the License.
namespace tfq {
namespace {
-using ::cirq::api::google::v2::Arg;
-using ::cirq::api::google::v2::Moment;
-using ::cirq::api::google::v2::Operation;
-using ::cirq::api::google::v2::Program;
-using ::cirq::api::google::v2::Qubit;
+using ::cirq::google::api::v2::Arg;
+using ::cirq::google::api::v2::Moment;
+using ::cirq::google::api::v2::Operation;
+using ::cirq::google::api::v2::Program;
+using ::cirq::google::api::v2::Qubit;
using ::tensorflow::Status;
// Adds the operation as a Gate in the circuit. The index is the moment number.
@@ -71,9 +71,9 @@ Status CircuitFromProgram(const Program& program, const int num_qubits,
Circuit* circuit) {
circuit->num_qubits = num_qubits;
- const cirq::api::google::v2::Circuit& cirq_circuit = program.circuit();
+ const cirq::google::api::v2::Circuit& cirq_circuit = program.circuit();
if (cirq_circuit.scheduling_strategy() !=
- cirq::api::google::v2::Circuit::MOMENT_BY_MOMENT) {
+ cirq::google::api::v2::Circuit::MOMENT_BY_MOMENT) {
return Status(tensorflow::error::INVALID_ARGUMENT,
"Circuit must be moment by moment.");
}
@@ -132,7 +132,7 @@ Status CircuitFromPauliTerm(const tfq::proto::PauliTerm& term,
const int num_qubits, Circuit* circuit) {
Program measurement_program;
measurement_program.mutable_circuit()->set_scheduling_strategy(
- cirq::api::google::v2::Circuit::MOMENT_BY_MOMENT);
+ cirq::google::api::v2::Circuit::MOMENT_BY_MOMENT);
Moment* term_moment = measurement_program.mutable_circuit()->add_moments();
for (const tfq::proto::PauliQubitPair& pair : term.paulis()) {
Operation* new_op = term_moment->add_operations();
diff --git a/tensorflow_quantum/core/src/circuit_parser.h b/tensorflow_quantum/core/src/circuit_parser.h
index a31cfc5f0..ad0abc4fe 100644
--- a/tensorflow_quantum/core/src/circuit_parser.h
+++ b/tensorflow_quantum/core/src/circuit_parser.h
@@ -20,7 +20,7 @@ limitations under the License.
#include
#include
-#include "cirq/api/google/v2/program.pb.h"
+#include "cirq/google/api/v2/program.pb.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow_quantum/core/proto/pauli_sum.pb.h"
#include "tensorflow_quantum/core/src/circuit.h"
@@ -29,7 +29,7 @@ namespace tfq {
// parse a serialized Cirq program into our internal representation
tensorflow::Status CircuitFromProgram(
- const cirq::api::google::v2::Program& program, const int num_qubits,
+ const cirq::google::api::v2::Program& program, const int num_qubits,
Circuit* circuit);
// build the circuit taking the computational basis to the measurement basis
diff --git a/tensorflow_quantum/core/src/circuit_parser_test.cc b/tensorflow_quantum/core/src/circuit_parser_test.cc
index 4bced7432..ce4e46a9b 100644
--- a/tensorflow_quantum/core/src/circuit_parser_test.cc
+++ b/tensorflow_quantum/core/src/circuit_parser_test.cc
@@ -21,7 +21,7 @@ limitations under the License.
#include "absl/container/flat_hash_map.h"
#include "absl/strings/numbers.h"
-#include "cirq/api/google/v2/program.pb.h"
+#include "cirq/google/api/v2/program.pb.h"
#include "gtest/gtest.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow_quantum/core/src/circuit.h"
@@ -31,11 +31,11 @@ limitations under the License.
namespace tfq {
namespace {
-using ::cirq::api::google::v2::Program;
+using ::cirq::google::api::v2::Program;
TEST(CircuitParserTest, CircuitFromProgramInvalidSchedule) {
Program program_proto;
- ::cirq::api::google::v2::Circuit* circuit_proto =
+ ::cirq::google::api::v2::Circuit* circuit_proto =
program_proto.mutable_circuit();
circuit_proto->set_scheduling_strategy(
circuit_proto->SCHEDULING_STRATEGY_UNSPECIFIED);
@@ -47,17 +47,17 @@ TEST(CircuitParserTest, CircuitFromProgramInvalidSchedule) {
TEST(CircuitParserTest, CircuitFromProgramInvalidQubitId) {
Program program_proto;
- ::cirq::api::google::v2::Circuit* circuit_proto =
+ ::cirq::google::api::v2::Circuit* circuit_proto =
program_proto.mutable_circuit();
circuit_proto->set_scheduling_strategy(circuit_proto->MOMENT_BY_MOMENT);
- ::cirq::api::google::v2::Moment* moments_proto = circuit_proto->add_moments();
+ ::cirq::google::api::v2::Moment* moments_proto = circuit_proto->add_moments();
// Add CNOT gate with invalid qubit
- ::cirq::api::google::v2::Operation* operations_proto =
+ ::cirq::google::api::v2::Operation* operations_proto =
moments_proto->add_operations();
- ::cirq::api::google::v2::Gate* gate_proto = operations_proto->mutable_gate();
+ ::cirq::google::api::v2::Gate* gate_proto = operations_proto->mutable_gate();
gate_proto->set_id("CNOT");
- ::cirq::api::google::v2::Qubit* qubits_proto = operations_proto->add_qubits();
+ ::cirq::google::api::v2::Qubit* qubits_proto = operations_proto->add_qubits();
qubits_proto->set_id("0");
qubits_proto = operations_proto->add_qubits();
qubits_proto->set_id("0_0");
@@ -71,7 +71,7 @@ TEST(CircuitParserTest, CircuitFromProgramInvalidQubitId) {
TEST(CircuitParserTest, CircuitFromProgramEmpty) {
Program program_proto;
- ::cirq::api::google::v2::Circuit* circuit_proto =
+ ::cirq::google::api::v2::Circuit* circuit_proto =
program_proto.mutable_circuit();
circuit_proto->set_scheduling_strategy(circuit_proto->MOMENT_BY_MOMENT);
@@ -86,12 +86,12 @@ TEST(CircuitParserTest, CircuitFromProgramEmpty) {
// Test application of orphan gate collection identity gates
// TODO(zaqqwerty): remove when orphan gate collection is moved to fuser
Program program_proto_ident_odd;
- ::cirq::api::google::v2::Circuit* circuit_proto_ident_odd =
+ ::cirq::google::api::v2::Circuit* circuit_proto_ident_odd =
program_proto_ident_odd.mutable_circuit();
circuit_proto_ident_odd->set_scheduling_strategy(
circuit_proto_ident_odd->MOMENT_BY_MOMENT);
Program program_proto_ident_even;
- ::cirq::api::google::v2::Circuit* circuit_proto_ident_even =
+ ::cirq::google::api::v2::Circuit* circuit_proto_ident_even =
program_proto_ident_even.mutable_circuit();
circuit_proto_ident_even->set_scheduling_strategy(
circuit_proto_ident_even->MOMENT_BY_MOMENT);
@@ -130,35 +130,35 @@ TEST(CircuitParserTest, CircuitFromProgramEmpty) {
TEST(CircuitParserTest, CircuitFromProgramPaulis) {
Program program_proto;
- ::cirq::api::google::v2::Circuit* circuit_proto =
+ ::cirq::google::api::v2::Circuit* circuit_proto =
program_proto.mutable_circuit();
circuit_proto->set_scheduling_strategy(circuit_proto->MOMENT_BY_MOMENT);
- ::cirq::api::google::v2::Moment* moments_proto = circuit_proto->add_moments();
+ ::cirq::google::api::v2::Moment* moments_proto = circuit_proto->add_moments();
// Add X gate
- ::cirq::api::google::v2::Operation* operations_proto =
+ ::cirq::google::api::v2::Operation* operations_proto =
moments_proto->add_operations();
- ::cirq::api::google::v2::Gate* gate_proto = operations_proto->mutable_gate();
+ ::cirq::google::api::v2::Gate* gate_proto = operations_proto->mutable_gate();
gate_proto->set_id("XP");
- ::cirq::api::google::v2::Qubit* qubits_proto = operations_proto->add_qubits();
+ ::cirq::google::api::v2::Qubit* qubits_proto = operations_proto->add_qubits();
qubits_proto->set_id("0");
// Create the required Arg protos
- ::cirq::api::google::v2::Arg global_shift_arg;
- ::cirq::api::google::v2::ArgValue* global_shift_arg_value =
+ ::cirq::google::api::v2::Arg global_shift_arg;
+ ::cirq::google::api::v2::ArgValue* global_shift_arg_value =
global_shift_arg.mutable_arg_value();
global_shift_arg_value->set_float_value(0.0);
- ::cirq::api::google::v2::Arg exponent_arg;
- ::cirq::api::google::v2::ArgValue* exponent_arg_value =
+ ::cirq::google::api::v2::Arg exponent_arg;
+ ::cirq::google::api::v2::ArgValue* exponent_arg_value =
exponent_arg.mutable_arg_value();
exponent_arg_value->set_float_value(1.0);
- ::cirq::api::google::v2::Arg exponent_scalar_arg;
- ::cirq::api::google::v2::ArgValue* exponent_scalar_arg_value =
+ ::cirq::google::api::v2::Arg exponent_scalar_arg;
+ ::cirq::google::api::v2::ArgValue* exponent_scalar_arg_value =
exponent_scalar_arg.mutable_arg_value();
exponent_scalar_arg_value->set_float_value(1.0);
// Add Arg protos to the operation arg map
- google::protobuf::Map* args_proto =
+ google::protobuf::Map* args_proto =
operations_proto->mutable_args();
(*args_proto)["global_shift"] = global_shift_arg;
(*args_proto)["exponent"] = exponent_arg;
diff --git a/tensorflow_quantum/core/src/program_resolution.cc b/tensorflow_quantum/core/src/program_resolution.cc
index 24ec06e1c..4a4482bb8 100644
--- a/tensorflow_quantum/core/src/program_resolution.cc
+++ b/tensorflow_quantum/core/src/program_resolution.cc
@@ -21,18 +21,18 @@ limitations under the License.
#include "absl/container/flat_hash_set.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
-#include "cirq/api/google/v2/program.pb.h"
+#include "cirq/google/api/v2/program.pb.h"
#include "tensorflow/core/lib/core/error_codes.pb.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow_quantum/core/proto/pauli_sum.pb.h"
namespace tfq {
-using cirq::api::google::v2::Arg;
-using cirq::api::google::v2::Moment;
-using cirq::api::google::v2::Operation;
-using cirq::api::google::v2::Program;
-using cirq::api::google::v2::Qubit;
+using cirq::google::api::v2::Arg;
+using cirq::google::api::v2::Moment;
+using cirq::google::api::v2::Operation;
+using cirq::google::api::v2::Program;
+using cirq::google::api::v2::Qubit;
using tensorflow::Status;
using tfq::proto::PauliQubitPair;
using tfq::proto::PauliSum;
diff --git a/tensorflow_quantum/core/src/program_resolution.h b/tensorflow_quantum/core/src/program_resolution.h
index fe0a6b2c8..3e46ee2b5 100644
--- a/tensorflow_quantum/core/src/program_resolution.h
+++ b/tensorflow_quantum/core/src/program_resolution.h
@@ -22,7 +22,7 @@ limitations under the License.
#include
#include "absl/container/flat_hash_map.h"
-#include "cirq/api/google/v2/program.pb.h"
+#include "cirq/google/api/v2/program.pb.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow_quantum/core/proto/pauli_sum.pb.h"
@@ -39,13 +39,13 @@ namespace tfq {
// The qubits are sorted in the same way Cirq would sort them, to provide
// consistent outputs.
tensorflow::Status ResolveQubitIds(
- cirq::api::google::v2::Program* program,
+ cirq::google::api::v2::Program* program,
std::vector* p_sums = nullptr);
// Returns the maximum number qubit in the Program. If the QubitIds were
// resolved with the above function, this is the number of qubits in the
// Program.
-int GetNumQubits(const cirq::api::google::v2::Program& program);
+int GetNumQubits(const cirq::google::api::v2::Program& program);
// Resolves all of the symbols present in the Program. Iterates through all
// operations in all moments, and if any Args have a symbol, replaces the one-of
@@ -55,7 +55,7 @@ int GetNumQubits(const cirq::api::google::v2::Program& program);
// isn't used.
tensorflow::Status ResolveSymbols(
const absl::flat_hash_map>& param_map,
- cirq::api::google::v2::Program* program);
+ cirq::google::api::v2::Program* program);
} // namespace tfq
diff --git a/tensorflow_quantum/core/src/program_resolution_test.cc b/tensorflow_quantum/core/src/program_resolution_test.cc
index fd76f12f9..c3070d2e7 100644
--- a/tensorflow_quantum/core/src/program_resolution_test.cc
+++ b/tensorflow_quantum/core/src/program_resolution_test.cc
@@ -20,14 +20,14 @@ limitations under the License.
#include
#include "absl/container/flat_hash_map.h"
-#include "cirq/api/google/v2/program.pb.h"
+#include "cirq/google/api/v2/program.pb.h"
#include "gtest/gtest.h"
#include "tensorflow/core/lib/core/status.h"
namespace tfq {
namespace {
-using cirq::api::google::v2::Program;
+using cirq::google::api::v2::Program;
using tensorflow::Status;
TEST(ProgramResolutionTest, ResolveQubitIds) {
diff --git a/tensorflow_quantum/python/util.py b/tensorflow_quantum/python/util.py
index 467a4bbc9..dbe05d5e8 100644
--- a/tensorflow_quantum/python/util.py
+++ b/tensorflow_quantum/python/util.py
@@ -192,7 +192,7 @@ def _parse_single(item):
try:
if b'tfq_gate_set' in item:
# Return a circuit parsing
- obj = cirq.api.google.v2.program_pb2.Program()
+ obj = cirq.google.api.v2.program_pb2.Program()
obj.ParseFromString(item)
out = serializer.deserialize_circuit(obj)
return out