Skip to content

Commit

Permalink
Change the main function in Pauli Twirling (unitaryfund#1977)
Browse files Browse the repository at this point in the history
* change execute in pt

* imports

* nate's feedback + reference

* test no CZ CNOT circuit

* Update mitiq/pt/pt.py

Co-authored-by: nate stemen <[email protected]>

* backslash

---------

Co-authored-by: nate stemen <[email protected]>
  • Loading branch information
purva-thakre and natestemen committed Sep 1, 2023
1 parent b12c2ec commit 3cf97ed
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 28 deletions.
8 changes: 8 additions & 0 deletions docs/source/refs.bib
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,14 @@ @article{Sagastizabal_2019_PRA
url = {https://link.aps.org/doi/10.1103/PhysRevA.100.010302},
}

@misc{saki2023hypothesis,
title={Hypothesis Testing for Error Mitigation: How to Evaluate Error Mitigation},
author={Abdullah Ash Saki and Amara Katabarwa and Salonik Resch and George Umbrarescu},
year={2023},
eprint={2301.02690},
archivePrefix={arXiv},
primaryClass={quant-ph}
}


@article{Serbyn_2021_NatPhys,
Expand Down
2 changes: 1 addition & 1 deletion mitiq/pt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# LICENSE file in the root directory of this source tree.

from mitiq.pt.pt import (
execute_with_pauli_twirling,
pauli_twirl_circuit,
twirl_CNOT_gates,
twirl_CZ_gates,
)
32 changes: 11 additions & 21 deletions mitiq/pt/pt.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
# LICENSE file in the root directory of this source tree.

import random
from typing import Callable, List, Optional, Union, cast
from typing import List

import cirq
import numpy as np

from mitiq import QPROGRAM, Executor, Observable, QuantumResult
from mitiq import QPROGRAM
from mitiq.interface import accept_qprogram_and_validate

# P, Q, R, S from https://arxiv.org/pdf/2301.02690.pdf
Expand Down Expand Up @@ -51,38 +50,29 @@
]


def execute_with_pauli_twirling(
def pauli_twirl_circuit(
circuit: QPROGRAM,
executor: Union[Executor, Callable[[QPROGRAM], QuantumResult]],
observable: Optional[Observable] = None,
*,
num_circuits: int = 10,
) -> float:
"""Estimates the expectation value of the input circuit by averaging
expectation values obtained from Pauli twirled circuits.
) -> List[QPROGRAM]:
r"""Return the Pauli twirled versions of the input circuit.
Only the $\mathrm{CZ}$ and $\mathrm{CNOT}$ gates in an
input circuit are Pauli twirled as specified in
:cite:`saki2023hypothesis`.
Args:
circuit: The input circuit to execute with twirling.
executor: A Mitiq executor that executes a circuit and returns the
unmitigated ``QuantumResult`` (e.g. an expectation value).
observable: Observable to compute the expectation value of. If
``None``, the ``executor`` must return an expectation value
(float). Otherwise, the ``QuantumResult`` returned by ``executor``
is used to compute the expectation of the observable.
num_circuits: Number of circuits to be twirled, and averaged.
Returns:
The expectation value estimated with Pauli twirling.
"""
executor = (
executor if isinstance(executor, Executor) else Executor(executor)
)
CNOT_twirled_circuits = twirl_CNOT_gates(circuit, num_circuits)
twirled_circuits = [
twirl_CZ_gates(c, num_circuits=1)[0] for c in CNOT_twirled_circuits
]
expvals = executor.evaluate(twirled_circuits, observable)
return cast(float, np.average(expvals))

return twirled_circuits


def twirl_CNOT_gates(circuit: QPROGRAM, num_circuits: int) -> List[QPROGRAM]:
Expand Down
28 changes: 22 additions & 6 deletions mitiq/pt/tests/test_pt.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@
import cirq
import networkx as nx
import numpy as np
import pytest
import qiskit

from mitiq.benchmarks import generate_mirror_circuit
from mitiq.interface.mitiq_cirq import compute_density_matrix
from mitiq.pt.pt import (
CNOT_twirling_gates,
CZ_twirling_gates,
execute_with_pauli_twirling,
pauli_twirl_circuit,
twirl_CNOT_gates,
twirl_CZ_gates,
)
from mitiq.utils import _equal

num_qubits = 2
qubits = cirq.LineQubit.range(num_qubits)
Expand Down Expand Up @@ -116,15 +118,29 @@ def test_twirl_CNOT_increases_layer_count():
assert num_gates_after == num_gates_before


def test_execute_with_pauli_twirling():
def test_pauli_twirl_circuit():
num_qubits = 3
num_layers = 20
circuit, _ = generate_mirror_circuit(
nlayers=num_layers,
two_qubit_gate_prob=1.0,
connectivity_graph=nx.complete_graph(num_qubits),
)
expval = execute_with_pauli_twirling(
circuit, amp_damp_executor, num_circuits=10
)
assert 0 <= expval < 0.5
num_circuits = 10
twirled_output = pauli_twirl_circuit(circuit, num_circuits)
assert len(twirled_output) == num_circuits


@pytest.mark.parametrize(
"twirl_func", [pauli_twirl_circuit, twirl_CNOT_gates, twirl_CZ_gates]
)
def test_no_CNOT_CZ_circuit(twirl_func):
num_qubits = 2
qubits = cirq.LineQubit.range(num_qubits)
circuit = cirq.Circuit()
circuit.append(cirq.X.on_each(qubits))
twirled_output = twirl_func(circuit, 5)
assert len(twirled_output) == 5

for i in range(5):
assert _equal(circuit, twirled_output[i])

0 comments on commit 3cf97ed

Please sign in to comment.