Skip to content

Commit

Permalink
misc fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
jettjaniak committed Feb 5, 2024
1 parent 43d689a commit 49bc620
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 64 deletions.
41 changes: 0 additions & 41 deletions src/delphi/train/permute.py

This file was deleted.

39 changes: 39 additions & 0 deletions src/delphi/train/shuffle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
class RNG:
"""Random Number Generator
Linear Congruential Generator equivalent to minstd_rand in C++11
https://en.cppreference.com/w/cpp/numeric/random
"""

a = 48271
m = 2147483647 # 2^31 - 1

def __init__(self, seed: int):
assert 0 <= seed < self.m
self.state = seed

def __call__(self):
self.state = (self.state * self.a) % self.m
return self.state


def shuffle_list(in_out: list, seed: int):
"""Deterministically shuffle a list in-place
Implements Fisher-Yates shuffle with LCG as randomness source
https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
"""
rng = RNG(seed)
n = len(in_out)
for i in range(n - 1, 0, -1):
j = rng() % (i + 1)
in_out[i], in_out[j] = in_out[j], in_out[i]


def shuffle_epoch(samples: list, seed: int, epoch_nr: int):
"""Shuffle the samples in-place for a given training epoch"""
rng = RNG(10_000 + seed)
for _ in range(epoch_nr):
rng()
shuffle_seed = rng()
shuffle_list(samples, shuffle_seed)
23 changes: 0 additions & 23 deletions tests/train/test_permutations.py

This file was deleted.

51 changes: 51 additions & 0 deletions tests/train/test_shuffle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import random

import pytest

from delphi.train.shuffle import RNG, shuffle_epoch, shuffle_list


def test_rng():
"""
Compare to the following C++ code:
#include <iostream>
#include <random>
int main() {
unsigned int seed = 12345;
std::minstd_rand generator(seed);
for (int i = 0; i < 5; i++)
std::cout << generator() << ", ";
}
"""
rng = RNG(12345)
expected = [595905495, 1558181227, 1498755989, 2021244883, 887213142]
for val in expected:
assert rng() == val


@pytest.mark.parametrize(
"input_list, seed",
[(random.sample(range(100), 10), random.randint(1, 1000)) for _ in range(5)],
)
def test_shuffle_list(input_list, seed):
original_list = input_list.copy()
shuffle_list(input_list, seed)
assert sorted(input_list) == sorted(original_list)


@pytest.mark.parametrize(
"seed, epoch_nr, expected",
[
(1, 1, [2, 5, 1, 3, 4]),
(2, 5, [2, 1, 4, 5, 3]),
(3, 10, [1, 4, 3, 5, 2]),
(4, 100, [3, 4, 5, 1, 2]),
],
)
def test_shuffle_epoch(seed, epoch_nr, expected):
samples = [1, 2, 3, 4, 5]
shuffle_epoch(samples, seed, epoch_nr)
assert samples == expected

0 comments on commit 49bc620

Please sign in to comment.