sQUlearn / squlearn

scikit-learn interface for quantum algorithms
https://squlearn.github.io
Apache License 2.0
58 stars 18 forks source link

Second order (and mixed) QNN Pennylane derivatives with shots are not working ( dfdxdp, dfdxdx, dfdxdop) #271

Open rupof opened 5 months ago

rupof commented 5 months ago

Describe the bug

Simulations with high order QNN Pennylane derivatives with shots (dfdxdp, dfdxdx, dfdxdop) give different results when comparing to qiskit_shots, qiskit_statevector and penylane_statevector (all giving the correct results).

To Reproduce

I calculated f, dfdx, dfdxdp, dfdxdx, for a simple qnn with ChebyshevRx encoding:

import numpy as np

from squlearn.observables import SummedPaulis
from squlearn.encoding_circuit import QiskitEncodingCircuit, ChebyshevRx
from squlearn.qnn.lowlevel_qnn import LowLevelQNN
from squlearn import Executor

num_qubits = 2
x_array = np.array([[0.75], 
                    [0.1]])

circuit = ChebyshevRx(num_features=1, num_qubits=num_qubits, num_layers=1)
observable = SummedPaulis(num_qubits)

qnn_pennylane_shots = LowLevelQNN(circuit, observable, Executor("pennylane", shots=50000))
qnn_pennylane_statevector = LowLevelQNN(circuit, observable, Executor("pennylane"))

qnn_qiskit_shots = LowLevelQNN(circuit, observable, Executor("qiskit", shots=50000))
qnn_qiskit = LowLevelQNN(circuit, observable, Executor("qiskit"))

np.random.seed(1)
param = np.random.rand(qnn_pennylane_shots.num_parameters)
param_obs = np.random.rand(observable.num_parameters)

print("Pennylane statevector ")
print("f\n", qnn_pennylane_statevector.evaluate(x_array, param, param_obs, "f")["f"])
print("dfdx\n", qnn_pennylane_statevector.evaluate(x_array, param, param_obs, "dfdx")["dfdx"])
print("dfdpdx\n", qnn_pennylane_statevector.evaluate(x_array, param, param_obs, "dfdpdx")["dfdpdx"])
print("dfdxdx\n", qnn_pennylane_statevector.evaluate(x_array, param, param_obs, "dfdxdx")["dfdxdx"])
#print("dfdxdop\n", qnn_pennylane_statevector.evaluate(x_array, param, param_obs, "dfdopdx")["dfdopdx"])

print("-----------------")
print("Pennylane shots") 
print("f \n", qnn_pennylane_shots.evaluate(x_array, param, param_obs, "f")["f"])
print("dfdx \n", qnn_pennylane_shots.evaluate(x_array, param, param_obs, "dfdx")["dfdx"])
print("dfdpdx \n", qnn_pennylane_shots.evaluate(x_array, param, param_obs, "dfdpdx")["dfdpdx"])
print("dfdxdx \n", qnn_pennylane_shots.evaluate(x_array, param, param_obs, "dfdxdx")["dfdxdx"])
#print("dfdxdop\n", qnn_pennylane_shots.evaluate(x_array, param, param_obs, "dfdopdx")["dfdopdx"])

print("-----------------")
print("qiskit") 
print("f \n", qnn_qiskit.evaluate(x_array, param, param_obs, "f")["f"])
print("dfdx \n", qnn_qiskit.evaluate(x_array, param, param_obs, "dfdx")["dfdx"])
print("dfdpdx \n", qnn_qiskit.evaluate(x_array, param, param_obs, "dfdpdx")["dfdpdx"])
print("dfdxdx \n", qnn_qiskit.evaluate(x_array, param, param_obs, "dfdxdx")["dfdxdx"])

print("-----------------")
print("qiskit shots") 
print("f \n", qnn_qiskit_shots.evaluate(x_array, param, param_obs, "f")["f"])
print("dfdx \n", qnn_qiskit_shots.evaluate(x_array, param, param_obs, "dfdx")["dfdx"])
print("dfdpdx \n", qnn_qiskit_shots.evaluate(x_array, param, param_obs, "dfdpdx")["dfdpdx"])
print("dfdxdx \n", qnn_qiskit_shots.evaluate(x_array, param, param_obs, "dfdxdx")["dfdxdx"])

Expected behavior

The output of dfdxdx, dfdxdp should give the same values as the other implementations (like for f and dfdx implementation below).

Pennylane statevector 
f
 [0.3558851  0.25388108]
dfdx
 [[0.18300929]
 [0.1394669 ]]
dfdpdx
 [[[ 0.16170338]
  [ 0.27386335]
  [ 0.08768908]
  [ 0.10615746]]

 [[ 0.03015719]
  [ 0.11875324]
  [-0.03103286]
  [-0.02106788]]]
dfdxdx
 [[[0.14283571]]

 [[0.04234631]]]
-----------------
Pennylane shots
f 
 [0.35595153 0.25376274]
dfdx 
 [[0.18361726]
 [0.14007897]]
dfdpdx 
 [[[0.09823249]
  [0.19851102]
  [0.        ]
  [0.        ]]

 [[0.07489638]
  [0.15020733]
  [0.        ]
  [0.        ]]]
dfdxdx 
 [[[0.31401326]]

 [[0.0140488 ]]]
-----------------
qiskit
f 
 [0.3558851  0.25388108]
dfdx 
 [[0.18300929]
 [0.1394669 ]]
dfdpdx 
 [[[ 0.16170338]
  [ 0.27386335]
  [ 0.08768908]
  [ 0.10615746]]

 [[ 0.03015719]
  [ 0.11875324]
  [-0.03103286]
  [-0.02106788]]]
dfdxdx 
 [[[0.14283571]]

 [[0.04234631]]]
-----------------
qiskit shots
f 
 [0.35561856 0.25455449]
dfdx 
 [[0.184588  ]
 [0.13953619]]
dfdpdx 
 [[[ 0.16126975]
  [ 0.27412348]
  [ 0.087647  ]
  [ 0.10636382]]

 [[ 0.03027624]
  [ 0.11678895]
  [-0.03101101]
  [-0.02085382]]]
dfdxdx 
 [[[0.14369644]]

 [[0.04309145]]]

I am happy to help if you have some suggestion :)

Thank you very much!

Additional context

David-Kreplin commented 5 months ago

Fix in PR #272

David-Kreplin commented 5 months ago

Higher-order derivatives like dxdx deppend on the PennyLane issue: https://github.com/PennyLaneAI/pennylane/issues/5824 There is nothing, we can do right now. :/