# 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."""This module contains the qml.probs measurement."""fromcollections.abcimportSequencefromtypingimportOptionalimportnumpyasnpimportpennylaneasqmlfrompennylane.typingimportTensorLikefrompennylane.wiresimportWiresfrom.measurementsimportProbability,SampleMeasurement,StateMeasurementfrom.mid_measureimportMeasurementValue
[docs]defprobs(wires=None,op=None)->"ProbabilityMP":r"""Probability of each computational basis state. This measurement function accepts either a wire specification or an observable. Passing wires to the function instructs the QNode to return a flat array containing the probabilities :math:`|\langle i | \psi \rangle |^2` of measuring the computational basis state :math:`| i \rangle` given the current state :math:`| \psi \rangle`. Marginal probabilities may also be requested by restricting the wires to a subset of the full system; the size of the returned array will be ``[2**len(wires)]``. .. Note:: If no wires or observable are given, the probability of all wires is returned. Args: wires (Sequence[int] or int): the wire the operation acts on op (Observable or MeasurementValue or Sequence[MeasurementValue]): Observable (with a ``diagonalizing_gates`` attribute) that rotates the computational basis, or a ``MeasurementValue`` corresponding to mid-circuit measurements. Returns: ProbabilityMP: Measurement process instance **Example:** .. code-block:: python3 dev = qml.device("default.qubit", wires=2) @qml.qnode(dev) def circuit(): qml.Hadamard(wires=1) return qml.probs(wires=[0, 1]) Executing this QNode: >>> circuit() array([0.5, 0.5, 0. , 0. ]) The returned array is in lexicographic order, so corresponds to a :math:`50\%` chance of measuring either :math:`|00\rangle` or :math:`|01\rangle`. .. code-block:: python3 dev = qml.device("default.qubit", wires=2) H = 1 / np.sqrt(2) * np.array([[1, 1], [1, -1]]) @qml.qnode(dev) def circuit(): qml.Z(0) qml.X(1) return qml.probs(op=qml.Hermitian(H, wires=0)) >>> circuit() array([0.14644661 0.85355339]) The returned array is in lexicographic order, so corresponds to a :math:`14.6\%` chance of measuring the rotated :math:`|0\rangle` state and :math:`85.4\%` of measuring the rotated :math:`|1\rangle` state. Note that the output shape of this measurement process depends on whether the device simulates qubit or continuous variable quantum systems. """ifisinstance(op,MeasurementValue):iflen(op.measurements)>1:raiseValueError("Cannot use qml.probs() when measuring multiple mid-circuit measurements collected ""using arithmetic operators. To collect probabilities for multiple mid-circuit ""measurements, use a list of mid-circuit measurements with qml.probs().")returnProbabilityMP(obs=op)ifisinstance(op,Sequence):ifnotqml.math.is_abstract(op[0])andnotall(isinstance(o,MeasurementValue)andlen(o.measurements)==1foroinop):raiseqml.QuantumFunctionError("Only sequences of single MeasurementValues can be passed with the op argument. ""MeasurementValues manipulated using arithmetic operators cannot be used when ""collecting statistics for a sequence of mid-circuit measurements.")returnProbabilityMP(obs=op)ifisinstance(op,qml.ops.LinearCombination):raiseqml.QuantumFunctionError("Hamiltonians are not supported for rotating probabilities.")ifopisnotNoneandnotqml.math.is_abstract(op)andnotop.has_diagonalizing_gates:raiseqml.QuantumFunctionError(f"{op} does not define diagonalizing gates : cannot be used to rotate the probability")ifwiresisnotNone:ifopisnotNone:raiseqml.QuantumFunctionError("Cannot specify the wires to probs if an observable is ""provided. The wires for probs will be determined directly from the observable.")wires=Wires(wires)returnProbabilityMP(obs=op,wires=wires)
[docs]classProbabilityMP(SampleMeasurement,StateMeasurement):"""Measurement process that computes the probability of each computational basis state. Please refer to :func:`pennylane.probs` for detailed documentation. Args: obs (Union[.Operator, .MeasurementValue]): The observable that is to be measured as part of the measurement process. Not all measurement processes require observables (for example ``Probability``); this argument is optional. wires (.Wires): The wires the measurement process applies to. This can only be specified if an observable was not provided. eigvals (array): A flat array representing the eigenvalues of the measurement. This can only be specified if an observable was not provided. id (str): custom label given to a measurement instance, can be useful for some applications where the instance has to be identified """_shortname=Probability#! Note: deprecated. Change the value to "probs" in v0.42@classmethoddef_abstract_eval(cls,n_wires=None,has_eigvals=False,shots=None,num_device_wires=0):n_wires=num_device_wiresifn_wires==0elsen_wiresshape=(2**n_wires,)returnshape,float@propertydefnumeric_type(self):returnfloat
[docs]defprocess_samples(self,samples:Sequence[complex],wire_order:Wires,shot_range:Optional[tuple[int,...]]=None,bin_size:Optional[int]=None,):wire_map=dict(zip(wire_order,range(len(wire_order))))mapped_wires=[wire_map[w]forwinself.wires]ifshot_rangeisnotNone:# Indexing corresponds to: (potential broadcasting, shots, wires). Note that the last# colon (:) is required because shots is the second-to-last axis and the# Ellipsis (...) otherwise would take up broadcasting and shots axes.samples=samples[...,slice(*shot_range),:]ifmapped_wires:# if wires are provided, then we only return samples from those wiressamples=samples[...,mapped_wires]num_wires=qml.math.shape(samples)[-1]# convert samples from a list of 0, 1 integers, to base 10 representationpowers_of_two=2**qml.math.arange(num_wires)[::-1]indices=samples@powers_of_two# `samples` typically has two axes ((shots, wires)) but can also have three with# broadcasting ((batch_size, shots, wires)) so that we simply read out the batch_size.batch_size=samples.shape[0]ifqml.math.ndim(samples)==3elseNonedim=2**num_wires# count the basis state occurrences, and construct the probability vectornew_bin_size=bin_sizeorsamples.shape[-2]new_shape=(-1,new_bin_size)ifbatch_sizeisNoneelse(batch_size,-1,new_bin_size)indices=indices.reshape(new_shape)prob=self._count_samples(indices,batch_size,dim)returnqml.math.squeeze(prob)ifbin_sizeisNoneelseprob
[docs]defprocess_state(self,state:Sequence[complex],wire_order:Wires):prob=qml.math.real(state)**2+qml.math.imag(state)**2ifself.wires==Wires([]):# no need to marginalizereturnprob# determine which subsystems are to be summed overinactive_wires=Wires.unique_wires([wire_order,self.wires])# translate to wire labels used by devicewire_map=dict(zip(wire_order,range(len(wire_order))))mapped_wires=[wire_map[w]forwinself.wires]inactive_wires=[wire_map[w]forwininactive_wires]# reshape the probability so that each axis corresponds to a wirenum_device_wires=len(wire_order)shape=[2]*num_device_wiresdesired_axes=np.argsort(np.argsort(mapped_wires))flat_shape=(-1,)expected_size=2**num_device_wiresbatch_size=qml.math.get_batch_size(prob,(expected_size,),expected_size)ifbatch_sizeisnotNone:# prob now is reshaped to have self.num_wires+1 axes in the case of broadcastingshape.insert(0,batch_size)inactive_wires=[idx+1foridxininactive_wires]desired_axes=np.insert(desired_axes+1,0,0)flat_shape=(batch_size,-1)prob=qml.math.reshape(prob,shape)# sum over all inactive wiresprob=qml.math.sum(prob,axis=tuple(inactive_wires))# rearrange wires if necessaryprob=qml.math.transpose(prob,desired_axes)# flatten and return probabilitiesreturnqml.math.reshape(prob,flat_shape)
[docs]defprocess_counts(self,counts:dict,wire_order:Wires)->np.ndarray:withqml.QueuingManager.stop_recording():helper_counts=qml.counts(wires=self.wires,all_outcomes=False)mapped_counts=helper_counts.process_counts(counts,wire_order)num_shots=sum(mapped_counts.values())num_wires=len(next(iter(mapped_counts)))dim=2**num_wires# constructs the probability vector# converts outcomes from binary strings to integers (base 10 representation)prob_vector=qml.math.zeros((dim),dtype="float64")foroutcome,occurrenceinmapped_counts.items():prob_vector[int(outcome,base=2)]=occurrence/num_shotsreturnprob_vector
[docs]defprocess_density_matrix(self,density_matrix:TensorLike,wire_order:Wires):iflen(np.shape(density_matrix))==2:prob=qml.math.diagonal(density_matrix)else:prob=qml.math.array([qml.math.diagonal(density_matrix[i])foriinrange(np.shape(density_matrix)[0])])# Since we only care about the probabilities, we can simplify the task here by creating a 'pseudo-state' to carry the diagonal elements and reuse the process_state methodprob=qml.math.convert_like(prob,density_matrix)p_state=qml.math.sqrt(prob)returnself.process_state(p_state,wire_order)
@staticmethoddef_count_samples(indices,batch_size,dim):"""Count the occurrences of sampled indices and convert them to relative counts in order to estimate their occurrence probability."""num_bins,bin_size=indices.shape[-2:]interface=qml.math.get_deep_interface(indices)ifqml.math.is_abstract(indices):def_count_samples_core(indices,dim,interface):returnqml.math.array([[qml.math.sum(idx==p)foridxinindices]forpinrange(dim)],like=interface,)else:def_count_samples_core(indices,dim,*_):probabilities=qml.math.zeros((dim,num_bins),dtype="float64")forb,idxinenumerate(indices):basis_states,counts=qml.math.unique(idx,return_counts=True)probabilities[basis_states,b]=countsreturnprobabilitiesifbatch_sizeisNone:return_count_samples_core(indices,dim,interface)/bin_size# count the basis state occurrences, and construct the probability vector# for each bin and broadcasting indexindices=indices.reshape((batch_size,num_bins,bin_size))probabilities=qml.math.array([_count_samples_core(_indices,dim,interface)for_indicesinindices],like=interface,)returnprobabilities/bin_size