Skip to content

Commit af9b90c

Browse files
committed
v0.25.0
1 parent 1a9716e commit af9b90c

File tree

7 files changed

+67
-168
lines changed

7 files changed

+67
-168
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
## 0.25.0
4+
- Save based on the number of cells not time
5+
36
## 0.24.0
47
- Increase the number of max iterations and cells
58
- Save at the end of the simulation

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ecdna-evo"
3-
version = "0.24.0"
3+
version = "0.25.0"
44
edition = "2021"
55
repository = "https://github.com/fraterenz/ecdna-evo"
66
description = "Evolutionary models of extra-chromosomal DNA (ecDNA)"

src/clap_app.rs

+21-95
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use anyhow::{ensure, Context};
1+
use anyhow::Context;
22
use clap::{ArgAction, Parser, ValueEnum};
33
use ecdna_evo::{
44
distribution::EcDNADistribution,
55
segregation::{
66
Binomial, BinomialNoNminus, BinomialNoUneven, Deterministic, Segregate,
77
},
8-
Snapshot,
8+
SnapshotCells,
99
};
1010
use rand::Rng;
1111
use sosa::{IterTime, NbIndividuals, Options};
@@ -89,112 +89,45 @@ pub struct Cli {
8989
#[arg(short, long, default_value_t = 12, conflicts_with = "debug")]
9090
/// Number of independent realisations to simulate of the same stochastic process
9191
runs: usize,
92-
#[arg(long, requires = "subsamples", value_delimiter = ',', require_equals = true, num_args = 0..)]
93-
/// Snapshots to take to save the simulation, requires `subsamples`.
94-
///
95-
/// The combination of `snapshots` with `subsamples` gives four different
96-
/// behaviours:
97-
///
98-
/// 1. when `snapshots.len() = 1` and `subsamples.len() = 1`: subsample once with the number of cells corresponding to `snapshots[0]`
99-
///
100-
/// 2. when `snapshots.len() > 1` and `subsamples.len() = 1`: for every `s` in `snapshots`, subsample with the number of cells corresponding to `snapshots[0]`
101-
///
102-
/// 3. when `snapshots.len() = 1` and `subsamples.len() > 1`: for every `c` in `subsamples`, subsample once with the number of cells corresponding to `c`
103-
///
104-
/// 4. when `snapshots.len() > 1` and `subsamples.len() > 1`: for every pair `(s, c)` in `snapshots.zip(subsamples)`, subsample at time `s` with `c` cells
105-
snapshots: Option<Vec<f32>>,
106-
/// Number of cells to subsample before saving the measurements, leave
107-
/// empty when no subsample is needed.
108-
/// If subsampling is performed, the measurements of the whole population
109-
/// will also be saved.
110-
///
111-
/// See help for `snapshots` for more details.
112-
#[arg(long, requires = "snapshots", num_args = 0.., value_delimiter = ',', require_equals = true)]
113-
subsamples: Option<Vec<usize>>,
92+
#[arg(long, value_delimiter = ',', require_equals = true, num_args = 0..)]
93+
/// Number of cells that will trigger the saving of the ecDNA distribution.
94+
snapshots: Option<Vec<usize>>,
11495
#[arg(short, long, action = clap::ArgAction::Count, conflicts_with = "debug", default_value_t = 0)]
11596
verbosity: u8,
11697
}
11798

