SSA-Tool: Working with Self-Stabilizing Algorithms
This is a set of tools for the representation, creation, and evaluation of self-stabilizing algorithms.
A self-stabilizing network is seen as a graph of state machines. Each privilege is realized as a transition from the current state to another state. The state of the graph is passed as input to this state machine. When the input graph matches some predicate, this privilege is set – there is some transition out of this state. This is why the choice of predicate to act upon is arbitrary: you can only move to one other state!
When every node of the graph is in an accepting state with no set predicates, then the graph has stabilized.
- We consider graphs of finite state machines.
- Privileges are boolean functions of the FSM’s state and the states of its neighbors.
- When these functions are true, the privilege is ‘present’.
- Each legitimate state must have at least one privilege present, even if the action is ‘do nothing’
- In each legitimate state, every possible action will maintain legitimacy.
- Each privilege must be present in at least one legitimate state.
- For any given pair of legitimate states, there exists a transformation between them.
Regardless of the initial state and regardless of the privilege selected each time for the next move, at least one privilege will always be present and the system is guaranteed to find itself in a legitimate state after a finite number of moves.
- Edsger W. Dijkstra. 1974. Self-stabilizing systems in spite of distributed control. Commun. ACM 17, 11 (November 1974), 643-644. DOI=http://dx.doi.org/10.1145/361179.361202
# define your predicates/moves # predicates have access to node (v) and neighborhood (N) attributes cat <<DONE > unmarked-and-neighbors-unmarked.py marked = v['marked'] neighbor_marked = any(map(lambda n: n['marked'], N)) RESULT = not (marked or neighbor_marked) DONE cat <<DONE > marked-and-neighbor-marked.py marked = v['marked'] neighbor_marked = any(map(lambda n: n['marked'], N)) RESULT = marked and neighbor_marked DONE # moves can set node state cat <<DONE > mark.py v['marked'] = True DONE cat <<DONE > unmark.py v['marked'] = False DONE # move predefined code to the bundle python3 ssa.py indset.ssax new predicate can-mark -p marked bool < unmarked-and-neighbors-unmarked.py python3 ssa.py indset.ssax new move mark -p marked bool < mark.py python3 ssa.py indset.ssax new predicate must-unmark -p marked bool < marked-and-neighbor-marked.py python3 ssa.py indset.ssax new move unmark -p marked bool < unmark.py # create a new algorithm python3 ssa.py indset.ssax new algorithm 'Independent Set' # define rules for that algorithm python3 ssa.py indset.ssax add-rule-to 'Independent Set' can-mark mark python3 ssa.py indset.ssax add-rule-to 'Independent Set' must-unmark unmark # run the algorithm python3 ssa.py indset.ssax run 'Independent Set' gn,5 1000 100
run command generates 1000 graphs and iterates the algorithm
100 times on each. The properties of nodes in the graph are
determined by the predicates and moves composing the rules in the
algorithm. For example:
-p age range,1,120...
In this example,
range is called the generator descriptor and
are its arguments. This particular specification will ensure each
node has an
age attribute with a value between
120. The actual
graph itself is determined by the
gn,5 string you see in the
command. The descriptor here (
gn) determines what mathematical
properties the generated graph will have (tree/path/cycle/grid/…).
Note some graph-generators can provide properties of their own.
Property generators defined on predicates/moves can be overridden at
run-time with the
--property-override option to
The definitive list of graph- and property-generators (and their
arguments) are available in
This package can be installed locally using
pip install .. Be sure to
install the requirements with
pip install -r requirements.txt.
Alternatively, use the targets from the Makefile: