Source code for pennylane.templates.state_preparations.qrom_state_prep
# 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.r"""Contains the QROMStatePreparation template."""importnumpyasnpimportpennylaneasqmlfrompennylane.operationimportOperationfrompennylane.wiresimportWiresdef_float_to_binary(val,num_bits):r"""Converts a value within the range [0, 1) to its binary representation with a specified precision. Args: val (float): The value to convert to binary. Must be in the range [0, 1). num_bits (int): the number of bits to use for the binary representation Returns: str: The binary representation of the value, with the specified precision. **Example** >>> _float_to_binary(0.5, 3) '100' Expected value as the binary representation of `0.5` is `0.100`. """binary_rep=bin(int(2**(num_bits+1)+2**(num_bits)*val))ifbinary_rep[-num_bits-1]=="1":return"1"*num_bitsreturnbinary_rep[-num_bits:]
[docs]classQROMStatePreparation(Operation):r"""Prepares a quantum state using Quantum Read-Only Memory (QROM). This operation implements the state preparation method described in `arXiv:0208112 <https://arxiv.org/abs/quant-ph/0208112>`_. Args: state_vector (tensor_like): The state vector of length :math:`2^n` to be prepared on :math:`n` wires. wires (Sequence[int]): The wires on which to prepare the state. precision_wires (Sequence[int]): The wires allocated for storing the binary representations of the rotation angles utilized in the template. work_wires (Sequence[int], optional): The work wires used for the QROM operations. Defaults to ``None``. Raises: ValueError: If the length of the input state vector array is not :math:`2^n` where :math:`n` is an integer, or if its norm is not equal to one. **Example** .. code-block:: state_vector = np.array([0.5, -0.5, 0.5, 0.5]) dev = qml.device("default.qubit") wires = qml.registers({"work_wires": 1, "prec_wires": 3, "state_wires": 2}) @qml.qnode(dev) def circuit(): qml.QROMStatePreparation( state_vector, wires["state_wires"], wires["prec_wires"], wires["work_wires"] ) return qml.state() .. code-block:: pycon >>> print(circuit()[:4].real) [ 0.5 -0.5 0.5 0.5] .. seealso:: :class:`~.QROM` .. details:: :title: Usage Details The ``precision_wires`` are used as the target wires in the underlying QROM operations. The number of ``precision_wires`` determines the precision with which the rotation angles of the template are encoded. This means that the binary representation of the angle is truncated up to the :math:`m`-th digit, where :math:`m` is the number of precision wires given. See Eq. 5 in `arXiv:0208112 <https://arxiv.org/abs/quant-ph/0208112>`_ for more details. The ``work_wires`` correspond to auxiliary qubits that can be specified in :class:`~.QROM` to reduce the overall resource requirements on the implementation. """def__init__(self,state_vector,wires,precision_wires,work_wires=None,id=None):# pylint: disable=too-many-argumentsn_amplitudes=qml.math.shape(state_vector)[0]ifn_amplitudes!=2**len(Wires(wires)):raiseValueError(f"State vectors must be of length {2**len(wires)}; vector has length {n_amplitudes}.")norm=qml.math.linalg.norm(state_vector)ifnotqml.math.allclose(norm,1.0,atol=1e-3):raiseValueError(f"Input state vectors must have a norm 1.0, the vector has squared norm {norm}")self.state_vector=state_vectorself.hyperparameters["input_wires"]=qml.wires.Wires(wires)self.hyperparameters["precision_wires"]=qml.wires.Wires(precision_wires)self.hyperparameters["work_wires"]=qml.wires.Wires(()ifwork_wiresisNoneelsework_wires)all_wires=(self.hyperparameters["input_wires"]+self.hyperparameters["precision_wires"]+self.hyperparameters["work_wires"])super().__init__(state_vector,wires=all_wires,id=id)@classmethoddef_primitive_bind_call(cls,*args,**kwargs):returncls._primitive.bind(*args,**kwargs)def_flatten(self):hyperparameters=(("wires",self.hyperparameters["input_wires"]),("precision_wires",self.hyperparameters["precision_wires"]),("work_wires",self.hyperparameters["work_wires"]),)return(self.state_vector,),hyperparameters@classmethoddef_unflatten(cls,data,metadata):hyperparams_dict=dict(metadata)returncls(data[0],**hyperparams_dict)
[docs]@staticmethoddefcompute_decomposition(state_vector,wires,input_wires,precision_wires,work_wires):# pylint: disable=arguments-differ,too-many-positional-argumentsr""" Computes the decomposition operations for the given state vector. Args: state_vector (tensor_like): The state vector to prepare. wires (Sequence[int]): The wires which the operator acts on. input_wires (Sequence[int]): The wires on which to prepare the state. precision_wires (Sequence[int]): The wires allocated for storing the binary representations of the rotation angles utilized in the template. work_wires (Sequence[int]): The wires used as work wires for the QROM operations. Defaults to ``None``. Returns: list: List of decomposition operations. """probs=qml.math.abs(state_vector)**2phases=qml.math.angle(state_vector)%(2*np.pi)eps=1e-15# Small constant to avoid division by zerodecomp_ops=[]num_iterations=int(qml.math.log2(qml.math.shape(probs)[0]))rotation_angles=[2**(-ind-1)forindinrange(len(precision_wires))]foriinrange(num_iterations):probs_aux=qml.math.reshape(probs,[1,-1])# Calculation of the numerator and denominator of the function f(x) (Eq.5 [arXiv:quant-ph/0208112])foritxinrange(i+1):probs_denominator=qml.math.sum(probs_aux,axis=1)probs_aux=qml.math.reshape(probs_aux,[int(2**(itx+1)),-1])probs_numerator=qml.math.sum(probs_aux,axis=1)[::2]# Compute the binary representations of the angles θithetas_binary=[_float_to_binary(2*qml.math.arccos(qml.math.sqrt(probs_numerator[j]/(probs_denominator[j]+eps)))/np.pi,len(precision_wires),)forjinrange(qml.math.shape(probs_numerator)[0])]# Apply the QROM operation to encode the thetas binary representationdecomp_ops.append(qml.QROM(bitstrings=thetas_binary,target_wires=precision_wires,control_wires=input_wires[:i],work_wires=work_wires,clean=False,))# Turn binary representation into proper rotationforind,wireinenumerate(precision_wires):decomp_ops.append(qml.CRY(np.pi*rotation_angles[ind],wires=[wire,wires[i]]))# Clean wires used to store the theta valuesdecomp_ops.append(qml.adjoint(qml.QROM)(bitstrings=thetas_binary,target_wires=precision_wires,control_wires=input_wires[:i],work_wires=work_wires,clean=False,))ifnotqml.math.allclose(phases,0.0):# Compute the binary representations of the phasesthetas_binary=[_float_to_binary(phase/(2*np.pi),len(precision_wires))forphaseinphases]# Apply the QROM operation to encode the thetas binary representationdecomp_ops.append(qml.QROM(bitstrings=thetas_binary,target_wires=precision_wires,control_wires=input_wires,work_wires=work_wires,clean=False,))forind,wireinenumerate(precision_wires):decomp_ops.append(qml.ctrl(qml.GlobalPhase((2*np.pi)*(-rotation_angles[ind]),wires=input_wires[0]),control=wire,))decomp_ops.append(qml.adjoint(qml.QROM)(bitstrings=thetas_binary,target_wires=precision_wires,control_wires=input_wires,work_wires=work_wires,clean=False,))returndecomp_ops