# 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-accessimportinspectimportwarningsimportnumpyasnpimportpennylaneasqmlfrompennylane.opsimportLinearCombination,Prod,SProd,Sum# pylint: disable=too-many-branchesdef_generator_hamiltonian(gen,op):"""Return the generator as type :class:`~ops.LinearCombination`."""ifisinstance(gen,LinearCombination):returngenifisinstance(gen,(qml.Hermitian,qml.SparseHamiltonian)):ifisinstance(gen,qml.Hermitian):mat=gen.parameters[0]elifisinstance(gen,qml.SparseHamiltonian):mat=gen.parameters[0].toarray()returnqml.pauli_decompose(mat,wire_order=op.wires,hide_identity=True)ifisinstance(gen,(SProd,Prod,Sum)):coeffs,ops=gen.terms()returnqml.Hamiltonian(coeffs,ops)returnqml.Hamiltonian([1.0],[gen])# pylint: disable=no-memberdef_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.0gen=qml.simplify(gen)ifisinstance(gen,Prod)elsegenifisinstance(gen,LinearCombination):gen=qml.dot(gen.coeffs,gen.ops)# convert to Sumifisinstance(gen,Prod):coeffs,ops=gen.terms()returnops[0],coeffs[0]ifisinstance(gen,Sum):ops=[o.baseifisinstance(o,SProd)elseoforoingen]coeffs=[o.scalarifisinstance(o,SProd)else1foroingen]abs_coeffs=list(qml.math.abs(coeffs))ifqml.math.allequal(coeffs[0],coeffs):# case where the Hamiltonian coefficients are all the samereturnqml.sum(*ops),coeffs[0]ifqml.math.allequal(abs_coeffs[0],abs_coeffs):# absolute value of coefficients is the sameprefactor=abs_coeffs[0]coeffs=[c/prefactorforcincoeffs]returnqml.dot(coeffs,ops),prefactorelifisinstance(gen,SProd):returngen.base,gen.scalarreturngen,prefactordef_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.generatorifinspect.isclass(gen[0]):returngen[1]*gen[0](wires=op.wires)ifisinstance(gen[0],np.ndarray)andlen(gen[0].shape)==2:returngen[1]*qml.Hermitian(gen[0],wires=op.wires)raiseqml.operation.GeneratorUndefinedError
[docs]defgenerator(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:`~.SparseHamiltonian`, or :class:`~.ops.LinearCombination`. * ``"hamiltonian"``: Similar to ``"observable"``, however the returned observable will always be converted into :class:`~.ops.LinearCombination` 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 be a LinearCombination -0.5 * X(0) >>> 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) """defprocessing_fn(*args,**kwargs):ifcallable(op):withqml.queuing.QueuingManager.stop_recording():gen_op=op(*args,**kwargs)else:gen_op=opifgen_op.num_params!=1:raiseValueError(f"Operation {gen_op.name} is not written in terms of a single parameter")try:gen=gen_op.generator()exceptTypeError:# For backwards compatibility with PennyLane# versions <=0.22, assume gen_op.generator is a propertygen=_generator_backcompatibility(gen_op)ifnotgen.is_hermitian:raiseqml.QuantumFunctionError(f"Generator {gen.name} of operation {gen_op.name} is not hermitian")ifformat=="prefactor":return_generator_prefactor(gen)ifformat=="hamiltonian":return_generator_hamiltonian(gen,gen_op)ifformat=="arithmetic":h=_generator_hamiltonian(gen,gen_op)returnqml.dot(h.coeffs,h.ops)ifformat=="observable":returngenraiseValueError("format must be one of ('prefactor', 'hamiltonian', 'observable', 'arithmetic')")ifcallable(op):returnprocessing_fnreturnprocessing_fn()