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
/// Basic traits which are carried over from lib-biodivine_std for now.
///
/// In the future, these will be replaced by a more stable variants.
use std::hash::Hash;

/// **Deprecated** marker trait for anything that can be a state of a graph.
///
/// Currently, we require each state to be a `Copy` struct, i.e. it has to be
/// "small enough" so that it can be copied whenever needed. In the future, we might
/// lift this restriction if the need for more complex states arises. Meanwhile, one
/// can use dynamically indexed states.
pub trait State: Hash + Eq + Clone + Copy {}

/// A very basic set trait.
///
/// Notice that we do not assume anything about the members of the set, we can't
/// iterate them or even retrieve them. This is because the sets might be uncountable
/// or the elements might not be well representable.
///
/// Also notice that there is no complement method available. This is because the
/// `unit` set can be different every time or completely unknown. To
/// implement complement, use `minus` with an appropriate `unit` set.
pub trait Set: Clone {
    fn union(&self, other: &Self) -> Self;
    fn intersect(&self, other: &Self) -> Self;
    fn minus(&self, other: &Self) -> Self;

    fn is_empty(&self) -> bool;
    fn is_subset(&self, other: &Self) -> bool;
}

/// A parametrised variant of an `EvolutionOperator`. For each state, a `Params` set is
/// returned as well which gives the set of parameter valuations for which the transition
/// is allowed.
pub trait EvolutionOperator {
    type State: State;
    type Params: Set;
    type Iterator: Iterator<Item = (Self::State, Self::Params)>;
    fn step(&self, current: Self::State) -> Self::Iterator;
}

/// A variant of the `EvolutionOperator` that can be inverted.
///
/// This is useful if you have algorithms that need to follow edges in both directions but have
/// some "preferred" sense of direction. For example, a model checking algorithm can verify
/// formulas that mix past and future. It typically starts in "future" mode, but can switch
/// to "past" depending on the formula. If the operator is invertible, one can just
/// invert the evolution operator in the algorithm without caring whether we are working on
/// past or future. In other words, the sense of time is relative.
///
/// Technically, this can be also achieved by switching between `fwd` and `bwd` in the algorithm,
/// but that can be cumbersome because the sense of "direction" becomes diluted. In other words,
/// it is easy to lose track of what is going on if you see something like `let fwd = bwd;`...
pub trait InvertibleEvolutionOperator: EvolutionOperator {
    type InvertedOperator: EvolutionOperator<State = Self::State, Params = Self::Params>;
    fn invert(&self) -> Self::InvertedOperator;
}

/// A parametrised variant of a `Graph`.
pub trait Graph {
    type State: State;
    type Params: Set;
    type States: Iterator<Item = Self::State>;
    type FwdEdges: EvolutionOperator;
    type BwdEdges: EvolutionOperator;

    fn states(&self) -> Self::States;
    fn fwd(&self) -> Self::FwdEdges;
    fn bwd(&self) -> Self::BwdEdges;
}

pub trait InvertibleGraph: Graph {
    type FwdEdges: InvertibleEvolutionOperator;
    type BwdEdges: InvertibleEvolutionOperator;
}