Source code for spin_pulse.transpilation.passes.rzz_echo
# --------------------------------------------------------------------------------------
# This code is part of SpinPulse.
#
# (C) Copyright Quobly 2025.
#
# 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.
# --------------------------------------------------------------------------------------
""""""
import numpy as np
from qiskit.circuit import QuantumRegister
from qiskit.circuit.library import RXGate, RZZGate
from qiskit.dagcircuit import DAGCircuit
from qiskit.transpiler.basepasses import TransformationPass
# Key written into ``DAGCircuit.metadata`` once the echo has been applied, so the
# pass can be a no-op if it runs again (e.g. once during ``HardwareSpecs.gate_transpile``
# and once again inside ``PulseCircuit.from_dag_circuit``).
_ECHOED_METADATA_KEY = "spin_pulse_rzz_echoed"
[docs]
class RZZEchoPass(TransformationPass):
r"""Echo :math:`R_{ZZ}` gates with :math:`X` pulses to mitigate Stark shifts.
This transpiler pass replaces each two-qubit :math:`R_{ZZ}` gate by an echoed
sequence consisting of interleaved :math:`\pi` rotations around the :math:`x` axis and
:math:`R_{ZZ}` gates with half the original angle. The echo sequence is applied
locally on each occurrence of an RZZ gate in the input DAG circuit.
The pass is idempotent: it records a marker in ``dag.metadata`` and returns the
DAG untouched if it is run a second time.
"""
[docs]
def run(
self,
dag: DAGCircuit,
) -> DAGCircuit:
r"""Apply the :math:`R_{ZZ}` echo transformation to all :math:`R_{ZZ}` gates in the DAG.
Each :math:`R_{ZZ}(\theta)` operation is replaced by the following pattern
on the two involved qubits:
- :math:`R_X(\pi)` on both qubits
- :math:`R_{ZZ}(\theta / 2)`
- :math:`R_X(\pi)` on both qubits
- :math:`R_{ZZ}(\theta / 2)`
All other operations are left unchanged.
Parameters:
dag (DAGCircuit): Input circuit represented as a DAG on which
the transformation is applied.
Returns:
DAGCircuit: New DAG circuit where each :math:`R_{ZZ}` gate has been replaced by the echoed :math:`R_{ZZ}` sequence.
"""
metadata = dag.metadata
if metadata is None:
metadata = {}
elif not isinstance(metadata, dict):
# Not a real metadata mapping (e.g. a test double); leave the DAG untouched.
return dag
elif metadata.get(_ECHOED_METADATA_KEY):
# Already echoed (e.g. by HardwareSpecs.gate_transpile) -- do not echo twice.
return dag
# Work on a fresh dict: ``DAGCircuit.metadata`` aliases the source circuit's
# ``metadata`` (``circuit_to_dag`` shares the reference), so mutating it in
# place would leak the marker back onto the caller's circuit.
metadata = dict(metadata)
for node in dag.op_nodes():
if not node.op.name == "rzz":
continue
theta = node.op.params[0]
# instantiate mini_dag and attach quantum register
mini_dag = DAGCircuit()
register = QuantumRegister(2)
mini_dag.add_qreg(register)
for _ in range(2):
mini_dag.apply_operation_back(RXGate(np.pi), [register[0]])
mini_dag.apply_operation_back(RXGate(np.pi), [register[1]])
mini_dag.apply_operation_back(
RZZGate(theta / 2), [register[0], register[1]]
)
dag.substitute_node_with_dag(node, mini_dag)
metadata[_ECHOED_METADATA_KEY] = True
dag.metadata = metadata
return dag