Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Noisy circuits demo doesn't work when run on Braket devices #309

Closed
licedric opened this issue Jul 29, 2021 · 7 comments
Closed

[BUG] Noisy circuits demo doesn't work when run on Braket devices #309

licedric opened this issue Jul 29, 2021 · 7 comments

Comments

@licedric
Copy link

Issue description

In the noisy circuits demo, I get an unexpected error in the last cell when replacing the device with
dev = qml.device('braket.local.qubit', backend="braket_dm", wires=2)

There are other unrelated errors in other cells because the Braket device doesn't implement dev.state, which is outside the scope of this issue.

  • Expected behavior: (What you expect to happen)
    No errors in the last cell.

  • Actual behavior: (What actually happens)
    Error parsing ndarray; see below.

  • System information: (include your operating system, Python version, Sphinx version, etc.)
    Run on Amazon Braket notebook instance, Python version 3.7.10, PennyLane version 0.16.0

Source code and tracebacks

TypeError                                 Traceback (most recent call last)
<ipython-input-11-d0a359c35d19> in <module>
      4 
      5 for i in range(steps):
----> 6     (x, ev), cost_val = opt.step_and_cost(cost, x, ev)
      7     if i % 5 == 0 or i == steps - 1:
      8         print(f"Step: {i}    Cost: {cost_val}")

~/anaconda3/envs/Braket/lib/python3.7/site-packages/pennylane/optimize/gradient_descent.py in step_and_cost(self, objective_fn, grad_fn, *args, **kwargs)
     68         """
     69 
---> 70         g, forward = self.compute_grad(objective_fn, args, kwargs, grad_fn=grad_fn)
     71         new_args = self.apply_grad(g, args)
     72 

~/anaconda3/envs/Braket/lib/python3.7/site-packages/pennylane/optimize/gradient_descent.py in compute_grad(objective_fn, args, kwargs, grad_fn)
    125         """
    126         g = get_gradient(objective_fn) if grad_fn is None else grad_fn
--> 127         grad = g(*args, **kwargs)
    128         forward = getattr(g, "forward", None)
    129 

~/anaconda3/envs/Braket/lib/python3.7/site-packages/pennylane/_grad.py in __call__(self, *args, **kwargs)
     99         """Evaluates the gradient function, and saves the function value
    100         calculated during the forward pass in :attr:`.forward`."""
--> 101         grad_value, ans = self._get_grad_fn(args)(*args, **kwargs)
    102         self._forward = ans
    103         return grad_value

~/anaconda3/envs/Braket/lib/python3.7/site-packages/autograd/wrap_util.py in nary_f(*args, **kwargs)
     18             else:
     19                 x = tuple(args[i] for i in argnum)
---> 20             return unary_operator(unary_f, x, *nary_op_args, **nary_op_kwargs)
     21         return nary_f
     22     return nary_operator

