qiskit icon indicating copy to clipboard operation
qiskit copied to clipboard

[WIP] Gradients with the primitives

Open a-matsuo opened this issue 3 years ago • 2 comments

Co-authored-by: Ikko Hamamura [email protected] Co-authored-by: Takashi Imamichi [email protected]

Summary

This PR adds Finite Difference/Parameter Shift/Linear Comb. of Unitaries gradients with the primitives. close #8496

Details and comments

There are 3 types of gradient methods for a sampler and estimator

  • [x] FiniteDiffSamplerGradient
  • [x] ParamShiftSamplerGradient
  • [x] LinCombSamplerGradient
  • [x] FiniteDiffEstimatorGradient
  • [x] ParamShiftEstimatorGradient
  • [x] LinCombEstimatorGradient
  • [ ] unittests

a-matsuo avatar Aug 12 '22 06:08 a-matsuo

Thank you for opening a new pull request.

Before your PR can be merged it will first need to pass continuous integration tests and be reviewed. Sometimes the review process can be slow, so please be patient.

While you're waiting, please feel free to review other open PRs. While only a subset of people are authorized to approve pull requests for merging, everyone is encouraged to review open pull requests. Doing reviews helps reduce the burden on the core team and helps make the project's code better for everyone.

One or more of the the following people are requested to review this:

  • @Qiskit/terra-core
  • @manoelmarques
  • @woodsp-ibm

qiskit-bot avatar Aug 12 '22 06:08 qiskit-bot

Pull Request Test Coverage Report for Build 2992392546

  • 656 of 679 (96.61%) changed or added relevant lines in 14 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.1%) to 84.361%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/algorithms/gradients/base_sampler_gradient.py 34 36 94.44%
qiskit/algorithms/gradients/finite_diff_estimator_gradient.py 39 41 95.12%
qiskit/algorithms/gradients/finite_diff_sampler_gradient.py 42 44 95.45%
qiskit/algorithms/gradients/lin_comb_estimator_gradient.py 60 62 96.77%
qiskit/algorithms/gradients/lin_comb_sampler_gradient.py 58 60 96.67%
qiskit/algorithms/gradients/param_shift_estimator_gradient.py 44 46 95.65%
qiskit/algorithms/gradients/param_shift_sampler_gradient.py 45 47 95.74%
qiskit/algorithms/gradients/spsa_estimator_gradient.py 44 46 95.65%
qiskit/algorithms/gradients/spsa_sampler_gradient.py 49 51 96.08%
qiskit/algorithms/gradients/utils.py 166 168 98.81%
<!-- Total: 656 679
Totals Coverage Status
Change from base Build 2990361532: 0.1%
Covered Lines: 57844
Relevant Lines: 68567

💛 - Coveralls

coveralls avatar Aug 12 '22 08:08 coveralls

