Package tulip :: Package transys :: Module machines :: Class MealyMachine
[frames] | no frames]

Class MealyMachine


Mealy machine.

Examples

Traffic Light: Fig. 3.14, p.72 [LS11]

>>> m = MealyMachine()
>>> pure_signal = {'present', 'absent'}
>>> m.add_inputs([('tick', pure_signal) ])
>>> m.add_outputs([('go', pure_signal), ('stop', pure_signal) ])
>>> m.states.add_from(['red', 'green', 'yellow'])
>>> m.states.initial.add('red')

For brevity:

>>> p = 'present'
>>> a = 'absent'

The transitions can equivalently be defined with dict(). So instead of the previous m.transitions.add, we can use:

>>> label = {'tick':p, 'go':p, 'stop':a}
>>> m.transitions.add('red', 'green', **label)
>>> label = {'tick':p, 'go':a, 'stop':p}
>>> m.transitions.add('green', 'yellow', **label)
>>> label = {'tick':p, 'go':a, 'stop':p}
>>> m.transitions.add('yellow', 'red', **label)

This avoids any ordering issues, i.e., changing the order of the sublabels does not matter:

>>> label = {'go':p, 'tick':p, 'stop':a}
>>> m.transitions.add('red', 'green', **label)

Theory

A Mealy machine implements the discrete dynamics:

   x[k+1] = f(x[k], u[k] )
   y[k] = g(x[k], u[k] )

where:

Observe that the output is defined when a reaction occurs to an input.

Note

valuation: assignment of values to each port

Reference

[M55]

Instance Methods
 
__init__(self)
Initialize the types of labelings on states and edges.
 
__str__(self)
Get informal string representation.
 
add_outputs(self, new_outputs, masks=None)
Add new outputs.
(outputs, next_state) where outputs: {'port_name':port_value, ...}
reaction(self, from_state, inputs)
Return next state and output, when reacting to given inputs.
 
run(self, from_state=None, input_sequences=None)
Guided or interactive run.

Inherited from Transducer: add_inputs, add_state_vars

Inherited from labeled_graphs.LabeledDiGraph: add_edge, add_edges_from, add_node, add_nodes_from, dot_str, has_deadends, is_consistent, plot, remove_deadends, remove_labeled_edge, remove_labeled_edges_from, save

Inherited from networkx.classes.multidigraph.MultiDiGraph: degree_iter, edges_iter, in_degree_iter, in_edges, in_edges_iter, is_directed, is_multigraph, out_degree_iter, out_edges, out_edges_iter, remove_edge, reverse, subgraph, to_directed, to_undirected

Inherited from networkx.classes.multigraph.MultiGraph: edges, get_edge_data, has_edge, number_of_edges, remove_edges_from, selfloop_edges

Inherited from networkx.classes.digraph.DiGraph: clear, has_predecessor, has_successor, in_degree, neighbors, neighbors_iter, out_degree, predecessors, predecessors_iter, remove_node, remove_nodes_from, successors, successors_iter

Inherited from networkx.classes.graph.Graph: __contains__, __getitem__, __iter__, __len__, add_cycle, add_path, add_star, add_weighted_edges_from, adjacency_iter, adjacency_list, copy, degree, has_node, nbunch_iter, nodes, nodes_iter, nodes_with_selfloops, number_of_nodes, number_of_selfloops, order, size

Inherited from object: __delattr__, __format__, __getattribute__, __hash__, __new__, __reduce__, __reduce_ex__, __repr__, __setattr__, __sizeof__, __subclasshook__

Properties

Inherited from networkx.classes.graph.Graph: name

Inherited from object: __class__

Method Details

__init__(self)
(Constructor)

 

Initialize the types of labelings on states and edges.

Label types by example

Use a dict for each label type you want to define, like this:

>>> types = [
        {'name': 'drink',
         'values': {'tea', 'coffee'},
         'setter': True,
         'default': 'tea'},
    ]

This will create a label type named 'drink' that can take the values 'tea' and 'coffee'.

Assuming this label type applies to nodes, you can now label a new node as:

>>> g = LabeledDiGraph(types)
>>> g.add_node(1, drink='coffee')

If you omit the label when adding a new node, it gets the default value:

>>> g.add_node(2)
>>> g.node[2]
{'drink': 'tea'}

The main difference with vanilla networkx is that the dict above includes type checking:

>>> type(g.node[2])
tulip.transys.mathset.TypedDict

The 'setter' key with value True creates also a field g.drink. Be careful to avoid name conflicts with existing networkx MultiDiGraph attributes.

This allows us to add more values after creating the graph:

>>> g.drink
{'coffee', 'tea'}
>>> g.drink.add('water')
{'coffee', 'tea', 'water'}

Finally, the graph will prevent us from accidentally using an untyped label name, by raising an AttributeError:

>>> g.add_node(3, day='Jan')
AttributeError: ...

To add untyped labels, do so explicitly:

>>> g.add_node(3, day='Jan', check=False)
>>> g.node[3]
{'day': 'Jan', 'drink': 'tea'}

Details on label types

Each label type is defined by a dict that must have the keys 'name' and 'values':

  • 'name': with str value
  • 'values' : B implements __contains__ used to check label validity.

    If you want the codomain B to be extensible even after initialization, it must implement method add.

and optionally the keys:

  • 'setter': C with 3 possibilities:
    • if absent, then no setter attribute is created
    • otherwise an attribute self.A is created, pointing at:
      • the given co-domain B if C is True
      • C, otherwise.
  • 'default': d is a value in B to be returned for node and edge labels not yet explicitly specified by the user.
Parameters:
  • node_label_types - applies to nodes, as described above.
  • edge_label_types - applies to edges, as described above.
  • deterministic - if True, then edge-label-deterministic
Overrides: object.__init__
(inherited documentation)

__str__(self)
(Informal representation operator)

 

Get informal string representation.

Overrides: object.__str__

add_outputs(self, new_outputs, masks=None)

 

Add new outputs.

Parameters:
  • new_outputs (dict) - dict of pairs {port_name : port_type} where:
    • port_name: str
    • port_type: Iterable | check class
  • masks (dict of functions keys are port_names (see arg: new_outputs) each function returns bool) - custom mask functions, for each sublabel based on its current value each such function returns:
    • True, if the sublabel should be shown
    • False, otherwise (to hide it)

reaction(self, from_state, inputs)

 

Return next state and output, when reacting to given inputs.

The machine must be deterministic. (for each state and input at most a single transition enabled, this notion does not coincide with output-determinism)

Not exactly a wrapper of Transitions.find, because it matches only that part of an edge label that corresponds to the inputs.

Parameters:
  • from_state (element of self.states) - transition starts from this state.
  • inputs ({'port_name':port_value, ...}) - dict assigning a valid value to each input port.
Returns: (outputs, next_state) where outputs: {'port_name':port_value, ...}
output values and next state.

run(self, from_state=None, input_sequences=None)

 

Guided or interactive run.

Parameters:
Returns:
output of guided_run, otherwise None.