Source code for pennylane.ops.qubit.parametric_ops_multi_qubit
# Copyright 2018-2023 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.# pylint: disable=too-many-arguments"""This submodule contains the discrete-variable quantum operations that are thecore parametrized gates."""# pylint:disable=abstract-method,arguments-differ,protected-access,invalid-overridden-methodimportfunctoolsfromoperatorimportmatmulfromtypingimportOptional,Unionimportnumpyasnpimportpennylaneasqmlfrompennylane.decompositionimportadd_decomps,register_resourcesfrompennylane.mathimportexpand_matrixfrompennylane.operationimportAnyWires,FlatPytree,Operationfrompennylane.typingimportTensorLikefrompennylane.wiresimportWires,WiresLikefrom.non_parametric_opsimportHadamard,PauliX,PauliY,PauliZfrom.parametric_ops_single_qubitimportRX,RY,RZ,PhaseShift,_can_replace,stack_last
[docs]classMultiRZ(Operation):r""" Arbitrary multi Z rotation. .. math:: MultiRZ(\theta) = \exp\left(-i \frac{\theta}{2} Z^{\otimes n}\right) **Details:** * Number of wires: Any * Number of parameters: 1 * Number of dimensions per parameter: (0,) * Gradient recipe: :math:`\frac{d}{d\theta}f(MultiRZ(\theta)) = \frac{1}{2}\left[f(MultiRZ(\theta +\pi/2)) - f(MultiRZ(\theta-\pi/2))\right]` where :math:`f` is an expectation value depending on :math:`MultiRZ(\theta)`. .. note:: If the ``MultiRZ`` gate is not supported on the targeted device, PennyLane will decompose the gate using :class:`~.RZ` and :class:`~.CNOT` gates. Args: theta (TensorLike): rotation angle :math:`\theta` wires (Sequence[int] or int): the wires the operation acts on id (str or None): String representing the operation (optional) """num_wires=AnyWiresnum_params=1"""int: Number of trainable parameters that the operator depends on."""ndim_params=(0,)"""tuple[int]: Number of dimensions per trainable parameter that the operator depends on."""resource_keys={"num_wires"}grad_method="A"parameter_frequencies=[(1,)]def_flatten(self)->FlatPytree:returnself.data,(self.wires,tuple())def__init__(self,theta:TensorLike,wires:WiresLike,id:Optional[str]=None):wires=Wires(wires)self.hyperparameters["num_wires"]=len(wires)super().__init__(theta,wires=wires,id=id)
[docs]@staticmethoddefcompute_matrix(theta:TensorLike,num_wires:int)->TensorLike:# pylint: disable=arguments-differr"""Representation of the operator as a canonical matrix in the computational basis (static method). The canonical matrix is the textbook matrix representation that does not consider wires. Implicitly, this assumes that the wires of the operator correspond to the global wire order. .. seealso:: :meth:`~.MultiRZ.matrix` Args: theta (TensorLike): rotation angle num_wires (int): number of wires the rotation acts on Returns: TensorLike: canonical matrix **Example** >>> qml.MultiRZ.compute_matrix(torch.tensor(0.1), 2) tensor([[0.9988-0.0500j, 0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j], [0.0000+0.0000j, 0.9988+0.0500j, 0.0000+0.0000j, 0.0000+0.0000j], [0.0000+0.0000j, 0.0000+0.0000j, 0.9988+0.0500j, 0.0000+0.0000j], [0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.9988-0.0500j]]) """eigs=qml.math.convert_like(qml.pauli.pauli_eigs(num_wires),theta)ifqml.math.get_interface(theta)=="tensorflow":theta=qml.math.cast_like(theta,1j)eigs=qml.math.cast_like(eigs,1j)ifqml.math.ndim(theta)==0:returnqml.math.diag(qml.math.exp(-0.5j*theta*eigs))diags=qml.math.exp(qml.math.outer(-0.5j*theta,eigs))returndiags[:,:,np.newaxis]*qml.math.cast_like(qml.math.eye(2**num_wires,like=diags),diags)
[docs]@staticmethoddefcompute_eigvals(theta:TensorLike,num_wires:int)->TensorLike:# pylint: disable=arguments-differr"""Eigenvalues of the operator in the computational basis (static method). If :attr:`diagonalizing_gates` are specified and implement a unitary :math:`U^{\dagger}`, the operator can be reconstructed as .. math:: O = U \Sigma U^{\dagger}, where :math:`\Sigma` is the diagonal matrix containing the eigenvalues. Otherwise, no particular order for the eigenvalues is guaranteed. .. seealso:: :meth:`~.MultiRZ.eigvals` Args: theta (TensorLike): rotation angle num_wires (int): number of wires the rotation acts on Returns: TensorLike: eigenvalues **Example** >>> qml.MultiRZ.compute_eigvals(torch.tensor(0.5), 3) tensor([0.9689-0.2474j, 0.9689+0.2474j, 0.9689+0.2474j, 0.9689-0.2474j, 0.9689+0.2474j, 0.9689-0.2474j, 0.9689-0.2474j, 0.9689+0.2474j]) """eigs=qml.math.convert_like(qml.pauli.pauli_eigs(num_wires),theta)ifqml.math.get_interface(theta)=="tensorflow":theta=qml.math.cast_like(theta,1j)eigs=qml.math.cast_like(eigs,1j)ifqml.math.ndim(theta)==0:returnqml.math.exp(-0.5j*theta*eigs)returnqml.math.exp(qml.math.outer(-0.5j*theta,eigs))
[docs]@staticmethoddefcompute_decomposition(# pylint: disable=arguments-differ,unused-argumenttheta:TensorLike,wires:WiresLike,**kwargs)->list["qml.operation.Operator"]:r"""Representation of the operator as a product of other operators (static method). : .. math:: O = O_1 O_2 \dots O_n. .. seealso:: :meth:`~.MultiRZ.decomposition`. Args: theta (TensorLike): rotation angle :math:`\theta` wires (Iterable, Wires): the wires the operation acts on Returns: list[Operator]: decomposition into lower level operations **Example:** >>> qml.MultiRZ.compute_decomposition(1.2, wires=(0,1)) [CNOT(wires=[1, 0]), RZ(1.2, wires=[0]), CNOT(wires=[1, 0])] """ops=[qml.CNOT(wires=(w0,w1))forw0,w1inzip(wires[~0:0:-1],wires[~1::-1])]ops.append(RZ(theta,wires=wires[0]))ops+=[qml.CNOT(wires=(w0,w1))forw0,w1inzip(wires[1:],wires[:~0])]returnops
[docs]classPauliRot(Operation):r""" Arbitrary Pauli word rotation. .. math:: RP(\theta, P) = \exp\left(-i \frac{\theta}{2} P\right) **Details:** * Number of wires: Any * Number of parameters: 1 * Number of dimensions per parameter: (0,) * Gradient recipe: :math:`\frac{d}{d\theta}f(RP(\theta)) = \frac{1}{2}\left[f(RP(\theta +\pi/2)) - f(RP(\theta-\pi/2))\right]` where :math:`f` is an expectation value depending on :math:`RP(\theta)`. .. note:: If the ``PauliRot`` gate is not supported on the targeted device, PennyLane will decompose the gate using :class:`~.RX`, :class:`~.Hadamard`, :class:`~.RZ` and :class:`~.CNOT` gates. Args: theta (float): rotation angle :math:`\theta` pauli_word (string): the Pauli word defining the rotation wires (Sequence[int] or int): the wire the operation acts on id (str or None): String representing the operation (optional) **Example** >>> dev = qml.device('default.qubit', wires=1) >>> @qml.qnode(dev) ... def example_circuit(): ... qml.PauliRot(0.5, 'X', wires=0) ... return qml.expval(qml.Z(0)) >>> print(example_circuit()) 0.8775825618903724 """num_wires=AnyWiresnum_params=1"""int: Number of trainable parameters that the operator depends on."""ndim_params=(0,)"""tuple[int]: Number of dimensions per trainable parameter that the operator depends on."""do_check_domain=Falsegrad_method="A"parameter_frequencies=[(1,)]resource_keys={"pauli_word",}_ALLOWED_CHARACTERS="IXYZ"_PAULI_CONJUGATION_MATRICES={"X":Hadamard.compute_matrix(),"Y":RX.compute_matrix(np.pi/2),"Z":np.array([[1,0],[0,1]]),}@classmethoddef_primitive_bind_call(cls,theta,pauli_word,wires=None,id=None):returnsuper()._primitive_bind_call(theta,pauli_word=pauli_word,wires=wires,id=id)def__init__(self,theta:TensorLike,pauli_word:str,wires:WiresLike,id:Optional[str]=None,):super().__init__(theta,wires=wires,id=id)self.hyperparameters["pauli_word"]=pauli_wordifnotPauliRot._check_pauli_word(pauli_word):raiseValueError(f'The given Pauli word "{pauli_word}" contains characters that are not allowed. '"Allowed characters are I, X, Y and Z")num_wires=1ifisinstance(wires,int)elselen(wires)ifnotlen(pauli_word)==num_wires:raiseValueError(f"The number of wires must be equal to the length of the Pauli word. "f"The Pauli word {pauli_word} has length {len(pauli_word)}, and "f"{num_wires} wires were given {wires}.")def__repr__(self)->str:returnf"PauliRot({self.data[0]}, {self.hyperparameters['pauli_word']}, wires={self.wires.tolist()})"
[docs]deflabel(self,decimals:Optional[int]=None,base_label:Optional[str]=None,cache:Optional[dict]=None,)->str:r"""A customizable string representation of the operator. Args: decimals=None (int): If ``None``, no parameters are included. Else, specifies how to round the parameters. base_label=None (str): overwrite the non-parameter component of the label cache=None (dict): dictionary that caries information between label calls in the same drawing Returns: str: label to use in drawings **Example:** >>> op = qml.PauliRot(0.1, "XYY", wires=(0,1,2)) >>> op.label() 'RXYY' >>> op.label(decimals=2) 'RXYY\n(0.10)' >>> op.label(base_label="PauliRot") 'PauliRot\n(0.10)' """pauli_word=self.hyperparameters["pauli_word"]op_label=base_labelor("R"+pauli_word)# TODO[dwierichs]: Implement a proper label for parameter-broadcasted operatorsifdecimalsisnotNoneandself.batch_sizeisNone:param_string=f"\n({qml.math.asarray(self.parameters[0]):.{decimals}f})"op_label+=param_stringreturnop_label
@propertydefresource_params(self)->dict:return{"pauli_word":self.hyperparameters["pauli_word"]}@staticmethoddef_check_pauli_word(pauli_word)->bool:"""Check that the given Pauli word has correct structure. Args: pauli_word (str): Pauli word to be checked Returns: bool: Whether the Pauli word has correct structure. """returnall(pauliinPauliRot._ALLOWED_CHARACTERSforpauliinset(pauli_word))
[docs]@staticmethoddefcompute_matrix(theta:TensorLike,pauli_word:str)->TensorLike:# pylint: disable=arguments-differr"""Representation of the operator as a canonical matrix in the computational basis (static method). The canonical matrix is the textbook matrix representation that does not consider wires. Implicitly, this assumes that the wires of the operator correspond to the global wire order. .. seealso:: :meth:`~.PauliRot.matrix` Args: theta (TensorLike): rotation angle pauli_word (str): string representation of Pauli word Returns: TensorLike: canonical matrix **Example** >>> qml.PauliRot.compute_matrix(0.5, 'X') [[9.6891e-01+4.9796e-18j 2.7357e-17-2.4740e-01j] [2.7357e-17-2.4740e-01j 9.6891e-01+4.9796e-18j]] """ifnotPauliRot._check_pauli_word(pauli_word):raiseValueError(f'The given Pauli word "{pauli_word}" contains characters that are not allowed. '"Allowed characters are I, X, Y and Z")interface=qml.math.get_interface(theta)ifinterface=="tensorflow":theta=qml.math.cast_like(theta,1j)# Simplest case is if the Pauli is the identity matrixifset(pauli_word)=={"I"}:returnqml.GlobalPhase.compute_matrix(0.5*theta,n_wires=len(pauli_word))# We first generate the matrix excluding the identity parts and expand it afterwards.# To this end, we have to store on which wires the non-identity parts actnon_identity_wires,non_identity_gates=zip(*[(wire,gate)forwire,gateinenumerate(pauli_word)ifgate!="I"])multi_Z_rot_matrix=MultiRZ.compute_matrix(theta,len(non_identity_gates))# now we conjugate with Hadamard and RX to create the Pauli stringconjugation_matrix=functools.reduce(qml.math.kron,[PauliRot._PAULI_CONJUGATION_MATRICES[gate]forgateinnon_identity_gates],)ifinterface=="tensorflow":conjugation_matrix=qml.math.cast_like(conjugation_matrix,1j)# Note: we use einsum with reverse arguments here because it is not multi-dispatched# and the tensordot containing multi_Z_rot_matrix should decide about the interfacereturnexpand_matrix(qml.math.einsum("...jk,ij->...ik",qml.math.tensordot(multi_Z_rot_matrix,conjugation_matrix,axes=[[-1],[0]]),qml.math.conj(conjugation_matrix),),non_identity_wires,list(range(len(pauli_word))),)
[docs]@staticmethoddefcompute_eigvals(theta:TensorLike,pauli_word:str)->TensorLike:# pylint: disable=arguments-differr"""Eigenvalues of the operator in the computational basis (static method). If :attr:`diagonalizing_gates` are specified and implement a unitary :math:`U^{\dagger}`, the operator can be reconstructed as .. math:: O = U \Sigma U^{\dagger}, where :math:`\Sigma` is the diagonal matrix containing the eigenvalues. Otherwise, no particular order for the eigenvalues is guaranteed. .. seealso:: :meth:`~.PauliRot.eigvals` Returns: TensorLike: eigenvalues **Example** >>> qml.PauliRot.compute_eigvals(torch.tensor(0.5), "X") tensor([0.9689-0.2474j, 0.9689+0.2474j]) """ifqml.math.get_interface(theta)=="tensorflow":theta=qml.math.cast_like(theta,1j)# Identity must be treated specially because its eigenvalues are all the sameifset(pauli_word)=={"I"}:returnqml.GlobalPhase.compute_eigvals(0.5*theta,n_wires=len(pauli_word))returnMultiRZ.compute_eigvals(theta,len(pauli_word))
[docs]@staticmethoddefcompute_decomposition(theta:TensorLike,wires:WiresLike,pauli_word:str)->list["qml.operation.Operator"]:r"""Representation of the operator as a product of other operators (static method). : .. math:: O = O_1 O_2 \dots O_n. .. seealso:: :meth:`~.PauliRot.decomposition`. Args: theta (TensorLike): rotation angle :math:`\theta` wires (Iterable, Wires): the wires the operation acts on pauli_word (string): the Pauli word defining the rotation Returns: list[Operator]: decomposition into lower level operations **Example:** >>> qml.PauliRot.compute_decomposition(1.2, "XY", wires=(0,1)) [H(0), RX(1.5707963267948966, wires=[1]), MultiRZ(1.2, wires=[0, 1]), H(0), RX(-1.5707963267948966, wires=[1])] """ifisinstance(wires,int):# Catch cases when the wire is passed as a single int.wires=[wires]# Check for identity and do nothingifset(pauli_word)=={"I"}:return[qml.GlobalPhase(phi=theta/2)]active_wires,active_gates=zip(*[(wire,gate)forwire,gateinzip(wires,pauli_word)ifgate!="I"])ops=[]forwire,gateinzip(active_wires,active_gates):ifgate=="X":ops.append(Hadamard(wires=[wire]))elifgate=="Y":ops.append(RX(np.pi/2,wires=[wire]))ops.append(MultiRZ(theta,wires=list(active_wires)))forwire,gateinzip(active_wires,active_gates):ifgate=="X":ops.append(Hadamard(wires=[wire]))elifgate=="Y":ops.append(RX(-np.pi/2,wires=[wire]))returnops
[docs]classPCPhase(Operation):r"""PCPhase(phi, dim, wires) A projector-controlled phase gate. This gate applies a complex phase :math:`e^{i\phi}` to the first :math:`dim` basis vectors of the input state while applying a complex phase :math:`e^{-i \phi}` to the remaining basis vectors. For example, consider the 2-qubit case where :math:`dim = 3`: .. math:: \Pi(\phi) = \begin{bmatrix} e^{i\phi} & 0 & 0 & 0 \\ 0 & e^{i\phi} & 0 & 0 \\ 0 & 0 & e^{i\phi} & 0 \\ 0 & 0 & 0 & e^{-i\phi} \end{bmatrix}. **Details:** * Number of wires: Any (the operation can act on any number of wires) * Number of parameters: 1 * Number of dimensions per parameter: (0,) Args: phi (float): rotation angle :math:`\phi` dim (int): the dimension of the subspace wires (Iterable[int, str], Wires): the wires the operation acts on id (str or None): String representing the operation (optional) **Example:** We can define a circuit using :class:`~.PCPhase` as follows: >>> dev = qml.device('default.qubit', wires=2) >>> @qml.qnode(dev) >>> def example_circuit(): ... qml.PCPhase(0.27, dim = 2, wires=range(2)) ... return qml.state() The resulting operation applies a complex phase :math:`e^{0.27i}` to the first :math:`dim = 2` basis vectors and :math:`e^{-0.27i}` to the remaining basis vectors. >>> print(np.round(qml.matrix(example_circuit)(),2)) [[0.96+0.27j 0. +0.j 0. +0.j 0. +0.j ] [0. +0.j 0.96+0.27j 0. +0.j 0. +0.j ] [0. +0.j 0. +0.j 0.96-0.27j 0. +0.j ] [0. +0.j 0. +0.j 0. +0.j 0.96-0.27j]] We can also choose a different :math:`dim` value to apply the phase shift to a different set of basis vectors as follows: >>> pc_op = qml.PCPhase(1.23, dim=3, wires=[1, 2]) >>> print(np.round(qml.matrix(pc_op),2)) [[0.33+0.94j 0. +0.j 0. +0.j 0. +0.j ] [0. +0.j 0.33+0.94j 0. +0.j 0. +0.j ] [0. +0.j 0. +0.j 0.33+0.94j 0. +0.j ] [0. +0.j 0. +0.j 0. +0.j 0.33-0.94j]] """num_wires=AnyWiresnum_params=1"""int: Number of trainable parameters that the operator depends on."""ndim_params=(0,)"""tuple[int]: Number of dimensions per trainable parameter that the operator depends on."""basis="Z"grad_method="A"parameter_frequencies=[(2,)]
def_flatten(self)->FlatPytree:hyperparameter=(("dim",self.hyperparameters["dimension"][0]),)returntuple(self.data),(self.wires,hyperparameter)def__init__(self,phi:TensorLike,dim:int,wires:WiresLike,id:Optional[str]=None):wires=wiresifisinstance(wires,Wires)elseWires(wires)ifnot(isinstance(dim,int)and(dim<=2**len(wires))):raiseValueError(f"The projected dimension {dim} must be an integer that is less than or equal to "f"the max size of the matrix {2**len(wires)}. Try adding more wires.")super().__init__(phi,wires=wires,id=id)self.hyperparameters["dimension"]=(dim,2**len(wires))
[docs]@staticmethoddefcompute_matrix(phi:TensorLike,dimension:tuple[int,int])->TensorLike:"""Get the matrix representation of Pi-controlled phase unitary."""d,t=dimensionifqml.math.get_interface(phi)=="tensorflow":p=qml.math.exp(1j*qml.math.cast_like(phi,1j))minus_p=qml.math.exp(-1j*qml.math.cast_like(phi,1j))zeros=qml.math.zeros_like(p)columns=[]foriinrange(t):columns.append([pifj==ielsezerosforjinrange(t)]ifi<delse[minus_pifj==ielsezerosforjinrange(t)])r=qml.math.stack(columns,like="tensorflow",axis=-2)returnrarg=1j*phiprefactors=qml.math.array([1ifindex<delse-1forindexinrange(t)],like=phi)ifqml.math.ndim(arg)==0:returnqml.math.diag(qml.math.exp(arg*prefactors))diags=qml.math.exp(qml.math.outer(arg,prefactors))returnqml.math.stack([qml.math.diag(d)fordindiags])
[docs]@staticmethoddefcompute_eigvals(*params:TensorLike,**hyperparams)->TensorLike:"""Get the eigvals for the Pi-controlled phase unitary."""phi=params[0]d,t=hyperparams["dimension"]ifqml.math.get_interface(phi)=="tensorflow":phase=qml.math.exp(1j*qml.math.cast_like(phi,1j))minus_phase=qml.math.exp(-1j*qml.math.cast_like(phi,1j))returnstack_last([phaseifindex<delseminus_phaseforindexinrange(t)])arg=1j*phiprefactors=qml.math.array([1ifindex<delse-1forindexinrange(t)],like=phi)ifqml.math.ndim(phi)==0:product=arg*prefactorselse:product=qml.math.outer(arg,prefactors)returnqml.math.exp(product)
[docs]@staticmethoddefcompute_decomposition(*params:TensorLike,wires:WiresLike,**hyperparams)->list["qml.operation.Operator"]:r"""Representation of the operator as a product of other operators (static method). .. math:: O = O_1 O_2 \dots O_n. .. note:: Operations making up the decomposition should be queued within the ``compute_decomposition`` method. .. seealso:: :meth:`~.Operator.decomposition`. Args: *params (list): trainable parameters of the operator, as stored in the ``parameters`` attribute wires (Iterable[Any], Wires): wires that the operator acts on **hyperparams (dict): non-trainable hyper-parameters of the operator, as stored in the ``hyperparameters`` attribute Returns: list[Operator]: decomposition of the operator """phi=params[0]k,n=hyperparams["dimension"]def_get_op_from_binary_rep(binary_rep,theta,wires):iflen(binary_rep)==1:op=(PhaseShift(theta,wires[0])ifint(binary_rep)elsePauliX(wires[0])@PhaseShift(theta,wires[0])@PauliX(wires[0]))else:base_op=(PhaseShift(theta,wires[-1])ifint(binary_rep[-1])elsePauliX(wires[-1])@PhaseShift(theta,wires[-1])@PauliX(wires[-1]))op=qml.ctrl(base_op,control=wires[:-1],control_values=[int(i)foriinbinary_rep[:-1]])returnopn_log2=int(np.log2(n))positive_binary_reps=[bin(_k)[2:].zfill(n_log2)for_kinrange(k)]negative_binary_reps=[bin(_k)[2:].zfill(n_log2)for_kinrange(k,n)]positive_ops=[_get_op_from_binary_rep(br,phi,wires=wires)forbrinpositive_binary_reps]negative_ops=[_get_op_from_binary_rep(br,-1*phi,wires=wires)forbrinnegative_binary_reps]returnpositive_ops+negative_ops
[docs]defadjoint(self)->"PCPhase":"""Computes the adjoint of the operator."""phi=self.parameters[0]dim,_=self.hyperparameters["dimension"]returnPCPhase(-1*phi,dim=dim,wires=self.wires)
[docs]defpow(self,z:Union[int,float])->list["qml.operation.Operator"]:"""Computes the operator raised to z."""phi=self.parameters[0]dim,_=self.hyperparameters["dimension"]return[PCPhase(phi*z,dim=dim,wires=self.wires)]
[docs]defsimplify(self)->"PCPhase":"""Simplifies the operator if possible."""phi=self.parameters[0]%(2*np.pi)dim,_=self.hyperparameters["dimension"]if_can_replace(phi,0):returnqml.Identity(wires=self.wires[0])returnPCPhase(phi,dim=dim,wires=self.wires)
[docs]deflabel(self,decimals:Optional[int]=None,base_label:Optional[str]=None,cache:Optional[dict]=None,)->str:"""The label of the operator when displayed in a circuit."""returnsuper().label(decimals=decimals,base_label=base_labelor"∏_ϕ",cache=cache)
[docs]classIsingXX(Operation):r""" Ising XX coupling gate .. math:: XX(\phi) = \exp\left(-i \frac{\phi}{2} (X \otimes X)\right) = \begin{bmatrix} = \cos(\phi / 2) & 0 & 0 & -i \sin(\phi / 2) \\ 0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\ 0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\ -i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2) \end{bmatrix}. .. note:: Special cases of using the :math:`XX` operator include: * :math:`XX(0) = I`; * :math:`XX(\pi) = i (X \otimes X)`. **Details:** * Number of wires: 2 * Number of parameters: 1 * Number of dimensions per parameter: (0,) * Gradient recipe: :math:`\frac{d}{d\phi}f(XX(\phi)) = \frac{1}{2}\left[f(XX(\phi +\pi/2)) - f(XX(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending on :math:`XX(\phi)`. Args: phi (float): the phase angle wires (int): the subsystem the gate acts on id (str or None): String representing the operation (optional) """num_wires=2num_params=1"""int: Number of trainable parameters that the operator depends on."""ndim_params=(0,)"""tuple[int]: Number of dimensions per trainable parameter that the operator depends on."""resource_keys=set()grad_method="A"parameter_frequencies=[(1,)]
[docs]@staticmethoddefcompute_matrix(phi:TensorLike)->TensorLike:# pylint: disable=arguments-differr"""Representation of the operator as a canonical matrix in the computational basis (static method). The canonical matrix is the textbook matrix representation that does not consider wires. .. seealso:: :meth:`~.IsingXX.matrix` Args: phi (TensorLike): phase angle Returns: TensorLike: canonical matrix **Example** >>> qml.IsingXX.compute_matrix(torch.tensor(0.5)) tensor([[0.9689+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.0000-0.2474j], [0.0000+0.0000j, 0.9689+0.0000j, 0.0000-0.2474j, 0.0000+0.0000j], [0.0000+0.0000j, 0.0000-0.2474j, 0.9689+0.0000j, 0.0000+0.0000j], [0.0000-0.2474j, 0.0000+0.0000j, 0.0000+0.0000j, 0.9689+0.0000j]], dtype=torch.complex128) """c=qml.math.cos(phi/2)s=qml.math.sin(phi/2)eye=qml.math.eye(4,like=phi)rev_eye=qml.math.convert_like(np.eye(4)[::-1].copy(),phi)ifqml.math.get_interface(phi)=="tensorflow":c=qml.math.cast_like(c,1j)s=qml.math.cast_like(s,1j)eye=qml.math.cast_like(eye,1j)rev_eye=qml.math.cast_like(rev_eye,1j)# The following avoids casting an imaginary quantity to reals when backpropagatingjs=-1j*sifqml.math.ndim(phi)==0:returnc*eye+js*rev_eyereturnqml.math.tensordot(c,eye,axes=0)+qml.math.tensordot(js,rev_eye,axes=0)
[docs]@staticmethoddefcompute_decomposition(phi:TensorLike,wires:WiresLike)->list["qml.operation.Operator"]:r"""Representation of the operator as a product of other operators (static method). : .. math:: O = O_1 O_2 \dots O_n. .. seealso:: :meth:`~.IsingXX.decomposition`. Args: phi (TensorLike): the phase angle wires (Iterable, Wires): the subsystem the gate acts on Returns: list[Operator]: decomposition into lower level operations **Example:** >>> qml.IsingXX.compute_decomposition(1.23, wires=(0,1)) [CNOT(wires=[0, 1]), RX(1.23, wires=[0]), CNOT(wires=[0, 1]] """decomp_ops=[qml.CNOT(wires=wires),RX(phi,wires=[wires[0]]),qml.CNOT(wires=wires),]returndecomp_ops
[docs]classIsingYY(Operation):r""" Ising YY coupling gate .. math:: \mathtt{YY}(\phi) = \exp\left(-i \frac{\phi}{2} (Y \otimes Y)\right) = \begin{bmatrix} \cos(\phi / 2) & 0 & 0 & i \sin(\phi / 2) \\ 0 & \cos(\phi / 2) & -i \sin(\phi / 2) & 0 \\ 0 & -i \sin(\phi / 2) & \cos(\phi / 2) & 0 \\ i \sin(\phi / 2) & 0 & 0 & \cos(\phi / 2) \end{bmatrix}. .. note:: Special cases of using the :math:`YY` operator include: * :math:`YY(0) = I`; * :math:`YY(\pi) = i (Y \otimes Y)`. **Details:** * Number of wires: 2 * Number of parameters: 1 * Number of dimensions per parameter: (0,) * Gradient recipe: :math:`\frac{d}{d\phi}f(YY(\phi)) = \frac{1}{2}\left[f(YY(\phi +\pi/2)) - f(YY(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending on :math:`YY(\phi)`. Args: phi (float): the phase angle wires (int): the subsystem the gate acts on id (str or None): String representing the operation (optional) """num_wires=2num_params=1"""int: Number of trainable parameters that the operator depends on."""ndim_params=(0,)"""tuple[int]: Number of dimensions per trainable parameter that the operator depends on."""resource_keys=set()grad_method="A"parameter_frequencies=[(1,)]
[docs]@staticmethoddefcompute_decomposition(phi:TensorLike,wires:WiresLike)->list["qml.operation.Operator"]:r"""Representation of the operator as a product of other operators (static method). : .. math:: O = O_1 O_2 \dots O_n. .. seealso:: :meth:`~.IsingYY.decomposition`. Args: phi (float): the phase angle wires (Iterable, Wires): the subsystem the gate acts on Returns: list[Operator]: decomposition into lower level operations **Example:** >>> qml.IsingYY.compute_decomposition(1.23, wires=(0,1)) [CY(wires=[0, 1]), RY(1.23, wires=[0]), CY(wires=[0, 1])] """return[qml.CY(wires=wires),RY(phi,wires=[wires[0]]),qml.CY(wires=wires),]
[docs]@staticmethoddefcompute_matrix(phi:TensorLike)->TensorLike:# pylint: disable=arguments-differr"""Representation of the operator as a canonical matrix in the computational basis (static method). The canonical matrix is the textbook matrix representation that does not consider wires. Implicitly, this assumes that the wires of the operator correspond to the global wire order. .. seealso:: :meth:`~.IsingYY.matrix` Args: phi (TensorLike): phase angle Returns: TensorLike: canonical matrix **Example** >>> qml.IsingYY.compute_matrix(torch.tensor(0.5)) tensor([[0.9689+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.2474j], [0.0000+0.0000j, 0.9689+0.0000j, 0.0000-0.2474j, 0.0000+0.0000j], [0.0000+0.0000j, 0.0000-0.2474j, 0.9689+0.0000j, 0.0000+0.0000j], [0.0000+0.2474j, 0.0000+0.0000j, 0.0000+0.0000j, 0.9689+0.0000j]]) """c=qml.math.cos(phi/2)s=qml.math.sin(phi/2)ifqml.math.get_interface(phi)=="tensorflow":c=qml.math.cast_like(c,1j)s=qml.math.cast_like(s,1j)js=1j*sr_term=qml.math.cast_like(qml.math.array([[0.0,0.0,0.0,1.0],[0.0,0.0,-1.0,0.0],[0.0,-1.0,0.0,0.0],[1.0,0.0,0.0,0.0],],like=js,),1j,)ifqml.math.ndim(phi)==0:returnc*qml.math.cast_like(qml.math.eye(4,like=c),c)+js*r_termreturnqml.math.tensordot(c,np.eye(4),axes=0)+qml.math.tensordot(js,r_term,axes=0)
[docs]classIsingZZ(Operation):r""" Ising ZZ coupling gate .. math:: ZZ(\phi) = \exp\left(-i \frac{\phi}{2} (Z \otimes Z)\right) = \begin{bmatrix} e^{-i \phi / 2} & 0 & 0 & 0 \\ 0 & e^{i \phi / 2} & 0 & 0 \\ 0 & 0 & e^{i \phi / 2} & 0 \\ 0 & 0 & 0 & e^{-i \phi / 2} \end{bmatrix}. .. note:: Special cases of using the :math:`ZZ` operator include: * :math:`ZZ(0) = I`; * :math:`ZZ(\pi) = - (Z \otimes Z)`; * :math:`ZZ(2\pi) = - I`; **Details:** * Number of wires: 2 * Number of parameters: 1 * Number of dimensions per parameter: (0,) * Gradient recipe: :math:`\frac{d}{d\phi}f(ZZ(\phi)) = \frac{1}{2}\left[f(ZZ(\phi +\pi/2)) - f(ZZ(\phi-\pi/2))\right]` where :math:`f` is an expectation value depending on :math:`ZZ(\theta)`. Args: phi (float): the phase angle wires (int): the subsystem the gate acts on id (str or None): String representing the operation (optional) """num_wires=2num_params=1"""int: Number of trainable parameters that the operator depends on."""ndim_params=(0,)"""tuple[int]: Number of dimensions per trainable parameter that the operator depends on."""resource_keys=set()grad_method="A"parameter_frequencies=[(1,)]
[docs]@staticmethoddefcompute_decomposition(phi:TensorLike,wires:WiresLike):r"""Representation of the operator as a product of other operators (static method). : .. math:: O = O_1 O_2 \dots O_n. .. seealso:: :meth:`~.IsingZZ.decomposition`. Args: phi (float): the phase angle wires (Iterable, Wires): the subsystem the gate acts on Returns: list[Operator]: decomposition into lower level operations **Example:** >>> qml.IsingZZ.compute_decomposition(1.23, wires=[0, 1]) [CNOT(wires=[0, 1]), RZ(1.23, wires=[1]), CNOT(wires=[0, 1])] """return[qml.CNOT(wires=wires),RZ(phi,wires=[wires[1]]),qml.CNOT(wires=wires),]
[docs]@staticmethoddefcompute_matrix(phi:TensorLike)->TensorLike:# pylint: disable=arguments-differr"""Representation of the operator as a canonical matrix in the computational basis (static method). The canonical matrix is the textbook matrix representation that does not consider wires. Implicitly, this assumes that the wires of the operator correspond to the global wire order. .. seealso:: :meth:`~.IsingZZ.matrix` Args: phi (TensorLike): phase angle Returns: TensorLike: canonical matrix **Example** >>> qml.IsingZZ.compute_matrix(torch.tensor(0.5)) tensor([[0.9689-0.2474j, 0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j], [0.0000+0.0000j, 0.9689+0.2474j, 0.0000+0.0000j, 0.0000+0.0000j], [0.0000+0.0000j, 0.0000+0.0000j, 0.9689+0.2474j, 0.0000+0.0000j], [0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.9689-0.2474j]]) """ifqml.math.get_interface(phi)=="tensorflow":p=qml.math.exp(-0.5j*qml.math.cast_like(phi,1j))ifqml.math.ndim(p)==0:returnqml.math.diag([p,qml.math.conj(p),qml.math.conj(p),p])diags=stack_last([p,qml.math.conj(p),qml.math.conj(p),p])returndiags[:,:,np.newaxis]*qml.math.cast_like(qml.math.eye(4,like=diags),diags)signs=qml.math.array([1,-1,-1,1],like=phi)arg=-0.5j*phiifqml.math.ndim(arg)==0:returnqml.math.diag(qml.math.exp(arg*signs))diags=qml.math.exp(qml.math.outer(arg,signs))returndiags[:,:,np.newaxis]*qml.math.cast_like(qml.math.eye(4,like=diags),diags)
[docs]@staticmethoddefcompute_eigvals(phi:TensorLike)->TensorLike:# pylint: disable=arguments-differr"""Eigenvalues of the operator in the computational basis (static method). If :attr:`diagonalizing_gates` are specified and implement a unitary :math:`U^{\dagger}`, the operator can be reconstructed as .. math:: O = U \Sigma U^{\dagger}, where :math:`\Sigma` is the diagonal matrix containing the eigenvalues. Otherwise, no particular order for the eigenvalues is guaranteed. .. seealso:: :meth:`~.IsingZZ.eigvals` Args: phi (TensorLike) phase angle Returns: TensorLike: eigenvalues **Example** >>> qml.IsingZZ.compute_eigvals(torch.tensor(0.5)) tensor([0.9689-0.2474j, 0.9689+0.2474j, 0.9689+0.2474j, 0.9689-0.2474j]) """ifqml.math.get_interface(phi)=="tensorflow":phase=qml.math.exp(-0.5j*qml.math.cast_like(phi,1j))returnstack_last([phase,qml.math.conj(phase),qml.math.conj(phase),phase])prefactors=qml.math.array([-0.5j,0.5j,0.5j,-0.5j],like=phi)ifqml.math.ndim(phi)==0:product=phi*prefactorselse:product=qml.math.outer(phi,prefactors)returnqml.math.exp(product)
[docs]@staticmethoddefcompute_decomposition(phi:TensorLike,wires:WiresLike)->list["qml.operation.Operator"]:r"""Representation of the operator as a product of other operators (static method). : .. math:: O = O_1 O_2 \dots O_n. .. seealso:: :meth:`~.IsingXY.decomposition`. Args: phi (float): the phase angle wires (Iterable, Wires): the subsystem the gate acts on Returns: list[Operator]: decomposition into lower level operations **Example:** >>> qml.IsingXY.compute_decomposition(1.23, wires=(0,1)) [H(0), CY(wires=[0, 1]), RY(0.615, wires=[0]), RX(-0.615, wires=[1]), CY(wires=[0, 1]), H(0)] """return[Hadamard(wires=[wires[0]]),qml.CY(wires=wires),RY(phi/2,wires=[wires[0]]),RX(-phi/2,wires=[wires[1]]),qml.CY(wires=wires),Hadamard(wires=[wires[0]]),]
[docs]@staticmethoddefcompute_matrix(phi:TensorLike)->TensorLike:# pylint: disable=arguments-differr"""Representation of the operator as a canonical matrix in the computational basis (static method). The canonical matrix is the textbook matrix representation that does not consider wires. Implicitly, this assumes that the wires of the operator correspond to the global wire order. .. seealso:: :meth:`~.IsingXY.matrix` Args: phi (TensorLike): phase angle Returns: TensorLike: canonical matrix **Example** >>> qml.IsingXY.compute_matrix(0.5) array([[1. +0.j , 0. +0.j , 0. +0.j , 0. +0.j ], [0. +0.j , 0.96891242+0.j , 0. +0.24740396j, 0. +0.j ], [0. +0.j , 0. +0.24740396j, 0.96891242+0.j , 0. +0.j ], [0. +0.j , 0. +0.j , 0. +0.j , 1. +0.j ]]) """c=qml.math.cos(phi/2)s=qml.math.sin(phi/2)ifqml.math.get_interface(phi)=="tensorflow":c=qml.math.cast_like(c,1j)s=qml.math.cast_like(s,1j)js=1j*soff_diag=qml.math.cast_like(qml.math.array([[0.0,0.0,0.0,0.0],[0.0,0.0,1.0,0.0],[0.0,1.0,0.0,0.0],[0.0,0.0,0.0,0.0],],like=js,),1j,)ifqml.math.ndim(phi)==0:returnqml.math.diag([1,c,c,1])+js*off_diagones=qml.math.ones_like(c)diags=stack_last([ones,c,c,ones])[:,:,np.newaxis]returndiags*np.eye(4)+qml.math.tensordot(js,off_diag,axes=0)
[docs]@staticmethoddefcompute_eigvals(phi:TensorLike)->TensorLike:# pylint: disable=arguments-differr"""Eigenvalues of the operator in the computational basis (static method). If :attr:`diagonalizing_gates` are specified and implement a unitary :math:`U^{\dagger}`, the operator can be reconstructed as .. math:: O = U \Sigma U^{\dagger}, where :math:`\Sigma` is the diagonal matrix containing the eigenvalues. Otherwise, no particular order for the eigenvalues is guaranteed. .. seealso:: :meth:`~.IsingXY.eigvals` Args: phi (TensorLike): phase angle Returns: TensorLike: eigenvalues **Example** >>> qml.IsingXY.compute_eigvals(0.5) array([0.96891242+0.24740396j, 0.96891242-0.24740396j, 1. +0.j , 1. +0.j ]) """ifqml.math.get_interface(phi)=="tensorflow":phi=qml.math.cast_like(phi,1j)signs=np.array([1,-1,0,0])ifqml.math.ndim(phi)==0:returnqml.math.exp(0.5j*phi*signs)returnqml.math.exp(qml.math.tensordot(0.5j*phi,signs,axes=0))
[docs]classPSWAP(Operation):r"""Phase SWAP gate .. math:: PSWAP(\phi) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & e^{i \phi} & 0 \\ 0 & e^{i \phi} & 0 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}. **Details:** * Number of wires: 2 * Number of parameters: 1 * Gradient recipe: .. math:: \frac{d}{d \phi} PSWAP(\phi) = \frac{1}{2} \left[ PSWAP(\phi + \pi / 2) - PSWAP(\phi - \pi / 2) \right] Args: phi (float): the phase angle wires (int): the subsystem the gate acts on id (str or None): String representing the operation (optional) """num_wires=2num_params=1"""int: Number of trainable parameters that the operator depends on."""ndim_params=(0,)"""tuple[int]: Number of dimensions per trainable parameter that the operator depends on."""resource_keys=set()grad_method="A"grad_recipe=([[0.5,1,np.pi/2],[-0.5,1,-np.pi/2]],)def__init__(self,phi:TensorLike,wires:WiresLike,id:Optional[str]=None):super().__init__(phi,wires=wires,id=id)@propertydefresource_params(self)->dict:return{}
[docs]@staticmethoddefcompute_decomposition(phi:TensorLike,wires:WiresLike)->list["qml.operation.Operator"]:r"""Representation of the operator as a product of other operators (static method). : .. math:: O = O_1 O_2 \dots O_n. .. seealso:: :meth:`~.PSWAP.decomposition`. Args: phi (float): the phase angle wires (Iterable, Wires): the subsystem the gate acts on Returns: list[Operator]: decomposition into lower level operations **Example:** >>> qml.PSWAP.compute_decomposition(1.23, wires=(0,1)) [SWAP(wires=[0, 1]), CNOT(wires=[0, 1]), PhaseShift(1.23, wires=[1]), CNOT(wires=[0, 1])] """return[qml.SWAP(wires=wires),qml.CNOT(wires=wires),PhaseShift(phi,wires=[wires[1]]),qml.CNOT(wires=wires),]
[docs]@staticmethoddefcompute_matrix(phi:TensorLike)->TensorLike:# pylint: disable=arguments-differr"""Representation of the operator as a canonical matrix in the computational basis (static method). The canonical matrix is the textbook matrix representation that does not consider wires. Implicitly, this assumes that the wires of the operator correspond to the global wire order. .. seealso:: :meth:`~.PSWAP.matrix` Args: phi (TensorLike): phase angle Returns: TensorLike: canonical matrix **Example** >>> qml.PSWAP.compute_matrix(0.5) array([[1. +0.j, 0. +0.j , 0. +0.j , 0. +0.j], [0. +0.j, 0. +0.j , 0.87758256+0.47942554j, 0. +0.j], [0. +0.j, 0.87758256+0.47942554j, 0. +0.j , 0. +0.j], [0. +0.j, 0. +0.j , 0. +0.j , 1. +0.j]]) """ifqml.math.get_interface(phi)=="tensorflow":phi=qml.math.cast_like(phi,1j)e=qml.math.exp(1j*phi)zero=qml.math.zeros_like(phi)one=qml.math.ones_like(phi)returnqml.math.stack([stack_last([one,zero,zero,zero]),stack_last([zero,zero,e,zero]),stack_last([zero,e,zero,zero]),stack_last([zero,zero,zero,one]),],axis=-2,)
[docs]@staticmethoddefcompute_eigvals(phi:TensorLike)->TensorLike:# pylint: disable=arguments-differr"""Eigenvalues of the operator in the computational basis (static method). If :attr:`diagonalizing_gates` are specified and implement a unitary :math:`U^{\dagger}`, the operator can be reconstructed as .. math:: O = U \Sigma U^{\dagger}, where :math:`\Sigma` is the diagonal matrix containing the eigenvalues. Otherwise, no particular order for the eigenvalues is guaranteed. .. seealso:: :meth:`~.PSWAP.eigvals` Args: phi (TensorLike): phase angle Returns: TensorLike: eigenvalues **Example** >>> qml.PSWAP.compute_eigvals(0.5) array([ 1. +0.j , 1. +0.j, -0.87758256-0.47942554j, 0.87758256+0.47942554j]) """ifqml.math.get_interface(phi)=="tensorflow":phi=qml.math.cast_like(phi,1j)e=qml.math.exp(1j*phi)one=qml.math.ones_like(phi)returnqml.math.transpose(qml.math.stack([one,one,-e,e]))
[docs]classCPhaseShift00(Operation):r""" A qubit controlled phase shift. .. math:: CR_{00}(\phi) = \begin{bmatrix} e^{i\phi} & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}. .. note:: The first wire provided corresponds to the **control qubit** and controls on the zero state :math:`|0\rangle`. **Details:** * Number of wires: 2 * Number of parameters: 1 * Number of dimensions per parameter: (0,) * Gradient recipe: .. math:: \frac{d}{d \phi} CR_{00}(\phi) = \frac{1}{2} \left[ CR_{00}(\phi + \pi / 2) - CR_{00}(\phi - \pi / 2) \right] Args: phi (float): rotation angle :math:`\phi` wires (Sequence[int]): the wire the operation acts on id (str or None): String representing the operation (optional) """num_wires=2num_params=1"""int: Number of trainable parameters that the operator depends on."""ndim_params=(0,)"""tuple[int]: Number of dimensions per trainable parameter that the operator depends on."""grad_method="A"parameter_frequencies=[(1,)]
[docs]@staticmethoddefcompute_matrix(phi:TensorLike)->TensorLike:# pylint: disable=arguments-differr"""Representation of the operator as a canonical matrix in the computational basis (static method). The canonical matrix is the textbook matrix representation that does not consider wires. Implicitly, this assumes that the wires of the operator correspond to the global wire order. .. seealso:: :meth:`~.CPhaseShift00.matrix` Args: phi (TensorLike): phase shift Returns: TensorLike: canonical matrix **Example** >>> qml.CPhaseShift00.compute_matrix(torch.tensor(0.5)) tensor([[0.8776+0.4794j, 0.0+0.0j, 0.0+0.0j, 0.0+0.0j], [0.0000+0.0000j, 1.0+0.0j, 0.0+0.0j, 0.0+0.0j], [0.0000+0.0000j, 0.0+0.0j, 1.0+0.0j, 0.0+0.0j], [0.0000+0.0000j, 0.0+0.0j, 0.0+0.0j, 1.0+0.0j]]) """ifqml.math.get_interface(phi)=="tensorflow":phi=qml.math.cast_like(phi,1j)exp_part=qml.math.exp(1j*phi)ifqml.math.ndim(phi)>0:ones=qml.math.ones_like(exp_part)zeros=qml.math.zeros_like(exp_part)matrix=[[exp_part,zeros,zeros,zeros],[zeros,ones,zeros,zeros],[zeros,zeros,ones,zeros],[zeros,zeros,zeros,ones],]returnqml.math.stack([stack_last(row)forrowinmatrix],axis=-2)returnqml.math.diag([exp_part,1,1,1])
[docs]@staticmethoddefcompute_eigvals(phi:TensorLike)->TensorLike:# pylint: disable=arguments-differr"""Eigenvalues of the operator in the computational basis (static method). If :attr:`diagonalizing_gates` are specified and implement a unitary :math:`U^{\dagger}`, the operator can be reconstructed as .. math:: O = U \Sigma U^{\dagger}, where :math:`\Sigma` is the diagonal matrix containing the eigenvalues. Otherwise, no particular order for the eigenvalues is guaranteed. .. seealso:: :meth:`~.CPhaseShift00.eigvals` Args: phi (TensorLike): phase shift Returns: TensorLike: eigenvalues **Example** >>> qml.CPhaseShift00.compute_eigvals(torch.tensor(0.5)) tensor([0.8776+0.4794j, 1.0000+0.0000j, 1.0000+0.0000j, 1.0000+0.0000j]) """ifqml.math.get_interface(phi)=="tensorflow":phi=qml.math.cast_like(phi,1j)exp_part=qml.math.exp(1j*phi)ones=qml.math.ones_like(exp_part)returnstack_last([exp_part,ones,ones,ones])
[docs]@staticmethoddefcompute_decomposition(phi:TensorLike,wires:WiresLike)->list["qml.operation.Operator"]:r"""Representation of the operator as a product of other operators (static method). : .. math:: O = O_1 O_2 \dots O_n. .. seealso:: :meth:`~.CPhaseShift00.decomposition`. Args: phi (float): rotation angle :math:`\phi` wires (Iterable, Wires): wires that the operator acts on Returns: list[Operator]: decomposition into lower level operations **Example:** >>> qml.CPhaseShift00.compute_decomposition(1.234, wires=(0,1)) [X(0), X(1), PhaseShift(0.617, wires=[0]), PhaseShift(0.617, wires=[1]), CNOT(wires=[0, 1]), PhaseShift(-0.617, wires=[1]), CNOT(wires=[0, 1]), X(1), X(0)] """decomp_ops=[PauliX(wires[0]),PauliX(wires[1]),PhaseShift(phi/2,wires=[wires[0]]),PhaseShift(phi/2,wires=[wires[1]]),qml.CNOT(wires=wires),PhaseShift(-phi/2,wires=[wires[1]]),qml.CNOT(wires=wires),PauliX(wires[1]),PauliX(wires[0]),]returndecomp_ops
@propertydefcontrol_values(self)->str:"""str: The control values of the operation"""return"0"@propertydefcontrol_wires(self)->Wires:returnself.wires[0:1]
[docs]classCPhaseShift01(Operation):r""" A qubit controlled phase shift. .. math:: CR_{01\phi}(\phi) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & e^{i\phi} & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}. .. note:: The first wire provided corresponds to the **control qubit** and controls on the zero state :math:`|0\rangle`. **Details:** * Number of wires: 2 * Number of parameters: 1 * Number of dimensions per parameter: (0,) * Gradient recipe: .. math:: \frac{d}{d \phi} CR_{01}(\phi) = \frac{1}{2} \left[ CR_{01}(\phi + \pi / 2) - CR_{01}(\phi - \pi / 2) \right] Args: phi (float): rotation angle :math:`\phi` wires (Sequence[int]): the wire the operation acts on id (str or None): String representing the operation (optional) """num_wires=2num_params=1"""int: Number of trainable parameters that the operator depends on."""ndim_params=(0,)"""tuple[int]: Number of dimensions per trainable parameter that the operator depends on."""grad_method="A"parameter_frequencies=[(1,)]
[docs]@staticmethoddefcompute_matrix(phi:TensorLike)->TensorLike:# pylint: disable=arguments-differr"""Representation of the operator as a canonical matrix in the computational basis (static method). The canonical matrix is the textbook matrix representation that does not consider wires. Implicitly, this assumes that the wires of the operator correspond to the global wire order. .. seealso:: :meth:`~.CPhaseShift01.matrix` Args: phi (TensorLike): phase shift Returns: TensorLike: canonical matrix **Example** >>> qml.CPhaseShift01.compute_matrix(torch.tensor(0.5)) tensor([[1.0+0.0j, 0.0000+0.0000j, 0.0+0.0j, 0.0+0.0j], [0.0+0.0j, 0.8776+0.4794j, 0.0+0.0j, 0.0+0.0j], [0.0+0.0j, 0.0000+0.0000j, 1.0+0.0j, 0.0+0.0j], [0.0+0.0j, 0.0000+0.0000j, 0.0+0.0j, 1.0+0.0j]]) """ifqml.math.get_interface(phi)=="tensorflow":phi=qml.math.cast_like(phi,1j)exp_part=qml.math.exp(1j*phi)ifqml.math.ndim(phi)>0:ones=qml.math.ones_like(exp_part)zeros=qml.math.zeros_like(exp_part)matrix=[[ones,zeros,zeros,zeros],[zeros,exp_part,zeros,zeros],[zeros,zeros,ones,zeros],[zeros,zeros,zeros,ones],]returnqml.math.stack([stack_last(row)forrowinmatrix],axis=-2)returnqml.math.diag([1,exp_part,1,1])
[docs]@staticmethoddefcompute_eigvals(phi:TensorLike)->TensorLike:# pylint: disable=arguments-differr"""Eigenvalues of the operator in the computational basis (static method). If :attr:`diagonalizing_gates` are specified and implement a unitary :math:`U^{\dagger}`, the operator can be reconstructed as .. math:: O = U \Sigma U^{\dagger}, where :math:`\Sigma` is the diagonal matrix containing the eigenvalues. Otherwise, no particular order for the eigenvalues is guaranteed. .. seealso:: :meth:`~.CPhaseShift01.eigvals` Args: phi (TensorLike): phase shift Returns: TensorLike: eigenvalues **Example** >>> qml.CPhaseShift01.compute_eigvals(torch.tensor(0.5)) tensor([1.0000+0.0000j, 0.8776+0.4794j, 1.0000+0.0000j, 1.0000+0.0000j]) """ifqml.math.get_interface(phi)=="tensorflow":phi=qml.math.cast_like(phi,1j)exp_part=qml.math.exp(1j*phi)ones=qml.math.ones_like(exp_part)returnstack_last([ones,exp_part,ones,ones])
[docs]@staticmethoddefcompute_decomposition(phi:TensorLike,wires:WiresLike)->list["qml.operation.Operator"]:r"""Representation of the operator as a product of other operators (static method). : .. math:: O = O_1 O_2 \dots O_n. .. seealso:: :meth:`~.CPhaseShift01.decomposition`. Args: phi (Tensorlike): rotation angle :math:`\phi` wires (Iterable, Wires): wires that the operator acts on Returns: list[Operator]: decomposition into lower level operations **Example:** >>> qml.CPhaseShift01.compute_decomposition(1.234, wires=(0,1)) [X(0), PhaseShift(0.617, wires=[0]), PhaseShift(0.617, wires=[1]), CNOT(wires=[0, 1]), PhaseShift(-0.617, wires=[1]), CNOT(wires=[0, 1]), X(0)] """decomp_ops=[PauliX(wires[0]),PhaseShift(phi/2,wires=[wires[0]]),PhaseShift(phi/2,wires=[wires[1]]),qml.CNOT(wires=wires),PhaseShift(-phi/2,wires=[wires[1]]),qml.CNOT(wires=wires),PauliX(wires[0]),]returndecomp_ops
@propertydefcontrol_values(self)->str:"""str: The control values of the operation"""return"0"@propertydefcontrol_wires(self)->Wires:returnself.wires[0:1]
[docs]@staticmethoddefcompute_matrix(phi:TensorLike)->TensorLike:# pylint: disable=arguments-differr"""Representation of the operator as a canonical matrix in the computational basis (static method). The canonical matrix is the textbook matrix representation that does not consider wires. Implicitly, this assumes that the wires of the operator correspond to the global wire order. .. seealso:: :meth:`~.CPhaseShift10.matrix` Args: phi (TensorLike): phase shift Returns: TensorLike: canonical matrix **Example** >>> qml.CPhaseShift10.compute_matrix(torch.tensor(0.5)) tensor([[1.0+0.0j, 0.0+0.0j, 0.0000+0.0000j, 0.0+0.0j], [0.0+0.0j, 1.0+0.0j, 0.0000+0.0000j, 0.0+0.0j], [0.0+0.0j, 0.0+0.0j, 0.8776+0.4794j, 0.0+0.0j], [0.0+0.0j, 0.0+0.0j, 0.0000+0.0000j, 1.0+0.0j]]) """ifqml.math.get_interface(phi)=="tensorflow":phi=qml.math.cast_like(phi,1j)exp_part=qml.math.exp(1j*phi)ifqml.math.ndim(phi)>0:ones=qml.math.ones_like(exp_part)zeros=qml.math.zeros_like(exp_part)matrix=[[ones,zeros,zeros,zeros],[zeros,ones,zeros,zeros],[zeros,zeros,exp_part,zeros],[zeros,zeros,zeros,ones],]returnqml.math.stack([stack_last(row)forrowinmatrix],axis=-2)returnqml.math.diag([1,1,exp_part,1])
[docs]@staticmethoddefcompute_eigvals(phi:TensorLike)->TensorLike:# pylint: disable=arguments-differr"""Eigenvalues of the operator in the computational basis (static method). If :attr:`diagonalizing_gates` are specified and implement a unitary :math:`U^{\dagger}`, the operator can be reconstructed as .. math:: O = U \Sigma U^{\dagger}, where :math:`\Sigma` is the diagonal matrix containing the eigenvalues. Otherwise, no particular order for the eigenvalues is guaranteed. .. seealso:: :meth:`~.CPhaseShift10.eigvals` Args: phi (TensorLike): phase shift Returns: TensorLike: eigenvalues **Example** >>> qml.CPhaseShift10.compute_eigvals(torch.tensor(0.5)) tensor([1.0000+0.0000j, 1.0000+0.0000j, 0.8776+0.4794j, 1.0000+0.0000j]) """ifqml.math.get_interface(phi)=="tensorflow":phi=qml.math.cast_like(phi,1j)exp_part=qml.math.exp(1j*phi)ones=qml.math.ones_like(exp_part)returnstack_last([ones,ones,exp_part,ones])
[docs]@staticmethoddefcompute_decomposition(phi:TensorLike,wires:WiresLike)->list["qml.operation.Operator"]:r"""Representation of the operator as a product of other operators (static method). : .. math:: O = O_1 O_2 \dots O_n. .. seealso:: :meth:`~.CPhaseShift10.decomposition`. Args: phi (TensorLike): rotation angle :math:`\phi` wires (Iterable, Wires): wires that the operator acts on Returns: list[Operator]: decomposition into lower level operations **Example:** >>> qml.CPhaseShift10.compute_decomposition(1.234, wires=(0,1)) [X(1), PhaseShift(0.617, wires=[0]), PhaseShift(0.617, wires=[1]), CNOT(wires=[0, 1]), PhaseShift(-0.617, wires=[1]), CNOT(wires=[0, 1]), X(1)] """decomp_ops=[PauliX(wires[1]),PhaseShift(phi/2,wires=[wires[0]]),PhaseShift(phi/2,wires=[wires[1]]),qml.CNOT(wires=wires),PhaseShift(-phi/2,wires=[wires[1]]),qml.CNOT(wires=wires),PauliX(wires[1]),]returndecomp_ops