Skip to content

Commit

Permalink
Merge branch 'master' into 0.5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
daemontus committed Dec 20, 2023
2 parents 6ad3002 + d352b20 commit 16067fa
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 43 deletions.
98 changes: 59 additions & 39 deletions src/_impl_fn_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::_aeon_parser::FnUpdateTemp;
use crate::{BinaryOp, BooleanNetwork, FnUpdate, ParameterId, VariableId};
use biodivine_lib_bdd::{Bdd, BddVariable};
use std::collections::{HashMap, HashSet};
use std::fmt::{Display, Formatter};

/// Constructor and destructor utility methods. These mainly avoid unnecessary boxing
/// and exhaustive pattern matching when not necessary.
Expand Down Expand Up @@ -258,50 +259,63 @@ impl FnUpdate {
result
}

/// Convert this update function to a string, taking names from the provided `BooleanNetwork`.
pub fn to_string(&self, context: &BooleanNetwork) -> String {
fn to_string_rec(function: &FnUpdate, context: &BooleanNetwork, no_paren: bool) -> String {
match function {
Const(value) => value.to_string(),
Var(id) => context.get_variable_name(*id).to_string(),
Not(inner) => format!("!{}", to_string_rec(inner, context, false)),
Binary(op, l, r) => {
// And/Or have special treatments so that chains don't produce
// unnecessary parenthesis.
let l_nested_and = *op == And && matches!(**l, Binary(And, _, _));
let r_nested_and = *op == And && matches!(**r, Binary(And, _, _));
let l_nested_or = *op == Or && matches!(**l, Binary(Or, _, _));
let r_nested_or = *op == Or && matches!(**r, Binary(Or, _, _));

let l_no_paren = l_nested_and || l_nested_or;
let r_no_paren = r_nested_and || r_nested_or;

let l = to_string_rec(l, context, l_no_paren);
let r = to_string_rec(r, context, r_no_paren);

if no_paren {
format!("{} {} {}", l, op, r)
} else {
format!("({} {} {})", l, op, r)
}
/// Private to_string helper.
fn to_string_rec(&self, context: Option<&BooleanNetwork>, no_paren: bool) -> String {
match self {
Const(value) => value.to_string(),
Var(id) => {
if let Some(ctx) = context {
ctx.get_variable_name(*id).to_string()
} else {
format!("v_{}", id.to_index())
}
Param(id, args) => {
if args.is_empty() {
context[*id].get_name().to_string()
} else {
// No need for top-level parenthesis in parameters, since commas
// serve the same purpose.
let mut arg_string = format!("({}", to_string_rec(&args[0], context, true));
for arg in args.iter().skip(1) {
arg_string =
format!("{}, {}", arg_string, to_string_rec(arg, context, true));
}
format!("{}{})", context[*id].get_name(), arg_string)
}
Not(inner) => format!("!{}", Self::to_string_rec(inner, context, false)),
Binary(op, l, r) => {
// And/Or have special treatments so that chains don't produce
// unnecessary parenthesis.
let l_nested_and = *op == And && matches!(**l, Binary(And, _, _));
let r_nested_and = *op == And && matches!(**r, Binary(And, _, _));
let l_nested_or = *op == Or && matches!(**l, Binary(Or, _, _));
let r_nested_or = *op == Or && matches!(**r, Binary(Or, _, _));

let l_no_paren = l_nested_and || l_nested_or;
let r_no_paren = r_nested_and || r_nested_or;

let l = l.to_string_rec(context, l_no_paren);
let r = r.to_string_rec(context, r_no_paren);

if no_paren {
format!("{} {} {}", l, op, r)
} else {
format!("({} {} {})", l, op, r)
}
}
Param(id, args) => {
let name = if let Some(ctx) = context {
ctx[*id].get_name().to_string()
} else {
format!("p_{}", id.to_index())
};
if args.is_empty() {
name
} else {
// No need for top-level parenthesis in parameters, since commas
// serve the same purpose.
let mut arg_string = format!("({}", args[0].to_string_rec(context, true));
for arg in args.iter().skip(1) {
arg_string =
format!("{}, {}", arg_string, arg.to_string_rec(context, true));
}
format!("{}{})", name, arg_string)
}
}
}
to_string_rec(self, context, true)
}

/// Convert this update function to a string, taking names from the provided `BooleanNetwork`.
pub fn to_string(&self, context: &BooleanNetwork) -> String {
self.to_string_rec(Some(context), true)
}

/// If possible, evaluate this function using the given network variable valuation.
Expand Down Expand Up @@ -667,6 +681,12 @@ impl FnUpdate {
}
}

impl Display for FnUpdate {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.to_string_rec(None, true))
}
}

#[cfg(test)]
mod tests {
use crate::symbolic_async_graph::SymbolicContext;
Expand Down
34 changes: 30 additions & 4 deletions src/symbolic_async_graph/_impl_symbolic_context.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::symbolic_async_graph::{FunctionTable, SymbolicContext};
use crate::{BinaryOp, BooleanNetwork, FnUpdate, ParameterId, VariableId, VariableIdIterator};
use crate::{
BinaryOp, BooleanNetwork, FnUpdate, ParameterId, ParameterIdIterator, VariableId,
VariableIdIterator,
};
use biodivine_lib_bdd::op_function::{and, and_not};
use biodivine_lib_bdd::{
bdd, Bdd, BddValuation, BddVariable, BddVariableSet, BddVariableSetBuilder,
Expand Down Expand Up @@ -132,6 +135,11 @@ impl SymbolicContext {
})
}

/// Iterator over all [VariableId] network variables managed by this [SymbolicContext].
pub fn network_variables(&self) -> VariableIdIterator {
(0..self.num_state_variables()).map(VariableId::from_index)
}

/// Obtain a [VariableId] of a state variable, assuming such state variable exists.
pub fn find_network_variable(&self, name: &str) -> Option<VariableId> {
self.bdd
Expand All @@ -144,9 +152,27 @@ impl SymbolicContext {
self.bdd.name_of(self.state_variables[variable.to_index()])
}

/// Iterator over all [VariableId] network variables managed by this [SymbolicContext].
pub fn network_variables(&self) -> VariableIdIterator {
(0..self.num_state_variables()).map(VariableId::from_index)
/// Iterator over all [ParameterId] of the parameter functions managed by this [SymbolicContext].
pub fn network_parameters(&self) -> ParameterIdIterator {
(0..self.explicit_function_tables.len()).map(ParameterId::from_index)
}

/// Obtain a [ParameterId] of a parameter function based on a name.
pub fn find_network_parameter(&self, name: &str) -> Option<ParameterId> {
self.explicit_function_tables
.iter()
.position(|it| it.name.as_str() == name)
.map(ParameterId::from_index)
}

/// Obtain the arity of an underlying parameter function.
pub fn get_network_parameter_arity(&self, id: ParameterId) -> u16 {
self.explicit_function_tables[id.to_index()].arity
}

/// Obtain the name of an underlying parameter function.
pub fn get_network_parameter_name(&self, id: ParameterId) -> String {
self.explicit_function_tables[id.to_index()].name.clone()
}

/// Create a new [SymbolicContext] which is compatible with the current context (it uses the same
Expand Down

0 comments on commit 16067fa

Please sign in to comment.