From eb1031895990796c8b50b58e2aa7da7edccdb1fb Mon Sep 17 00:00:00 2001 From: Seneca Meeks Date: Mon, 27 Nov 2023 10:38:53 -0800 Subject: [PATCH 01/12] Created using Colaboratory --- docs/tutorials/google/start.ipynb | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/docs/tutorials/google/start.ipynb b/docs/tutorials/google/start.ipynb index 92ae54b7be4..0f910cd3bba 100644 --- a/docs/tutorials/google/start.ipynb +++ b/docs/tutorials/google/start.ipynb @@ -175,12 +175,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": { "cellView": "both", - "id": "EQoTYZIEPa9S" - }, - "outputs": [], + "id": "EQoTYZIEPa9S", + "outputId": "43c72568-3bc8-4b44-871b-b9db19c9e672", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Circuit:\n", + "(0, 0): ───X───M('result')───\n" + ] + } + ], "source": [ "# Define a qubit at an arbitrary grid location.\n", "qubit = cirq.GridQubit(0, 0)\n", From f4e0d88a18f487dd8a597a462eb4a0336c239476 Mon Sep 17 00:00:00 2001 From: Seneca Meeks Date: Fri, 15 Dec 2023 22:00:14 +0000 Subject: [PATCH 02/12] call run_sweep_async in parallel --- cirq-google/cirq_google/engine/processor_sampler.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cirq-google/cirq_google/engine/processor_sampler.py b/cirq-google/cirq_google/engine/processor_sampler.py index ecf18230d56..21410bbdc10 100644 --- a/cirq-google/cirq_google/engine/processor_sampler.py +++ b/cirq-google/cirq_google/engine/processor_sampler.py @@ -16,11 +16,16 @@ import cirq import duet +import concurrent.futures +from typing import TypeVar, Generic if TYPE_CHECKING: import cirq_google as cg +T = TypeVar("T") + + class ProcessorSampler(cirq.Sampler): """A wrapper around AbstractProcessor to implement the cirq.Sampler interface.""" @@ -76,6 +81,7 @@ async def run_batch_async( params_list: Optional[Sequence[cirq.Sweepable]] = None, repetitions: Union[int, Sequence[int]] = 1, ) -> Sequence[Sequence['cg.EngineResult']]: + print("outer") return cast( Sequence[Sequence['cg.EngineResult']], await super().run_batch_async(programs, params_list, repetitions), From ba7de2907ba39ce59167c589ae06cce50c96deca Mon Sep 17 00:00:00 2001 From: Seneca Meeks Date: Mon, 18 Dec 2023 21:11:26 +0000 Subject: [PATCH 03/12] call circuits asynchronously --- cirq-core/cirq/work/sampler.py | 16 +++-- hello.py | 121 +++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 hello.py diff --git a/cirq-core/cirq/work/sampler.py b/cirq-core/cirq/work/sampler.py index 0b766b5ca1e..ed13bd8cbdb 100644 --- a/cirq-core/cirq/work/sampler.py +++ b/cirq-core/cirq/work/sampler.py @@ -277,6 +277,7 @@ def run_batch( ValueError: If length of `programs` is not equal to the length of `params_list` or the length of `repetitions`. """ + print("um") params_list, repetitions = self._normalize_batch_args(programs, params_list, repetitions) return [ self.run_sweep(circuit, params=params, repetitions=repetitions) @@ -293,11 +294,18 @@ async def run_batch_async( See docs for `cirq.Sampler.run_batch`. """ + print("finally") params_list, repetitions = self._normalize_batch_args(programs, params_list, repetitions) - return [ - await self.run_sweep_async(circuit, params=params, repetitions=repetitions) - for circuit, params, repetitions in zip(programs, params_list, repetitions) - ] + results = [] + for circuit, params, repetitions in zip(programs, params_list, repetitions): + results.append(duet.run(self.run_sweep_async, circuit, params, repetitions)) + + return results + + # return [ + # await self.run_sweep_async(circuit, params=params, repetitions=repetitions) + # for circuit, params, repetitions in zip(programs, params_list, repetitions) + # ] def _normalize_batch_args( self, diff --git a/hello.py b/hello.py new file mode 100644 index 00000000000..1d933757e0a --- /dev/null +++ b/hello.py @@ -0,0 +1,121 @@ +import cirq +from typing import Sequence +import numpy as np +from cirq.experiments.qubit_characterizations import ( + RandomizedBenchMarkResult, + _random_single_q_clifford, + _single_qubit_cliffords, + _gate_seq_to_mats, +) +from scipy.optimize import curve_fit +import matplotlib.pyplot as plt +import datetime + + +def _create_rb_circuit( + qubits: tuple[cirq.GridQubit], num_cfds: int, c1: list, cfd_mats: np.array +) -> cirq.Circuit: + circuits_to_zip = [_random_single_q_clifford(qubit, num_cfds, c1, cfd_mats) for qubit in qubits] + circuit = cirq.Circuit.zip(*circuits_to_zip) + measure_moment = cirq.Moment( + cirq.measure_each(*qubits, key_func=lambda q: 'q{}_{}'.format(q.row, q.col)) + ) + circuit_with_meas = cirq.Circuit.from_moments(*(circuit.moments + [measure_moment])) + return circuit_with_meas + + +def single_qubit_randomized_benchmarking( + sampler: cirq.Sampler, + use_xy_basis: bool = True, + *, + qubits: tuple[cirq.GridQubit] | None = None, + num_clifford_range: Sequence[int] = [5, 18, 70, 265, 1000], + num_circuits: int = 10, + repetitions: int = 600, +) -> list[RandomizedBenchMarkResult]: + if qubits is None: + device = sampler.processor.get_device() + qubits = tuple(sorted(list(device.metadata.qubit_set))) + + cliffords = _single_qubit_cliffords() + c1 = cliffords.c1_in_xy if use_xy_basis else cliffords.c1_in_xz + cfd_mats = np.array([_gate_seq_to_mats(gates) for gates in c1]) + + # create circuits + circuits = [] + for num_cfds in num_clifford_range: + for _ in range(num_circuits): + circuits.append(_create_rb_circuit(qubits, num_cfds, c1, cfd_mats)) + + # run circuits + results_all = sampler.run_batch(circuits, repetitions=repetitions) + gnd_probs = {q: [] for q in qubits} + idx = 0 + for num_cfds in num_clifford_range: + excited_probs_l = {q: [] for q in qubits} + for _ in range(num_circuits): + results = results_all[idx][0] + for qubit in qubits: + excited_probs_l[qubit].append( + np.mean(results.measurements['q{}_{}'.format(qubit.row, qubit.col)]) + ) + idx += 1 + for qubit in qubits: + gnd_probs[qubit].append(1.0 - np.mean(excited_probs_l[qubit])) + return {q: RandomizedBenchMarkResult(num_clifford_range, gnd_probs[q]) for q in qubits} + + +def compute_pauli_errors(rb_result: dict) -> dict: + exp_fit = lambda x, A, B, p: A * p**x + B + rb_errors = {} + d = 2 + for qubit in rb_result: + data = np.array(result[qubit].data) + x = data[:, 0] + y = data[:, 1] + fit = curve_fit(exp_fit, x, y) + p = fit[0][2] + pauli_error = (1.0 - 1.0 / (d * d)) * (1.0 - p) + rb_errors[qubit] = pauli_error + return rb_errors + + +def plot_error_rates(rb_result: dict, ax: plt.Axes | None = None) -> plt.Axes: + errors = compute_pauli_errors(rb_result) + heatmap = cirq.Heatmap(errors) + if ax is None: + _, ax = plt.subplots(figsize=(8, 8)) + _ = heatmap.plot(ax, vmin=0, vmax=0.01) + return ax + + +if __name__ == "__main__": + # The Google Cloud Project id to use. + project_id = "i-dont-have-experimental" # @param {type:"string"} + + from cirq_google.engine.qcs_notebook import get_qcs_objects_for_notebook + + # For real engine instances, delete 'virtual=True' below. + qcs_objects = get_qcs_objects_for_notebook(project_id) + + project_id = qcs_objects.project_id + engine = qcs_objects.engine + if not qcs_objects.signed_in: + print( + "ERROR: Please setup project_id in this cell or set the `GOOGLE_CLOUD_PROJECT` env var to your project id." + ) + print("Using noisy simulator instead.") + + processor_id = "bodega_sim_gmon18" # @param {type:"string"} + processor = engine.get_processor(processor_id) + + sampler = processor.get_sampler( + # run_name = + # device_config_name = + ) + start = datetime.datetime.now().timestamp() + result = single_qubit_randomized_benchmarking(sampler) + end = datetime.datetime.now().timestamp() + print(end - start) + + plot_error_rates(result) From d6c4237f66c08d7a7e0988219e0b88ff48b1fc52 Mon Sep 17 00:00:00 2001 From: Seneca Meeks Date: Mon, 18 Dec 2023 21:18:02 +0000 Subject: [PATCH 04/12] Revert "Created using Colaboratory" This reverts commit eb1031895990796c8b50b58e2aa7da7edccdb1fb. --- docs/tutorials/google/start.ipynb | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/docs/tutorials/google/start.ipynb b/docs/tutorials/google/start.ipynb index 0f910cd3bba..92ae54b7be4 100644 --- a/docs/tutorials/google/start.ipynb +++ b/docs/tutorials/google/start.ipynb @@ -175,25 +175,12 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": { "cellView": "both", - "id": "EQoTYZIEPa9S", - "outputId": "43c72568-3bc8-4b44-871b-b9db19c9e672", - "colab": { - "base_uri": "https://localhost:8080/" - } - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Circuit:\n", - "(0, 0): ───X───M('result')───\n" - ] - } - ], + "id": "EQoTYZIEPa9S" + }, + "outputs": [], "source": [ "# Define a qubit at an arbitrary grid location.\n", "qubit = cirq.GridQubit(0, 0)\n", From 5f8a3ca69a3011b6daf80ba7f394a7208065cb9c Mon Sep 17 00:00:00 2001 From: Seneca Meeks Date: Mon, 18 Dec 2023 21:18:32 +0000 Subject: [PATCH 05/12] Revert "call run_sweep_async in parallel" This reverts commit f4e0d88a18f487dd8a597a462eb4a0336c239476. --- cirq-google/cirq_google/engine/processor_sampler.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cirq-google/cirq_google/engine/processor_sampler.py b/cirq-google/cirq_google/engine/processor_sampler.py index 21410bbdc10..ecf18230d56 100644 --- a/cirq-google/cirq_google/engine/processor_sampler.py +++ b/cirq-google/cirq_google/engine/processor_sampler.py @@ -16,16 +16,11 @@ import cirq import duet -import concurrent.futures -from typing import TypeVar, Generic if TYPE_CHECKING: import cirq_google as cg -T = TypeVar("T") - - class ProcessorSampler(cirq.Sampler): """A wrapper around AbstractProcessor to implement the cirq.Sampler interface.""" @@ -81,7 +76,6 @@ async def run_batch_async( params_list: Optional[Sequence[cirq.Sweepable]] = None, repetitions: Union[int, Sequence[int]] = 1, ) -> Sequence[Sequence['cg.EngineResult']]: - print("outer") return cast( Sequence[Sequence['cg.EngineResult']], await super().run_batch_async(programs, params_list, repetitions), From ef9b3248fcc53ed8c21bf6863027daa53a4b469a Mon Sep 17 00:00:00 2001 From: Seneca Meeks Date: Mon, 18 Dec 2023 21:19:39 +0000 Subject: [PATCH 06/12] revert colab --- hello.py | 121 ------------------------------------------------------- 1 file changed, 121 deletions(-) delete mode 100644 hello.py diff --git a/hello.py b/hello.py deleted file mode 100644 index 1d933757e0a..00000000000 --- a/hello.py +++ /dev/null @@ -1,121 +0,0 @@ -import cirq -from typing import Sequence -import numpy as np -from cirq.experiments.qubit_characterizations import ( - RandomizedBenchMarkResult, - _random_single_q_clifford, - _single_qubit_cliffords, - _gate_seq_to_mats, -) -from scipy.optimize import curve_fit -import matplotlib.pyplot as plt -import datetime - - -def _create_rb_circuit( - qubits: tuple[cirq.GridQubit], num_cfds: int, c1: list, cfd_mats: np.array -) -> cirq.Circuit: - circuits_to_zip = [_random_single_q_clifford(qubit, num_cfds, c1, cfd_mats) for qubit in qubits] - circuit = cirq.Circuit.zip(*circuits_to_zip) - measure_moment = cirq.Moment( - cirq.measure_each(*qubits, key_func=lambda q: 'q{}_{}'.format(q.row, q.col)) - ) - circuit_with_meas = cirq.Circuit.from_moments(*(circuit.moments + [measure_moment])) - return circuit_with_meas - - -def single_qubit_randomized_benchmarking( - sampler: cirq.Sampler, - use_xy_basis: bool = True, - *, - qubits: tuple[cirq.GridQubit] | None = None, - num_clifford_range: Sequence[int] = [5, 18, 70, 265, 1000], - num_circuits: int = 10, - repetitions: int = 600, -) -> list[RandomizedBenchMarkResult]: - if qubits is None: - device = sampler.processor.get_device() - qubits = tuple(sorted(list(device.metadata.qubit_set))) - - cliffords = _single_qubit_cliffords() - c1 = cliffords.c1_in_xy if use_xy_basis else cliffords.c1_in_xz - cfd_mats = np.array([_gate_seq_to_mats(gates) for gates in c1]) - - # create circuits - circuits = [] - for num_cfds in num_clifford_range: - for _ in range(num_circuits): - circuits.append(_create_rb_circuit(qubits, num_cfds, c1, cfd_mats)) - - # run circuits - results_all = sampler.run_batch(circuits, repetitions=repetitions) - gnd_probs = {q: [] for q in qubits} - idx = 0 - for num_cfds in num_clifford_range: - excited_probs_l = {q: [] for q in qubits} - for _ in range(num_circuits): - results = results_all[idx][0] - for qubit in qubits: - excited_probs_l[qubit].append( - np.mean(results.measurements['q{}_{}'.format(qubit.row, qubit.col)]) - ) - idx += 1 - for qubit in qubits: - gnd_probs[qubit].append(1.0 - np.mean(excited_probs_l[qubit])) - return {q: RandomizedBenchMarkResult(num_clifford_range, gnd_probs[q]) for q in qubits} - - -def compute_pauli_errors(rb_result: dict) -> dict: - exp_fit = lambda x, A, B, p: A * p**x + B - rb_errors = {} - d = 2 - for qubit in rb_result: - data = np.array(result[qubit].data) - x = data[:, 0] - y = data[:, 1] - fit = curve_fit(exp_fit, x, y) - p = fit[0][2] - pauli_error = (1.0 - 1.0 / (d * d)) * (1.0 - p) - rb_errors[qubit] = pauli_error - return rb_errors - - -def plot_error_rates(rb_result: dict, ax: plt.Axes | None = None) -> plt.Axes: - errors = compute_pauli_errors(rb_result) - heatmap = cirq.Heatmap(errors) - if ax is None: - _, ax = plt.subplots(figsize=(8, 8)) - _ = heatmap.plot(ax, vmin=0, vmax=0.01) - return ax - - -if __name__ == "__main__": - # The Google Cloud Project id to use. - project_id = "i-dont-have-experimental" # @param {type:"string"} - - from cirq_google.engine.qcs_notebook import get_qcs_objects_for_notebook - - # For real engine instances, delete 'virtual=True' below. - qcs_objects = get_qcs_objects_for_notebook(project_id) - - project_id = qcs_objects.project_id - engine = qcs_objects.engine - if not qcs_objects.signed_in: - print( - "ERROR: Please setup project_id in this cell or set the `GOOGLE_CLOUD_PROJECT` env var to your project id." - ) - print("Using noisy simulator instead.") - - processor_id = "bodega_sim_gmon18" # @param {type:"string"} - processor = engine.get_processor(processor_id) - - sampler = processor.get_sampler( - # run_name = - # device_config_name = - ) - start = datetime.datetime.now().timestamp() - result = single_qubit_randomized_benchmarking(sampler) - end = datetime.datetime.now().timestamp() - print(end - start) - - plot_error_rates(result) From 71733921d026ec6e0a6892412049cc27d9997737 Mon Sep 17 00:00:00 2001 From: Seneca Meeks Date: Mon, 18 Dec 2023 21:24:48 +0000 Subject: [PATCH 07/12] lint --- cirq-core/cirq/work/sampler.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cirq-core/cirq/work/sampler.py b/cirq-core/cirq/work/sampler.py index ed13bd8cbdb..2ef087ee1c2 100644 --- a/cirq-core/cirq/work/sampler.py +++ b/cirq-core/cirq/work/sampler.py @@ -277,7 +277,6 @@ def run_batch( ValueError: If length of `programs` is not equal to the length of `params_list` or the length of `repetitions`. """ - print("um") params_list, repetitions = self._normalize_batch_args(programs, params_list, repetitions) return [ self.run_sweep(circuit, params=params, repetitions=repetitions) @@ -294,7 +293,6 @@ async def run_batch_async( See docs for `cirq.Sampler.run_batch`. """ - print("finally") params_list, repetitions = self._normalize_batch_args(programs, params_list, repetitions) results = [] for circuit, params, repetitions in zip(programs, params_list, repetitions): @@ -302,11 +300,6 @@ async def run_batch_async( return results - # return [ - # await self.run_sweep_async(circuit, params=params, repetitions=repetitions) - # for circuit, params, repetitions in zip(programs, params_list, repetitions) - # ] - def _normalize_batch_args( self, programs: Sequence['cirq.AbstractCircuit'], From 3c63bedff0b1f90ed797e1d865b5371728f2214f Mon Sep 17 00:00:00 2001 From: Seneca Meeks Date: Mon, 18 Dec 2023 21:40:37 +0000 Subject: [PATCH 08/12] use pmap --- cirq-core/cirq/work/sampler.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cirq-core/cirq/work/sampler.py b/cirq-core/cirq/work/sampler.py index 2ef087ee1c2..bd1e34e73ee 100644 --- a/cirq-core/cirq/work/sampler.py +++ b/cirq-core/cirq/work/sampler.py @@ -294,11 +294,9 @@ async def run_batch_async( See docs for `cirq.Sampler.run_batch`. """ params_list, repetitions = self._normalize_batch_args(programs, params_list, repetitions) - results = [] - for circuit, params, repetitions in zip(programs, params_list, repetitions): - results.append(duet.run(self.run_sweep_async, circuit, params, repetitions)) - - return results + return await duet.pstarmap_async( + self.run_sweep_async, zip(programs, params_list, repetitions) + ) def _normalize_batch_args( self, From 5d2e52de9bf47209cd2fe871f07c99290e6a3220 Mon Sep 17 00:00:00 2001 From: Seneca Meeks Date: Tue, 19 Dec 2023 17:52:34 +0000 Subject: [PATCH 09/12] add test --- cirq-core/cirq/work/sampler_test.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/cirq-core/cirq/work/sampler_test.py b/cirq-core/cirq/work/sampler_test.py index d3bc7af87fd..62a9774326c 100644 --- a/cirq-core/cirq/work/sampler_test.py +++ b/cirq-core/cirq/work/sampler_test.py @@ -211,6 +211,34 @@ def test_sampler_run_batch(): assert np.array_equal(result.measurements['m'], np.array([[0], [0]], dtype='uint8')) +@duet.sync +async def test_run_batch_async_impl(): + """Test run_batch_async calls run_sweep_async without waiting.""" + finished = [] + a = cirq.LineQubit(0) + circuit1 = cirq.Circuit(cirq.X(a) ** sympy.Symbol('t'), cirq.measure(a, key='m')) + circuit2 = cirq.Circuit(cirq.Y(a) ** sympy.Symbol('t'), cirq.measure(a, key='m')) + params1 = cirq.Points('t', [0.3, 0.7]) + params2 = cirq.Points('t', [0.4, 0.6]) + params_list = [params1, params2] + + class AsyncSampler(cirq.Sampler): + async def run_sweep_async(self, program, params, repetitions: int = 1): + if params == params1: + await duet.sleep(0.001) + + result = cirq.Simulator().run_sweep(program, params, repetitions) + finished.append(params) + + return result + + results = await AsyncSampler().run_batch_async( + [circuit1, circuit2], params_list=params_list, repetitions=[1, 2] + ) + assert len(results) == 2 + assert finished == list(reversed(params_list)) + + def test_sampler_run_batch_default_params_and_repetitions(): sampler = cirq.ZerosSampler() a = cirq.LineQubit(0) From 2f055f76db381a6d52d49dc477785e543ffa2a4a Mon Sep 17 00:00:00 2001 From: Seneca Meeks Date: Tue, 19 Dec 2023 17:56:06 +0000 Subject: [PATCH 10/12] lint --- cirq-core/cirq/work/sampler_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/work/sampler_test.py b/cirq-core/cirq/work/sampler_test.py index 62a9774326c..8ab126d30a9 100644 --- a/cirq-core/cirq/work/sampler_test.py +++ b/cirq-core/cirq/work/sampler_test.py @@ -235,7 +235,7 @@ async def run_sweep_async(self, program, params, repetitions: int = 1): results = await AsyncSampler().run_batch_async( [circuit1, circuit2], params_list=params_list, repetitions=[1, 2] ) - assert len(results) == 2 + assert finished == list(reversed(params_list)) From cee32411e632581d35d61cdc9392e20ce26b6f6e Mon Sep 17 00:00:00 2001 From: Seneca Meeks Date: Tue, 19 Dec 2023 18:14:57 +0000 Subject: [PATCH 11/12] lint --- cirq-core/cirq/work/sampler_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/work/sampler_test.py b/cirq-core/cirq/work/sampler_test.py index 8ab126d30a9..a26f996f074 100644 --- a/cirq-core/cirq/work/sampler_test.py +++ b/cirq-core/cirq/work/sampler_test.py @@ -232,7 +232,7 @@ async def run_sweep_async(self, program, params, repetitions: int = 1): return result - results = await AsyncSampler().run_batch_async( + await AsyncSampler().run_batch_async( [circuit1, circuit2], params_list=params_list, repetitions=[1, 2] ) From 78025e205fd1116bfe1ab303032d727fdffe43ee Mon Sep 17 00:00:00 2001 From: Seneca Meeks Date: Wed, 20 Dec 2023 15:40:22 +0000 Subject: [PATCH 12/12] nits --- cirq-core/cirq/work/sampler_test.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cirq-core/cirq/work/sampler_test.py b/cirq-core/cirq/work/sampler_test.py index a26f996f074..195b44c0ff2 100644 --- a/cirq-core/cirq/work/sampler_test.py +++ b/cirq-core/cirq/work/sampler_test.py @@ -212,7 +212,7 @@ def test_sampler_run_batch(): @duet.sync -async def test_run_batch_async_impl(): +async def test_run_batch_async_calls_run_sweep_asynchronously(): """Test run_batch_async calls run_sweep_async without waiting.""" finished = [] a = cirq.LineQubit(0) @@ -227,11 +227,8 @@ async def run_sweep_async(self, program, params, repetitions: int = 1): if params == params1: await duet.sleep(0.001) - result = cirq.Simulator().run_sweep(program, params, repetitions) finished.append(params) - return result - await AsyncSampler().run_batch_async( [circuit1, circuit2], params_list=params_list, repetitions=[1, 2] )