def _parse_atom(token_list): if len(token_list) == 1: return (LTL(token_list[0], None, None)) last_index = len(token_list) - 1 if token_list[0] != '(' or token_list[last_index] != ')': raise AssertionError( 'Unexpected atom %s. did you forget parentheses?' % str(token_list)) return _parse_formula(token_list[1:last_index])
def _parse_formula(token_list): if token_list[0] in 'FGX!': if len(token_list) == 1: raise Exception('Error while parsing: solitary LTL operator %s' % token_list[0]) return LTL(token_list[0], None, _parse_formula(token_list[1:])) elif len(token_list) == 1: return _parse_atom(token_list) else: return _parse_orformula(token_list)
def __init__(self, ltl): """ This is merely a mock for an actual constructor """ if (ltl == parse('obstacle R (!goal)')): self.states = [ KripkeStructure.State(0, ltl), KripkeStructure.State(1, LTL('true', None, None)), KripkeStructure.State(2, LTL('false', None, None)) ] # The interpretation of an atomic proposition with a '$' at the end # is the opposite of the interpretation of the atomic proposition # without the '$'. E.g. the interpretation of 'goal$' is everything # but the interpretation of 'goal'. self.aps = set(['goal', 'goal$', 'obstacle', 'obstacle$']) self.states[0].add_transition(frozenset(), 0) self.states[0].add_transition(frozenset(['goal']), 2) self.states[0].add_transition(frozenset(['obstacle']), 1) self.states[0].add_transition(frozenset(['goal$']), 1) self.states[0].add_transition(frozenset(['obstacle$']), 0) self.states[0].add_transition(frozenset(['goal$', 'obstacle$']), 0) self.states[0].add_transition(frozenset(['goal$', 'obstacle']), 1) self.states[0].add_transition(frozenset(['goal', 'obstacle$']), 2) self.states[0].add_transition(frozenset(['goal', 'obstacle']), 2) self.states[1].add_transition(frozenset(), 1) self.states[1].add_transition(frozenset(['goal']), 1) self.states[1].add_transition(frozenset(['obstacle']), 1) self.states[1].add_transition(frozenset(['goal$']), 1) self.states[1].add_transition(frozenset(['obstacle$']), 1) self.states[1].add_transition(frozenset(['goal$', 'obstacle$']), 1) self.states[1].add_transition(frozenset(['goal$', 'obstacle']), 1) self.states[1].add_transition(frozenset(['goal', 'obstacle$']), 1) self.states[1].add_transition(frozenset(['goal', 'obstacle']), 1) self.states[2].add_transition(frozenset(), 2) self.states[2].add_transition(frozenset(['goal']), 2) self.states[2].add_transition(frozenset(['obstacle']), 2) self.states[2].add_transition(frozenset(['goal$']), 2) self.states[2].add_transition(frozenset(['obstacle$']), 2) self.states[2].add_transition(frozenset(['goal$', 'obstacle$']), 2) self.states[2].add_transition(frozenset(['goal$', 'obstacle']), 2) self.states[2].add_transition(frozenset(['goal', 'obstacle$']), 2) self.states[2].add_transition(frozenset(['goal', 'obstacle']), 2) self.initial_state = self.states[0]
def find_witness(self, max_iterations=50000): """ Attempts to find an accepting run for the product automaton, using breadth-first search. Returns the trace of maneuvers, if an accepting run is found, or None otherwise. """ depth = max_iterations final_maneuver = None queue = [(self.ma.initial_state, self.ks.initial_state, [])] while final_maneuver is None and depth >= 0 and len(queue) > 0: qm, qk, trace = queue[0] # The atomic propositions that hold true in the beginning of qm valid_aps = [ap for ap in self.ks.aps if qm.entails(ap, False)] if (self.ks.states[qk.readletter( frozenset(valid_aps))].label == LTL('false', None, None)): # Found counterexample, return witness trajectory return True, trace # We need a deep copy here, since the trace differs for every # maneuver newtrace = deepcopy(trace) newtrace.append(qm) # The atomic propositions that hold true for the duration of qm valid_aps = [ap for ap in self.ks.aps if qm.entails(ap, True)] # The next state in the Kripke structure after reading the # valid atomic propositions qk_next = self.ks.states[qk.readletter(frozenset(valid_aps))] if len(queue) > 0: queue = queue[1:] depth -= 1 if (qk_next.label == LTL('true', None, None)): continue # The successor maneuvers of qm qm_sms = qm.successors() for qm_next in qm_sms: queue.append((qm_next, qk_next, newtrace)) return False, None
def run_model_checker(canvas): phi = parse('(!obstacle) U goal') notphi = LTL('!', None, phi).toSafeLTL() ks = KripkeStructure(notphi) ma = MA(init_position=(32.0, 2.0)) pa = PA(ks, ma) draw_rectangle(canvas, interpretation('goal'), green, green) draw_rectangle(canvas, interpretation('obstacle'), red, red) witness_found, trace = pa.find_witness() if witness_found: for qm in trace: xs, ys = qm.starting_position xf, yf = qm.final_position draw_dot(canvas, xs, ys) draw_dot(canvas, xf, yf) draw_line(canvas, xs, ys, xf, yf) draw_rectangle(canvas, qm.initial_occupancy(), blue, transp) draw_rectangle(canvas, qm.overall_occupancy(), blue, transp) else: print('No witness trajectory found')
def _parse_orformula(token_list): left, right, _ = _find_outer('|', token_list) if right is None: return _parse_andformula(left) return LTL('|', _parse_andformula(left), _parse_orformula(right))
def _parse_uformula(token_list): left, right, token = _find_outer('UR', token_list) if right is None: return _parse_atom(left) return LTL(token, _parse_atom(left), _parse_atom(right))