diff options
| author | George Steel <george.steel@gmail.com> | 2023-01-08 18:33:02 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-01-08 18:33:02 -0500 |
| commit | fbf57b92d2f37814e0029a1e5b8b413f76697463 (patch) | |
| tree | a5c302b6e093edbca4c20b18ebd9b14dea29da11 /lib/utils/prng.py | |
| parent | a7b2122d91703d72177cf980cecbef8fca3d54ec (diff) | |
| parent | 8d13e09bc41f9cfeaa0cd32cd75fa4da07afc03c (diff) | |
Merge pull request #1918 from inkstitch/george-steel/random-base-satin
Add randomization options to satin columns and rewrite split stitches
Diffstat (limited to 'lib/utils/prng.py')
| -rw-r--r-- | lib/utils/prng.py | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/lib/utils/prng.py b/lib/utils/prng.py new file mode 100644 index 00000000..2ec037c6 --- /dev/null +++ b/lib/utils/prng.py @@ -0,0 +1,58 @@ +from hashlib import blake2s +from math import ceil +from itertools import count, chain +import numpy as np + +# Framework for reproducable pseudo-random number generation. + +# Unlike python's random module (which uses a stateful generator based on global variables), +# a counter-mode PRNG like uniformFloats can be used to generate multiple, independent random streams +# by including an additional parameter before the loop counter. +# This allows different aspects of an embroidery element to not effect each other's rolls, +# making random generation resistant to small edits in the control paths or refactoring. +# Using multiple counters for n-dimentional random streams is also possible and is useful for grid-like structures. + + +def joinArgs(*args): + # Stringifies parameters into a slash-separated string for use in hash keys. + # Idempotent and associative. + return "/".join([str(x) for x in args]) + + +MAX_UNIFORM_INT = 2 ** 32 - 1 + + +def uniformInts(*args): + # Single pseudo-random drawing determined by the joined parameters. + # To get a longer sequence of random numbers, call this loop with a counter as one of the parameters. + # Returns 8 uniformly random uint32. + + s = joinArgs(*args) + # blake2s is python's fastest hash algorithm for small inputs and is designed to be usable as a PRNG. + h = blake2s(s.encode()).hexdigest() + nums = [] + for i in range(0, 64, 8): + nums.append(int(h[i:i+8], 16)) + return np.array(nums) + + +def uniformFloats(*args): + # Single pseudo-random drawing determined by the joined parameters. + # To get a longer sequence of random numbers, call this loop with a counter as one of the parameters. + # Returns an array of 8 floats in the range [0,1] + return uniformInts(*args) / MAX_UNIFORM_INT + + +def nUniformFloats(n: int, *args): + # returns a fixed number (which may exceed 8) of floats in the range [0,1] + seed = joinArgs(*args) + nBlocks = ceil(n/8) + blocks = [uniformFloats(seed, x) for x in range(nBlocks)] + return np.concatenate(blocks)[0:n] + + +def iterUniformFloats(*args): + # returns an infinite sequence of floats in the range [0,1] + seed = joinArgs(*args) + blocks = map(lambda x: list(uniformFloats(seed, x)), count(0)) + return chain.from_iterable(blocks) |
