Skip to content

Commit

Permalink
Upgrade mthree to 2.0 (Qiskit#341)
Browse files Browse the repository at this point in the history
* fix primiitives that use mthree 2.0

Additionlly, it supports mid-circuit measurements thanks to mthree 2.0

* enable mid-circuit measurements for sampler

* fix seed_mitigation of TREX
  • Loading branch information
t-imamichi authored and GitHub Enterprise committed Feb 22, 2023
1 parent 62418d3 commit 9611407
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 58 deletions.
3 changes: 1 addition & 2 deletions programs/estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,14 @@
from typing import Dict, List, Optional, cast

import numpy as np
from mthree.utils import final_measurement_mapping
from qiskit.circuit import Parameter, QuantumCircuit
from qiskit.circuit.library import RZGate, XGate
from qiskit.circuit.parametertable import ParameterView
from qiskit.compiler import transpile
from qiskit.exceptions import QiskitError
from qiskit.opflow import PauliSumOp
from qiskit.primitives import BackendEstimator, EstimatorResult
from qiskit.primitives.utils import init_observable
from qiskit.primitives.utils import init_observable, final_measurement_mapping
from qiskit.providers import Backend, BackendV1, Options
from qiskit.quantum_info import Pauli, PauliList, SparsePauliOp
from qiskit.quantum_info.operators.base_operator import BaseOperator
Expand Down
34 changes: 9 additions & 25 deletions programs/sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,6 @@
DEBUG = environ.get("PRIMITIVES_DEBUG", "false") == "true"


class MidcircuitMeasurementError(QiskitError):
"""Error related to midcircuit measurements"""

pass


class Sampler:
"""
Sampler class
Expand Down Expand Up @@ -459,43 +453,33 @@ def _apply_correction(
self, counts: Counts, circuit: QuantumCircuit
) -> tuple[M3QuasiDistribution, dict]:
mapping = final_measurement_mapping(circuit)
used_clbits = set(mapping.values())
used_clbits = set(mapping.keys())
all_clbits = set(range(circuit.num_clbits))
if used_clbits != all_clbits:
unused_clbits = list(all_clbits - used_clbits)
unused_counts = marginal_distribution(counts, unused_clbits)
if len(unused_counts) > 1 or set(next(iter(unused_counts))) != {"0"}:
raise MidcircuitMeasurementError(
"Sampler does not currently support mid-circuit measurements "
"when resilience_level is not 0"
)
reduced_counts, reduced_mapping = marginal_distribution(
counts, sorted(used_clbits), mapping
)
quasi, details = self._m3_mitigation.apply_correction(
reduced_counts, reduced_mapping, return_mitigation_overhead=True, details=True
)
quasi = self._expand_keys(quasi, unused_clbits, circuit.num_clbits)
quasi = self._expand_keys(quasi, unused_clbits)
else:
quasi, details = self._m3_mitigation.apply_correction(
counts, mapping, return_mitigation_overhead=True, details=True
)
return quasi, details

def _expand_keys(
self, quasi: M3QuasiDistribution, unused_clbits: list[int], num_clbits: int
self, quasi: M3QuasiDistribution, unused_clbits: list[int]
) -> M3QuasiDistribution:
"""fill '0' to unused qubits"""

def _expand(key: str):
lst = [""] * num_clbits
for i in unused_clbits:
lst[num_clbits - i - 1] = "0"
i = 0
for char in key:
while lst[i]:
i += 1
lst[i] = char
i += 1
return "".join(lst)
lst = list(key[::-1])
for i in sorted(unused_clbits):
lst.insert(i, "0")
return "".join(lst[::-1])

return M3QuasiDistribution(
{_expand(key): val for key, val in quasi.items()},
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ jupyter
matplotlib
numpy>=1.22.0
dill>=0.3.4
mthree>=0.24,<2.0
mthree>=2.0.0
git+ssh://git@github.ibm.com/IBM-Q-Software/pec-runtime.git@0.0.2
git+https://github.com/qiskit-community/prototype-zne.git@78ea32656b461a438a3bf3e3436339304d419635
7 changes: 7 additions & 0 deletions test/unit/test_noise_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ def test_estimator_noise_model(self):
fake_backend = FakeGuadalupe()
noise_model = NoiseModel.from_backend(fake_backend)
seed_simulator = 42
seed_mitigation = 42
shots = 100
backend = Aer.get_backend("aer_simulator")

Expand All @@ -127,6 +128,12 @@ def test_estimator_noise_model(self):
"coupling_map": fake_backend.configuration().coupling_map,
"basis_gates": fake_backend.configuration().basis_gates,
},
resilience_settings={
"pauli_twirled_mitigation": {
"seed_mitigation": seed_mitigation,
"seed_simulator": seed_simulator,
},
},
)

self.assertGreater(result["metadata"][0]["variance"], 0)
72 changes: 42 additions & 30 deletions test/unit/test_sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from qiskit.exceptions import QiskitError
from qiskit.providers.fake_provider import FakeBogota

from programs.sampler import CircuitCache, MidcircuitMeasurementError, Sampler, main
from programs.sampler import CircuitCache, Sampler, main

from .mock.mock_cache import MockCache
from .test_estimator import get_simulator
Expand Down Expand Up @@ -771,11 +771,12 @@ def test_sampler_separated_cregs(self, resilience_level):

@combine(resilience_level=[0, 1])
def test_sampler_separated_cregs_unused_clbits(self, resilience_level):
"""test sampler with separated cregs with unused clbits"""
"""test sampler with separated cregs and unused clbits"""
backend = get_simulator()
circ = QuantumCircuit(2, 2)
circ.h(0)
circ.cx(0, 1)
# clbits[0] and clbits[1] are not used
circ.measure_all()
result = main(
backend=backend,
Expand All @@ -791,40 +792,51 @@ def test_sampler_separated_cregs_unused_clbits(self, resilience_level):
self._compare_probs(result["quasi_dists"], targets)

@combine(resilience_level=[0, 1])
def test_sampler_multiple_measurements(self, resilience_level):
"""test sampler with multiple measurements of the same qubit to raise an error
if readout error mitigation is enabled"""
def test_sampler_midcircuit_measurements(self, resilience_level):
"""test sampler with mid-circuit measurements of the same qubit"""
backend = get_simulator()
circ = QuantumCircuit(1, 2)
circ.h(0)
circ.measure(0, 0)
circ.x(0)
circ.measure(0, 1)

if resilience_level == 0:
result = main(
backend=backend,
user_messenger=None,
circuits=[circ],
circuit_indices=[0],
parameter_values=[[]],
run_options={"shots": 10000, "seed_simulator": 123},
transpilation_settings={"seed_transpiler": 15},
resilience_settings={"level": resilience_level},
)
targets = [{"00": 0.5, "11": 0.5}]
self._compare_probs(result["quasi_dists"], targets)
else:
with self.assertRaises(MidcircuitMeasurementError):
_ = main(
backend=backend,
user_messenger=None,
circuits=[circ],
circuit_indices=[0],
parameter_values=[[]],
run_options={"shots": 10000, "seed_simulator": 123},
transpilation_settings={"seed_transpiler": 15},
resilience_settings={"level": resilience_level},
)
result = main(
backend=backend,
user_messenger=None,
circuits=[circ],
circuit_indices=[0],
parameter_values=[[]],
run_options={"shots": 10000, "seed_simulator": 123},
transpilation_settings={"seed_transpiler": 15},
resilience_settings={"level": resilience_level},
)
targets = [{"10": 0.5, "01": 0.5}]
self._compare_probs(result["quasi_dists"], targets)

@combine(resilience_level=[0, 1])
def test_sampler_sep_unused_mid(self, resilience_level):
"""test sampler with separated cregs, unused clbits, and mid-circuit measurements"""
backend = get_simulator()
circ = QuantumCircuit(2, 2)
circ.h(0)
circ.cx(0, 1)
circ.measure(0, 0)
# clbit[1] is not used
circ.x([0, 1])
circ.measure_all()
result = main(
backend=backend,
user_messenger=None,
circuits=[circ],
circuit_indices=[0],
parameter_values=[[]],
run_options={"shots": 10000, "seed_simulator": 123},
transpilation_settings={"seed_transpiler": 15},
resilience_settings={"level": resilience_level},
)
targets = [{"1100": 0.5, "0001": 0.5}]
self._compare_probs(result["quasi_dists"], targets)


@ddt
Expand Down

0 comments on commit 9611407

Please sign in to comment.