From 0b6ea5f9148e69a7c423e7775ef5085ba2df862c Mon Sep 17 00:00:00 2001 From: KevinVillela Date: Thu, 16 Apr 2020 20:29:19 -0700 Subject: [PATCH 1/4] feat(qvol): First version of adding Quantum Volume to Recirq. This just adds a simple task and driver Script for now. More to come, such as precomputing the generated circuits and the optimal qubits to run on in other tasks. --- recirq/quantum_volume/__init__.py | 13 ++ recirq/quantum_volume/experiments/__init__.py | 13 ++ .../experiments/quantum_volume.py | 125 ++++++++++++++++++ .../experiments/run-quantum-volume.py | 48 +++++++ 4 files changed, 199 insertions(+) create mode 100644 recirq/quantum_volume/__init__.py create mode 100644 recirq/quantum_volume/experiments/__init__.py create mode 100644 recirq/quantum_volume/experiments/quantum_volume.py create mode 100644 recirq/quantum_volume/experiments/run-quantum-volume.py diff --git a/recirq/quantum_volume/__init__.py b/recirq/quantum_volume/__init__.py new file mode 100644 index 00000000..d0feedfc --- /dev/null +++ b/recirq/quantum_volume/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2020 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/recirq/quantum_volume/experiments/__init__.py b/recirq/quantum_volume/experiments/__init__.py new file mode 100644 index 00000000..d0feedfc --- /dev/null +++ b/recirq/quantum_volume/experiments/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2020 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/recirq/quantum_volume/experiments/quantum_volume.py b/recirq/quantum_volume/experiments/quantum_volume.py new file mode 100644 index 00000000..2257a7e4 --- /dev/null +++ b/recirq/quantum_volume/experiments/quantum_volume.py @@ -0,0 +1,125 @@ +# Copyright 2020 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +from typing import Optional, List + +import numpy as np + +import cirq +from cirq.contrib.quantum_volume import quantum_volume + +import recirq + + +@recirq.json_serializable_dataclass(namespace='recirq.quantum_volume', + registry=recirq.Registry, + frozen=True) +class QuantumVolumeTask: + """Script to run the quantum volume algorithm on a device, per the benchmark defined by IBM in + https://arxiv.org/abs/1811.12926. + + This will run the Quantum Volume benchmark on the Sycamore device on the given depths (every combination of + depths 2 through 6 by default) with 100 repetitions each depth. You can configure these parameters by editing + this script directly. All the business logic is deferred to a Quantum Volume back in public Cirq - this file + exists primarily to pull in devices that we are not yet ready to release. + + Attributes: + dataset_id: A unique identifier for this dataset. + device_name: The device to run on, by name. + num_qubits: The number of qubits in the generated circuits. + qubits: All of the possible qubits that the algorithm can run on. If empty, + we will use any qubits on the device. + n_shots: The number of repetitions for each circuit. + n_circuits: The number of circuits to run the algorithm. + depth: The number of layers to generate in the circuit. + """ + dataset_id: str + device_name: str + n_qubits: int + n_shots: int + n_circuits: int + depth: int + readout_error_correction: bool + qubits: Optional[List[cirq.GridQubit]] = None + + @property + def fn(self): + n_shots = _abbrev_n_shots(self.n_shots) + qubits = _abbrev_grid_qubits(self.qubits) + '/' + + return (f'{self.dataset_id}/' + f'{self.device_name}/' + f'{qubits}/' + f'{self.n_circuits}_{self.depth}_{self.n_qubits}_{n_shots}') + + +# Define the following helper functions to make nicer `fn` keys +# for the tasks: + + +def _abbrev_n_shots(n_shots: int) -> str: + """Shorter n_shots component of a filename""" + if n_shots % 1000 == 0: + return f'{n_shots // 1000}k' + return str(n_shots) + + +def _abbrev_grid_qubits(qubits: Optional[List[cirq.GridQubit]]) -> str: + """Formatted a list of grid qubits component of a filename""" + if not qubits: + return '' + return '-'.join([f'{qubit.row}_{qubit.col}' for qubit in qubits]) + + +EXPERIMENT_NAME = 'quantum-volume' +DEFAULT_BASE_DIR = os.path.expanduser(f'~/cirq-results/{EXPERIMENT_NAME}') + + +def run_quantum_volume(task: QuantumVolumeTask, base_dir=None): + """Execute a :py:class:`QuantumVolumeTask` task.""" + if base_dir is None: + base_dir = DEFAULT_BASE_DIR + + if recirq.exists(task, base_dir=base_dir): + print(f"{task} already exists. Skipping.") + return + + sampler = recirq.get_sampler_by_name(device_name=task.device_name) + device = recirq.get_device_obj_by_name(device_name=task.device_name) + device_or_qubits = task.qubits if task.qubits else device + + # Run the jobs + print("Collecting data", flush=True) + results = quantum_volume.calculate_quantum_volume( + num_qubits=task.n_qubits, + depth=task.depth, + num_circuits=task.n_circuits, + device_or_qubits=device_or_qubits, + samplers=[sampler], + repetitions=task.n_shots, + random_state=np.random.RandomState( + int(hash(task.dataset_id)) % (2**32 - 1)), + compiler=lambda c: cirq.google.optimized_for_sycamore( + c, + new_device=device, + optimizer_type='sycamore', + tabulation_resolution=0.008), + add_readout_error_correction=task.readout_error_correction) + # Save the results CODE REVIEW QUESTION: This attempts to serialize a QuantumVolumeResult, which contains a + # Circuit, which contains a SerializableDevice, which is not JSON serializable. What's the best way to resolve + # this? + recirq.save(task=task, data={ + 'results': results, + }, base_dir=base_dir) diff --git a/recirq/quantum_volume/experiments/run-quantum-volume.py b/recirq/quantum_volume/experiments/run-quantum-volume.py new file mode 100644 index 00000000..af91e6d3 --- /dev/null +++ b/recirq/quantum_volume/experiments/run-quantum-volume.py @@ -0,0 +1,48 @@ +# Copyright 2020 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from recirq.quantum_volume.experiments.quantum_volume import QuantumVolumeTask, run_quantum_volume +import datetime + + +def main(): + """Main driver script entry point. + + This function contains configuration options and you will likely need + to edit it to suit your needs. Of particular note, please make sure + `dataset_id` and `device_name` + are set how you want them. You may also want to change the values in + the list comprehension to set the qubits. + """ + # Uncomment below for an auto-generated unique dataset_id + # dataset_id = datetime.datetime.now().isoformat(timespec='minutes') + dataset_id = '2020-04-14' + data_collection_tasks = [ + QuantumVolumeTask( + dataset_id=dataset_id, + device_name='Syc23-simulator', + n_shots=10_000, + n_circuits=1, + n_qubits=4, + depth=4, + readout_error_correction=True, + ) + ] + + for dc_task in data_collection_tasks: + run_quantum_volume(dc_task) + + +if __name__ == '__main__': + main() From 8ce44aa1fd655eb96260f8b2517f5a15fcf2a616 Mon Sep 17 00:00:00 2001 From: KevinVillela Date: Sun, 26 Apr 2020 21:10:53 -0700 Subject: [PATCH 2/4] Remove device from circuits --- recirq/quantum_volume/experiments/quantum_volume.py | 10 +++++++--- .../quantum_volume/experiments/run-quantum-volume.py | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/recirq/quantum_volume/experiments/quantum_volume.py b/recirq/quantum_volume/experiments/quantum_volume.py index 2257a7e4..78773ac9 100644 --- a/recirq/quantum_volume/experiments/quantum_volume.py +++ b/recirq/quantum_volume/experiments/quantum_volume.py @@ -117,9 +117,13 @@ def run_quantum_volume(task: QuantumVolumeTask, base_dir=None): optimizer_type='sycamore', tabulation_resolution=0.008), add_readout_error_correction=task.readout_error_correction) - # Save the results CODE REVIEW QUESTION: This attempts to serialize a QuantumVolumeResult, which contains a - # Circuit, which contains a SerializableDevice, which is not JSON serializable. What's the best way to resolve - # this? + # Save the results. NB: The circuits in the results have Circuits that contain a SerializableDevice, which at the + # time of this writing is not JSON serializable. Because we don't actually need the device to be in the circuits, + # we simply set them to None for now. + for result in results: + # We can't actually set the device property to None, so set _device directly. + result.compiled_circuit._device = None + result.model_circuit._device = None recirq.save(task=task, data={ 'results': results, }, base_dir=base_dir) diff --git a/recirq/quantum_volume/experiments/run-quantum-volume.py b/recirq/quantum_volume/experiments/run-quantum-volume.py index af91e6d3..6d287758 100644 --- a/recirq/quantum_volume/experiments/run-quantum-volume.py +++ b/recirq/quantum_volume/experiments/run-quantum-volume.py @@ -27,7 +27,7 @@ def main(): """ # Uncomment below for an auto-generated unique dataset_id # dataset_id = datetime.datetime.now().isoformat(timespec='minutes') - dataset_id = '2020-04-14' + dataset_id = '2020-04-26' data_collection_tasks = [ QuantumVolumeTask( dataset_id=dataset_id, From 713c70b421c601f350f438a772e3b5e112b1233c Mon Sep 17 00:00:00 2001 From: KevinVillela Date: Sun, 26 Apr 2020 21:12:12 -0700 Subject: [PATCH 3/4] Rename quantum_volume dir to benchmarks --- recirq/{quantum_volume => benchmarks}/__init__.py | 0 recirq/{quantum_volume => benchmarks}/experiments/__init__.py | 0 .../{quantum_volume => benchmarks}/experiments/quantum_volume.py | 0 .../experiments/run-quantum-volume.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename recirq/{quantum_volume => benchmarks}/__init__.py (100%) rename recirq/{quantum_volume => benchmarks}/experiments/__init__.py (100%) rename recirq/{quantum_volume => benchmarks}/experiments/quantum_volume.py (100%) rename recirq/{quantum_volume => benchmarks}/experiments/run-quantum-volume.py (100%) diff --git a/recirq/quantum_volume/__init__.py b/recirq/benchmarks/__init__.py similarity index 100% rename from recirq/quantum_volume/__init__.py rename to recirq/benchmarks/__init__.py diff --git a/recirq/quantum_volume/experiments/__init__.py b/recirq/benchmarks/experiments/__init__.py similarity index 100% rename from recirq/quantum_volume/experiments/__init__.py rename to recirq/benchmarks/experiments/__init__.py diff --git a/recirq/quantum_volume/experiments/quantum_volume.py b/recirq/benchmarks/experiments/quantum_volume.py similarity index 100% rename from recirq/quantum_volume/experiments/quantum_volume.py rename to recirq/benchmarks/experiments/quantum_volume.py diff --git a/recirq/quantum_volume/experiments/run-quantum-volume.py b/recirq/benchmarks/experiments/run-quantum-volume.py similarity index 100% rename from recirq/quantum_volume/experiments/run-quantum-volume.py rename to recirq/benchmarks/experiments/run-quantum-volume.py From dec711ac89bb2becc961f0a15f017cfc17f0dcbc Mon Sep 17 00:00:00 2001 From: KevinVillela Date: Mon, 25 May 2020 09:03:26 -0700 Subject: [PATCH 4/4] Create simple Jupyter notebook and change directory to 'benchmarks' --- recirq/benchmarks/Quantum-Volume.ipynb | 94 +++++++++++++++++++ .../experiments/run-quantum-volume.py | 4 +- 2 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 recirq/benchmarks/Quantum-Volume.ipynb diff --git a/recirq/benchmarks/Quantum-Volume.ipynb b/recirq/benchmarks/Quantum-Volume.ipynb new file mode 100644 index 00000000..f1a24b7c --- /dev/null +++ b/recirq/benchmarks/Quantum-Volume.ipynb @@ -0,0 +1,94 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Running the Quantum Volume Algorithm\n", + "This example runs the quantum volume algorithm of https://arxiv.org/pdf/1811.12926.pdf. In general, the algorithm will generate a model circuit, classically compute its Heavy Output Group, then run a sampler to evaluate how often it generates Heavy results.\n", + "\n", + "TODO: Flesh out this notebook per https://github.com/quantumlib/ReCirq/issues/28.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# This cell sets up the parameters for the quantum volume algorithm.\n", + "# Feel free to mess with these!\n", + "from recirq.benchmarks.experiments.quantum_volume import QuantumVolumeTask, run_quantum_volume\n", + "import datetime\n", + "\n", + "dataset_id = datetime.datetime.now().isoformat(timespec='minutes')\n", + "dc_task = QuantumVolumeTask(\n", + " dataset_id=dataset_id,\n", + " device_name='Syc23-simulator',\n", + " n_shots=10_000,\n", + " n_circuits=1,\n", + " n_qubits=4,\n", + " depth=4,\n", + " readout_error_correction=True,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Actually run the task and collect the data for analysis.\n", + "from recirq.benchmarks.experiments.quantum_volume import EXPERIMENT_NAME, DEFAULT_BASE_DIR, QuantumVolumeTask, run_quantum_volume\n", + "\n", + "run_quantum_volume(dc_task)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import recirq\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "records = []\n", + "# Load all data, do some light processing\n", + "for record in recirq.iterload_records(dataset_id=dataset_id, base_dir=DEFAULT_BASE_DIR):\n", + " # Expand task dataclass into columns\n", + " recirq.flatten_dataclass_into_record(record, 'task')\n", + "\n", + " records.append(record)\n", + " \n", + "df = pd.DataFrame(records)\n", + "print(len(df))\n", + "df.head()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "ReCirq", + "language": "python", + "name": "recirq" + }, + "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.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/recirq/benchmarks/experiments/run-quantum-volume.py b/recirq/benchmarks/experiments/run-quantum-volume.py index 6d287758..9075773f 100644 --- a/recirq/benchmarks/experiments/run-quantum-volume.py +++ b/recirq/benchmarks/experiments/run-quantum-volume.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from recirq.quantum_volume.experiments.quantum_volume import QuantumVolumeTask, run_quantum_volume +from recirq.benchmarks.experiments.quantum_volume import QuantumVolumeTask, run_quantum_volume import datetime @@ -27,7 +27,7 @@ def main(): """ # Uncomment below for an auto-generated unique dataset_id # dataset_id = datetime.datetime.now().isoformat(timespec='minutes') - dataset_id = '2020-04-26' + dataset_id = '2020-05-21' data_collection_tasks = [ QuantumVolumeTask( dataset_id=dataset_id,