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