Source code for pennylane.templates.subroutines.select
# 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."""Contains the Select template."""# pylint: disable=too-many-argumentsimportcopyimportitertoolsimportpennylaneasqmlfrompennylaneimportmathfrompennylane.operationimportOperation
[docs]classSelect(Operation):r"""Applies specific input operations depending on the state of the designated control qubits. .. math:: Select|i\rangle \otimes |\psi\rangle = |i\rangle \otimes U_i |\psi\rangle .. figure:: ../../../doc/_static/templates/subroutines/select.png :align: center :width: 60% :target: javascript:void(0); Args: ops (list[Operator]): operations to apply control (Sequence[int]): the wires controlling which operation is applied id (str or None): String representing the operation (optional) .. note:: The position of the operation in the list determines which qubit state implements that operation. For example, when the qubit register is in the state :math:`|00\rangle`, we will apply ``ops[0]``. When the qubit register is in the state :math:`|10\rangle`, we will apply ``ops[2]``. To obtain the binary bitstring representing the state for list position ``index`` we can use the following relationship: ``index = int(state_string, 2)``. For example, ``2 = int('10', 2)``. **Example** >>> dev = qml.device('default.qubit', wires=4) >>> ops = [qml.X(2), qml.X(3), qml.Y(2), qml.SWAP([2,3])] >>> @qml.qnode(dev) >>> def circuit(): >>> qml.Select(ops, control=[0,1]) >>> return qml.state() ... >>> print(qml.draw(circuit, level='device')()) 0: ─╭○─╭○─╭●─╭●────┤ State 1: ─├○─├●─├○─├●────┤ State 2: ─╰X─│──╰Y─├SWAP─┤ State 3: ────╰X────╰SWAP─┤ State """num_wires=qml.operation.AnyWiresdef_flatten(self):return(self.ops),(self.control)@classmethoddef_primitive_bind_call(cls,*args,**kwargs):returncls._primitive.bind(*args,**kwargs)@classmethoddef_unflatten(cls,data,metadata)->"Select":returncls(data,metadata)def__repr__(self):returnf"Select(ops={self.ops}, control={self.control})"def__init__(self,ops,control,id=None):control=qml.wires.Wires(control)self.hyperparameters["ops"]=tuple(ops)self.hyperparameters["control"]=controlif2**len(control)<len(ops):raiseValueError(f"Not enough control wires ({len(control)}) for the desired number of "+f"operations ({len(ops)}). At least {int(math.ceil(math.log2(len(ops))))} control "+"wires required.")ifany(control_wireinqml.wires.Wires.all_wires([op.wiresforopinops])forcontrol_wireincontrol):raiseValueError("Control wires should be different from operation wires.")foropinops:qml.QueuingManager.remove(op)target_wires=qml.wires.Wires.all_wires([op.wiresforopinops])self.hyperparameters["target_wires"]=target_wiresall_wires=target_wires+controlsuper().__init__(*self.data,wires=all_wires,id=id)
def__copy__(self):"""Copy this op"""cls=self.__class__copied_op=cls.__new__(cls)new_data=copy.copy(self.data)forattr,valueinvars(self).items():ifattr!="data":setattr(copied_op,attr,value)copied_op.data=new_datareturncopied_op@propertydefdata(self):"""Create data property"""returntuple(dforopinself.opsfordinop.data)@data.setterdefdata(self,new_data):"""Set the data property"""foropinself.ops:op_num_params=op.num_paramsifop_num_params>0:op.data=new_data[:op_num_params]new_data=new_data[op_num_params:]
[docs]defdecomposition(self):r"""Representation of the operator as a product of other operators. .. math:: O = O_1 O_2 \dots O_n A ``DecompositionUndefinedError`` is raised if no representation by decomposition is defined. .. seealso:: :meth:`~.Operator.compute_decomposition`. Returns: list[Operator]: decomposition of the operator **Example** >>> ops = [qml.X(2), qml.X(3), qml.Y(2), qml.SWAP([2,3])] >>> op = qml.Select(ops, control=[0,1]) >>> op.decomposition() [MultiControlledX(wires=[0, 1, 2], control_values=[0, 0]), MultiControlledX(wires=[0, 1, 3], control_values=[0, 1]), Controlled(Y(2), control_wires=[0, 1], control_values=[True, False]), Controlled(SWAP(wires=[2, 3]), control_wires=[0, 1])] """returnself.compute_decomposition(self.ops,control=self.control)
[docs]@staticmethoddefcompute_decomposition(ops,control,):# pylint: disable=arguments-differ, unused-argumentr"""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: ops (list[Operator]): operations to apply control (Sequence[int]): the wires controlling which operation is applied Returns: list[Operator]: decomposition of the operator **Example** >>> ops = [qml.X(2), qml.X(3), qml.Y(2), qml.SWAP([2,3])] >>> qml.Select.compute_decomposition(ops, control=[0,1]) [MultiControlledX(wires=[0, 1, 2], control_values=[0, 0]), MultiControlledX(wires=[0, 1, 3], control_values=[0, 1), Controlled(Y(2), control_wires=[0, 1], control_values=[True, False]), Controlled(SWAP(wires=[2, 3]), control_wires=[0, 1])] """states=list(itertools.product([0,1],repeat=len(control)))decomp_ops=[qml.ctrl(op,control,control_values=states[index])forindex,opinenumerate(ops)]returndecomp_ops
@propertydefops(self):"""Operations to be applied."""returnself.hyperparameters["ops"]@propertydefcontrol(self):"""The control wires."""returnself.hyperparameters["control"]@propertydeftarget_wires(self):"""The wires of the input operators."""returnself.hyperparameters["target_wires"]@propertydefwires(self):"""All wires involved in the operation."""returnself.hyperparameters["control"]+self.hyperparameters["target_wires"]