Skip to content

Commit 91c9b97

Browse files
committed
Add validation step to simplification.
Make existing error types provide PartialEq.
1 parent 6491fce commit 91c9b97

File tree

7 files changed

+87
-32
lines changed

7 files changed

+87
-32
lines changed

forrustts_examples/neutral_wf.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use clap::{value_t, value_t_or_exit, App, Arg};
22
use forrustts::wright_fisher::*;
3+
use forrustts::SimplificationFlags;
34

45
fn main() {
56
let matches = App::new("neutral_wf")
@@ -58,6 +59,13 @@ fn main() {
5859
.help("Survival probability")
5960
.takes_value(true),
6061
)
62+
.arg(
63+
Arg::with_name("validate_tables")
64+
.short("v")
65+
.long("validate_tables")
66+
.help("Validate all tables prior to simplification")
67+
.takes_value(false),
68+
)
6169
.get_matches();
6270

6371
// TODO: default params
@@ -69,6 +77,7 @@ fn main() {
6977
let psurvival = value_t!(matches.value_of("psurvival"), f64).unwrap_or(0.0);
7078
let seed = value_t_or_exit!(matches.value_of("seed"), usize);
7179
let outfile = value_t_or_exit!(matches.value_of("outfile"), String);
80+
let validate_tables = matches.is_present("validate_tables");
7281

7382
// TODO: parameter validation..
7483

@@ -86,6 +95,12 @@ fn main() {
8695
SimulationFlags::USE_STATE
8796
};
8897

98+
let mut simplification_flags = SimplificationFlags::empty();
99+
100+
if validate_tables {
101+
simplification_flags |= SimplificationFlags::VALIDATE_ALL;
102+
}
103+
89104
let (mut tables, is_sample) = neutral_wf(
90105
PopulationParams {
91106
size: popsize,
@@ -98,6 +113,7 @@ fn main() {
98113
seed,
99114
nsteps: g,
100115
flags,
116+
simplification_flags,
101117
},
102118
)
103119
.unwrap();

src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use thiserror::Error;
66
///
77
/// Some members of this enum implement ``From``
88
/// in order to redirect other error types.
9-
#[derive(Error, Debug)]
9+
#[derive(Error, Debug, PartialEq)]
1010
pub enum ForrusttsError {
1111
/// An error that occurs during simplification.
1212
#[error("{value:?}")]

src/nested_forward_list.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use thiserror::Error;
1212

1313
/// Errror type for [``NestedForwardList``] operations.
14-
#[derive(Error, Debug)]
14+
#[derive(Error, Debug, PartialEq)]
1515
pub enum NestedForwardListError {
1616
/// Tail of a list is unexpectedly null.
1717
#[error("Tail is null")]

src/simplification_common.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/// Common functions to reuse in various "simplify tables"
22
/// functions
33
use crate::simplification_logic;
4+
use crate::validate_edge_table;
45
use crate::ForrusttsError;
56
use crate::SamplesInfo;
67
use crate::SimplificationBuffers;
@@ -9,6 +10,16 @@ use crate::SimplificationOutput;
910
use crate::{IdType, NULL_ID};
1011
use crate::{Node, TableCollection};
1112

13+
pub fn validate_tables(
14+
tables: &TableCollection,
15+
flags: &SimplificationFlags,
16+
) -> Result<(), ForrusttsError> {
17+
if flags.contains(SimplificationFlags::VALIDATE_EDGES) {
18+
validate_edge_table(tables.genome_length(), tables.edges(), tables.nodes())?;
19+
}
20+
Ok(())
21+
}
22+
1223
fn setup_idmap(nodes: &[Node], idmap: &mut Vec<IdType>) {
1324
idmap.resize(nodes.len(), NULL_ID);
1425
idmap.iter_mut().for_each(|x| *x = NULL_ID);
@@ -27,12 +38,7 @@ pub fn setup_simplification(
2738
});
2839
}
2940

30-
if flags.bits() != 0 {
31-
return Err(ForrusttsError::SimplificationError {
32-
value: "SimplificationFlags must be zero".to_string(),
33-
});
34-
}
35-
41+
validate_tables(tables, &flags)?;
3642
setup_idmap(&tables.nodes_, &mut output.idmap);
3743

3844
state.clear();

src/simplify_tables.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,43 @@ pub fn simplify_tables(
9191

9292
Ok(())
9393
}
94+
95+
#[cfg(test)]
96+
mod tests {
97+
use super::*;
98+
99+
// TODO: we need lots more tests of these validations!
100+
101+
#[test]
102+
fn test_simplify_tables_unsorted_edges() {
103+
let mut tables = TableCollection::new(1000).unwrap();
104+
105+
tables.add_node(0, 0).unwrap(); // parent
106+
tables.add_node(1, 0).unwrap(); // child
107+
tables.add_edge(100, tables.genome_length(), 0, 1).unwrap();
108+
tables.add_edge(0, 100, 0, 1).unwrap();
109+
110+
let mut output = SimplificationOutput::new();
111+
112+
let mut samples = SamplesInfo::new();
113+
samples.samples.push(1);
114+
115+
let _ = simplify_tables_without_state(
116+
&samples,
117+
SimplificationFlags::VALIDATE_ALL,
118+
&mut tables,
119+
&mut output,
120+
)
121+
.map_or_else(
122+
|x: ForrusttsError| {
123+
assert_eq!(
124+
x,
125+
ForrusttsError::TablesError {
126+
value: TablesError::EdgesNotSortedByLeft
127+
}
128+
)
129+
},
130+
|_| panic!(),
131+
);
132+
}
133+
}

src/test_simplify_tables.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ mod test {
3838
genome_length: Position,
3939
psurvival: f64,
4040
seed: usize,
41+
// None here means "never simplify".
4142
simplification_interval: Option<Time>,
4243
flags: SimulationFlags,
4344
) -> Result<(TableCollection, Vec<i32>), ForrusttsError> {
44-
// None here means "never simplify".
4545
neutral_wf(
4646
PopulationParams {
4747
size: 250,
@@ -54,6 +54,7 @@ mod test {
5454
seed,
5555
nsteps: num_generations,
5656
flags,
57+
simplification_flags: SimplificationFlags::VALIDATE_ALL,
5758
},
5859
)
5960
}

src/wright_fisher.rs

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//! to others. Feel free to copy them!
88
use crate::simplify_from_edge_buffer::simplify_from_edge_buffer;
99
use crate::simplify_tables::*;
10-
use crate::tables::{validate_edge_table, TableCollection};
10+
use crate::tables::TableCollection;
1111
use crate::tsdef::*;
1212
use crate::EdgeBuffer;
1313
use crate::ForrusttsError;
@@ -300,47 +300,31 @@ fn fill_samples(parents: &[Parent], samples: &mut SamplesInfo) {
300300

301301
fn sort_and_simplify(
302302
flags: SimulationFlags,
303+
simplification_flags: SimplificationFlags,
303304
samples: &SamplesInfo,
304305
state: &mut SimplificationBuffers,
305306
pop: &mut PopulationState,
306307
output: &mut SimplificationOutput,
307308
) {
308309
if !flags.contains(SimulationFlags::BUFFER_EDGES) {
309310
pop.tables.sort_tables(crate::TableSortingFlags::empty());
310-
debug_assert!(validate_edge_table(
311-
pop.tables.genome_length(),
312-
pop.tables.edges(),
313-
pop.tables.nodes()
314-
)
315-
.unwrap());
316311
if flags.contains(SimulationFlags::USE_STATE) {
317312
simplify_tables(
318313
samples,
319-
SimplificationFlags::empty(),
314+
simplification_flags,
320315
state,
321316
&mut pop.tables,
322317
output,
323318
)
324319
.unwrap();
325320
} else {
326-
simplify_tables_without_state(
327-
samples,
328-
SimplificationFlags::empty(),
329-
&mut pop.tables,
330-
output,
331-
)
332-
.unwrap();
321+
simplify_tables_without_state(samples, simplification_flags, &mut pop.tables, output)
322+
.unwrap();
333323
}
334-
debug_assert!(validate_edge_table(
335-
pop.tables.genome_length(),
336-
pop.tables.edges(),
337-
pop.tables.nodes()
338-
)
339-
.unwrap());
340324
} else {
341325
simplify_from_edge_buffer(
342326
samples,
343-
SimplificationFlags::empty(),
327+
simplification_flags,
344328
state,
345329
&mut pop.edge_buffer,
346330
&mut pop.tables,
@@ -352,13 +336,14 @@ fn sort_and_simplify(
352336

353337
fn simplify_and_remap_nodes(
354338
flags: SimulationFlags,
339+
simplification_flags: SimplificationFlags,
355340
samples: &mut SamplesInfo,
356341
state: &mut SimplificationBuffers,
357342
pop: &mut PopulationState,
358343
output: &mut SimplificationOutput,
359344
) {
360345
fill_samples(&pop.parents, samples);
361-
sort_and_simplify(flags, samples, state, pop, output);
346+
sort_and_simplify(flags, simplification_flags, samples, state, pop, output);
362347

363348
for p in &mut pop.parents {
364349
p.node0 = output.idmap[p.node0 as usize];
@@ -451,10 +436,14 @@ pub struct SimulationParams {
451436
/// Bitwise flag tweaking the behavior of the
452437
/// simplification algorithm.
453438
pub flags: SimulationFlags,
439+
/// Flags to affect simplification behavior
440+
pub simplification_flags: SimplificationFlags,
454441
}
455442

456443
impl SimulationParams {
457444
/// Create a new instance
445+
/// [``SimulationParams::simplification_flags``]
446+
/// is initialized to empty.
458447
pub fn new(
459448
simplification_interval: Option<Time>,
460449
seed: usize,
@@ -466,6 +455,7 @@ impl SimulationParams {
466455
seed,
467456
nsteps,
468457
flags,
458+
simplification_flags: SimplificationFlags::empty(),
469459
}
470460
}
471461
}
@@ -561,6 +551,7 @@ pub fn neutral_wf(
561551
{
562552
simplify_and_remap_nodes(
563553
params.flags,
554+
params.simplification_flags,
564555
&mut samples,
565556
&mut state,
566557
&mut pop,
@@ -575,6 +566,7 @@ pub fn neutral_wf(
575566
if !simplified && actual_simplification_interval != -1 {
576567
simplify_and_remap_nodes(
577568
params.flags,
569+
params.simplification_flags,
578570
&mut samples,
579571
&mut state,
580572
&mut pop,

0 commit comments

Comments
 (0)