Circuit Transformers

View on QuantumAI Run in Google Colab View source on GitHub Download notebook

Setup

try:
    import cirq
except ImportError:
    print("installing cirq...")
    !pip install --quiet cirq
    import cirq

    print("installed cirq.")

What is a Transformer?

A transformer in Cirq is any callable that satisfies the cirq.TRANSFORMER API, and transforms an input circuit into an output circuit.

Circuit transformations are often necessary to compile a user-defined circuit to an equivalent circuit that satisfies the constraints necessary to be executable on a specific device or simulator. The compilation process often involves steps like:

  • Gate Decompositions: Rewrite the circuit using only gates that belong to the device target gateset, i.e. set of gates which the device can execute.
  • Qubit Mapping and Routing: Map the logic qubits in the input circuit to physical qubits on the device and insert appropriate swap operations such that final circuit respects the hardware topology.
  • Circuit Optimizations: Perform hardware specific optimizations, like merging and replacing connected components of 1 and 2 operations with more efficient rewrite operations, commuting Z gates through the circuit, aligning gates in moments and more.

Cirq provides many out-of-the-box transformers which can be used as individual compilation passes. It also supplies a general framework for users to create their own transformers, by using powerful primitives and by bundling existing transformers together, to enable the compilation of circuits for specific targets. This page covers the available transformers in Cirq, how to use them, and how to write a simple transformer. The Custom Transformers page presents the details on creating more complex custom transformers through primitives and composition.

Built-in Transformers in Cirq

Overview

Transformers that come with cirq can be found in the /cirq/transformers package.

A few notable examples are:

Below you can see how to implement a transformer pipeline as a function called optimize_circuit, which composes a few of the available Cirq transformers.

def optimize_circuit(circuit, context=None, k=2):
    # Merge 2-qubit connected components into circuit operations.
    optimized_circuit = cirq.merge_k_qubit_unitaries(
        circuit, k=k, rewriter=lambda op: op.with_tags("merged"), context=context
    )

    # Drop operations with negligible effect / close to identity.
    optimized_circuit = cirq.drop_negligible_operations(optimized_circuit, context=context)

    # Expand all remaining merged connected components.
    optimized_circuit = cirq.expand_composite(
        optimized_circuit, no_decomp=lambda op: "merged" not in op.tags, context=context
    )

    # Synchronize terminal measurements to be in the same moment.
    optimized_circuit = cirq.synchronize_terminal_measurements(optimized_circuit, context=context)

    # Assert the original and optimized circuit are equivalent.
    cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent(
        circuit, optimized_circuit
    )

    return optimized_circuit


q = cirq.LineQubit.range(3)
circuit = cirq.Circuit(
    cirq.H(q[1]),
    cirq.CNOT(*q[1:]),
    cirq.H(q[0]),
    cirq.CNOT(*q[:2]),
    cirq.H(q[1]),
    cirq.CZ(*q[:2]),
    cirq.H.on_each(*q[:2]),
    cirq.CNOT(q[2], q[0]),
    cirq.measure_each(*q),
)
print("Original Circuit:", circuit, sep="\n")
print("Optimized Circuit:", optimize_circuit(circuit), sep="\n")
Original Circuit:
                              ┌──┐
0: ───H───────@───────@───H────X─────M───
              │       │        │
1: ───H───@───X───H───@───H────┼M────────
          │                    │
2: ───────X────────────────────@─────M───
                              └──┘
Optimized Circuit:
0: ───────────X───M───
              │
1: ───H───@───┼───M───
          │   │
2: ───────X───@───M───

Inspecting transformer actions

Every transformer in Cirq accepts a cirq.TransformerContext instance, which stores common configurable options useful for all transformers.

One of the members of transformer context dataclass is cirq.TransformerLogger instance. When a logger instance is specified, every cirq transformer logs its action on the input circuit using the given logger instance. The logs can then be inspected to understand the action of each individual transformer on the circuit.

Below, you can inspect the action of each transformer in the optimize_circuit method defined above.

context = cirq.TransformerContext(logger=cirq.TransformerLogger())
optimized_circuit = optimize_circuit(circuit, context)
context.logger.show()
Transformer-1: merge_k_qubit_unitaries
Initial Circuit:
                              ┌──┐
0: ───H───────@───────@───H────X─────M───
              │       │        │
1: ───H───@───X───H───@───H────┼M────────
          │                    │
2: ───────X────────────────────@─────M───
                              └──┘


