-
Notifications
You must be signed in to change notification settings - Fork 603
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
Add qml.generator(op)
function
#2256
Merged
Changes from 27 commits
Commits
Show all changes
60 commits
Select commit
Hold shift + click to select a range
cb1fe52
Add operator transform functionality
josh146 931515f
Add matrix function:
josh146 17bf5ba
add tests
josh146 b5586b2
add tests
josh146 ef42ab2
add tests
josh146 2beaa40
add tests
josh146 1c49491
add tests
josh146 397c343
Merge branch 'op-transform' into matrix-function
josh146 3580cf9
add tests
josh146 e7c803c
add tests
josh146 955ca89
Merge branch 'master' into op-transform
josh146 0ae781c
more docs
josh146 94b2f5a
Merge branch 'op-transform' into matrix-function
josh146 da6278f
more
josh146 1018410
add tests
josh146 190a7d4
Apply suggestions from code review
josh146 6ce4f9b
Add qml.eigval() function
josh146 cf045e0
add test
josh146 80124c4
Merge branch 'matrix-function' of github.com:PennyLaneAI/pennylane in…
josh146 4ea7fb1
Merge branch 'matrix-function' into eigvals-function
josh146 a66e6e1
finish adding tests
josh146 f3d72c0
finish adding tests
josh146 874c3ca
Add qml.generator(op) function
josh146 502a105
fixes
josh146 8c2a303
more tests
josh146 5a88c52
more tests
josh146 b0161f9
fix
josh146 c19fffe
more tests
josh146 8d56cdf
Apply suggestions from code review
josh146 f85f106
Apply suggestions from code review
josh146 249ff25
Update tests/ops/functions/test_eigvals.py
josh146 780e943
Apply suggestions from code review
josh146 7b63422
update deprecation warning
josh146 6215b74
Update tests/ops/functions/test_eigvals.py
josh146 696504b
update deprecation warning
josh146 c6ca52f
Merge branch 'matrix-function' into eigvals-function
josh146 f3e219e
add another test
josh146 2f5762d
suggested changes
josh146 131e752
black
josh146 c7653ee
black
josh146 379aa56
Merge branch 'eigvals-function' into gen-fn
josh146 fbd3d94
Merge branch 'master' into matrix-function
josh146 1b1801a
Merge branch 'matrix-function' into eigvals-function
josh146 2282b76
Apply suggestions from code review
josh146 7109d2c
Merge branch 'eigvals-function' into gen-fn
josh146 e40a691
fix
josh146 3b79b7a
changelog
josh146 9688f82
more
josh146 3a54758
Merge branch 'master' into eigvals-function
josh146 0376469
Merge branch 'eigvals-function' into gen-fn
josh146 20c102d
bugfix
josh146 9749648
suggested changes
josh146 0e24463
change parameter-frequency logic
josh146 109a288
Apply suggestions from code review
josh146 a398f03
Merge branch 'eigvals-function' into gen-fn
josh146 5d7073e
Merge branch 'master' into eigvals-function
josh146 1a346b0
fix
josh146 ae410b7
Merge branch 'eigvals-function' into gen-fn
josh146 06a6e2e
Merge branch 'master' into gen-fn
josh146 dcf79f4
Merge branch 'master' into gen-fn
josh146 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Copyright 2018-2021 Xanadu Quantum Technologies Inc. | ||
|
||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
|
||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
""" | ||
This module contains functions that act on operators and tapes. | ||
""" | ||
from .eigvals import eigvals | ||
from .generator import generator | ||
from .matrix import matrix |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
# Copyright 2018-2021 Xanadu Quantum Technologies Inc. | ||
|
||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
|
||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
""" | ||
This module contains the qml.eigvals function. | ||
""" | ||
# pylint: disable=protected-access | ||
from functools import reduce | ||
import warnings | ||
|
||
import pennylane as qml | ||
|
||
|
||
@qml.op_transform | ||
def eigvals(op): | ||
r"""The matrix representation of an operation or quantum circuit. | ||
|
||
Args: | ||
op (.Operator, pennylane.QNode, .QuantumTape, or Callable): An operator, quantum node, tape, | ||
or function that applies quantum operations. | ||
|
||
Returns: | ||
tensor_like or function: If an operator is provided as input, the eigenvalues are returned directly. | ||
If a QNode or quantum function is provided as input, a function which accepts the | ||
same arguments as the QNode or quantum function is returned. When called, this function will | ||
return the unitary matrix in the appropriate autodiff framework (Autograd, TensorFlow, PyTorch, JAX) | ||
given its parameters. | ||
|
||
**Example** | ||
|
||
Given an operation, ``qml.matrix`` returns the matrix representation: | ||
|
||
>>> op = qml.PauliZ(0) @ qml.PauliX(1) - 0.5 * qml.PauliY(1) | ||
>>> qml.eigvals(op) | ||
array([-1.11803399, -1.11803399, 1.11803399, 1.11803399]) | ||
|
||
It can also be used in a functional form: | ||
|
||
>>> x = torch.tensor(0.6, requires_grad=True) | ||
>>> eigval_fn = qml.eigvals(qml.RX) | ||
>>> eigval_fn(x, wires=0) | ||
tensor([0.9553+0.2955j, 0.9553-0.2955j], grad_fn=<LinalgEigBackward>) | ||
|
||
In its functional form, it is fully differentiable with respect to gate arguments: | ||
|
||
>>> loss = torch.real(torch.sum(eigval_fn(x, wires=0))) | ||
>>> loss.backward() | ||
>>> x.grad | ||
tensor(-0.2955) | ||
|
||
This operator transform can also be applied to QNodes, tapes, and quantum functions | ||
that contain multiple operations; see Usage Details below for more details. | ||
|
||
.. UsageDetails:: | ||
|
||
``qml.eigvals`` can also be used with QNodes, tapes, or quantum functions that | ||
contain multiple operations. However, in this situation, **eigenvalues may | ||
be computed numerically**. This can lead to a large computational overhead | ||
for a large number of wires. | ||
|
||
Consider the following quantum function: | ||
|
||
.. code-block:: python3 | ||
|
||
def circuit(theta): | ||
qml.RX(theta, wires=1) | ||
qml.PauliZ(wires=0) | ||
|
||
We can use ``qml.eigvals`` to generate a new function that returns the eigenvalues | ||
corresponding to the function ``circuit``: | ||
|
||
>>> eigvals_fn = qml.eigvals(circuit) | ||
>>> theta = np.pi / 4 | ||
>>> eigvals_fn(theta) | ||
array([ 0.92387953+0.38268343j, 0.92387953-0.38268343j, | ||
-0.92387953+0.38268343j, -0.92387953-0.38268343j]) | ||
""" | ||
if isinstance(op, qml.Hamiltonian): | ||
warnings.warn( | ||
"For Hamiltonians, the eigenvalues will be computed numerically. " | ||
"This may be computationally intensive for a large number of wires.", | ||
UserWarning, | ||
) | ||
return qml.math.linalg.eigvalsh(qml.matrix(op)) | ||
|
||
# TODO: take into account for wire ordering | ||
return op.get_eigvals() | ||
|
||
|
||
@eigvals.tape_transform | ||
def eigvals(tape): | ||
op_wires = [op.wires for op in tape.operations] | ||
all_wires = qml.wires.Wires.all_wires(op_wires).tolist() | ||
unique_wires = qml.wires.Wires.unique_wires(op_wires).tolist() | ||
|
||
if len(all_wires) != len(unique_wires): | ||
warnings.warn( | ||
"For multiple operations, the eigenvalues will be computed numerically. " | ||
"This may be computationally intensive for a large number of wires.", | ||
UserWarning, | ||
) | ||
return qml.math.linalg.eigvals(qml.matrix(tape)) | ||
|
||
# TODO: take into account for wire ordering, by reordering eigenvalues | ||
# as per operator wires/wire ordering, and by inserting implicit identity | ||
# matrices (eigenvalues [1, 1]) at missing locations. | ||
|
||
ev = [eigvals(op) for op in tape.operations] | ||
|
||
if len(ev) == 1: | ||
return ev[0] | ||
|
||
return reduce(qml.math.kron, ev) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
# Copyright 2018-2021 Xanadu Quantum Technologies Inc. | ||
|
||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
|
||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
""" | ||
This module contains the qml.generator function. | ||
""" | ||
# pylint: disable=protected-access | ||
import pennylane as qml | ||
|
||
|
||
def _generator_observable(gen, op): | ||
"""Return the generator as type :class:`~.Hermitian`, | ||
:class:`~.SparseHamiltonian`, or :class:`~.Hamiltonian`, | ||
as provided by the original gate. | ||
""" | ||
if isinstance(gen, (qml.Hermitian, qml.SparseHamiltonian)): | ||
if not op.inverse: | ||
return gen | ||
|
||
param = gen.parameters[0] | ||
wires = gen.wires | ||
|
||
return gen.__class__(-param, wires=wires) | ||
|
||
if op.inverse: | ||
gen = -1.0 * gen | ||
|
||
return gen | ||
|
||
|
||
def _generator_hamiltonian(gen, op): | ||
"""Return the generator as type :class:`~.Hamiltonian`.""" | ||
wires = op.wires | ||
|
||
if isinstance(gen, qml.Hamiltonian): | ||
H = gen | ||
|
||
elif isinstance(gen, (qml.Hermitian, qml.SparseHamiltonian)): | ||
|
||
if isinstance(gen, qml.Hermitian): | ||
mat = gen.parameters[0] | ||
|
||
elif isinstance(gen, qml.SparseHamiltonian): | ||
mat = gen.parameters[0].toarray() | ||
|
||
coeffs, obs = qml.utils.decompose_hamiltonian(mat, wire_order=wires, hide_identity=True) | ||
H = qml.Hamiltonian(coeffs, obs) | ||
|
||
elif isinstance(gen, qml.operation.Observable): | ||
H = 1.0 * gen | ||
|
||
if op.inverse: | ||
H = -1.0 * H | ||
|
||
return H | ||
|
||
|
||
def _generator_prefactor(gen, op): | ||
r"""Return the generator as ```(prefactor, obs)`` representing | ||
:math:`G=p \hat{O}`, where | ||
|
||
- prefactor :math:`p` is a float | ||
- observable `\hat{O}` is one of :class:`~.Hermitian`, | ||
:class:`~.SparseHamiltonian`, or a tensor product | ||
of Pauli words. | ||
""" | ||
if isinstance(gen, (qml.Hermitian, qml.SparseHamiltonian)): | ||
obs = gen | ||
prefactor = 1.0 | ||
|
||
elif isinstance(gen, qml.operation.Observable): | ||
# convert to a qml.Hamiltonian | ||
gen = 1.0 * gen | ||
|
||
if len(gen.ops) == 1: | ||
# case where the Hamiltonian is a single Pauli word | ||
obs = gen.ops[0] | ||
prefactor = gen.coeffs[0] | ||
else: | ||
obs = gen | ||
prefactor = 1.0 | ||
|
||
if op.inverse: | ||
prefactor *= -1.0 | ||
|
||
return prefactor, obs | ||
|
||
|
||
@qml.op_transform | ||
def generator(op, format="prefactor"): | ||
r"""Returns the generator of operation. | ||
josh146 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Args: | ||
op (.Operator or Callable): A single operator, or a function that | ||
applies a single quantum operation. | ||
format (str): The format to return the generator in. Must be one of ``'prefactor'``, | ||
``'observable'``, or ``'hamiltonian'``. See below for more details. | ||
|
||
Returns: | ||
.Observable or tuple[float, .Observable]: The returned generator, with format/type | ||
dependent on the ``format`` argument. | ||
|
||
* ``"prefactor"``: Return the generator as ```(prefactor, obs)`` (representing | ||
josh146 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
:math:`G=p \hat{O}`), where | ||
|
||
- prefactor :math:`p` is a float | ||
- observable `\hat{O}` is one of :class:`~.Hermitian`, | ||
:class:`~.SparseHamiltonian`, or a tensor product | ||
of Pauli words. | ||
josh146 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
* ``"observable"``: Return the generator as a single observable as directly defined | ||
by ``op``. Returned generators may be any type of observable, including | ||
:class:`~.Hermitian`, :class:`~.Tensor`, | ||
:class:`~.SparseHamiltonian`, or :class:`~.Hamiltonian`. | ||
|
||
* ``"hamiltonian"``: Similar to ``"observable"``, however the returned observable | ||
will always be converted into :class:`~.Hamiltonian` irregardless of how ``op`` | ||
josh146 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
encodes the generator. | ||
|
||
**Example** | ||
|
||
Given an operation, ``qml.generator`` returns the generator representation: | ||
|
||
>>> op = qml.CRX(0.6, wires=[0, 1]) | ||
>>> qml.generator(op) | ||
(-0.5, Projector([1], wires=[0]) @ PauliX(wires=[1])) | ||
|
||
It can also be used in a functional form: | ||
|
||
>>> qml.generator(qml.CRX)(0.6, wires=[0, 1]) | ||
(-0.5, Projector([1], wires=[0]) @ PauliX(wires=[1])) | ||
|
||
By default, ``generator`` will return the generator in the format of ``(prefactor, obs)``, | ||
corresponding to :math:`G=p \hat{O}`, where the observable :math:`\hat{O}` will | ||
always be given in as in tensor product form, or as a dense/sparse matrix. | ||
|
||
By using the ``format`` argument, the returned generator representation can | ||
be altered: | ||
|
||
>>> op = qml.RX(0.2, wires=0) | ||
>>> qml.generator(op, format="prefactor") # output will always be (prefactor, obs) | ||
(1.0, Projector([1], wires=[0])) | ||
>>> qml.generator(op, format="hamiltonian") # output will always be a Hamiltonian | ||
<Hamiltonian: terms=1, wires=[0]> | ||
>>> qml.generator(op, format="observable") # ouput will be a simplified obs where possible | ||
Projector([1], wires=[0]) | ||
""" | ||
gen = op.generator() | ||
|
||
if op.num_params != 1: | ||
raise ValueError(f"Operation {op.name} is not written in terms of a single parameter") | ||
josh146 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if not isinstance(gen, qml.operation.Observable): | ||
raise qml.QuantumFunctionError(f"Generator {gen} is not an observable") | ||
josh146 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if format == "prefactor": | ||
return _generator_prefactor(gen, op) | ||
|
||
if format == "hamiltonian": | ||
return _generator_hamiltonian(gen, op) | ||
|
||
if format == "observable": | ||
return _generator_observable(gen, op) | ||
|
||
raise ValueError("'format' must be one of ('prefactor', 'hamiltonian', 'observable'") | ||
josh146 marked this conversation as resolved.
Show resolved
Hide resolved
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could have been done in #2182 already, but now that we're touching this code, we could simply use
parameter_frequencies
instead of this whole utility function? Might be a separate PR, though (maybe together with Rotosolve usingparameter_frequencies
, grouping it into an "application" PR).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good idea!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dwierichs which would you prefer, doing it here, or doing it in a separate PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess a separate one that uses
parameter_frequencies
where applicable is more clean? :)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep :)