Skip to content

Commit

Permalink
Merge pull request #1327 from qiboteam/ZNE_get_noisy_circuit
Browse files Browse the repository at this point in the history
Added global unitary folding for get_noisy_circuits
  • Loading branch information
scarrazza authored Aug 21, 2024
2 parents 8fb9af9 + 4455a70 commit 386fe25
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 39 deletions.
107 changes: 69 additions & 38 deletions src/qibo/models/error_mitigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,51 +52,77 @@ def get_gammas(noise_levels, analytical: bool = True):
return zne_coefficients


def get_noisy_circuit(circuit, num_insertions: int, insertion_gate: str = "CNOT"):
def get_noisy_circuit(
circuit, num_insertions: int, global_unitary_folding=True, insertion_gate=None
):
"""Standalone function to generate the noisy circuit with the inverse gate pairs insertions.
Args:
circuit (:class:`qibo.models.circuit.Circuit`): circuit to modify.
num_insertions (int): number of insertion gate pairs to add.
insertion_gate (str, optional): gate to be used in the insertion.
If ``"RX"``, the gate used is :math:``RX(\\pi / 2)``.
Default is ``"CNOT"``.
num_insertions (int): number of insertion gate pairs / global unitary folds to add.
global_unitary_folding (bool): If ``True``, noise is increased by global unitary folding.
If ``False``, local unitary folding is used. Defaults to ``True``.
insertion_gate (str, optional): gate to be folded in the local unitary folding.
If ``RX``, the gate used is :math:``RX(\\pi / 2)``. Otherwise, it is the ``CNOT`` gate.
Returns:
:class:`qibo.models.Circuit`: circuit with the inserted gate pairs.
:class:`qibo.models.Circuit`: circuit with the inserted gate pairs or with global folding.
"""
if insertion_gate not in ("CNOT", "RX"): # pragma: no cover
raise_error(
ValueError,
"Invalid insertion gate specification. Please select between 'CNOT' and 'RX'.",
)
if insertion_gate == "CNOT" and circuit.nqubits < 2: # pragma: no cover
raise_error(
ValueError,
"Provide a circuit with at least 2 qubits when using the 'CNOT' insertion gate. "
+ "Alternatively, try with the 'RX' insertion gate instead.",
)

i_gate = gates.CNOT if insertion_gate == "CNOT" else gates.RX
from qibo import Circuit # pylint: disable=import-outside-toplevel

if global_unitary_folding:

copy_c = Circuit(**circuit.init_kwargs)
for g in circuit.queue:
if not isinstance(g, gates.M):
copy_c.add(g)

noisy_circuit = copy_c

theta = np.pi / 2
noisy_circuit = circuit.__class__(**circuit.init_kwargs)
for _ in range(num_insertions):
noisy_circuit += copy_c.invert() + copy_c

for gate in circuit.queue:
noisy_circuit.add(gate)
for m in circuit.measurements:
noisy_circuit.add(m)

if isinstance(gate, i_gate):
if insertion_gate == "CNOT":
control = gate.control_qubits[0]
target = gate.target_qubits[0]
for _ in range(num_insertions):
noisy_circuit.add(gates.CNOT(control, target))
noisy_circuit.add(gates.CNOT(control, target))
elif gate.init_kwargs["theta"] == theta:
qubit = gate.qubits[0]
for _ in range(num_insertions):
noisy_circuit.add(gates.RX(qubit, theta=theta))
noisy_circuit.add(gates.RX(qubit, theta=-theta))
else:
if insertion_gate is None or insertion_gate not in (
"CNOT",
"RX",
): # pragma: no cover
raise_error(
ValueError,
"Invalid insertion gate specification. Please select between 'CNOT' and 'RX'.",
)
if insertion_gate == "CNOT" and circuit.nqubits < 2: # pragma: no cover
raise_error(
ValueError,
"Provide a circuit with at least 2 qubits when using the 'CNOT' insertion gate. "
+ "Alternatively, try with the 'RX' insertion gate instead.",
)

i_gate = gates.CNOT if insertion_gate == "CNOT" else gates.RX

theta = np.pi / 2
noisy_circuit = Circuit(**circuit.init_kwargs)

for gate in circuit.queue:
noisy_circuit.add(gate)

if isinstance(gate, i_gate):
if insertion_gate == "CNOT":
control = gate.control_qubits[0]
target = gate.target_qubits[0]
for _ in range(num_insertions):
noisy_circuit.add(gates.CNOT(control, target))
noisy_circuit.add(gates.CNOT(control, target))
elif insertion_gate == "RX":
qubit = gate.qubits[0]
theta = gate.init_kwargs["theta"]
for _ in range(num_insertions):
noisy_circuit.add(gates.RX(qubit, theta=theta))
noisy_circuit.add(gates.RX(qubit, theta=-theta))

return noisy_circuit

Expand All @@ -108,6 +134,7 @@ def ZNE(
noise_model=None,
nshots=10000,
solve_for_gammas=False,
global_unitary_folding=True,
insertion_gate="CNOT",
readout=None,
qubit_map=None,
Expand All @@ -129,9 +156,10 @@ def ZNE(
nshots (int, optional): Number of shots. Defaults to :math:`10000`.
solve_for_gammas (bool, optional): If ``True``, explicitly solve the
equations to obtain the ``gamma`` coefficients. Default is ``False``.
insertion_gate (str, optional): gate to be used in the insertion.
If ``"RX"``, the gate used is :math:``RX(\\pi / 2)``.
Defaults to ``"CNOT"``.
global_unitary_folding (bool, optional): If ``True``, noise is increased by global unitary folding.
If ``False``, local unitary folding is used. Defaults to ``True``.
insertion_gate (str, optional): gate to be folded in the local unitary folding.
If ``RX``, the gate used is :math:``RX(\\pi / 2)``. Otherwise, it is the ``CNOT`` gate.
readout (dict, optional): a dictionary that may contain the following keys:
* ncircuits: int, specifies the number of random circuits to use for the randomized method of readout error mitigation.
Expand Down Expand Up @@ -162,7 +190,10 @@ def ZNE(
expected_values = []
for num_insertions in noise_levels:
noisy_circuit = get_noisy_circuit(
circuit, num_insertions, insertion_gate=insertion_gate
circuit,
num_insertions,
global_unitary_folding,
insertion_gate=insertion_gate,
)
val = get_expectation_val_with_readout_mitigation(
noisy_circuit,
Expand Down
4 changes: 3 additions & 1 deletion tests/test_models_error_mitigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ def get_circuit(nqubits, nmeas=None):
],
)
@pytest.mark.parametrize("solve", [False, True])
def test_zne(backend, nqubits, noise, solve, insertion_gate, readout):
@pytest.mark.parametrize("GUF", [False, True])
def test_zne(backend, nqubits, noise, solve, GUF, insertion_gate, readout):
"""Test that ZNE reduces the noise."""
if backend.name == "tensorflow":
import tensorflow as tf
Expand Down Expand Up @@ -128,6 +129,7 @@ def test_zne(backend, nqubits, noise, solve, insertion_gate, readout):
noise_model=noise,
nshots=10000,
solve_for_gammas=solve,
global_unitary_folding=GUF,
insertion_gate=insertion_gate,
readout=readout,
backend=backend,
Expand Down

0 comments on commit 386fe25

Please sign in to comment.