Ejemplo n.º 1
0
def hold(props, prop, duration, negation=False):
    '''Creates a DFA which accepts a sequence of symbols all containing
    proposition prop. The length of the sequence is duration+1 corresponding to
    duration time intervals. If negation is True, then the symbols must not
    contain prop instead.
    '''
    assert prop in props

    guard = prop if not negation else '!' + prop
    dfa = Fsa(props, directed=True, multi=False)
    dfa.name = '(Hold {} {}{} )'.format(duration, 'not ' if negation else '', prop)
    bitmaps = dfa.get_guard_bitmap(guard)

    ngen = it.count()
    nodes = [ngen.next() for _ in range(duration+2)]
    attr_dict={'weight': 0, 'input': bitmaps, 'guard' : guard, 'label': guard}
    dfa.g.add_path(nodes, **attr_dict)

    u, v = nodes[0], nodes[-1]
    dfa.init[u] = 1
    dfa.final.add(v)

    init_tree(dfa, operation=Op.hold)
    logger.debug('[hold] Prop: {} Duration: {} Negation: {} Props: {}'.format(prop, duration, negation, props))
    return dfa
Ejemplo n.º 2
0
def accept_prop(props, prop=None, boolean=None):
    '''Creates a DFA which accepts:
    1) all symbols which contain proposition prop, if prop is not None;
    2) all symbols, if boolean is True;
    3) no symbol, if boolean is False.
    '''
    if prop is not None:
        assert prop in props
        guard = prop
        name = '(Prop ' + str(prop) + ')'
        logger.debug('[accent_prop] Prop: {} Props: {}'.format(prop, props))
    elif boolean is not None:
        assert type(boolean) == bool
        guard = '(1)' if boolean else '(0)'
        name = '(Bool ' + str(boolean) + ')'
        logger.debug('[accent_prop] Boolean: {} Props: {}'.format(boolean, props))
    else:
        raise AssertionError('Either prop or boolean must be given!')

    dfa = Fsa(props, directed=True, multi=False)
    dfa.name = name
    bitmaps = dfa.get_guard_bitmap(guard)
    ngen = it.count()
    u, v = ngen.next(), ngen.next()
    dfa.g.add_edge(u, v, attr_dict={'weight': 0, 'input': bitmaps, 'guard' : guard, 'label': guard})
    dfa.init[u] = 1
    dfa.final.add(v)

    init_tree(dfa, operation=Op.accept)
    return dfa
Ejemplo n.º 3
0
def concatenation(dfa1, dfa2):
    '''Creates a DFA which accepts the language of concatenated word accepted by
    dfa1 and dfa2. Is assumes that concatenation is non-ambiguous, i.e. every
    word in the resulting language can be uniquely decomposed into a prefix word
    from the language accepted by dfa1 and a suffix word from the language
    accepted by dfa2.
    Theorem: card(lang(concatenate(dfa1, dfa2))) ==
                                             card(lang(dfa1)) x card(lang(dfa2))
    '''
    assert dfa1.directed == dfa2.directed and dfa1.multi == dfa2.multi
    assert dfa1.props == dfa2.props
    assert dfa1.alphabet == dfa2.alphabet
    assert len(dfa1.init) == 1 and len(dfa2.init) == 1
    assert len(dfa1.final) == 1 and len(dfa2.final) == 1

    dfa = Fsa(dfa1.props, dfa1.directed, dfa1.multi)
    dfa.name = '(Concat {} {} )'.format(dfa1.name, dfa2.name)

    # relabel the two DFAs to avoid state name collisions and merge the final
    # state of dfa1 with the initial state of dfa2
    relabel_dfa(dfa1, start=0)
    init2 = dfa2.init.keys()[0]
    final1 = iter(dfa1.final).next()
    relabel_dfa(dfa2, mapping={init2: final1}, start=dfa1.g.number_of_nodes())
    assert len(set(dfa1.g.nodes()) & set(dfa2.g.nodes())) == 1
    dfa.g.add_edges_from(dfa1.g.edges_iter(data=True))
    dfa.g.add_edges_from(dfa2.g.edges_iter(data=True))

    # define initial state and final state
    dfa.init = dict(dfa1.init)
    dfa.final = set(dfa2.final)

    if getDFAType() == DFAType.Infinity:
        mark_concatenation(dfa1, dfa2, dfa)
    elif getDFAType() == DFAType.Normal:
        # minimize the DFA
        dfa = minimize_dfa(dfa)
    else:
        raise ValueError('DFA type must be either DFAType.Normal or ' +
                         'DFAType.Infinity! {} was given!'.format(getDFAType()))

    logger.debug('[concatenation] DFA1: {} DFA2: {}'.format(dfa1.name, dfa2.name))
    return dfa
