[docs]@custom_type(RNGCONTEXT_T,copyable=False,droppable=False)classRNG:"""Random number generator."""
[docs]@guppy# type: ignore[misc] # Unsupported decorated constructor type; Self argument missing for a non-static method (or an invalid type for self)def__new__(seed:int)->"RNG":"""Create a new random number generator using a seed."""return_new_rng_context(seed).unwrap()
[docs]@guppydefrandom_angle(self:"RNG")->angle:r"""Generate a random angle in the range :math:`[-\pi, \pi)`."""return(2.0*self.random_float()-1.0)*pi
[docs]@guppydefrandom_clifford_angle(self:"RNG")->angle:r"""Generate a random Clifford angle (multiple of :math:`\pi/2`)."""returnself.random_int_bounded(4)*pi/2
[docs]@hugr_op(external_op("DeleteRNGContext",[],ext=QSYSTEM_RANDOM_EXTENSION))@no_type_checkdefdiscard(self:"RNG"@owned)->None:"""Discard the random number generator."""
[docs]@custom_function(RandomIntCompiler())@no_type_checkdefrandom_int(self:"RNG")->int:"""Generate a random 32-bit signed integer."""
[docs]@hugr_op(external_op("RandomFloat",[],ext=QSYSTEM_RANDOM_EXTENSION))@no_type_checkdefrandom_float(self:"RNG")->float:"""Generate a random floating point value in the range [0,1)."""
[docs]@custom_function(RandomIntBoundedCompiler())@no_type_checkdefrandom_int_bounded(self:"RNG",bound:int)->int:"""Generate a random 32-bit integer in the range [0, bound). Args: bound: The upper bound of the range, needs to less than 2^31. """
[docs]@guppy@no_type_checkdefshuffle(self:"RNG",array:array[SHUFFLE_T,SHUFFLE_N])->None:"""Randomly shuffle the elements of a possibly linear array in place. Uses the Fisher-Yates algorithm."""forkinrange(SHUFFLE_N):i=SHUFFLE_N-1-kj=self.random_int_bounded(i+1)# TODO use array swap once lowering implemented# https://github.com/CQCL/guppylang/issues/924ifi!=j:mem_swap(array[i],array[j])
[docs]@guppy.structclassDiscreteDistribution(Generic[DISCRETE_N]):# type: ignore[misc]"""A generic probability distribution over a set of the form {0, 1, ..., N-1}. Objects of this class should be generated using :py:meth:`make_discrete_distribution`. """# The `sums` array represents the cumulative probability distribution. That is,# sums[i] is the probability of drawing a value <= i from the distribution.sums:array[float,DISCRETE_N]# type: ignore[valid-type]
[docs]@guppy@no_type_checkdefsample(self:"DiscreteDistribution[DISCRETE_N]",rng:RNG)->int:"""Return a sample value from the distribution, using the provided :py:class:`RNG`. """x=rng.random_float()# Use binary search to find the least i s.t. sums[i] >= x.i_min=0i_max=DISCRETE_N-1whilei_min<i_max:i=(i_min+i_max)//2ifself.sums[i]>=x:i_max=ielse:i_min=i+1returni_min
[docs]@guppy@no_type_checkdefmake_discrete_distribution(weights:array[float,DISCRETE_N],)->DiscreteDistribution[DISCRETE_N]:"""Construct a discrete probability distribution over the set {0, 1, 2, ... DISCRETE_N - 1}, given as an array of weights which represent probabilities. The weights need not be normalized, but must be non-negative and not all zero."""W=0.0forwinweights.copy():ifw<0.0:panic("Negative weight included in discrete distribution.")W+=wifW==0.0:panic("No positive weights included in discrete distribution.")sums=array(0.0for_inrange(DISCRETE_N))s=0.0foriinrange(DISCRETE_N-1):s+=weights[i]sums[i]=s/Wsums[DISCRETE_N-1]=1.0returnDiscreteDistribution(sums)