Skip to content

Commit ce3d8f4

Browse files
authored
Refine forward sim example (#90)
* Change variable names so that default field names can be used. * Handle errors where they occur (due to backtrace issues). * Keep input roots when simplifying.
1 parent b1022e1 commit ce3d8f4

File tree

1 file changed

+91
-81
lines changed

1 file changed

+91
-81
lines changed

examples/forward_simulation.rs

+91-81
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
* * Constant population size
55
* * Poisson number of crossovers per parent, per mating.
66
* * Overlapping generations supported.
7-
* * Simplifcation includes unary nodes for later "recapitation".
7+
* * Simplifcation keeps input roots for later "recapitation".
88
*
99
* On the rust side:
1010
*
11-
* * Runtime errors from all dependencies propagate to main.
11+
* * Runtime errors are handled when they occur.
12+
* We do this because stable rust does not have
13+
* support for back traces.
1214
* * No "unsafe" tskit C code is called directly.
1315
* * By default, clap doesn't allow argument values to start
1416
* with a hyphen ('-'). This is handy, as it automatically
@@ -21,7 +23,6 @@ use rand::rngs::StdRng;
2123
use rand::Rng;
2224
use rand::SeedableRng;
2325
use rand_distr::{Exp, Uniform};
24-
use std::error::Error;
2526

2627
struct SimParams {
2728
pub popsize: u32,
@@ -200,24 +201,15 @@ fn death_and_parents(
200201
}
201202
}
202203

203-
fn mendel(
204-
pnodes: &mut (tskit::tsk_id_t, tskit::tsk_id_t),
205-
rng: &mut StdRng,
206-
) -> Result<(), Box<dyn Error>> {
204+
fn mendel(pnodes: &mut (tskit::tsk_id_t, tskit::tsk_id_t), rng: &mut StdRng) {
207205
let x: f64 = rng.gen();
208206
match x.partial_cmp(&0.5) {
209207
Some(std::cmp::Ordering::Less) => {
210208
std::mem::swap(&mut pnodes.0, &mut pnodes.1);
211209
}
212210
Some(_) => (),
213-
None => {
214-
return Err(Box::new(tskit::TskitError::ValueError {
215-
got: String::from("None"),
216-
expected: String::from("Some(std::cmp::Ordering)"),
217-
}));
218-
}
211+
None => panic!("Unexpected None"),
219212
}
220-
Ok(())
221213
}
222214

223215
fn crossover_and_record_edges_details(
@@ -226,40 +218,52 @@ fn crossover_and_record_edges_details(
226218
params: &SimParams,
227219
tables: &mut tskit::TableCollection,
228220
rng: &mut StdRng,
229-
) -> Result<(), Box<dyn Error>> {
221+
) {
230222
if params.xovers == 0.0 {
231-
let _ = tables.add_edge(0., tables.sequence_length(), parent.node0, offspring_node)?;
223+
match tables.add_edge(0., tables.sequence_length(), parent.node0, offspring_node) {
224+
Ok(_) => (),
225+
Err(e) => panic!("{}", e),
226+
}
232227
} else {
233228
let mut pnodes = (parent.node0, parent.node1);
234-
mendel(&mut pnodes, rng)?;
229+
mendel(&mut pnodes, rng);
235230

236231
let mut p0 = parent.node0;
237232
let mut p1 = parent.node1;
238233

239-
let exp = Exp::new(params.xovers / tables.sequence_length())?;
234+
let exp = match Exp::new(params.xovers / tables.sequence_length()) {
235+
Ok(e) => e,
236+
Err(e) => panic!("{}", e),
237+
};
240238
let mut current_pos = 0.0;
241239
loop {
242240
let next_length = rng.sample(exp);
243241
match (current_pos + next_length).partial_cmp(&tables.sequence_length()) {
244242
Some(std::cmp::Ordering::Less) => {
245-
tables.add_edge(current_pos, current_pos + next_length, p0, offspring_node)?;
243+
match tables.add_edge(
244+
current_pos,
245+
current_pos + next_length,
246+
p0,
247+
offspring_node,
248+
) {
249+
Ok(_) => (),
250+
Err(e) => panic!("{}", e),
251+
}
246252
std::mem::swap(&mut p0, &mut p1);
247253
current_pos += next_length;
248254
}
249255
Some(_) => {
250-
tables.add_edge(current_pos, tables.sequence_length(), p0, offspring_node)?;
256+
match tables.add_edge(current_pos, tables.sequence_length(), p0, offspring_node)
257+
{
258+
Ok(_) => (),
259+
Err(e) => panic!("{}", e),
260+
}
251261
break;
252262
}
253-
None => {
254-
return Err(Box::new(tskit::TskitError::ValueError {
255-
got: String::from("None"),
256-
expected: String::from("Some(std::cmp::Ordering)"),
257-
}));
258-
}
263+
None => panic!("Unexpected None"),
259264
}
260265
}
261266
}
262-
Ok(())
263267
}
264268

265269
fn crossover_and_record_edges(
@@ -268,9 +272,9 @@ fn crossover_and_record_edges(
268272
params: &SimParams,
269273
tables: &mut tskit::TableCollection,
270274
rng: &mut StdRng,
271-
) -> Result<(), Box<dyn Error>> {
272-
crossover_and_record_edges_details(parents.parent0, offspring_nodes.0, params, tables, rng)?;
273-
crossover_and_record_edges_details(parents.parent1, offspring_nodes.1, params, tables, rng)
275+
) {
276+
crossover_and_record_edges_details(parents.parent0, offspring_nodes.0, params, tables, rng);
277+
crossover_and_record_edges_details(parents.parent1, offspring_nodes.1, params, tables, rng);
274278
}
275279

276280
fn births(
@@ -280,70 +284,85 @@ fn births(
280284
tables: &mut tskit::TableCollection,
281285
alive: &mut [Diploid],
282286
rng: &mut StdRng,
283-
) -> Result<(), Box<dyn Error>> {
287+
) {
284288
for p in parents {
285289
// Register the two nodes for our offspring
286-
let offspring_node_0 = tables.add_node(
290+
let node0 = match tables.add_node(
287291
0, // flags
288292
birth_time as f64, // time
289293
tskit::TSK_NULL, // population
290294
// individual
291295
tskit::TSK_NULL,
292-
)?;
293-
let offspring_node_1 =
294-
tables.add_node(0, birth_time as f64, tskit::TSK_NULL, tskit::TSK_NULL)?;
296+
) {
297+
Ok(x) => x,
298+
Err(e) => panic!("{}", e),
299+
};
300+
let node1 = match tables.add_node(0, birth_time as f64, tskit::TSK_NULL, tskit::TSK_NULL) {
301+
Ok(x) => x,
302+
Err(e) => panic!("{}", e),
303+
};
295304

296305
// Replace a dead individual
297306
// with our newborn.
298-
alive[p.index] = Diploid {
299-
node0: offspring_node_0,
300-
node1: offspring_node_1,
301-
};
307+
alive[p.index] = Diploid { node0, node1 };
302308

303-
crossover_and_record_edges(p, (offspring_node_0, offspring_node_1), params, tables, rng)?;
309+
crossover_and_record_edges(p, (node0, node1), params, tables, rng);
304310
}
305-
Ok(())
306311
}
307312

308-
fn simplify(
309-
alive: &mut [Diploid],
310-
tables: &mut tskit::TableCollection,
311-
) -> Result<(), Box<dyn Error>> {
313+
fn simplify(alive: &mut [Diploid], tables: &mut tskit::TableCollection) {
312314
let mut samples = vec![];
313315
for a in alive.iter() {
314316
assert!(a.node0 != a.node1);
315317
samples.push(a.node0);
316318
samples.push(a.node1);
317319
}
318320

319-
tables.full_sort(tskit::TableSortOptions::default())?;
321+
match tables.full_sort(tskit::TableSortOptions::default()) {
322+
Ok(_) => (),
323+
Err(e) => panic!("{}", e),
324+
}
320325

321-
match tables.simplify(&samples, tskit::SimplificationOptions::KEEP_UNARY, true)? {
322-
Some(idmap) => {
323-
for a in alive.iter_mut() {
324-
a.node0 = idmap[a.node0 as usize];
325-
assert!(a.node0 != tskit::TSK_NULL);
326-
a.node1 = idmap[a.node1 as usize];
327-
assert!(a.node1 != tskit::TSK_NULL);
326+
match tables.simplify(
327+
&samples,
328+
tskit::SimplificationOptions::KEEP_INPUT_ROOTS,
329+
true,
330+
) {
331+
Ok(x) => match x {
332+
Some(idmap) => {
333+
for a in alive.iter_mut() {
334+
a.node0 = idmap[a.node0 as usize];
335+
assert!(a.node0 != tskit::TSK_NULL);
336+
a.node1 = idmap[a.node1 as usize];
337+
assert!(a.node1 != tskit::TSK_NULL);
338+
}
328339
}
329-
Ok(())
330-
}
331-
None => Err(Box::new(tskit::TskitError::ValueError {
332-
got: String::from("None"),
333-
expected: String::from("Some(idmap) from simplification function"),
334-
})),
335-
}
340+
None => panic!("Unexpected None"),
341+
},
342+
Err(e) => panic!("{}", e),
343+
};
336344
}
337345

338-
fn runsim(params: &SimParams) -> Result<tskit::TableCollection, Box<dyn std::error::Error>> {
339-
let mut tables = tskit::TableCollection::new(params.genome_length)?;
346+
fn runsim(params: &SimParams) -> tskit::TableCollection {
347+
let mut tables = match tskit::TableCollection::new(params.genome_length) {
348+
Ok(x) => x,
349+
Err(e) => panic!("{}", e),
350+
};
340351

341352
let mut rng = StdRng::seed_from_u64(params.seed);
342353

343354
let mut alive: Vec<Diploid> = vec![];
344355
for _ in 0..params.popsize {
345-
let node0 = tables.add_node(0, params.nsteps as f64, tskit::TSK_NULL, tskit::TSK_NULL)?;
346-
let node1 = tables.add_node(0, params.nsteps as f64, tskit::TSK_NULL, tskit::TSK_NULL)?;
356+
let node0 = match tables.add_node(0, params.nsteps as f64, tskit::TSK_NULL, tskit::TSK_NULL)
357+
{
358+
Ok(x) => x,
359+
Err(e) => panic!("{}", e),
360+
};
361+
let node1 = match tables.add_node(0, params.nsteps as f64, tskit::TSK_NULL, tskit::TSK_NULL)
362+
{
363+
Ok(x) => x,
364+
Err(e) => panic!("{}", e),
365+
};
347366
alive.push(Diploid { node0, node1 });
348367
}
349368

@@ -353,32 +372,29 @@ fn runsim(params: &SimParams) -> Result<tskit::TableCollection, Box<dyn std::err
353372
for step in (0..params.nsteps).rev() {
354373
parents.clear();
355374
death_and_parents(&alive, &params, &mut parents, &mut rng);
356-
births(&parents, &params, step, &mut tables, &mut alive, &mut rng)?;
375+
births(&parents, &params, step, &mut tables, &mut alive, &mut rng);
357376
let remainder = step % params.simplification_interval;
358377
match step < params.nsteps && remainder == 0 {
359378
true => {
360-
simplify(&mut alive, &mut tables)?;
379+
simplify(&mut alive, &mut tables);
361380
simplified = true;
362381
}
363382
false => simplified = false,
364383
}
365384
}
366385

367386
if !simplified {
368-
simplify(&mut alive, &mut tables)?;
387+
simplify(&mut alive, &mut tables);
369388
}
370389

371-
Ok(tables)
390+
tables
372391
}
373392

374393
fn main() {
375394
let params = SimParams::new();
376395
params.validate().unwrap();
377396

378-
let tables = match runsim(&params) {
379-
Ok(t) => t,
380-
Err(e) => panic!("{}", e),
381-
};
397+
let tables = runsim(&params);
382398
let treeseq = tables
383399
.tree_sequence(tskit::TreeSequenceFlags::BUILD_INDEXES)
384400
.unwrap();
@@ -392,7 +408,7 @@ fn main() {
392408
fn test_bad_genome_length() {
393409
let mut params = SimParams::new();
394410
params.genome_length = -1.0;
395-
let _tables = runsim(&params).unwrap();
411+
let _tables = runsim(&params);
396412
}
397413

398414
#[test]
@@ -401,10 +417,7 @@ fn test_nonoverlapping_generations() {
401417
params.nsteps = 100;
402418
params.xovers = 1e-3;
403419
params.validate().unwrap();
404-
match runsim(&params) {
405-
Ok(_) => (),
406-
Err(e) => panic!("{}", e),
407-
}
420+
runsim(&params);
408421
}
409422

410423
#[test]
@@ -415,8 +428,5 @@ fn test_overlapping_generations() {
415428
params.psurvival = 0.25;
416429
params.seed = 616161;
417430
params.validate().unwrap();
418-
match runsim(&params) {
419-
Ok(_) => (),
420-
Err(e) => panic!("{}", e),
421-
}
431+
runsim(&params);
422432
}

0 commit comments

Comments
 (0)