~/anaconda3/envs/Braket/lib/python3.7/site-packages/pennylane/_grad.py in _grad_with_forward(fun, x)
    116         difference being that it returns both the gradient *and* the forward pass
    117         value."""
--> 118         vjp, ans = _make_vjp(fun, x)
    119 
    120         if not vspace(ans).size == 1:

~/anaconda3/envs/Braket/lib/python3.7/site-packages/autograd/core.py in make_vjp(fun, x)
      8 def make_vjp(fun, x):
      9     start_node = VJPNode.new_root()
---> 10     end_value, end_node =  trace(start_node, fun, x)
     11     if end_node is None:
     12         def vjp(g): return vspace(x).zeros()

~/anaconda3/envs/Braket/lib/python3.7/site-packages/autograd/tracer.py in trace(start_node, fun, x)
      8     with trace_stack.new_trace() as t:
      9         start_box = new_box(x, t, start_node)
---> 10         end_box = fun(start_box)
     11         if isbox(end_box) and end_box._trace == start_box._trace:
     12             return end_box._value, end_box._node

~/anaconda3/envs/Braket/lib/python3.7/site-packages/autograd/wrap_util.py in unary_f(x)
     13                 else:
     14                     subargs = subvals(args, zip(argnum, x))
---> 15                 return fun(*subargs, **kwargs)
     16             if isinstance(argnum, int):
     17                 x = args[argnum]

<ipython-input-9-df7c2f6664e8> in cost(x, target)
      1 def cost(x, target):
----> 2     return (damping_circuit(x) - target[0])**2

~/anaconda3/envs/Braket/lib/python3.7/site-packages/pennylane/qnode.py in __call__(self, *args, **kwargs)
    596 
    597         # execute the tape
--> 598         res = self.qtape.execute(device=self.device)
    599 
    600         # if shots was changed

~/anaconda3/envs/Braket/lib/python3.7/site-packages/pennylane/tape/tape.py in execute(self, device, params)
   1321             params = self.get_parameters()
   1322 
-> 1323         return self._execute(params, device=device)
   1324 
   1325     def execute_device(self, params, device):

~/anaconda3/envs/Braket/lib/python3.7/site-packages/autograd/tracer.py in f_wrapped(*args, **kwargs)
     42             parents = tuple(box._node for _     , box in boxed_args)
     43             argnums = tuple(argnum    for argnum, _   in boxed_args)
---> 44             ans = f_wrapped(*argvals, **kwargs)
     45             node = node_constructor(ans, f_wrapped, argvals, kwargs, argnums, parents)
     46             return new_box(ans, trace, node)

~/anaconda3/envs/Braket/lib/python3.7/site-packages/autograd/tracer.py in f_wrapped(*args, **kwargs)
     46             return new_box(ans, trace, node)
     47         else:
---> 48             return f_raw(*args, **kwargs)
     49     f_wrapped.fun = f_raw
     50     f_wrapped._is_autograd_primitive = True

~/anaconda3/envs/Braket/lib/python3.7/site-packages/pennylane/interfaces/autograd.py in _execute(self, params, device)
    163         # evaluate the tape
    164         self.set_parameters(self._all_params_unwrapped, trainable_only=False)
--> 165         res = self.execute_device(params, device=device)
    166         self.set_parameters(self._all_parameter_values, trainable_only=False)
    167 

~/anaconda3/envs/Braket/lib/python3.7/site-packages/pennylane/tape/tape.py in execute_device(self, params, device)
   1352 
   1353         if isinstance(device, qml.QubitDevice):
-> 1354             res = device.execute(self)
   1355         else:
   1356             res = device.execute(self.operations, self.observables, {})

~/anaconda3/envs/Braket/lib/python3.7/site-packages/braket/pennylane_plugin/braket_device.py in execute(self, circuit, **run_kwargs)
    189     def execute(self, circuit: CircuitGraph, **run_kwargs) -> np.ndarray:
    190         self.check_validity(circuit.operations, circuit.observables)
--> 191         self._circuit = self._pl_to_braket_circuit(circuit, **run_kwargs)
    192         self._task = self._run_task(self._circuit)
    193         return self._braket_to_pl_result(self._task.result(), circuit)

~/anaconda3/envs/Braket/lib/python3.7/site-packages/braket/pennylane_plugin/braket_device.py in _pl_to_braket_circuit(self, circuit, **run_kwargs)
    139             circuit.operations,
    140             rotations=None,  # Diagonalizing gates are applied in Braket SDK
--> 141             **run_kwargs,
    142         )
    143         for observable in circuit.observables:

~/anaconda3/envs/Braket/lib/python3.7/site-packages/braket/pennylane_plugin/braket_device.py in apply(self, operations, rotations, **run_kwargs)
    203         for operation in operations + rotations:
    204             params = [p.numpy() if isinstance(p, np.tensor) else p for p in operation.parameters]
--> 205             gate = translate_operation(operation, params)
    206             dev_wires = self.map_wires(operation.wires).tolist()
    207             ins = Instruction(gate, dev_wires)

~/anaconda3/envs/Braket/lib/python3.7/site-packages/braket/pennylane_plugin/translation.py in translate_operation(operation, parameters)
     61         Gate: The Braket gate corresponding to the given operation
     62     """
