use crate::algorithms::fo_logic::fol_tree::{FolTreeNode, NodeType};
use crate::algorithms::fo_logic::operator_enums::*;
use crate::algorithms::fo_logic::utils::{get_var_base_and_offset, get_var_from_implicit};
use biodivine_lib_bdd::Bdd;
use biodivine_lib_param_bn::biodivine_std::traits::Set;
use biodivine_lib_param_bn::symbolic_async_graph::{GraphColoredVertices, SymbolicAsyncGraph};
pub fn eval_node(node: FolTreeNode, graph: &SymbolicAsyncGraph) -> GraphColoredVertices {
match node.node_type {
NodeType::Terminal(atom) => match atom {
Atom::True => graph.mk_unit_colored_vertices(),
Atom::False => graph.mk_empty_colored_vertices(),
Atom::Var(name) => eval_variable(graph, name.as_str()),
},
NodeType::Unary(op, child) => match op {
UnaryOp::Not => eval_neg(graph, &eval_node(*child, graph)),
},
NodeType::Binary(op, left, right) => {
let left = eval_node(*left, graph);
let right = eval_node(*right, graph);
match op {
BinaryOp::And => left.intersect(&right),
BinaryOp::Or => left.union(&right),
BinaryOp::Xor => eval_xor(graph, &left, &right),
BinaryOp::Imp => eval_imp(graph, &left, &right),
BinaryOp::Iff => eval_equiv(graph, &left, &right),
}
}
NodeType::Quantifier(op, var_name, child) => match op {
Quantifier::Exists => eval_exists(graph, &eval_node(*child, graph), &var_name),
Quantifier::Forall => eval_forall(graph, &eval_node(*child, graph), &var_name),
},
NodeType::Function(fn_symbol, arguments) => {
let name = fn_symbol.name;
let arguments = arguments.into_iter().map(|a| *a).collect();
if fn_symbol.is_update_fn {
eval_applied_update_function(graph, &name, arguments)
} else {
eval_applied_uninterpred_function(graph, &name, arguments)
}
}
}
}
fn eval_neg(graph: &SymbolicAsyncGraph, set: &GraphColoredVertices) -> GraphColoredVertices {
let unit_set = graph.mk_unit_colored_vertices();
unit_set.minus(set)
}
fn eval_imp(
graph: &SymbolicAsyncGraph,
left: &GraphColoredVertices,
right: &GraphColoredVertices,
) -> GraphColoredVertices {
eval_neg(graph, left).union(right)
}
fn eval_equiv(
graph: &SymbolicAsyncGraph,
left: &GraphColoredVertices,
right: &GraphColoredVertices,
) -> GraphColoredVertices {
left.intersect(right)
.union(&eval_neg(graph, left).intersect(&eval_neg(graph, right)))
}
fn eval_xor(
graph: &SymbolicAsyncGraph,
left: &GraphColoredVertices,
right: &GraphColoredVertices,
) -> GraphColoredVertices {
eval_neg(graph, &eval_equiv(graph, left, right))
}
fn eval_variable(graph: &SymbolicAsyncGraph, var_name: &str) -> GraphColoredVertices {
let bn = graph.as_network().unwrap();
let (base_var_name, offset) = get_var_base_and_offset(var_name).unwrap();
let base_variable = bn.as_graph().find_variable(&base_var_name).unwrap();
let bdd = graph
.symbolic_context()
.mk_extra_state_variable_is_true(base_variable, offset);
GraphColoredVertices::new(bdd, graph.symbolic_context())
.intersect(graph.unit_colored_vertices())
}
fn eval_applied_uninterpred_function(
graph: &SymbolicAsyncGraph,
fn_name: &str,
arguments: Vec<FolTreeNode>,
) -> GraphColoredVertices {
let arguments_results: Vec<GraphColoredVertices> = arguments
.into_iter()
.map(|child| eval_node(child, graph))
.collect();
let bn = graph.as_network().unwrap();
let function = bn.find_parameter(fn_name).unwrap();
let fn_table = graph
.symbolic_context()
.get_explicit_function_table(function);
let arguments_bdds: Vec<Bdd> = arguments_results
.into_iter()
.map(|x| x.into_bdd())
.collect();
let bdd = graph
.symbolic_context()
.mk_function_table_true(fn_table, &arguments_bdds);
GraphColoredVertices::new(bdd, graph.symbolic_context())
.intersect(graph.unit_colored_vertices())
}
fn eval_applied_update_function(
graph: &SymbolicAsyncGraph,
fn_name: &str,
arguments: Vec<FolTreeNode>,
) -> GraphColoredVertices {
let var_name = get_var_from_implicit(fn_name).unwrap();
let bn = graph.as_network().unwrap();
let variable = bn.as_graph().find_variable(&var_name).unwrap();
if let Some(update_fn) = bn.get_update_function(variable) {
let mut converted_update_fn = FolTreeNode::from_fn_update(update_fn.clone(), bn);
let input_vars = bn.regulators(variable);
for (input_var, expression) in input_vars.iter().zip(arguments) {
let input_name = bn.get_variable_name(*input_var);
converted_update_fn = converted_update_fn.substitute_variable(input_name, &expression);
}
eval_node(converted_update_fn, graph)
} else {
unreachable!()
}
}
fn eval_exists(
graph: &SymbolicAsyncGraph,
set: &GraphColoredVertices,
var_name: &str,
) -> GraphColoredVertices {
let bn = graph.as_network().unwrap();
let (base_var_name, offset) = get_var_base_and_offset(var_name).unwrap();
let variable = bn.as_graph().find_variable(&base_var_name).unwrap();
let bbd_var = graph
.symbolic_context()
.get_extra_state_variable(variable, offset);
let bbd_var_singleton = vec![bbd_var];
let result_bdd = set.as_bdd().exists(&bbd_var_singleton);
GraphColoredVertices::new(result_bdd, graph.symbolic_context())
}
fn eval_forall(
graph: &SymbolicAsyncGraph,
set: &GraphColoredVertices,
var_name: &str,
) -> GraphColoredVertices {
eval_neg(graph, &eval_exists(graph, &eval_neg(graph, set), var_name))
}