def fts2SC(ts, env_name='ctrl', act='act'): """ Get a mealy machine with reach + move state on all transitions Parameters ---------- ts : FTS env_name='move' : label for on transitions environment action reach='reach' : ouput of each transition """ h = transys.MealyMachine() mlist = list() for node in ts.nodes(): mlist += [node] inputs = transys.machines.create_machine_ports(dict({env_name: mlist})) h.add_inputs(inputs) outputs = transys.machines.create_machine_ports(dict({act: mlist})) h.add_outputs(outputs) h.add_nodes_from(ts.nodes()) for (st1, st2) in ts.transitions(): q = {env_name: st2, act: st2} h.transitions.add(st1, st2, **dict(q)) h.add_node('Minit') h.states.initial |= {'Minit'} for init in ts.states.initial: q = {env_name: init, act: init} h.transitions.add('Minit', init, **dict(q)) return h
def fts2mealy(ts, env_name='move', reach_name='reach'): """ Get a mealy machine with reach + move state on all transitions Parameters ---------- ts : FTS env_name='move' : label for on transitions environment action reach='reach' : ouput of each transition """ h = transys.MealyMachine() mlist = list() for node in ts.nodes(): mlist += [node] inputs = transys.machines.create_machine_ports(dict({env_name: mlist})) h.add_inputs(inputs) outputs = transys.machines.create_machine_ports( dict({reach_name: 'boolean'})) h.add_outputs(outputs) h.add_nodes_from(ts.nodes()) for (st1, st2) in ts.transitions(): q = {reach_name: 1, env_name: st2} h.transitions.add(st1, st2, **dict(q)) for init in ts.states.initial: h.states.initial |= {init} return h
def remove_aux_inputs(ctrl, inputs): #1. check whether you are allowed to remove the aux inputs. <= not done #2. remove aux. inputs. ctrl_new = transys.MealyMachine() ctrl_new.add_outputs(ctrl.outputs) # this needs to be changed to be a limited set inputs_dict = dict() for i in inputs: inputs_dict[i] = ctrl.inputs[i] ctrl_new.add_inputs(inputs_dict) # add nodes from original mealy ctrl_new.add_nodes_from(ctrl.nodes()) block_pairs = it_product(ctrl, ctrl) for (b, c) in block_pairs: labels = { frozenset([(key, label[key]) for key in ctrl_new.inputs.keys()] + [(output, label[output]) for output in ctrl_new.outputs.keys()]) for (x, y, label) in ctrl.transitions.find(b, c) } for q in labels: ctrl_new.transitions.add(b, c, **dict(q)) ctrl_new.states.initial.add_from(ctrl.states.initial) return ctrl_new
def thermostat_with_hysteresis(): """Example 3.5, p.50 [LS11] """ class temperature_type(): def is_valid_value(x): if not isinstance(x, [float, int]): raise TypeError('Input temperature must be float.') def __contains__(self, guard): # when properly implemented, do appropriate syntactic check if isinstance(guard, float): return True return False m = trs.MealyMachine() m.add_inputs({'temperature': set()})
def pedestrians(): """Example 2.14, p.63 [LS11] """ m = trs.MealyMachine() m.add_inputs({'sigR': mc.pure, 'sigG': mc.pure, 'sigY': mc.pure}) m.add_outputs({'pedestrian': mc.pure}) m.states.add_from(['none', 'waiting', 'crossing']) m.states.initial.add('crossing') for sigR in mc.pure: for sigG in mc.pure: for sigY in mc.pure: m.transitions.add('none', 'none', sigR=sigR, sigG=sigG, sigY=sigY, pedestrian='absent') m.transitions.add('none', 'waiting', sigR=sigR, sigG=sigG, sigY=sigY, pedestrian='present') m.transitions.add('waiting', 'crossing', sigR='present', sigG='absent', sigY='absent', pedestrian='absent') m.transitions.add('crossing', 'none', sigR='absent', sigG='present', sigY='absent', pedestrian='absent') m.save() return m
def traffic_light(): m = trs.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') p = 'present' a = 'absent' m.transitions.add('red', 'green', tick=p, go=p, stop=a) m.transitions.add('green', 'yellow', tick=p, go=a, stop=p) m.transitions.add('yellow', 'red', tick=p, go=a, stop=p) m.save() return m
def test_determinize_machine_init(): mach = transys.MealyMachine() mach.add_inputs({'a': {0, 1}}) mach.add_outputs({'b': {0, 1}, 'c': {0, 1}}) u = 'Sinit' mach.add_nodes_from([u, 1, 2]) # initial reactions: # to input: a=0 mach.add_edge(u, 1, a=0, b=0, c=1) mach.add_edge(u, 2, a=0, b=0, c=1) mach.add_edge(u, 1, a=0, b=1, c=0) mach.add_edge(u, 2, a=0, b=1, c=1) # to input: a=1 mach.add_edge(u, 1, a=1, b=0, c=1) mach.add_edge(u, 2, a=1, b=0, c=1) mach.add_edge(u, 1, a=1, b=1, c=0) mach.add_edge(u, 2, a=1, b=1, c=1) # determinize all outputs arbitrarily detmach = synth.determinize_machine_init(mach) assert detmach is not mach for a in {0, 1}: edges = [(i, j) for (i, j, d) in detmach.edges_iter(u, data=True) if d['a'] == a] assert len(edges) == 1 # determinize output b arbitrarily, # but output c is constrained to the initial value 0 detmach = synth.determinize_machine_init(mach, {'c': 0}) for a in {0, 1}: edges = [(i, j, d) for (i, j, d) in detmach.edges_iter(u, data=True) if d['a'] == a] assert len(edges) == 1 ((i, j, d), ) = edges assert j == 1 assert d['b'] == 1
def garage_counter(ploting=True): """Example 3.4, p.49 [LS11], for M=2 no state variables in this Finite-State Machine """ m = trs.MealyMachine() m.add_inputs({'up': {'present', 'absent'}, 'down': {'present', 'absent'}}) m.add_outputs({'count': range(3)}) m.states.add_from(range(3)) m.states.initial.add(0) m.transitions.add(0, 1, up='present', down='absent', count=1) m.transitions.add(1, 0, up='absent', down='present', count=0) m.transitions.add(1, 2, up='present', down='absent', count=2) m.transitions.add(2, 1, up='absent', down='present', count=1) if ploting: m.plot() return m
def mealy_machine_example(): import numpy as np class check_diaphragm(): """camera f-number.""" def is_valid_value(x): if x <= 0.7 or x > 256: raise TypeError('This f-# is outside allowable range.') def __contains__(self, guard): # when properly implemented, do appropriate syntactic check if isinstance(guard, float): return True return False def __call__(self, guard_set, input_port_value): """This method "knows" that we are using x to denote the input within guards.""" self.is_valid_value(input_port_value) guard_var_def = {'x': input_port_value} guard_value = eval(guard_set, guard_var_def) if not isinstance(guard_value, bool): raise TypeError('Guard value is non-boolean.\n' 'A guard is a predicate, ' 'so it can take only boolean values.') class check_camera(): """is it looking upwards ?""" def is_valid_value(self, x): if x.shape != (3,): raise Exception('Not a 3d vector!') def __contains__(self, guard): # when properly implemented, do appropriate syntactic check if isinstance(guard, np.ndarray) and guard.shape == (3,): return True return False def __call__(self, guard_set, input_port_value): self.is_valid_value(input_port_value) v1 = guard_set # guard_halfspace_normal_vector v2 = input_port_value # camera_direction_vector if np.inner(v1, v2) > 0.8: return True return False # note: guards are conjunctions, # any disjunction is represented by 2 edges # input defs inputs = [ ('speed', {'zero', 'low', 'high', 'crazy'} ), ('seats', trs.PowerSet(range(5) ) ), ('aperture', check_diaphragm() ), ('camera', check_camera() ) ] # outputs def outputs = [('photo', {'capture', 'wait'} ) ] # state variables def state_vars = [('light', {'on', 'off'} ) ] # define the machine itself m = trs.MealyMachine() m.add_state_vars(state_vars) m.add_inputs(inputs) m.add_outputs(outputs) m.states.add('s0') m.states.add_from(['s1', 's2']) m.states.initial.add('s0') m.transitions.add( 's0', 's1', speed='low', seats=(0, 1), aperture=0.3, camera=np.array([0,0,1]), photo='capture' ) guard = {'camera':np.array([1,1,1]), 'speed':'high', 'aperture':0.3, 'seats':(2, 3), 'photo':'wait'} m.transitions.add('s1', 's2', **guard) m.plot(rankdir='TB') return m
def quotient_mealy(mealy, node_relation=None, relabel=False, outputs={'loc'}): """Returns the quotient graph of ``G`` under the specified equivalence relation on nodes. Parameters ---------- mealy : NetworkX graph The graph for which to return the quotient graph with the specified node relation. node_relation : Boolean function with two arguments This function must represent an equivalence relation on the nodes of ``G``. It must take two arguments *u* and *v* and return ``True`` exactly when *u* and *v* are in the same equivalence class. The equivalence classes form the nodes in the returned graph. unlike the original networkx.quotient_graph selfloops are maintained relabel : Boolean if true relabel nodes in the graph outputs : Tells which outputs are critical and should be kept. Given as a set of strings. """ if node_relation is None: node_relation = lambda u, v: mealy.states.post(u) == mealy.states.post( v) q_mealy = transys.MealyMachine() q_mealy.add_inputs(mealy.inputs) q_mealy.add_outputs(mealy.outputs) # Compute the blocks of the partition on the nodes of G induced by the # equivalence relation R. if relabel: mapping = dict( (n, i) for (i, n) in enumerate(equivalence_classes(mealy, node_relation))) for (n, i) in mapping.items(): if {'Sinit'} <= set(n): mapping[n] = 'Sinit' q_mealy.add_nodes_from({n for (i, n) in mapping.items()}) else: q_mealy.add_nodes_from(equivalence_classes(mealy, node_relation)) if relabel: block_pairs = it_product(mapping.keys(), mapping.keys()) for (b, c) in block_pairs: labels = { frozenset([(key, label[key]) for key in mealy.inputs.keys()] + [(output, label[output]) for output in outputs]) for (x, y, label) in mealy.transitions.find(b, c) } for q in labels: q_mealy.transitions.add(mapping[b], mapping[c], **dict(q)) else: block_pairs = it_product(q_mealy, q_mealy) for (b, c) in block_pairs: labels = { frozenset([(key, label[key]) for key in mealy.inputs.keys()] + [(output, label[output]) for output in outputs]) for (x, y, label) in mealy.transitions.find(b, c) } for q in labels: q_mealy.transitions.add(b, c, **dict(q)) if relabel: for node_eq in mapping.keys(): if any(init in node_eq for init in mealy.states.initial): q_mealy.states.initial.add(mapping[node_eq]) else: # only initializing after relabel for node_eq in q_mealy.nodes(): if any(init in node_eq for init in mealy.states.initial): q_mealy.states.initial.add(node_eq) return q_mealy
def strategy2mealy(A, spec): """Convert strategy to Mealy transducer. Note that the strategy is a deterministic game graph, but the input C{A} is given as the contraction of this game graph. @param A: strategy @type A: C{networkx.DiGraph} @type spec: L{GRSpec} @rtype: L{MealyMachine} """ assert len(A) > 0 logger.info('converting strategy (compact) to Mealy machine') env_vars = spec.env_vars sys_vars = spec.sys_vars mach = transys.MealyMachine() inputs = transys.machines.create_machine_ports(env_vars) mach.add_inputs(inputs) outputs = transys.machines.create_machine_ports(sys_vars) mach.add_outputs(outputs) str_vars = {k: v for k, v in env_vars.items() if isinstance(v, list)} str_vars.update({k: v for k, v in sys_vars.items() if isinstance(v, list)}) mach.states.add_from(A) # transitions labeled with I/O for u in A: for v in A.successors_iter(u): d = A.node[v]['state'] d = _int2str(d, str_vars) mach.transitions.add(u, v, **d) logger.info('node: {v}, state: {d}'.format(v=v, d=d)) # special initial state, for first reaction initial_state = 'Sinit' mach.states.add(initial_state) mach.states.initial.add(initial_state) # fix an ordering for keys # because tuple(dict.items()) is not safe: # https://docs.python.org/2/library/stdtypes.html#dict.items try: u = next(iter(A)) keys = A.node[u]['state'].keys() except Exception: logger.warning('strategy has no states.') # to store tuples of dict values for fast search # isinit = spec.compile_init(no_str=True) # Previous line is the original. Use this module's isinit_test() to create # transitions to only specified initial conditions. isinit = isinit_test(spec, True) # Mealy reaction to initial env input init_valuations = set() tmp = dict() for u, d in A.nodes_iter(data=True): var_values = d['state'] vals = tuple(var_values[k] for k in keys) # already an initial valuation ? if vals in init_valuations: continue # add edge: Sinit -> u ? tmp.update(var_values) if eval(isinit, tmp): label = _int2str(var_values, str_vars) mach.transitions.add(initial_state, u, **label) # remember variable values to avoid # spurious non-determinism wrt the machine's memory # # in other words, # "state" omits the strategy's memory # hidden (existentially quantified) # so multiple nodes can be labeled with the same state # # non-uniqueness here would be equivalent to # multiple choices for initializing the hidden memory. init_valuations.add(vals) logger.debug('found initial state: {u}'.format(u=u)) logger.debug('machine vertex: {u}, has var values: {v}'.format( u=u, v=var_values)) n = len(A) m = len(mach) assert m == n + 1, (n, m) if not mach.successors('Sinit'): raise Exception('The machine obtained from the strategy ' 'does not have any initial states !\n' 'The strategy is:\n' 'vertices:' + pprint.pformat(A.nodes(data=True)) + 2 * '\n' + 'edges:\n' + str(A.edges()) + 2 * '\n' + 'and the machine:\n' + str(mach) + 2 * '\n' + 'and the specification is:\n' + str(spec.pretty()) + 2 * '\n') return mach