Skip to content

Commit

Permalink
Add documentation for Engine (quantumlib#2663)
Browse files Browse the repository at this point in the history
* Add documentation for Engine

- Adds a stub page for Engine documentation.
- Includes information about device specification and calibration
metrics.
- Adding more information on device specification.
  • Loading branch information
dstrain115 authored Feb 6, 2020
1 parent 4b91b2c commit 8a0339b
Show file tree
Hide file tree
Showing 4 changed files with 320 additions and 0 deletions.
81 changes: 81 additions & 0 deletions docs/google/devices.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,77 @@ using a error rate of 0.5% per gate, a circuit of depth 20 and width 20 could be
at 0.995^(20*20) = 0.135. Using separate error rates per gates (i.e. based on calibration
metrics) or a more complicated noise model can result in more accurate error estimation.

### Use sweeps when possible

Round trip network time to and from the engine typically adds latency on the order of a second
to the overall computation time. Reducing the number of trips and allowing the engine to
properly batch circuits can improve the throughput of your calculations. One way to do this
is to use parameter sweeps to send multiple variations of a circuit at once.

One example is to turn single-qubit gates on or off by using parameter sweeps.
For instance, the following code illustrates how to combine measuring in the
Z basis or the X basis in one circuit.

```python
import cirq
import sympy
q = cirq.GridQubit(1, 1)
sampler = cirq.Simulator()

# STRATEGY #1: Have a separate circuit and sample call for each basis.
circuit_z = cirq.Circuit(
cirq.measure(q, key='out'))
circuit_x = cirq.Circuit(
cirq.H(q),
cirq.measure(q, key='out'))
samples_z = sampler.sample(circuit_z, repetitions=5)
samples_x = sampler.sample(circuit_x, repetitions=5)

print(samples_z)
# prints
# out
# 0 0
# 1 0
# 2 0
# 3 0
# 4 0

print(samples_x)
# prints something like:
# out
# 0 0
# 1 1
# 2 1
# 3 0
# 4 0

# STRATEGY #2: Have a parameterized circuit.
circuit_sweep = cirq.Circuit(
cirq.H(q)**sympy.Symbol('t'),
cirq.measure(q, key='out'))

samples_sweep = sampler.sample(circuit_sweep,
repetitions=5,
params=[{'t': 0}, {'t': 1}])
print(samples_sweep)
# prints something like:
# t out
# 0 0 0
# 1 0 0
# 2 0 0
# 3 0 0
# 4 0 0
# 0 1 0
# 1 1 1
# 2 1 1
# 3 1 0
# 4 1 1
```

One word of caution is there is a limit to the total number of repetitions. Take some care
that your parameter sweeps, especially products of sweeps, do not become so excessively large
that they overcome this limit.


### Keep qubits busy

Expand All @@ -105,6 +176,16 @@ of the circuit by pushing them back to the next non-commuting operator. If the r
circuit still contains Z operations, they should be aggregated into their own moment,
if possible.

### Use caution with symbols

Symbols are extremely useful for constructing parameterized circuits (see above). However,
only some sympy formulas can be serialized for network transport to the engine.
Currently, sums and products of symbols, including linear combinations, are supported.
See `cirq.google.arg_func_langs` for details.

The sympy library is also infamous for being slow, so avoid using complicated formulas if you
care about performance. Avoid using parameter resolvers that have formulas in them.

## Specific Device Layouts

### Sycamore
Expand Down
107 changes: 107 additions & 0 deletions docs/google/engine.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Quantum Engine

The Quantum Engine, via the `cirq.google.Engine` class, executes programs and jobs using the
Quantum Engine API.

Note that the Quantum Engine API is not yet open for public access.

## Authenticating to Google Cloud

Before you begin, you will need to create a Google Cloud project with the API
enabled and billing enabled. You will then to create credentials in order to
access the API.

You can create application default credentials from the command line using the
gcloud client:

`gcloud auth application-default login`

From a colab, you can execute:

```
from google.colab import auth
auth.authenticate_user(clear_output=False)
```

More information on creating application default credentials can be found on the
[Google cloud](https://cloud.google.com/docs/authentication/production) website.

## Engine class

The `Engine` class is the entry point to communicate with the API.

It can be initialized using your project id (found within your
[Google Cloud Platform Console](https://console.cloud.google.com)).
You can use this instance to run quantum circuits or sweeps (parameterized
variants of a general circuit).

<!---test_substitution
cg.Engine(.*)
cirq.Simulator()
--->
<!---test_substitution
sampler = .*
sampler = engine
--->
```python
import cirq
import cirq.google as cg
import random
import string

# A simple sample circuit
qubit = cirq.GridQubit(5, 2)
circuit = cirq.Circuit(
cirq.X(qubit)**0.5, # Square root of NOT.
cirq.measure(qubit, key='result') # Measurement.
)

# Create an Engine object to use.
# Replace YOUR_PROJECT_ID with the id from your cloud project.
engine = cg.Engine(project_id=YOUR_PROJECT_ID, proto_version=cg.ProtoVersion.V2)

# Create a sampler from the engine
sampler = engine.sampler(processor_id='PROCESSOR_ID', gate_set=cg.SYC_GATESET)

# This will run the circuit and return the results in a 'TrialResult'
results = sampler.run(circuit, repetitions=1000)

# Sampler results can be accessed several ways

# For instance, to see the histogram of results
print(results.histogram(key='result'))

# Or the data itself
print(results.data)
```

## Device Specification

Several public devices have been released and can be found in the `cirq.google`
package. These are documented further on the [Google Device](devices.md) page.

However, you can also retrieve the device using the `get_device_specification` of an
`Engine` object. This is a [protocol buffer](https://developers.google.com/protocol-buffers)
message that contains information about the qubits on the device, the
connectivity, and the supported gates.

This proto can be queried directly to get information about the device or can be transformed
into a `cirq.Device` by using `cirq.google.SerializableDevice.from_proto()` that will
enforce constraints imposed by the hardware.

See the [Device Specification](specification.md) page for more information on
device specifications.


## Calibration Metrics

Metrics from the current status of the device can be retrieved using the\
`get_latest_calibration` method of the `Engine` object. This will return a
Python dictionary where each key is the metric name. The value of the
dictionary will be the value of the metric, which can also be a dictionary.

For example, the key may refer to a two-qubit gate error, and the value may
be a dictionary from 2-tuples of `cirq.GridQubits` to an error rate represented
as a float value.

Information about specific metrics will be released at a later date.
2 changes: 2 additions & 0 deletions docs/google/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ of devices that have become public information.
:maxdepth: 1

devices
engine
specification
130 changes: 130 additions & 0 deletions docs/google/specification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Device Specifications

The device specification proto defines basic layout of a device as well as the
gate set and serialized ids that can be used. This specification can be used
to find out specific characteristics of though.

Though several standard [Google devices](devices.md) are defined for your
convenience, specific devices may have specialized layouts particular to that
processor. For instance, there may be one or more qubit "drop-outs" that are
non-functional for whatever reason. There could also be new or experimental
features enabled on some devices but not on others.

This specification is defined in the Device proto within `cirq.google.api.v2`.

## Gate Set Specifications

Most devices can only accept a limited set of gates. This is known as the
gate set of the device. Any circuits sent to this device must only use gates
within this set. The gate set portion of the protocol buffer defines which
gate set(s) are valid on the device, and which gates make up that set.

### Gate Definitions

Each gate in the gate set will have a definition that defines the id that
the gate is serialized as, the number of qubits for the gates, the arguments
to the gate, the duration, and which qubits it can be applied to.

This definition uses "target sets" to specify which qubits the operation can
be applied to. See the section below for more information.

### Gate Durations

The time it takes the device to perform each gate is stored within the device
specification. This time is stored as an integer number of picoseconds.

Example code to print out the gate durations for every gate supported by the
device is shown below:

```
import cirq
# Create an Engine object to use.
engine = cirq.google.Engine(project_id='your_project_id',
proto_version=cirq.google.ProtoVersion.V2)
# Replace the processor id to get the device specification with that id.
spec = engine.get_device_specification('processor_id')
# Iterate through each gate set valid on the device.
for gateset in spec.valid_gate_sets:
print(gateset.name)
print('-------')
# Prints each gate valid in the set with its duration
for gate in gateset.valid_gates:
print('%s %d' % (gate.id, gate.gate_duration_picos))
```

Note that, by convention, measurement gate duration includes both the duration
of "read-out" pulses to measure the qubit as well as the "ring-down" time that
it takes the measurement resonator to reset to a ground state.

## Target Sets

Generally, most gates apply to the same set of qubits. To avoid repeating
these qubits (or pairs of qubits) for each gate, each gate instead uses a
target set to define the set of qubits that are valid.

Each target set contains a list of valid targets. A target is a list of qubits.
For one-qubit gates, a target is simply a single qubit. For two qubit gates,
a target is a pair of qubits.

The type of a target set defines how the targets are interpreted. If the
target set is set to SYMMETRIC, the order of each target does not matter (e.g.
if `gate.on(q1, q2)` is valid, then so is `gate.on(q2, q1)`). If the target
type is set to ASSYMMETRIC, then the order of qubits does matter, and other
orderings of the qubits that are not specified in the definition cannot be
assumed to be valid.

The last type is PERMUTATION_SET. This type specified that any permutation of
the targets is valid. This is typically used for measurement gates. If `q0`,
`q1` and `q2` are all specified as valid targets for a permutation set of the
gate, then `gate.on(q0)`, `gate.on(q1)`, `gate.on(q2)`, `gate.on(q0, q1)`,
`gate.on(q0, q2)`, `gate.on(q1, q2)` and `gate.on(q0, q1, q2)` are all valid
uses of the gate.

### Developer Recommendations

This is a free form text field for additional recommendations and soft
requirements that should be followed for proper operation of the device that
are not captured by the hard requirements above.

For instance, "Do not apply two CZ gates in a row."

## Serializable Devices

The `cirq.google.SerializableDevice` class allows someone to take this
device specification and turn it into a `cirq.Device` that can be used to
verify a circuit.

The `cirq.google.SerializableDevice` combines a `DeviceSpecification` protocol
buffer (defining the device) with a `SerializableGateSet` (that defines the
translation from serialized id to cirq) to produce a `cirq.Device` that can
be used to validate a circuit.

The following example illustrates retrieving the device specification live
from the engine and then using it to validate a circuit.

```
import cirq
import cirq.google as cg
# Create an Engine object to use.
engine = cg.Engine(project_id='your_project_id',
proto_version=cirq.google.ProtoVersion.V2)
# Replace the processor id to get the device specification with that id.
spec = engine.get_device_specification('processor_id')
device = cg.SerializableDevice.from_proto(
proto=spec,
gate_sets=[cg.gate_sets.SQRT_ISWAP_GATESET)
q0, q1 = cirq.LineQubit.range(2)
# Raises a ValueError, since this is not a supported gate.
cirq.Circuit(cirq.CZ(q0,q1), device=device)
```

Note that, if network traffic is undesired, the `DeviceSpecification` can
easily be stored in either binary format or TextProto format for later usage.

0 comments on commit 8a0339b

Please sign in to comment.