Final Circuit:
                                                                                ┌─────────────────────────┐
                                     [ 0: ───H───@───────@───H─── ]               [ 0: ───X─── ]
0: ──────────────────────────────────[           │       │        ]───────────────[       │    ]──────────────M───
                                     [ 1: ───────X───H───@───H─── ]['merged']     [ 2: ───@─── ]['merged']
                                     │                                            │
      [ 1: ───H───@─── ]             │                                            │
1: ───[           │    ]─────────────#2──────────────────────────────────────────M┼───────────────────────────────
      [ 2: ───────X─── ]['merged']                                                │
      │                                                                           │
2: ───#2──────────────────────────────────────────────────────────────────────────#2──────────────────────────M───
                                                                                └─────────────────────────┘
----------------------------------------
Transformer-2: drop_negligible_operations
Initial Circuit:
                                                                                ┌─────────────────────────┐
                                     [ 0: ───H───@───────@───H─── ]               [ 0: ───X─── ]
0: ──────────────────────────────────[           │       │        ]───────────────[       │    ]──────────────M───
                                     [ 1: ───────X───H───@───H─── ]['merged']     [ 2: ───@─── ]['merged']
                                     │                                            │
      [ 1: ───H───@─── ]             │                                            │
1: ───[           │    ]─────────────#2──────────────────────────────────────────M┼───────────────────────────────
      [ 2: ───────X─── ]['merged']                                                │
      │                                                                           │
2: ───#2──────────────────────────────────────────────────────────────────────────#2──────────────────────────M───
                                                                                └─────────────────────────┘


Final Circuit:
                                         ┌─────────────────────────┐
                                           [ 0: ───X─── ]
0: ────────────────────────────────────────[       │    ]──────────────M───
                                           [ 2: ───@─── ]['merged']
                                           │
      [ 1: ───H───@─── ]                   │
1: ───[           │    ]──────────────────M┼───────────────────────────────
      [ 2: ───────X─── ]['merged']         │
      │                                    │
2: ───#2───────────────────────────────────#2──────────────────────────M───
                                         └─────────────────────────┘
----------------------------------------
Transformer-3: expand_composite
Initial Circuit:
                                         ┌─────────────────────────┐
                                           [ 0: ───X─── ]
0: ────────────────────────────────────────[       │    ]──────────────M───
                                           [ 2: ───@─── ]['merged']
                                           │
      [ 1: ───H───@─── ]                   │
1: ───[           │    ]──────────────────M┼───────────────────────────────
      [ 2: ───────X─── ]['merged']         │
      │                                    │
2: ───#2───────────────────────────────────#2──────────────────────────M───
                                         └─────────────────────────┘


Final Circuit:
              ┌──┐
0: ─────────────X────M───
                │
1: ───H───@────M┼────────
          │     │
2: ───────X─────@────M───
              └──┘
----------------------------------------
Transformer-4: synchronize_terminal_measurements
Initial Circuit:
              ┌──┐
0: ─────────────X────M───
                │
1: ───H───@────M┼────────
          │     │
2: ───────X─────@────M───
              └──┘


Final Circuit:
0: ───────────X───M───
              │
1: ───H───@───┼───M───
          │   │
2: ───────X───@───M───
----------------------------------------

By first using cirq.merge_k_qubit_unitaries to turn connected components of the circuit into cirq.CircuitOperations, cirq.drop_negligible_operations was able to identify that one of the merged connected components was equivalent to the identity operation and remove it. The remaining steps returned the circuit to a more typical state, expanding intermediate cirq.CircuitOperations and aligning measurements to be terminal measurements.

Support for no-compile tags

Cirq also supports tagging operations with no-compile tags such that these tagged operations are ignored when applying transformations on the circuit. This allows users to gain more fine-grained conrol over the compilation process.

Any valid tag can be used as a "no-compile" tag by adding it to the tags_to_ignore field in cirq.TransformerContext. When called with a context, cirq transformers will inspect the context.tags_to_ignore field and ignore an operation if op.tags & context.tags_to_ignore is not empty.

Below, you can use no-compile tags when transforming a circuit using the optimize_circuit mehod defined above.

