1
0
mirror of https://github.com/exaloop/codon.git synced 2025-06-03 15:03:52 +08:00

stdlib/random.codon

This commit is contained in:
Ishak Numanagić 2022-01-24 11:10:09 +01:00
parent 4171d553d9
commit 191da796c7

View File

@ -1,3 +1,5 @@
# (c) 2022 Exaloop Inc. All rights reserved.
import sys import sys
from math import inf as INF, sqrt as _sqrt, acos as _acos, cos as _cos from math import inf as INF, sqrt as _sqrt, acos as _acos, cos as _cos
from math import log as _log, exp as _exp, pi as _pi, e as _e, ceil as _ceil from math import log as _log, exp as _exp, pi as _pi, e as _e, ceil as _ceil
@ -29,9 +31,11 @@ class RandomGenerator:
initializes state[N] with a seed initializes state[N] with a seed
""" """
self.state[0] = s & u32(0xffffffff) self.state[0] = s & u32(0xFFFFFFFF)
for i in range(1, N): for i in range(1, N):
self.state[i] = u32(1812433253) * (self.state[i-1] ^ (self.state[i-1]) >> u32(30)) + u32(i) self.state[i] = u32(1812433253) * (
self.state[i - 1] ^ (self.state[i - 1]) >> u32(30)
) + u32(i)
self.next = N self.next = N
def init_by_array(self, init_key: Array[u32], key_length: int): def init_by_array(self, init_key: Array[u32], key_length: int):
@ -47,7 +51,17 @@ class RandomGenerator:
k = N if N > key_length else key_length k = N if N > key_length else key_length
while k > 0: while k > 0:
self.state[i] = (self.state[i] ^ ((self.state[i-1] ^ (self.state[i-1] >> u32(30))) * u32(1664525))) + init_key[j] + j self.state[i] = (
(
self.state[i]
^ (
(self.state[i - 1] ^ (self.state[i - 1] >> u32(30)))
* u32(1664525)
)
)
+ init_key[j]
+ j
)
i += 1 i += 1
j += 1 j += 1
@ -60,7 +74,13 @@ class RandomGenerator:
k = N - 1 k = N - 1
while k > 0: while k > 0:
self.state[i] = (self.state[i] ^ ((self.state[i-1] ^ (self.state[i-1] >> u32(30))) * u32(1566083941))) - i self.state[i] = (
self.state[i]
^ (
(self.state[i - 1] ^ (self.state[i - 1] >> u32(30)))
* u32(1566083941)
)
) - i
i += 1 i += 1
if i >= N: if i >= N:
self.state[0] = self.state[N - 1] self.state[0] = self.state[N - 1]
@ -75,9 +95,9 @@ class RandomGenerator:
generates a random number on [0,0xffffffff]-interval generates a random number on [0,0xffffffff]-interval
""" """
MATRIX_A = u32(0x9908b0df) MATRIX_A = u32(0x9908B0DF)
UPPER_MASK = u32(0x80000000) UPPER_MASK = u32(0x80000000)
LOWER_MASK = u32(0x7fffffff) LOWER_MASK = u32(0x7FFFFFFF)
mag01 = __array__[u32](2) mag01 = __array__[u32](2)
mag01[0] = u32(0) mag01[0] = u32(0)
mag01[1] = MATRIX_A mag01[1] = MATRIX_A
@ -107,10 +127,10 @@ class RandomGenerator:
self.next += 1 self.next += 1
# Tempering # Tempering
y ^= (y >> u32(11)) y ^= y >> u32(11)
y ^= (y << u32(7)) & u32(0x9d2c5680) y ^= (y << u32(7)) & u32(0x9D2C5680)
y ^= (y << u32(15)) & u32(0xefc60000) y ^= (y << u32(15)) & u32(0xEFC60000)
y ^= (y >> u32(18)) y ^= y >> u32(18)
return y return y
@ -132,16 +152,15 @@ class RandomGenerator:
now = u32(self.gettimeofday()) now = u32(self.gettimeofday())
key = __array__[u32](5) key = __array__[u32](5)
key[0] = u32(now & u32(0xffffffff)) key[0] = u32(now & u32(0xFFFFFFFF))
key[1] = u32(now >> u32(32)) key[1] = u32(now >> u32(32))
key[2] = u32(_C.seq_pid()) key[2] = u32(_C.seq_pid())
now = u32(_C.seq_time_monotonic()) now = u32(_C.seq_time_monotonic())
key[3] = u32(now & u32(0xffffffff)) key[3] = u32(now & u32(0xFFFFFFFF))
key[4] = u32(now >> u32(32)) key[4] = u32(now >> u32(32))
self.init_by_array(key, len(key)) self.init_by_array(key, len(key))
def seed(self): def seed(self):
""" """
Initialize internal state from hashable object. Initialize internal state from hashable object.
@ -150,6 +169,7 @@ class RandomGenerator:
""" """
self.random_seed_time_pid() self.random_seed_time_pid()
""" """
Random number generator base class used by bound module functions. Random number generator base class used by bound module functions.
Used to instantiate instances of Random to get generators that don't Used to instantiate instances of Random to get generators that don't
@ -160,6 +180,8 @@ methods: random(), seed(), getstate(), and setstate().
Optionally, implement a getrandbits() method so that randrange() Optionally, implement a getrandbits() method so that randrange()
can cover arbitrarily large ranges. can cover arbitrarily large ranges.
""" """
class Random: class Random:
gen: RandomGenerator # comment for another error gen: RandomGenerator # comment for another error
@ -222,15 +244,15 @@ class Random:
wordarray = __array__[u32](4) wordarray = __array__[u32](4)
for i in range(words): for i in range(words):
r = int(self.gen.genrand_int32()) r = int(self.gen.genrand_int32())
if k < 32: r >>= (32 - k) if k < 32:
r >>= 32 - k
wordarray[i] = u32(r) wordarray[i] = u32(r)
k -= 32 k -= 32
return self.from_bytes_big(wordarray.slice(0, words)) return self.from_bytes_big(wordarray.slice(0, words))
def bit_length(self, n: int) -> int: def bit_length(self, n: int) -> int:
""" """ """
"""
len = 0 len = 0
while n: while n:
len += 1 len += 1
@ -300,7 +322,7 @@ class Random:
""" """
return self.gen.genrand_res53() return self.gen.genrand_res53()
def choice[T](self, sequence: Generator[T]) -> T: def choice(self, sequence: Generator[T], T: type) -> T:
""" """
Choose a random element from a non-empty sequence. Choose a random element from a non-empty sequence.
""" """
@ -383,7 +405,7 @@ class Random:
# Warning: a few older sources define the gamma distribution in terms # Warning: a few older sources define the gamma distribution in terms
# of alpha > -1.0 # of alpha > -1.0
if alpha <= 0.0 or beta <= 0.0: if alpha <= 0.0 or beta <= 0.0:
raise ValueError('gammavariate: alpha and beta must be > 0.0') raise ValueError("gammavariate: alpha and beta must be > 0.0")
if alpha > 1.0: if alpha > 1.0:
@ -397,7 +419,7 @@ class Random:
while 1: while 1:
u1 = self.random() u1 = self.random()
if not 1e-7 < u1 < .9999999: if not 1e-7 < u1 < 0.9999999:
continue continue
u2 = 1.0 - self.random() u2 = 1.0 - self.random()
v = _log(u1 / (1.0 - u1)) / ainv v = _log(u1 / (1.0 - u1)) / ainv
@ -556,7 +578,7 @@ class Random:
return theta return theta
def sample[T](self, population: List[T], k: int): def sample(self, population: List[T], k: int, T: type):
""" """
Chooses k unique random elements from a population sequence or set. Chooses k unique random elements from a population sequence or set.
@ -603,7 +625,13 @@ class Random:
result[i] = population[j] result[i] = population[j]
return result return result
def choices(self, population, weights: Optional[List[int]], cum_weights: Optional[List[int]], k: int): def choices(
self,
population,
weights: Optional[List[int]],
cum_weights: Optional[List[int]],
k: int,
):
""" """
Return a k sized list of population elements chosen with replacement. Return a k sized list of population elements chosen with replacement.
@ -612,6 +640,7 @@ class Random:
Since weights and cum_weights is assumed to be positive, we will replace None with [-1]. Since weights and cum_weights is assumed to be positive, we will replace None with [-1].
""" """
def accumulate(weights: List[int]) -> List[int]: def accumulate(weights: List[int]) -> List[int]:
""" """
Calculate cum_weights Calculate cum_weights
@ -632,81 +661,105 @@ class Random:
return [population[int(self.random() * n)] for i in range(k)] return [population[int(self.random() * n)] for i in range(k)]
cum_weights = accumulate(weights) cum_weights = accumulate(weights)
elif weights is not None: elif weights is not None:
raise TypeError('Cannot specify both weights and cumulative weights') raise TypeError("Cannot specify both weights and cumulative weights")
if len(cum_weights) != n: if len(cum_weights) != n:
raise ValueError('The number of weights does not match the population') raise ValueError("The number of weights does not match the population")
total = float(cum_weights[-1]) # convert to float total = float(cum_weights[-1]) # convert to float
hi = n - 1 hi = n - 1
return [population[_bisect(cum_weights, int(self.random() * total), 0, hi)] return [
for i in range(k)] population[_bisect(cum_weights, int(self.random() * total), 0, hi)]
for i in range(k)
]
_gen = RandomGenerator() _gen = RandomGenerator()
_rnd = Random(_gen) _rnd = Random(_gen)
def seed(a: int): def seed(a: int):
_gen.init_genrand(u32(a)) _gen.init_genrand(u32(a))
seed(int(_time())) seed(int(_time()))
def getrandbits(k: int): def getrandbits(k: int):
return _rnd.getrandbits(k) return _rnd.getrandbits(k)
def randrange(start: int, stop: Optional[int] = None, step: int = 1): def randrange(start: int, stop: Optional[int] = None, step: int = 1):
stopx = start return _rnd.randrange(start, stop, step) if stop else _rnd.randrange(0, start, step)
if stop:
stopx = ~stop
else:
start = 0
return _rnd.randrange(start, stopx, step)
def randint(a: int, b: int): def randint(a: int, b: int):
return _rnd.randint(a, b) return _rnd.randint(a, b)
def choice(s): def choice(s):
return _rnd.choice(s) return _rnd.choice(s)
def choices(population, weights: Optional[List[int]] = None, cum_weights: Optional[List[int]] = None, k: int = 1):
def choices(
population,
weights: Optional[List[int]] = None,
cum_weights: Optional[List[int]] = None,
k: int = 1,
):
return _rnd.choices(population, weights, cum_weights, k) return _rnd.choices(population, weights, cum_weights, k)
def shuffle(s): def shuffle(s):
_rnd.shuffle(s) _rnd.shuffle(s)
def sample(population, k: int): def sample(population, k: int):
return _rnd.sample(population, k) return _rnd.sample(population, k)
def random(): def random():
return _rnd.random() return _rnd.random()
def uniform(a, b): def uniform(a, b):
return _rnd.uniform(a, b) return _rnd.uniform(a, b)
def triangular(low: float = 0.0, high: float = 1.0, mode: Optional[float] = None): def triangular(low: float = 0.0, high: float = 1.0, mode: Optional[float] = None):
return _rnd.triangular(low, high, ~mode if mode else (low + high)/2) return _rnd.triangular(low, high, mode if mode is not None else (low + high) / 2)
def betavariate(alpha: float, beta: float): def betavariate(alpha: float, beta: float):
return _rnd.betavariate(alpha, beta) return _rnd.betavariate(alpha, beta)
def expovariate(lambd: float): def expovariate(lambd: float):
return _rnd.expovariate(lambd) return _rnd.expovariate(lambd)
def gammavariate(alpha: float, beta: float): def gammavariate(alpha: float, beta: float):
return _rnd.gammavariate(alpha, beta) return _rnd.gammavariate(alpha, beta)
def gauss(mu: float, sigma: float): def gauss(mu: float, sigma: float):
return _rnd.gauss(mu, sigma) return _rnd.gauss(mu, sigma)
def lognormvariate(mu: float, sigma: float): def lognormvariate(mu: float, sigma: float):
return _rnd.lognormvariate(mu, sigma) return _rnd.lognormvariate(mu, sigma)
def normalvariate(mu: float, sigma: float): def normalvariate(mu: float, sigma: float):
return _rnd.normalvariate(mu, sigma) return _rnd.normalvariate(mu, sigma)
def vonmisesvariate(mu: float, kappa: float): def vonmisesvariate(mu: float, kappa: float):
return _rnd.vonmisesvariate(mu, kappa) return _rnd.vonmisesvariate(mu, kappa)
def paretovariate(alpha: float): def paretovariate(alpha: float):
return _rnd.paretovariate(alpha) return _rnd.paretovariate(alpha)
def weibullvariate(alpha: float, beta: float): def weibullvariate(alpha: float, beta: float):
return _rnd.weibullvariate(alpha, beta) return _rnd.weibullvariate(alpha, beta)