Skip to content

Commit e02708a

Browse files
Aaron Meyerclaude
andcommitted
Seed all random lineage generation in tests to fix flaky CI failures
Several tests defined a seeded rng but never passed it into LineageTree.rand_init, Analyze_list, or hide_observation, so those calls fell back to global unseeded randomness. Occasionally this produced degenerate data (e.g. all cells eliminated from an estimator, or optimizer non-convergence on badly conditioned draws) causing sporadic CI failures. Thread the seeded rng through consistently, and add an rng parameter to crossval/hide_observation so tests can do the same. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PVLX72yJpEABz61TPrJ9Ea
1 parent 2eb08ad commit e02708a

5 files changed

Lines changed: 12 additions & 8 deletions

File tree

‎lineage/crossval.py‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,35 @@
1212
exe = ProcessPoolExecutor()
1313

1414

15-
def hide_observation(lineages: list, percentage: float) -> list:
15+
def hide_observation(lineages: list, percentage: float, rng=None) -> list:
1616
"""Taking a list of lineages and the percentage of cells want to be masked, it marks those x% negative."""
17+
rng = np.random.default_rng(rng)
1718
new_lineages = deepcopy(lineages)
1819
for new_lineage in new_lineages:
1920
for cell in new_lineage.output_lineage:
20-
if bernoulli.rvs(p=percentage, size=1):
21+
if bernoulli.rvs(p=percentage, size=1, random_state=rng):
2122
# negate the cell observations to mask them
2223
cell.obs = [-1 * o for o in cell.obs]
2324
new_lineage.obs = np.array([cell.obs for cell in new_lineage.output_lineage])
2425

2526
return new_lineages
2627

2728

28-
def crossval(train_populations: list, num_states: np.ndarray):
29+
def crossval(train_populations: list, num_states: np.ndarray, rng=None):
2930
"""Perform cross validation for the experimental data which runs in parallel for all states.
3031
:param train_populations: the populations after applying hide_observation. This includes the list of list of lineages.
3132
:param hidden_indexes: is a list of list of np.arrays for each lineage,
3233
filled with zeros and ones. ones refer to the index of those cells that have been hidden.
3334
:param hidden_obs: list of list of tuples of observations that have been masked in the train_lineage.
3435
:param num_states: is a range of states we want to run the cross validation for.
3536
"""
37+
rng = np.random.default_rng(rng)
3638
# fit training data by parallel.
3739

3840
# save the tHMMobj for each number of states that is being run
3941
LLs = []
4042
for k in num_states:
41-
tHMMobj_list, _, gamma_list = Analyze_list(train_populations, k)
43+
tHMMobj_list, _, gamma_list = Analyze_list(train_populations, k, rng=rng)
4244

4345
Logls = 0
4446
# calculate the log likelihood of hidden observations

‎lineage/tests/test_BaumWelch.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def test_BW(cens, nStates):
5555
desired_num_cells=num_cells,
5656
desired_experiment_time=expt_time,
5757
censor_condition=cens,
58+
rng=rng,
5859
)
5960
tHMMobj = tHMM([X], num_states=nStates) # build the tHMM class with X
6061

‎lineage/tests/test_CrossVal.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def test_cv(cen):
3838
for _ in range(4)
3939
]
4040

41-
train_lineages = [hide_observation(complete_lin, 0.25) for complete_lin in complete_lineages]
41+
train_lineages = [hide_observation(complete_lin, 0.25, rng=local_rng) for complete_lin in complete_lineages]
4242

43-
ll = crossval(train_lineages, np.arange(1, 3))
43+
ll = crossval(train_lineages, np.arange(1, 3), rng=local_rng)
4444
assert ll[0] < ll[1]

‎lineage/tests/test_StateEstimation.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@ def gen():
3535
2**8,
3636
censor_condition=censored,
3737
desired_experiment_time=100,
38+
rng=rng,
3839
)
3940

4041
lineage_gamma = [gen() for _ in range(50)]
4142
solver_gamma = tHMM(lineage_gamma, 1) # evaluating for one state
42-
fit_list([solver_gamma])
43+
fit_list([solver_gamma], rng=rng)
4344

4445
assert solver_gamma.estimate.E[0].dist(E_gamma[0]) < 4.0
4546

‎lineage/tests/test_tHMM.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def test_fit_performance():
8888
def test_small_lineages(sizze, stateNum):
8989
"""To test lineages with 3 cells in them for simple gamma."""
9090
# test with 2 state model
91-
lin = [LineageTree.rand_init(pi, T, E, sizze) for _ in range(2)]
91+
lin = [LineageTree.rand_init(pi, T, E, sizze, rng=rng) for _ in range(2)]
9292

9393
_, LL1, _ = Analyze_list([lin], stateNum, rng=rng)
9494
assert np.all(np.isfinite(LL1))

0 commit comments

Comments
 (0)