# Echo pulses inserted in the circuit to prevent dephasing during idling should be ignored.
circuit = cirq.Circuit(
    cirq.H(q[0]),
    cirq.CNOT(*q[:2]),
    [
        op.with_tags("spin_echoes") for op in [cirq.X(q[0]) ** 0.5, cirq.X(q[0]) ** -0.5]
    ],  # the echo pulses
    [cirq.CNOT(*q[1:]), cirq.CNOT(*q[1:])],
    [cirq.CNOT(*q[:2]), cirq.H(q[0])],
    cirq.measure_each(*q),
)
# Original Circuit
print("Original Circuit:", circuit, "\n", sep="\n")

# Optimized Circuit without tags_to_ignore
print("Optimized Circuit without specifying tags_to_ignore:")
print(optimize_circuit(circuit, k=1), "\n")

# Optimized Circuit ignoring operations marked with tags_to_ignore.
print("Optimized Circuit while ignoring operations marked with tags_to_ignore:")
context = cirq.TransformerContext(tags_to_ignore=["spin_echoes"])
print(optimize_circuit(circuit, k=1, context=context), "\n")
Original Circuit:
0: ───H───@───X['spin_echoes']^0.5───X['spin_echoes']^-0.5───@───H───M───
          │                                                  │
1: ───────X───@──────────────────────@───────────────────────X───M───────
              │                      │
2: ───────────X──────────────────────X───────────────────────M───────────


Optimized Circuit without specifying tags_to_ignore:
0: ───H───@───────────@───H───M───
          │           │
1: ───────X───@───@───X───────M───
              │   │
2: ───────────X───X───────────M─── 

Optimized Circuit while ignoring operations marked with tags_to_ignore:
0: ───H───@───X['spin_echoes']^0.5───X['spin_echoes']^-0.5───@───H───M───
          │                                                  │
1: ───────X───@──────────────────────@───────────────────────X───────M───
              │                      │
2: ───────────X──────────────────────X───────────────────────────────M───

Support for recursively transforming sub-circuits

By default, an operation op of type cirq.CircuitOperation is considered as a single top-level operation by cirq transformers. As a result, the sub-circuits wrapped inside circuit operations will often be left as it is and a transformer will only modify the top-level circuit.

If you wish to recursively run a transformer on every nested sub-circuit wrapped inside a cirq.CircuitOperation, you can set context.deep=True in the cirq.TransformerContext object. Note that tagged circuit operations marked with any of context.tags_to_ignore will be ignored even if context.deep is True. See the example below for a better understanding.

q = cirq.LineQubit.range(2)
circuit_op = cirq.CircuitOperation(
    cirq.FrozenCircuit(cirq.I.on_each(*q), cirq.CNOT(*q), cirq.I(q[0]).with_tags("ignore"))
)
circuit = cirq.Circuit(
    cirq.I(q[0]), cirq.I(q[1]).with_tags("ignore"), circuit_op, circuit_op.with_tags("ignore")
)
print("Original Circuit:", circuit, "\n", sep="\n\n")

context = cirq.TransformerContext(tags_to_ignore=["ignore"], deep=False)
print("Optimized Circuit with deep=False and tags_to_ignore=['ignore']:\n")
print(cirq.drop_negligible_operations(circuit, context=context), "\n\n")

context = cirq.TransformerContext(tags_to_ignore=["ignore"], deep=True)
print("Optimized Circuit with deep=True and tags_to_ignore=['ignore']:\n")
print(cirq.drop_negligible_operations(circuit, context=context), "\n")
Original Circuit:

                    [ 0: ───I───@───I['ignore']─── ]   [ 0: ───I───@───I['ignore']─── ]
0: ───I─────────────[           │                  ]───[           │                  ]─────────────
                    [ 1: ───I───X───────────────── ]   [ 1: ───I───X───────────────── ]['ignore']
                    │                                  │
1: ───I['ignore']───#2─────────────────────────────────#2───────────────────────────────────────────



Optimized Circuit with deep=False and tags_to_ignore=['ignore']:

                    [ 0: ───I───@───I['ignore']─── ]   [ 0: ───I───@───I['ignore']─── ]
0: ─────────────────[           │                  ]───[           │                  ]─────────────
                    [ 1: ───I───X───────────────── ]   [ 1: ───I───X───────────────── ]['ignore']
                    │                                  │
1: ───I['ignore']───#2─────────────────────────────────#2─────────────────────────────────────────── 


Optimized Circuit with deep=True and tags_to_ignore=['ignore']:

                    [ 0: ───────@───I['ignore']─── ]   [ 0: ───I───@───I['ignore']─── ]
