Skip to content

Commit 027422b

Browse files
committed
Make kinetic tRNA charging deterministic
1 parent 1cce545 commit 027422b

3 files changed

Lines changed: 95 additions & 10 deletions

File tree

ecoli/processes/polypeptide_elongation.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from wholecell.utils.random import stochasticRound
2525
from wholecell.utils import units
2626
from wholecell.utils._trna_charging import (
27+
seed_rng,
2728
get_initiations,
2829
reconcile_via_ribosome_positions,
2930
reconcile_via_trna_pools,
@@ -321,6 +322,7 @@ def __init__(self, parameters=None):
321322

322323
self.seed = self.parameters["seed"]
323324
self.random_state = np.random.RandomState(seed=self.seed)
325+
seed_rng(self.seed)
324326

325327
def ports_schema(self):
326328
return {
@@ -2886,7 +2888,7 @@ def protein_maturation(
28862888
candidates = np.logical_and(
28872889
did_terminate, [self.is_map_substrate[x] for x in protein_indexes]
28882890
)
2889-
i_cannot_cleave = np.random.multinomial(
2891+
i_cannot_cleave = self.process.random_state.multinomial(
28902892
not_cleaved, candidates / candidates.sum()
28912893
).astype(bool)
28922894

wholecell/tests/utils/test_trna_charging.py

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from __future__ import absolute_import, division, print_function
99

1010
from wholecell.utils._trna_charging import (
11+
seed_rng,
1112
get_initiations,
1213
get_codon_at,
1314
get_candidates_to_C,
@@ -35,7 +36,7 @@ def tearDownClass(cls):
3536
pass
3637

3738
def setUp(self):
38-
pass
39+
seed_rng(0)
3940

4041
def tearDown(self):
4142
pass
@@ -489,5 +490,91 @@ def test_get_codons_read(self):
489490
assert_equal(n_codons_read, np.array([3, 3], dtype=np.int64))
490491

491492

493+
def test_reconcile_different_seeds_different_results():
494+
"""
495+
Tests that reconcile_via_ribosome_positions:
496+
- Produces DIFFERENT outputs given the same inputs but DIFFERENT random seeds
497+
- Produces SAME outputs given the same inputs and SAME random seeds
498+
"""
499+
# --- 1. Define two different seeds ---
500+
seed1 = 12345
501+
seed2 = 54321
502+
503+
# --- 2. Define fixed, non-trivial inputs ---
504+
# (Using the same inputs as the previous 20-codon example)
505+
initial_sequence_codons = np.array(
506+
[5, 2, 8, 3, 1, 4, 6, 5, 9, 3, 7, 3, 5, 1, 8, 2, 4, 6, 2, 10], dtype=np.int64
507+
)
508+
kinetics_codons = np.array(
509+
[6, 1, 7, 4, 2, 3, 5, 3, 8, 2, 6, 4, 6, 0, 9, 1, 5, 5, 1, 9], dtype=np.int64
510+
)
511+
initial_elongations = np.array([8, 12, 5, 10, 7], dtype=np.int64)
512+
sequences = np.array(
513+
[
514+
[0, 1, 2, 3, 4, 5, 6, 7, -1, -1, -1, -1, -1, -1, -1],
515+
[8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, -1, -1, -1],
516+
[1, 3, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
517+
[2, 4, 6, 8, 10, 12, 14, 16, 18, 0, -1, -1, -1, -1, -1],
518+
[19, 17, 15, 13, 11, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1],
519+
],
520+
dtype=np.int8,
521+
)
522+
max_attempts = np.int8(50)
523+
524+
# --- 3. Run 1 with seed1 ---
525+
sequence_codons_run1 = np.copy(initial_sequence_codons)
526+
elongations_run1 = np.copy(initial_elongations)
527+
seed_rng(seed1) # Seed with first seed
528+
reconcile_via_ribosome_positions(
529+
sequence_codons_run1,
530+
elongations_run1,
531+
kinetics_codons,
532+
sequences,
533+
max_attempts,
534+
)
535+
536+
# --- 4. Run 2 with seed2 ---
537+
sequence_codons_run2 = np.copy(initial_sequence_codons)
538+
elongations_run2 = np.copy(initial_elongations)
539+
seed_rng(seed2) # Seed with second, different seed
540+
reconcile_via_ribosome_positions(
541+
sequence_codons_run2,
542+
elongations_run2,
543+
kinetics_codons,
544+
sequences,
545+
max_attempts,
546+
)
547+
548+
# --- 5. Run 3 with seed1 ---
549+
sequence_codons_run3 = np.copy(initial_sequence_codons)
550+
elongations_run3 = np.copy(initial_elongations)
551+
seed_rng(seed1) # Seed with first seed
552+
reconcile_via_ribosome_positions(
553+
sequence_codons_run3,
554+
elongations_run3,
555+
kinetics_codons,
556+
sequences,
557+
max_attempts,
558+
)
559+
560+
# --- 6. Assert inequality ---
561+
# Check that the results are different for different seed.
562+
sequence_codons_differ = np.any(sequence_codons_run1 != sequence_codons_run2)
563+
elongations_differ = np.any(elongations_run1 != elongations_run2)
564+
565+
# --- 7. Assert equality ---
566+
# Check that the results are same for same seed.
567+
sequence_codons_same = np.all(sequence_codons_run1 == sequence_codons_run3)
568+
elongations_same = np.all(elongations_run1 == elongations_run3)
569+
570+
assert sequence_codons_differ or elongations_differ, (
571+
"Outputs were unexpectedly identical for different seeds."
572+
)
573+
574+
assert sequence_codons_same or elongations_same, (
575+
"Outputs were unexpectedly different for same seeds."
576+
)
577+
578+
492579
if __name__ == "__main__":
493580
unittest.main()

wholecell/utils/_trna_charging.pyx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ from libc.time cimport time, time_t
1717
np.import_array()
1818
cimport cython
1919

20+
cpdef seed_rng(unsigned int seed):
21+
"""Seeds the C standard library RNG used by this module."""
22+
srand(seed)
23+
2024
@cython.boundscheck(False) # Deactivate bounds checking
2125
@cython.wraparound(False) # Deactivate negative indexing
2226
@cython.cdivision(True) # Deactivate 0 division checking
@@ -165,10 +169,6 @@ cpdef reconcile_via_ribosome_positions(
165169
char max_attempts, # 8-bit integer
166170
):
167171

168-
# Set random seed
169-
cdef time_t t = time(NULL)
170-
srand(t)
171-
172172
# Initialize variables
173173
cdef char attempt = 0
174174
cdef int i = 0
@@ -359,10 +359,6 @@ cpdef reconcile_via_trna_pools(
359359
char [:] trnas_to_amino_acid_indexes, # 8-bit integer
360360
):
361361

362-
# Set random seed
363-
cdef time_t t = time(NULL)
364-
srand(t)
365-
366362
# Initialize variables
367363
cdef int i = 0
368364
# cdef int j = 0

0 commit comments

Comments
 (0)