Example #1
0
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)
Example #2
0
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
Example #3
0
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
Example #4
0
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)
Example #5
0
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))})
Example #6
0
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)
Example #7
0
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)
Example #8
0
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]
Example #9
0
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]
Example #10
0
    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)
Example #11
0
    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)