0: ─────────────────[           │                  ]───[           │                  ]─────────────
                    [ 1: ───────X───────────────── ]   [ 1: ───I───X───────────────── ]['ignore']
                    │                                  │
1: ───I['ignore']───#2─────────────────────────────────#2───────────────────────────────────────────

The leading identity gate that wasn't tagged was removed from both optimized circuits, but the identity gates within each cirq.CircuitOperation were removed if deep = true and the CircuitOperation wasn't tagged and the identity operation wasn't tagged.

Compiling to NISQ targets: cirq.CompilationTargetGateset

Cirq's philosophy on compiling circuits for execution on a NISQ target device or simulator is that it would often require running only a handful of individual compilation passes on the input circuit, one after the other.

cirq.CompilationTargetGateset is an abstraction in Cirq to represent such compilation targets as well as the bundles of transformer passes which should be executed to compile a circuit to this target. Cirq has implementations for common target gatesets like cirq.CZTargetGateset, cirq.SqrtIswapTargetGateset etc.

cirq.optimize_for_target_gateset is a transformer which compiles a given circuit for a cirq.CompilationTargetGateset via the following steps:

  1. Run all gateset.preprocess_transformers
  2. Convert operations using built-in cirq.decompose + gateset.decompose_to_target_gateset.
  3. Run all gateset.postprocess_transformers

The preprocess transformers often includes optimizations like merging connected components of 1/2 qubit unitaries into a single unitary matrix, which can then be replaced with an efficient analytical decomposition as part of step-2.

The post-process transformers often includes cleanups and optimizations like dropping negligible operations, converting single qubit rotations into desired form, circuit alignments etc.

# Original QFT Circuit on 3 qubits.
q = cirq.LineQubit.range(3)
circuit = cirq.Circuit(cirq.QuantumFourierTransformGate(3).on(*q), cirq.measure(*q))
print("Original Circuit:", circuit, "\n", sep="\n")

# Compile the circuit for CZ Target Gateset.
gateset = cirq.CZTargetGateset(allow_partial_czs=True)
cz_circuit = cirq.optimize_for_target_gateset(circuit, gateset=gateset)
cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent(circuit, cz_circuit)
print("Circuit compiled for CZ Target Gateset:", cz_circuit, "\n", sep="\n")
Original Circuit:
0: ───qft───M───
      │     │
1: ───#2────M───
      │     │
2: ───#3────M───


Circuit compiled for CZ Target Gateset:
0: ───PhXZ(a=0.5,x=0.5,z=0)───@────────PhXZ(a=1.11e-16,x=1,z=0)───────────PhXZ(a=0.5,x=-0.5,z=0)───@───PhXZ(a=0.5,x=0.5,z=0)────@───PhXZ(a=0.5,x=-0.5,z=0)───@────────PhXZ(a=0.5,x=0.5,z=-1)────M───
                              │                                                                    │                            │                            │                                  │
1: ───────────────────────────@^-0.5───PhXZ(a=-1,x=0.5,z=-0.5)────@────────────────────────────────┼────────────────────────────┼────────────────────────────┼──────────────────────────────────M───
                                                                  │                                │                            │                            │                                  │
2: ───────────────────────────────────────────────────────────────@^0.5───PhXZ(a=0.5,x=-0.5,z=0)───@───PhXZ(a=0.5,x=-0.5,z=0)───@───PhXZ(a=0.5,x=0.5,z=0)────@^0.75───PhXZ(a=0.25,x=0,z=0.25)───M───

cirq.optimize_for_target_gateset also supports all the features discussed above, using cirq.TransformerContext. For example, you can compile the circuit for sqrt-iswap target gateset and inspect action of individual transformers using cirq.TransformerLogger, as shown below.

context = cirq.TransformerContext(logger=cirq.TransformerLogger())
gateset = cirq.SqrtIswapTargetGateset()
sqrt_iswap_circuit = cirq.optimize_for_target_gateset(circuit, gateset=gateset, context=context)
cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent(circuit, sqrt_iswap_circuit)
context.logger.show()
Transformer-1: optimize_for_target_gateset
Initial Circuit:
0: ───qft───M───
      │     │
1: ───#2────M───
      │     │
2: ───#3────M───


