def filter_cew_moves(mas, agents, moves_1, moves_2, moves): """ mas -- a multi-agent system; agents -- a subset of agents of mas; moves_1 -- a subset of moves for agents; moves_2 -- a subset of moves for agents; moves -- a closed set of moves for agents. """ # Abstract away actions of states states_1 = moves_1.forsome(mas.bddEnc.inputsCube) states_2 = moves_2.forsome(mas.bddEnc.inputsCube) states_1 = states_1 & mas.bddEnc.statesMask states_2 = states_2 & mas.bddEnc.statesMask moves_1 = states_1 & mas.protocol(agents) moves_2 = states_2 & mas.protocol(agents) # If there are no fairness constraints, the computation is simpler if not mas.fairness_constraints: # nu Q'. moves_2 & moves | (moves_1 & moves & pre_ce(Q')) return fixpoint(lambda Z: moves_2 & moves | (moves_1 & moves & pre_ce_moves(mas, agents, Z, moves)), BDD.true(mas)) else: moves_1_2_n = moves_1 | moves_2 | nfair_ce_moves(mas, agents, moves) moves_1_2_n = moves_1_2_n & moves return stay_ce_moves(mas, agents, moves_1_2_n, states_2 & moves, moves)
def _nfair(mas, formula, agents): """ Return the set of states in which the given agents cannot avoid a fair path by using the strategy encoded in these states. """ jump = mas.transitions[formula]["jump"] equiv = mas.transitions[formula]["equiv"] follow = mas.transitions[formula]["follow"] if not mas.fairness_constraints: return BDD.false(mas) else: # nfair = # mu Z. \/_fc []_group_follow(nu Y. (Z \/ ~fc) /\ []_group_follow(Y)) def inner(Z): # \/_fc []_group_follow(nu Y. (Z \/ ~fc) /\ []_group_follow(Y)) res = BDD.false(mas) for fc in mas.fairness_constraints: fc = fc.forsome(mas.bddEnc.inputsCube) nfc = ~fc res = res | ~follow.pre(~fixpoint( lambda Y: (Z | nfc) & ~follow.pre(~Y), BDD.true(mas))) return res res = fixpoint(inner, BDD.false(mas)) return res
def filter_cew(mas, agents, states_1, states_2, moves): """ Return the set of states of mas for which there exists a strategy for agents compatible with moves such that all fair paths enforced by the strategy reach a state of states_2 through states of states_1, or stay in states_1 forever. mas -- a multi-agent system; agents -- a subset of agents of mas; states_1 -- a subset of states of mas; states_2 -- a subset of states of mas; moves -- a closed set of moves for agents. """ # Abstract away actions of states states_1 = states_1.forsome(mas.bddEnc.inputsCube) states_2 = states_2.forsome(mas.bddEnc.inputsCube) states_1 = states_1 & mas.bddEnc.statesMask states_2 = states_2 & mas.bddEnc.statesMask # If there are no fairness constraints, the computation is simpler if not mas.fairness_constraints: # nu Q'. states_2 | (states_1 & pre_ce(Q')) return fixpoint(lambda Z: states_2 | (states_1 & pre_ce(mas, agents, Z, moves)), BDD.true(mas)) else: states_1_2_n = states_1 | states_2 | nfair_ce(mas, agents, moves) return stay_ce(mas, agents, states_1_2_n, states_2, moves)
def nfair_ce_moves(mas, agents, moves): """ mas -- a multi-agent system; agents -- a subset of agents of mas; moves -- a closed set of moves for agents. """ # If there are no fairness constraints, there are no nfair states. if not mas.fairness_constraints: return BDD.false(mas) else: def inner(Z): res = BDD.false(mas) for fc in mas.fairness_constraints: fc = fc.forsome(mas.bddEnc.inputsCube) nfc = ~fc & mas.bddEnc.statesMask nfc_moves = nfc & mas.protocol(agents) & moves m = stay_ce_moves(mas, agents, Z | nfc_moves, BDD.false(mas), moves) res |= pre_ce_moves(mas, agents, m, moves) return res return fixpoint(inner, BDD.false(mas))
def inner(Z): res = BDD.true(mas) for fc in mas.fairness_constraints: fc = fc.forsome(mas.bddEnc.inputsCube) res = res & run.pre( fixpoint(lambda Y: (Z & fc) | run.pre(Y), BDD.false(mas))) return res
def inner(Z): res = Z for f in fsm.fairness_constraints: res = res & fixpoint(lambda Y : (Z & f) | (phi & fsm.weak_pre(Y.forsome(fsm.bddEnc.inputsCube))), BDD.false(fsm.bddEnc.DDmanager)) return phi & fsm.weak_pre(res.forsome(fsm.bddEnc.inputsCube))
def inner(Z): res = Z for f in fsm.fairness_constraints: res = res & fixpoint( lambda Y: (Z & f) | (phi & fsm.weak_pre(Y.forsome(fsm.bddEnc.inputsCube))), BDD.false(fsm.bddEnc.DDmanager)) return phi & fsm.weak_pre(res.forsome(fsm.bddEnc.inputsCube))
def reach(mas, states): """ Return the set of states reachable from states in mas. mas -- a multi-agents system; states -- a subset of states of mas. """ return fixpoint(lambda Z: states | mas.post(Z), BDD.false(mas))
def inner(Z): # \/_fc []_group_follow(nu Y. (Z \/ ~fc) /\ []_group_follow(Y)) res = BDD.false(mas) for fc in mas.fairness_constraints: fc = fc.forsome(mas.bddEnc.inputsCube) nfc = ~fc res = res | ~follow.pre(~fixpoint( lambda Y: (Z | nfc) & ~follow.pre(~Y), BDD.true(mas))) return res
def inner(Z): res = BDD.true(mas) for fc in mas.fairness_constraints: fc = fc.forsome(mas.bddEnc.inputsCube) res = res & run.pre( fixpoint( lambda Y: (states_2 & _fair(mas)) | (Z & fc) | (states_1 & run.pre(Y)), BDD.false(mas))) return (res & states_1) | (states_2 & _fair(mas))
def ew(mas, states_1, states_2): run = mas.trans if not mas.fairness_constraints: return fixpoint(lambda Z: states_2 | (states_1 & run.pre(Z)), BDD.true(mas)) else: def inner(Z): res = BDD.true(mas) for fc in mas.fairness_constraints: fc = fc.forsome(mas.bddEnc.inputsCube) res = res & run.pre( fixpoint( lambda Y: (states_2 & _fair(mas)) | (Z & fc) | (states_1 & run.pre(Y)), BDD.false(mas))) return (res & states_1) | (states_2 & _fair(mas)) return fixpoint(inner, BDD.true(mas))
def filter_ceu_moves(mas, agents, moves_1, moves_2, moves): """ mas -- a multi-agent system; agents -- a subset of agents of mas; moves_1 -- a subset of moves for agents; moves_2 -- a subset of moves for agents; moves -- a closed set of moves for agents. """ # Abstract away actions of states states_1 = moves_1.forsome(mas.bddEnc.inputsCube) states_2 = moves_2.forsome(mas.bddEnc.inputsCube) states_1 = states_1 & mas.bddEnc.statesMask states_2 = states_2 & mas.bddEnc.statesMask moves_1 = states_1 & mas.protocol(agents) moves_2 = states_2 & mas.protocol(agents) # If there are no fairness constraints, the computation is simpler if not mas.fairness_constraints: # mu Q'. moves_2 & moves | (moves_1 & moves & pre_ce_moves(Q')) return fixpoint(lambda Z: moves_2 & moves | (moves_1 & moves & pre_ce_moves(mas, agents, Z, moves)), BDD.false(mas)) else: moves_1_2_n = moves_1 | moves_2 | nfair_ce_moves(mas, agents, moves) moves_1_2_n = moves_1_2_n & moves def inner(Z): res = moves_2 & moves for fc in mas.fairness_constraints: fc = fc.forsome(mas.bddEnc.inputsCube) nfc = ~fc & mas.bddEnc.statesMask moves_nfc = nfc & mas.protocol(agents) & moves m = stay_ce_moves(mas, agents, moves_1_2_n & (Z | moves_nfc), moves_2 & moves & (Z | moves_nfc), moves) res |= pre_ce_moves(mas, agents, m, moves) return res & moves_1_2_n return fixpoint(inner, BDD.false(mas))
def eval_ceu(mas, formula, agents, states_1, states_2): jump = mas.transitions[formula]["jump"] equiv = mas.transitions[formula]["equiv"] follow = mas.transitions[formula]["follow"] reachable = mas.reachable_states if not mas.fairness_constraints: # <group>i[p U q] = # <>_group_jump []_group_equiv # (reachable => mu Z . q \/ (p /\ []_group_follow Z)) return jump.pre(~equiv.pre(~(reachable.imply( fixpoint(lambda Z: states_2 | (states_1 & ~follow.pre(~Z)), BDD.false(mas)))))) else: nfair = _nfair(mas, formula, agents) # <group>i fair [p U q] = # <>_group_jump []_group_equiv # reachable => # mu Z. (p \/ q \/ ~ifair) /\ # (q \/_fc []_group_follow # (nu Y. (Z \/ ~fc) /\ # (p \/ q \/ ~ifair) /\ # (q \/ []_group_follow(Y)))) def inner(Z): # (p \/ q \/ ~ifair) /\ # (q \/_fc []_group_follow # (nu Y. (Z \/ ~fc) /\ # (p \/ q \/ ~ifair) /\ # (q \/ []_group_follow(Y)))) res = BDD.false(mas) for fc in mas.fairness_constraints: fc = fc.forsome(mas.bddEnc.inputsCube) nfc = ~fc # []_group_follow (nu Y. (Z \/ ~fc) /\ # (p \/ q \/ ~ifair) /\ # (q \/ []_group_follow(Y))) res = res | ~follow.pre(~(fixpoint( lambda Y: ((Z | nfc) & (states_1 | states_2 | nfair) & (states_2 | ~follow.pre(~Y))), BDD.true(mas)))) return ((states_1 | states_2 | nfair) & (states_2 | res)) return jump.pre( ~equiv.pre(~(reachable.imply(fixpoint(inner, BDD.false(mas))))))
def Cequiv(mas, agents, states): """ Return the set of states of mas that are equivalent to some state in states w.r.t. common knowledge of agents. mas -- a multi-agent system; agents -- a set of agents; states -- a subset of states of mas. """ return fixpoint(lambda Z: Eequiv(mas, agents, states | Z), BDD.false(fsm.bddEnc.DDmanager))
def filter_ceu(mas, agents, states_1, states_2, moves): """ Return the set of states of mas for which there exists a strategy for agents compatible with moves such that all fair paths enforced by the strategy reach a state of states_2 through states of states_1. mas -- a multi-agent system; agents -- a subset of agents of mas; states_1 -- a subset of states of mas; states_2 -- a subset of states of mas; moves -- a closed set of moves for agents. """ # Abstract away actions of states states_1 = states_1.forsome(mas.bddEnc.inputsCube) states_2 = states_2.forsome(mas.bddEnc.inputsCube) states_1 = states_1 & mas.bddEnc.statesMask states_2 = states_2 & mas.bddEnc.statesMask # If there are no fairness constraints, the computation is simpler if not mas.fairness_constraints: # mu Q'. states_2 | (states_1 & pre_ce(Q')) return fixpoint(lambda Z: states_2 | (states_1 & pre_ce(mas, agents, Z, moves)), BDD.false(mas)) else: states_1_2_n = states_1 | states_2 | nfair_ce(mas, agents, moves) def inner(Z): res = states_2 for fc in mas.fairness_constraints: fc = fc.forsome(mas.bddEnc.inputsCube) nfc = ~fc & mas.bddEnc.statesMask states = stay_ce(mas, agents, states_1_2_n & (Z | nfc), states_2 & (Z | nfc), moves) res |= pre_ce(mas, agents, states, moves) return res & states_1_2_n return fixpoint(inner, BDD.false(mas))
def _f_sol(fsm, gamma, phi, states, cycle): """ Find the states of `fsm` with highest indices among the states at which `gamma` has a solution, as defined by Samer and Veith at TIME'05. :param fsm: the concerned FSM :param gamma: an AST-based CTL query :param phi: an AST-based CTL formula :param states: a set of states :param cycle: True if there is a cycle, False otherwise :return: the states of `fsm` with highest indices among the states at which `gamma` has a solution :rtype: :class:`pynusmv.dd.BDD` """ if cycle: c = _cycle(fsm, gamma, phi, states) else: c = BDD.false(fsm.bddEnc.DDmanager) chi = replace_placeholder(gamma, TrueExp()) psi = replace_placeholder(gamma, FalseExp()) new_phi = And(phi, Not(psi)) u_1 = fixpoint(lambda z: ((c - eval_ctl_spec(fsm, ast_to_spec(chi))) & fsm.post(z)), BDD.true(fsm.bddEnc.DDmanager)) # Greatest fixed point. u_2 = u_1 | (_boundary(fsm, gamma, phi, states) - eval_ctl_spec(fsm, ast_to_spec(chi))) u_3 = fixpoint(lambda z: (((u_2 | fsm.pre(z)) & _reachable(fsm, new_phi, states)) - eval_ctl_spec(fsm, ast_to_spec(chi))), BDD.false(fsm.bddEnc.DDmanager)) # Least fixed point. return (((fsm.pre(u_3) & _reachable(fsm, new_phi, states)) | _boundary(fsm, gamma, phi, states) | c) & eval_ctl_spec(fsm, ast_to_spec(chi)))
def _reachable(fsm, phi, states): """ First auxiliary set, as defined by Chan at CAV 2000. :param fsm: the concerned FSM :param phi: an AST-based CTL formula :param states: a set of states :return: the states of `fsm` that are reachable from states in `states` by going only through states at which `phi` holds :rtype: :class:`pynusmv.dd.BDD` """ return fixpoint(lambda z: ((states | fsm.post(z)) & eval_ctl_spec(fsm, ast_to_spec(phi))), BDD.false(fsm.bddEnc.DDmanager)) # Least fixed point.
def test_eg(self): glob.load("tests/pynusmv/models/admin.smv") glob.compute_model() fsm = glob.prop_database().master.bddFsm alice = mc.eval_simple_expression(fsm, "admin = alice") spec = prop.Spec(parser.parse_ctl_spec("EG admin = alice")) egalice = mc.eval_ctl_spec(fsm, spec) self.assertEqual(mc.eg(fsm, alice), egalice) self.assertEqual( egalice, fixpoint(lambda Z: alice & fsm.pre(Z), BDD.true()) & fsm.reachable_states)
def eg(fsm, phi): # EG p = nu Z . p & &_(f in F) Pre( mu Y . (Z & f) | (p & Pre(Y)) ) # = nu Z . p & &_(f in F) EX( mu Y . (Z & f) | (p & EX(Y))) def inner(Z): res = Z for f in fsm.fairness_constraints: res = res & fixpoint(lambda Y : (Z & f) | (phi & fsm.weak_pre(Y.forsome(fsm.bddEnc.inputsCube))), BDD.false(fsm.bddEnc.DDmanager)) return phi & fsm.weak_pre(res.forsome(fsm.bddEnc.inputsCube)) r = fixpoint(inner, BDD.true(fsm.bddEnc.DDmanager)) return r.forsome(fsm.bddEnc.inputsCube)
def _cycle(fsm, gamma, phi, states): """ Second auxiliary set, as defined by Chan at CAV 2000. :param fsm: the concerned FSM :param gamma: an AST-based CTL query :param phi: an AST-based CTL formula :param states: a set of states :return: all states within a cycle in the first auxiliary set :rtype: :class:`pynusmv.dd.BDD` """ psi = replace_placeholder(gamma, FalseExp()) new_phi = And(phi, Not(psi)) return fixpoint(lambda z: _reachable(fsm, new_phi, states) & fsm.post(z), BDD.true(fsm.bddEnc.DDmanager)) # Greatest fixed point.
def eg(fsm, phi): # EG p = nu Z . p & &_(f in F) Pre( mu Y . (Z & f) | (p & Pre(Y)) ) # = nu Z . p & &_(f in F) EX( mu Y . (Z & f) | (p & EX(Y))) def inner(Z): res = Z for f in fsm.fairness_constraints: res = res & fixpoint( lambda Y: (Z & f) | (phi & fsm.weak_pre(Y.forsome(fsm.bddEnc.inputsCube))), BDD.false(fsm.bddEnc.DDmanager)) return phi & fsm.weak_pre(res.forsome(fsm.bddEnc.inputsCube)) r = fixpoint(inner, BDD.true(fsm.bddEnc.DDmanager)) return r.forsome(fsm.bddEnc.inputsCube)
def eval_cew(mas, formula, agents, states_1, states_2): jump = mas.transitions[formula]["jump"] equiv = mas.transitions[formula]["equiv"] follow = mas.transitions[formula]["follow"] reachable = mas.reachable_states if not mas.fairness_constraints: # <group>i[p W q] = # <>_group_jump []_group_equiv # (reachable => nu Z . q \/ (p /\ []_group_follow Z)) return jump.pre(~equiv.pre(~(reachable.imply( fixpoint(lambda Z: states_2 | (states_1 & ~follow.pre(~Z)), BDD.true(mas)))))) else: nfair = _nfair(mas, formula, agents) # <group>i fair [p W q] = # <>_group_jump []_group_equiv # reachable => # nu Z. (p \/ q \/ ~ifair) /\ (q \/ []_group_follow(Z)) res = jump.pre(~equiv.pre(~(reachable.imply( fixpoint( lambda Z: ((states_1 | states_2 | nfair) & (states_2 | ~follow.pre(~Z))), BDD.true(mas)))))) return res
def _fair(mas): if not mas.fairness_constraints: return BDD.true(mas) else: run = mas.trans # fair = nu Z. /\_fc pre(mu Y. (Z /\ fc) \/ pre(Y)) def inner(Z): res = BDD.true(mas) for fc in mas.fairness_constraints: fc = fc.forsome(mas.bddEnc.inputsCube) res = res & run.pre( fixpoint(lambda Y: (Z & fc) | run.pre(Y), BDD.false(mas))) return res return fixpoint(inner, BDD.true(mas))
def inner(Z): # (p \/ q \/ ~ifair) /\ # (q \/_fc []_group_follow # (nu Y. (Z \/ ~fc) /\ # (p \/ q \/ ~ifair) /\ # (q \/ []_group_follow(Y)))) res = BDD.false(mas) for fc in mas.fairness_constraints: fc = fc.forsome(mas.bddEnc.inputsCube) nfc = ~fc # []_group_follow (nu Y. (Z \/ ~fc) /\ # (p \/ q \/ ~ifair) /\ # (q \/ []_group_follow(Y))) res = res | ~follow.pre(~(fixpoint( lambda Y: ((Z | nfc) & (states_1 | states_2 | nfair) & (states_2 | ~follow.pre(~Y))), BDD.true(mas)))) return ((states_1 | states_2 | nfair) & (states_2 | res))
def stay_ce(mas, agents, states_1, states_2, moves): """ mas -- a multi-agent system; agents -- a subset of agents of mas; states_1 -- a subset of states of mas; states_2 -- a subset of states of mas; moves -- a set of moves for agents. """ # Abstract away actions of states states_1 = states_1.forsome(mas.bddEnc.inputsCube) states_2 = states_2.forsome(mas.bddEnc.inputsCube) states_1 = states_1 & mas.bddEnc.statesMask states_2 = states_2 & mas.bddEnc.statesMask return fixpoint(lambda Z: states_2 | (states_1 & pre_ce(mas, agents, Z, moves)), BDD.true(mas))
def stay_ce_moves(mas, agents, moves_1, moves_2, moves): """ mas -- a multi-agent system; agents -- a subset of agents of mas; moves_1 -- a subset of moves for agents; moves_2 -- a subset of moves for agents; moves -- a set of moves for agents. """ agents_cube = mas.inputs_cube_for_agents(agents) others_cube = mas.bddEnc.inputsCube - agents_cube # Abstract away actions of states moves_1 = moves_1.forsome(others_cube) moves_2 = moves_2.forsome(others_cube) moves_1 = moves_1 & mas.bddEnc.statesInputsMask moves_2 = moves_2 & mas.bddEnc.statesInputsMask return fixpoint(lambda Z: moves_2 | (moves_1 & pre_ce_moves(mas, agents, Z, moves)), BDD.true(mas))
def eu(fsm, phi, psi): # E[p U q] = q | (p & EX E[p U q]) = mu Z . q | (p & Pre(Z)) return fixpoint( lambda X: (psi & fair_states(fsm) & fsm.reachable_states) | (phi & ex(fsm, X)), BDD.false(fsm.bddEnc.DDmanager))
def eu(fsm, phi, psi): # E[p U q] = q | (p & EX E[p U q]) = mu Z . q | (p & Pre(Z)) return fixpoint(lambda X : (psi & fair_states(fsm) & fsm.reachable_states) | (phi & ex(fsm, X)), BDD.false(fsm.bddEnc.DDmanager))
def eu(mas, states_1, states_2): run = mas.trans return fixpoint( lambda Y: (states_2 & _fair(mas)) | (states_1 & run.pre(Y)), BDD.false(mas))