# Shot Adaptive Optimiser not Implemented Correctly in Pennylane?

Hello!

I would like to use the ShotAdaptiveOptimiser for depth 1 QAOA. However, I get an error if the depth of the QAOA circuit is not greater or equal to 2. Below, I have reproduced the same error using the example code from the ShotAdaptiveOptimiser documentation page, by only modifying the number of StronglyEntanglingLayers to 1.

``````import pennylane as qml
from pennylane import numpy as npp

coeffs = [2, 4, -1, 5, 2]
obs = [
qml.PauliX(1),
qml.PauliZ(1),
qml.PauliX(0) @ qml.PauliX(1),
qml.PauliY(0) @ qml.PauliY(1),
qml.PauliZ(0) @ qml.PauliZ(1)
]
H = qml.Hamiltonian(coeffs, obs)
dev = qml.device("default.qubit", wires=2, shots=100)
cost = qml.ExpvalCost(qml.templates.StronglyEntanglingLayers, H, dev)

shape = qml.templates.StronglyEntanglingLayers.shape(n_layers=1, n_wires=2)
params = npp.random.random(shape)
print(params)

for i in range(6):
params = opt.step(cost, params)
print(f"Step {i}: cost = {cost(params):.2f}, shots_used = {opt.total_shots_used}")
``````

Here is the output of running the code above:

``````[[[0.34656026 0.20603406 0.97567429]
[0.89482299 0.70363518 0.84379755]]]
Step 0: cost = 1.14, shots_used = 120
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
18 for i in range(6):
---> 19     params = opt.step(cost, params)
20     print(f"Step {i}: cost = {cost(params):.2f}, shots_used = {opt.total_shots_used}")

444 self.total_shots_used += self.shots_used
446 # compute the gradient, as well as the variance in the gradient,
447 # using the number of shots determined by the array s.
451 if self.xi is None:

403 for idx in p_ind:

File c:\ProgramData\Anaconda3\lib\site-packages\pennylane\numpy\tensor.py:187, in tensor.__getitem__(self, *args, **kwargs)
186 def __getitem__(self, *args, **kwargs):
--> 187     item = super().__getitem__(*args, **kwargs)
189     if not isinstance(item, tensor):

IndexError: too many indices for array: array is 2-dimensional, but 3 were indexed
``````

The output of running `qml.about()` is the following:

``````Name: PennyLane
Version: 0.30.0
Summary: PennyLane is a Python quantum machine learning library by Xanadu Inc.
Author:
Author-email:
Location: c:\programdata\anaconda3\lib\site-packages
Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, pennylane-lightning, requests, rustworkx, scipy, semantic-version, toml
Required-by: PennyLane-Lightning

Platform info:           Windows-10-10.0.19045-SP0
Python version:          3.9.15
Numpy version:           1.23.5
Scipy version:           1.10.1
Installed devices:
- lightning.qubit (PennyLane-Lightning-0.30.0)
- default.gaussian (PennyLane-0.30.0)
- default.mixed (PennyLane-0.30.0)
- default.qubit (PennyLane-0.30.0)
- default.qubit.jax (PennyLane-0.30.0)
- default.qubit.tf (PennyLane-0.30.0)
- default.qubit.torch (PennyLane-0.30.0)
- default.qutrit (PennyLane-0.30.0)
- null.qubit (PennyLane-0.30.0)
None
``````

I have tried modifying the ShotAdaptiveOptimiser class code in the pennylane package to get more information about the error root, and I could find out that the first time the class method `compute_grad` is executed, the class attribute `self.s` has the same shape as the `params` variable defined and printed in the code above, that is

``````[tensor([[[10, 10, 10],
``````

However, after the first iteration, the class attribute `self.s` has lost one axis, and looks like this

``````[tensor([[5, 5, 5],
``````

Moreover, I also believe that the code in line 482 in `shot_adaptive.py` which is

``````self.s[idx] = np.squeeze(np.int64(np.clip(s, min(2, self.min_shots), smax)))
``````

should be

``````self.s[idx] = np.squeeze(np.int64(np.clip(s, max(2, self.min_shots), smax)))
``````

so that the number of shots to use is always greater or equal to `self.min_shots`, which might be greater than 2. There must also be another error somewhere, because as you can see in the value of `self.s` after the first iteration, the number of shots to use is strictly less than 10, which was the minimum defined when the `qml.ShotAdaptiveOptimizer` object was created.

Thank you very much in advance.

Hey @pormelrog!

Excellent post . Was able to replicate your error and appreciated you digging into it yourself .

Do you mind making a bug report?

In the meantime, a temporary solution would be for you to do this in the source code (line 482 of `shot_adaptive.py`):

``````            self.s[idx] = (
np.int64(np.clip(s, min(2, self.min_shots), smax)).squeeze()
if len(self.s[idx]) > 1
else np.int64(np.clip(s, min(2, self.min_shots), smax))
)
``````

I’m not sure if this is the best longterm & efficient solution, so best to leave it to the developers to figure out . They can reference your bug report though!

Hello Isaac,

As I mentioned in my post above (and also in the bug report), I also believe that the clipping is not done correctly, because according to the paper on which the AdaptiveShotOptimiser (iCANS1) is based, the elements in the vector `self.s` must all be greater or equal to `self.min_shots`. However, the code `self.s[idx] = np.squeeze(np.int64(np.clip(s, min(2, self.min_shots), smax)))` floors the elements at 2, and it must be replaced by `self.s[idx] = np.squeeze(np.int64(np.clip(s, max(2, self.min_shots), smax)))`.

Let me know if you need more information, the bug report is missing anything or there is something I should do.

Kind regards,

Francisco

Looks good! Thank you . I’ve notified the relevant people and we’ll look into it