Skip to content

Commit

Permalink
Merge fbe683a into 97e1f56
Browse files Browse the repository at this point in the history
  • Loading branch information
nbronn committed Jun 16, 2023
2 parents 97e1f56 + fbe683a commit 720269d
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 33 deletions.
93 changes: 60 additions & 33 deletions qiskit_research/utils/pauli_twirling.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,45 +14,42 @@

import numpy as np
from qiskit.circuit import QuantumRegister
from qiskit.circuit.library import IGate, XGate, YGate, ZGate
from qiskit.circuit.library import (
IGate,
XGate,
YGate,
ZGate,
)
from qiskit.dagcircuit import DAGCircuit
from qiskit.transpiler.basepasses import BasePass, TransformationPass
from qiskit.transpiler.passes import (
CXCancellation,
Optimize1qGatesDecomposition,
)
from qiskit.quantum_info import Pauli, pauli_basis
from qiskit_research.utils.pulse_scaling import BASIS_GATES

I = IGate()
X = XGate()
Y = YGate()
Z = ZGate()

# this list consists of the 2-qubit rotation gates
TWO_QUBIT_PAULI_GENERATORS = {
"rxx": Pauli("XX"),
"ryy": Pauli("YY"),
"rzx": Pauli("XZ"),
"rzz": Pauli("ZZ"),
"secr": Pauli("XZ"),
}

# this dictionary stores the twirl sets for each supported gate
# each key is the name of a supported gate
# each value is a tuple that represents the twirl set for the gate
# the twirl set is a list of (before, after) pairs describing twirl gates
# "before" and "after" are tuples of single-qubit gates to be applied
# before and after the gate to be twirled
TWIRL_GATES = {
"rzx": (
((I, I), (I, I)),
((X, Z), (X, Z)),
((Y, Y), (Y, Y)),
((Z, X), (Z, X)),
),
"secr": (
((I, I), (I, I)),
((X, Z), (X, Z)),
((X, Y), (X, Y)),
((Z, Z), (Z, Z)),
),
"rzz": (
((I, I), (I, I)),
((X, X), (X, X)),
((Y, Y), (Y, Y)),
((Z, Z), (Z, Z)),
),
"cx": (
((I, I), (I, I)),
((I, X), (I, X)),
Expand Down Expand Up @@ -96,7 +93,7 @@ def __init__(
seed: Seed for the pseudorandom number generator.
"""
if gates_to_twirl is None:
gates_to_twirl = TWIRL_GATES.keys()
gates_to_twirl = [*(TWIRL_GATES | TWO_QUBIT_PAULI_GENERATORS).keys()]
self.gates_to_twirl = gates_to_twirl
self.rng = parse_random_seed(seed)
super().__init__()
Expand All @@ -107,19 +104,49 @@ def run(
) -> DAGCircuit:
for run in dag.collect_runs(list(self.gates_to_twirl)):
for node in run:
twirl_gates = TWIRL_GATES[node.op.name]
(before0, before1), (after0, after1) = twirl_gates[
self.rng.integers(len(twirl_gates))
]
mini_dag = DAGCircuit()
register = QuantumRegister(2)
mini_dag.add_qreg(register)
mini_dag.apply_operation_back(before0, [register[0]])
mini_dag.apply_operation_back(before1, [register[1]])
mini_dag.apply_operation_back(node.op, [register[0], register[1]])
mini_dag.apply_operation_back(after0, [register[0]])
mini_dag.apply_operation_back(after1, [register[1]])
dag.substitute_node_with_dag(node, mini_dag)
if node.op.name in TWO_QUBIT_PAULI_GENERATORS:
mini_dag = DAGCircuit()
q0, q1 = node.qargs
mini_dag.add_qreg(q0.register)

theta = node.op.params[0]
this_pauli = Pauli(
self.rng.choice(pauli_basis(2).to_labels())
).to_instruction()
if TWO_QUBIT_PAULI_GENERATORS[node.op.name].anticommutes(
this_pauli
):
theta *= -1

new_op = node.op.copy()
new_op.params[0] = theta

mini_dag.apply_operation_back(this_pauli, [q0, q1])
mini_dag.apply_operation_back(new_op, [q0, q1])
if node.op.name == "secr":
mini_dag.apply_operation_back(X, [q0])
mini_dag.apply_operation_back(this_pauli, [q0, q1])
if node.op.name == "secr":
mini_dag.apply_operation_back(X, [q0])

dag.substitute_node_with_dag(node, mini_dag, wires=[q0, q1])

elif node.op.name in TWIRL_GATES:
twirl_gates = TWIRL_GATES[node.op.name]
(before0, before1), (after0, after1) = twirl_gates[
self.rng.integers(len(twirl_gates))
]
mini_dag = DAGCircuit()
register = QuantumRegister(2)
mini_dag.add_qreg(register)
mini_dag.apply_operation_back(before0, [register[0]])
mini_dag.apply_operation_back(before1, [register[1]])
mini_dag.apply_operation_back(node.op, [register[0], register[1]])
mini_dag.apply_operation_back(after0, [register[0]])
mini_dag.apply_operation_back(after1, [register[1]])
dag.substitute_node_with_dag(node, mini_dag)
else:
raise TypeError(f"Unknown how to twirl Instruction {node.op}.")
return dag


Expand Down
8 changes: 8 additions & 0 deletions test/utils/test_pauli_twirling.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from qiskit.circuit.library import CXGate
from qiskit.quantum_info import Operator
from qiskit_research.utils.convenience import add_pauli_twirls
from qiskit_research.utils.gates import SECRGate
from qiskit_research.utils.pauli_twirling import TWIRL_GATES


Expand Down Expand Up @@ -53,6 +54,13 @@ def test_add_pauli_twirls(self):
circuit.rzz(phi, 1, 2)
circuit.h(2)
circuit.cx(0, 1)

circuit.h(1)
circuit.append(SECRGate(theta), [0, 1])
circuit.h(0)
circuit.append(SECRGate(phi), [2, 0])
circuit.h(2)

twirled_circs = add_pauli_twirls(circuit, num_twirled_circuits=5)
more_twirled_circs = add_pauli_twirls(
[circuit], num_twirled_circuits=5, seed=1234
Expand Down

0 comments on commit 720269d

Please sign in to comment.