---> 63     return _translate_operation(operation, parameters)
     64 
     65 

~/anaconda3/envs/Braket/lib/python3.7/functools.py in wrapper(*args, **kw)
    838                             '1 positional argument')
    839 
--> 840         return dispatch(args[0].__class__)(*args, **kw)
    841 
    842     funcname = getattr(func, '__name__', 'singledispatch function')

~/anaconda3/envs/Braket/lib/python3.7/site-packages/braket/pennylane_plugin/translation.py in _(amplitude_damping, parameters)
    169 def _(amplitude_damping: qml.AmplitudeDamping, parameters):
    170     gamma = parameters[0]
--> 171     return noises.AmplitudeDamping(gamma)
    172 
    173 

~/anaconda3/envs/Braket/lib/python3.7/site-packages/braket/circuits/noises.py in __init__(self, gamma)
    581             gamma=gamma,
    582             qubit_count=None,
--> 583             ascii_symbols=["AD({:.2g})".format(gamma)],
    584         )
    585 

TypeError: unsupported format string passed to numpy.ndarray.__format__
@josh146 josh146 added the bug Something isn't working label Jul 30, 2021
@josh146
Copy link
Member

josh146 commented Jul 30, 2021

@licedric this might be a dimensional issue 🤔 For some reason, either intentional or not, the Braket device is receiving an ndarray for the parameter of the AmplitudeDamping operation.

Either it is something like AmplitudeDamping([0.543]) instead of Amplitude(0.543), or something even weirder is happening and it is receiving an array of size > 1?

@josh146
Copy link
Member

josh146 commented Jul 30, 2021

I wonder if this ties in with the other dimensional issue you mentioned?

@licedric
Copy link
Author

Most probably - I think somehow default.qubit takes arrays as input to parametrized gates gracefully, but the Braket device much less so. I wonder how this behaves on other devices?

@josh146
Copy link
Member

josh146 commented Jul 30, 2021

I think somehow default.qubit takes arrays as input to parametrized gates gracefully

Potentially yeah. This could be because default.qubit parametrized gates are all written using NumPy vectorization.

Either way, if this is the case this is definitely a bug. Gate parameters should always be passed as scalars to devices.

@antalszava
Copy link
Contributor

Hi @licedric, thanks again for catching this! 🙂

The main pain point is indeed that we're passing a numpy array to the operation. The fact that format cannot handle the array well causes the TypeError: unsupported format string passed to numpy.ndarray.__format__ error for Braket when the noise channel is created.

Tried two other devices, the Qiskit and Qulacs devices (dev = qml.device('qulacs.simulator', wires=2) dev = qml.device('qiskit.aer', wires=2)), both seem to handle arrays as gate parameters well.

As Josh noted, however, we'd like to pass scalars to the operations. Changing from x = np.tensor([0.0], requires_grad=True) to x = np.tensor(0.0, requires_grad=True) should be preferred in the demo (it's proposed for the demo too here: #312).

Doing this uncovered an edge case with the optimizer and an IndexError: invalid index to scalar variable. is raised. This issue is being addressed in PennyLaneAI/pennylane#1495.

@antalszava
Copy link
Contributor

#312 has been added to the dev branch and PennyLaneAI/pennylane#1495 is now part of the master branch of PennyLane. For me locally, using these modifications locally resolved the original issue.

This issue draws our attention to further addressing the following:

  1. double-checking multiple devices running QML demos regularly,
  2. being more strict with the type of object a gate can accept.

@antalszava antalszava removed the bug Something isn't working label Aug 9, 2021
@co9olguy
Copy link
Member

Closing as issue seems to be resolved. If it persists on different envs, please feel free to reopen 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants