Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate noise amplification option #1035

Merged
merged 5 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions qiskit_ibm_runtime/options/resilience_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from dataclasses import dataclass

from .utils import _flexible
from ..utils.deprecation import issue_deprecation_msg

ResilienceSupportedOptions = Literal[
"noise_amplifier",
Expand Down Expand Up @@ -47,7 +48,7 @@ class ResilienceOptions:
Only applicable for ``resilience_level=2``.
Default: (1, 3, 5).

noise_amplifier: A noise amplification strategy. One of ``"TwoQubitAmplifier"``,
noise_amplifier (DEPRECATED): A noise amplification strategy. One of ``"TwoQubitAmplifier"``,
``"GlobalFoldingAmplifier"``, ``"LocalFoldingAmplifier"``, ``"CxAmplifier"``.
Only applicable for ``resilience_level=2``.
Default: "TwoQubitAmplifier".
Expand All @@ -60,7 +61,7 @@ class ResilienceOptions:
Default: "LinearExtrapolator".
"""

noise_amplifier: NoiseAmplifierType = "TwoQubitAmplifier"
noise_amplifier: NoiseAmplifierType = None
noise_factors: Sequence[float] = (1, 3, 5)
extrapolator: ExtrapolatorType = "LinearExtrapolator"

Expand All @@ -74,10 +75,21 @@ def validate_resilience_options(resilience_options: dict) -> None:
ValueError: if extrapolator == "QuarticExtrapolator" and number of noise_factors < 5.
ValueError: if extrapolator == "CubicExtrapolator" and number of noise_factors < 4.
"""
if resilience_options.get("noise_amplifier", None) is not None:
issue_deprecation_msg(
msg="The 'noise_amplifier' resilience option is deprecated",
version="0.12.0",
period="1 month",
remedy="After the deprecation period, only local folding amplification "
"will be supported. "
"Refer to https://github.com/qiskit-community/prototype-zne "
"for global folding amplification in ZNE.",
)

for opt in resilience_options:
if not opt in get_args(ResilienceSupportedOptions):
raise ValueError(f"Unsupported value '{opt}' for resilience.")
noise_amplifier = resilience_options.get("noise_amplifier")
noise_amplifier = resilience_options.get("noise_amplifier") or "TwoQubitAmplifier"
if not noise_amplifier in get_args(NoiseAmplifierType):
raise ValueError(
f"Unsupported value {noise_amplifier} for noise_amplifier. "
Expand Down
7 changes: 5 additions & 2 deletions qiskit_ibm_runtime/utils/deprecation.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,21 @@ def deprecate_arguments(deprecated: str, version: str, remedy: str, stacklevel:
)


def issue_deprecation_msg(msg: str, version: str, remedy: str, stacklevel: int = 2) -> None:
def issue_deprecation_msg(
msg: str, version: str, remedy: str, stacklevel: int = 2, period: str = "3 months"
) -> None:
"""Emit a deprecation warning.

Args:
msg: Deprecation message.
version: First release the function is deprecated.
remedy: User action to take.
stacklevel: The warning stackevel to use.
period: Deprecation period.
"""
warnings.warn(
f"{msg} as of qiskit-ibm-runtime {version} "
f"and will be removed no sooner than 3 months after the release date. {remedy}",
f"and will be removed no sooner than {period} after the release date. {remedy}",
DeprecationWarning,
stacklevel=stacklevel + 1, # Increment to account for this function.
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
deprecations:
- |
The ``noise_amplifier`` resilience options is deprecated.
After the deprecation period, only local folding amplification will be supported.
Refer to https://github.com/qiskit-community/prototype-zne for global folding
amplification.
36 changes: 32 additions & 4 deletions test/unit/test_estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,26 @@

"""Tests for estimator class."""

import warnings

from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp

from qiskit_ibm_runtime import Estimator, Session
from qiskit_ibm_runtime import Estimator, Session, Options

from ..ibm_test_case import IBMTestCase
from ..utils import get_mocked_backend
from .mock.fake_runtime_service import FakeRuntimeService


class TestEstimator(IBMTestCase):
"""Class for testing the Estimator class."""

def setUp(self) -> None:
super().setUp()
self.circuit = QuantumCircuit(1, 1)
self.observables = SparsePauliOp.from_list([("I", 1)])

def test_unsupported_values_for_estimator_options(self):
"""Test exception when options levels are not supported."""
options_bad = [
Expand All @@ -34,10 +42,30 @@ def test_unsupported_values_for_estimator_options(self):
service=FakeRuntimeService(channel="ibm_quantum", token="abc"),
backend="common_backend",
) as session:
circuit = QuantumCircuit(1, 1)
obs = SparsePauliOp.from_list([("I", 1)])
for bad_opt in options_bad:
inst = Estimator(session=session)
with self.assertRaises(ValueError) as exc:
_ = inst.run(circuit, observables=obs, **bad_opt)
_ = inst.run(self.circuit, observables=self.observables, **bad_opt)
self.assertIn(list(bad_opt.keys())[0], str(exc.exception))

def test_deprecated_noise_amplifier(self):
"""Test noise_amplifier deprecation."""
opt = Options()
opt.resilience.noise_amplifier = "GlobalFoldingAmplifier"

with warnings.catch_warnings(record=True) as warn:
warnings.simplefilter("always")
estimator = Estimator(backend=get_mocked_backend(), options=opt)
estimator.run(self.circuit, self.observables)
self.assertEqual(len(warn), 1, "Deprecation warning not found.")
self.assertIn("noise_amplifier", str(warn[-1].message))

def test_deprecated_noise_amplifier_run(self):
"""Test noise_amplifier deprecation in run."""

with warnings.catch_warnings(record=True) as warn:
warnings.simplefilter("always")
estimator = Estimator(backend=get_mocked_backend())
estimator.run(self.circuit, self.observables, noise_amplifier="GlobalFoldingAmplifier")
self.assertEqual(len(warn), 1, "Deprecation warning not found.")
self.assertIn("noise_amplifier", str(warn[-1].message))
5 changes: 2 additions & 3 deletions test/unit/test_ibm_primitives.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ def test_accept_level_1_options(self):
{"shots": 10},
{"seed_simulator": 123},
{"skip_transpilation": True, "log_level": "ERROR"},
{"initial_layout": [1, 2], "shots": 100, "noise_amplifier": "CxAmplifier"},
{"initial_layout": [1, 2], "shots": 100, "noise_factors": (0, 2, 4)},
]

expected_list = [Options(), Options(), Options(), Options(), Options()]
Expand All @@ -521,7 +521,7 @@ def test_accept_level_1_options(self):
expected_list[3].environment.log_level = "ERROR"
expected_list[4].transpilation.initial_layout = [1, 2]
expected_list[4].execution.shots = 100
expected_list[4].resilience.noise_amplifier = "CxAmplifier"
expected_list[4].resilience.noise_factors = (0, 2, 4)

session = MagicMock(spec=MockSession)
primitives = [Sampler, Estimator]
Expand Down Expand Up @@ -603,7 +603,6 @@ def test_default_error_levels(self):
def test_resilience_options(self):
"""Test resilience options."""
options_dicts = [
{"resilience": {"noise_amplifier": "NoAmplifier"}},
{"resilience": {"extrapolator": "NoExtrapolator"}},
{
"resilience": {
Expand Down
8 changes: 4 additions & 4 deletions test/unit/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def test_program_inputs(self):
execution={"shots": 100},
environment={"log_level": "DEBUG"},
simulator={"noise_model": noise_model},
resilience={"noise_amplifier": "GlobalFoldingAmplifier"},
resilience={"noise_factors": (0, 2, 4)},
foo="foo",
bar="bar",
)
Expand All @@ -154,7 +154,7 @@ def test_program_inputs(self):
},
"resilience_settings": {
"level": 2,
"noise_amplifier": "GlobalFoldingAmplifier",
"noise_factors": (0, 2, 4),
},
"foo": "foo",
}
Expand All @@ -175,7 +175,7 @@ def test_init_options_with_dictionary(self):
"transpilation": {"initial_layout": [1, 2], "layout_method": "trivial"},
"execution": {"shots": 100},
},
{"resilience": {"noise_amplifier": "GlobalFoldingAmplifier"}},
{"resilience": {"noise_factors": (0, 2, 4)}},
{"environment": {"log_level": "ERROR"}},
]

Expand All @@ -201,7 +201,7 @@ def test_unsupported_options(self):
"environment": {"log_level": "DEBUG"},
"simulator": {"noise_model": "model"},
"resilience": {
"noise_amplifier": "GlobalFoldingAmplifier",
"noise_factors": (0, 2, 4),
"extrapolator": "LinearExtrapolator",
},
}
Expand Down
10 changes: 9 additions & 1 deletion test/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import time
import unittest
from unittest import mock
from typing import Dict, Optional
from typing import Dict, Optional, Any
from datetime import datetime

from qiskit.circuit import QuantumCircuit
Expand Down Expand Up @@ -247,3 +247,11 @@ def create_faulty_backend(
)
out_backend.properties = lambda: BackendProperties.from_dict(properties) # type: ignore
return out_backend


def get_mocked_backend(name: str = "ibm_gotham") -> Any:
"""Return a mock backend."""
mock_backend = mock.MagicMock(spec=IBMBackend)
mock_backend.name = name
mock_backend._instance = None
return mock_backend