# Gradients computing fail for evolution under a hamiltonian

Hello, I’m trying to implement quantum ML model with evolution under a hamiltonian.
exp(-iT H)
with
H=\sum_{j=1}^N a_j X_j+\sum_{j=1}^N \sum_{k=1}^{j-1} J_{j k} Z_j Z_k

I want to optimize hamiltonian coefficients a_j and J_{j k} but it doesn’t seem to work.

I tried with torch engine and got error because of some converting of coefficient tensor to numpy with is not possible with gradient requiring tensors.
And also tried with pure pennylane implementation but it also failes with some error.

Is it not yet supported by pennylane operation or I just use something incorrectly?

### Code and error messages:

torch

import pennylane as qml
from pennylane import numpy as np

import matplotlib.pyplot as plt
import torch
import torch.optim as optim

n_qubits = 3
device = qml.device("default.qubit", wires=n_qubits)

obs = [qml.PauliX(i) for i in range(n_qubits)] + [qml.PauliZ(i)@qml.PauliZ(j) for i in range(n_qubits) for j in range(i+1, n_qubits)]

@qml.qnode(device)
def model_qnode(inputs, coeffs):
for i in range(n_qubits):
qml.RX(inputs, wires=i)

H = qml.Hamiltonian(coeffs, obs)
qml.exp(H, 1j)

return qml.expval(qml.PauliZ(0))

