Source code for pennylane.templates.subroutines.qrom
# Copyright 2018-2025 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 submodule contains the template for QROM."""importmathimportnumpyasnpimportpennylaneasqmlfrompennylane.operationimportOperationfrompennylane.wiresimportWiresLikedef_multi_swap(wires1,wires2):"""Apply a series of SWAP gates between two sets of wires."""forwire1,wire2inzip(wires1,wires2):qml.SWAP(wires=[wire1,wire2])
[docs]classQROM(Operation):r"""Applies the QROM operator. This operator encodes bitstrings associated with indexes: .. math:: \text{QROM}|i\rangle|0\rangle = |i\rangle |b_i\rangle, where :math:`b_i` is the bitstring associated with index :math:`i`. Args: bitstrings (list[str]): the bitstrings to be encoded control_wires (Sequence[int]): the wires where the indexes are specified target_wires (Sequence[int]): the wires where the bitstring is loaded work_wires (Sequence[int]): the auxiliary wires used for the computation clean (bool): if True, the work wires are not altered by operator, default is ``True`` **Example** In this example, the QROM operator is applied to encode the third bitstring, associated with index 2, in the target wires. .. code-block:: # a list of bitstrings is defined bitstrings = ["010", "111", "110", "000"] dev = qml.device("default.qubit", shots = 1) @qml.qnode(dev) def circuit(): # the third index is encoded in the control wires [0, 1] qml.BasisEmbedding(2, wires = [0,1]) qml.QROM(bitstrings = bitstrings, control_wires = [0,1], target_wires = [2,3,4], work_wires = [5,6,7]) return qml.sample(wires = [2,3,4]) .. code-block:: pycon >>> print(circuit()) [1 1 0] .. details:: :title: Usage Details This template takes as input three different sets of wires. The first one is ``control_wires`` which is used to encode the desired index. Therefore, if we have :math:`m` bitstrings, we need at least :math:`\lceil \log_2(m)\rceil` control wires. The second set of wires is ``target_wires`` which stores the bitstrings. For instance, if the bitstring is "0110", we will need four target wires. Internally, the bitstrings are encoded using the :class:`~.BasisEmbedding` template. The ``work_wires`` are the auxiliary qubits used by the template to reduce the number of gates required. Let :math:`k` be the number of work wires. If :math:`k = 0`, the template is equivalent to executing :class:`~.Select`. Following the idea in [`arXiv:1812.00954 <https://arxiv.org/abs/1812.00954>`__], auxiliary qubits can be used to load more than one bitstring in parallel . Let :math:`\lambda` be the number of bitstrings we want to store in parallel, assumed to be a power of :math:`2`. Then, :math:`k = l \cdot (\lambda-1)` work wires are needed, where :math:`l` is the length of the bitstrings. The QROM template has two variants. The first one (``clean = False``) is based on [`arXiv:1812.00954 <https://arxiv.org/abs/1812.00954>`__] that alternates the state in the ``work_wires``. The second one (``clean = True``), based on [`arXiv:1902.02134 <https://arxiv.org/abs/1902.02134>`__], solves that issue by returning ``work_wires`` to their initial state. This technique can be applied when the ``work_wires`` are not initialized to zero. """def__init__(self,bitstrings,control_wires:WiresLike,target_wires:WiresLike,work_wires:WiresLike,clean=True,id=None,):# pylint: disable=too-many-argumentscontrol_wires=qml.wires.Wires(control_wires)target_wires=qml.wires.Wires(target_wires)work_wires=qml.wires.Wires(()ifwork_wiresisNoneelsework_wires)self.hyperparameters["bitstrings"]=tuple(bitstrings)self.hyperparameters["control_wires"]=control_wiresself.hyperparameters["target_wires"]=target_wiresself.hyperparameters["work_wires"]=work_wiresself.hyperparameters["clean"]=cleaniflen(work_wires)!=0:ifany(wireinwork_wiresforwireincontrol_wires):raiseValueError("Control wires should be different from work wires.")ifany(wireinwork_wiresforwireintarget_wires):raiseValueError("Target wires should be different from work wires.")ifany(wireincontrol_wiresforwireintarget_wires):raiseValueError("Target wires should be different from control wires.")if2**len(control_wires)<len(bitstrings):raiseValueError(f"Not enough control wires ({len(control_wires)}) for the desired number of "+f"bitstrings ({len(bitstrings)}). At least {int(math.ceil(math.log2(len(bitstrings))))} control "+"wires are required.")iflen(bitstrings[0])!=len(target_wires):raiseValueError("Bitstring length must match the number of target wires.")all_wires=target_wires+control_wires+work_wiressuper().__init__(wires=all_wires,id=id)def_flatten(self):metadata=tuple((key,value)forkey,valueinself.hyperparameters.items())returntuple(),metadata@classmethoddef_unflatten(cls,data,metadata):hyperparams_dict=dict(metadata)returncls(**hyperparams_dict)def__repr__(self):returnf"QROM(control_wires={self.control_wires}, target_wires={self.target_wires}, work_wires={self.work_wires}, clean={self.clean})"
def__copy__(self):"""Copy this op"""cls=self.__class__copied_op=cls.__new__(cls)forattr,valueinvars(self).items():setattr(copied_op,attr,value)returncopied_op
[docs]@staticmethoddefcompute_decomposition(bitstrings,control_wires,target_wires,work_wires,clean):# pylint: disable=arguments-differiflen(control_wires)==0:return[qml.BasisEmbedding(int(bits,2),wires=target_wires)forbitsinbitstrings]withqml.QueuingManager.stop_recording():swap_wires=target_wires+work_wires# number of operators we store per column (power of 2)depth=len(swap_wires)//len(target_wires)depth=int(2**np.floor(np.log2(depth)))depth=min(depth,len(bitstrings))ops=[qml.BasisEmbedding(int(bits,2),wires=target_wires)forbitsinbitstrings]ops_identity=ops+[qml.I(target_wires)]*int(2**len(control_wires)-len(ops))n_columns=len(ops)//depthiflen(ops)%depth==0elselen(ops)//depth+1new_ops=[]foriinrange(n_columns):column_ops=[]forjinrange(depth):dic_map={ops_identity[i*depth+j].wires[l]:swap_wires[j*len(target_wires)+l]forlinrange(len(target_wires))}column_ops.append(qml.map_wires(ops_identity[i*depth+j],dic_map))new_ops.append(qml.prod(*column_ops))# Select blockn_control_select_wires=int(math.ceil(math.log2(2**len(control_wires)/depth)))control_select_wires=control_wires[:n_control_select_wires]select_ops=[]ifcontrol_select_wires:select_ops+=[qml.Select(new_ops,control=control_select_wires)]else:select_ops=new_ops# Swap blockcontrol_swap_wires=control_wires[n_control_select_wires:]swap_ops=[]forindinrange(len(control_swap_wires)):forjinrange(2**ind):new_op=qml.prod(_multi_swap)(swap_wires[(j)*len(target_wires):(j+1)*len(target_wires)],swap_wires[(j+2**ind)*len(target_wires):(j+2**(ind+1))*len(target_wires)],)swap_ops.insert(0,qml.ctrl(new_op,control=control_swap_wires[-ind-1]))ifnotcleanordepth==1:# Based on this paper (Fig 1.c): https://arxiv.org/abs/1812.00954decomp_ops=select_ops+swap_opselse:# Based on this paper (Fig 4): https://arxiv.org/abs/1902.02134adjoint_swap_ops=swap_ops[::-1]hadamard_ops=[qml.Hadamard(wires=w)forwintarget_wires]decomp_ops=2*(hadamard_ops+adjoint_swap_ops+select_ops+swap_ops)ifqml.QueuingManager.recording():foropindecomp_ops:qml.apply(op)returndecomp_ops
@classmethoddef_primitive_bind_call(cls,*args,**kwargs):returncls._primitive.bind(*args,**kwargs)@propertydefbitstrings(self):"""bitstrings to be added."""returnself.hyperparameters["bitstrings"]@propertydefcontrol_wires(self):"""The control wires."""returnself.hyperparameters["control_wires"]@propertydeftarget_wires(self):"""The wires where the bitstring is loaded."""returnself.hyperparameters["target_wires"]@propertydefwork_wires(self):"""The wires where the index is specified."""returnself.hyperparameters["work_wires"]@propertydefwires(self):"""All wires involved in the operation."""return(self.hyperparameters["control_wires"]+self.hyperparameters["target_wires"]+self.hyperparameters["work_wires"])@propertydefclean(self):"""Boolean to select the version of QROM."""returnself.hyperparameters["clean"]