Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
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()
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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