def model_class():
return qml.qnn.TorchLayer(model_qnode, {"coeffs":[n_qubits + n_qubits*(n_qubits-1)//2]})

model = model_class()
criterion = torch.nn.MSELoss()
hist = []
for i in range(50):

pred = model(torch.tensor([0.0]))

loss = criterion(pred, torch.tensor([1.0]))
hist.append(loss.item())
loss.backward()
optimizer.step()

plt.plot(hist)
plt.show()


Error message

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
DecompositionUndefinedError               Traceback (most recent call last)
File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/transforms/op_transforms.py:264, in op_transform.fn(self, obj, *args, **kwargs)
261 try:
262     # attempt to decompose the operation and call
263     # the tape transform function if defined
--> 264     return self.tape_fn(obj.expand(), *args, **kwargs)
266 except (
267     AttributeError,
268     qml.operation.OperatorPropertyUndefined,
(...)
271     # if obj.expand() does not exist, a required operation property was not found,
272     # or the tape transform function does not exist, simply raise the original exception

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/operation.py:1444, in Operator.expand(self)
1443 if not self.has_decomposition:
-> 1444     raise DecompositionUndefinedError
1446 qscript = qml.tape.QuantumScript(self.decomposition())

DecompositionUndefinedError:

The above exception was the direct cause of the following exception:

RuntimeError                              Traceback (most recent call last)
32 for i in range(50):
---> 35     pred = model(torch.tensor([0.0]))
37     loss = criterion(pred, torch.tensor([1.0]))
38     hist.append(loss.item())

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/torch/nn/modules/module.py:1501, in Module._call_impl(self, *args, **kwargs)
1496 # If we don't have any hooks, we want to skip the rest of the logic in
1497 # this function, and just call forward.
1498 if not (self._backward_hooks or self._backward_pre_hooks or self._forward_hooks or self._forward_pre_hooks
1499         or _global_backward_pre_hooks or _global_backward_hooks
1500         or _global_forward_hooks or _global_forward_pre_hooks):
-> 1501     return forward_call(*args, **kwargs)
1502 # Do not call functions when jit is used
1503 full_backward_hooks, non_full_backward_hooks = [], []

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/qnn/torch.py:408, in TorchLayer.forward(self, inputs)
405     results = torch.stack(reconstructor)
406 else:
407     # calculate the forward pass as usual
--> 408     results = self._evaluate_qnode(inputs)
410 # reshape to the correct number of batch dims
411 if has_batch_dim:

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/qnn/torch.py:429, in TorchLayer._evaluate_qnode(self, x)
417 """Evaluates the QNode for a single input datapoint.
418
419 Args:
(...)
423     tensor: output datapoint
424 """
425 kwargs = {
426     **{self.input_arg: x},
427     **{arg: weight.to(x) for arg, weight in self.qnode_weights.items()},
428 }
--> 429 res = self.qnode(**kwargs)
431 if isinstance(res, torch.Tensor):
432     return res.type(x.dtype)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/qnode.py:989, in QNode.__call__(self, *args, **kwargs)
986     self.execute_kwargs.pop("mode")
988 # pylint: disable=unexpected-keyword-arg
--> 989 res = qml.execute(
990     (self._tape,),
991     device=self.device,
993     interface=self.interface,
994     transform_program=self.transform_program,
996     override_shots=override_shots,
997     **self.execute_kwargs,
998 )
1000 res = res[0]
1002 # convert result to the interface in case the qfunc has no parameters

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/interfaces/execution.py:636, in execute(tapes, device, gradient_fn, interface, transform_program, grad_on_execution, gradient_kwargs, cache, cachesize, max_diff, override_shots, expand_fn, max_expansion, device_batch_transform)
634 # Exiting early if we do not need to deal with an interface boundary
635 if no_interface_boundary_required:
--> 636     results = inner_execute(tapes)
637     results = batch_fn(results)
638     return program_post_processing(results)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/interfaces/execution.py:255, in _make_inner_execute.<locals>.inner_execute(tapes, **_)
253 if numpy_only:
254     tapes = tuple(qml.transforms.convert_to_numpy_parameters(t) for t in tapes)
--> 255 return cached_device_execution(tapes)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/interfaces/execution.py:377, in cache_execute.<locals>.wrapper(tapes, **kwargs)
372         return (res, []) if return_tuple else res
374 else:
375     # execute all unique tapes that do not exist in the cache
376     # convert to list as new device interface returns a tuple
--> 377     res = list(fn(tuple(execution_tapes.values()), **kwargs))
379 final_res = []
381 for i, tape in enumerate(tapes):

File ~/miniconda3/envs/qml_torch/lib/python3.10/contextlib.py:79, in ContextDecorator.__call__.<locals>.inner(*args, **kwds)
76 @wraps(func)
77 def inner(*args, **kwds):
78     with self._recreate_cm():
---> 79         return func(*args, **kwds)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/_qubit_device.py:629, in QubitDevice.batch_execute(self, circuits)
624 for circuit in circuits:
625     # we need to reset the device here, else it will
626     # not start the next computation in the zero state
627     self.reset()
--> 629     res = self.execute(circuit)
630     results.append(res)
632 if self.tracker.active:

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/devices/default_qubit_torch.py:247, in DefaultQubitTorch.execute(self, circuit, **kwargs)
239         if params_cuda_device != specified_device_cuda:
240             warnings.warn(
241                 f"Torch device {self._torch_device} specified "
242                 "upon PennyLane device creation does not match the "
243                 "Torch device of the gate parameters; "
244                 f"{self._torch_device} will be used."
245             )
--> 247 return super().execute(circuit, **kwargs)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/_qubit_device.py:337, in QubitDevice.execute(self, circuit, **kwargs)
334 self.check_validity(circuit.operations, circuit.observables)
336 # apply all circuit operations
--> 337 self.apply(circuit.operations, rotations=self._get_diagonalizing_gates(circuit), **kwargs)
339 # generate computational basis samples
340 if self.shots is not None or circuit.is_sampled:

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/devices/default_qubit.py:294, in DefaultQubit.apply(self, operations, rotations, **kwargs)
292         self._state = self._apply_parametrized_evolution(self._state, operation)
293     else:
--> 294         self._state = self._apply_operation(self._state, operation)
296 # store the pre-rotated state
297 self._pre_rotated_state = self._state

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/devices/default_qubit.py:334, in DefaultQubit._apply_operation(self, state, operation)
331     axes = [ax + shift for ax in self.wires.indices(wires)]
332     return self._apply_ops[operation.name](state, axes)
--> 334 matrix = self._asarray(self._get_unitary_matrix(operation), dtype=self.C_DTYPE)
336 if operation in diagonal_in_z_basis:
337     return self._apply_diagonal_unitary(state, matrix, wires)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/devices/default_qubit_torch.py:319, in DefaultQubitTorch._get_unitary_matrix(self, unitary)
317 if unitary in diagonal_in_z_basis:
318     return self._asarray(unitary.eigvals(), dtype=self.C_DTYPE)
--> 319 return self._asarray(unitary.matrix(), dtype=self.C_DTYPE)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/ops/op_math/exp.py:393, in Exp.matrix(self, wire_order)
387     except OperatorPropertyUndefined:
388         warn(
389             f"The autograd matrix for {self} is not differentiable. "
390             "Use a different interface if you need backpropagation.",
391             UserWarning,
392         )
--> 393 return super().matrix(wire_order=wire_order)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/ops/op_math/symbolicop.py:239, in ScalarSymbolicOp.matrix(self, wire_order)
237 # compute base matrix
238 if isinstance(self.base, qml.Hamiltonian):
--> 239     base_matrix = qml.matrix(self.base)
240 else:
241     base_matrix = self.base.matrix()

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/transforms/op_transforms.py:213, in op_transform.__call__(self, *targs, **tkwargs)
210     obj, *targs = targs
212 if isinstance(obj, (qml.operation.Operator, qml.tape.QuantumScript)) or callable(obj):
--> 213     return self._create_wrapper(obj, *targs, **tkwargs)
215 # Input is not an operator nor a QNode nor a quantum tape nor a qfunc.
216 # Assume Python decorator syntax:
217 #
(...)
229 # Prepend the input to the transform args,
230 # and create a wrapper function.
231 if obj is not None:

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/transforms/op_transforms.py:410, in op_transform._create_wrapper(self, obj, wire_order, *targs, **tkwargs)
407     if wire_order is not None:
408         tkwargs["wire_order"] = wire_order
--> 410     wrapper = self.fn(obj, *targs, **tkwargs)
412 elif isinstance(obj, qml.tape.QuantumScript):
413     # Input is a quantum tape. Get the quantum tape.
414     tape, verified_wire_order = self._make_tape(obj, wire_order)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/transforms/op_transforms.py:273, in op_transform.fn(self, obj, *args, **kwargs)
264     return self.tape_fn(obj.expand(), *args, **kwargs)
266 except (
267     AttributeError,
268     qml.operation.OperatorPropertyUndefined,
(...)
271     # if obj.expand() does not exist, a required operation property was not found,
272     # or the tape transform function does not exist, simply raise the original exception
--> 273     raise e1 from e

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/transforms/op_transforms.py:258, in op_transform.fn(self, obj, *args, **kwargs)
240 """Evaluate the underlying operator transform function.
241
242 If a corresponding tape transform for the operator has been registered
(...)
255     any: the result of evaluating the transform
256 """
257 try:
--> 258     return self._fn(obj, *args, **kwargs)
260 except Exception as e1:  # pylint: disable=broad-except
261     try:
262         # attempt to decompose the operation and call
263         # the tape transform function if defined

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/ops/functions/matrix.py:125, in matrix(op, wire_order)
122     op = 1.0 * op  # convert to a Hamiltonian
124 if isinstance(op, qml.Hamiltonian):
--> 125     return op.sparse_matrix(wire_order=wire_order).toarray()
127 return op.matrix(wire_order=wire_order)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/ops/qubit/hamiltonian.py:400, in Hamiltonian.sparse_matrix(self, wire_order)
397 n = len(wires)
398 matrix = scipy.sparse.csr_matrix((2**n, 2**n), dtype="complex128")
--> 400 coeffs = qml.math.toarray(self.data)
402 temp_mats = []
403 for coeff, op in zip(coeffs, self.ops):

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/autoray/autoray.py:80, in do(fn, like, *args, **kwargs)
31 """Do function named fn on (*args, **kwargs), peforming single
32 dispatch to retrieve fn based on whichever library defines the class of
33 the args[0], or the like keyword argument if specified.
(...)
77     <tf.Tensor: id=91, shape=(3, 3), dtype=float32>
78 """
79 backend = choose_backend(fn, *args, like=like, **kwargs)
---> 80 return get_lib_fn(backend, fn)(*args, **kwargs)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/autoray/autoray.py:1415, in numpy_to_numpy(x)
1414 def numpy_to_numpy(x):
-> 1415     return do("asarray", x, like="numpy")

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/autoray/autoray.py:80, in do(fn, like, *args, **kwargs)
31 """Do function named fn on (*args, **kwargs), peforming single
32 dispatch to retrieve fn based on whichever library defines the class of
33 the args[0], or the like keyword argument if specified.
(...)
77     <tf.Tensor: id=91, shape=(3, 3), dtype=float32>
78 """
79 backend = choose_backend(fn, *args, like=like, **kwargs)
---> 80 return get_lib_fn(backend, fn)(*args, **kwargs)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/torch/_tensor.py:970, in Tensor.__array__(self, dtype)
968     return handle_torch_function(Tensor.__array__, (self,), self, dtype=dtype)
969 if dtype is None:
--> 970     return self.numpy()
971 else:
972     return self.numpy().astype(dtype, copy=False)

RuntimeError: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.


#### pure pennylane implementation

import pennylane as qml
from pennylane import numpy as np

import matplotlib.pyplot as plt

n_qubits = 3

device = qml.device("default.qubit", wires=n_qubits)

obs = [qml.PauliX(i) for i in range(n_qubits)] + [qml.PauliZ(i)@qml.PauliZ(j) for i in range(n_qubits) for j in range(i+1, n_qubits)]

coeffs = np.random.rand(n_qubits + n_qubits*(n_qubits-1)//2)*np.pi*2

@qml.qnode(device)
def model(inputs, coeffs):
for i in range(n_qubits):
qml.RX(inputs, wires=i)

H = qml.Hamiltonian(coeffs, obs)
qml.exp(H, 1j)

return qml.expval(qml.PauliZ(0))

def cost(coeffs):
loss = (pred - np.array([1.0]))**2
return loss

for i in range(50):

coeffs = optimizer.step(cost, coeffs)


Error:

---------------------------------------------------------------------------
DecompositionUndefinedError               Traceback (most recent call last)
File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/transforms/op_transforms.py:264, in op_transform.fn(self, obj, *args, **kwargs)
261 try:
262     # attempt to decompose the operation and call
263     # the tape transform function if defined
--> 264     return self.tape_fn(obj.expand(), *args, **kwargs)
266 except (
267     AttributeError,
268     qml.operation.OperatorPropertyUndefined,
(...)
271     # if obj.expand() does not exist, a required operation property was not found,
272     # or the tape transform function does not exist, simply raise the original exception

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/operation.py:1444, in Operator.expand(self)
1443 if not self.has_decomposition:
-> 1444     raise DecompositionUndefinedError
1446 qscript = qml.tape.QuantumScript(self.decomposition())

DecompositionUndefinedError:

The above exception was the direct cause of the following exception:

AttributeError                            Traceback (most recent call last)
32     return loss
34     for i in range(50):
---> 36     coeffs = optimizer.step(cost, coeffs)
37     hist.append(loss)
39     plt.plot(hist)

70 def step(self, objective_fn, *args, grad_fn=None, **kwargs):
71     \"\"\"Update trainable arguments with one step of the optimizer.
72
73     Args:
(...)
85         If single arg is provided, list [array] is replaced by array.
86     \"\"\"
91     # unwrap from list if one argument, cleaner return

99 r\"\"\"Compute gradient of the objective function at the given point and return it along with
100 the objective function forward pass (if available).
101
(...)
114     will not be evaluted and instead None will be returned.
115 \"\"\"
--> 117 grad = g(*args, **kwargs)
118 forward = getattr(g, \"forward\", None)
120 num_trainable_args = sum(getattr(arg, \"requires_grad\", False) for arg in args)

117     self._forward = self._fun(*args, **kwargs)
118     return ()
121 self._forward = ans

18 else:
19     x = tuple(args[i] for i in argnum)
---> 20 return unary_operator(unary_f, x, *nary_op_args, **nary_op_kwargs)

132 @staticmethod
133 @unary_to_nary
135     \"\"\"This function is a replica of autograd.grad, with the only
136     difference being that it returns both the gradient *and* the forward pass
137     value.\"\"\"
--> 138     vjp, ans = _make_vjp(fun, x)
140     if not vspace(ans).size == 1:
141         raise TypeError(
142             \"Grad only applies to real scalar-output functions. \"
144         )

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()

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/autograd/tracer.py:10, 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

13 else:
14     subargs = subvals(args, zip(argnum, x))
---> 15 return fun(*subargs, **kwargs)

<a >29</a> def cost(coeffs):
---> <a >30</a>     pred = model(np.array([0.0], requires_grad=False), coeffs)
<a >31</a>     loss = (pred - np.array([1.0]))**2
<a >32</a>     return loss

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/qnode.py:989, in QNode.__call__(self, *args, **kwargs)
986     self.execute_kwargs.pop(\"mode\")
988 # pylint: disable=unexpected-keyword-arg
--> 989 res = qml.execute(
990     (self._tape,),
991     device=self.device,
993     interface=self.interface,
994     transform_program=self.transform_program,
996     override_shots=override_shots,
997     **self.execute_kwargs,
998 )
1000 res = res[0]
1002 # convert result to the interface in case the qfunc has no parameters

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/interfaces/execution.py:636, in execute(tapes, device, gradient_fn, interface, transform_program, grad_on_execution, gradient_kwargs, cache, cachesize, max_diff, override_shots, expand_fn, max_expansion, device_batch_transform)
634 # Exiting early if we do not need to deal with an interface boundary
635 if no_interface_boundary_required:
--> 636     results = inner_execute(tapes)
637     results = batch_fn(results)
638     return program_post_processing(results)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/interfaces/execution.py:255, in _make_inner_execute.<locals>.inner_execute(tapes, **_)
253 if numpy_only:
254     tapes = tuple(qml.transforms.convert_to_numpy_parameters(t) for t in tapes)
--> 255 return cached_device_execution(tapes)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/interfaces/execution.py:377, in cache_execute.<locals>.wrapper(tapes, **kwargs)
372         return (res, []) if return_tuple else res
374 else:
375     # execute all unique tapes that do not exist in the cache
376     # convert to list as new device interface returns a tuple
--> 377     res = list(fn(tuple(execution_tapes.values()), **kwargs))
379 final_res = []
381 for i, tape in enumerate(tapes):

File ~/miniconda3/envs/qml_torch/lib/python3.10/contextlib.py:79, in ContextDecorator.__call__.<locals>.inner(*args, **kwds)
76 @wraps(func)
77 def inner(*args, **kwds):
78     with self._recreate_cm():
---> 79         return func(*args, **kwds)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/_qubit_device.py:629, in QubitDevice.batch_execute(self, circuits)
624 for circuit in circuits:
625     # we need to reset the device here, else it will
626     # not start the next computation in the zero state
627     self.reset()
--> 629     res = self.execute(circuit)
630     results.append(res)
632 if self.tracker.active:

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/_qubit_device.py:337, in QubitDevice.execute(self, circuit, **kwargs)
334 self.check_validity(circuit.operations, circuit.observables)
336 # apply all circuit operations
--> 337 self.apply(circuit.operations, rotations=self._get_diagonalizing_gates(circuit), **kwargs)
339 # generate computational basis samples
340 if self.shots is not None or circuit.is_sampled:

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/devices/default_qubit.py:294, in DefaultQubit.apply(self, operations, rotations, **kwargs)
292         self._state = self._apply_parametrized_evolution(self._state, operation)
293     else:
--> 294         self._state = self._apply_operation(self._state, operation)
296 # store the pre-rotated state
297 self._pre_rotated_state = self._state

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/devices/default_qubit.py:334, in DefaultQubit._apply_operation(self, state, operation)
331     axes = [ax + shift for ax in self.wires.indices(wires)]
332     return self._apply_ops[operation.name](state, axes)
--> 334 matrix = self._asarray(self._get_unitary_matrix(operation), dtype=self.C_DTYPE)
336 if operation in diagonal_in_z_basis:
337     return self._apply_diagonal_unitary(state, matrix, wires)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/devices/default_qubit.py:674, in DefaultQubit._get_unitary_matrix(self, unitary)
671 if unitary in diagonal_in_z_basis:
672     return unitary.eigvals()
--> 674 return unitary.matrix()

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/ops/op_math/exp.py:393, in Exp.matrix(self, wire_order)
387     except OperatorPropertyUndefined:
388         warn(
389             f\"The autograd matrix for {self} is not differentiable. \"
390             \"Use a different interface if you need backpropagation.\",
391             UserWarning,
392         )
--> 393 return super().matrix(wire_order=wire_order)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/ops/op_math/symbolicop.py:239, in ScalarSymbolicOp.matrix(self, wire_order)
237 # compute base matrix
238 if isinstance(self.base, qml.Hamiltonian):
--> 239     base_matrix = qml.matrix(self.base)
240 else:
241     base_matrix = self.base.matrix()

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/transforms/op_transforms.py:213, in op_transform.__call__(self, *targs, **tkwargs)
210     obj, *targs = targs
212 if isinstance(obj, (qml.operation.Operator, qml.tape.QuantumScript)) or callable(obj):
--> 213     return self._create_wrapper(obj, *targs, **tkwargs)
215 # Input is not an operator nor a QNode nor a quantum tape nor a qfunc.
216 # Assume Python decorator syntax:
217 #
(...)
229 # Prepend the input to the transform args,
230 # and create a wrapper function.
231 if obj is not None:

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/transforms/op_transforms.py:410, in op_transform._create_wrapper(self, obj, wire_order, *targs, **tkwargs)
407     if wire_order is not None:
408         tkwargs[\"wire_order\"] = wire_order
--> 410     wrapper = self.fn(obj, *targs, **tkwargs)
412 elif isinstance(obj, qml.tape.QuantumScript):
413     # Input is a quantum tape. Get the quantum tape.
414     tape, verified_wire_order = self._make_tape(obj, wire_order)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/transforms/op_transforms.py:273, in op_transform.fn(self, obj, *args, **kwargs)
264     return self.tape_fn(obj.expand(), *args, **kwargs)
266 except (
267     AttributeError,
268     qml.operation.OperatorPropertyUndefined,
(...)
271     # if obj.expand() does not exist, a required operation property was not found,
272     # or the tape transform function does not exist, simply raise the original exception
--> 273     raise e1 from e

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/transforms/op_transforms.py:258, in op_transform.fn(self, obj, *args, **kwargs)
240 \"\"\"Evaluate the underlying operator transform function.
241
242 If a corresponding tape transform for the operator has been registered
(...)
255     any: the result of evaluating the transform
256 \"\"\"
257 try:
--> 258     return self._fn(obj, *args, **kwargs)
260 except Exception as e1:  # pylint: disable=broad-except
261     try:
262         # attempt to decompose the operation and call
263         # the tape transform function if defined

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/ops/functions/matrix.py:125, in matrix(op, wire_order)
122     op = 1.0 * op  # convert to a Hamiltonian
124 if isinstance(op, qml.Hamiltonian):
--> 125     return op.sparse_matrix(wire_order=wire_order).toarray()
127 return op.matrix(wire_order=wire_order)

File ~/miniconda3/envs/qml_torch/lib/python3.10/site-packages/pennylane/ops/qubit/hamiltonian.py:439, in Hamiltonian.sparse_matrix(self, wire_order)
433     mat.append(scipy.sparse.eye(2**i_count, format=\"coo\"))
435 red_mat = (
436     functools.reduce(lambda i, j: scipy.sparse.kron(i, j, format=\"coo\"), mat) * coeff
437 )
--> 439 temp_mats.append(red_mat.tocsr())
440 # Value of 100 arrived at empirically to balance time savings vs memory use. At this point
441 # the temp_mats are summed into the final result and the temporary storage array is
442 # cleared.
443 if (len(temp_mats) % 100) == 0:

AttributeError: 'ArrayBox' object has no attribute 'tocsr'


output of qml.about()

Name: PennyLane
Version: 0.32.0
Summary: PennyLane is a Python quantum machine learning library by Xanadu Inc.
Home-page: https://github.com/PennyLaneAI/pennylane
Author:
Author-email:
Location: /home/amir/miniconda3/envs/qml_torch/lib/python3.10/site-packages
Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, pennylane-lightning, requests, rustworkx, scipy, semantic-version, toml, typing-extensions
Required-by: PennyLane-Lightning

Platform info:           Linux-5.15.90.1-microsoft-standard-WSL2-x86_64-with-glibc2.35
Python version:          3.10.12
Numpy version:           1.23.5
Scipy version:           1.10.0
Installed devices:
- default.gaussian (PennyLane-0.32.0)
- default.mixed (PennyLane-0.32.0)
- default.qubit (PennyLane-0.32.0)
- default.qubit.jax (PennyLane-0.32.0)
- default.qubit.tf (PennyLane-0.32.0)
- default.qubit.torch (PennyLane-0.32.0)
- default.qutrit (PennyLane-0.32.0)
- null.qubit (PennyLane-0.32.0)
- lightning.qubit (PennyLane-Lightning-0.32.0)


Hello @Amir_Akhundzianov , great question!
That error is related to the construction of the Hamiltonian. If instead of writing:

you write:

qml.dot(coeffs, obs)

it should work. I have informed the team so that they can study the case and see if they can make the script compatible with qml.Hamiltonian as well.

Also, we careful with:

for i in range(n_qubits):
qml.RX(inputs, wires=i)


Probably you want to put inputs[I].

I hope that helps

1 Like

Thank you very much! This helped.

1 Like