Final Circuit:
0: ───PhXZ(a=0,x=0.3,z=-0.5)───────────iSwap───────PhXZ(a=-0.5,x=0,z=-1)────iSwap───────PhXZ(a=0,x=0.5,z=0.45)──────────────────────────────────────────────────────────────PhXZ(a=-0.5,x=0.5,z=-1)──────────iSwap───────PhXZ(a=-0.585,x=0.5,z=0.585)───iSwap───────PhXZ(a=0.5,x=0.199,z=0)───iSwap───────PhXZ(a=-0.879,x=0.532,z=0.457)───M───
                                       │                                    │                                                                                                                                │                                          │                                     │                                            │
1: ───PhXZ(a=-0.202,x=0.136,z=0.202)───iSwap^0.5───PhXZ(a=-1,x=0.364,z=0)───iSwap^0.5───PhXZ(a=-0.0079,x=0.161,z=-0.451)───iSwap────────────────────────────────iSwap───────PhXZ(a=-1,x=0.5,z=-0.275)────────┼──────────────────────────────────────────┼─────────────────────────────────────┼────────────────────────────────────────────M───
                                                                                                                           │                                    │                                            │                                          │                                     │                                            │
2: ─────────────────────────────────────────────────────────────────────────────────────PhXZ(a=-0.124,x=0.864,z=0.124)─────iSwap^0.5───PhXZ(a=0,x=0.364,z=-1)───iSwap^0.5───PhXZ(a=0.616,x=0.449,z=-0.243)───iSwap^0.5───PhXZ(a=-0.852,x=0.5,z=0.852)───iSwap^0.5───PhXZ(a=0.5,x=0.623,z=0)───iSwap^0.5───PhXZ(a=-0.5,x=0.352,z=0.625)─────M───
----------------------------------------
    Transformer-2: expand_composite
    Initial Circuit:
    0: ───qft───M───
          │     │
    1: ───#2────M───
          │     │
    2: ───#3────M───


    Final Circuit:
    0: ───H───Grad^0.5───────────────@────────────×───M───
              │                      │            │   │
    1: ───────@──────────H───@───────┼────────────┼───M───
                             │       │            │   │
    2: ──────────────────────@^0.5───@^0.25───H───×───M───
----------------------------------------
    Transformer-3: merge_k_qubit_unitaries
    Initial Circuit:
    0: ───H───Grad^0.5───────────────@────────────×───M───
              │                      │            │   │
    1: ───────@──────────H───@───────┼────────────┼───M───
                             │       │            │   │
    2: ──────────────────────@^0.5───@^0.25───H───×───M───


    Final Circuit:
          [ 0: ───H───Grad^0.5─────── ]                                                                                                  [ 0: ───@────────────×─── ]
    0: ───[           │               ]──────────────────────────────────────────────────────────────────────────────────────────────────[       │            │    ]────────────────────────────────────────M───
          [ 1: ───────@──────────H─── ]['_default_merged_k_qubit_unitaries']                                                             [ 2: ───@^0.25───H───×─── ]['_default_merged_k_qubit_unitaries']   │
          │                                                                                                                              │                                                                  │
          │                                                                    [ 1: ───@─────── ]                                        │                                                                  │
    1: ───#2───────────────────────────────────────────────────────────────────[       │        ]────────────────────────────────────────┼──────────────────────────────────────────────────────────────────M───
                                                                               [ 2: ───@^0.5─── ]['_default_merged_k_qubit_unitaries']   │                                                                  │
                                                                               │                                                         │                                                                  │
    2: ────────────────────────────────────────────────────────────────────────#2────────────────────────────────────────────────────────#2─────────────────────────────────────────────────────────────────M───
