def compute_weighted_supervisor(comp, req): """ Compute weighted supervisor. @param comp: Available component (weighted automaton). @type comp: C{WeightedAutomaton} @param req: Available requirement (unweighted automaton). @type req: C{UnweightedAutomaton} @return: Resulting supervisor (unweighted automaton). @type: C{UnweightedAutomaton} @note: It starts the same way as L{compute_optimal_weighted_supervisor}. """ # Compute supremal supervisor without considering weight. wsup = compute_weight.compute_weighted_supremal(comp, req) if wsup is None: return None obs_alphabet = set(evt for evt in comp.alphabet if evt.observable) waut2 = weighted_projection.weighted_projection(wsup, obs_alphabet) waut2 = weighted_determinization(waut2) weight_map = compute_weight.compute_state_weights(waut2, marker_valfn = lambda s: 0) props = algorithm.ManagerProperties(waut2.collection) props.aut_type = algorithm.UNWEIGHTED_AUT props.alphabet = waut2.alphabet props.marker_func = algorithm.MARKED_ANY props.explore_mgr = algorithm.ORIGINAL_STATE props.edge_calc = algorithm.COPY_LABEL mgr = algorithm.Manager(props) mgr.set_initial((waut2.initial,)) while True: state = mgr.get_next() if state is None: break state = state[0] for edge in state.get_outgoing(): dest_weight = maxplus.otimes(weight_map[edge.succ], edge.weight) if maxplus.equal(weight_map[state], dest_weight) or \ maxplus.biggerthan(weight_map[state], dest_weight): mgr.add_edge((edge.pred,), (edge.succ,), [edge]) unw_comp = conversion.remove_weights(comp) result = product.n_ary_unweighted_product([unw_comp, mgr.get_automaton()]) return result
def check_weighted_equality(wa1, wa2): """ Compare deterministic weighted automataton L{wa1} with L{wa2}. @param wa1: First automaton to use for comparison. @type wa1: L{WeightedAutomaton} @param wa2: Second automaton to use for comparison. @type wa2: L{WeightedAutomaton} @return: Automata are equal. @rtype: C{bool} """ def get_edge_dict(state): edges = list(state.get_outgoing()) edict = dict(((edge.label, edge.weight), edge) for edge in edges) if len(edges) != len(edict): msg = "Non-deterministic weighted automata are not supported " \ "by this check." raise exceptions.ModelError(msg) return edict props = algorithm.ManagerProperties(None) props.explore_mgr = algorithm.ORIGINAL_STATE mgr = algorithm.Manager(props) mgr.get_state((wa1.initial, wa2.initial)) while True: sab = mgr.get_next() if sab is None: break sa, sb = sab if sa.marked != sb.marked: return False edges_a = get_edge_dict(sa) edges_b = get_edge_dict(sb) if len(edges_a) != len(edges_b): return False # Different number of outgoing edges => not equal. for edge_a in edges_a.itervalues(): edge_b = edges_b.get((edge_a.label, edge_a.weight)) if edge_b is None: return False mgr.get_state((edge_a.succ, edge_b.succ)) return True
def common_determinization(initial, props): """ Make equivalent deterministic automaton. @param initial: Initial state of the old automaton. @type initial: L{BaseState} @param props: Algorithm properties. @type props: L{ManagerProperties} @return: Deterministic automaton. @rtype: L{BaseAutomaton} @note: Used both by unweighted and weighted determinization. """ mgr = algorithm.Manager(props) mgr.set_initial(frozenset([initial])) while True: orig_state_set = mgr.get_next() if orig_state_set is None: break # For all edges of all original states, collect new set of states for # each event. #: Mapping of event to tuple (set of new states, edges). evt_storage = {} for orig_state in orig_state_set: for edge in orig_state.get_outgoing(): entry = evt_storage.get(edge.label) if entry is None: entry = (set(), []) evt_storage[edge.label] = entry entry[0].add(edge.succ) entry[1].append(edge) # For each event, make a new edge. for new_set, edges in evt_storage.itervalues(): mgr.add_edge(orig_state_set, frozenset(new_set), edges) return mgr.get_automaton()
def supervisor_product(comp, comp_bad, compreq, compreq_is_requirement, usable_events, verbose=True): """ Perform a product calculation for the purpose of supervisor synthesis. @param comp: First automaton, always a component (possbily the result of a previous call). @type comp: L{BaseAutomaton} @param comp_bad: Known bad states of L{comp}. @type comp_bad: C{set} of L{BaseState} @param compreq: Second automaton, either a component or a requirement. @type compreq: L{BaseAutomaton} @param compreq_is_requirement: Second automaton is a requirement automaton. @type compreq_is_requirement: C{bool} @param usable_events: Set of events that may be traversed. @type usable_events: C{set} of L{Event} @return: Resulting automaton, and its bad state set. @rtype: L{BaseAutomaton}, C{set} of L{BaseState} @note: The alphabet of the resulting automaton is inserted into the properties by the function. @precond: If L{compreq_is_requirement}, the alphabet of L{compreq} must be a subset of L{comp}. """ # Generate progress message. if compreq_is_requirement: compreq_text = "spec" else: compreq_text = "plant" msg = "Start supervisor product %d states (%d bad) with %s %d states" \ % (comp.get_num_states(), len(comp_bad), compreq_text, compreq.get_num_states()) if verbose: common.print_line(msg) props = algorithm.ManagerProperties(comp.collection) props.aut_type = algorithm.UNWEIGHTED_AUT props.marker_func = algorithm.MARKED_ALL props.explore_mgr = algorithm.ORIGINAL_STATE props.edge_calc = algorithm.COPY_LABEL result_alphabet = comp.alphabet.union(compreq.alphabet) props.alphabet = result_alphabet compreq_only_alphabet = compreq.alphabet.difference(comp.alphabet) # Either compreq is not a requirement, or it has no edges of its own. assert not compreq_is_requirement or len(compreq_only_alphabet) == 0 bad_states = set() #: New bad states, list of original state combinations. mgr = algorithm.Manager(props) mgr.set_initial((comp.initial, compreq.initial)) while True: orig_state = mgr.get_next() if orig_state is None: break # If it was a bad state previously, it will be again. if orig_state[0] in comp_bad: # Reset marker property of bad state. state = mgr.state_mgr.mapping[orig_state] state.marked = False bad_states.add(state) continue # Pick the next one. #: Available compreq edges ordered by event-name. compreq_event_edges = {} for edge in orig_state[1].get_outgoing(): edges = compreq_event_edges.get(edge.label) if edges is None: edges = [] compreq_event_edges[edge.label] = edges edges.append(edge) if compreq_is_requirement: # If compreq is a requirement, look whether we are at a bad state. # Those happen when the event is enabled in comp, disabled in # compreq, and the event is uncontrollable (ie from the current # state, we disable an uncontrollable event by a spec). In that # case, the current state in the product is bad (it violates the # controllability property). Add the product state to the bad # states. # Decide whether it is a bad state. bad_state = False for edge in orig_state[0].get_outgoing(): # The edge label is controllable, or # compreq also has an outgoing edge for this event, or # the label is not in the compreq alphabet. if edge.label.controllable or \ edge.label in compreq_event_edges or \ edge.label not in compreq.alphabet: continue bad_state = True break if bad_state: # Reset marker property of bad state. state = mgr.state_mgr.mapping[orig_state] state.marked = False bad_states.add(state) continue # Do not expand current state, pick the next one. # A good state, expand to new states. # Expand edges of first automaton. for edge in orig_state[0].get_outgoing(): if edge.label not in compreq.alphabet: # comp only. mgr.add_edge(orig_state, (edge.succ, orig_state[1]), [edge]) continue compreq_edges = compreq_event_edges.get(edge.label) if compreq_edges is None: # Disabled by compreq, but not in a bad way. continue for edge2 in compreq_edges: mgr.add_edge(orig_state, (edge.succ, edge2.succ), [edge, edge2]) # Perform compreq only for evt in compreq_only_alphabet: compreq_edges = compreq_event_edges.get(evt) if compreq_edges is not None: for edge2 in compreq_edges: mgr.add_edge(orig_state, (orig_state[0], edge2.succ), [edge2]) # Finished with the product. prod_aut = mgr.get_automaton() prod_aut.aut_kind = 'supervisor' mgr.get_mapping().clear() # Do a co-reachability search, and trim out all states AFTER the initial # non-coreachable state. The initial non-coreachable states must be added # to the bad states as well. # Compute co-reachable set of the product. coreachables = set() not_done = [] for state in prod_aut.get_states(): if state.marked: # Bad states are never marked. coreachables.add(state) not_done.append(state) if len(not_done) == 0: # No marker states at all in the product msg = "Supervisor product is empty (no marker states in the product)." raise exceptions.ModelError(msg) while len(not_done) > 0: state = not_done.pop() for edge in state.get_incoming(): if edge.pred not in coreachables: coreachables.add(edge.pred) not_done.append(edge.pred) # Finished, all states are coreacahable. # Will probably not happen often due to bad states. if len(coreachables) == prod_aut.get_num_states(): assert len(bad_states) == 0 if DBG: common.print_line("Finished, %d states, no bad states" % prod_aut.get_num_states()) coreachables.clear() return prod_aut, bad_states # Non-coreachables that have a co-reachable predecessor are bad too. non_coreachables = [] for state in prod_aut.get_states(): if state in coreachables: continue if state in bad_states: continue pred_coreachable = False for edge in state.get_incoming(): if edge.pred in coreachables: pred_coreachable = True break if pred_coreachable: bad_states.add(state) # Reset marker property of bad state. state.marked = False # Remove all outgoing edges of the new bad state. for edge in list(state.get_outgoing()): prod_aut.remove_edge(edge) else: non_coreachables.append(state) coreachables.clear() # Remove states that are not bad and not co-reachable. for state in non_coreachables: if state not in bad_states: prod_aut.remove_state(state) del non_coreachables # Extend the number of bad states by walking backwards over # 'usable_events'. illegal_states = coreachable_bad_states(prod_aut, bad_states, usable_events) if illegal_states == bad_states: if DBG: common.print_line("Finished, %d states (%d bad)" % (prod_aut.get_num_states(), len(bad_states))) return prod_aut, bad_states # Found new illegal states. assert bad_states.issubset(illegal_states) bad_states = set() for state in illegal_states: # Check whether 'state' has an ancestor state which is good. found_good_state = False for edge in state.get_incoming(): if edge.pred not in illegal_states: found_good_state = True break if found_good_state: bad_states.add(state) # Reset marker property of bad state. state.marked = False # Remove all outgoing edges of the new bad state. for edge in list(state.get_outgoing()): prod_aut.remove_edge(edge) else: prod_aut.remove_state(state) illegal_states.clear() if DBG: common.print_line("Finished, %d states (%d bad)" % (prod_aut.get_num_states(), len(bad_states))) prod_aut.save_as_dot("test.dot") return prod_aut, bad_states
def do_n_ary_product_map(props, auts, preserve_names): """ Perform n-ary product calculation both for unweighted and weighted automata. @param props: Manager properties controlling the computation, except for the resulting alphabet. @type props: L{ManagerProperties} @param auts: Input automata. @type auts: C{list} of L{BaseAutomaton} @param preserve_names: Try to preserve state names in the product. @type preserve_names: C{bool} @return: Resulting automaton, and state map. @rtype: L{BaseAutomaton}, C{dict} of (C{tuple} of L{BaseState}) to L{BaseState} @note: The alphabet of the resulting automaton is inserted into the properties by the function. """ # assert len(auts) >= 2 has_plant, has_req, has_other = False, False, False result_alphabet = set() for aut in auts: # Verify that all automata use the same collection, and have an initial # state. assert aut.collection is auts[0].collection assert aut.initial is not None result_alphabet.update(aut.alphabet) if aut.aut_kind == 'plant': has_plant = True elif aut.aut_kind == 'requirement': has_req = True else: has_other = True props.alphabet = result_alphabet if has_plant and not has_req and not has_other: result_kind = 'plant' elif not has_plant and has_req and not has_other: result_kind = 'requirement' else: result_kind = 'unknown' # Construct a mapping from event to a boolean whether or not each automaton # participates with the event. participate = {} for evt in result_alphabet: participate[evt] = [evt in aut.alphabet for aut in auts] mgr = algorithm.Manager(props) mgr.set_initial(tuple(aut.initial for aut in auts)) while True: orig_state = mgr.get_next() if orig_state is None: break # Find current edges, collect disabled events from the orig_state. edges = [] #: List of lists with edges of each automaton. disabled = set() #: Disabled events for aut, state in zip(auts, orig_state): aut_edges = [] aut_events = set() for edge in state.get_outgoing(): aut_edges.append(edge) aut_events.add(edge.label) edges.append(aut_edges) disabled.update(aut.alphabet.difference(aut_events)) # Do every event that is enabled. for evt in result_alphabet.difference(disabled): add_new_states(orig_state, evt, participate[evt], edges, mgr, [], []) prod_aut = mgr.get_automaton() prod_aut.aut_kind = result_kind mapping = mgr.get_mapping() if preserve_names: # Construct 'nice' human readable state names in the product. for aut in auts: aut.make_state_names_complete() destnames = set(prod_aut.state_names.itervalues()) for origstates, deststate in mapping.iteritems(): name = "-".join(aut.state_names[state.number] for aut, state in zip(auts, origstates)) if name not in destnames: if deststate.number in prod_aut.state_names: destnames.remove(prod_aut.state_names[deststate.number]) destnames.add(name) prod_aut.set_state_name(deststate, name) del destnames return prod_aut, mapping
def do_n_ary_product_map(auts): props = algorithm.ManagerProperties(auts[0].collection) props.aut_type = algorithm.WEIGHTED_AUT props.marker_func = algorithm.MARKED_ALL props.explore_mgr = algorithm.ORIGINAL_STATE props.edge_calc = algorithm.EQUAL_WEIGHT_EDGES has_plant, has_req, has_other = False, False, False result_alphabet = set() for aut in auts: # Verify that all automata use the same collection, and have an initial # state. assert aut.collection is auts[0].collection assert aut.initial is not None result_alphabet.update(aut.alphabet) if aut.aut_kind == 'plant': has_plant = True elif aut.aut_kind == 'requirement': has_req = True else: has_other = True props.alphabet = result_alphabet if has_plant and not has_req and not has_other: result_kind = 'plant' elif not has_plant and has_req and not has_other: result_kind = 'requirement' else: result_kind = 'unknown' # Construct a mapping from event to a boolean whether or not each automaton # participates with the event. participate = {} for evt in result_alphabet: participate[evt] = [evt in aut.alphabet for aut in auts] mgr = algorithm.Manager(props) mgr.set_initial(tuple(aut.initial for aut in auts)) while True: orig_state = mgr.get_next() if orig_state is None: break # Find current edges, collect disabled events from the orig_state. edges = [] #: List of lists with edges of each automaton. disabled = set() #: Disabled events for aut, state in zip(auts, orig_state): aut_edges = [] aut_events = set() for edge in state.get_outgoing(): aut_edges.append(edge) aut_events.add(edge.label) edges.append(aut_edges) disabled.update(aut.alphabet.difference(aut_events)) # Do every event that is enabled. for evt in result_alphabet.difference(disabled): product.add_new_states(orig_state, evt, participate[evt], edges, mgr, [], []) prod_aut = mgr prod_aut.aut_kind = result_kind # mapping = mgr.get_mapping() return prod_aut