Ejemplo n.º 4
0
def relabel_dfa(dfa, mapping='default', start=0, copy=False):
    '''Relabels the DFA. The new labels are given by the mapping dictionary. By
    default, it relabels the states with integers with the lowest one given by
    start. The dictionary can be a partial mapping of the nodes. The states
    which are not specified are labeled with integers starting from start.
    If copy is True a new copy of the DFA is returned, otherwise it performs an
    in-place relabeling.
    '''
    if mapping is 'default': # default mapping
        mapping = dict()
    keys = mapping.keys()
    nodes = [u for u in dfa.g.nodes_iter() if u not in keys]
    mapping.update(dict(zip(nodes, it.count(start))))

    if copy: # create new dfa
        ret = Fsa(dfa.props, dfa.directed, dfa.multi)
        ret.name = str(dfa.name)
    else: # in-place relabeling
        ret = dfa
    # relabel state, inital state and set of final states
    ret.g = nx.relabel_nodes(dfa.g, mapping=mapping, copy=True)
    ret.init = dict([(mapping[u], 1) for u in dfa.init.keys()])
    ret.final = set([mapping[u] for u in dfa.final])
    # copy tree
    copy_tree(dfa, ret, mapping=mapping)
    return ret
Ejemplo n.º 5
0
def gdtl2fsa(formula):
    tl, ap = gdtl2ltl(formula)
    ltl_formula = tl.formulaString(ltl=True)
    assert tl.isSynCoSafe()
    fsa = Fsa(multi=False)
    fsa.from_formula(ltl_formula)
    # optional add trap state
    fsa.add_trap_state()

    logging.info('Automaton size: %s', fsa.size())
    logging.info('Automaton: %s', fsa)

    return fsa, tl, ap
def construct_fsa():
    ap = set(['a', 'b'])  # set of atomic propositions
    fsa = Fsa(props=ap, multi=False)  # empty FSA with propsitions from `ap`

    # add states
    fsa.g.add_nodes_from(['s0', 's1', 's2', 's3'])

    # add transitions
    inputs = set(fsa.bitmap_of_props(value) for value in [set()])
    fsa.g.add_edge('s0', 's0', attr_dict={'input': inputs})

    inputs = set(fsa.bitmap_of_props(value) for value in [set(['a'])])
    fsa.g.add_edge('s0', 's1', attr_dict={'input': inputs})

    inputs = set(fsa.bitmap_of_props(value) for value in [set(['b'])])
    fsa.g.add_edge('s0', 's2', attr_dict={'input': inputs})

    inputs = set(fsa.bitmap_of_props(value) for value in [set(['a', 'b'])])
    fsa.g.add_edge('s0', 's3', attr_dict={'input': inputs})

    inputs = set(fsa.bitmap_of_props(value) for value in [set(), set(['a'])])
    fsa.g.add_edge('s1', 's1', attr_dict={'input': inputs})

    inputs = set(
        fsa.bitmap_of_props(value)
        for value in [set(['b']), set(['a', 'b'])])
    fsa.g.add_edge('s1', 's3', attr_dict={'input': inputs})

    inputs = set(fsa.bitmap_of_props(value) for value in [set(), set(['b'])])
    fsa.g.add_edge('s2', 's2', attr_dict={'input': inputs})

    inputs = set(
        fsa.bitmap_of_props(value)
        for value in [set(['a']), set(['a', 'b'])])
    fsa.g.add_edge('s2', 's3', attr_dict={'input': inputs})

    fsa.g.add_edge('s3', 's3', attr_dict={'input': fsa.alphabet})

    # set the initial state
    fsa.init['s0'] = 1

    # add `s3` to set of final/accepting states
    fsa.final.add('s3')
    return fsa