----------------------------------------
    Transformer-4: _decompose_operations_to_target_gateset
    Initial Circuit:
          [ 0: ───H───Grad^0.5─────── ]                                                                                                  [ 0: ───@────────────×─── ]
    0: ───[           │               ]──────────────────────────────────────────────────────────────────────────────────────────────────[       │            │    ]────────────────────────────────────────M───
          [ 1: ───────@──────────H─── ]['_default_merged_k_qubit_unitaries']                                                             [ 2: ───@^0.25───H───×─── ]['_default_merged_k_qubit_unitaries']   │
          │                                                                                                                              │                                                                  │
          │                                                                    [ 1: ───@─────── ]                                        │                                                                  │
    1: ───#2───────────────────────────────────────────────────────────────────[       │        ]────────────────────────────────────────┼──────────────────────────────────────────────────────────────────M───
                                                                               [ 2: ───@^0.5─── ]['_default_merged_k_qubit_unitaries']   │                                                                  │
                                                                               │                                                         │                                                                  │
    2: ────────────────────────────────────────────────────────────────────────#2────────────────────────────────────────────────────────#2─────────────────────────────────────────────────────────────────M───


    Final Circuit:
    0: ───PhXZ(a=0,x=0.3,z=-0.5)───────────iSwap───────PhXZ(a=-0.5,x=0,z=-1)────iSwap───────PhXZ(a=0,x=0.5,z=0.45)───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────PhXZ(a=-0.5,x=0.5,z=-1)───iSwap───────PhXZ(a=-0.585,x=0.5,z=0.585)───iSwap───────PhXZ(a=0.5,x=0.199,z=0)───iSwap───────PhXZ(a=-0.879,x=0.532,z=0.457)───M───
                                           │                                    │                                                                                                                                                                                      │                                          │                                     │                                            │
    1: ───PhXZ(a=-0.202,x=0.136,z=0.202)───iSwap^0.5───PhXZ(a=-1,x=0.364,z=0)───iSwap^0.5───PhXZ(a=-0.544,x=0.48,z=-0.821)───PhXZ(a=-0.525,x=0.5,z=0.525)─────iSwap────────────────────────────────iSwap───────PhXZ(a=-1,x=0.5,z=-0.275)───────────────────────────────┼──────────────────────────────────────────┼─────────────────────────────────────┼────────────────────────────────────────────M───
                                                                                                                                                              │                                    │                                                                   │                                          │                                     │                                            │
    2: ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────PhXZ(a=-0.124,x=0.864,z=0.124)───iSwap^0.5───PhXZ(a=0,x=0.364,z=-1)───iSwap^0.5───PhXZ(a=-1,x=0.864,z=-0.874)───PhXZ(a=-0.5,x=0.5,z=0)────iSwap^0.5───PhXZ(a=-0.852,x=0.5,z=0.852)───iSwap^0.5───PhXZ(a=0.5,x=0.623,z=0)───iSwap^0.5───PhXZ(a=-0.5,x=0.352,z=0.625)─────M───
----------------------------------------
    Transformer-5: merge_single_qubit_moments_to_phxz
    Initial Circuit:
    0: ───PhXZ(a=0,x=0.3,z=-0.5)───────────iSwap───────PhXZ(a=-0.5,x=0,z=-1)────iSwap───────PhXZ(a=0,x=0.5,z=0.45)───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────PhXZ(a=-0.5,x=0.5,z=-1)───iSwap───────PhXZ(a=-0.585,x=0.5,z=0.585)───iSwap───────PhXZ(a=0.5,x=0.199,z=0)───iSwap───────PhXZ(a=-0.879,x=0.532,z=0.457)───M───
                                           │                                    │                                                                                                                                                                                      │                                          │                                     │                                            │
    1: ───PhXZ(a=-0.202,x=0.136,z=0.202)───iSwap^0.5───PhXZ(a=-1,x=0.364,z=0)───iSwap^0.5───PhXZ(a=-0.544,x=0.48,z=-0.821)───PhXZ(a=-0.525,x=0.5,z=0.525)─────iSwap────────────────────────────────iSwap───────PhXZ(a=-1,x=0.5,z=-0.275)───────────────────────────────┼──────────────────────────────────────────┼─────────────────────────────────────┼────────────────────────────────────────────M───
                                                                                                                                                              │                                    │                                                                   │                                          │                                     │                                            │
    2: ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────PhXZ(a=-0.124,x=0.864,z=0.124)───iSwap^0.5───PhXZ(a=0,x=0.364,z=-1)───iSwap^0.5───PhXZ(a=-1,x=0.864,z=-0.874)───PhXZ(a=-0.5,x=0.5,z=0)────iSwap^0.5───PhXZ(a=-0.852,x=0.5,z=0.852)───iSwap^0.5───PhXZ(a=0.5,x=0.623,z=0)───iSwap^0.5───PhXZ(a=-0.5,x=0.352,z=0.625)─────M───


    Final Circuit:
    0: ───PhXZ(a=0,x=0.3,z=-0.5)───────────iSwap───────PhXZ(a=-0.5,x=0,z=-1)────iSwap───────PhXZ(a=0,x=0.5,z=0.45)──────────────────────────────────────────────────────────────PhXZ(a=-0.5,x=0.5,z=-1)──────────iSwap───────PhXZ(a=-0.585,x=0.5,z=0.585)───iSwap───────PhXZ(a=0.5,x=0.199,z=0)───iSwap───────PhXZ(a=-0.879,x=0.532,z=0.457)───M───
                                           │                                    │                                                                                                                                │                                          │                                     │                                            │
    1: ───PhXZ(a=-0.202,x=0.136,z=0.202)───iSwap^0.5───PhXZ(a=-1,x=0.364,z=0)───iSwap^0.5───PhXZ(a=-0.0079,x=0.161,z=-0.451)───iSwap────────────────────────────────iSwap───────PhXZ(a=-1,x=0.5,z=-0.275)────────┼──────────────────────────────────────────┼─────────────────────────────────────┼────────────────────────────────────────────M───
                                                                                                                               │                                    │                                            │                                          │                                     │                                            │
    2: ─────────────────────────────────────────────────────────────────────────────────────PhXZ(a=-0.124,x=0.864,z=0.124)─────iSwap^0.5───PhXZ(a=0,x=0.364,z=-1)───iSwap^0.5───PhXZ(a=0.616,x=0.449,z=-0.243)───iSwap^0.5───PhXZ(a=-0.852,x=0.5,z=0.852)───iSwap^0.5───PhXZ(a=0.5,x=0.623,z=0)───iSwap^0.5───PhXZ(a=-0.5,x=0.352,z=0.625)─────M───
