use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::fmt;
use std::marker::PhantomData;
use crate::law::{Between01, IsTrue, Require};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DependencyMeasure<const NUM: u64, const DEN: u64>
where
Require<{ DEN > 0 }>: IsTrue,
Require<{ NUM <= DEN }>: IsTrue,
{
_measure: Between01<NUM, DEN>,
}
impl<const NUM: u64, const DEN: u64> DependencyMeasure<NUM, DEN>
where
Require<{ DEN > 0 }>: IsTrue,
Require<{ NUM <= DEN }>: IsTrue,
{
pub const fn new() -> Self {
DependencyMeasure {
_measure: Between01::new(),
}
}
pub const fn num(&self) -> u64 {
NUM
}
pub const fn den(&self) -> u64 {
DEN
}
pub fn as_f64(&self) -> f64 {
NUM as f64 / DEN as f64
}
}
impl<const NUM: u64, const DEN: u64> Default for DependencyMeasure<NUM, DEN>
where
Require<{ DEN > 0 }>: IsTrue,
Require<{ NUM <= DEN }>: IsTrue,
{
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct InputBinding<A, B> {
pub source: A,
pub target: B,
}
impl<A, B> InputBinding<A, B> {
pub const fn new(source: A, target: B) -> Self {
InputBinding { source, target }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct OutputBinding<A, B> {
pub source: A,
pub target: B,
}
impl<A, B> OutputBinding<A, B> {
pub const fn new(source: A, target: B) -> Self {
OutputBinding { source, target }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CausalNetConst<Nodes, Arcs, Inputs, Outputs> {
_nodes: PhantomData<Nodes>,
_arcs: PhantomData<Arcs>,
_inputs: PhantomData<Inputs>,
_outputs: PhantomData<Outputs>,
}
impl<Nodes, Arcs, Inputs, Outputs> CausalNetConst<Nodes, Arcs, Inputs, Outputs> {
pub const fn new() -> Self {
CausalNetConst {
_nodes: PhantomData,
_arcs: PhantomData,
_inputs: PhantomData,
_outputs: PhantomData,
}
}
}
impl<Nodes, Arcs, Inputs, Outputs> Default for CausalNetConst<Nodes, Arcs, Inputs, Outputs> {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct CausalNet {
pub nodes: Vec<String>,
pub initial_node: Option<String>,
pub final_node: Option<String>,
pub dependency_measures: Vec<(String, String, f64)>,
pub inputs: Vec<CausalBinding>,
pub outputs: Vec<CausalBinding>,
pub loops_len1: Vec<(String, f64)>,
pub loops_len2: Vec<(String, String, f64)>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct CausalBinding {
pub source_tasks: Vec<String>,
pub target_tasks: Vec<String>,
}
impl CausalBinding {
pub fn new(
sources: impl IntoIterator<Item = String>,
targets: impl IntoIterator<Item = String>,
) -> Self {
CausalBinding {
source_tasks: sources.into_iter().collect(),
target_tasks: targets.into_iter().collect(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum CausalNetRefusal {
MissingActivity,
InvalidDependencyScore,
DisconnectedGraph,
}
impl CausalNet {
pub fn validate(&self) -> Result<(), CausalNetRefusal> {
for node in &self.nodes {
if node.trim().is_empty() {
return Err(CausalNetRefusal::MissingActivity);
}
}
for (_, _, score) in &self.dependency_measures {
if score.is_nan() || *score < 0.0 || *score > 1.0 {
return Err(CausalNetRefusal::InvalidDependencyScore);
}
}
for (_, score) in &self.loops_len1 {
if score.is_nan() || *score < 0.0 || *score > 1.0 {
return Err(CausalNetRefusal::InvalidDependencyScore);
}
}
for (_, _, score) in &self.loops_len2 {
if score.is_nan() || *score < 0.0 || *score > 1.0 {
return Err(CausalNetRefusal::InvalidDependencyScore);
}
}
if self.nodes.len() > 1 {
let mut arc_nodes: HashSet<&str> = self
.dependency_measures
.iter()
.flat_map(|(src, tgt, _)| [src.as_str(), tgt.as_str()])
.collect();
for (node, _) in &self.loops_len1 {
arc_nodes.insert(node.as_str());
}
for (src, tgt, _) in &self.loops_len2 {
arc_nodes.insert(src.as_str());
arc_nodes.insert(tgt.as_str());
}
for node in &self.nodes {
if !arc_nodes.contains(node.as_str()) {
return Err(CausalNetRefusal::DisconnectedGraph);
}
}
}
Ok(())
}
}
impl fmt::Display for CausalNetRefusal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CausalNetRefusal::MissingActivity => write!(f, "Causal net refused: MissingActivity"),
CausalNetRefusal::InvalidDependencyScore => {
write!(f, "Causal net refused: InvalidDependencyScore")
}
CausalNetRefusal::DisconnectedGraph => {
write!(f, "Causal net refused: DisconnectedGraph")
}
}
}
}
impl std::error::Error for CausalNetRefusal {}