def explain_ax(fsm, state, phi): """ Explain why state of fsm satisfies AX phi. fsm -- the fsm; state -- a state of fsm satisfying AX phi; phi -- the set of states of fsm satifying phi. """ # To show that state satisfies AX phi, we have to show that all successors # satisfy phi or are not fair. # AX phi = ~fsm.pre((~phi & fair)) # We don't explain why the extracted successors are not fair (when it is # the case). # phi or not fair state phi = (phi | ~fair_states(fsm)) & fsm.reachable_states # Get successor of state belonging to phi all_succ = explain_all_succ(fsm, state, phi) # Build successors successors = set() for inputs, next in all_succ: succ.add((inputs, Explanation(next))) return Explanation(state, succ)
def explain_ceu(fsm, state, agents, phi, psi, states=None): """ Explain why state of fsm satisfies <agents> phi U psi. fsm -- the fsm; state -- a state of fsm satisfying <agents> phi U psi; agents -- the agents of the specification; phi -- the set of states of fsm satifying phi; psi -- the set of states of fsm satifying psi; states -- a dictionary of state->explanation pairs. """ # <agents> phi U psi = mu Y. psi | (phi & fsm.pre_strat(Y, agents)) # To show that state satisfies <agents> phi U psi # if state in psi, return Explanation(state) because state is its own # explanation # otherwise, compute the fixpoint, until state is in the last set # then choose one action always leading to states of the previous set # (it is possible because state is added because it can go one step in # the right direction), and recursively explain why the found states # satisfy <agents> phi U psi. # states is used to store already explained states and create a directed # acyclic graph instead of the pure execution tree, to avoid a blow up # in terms of number of states. if states is None: states = {} if state in states: return states[state] if state <= psi: new = Explanation(state) states[state] = new return new else: f = lambda Y: psi | (phi & fsm.pre_strat(Y, agents)) old = BDD.false(fsm.bddEnc.DDmanager) new = psi while not state <= new: old = new new = f(old) expl = explain_cex(fsm, state, agents, old) successors = set() for action, succ in expl.successors: successors.add( (action, explain_ceu(fsm, succ.state, agents, phi, psi, states))) new = Explanation(state, successors) states[state] = new return new
def explain_eg(fsm, state, phi): """ Explain why state of fsm satisfies EG phi. fsm -- the fsm; state -- a state of fsm satisfying EG phi; phi -- the set of states of fsm satifying phi. """ # To explain why state satisfies EG phi, # we have to show a path ending with a loop going through all fairness # constraints # If no fairness constraints are provided, # model checking sums up to # EG p = nu Z. p & Pre(Z) # In this case, just explain by extending EX EG p until a former state is # encountered if len(fsm.fairness_contraints) <= 0: states = BDD.false(fsm.bddEnc.DDmanager) orig_expl = expl = Explanation(state) eg_phi = eg(fsm, phi) while not state <= states: states = states | state if (fsm.post(state) & states).isnot_false(): # There is a successor of state that we already saw. # Use this one action, state = explain_one_succ(fsm, state, states) else: # There is no successor of state in states, guess a successor action, state = explain_one_succ(fsm, state, eg_phi) nextexpl = Explanation(state) expl.successors = {(action, nextexpl)} expl = nextexpl # state <= states # this means that state lead to a previous state in the path # we must explain it cur = orig_expl while cur.state != state: cur = cur.successors[0][1] action = fsm.pick_one_inputs( fsm.get_inputs_between_states(state, cur.state)) expl.successors = {(action, cur)} return orig_expl # Otherwise, there are fairness constraints else: pass # TODO
def explain_eu(fsm, state, phi, psi): """ Explain why state of fsm satisfies E phi U psi. fsm -- the fsm; state -- a state of fsm satisfying E phi U psi; phi -- the set of states of fsm satifying phi; psi -- the set of states of fsm satifying psi. """ # E phi U psi = mu Z . (psi & fair) | (phi & fsm.pre(Z)) # To show that state satisfies E phi U psi # we have to show a path of successors satisfying phi, # ending in a fair state satisfying psi. # If state satisfies psi, stop; # otherwise, compute the fixpoint until state is in the last set, # then show that state can reach the previous set, # and recursively explain why the reached state satisfies E phi U psi. # We don't explain why the psi-states are fair. if state <= psi: return Explanation(state) else: # Compute the fixpoint until state is reached (backward) fair = fair_states(fsm) f = lambda Y: (psi & fair) | (phi & fsm.pre(Y)) old = BDD.false(fsm.bddEnc.DDmanager) new = psi while not state <= new: old = new new = f(old) # If state <= new, (and it was not the case in the previous iteration) # old does not contain state but a successor of state. # explain that state can reach old inputs, next = explain_one_succ(fsm, state, old) successors = {(inputs, explain_eu(fsm, next, phi, psi))} return Explanation(state, successors)
def explain_ex(fsm, state, phi): """ Explain why state of fsm satisfies EX phi. fsm -- the fsm; state -- a state of fsm satisfying EX phi; phi -- the set of states of fsm satifying phi. """ # To show that state satisfies EX phi, we have to exhibit a successor # of state belonging to phi that is reachable and fair. # EX phi = fsm.pre(phi & fair) # We don't explain why the extracted successor is fair. phi = phi & fair_states(fsm) & fsm.reachable_states # Get successor of state belonging to phi inputs, next = explain_one_succ(fsm, state, phi) return Explanation(state, {(inputs, Explanation(next))})
def explain_cax(fsm, state, agents, phi): """ Explain why state of fsm satisfies [agents] X phi. fsm -- the fsm; state -- a state of fsm satisfying [agents] X phi; agents -- the agents of the specification; phi -- the set of states of fsm satifying phi. """ # To show that state satisfies [agents] X phi, we have to show that # for each action of agents, there is a completion by other agents such that # the reached state satisfies phi. # That is, we have to take all actions and of agents and for each of them # exhibit a successor satisfying phi. # Cubes gamma_cube = fsm.inputs_cube_for_agents(agents) ngamma_cube = fsm.bddEnc.inputsCube - gamma_cube # Get all different actions of agents ag_actions = set() for action in fsm.pick_all_inputs( (fsm.protocol(agents) & state).forsome(fsm.bddEnc.statesCube)): ag_actions.add(action.forsome(ngamma_cube)) # Get successors successors = set() for ag_action in ag_actions: # Choose one successor through ag_action succ = fsm.pick_one_state( fsm.post(state, ag_action) & phi & fsm.bddEnc.statesMask) # Get the full action act = fsm.pick_one_inputs( fsm.get_inputs_between_states(state, succ) & ag_action & fsm.bddEnc.inputsMask) successors.add((act, Explanation(succ))) return Explanation(state, successors)
def explain_cex(fsm, state, agents, phi): """ Explain why state of fsm satisfies <agents> X phi. fsm -- the fsm; state -- a state of fsm satisfying <agents> X phi; agents -- the agents of the specification; phi -- the set of states of fsm satifying phi. """ # To show that state satisfies <agents> X phi, we have to exhibit an action # of agents such that all reached states are in phi. # We choose to execute this action, that is, exhibit the state, and # all the reached states through the action. # Cubes gamma_cube = fsm.inputs_cube_for_agents(agents) ngamma_cube = fsm.bddEnc.inputsCube - gamma_cube # Get the winning actions, for all state si = fsm.pre_strat_si(phi, agents) # The winning actions from state are the ones belonging to its protocol # and to si actions = (si & (fsm.protocol(agents) & state) & fsm.bddEnc.inputsMask).forsome(fsm.bddEnc.statesCube) fullaction = fsm.pick_one_inputs(actions) ag_action = fullaction.forsome(ngamma_cube) # Build the graph successors = set() for successor in fsm.pick_all_states( fsm.post(state, ag_action) & fsm.bddEnc.statesMask): nextexpl = Explanation(successor) allactions = fsm.get_inputs_between_states(state, successor) & ag_action for action in fsm.pick_all_inputs(allactions & fsm.bddEnc.inputsMask): successors.add((action, nextexpl)) return Explanation(state, successors)
def explain_caw(fsm, state, agents, phi, psi): """ Explain why state of fsm satisfies [agents] phi W psi. fsm -- the fsm; state -- a state of fsm satisfying [agents] phi W psi; agents -- the agents of the specification; phi -- the set of states of fsm satifying phi; psi -- the set of states of fsm satifying psi. """ # To show that state satisfies [agents] phi W psi # either it satisfies psi and we are done, # or we have to explain why state satisfies [agents]X[agents]phi W psi # and iterate until we reach the fixpoint or stop at psi. # By keeping track of already visited states, # we know that we will finally extract the full sub-system. # Get CEW(phi,psi) cawpp = ~ceu(fsm, agents, ~psi, ~psi & ~phi) # Get all states and transitions extract = {state} states = set() transitions = set() while len(extract) > 0: cur = extract.pop() states.add(cur) if not cur <= psi: expl = explain_cax(fsm, cur, agents, cawpp) for act, succ in expl.successors: transitions.add((cur, act, succ.state)) if succ.state not in states: extract.add(succ.state) # Build the graph nodes = {} for s in states: nodes[s] = Explanation(s) for cur, act, succ in transitions: nodes[cur].successors.add((act, nodes[succ])) return nodes[state]
def explain_cag(fsm, state, agents, phi): """ Explain why state of fsm satisfies [agents] G phi. fsm -- the fsm; state -- a state of fsm satisfying [agents] G phi; agents -- the agents of the specification; phi -- the set of states of fsm satifying phi. """ # To show that state satisfies [agents] G phi # we just have to explain why state satisfies [agents]X[agents]G phi # and iterate until we reach the fixpoint. # By keeping track of already visited states, # we know that we will finally extract the full sub-system of phi states. # Get CAG(phi) cagphi = ~ceu(fsm, agents, BDD.true(fsm.bddEnc.DDmanager), ~phi) # Get all states and transitions extract = {state} states = set() transitions = set() while len(extract) > 0: cur = extract.pop() states.add(cur) expl = explain_cax(fsm, cur, agents, cagphi) for act, succ in expl.successors: transitions.add((cur, act, succ.state)) if succ.state not in states: extract.add(succ.state) # Build the graph nodes = {} for s in states: nodes[s] = Explanation(s) for cur, act, succ in transitions: nodes[cur].successors.add((act, nodes[succ])) return nodes[state]
def cntex_top(mas, spec): """ Explain why the top operator of spec is violated. mas -- the concerned MAS; spec -- the given specification; must be violated by mas. """ # Get violating initial state sat = evalATL(mas, spec) state = mas.pick_one_state( (~sat & mas.bddEnc.statesInputsMask & mas.init)) # Switch between operators if type(spec) is CEF: # ~<a> F p = [a] G ~p phi = ~evalATL(mas, spec.child) agents = {atom.value for atom in spec.group} return explain_cag(mas, state, agents, phi) elif type(spec) is CEG: # ~<a> G p = [a] F ~p = [a] true U ~p phi = BDD.true(mas.bddEnc.DDmanager) psi = ~evalATL(mas, spec.child) agents = {atom.value for atom in spec.group} return explain_cau(mas, state, agents, phi, psi) elif type(spec) is CEX: # ~<a> X p = [a] X ~p phi = ~evalATL(mas, spec.child) agents = {atom.value for atom in spec.group} return explain_cax(mas, state, agents, phi) elif type(spec) is CEU: # ~<a> p U q = [q] ~q W ~p & ~q p = evalATL(mas, spec.left) q = evalATL(mas, spec.right) phi = ~q psi = ~p & ~q agents = {atom.value for atom in spec.group} return explain_caw(mas, state, agents, phi, psi) elif type(spec) is CEW: # ~<a> p W q = [q] ~q U ~p & ~q p = evalATL(mas, spec.left) q = evalATL(mas, spec.right) phi = ~q psi = ~p & ~q agents = {atom.value for atom in spec.group} return explain_cau(mas, state, agents, phi, psi) elif type(spec) is CAF: # ~[a] F p = <a> G ~p phi = ~evalATL(mas, spec.child) agents = {atom.value for atom in spec.group} return explain_ceg(mas, state, agents, phi) elif type(spec) is CAG: # ~[a] G p = <a> F ~p = <a> true U ~p phi = BDD.true(mas.bddEnc.DDmanager) psi = ~evalATL(mas, spec.child) agents = {atom.value for atom in spec.group} return explain_ceu(mas, state, agents, phi, psi) elif type(spec) is CAX: # ~[a] X p = <a> X ~p phi = ~evalATL(mas, spec.child) agents = {atom.value for atom in spec.group} return explain_cex(mas, state, agents, phi) elif type(spec) is CAU: # ~[a] p U q = <q> ~q W ~p & ~q p = evalATL(mas, spec.left) q = evalATL(mas, spec.right) phi = ~q psi = ~p & ~q agents = {atom.value for atom in spec.group} return explain_cew(mas, state, agents, phi, psi) elif type(spec) is CAW: # ~[a] p W q = <q> ~q U ~p & ~q p = evalATL(mas, spec.left) q = evalATL(mas, spec.right) phi = ~q psi = ~p & ~q agents = {atom.value for atom in spec.group} return explain_ceu(mas, state, agents, phi, psi) else: return Explanation(state)
def explain_top(mas, spec): """ Explain why the top operator of spec is satisfied. mas -- the concerned MAS; spec -- the given specification; must be satisfied by mas. """ # Get satisfying initial state sat = evalATL(mas, spec) state = mas.pick_one_state( (sat & mas.bddEnc.statesInputsMask & mas.init)) # Switch between operators if type(spec) is CEF: phi = BDD.true(mas.bddEnc.DDmanager) psi = evalATL(mas, spec.child) agents = {atom.value for atom in spec.group} return explain_ceu(mas, state, agents, phi, psi) elif type(spec) is CEG: phi = evalATL(mas, spec.child) agents = {atom.value for atom in spec.group} return explain_ceg(mas, state, agents, phi) elif type(spec) is CEX: phi = evalATL(mas, spec.child) agents = {atom.value for atom in spec.group} return explain_cex(mas, state, agents, phi) elif type(spec) is CEU: phi = evalATL(mas, spec.left) psi = evalATL(mas, spec.right) agents = {atom.value for atom in spec.group} return explain_ceu(mas, state, agents, phi, psi) elif type(spec) is CEW: phi = evalATL(mas, spec.left) psi = evalATL(mas, spec.right) agents = {atom.value for atom in spec.group} return explain_cew(mas, state, agents, phi, psi) elif type(spec) is CAF: phi = BDD.true(mas.bddEnc.DDmanager) psi = evalATL(mas, spec.child) agents = {atom.value for atom in spec.group} return explain_cau(mas, state, agents, phi, psi) elif type(spec) is CAG: phi = evalATL(mas, spec.child) agents = {atom.value for atom in spec.group} return explain_cag(mas, state, agents, phi) elif type(spec) is CAX: phi = evalATL(mas, spec.child) agents = {atom.value for atom in spec.group} return explain_cax(mas, state, agents, phi) elif type(spec) is CAU: phi = evalATL(mas, spec.left) psi = evalATL(mas, spec.right) agents = {atom.value for atom in spec.group} return explain_cau(mas, state, agents, phi, psi) elif type(spec) is CAW: phi = evalATL(mas, spec.left) psi = evalATL(mas, spec.right) agents = {atom.value for atom in spec.group} return explain_caw(mas, state, agents, phi, psi) else: return Explanation(state)