@Cryoris (I couldn't make a reply to this comment.)

I have one main question: could we build the gradients in such a way that we export the logic to make all parameters in a circuit unique in a separate function? This is something that the classically efficient gradients will also need (and the linear combination of unitaries, too?) so if it's inside the parameter shift rule that's a bit hidden 🙂

make_param_shift_gradient_circuit_data in gradients/utils.py does it. It adds virtual variables for the parameter shift rule if necessary. Maybe, we can make that functionality optional and reuse it for the classically efficient gradients? https://github.com/a-matsuo/qiskit-terra/blob/9abbd2ac79ef1177308c236c0be344d2333384c6/qiskit/algorithms/gradients/utils.py#L75

a-matsuo avatar Aug 29 '22 04:08 a-matsuo

Thank you so much, everyone! I fixed the codes based on the review comments.

a-matsuo avatar Aug 31 '22 11:08 a-matsuo

It's good to use ``...`` instead of `...` for make code part of docstrings, e.g., ``circuit`` and ``run``.

t-imamichi avatar Aug 31 '22 13:08 t-imamichi

Update: the following example is a problem of CU gate #7326


The following example works fine with param shift and lcu, but finite diff and SPSA raise an error. Could you try it?

from qiskit import QuantumCircuit
from qiskit.circuit import ParameterVector
from qiskit.algorithms.gradients import (
    FiniteDiffEstimatorGradient,
    LinCombEstimatorGradient,
    ParamShiftEstimatorGradient,
    SPSAEstimatorGradient,
)
from qiskit.primitives import Estimator
from qiskit.quantum_info import SparsePauliOp

qc = QuantumCircuit(2)
param = ParameterVector("θ", 4)
qc.h(0)
qc.cu(param[0], param[1], param[2], param[3], 0, 1)
print(qc)

op = SparsePauliOp(["IZ", "XY"])

estimator = Estimator()

for grad in [ParamShiftEstimatorGradient, LinCombEstimatorGradient]:
    print(grad)
    g = grad(estimator)
    result = g.run([qc], [op], [[1, 2, 3, 4]]).result()
    print(result.gradients)
    print()

for grad in [FiniteDiffEstimatorGradient, SPSAEstimatorGradient]:
    print(grad)
    g = grad(estimator, 1e-6)
    result = g.run([qc], [op], [[1, 2, 3, 4]]).result()
    print(result.gradients)
    print()

output

     ┌───┐
q_0: ┤ H ├────────────■─────────────
     └───┘┌───────────┴────────────┐
q_1: ─────┤ U(θ[0],θ[1],θ[2],θ[3]) ├
          └────────────────────────┘
<class 'qiskit.algorithms.gradients.param_shift_estimator_gradient.ParamShiftEstimatorGradient'>
[array([-1.22605084e-01,  4.60330157e-01,  1.66533454e-16,  4.60330157e-01])]

<class 'qiskit.algorithms.gradients.lin_comb_estimator_gradient.LinCombEstimatorGradient'>
[array([-0.12260508,  0.46033016,  0.        ,  0.46033016])]

<class 'qiskit.algorithms.gradients.finite_diff_estimator_gradient.FiniteDiffEstimatorGradient'>
Traceback (most recent call last):
  File "/Users/ima/envs/dev39/lib/python3.9/site-packages/qiskit/circuit/parameterexpression.py", line 459, in __float__
    return float(self._symbol_expr)
  File "symengine_wrapper.pyx", line 1151, in symengine.lib.symengine_wrapper.Basic.__float__
  File "symengine_wrapper.pyx", line 976, in symengine.lib.symengine_wrapper.Basic.n
  File "symengine_wrapper.pyx", line 4346, in symengine.lib.symengine_wrapper.evalf
RuntimeError: Symbol cannot be evaluated.

The above exception was the direct cause of the following exception:
...
TypeError: ParameterExpression with unbound parameters ({ParameterVectorElement(θ[0])}) cannot be cast to a float.

t-imamichi avatar Sep 01 '22 09:09 t-imamichi

@t-imamichi This seems to be a problem with the primitives themselves, not the gradient code:

from qiskit import QuantumCircuit
from qiskit.circuit import ParameterVector
from qiskit.primitives import Estimator
from qiskit.quantum_info import SparsePauliOp

qc = QuantumCircuit(2)
param = ParameterVector("θ", 4)
qc.h(0)
qc.cu(param[0], param[1], param[2], param[3], 0, 1)
print(qc)

op = SparsePauliOp(["IZ", "XY"])

estimator = Estimator()
res = estimator.run([qc], [op], [[1, 2, 3, 4]]).result()
print(res)

This gives the same error...

Cryoris avatar Sep 01 '22 09:09 Cryoris

@Cryoris Thanks. I didn't notice it. I look into primitives.

t-imamichi avatar Sep 01 '22 09:09 t-imamichi

I found that CU gate has an issue of parameter binding #7326. We cannot apply primitives to circuits with CU gates...

t-imamichi avatar Sep 01 '22 10:09 t-imamichi