diff --git a/circuit_knitting/cutting/cutting_experiments.py b/circuit_knitting/cutting/cutting_experiments.py index f4945409e..a6fc928a4 100644 --- a/circuit_knitting/cutting/cutting_experiments.py +++ b/circuit_knitting/cutting/cutting_experiments.py @@ -37,6 +37,7 @@ def generate_cutting_experiments( circuits: QuantumCircuit | dict[Hashable, QuantumCircuit], observables: PauliList | dict[Hashable, PauliList], num_samples: int | float, + basis_gate_set: str | None = None, ) -> tuple[ list[QuantumCircuit] | dict[Hashable, list[QuantumCircuit]], list[tuple[float, WeightType]], @@ -64,6 +65,8 @@ def generate_cutting_experiments( num_samples: The number of samples to draw from the quasi-probability distribution. If set to infinity, the weights will be generated rigorously rather than by sampling from the distribution. + basis_gate_set: A QPU architecture for which the sampled instructions should be + translated. Supported inputs are: {"heron", "eagle", None} Returns: A tuple containing the cutting experiments and their associated coefficients. If the input circuits is a :class:`QuantumCircuit` instance, the output subexperiments @@ -151,7 +154,11 @@ def generate_cutting_experiments( for j, cog in enumerate(so.groups): new_qc = _append_measurement_register(subcircuit, cog) decompose_qpd_instructions( - new_qc, subcirc_qpd_gate_ids[label], map_ids_tmp, inplace=True + new_qc, + subcirc_qpd_gate_ids[label], + map_ids_tmp, + basis_gate_set=basis_gate_set, + inplace=True, ) _append_measurement_circuit(new_qc, cog, inplace=True) subexperiments_dict[label].append(new_qc) diff --git a/circuit_knitting/cutting/qpd/__init__.py b/circuit_knitting/cutting/qpd/__init__.py index ca0a6d334..44f9f4785 100644 --- a/circuit_knitting/cutting/qpd/__init__.py +++ b/circuit_knitting/cutting/qpd/__init__.py @@ -24,11 +24,13 @@ TwoQubitQPDGate, QPDMeasure, ) +from .translation import translate_qpd_gate __all__ = [ "qpdbasis_from_instruction", "generate_qpd_weights", "decompose_qpd_instructions", + "translate_qpd_gate", "QPDBasis", "BaseQPDGate", "TwoQubitQPDGate", diff --git a/circuit_knitting/cutting/qpd/decompose.py b/circuit_knitting/cutting/qpd/decompose.py index 035b2101e..7cd1fa3e5 100644 --- a/circuit_knitting/cutting/qpd/decompose.py +++ b/circuit_knitting/cutting/qpd/decompose.py @@ -23,6 +23,7 @@ ) from .instructions import BaseQPDGate, TwoQubitQPDGate +from .translation import translate_qpd_gate def decompose_qpd_instructions( @@ -30,6 +31,7 @@ def decompose_qpd_instructions( instruction_ids: Sequence[Sequence[int]], map_ids: Sequence[int] | None = None, *, + basis_gate_set: str | None = None, inplace: bool = False, ) -> QuantumCircuit: r""" @@ -43,6 +45,9 @@ def decompose_qpd_instructions( map_ids: Indices to a specific linear mapping to be applied to the decompositions in the circuit. If no map IDs are provided, the circuit will be decomposed randomly according to the decompositions' joint probability distribution. + basis_gate_set: A QPU architecture for which the sampled instructions should be + translated. Supported inputs are: {"heron", "eagle", None} + inplace: Whether to modify the input circuit directly Returns: Circuit which has had all its :class:`BaseQPDGate` instances decomposed into local operations. @@ -76,7 +81,7 @@ def decompose_qpd_instructions( circuit.data[gate_id].operation.basis_id = map_ids[i] # Convert all instances of BaseQPDGate in the circuit to Qiskit instructions - _decompose_qpd_instructions(circuit, instruction_ids) + _decompose_qpd_instructions(circuit, instruction_ids, basis_gate_set=basis_gate_set) return circuit @@ -170,6 +175,7 @@ def _decompose_qpd_instructions( circuit: QuantumCircuit, instruction_ids: Sequence[Sequence[int]], inplace: bool = True, + basis_gate_set: str | None = None, ) -> QuantumCircuit: """Decompose all BaseQPDGate instances, ignoring QPDMeasure().""" if not inplace: @@ -198,6 +204,10 @@ def _decompose_qpd_instructions( data_id_offset += 1 circuit.data.insert(i + data_id_offset, inst2) + # Get equivalence library + if basis_gate_set is not None: + basis_gate_set = basis_gate_set.lower() + # Decompose all the QPDGates (should all be single qubit now) into Qiskit operations new_instruction_ids = [] for i, inst in enumerate(circuit.data): @@ -214,7 +224,13 @@ def _decompose_qpd_instructions( for data in inst.operation.definition.data: # Can ignore clbits here, as QPDGates don't use clbits directly assert data.clbits == () - tmp_data.append(CircuitInstruction(data.operation, qubits=[qubits[0]])) + if basis_gate_set is None or data.operation.name in {"qpd_measure"}: + tmp_data.append(CircuitInstruction(data.operation, qubits=[qubits[0]])) + else: + equiv_circ = translate_qpd_gate(data.operation, basis_gate_set) + for d in equiv_circ.data: + tmp_data.append(CircuitInstruction(d.operation, qubits=[qubits[0]])) + # Replace QPDGate with local operations if tmp_data: # Overwrite the QPDGate with first instruction diff --git a/circuit_knitting/cutting/qpd/translation.py b/circuit_knitting/cutting/qpd/translation.py new file mode 100644 index 000000000..e9c86bc8b --- /dev/null +++ b/circuit_knitting/cutting/qpd/translation.py @@ -0,0 +1,227 @@ +# This code is a Qiskit project. + +# (C) Copyright IBM 2024. + +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Equivalence utilities. + +.. currentmodule:: circuit_knitting.cutting.qpd.equivalence + +.. autosummary:: + :toctree: ../stubs/ + +""" +from __future__ import annotations + +from collections.abc import Callable + +import numpy as np +from qiskit.circuit import QuantumCircuit, QuantumRegister, Gate +from qiskit.circuit.library.standard_gates import ( + RZGate, + XGate, + YGate, + ZGate, + HGate, + SGate, + SdgGate, + SXGate, + SXdgGate, + TGate, + TdgGate, + RXGate, + RYGate, + PhaseGate, +) + + +_equivalence_from_gate_funcs: dict[str, Callable[[Gate], QuantumCircuit]] = {} + + +def _register_gate(*args): + def g(f): + for name in args: + _equivalence_from_gate_funcs[name] = f + return f + + return g + + +def translate_qpd_gate(gate: Gate, basis_gate_set: str, /) -> QuantumCircuit: + """ + Translate a ``gate`` into a given basis gate set. + + This function is designed to handle only the gates to which a :class:`.QPDBasis` can + decompose; therefore, not every Qiskit gate is supported by this function. + + Args: + gate: The gate to translate + + Returns: + A :class:`qiskit.QuantumCircuit` implementing the gate in the given basis gate set. + + Raises: + ValueError: Unsupported basis gate set + ValueError: Unsupported gate + """ + # We otherwise ignore this arg for now since our only two equivalences are equivalent :) + if basis_gate_set not in {"heron", "eagle"}: + raise ValueError(f"Unknown basis gate set: {basis_gate_set}") + try: + f = _equivalence_from_gate_funcs[gate.name] + except KeyError as exc: + raise ValueError(f"Cannot translate gate: {gate.name}") from exc + else: + return f(gate) + + +# XGate +@_register_gate("x") +def _(gate: XGate): + q = QuantumRegister(1, "q") + def_x = QuantumCircuit(q) + def_x.append(gate, [0], []) + return def_x + + +# SXGate +@_register_gate("sx") +def _(gate: SXGate): + q = QuantumRegister(1, "q") + def_sx = QuantumCircuit(q) + def_sx.append(gate, [0], []) + return def_sx + + +# RZGate +@_register_gate("rz") +def _(gate: RZGate): + q = QuantumRegister(1, "q") + def_rz = QuantumCircuit(q) + def_rz.append(gate, [0], []) + return def_rz + + +# YGate +@_register_gate("y") +def _(_: YGate): + q = QuantumRegister(1, "q") + def_y = QuantumCircuit(q) + for inst in [RZGate(np.pi), XGate()]: + def_y.append(inst, [0], []) + return def_y + + +# ZGate +@_register_gate("z") +def _(_: ZGate): + q = QuantumRegister(1, "q") + def_z = QuantumCircuit(q) + def_z.append(RZGate(np.pi), [0], []) + return def_z + + +# HGate +@_register_gate("h") +def _(_: HGate): + q = QuantumRegister(1, "q") + def_h = QuantumCircuit(q) + for inst in [RZGate(np.pi / 2), SXGate(), RZGate(np.pi / 2)]: + def_h.append(inst, [0], []) + return def_h + + +# SGate +@_register_gate("s") +def _(_: SGate): + q = QuantumRegister(1, "q") + def_s = QuantumCircuit(q) + def_s.append(RZGate(np.pi / 2), [0], []) + return def_s + + +# SdgGate +@_register_gate("sdg") +def _(_: SdgGate): + q = QuantumRegister(1, "q") + def_sdg = QuantumCircuit(q) + def_sdg.append(RZGate(-np.pi / 2), [0], []) + return def_sdg + + +# SXdgGate +@_register_gate("sxdg") +def _(_: SXdgGate): + q = QuantumRegister(1, "q") + def_sxdg = QuantumCircuit(q) + for inst in [ + RZGate(np.pi / 2), + RZGate(np.pi / 2), + SXGate(), + RZGate(np.pi / 2), + RZGate(np.pi / 2), + ]: + def_sxdg.append(inst, [0], []) + return def_sxdg + + +# TGate +@_register_gate("t") +def _(_: TGate): + q = QuantumRegister(1, "q") + def_t = QuantumCircuit(q) + def_t.append(RZGate(np.pi / 4), [0], []) + return def_t + + +# TdgGate +@_register_gate("tdg") +def _(_: TdgGate): + q = QuantumRegister(1, "q") + def_tdg = QuantumCircuit(q) + def_tdg.append(RZGate(-np.pi / 4), [0], []) + return def_tdg + + +# RXGate +@_register_gate("rx") +def _(gate: RXGate): + q = QuantumRegister(1, "q") + def_rx = QuantumCircuit(q) + param = gate.params[0] + for inst in [ + RZGate(np.pi / 2), + SXGate(), + RZGate(param + np.pi), + SXGate(), + RZGate(5 * np.pi / 2), + ]: + def_rx.append(inst, [0], []) + return def_rx + + +# RYGate +@_register_gate("ry") +def _(gate: RYGate): + q = QuantumRegister(1, "q") + def_ry = QuantumCircuit(q) + param = gate.params[0] + for inst in [SXGate(), RZGate(param + np.pi), SXGate(), RZGate(3 * np.pi)]: + def_ry.append(inst, [0], []) + + +# PhaseGate +@_register_gate("p") +def _(gate: PhaseGate): + q = QuantumRegister(1, "q") + def_p = QuantumCircuit(q) + param = gate.params[0] + def_p.append(RZGate(param), [0], []) + return def_p diff --git a/docs/circuit_cutting/how-tos/how_to_translate_subexperiments_to_basis_gate_set.ipynb b/docs/circuit_cutting/how-tos/how_to_translate_subexperiments_to_basis_gate_set.ipynb new file mode 100644 index 000000000..4c4e75279 --- /dev/null +++ b/docs/circuit_cutting/how-tos/how_to_translate_subexperiments_to_basis_gate_set.ipynb @@ -0,0 +1,148 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f9e40036", + "metadata": {}, + "source": [ + "## How to translate sampled instructions\n", + "\n", + "This how-to guide is intended to show users how they can generate subexperiments which are already translated to a specified QPU architecture. This is useful as it prevents the need for transpiling each individual subexperiment. Users should now be able to transpile the cut circuit a single time and generate subexperiments which are already transpiled for the backend." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "072055cb", + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit import QuantumCircuit\n", + "from qiskit.quantum_info import PauliList\n", + "\n", + "from circuit_knitting.cutting import (\n", + " partition_problem,\n", + " generate_cutting_experiments,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "940334fd", + "metadata": {}, + "source": [ + "Prepare inputs to `generate_cutting_experiments`" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "dc4af922", + "metadata": {}, + "outputs": [], + "source": [ + "circuit = QuantumCircuit(2)\n", + "circuit.h(0)\n", + "circuit.cx(0, 1)\n", + "observables = PauliList([\"ZZ\"])\n", + "partitioned_problem = partition_problem(\n", + " circuit=circuit, partition_labels=\"AB\", observables=observables\n", + ")\n", + "subcircuits = partitioned_problem.subcircuits\n", + "subobservables = partitioned_problem.subobservables" + ] + }, + { + "cell_type": "markdown", + "id": "d6361a9d", + "metadata": {}, + "source": [ + "Call `generate_cutting_experiments` and don't specify any translation." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "d095701f", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "subexperiments, coefficients = generate_cutting_experiments(\n", + " circuits=subcircuits,\n", + " observables=subobservables,\n", + " num_samples=1000,\n", + ")\n", + "subexperiments[\"A\"][0].draw(\"mpl\", style=\"iqp\", scale=0.8)" + ] + }, + { + "cell_type": "markdown", + "id": "bc59b1be", + "metadata": {}, + "source": [ + "Now call `generate_cutting_experiments` and translate the sampled instructions to the specified architecture. Valid input arguments are `\"heron\"` and `\"eagle\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "7a74f709", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "subexperiments, coefficients = generate_cutting_experiments(\n", + " circuits=subcircuits,\n", + " observables=subobservables,\n", + " num_samples=1000,\n", + " basis_gate_set=\"eagle\",\n", + ")\n", + "subexperiments[\"A\"][0].draw(\"mpl\", style=\"iqp\", scale=0.8)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "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.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/releasenotes/notes/translate-sampled-gates-06b301581986fec4.yaml b/releasenotes/notes/translate-sampled-gates-06b301581986fec4.yaml new file mode 100644 index 000000000..3a4243012 --- /dev/null +++ b/releasenotes/notes/translate-sampled-gates-06b301581986fec4.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + The functions, :func:`.generate_qpd_experiments` and :func:`.decompose_qpd_instructions` now take an optional kwarg, ``basis_gate_set``, which may be used to specify a QPU architecture for which to translate the sampled gates during creation of the subexperiments. This is useful for users who wish to transpile their cut circuit once and have the sampled local operations in the subexperiments be specified in terms of a native gate set. This prevents the need for any transpilation of subexperiments before running on the QPU. The accepted inputs are: ``{"eagle", "heron"}``. diff --git a/test/cutting/qpd/test_qpd.py b/test/cutting/qpd/test_qpd.py index a3478d040..3b9ef8597 100644 --- a/test/cutting/qpd/test_qpd.py +++ b/test/cutting/qpd/test_qpd.py @@ -167,6 +167,17 @@ def test_decompose_qpd_instructions(self): decomp_circ = decompose_qpd_instructions(circ, [[0]], map_ids=[0]) circ_compare.add_register(ClassicalRegister(1, name="qpd_measurements")) self.assertEqual(decomp_circ, circ_compare) + with self.subTest("Single QPD gate with translation"): + eagle_basis_gate_set = {"id", "rz", "sx", "x", "measure"} + circ = QuantumCircuit(2) + qpd_basis = QPDBasis.from_instruction(RXXGate(np.pi / 3)) + qpd_gate = TwoQubitQPDGate(qpd_basis) + circ.data.append(CircuitInstruction(qpd_gate, qubits=[0, 1])) + decomp_circ = decompose_qpd_instructions( + circ, [[0]], map_ids=[1], basis_gate_set="eagle" + ) + for inst in decomp_circ.data: + assert inst.operation.name in eagle_basis_gate_set with self.subTest("Incorrect map index size"): with pytest.raises(ValueError) as e_info: decomp_circ = decompose_qpd_instructions( diff --git a/test/cutting/qpd/test_translation.py b/test/cutting/qpd/test_translation.py new file mode 100644 index 000000000..a08bb7814 --- /dev/null +++ b/test/cutting/qpd/test_translation.py @@ -0,0 +1,49 @@ +# This code is a Qiskit project. + +# (C) Copyright IBM 2024. + +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Tests for QPD gate translation.""" + +import unittest + +import pytest +import numpy as np +from qiskit.circuit import Parameter +from qiskit.circuit.library.standard_gates import SdgGate, U3Gate, PhaseGate + +from circuit_knitting.cutting.qpd import translate_qpd_gate + + +class TestQPDGateTranslation(unittest.TestCase): + def test_equivalence_heron(self): + equiv = translate_qpd_gate(SdgGate(), "heron") + assert equiv.data[0].operation.name == "rz" + assert equiv.data[0].operation.params == [-np.pi / 2] + + def test_equivalence_eagle(self): + equiv = translate_qpd_gate(PhaseGate(1.0), "eagle") + assert equiv.data[0].operation.name == "rz" + assert equiv.data[0].operation.params == [1.0] + + def test_unassigned_param(self): + param = Parameter("θ") + equiv = translate_qpd_gate(PhaseGate(param), "eagle") + assert equiv.data[0].operation.name == "rz" + assert equiv.data[0].operation.params == [param] + + def test_equivalence_unsupported_basis(self): + with pytest.raises(ValueError) as e_info: + translate_qpd_gate(SdgGate(), "falcon") + assert e_info.value.args[0] == "Unknown basis gate set: falcon" + + def test_equivalence_unsupported_gate(self): + with pytest.raises(ValueError) as e_info: + translate_qpd_gate(U3Gate(1.0, 1.0, 1.0), "eagle") + assert e_info.value.args[0] == "Cannot translate gate: u3" diff --git a/test/cutting/test_cutting_experiments.py b/test/cutting/test_cutting_experiments.py index 47c6e15c0..a232d8a7b 100644 --- a/test/cutting/test_cutting_experiments.py +++ b/test/cutting/test_cutting_experiments.py @@ -89,7 +89,33 @@ def test_generate_cutting_experiments(self): assert len(coeffs) == len(subexperiments["A"]) for circ in subexperiments["A"]: assert isinstance(circ, QuantumCircuit) - + with self.subTest("translation"): + eagle_basis_gate_set = {"id", "rz", "sx", "x", "measure"} + qc = QuantumCircuit(2) + qc.append( + TwoQubitQPDGate(QPDBasis.from_instruction(CXGate()), label="cut_cx"), + qargs=[0, 1], + ) + comp_coeffs = [ + (0.5, WeightType.EXACT), + (0.5, WeightType.EXACT), + (0.5, WeightType.EXACT), + (-0.5, WeightType.EXACT), + (0.5, WeightType.EXACT), + (-0.5, WeightType.EXACT), + ] + subexperiments, coeffs = generate_cutting_experiments( + qc, + PauliList(["ZZ"]), + np.inf, + basis_gate_set="eagle", + ) + assert coeffs == comp_coeffs + assert len(coeffs) == len(subexperiments) + for exp in subexperiments: + assert isinstance(exp, QuantumCircuit) + for inst in exp.data: + assert inst.operation.name in eagle_basis_gate_set with self.subTest("test bad num_samples"): qc = QuantumCircuit(4) with pytest.raises(ValueError) as e_info: diff --git a/test/cutting/test_cutting_roundtrip.py b/test/cutting/test_cutting_roundtrip.py index 6ea7547cb..ba52983e9 100644 --- a/test/cutting/test_cutting_roundtrip.py +++ b/test/cutting/test_cutting_roundtrip.py @@ -147,8 +147,14 @@ def test_cutting_exact_reconstruction(example_circuit): subcircuits, bases, subobservables = partition_problem( qc, "AAB", observables=observables_nophase ) + basis_gate_set = [None, "eagle", "heron"][ + np.random.choice([0, 1, 2], p=[0.5, 0.25, 0.25]) + ] subexperiments, coefficients = generate_cutting_experiments( - subcircuits, subobservables, num_samples=np.inf + subcircuits, + subobservables, + num_samples=np.inf, + basis_gate_set=basis_gate_set, ) if np.random.randint(2): # Re-use a single sampler