Skip to content

Commit

Permalink
Deprecate approximation option in Estimator (Qiskit#1963)
Browse files Browse the repository at this point in the history
* Deprecate approximation

* fix tests
  • Loading branch information
ikkoham authored Oct 24, 2023
1 parent 02506b4 commit fd5e283
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 31 deletions.
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.",
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

0 comments on commit fd5e283

Please sign in to comment.