----------------------------------------
    Transformer-6: drop_negligible_operations
    Initial Circuit:
    0: ───PhXZ(a=0,x=0.3,z=-0.5)───────────iSwap───────PhXZ(a=-0.5,x=0,z=-1)────iSwap───────PhXZ(a=0,x=0.5,z=0.45)──────────────────────────────────────────────────────────────PhXZ(a=-0.5,x=0.5,z=-1)──────────iSwap───────PhXZ(a=-0.585,x=0.5,z=0.585)───iSwap───────PhXZ(a=0.5,x=0.199,z=0)───iSwap───────PhXZ(a=-0.879,x=0.532,z=0.457)───M───
                                           │                                    │                                                                                                                                │                                          │                                     │                                            │
    1: ───PhXZ(a=-0.202,x=0.136,z=0.202)───iSwap^0.5───PhXZ(a=-1,x=0.364,z=0)───iSwap^0.5───PhXZ(a=-0.0079,x=0.161,z=-0.451)───iSwap────────────────────────────────iSwap───────PhXZ(a=-1,x=0.5,z=-0.275)────────┼──────────────────────────────────────────┼─────────────────────────────────────┼────────────────────────────────────────────M───
                                                                                                                               │                                    │                                            │                                          │                                     │                                            │
    2: ─────────────────────────────────────────────────────────────────────────────────────PhXZ(a=-0.124,x=0.864,z=0.124)─────iSwap^0.5───PhXZ(a=0,x=0.364,z=-1)───iSwap^0.5───PhXZ(a=0.616,x=0.449,z=-0.243)───iSwap^0.5───PhXZ(a=-0.852,x=0.5,z=0.852)───iSwap^0.5───PhXZ(a=0.5,x=0.623,z=0)───iSwap^0.5───PhXZ(a=-0.5,x=0.352,z=0.625)─────M───


    Final Circuit:
    0: ───PhXZ(a=0,x=0.3,z=-0.5)───────────iSwap───────PhXZ(a=-0.5,x=0,z=-1)────iSwap───────PhXZ(a=0,x=0.5,z=0.45)──────────────────────────────────────────────────────────────PhXZ(a=-0.5,x=0.5,z=-1)──────────iSwap───────PhXZ(a=-0.585,x=0.5,z=0.585)───iSwap───────PhXZ(a=0.5,x=0.199,z=0)───iSwap───────PhXZ(a=-0.879,x=0.532,z=0.457)───M───
                                           │                                    │                                                                                                                                │                                          │                                     │                                            │
    1: ───PhXZ(a=-0.202,x=0.136,z=0.202)───iSwap^0.5───PhXZ(a=-1,x=0.364,z=0)───iSwap^0.5───PhXZ(a=-0.0079,x=0.161,z=-0.451)───iSwap────────────────────────────────iSwap───────PhXZ(a=-1,x=0.5,z=-0.275)────────┼──────────────────────────────────────────┼─────────────────────────────────────┼────────────────────────────────────────────M───
                                                                                                                               │                                    │                                            │                                          │                                     │                                            │
    2: ─────────────────────────────────────────────────────────────────────────────────────PhXZ(a=-0.124,x=0.864,z=0.124)─────iSwap^0.5───PhXZ(a=0,x=0.364,z=-1)───iSwap^0.5───PhXZ(a=0.616,x=0.449,z=-0.243)───iSwap^0.5───PhXZ(a=-0.852,x=0.5,z=0.852)───iSwap^0.5───PhXZ(a=0.5,x=0.623,z=0)───iSwap^0.5───PhXZ(a=-0.5,x=0.352,z=0.625)─────M───