11899
fn build_snapshots(
119-
years: u64,
120100
cells: NbIndividuals,
121-
subsamples: Option<Vec<usize>>,
122-
snapshots: Option<Vec<f32>>,
123-
) -> anyhow::Result<VecDeque<Snapshot>> {
124-
let mut snapshots = match (subsamples, snapshots) {
125-
(Some(sub), Some(snap)) => {
126-
match (&sub[..], &snap[..]) {
127-
// subsample `unique_sub` once at `unique_snap` time
128-
([unique_sub], [unique_snap]) => VecDeque::from_iter(
129-
[(unique_sub, unique_snap)].into_iter().map(
130-
|(&cells2sample, &time)| Snapshot {
131-
cells2sample,
132-
time,
133-
},
134-
),
135-
),
136-
// subsample `unique_sub` at several `snaps` times
137-
([unique_sub], snaps) => VecDeque::from_iter(
138-
snaps.iter().zip(std::iter::repeat(unique_sub)).map(
139-
|(&time, &cells2sample)| Snapshot {
140-
cells2sample,
141-
time,
142-
},
143-
),
144-
),
145-
// subsample with different cells `unique_sub` once at `unique_snap` time
146-
(subs, [unique_snap]) => VecDeque::from_iter(
147-
subs.iter().zip(std::iter::repeat(unique_snap)).map(
148-
|(&cells2sample, &time)| Snapshot {
149-
cells2sample,
150-
time,
151-
},
152-
),
153-
),
154-
// subsample with many cells `subs` at specific `snaps`
155-
(subs, snaps) => {
156-
ensure!(
157-
subs.len() == snaps.len(),
158-
"the lenght of snapshots do not match the lenght of subsamples"
159-
);
160-
VecDeque::from_iter(subs.iter().zip(snaps).map(
161-
|(&cells2sample, &time)| Snapshot {
162-
cells2sample,
163-
time,
164-
},
165-
))
166-
}
167-
}
168-
}
169-
(None, None) => VecDeque::from_iter(
170-
build_snapshots_from_time(
171-
10usize,
172-
if years > 1 { years as f32 - 1. } else { 0.9 },
173-
)
174-
.into_iter()
175-
.map(|t| Snapshot { cells2sample: cells as usize, time: t }),
101+
snapshots: Option<Vec<usize>>,
102+
) -> anyhow::Result<VecDeque<SnapshotCells>> {
103+
let mut snapshots = match snapshots {
104+
Some(s) => VecDeque::from_iter(
105+
s.into_iter().map(|cells| SnapshotCells { cells }),
176106
),
177-
_ => unreachable!(),
107+
None => VecDeque::from(build_snapshots_from_cells(11, cells as usize)),
178108
};
179109

180110
snapshots.make_contiguous();
181111
snapshots
182112
.as_mut_slices()
183113
.0
184-
.sort_by(|s1, s2| s1.time.partial_cmp(&s2.time).unwrap());
114+
.sort_by(|s1, s2| s1.cells.partial_cmp(&s2.cells).unwrap());
185115
Ok(snapshots)
186116
}
187117

188-
fn build_snapshots_from_time(n_snapshots: usize, time: f32) -> Vec<f32> {
189-
let dx = time / ((n_snapshots - 1) as f32);
190-
let mut x = vec![0.; n_snapshots];
118+
fn build_snapshots_from_cells(
119+
n_snapshots: usize,
120+
cells: usize,
121+
) -> Vec<SnapshotCells> {
122+
let dx = cells / (n_snapshots - 1);
123+
let mut x = vec![1; n_snapshots];
191124
for i in 1..n_snapshots - 1 {
192125
x[i] = x[i - 1] + dx;
193126
}
194127

195128
x.shrink_to_fit();
196-
x[n_snapshots - 1] = time;
197-
x
129+
x[n_snapshots - 1] = cells;
130+
x.into_iter().map(|v| SnapshotCells { cells: v }).collect()
198131
}
199132

200133
impl Cli {
@@ -220,14 +153,7 @@ impl Cli {
220153
}
221154
};
222155

223-
let snapshots =
224-
build_snapshots(years, cells, cli.subsamples, cli.snapshots)
225-
.unwrap();
226-
ensure!(
227-
snapshots.iter().all(|s| s.time < years as f32),
228-
"times to take `snapshots` must be smaller than total `years`"
229-
);
230-
156+
let snapshots = build_snapshots(cells, cli.snapshots).unwrap();
231157
let segregation = cli.segregation.into();
232158

233159
// if both d0, d1 are either unset or equal to 0, pure birth,

src/lib.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,14 @@ use std::{collections::VecDeque, path::PathBuf};
1111
pub use ecdna_lib::{distribution, DNACopy};
1212

1313
#[derive(Debug, Clone)]
14-
pub struct Snapshot {
15-
/// The number of cells to subsample
16-
pub cells2sample: usize,
17-
/// The time at which we subsample
18-
pub time: f32,
14+
/// Save the ecDNA distribution when the number of cells in the system is equal
15+
/// to `cells`
16+
pub struct SnapshotCells {
17+
pub cells: usize,
1918
}
2019

21-
#[derive(Debug, Clone)]
2220
pub struct SavingOptions {
23-
pub snapshots: VecDeque<Snapshot>,
21+
pub snapshots: VecDeque<SnapshotCells>,
2422
pub path2dir: PathBuf,
2523
pub filename: String,
2624
}

src/main.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use ecdna_evo::{
77
distribution::EcDNADistribution,
88
process::{save, BirthDeath, EcDNAEvent, PureBirth},
99
proliferation::{CellDeath, Exponential},
10-
SavingOptions, Snapshot,
10+
SavingOptions, SnapshotCells,
1111
};
1212
use indicatif::ParallelProgressIterator;
1313
use rand::SeedableRng;
@@ -39,7 +39,7 @@ pub struct SimulationOptions {
3939
seed: u64,
4040
path2dir: PathBuf,
4141
options: Options,
42-
snapshots: VecDeque<Snapshot>,
42+
snapshots: VecDeque<SnapshotCells>,
4343
}
4444

4545
fn main() {

src/process.rs

+34-62
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::{
99

1010
use crate::{
1111
distribution::EcDNADistribution, segregation::Segregate, SavingOptions,
12-
Snapshot,
12+
SnapshotCells,
1313
};
1414

1515
use super::{
@@ -66,7 +66,7 @@ where
6666
proliferation: P,
6767
segregation: S,
6868
pub time: f32,
69-
pub snapshots: VecDeque<Snapshot>,
69+
pub snapshots: VecDeque<SnapshotCells>,
7070
pub path2dir: PathBuf,
7171
pub filename: String,
7272
pub verbosity: u8,
@@ -116,41 +116,28 @@ impl<P: EcDNAProliferation, S: Segregate> AdvanceStep<2> for PureBirth<P, S> {
116116
rng: &mut impl Rng,
117117
) {
118118
while !self.snapshots.is_empty()
119-
&& self.snapshots.iter().any(|s| self.time >= s.time)
119+
&& self.snapshots.iter().any(|s| {
120+
*self.distribution.get_nminus()
121+
+ self.distribution.compute_nplus()
122+
== s.cells as u64
123+
})
120124
{
121125
let snapshot = self.snapshots.pop_front().unwrap();
122126
if self.verbosity > 0 {
123127
println!(
124-
"saving state for timepoint at time {:#?} at simulation's time {} with {} cells",
125-
snapshot.time, self.time, snapshot.cells2sample
128+
"saving state for timepoint at time {:#?} with cells {} ",
129+
self.time, snapshot.cells,
126130
);
127131
}
128-
let cells = (*self.distribution.get_nminus()
129-
+ self.distribution.compute_nplus())
130-
as usize;
131132

132-
if snapshot.cells2sample == cells || cells < snapshot.cells2sample
133-
{
134-
save(
135-
&self.path2dir,
136-
&self.filename,
137-
snapshot.time,
138-
&self.distribution,
139-
self.verbosity,
140-
)
141-
.expect("cannot save snapshot");
142-
} else {
143-
save(
144-
&self.path2dir,
145-
&self.filename,
146-
snapshot.time,
147-
&self
148-
.distribution
149-
.into_subsampled(snapshot.cells2sample as u64, rng),
150-
self.verbosity,
151-
)
152-
.expect("cannot save snapshot");
153-
}
133+
save(
134+
&self.path2dir,
135+
&self.filename,
136+
self.time,
137+
&self.distribution,
138+
self.verbosity,
139+
)
140+
.expect("cannot save snapshot");
154141
}
155142

156143
match reaction.event {
@@ -218,7 +205,7 @@ where
218205
segregation: S,
219206
death: CellDeath,
220207
pub time: f32,
221-
pub snapshots: VecDeque<Snapshot>,
208+
pub snapshots: VecDeque<SnapshotCells>,
222209
pub path2dir: PathBuf,
223210
pub filename: String,
224211
pub verbosity: u8,
@@ -270,40 +257,28 @@ impl<P: EcDNAProliferation, S: Segregate> AdvanceStep<4> for BirthDeath<P, S> {
270257
rng: &mut impl Rng,
271258
) {
272259
while !self.snapshots.is_empty()
273-
&& self.snapshots.iter().any(|s| self.time >= s.time)
260+
&& self.snapshots.iter().any(|s| {
261+
*self.distribution.get_nminus()
262+
+ self.distribution.compute_nplus()
263+
== s.cells as u64
264+
})
274265
{
275266
let snapshot = self.snapshots.pop_front().unwrap();
276267
if self.verbosity > 0 {
277268
println!(
278-
"saving state for timepoint at time {:#?} at simulation's time {} with {} cells",
279-
snapshot.time, self.time, snapshot.cells2sample
269+
"saving state for timepoint at time {:#?} with {} cells",
270+
self.time, snapshot.cells
280271
);
281272
}
282-
let cells = (*self.distribution.get_nminus()
283-
+ self.distribution.compute_nplus())
284-
as usize;
285273

286-
if snapshot.cells2sample == cells {
287-
save(
288-
&self.path2dir,
289-
&self.filename,
290-
snapshot.time,
291-
&self.distribution,
292-
self.verbosity,
293-
)
294-
.expect("cannot save snapshot");
295-
} else {
296-
save(
297-
&self.path2dir,
298-
&self.filename,
299-
snapshot.time,
300-
&self
301-
.distribution
302-
.into_subsampled(snapshot.cells2sample as u64, rng),
303-
self.verbosity,
304-
)
305-
.expect("cannot save snapshot");
306-
}
274+
save(
275+
&self.path2dir,
276+
&self.filename,
277+
self.time,
278+
&self.distribution,
279+
self.verbosity,
280+
)
281+
.expect("cannot save snapshot");
307282
}
308283
match reaction.event {
309284
EcDNAEvent::ProliferateNPlus => {
@@ -387,10 +362,7 @@ mod tests {
387362
CellDeath,
388363
time,
389364
SavingOptions {
390-
snapshots: VecDeque::from([Snapshot {
391-
cells2sample: 10,
392-
time: 2.,
393-
}]),
365+
snapshots: VecDeque::from([SnapshotCells { cells: 2 }]),
394366
path2dir: PathBuf::default(),
395367
filename: String::from("filename"),
396368
},

0 commit comments

Comments
 (0)