For custom gates translation rules can be added to the default equivalence library using the SessionEquivalence object.
This is not always enough for the transpiler to compile circuits with custom asymmetric gates. A minimal example with a custom gate, some equivalence rules and 3 circuits to transpile.
import qiskit
from qiskit import QuantumCircuit, transpile
from qiskit.circuit import Gate, Measure, Parameter
from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary
from qiskit.circuit.library import CZGate, RXGate, RZGate
from qiskit.providers import BackendV2 as Backend
from qiskit.providers import Options
from qiskit.transpiler import Target
class WGate(Gate):
def __init__(self, label=None):
super().__init__("w", 2, [], label=label)
def _define(self):
qc = QuantumCircuit(2)
q.x(1)
q.cz(0, 1)
q.x(2)
self.definition = qc
def_cz_in = qiskit.QuantumCircuit(2)
def_cz_in.x(1)
def_cz_in.append(WGate(), [0, 1])
def_cz_in.x(1)
SessionEquivalenceLibrary.add_equivalence(CZGate(), def_cz_in)
def_cz_in = qiskit.QuantumCircuit(2)
def_cz_in.x(0)
def_cz_in.append(WGate(), [1, 0])
def_cz_in.x(0)
SessionEquivalenceLibrary.add_equivalence(CZGate(), def_cz_in)
# does rule should allow the transpiler to replace W(0,1) with W(1, 0)
w_swap = qiskit.QuantumCircuit(2)
w_swap.x(0)
w_swap.x(1)
w_swap.append(WGate(), [1, 0])
w_swap.x(0)
w_swap.x(1)
SessionEquivalenceLibrary.add_equivalence(WGate(), w_swap)
_qc = qiskit.QuantumCircuit(2)
_qc.x(1)
_qc.cz(0, 1)
_qc.x(1)
SessionEquivalenceLibrary.add_equivalence(WGate(), _qc)
class MyBackend(Backend):
def __init__(self):
super().__init__()
number_of_qubits = 2
self._target = Target("Test")
theta = Parameter("ϴ")
single_qubit_props = {(qubit,): None for qubit in range(number_of_qubits)}
self._target.add_instruction(RXGate(theta), single_qubit_props)
self._target.add_instruction(RZGate(theta), single_qubit_props)
w_props = {(1, 0): None}
self._target.add_instruction(WGate(), w_props)
meas_props = {(qubit,): None for qubit in range(number_of_qubits)}
self._target.add_instruction(Measure(), meas_props)
self.options.set_validator("shots", (1, 24096))
self.options.set_validator("memory", bool)
@property
def target(self):
return self._target
@property
def max_circuits(self):
return 1024
@classmethod
def _default_options(cls):
return Options(shots=1024, memory=False)
def run(circuits, **kwargs):
pass
if __name__ == "__main__":
d=SessionEquivalenceLibrary.get_entry(WGate())
for c in d:
print(c.draw())
if __name__ == "__main__":
b = MyBackend()
q = QuantumCircuit(2)
q.cx(0, 1)
qc = transpile(q, backend=b) # fine
print(qc.draw())
q = QuantumCircuit(2)
q.append(WGate(), [1, 0])
qc = transpile(q, backend=b) # fine
print(qc.draw())
q = QuantumCircuit(2)
q.append(WGate(), [0, 1])
qc = transpile(q, backend=b) # raises error
print(qc.draw())
What should we add?
For custom gates translation rules can be added to the default equivalence library using the
SessionEquivalence
object. This is not always enough for the transpiler to compile circuits with custom asymmetric gates. A minimal example with a custom gate, some equivalence rules and 3 circuits to transpile.On Slack some suggestions for adding this functionality have already been made. See https://qiskit.slack.com/archives/C7SS31917/p1719247227017329