----------------------------------------
    Transformer-7: drop_empty_moments
    Initial Circuit:
    0: ───PhXZ(a=0,x=0.3,z=-0.5)───────────iSwap───────PhXZ(a=-0.5,x=0,z=-1)────iSwap───────PhXZ(a=0,x=0.5,z=0.45)──────────────────────────────────────────────────────────────PhXZ(a=-0.5,x=0.5,z=-1)──────────iSwap───────PhXZ(a=-0.585,x=0.5,z=0.585)───iSwap───────PhXZ(a=0.5,x=0.199,z=0)───iSwap───────PhXZ(a=-0.879,x=0.532,z=0.457)───M───
                                           │                                    │                                                                                                                                │                                          │                                     │                                            │
    1: ───PhXZ(a=-0.202,x=0.136,z=0.202)───iSwap^0.5───PhXZ(a=-1,x=0.364,z=0)───iSwap^0.5───PhXZ(a=-0.0079,x=0.161,z=-0.451)───iSwap────────────────────────────────iSwap───────PhXZ(a=-1,x=0.5,z=-0.275)────────┼──────────────────────────────────────────┼─────────────────────────────────────┼────────────────────────────────────────────M───
                                                                                                                               │                                    │                                            │                                          │                                     │                                            │
    2: ─────────────────────────────────────────────────────────────────────────────────────PhXZ(a=-0.124,x=0.864,z=0.124)─────iSwap^0.5───PhXZ(a=0,x=0.364,z=-1)───iSwap^0.5───PhXZ(a=0.616,x=0.449,z=-0.243)───iSwap^0.5───PhXZ(a=-0.852,x=0.5,z=0.852)───iSwap^0.5───PhXZ(a=0.5,x=0.623,z=0)───iSwap^0.5───PhXZ(a=-0.5,x=0.352,z=0.625)─────M───


    Final Circuit:
    0: ───PhXZ(a=0,x=0.3,z=-0.5)───────────iSwap───────PhXZ(a=-0.5,x=0,z=-1)────iSwap───────PhXZ(a=0,x=0.5,z=0.45)──────────────────────────────────────────────────────────────PhXZ(a=-0.5,x=0.5,z=-1)──────────iSwap───────PhXZ(a=-0.585,x=0.5,z=0.585)───iSwap───────PhXZ(a=0.5,x=0.199,z=0)───iSwap───────PhXZ(a=-0.879,x=0.532,z=0.457)───M───
                                           │                                    │                                                                                                                                │                                          │                                     │                                            │
    1: ───PhXZ(a=-0.202,x=0.136,z=0.202)───iSwap^0.5───PhXZ(a=-1,x=0.364,z=0)───iSwap^0.5───PhXZ(a=-0.0079,x=0.161,z=-0.451)───iSwap────────────────────────────────iSwap───────PhXZ(a=-1,x=0.5,z=-0.275)────────┼──────────────────────────────────────────┼─────────────────────────────────────┼────────────────────────────────────────────M───
                                                                                                                               │                                    │                                            │                                          │                                     │                                            │
    2: ─────────────────────────────────────────────────────────────────────────────────────PhXZ(a=-0.124,x=0.864,z=0.124)─────iSwap^0.5───PhXZ(a=0,x=0.364,z=-1)───iSwap^0.5───PhXZ(a=0.616,x=0.449,z=-0.243)───iSwap^0.5───PhXZ(a=-0.852,x=0.5,z=0.852)───iSwap^0.5───PhXZ(a=0.5,x=0.623,z=0)───iSwap^0.5───PhXZ(a=-0.5,x=0.352,z=0.625)─────M───
----------------------------------------

Summary

Cirq provides a plethora of built-in transformers which can be composed together into useful abstractions, like cirq.CompilationTargetGateset, which in-turn can be serialized and can be used as a parameter in larger compilation pipelines and experiment workflows.

Try using these transformers to compile your circuits and refer to the API reference docs of cirq.TRANSFORMER for more details.