Ejemplo n.º 7
0
def repeat(phi_dfa, low, high):
    '''Creates a DFA which accepts the language associated with a within
    operator which encloses the formula corresponding to phi_dfa.
    '''
    assert len(phi_dfa.init) == 1
    assert len(phi_dfa.final) == 1

    init_state = phi_dfa.init.keys()[0]
    final_state = set(phi_dfa.final).pop()

    # remove trap states if there are any
    phi_dfa.remove_trap_states()
    # initialize the resulting dfa
    dfa = Fsa(phi_dfa.props, phi_dfa.directed, phi_dfa.multi)
    dfa.name = '(Repeat {} {} {} )'.format(phi_dfa.name, low, high)
    # compute the maximum number of restarts
    b = nx.shortest_path_length(phi_dfa.g, source=init_state, target=final_state)
    d = high - low - b + 2
    # copy dfa to dfa_aux and initialize the list of restart states
    inits = []
    nstates = 0
    for k in range(d):
        # 1. relabel dfa_aux
        mapping = dict(zip(phi_dfa.g.nodes_iter(),
                         range(nstates, nstates + phi_dfa.g.number_of_nodes())))
        mapping[final_state] = -1 # mark final state as special
        dfa_aux = relabel_dfa(phi_dfa, mapping, copy=True)
        # 2. compute truncated dfa_aux
        truncate_dfa(dfa_aux, cutoff=(high-low+1)-k)
        # 3. add truncated dfa_aux to dfa
        dfa.g.add_edges_from(dfa_aux.g.edges_iter(data=True))
        inits.append(dfa_aux.init.keys()[0])
        nstates += dfa_aux.g.number_of_nodes()
    # set initial and final state
    dfa.init = {inits[0] : 1}
    dfa.final = set([-1])
    # create restart transitions
    current_states = set([inits[0]])
    for rstate in inits[1:]: # current restart state
        next_states = set([])
        # connect current states to restart state for (else) symbols
        for state in current_states:
            bitmaps = set()
            guard = '(else)'
            for _, next_state, d in dfa.g.out_edges_iter(state, data=True):
                bitmaps |= d['input']
                next_states.add(next_state)
            bitmaps = dfa.alphabet - bitmaps

            if state not in dfa.final and bitmaps:
                dfa.g.add_edge(state, rstate, attr_dict={'weight': 0, 'input': bitmaps, 'guard' : guard, 'label': guard})
        # update current states
        current_states = next_states | set([rstate])

    # relabel states
    relabel_dfa(dfa)
    # add states to accept a prefix word of any symbol of length low
    if low > 0:
        guard = '(1)'
        bitmaps = dfa.get_guard_bitmap(guard)
        ngen = it.count(start=dfa.g.number_of_nodes())
        nodes = [ngen.next() for _ in range(low)]
        attr_dict={'weight': 0, 'input': bitmaps, 'guard' : guard, 'label': guard}
        dfa.g.add_path(nodes, **attr_dict)
        dfa.g.add_edge(nodes[-1], dfa.init.keys()[0], attr_dict)
        dfa.init = {nodes[0] : 1}

    logger.debug('[within] Low: {} High: {} DFA: {}'.format(low, high, phi_dfa.name))
    return dfa
