Skip to content

Commit

Permalink
Merge branch 'bugfix/nonnative-1q-heuristic' of github.com:ecpeterson…
Browse files Browse the repository at this point in the history
…/qiskit-terra into bugfix/nonnative-1q-heuristic
  • Loading branch information
ecpeterson committed Jul 6, 2021
2 parents d808985 + 4043322 commit 97b7f00
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 7 deletions.
15 changes: 11 additions & 4 deletions qiskit/opflow/gradients/derivative_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
import numpy as np
from qiskit.utils.quantum_instance import QuantumInstance
from qiskit.circuit import ParameterExpression, ParameterVector
from qiskit.providers import BaseBackend
from qiskit.providers import BaseBackend, Backend

from ..converters.converter_base import ConverterBase
from ..expectations import ExpectationBase, PauliExpectation
from ..list_ops.composed_op import ComposedOp
from ..list_ops.list_op import ListOp
from ..list_ops.tensored_op import TensoredOp
Expand Down Expand Up @@ -82,7 +83,8 @@ def gradient_wrapper(
List[Tuple[ParameterExpression, ParameterExpression]],
]
] = None,
backend: Optional[Union[BaseBackend, QuantumInstance]] = None,
backend: Optional[Union[BaseBackend, Backend, QuantumInstance]] = None,
expectation: Optional[ExpectationBase] = None,
) -> Callable[[Iterable], np.ndarray]:
"""Get a callable function which provides the respective gradient, Hessian or QFI for given
parameter values. This callable can be used as gradient function for optimizers.
Expand All @@ -91,12 +93,14 @@ def gradient_wrapper(
operator: The operator for which we want to get the gradient, Hessian or QFI.
bind_params: The operator parameters to which the parameter values are assigned.
grad_params: The parameters with respect to which we are taking the gradient, Hessian
or QFI. If grad_params = None, then grad_params = bind_params
or QFI. If grad_params = None, then grad_params = bind_params
backend: The quantum backend or QuantumInstance to use to evaluate the gradient,
Hessian or QFI.
expectation: The expectation converter to be used. If none is set then
`PauliExpectation()` is used.
Returns:
callable(param_values): Function to compute a gradient, Hessian or QFI. The function
Function to compute a gradient, Hessian or QFI. The function
takes an iterable as argument which holds the parameter values.
"""
from ..converters import CircuitSampler
Expand All @@ -105,6 +109,9 @@ def gradient_wrapper(
grad_params = bind_params

grad = self.convert(operator, grad_params)
if expectation is None:
expectation = PauliExpectation()
grad = expectation.convert(grad)

def gradient_fn(p_values):
p_values_dict = dict(zip(bind_params, p_values))
Expand Down
4 changes: 2 additions & 2 deletions qiskit/transpiler/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,11 @@ def _set_type_checked_item(self, virtual, physical):

def __delitem__(self, key):
if isinstance(key, int):
del self._p2v[key]
del self._v2p[self._p2v[key]]
del self._p2v[key]
elif isinstance(key, Qubit):
del self._v2p[key]
del self._p2v[self._v2p[key]]
del self._v2p[key]
else:
raise LayoutError(
"The key to remove should be of the form"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
fixes:
- |
Include an expectation converter in the :meth:`qiskit.opflow.Gradient.gradient_wrapper`
methods to fix gradient calculation, in particular when computed with a shot-based
simulator or backend. Without an expectation converter, as before, we measured only
in the computational basis which lead to wrong results if the operator was not diagonal.
47 changes: 46 additions & 1 deletion test/python/opflow/test_gradients.py
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,9 @@ def test_gradient_wrapper(self, backend_type):

shots = 8000
backend = BasicAer.get_backend(backend_type)
q_instance = QuantumInstance(backend=backend, shots=shots)
q_instance = QuantumInstance(
backend=backend, shots=shots, seed_simulator=2, seed_transpiler=2
)
if method == "fin_diff":
np.random.seed(8)
prob_grad = Gradient(grad_method=method, epsilon=shots ** (-1 / 6.0)).gradient_wrapper(
Expand All @@ -949,6 +951,49 @@ def test_gradient_wrapper(self, backend_type):
self.assertTrue(np.allclose(result[0], correct_values[i][0], atol=0.1))
self.assertTrue(np.allclose(result[1], correct_values[i][1], atol=0.1))

@data(("statevector_simulator", 1e-7), ("qasm_simulator", 2e-1))
@unpack
def test_gradient_wrapper2(self, backend_type, atol):
"""Test the gradient wrapper for gradients checking that statevector and qasm gives the
same results
dp0/da = cos(a)sin(b) / 2
dp1/da = - cos(a)sin(b) / 2
dp0/db = sin(a)cos(b) / 2
dp1/db = - sin(a)cos(b) / 2
"""
method = "lin_comb"
a = Parameter("a")
b = Parameter("b")
params = [a, b]

qc = QuantumCircuit(2)
qc.h(1)
qc.h(0)
qc.sdg(1)
qc.cz(0, 1)
qc.ry(params[0], 0)
qc.rz(params[1], 0)
qc.h(1)

obs = (Z ^ X) - (Y ^ Y)
op = StateFn(obs, is_measurement=True) @ CircuitStateFn(primitive=qc)

shots = 8192 if backend_type == "qasm_simulator" else 1

values = [[0, np.pi / 2], [np.pi / 4, np.pi / 4], [np.pi / 3, np.pi / 9]]
correct_values = [[-4.0, 0], [-2.0, -4.82842712], [-0.68404029, -7.01396121]]
for i, value in enumerate(values):
backend = BasicAer.get_backend(backend_type)
q_instance = QuantumInstance(
backend=backend, shots=shots, seed_simulator=2, seed_transpiler=2
)
grad = NaturalGradient(grad_method=method).gradient_wrapper(
operator=op, bind_params=params, backend=q_instance
)
result = grad(value)
self.assertTrue(np.allclose(result, correct_values[i], atol=atol))

@slow_test
def test_vqe(self):
"""Test VQE with gradients"""
Expand Down
7 changes: 7 additions & 0 deletions test/python/transpiler/test_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ def test_layout_set(self):
self.assertEqual(layout[self.qr[0]], 0)
self.assertEqual(layout[0], self.qr[0])

def test_layout_del(self):
"""Deleter"""
layout = Layout()
layout[self.qr[0]] = 0
del layout[self.qr[0]]
self.assertTrue(self.qr[0] not in layout)

def test_layout_avoid_dangling_physical(self):
"""No dangling pointers for physical qubits."""
layout = Layout({self.qr[0]: 0})
Expand Down

0 comments on commit 97b7f00

Please sign in to comment.