1# Quantum Engine API
2
3Google's Quantum Computing Service provides the Quantum Engine API to execute
4circuits on Google's quantum processor or simulator backends and
5to access or manage the jobs, programs, reservations and calibrations. As of Cirq is
6the only supported client for this API, using the `cirq.google.Engine` class.
7For other use cases (e.g. from a different language), contact
8[cirq-maintainers@googlegroups.com](mailto:cirq-maintainers@googlegroups.com)
9with a short proposal or submit an [RFC](../dev/rfc_process.md).
10
11Note: the Quantum Engine API is not yet open for public access.
12
13## Authenticating to Google Cloud
14
15Before you begin, you will need to create a Google Cloud project with the API
16enabled and billing enabled.  You will then to create credentials in order to
17access the API.
18
19You can create application default credentials from the command line using the
20gcloud client:
21
22`gcloud auth application-default login`
23
24From a colab, you can execute:
25
26```
27from google.colab import auth
28auth.authenticate_user(clear_output=False)
29```
30
31More information on creating application default credentials can be found on the
32[Google cloud](https://cloud.google.com/docs/authentication/production) website.
33
34## Engine class
35
36The `Engine` class is the entry point to communicate with the API.
37
38It can be initialized using your project id (found within your
39[Google Cloud Platform Console](https://console.cloud.google.com)).
40You can use this instance to run quantum circuits or sweeps (parameterized
41variants of a general circuit).
42
43<!---test_substitution
44# Add each circuit to the batch.*
45class MockEngine:\n  def run_batch(self, *args, **kwargs):\n    pass
46--->
47<!---test_substitution
48results = job.results.*
49results = None
50--->
51<!---test_substitution
52print.results.idx.*
53print()
54--->
55<!---test_substitution
56engine = cirq.google.Engine(.*)
57engine = MockEngine()
58--->
59<!---test_substitution
60cg.Engine(.*)
61cirq.Simulator()
62--->
63<!---test_substitution
64sampler = .*
65sampler = engine
66--->
67```python
68import cirq
69import cirq.google as cg
70
71# A simple sample circuit
72qubit = cirq.GridQubit(5, 2)
73circuit = cirq.Circuit(
74    cirq.X(qubit)**0.5,                 # Square root of NOT.
75    cirq.measure(qubit, key='result')   # Measurement.
76)
77
78# Create an Engine object.
79# Replace YOUR_PROJECT_ID with the id from your cloud project.
80engine = cg.Engine(project_id=YOUR_PROJECT_ID)
81
82# Create a sampler from the engine
83sampler = engine.sampler(processor_id='PROCESSOR_ID', gate_set=cg.SYC_GATESET)
84
85# This will run the circuit and return the results in a 'Result'
86results = sampler.run(circuit, repetitions=1000)
87
88# Sampler results can be accessed several ways
89
90# For instance, to see the histogram of results
91print(results.histogram(key='result'))
92
93# Or the data itself
94print(results.data)
95```
96
97## Device Specification
98
99Several public devices have been released and can be found in the `cirq.google`
100package.  These are documented further on the [Google Device](devices.md) page.
101
102However, you can also retrieve the device using the `get_device_specification` of an
103`Engine` object.  This is a [protocol buffer](https://developers.google.com/protocol-buffers)
104message that contains information about the qubits on the device, the
105connectivity, and the supported gates.
106
107This proto can be queried directly to get information about the device or can be transformed
108into a `cirq.Device` by using `cirq.google.SerializableDevice.from_proto()` that will
109enforce constraints imposed by the hardware.
110
111See the [Device Specification](specification.md) page for more information on
112device specifications.
113
114
115## Calibration Metrics
116
117Metrics from the current status of the device can be retrieved using the\
118`get_current_calibration` method of an `EngineProcessor` object.
119`EngineProcessor` objects can be retrieved from `Engine` using `get_processor`.
120This will return a Python dictionary where each key is the metric name.  The
121value of the dictionary will be the value of the metric, which can also be
122a dictionary.
123
124For example, the key may refer to a two-qubit gate error, and the value may
125be a dictionary from 2-tuples of `cirq.GridQubits` to an error rate represented
126as a float value.
127
128See the [Calibration Metrics](calibration.md) page for more information.
129
130## Running circuits in batch
131
132Circuits can be batched together for improved performance.  The engine object
133has a method `run_batch()` that functions similar to `run()` but accepts a
134list of circuits and parameter sweeps.  Each circuit must have a corresponding
135parameter sweep.  If the circuit does not use a sweep, pass in `None`.
136
137There are some restrictions on the circuits that can be batched together:
138
139*   **Same qubits**: All circuits in the same batch must measure the same
140set of qubits.
141*   **Same repetitions**: All circuits in the same batch must have the same
142number of repetitions.
143
144Batching circuits together that do not follow these restrictions may not
145cause an error, but your performance will not be significantly improved.
146
147Results can be retrieved in two different forms:
148
149*    `EngineJob.results()` will return a single `List` object,
150with all the sweeps of the first circuit in the batch
151followed by all the sweeps in the second circuit, and so on.
152*     EngineJob.batched_results()` will return a `List` of `List`s.
153The first index will refer to the circuit run, and the second index
154will refer to the sweep result in that circuit.
155
156If the circuits are not parameterized, there will only be one `Result`
157per circuit using either variant.
158
159The following code shows an example of batching together parameterized
160circuits, each of which is a sweep.
161
162```python
163import sympy
164import cirq
165
166q = cirq.GridQubit(5, 2)
167
168# Create a list of example circuits
169circuit_list = []
170param_list = []
171
172# Create a list of 5 circuits with 10 sweeps each
173num_circuits_in_batch = 5
174num_sweeps_in_circuit = 10
175
176# Add each circuit to the batch
177for circuit_num in range(num_circuits_in_batch):
178  # Example circuit
179  circuit = cirq.Circuit(
180      cirq.YPowGate(exponent=circuit_num / 10.0)(q),
181      cirq.XPowGate(exponent=sympy.Symbol('t'))(q),
182      cirq.measure(q, key='m', invert_mask=(True,)))
183  # add a sweep for each circuit
184  param_sweep = cirq.Linspace('t', start=0, stop=1, length=num_sweeps_in_circuit)
185  # Add the circuit/sweep pair to the list
186  circuit_list.append(circuit)
187  param_list.append(param_sweep)
188
189# Create an Engine object.
190# Replace YOUR_PROJECT_ID with the id from your cloud project.
191engine = cirq.google.Engine(project_id='YOUR_PROJECT_ID')
192
193# Create a sampler from the engine
194job = engine.run_batch(circuit_list,
195                       processor_ids=['PROCESSOR_ID'],
196                       gate_set=cirq.google.FSIM_GATESET,
197                       repetitions=1000,
198                       params_list=param_list)
199results = job.results()
200
201# The results will be flattened into one list
202# You will need to iterate through each circuit and each sweep value
203idx = 0
204for b in range(num_circuits_in_batch):
205  for s in range(num_sweeps_in_circuit):
206     print(f'Batch #{b}, Sweep #{s}')
207     print(results[idx].histogram(key='m'))
208     idx+=1
209
210# Alternative way of getting results.
211# Results will be nested in Lists
212batch_results = job.batched_results()
213for batch_idx, batch in enumerate(batch_results):
214  for sweep_idx, result in enumerate(batch):
215     print(f'Batch #{batch_idx}, Sweep #{sweep_idx}')
216     print(result.histogram(key='m'))
217```
218
219## Downloading historical results
220
221Results from  previous computations are archived and can be downloaded later
222by those in the same cloud project.  You must use the same project id to
223access historical results or your request will be denied.
224
225Each time that you run a circuit or sweep, the `Engine` class will generate
226a program id and job id for you.  (You can also specify the program and job id
227yourself when running the program).  Both the program and job id will need to be
228unique within the project.  In order to retrieve previous results,
229you will need both this program id as well as the job id.
230If these were generated by the `Engine`, they can be retrieved from the
231job object when you run a sweep.
232Currently, getting the program and job ids can only be done through the
233`Engine` interface and not through the sampler interface.
234You can then use `get_program` and `get_job` to retrieve the results.
235See below for an example:
236
237```python
238# Initialize the engine object
239engine = cirq.google.Engine(project_id='YOUR_PROJECT_ID')
240
241# Create an example circuit
242qubit = cirq.GridQubit(5, 2)
243circuit = cirq.Circuit(
244    cirq.X(qubit)**sympy.Symbol('t'),
245    cirq.measure(qubit, key='result')
246)
247param_sweep = cirq.Linspace('t', start=0, stop=1, length=10)
248
249# Run the circuit
250job = e.run_sweep(program=circuit,
251                  params=param_sweep,
252                  repetitions=1000,
253                  processor_ids=[PROCESSOR_ID],
254                  gate_set=GATE_SET)
255
256# Save the program and jo id for later
257program_id = job.program_id
258job_id = job.job_id
259
260# Retrieve the results
261results = job.results()
262
263# ...
264# Some time later, the results can be retrieved
265# ...
266
267# Recreate the job object
268historical_job = engine.get_program(program_id=program_id).get_job(job_id=job_id)
269
270# Retrieve the results
271historical_results = historical_job.results()
272
273```
274
275If you did not save the ids, you can still find them from your
276job using the [Cloud Console](https://console.cloud.google.com/quantum/jobs) or
277by using our list methods.
278
279
280### Listing jobs
281
282To list the executions of your circuit, i.e. the jobs, you can use `cirq.google.Engine.list_jobs()`.
283You can search in all the jobs within your project using filtering criteria on creation time, execution state and labels.
284
285```python
286from cirq.google.engine.client.quantum import enums
287
288# Initialize the engine object
289engine = cirq.google.Engine(project_id='YOUR_PROJECT_ID')
290
291# List all the jobs on the project since 2020/09/20 that succeeded:
292jobs = engine.list_jobs(created_after=datetime.date(2020,9,20),
293                        execution_states=[enums.ExecutionStatus.State.SUCCESS])
294for j in jobs:
295   print(j.job_id, j.status(), j.create_time())
296```
297
298### Listing programs
299
300To list the different instances of your circuits uploaded, i.e. the programs, you can use `cirq.google.Engine.list_programs()`.
301Similar to jobs, filtering makes it possible to list programs by creation time and labels.
302With an existing `cirq.google.EngineProgram` object, you can list any jobs that were run using that program.
303
304```python
305from cirq.google.engine.client.quantum import enums
306
307# Initialize the engine object
308engine = cirq.google.Engine(project_id='YOUR_PROJECT_ID')
309
310# List all the programs on the project since 2020/09/20 that have
311# the "variational" label with any value and the "experiment" label
312# with value "vqe001":
313programs = engine.list_programs(
314                created_after=datetime.date(2020,9,20),
315                has_labels={"variational":"*", "experiment":"vqe001"}
316           )
317for p in programs:
318   print(p.program_id, p.create_time())
319   # the same filtering parametrization is available as in engine.list_jobs()
320   # for example here we list the jobs under the programs that failed
321   for j in p.list_jobs(execution_states=[enums.ExecutionStatus.State.FAILURE]):
322     print(j.job_id, j.status())
323```
324
325