Ejemplo n.º 8
0
def union(dfa1, dfa2):
    '''Creates a DFA which accepts the union of the languages corresponding to
    the two DFAs. The disjunction operation of TWTL is mapped to union.
    If an infinity DFA is generated, the corresponding meta-data is copied as
    well.
    '''
    assert dfa1.directed == dfa2.directed and dfa1.multi == dfa2.multi
    assert dfa1.props == dfa2.props
    assert dfa1.alphabet == dfa2.alphabet
    assert len(dfa1.init) == 1 and len(dfa2.init) == 1
    assert len(dfa1.final) == 1 and len(dfa2.final) == 1

    dfa = Fsa(dfa1.props, dfa1.directed, dfa1.multi)
    dfa.name = '(Union {} {} )'.format(dfa1.name, dfa2.name)

    # add self-loops on final states and trap states
    attr_dict={'weight': 0, 'input': dfa.alphabet, 'guard' : '(1)', 'label': '(1)'}
    dfa1.g.add_edges_from([(s, s, attr_dict) for s in dfa1.final])
    dfa2.g.add_edges_from([(s, s, attr_dict) for s in dfa2.final])
    dfa1.add_trap_state()
    dfa2.add_trap_state()
    dfa1.g.remove_edges_from([(s, s) for s in dfa1.final])
    dfa2.g.remove_edges_from([(s, s) for s in dfa2.final])

    init = list(it.product(dfa1.init.keys(), dfa2.init.keys()))
    dfa.init = dict(zip(init, (1,)*len(init)))
    assert len(dfa.init) == 1 # dfa1 and dfa2 are deterministic

    stack = list(init)
    while stack:
        u1, u2 = stack.pop()
        for _, v1, d1 in dfa1.g.edges_iter(u1, data=True):
            for _, v2, d2 in dfa2.g.edges_iter(u2, data=True):
                bitmaps = d1['input'] & d2['input']
                if bitmaps:
                    if (v1, v2) not in dfa.g:
                        stack.append((v1, v2))
                    guard = '({}) & ({})'.format(d1['guard'], d2['guard'])
                    dfa.g.add_edge((u1, u2), (v1, v2), attr_dict={'weight': 0, 'input': bitmaps, 'guard' : guard, 'label': guard})

    # compute set of final states
    dfa.final = set([(u, v) for u, v in dfa.g.nodes_iter()
                                         if u in dfa1.final or v in dfa2.final])

    # remove trap states
    dfa1.g.remove_nodes_from(['trap'])
    dfa2.g.remove_nodes_from(['trap'])
    dfa.g.remove_nodes_from([('trap', 'trap')])

    # merge finals
    if len(dfa.final) > 1:
        final = (iter(dfa1.final).next(), iter(dfa2.final).next())
        # satisfies both left and right sub-formulae
        choices = dict([(u, Choice(both=d['input']))
                               for u, _, d in dfa.g.in_edges(final, data=True)])
        for u, v, d in dfa.g.in_edges_iter(dfa.final - set([final]), data=True):
            bitmaps = set(d['input'])
            guard = d['guard']
            if dfa.g.has_edge(u, final):
                bitmaps |= dfa.g[u][final]['input']
                guard = '({}) | ({})'.format(guard, dfa.g[u][final]['guard'])
            dfa.g.add_edge(u, final, attr_dict={'weight': 0, 'input': bitmaps, 'guard' : guard, 'label': guard})
            if v[0] in dfa1.final: # satisfies only the left sub-formula
                assert v[1] not in dfa2.final
                choices.setdefault(u, Choice()).left.update(d['input'])
            if v[1] in dfa2.final: # satisfies only the right sub-formula
                assert v[0] not in dfa1.final
                choices.setdefault(u, Choice()).right.update(d['input'])
        # remove all other final states
        dfa.g.remove_nodes_from(dfa.final - set([final]))
        dfa.final = set([final])

    if getDFAType() == DFAType.Infinity:
        mark_product(dfa1, dfa2, dfa, Op.union, choices)
    elif getDFAType() == DFAType.Normal:
        # minimize the DFA
        dfa = minimize_dfa(dfa)
    else:
        raise ValueError('DFA type must be either DFAType.Normal or ' +
                         'DFAType.Infinity! {} was given!'.format(getDFAType()))

    # relabel states
    relabel_dfa(dfa)

    logger.debug('[union] DFA1: {} DFA2: {}'.format(dfa1.name, dfa2.name))
    return dfa
