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 approximation option in Estimator #1963

Merged
merged 2 commits into from
Oct 24, 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
51 changes: 47 additions & 4 deletions qiskit_aer/primitives/estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from qiskit.quantum_info import Pauli, PauliList
from qiskit.quantum_info.operators.base_operator import BaseOperator
from qiskit.result.models import ExperimentResult
from qiskit.utils import deprecate_arg, deprecate_func

from .. import AerError, AerSimulator

Expand Down Expand Up @@ -68,6 +69,12 @@ class Estimator(BaseEstimator):
normal distribution approximation.
"""

@deprecate_arg(
"approximation",
since=0.13,
package_name="qiskit-aer",
additional_msg="approximation=True will be default in the future.",
)
def __init__(
self,
*,
Expand Down Expand Up @@ -100,7 +107,15 @@ def __init__(
self._transpile_options = Options()
if transpile_options is not None:
self._transpile_options.update_options(**transpile_options)
self.approximation = approximation
if not approximation:
warn(
"Option approximation=False is deprecated as of qiskit-aer 0.13. "
"It will be removed no earlier than 3 months after the release date. "
"Instead, use BackendEstmator from qiskit.primitives.",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just happened to notice this PR - in looking over it I see that BackendEstimator is missing the i in this warning - same in the one below. Pretty minor but I thought I'd point it out.

DeprecationWarning,
stacklevel=3,
)
self._approximation = approximation
self._skip_transpilation = skip_transpilation
self._cache: dict[tuple[tuple[int], tuple[int], bool], tuple[dict, dict]] = {}
self._transpiled_circuits: dict[int, QuantumCircuit] = {}
Expand All @@ -109,6 +124,34 @@ def __init__(
self._observable_ids: dict[tuple, int] = {}
self._abelian_grouping = abelian_grouping

@property
@deprecate_func(
since=0.13,
package_name="qiskit-aer",
is_property=True,
)
def approximation(self):
"""The approximation property"""
return self._approximation

@approximation.setter
@deprecate_func(
since=0.13,
package_name="qiskit-aer",
is_property=True,
)
def approximation(self, approximation):
"""Setter for approximation"""
if not approximation:
warn(
"Option approximation=False is deprecated as of qiskit-aer 0.13. "
"It will be removed no earlier than 3 months after the release date. "
"Instead, use BackendEstmator from qiskit.primitives.",
DeprecationWarning,
stacklevel=3,
)
self._approximation = approximation

def _call(
self,
circuits: Sequence[int],
Expand All @@ -120,7 +163,7 @@ def _call(
if seed is not None:
run_options.setdefault("seed_simulator", seed)

if self.approximation:
if self._approximation:
return self._compute_with_approximation(
circuits, observables, parameter_values, run_options, seed
)
Expand Down Expand Up @@ -174,7 +217,7 @@ def _compute(self, circuits, observables, parameter_values, run_options):
)

# Key for cache
key = (tuple(circuits), tuple(observables), self.approximation)
key = (tuple(circuits), tuple(observables), self._approximation)

# Create expectation value experiments.
if key in self._cache: # Use a cache
Expand Down Expand Up @@ -363,7 +406,7 @@ def _compute_with_approximation(
self, circuits, observables, parameter_values, run_options, seed
):
# Key for cache
key = (tuple(circuits), tuple(observables), self.approximation)
key = (tuple(circuits), tuple(observables), self._approximation)
shots = run_options.pop("shots", None)

# Create expectation value experiments.
Expand Down
68 changes: 41 additions & 27 deletions test/terra/primitives/test_estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,10 @@ def test_estimator(self, abelian_grouping):
with self.subTest("SparsePauliOp"):
observable = SparsePauliOp.from_list(lst)
ansatz = RealAmplitudes(num_qubits=2, reps=2)
est = Estimator(
backend_options={"method": "statevector"}, abelian_grouping=abelian_grouping
)
with self.assertWarns(DeprecationWarning):
est = Estimator(
backend_options={"method": "statevector"}, abelian_grouping=abelian_grouping
)
result = est.run(
ansatz, observable, parameter_values=[[0, 1, 1, 2, 3, 5]], seed=15
).result()
Expand All @@ -88,9 +89,10 @@ def test_estimator(self, abelian_grouping):
]
)
ansatz = RealAmplitudes(num_qubits=2, reps=2)
est = Estimator(
backend_options={"method": "statevector"}, abelian_grouping=abelian_grouping
)
with self.assertWarns(DeprecationWarning):
est = Estimator(
backend_options={"method": "statevector"}, abelian_grouping=abelian_grouping
)
result = est.run(ansatz, observable, parameter_values=[[0] * 6], seed=15).result()
self.assertIsInstance(result, EstimatorResult)
np.testing.assert_allclose(result.values, [-0.4], rtol=0.02)
Expand All @@ -107,15 +109,17 @@ def test_init_observable_from_operator(self, abelian_grouping):
[0.1809312, 0.0, 0.0, -1.06365335],
]
)
est = Estimator(abelian_grouping=abelian_grouping)
with self.assertWarns(DeprecationWarning):
est = Estimator(abelian_grouping=abelian_grouping)
result = est.run([circuit], [matrix], seed=15, shots=8192).result()
self.assertIsInstance(result, EstimatorResult)
np.testing.assert_allclose(result.values, [self.expval], rtol=0.02)

@data(True, False)
def test_evaluate(self, abelian_grouping):
"""test for evaluate"""
est = Estimator(abelian_grouping=abelian_grouping)
with self.assertWarns(DeprecationWarning):
est = Estimator(abelian_grouping=abelian_grouping)
result = est.run(
self.ansatz, self.observable, parameter_values=[[0, 1, 1, 2, 3, 5]], seed=15, shots=8192
).result()
Expand All @@ -125,7 +129,8 @@ def test_evaluate(self, abelian_grouping):
@data(True, False)
def test_evaluate_multi_params(self, abelian_grouping):
"""test for evaluate with multiple parameters"""
est = Estimator(abelian_grouping=abelian_grouping)
with self.assertWarns(DeprecationWarning):
est = Estimator(abelian_grouping=abelian_grouping)
result = est.run(
[self.ansatz] * 2,
[self.observable] * 2,
Expand All @@ -139,7 +144,8 @@ def test_evaluate_multi_params(self, abelian_grouping):
def test_evaluate_no_params(self, abelian_grouping):
"""test for evaluate without parameters"""
circuit = self.ansatz.assign_parameters([0, 1, 1, 2, 3, 5])
est = Estimator(abelian_grouping=abelian_grouping)
with self.assertWarns(DeprecationWarning):
est = Estimator(abelian_grouping=abelian_grouping)
result = est.run(circuit, self.observable, seed=15, shots=8192).result()
self.assertIsInstance(result, EstimatorResult)
np.testing.assert_allclose(result.values, [self.expval], rtol=0.02)
Expand All @@ -151,7 +157,8 @@ def test_run_with_multiple_observables_and_none_parameters(self, abelian_groupin
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
est = Estimator(abelian_grouping=abelian_grouping)
with self.assertWarns(DeprecationWarning):
est = Estimator(abelian_grouping=abelian_grouping)
result = est.run(
[circuit] * 2, [SparsePauliOp("ZZZ"), SparsePauliOp("III")], seed=15
).result()
Expand All @@ -168,7 +175,8 @@ def test_1qubit(self, abelian_grouping):
op0 = SparsePauliOp.from_list([("I", 1)])
op1 = SparsePauliOp.from_list([("Z", 1)])

est = Estimator(abelian_grouping=abelian_grouping)
with self.assertWarns(DeprecationWarning):
est = Estimator(abelian_grouping=abelian_grouping)
with self.subTest("test circuit 0, observable 0"):
result = est.run(qc0, op0).result()
self.assertIsInstance(result, EstimatorResult)
Expand Down Expand Up @@ -200,7 +208,8 @@ def test_2qubits(self, abelian_grouping):
op1 = SparsePauliOp.from_list([("ZI", 1)])
op2 = SparsePauliOp.from_list([("IZ", 1)])

est = Estimator(abelian_grouping=abelian_grouping)
with self.assertWarns(DeprecationWarning):
est = Estimator(abelian_grouping=abelian_grouping)
with self.subTest("test circuit 0, observable 0"):
result = est.run(qc0, op0).result()
self.assertIsInstance(result, EstimatorResult)
Expand Down Expand Up @@ -237,7 +246,8 @@ def test_empty_parameter(self, abelian_grouping):
n = 2
qc = QuantumCircuit(n)
op = SparsePauliOp.from_list([("I" * n, 1)])
estimator = Estimator(abelian_grouping=abelian_grouping)
with self.assertWarns(DeprecationWarning):
estimator = Estimator(abelian_grouping=abelian_grouping)
with self.subTest("one circuit"):
result = estimator.run(qc, op, shots=1000).result()
np.testing.assert_allclose(result.values, [1])
Expand All @@ -257,7 +267,8 @@ def test_numpy_params(self, abelian_grouping):
params_array = np.random.rand(k, qc.num_parameters)
params_list = params_array.tolist()
params_list_array = list(params_array)
estimator = Estimator(abelian_grouping=abelian_grouping)
with self.assertWarns(DeprecationWarning):
estimator = Estimator(abelian_grouping=abelian_grouping)
target = estimator.run([qc] * k, [op] * k, params_list, seed=15).result()

with self.subTest("ndarrary"):
Expand All @@ -275,7 +286,8 @@ def test_with_shots_option_with_approximation(self, abelian_grouping):
"""test with shots option."""
# Note: abelian_gropuing is ignored when approximation is True as documented.
# The purpose of this test is to make sure the results remain the same.
est = Estimator(approximation=True, abelian_grouping=abelian_grouping)
with self.assertWarns(DeprecationWarning):
est = Estimator(approximation=True, abelian_grouping=abelian_grouping)
result = est.run(
self.ansatz, self.observable, parameter_values=[[0, 1, 1, 2, 3, 5]], shots=1024, seed=15
).result()
Expand All @@ -285,7 +297,8 @@ def test_with_shots_option_with_approximation(self, abelian_grouping):

def test_with_shots_option_without_approximation(self):
"""test with shots option."""
est = Estimator(approximation=False, abelian_grouping=False)
with self.assertWarns(DeprecationWarning):
est = Estimator(approximation=False, abelian_grouping=False)
result = est.run(
self.ansatz, self.observable, parameter_values=[[0, 1, 1, 2, 3, 5]], shots=1024, seed=15
).result()
Expand All @@ -295,15 +308,15 @@ def test_with_shots_option_without_approximation(self):

def test_warn_shots_none_without_approximation(self):
"""Test waning for shots=None without approximation."""
est = Estimator(approximation=False)
with self.assertWarns(RuntimeWarning):
result = est.run(
self.ansatz,
self.observable,
parameter_values=[[0, 1, 1, 2, 3, 5]],
shots=None,
seed=15,
).result()
with self.assertWarns(DeprecationWarning):
est = Estimator(approximation=False)
result = est.run(
self.ansatz,
self.observable,
parameter_values=[[0, 1, 1, 2, 3, 5]],
shots=None,
seed=15,
).result()
self.assertIsInstance(result, EstimatorResult)
np.testing.assert_allclose(result.values, [-1.313831587508902])
self.assertIsInstance(result.metadata[0]["variance"], float)
Expand All @@ -318,7 +331,8 @@ def test_result_order(self):
qc2.ry(np.pi / 2 * param, 0)
qc2.measure_all()

estimator = Estimator(approximation=True)
with self.assertWarns(DeprecationWarning):
estimator = Estimator(approximation=True)
job = estimator.run([qc1, qc2, qc1, qc1, qc2], ["Z"] * 5, [[], [1], [], [], [1]])
result = job.result()
np.testing.assert_allclose(result.values, [1, 0, 1, 1, 0], atol=1e-10)
Expand Down
Loading