{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": { "cellView": "form", "execution": { "iopub.execute_input": "2024-05-31T09:17:18.884384Z", "iopub.status.busy": "2024-05-31T09:17:18.883847Z", "iopub.status.idle": "2024-05-31T09:17:18.887899Z", "shell.execute_reply": "2024-05-31T09:17:18.887289Z" }, "id": "906e07f6e562" }, "outputs": [], "source": [ "# @title Copyright 2022 The Cirq Developers\n", "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", "# you may not use this file except in compliance with the License.\n", "# You may obtain a copy of the License at\n", "#\n", "# https://www.apache.org/licenses/LICENSE-2.0\n", "#\n", "# Unless required by applicable law or agreed to in writing, software\n", "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", "# See the License for the specific language governing permissions and\n", "# limitations under the License." ] }, { "cell_type": "markdown", "metadata": { "id": "8bbd73c03ac2" }, "source": [ "# Circuit Transformers" ] }, { "cell_type": "markdown", "metadata": { "id": "25eb74f260d6" }, "source": [ "\n", " \n", " \n", " \n", " \n", "
\n", " View on QuantumAI\n", " \n", " Run in Google Colab\n", " \n", " View source on GitHub\n", " \n", " Download notebook\n", "
" ] }, { "cell_type": "markdown", "metadata": { "id": "03d216c7f7b1" }, "source": [ "## Setup" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2024-05-31T09:17:18.891208Z", "iopub.status.busy": "2024-05-31T09:17:18.890941Z", "iopub.status.idle": "2024-05-31T09:17:37.638828Z", "shell.execute_reply": "2024-05-31T09:17:37.638091Z" }, "id": "846b32703c5c" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "installing cirq...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\r\n", "jsonschema 4.22.0 requires attrs>=22.2.0, but you have attrs 21.4.0 which is incompatible.\r\n", "jupyterlab 4.2.1 requires httpx>=0.25.0, but you have httpx 0.23.3 which is incompatible.\r\n", "referencing 0.35.1 requires attrs>=22.2.0, but you have attrs 21.4.0 which is incompatible.\r\n", "tensorflow-metadata 1.15.0 requires protobuf<4.21,>=3.20.3; python_version < \"3.11\", but you have protobuf 4.25.3 which is incompatible.\u001b[0m\u001b[31m\r\n", "\u001b[0m" ] }, { "name": "stdout", "output_type": "stream", "text": [ "installed cirq.\n" ] } ], "source": [ "try:\n", " import cirq\n", "except ImportError:\n", " print(\"installing cirq...\")\n", " !pip install --quiet cirq\n", " import cirq\n", "\n", " print(\"installed cirq.\")" ] }, { "cell_type": "markdown", "metadata": { "id": "9d3d49b9ca2a" }, "source": [ "## What is a Transformer?\n", "A transformer in Cirq is any callable that satisfies the `cirq.TRANSFORMER` API, and *transforms* an input circuit into an output circuit.\n", "\n", "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:\n", "- 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. \n", "- 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. \n", "- 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.\n", "\n", "\n", "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](./transformers.ipynb) page presents the details on creating more complex custom transformers through primitives and composition." ] }, { "cell_type": "markdown", "metadata": { "id": "KUor1pGZi0Iw" }, "source": [ "## Built-in Transformers in Cirq" ] }, { "cell_type": "markdown", "metadata": { "id": "861ea1ada088" }, "source": [ "## Overview\n", "Transformers that come with cirq can be found in the [`/cirq/transformers`](https://github.com/quantumlib/Cirq/tree/main/cirq-core/cirq/transformers) package.\n", "\n", "A few notable examples are:\n", "* **`cirq.align_left` / `cirq.align_right`**: Align gates to the left/right of the circuit by sliding them as far as possible along each qubit in the chosen direction.\n", "* **`cirq.defer_measurements`**: Moves all (non-terminal) measurements in a circuit to the end of circuit by implementing the deferred measurement principle.\n", "* **`cirq.drop_empty_moments`** / **`cirq.drop_negligible_operations`**: Removes moments that are empty or operations that have very small effects, respectively.\n", "* **`cirq.eject_phased_paulis`**: Pushes X, Y, and PhasedX gates towards the end of the circuit, potentially absorbing Z gates and modifying gates along the way.\n", "* **`cirq.eject_z`**: Pushes Z gates towards the end of the circuit, potentially adjusting phases of gates that they pass through.\n", "* **`cirq.expand_composite`**: Uses `cirq.decompose` to expand gates built from other gates (composite gates).\n", "* **`cirq.merge_k_qubit_unitaries`**: Replaces connected components of unitary operations, acting on <= k qubits, with op-tree given by `rewriter(circuit_op)`.\n", "* **`cirq.optimize_for_target_gateset`**: Attempts to convert a circuit into an equivalent circuit using only gates from a given target gateset.\n", "* **`cirq.stratified_circuit`**: Repacks the circuit to ensure that moments only contain operations from the same category.\n", "* **`cirq.synchronize_terminal_measurements`**: Moves all terminal measurements in a circuit to the final moment, if possible.\n", "\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2024-05-31T09:17:37.642834Z", "iopub.status.busy": "2024-05-31T09:17:37.642385Z", "iopub.status.idle": "2024-05-31T09:17:37.663345Z", "shell.execute_reply": "2024-05-31T09:17:37.662686Z" }, "id": "AqTylIpssdex" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Original Circuit:\n", " ┌──┐\n", "0: ───H───────@───────@───H────X─────M───\n", " │ │ │\n", "1: ───H───@───X───H───@───H────┼M────────\n", " │ │\n", "2: ───────X────────────────────@─────M───\n", " └──┘\n", "Optimized Circuit:\n", "0: ───────────X───M───\n", " │\n", "1: ───H───@───┼───M───\n", " │ │\n", "2: ───────X───@───M───\n" ] } ], "source": [ "def optimize_circuit(circuit, context=None, k=2):\n", " # Merge 2-qubit connected components into circuit operations.\n", " optimized_circuit = cirq.merge_k_qubit_unitaries(\n", " circuit, k=k, rewriter=lambda op: op.with_tags(\"merged\"), context=context\n", " )\n", "\n", " # Drop operations with negligible effect / close to identity.\n", " optimized_circuit = cirq.drop_negligible_operations(optimized_circuit, context=context)\n", "\n", " # Expand all remaining merged connected components.\n", " optimized_circuit = cirq.expand_composite(\n", " optimized_circuit, no_decomp=lambda op: \"merged\" not in op.tags, context=context\n", " )\n", "\n", " # Synchronize terminal measurements to be in the same moment.\n", " optimized_circuit = cirq.synchronize_terminal_measurements(optimized_circuit, context=context)\n", "\n", " # Assert the original and optimized circuit are equivalent.\n", " cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent(\n", " circuit, optimized_circuit\n", " )\n", "\n", " return optimized_circuit\n", "\n", "\n", "q = cirq.LineQubit.range(3)\n", "circuit = cirq.Circuit(\n", " cirq.H(q[1]),\n", " cirq.CNOT(*q[1:]),\n", " cirq.H(q[0]),\n", " cirq.CNOT(*q[:2]),\n", " cirq.H(q[1]),\n", " cirq.CZ(*q[:2]),\n", " cirq.H.on_each(*q[:2]),\n", " cirq.CNOT(q[2], q[0]),\n", " cirq.measure_each(*q),\n", ")\n", "print(\"Original Circuit:\", circuit, sep=\"\\n\")\n", "print(\"Optimized Circuit:\", optimize_circuit(circuit), sep=\"\\n\")" ] }, { "cell_type": "markdown", "metadata": { "id": "mu83U5hGzNcH" }, "source": [ "## Inspecting transformer actions\n", "\n", "Every transformer in Cirq accepts a `cirq.TransformerContext` instance, which stores common configurable options useful for all transformers. \n", "\n", "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.\n", "\n", "Below, you can inspect the action of each transformer in the `optimize_circuit` method defined above. " ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2024-05-31T09:17:37.666583Z", "iopub.status.busy": "2024-05-31T09:17:37.666310Z", "iopub.status.idle": "2024-05-31T09:17:37.695831Z", "shell.execute_reply": "2024-05-31T09:17:37.695229Z" }, "id": "1cqUa2Kc0Hni" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Transformer-1: merge_k_qubit_unitaries\n", "Initial Circuit:\n", " ┌──┐\n", "0: ───H───────@───────@───H────X─────M───\n", " │ │ │\n", "1: ───H───@───X───H───@───H────┼M────────\n", " │ │\n", "2: ───────X────────────────────@─────M───\n", " └──┘\n", "\n", "\n", "Final Circuit:\n", " ┌───────────────────────┐\n", " [ 0: ───H───@───────@───H─── ] [ 0: ───X─── ]\n", "0: ────────────────────────────────[ │ │ ]─────────────[ │ ]────────────M───\n", " [ 1: ───────X───H───@───H─── ][merged] [ 2: ───@─── ][merged]\n", " │ │\n", " [ 1: ───H───@─── ] │ │\n", "1: ───[ │ ]───────────#2────────────────────────────────────────M┼─────────────────────────────\n", " [ 2: ───────X─── ][merged] │\n", " │ │\n", "2: ───#2──────────────────────────────────────────────────────────────────────#2────────────────────────M───\n", " └───────────────────────┘\n", "----------------------------------------\n", "Transformer-2: drop_negligible_operations\n", "Initial Circuit:\n", " ┌───────────────────────┐\n", " [ 0: ───H───@───────@───H─── ] [ 0: ───X─── ]\n", "0: ────────────────────────────────[ │ │ ]─────────────[ │ ]────────────M───\n", " [ 1: ───────X───H───@───H─── ][merged] [ 2: ───@─── ][merged]\n", " │ │\n", " [ 1: ───H───@─── ] │ │\n", "1: ───[ │ ]───────────#2────────────────────────────────────────M┼─────────────────────────────\n", " [ 2: ───────X─── ][merged] │\n", " │ │\n", "2: ───#2──────────────────────────────────────────────────────────────────────#2────────────────────────M───\n", " └───────────────────────┘\n", "\n", "\n", "Final Circuit:\n", " ┌───────────────────────┐\n", " [ 0: ───X─── ]\n", "0: ──────────────────────────────────────[ │ ]────────────M───\n", " [ 2: ───@─── ][merged]\n", " │\n", " [ 1: ───H───@─── ] │\n", "1: ───[ │ ]────────────────M┼─────────────────────────────\n", " [ 2: ───────X─── ][merged] │\n", " │ │\n", "2: ───#2─────────────────────────────────#2────────────────────────M───\n", " └───────────────────────┘\n", "----------------------------------------\n", "Transformer-3: expand_composite\n", "Initial Circuit:\n", " ┌───────────────────────┐\n", " [ 0: ───X─── ]\n", "0: ──────────────────────────────────────[ │ ]────────────M───\n", " [ 2: ───@─── ][merged]\n", " │\n", " [ 1: ───H───@─── ] │\n", "1: ───[ │ ]────────────────M┼─────────────────────────────\n", " [ 2: ───────X─── ][merged] │\n", " │ │\n", "2: ───#2─────────────────────────────────#2────────────────────────M───\n", " └───────────────────────┘\n", "\n", "\n", "Final Circuit:\n", " ┌──┐\n", "0: ─────────────X────M───\n", " │\n", "1: ───H───@────M┼────────\n", " │ │\n", "2: ───────X─────@────M───\n", " └──┘\n", "----------------------------------------\n", "Transformer-4: synchronize_terminal_measurements\n", "Initial Circuit:\n", " ┌──┐\n", "0: ─────────────X────M───\n", " │\n", "1: ───H───@────M┼────────\n", " │ │\n", "2: ───────X─────@────M───\n", " └──┘\n", "\n", "\n", "Final Circuit:\n", "0: ───────────X───M───\n", " │\n", "1: ───H───@───┼───M───\n", " │ │\n", "2: ───────X───@───M───\n", "----------------------------------------\n" ] } ], "source": [ "context = cirq.TransformerContext(logger=cirq.TransformerLogger())\n", "optimized_circuit = optimize_circuit(circuit, context)\n", "context.logger.show()" ] }, { "cell_type": "markdown", "metadata": { "id": "d646f99aad84" }, "source": [ "By first using `cirq.merge_k_qubit_unitaries` to turn connected components of the circuit into `cirq.CircuitOperation`s, `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.CircuitOperation`s and aligning measurements to be terminal measurements." ] }, { "cell_type": "markdown", "metadata": { "id": "kLm-5LHr0YAd" }, "source": [ "## Support for no-compile tags\n", "\n", "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. \n", "\n", "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. \n", "\n", "Below, you can use no-compile tags when transforming a circuit using the `optimize_circuit` mehod defined above." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2024-05-31T09:17:37.698970Z", "iopub.status.busy": "2024-05-31T09:17:37.698730Z", "iopub.status.idle": "2024-05-31T09:17:37.722428Z", "shell.execute_reply": "2024-05-31T09:17:37.721794Z" }, "id": "HEhXziehR29V" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Original Circuit:\n", "0: ───H───@───X[spin_echoes]^0.5───X[spin_echoes]^-0.5───@───H───M───\n", " │ │\n", "1: ───────X───@────────────────────@─────────────────────X───M───────\n", " │ │\n", "2: ───────────X────────────────────X─────────────────────M───────────\n", "\n", "\n", "Optimized Circuit without specifying tags_to_ignore:\n", "0: ───H───@───────────@───H───M───\n", " │ │\n", "1: ───────X───@───@───X───────M───\n", " │ │\n", "2: ───────────X───X───────────M─── \n", "\n", "Optimized Circuit while ignoring operations marked with tags_to_ignore:\n", "0: ───H───@───X[spin_echoes]^0.5───X[spin_echoes]^-0.5───@───H───M───\n", " │ │\n", "1: ───────X───@────────────────────@─────────────────────X───────M───\n", " │ │\n", "2: ───────────X────────────────────X─────────────────────────────M─── \n", "\n" ] } ], "source": [ "# Echo pulses inserted in the circuit to prevent dephasing during idling should be ignored.\n", "circuit = cirq.Circuit(\n", " cirq.H(q[0]),\n", " cirq.CNOT(*q[:2]),\n", " [\n", " op.with_tags(\"spin_echoes\") for op in [cirq.X(q[0]) ** 0.5, cirq.X(q[0]) ** -0.5]\n", " ], # the echo pulses\n", " [cirq.CNOT(*q[1:]), cirq.CNOT(*q[1:])],\n", " [cirq.CNOT(*q[:2]), cirq.H(q[0])],\n", " cirq.measure_each(*q),\n", ")\n", "# Original Circuit\n", "print(\"Original Circuit:\", circuit, \"\\n\", sep=\"\\n\")\n", "\n", "# Optimized Circuit without tags_to_ignore\n", "print(\"Optimized Circuit without specifying tags_to_ignore:\")\n", "print(optimize_circuit(circuit, k=1), \"\\n\")\n", "\n", "# Optimized Circuit ignoring operations marked with tags_to_ignore.\n", "print(\"Optimized Circuit while ignoring operations marked with tags_to_ignore:\")\n", "context = cirq.TransformerContext(tags_to_ignore=[\"spin_echoes\"])\n", "print(optimize_circuit(circuit, k=1, context=context), \"\\n\")" ] }, { "cell_type": "markdown", "metadata": { "id": "3f3c7851a571" }, "source": [ "## Support for recursively transforming sub-circuits\n", "\n", "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. \n", "\n", "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. " ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2024-05-31T09:17:37.725573Z", "iopub.status.busy": "2024-05-31T09:17:37.725328Z", "iopub.status.idle": "2024-05-31T09:17:37.738248Z", "shell.execute_reply": "2024-05-31T09:17:37.737530Z" }, "id": "994ffb523487" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Original Circuit:\n", "\n", " [ 0: ───I───@───I[ignore]─── ] [ 0: ───I───@───I[ignore]─── ]\n", "0: ───I───────────[ │ ]───[ │ ]───────────\n", " [ 1: ───I───X─────────────── ] [ 1: ───I───X─────────────── ][ignore]\n", " │ │\n", "1: ───I[ignore]───#2───────────────────────────────#2───────────────────────────────────────\n", "\n", "\n", "\n", "Optimized Circuit with deep=False and tags_to_ignore=['ignore']:\n", "\n", " [ 0: ───I───@───I[ignore]─── ] [ 0: ───I───@───I[ignore]─── ]\n", "0: ───────────────[ │ ]───[ │ ]───────────\n", " [ 1: ───I───X─────────────── ] [ 1: ───I───X─────────────── ][ignore]\n", " │ │\n", "1: ───I[ignore]───#2───────────────────────────────#2─────────────────────────────────────── \n", "\n", "\n", "Optimized Circuit with deep=True and tags_to_ignore=['ignore']:\n", "\n", " [ 0: ───────@───I[ignore]─── ] [ 0: ───I───@───I[ignore]─── ]\n", "0: ───────────────[ │ ]───[ │ ]───────────\n", " [ 1: ───────X─────────────── ] [ 1: ───I───X─────────────── ][ignore]\n", " │ │\n", "1: ───I[ignore]───#2───────────────────────────────#2─────────────────────────────────────── \n", "\n" ] } ], "source": [ "q = cirq.LineQubit.range(2)\n", "circuit_op = cirq.CircuitOperation(\n", " cirq.FrozenCircuit(cirq.I.on_each(*q), cirq.CNOT(*q), cirq.I(q[0]).with_tags(\"ignore\"))\n", ")\n", "circuit = cirq.Circuit(\n", " cirq.I(q[0]), cirq.I(q[1]).with_tags(\"ignore\"), circuit_op, circuit_op.with_tags(\"ignore\")\n", ")\n", "print(\"Original Circuit:\", circuit, \"\\n\", sep=\"\\n\\n\")\n", "\n", "context = cirq.TransformerContext(tags_to_ignore=[\"ignore\"], deep=False)\n", "print(\"Optimized Circuit with deep=False and tags_to_ignore=['ignore']:\\n\")\n", "print(cirq.drop_negligible_operations(circuit, context=context), \"\\n\\n\")\n", "\n", "context = cirq.TransformerContext(tags_to_ignore=[\"ignore\"], deep=True)\n", "print(\"Optimized Circuit with deep=True and tags_to_ignore=['ignore']:\\n\")\n", "print(cirq.drop_negligible_operations(circuit, context=context), \"\\n\")" ] }, { "cell_type": "markdown", "metadata": { "id": "521afc8ef6be" }, "source": [ "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. " ] }, { "cell_type": "markdown", "metadata": { "id": "iIHUZvLRlHFj" }, "source": [ "## Compiling to NISQ targets: `cirq.CompilationTargetGateset`\n", "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.\n", "\n", "**`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.\n", "\n", "\n", "**`cirq.optimize_for_target_gateset`** is a transformer which compiles a given circuit for a `cirq.CompilationTargetGateset` via the following steps:\n", "\n", "1. Run all `gateset.preprocess_transformers`\n", "2. Convert operations using built-in `cirq.decompose` + `gateset.decompose_to_target_gateset`.\n", "3. Run all `gateset.postprocess_transformers`\n", "\n", "\n", "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. \n", "\n", "The post-process transformers often includes cleanups and optimizations like dropping negligible operations, \n", "converting single qubit rotations into desired form, circuit alignments etc. " ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2024-05-31T09:17:37.741398Z", "iopub.status.busy": "2024-05-31T09:17:37.741159Z", "iopub.status.idle": "2024-05-31T09:17:37.790512Z", "shell.execute_reply": "2024-05-31T09:17:37.789822Z" }, "id": "-QFPCdW0qS3R" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Original Circuit:\n", "0: ───qft───M───\n", " │ │\n", "1: ───#2────M───\n", " │ │\n", "2: ───#3────M───\n", "\n", "\n", "Circuit compiled for CZ Target Gateset:\n", "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───\n", " │ │ │ │ │\n", "1: ───────────────────────────@^-0.5───PhXZ(a=-1,x=0.5,z=-0.5)────@────────────────────────────────┼────────────────────────────┼────────────────────────────┼─────────────────────────────────M───\n", " │ │ │ │ │\n", "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,x=0,z=0.25)─────M───\n", "\n", "\n" ] } ], "source": [ "# Original QFT Circuit on 3 qubits.\n", "q = cirq.LineQubit.range(3)\n", "circuit = cirq.Circuit(cirq.QuantumFourierTransformGate(3).on(*q), cirq.measure(*q))\n", "print(\"Original Circuit:\", circuit, \"\\n\", sep=\"\\n\")\n", "\n", "# Compile the circuit for CZ Target Gateset.\n", "gateset = cirq.CZTargetGateset(allow_partial_czs=True)\n", "cz_circuit = cirq.optimize_for_target_gateset(circuit, gateset=gateset)\n", "cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent(circuit, cz_circuit)\n", "print(\"Circuit compiled for CZ Target Gateset:\", cz_circuit, \"\\n\", sep=\"\\n\")" ] }, { "cell_type": "markdown", "metadata": { "id": "b963809fa20b" }, "source": [ "`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." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2024-05-31T09:17:37.793739Z", "iopub.status.busy": "2024-05-31T09:17:37.793508Z", "iopub.status.idle": "2024-05-31T09:17:37.874400Z", "shell.execute_reply": "2024-05-31T09:17:37.873781Z" }, "id": "140330585db5" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Transformer-1: optimize_for_target_gateset\n", "Initial Circuit:\n", "0: ───qft───M───\n", " │ │\n", "1: ───#2────M───\n", " │ │\n", "2: ───#3────M───\n", "\n", "\n", "Final Circuit:\n", "0: ───PhXZ(a=-1,x=0.676,z=-0.5)────────iSwap───────PhXZ(a=-0.5,x=0,z=-1)────iSwap───────PhXZ(a=0,x=0.5,z=-0.574)──────────────────────────────────────────────────────────────────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───\n", " │ │ │ │ │ │\n", "1: ───PhXZ(a=0.709,x=0.136,z=-0.709)───iSwap^0.5───PhXZ(a=-1,x=0.364,z=0)───iSwap^0.5───PhXZ(a=0.999,x=0.866,z=0.542)────iSwap────────────────────────────────────────iSwap───────PhXZ(a=-1,x=0.5,z=0.748)─────────┼──────────────────────────────────────────┼──────────────────────────────────────┼────────────────────────────────────────────M───\n", " │ │ │ │ │ │\n", "2: ─────────────────────────────────────────────────────────────────────────────────────PhXZ(a=0.966,x=0.864,z=-0.966)───iSwap^0.5───PhXZ(a=2.22e-16,x=0.364,z=1.0)───iSwap^0.5───PhXZ(a=-0.299,x=0.584,z=0.907)───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.648,z=0.625)─────M───\n", "----------------------------------------\n", " Transformer-2: expand_composite\n", " Initial Circuit:\n", " 0: ───qft───M───\n", " │ │\n", " 1: ───#2────M───\n", " │ │\n", " 2: ───#3────M───\n", "\n", "\n", " Final Circuit:\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " 0: ───H───Grad^0.5───────────────@────────────×───M───\n", " │ │ │ │\n", " 1: ───────@──────────H───@───────┼────────────┼───M───\n", " │ │ │ │\n", " 2: ──────────────────────@^0.5───@^0.25───H───×───M───\n", "----------------------------------------\n", " Transformer-3: merge_k_qubit_unitaries\n", " Initial Circuit:\n", " 0: ───H───Grad^0.5───────────────@────────────×───M───\n", " │ │ │ │\n", " 1: ───────@──────────H───@───────┼────────────┼───M───\n", " │ │ │ │\n", " 2: ──────────────────────@^0.5───@^0.25───H───×───M───\n", "\n", "\n", " Final Circuit:\n", " [ 0: ───H───Grad^0.5─────── ] [ 0: ───@────────────×─── ]\n", " 0: ───[ │ ]──────────────────────────────────────────────────────────────────────────────────────────────[ │ │ ]──────────────────────────────────────M───\n", " [ 1: ───────@──────────H─── ][_default_merged_k_qubit_unitaries] [ 2: ───@^0.25───H───×─── ][_default_merged_k_qubit_unitaries] │\n", " │ │ │\n", " │ [ 1: ───@─────── ] │ │\n", " 1: ───#2─────────────────────────────────────────────────────────────────[ │ ]──────────────────────────────────────┼────────────────────────────────────────────────────────────────M───\n", " [ 2: ───@^0.5─── ][_default_merged_k_qubit_unitaries] │ │\n", " │ │ │\n", " 2: ──────────────────────────────────────────────────────────────────────#2──────────────────────────────────────────────────────#2───────────────────────────────────────────────────────────────M───\n", "----------------------------------------\n", " Transformer-4: _decompose_operations_to_target_gateset\n", " Initial Circuit:\n", " [ 0: ───H───Grad^0.5─────── ] [ 0: ───@────────────×─── ]\n", " 0: ───[ │ ]──────────────────────────────────────────────────────────────────────────────────────────────[ │ │ ]──────────────────────────────────────M───\n", " [ 1: ───────@──────────H─── ][_default_merged_k_qubit_unitaries] [ 2: ───@^0.25───H───×─── ][_default_merged_k_qubit_unitaries] │\n", " │ │ │\n", " │ [ 1: ───@─────── ] │ │\n", " 1: ───#2─────────────────────────────────────────────────────────────────[ │ ]──────────────────────────────────────┼────────────────────────────────────────────────────────────────M───\n", " [ 2: ───@^0.5─── ][_default_merged_k_qubit_unitaries] │ │\n", " │ │ │\n", " 2: ──────────────────────────────────────────────────────────────────────#2──────────────────────────────────────────────────────#2───────────────────────────────────────────────────────────────M───\n", "\n", "\n", " Final Circuit:\n", " 0: ───PhXZ(a=-1,x=0.676,z=-0.5)────────iSwap───────PhXZ(a=-0.5,x=0,z=-1)────iSwap───────PhXZ(a=0,x=0.5,z=-0.574)────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────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───\n", " │ │ │ │ │ │\n", " 1: ───PhXZ(a=0.709,x=0.136,z=-0.709)───iSwap^0.5───PhXZ(a=-1,x=0.364,z=0)───iSwap^0.5───PhXZ(a=0.537,x=0.483,z=-0.172)───PhXZ(a=0.498,x=0.5,z=-0.498)─────iSwap────────────────────────────────────────iSwap───────PhXZ(a=-1,x=0.5,z=0.748)───────────────────────────────┼──────────────────────────────────────────┼──────────────────────────────────────┼────────────────────────────────────────────M───\n", " │ │ │ │ │ │\n", " 2: ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────PhXZ(a=0.966,x=0.864,z=-0.966)───iSwap^0.5───PhXZ(a=2.22e-16,x=0.364,z=1.0)───iSwap^0.5───PhXZ(a=-1,x=0.864,z=0.216)───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.648,z=0.625)─────M───\n", "----------------------------------------\n", " Transformer-5: merge_single_qubit_moments_to_phxz\n", " Initial Circuit:\n", " 0: ───PhXZ(a=-1,x=0.676,z=-0.5)────────iSwap───────PhXZ(a=-0.5,x=0,z=-1)────iSwap───────PhXZ(a=0,x=0.5,z=-0.574)────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────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───\n", " │ │ │ │ │ │\n", " 1: ───PhXZ(a=0.709,x=0.136,z=-0.709)───iSwap^0.5───PhXZ(a=-1,x=0.364,z=0)───iSwap^0.5───PhXZ(a=0.537,x=0.483,z=-0.172)───PhXZ(a=0.498,x=0.5,z=-0.498)─────iSwap────────────────────────────────────────iSwap───────PhXZ(a=-1,x=0.5,z=0.748)───────────────────────────────┼──────────────────────────────────────────┼──────────────────────────────────────┼────────────────────────────────────────────M───\n", " │ │ │ │ │ │\n", " 2: ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────PhXZ(a=0.966,x=0.864,z=-0.966)───iSwap^0.5───PhXZ(a=2.22e-16,x=0.364,z=1.0)───iSwap^0.5───PhXZ(a=-1,x=0.864,z=0.216)───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.648,z=0.625)─────M───\n", "\n", "\n", " Final Circuit:\n", " 0: ───PhXZ(a=-1,x=0.676,z=-0.5)────────iSwap───────PhXZ(a=-0.5,x=0,z=-1)────iSwap───────PhXZ(a=0,x=0.5,z=-0.574)──────────────────────────────────────────────────────────────────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───\n", " │ │ │ │ │ │\n", " 1: ───PhXZ(a=0.709,x=0.136,z=-0.709)───iSwap^0.5───PhXZ(a=-1,x=0.364,z=0)───iSwap^0.5───PhXZ(a=0.999,x=0.866,z=0.542)────iSwap────────────────────────────────────────iSwap───────PhXZ(a=-1,x=0.5,z=0.748)─────────┼──────────────────────────────────────────┼──────────────────────────────────────┼────────────────────────────────────────────M───\n", " │ │ │ │ │ │\n", " 2: ─────────────────────────────────────────────────────────────────────────────────────PhXZ(a=0.966,x=0.864,z=-0.966)───iSwap^0.5───PhXZ(a=2.22e-16,x=0.364,z=1.0)───iSwap^0.5───PhXZ(a=-0.299,x=0.584,z=0.907)───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.648,z=0.625)─────M───\n", "----------------------------------------\n", " Transformer-6: drop_negligible_operations\n", " Initial Circuit:\n", " 0: ───PhXZ(a=-1,x=0.676,z=-0.5)────────iSwap───────PhXZ(a=-0.5,x=0,z=-1)────iSwap───────PhXZ(a=0,x=0.5,z=-0.574)──────────────────────────────────────────────────────────────────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───\n", " │ │ │ │ │ │\n", " 1: ───PhXZ(a=0.709,x=0.136,z=-0.709)───iSwap^0.5───PhXZ(a=-1,x=0.364,z=0)───iSwap^0.5───PhXZ(a=0.999,x=0.866,z=0.542)────iSwap────────────────────────────────────────iSwap───────PhXZ(a=-1,x=0.5,z=0.748)─────────┼──────────────────────────────────────────┼──────────────────────────────────────┼────────────────────────────────────────────M───\n", " │ │ │ │ │ │\n", " 2: ─────────────────────────────────────────────────────────────────────────────────────PhXZ(a=0.966,x=0.864,z=-0.966)───iSwap^0.5───PhXZ(a=2.22e-16,x=0.364,z=1.0)───iSwap^0.5───PhXZ(a=-0.299,x=0.584,z=0.907)───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.648,z=0.625)─────M───\n", "\n", "\n", " Final Circuit:\n", " 0: ───PhXZ(a=-1,x=0.676,z=-0.5)────────iSwap───────PhXZ(a=-0.5,x=0,z=-1)────iSwap───────PhXZ(a=0,x=0.5,z=-0.574)──────────────────────────────────────────────────────────────────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───\n", " │ │ │ │ │ │\n", " 1: ───PhXZ(a=0.709,x=0.136,z=-0.709)───iSwap^0.5───PhXZ(a=-1,x=0.364,z=0)───iSwap^0.5───PhXZ(a=0.999,x=0.866,z=0.542)────iSwap────────────────────────────────────────iSwap───────PhXZ(a=-1,x=0.5,z=0.748)─────────┼──────────────────────────────────────────┼──────────────────────────────────────┼────────────────────────────────────────────M───\n", " │ │ │ │ │ │\n", " 2: ─────────────────────────────────────────────────────────────────────────────────────PhXZ(a=0.966,x=0.864,z=-0.966)───iSwap^0.5───PhXZ(a=2.22e-16,x=0.364,z=1.0)───iSwap^0.5───PhXZ(a=-0.299,x=0.584,z=0.907)───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.648,z=0.625)─────M───\n", "----------------------------------------\n", " Transformer-7: drop_empty_moments\n", " Initial Circuit:\n", " 0: ───PhXZ(a=-1,x=0.676,z=-0.5)────────iSwap───────PhXZ(a=-0.5,x=0,z=-1)────iSwap───────PhXZ(a=0,x=0.5,z=-0.574)──────────────────────────────────────────────────────────────────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───\n", " │ │ │ │ │ │\n", " 1: ───PhXZ(a=0.709,x=0.136,z=-0.709)───iSwap^0.5───PhXZ(a=-1,x=0.364,z=0)───iSwap^0.5───PhXZ(a=0.999,x=0.866,z=0.542)────iSwap────────────────────────────────────────iSwap───────PhXZ(a=-1,x=0.5,z=0.748)─────────┼──────────────────────────────────────────┼──────────────────────────────────────┼────────────────────────────────────────────M───\n", " │ │ │ │ │ │\n", " 2: ─────────────────────────────────────────────────────────────────────────────────────PhXZ(a=0.966,x=0.864,z=-0.966)───iSwap^0.5───PhXZ(a=2.22e-16,x=0.364,z=1.0)───iSwap^0.5───PhXZ(a=-0.299,x=0.584,z=0.907)───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.648,z=0.625)─────M───\n", "\n", "\n", " Final Circuit:\n", " 0: ───PhXZ(a=-1,x=0.676,z=-0.5)────────iSwap───────PhXZ(a=-0.5,x=0,z=-1)────iSwap───────PhXZ(a=0,x=0.5,z=-0.574)──────────────────────────────────────────────────────────────────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───\n", " │ │ │ │ │ │\n", " 1: ───PhXZ(a=0.709,x=0.136,z=-0.709)───iSwap^0.5───PhXZ(a=-1,x=0.364,z=0)───iSwap^0.5───PhXZ(a=0.999,x=0.866,z=0.542)────iSwap────────────────────────────────────────iSwap───────PhXZ(a=-1,x=0.5,z=0.748)─────────┼──────────────────────────────────────────┼──────────────────────────────────────┼────────────────────────────────────────────M───\n", " │ │ │ │ │ │\n", " 2: ─────────────────────────────────────────────────────────────────────────────────────PhXZ(a=0.966,x=0.864,z=-0.966)───iSwap^0.5───PhXZ(a=2.22e-16,x=0.364,z=1.0)───iSwap^0.5───PhXZ(a=-0.299,x=0.584,z=0.907)───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.648,z=0.625)─────M───\n", "----------------------------------------\n" ] } ], "source": [ "context = cirq.TransformerContext(logger=cirq.TransformerLogger())\n", "gateset = cirq.SqrtIswapTargetGateset()\n", "sqrt_iswap_circuit = cirq.optimize_for_target_gateset(circuit, gateset=gateset, context=context)\n", "cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent(circuit, sqrt_iswap_circuit)\n", "context.logger.show()" ] }, { "cell_type": "markdown", "metadata": { "id": "aTNKFcPesNpy" }, "source": [ "# Summary\n", "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. \n", "\n", "Try using these transformers to compile your circuits and refer to the API reference docs of `cirq.TRANSFORMER` for more details. " ] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "transformers.ipynb", "toc_visible": true }, "kernelspec": { "display_name": "Python 3", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.14" } }, "nbformat": 4, "nbformat_minor": 0 }