Source code for pennylane.ops.functions.is_commuting
# 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."""Defines `is_commuting`, an function for determining if two functions commute."""importnumpyasnpimportpennylaneasqmlfrompennylane.ops.op_mathimportProd,SProd,Sumdef_pword_is_commuting(pauli_word_1,pauli_word_2):r"""Checks if two Pauli words commute. Args: pauli_word_1 (Operator): first Pauli word in commutator. pauli_word_2 (Operator): second Pauli word in commutator. Returns: bool: returns True if the input Pauli commute, False otherwise. **Example** >>> pauli_word_1 = qml.prod(qml.X("a"), qml.Y("b")) >>> pauli_word_2 = qml.prod(qml.Z("a"), qml.Z("c")) >>> _pword_is_commuting(pauli_word_1, pauli_word_2) False >>> pauli_word_1 = qml.sum(qml.X('a') , qml.Y('b')) >>> pauli_word_2 = qml.sum(qml.Z('c') , qml.X('a')) >>> _pword_is_commuting(pauli_word_1, pauli_word_2) True """pr1=pauli_word_1.pauli_reppr2=pauli_word_2.pauli_repcomm=pr1.commutator(pr2)comm.simplify()returncomm==qml.pauli.pauli_arithmetic.PauliSentence({})def_get_target_name(op):"""Get the name for the target operation. If the operation is not controlled, this is simplify the operation's name. """_control_base_map={"CNOT":"PauliX","CZ":"PauliZ","CCZ":"PauliZ","CY":"PauliY","CH":"Hadamard","CSWAP":"SWAP","Toffoli":"PauliX","ControlledPhaseShift":"PhaseShift","CRX":"RX","CRY":"RY","CRZ":"RZ","CRot":"Rot","MultiControlledX":"PauliX",}ifop.namein_control_base_map:return_control_base_map[op.name]ifisinstance(op,qml.ops.op_math.Controlled):# pylint: disable=no-memberreturnop.base.namereturnop.namedef_check_mat_commutation(op1,op2):"""Uses matrices and matrix multiplication to determine whether op1 and op2 commute. ``op1`` and ``op2`` must be on the same wires. """op1_mat=op1.matrix()op2_mat=op2.matrix()mat_12=np.matmul(op1_mat,op2_mat)mat_21=np.matmul(op2_mat,op1_mat)returnqml.math.allclose(mat_12,mat_21)def_create_commute_function():"""This function constructs the ``_commutes`` helper utility function while using closure to hide the ``commutation_map`` data away from the global scope of the file. This function only needs to be called a single time. Returns: function """pauliz_group={"PauliZ","ctrl","S","Adjoint(S)","T","Adjoint(T)","RZ","PhaseShift","MultiRZ","Identity","U1","IsingZZ",}swap_group={"SWAP","ISWAP","SISWAP","Identity","Adjoint(ISWAP)","Adjoint(SISWAP)"}paulix_group={"PauliX","SX","RX","Identity","IsingXX","Adjoint(SX)"}pauliy_group={"PauliY","RY","Identity","IsingYY"}commutation_map={}forgroupin[paulix_group,pauliy_group,pauliz_group,swap_group]:foropingroup:commutation_map[op]=groupidentity_only={"Hadamard","U2","U3","Rot"}foropinidentity_only:commutation_map[op]={"Identity",op}commutation_map["Identity"]=pauliz_group.union(swap_group,paulix_group,pauliy_group,identity_only)defcommutes_inner(op_name1,op_name2):"""Determine whether or not two operations commute. Relies on ``commutation_map`` from the enclosing namespace of ``_create_commute_function``. Args: op_name1 (str): name of one operation. op_name2 (str): name of the second operation. Returns: bool: True if the operations commute, False otherwise. """returnop_name1incommutation_map[op_name2]returncommutes_inner_commutes=_create_commute_function()def_check_opmath_operations(operation1,operation2):"""Check that `SProd`, `Prod`, and `Sum` instances only contain Pauli words."""foropin[operation1,operation2]:ifop.pauli_repisnotNone:continueifisinstance(op,(SProd,Prod,Sum)):raiseqml.QuantumFunctionError(f"Operation {op} currently not supported. Prod, Sprod, and Sum instances must have a valid Pauli representation.")defintersection(wires1,wires2):r"""Check if two sets of wires intersect. Args: wires1 (pennylane.wires.Wires): First set of wires. wires2 (pennylane.wires.Wires: Second set of wires. Returns: bool: True if the two sets of wires are not disjoint and False if disjoint. """returnlen(qml.wires.Wires.shared_wires([wires1,wires2]))!=0defcheck_commutation_two_non_simplified_crot(operation1,operation2):r"""Check commutation for two CRot that were not simplified. Args: operation1 (pennylane.Operation): First operation. operation2 (pennylane.Operation): Second operation. Returns: bool: True if commutation, False otherwise. """# Two non simplified CRottarget_wires_1=qml.wires.Wires([wforwinoperation1.wiresifwnotinoperation1.control_wires])target_wires_2=qml.wires.Wires([wforwinoperation2.wiresifwnotinoperation2.control_wires])control_control=intersection(operation1.control_wires,operation2.control_wires)target_target=intersection(target_wires_1,target_wires_2)ifcontrol_control:iftarget_target:return_check_mat_commutation(operation1,operation2)# control_control and not target_targetreturnTrueiftarget_target:return_check_mat_commutation(qml.Rot(*operation1.data,wires=operation1.wires[1]),qml.Rot(*operation2.data,wires=operation2.wires[1]),)returnFalsedefcheck_commutation_two_non_simplified_rotations(operation1,operation2):r"""Check that the operations are two non simplified operations. If it is the case, then it checks commutation for two rotations that were not simplified. Only allowed ops are `U2`, `U3`, `Rot`, `CRot`. Args: operation1 (pennylane.Operation): First operation. operation2 (pennylane.Operation): Second operation. Returns: bool: True if commutation, False otherwise, None if not two rotations. """target_wires_1=qml.wires.Wires([wforwinoperation1.wiresifwnotinoperation1.control_wires])target_wires_2=qml.wires.Wires([wforwinoperation2.wiresifwnotinoperation2.control_wires])ifoperation1.name=="CRot":ifintersection(target_wires_1,operation2.wires):op1_rot=qml.Rot(*operation1.data,wires=target_wires_1)return_check_mat_commutation(op1_rot,operation2)return_commutes(operation2.name,"ctrl")ifoperation2.name=="CRot":ifintersection(target_wires_2,operation1.wires):op2_rot=qml.Rot(*operation2.data,wires=target_wires_2)return_check_mat_commutation(op2_rot,operation1)return_commutes(operation1.name,"ctrl")return_check_mat_commutation(operation1,operation2)unsupported_operations=["PauliRot","QubitDensityMatrix","CVNeuralNetLayers","ApproxTimeEvolution","ArbitraryUnitary","CommutingEvolution","DisplacementEmbedding","SqueezingEmbedding","Exp",]non_commuting_operations=[# StatePrepBase"StatePrep","BasisState",# Templates"ArbitraryStatePreparation","MottonenStatePreparation","QubitCarry","QubitSum","SingleExcitation","SingleExcitationMinus","SingleExcitationPlus","DoubleExcitation","DoubleExcitationPlus","DoubleExcitationMinus","BasicEntanglerLayers","GateFabric","ParticleConservingU1","ParticleConservingU2","RandomLayers","SimplifiedTwoDesign","StronglyEntanglingLayers","AllSinglesDoubles","FermionicDoubleExcitation","FermionicSingleExcitation","Grover","kUpCCGSD","Permute","QFT","QuantumMonteCarlo","QuantumPhaseEstimation","UCCSD","MPS","TTN","AmplitudeEmbedding","AngleEmbedding","BasisEmbedding","IQPEmbedding","QAOAEmbedding",# utility ops"Barrier","WireCut","Snapshot",]
[docs]defis_commuting(operation1,operation2):r"""Check if two operations are commuting using a lookup table. A lookup table is used to check the commutation between the controlled, targeted part of operation 1 with the controlled, targeted part of operation 2. .. note:: Most qubit-based PennyLane operations are supported --- CV operations are not supported at this time. Unsupported qubit-based operations include: :class:`~.PauliRot`, :class:`~.QubitDensityMatrix`, :class:`~.CVNeuralNetLayers`, :class:`~.ApproxTimeEvolution`, :class:`~.ArbitraryUnitary`, :class:`~.CommutingEvolution`, :class:`~.DisplacementEmbedding`, :class:`~.SqueezingEmbedding` :class:`~.Exp` Args: operation1 (.Operation): A first quantum operation. operation2 (.Operation): A second quantum operation. Returns: bool: True if the operations commute, False otherwise. **Example** >>> qml.is_commuting(qml.X(0), qml.Z(0)) False """# pylint: disable=too-many-branches# pylint: disable=too-many-return-statementsifoperation1.nameinunsupported_operationsorisinstance(operation1,(qml.operation.CVOperation,qml.operation.Channel)):raiseqml.QuantumFunctionError(f"Operation {operation1.name} not supported.")ifoperation2.nameinunsupported_operationsorisinstance(operation2,(qml.operation.CVOperation,qml.operation.Channel)):raiseqml.QuantumFunctionError(f"Operation {operation2.name} not supported.")ifoperation1.pauli_repisnotNoneandoperation2.pauli_repisnotNone:return_pword_is_commuting(operation1,operation2)# operations are disjointsifnotintersection(operation1.wires,operation2.wires):returnTrue# Simplify the rotations if possiblewithqml.QueuingManager.stop_recording():operation1=qml.simplify(operation1)operation2=qml.simplify(operation2)# Arithmetic non-disjoint operations only contain Pauli words_check_opmath_operations(operation1,operation2)# Operation is in the non commuting listifoperation1.nameinnon_commuting_operationsoroperation2.nameinnon_commuting_operations:returnFalse# Two CRot that cannot be simplifiedifoperation1.name=="CRot"andoperation2.name=="CRot":returncheck_commutation_two_non_simplified_crot(operation1,operation2)if"Identity"in(operation1.name,operation2.name):returnTrue# Check if operations are non simplified rotations and return commutation if it is the case.op_set={"U2","U3","Rot","CRot"}ifoperation1.nameinop_setandoperation2.nameinop_set:returncheck_commutation_two_non_simplified_rotations(operation1,operation2)ctrl_base_1=_get_target_name(operation1)ctrl_base_2=_get_target_name(operation2)op1_control_wires=getattr(operation1,"control_wires",{})op2_control_wires=getattr(operation2,"control_wires",{})target_wires_1=qml.wires.Wires([wforwinoperation1.wiresifwnotinop1_control_wires])target_wires_2=qml.wires.Wires([wforwinoperation2.wiresifwnotinop2_control_wires])ifintersection(target_wires_1,target_wires_2)andnot_commutes(ctrl_base_1,ctrl_base_2):returnFalseifintersection(target_wires_1,op2_control_wires)andnot_commutes("ctrl",ctrl_base_1):returnFalseifintersection(target_wires_2,op1_control_wires)andnot_commutes("ctrl",ctrl_base_2):returnFalsereturnTrue