pennylane
pennylane copied to clipboard
[BUG] Hermitian operator fails during `expval` with TensorFlow
Expected behavior
Hi, I'm trying to compute the expectation value of a Hermitian operator which works with the NumPy backend:
dev = qml.device("default.qubit", wires=2)
@qml.qnode(dev)
def circuit(K):
return qml.expval(qml.Hermitian(K, wires=range(2)))
state = tf.constant([[1,2,3,4]], dtype=tf.float64)
K = tf.transpose(state)@state / 16.
circuit(K.numpy())
# tensor(0.0625, requires_grad=True)
However, when initiated with TensorFlow backend, the same code raises an AttributeError
(see the details below). I would like to be able to take the gradient of this operator eventually. However, I'm afraid it won't be possible with TensorFlow if I convert it to NumPy to compute the expectation value. As far as I know, it won't compute the gradient if I take the state and compute the expectation value outside the circuit since there will be complex numbers. Would it work correctly if I only used the real portion of the expectation value after computing it out of the circuit? Any workaround would be appreciated if there is no quick fix.
Thanks!
Actual behavior
Code crashes and raises an AttributeError.
Additional information
No response
Source code
import pennylane as qml
import tensorflow as tf
dev = qml.device("default.qubit.tf", wires=2)
@qml.qnode(dev, interface="tf", diff_method="backprop")
def circuit(K):
return qml.expval(qml.Hermitian(K, wires=range(2)))
state = tf.constant([[1,2,3,4]], dtype=tf.float64)
K = tf.transpose(state)@state / 16.
circuit(K)
### Second option
dev2 = qml.device("default.qubit.tf", wires=2)
@qml.qnode(dev2, interface="tf", diff_method="backprop")
def circuit2(op):
return qml.expval(op)
circuit2(qml.Hermitian(K, wires=range(2)))
Tracebacks
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Input In [46], in <cell line: 3>()
1 state = tf.constant([[1,2,3,4]], dtype=tf.float64)
2 K = tf.transpose(state)@state / 16.
----> 3 circuit(K)
File ~/packages/miniforge3/lib/python3.9/site-packages/pennylane/qnode.py:619, in QNode.__call__(self, *args, **kwargs)
612 using_custom_cache = (
613 hasattr(cache, "__getitem__")
614 and hasattr(cache, "__setitem__")
615 and hasattr(cache, "__delitem__")
616 )
617 self._tape_cached = using_custom_cache and self.tape.hash in cache
--> 619 res = qml.execute(
620 [self.tape],
621 device=self.device,
622 gradient_fn=self.gradient_fn,
623 interface=self.interface,
624 gradient_kwargs=self.gradient_kwargs,
625 override_shots=override_shots,
626 **self.execute_kwargs,
627 )
629 if autograd.isinstance(res, (tuple, list)) and len(res) == 1:
630 # If a device batch transform was applied, we need to 'unpack'
631 # the returned tuple/list to a float.
(...)
638 # TODO: find a more explicit way of determining that a batch transform
639 # was applied.
641 res = res[0]
File ~/packages/miniforge3/lib/python3.9/site-packages/pennylane/interfaces/execution.py:344, in execute(tapes, device, gradient_fn, interface, mode, gradient_kwargs, cache, cachesize, max_diff, override_shots, expand_fn, max_expansion, device_batch_transform)
340 return batch_fn(res)
342 if gradient_fn == "backprop" or interface is None:
343 return batch_fn(
--> 344 qml.interfaces.cache_execute(
345 batch_execute, cache, return_tuple=False, expand_fn=expand_fn
346 )(tapes)
347 )
349 # the default execution function is batch_execute
350 execute_fn = qml.interfaces.cache_execute(batch_execute, cache, expand_fn=expand_fn)
File ~/packages/miniforge3/lib/python3.9/site-packages/pennylane/interfaces/execution.py:172, in cache_execute.<locals>.wrapper(tapes, **kwargs)
168 return (res, []) if return_tuple else res
170 else:
171 # execute all unique tapes that do not exist in the cache
--> 172 res = fn(execution_tapes.values(), **kwargs)
174 final_res = []
176 for i, tape in enumerate(tapes):
File ~/packages/miniforge3/lib/python3.9/site-packages/pennylane/interfaces/execution.py:97, in cache_execute.<locals>.fn(tapes, **kwargs)
95 def fn(tapes, **kwargs): # pylint: disable=function-redefined
96 tapes = [expand_fn(tape) for tape in tapes]
---> 97 return original_fn(tapes, **kwargs)
File ~/packages/miniforge3/lib/python3.9/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 ~/packages/miniforge3/lib/python3.9/site-packages/pennylane/_qubit_device.py:355, in QubitDevice.batch_execute(self, circuits)
350 for circuit in circuits:
351 # we need to reset the device here, else it will
352 # not start the next computation in the zero state
353 self.reset()
--> 355 res = self.execute(circuit)
356 results.append(res)
358 if self.tracker.active:
File ~/packages/miniforge3/lib/python3.9/site-packages/pennylane/_qubit_device.py:257, in QubitDevice.execute(self, circuit, **kwargs)
254 self.check_validity(circuit.operations, circuit.observables)
256 # apply all circuit operations
--> 257 self.apply(circuit.operations, rotations=circuit.diagonalizing_gates, **kwargs)
259 # generate computational basis samples
260 if self.shots is not None or circuit.is_sampled:
File ~/packages/miniforge3/lib/python3.9/site-packages/pennylane/tape/tape.py:1319, in QuantumTape.diagonalizing_gates(self)
1315 for observable in self.observables:
1316 # some observables do not have diagonalizing gates,
1317 # in which case we just don't append any
1318 try:
-> 1319 rotation_gates.extend(observable.diagonalizing_gates())
1320 except qml.operation.DiagGatesUndefinedError:
1321 pass
File ~/packages/miniforge3/lib/python3.9/site-packages/pennylane/ops/qubit/observables.py:171, in Hermitian.diagonalizing_gates(self)
164 """Return the gate set that diagonalizes a circuit according to the
165 specified Hermitian observable.
166
167 Returns:
168 list: list containing the gates diagonalizing the Hermitian observable
169 """
170 # note: compute_diagonalizing_gates has a custom signature, which is why we overwrite this method
--> 171 return self.compute_diagonalizing_gates(self.eigendecomposition["eigvec"], self.wires)
File ~/packages/miniforge3/lib/python3.9/site-packages/pennylane/ops/qubit/observables.py:113, in Hermitian.eigendecomposition(self)
101 @property
102 def eigendecomposition(self):
103 """Return the eigendecomposition of the matrix specified by the Hermitian observable.
104
105 This method uses pre-stored eigenvalues for standard observables where
(...)
111 dict[str, array]: dictionary containing the eigenvalues and the eigenvectors of the Hermitian observable
112 """
--> 113 Hmat = self.matrix()
114 Hmat = qml.math.to_numpy(Hmat)
115 Hkey = tuple(Hmat.flatten().tolist())
File ~/packages/miniforge3/lib/python3.9/site-packages/pennylane/operation.py:585, in Operator.matrix(self, wire_order)
565 def matrix(self, wire_order=None):
566 r"""Representation of the operator as a matrix in the computational basis.
567
568 If ``wire_order`` is provided, the numerical representation considers the position of the
(...)
583 tensor_like: matrix representation
584 """
--> 585 canonical_matrix = self.compute_matrix(*self.parameters, **self.hyperparameters)
587 if wire_order is None or self.wires == Wires(wire_order):
588 return canonical_matrix
File ~/packages/miniforge3/lib/python3.9/site-packages/pennylane/ops/qubit/observables.py:96, in Hermitian.compute_matrix(A)
93 if A.shape[0] != A.shape[1]:
94 raise ValueError("Observable must be a square matrix.")
---> 96 if not qml.math.allclose(A, A.conj().T):
97 raise ValueError("Observable must be Hermitian.")
99 return A
File ~/packages/miniforge3/lib/python3.9/site-packages/tensorflow/python/framework/ops.py:513, in Tensor.__getattr__(self, name)
505 if name in {"T", "astype", "ravel", "transpose", "reshape", "clip", "size",
506 "tolist", "data"}:
507 # TODO(wangpeng): Export the enable_numpy_behavior knob
508 raise AttributeError("""
509 '{}' object has no attribute '{}'.
510 If you are looking for numpy-related methods, please run the following:
511 from tensorflow.python.ops.numpy_ops import np_config
512 np_config.enable_numpy_behavior()""".format(type(self).__name__, name))
--> 513 self.__getattribute__(name)
AttributeError: 'tensorflow.python.framework.ops.EagerTensor' object has no attribute 'conj'
System information
Name: PennyLane
Version: 0.24.0
Summary: PennyLane is a Python quantum machine learning library by Xanadu Inc.
Home-page: https://github.com/XanaduAI/pennylane
Author:
Author-email:
License: Apache License 2.0
Location: /Users/jackaraz/packages/miniforge3/lib/python3.9/site-packages
Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, pennylane-lightning, retworkx, scipy, semantic-version, toml
Required-by: PennyLane-Lightning, qhbm
Platform info: macOS-12.5-arm64-arm-64bit
Python version: 3.9.10
Numpy version: 1.21.6
Scipy version: 1.8.0
Installed devices:
- lightning.qubit (PennyLane-Lightning-0.24.0)
- default.gaussian (PennyLane-0.24.0)
- default.mixed (PennyLane-0.24.0)
- default.qubit (PennyLane-0.24.0)
- default.qubit.autograd (PennyLane-0.24.0)
- default.qubit.jax (PennyLane-0.24.0)
- default.qubit.tf (PennyLane-0.24.0)
- default.qubit.torch (PennyLane-0.24.0)
### Existing GitHub issues
- [X] I have searched existing GitHub issues to make sure the issue does not already exist.
Hi @jackaraz, thanks for reporting this! We'll be looking into this. :)
I believe this is solved. Closing issue.