Replace Gate with Known Identity in Quantum Circuit

I have a quantum circuit with 3 two-qubits gates XX, YY, and ZZ (as defined in the qiskit circuit library) with arbitrary angles on each gate. I'd like to replace the YY and ZZ gates with XXs. I know how to do this mathematically with RZ and RY single qubit rotations, but I don't know how to programmically get Qiskit to do this switch. Unfortunately, the transpiler converts the YY and ZZ into CNOTs and then into XXs (evaluated at $$\pi/2$$). I want to avoid this though and preserve the arbitrary angles on YY and ZZ when converting them into XX gates. Is there a way I can tell the transpiler how to handle YY and ZZ gates?

Or maybe remove the YY and ZZ gates from the quantum circuit from QuantumCircuit.data and insert XX gates (with the appropriate RZ and RY rotations)?

The proper way to implement this would be writing your own transpiler pass, a TransformationPass to be more precise, which replaces the RYY and RZZ gates with your decomposition. If you want to decompose it into RXX gates the following should do the trick:

from qiskit.circuit import QuantumCircuit
from qiskit.converters import circuit_to_dag
from qiskit.transpiler import TransformationPass

class RXXTranslator(TransformationPass):
"""A transpiler pass to replace RYY and RZZ gates with RXX gates."""

def run(self, dag):
"""Run the pass."""

# iterate over all operations
for node in dag.op_nodes():

# if we hit a RYY or RZZ gate replace it
if node.op.name in ["ryy", "rzz"]:
# get the rotation angle
angle = node.op.params

# calculate the replacement
replacement = QuantumCircuit(2)
if node.op.name == "ryy":
replacement.sdg([0, 1])
else:
replacement.h([0, 1])

replacement.rxx(angle, 0, 1)

if node.op.name == "ryy":
replacement.s([0, 1])
else:
replacement.h([0, 1])

# replace the node with our new decomposition
dag.substitute_node_with_dag(node, circuit_to_dag(replacement))

return dag

circuit = QuantumCircuit(2)
circuit.rxx(0.1, 0, 1)
circuit.ryy(0.2, 0, 1)
circuit.rzz(0.3, 0, 1)

as_rxx = RXXTranslator()(circuit)
print(as_rxx.draw())

which outputs

┌───────────┐┌─────┐┌───────────┐┌───┐┌───┐┌───────────┐┌───┐
q_0: ┤0          ├┤ Sdg ├┤0          ├┤ S ├┤ H ├┤0          ├┤ H ├
│  Rxx(0.1) │├─────┤│  Rxx(0.2) │├───┤├───┤│  Rxx(0.3) │├───┤
q_1: ┤1          ├┤ Sdg ├┤1          ├┤ S ├┤ H ├┤1          ├┤ H ├
└───────────┘└─────┘└───────────┘└───┘└───┘└───────────┘└───┘
• Wow! Thank you so much! I haven't seen examples of making custom transpiler passes before. This works great! Dec 1 '21 at 16:09