Ejemplo n.º 9
0
def intersection(dfa1, dfa2):
    '''Creates a DFA which accepts the intersection of the languages
    corresponding to the two DFAs. The conjunction operation of TWTL is mapped
    to intersection.
    If an infinity DFA is generated, the corresponding meta-data is copied as
    well.
    '''
    assert dfa1.directed == dfa2.directed and dfa1.multi == dfa2.multi
    assert dfa1.props == dfa2.props
    assert dfa1.alphabet == dfa2.alphabet
    assert len(dfa1.init) == 1 and len(dfa2.init) == 1
    assert len(dfa1.final) == 1 and len(dfa2.final) == 1

    dfa = Fsa(dfa1.props, dfa1.directed, dfa1.multi)
    dfa.name = '(Intersection {} {} )'.format(dfa1.name, dfa2.name)

    init = list(it.product(dfa1.init.keys(), dfa2.init.keys()))
    dfa.init = dict(zip(init, (1,)*len(init)))
    assert len(dfa.init) == 1

    stack = list(init)
    while stack:
        u1, u2 = stack.pop()
        for _, v1, d1 in dfa1.g.edges_iter(u1, data=True):
            for _, v2, d2 in dfa2.g.edges_iter(u2, data=True):
                bitmaps = d1['input'] & d2['input']
                if bitmaps:
                    if (v1, v2) not in dfa.g:
                        stack.append((v1, v2))
                    guard = '({}) & ({})'.format(d1['guard'], d2['guard'])
                    dfa.g.add_edge((u1, u2), (v1, v2), attr_dict={'weight': 0, 'input': bitmaps, 'guard' : guard, 'label': guard})
        if u1 in dfa1.final:
            for _, v2, d2 in dfa2.g.edges_iter(u2, data=True):
                if (u1, v2) not in dfa.g:
                    stack.append((u1, v2))
                bitmaps = set(d2['input'])
                guard = d2['guard']
                dfa.g.add_edge((u1, u2), (u1, v2), attr_dict={'weight': 0, 'input': bitmaps, 'guard' : guard, 'label': guard})
        if u2 in dfa2.final:
            for _, v1, d1 in dfa1.g.edges_iter(u1, data=True):
                if (v1, u2) not in dfa.g:
                    stack.append((v1, u2))
                bitmaps = set(d1['input'])
                guard = d1['guard']
                dfa.g.add_edge((u1, u2), (v1, u2), attr_dict={'weight': 0, 'input': bitmaps, 'guard' : guard, 'label': guard})

    # the set of final states is the product of final sets of dfa1 and dfa2
    dfa.final = set(it.product(dfa1.final, dfa2.final))
    assert len(dfa.final) == 1

    if getDFAType() == DFAType.Infinity:
        mark_product(dfa1, dfa2, dfa, Op.intersection)
    elif getDFAType() == DFAType.Normal:
        # minimize the DFA
        dfa = minimize_dfa(dfa)
    else:
        raise ValueError('DFA type must be either DFAType.Normal or ' +
                         'DFAType.Infinity! {} was given!'.format(getDFAType()))

    # relabel states
    relabel_dfa(dfa)

    logger.debug('[intersection] DFA1: {} DFA2: {}'.format(dfa1.name, dfa2.name))
    return dfa