Source code for pennylane.ops.functions.generator
# 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 inspect
import warnings
import numpy as np
import pennylane as qml
from pennylane.operation import convert_to_H
from pennylane.ops import Hamiltonian, LinearCombination, Prod, SProd, Sum
# pylint: disable=too-many-branches
def _generator_hamiltonian(gen, op):
"""Return the generator as type :class:`~.Hamiltonian`."""
wires = op.wires
if isinstance(gen, (Hamiltonian, LinearCombination)):
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()
H = qml.pauli_decompose(mat, wire_order=wires, hide_identity=True)
elif isinstance(gen, qml.operation.Observable):
H = qml.Hamiltonian([1.0], [gen])
elif isinstance(gen, (SProd, Prod, Sum)):
H = convert_to_H(gen)
return H
# pylint: disable=no-member
def _generator_prefactor(gen):
r"""Return the generator as ```(obs, prefactor)`` 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.
"""
prefactor = 1.0
if isinstance(gen, Prod):
gen = qml.simplify(gen)
if isinstance(gen, (Hamiltonian, LinearCombination)):
gen = qml.dot(gen.coeffs, gen.ops) # convert to Sum
if isinstance(gen, Sum):
ops = [o.base if isinstance(o, SProd) else o for o in gen]
coeffs = [o.scalar if isinstance(o, SProd) else 1 for o in gen]
abs_coeffs = list(qml.math.abs(coeffs))
if qml.math.allequal(coeffs[0], coeffs):
# case where the Hamiltonian coefficients are all the same
return qml.sum(*ops), coeffs[0]
if qml.math.allequal(abs_coeffs[0], abs_coeffs):
# absolute value of coefficients is the same
prefactor = abs_coeffs[0]
coeffs = [c / prefactor for c in coeffs]
return qml.dot(coeffs, ops), prefactor
elif isinstance(gen, SProd):
return gen.base, gen.scalar
return gen, prefactor
def _generator_backcompatibility(op):
r"""Preserve backwards compatibility behaviour for PennyLane
versions <=0.22, where generators returned List[type or ndarray, float].
This function raises a deprecation warning, and converts to the new
format where an instantiated Operator is returned."""
warnings.warn(
"The Operator.generator property is deprecated. Please update the operator so that "
"\n\t1. Operator.generator() is a method, and"
"\n\t2. Operator.generator() returns an Operator instance representing the operator.",
qml.PennyLaneDeprecationWarning,
)
gen = op.generator
if inspect.isclass(gen[0]):
return gen[1] * gen[0](wires=op.wires)
if isinstance(gen[0], np.ndarray) and len(gen[0].shape) == 2:
return gen[1] * qml.Hermitian(gen[0], wires=op.wires)
raise qml.operation.GeneratorUndefinedError
[docs]def generator(op: qml.operation.Operator, format="prefactor"):
r"""Returns the generator of an operation.
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:
.Operator or tuple[.Observable, float]: The returned generator, with format/type
dependent on the ``format`` argument.
* ``"prefactor"``: Return the generator as ``(obs, prefactor)`` (representing
:math:`G=p \hat{O}`), where:
- observable :math:`\hat{O}` is one of :class:`~.Hermitian`,
:class:`~.SparseHamiltonian`, or a tensor product
of Pauli words.
- prefactor :math:`p` is a float.
* ``"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` regardless of how ``op``
encodes the generator.
* ``"arithmetic"``: Similar to ``"hamiltonian"``, however the returned observable
will always be converted into an arithmetic operator. The returned generator may be
any type, including:
:class:`~.ops.op_math.SProd`, :class:`~.ops.op_math.Prod`, :class:`~.ops.op_math.Sum`, or the operator itself.
**Example**
Given an operation, ``qml.generator`` returns the generator representation:
>>> op = qml.CRX(0.6, wires=[0, 1])
>>> qml.generator(op)
(Projector([1], wires=[0]) @ X(1), -0.5)
It can also be used in a functional form:
>>> qml.generator(qml.CRX)(0.6, wires=[0, 1])
(Projector([1], wires=[0]) @ X(1), -0.5)
By default, ``generator`` will return the generator in the format of ``(obs, prefactor)``,
corresponding to :math:`G=p \hat{O}`, where the observable :math:`\hat{O}` will
always be given 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 (obs, prefactor)
(X(0), -0.5)
>>> qml.generator(op, format="hamiltonian") # output will always be a Hamiltonian/LinearCombination
-0.5 * X(0)
>>> with qml.operation.disable_new_opmath_cm():
... gen = qml.generator(op, format="hamiltonian")) # legacy Hamiltonian class
... print(gen, type(gen))
(-0.5) [X0] <class 'pennylane.ops.qubit.hamiltonian.Hamiltonian'>
>>> qml.generator(qml.PhaseShift(0.1, wires=0), format="observable") # ouput will be a simplified obs where possible
Projector([1], wires=[0])
>>> qml.generator(op, format="arithmetic") # output is an instance of `SProd`
-0.5 * X(0)
"""
def processing_fn(*args, **kwargs):
if callable(op):
with qml.queuing.QueuingManager.stop_recording():
gen_op = op(*args, **kwargs)
else:
gen_op = op
if gen_op.num_params != 1:
raise ValueError(
f"Operation {gen_op.name} is not written in terms of a single parameter"
)
try:
gen = gen_op.generator()
except TypeError:
# For backwards compatibility with PennyLane
# versions <=0.22, assume gen_op.generator is a property
gen = _generator_backcompatibility(gen_op)
if not gen.is_hermitian:
raise qml.QuantumFunctionError(
f"Generator {gen.name} of operation {gen_op.name} is not hermitian"
)
if format == "prefactor":
return _generator_prefactor(gen)
if format == "hamiltonian":
return _generator_hamiltonian(gen, gen_op)
if format == "arithmetic":
h = _generator_hamiltonian(gen, gen_op)
return qml.dot(h.coeffs, h.ops)
if format == "observable":
return gen
raise ValueError(
"format must be one of ('prefactor', 'hamiltonian', 'observable', 'arithmetic')"
)
if callable(op):
return processing_fn
return processing_fn()
_modules/pennylane/ops/functions/generator
Download Python script
Download Notebook
View on GitHub