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
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
@ -29,9 +31,11 @@ class RandomGenerator:
initializes state[N] with a seed
"""
self.state[0] = s & u32(0xffffffff)
self.state[0] = s & u32(0xFFFFFFFF)
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
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
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
j += 1
@ -60,7 +74,13 @@ class RandomGenerator:
k = N - 1
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
if i >= N:
self.state[0] = self.state[N - 1]
@ -75,9 +95,9 @@ class RandomGenerator:
generates a random number on [0,0xffffffff]-interval
"""
MATRIX_A = u32(0x9908b0df)
MATRIX_A = u32(0x9908B0DF)
UPPER_MASK = u32(0x80000000)
LOWER_MASK = u32(0x7fffffff)
LOWER_MASK = u32(0x7FFFFFFF)
mag01 = __array__[u32](2)
mag01[0] = u32(0)
mag01[1] = MATRIX_A
@ -107,10 +127,10 @@ class RandomGenerator:
self.next += 1
# Tempering
y ^= (y >> u32(11))
y ^= (y << u32(7)) & u32(0x9d2c5680)
y ^= (y << u32(15)) & u32(0xefc60000)
y ^= (y >> u32(18))
y ^= y >> u32(11)
y ^= (y << u32(7)) & u32(0x9D2C5680)
y ^= (y << u32(15)) & u32(0xEFC60000)
y ^= y >> u32(18)
return y
@ -132,16 +152,15 @@ class RandomGenerator:
now = u32(self.gettimeofday())
key = __array__[u32](5)
key[0] = u32(now & u32(0xffffffff))
key[0] = u32(now & u32(0xFFFFFFFF))
key[1] = u32(now >> u32(32))
key[2] = u32(_C.seq_pid())
now = u32(_C.seq_time_monotonic())
key[3] = u32(now & u32(0xffffffff))
key[3] = u32(now & u32(0xFFFFFFFF))
key[4] = u32(now >> u32(32))
self.init_by_array(key, len(key))
def seed(self):
"""
Initialize internal state from hashable object.
@ -150,6 +169,7 @@ class RandomGenerator:
"""
self.random_seed_time_pid()
"""
Random number generator base class used by bound module functions.
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()
can cover arbitrarily large ranges.
"""
class Random:
gen: RandomGenerator # comment for another error
@ -222,15 +244,15 @@ class Random:
wordarray = __array__[u32](4)
for i in range(words):
r = int(self.gen.genrand_int32())
if k < 32: r >>= (32 - k)
if k < 32:
r >>= 32 - k
wordarray[i] = u32(r)
k -= 32
return self.from_bytes_big(wordarray.slice(0, words))
def bit_length(self, n: int) -> int:
"""
"""
""" """
len = 0
while n:
len += 1
@ -300,7 +322,7 @@ class Random:
"""
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.
"""
@ -383,7 +405,7 @@ class Random:
# Warning: a few older sources define the gamma distribution in terms
# of alpha > -1.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:
@ -397,7 +419,7 @@ class Random:
while 1:
u1 = self.random()
if not 1e-7 < u1 < .9999999:
if not 1e-7 < u1 < 0.9999999:
continue
u2 = 1.0 - self.random()
v = _log(u1 / (1.0 - u1)) / ainv
@ -556,7 +578,7 @@ class Random:
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.
@ -603,7 +625,13 @@ class Random:
result[i] = population[j]
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.
@ -612,6 +640,7 @@ class Random:
Since weights and cum_weights is assumed to be positive, we will replace None with [-1].
"""
def accumulate(weights: List[int]) -> List[int]:
"""
Calculate cum_weights
@ -632,81 +661,105 @@ class Random:
return [population[int(self.random() * n)] for i in range(k)]
cum_weights = accumulate(weights)
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:
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
hi = n - 1
return [population[_bisect(cum_weights, int(self.random() * total), 0, hi)]
for i in range(k)]
return [
population[_bisect(cum_weights, int(self.random() * total), 0, hi)]
for i in range(k)
]
_gen = RandomGenerator()
_rnd = Random(_gen)
def seed(a: int):
_gen.init_genrand(u32(a))
seed(int(_time()))
def getrandbits(k: int):
return _rnd.getrandbits(k)
def randrange(start: int, stop: Optional[int] = None, step: int = 1):
stopx = start
if stop:
stopx = ~stop
else:
start = 0
return _rnd.randrange(start, stopx, step)
return _rnd.randrange(start, stop, step) if stop else _rnd.randrange(0, start, step)
def randint(a: int, b: int):
return _rnd.randint(a, b)
def 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)
def shuffle(s):
_rnd.shuffle(s)
def sample(population, k: int):
return _rnd.sample(population, k)
def random():
return _rnd.random()
def uniform(a, b):
return _rnd.uniform(a, b)
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):
return _rnd.betavariate(alpha, beta)
def expovariate(lambd: float):
return _rnd.expovariate(lambd)
def gammavariate(alpha: float, beta: float):
return _rnd.gammavariate(alpha, beta)
def gauss(mu: float, sigma: float):
return _rnd.gauss(mu, sigma)
def lognormvariate(mu: float, sigma: float):
return _rnd.lognormvariate(mu, sigma)
def normalvariate(mu: float, sigma: float):
return _rnd.normalvariate(mu, sigma)
def vonmisesvariate(mu: float, kappa: float):
return _rnd.vonmisesvariate(mu, kappa)
def paretovariate(alpha: float):
return _rnd.paretovariate(alpha)
def weibullvariate(alpha: float, beta: float):
return _rnd.weibullvariate(alpha, beta)