use crate::*;
use deep_causality_haft::LogAppend;
use std::collections::VecDeque;
use std::fmt::Debug;
use ultragraph::GraphTraversal;
pub trait MonadicCausableGraphReasoning<V, PS, C>: CausableGraph<Causaloid<V, V, PS, C>>
where
V: Default + Clone + Send + Sync + 'static + Debug,
PS: Default + Clone + Send + Sync + 'static,
C: Clone + Send + Sync + 'static,
Causaloid<V, V, PS, C>: MonadicCausable<V, V>,
{
fn evaluate_single_cause(
&self,
index: usize,
effect: &PropagatingEffect<V>,
) -> PropagatingEffect<V> {
if !self.is_frozen() {
return PropagatingEffect::from_error(CausalityError(CausalityErrorEnum::Custom(
"Graph is not frozen. Call freeze() first".into(),
)));
}
let causaloid = match self.get_causaloid(index) {
Some(c) => c,
None => {
return PropagatingEffect::from_error(CausalityError(CausalityErrorEnum::Custom(
format!("Causaloid with index {index} not found in graph"),
)));
}
};
causaloid.evaluate(effect)
}
fn evaluate_subgraph_from_cause(
&self,
start_index: usize,
initial_effect: &PropagatingEffect<V>,
) -> PropagatingEffect<V> {
if !self.is_frozen() {
return PropagatingEffect::from_error(CausalityError(CausalityErrorEnum::Custom(
"Graph is not frozen. Call freeze() first".into(),
)));
}
if !self.contains_causaloid(start_index) {
return PropagatingEffect::from_error(CausalityError(CausalityErrorEnum::Custom(
format!("Graph does not contain start causaloid with index {start_index}"),
)));
}
let mut queue =
VecDeque::<(usize, PropagatingEffect<V>)>::with_capacity(self.number_nodes());
let mut visited = vec![false; self.number_nodes()];
queue.push_back((start_index, initial_effect.clone()));
visited[start_index] = true;
let mut last_propagated_effect = initial_effect.clone();
while let Some((current_index, incoming_effect)) = queue.pop_front() {
let causaloid = match self.get_causaloid(current_index) {
Some(c) => c,
None => {
return PropagatingEffect::from_error(CausalityError(
CausalityErrorEnum::Custom(format!(
"Failed to get causaloid at index {current_index}"
)),
));
}
};
let result_effect = causaloid.evaluate(&incoming_effect);
last_propagated_effect = result_effect.clone();
if result_effect.is_err() {
return result_effect;
}
match &result_effect.value {
EffectValue::RelayTo(target_index, inner_effect) => {
visited.fill(false);
queue.clear();
let target_idx = *target_index;
if !self.contains_causaloid(target_idx) {
let mut err_effect = last_propagated_effect.clone();
err_effect.error = Some(CausalityError(CausalityErrorEnum::Custom(
format!(
"RelayTo target causaloid with index {target_idx} not found in graph."
),
)));
return err_effect;
}
visited[target_idx] = true;
let mut relayed = *inner_effect.clone();
relayed.logs.append(&mut last_propagated_effect.logs);
queue.push_back((target_idx, relayed));
}
_ => {
let children = match self.get_graph().outbound_edges(current_index) {
Ok(c) => c,
Err(e) => {
let mut err_effect = last_propagated_effect.clone();
err_effect.error =
Some(CausalityError(CausalityErrorEnum::Custom(format!("{e}"))));
return err_effect;
}
};
for child_index in children {
if !visited[child_index] {
visited[child_index] = true;
queue.push_back((child_index, result_effect.clone()));
}
}
}
}
}
last_propagated_effect
}
fn evaluate_shortest_path_between_causes(
&self,
start_index: usize,
stop_index: usize,
initial_effect: &PropagatingEffect<V>,
) -> PropagatingEffect<V> {
if !self.is_frozen() {
return PropagatingEffect::from_error(CausalityError(CausalityErrorEnum::Custom(
"Graph is not frozen. Call freeze() first".into(),
)));
}
if start_index == stop_index {
let causaloid = match self.get_causaloid(start_index) {
Some(c) => c,
None => {
return PropagatingEffect::from_error(CausalityError(
CausalityErrorEnum::Custom(format!(
"Failed to get causaloid at index {start_index}"
)),
));
}
};
return causaloid.evaluate(initial_effect);
}
let path = match self.get_shortest_path(start_index, stop_index) {
Ok(p) => p,
Err(e) => {
return PropagatingEffect::from_error(CausalityError(CausalityErrorEnum::Custom(
format!("{:?}", e),
)));
}
};
let mut current_effect = initial_effect.clone();
for index in path {
let causaloid = match self.get_causaloid(index) {
Some(c) => c,
None => {
return PropagatingEffect::from_error(CausalityError(
CausalityErrorEnum::Custom(format!(
"Failed to get causaloid at index {index}"
)),
));
}
};
current_effect = causaloid.evaluate(¤t_effect);
if current_effect.is_err() {
return current_effect;
}
if let EffectValue::RelayTo(_, _) = current_effect.value {
return current_effect;
}
}
current_effect
}
}