1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
use crate::sketchbook::bn_utils;
use crate::sketchbook::ids::VarId;
use crate::sketchbook::model::ModelState;
use biodivine_lib_param_bn::RegulatoryGraph;
/// Methods for converting between `ModelState` and `RegulatoryGraph` (from the `lib-param-bn`).
impl ModelState {
/// Extract the regulatory graph (`RegulatoryGraph` object) from the `ModelState`.
/// Sorted variable IDs of the `ModelState` are used for variable names in `RegulatoryGraph`.
///
/// The conversion might loose some information, as the `RegulatoryGraph` does not support
/// all the variants of `Monotonicity` and `Essentiality`. See also [bn_utils::sign_to_monotonicity].
///
/// Note that we can convert the resulting `RegulatoryGraph` back, but the conversion loses
/// some information, like the original variable names and layout information.
/// Also, all of the other model components, such as `update functions` or `uninterpreted functions`
/// are not part of the `RegulatoryGraph`.
///
/// You can add optional extra variables (`extra_vars`).
pub fn to_reg_graph(&self, extra_vars: Option<Vec<String>>) -> RegulatoryGraph {
self._to_reg_graph(true, extra_vars)
}
/// Extract the regulatory graph (`RegulatoryGraph` object) from the `ModelState`.
/// Sorted variable IDs of the `ModelState` are used for variable names in `RegulatoryGraph`.
///
/// The types of regulations (their essentiality and monotonicity) are ignored, and unspecified
/// versions are used instead.
///
/// This might be useful in inference, if we want to process regulation types later via static
/// properties.
///
/// You can add optional extra variables (`extra_vars`).
pub fn to_reg_graph_with_unspecified_regs(
&self,
extra_vars: Option<Vec<String>>,
) -> RegulatoryGraph {
self._to_reg_graph(false, extra_vars)
}
/// Internal utility to extract the regulatory graph (`RegulatoryGraph` object) from the
/// `ModelState`. Sorted variable IDs of the `ModelState` are used for variable names in
/// `RegulatoryGraph`.
///
/// There are two modes based on `include_reg_types` argument. If it is set to `true`, the
/// types of regulations (their essentiality and monotonicity) are preserved. If it is set to
/// `false`, they are ignored, and unspecified versions are used instead.
///
/// You can add optional extra variables (`extra_vars`).
fn _to_reg_graph(
&self,
include_reg_types: bool,
extra_vars: Option<Vec<String>>,
) -> RegulatoryGraph {
// create `RegulatoryGraph` from a list of variable ID strings (these are unique and
// can be mapped back)
let mut variable_vec = self
.variables
.keys()
.map(|v| v.to_string())
.collect::<Vec<_>>();
if let Some(vars) = extra_vars {
variable_vec.extend(vars);
}
// sort the IDs, so that the process is kinda deterministic - in `RegulatoryGraph`, the
// order of the variables matters (but regulations order does not)
variable_vec.sort();
let mut reg_graph = RegulatoryGraph::new(variable_vec);
// regulations
for r in self.regulations() {
if include_reg_types {
// add the regulation with its original monotonicity and essentiality (as far as conversion allows)
reg_graph
.add_regulation(
r.get_regulator().as_str(),
r.get_target().as_str(),
r.is_essential(),
bn_utils::sign_to_monotonicity(r.get_sign()),
)
.unwrap();
// we can use unwrap, cause the regulation is ensured to be unique and correctly added
} else {
// add the regulation unspecified monotonicity and essentiality
reg_graph
.add_regulation(
r.get_regulator().as_str(),
r.get_target().as_str(),
false,
None,
)
.unwrap();
// we can use unwrap, cause the regulation is ensured to be unique and correctly added
}
}
reg_graph
}
/// Convert the `RegulatoryGraph` into the corresponding `ModelState` instance. A name
/// of the variable used in `RegulatoryGraph` (which should be unique) is used as both its ID
/// and name in the resulting `ModelState`.
///
/// Note that only the default layout (all nodes at 0,0) is created for the `ModelState`.
/// Variables' annotations are left empty.
pub fn from_reg_graph(reg_graph: &RegulatoryGraph) -> Result<ModelState, String> {
let mut model = ModelState::new_empty();
// variables
for v in reg_graph.variables() {
// name in the `RegulatoryGraph` is a unique valid identifier
let name_in_graph = reg_graph.get_variable_name(v);
model.add_var(VarId::new(name_in_graph.as_str())?, name_in_graph, "")?;
}
// regulations
for r in reg_graph.regulations() {
let name_regulator = reg_graph.get_variable_name(r.get_regulator());
let name_target = reg_graph.get_variable_name(r.get_target());
model.add_regulation(
VarId::new(name_regulator.as_str())?,
VarId::new(name_target.as_str())?,
bn_utils::essentiality_from_bool(r.is_observable()),
bn_utils::sign_from_monotonicity(r.get_monotonicity()),
)?;
}
Ok(model)
}
}
#[cfg(test)]
mod tests {
use crate::sketchbook::model::ModelState;
use biodivine_lib_param_bn::RegulatoryGraph;
/// Prepare a test model containing only variables and regulations.
fn prepare_test_model() -> ModelState {
let mut model = ModelState::new_from_vars(vec![("a", "a"), ("b", "b")]).unwrap();
model
.add_multiple_regulations(vec!["a -> b", "b -> a", "a -| a"])
.unwrap();
model
}
#[test]
fn test_to_reg_graph() {
let model = prepare_test_model();
let reg_graph = model.to_reg_graph(None);
let var_a = reg_graph.find_variable("a").unwrap();
let var_b = reg_graph.find_variable("b").unwrap();
assert_eq!(reg_graph.num_vars(), 2);
assert_eq!(reg_graph.regulators(var_a), vec![var_a, var_b]);
let model_back = ModelState::from_reg_graph(®_graph).unwrap();
assert_eq!(model, model_back);
}
#[test]
fn test_from_reg_graph() {
let mut reg_graph = RegulatoryGraph::new(vec!["a".to_string(), "b".to_string()]);
reg_graph.add_string_regulation("a -> b").unwrap();
reg_graph.add_string_regulation("b -> a").unwrap();
let model = ModelState::from_reg_graph(®_graph).unwrap();
assert_eq!(model.num_vars(), 2);
let reg_graph_back = model.to_reg_graph(None);
assert_eq!(reg_graph, reg_graph_back);
}
}