def test_prspeed(): program = parse_pgcl(""" nat x; nat y; nat m; nat n; while ((x + 3 <= n)) { if (y < m) { { y := y + 1; } [1/2] { y := y + 0; } } else { { x := x + 0; } [1/4] { { x := x + 1; } [1/3] { { x := x + 2; } [1/2] { x := x + 3; } } } } tick(1); } """) tf = one_loop_wp_transformer(program, program.instructions) assert str( tf ) == '位饾惞. lfp 饾憢. [(x + 3) <= n] * (([y < m] * (((((饾憢)[y/y + 1]) + (tick(1))) * 1/2) + ((((饾憢)[y/y + 0]) + (tick(1))) * (1.0 - 1/2)))) + ([not (y < m)] * (((((饾憢)[x/x + 0]) + (tick(1))) * 1/4) + ((((((饾憢)[x/x + 1]) + (tick(1))) * 1/3) + ((((((饾憢)[x/x + 2]) + (tick(1))) * 1/2) + ((((饾憢)[x/x + 3]) + (tick(1))) * (1.0 - 1/2))) * (1.0 - 1/3))) * (1.0 - 1/4))))) + [not ((x + 3) <= n)] * 饾惞' snf = normalize_expectation_transformer(program, tf.body) assert str( snf ) == '位饾憢. [((x + 3) <= n) & (y < m)] * 1/2 * ((饾憢)[y/y + 1] + tick(1)) + [((x + 3) <= n) & (y < m)] * (1.0 - 1/2) * ((饾憢)[y/y + 0] + tick(1)) + [((x + 3) <= n) & not (y < m)] * 1/4 * ((饾憢)[x/x + 0] + tick(1)) + [((x + 3) <= n) & not (y < m)] * (1/3 * (1.0 - 1/4)) * ((饾憢)[x/x + 1] + tick(1)) + [((x + 3) <= n) & not (y < m)] * ((1/2 * (1.0 - 1/3)) * (1.0 - 1/4)) * ((饾憢)[x/x + 2] + tick(1)) + [((x + 3) <= n) & not (y < m)] * (((1.0 - 1/2) * (1.0 - 1/3)) * (1.0 - 1/4)) * ((饾憢)[x/x + 3] + tick(1))'
def test_zero_conf_parameterized(): program = parse_pgcl(""" # Variable free is either 0 or 1. Free=1 means hosts received answer address free. Free=1 means a collision occurred. # If free=0 holds on termination, then the host erroneously assumes that address is free. # (inital state constraint: free=0). nat free; # answerReceived = 1 (= 0) if host does (not) receive an answer. nat answerReceived; # count keeps track of the number of times the host requested an answer. # (initial state constraint: count = 0) nat count; nat maxCount; while(count < maxCount & free = 0){ {answerReceived := 0}[0.8]{answerReceived := 1} if(answerReceived=1){ {free:=1}[0.5]{free:=0} count := 0 }{ count:=count+1 } } """) tf = one_loop_wp_transformer(program, program.instructions) assert str( tf ) == "位饾惞. lfp 饾憢. [(count < maxCount) & (free = 0)] * (((([0 = 1] * ((((饾憢)[answerReceived/0, free/1, count/0]) * 0.5) + (((饾憢)[answerReceived/0, free/0, count/0]) * (1.0 - 0.5)))) + ([not (0 = 1)] * ((饾憢)[answerReceived/0, count/count + 1]))) * 0.8) + ((([1 = 1] * ((((饾憢)[answerReceived/1, free/1, count/0]) * 0.5) + (((饾憢)[answerReceived/1, free/0, count/0]) * (1.0 - 0.5)))) + ([not (1 = 1)] * ((饾憢)[answerReceived/1, count/count + 1]))) * (1.0 - 0.8))) + [not ((count < maxCount) & (free = 0))] * 饾惞" snf = normalize_expectation_transformer(program, tf.body) assert str( snf ) == '位饾憢. [((count < maxCount) & (free = 0)) & (0 = 1)] * (0.5 * 0.8) * (饾憢)[answerReceived/0, free/1, count/0] + [((count < maxCount) & (free = 0)) & (0 = 1)] * ((1.0 - 0.5) * 0.8) * (饾憢)[answerReceived/0, free/0, count/0] + [((count < maxCount) & (free = 0)) & not (0 = 1)] * 0.8 * (饾憢)[answerReceived/0, count/count + 1] + [((count < maxCount) & (free = 0)) & (1 = 1)] * (0.5 * (1.0 - 0.8)) * (饾憢)[answerReceived/1, free/1, count/0] + [((count < maxCount) & (free = 0)) & (1 = 1)] * ((1.0 - 0.5) * (1.0 - 0.8)) * (饾憢)[answerReceived/1, free/0, count/0] + [((count < maxCount) & (free = 0)) & not (1 = 1)] * (1.0 - 0.8) * (饾憢)[answerReceived/1, count/count + 1]'
def test_dueling_cowboys_count(): program = parse_pgcl(""" # we have a variable player: player=i (i=0 or i=1) means it's player i's turn nat player; # and a variable shot: shot=0 means the current player did not shoot the opponent. shot=1 means he did nat shot; nat c; # In this model, player 0 wins with a higher probability while(shot=0){ if(player = 0){ {shot := 1}[0.6]{player := 1} }{ {shot := 1}[0.4]{player := 0} } c := c + 1 } """) tf = one_loop_wp_transformer(program, program.instructions) assert str( tf ) == "位饾惞. lfp 饾憢. [shot = 0] * (([player = 0] * ((((饾憢)[shot/1, c/c + 1]) * 0.6) + (((饾憢)[player/1, c/c + 1]) * (1.0 - 0.6)))) + ([not (player = 0)] * ((((饾憢)[shot/1, c/c + 1]) * 0.4) + (((饾憢)[player/0, c/c + 1]) * (1.0 - 0.4))))) + [not (shot = 0)] * 饾惞" snf = normalize_expectation_transformer(program, tf.body) assert str( snf ) == '位饾憢. [(shot = 0) & (player = 0)] * 0.6 * (饾憢)[shot/1, c/c + 1] + [(shot = 0) & (player = 0)] * (1.0 - 0.6) * (饾憢)[player/1, c/c + 1] + [(shot = 0) & not (player = 0)] * 0.4 * (饾憢)[shot/1, c/c + 1] + [(shot = 0) & not (player = 0)] * (1.0 - 0.4) * (饾憢)[player/0, c/c + 1]'
def test_loop_forever(): program = parse_pgcl(""" while(True){skip} """) tf = one_loop_wp_transformer(program, program.instructions) assert str(tf) == "位饾惞. lfp 饾憢. [True] * ((饾憢)[]) + [not True] * 饾惞" snf = normalize_expectation_transformer(program, tf.body) assert str(snf) == '位饾憢. [True] * 1.0 * (饾憢)[]'
def test_unfair_random_walk(): program = parse_pgcl(""" nat x; while((not x<=0)){ {x := x+1 }[0.1]{ x := x-1} } """) tf = one_loop_wp_transformer(program, program.instructions) assert str( tf ) == "位饾惞. lfp 饾憢. [not x <= 0] * ((((饾憢)[x/x + 1]) * 0.1) + (((饾憢)[x/x - 1]) * (1.0 - 0.1))) + [not (not x <= 0)] * 饾惞" snf = normalize_expectation_transformer(program, tf.body) assert str( snf ) == '位饾憢. [not x <= 0] * 0.1 * (饾憢)[x/x + 1] + [not x <= 0] * (1.0 - 0.1) * (饾憢)[x/x - 1]'
def test_geometric_monus_2(): program = parse_pgcl(""" nat c; nat f; while(f=1){ {f := 0}[0.5]{ c := c - 1 + 2} } """) tf = one_loop_wp_transformer(program, program.instructions) assert str( tf ) == "位饾惞. lfp 饾憢. [f = 1] * ((((饾憢)[f/0]) * 0.5) + (((饾憢)[c/(c - 1) + 2]) * (1.0 - 0.5))) + [not (f = 1)] * 饾惞" snf = normalize_expectation_transformer(program, tf.body) assert str( snf ) == '位饾憢. [f = 1] * 0.5 * (饾憢)[f/0] + [f = 1] * (1.0 - 0.5) * (饾憢)[c/(c - 1) + 2]'
def test_branchy(): code = """ while (c < 6) { {c := 3} [0.8] {c := 7} if (c=2) { d:=10 } {d := 20} if (f=1) { f:=0 } {f := 1} } """ program = parse_pgcl(code) tf = one_loop_wp_transformer(program, program.instructions) assert str( tf ) == '位饾惞. lfp 饾憢. [c < 6] * (((([3 = 2] * (([f = 1] * ((饾憢)[c/3, d/10, f/0])) + ([not (f = 1)] * ((饾憢)[c/3, d/10, f/1])))) + ([not (3 = 2)] * (([f = 1] * ((饾憢)[c/3, d/20, f/0])) + ([not (f = 1)] * ((饾憢)[c/3, d/20, f/1]))))) * 0.8) + ((([7 = 2] * (([f = 1] * ((饾憢)[c/7, d/10, f/0])) + ([not (f = 1)] * ((饾憢)[c/7, d/10, f/1])))) + ([not (7 = 2)] * (([f = 1] * ((饾憢)[c/7, d/20, f/0])) + ([not (f = 1)] * ((饾憢)[c/7, d/20, f/1]))))) * (1.0 - 0.8))) + [not (c < 6)] * 饾惞' snf = normalize_expectation_transformer(program, tf.body) assert str( snf ) == '位饾憢. [(c < 6) & ((3 = 2) & (f = 1))] * 0.8 * (饾憢)[c/3, d/10, f/0] + [(c < 6) & ((3 = 2) & not (f = 1))] * 0.8 * (饾憢)[c/3, d/10, f/1] + [(c < 6) & (not (3 = 2) & (f = 1))] * 0.8 * (饾憢)[c/3, d/20, f/0] + [(c < 6) & (not (3 = 2) & not (f = 1))] * 0.8 * (饾憢)[c/3, d/20, f/1] + [(c < 6) & ((7 = 2) & (f = 1))] * (1.0 - 0.8) * (饾憢)[c/7, d/10, f/0] + [(c < 6) & ((7 = 2) & not (f = 1))] * (1.0 - 0.8) * (饾憢)[c/7, d/10, f/1] + [(c < 6) & (not (7 = 2) & (f = 1))] * (1.0 - 0.8) * (饾憢)[c/7, d/20, f/0] + [(c < 6) & (not (7 = 2) & not (f = 1))] * (1.0 - 0.8) * (饾憢)[c/7, d/20, f/1]'
def test_linear01(): program = parse_pgcl(""" nat x; while (2 <= x) { { x := x - 1; } [1/3] { x := x - 2; } tick(1); } """) tf = one_loop_wp_transformer(program, program.instructions) assert str( tf ) == '位饾惞. lfp 饾憢. [2 <= x] * (((((饾憢)[x/x - 1]) + (tick(1))) * 1/3) + ((((饾憢)[x/x - 2]) + (tick(1))) * (1.0 - 1/3))) + [not (2 <= x)] * 饾惞' snf = normalize_expectation_transformer(program, tf.body) assert str( snf ) == '位饾憢. [2 <= x] * 1/3 * ((饾憢)[x/x - 1] + tick(1)) + [2 <= x] * (1.0 - 1/3) * ((饾憢)[x/x - 2] + tick(1))'
def test_ber_ert(): program = parse_pgcl(""" nat x; nat n; nat r; while (x < n) { r := 1 : 1/2 + 0 : 1/2; x := x + r; tick(1); } """) tf = one_loop_wp_transformer(program, program.instructions) assert str( tf ) == '位饾惞. lfp 饾憢. [x < n] * ((1/2 * (((饾憢)[r/1, x/x + 1]) + (tick(1)))) + (1/2 * (((饾憢)[r/0, x/x + 0]) + (tick(1))))) + [not (x < n)] * 饾惞' snf = normalize_expectation_transformer(program, tf.body) assert str( snf ) == '位饾憢. [x < n] * 1/2 * ((饾憢)[r/1, x/x + 1] + tick(1)) + [x < n] * 1/2 * ((饾憢)[r/0, x/x + 0] + tick(1))'
def test_complete_binary_tree(): program = parse_pgcl(""" nat a; nat b; nat c; nat maxA; nat maxB; while (a < maxA & b < maxB) { {a:=a+1} [0.5] {b := b+1} c:=c+1 } """) tf = one_loop_wp_transformer(program, program.instructions) assert str( tf ) == "位饾惞. lfp 饾憢. [(a < maxA) & (b < maxB)] * ((((饾憢)[a/a + 1, c/c + 1]) * 0.5) + (((饾憢)[b/b + 1, c/c + 1]) * (1.0 - 0.5))) + [not ((a < maxA) & (b < maxB))] * 饾惞" snf = normalize_expectation_transformer(program, tf.body) assert str( snf ) == '位饾憢. [(a < maxA) & (b < maxB)] * 0.5 * (饾憢)[a/a + 1, c/c + 1] + [(a < maxA) & (b < maxB)] * (1.0 - 0.5) * (饾憢)[b/b + 1, c/c + 1]'
def test_geometric_flipping(): program = parse_pgcl(""" nat c; nat f; nat k; while(f=1){ if(k=0){ {f := 0}[0.5]{ c := c +1 }; k := 1 }{ k :=0 } } """) tf = one_loop_wp_transformer(program, program.instructions) assert str( tf ) == "位饾惞. lfp 饾憢. [f = 1] * (([k = 0] * ((((饾憢)[f/0, k/1]) * 0.5) + (((饾憢)[c/c + 1, k/1]) * (1.0 - 0.5)))) + ([not (k = 0)] * ((饾憢)[k/0]))) + [not (f = 1)] * 饾惞" snf = normalize_expectation_transformer(program, tf.body) assert str( snf ) == '位饾憢. [(f = 1) & (k = 0)] * 0.5 * (饾憢)[f/0, k/1] + [(f = 1) & (k = 0)] * (1.0 - 0.5) * (饾憢)[c/c + 1, k/1] + [(f = 1) & not (k = 0)] * 1.0 * (饾憢)[k/0]'
def test_brp_simple_parameterized(): program = parse_pgcl(""" # The number of total packages to send nat toSend; # Number of packages sent nat sent; # The maximal number of retransmission tries nat maxFailed; # The number of failed retransmission tries nat failed; while(failed < maxFailed & sent < toSend){ { # Transmission of current packages successful failed := 0; sent := sent + 1; } [0.9] { # Transmission not successful failed := failed +1; } } """) tf = one_loop_wp_transformer(program, program.instructions) assert str( tf ) == "位饾惞. lfp 饾憢. [(failed < maxFailed) & (sent < toSend)] * ((((饾憢)[failed/0, sent/sent + 1]) * 0.9) + (((饾憢)[failed/failed + 1]) * (1.0 - 0.9))) + [not ((failed < maxFailed) & (sent < toSend))] * 饾惞" snf = normalize_expectation_transformer(program, tf.body) assert str( snf ) == '位饾憢. [(failed < maxFailed) & (sent < toSend)] * 0.9 * (饾憢)[failed/0, sent/sent + 1] + [(failed < maxFailed) & (sent < toSend)] * (1.0 - 0.9) * (饾憢)[failed/failed + 1]'
def _summation_snf_to_pysmt_dnf(self, post_expectation): """ Computes and returns a PySMT representation of the disjunctive normal form of the wp-characteristic functional of program w.r.t. post_expectation :param program: The program text. :param post_expectation: The postexpectation. :return: The PySMT representation of the disjunctive normal form (pysmt_dnf_loop_execute, pysmt_dnf_loop_terminate), where: pysmt_dnf_loop_execute is a list of pairs (guard, prob_sub_pairs), where prob_sub_pairs is a list of pairs (prob, variable_substitutions). Every two guards guard and guard' in this dnf are mutually exclusive, i.e. guard AND guard' is unsatisfiable pysmt_dnf_loop_terminate is a list of pairs (guard, arithmetic_expression). Every two guards guard and guard' in this dnf are mutually exclusive, i.e. guard AND guard' is unsatisfiable. """ # parse program and variable initializations using probably # retrieve the wp-characteristic functional of the loop (not containing the post-expectation) from probably if self._apply_general_wp: probably_wp_transformer = general_wp_transformer(self.program) else: probably_wp_transformer = one_loop_wp_transformer(self.program, self.program.instructions) probably_summation_nf = SnfLoopExpectationTransformer(self.program, probably_wp_transformer) #logger.info("Program weakest pre-expectation transformer: \n %s \n" % probably_wp_transformer) # Set-Up PySMT Variables: For every program variable, we have a corresponding pysmt variable. self._pysmt_program_variables = self._setup_variables() self._pysmt_program_variables_argument = tuple(self._pysmt_program_variables) # Get (guard, probability, substitution, ticks) quadruples probably_guards, probably_probs, probably_subs, probably_ticks = zip(*probably_summation_nf.body_tuples()) # Convert guards and probabilities to pysmt formulae # Notice: We simplify the guards here as this reduces trivial conjuncts such as 2=2 that resulting from substitutions. # We also simplify the probabilities, e.g. 1 - 4/5 -> 1/5. pysmt_guards = list(map(simplify, map(lambda guard:probably_expr_to_pysmt(guard, None, True, self.monus_euf), probably_guards))) pysmt_probs = list(map(simplify, map(probably_expr_to_pysmt, probably_probs))) # A sub is a list of dicts, where every dict is a map from variables to substitutions pysmt_subs = self._get_pysmt_subs(probably_subs) pysmt_ticks = list(map(simplify, map(lambda guard:probably_expr_to_pysmt(guard, None, True, self.rmonus_euf, True), probably_ticks))) pysmt_summation_nf = list() for i in range(0, len(pysmt_guards)): pysmt_summation_nf.append((pysmt_guards[i], pysmt_probs[i], pysmt_subs[i], pysmt_ticks[i])) logger.debug("PySMT Summmation Normal Form Objects *before* preprocessing (length = %s): \n %s \n" % ( len(pysmt_summation_nf), pysmt_summation_nf)) pysmt_summation_nf = self._remove_unsatisfiable_guards(pysmt_summation_nf) logger.debug("PySMT Summmation Normal Form Objects *after* preprocessing (length = %s): \n %s \n" % ( len(pysmt_summation_nf), pysmt_summation_nf)) # Constraint asserting that all program variables are non-negative self.non_negative_constraint = And(GE(var, Int(0)) for var in self._pysmt_program_variables) pysmt_dnf_loop_execute = self._get_pysmt_dnf_loop_execute(pysmt_summation_nf) # Now deal with (not guard)-part and postexpectation pysmt_dnf_loop_terminated \ = self._get_pysmt_loop_terminated_dnf(probably_wp_transformer, post_expectation) return (pysmt_dnf_loop_execute, pysmt_dnf_loop_terminated)