Skip to content

Commit

Permalink
integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
SamFerracin committed Aug 12, 2024
1 parent 5b241fd commit 0e5a2e8
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 38 deletions.
23 changes: 9 additions & 14 deletions qiskit_ibm_runtime/utils/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,12 +276,6 @@ def default(self, obj: Any) -> Any: # pylint: disable=arguments-differ
"fields": dict(obj.items()),
}
return {"__type__": "DataBin", "__value__": out_val}
if isinstance(obj, LayerError):
out_val = {"circuit": obj.circuit, "qubits": obj.qubits, "error": obj.error}
return {"__type__": "LayerError", "__value__": out_val}
if isinstance(obj, PauliLindbladError):
out_val = {"generators": obj.generators, "rates": obj.rates}
return {"__type__": "PauliLindbladError", "__value__": out_val}
if isinstance(obj, EstimatorPub):
return (
obj.circuit,
Expand Down Expand Up @@ -357,14 +351,15 @@ def object_hook(self, obj: Any) -> Any:
# to deserialize load qpy circuit and return first instruction object in that circuit.
circuit = _decode_and_deserialize(obj_val, load)[0]
return circuit.data[0][0]
if obj_type == "settings" and obj["__module__"].startswith(
"qiskit.quantum_info.operators"
):
return _deserialize_from_settings(
mod_name=obj["__module__"],
class_name=obj["__class__"],
settings=_cast_strings_keys_to_int(obj_val),
)
if obj_type == "settings":
deserialize = obj["__module__"].startswith("qiskit.quantum_info.operators")
deserialize = deserialize or obj["__class__"] in [PauliLindbladError, LayerError]
if deserialize is True:
return _deserialize_from_settings(
mod_name=obj["__module__"],
class_name=obj["__class__"],
settings=_cast_strings_keys_to_int(obj_val),
)
if obj_type == "Result":
return Result.from_dict(obj_val)
if obj_type == "spmatrix":
Expand Down
10 changes: 10 additions & 0 deletions qiskit_ibm_runtime/utils/noise_learner_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ def num_qubits(self) -> int:
The number of qubits in this :class:`~.PauliLindbladError`.
"""
return self.generators.num_qubits

@property
def settings(self):
"""Return settings."""
return {"generators": self.generators, "rates": self.rates}

def __repr__(self) -> str:
return f"PauliLindbladError(generators={self.generators}, rates={self.rates.tolist()})"
Expand Down Expand Up @@ -170,6 +175,11 @@ def num_qubits(self) -> int:
The number of qubits in this :class:`~.LayerError`.
"""
return len(self.qubits)

@property
def settings(self):
"""Return settings."""
return {"circuit": self.circuit, "qubits": self.qubits, "error": self.error}

def __repr__(self) -> str:
ret = f"circuit={repr(self.circuit)}, qubits={self.qubits}, error={self.error})"
Expand Down
77 changes: 53 additions & 24 deletions test/integration/test_noise_learner.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@

from qiskit.circuit import QuantumCircuit
from qiskit.providers.jobstatus import JobStatus
from qiskit.compiler import transpile

from qiskit_ibm_runtime import RuntimeJob, Session
from qiskit_ibm_runtime import RuntimeJob, Session, EstimatorV2
from qiskit_ibm_runtime.noise_learner import NoiseLearner
from qiskit_ibm_runtime.utils.noise_learner_result import PauliLindbladError, LayerError
from qiskit_ibm_runtime.options import NoiseLearnerOptions
from qiskit_ibm_runtime.options import NoiseLearnerOptions, EstimatorOptions

from ..decorators import run_integration_test
from ..ibm_test_case import IBMIntegrationTestCase

# TODO: remove
image = "prim-custom-img-noise-learner-phase2:56a77a61da2801fdf8b1e957157f9373ccf243f7"


class TestIntegrationNoiseLearner(IBMIntegrationTestCase):
"""Integration tests for NoiseLearner."""
Expand All @@ -39,12 +41,12 @@ def setUp(self) -> None:
raise SkipTest("test_eagle not available in this environment")

c1 = QuantumCircuit(2)
c1.cx(0, 1)
c1.ecr(0, 1)

c2 = QuantumCircuit(3)
c2.cx(0, 1)
c2.cx(1, 2)
c2.cx(0, 1)
c2.ecr(0, 1)
c2.ecr(1, 2)
c2.ecr(0, 1)

self.circuits = [c1, c2]

Expand All @@ -64,11 +66,11 @@ def test_with_default_options(self, service): # pylint: disable=unused-argument

options = NoiseLearnerOptions()
learner = NoiseLearner(mode=backend, options=options)
learner.options.experimental = {"image": image}

circuits = transpile(self.circuits, backend=backend)
job = learner.run(circuits)
job = learner.run(self.circuits)

self._verify(job, self.default_input_options)
self._verify(job, self.default_input_options, 3)

@run_integration_test
def test_with_non_default_options(self, service): # pylint: disable=unused-argument
Expand All @@ -80,43 +82,42 @@ def test_with_non_default_options(self, service): # pylint: disable=unused-argu
options.layer_pair_depths = [0, 1]
learner = NoiseLearner(mode=backend, options=options)

circuits = transpile(self.circuits, backend=backend)
job = learner.run(circuits)
job = learner.run(self.circuits)
learner.options.experimental = {"image": image}

input_options = deepcopy(self.default_input_options)
input_options["max_layers_to_learn"] = 1
input_options["layer_pair_depths"] = [0, 1]
self._verify(job, input_options)
self._verify(job, input_options, 1)

@run_integration_test
def test_in_session(self, service):
"""Test noise learner when used within a session."""
backend = self.backend

options = NoiseLearnerOptions()
options.max_layers_to_learn = 1
options.layer_pair_depths = [0, 1]

input_options = deepcopy(self.default_input_options)
input_options["max_layers_to_learn"] = 1
input_options["layer_pair_depths"] = [0, 1]

circuits = transpile(self.circuits, backend=backend)

with Session(service, backend) as session:
options.twirling_strategy = "all"
learner1 = NoiseLearner(mode=session, options=options)
job1 = learner1.run(circuits)
learner1.options.experimental = {"image": image}
job1 = learner1.run(self.circuits)

input_options["twirling_strategy"] = "all"
self._verify(job1, input_options)
self._verify(job1, input_options, 2)

options.twirling_strategy = "active-circuit"
learner2 = NoiseLearner(mode=session, options=options)
job2 = learner2.run(circuits)
learner2.options.experimental = {"image": image}
job2 = learner2.run(self.circuits)

input_options["twirling_strategy"] = "active-circuit"
self._verify(job2, input_options)
self._verify(job2, input_options, 3)

@run_integration_test
def test_with_no_layers(self, service): # pylint: disable=unused-argument
Expand All @@ -126,21 +127,49 @@ def test_with_no_layers(self, service): # pylint: disable=unused-argument
options = NoiseLearnerOptions()
options.max_layers_to_learn = 0
learner = NoiseLearner(mode=backend, options=options)
learner.options.experimental = {"image": image}

circuits = transpile(self.circuits, backend=backend)
job = learner.run(circuits)
job = learner.run(self.circuits)

self.assertEqual(job.result().data, [])

input_options = deepcopy(self.default_input_options)
input_options["max_layers_to_learn"] = 0
self._verify(job, input_options)
self._verify(job, input_options, 0)

@run_integration_test
def test_learner_plus_estimator(self, service): # pylint: disable=unused-argument
"""Test feeding noise learner data to estimator."""
backend = self.backend

def _verify(self, job: RuntimeJob, expected_input_options: dict) -> None:
options = EstimatorOptions()
options.resilience.zne_mitigation = True
options.resilience.layer_noise_learning.layer_pair_depths = [0, 1]
options.twirling.strategy = "all"
# TODO: remove experimental options
options.experimental = {"image": image, "resilience": {"zne": {"amplifier": "pea"}}}

with Session(service, backend) as session:
learner = NoiseLearner(mode=session, options=options)
nl_job = learner.run(self.circuits)
layer_noise_model = nl_job.result()
self.assertEqual(len(layer_noise_model), 2)

estimator = EstimatorV2(mode=session, options=options)
estimator.options.resilience.pec_mitigation = True
estimator.options.resilience.layer_noise_model = layer_noise_model

pubs = [(c, "Z" * c.num_qubits) for c in self.circuits]
e_job = estimator.run(pubs)
self.assertEqual(e_job.metadata["resilience"]["layer_noise_model"], layer_noise_model)

def _verify(self, job: RuntimeJob, expected_input_options: dict, n_results: int) -> None:
job.wait_for_final_state()
self.assertEqual(job.status(), JobStatus.DONE, job.error_message())

result = job.result()
self.assertEqual(len(result), n_results)

for datum in result.data:
circuit = datum.circuit
qubits = datum.qubits
Expand Down

0 comments on commit 0e5a2e8

Please sign in to comment.