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] imaginary part of expectation values of observables with complex coefficients is silently discarded #6076

Open
1 task done
cvjjm opened this issue Aug 6, 2024 · 5 comments
Labels
bug 🐛 Something isn't working

Comments

@cvjjm
Copy link
Contributor

cvjjm commented Aug 6, 2024

Expected behavior

The PL operator arithmetic allows for operators with imaginary/complex coefficients. If such expectation values of such operators are measured I expect to either get a imaginary/complex value (the real expectation value of the Hermitian operator times the complex coefficient) or an exception that only Hermitian observables can be measured.

Actual behavior

Instead PennyLane silently discards the imaginary part, as the attached example code shows.

Additional information

No response

Source code

import pennylane as qml

obs = (0.2 + 0.7j) * qml.PauliZ(0)

@qml.qnode(qml.device("default.qubit", 1))
def qnode():
    return qml.expval(obs)

qnode()

Tracebacks

No response

System information

Name: PennyLane
Version: 0.35.1
Summary: PennyLane is a cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. Train a quantum computer the same way as a neural network.
Home-page: https://github.com/PennyLaneAI/pennylane
Author:
Author-email:
License: Apache License 2.0
Location: /home/cvjjm/miniforge3/envs/[...]/lib/python3.11/site-packages
Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, pennylane-lightning, requests, rustworkx, scipy, semantic-version, toml, typing-extensions
Required-by: matchgate_shadows, PennyLane_Lightning

Platform info:           Linux-5.15.153.1-microsoft-standard-WSL2-x86_64-with-glibc2.31
Python version:          3.11.9
Numpy version:           1.23.5
Scipy version:           1.13.0
Installed devices:
- lightning.qubit (PennyLane_Lightning-0.35.1)
- default.clifford (PennyLane-0.35.1)
- default.gaussian (PennyLane-0.35.1)
- default.mixed (PennyLane-0.35.1)
- default.qubit (PennyLane-0.35.1)
- default.qubit.autograd (PennyLane-0.35.1)
- default.qubit.jax (PennyLane-0.35.1)
- default.qubit.legacy (PennyLane-0.35.1)
- default.qubit.tf (PennyLane-0.35.1)
- default.qubit.torch (PennyLane-0.35.1)
- default.qutrit (PennyLane-0.35.1)
- null.qubit (PennyLane-0.35.1)

Existing GitHub issues

  • I have searched existing GitHub issues to make sure the issue does not already exist.
@cvjjm cvjjm added the bug 🐛 Something isn't working label Aug 6, 2024
@cvjjm cvjjm changed the title [BUG] [BUG] imaginary part of expectation values of observables with complex coefficients is silently discarded Aug 6, 2024
@cvjjm
Copy link
Contributor Author

cvjjm commented Aug 6, 2024

While trying to debug the problem that made me notice the behavior above, I also noticed the following unexpected behavior: Some of the asserts in the following code do not work as I/one would expect:

hermitian_obs = qml.PauliZ(0)
assert hermitian_obs.is_hermitian
assert qml.adjoint(hermitian_obs) == hermitian_obs # fails
assert hermitian_obs.adjoint() == hermitian_obs


hermitian_obs2 = 0.2 * qml.PauliZ(0)
assert hermitian_obs2.is_hermitian
assert qml.adjoint(hermitian_obs2) == hermitian_obs2 # fails
assert hermitian_obs2.adjoint() == hermitian_obs2 # fails

non_hermitian_obs = 0.2j * qml.PauliZ(0)
assert qml.adjoint(non_hermitian_obs) != non_hermitian_obs
assert non_hermitian_obs.adjoint() != non_hermitian_obs
assert not non_hermitian_obs.is_hermitian

@albi3ro
Copy link
Contributor

albi3ro commented Aug 6, 2024

The qml.adjoint function is lazy by default, simply wrapping the target in an Adjoint class for later handling. You can either specify lazy=False when constructing the adjoint, or simplify the operators before comparison:

assert qml.adjoint(hermitian_obs, lazy=False) == hermitian_obs
assert qml.simplify(qml.adjoint(hermitian_obs)) == hermitian_obs

As for the error from:

>>> op = 0.2 * qml.PauliZ(0)
>>> qml.equal(qml.adjoint(op, lazy=False), op)
AttributeError: 'PauliZ' object has no attribute 'base'

That does seem to indeed be a bug.

Handling non-hermitian observables has been an ongoing debate. We used to raise a warning when constructing measurments from non-hermitian observables, but found that introduced a substantial performance penalty for a more obscure edge case. The challenge is finding a middle ground between returning always real results for hermitian observables, but also sometimes permitting non-hermitian obs.

@trbromley
Copy link
Contributor

Thanks @cvjjm for reporting this. So far we've not focused too much on taking expectation values of non-Hermitian operators, so it doesn't surprise me too much that we are casting the result to a real value. However, it would be nice to have some limited support for non-Hermitian operators, or to at least not preclude it by casting to reals if otherwise the result would be correct.

We can definitely look into this, how blocking is it for you?

@cvjjm
Copy link
Contributor Author

cvjjm commented Aug 7, 2024

Not high priority. We can work around it. It was just unexpected behavior that cause a hard to debug problem and I though I could save other peoples time by reporting...

@CatalinaAlbornoz
Copy link
Contributor

Thanks for reporting this @cvjjm ! It can surely help others in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants