def language_inclusion(aut1, aut2): """ Verify that language of L{aut1} is a sub-set of the language of L{aut2}. @param aut1: Automaton with smallest language. @type aut1: L{Automaton} @param aut2: Automaton with biggest language. @type aut2: L{Automaton} @return: Indication whether the condition holds (L{aut1} has a language subset of L{aut2}). @rtype: C{bool} """ aut1 = supervisor.unweighted_determinization(aut1) aut2 = supervisor.unweighted_determinization(aut2) aut2 = supervisor.complement(aut2) prod = product.n_ary_unweighted_product([aut1, aut2]) for state in prod.get_states(): if state.marked: return False return True
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 make_minimal_weighted_supervisor(plant, req, new_fname): """ Compute deterministic weighted supervisor for non-deterministic plant and deterministic requirements. @param plant: Filename of the non-deterministic weighted (but not minimally) automaton. @type plant: C{str} @param req: Filename of the requirements as deterministic non-weighted automaton. @type req: C{str} @param new_fname: Filename of the created deterministic controller. @type new_fname: C{str} """ common.print_line("Started minimal weighted supervisor computations " "(version %s)" % automata.version) coll = collection.Collection() plant_waut = load_weighted_automaton(coll, plant, False, True) req_aut = frontend.load_automaton(coll, req, False, True) wsup = compute_weight.compute_weighted_supremal(plant_waut, req_aut) if wsup is None: new_aut = None else: observables = set( [evt for evt in plant.events.itervalues() if evt.observable]) check_marking_aware(wsup, observables) wsup2 = weighted_projection.weighted_projection(wsup, observables) new_aut = compute_weight.minimal_weight_deterministic_controllable( wsup2) if new_aut is None: common.print_line("No minimal weight controller found!") else: unw_plant = conversion.remove_weights(plant_waut) prod = product.n_ary_unweighted_product([unw_plant, new_aut[0]]) result = supervisor.unweighted_determinization(prod) common.print_line("Minimum weight is %s" % new_aut[1]) frontend.save_automaton(result, "Saving minimal weight controller in %s", new_fname)
def abstract_obervables(plant, sup): """ Reduce the supervisor to contain only observable transitions between states and self loops for non-observable events. @param plant: Plant automaton. @type plant: L{Automaton} @param sup: Supervisor with observable and non-observable transitions. @type sup: L{Automaton} @return: Reduced supervisor. @rtype: L{Automaton} """ prod = product.n_ary_unweighted_product([sup, plant]) det_prod = unweighted_determinization(prod) prod.clear() observables = set([ evt for evt in det_prod.alphabet if evt.observable or evt.name == 'tau' ]) obs_sup, obsup_map = natural_projection_map(det_prod, observables) non_observables = det_prod.alphabet.difference(observables) obs_sup.add_event_set(non_observables) for det_stateset, obssup_state in obsup_map.iteritems(): events = set(edge.label for state in det_stateset for edge in state.get_outgoing()) # Get non-observable events from the set of det-states events = events.intersection(non_observables) for evt in events: obs_sup.add_edge_data(obssup_state, obssup_state, evt) obsup_map.clear() det_prod.clear() # Used by obsup_map return obs_sup
def row_vector_compute(plant, req_names, evt_pairs, row_vector_names, operator_class): coll = collection.Collection() comp_list = weighted_frontend.load_weighted_automata( coll, plant, False, True) req_list = frontend.load_automata(coll, req_names, False, True) evt_pairs = taskresource.process_event_pairs(coll, req_list, evt_pairs) result = taskresource.compute_custom_eventdata(comp_list, evt_pairs) if result is None: common.print_line('Could not compute the event data from the ' 'components and event pairs\n' 'Perhaps they are inconsistent?') return eventdata, heap_len = result plant = weighted_product.n_ary_weighted_product( comp_list, algorithm.EQUAL_WEIGHT_EDGES) requirement = product.n_ary_unweighted_product(req_list) for comp in comp_list: comp.clear() del comp_list wsup = compute_weight.compute_weighted_supremal(plant, requirement) if wsup is None: return None requirement.clear() del requirement row_zero_mat = maxplus.make_rowmat(0, heap_len) row_epsilon_mat = maxplus.make_rowmat(maxplus.INFINITE, heap_len) marker_valfn = lambda state: row_zero_mat nonmarker_valfn = lambda state: row_epsilon_mat row_vecs = compute_state_row_vector(wsup, marker_valfn, nonmarker_valfn, eventdata, operator_class) return row_vecs
def make_product(aut_fnames, result_fname, preserve_names = False): """ Multiply the automata in the L{aut_fnames} list, and write the result to L{result_fname}. @param aut_fnames: Comma-seperated list of automata filenames. @type aut_fnames: C{str} @param result_fname: Filename for writing the resulting automaton. @type result_fname: C{str} @param preserve_names: Try to preserve state names in the product. @type preserve_names: C{bool} """ common.print_line("Started product computations (version %s)" % automata.version) coll = collection.Collection() aut_list = load_automata(coll, aut_fnames, False, False) result = product.n_ary_unweighted_product(aut_list, True, True, preserve_names) dump_stats("Computed product", result) save_automaton(result, "Product is saved in %s\n", result_fname)
def make_supervisor(plants, specs, verbose=True): """ Construct a supervisor for the L{plants} that behaves safely within the L{specs} requirements. @param plants: Plant automata. @type plants: C{list} of L{BaseAutomaton} @param specs: Requirements specification automata. @type specs: C{list} of L{BaseAutomaton} @return: Supervisor automaton if it exists, C{None} otherwise. @rtype: L{Automaton} or C{None} """ assert len(plants) > 0 assert len(specs) > 0 # The iteration below replaces the specs by its own automaton. # To prevent leaking automata, these own automata should be deleted # before exit. delete_specs = False deterministic_and_all_observable = True # Check that all events are observable for plant in plants: for evt in plant.alphabet: if not evt.observable: deterministic_and_all_observable = False break if deterministic_and_all_observable: # Check that plant is deterministic for plant in plants: for state in plant.get_states(): evts = set() #: Set of events encountered at a state. for edge in state.get_outgoing(): if edge.label in evts: deterministic_and_all_observable = False break evts.add(edge.label) while True: # make_supervisor() is an iterative function result = controllable_coreachable_product(plants, specs, verbose) if result is None: if delete_specs: for spec in specs: spec.clear() return None if deterministic_and_all_observable: # We are finished! if delete_specs: for spec in specs: spec.clear() return result[0] chi_aut, boundary_disableds = result if len(boundary_disableds) == 0: if delete_specs: for spec in specs: spec.clear() return unweighted_determinization(chi_aut) # # Construct automaton A # aut_A = data_structure.Automaton(chi_aut.alphabet.copy(), chi_aut.collection) for state in chi_aut.coreachable_states_set( set(boundary_disableds.keys()), None): aut_A.add_new_state(marked=False, num=state.number) assert aut_A.has_state(chi_aut.initial.number) aut_A.set_initial(aut_A.get_state(chi_aut.initial.number)) # Copy edges for state in chi_aut.get_states(): if not aut_A.has_state(state.number): continue for edge in state.get_outgoing(): if not aut_A.has_state(edge.succ.number): continue aut_A.add_edge_data(aut_A.get_state(state.number), aut_A.get_state(edge.succ.number), edge.label) # Add dump state dump_state = aut_A.add_new_state(True) for state, disableds in boundary_disableds.iteritems(): for disabled in disableds: aut_A.add_edge_data(aut_A.get_state(state.number), dump_state, disabled) # Self-loops for all events in dump-state for evt in aut_A.alphabet: aut_A.add_edge_data(dump_state, dump_state, evt) A2 = unweighted_determinization(aut_A) b = A2.reduce(False, True) assert b A3 = projection(A2) A4 = inverse_projection(A3) A2.clear() A3.clear() A5 = product.n_ary_unweighted_product([chi_aut, A4]) marked = False for state in A5.get_states(): if state.marked: marked = True break if not marked: A4.clear() A5.clear() if delete_specs: for spec in specs: spec.clear() return unweighted_determinization(chi_aut) A5.clear() A6 = complement(A4) A7 = unweighted_determinization( product.n_ary_unweighted_product([chi_aut, A6])) b = A7.reduce(False, True) if not b: if delete_specs: for spec in specs: spec.clear() return None A4.clear() A6.clear() # Iteration: # plants = plants specs = [A7] delete_specs = True
def sequential_abstraction(automata_list, target_events): """ Perform a sequence of abstraction and product computation steps @param automata_list: List of automata @type automata_list: C{list} of L{Automaton} @param target_events: List of events to preserve after abstraction @type target_events: C{set} of L{Event} @return: An automaton @rtype: L{Automaton} """ assert len(automata_list) > 0 coll = automata_list[0].collection common.print_line("Started") # Paranoia check, all automata should use the same collection for aut in automata_list: assert aut.collection is coll # Setup for first automaton k = 1 t_k = compute_t(1, automata_list, target_events) aut_k = automata_list[k - 1] msg = "#states after adding %d automata: %d" % (k, aut_k.get_num_states()) common.print_line(msg) result = abstraction(aut_k, t_k) msg = "#states and #transitions after abstraction: %d, %d" \ % (result.get_num_states(), result.get_num_edges()) common.print_line(msg) k = k + 1 # For each automaton 2..len(automata_list) (including upper-bound) while k <= len(automata_list): prev_result = result aut_k = automata_list[k - 1] prod = product.n_ary_unweighted_product([prev_result, aut_k]) msg = "#states of %d automata: %d; #states and #transitions " \ "of product: %d %d" % (k, aut_k.get_num_states(), prod.get_num_states(), prod.get_num_edges()) common.print_line(msg) t_k = compute_t(k, automata_list, target_events) result = abstraction(prod, t_k) msg = "#states and #transitions after abstraction: %d, %d" \ % (result.get_num_states(), result.get_num_edges()) common.print_line(msg) # Remove intermediate results from collection prev_result.clear() prod.clear() k = k + 1 return result