Skip to content

Commit 2414e8a

Browse files
committed
Ensure that parameters are sorted lexicographically when reading SBML models.
1 parent 2a71e53 commit 2414e8a

File tree

2 files changed

+80
-10
lines changed

2 files changed

+80
-10
lines changed

src/sbml/import/mod.rs

+27-10
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,31 @@ impl BooleanNetwork {
8989
create_regulations(&mut regulatory_graph, &transitions, &specie_to_name)?;
9090

9191
let mut boolean_network = BooleanNetwork::new(regulatory_graph);
92+
93+
// First, collect and create parameters (in alphabetic order)
94+
let mut parameters = HashMap::new();
9295
for transition in &transitions {
9396
if transition.default_term.is_some() {
94-
// Only continue if this transition has a function
95-
// First, create parameters used by this transition.
97+
// First, register parameters used by this transition.
9698
for term in &transition.function_terms {
9799
if let Some(math) = &term.math {
98-
create_explicit_parameters(math, &mut boolean_network)?;
100+
create_explicit_parameters(math, &mut parameters)?;
99101
}
100102
}
103+
}
104+
}
105+
106+
let mut parameters = Vec::from_iter(parameters);
107+
parameters.sort_by_cached_key(|(it, _)| it.clone());
108+
for (name, arity) in parameters {
109+
boolean_network.add_parameter(name.as_str(), arity)?;
110+
}
101111

102-
// At this point we know this won't fail because we already created the regulations.
112+
// Then actually go through the functions and create them.
113+
for transition in &transitions {
114+
if transition.default_term.is_some() {
115+
// At this point we know this won't fail because we already
116+
// created the regulations and parameters.
103117
let out_var = &specie_to_name[&transition.outputs[0].qual_species];
104118
let out_var = boolean_network.graph.find_variable(out_var).unwrap();
105119
let update_function = sbml_transition_to_update_function(
@@ -339,20 +353,23 @@ fn create_regulations(
339353
}
340354

341355
/// **(internal)** Create any explicit parameters used in the given MathML tree.
342-
fn create_explicit_parameters(math: &MathMl, network: &mut BooleanNetwork) -> Result<(), String> {
356+
fn create_explicit_parameters(
357+
math: &MathMl,
358+
params: &mut HashMap<String, u32>,
359+
) -> Result<(), String> {
343360
match math {
344361
MathMl::Boolean(_) => Ok(()),
345362
MathMl::Integer(_) => Ok(()),
346363
MathMl::Identifier(_) => Ok(()),
347364
MathMl::Apply(_, args) => {
348365
for a in args {
349-
create_explicit_parameters(a, network)?;
366+
create_explicit_parameters(a, params)?;
350367
}
351368
Ok(())
352369
}
353370
MathMl::SymbolApply(name, args) => {
354-
if let Some(p) = network.find_parameter(name) {
355-
let current = network.get_parameter(p).get_arity();
371+
if let Some(p) = params.get(name) {
372+
let current = *p;
356373
if current != u32::try_from(args.len()).unwrap() {
357374
return Err(format!(
358375
"Parameter `{}` is used with cardinality {} as well as {}",
@@ -363,11 +380,11 @@ fn create_explicit_parameters(math: &MathMl, network: &mut BooleanNetwork) -> Re
363380
}
364381
} else {
365382
// Seems that at the moment there are no restrictions on parameter names (weird).
366-
network.add_parameter(name, u32::try_from(args.len()).unwrap())?;
383+
params.insert(name.clone(), u32::try_from(args.len()).unwrap());
367384
}
368385
// Also run this, in case we allow complex parameter applications in the future.
369386
for a in args {
370-
create_explicit_parameters(a, network)?;
387+
create_explicit_parameters(a, params)?;
371388
}
372389
Ok(())
373390
}

src/sbml/mod.rs

+53
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,56 @@ pub mod export;
1313

1414
/// A layout type for transferring information about node position from SBML files.
1515
pub type Layout = HashMap<String, (f64, f64)>;
16+
17+
#[cfg(test)]
18+
mod tests {
19+
use crate::BooleanNetwork;
20+
21+
#[test]
22+
fn test_sbml_extended_support() {
23+
let model = BooleanNetwork::try_from(
24+
"\
25+
$C:(A & g(C, B))
26+
A -> C
27+
B -> C
28+
C -| C
29+
$A:h(C)
30+
C -?? A
31+
$B:f(A, D)
32+
A -> B
33+
D -> B
34+
D -? D
35+
",
36+
)
37+
.unwrap();
38+
let (model2, _layout) =
39+
BooleanNetwork::try_from_sbml(model.to_sbml(None).as_str()).unwrap();
40+
assert_eq!(
41+
model.as_graph().find_variable("A"),
42+
model.as_graph().find_variable("A")
43+
);
44+
assert_eq!(
45+
model.as_graph().find_variable("B"),
46+
model.as_graph().find_variable("B")
47+
);
48+
assert_eq!(
49+
model.as_graph().find_variable("C"),
50+
model.as_graph().find_variable("C")
51+
);
52+
assert_eq!(
53+
model.as_graph().find_variable("D"),
54+
model.as_graph().find_variable("D")
55+
);
56+
57+
for s in model.variables() {
58+
for t in model.variables() {
59+
assert_eq!(
60+
model.as_graph().find_regulation(s, t),
61+
model2.as_graph().find_regulation(s, t)
62+
);
63+
}
64+
}
65+
66+
assert_eq!(model2, model);
67+
}
68+
}

0 commit comments

Comments
 (0)