Transforming circuits

View on QuantumAI Run in Google Colab View source on GitHub Download notebook
try:
    import cirq
except ImportError:
    print("installing cirq...")
    !pip install --quiet cirq
    print("installed cirq.")

Circuit optimizers

Cirq comes with the concept of an optimizer. Optimizers will pass over a circuit and perform tasks that will modify the circuit in place. These can be used to transform a circuit in specific ways, such as combining single-qubit gates, commuting Z gates through the circuit, or readying the circuit for certain hardware or gate set configurations.

Optimizers will have a function optimize_circuit() that can be used to perform this optimization. Here is a simple example that removes empty moments:

import cirq
c=cirq.Circuit()
c.append(cirq.Moment([]))
c.append(cirq.Moment([cirq.X(cirq.GridQubit(1,1))]))
c.append(cirq.Moment([]))
print(f'Before optimization, Circuit has {len(c)} moments')

cirq.DropEmptyMoments().optimize_circuit(circuit=c)
print(f'After optimization, Circuit has {len(c)} moments')
Before optimization, Circuit has 3 moments
After optimization, Circuit has 1 moments

Optimizers that come with cirq can be found in the cirq.optimizers package.

A few notable examples are:

  • ConvertToCzAndSingleGates: Attempts to convert a circuit into CZ gates and single qubit gates. This uses gate's unitary and decompose methods to transform them into CZ + single qubit gates.
  • DropEmptyMoments / DropNegligible: Removes moments that are empty or have very small effects, respectively.
  • EjectPhasedPaulis: Pushes X, Y, and PhasedX gates towards the end of the circuit, potentially absorbing Z gates and modifying gates along the way.
  • EjectZ: Pushes Z gates towards the end of the circuit, potentially adjusting phases of gates that they pass through.
  • ExpandComposite: Uses cirq.decompose to expand composite gates.
  • MergeInteractions: Combines series of adjacent one and two-qubit gates acting on a pair of qubits.
  • MergeSingleQubitGates: Combines series of adjacent unitary 1-qubit operations
  • SynchronizeTerminalMeasurements: Moves all measurements in a circuit to the final moment if possible.

Create your own optimizers

You can create your own optimizers to transform and modify circuits to fit hardware, gate sets, or other requirements. Optimizers can also be used to generate noise. See noise for details.

You can do this by implementing the function optimize_circuit.

If your optimizer is a local optimizer and depends primarily on operator being examined, you can alternatively inherit cirq.PointOptimizer and implement the function optimization_at(self, circuit, index, op) that optimizes a single operation.

Below is an example of implementing a simple PointOptimizer that removes measurements.

class RemoveMeasurements(cirq.PointOptimizer):
    def optimization_at(self, circuit: cirq.Circuit, index: int, op: cirq.Operation):
        if isinstance(op.gate, cirq.MeasurementGate):
            return cirq.PointOptimizationSummary(clear_span=1,
                                                 new_operations=[],
                                                 clear_qubits=op.qubits)
        else:
            return None

q=cirq.LineQubit(0)
c=cirq.Circuit(cirq.X(q), cirq.measure(q))
print('Before optimization')
print(c)
RemoveMeasurements().optimize_circuit(c)
print('After optimization')
print(c)
Before optimization
0: ───X───M───
After optimization
0: ───X───────