1use crate::genotype::NeutralMutationPoisson;
2use crate::Probs;
3use crate::{stemcell::StemCell, write2file, MAX_SUBCLONES};
4use anyhow::{ensure, Context};
5use log::{debug, trace};
6use rand::seq::IteratorRandom;
7use rand::Rng;
8use rand_distr::{Bernoulli, Distribution, Gamma};
9use sosa::ReactionRates;
10use std::num::NonZeroUsize;
11use std::path::Path;
12
13#[derive(Debug, Default, Clone)]
15pub struct Distributions {
16 pub u: f32,
18 pub neutral_poisson: NeutralMutationPoisson,
20}
21
22impl Distributions {
23 pub fn new(probs: Probs) -> Self {
24 let (u, background, division) = match probs {
25 Probs::Symmetric { u, probs_per_year } => {
26 (u, probs_per_year.mu_background, probs_per_year.mu_division)
27 }
28 Probs::Asymmetric {
29 u, probs_per_year, ..
30 } => (u, probs_per_year.mu_background, probs_per_year.mu_division),
31 };
32 debug!(
33 "creating distributions with u: {u}, lambda_background: {background}, lambda_division: {division}"
34 );
35
36 Self {
37 u,
38 neutral_poisson: NeutralMutationPoisson::new(division, background).unwrap(),
39 }
40 }
41}
42
43pub fn from_mean_std_to_shape_scale(mean: f32, std: f32) -> (f32, f32) {
44 (mean.powf(2.) / std.powf(2.), std.powf(2.) / mean)
45}
46
47#[derive(Clone, Copy, Debug)]
49pub enum Fitness {
50 Neutral,
54 Fixed { s: f32 },
60 GammaSampled { shape: f32, scale: f32 },
66}
67
68impl Fitness {
69 pub fn get_mean_std(&self) -> (f32, f32) {
70 match self {
71 Fitness::Neutral => (0., 0.),
72 Fitness::Fixed { s } => (*s, 0.),
73 Fitness::GammaSampled { shape, scale } => {
74 (shape * scale, f32::sqrt(shape * scale.powi(2)))
75 }
76 }
77 }
78}
79
80pub type CloneId = usize;
82
83#[derive(Debug, Clone)]
84pub struct SubClone {
93 cells: Vec<StemCell>,
94 pub id: CloneId,
95}
96
97impl Iterator for SubClone {
98 type Item = StemCell;
99
100 fn next(&mut self) -> Option<Self::Item> {
101 self.cells.pop()
102 }
103}
104
105impl SubClone {
106 pub fn new(id: CloneId, cell_capacity: usize) -> SubClone {
107 SubClone {
108 cells: Vec::with_capacity(cell_capacity),
109 id,
110 }
111 }
112
113 pub fn with_capacity(id: usize, capacity: usize) -> SubClone {
114 SubClone {
115 cells: Vec::with_capacity(capacity),
116 id,
117 }
118 }
119
120 pub fn empty_with_id(id: usize) -> SubClone {
121 SubClone { cells: vec![], id }
122 }
123
124 pub fn get_mut_cells(&mut self) -> &mut [StemCell] {
125 &mut self.cells
126 }
127
128 pub fn get_cells(&self) -> &[StemCell] {
129 &self.cells
130 }
131
132 pub fn get_cells_subclones_idx(&self) -> Vec<(&StemCell, usize)> {
133 self.cells.iter().map(|cell| (cell, self.id)).collect()
134 }
135
136 pub fn is_empty(&self) -> bool {
137 self.get_cells().is_empty()
138 }
139
140 pub fn assign_cell(&mut self, cell: StemCell) {
141 self.cells.push(cell);
142 }
143
144 pub fn random_cell(&mut self, rng: &mut impl Rng) -> anyhow::Result<StemCell> {
145 ensure!(!self.cells.is_empty());
146 Ok(self
147 .cells
148 .swap_remove(rng.random_range(0..self.cells.len())))
149 }
150
151 pub fn cell_count(&self) -> u64 {
152 self.cells.len() as u64
153 }
154}
155
156pub fn next_clone(
157 subclones: &SubClones,
158 old_subclone_id: CloneId,
159 p: f64,
160 rng: &mut impl Rng,
161) -> CloneId {
162 if Bernoulli::new(p).unwrap().sample(rng) {
170 let mut rnd_clone_id = rng.random_range(0..MAX_SUBCLONES);
171 let mut counter = 0;
172 while (rnd_clone_id == old_subclone_id
174 || !subclones.get_clone(rnd_clone_id).unwrap().is_empty())
175 && counter <= MAX_SUBCLONES
176 {
177 rnd_clone_id = rng.random_range(0..MAX_SUBCLONES);
178 counter += 1;
179 }
180 assert!(counter <= MAX_SUBCLONES, "max number of clones reached");
181 debug!("new fit variant: assign cell to clone {rnd_clone_id}");
182 return rnd_clone_id;
183 }
184 debug!("no new fit variants with p {p}",);
185 old_subclone_id
186}
187
188#[derive(Clone, Debug)]
191pub struct SubClones([SubClone; MAX_SUBCLONES]);
192
193impl SubClones {
194 pub fn new(cells: Vec<StemCell>, capacity: usize) -> Self {
195 let mut subclones: [SubClone; MAX_SUBCLONES] =
201 std::array::from_fn(|i| SubClone::new(i, capacity));
202 let tot_cells = cells.len();
203 debug!("assigning {tot_cells} cells to the wild-type clone");
204 for cell in cells {
205 subclones[0].assign_cell(cell);
206 }
207 assert_eq!(subclones[0].cells.len(), tot_cells);
208
209 Self(subclones)
210 }
211
212 pub fn new_empty() -> Self {
213 SubClones(std::array::from_fn(SubClone::empty_with_id))
214 }
215
216 pub fn with_capacity(capacity: usize) -> Self {
217 SubClones(std::array::from_fn(|id| {
218 SubClone::with_capacity(id, capacity)
219 }))
220 }
221
222 pub fn compute_tot_cells(&self) -> u64 {
223 self.0.iter().map(|subclone| subclone.cell_count()).sum()
224 }
225
226 pub(crate) fn get_clone(&self, id: usize) -> Option<&SubClone> {
227 self.0.get(id)
228 }
229
230 pub(crate) fn get_mut_clone_unchecked(&mut self, id: usize) -> &mut SubClone {
231 &mut self.0[id]
232 }
233
234 pub fn get_neutral_clone(&self) -> &SubClone {
235 &self.0[0]
236 }
237
238 pub fn array_of_gillespie_reactions(&self) -> [CloneId; MAX_SUBCLONES] {
239 core::array::from_fn(|i| i)
240 }
241
242 pub(crate) fn into_subsampled(self, nb_cells: NonZeroUsize, rng: &mut impl Rng) -> Self {
243 let mut subclones = Self::new_empty();
245
246 for (cell, clone_id) in self
247 .0
248 .into_iter()
249 .flat_map(|subclone| {
250 subclone
257 .cells
258 .into_iter()
259 .map(|cell| (cell, subclone.id))
260 .collect::<Vec<(StemCell, usize)>>()
261 })
262 .choose_multiple(rng, nb_cells.get())
263 {
264 subclones.0[clone_id].cells.push(cell);
265 }
266 subclones
267 }
268
269 pub fn get_mut_cells(&mut self) -> Vec<&mut StemCell> {
270 self.0
271 .iter_mut()
272 .flat_map(|subclone| subclone.get_mut_cells())
273 .collect()
274 }
275
276 pub fn get_cells(&self) -> Vec<&StemCell> {
277 self.0
278 .iter()
279 .flat_map(|subclone| subclone.get_cells())
280 .collect()
281 }
282
283 pub(crate) fn get_cells_with_clones_idx(&self) -> Vec<(&StemCell, usize)> {
284 self.0
285 .iter()
286 .flat_map(|subclone| subclone.get_cells_subclones_idx())
287 .collect()
288 }
289
290 pub(crate) fn get_cells_subsampled_with_clones_idx(
291 &self,
292 nb_cells: usize,
293 rng: &mut impl Rng,
294 ) -> Vec<(&StemCell, usize)> {
295 self.0
296 .iter()
297 .flat_map(|subclone| subclone.get_cells_subclones_idx())
298 .choose_multiple(rng, nb_cells)
299 }
300
301 pub fn gillespie_rates(
302 &self,
303 fitness: &Fitness,
304 b0: f32,
305 rng: &mut impl Rng,
306 ) -> ReactionRates<MAX_SUBCLONES> {
307 match fitness {
310 Fitness::Fixed { s } => ReactionRates(core::array::from_fn(|i| {
311 if i == 0 {
312 b0
313 } else {
314 b0 * (1. + s)
315 }
316 })),
317 &Fitness::GammaSampled { shape, scale } => {
318 let gamma = Gamma::new(shape, scale).unwrap();
319 ReactionRates(core::array::from_fn(|i| {
320 if i == 0 {
321 b0
322 } else {
323 b0 * (1. + gamma.sample(rng))
324 }
325 }))
326 }
327 &Fitness::Neutral => ReactionRates(core::array::from_fn(|_| b0)),
328 }
329 }
330}
331
332impl Default for SubClones {
333 fn default() -> Self {
334 let subclones = std::array::from_fn(|i| {
336 let mut subclone = SubClone::new(i, 2);
337 subclone.assign_cell(StemCell::new());
338 subclone
339 });
340 Self(subclones)
341 }
342}
343
344impl From<Vec<(StemCell, usize)>> for SubClones {
345 fn from(cells: Vec<(StemCell, usize)>) -> Self {
346 let mut subclones = SubClones::new_empty();
347 for (cell, id) in cells.into_iter() {
348 subclones.get_mut_clone_unchecked(id).assign_cell(cell);
349 }
350 subclones
351 }
352}
353
354pub struct Variants {}
356
357impl Variants {
358 pub fn variant_counts(subclones: &SubClones) -> [u64; MAX_SUBCLONES] {
359 std::array::from_fn(|i| subclones.0[i].cell_count())
369 }
370
371 pub fn variant_fractions(subclones: &SubClones) -> Vec<f32> {
372 let tot_cells = subclones.compute_tot_cells();
396 Variants::variant_counts(subclones)
397 .into_iter()
398 .map(|frac| frac as f32 / tot_cells as f32)
399 .collect()
400 }
401}
402
403pub fn save_variant_fraction(subclones: &SubClones, path2file: &Path) -> anyhow::Result<()> {
404 let path2file = path2file.with_extension("csv");
405 let total_variant_frac = Variants::variant_fractions(subclones);
406 debug!("total variant fraction in {path2file:#?}");
407 write2file(&total_variant_frac, &path2file, None, false)?;
408 Ok(())
409}
410
411pub fn proliferating_cell(
412 subclones: &mut SubClones,
413 subclone_id: CloneId,
414 rng: &mut impl Rng,
415) -> StemCell {
416 trace!(
421 "a cell from clone {:#?} will divide",
422 subclones.0[subclone_id]
423 );
424 subclones.0[subclone_id]
425 .random_cell(rng)
426 .with_context(|| "found empty subclone")
427 .unwrap()
428}
429
430#[cfg(test)]
431mod tests {
432 use std::num::NonZeroU8;
433
434 use crate::tests::LambdaFromNonZeroU8;
435
436 use super::*;
437 use quickcheck_macros::quickcheck;
438 use rand::{rngs::SmallRng, SeedableRng};
439
440 #[quickcheck]
441 fn assign_cell_test(id: usize) -> bool {
442 let mut neutral_clone = SubClone { cells: vec![], id };
443 let cell = StemCell::new();
444 assert!(neutral_clone.cells.is_empty());
445
446 neutral_clone.assign_cell(cell);
447 !neutral_clone.cells.is_empty()
448 }
449
450 #[quickcheck]
451 fn proliferating_cell_test(seed: u64, mut subclone_id: u8) -> bool {
452 let mut rng = SmallRng::seed_from_u64(seed);
453 let mut subclones = SubClones::default();
454 if subclone_id >= subclones.0.len() as u8 {
455 subclone_id = 1;
456 }
457 let number_of_cells = subclones.0[subclone_id as usize].get_cells().len();
458 let tot_cells = subclones.compute_tot_cells();
459
460 let _ = proliferating_cell(&mut subclones, subclone_id as usize, &mut rng);
461
462 let subclones_have_lost_cell =
463 Variants::variant_counts(&subclones).iter().sum::<u64>() < tot_cells;
464 let subclone_has_lost_cell =
465 subclones.0[subclone_id as usize].get_cells().len() == number_of_cells - 1;
466 subclones_have_lost_cell && subclone_has_lost_cell
467 }
468
469 #[test]
470 fn variant_fraction() {
471 let subclones = SubClones::default();
472 assert_eq!(
473 Variants::variant_fractions(&subclones),
474 &[1. / MAX_SUBCLONES as f32; MAX_SUBCLONES]
475 );
476 }
477
478 #[quickcheck]
479 fn division_no_new_clone(seed: u64, cells_present: NonZeroU8) -> bool {
480 let mut rng = SmallRng::seed_from_u64(seed);
481 let mut cell = StemCell::new();
482 cell.set_last_division_time(1.1).unwrap();
483
484 let cells = vec![cell; cells_present.get() as usize];
485 let subclones = SubClones::new(cells, cells_present.get() as usize + 1);
486
487 let old_id = 0;
488 let id = next_clone(&subclones, old_id, 0., &mut rng);
489 id == old_id
490 }
491
492 #[quickcheck]
493 fn division_new_clone(seed: u64, cells_present: NonZeroU8) -> bool {
494 let mut rng = SmallRng::seed_from_u64(seed);
495 let mut cell = StemCell::new();
496 cell.set_last_division_time(1.1).unwrap();
497
498 let cells = vec![cell; cells_present.get() as usize];
499 let before_assignment = cells.len();
500 let subclones = SubClones::new(cells, cells_present.get() as usize + 1);
501
502 next_clone(&subclones, 0, 1., &mut rng);
503 subclones.0[0].cell_count() as usize == before_assignment
504 }
505
506 #[test]
507 #[should_panic]
508 fn assign_all_clones_occupied() {
509 let mut rng = SmallRng::seed_from_u64(26);
510
511 let subclones = SubClones::default();
512 let mut cell = StemCell::new();
513 cell.set_last_division_time(1.).unwrap();
514
515 next_clone(&subclones, 0, 1., &mut rng);
516 }
517
518 #[quickcheck]
519 fn new_distribution_test(
520 lambda_division: LambdaFromNonZeroU8,
521 lambda_background: LambdaFromNonZeroU8,
522 ) -> bool {
523 let probs = Probs::new(lambda_background.0, lambda_division.0, 0.01, 0., 10);
524 let distrs = Distributions::new(probs);
525 distrs.neutral_poisson.eq(&NeutralMutationPoisson::new(
526 lambda_division.0,
527 lambda_background.0,
528 )
529 .unwrap())
530 }
531}