示例#1
0
    def solve_safety_inc_zz(self, hts, prop, k):
        self._reset_assertions(self.solver)

        if TS.has_next(prop):
            Logger.error(
                "Invariant checking with next variables only supports FWD strategy"
            )

        init = hts.single_init()
        trans = hts.single_trans()
        invar = hts.single_invar()

        initt = self.at_time(And(init, invar), 0)
        Logger.log("Add init at_0", 2)
        self._add_assertion(self.solver, initt)

        propt = self.at_ptime(And(Not(prop), invar), -1)
        Logger.log("Add property pat_%d" % 0, 2)
        self._add_assertion(self.solver, propt)

        t = 0
        while (t < k + 1):
            self._push(self.solver)
            even = (t % 2) == 0
            th = int(t / 2)

            if even:
                eq = And([
                    EqualsOrIff(self.at_time(v, th), self.at_ptime(v, th - 1))
                    for v in hts.vars
                ])
            else:
                eq = And([
                    EqualsOrIff(self.at_time(v, th + 1),
                                self.at_ptime(v, th - 1)) for v in hts.vars
                ])

            Logger.log("Add equivalence time %d" % t, 2)
            self._add_assertion(self.solver, eq)

            if self._solve(self.solver):
                Logger.log("Counterexample found with k=%s" % (t), 1)
                model = self._get_model(self.solver)
                return (t, model)
            else:
                Logger.log("No counterexample found with k=%s" % (t), 1)
                Logger.msg(".", 0, not (Logger.level(1)))

            self._pop(self.solver)

            if even:
                trans_t = self.unroll(trans, invar, th + 1, th)
            else:
                trans_t = self.unroll(trans, invar, th, th + 1)

            self._add_assertion(self.solver, trans_t)

            t += 1

        return (t - 1, None)
示例#2
0
    def extend_ts(ts, modifier):

        affect_init = False

        if ts.ftrans is None:
            return (ts, [])

        new_ftrans = {}

        vars = []

        for (assign, cond_assign_list) in ts.ftrans.items():
            fv = get_free_variables(assign)
            assert len(fv) == 1
            var = fv.pop()
            is_next = TS.has_next(var)

            refvar = TS.get_ref_var(var)
            nomvar = Symbol(NOMIN % refvar.symbol_name(), var.symbol_type())
            fvar = Symbol(FAULT % refvar.symbol_name(), BOOL)

            vars.append(nomvar)
            vars.append(fvar)

            repldic = dict([(refvar.symbol_name(), nomvar.symbol_name()), \
                            (TS.get_prime(refvar).symbol_name(), TS.get_prime(nomvar).symbol_name())])

            # Remapping nominal behavior to new variable
            new_ftrans[substitute(assign,
                                  repldic)] = [(substitute(c[0], repldic),
                                                substitute(c[1], repldic))
                                               for c in cond_assign_list]

            # Definition of the nominal behavior
            new_ftrans[refvar] = [(Not(fvar), nomvar)]

            # Application of the faulty behavior
            new_ftrans[refvar].append(
                (fvar, modifier.get_behavior(nomvar, refvar)))

            ts.trans = And(ts.trans, Implies(fvar, TS.get_prime(fvar)))

            if affect_init:
                ts.init = substitute(ts.init, repldic)
            else:
                ts.init = And(ts.init, Not(fvar))

        # add the vars to the transition system
        for var in vars:
            ts.add_var(var)

        ts.ftrans = new_ftrans

        return (ts, vars)
示例#3
0
    def solve_safety_inc_bwd(self, hts, prop, k, assert_property=False):
        self._reset_assertions(self.solver)

        if TS.has_next(prop):
            Logger.error(
                "Invariant checking with next variables only supports FWD strategy"
            )

        init = hts.single_init()
        trans = hts.single_trans()
        invar = hts.single_invar()

        formula = self.at_ptime(And(Not(prop), invar), -1)
        Logger.log("Add not property at time %d" % 0, 2)
        self._add_assertion(self.solver, formula)

        t = 0
        while (t < k + 1):
            self._push(self.solver)

            pinit = self.at_ptime(init, t - 1)
            Logger.log("Add init at time %d" % t, 2)
            self._add_assertion(self.solver, pinit)

            if self._solve(self.solver):
                Logger.log("Counterexample found with k=%s" % (t), 1)
                model = self._get_model(self.solver)
                return (t, model)
            else:
                Logger.log("No counterexample found with k=%s" % (t), 1)
                Logger.msg(".", 0, not (Logger.level(1)))

            self._pop(self.solver)

            trans_t = self.unroll(trans, invar, t, t + 1)
            self._add_assertion(self.solver, trans_t)

            if assert_property and t > 0:
                prop_t = self.unroll(TRUE(), prop, t - 1, t)
                self._add_assertion(self.solver, prop_t)
                Logger.log("Add property at time %d" % t, 2)

            t += 1

        return (t - 1, None)
示例#4
0
    def solve_safety_inc(self, hts, prop, k, k_min):
        if self.config.strategy == VerificationStrategy.ALL:
            res = self.solve_safety_inc_fwd(hts, prop, k, k_min)
            if res[1] is not None:
                return res
            if self.config.prove and not TS.has_next(prop):
                res = self.solve_safety_int(hts, prop, k)
                if res[1] is not None:
                    return res
            res = self.solve_safety_inc_bwd(hts, prop, k)
            if res[1] is not None:
                self.config.strategy == VerificationStrategy.BWD
                return res
            res = self.solve_safety_inc_zz(hts, prop, k)
            self.config.strategy == VerificationStrategy.ZZ
            return res

        if self.config.strategy in [
                VerificationStrategy.FWD, VerificationStrategy.AUTO
        ]:
            return self.solve_safety_inc_fwd(hts, prop, k, k_min)

        if self.config.strategy == VerificationStrategy.BWD:
            return self.solve_safety_inc_bwd(hts, prop, k)

        if self.config.strategy == VerificationStrategy.ZZ:
            return self.solve_safety_inc_zz(hts, prop, k)

        # Redirecting strategy selection error
        if self.config.strategy == VerificationStrategy.INT:
            Logger.warning(
                "Interpolation is not available in incremental mode. Switching to not incremental"
            )
            return self.solve_safety_ninc(hts, prop, k)

        Logger.error("Invalid configuration strategy")

        return None
示例#5
0
    def solve_liveness_inc_fwd(self, hts, prop, k, k_min, eventually=False):
        self._reset_assertions(self.solver)

        init = hts.single_init()
        trans = hts.single_trans()
        invar = hts.single_invar()

        if self.config.simplify:
            Logger.log("Simplifying the Transition System", 1)
            if Logger.level(1):
                timer = Logger.start_timer("Simplify")

            init = simplify(init)
            trans = simplify(trans)
            invar = simplify(invar)

            if Logger.level(1):
                Logger.get_timer(timer)

        heqvar = None
        if not eventually:
            heqvar = Symbol(HEQVAR, BOOL)
            self._init_at_time(hts.vars.union(set([heqvar])), k)

        if self.config.prove:
            self.solver_klive = self.solver.copy("klive")

            self._reset_assertions(self.solver_klive)
            self._add_assertion(self.solver_klive, self.at_time(invar, 0))

            if eventually:
                self._add_assertion(self.solver_klive, self.at_time(init, 0))

        propt = FALSE()
        formula = And(init, invar)
        formula = self.at_time(formula, 0)
        Logger.log("Add init and invar", 2)
        self._add_assertion(self.solver, formula)

        next_prop = TS.has_next(prop)
        if next_prop:
            if k < 1:
                Logger.error(
                    "Liveness checking with next variables requires at least k=1"
                )
            k_min = 1

        t = 0
        while (t < k + 1):
            self._push(self.solver)

            loopback = FALSE()
            if t > 0:
                loopback = self.all_loopbacks(self.hts.vars, t, heqvar)

            Logger.log("Add loopbacks at time %d" % t, 2)
            self._add_assertion(self.solver, loopback)

            if t >= k_min:
                self._write_smt2_comment(self.solver, "Solving for k=%s" % (t))
                Logger.log("\nSolving for k=%s" % (t), 1)

                if self._solve(self.solver):
                    Logger.log("Counterexample found with k=%s" % (t), 1)
                    model = self._get_model(self.solver)
                    return (t, model)
                else:
                    Logger.log("No counterexample found with k=%s" % (t), 1)
                    Logger.msg(".", 0, not (Logger.level(1)))
            else:
                Logger.log("Skipping solving for k=%s (k_min=%s)" % (t, k_min),
                           1)
                Logger.msg(".", 0, not (Logger.level(1)))

            self._pop(self.solver)

            n_prop = Not(prop)
            if not eventually:
                n_prop = Or(n_prop, Not(heqvar))

            if next_prop:
                if t > 0:
                    propt = self.at_time(n_prop, t - 1)
            else:
                propt = self.at_time(n_prop, t)

            self._add_assertion(self.solver, propt)

            if self.config.prove:

                if t > 0:
                    self._add_assertion(self.solver_klive, trans_t)
                    self._write_smt2_comment(self.solver_klive,
                                             "Solving for k=%s" % (t))

                    if next_prop:
                        if t > 0:
                            propt = self.at_time(Not(prop), t - 1)
                    else:
                        propt = self.at_time(Not(prop), t)

                    self._add_assertion(self.solver_klive, propt)

                    if t >= k_min:
                        if self._solve(self.solver_klive):
                            Logger.log("K-Liveness failed with k=%s" % (t), 1)
                        else:
                            Logger.log("K-Liveness holds with k=%s" % (t), 1)
                            return (t, True)

                else:
                    self._add_assertion(self.solver_klive,
                                        self.at_time(Not(prop), 0))

                    # self._push(self.solver_klive)
                    # self._add_assertion(self.solver_klive, self.at_time(prop, 0))
                    # res = self._solve(self.solver_klive)
                    # self._pop(self.solver_klive)
                    # if res:
                    #     self._add_assertion(self.solver_klive, self.at_time(prop, 0))
                    # else:
                    #     self._add_assertion(self.solver_klive, self.at_time(Not(prop), 0))

            trans_t = self.unroll(trans, invar, t + 1, t)
            self._add_assertion(self.solver, trans_t)

            if self.assert_property:
                prop_t = self.unroll(TRUE(), prop, t, t - 1)
                self._add_assertion(self.solver, prop_t)
                Logger.log("Add property at time %d" % t, 2)

            t += 1

        return (t - 1, None)
示例#6
0
    def solve_problems(self, problems, config):
        encoder_config = self.problems2encoder_config(config, problems)

        self.sparser = StringParser(encoder_config)
        self.lparser = LTLParser()

        self.coi = ConeOfInfluence()

        invar_props = []
        ltl_props = []
        si = False

        if len(problems.symbolic_inits) == 0:
            problems.symbolic_inits.add(si)

        HTSM = 0
        HTS2 = 1
        HTSD = (HTSM, si)

        model_extension = config.model_extension if problems.model_extension is None else problems.model_extension
        assume_if_true = config.assume_if_true or problems.assume_if_true
        cache_files = config.cache_files or problems.cache_files
        clean_cache = config.clean_cache

        modifier = None
        if model_extension is not None:
            modifier = lambda hts: ModelExtension.extend(
                hts, ModelModifiersFactory.modifier_by_name(model_extension))

        # generate systems for each problem configuration
        systems = {}
        for si in problems.symbolic_inits:
            encoder_config.symbolic_init = si
            (systems[(HTSM, si)], invar_props, ltl_props) = self.parse_model(problems.relative_path, \
                                                                             problems.model_file, \
                                                                             encoder_config, \
                                                                             "System 1", \
                                                                             modifier, \
                                                                             cache_files=cache_files, \
                                                                             clean_cache=clean_cache)

        if problems.equivalence is not None:
            (systems[(HTS2, si)], _, _) = self.parse_model(problems.relative_path, \
                                                           problems.equivalence, \
                                                           encoder_config, \
                                                           "System 2", \
                                                           cache_files=cache_files, \
                                                           clean_cache=clean_cache)
        else:
            systems[(HTS2, si)] = None

        if config.safety or config.problems:
            for invar_prop in invar_props:
                inv_prob = problems.new_problem()
                inv_prob.verification = VerificationType.SAFETY
                inv_prob.name = invar_prop[0]
                inv_prob.description = invar_prop[1]
                inv_prob.formula = invar_prop[2]
                problems.add_problem(inv_prob)

        if config.ltl or config.problems:
            for ltl_prop in ltl_props:
                ltl_prob = problems.new_problem()
                ltl_prob.verification = VerificationType.LTL
                ltl_prob.name = ltl_prop[0]
                ltl_prob.description = ltl_prop[1]
                ltl_prob.formula = ltl_prop[2]
                problems.add_problem(ltl_prob)

        if HTSD in systems:
            problems._hts = systems[HTSD]

        for problem in problems.problems:
            problem.hts = systems[(HTSM, problem.symbolic_init)]

            if problems._hts is None:
                problems._hts = problem.hts
            problem.hts2 = systems[(HTS2, problem.symbolic_init)]
            if problems._hts2 is None:
                problems._hts2 = problem.hts2
            problem.vcd = problems.vcd or config.vcd or problem.vcd
            problem.abstract_clock = problems.abstract_clock or config.abstract_clock
            problem.add_clock = problems.add_clock or config.add_clock
            problem.coi = problems.coi or config.coi
            problem.run_coreir_passes = problems.run_coreir_passes
            problem.relative_path = problems.relative_path
            problem.cardinality = max(problems.cardinality, config.cardinality)

            if not problem.full_trace:
                problem.full_trace = problems.full_trace
            if not problem.trace_vars_change:
                problem.trace_vars_change = problems.trace_vars_change
            if not problem.trace_all_vars:
                problem.trace_all_vars = problems.trace_all_vars
            if not problem.clock_behaviors:
                clk_bhvs = [
                    p for p in
                    [problems.clock_behaviors, config.clock_behaviors]
                    if p is not None
                ]
                if len(clk_bhvs) > 0:
                    problem.clock_behaviors = ";".join(clk_bhvs)
            if not problem.generators:
                problem.generators = config.generators

            Logger.log(
                "Solving with abstract_clock=%s, add_clock=%s" %
                (problem.abstract_clock, problem.add_clock), 2)

            if problem.trace_prefix is not None:
                problem.trace_prefix = "".join(
                    [problem.relative_path, problem.trace_prefix])

            if config.time or problems.time:
                timer_solve = Logger.start_timer("Problem %s" % problem.name,
                                                 False)
            try:
                self.__solve_problem(problem, config)

                if problem.verification is None:
                    Logger.log("Unset verification", 2)
                    continue

                Logger.msg(" %s\n" % problem.status, 0, not (Logger.level(1)))

                if (assume_if_true) and \
                   (problem.status == VerificationStatus.TRUE) and \
                   (problem.assumptions == None) and \
                   (problem.verification == VerificationType.SAFETY):

                    ass_ts = TS("Previous assumption from property")
                    if TS.has_next(problem.formula):
                        ass_ts.trans = problem.formula
                    else:
                        ass_ts.invar = problem.formula
                    problem.hts.reset_formulae()

                    problem.hts.add_ts(ass_ts)

                if config.time or problems.time:
                    problem.time = Logger.get_timer(timer_solve, False)

            except KeyboardInterrupt as e:
                Logger.msg("\b\b Skipped!\n", 0)
示例#7
0
    def solve_safety_inc_bwd(self, hts, prop, k, assert_property=False, generalize=None):
        solver = self.solver.copy("inc_bwd")

        self._reset_assertions(solver)

        has_next = TS.has_next(prop)

        if has_next:
            prop = TS.to_prev(prop)

        init = hts.single_init()
        trans = hts.single_trans()
        invar = hts.single_invar()

        formula = self.at_ptime(And(Not(prop), invar), -1)
        Logger.log("Add not property at time %d"%0, 2)
        self._add_assertion(solver, formula)

        skip_push = False
        constraints = TRUE()

        models = 0

        t = 0
        k_min = 1 if has_next else 0
        while (t < k+1):
            if not skip_push:
                self._push(solver)
                skip_push = False

            if not skip_push:
                pinit = self.at_ptime(init, t-1)
                Logger.log("Add init at time %d"%t, 2)
                self._add_assertion(solver, pinit)

            if constraints != TRUE():
                for j in range(t+1):
                    self._add_assertion(solver, self.at_ptime(constraints, j-1), "Addditional Constraints")

            if self.preferred is not None:
                try:
                    for (var, val) in self.preferred:
                        solver.solver.set_preferred_var(TS.get_timed(var, t), val)
                except:
                    Logger.warning("Current solver does not support preferred variables")
                    self.preferred = None

            if (t >= k_min) and self._solve(solver):
                Logger.log("Counterexample found with k=%s"%(t), 1)
                model = self._get_model(solver)
                models += 1

                if models > 20:
                    Logger.msg("R", 0, not(Logger.level(1)))
                    self._reset_solver(solver)
                    models = 0

                if generalize is not None:
                    constr, res = generalize(model, t)
                    if res:
                        return (t, model)
                    constraints = And(constraints, Not(constr))
                    skip_push = True
                    continue
                else:
                    return (t, model)
            else:
                Logger.log("No counterexample found with k=%s"%(t), 1)
                Logger.msg(".", 0, not(Logger.level(1)))

            self._pop(solver)
            skip_push = False

            trans_t = self.unroll(trans, invar, t, t+1)
            self._add_assertion(solver, trans_t)

            if assert_property and t > 0:
                prop_t = self.unroll(TRUE(), prop, t-1, t)
                self._add_assertion(solver, prop_t)

                Logger.log("Add property at time %d"%t, 2)

            t += 1

        return (t-1, None)
示例#8
0
    def solve_safety_inc_fwd(self, hts, prop, k, k_min, \
                             all_vars=False, generalize=None, prove=None):

        add_unsat_cons = False
        prove = self.config.prove if prove is None else prove

        solver_name = "inc_fwd%s"%("_prove" if prove else "")
        solver = self.solver.copy(solver_name)

        self._reset_assertions(solver)

        if prove:
            solver_ind = self.solver.copy("%s_ind"%solver_name)
            self._reset_assertions(solver_ind)

            if all_vars:
                relevant_vars = hts.vars
            else:
                relevant_vars = hts.state_vars | hts.output_vars

        init = hts.single_init()
        trans = hts.single_trans()
        invar = hts.single_invar()

        acc_init = TRUE()
        acc_prop = TRUE()
        acc_loop_free = TRUE()
        trans_t = TRUE()

        if self.config.simplify:
            Logger.log("Simplifying the Transition System", 1)
            if Logger.level(2):
                timer = Logger.start_timer("Simplify")

            init = simplify(init)
            trans = simplify(trans)
            invar = simplify(invar)
            if Logger.level(2):
                Logger.get_timer(timer)

        n_prop_t = FALSE()
        init_0 = self.at_time(And(init, invar), 0)
        Logger.log("Add init and invar", 2)
        self._add_assertion(solver, init_0)

        if prove:
            # add invariants at time 0, but not init
            self._add_assertion(solver_ind, self.at_time(invar, 0), "invar")

        next_prop = TS.has_next(prop)
        if next_prop:
            if k < 1:
                Logger.error("Invariant checking with next variables requires at least k=1")
            k_min = 1

        skip_push = False

        constraints = TRUE()

        t = k_min
        for i in range(t):
            trans_t = self.unroll(trans, invar, i+1, i)
            self._add_assertion(solver, trans_t)
            Logger.msg("Unroll and call check-sat without property", 2)
            # Note: Seems to help a lot to call check-sat here
            #       without this some solvers will run out of memory on large problems
            #       it likely lets them do some internal clean up, and learn some things
            #       about the model
            self._solve(solver)
            Logger.msg("_", 0, not(Logger.level(1)))
        while (t < k+1):
            if not skip_push:
                self._push(solver)
                skip_push = False

            t_prop = t-1 if next_prop else t

            if k_min > 0:
                if (not next_prop) or (next_prop and t>0):
                    if n_prop_t == FALSE():
                        n_prop_t = self.at_time(Not(prop), t_prop)
                    else:
                        n_prop_t = Or(n_prop_t, self.at_time(Not(prop), t_prop))
            else:
                n_prop_t = self.at_time(Not(prop), t)

            Logger.log("Add not property at time %d"%t, 2)
            if not skip_push:
                self._add_assertion(solver, n_prop_t, "Property")

            if constraints != TRUE():
                self._add_assertion(solver, self.at_time(constraints, t), "Addditional Constraints")

            if t >= k_min:
                Logger.log("\nSolving for k=%s"%(t), 1)

                if self.preferred is not None:
                    try:
                        for (var, val) in self.preferred:
                            for j in range(t+1):
                                solver.solver.set_preferred_var(TS.get_timed(var, j), val)
                    except:
                        Logger.warning("Current solver does not support preferred variables")
                        self.preferred = None

                if self._solve(solver):
                    Logger.log("Counterexample found with k=%s"%(t), 1)
                    model = self._get_model(solver)

                    if generalize is not None:
                        constr, res = generalize(model, t)
                        if res:
                            return (t, model)
                        constraints = And(constraints, Not(constr))
                        skip_push = True
                        continue
                    else:
                        return (t, model)
                else:
                    Logger.log("No counterexample found with k=%s"%(t), 1)
                    Logger.msg(".", 0, not(Logger.level(1)))

                    if add_unsat_cons and prove:
                        self._add_assertion(solver, Implies(self.at_time(And(init, invar), 1), self.at_time(Not(prop), t_prop+1)))
                        self._add_assertion(solver, Not(n_prop_t))
            else:
                Logger.log("\nSkipping solving for k=%s (k_min=%s)"%(t,k_min), 1)
                Logger.msg("_", 0, not(Logger.level(1)))

            self._pop(solver)
            skip_push = False

            if prove:
                if t > k_min:
                    loop_free = self.loop_free(relevant_vars, t, t-1)

                    # Checking I & T & loopFree
                    acc_init = And(acc_init, self.at_time(Not(init), t))
                    acc_loop_free = And(acc_loop_free, loop_free)

                    self._push(solver)

                    self._add_assertion(solver, acc_init)
                    self._add_assertion(solver, acc_loop_free)

                    if self._solve(solver):
                        Logger.log("Induction (I & lF) failed with k=%s"%(t), 1)
                    else:
                        Logger.log("Induction (I & lF) holds with k=%s"%(t), 1)
                        return (t, True)

                    self._pop(solver)

                    # Checking T & loopFree & !P
                    self._add_assertion(solver_ind, trans_t, comment="trans")
                    self._add_assertion(solver_ind, loop_free, comment="loop_free")

                    self._push(solver_ind)

                    self._add_assertion(solver_ind, self.at_time(Not(prop), t_prop))

                    if self._solve(solver_ind):
                        Logger.log("Induction (lF & !P) failed with k=%s"%(t), 1)
                    else:
                        Logger.log("Induction (lF & !P) holds with k=%s"%(t), 1)
                        return (t, True)

                    self._pop(solver_ind)

                    self._add_assertion(solver_ind, self.at_time(prop, t_prop), "prop")
                else:
                    if not next_prop:
                        self._add_assertion(solver_ind, self.at_time(prop, t_prop), "prop")
                    else:
                        # add skipped transition
                        self._add_assertion(solver_ind, trans_t, comment="trans")


            trans_t = self.unroll(trans, invar, t+1, t)
            self._add_assertion(solver, trans_t)

            t += 1

        return (t-1, None)
示例#9
0
    def solve_safety_inc_int(self, hts, prop, k):
        init = hts.single_init()
        trans = hts.single_trans()
        invar = hts.single_invar()

        solver_proof = self.solver.copy("inc_int_proof")
        solver = self.solver.copy("inc_int")

        has_next = TS.has_next(prop)

        map_10 = dict([(TS.get_timed_name(v.symbol_name(), 1), TS.get_timed_name(v.symbol_name(), 0)) for v in hts.vars])

        itp = Interpolator(logic=get_logic(trans))
        init = And(init, invar)
        nprop = Not(prop)

        def check_overappr(Ri, R):
            self._reset_assertions(solver_proof)
            self._add_assertion(solver_proof, And(Ri, Not(R)))

            if not self._solve(solver_proof):
                Logger.log("Proof found with k=%s"%(t), 1)
                return TRUE()

            Logger.log("Extending initial states (%s)"%int_c, 1)
            return Or(R, Ri)

        t = 1 if has_next else 0

        trans_t = self.unroll(trans, invar, k+1, gen_list=True)

        pivot = 2
        trans_tA = And(trans_t[:pivot])
        init_0 = self.at_time(init, 0)

        is_sat = True
        Ri = None

        self._reset_assertions(solver)

        while (t < k+1):
            Logger.log("\nSolving for k=%s"%t, 1)
            int_c = 0
            R = init_0

            # trans_t is composed as trans_i, invar_i, trans_i+1, invar_i+1, ...
            self._add_assertion(solver, trans_t[2*t])
            self._add_assertion(solver, trans_t[(2*t)+1])

            while True:
                Logger.log("Add init and invar", 2)
                self._push(solver)
                self._add_assertion(solver, R)

                npropt = self.at_time(nprop, t-1 if has_next else t)
                Logger.log("Add property time %d"%t, 2)
                self._add_assertion(solver, npropt)

                Logger.log("Interpolation at k=%s"%(t), 2)

                if t > 0:
                    trans_tB = And(trans_t[pivot:(t*2)])
                    Ri = And(itp.binary_interpolant(And(R, trans_tA), And(trans_tB, npropt)))
                    is_sat = Ri == None

                if is_sat and self._solve(solver):
                    if R == init_0:
                        Logger.log("Counterexample found with k=%s"%(t), 1)
                        model = self._get_model(solver)
                        return (t, model)
                    else:
                        Logger.log("No counterexample or proof found with k=%s"%(t), 1)
                        Logger.msg(".", 0, not(Logger.level(1)))
                        self._pop(solver)
                        break
                else:
                    self._pop(solver)
                    if Ri is None:
                        break

                    Ri = substitute(Ri, map_10)
                    res = check_overappr(Ri, R)

                    if res == TRUE():
                        Logger.log("Proof found with k=%s"%(t), 1)
                        return (t, True)

                    R = res
                    int_c += 1

            t += 1

        return (t-1, None)
示例#10
0
    def solve_safety_int(self, hts, prop, k):
        init = hts.single_init()
        trans = hts.single_trans()
        invar = hts.single_invar()

        has_next = TS.has_next(prop)

        map_10 = dict([(TS.get_timed_name(v.symbol_name(), 1), TS.get_timed_name(v.symbol_name(), 0)) for v in hts.vars])

        itp = Interpolator(logic=get_logic(trans))
        init = And(init, invar)
        nprop = Not(prop)

        pivot = 2

        t = 1 if has_next else 0
        while (t < k+1):
            Logger.log("\nSolving for k=%s"%t, 1)
            int_c = 0
            init_0 = self.at_time(init, 0)
            R = init_0

            trans_t = self.unroll(trans, invar, t, gen_list=True)
            trans_tA = And(trans_t[:pivot]) if t > 0 else TRUE()
            trans_tB = And(trans_t[pivot:]) if t > 0 else TRUE()

            while True:
                self._reset_assertions(self.solver)
                Logger.log("Add init and invar", 2)
                self._add_assertion(self.solver, R)

                self._add_assertion(self.solver, And(trans_tA, trans_tB))

                npropt = self.at_time(nprop, t-1 if has_next else t)
                Logger.log("Add property time %d"%t, 2)
                self._add_assertion(self.solver, npropt)

                if self._solve(self.solver):
                    if R == init_0:
                        Logger.log("Counterexample found with k=%s"%(t), 1)
                        model = self._get_model(self.solver)
                        return (t, model)
                    else:
                        Logger.log("No counterexample or proof found with k=%s"%(t), 1)
                        Logger.msg(".", 0, not(Logger.level(1)))
                        break
                else:
                    if len(trans_t) < 2:
                        Logger.log("No counterexample found with k=%s"%(t), 1)
                        Logger.msg(".", 0, not(Logger.level(1)))
                        break

                    Ri = And(itp.binary_interpolant(And(R, trans_tA), And(trans_tB, npropt)))
                    Ri = substitute(Ri, map_10)

                    self._reset_assertions(self.solver)
                    self._add_assertion(self.solver, And(Ri, Not(R)))

                    if not self._solve(self.solver):
                        Logger.log("Proof found with k=%s"%(t), 1)
                        return (t, True)
                    else:
                        R = Or(R, Ri)
                        int_c += 1

                    Logger.log("Extending initial states (%s)"%int_c, 1)

            t += 1

        return (t-1, None)
示例#11
0
    def solve_safety_inc(self, hts, prop, k, k_min, processes=1):
        retdic = {}

        if self.config.strategy == VerificationStrategy.MULTI:
            solvers = []

            if self.config.prove:
                active_workers = [FWDK, VerificationStrategy.INT, VerificationStrategy.BWD, VerificationStrategy.FWD]
            else:
                active_workers = [VerificationStrategy.FWD, VerificationStrategy.BWD]

            active_workers = active_workers[:min(processes, len(active_workers))]
            workers = len(active_workers)

            with Manager() as manager:
                ret = manager.dict({})

                # Forward
                if VerificationStrategy.FWD in active_workers:
                    Logger.log("Starting \"%s\""%(VerificationStrategy.FWD), 1)
                    solvers.append(Process(target=self._run_as_process, \
                                           args=(self.solve_safety_inc_fwd, VerificationStrategy.FWD, ret, \
                                                 *[hts, prop, k, k_min, False, None, False])))

                # Backward
                if VerificationStrategy.BWD in active_workers:
                    Logger.log("Starting \"%s\""%(VerificationStrategy.BWD), 1)
                    solvers.append(Process(target=self._run_as_process, \
                                           args=(self.solve_safety_inc_bwd, VerificationStrategy.BWD, ret, \
                                                 *[hts, prop, k, False])))

                # K-Induction
                if FWDK in active_workers:
                    Logger.log("Starting \"%s\""%(FWDK), 1)
                    solvers.append(Process(target=self._run_as_process, \
                                           args=(self.solve_safety_inc_fwd, FWDK, ret, \
                                                 *[hts, prop, k, k_min, False, None, True])))


                # Interpolation
                if VerificationStrategy.INT in active_workers:
                    Logger.log("Starting \"%s\""%(VerificationStrategy.INT), 1)
                    solvers.append(Process(target=self._run_as_process, \
                                           args=(self.solve_safety_inc_int, VerificationStrategy.INT, ret, \
                                                 *[hts, prop, k])))

                # Monitoring of the status
                status_checker = Process(target=self._status_checker, args=[ret, workers])


                for solver in solvers:
                    solver.start()

                status_checker.start()
                status_checker.join()

                for solver in solvers:
                    solver.terminate()

                for key,val in ret.items():
                    retdic[key] = val

            tru_res = [(key,val) for key,val in retdic.items() if (val is not None) and (val[1] is not None) and (val[1] == True)]
            fal_res = [(key,val) for key,val in retdic.items() if (val is not None) and (val[1] is not None) and (val[1] != True)]
            unk_res = [(key,val) for key,val in retdic.items() if (val is not None) and (val[1] is None)]

            if (len(tru_res) > 0) and (len(fal_res) > 0):
                Logger.warning("Unconsistent results")

            if len(fal_res) > 0:
                winning = fal_res[0]
            elif len(tru_res) > 0:
                winning = tru_res[0]
            elif len(unk_res) > 0:
                winning = unk_res[0]
            else:
                Logger.error("No solution")

            Logger.msg("(%s)"%(winning[0]), 0, not(Logger.level(1)))

            # TODO: figure out a better way to handle this
            #       config object is not settable anymore
            # if winning[0] == VerificationStrategy.BWD:
            #     self.config.strategy = VerificationStrategy.BWD

            return winning[1]

        if self.config.strategy == VerificationStrategy.ALL:
            res = self.solve_safety_inc_fwd(hts, prop, k, k_min)
            if res[1] is not None:
                return res
            if self.config.prove and not TS.has_next(prop):
                res = self.solve_safety_int(hts, prop, k)
                if res[1] is not None:
                    return res
            res = self.solve_safety_inc_bwd(hts, prop, k)
            if res[1] is not None:
                self.config.strategy == VerificationStrategy.BWD
                return res
            res = self.solve_safety_inc_zz(hts, prop, k)
            self.config.strategy == VerificationStrategy.ZZ
            return res

        if self.config.strategy in [VerificationStrategy.FWD, VerificationStrategy.AUTO]:
            return self.solve_safety_inc_fwd(hts, prop, k, k_min)

        if self.config.strategy == VerificationStrategy.BWD:
            return self.solve_safety_inc_bwd(hts, prop, k)

        if self.config.strategy == VerificationStrategy.ZZ:
            return self.solve_safety_inc_zz(hts, prop, k)

        if self.config.strategy == VerificationStrategy.INT:
            return self.solve_safety_inc_int(hts, prop, k)

        Logger.error("Invalid configuration strategy")

        return None
示例#12
0
    def solve_safety_inc_fwd(self, hts, prop, k, k_min, all_vars=False):
        self._reset_assertions(self.solver)

        if self.config.prove:
            self.solver_ind = self.solver.copy("ind")
            self._reset_assertions(self.solver_ind)

            if all_vars:
                relevant_vars = hts.vars
            else:
                relevant_vars = hts.state_vars | hts.input_vars | hts.output_vars

        init = hts.single_init()
        trans = hts.single_trans()
        invar = hts.single_invar()

        acc_init = TRUE()
        acc_prop = TRUE()
        acc_loop_free = TRUE()
        trans_t = TRUE()

        if self.config.simplify:
            Logger.log("Simplifying the Transition System", 1)
            if Logger.level(2):
                timer = Logger.start_timer("Simplify")

            init = simplify(init)
            trans = simplify(trans)
            invar = simplify(invar)
            if Logger.level(2):
                Logger.get_timer(timer)

        n_prop_t = FALSE()
        init_0 = self.at_time(And(init, invar), 0)
        Logger.log("Add init and invar", 2)
        self._add_assertion(self.solver, init_0)

        if self.config.prove:
            # add invariants at time 0, but not init
            self._add_assertion(self.solver_ind, self.at_time(invar, 0),
                                "invar")

        next_prop = TS.has_next(prop)
        if next_prop:
            if k < 1:
                Logger.error(
                    "Invariant checking with next variables requires at least k=1"
                )
            k_min = 1

        t = 0
        while (t < k + 1):
            self._push(self.solver)

            t_prop = t - 1 if next_prop else t

            if k_min > 0:
                if (not next_prop) or (next_prop and t > 0):
                    n_prop_t = Or(n_prop_t, self.at_time(Not(prop), t_prop))
            else:
                n_prop_t = self.at_time(Not(prop), t)

            Logger.log("Add not property at time %d" % t, 2)
            self._add_assertion(self.solver, n_prop_t)

            if t >= k_min:
                Logger.log("\nSolving for k=%s" % (t), 1)

                if self._solve(self.solver):
                    Logger.log("Counterexample found with k=%s" % (t), 1)
                    model = self._get_model(self.solver)
                    return (t, model)
                else:
                    Logger.log("No counterexample found with k=%s" % (t), 1)
                    Logger.msg(".", 0, not (Logger.level(1)))
                    #self._add_assertion(self.solver, Not(n_prop_t))
            else:
                Logger.log(
                    "\nSkipping solving for k=%s (k_min=%s)" % (t, k_min), 1)
                Logger.msg(".", 0, not (Logger.level(1)))

            self._pop(self.solver)

            if self.config.prove:
                if t > k_min:
                    loop_free = self.loop_free(relevant_vars, t, t - 1)

                    # Checking I & T & loopFree
                    acc_init = And(acc_init, self.at_time(Not(init), t))
                    acc_loop_free = And(acc_loop_free, loop_free)

                    self._push(self.solver)

                    self._add_assertion(self.solver, acc_init)
                    self._add_assertion(self.solver, acc_loop_free)

                    if self._solve(self.solver):
                        Logger.log("Induction (I & lF) failed with k=%s" % (t),
                                   1)
                    else:
                        Logger.log("Induction (I & lF) holds with k=%s" % (t),
                                   1)
                        return (t, True)

                    self._pop(self.solver)

                    # Checking T & loopFree & !P
                    self._add_assertion(self.solver_ind,
                                        trans_t,
                                        comment="trans")
                    self._add_assertion(self.solver_ind,
                                        loop_free,
                                        comment="loop_free")

                    self._push(self.solver_ind)

                    self._add_assertion(self.solver_ind,
                                        self.at_time(Not(prop), t_prop))

                    if self._solve(self.solver_ind):
                        Logger.log(
                            "Induction (lF & !P) failed with k=%s" % (t), 1)
                    else:
                        Logger.log("Induction (lF & !P) holds with k=%s" % (t),
                                   1)
                        return (t, True)

                    self._pop(self.solver_ind)

                    self._add_assertion(self.solver_ind,
                                        self.at_time(prop, t_prop), "prop")
                else:
                    if not next_prop:
                        self._add_assertion(self.solver_ind,
                                            self.at_time(prop, t_prop), "prop")

            trans_t = self.unroll(trans, invar, t + 1, t)
            self._add_assertion(self.solver, trans_t)

            t += 1

        return (t - 1, None)
示例#13
0
    def solve_problems(self, problems_config: ProblemsManager) -> None:

        general_config = problems_config.general_config
        model_extension = general_config.model_extension
        assume_if_true = general_config.assume_if_true

        self.sparser = StringParser(general_config)
        self.lparser = LTLParser()

        self.coi = ConeOfInfluence()

        modifier = None
        if general_config.model_extension is not None:
            modifier = lambda hts: ModelExtension.extend(
                hts,
                ModelModifiersFactory.modifier_by_name(general_config.
                                                       model_extension))

        # generate main system system
        hts, invar_props, ltl_props = self.parse_model(
            general_config.model_files, problems_config.relative_path,
            general_config, "System 1", modifier)

        # Generate second models if any are necessary
        for problem in problems_config.problems:
            if problem.verification == VerificationType.EQUIVALENCE:
                if problem.equal_to is None:
                    raise RuntimeError(
                        "No second model for equivalence "
                        "checking provided for problem {}".format(
                            problem.name))

                hts2, _, _ = self.parse_model(problem.equal_to,
                                              problems_config.relative_path,
                                              general_config, "System 2",
                                              modifier)
                problems_config.add_second_model(problem, hts2)

        # TODO : contain these types of passes in functions
        #        they should be registered as passes

        if general_config.init is not None:
            iparser = InitParser()
            init_hts, inv_a, ltl_a = iparser.parse_file(
                general_config.init, general_config)
            assert inv_a is None and ltl_a is None, "Not expecting assertions from init state file"

            # remove old inits
            for ts in hts.tss:
                ts.init = TRUE()

            hts.combine(init_hts)
            hts.single_init(rebuild=True)

        # set default bit-wise initial values (0 or 1)
        if general_config.default_initial_value is not None:
            def_init_val = int(general_config.default_initial_value)
            try:
                if int(def_init_val) not in {0, 1}:
                    raise RuntimeError
            except:
                raise RuntimeError(
                    "Expecting 0 or 1 for default_initial_value,"
                    "but received {}".format(def_init_val))
            def_init_ts = TS("Default initial values")
            new_init = []
            initialized_vars = get_free_variables(hts.single_init())
            state_vars = hts.state_vars
            num_def_init_vars = 0
            num_state_vars = len(state_vars)

            const_arr_supported = True

            if hts.logic == L_ABV:
                for p in problems_config.problems:
                    if p.solver_name not in CONST_ARRAYS_SUPPORT:
                        const_arr_supported = False
                        Logger.warning(
                            "Using default_initial_value with arrays, "
                            "but one of the selected solvers, "
                            "{} does not support constant arrays. "
                            "Any assumptions on initial array values will "
                            "have to be done manually".format(
                                problem.solver_name))
                        break

            for sv in state_vars - initialized_vars:
                if sv.get_type().is_bv_type():
                    width = sv.get_type().width
                    if int(def_init_val) == 1:
                        val = BV((2**width) - 1, width)
                    else:
                        val = BV(0, width)

                    num_def_init_vars += 1
                elif sv.get_type().is_array_type() and \
                     sv.get_type().elem_type.is_bv_type() and \
                     const_arr_supported:
                    svtype = sv.get_type()
                    width = svtype.elem_type.width
                    if int(def_init_val) == 1:
                        val = BV((2**width) - 1, width)
                    else:
                        val = BV(0, width)
                    # create a constant array with a default value
                    val = Array(svtype.index_type, val)
                else:
                    continue

                def_init_ts.add_state_var(sv)
                new_init.append(EqualsOrIff(sv, val))
            def_init_ts.set_behavior(simplify(And(new_init)), TRUE(), TRUE())
            hts.add_ts(def_init_ts)
            Logger.msg(
                "Set {}/{} state elements to zero "
                "in initial state\n".format(num_def_init_vars, num_state_vars),
                1)

        problems_config.hts = hts

        # TODO: Update this so that we can control whether embedded assertions are solved automatically
        if not general_config.skip_embedded:
            for invar_prop in invar_props:
                problems_config.add_problem(
                    verification=VerificationType.SAFETY,
                    name=invar_prop[0],
                    description=invar_prop[1],
                    properties=invar_prop[2])
                self.properties.append(invar_prop[2])
            for ltl_prop in ltl_props:
                problems_config.add_problem(verification=VerificationType.LTL,
                                            name=invar_prop[0],
                                            description=invar_prop[1],
                                            properties=invar_prop[2])
                self.properties.append(ltl_prop[2])

        Logger.log(
            "Solving with abstract_clock=%s, add_clock=%s" %
            (general_config.abstract_clock, general_config.add_clock), 2)

        # ensure the miter_out variable exists
        miter_out = None

        for problem in problems_config.problems:
            if problem.name is not None:
                Logger.log(
                    "\n*** Analyzing problem \"%s\" ***" % (problem.name), 1)
                Logger.msg("Solving \"%s\" " % problem.name, 0,
                           not (Logger.level(1)))

            # apply parametric behaviors (such as toggling the clock)
            # Note: This is supposed to be *before* creating the combined system for equivalence checking
            #       we want this assumption to be applied to both copies of the clock
            problem_hts = ParametricBehavior.apply_to_problem(
                problems_config.hts, problem, general_config, self.model_info)

            if problem.verification == VerificationType.EQUIVALENCE:
                hts2 = problems_config.get_second_model(problem)
                problem_hts, miter_out = Miter.combine_systems(
                    hts, hts2, problem.bmc_length,
                    general_config.symbolic_init, problem.properties, True)

            try:
                # convert the formulas to PySMT FNodes
                # lemmas, assumptions and precondition always use the regular parser
                lemmas, assumptions, precondition = self.convert_formulae(
                    [
                        problem.lemmas, problem.assumptions,
                        problem.precondition
                    ],
                    parser=self.sparser,
                    relative_path=problems_config.relative_path)

                if problem.verification != VerificationType.LTL:
                    parser = self.sparser
                else:
                    parser = self.lparser

                prop = None
                if problem.properties is not None:
                    prop = self.convert_formula(
                        problem.properties,
                        relative_path=problems_config.relative_path,
                        parser=parser)
                    assert len(prop) == 1, "Properties should already have been split into " \
                        "multiple problems but found {} properties here".format(len(prop))
                    prop = prop[0]
                    self.properties.append(prop)
                else:
                    if problem.verification == VerificationType.SIMULATION:
                        prop = TRUE()
                    elif (problem.verification
                          is not None) and (problem.verification !=
                                            VerificationType.EQUIVALENCE):
                        Logger.error(
                            "Property not provided for problem {}".format(
                                problem.name))

                if problem.verification == VerificationType.EQUIVALENCE:
                    assert miter_out is not None
                    # set property to be the miter output
                    # if user provided a different equivalence property, this has already
                    # been incorporated in the miter_out
                    prop = miter_out
                    # reset the miter output
                    miter_out = None

                if precondition:
                    assert len(precondition
                               ) == 1, "There should only be one precondition"
                    prop = Implies(precondition[0], prop)

                # TODO: keep assumptions separate from the hts
                # IMPORTANT: CLEAR ANY PREVIOUS ASSUMPTIONS AND LEMMAS
                #   This was previously done in __solve_problem and has been moved here
                #   during the frontend refactor in April 2019
                # this is necessary because the problem hts is just a reference to the
                #   overall (shared) HTS
                problem_hts.assumptions = None
                problem_hts.lemmas = None

                # Compute the Cone Of Influence
                # Returns a *new* hts (not pointing to the original one anymore)
                if problem.coi:
                    if Logger.level(2):
                        timer = Logger.start_timer("COI")
                    hts = self.coi.compute(hts, prop)
                    if Logger.level(2):
                        Logger.get_timer(timer)

                if general_config.time:
                    timer_solve = Logger.start_timer(
                        "Problem %s" % problem.name, False)

                status, trace, traces, region = self.__solve_problem(
                    problem_hts, prop, lemmas, assumptions, problem)

                # set status for this problem
                problems_config.set_problem_status(problem, status)

                # TODO: Determine whether we need both trace and traces
                assert trace is None or traces is None, "Expecting either a trace or a list of traces"
                if trace is not None:
                    problem_traces = self.__process_trace(
                        hts, trace, general_config, problem)
                    problems_config.set_problem_traces(problem, problem_traces)

                if traces is not None:
                    traces_to_add = []
                    for trace in traces:
                        problem_trace = self.__process_trace(
                            hts, trace, general_config, problem)
                        for pt in problem_trace:
                            traces_to_add.append(pt)
                    problems_config.set_problem_traces(problem, traces_to_add)

                if problem.verification == VerificationType.PARAMETRIC:
                    assert region is not None
                    problems_config.set_problem_region(problem, region)

                if status is not None:
                    Logger.msg(" %s\n" % status, 0, not (Logger.level(1)))

                if (assume_if_true) and \
                   (status == VerificationStatus.TRUE) and \
                   (problem.assumptions == None) and \
                   (problem.verification == VerificationType.SAFETY):

                    # TODO: relax the assumption on problem.assumptions
                    #       can still add it, just need to make it an implication

                    ass_ts = TS("Previous assumption from property")
                    if TS.has_next(prop):
                        ass_ts.trans = prop
                    else:
                        ass_ts.invar = prop
                    # add assumptions to main system
                    problem_hts.reset_formulae()
                    problem_hts.add_ts(ass_ts)

                if general_config.time:
                    problems_config.set_problem_time(
                        problem, Logger.get_timer(timer_solve, False))

            except KeyboardInterrupt as e:
                Logger.msg("\b\b Skipped!\n", 0)
示例#14
0
    def parametric_safety(self,
                          prop,
                          k_max,
                          k_min,
                          parameters,
                          monotonic=True,
                          at_most=-1):
        if len(parameters) == 0:
            Logger.error("Parameters size cannot be 0")

        lemmas = self.hts.lemmas
        self._init_at_time(self.hts.vars, k_max)

        monotonic = True

        # if monotonic:
        #     for p in parameters:
        #         self.set_preferred((p, False))

        self.region = FALSE()

        generalize = lambda model, t: self._get_param_assignments(
            model, t, parameters, monotonic)

        if at_most == -1:
            cardinality = len(parameters)
        else:
            cardinality = at_most

        prev_cs_count = 0

        prove = self.config.prove

        step = 5
        same_res_counter = 0
        k = step
        end = False
        has_next = TS.has_next(prop)

        # Strategy selection
        increase_k = False

        if cardinality == -2:
            (t, status) = self.solve_safety_inc_fwd(self.hts,
                                                    prop,
                                                    k_max,
                                                    k_min,
                                                    all_vars=False,
                                                    generalize=generalize)
        else:
            sn = SortingNetwork.sorting_network(parameters)

            if increase_k:
                # Approach with incremental increase of bmc k
                while k < k_max + 1:
                    for at in range(cardinality):
                        Logger.msg("[%d,%d]" % ((at + 1), k), 0,
                                   not (Logger.level(1)))

                        sn_k = sn[at + 1] if at + 1 < len(sn) else FALSE()
                        bound_constr = Or(sn_k, self.region)
                        bound_constr = bound_constr if not has_next else Or(
                            bound_constr, TS.to_next(bound_constr))

                        self.config.prove = False
                        (t, status) = self.solve_safety_inc_bwd(
                            self.hts,
                            Or(prop, bound_constr),
                            k,
                            generalize=generalize)

                        if (prev_cs_count == self.cs_count):
                            same_res_counter += 1
                        else:
                            same_res_counter = 0

                        prev_cs_count = self.cs_count

                        if (prove == True) and ((same_res_counter > 1) or
                                                (at == cardinality - 1)):
                            Logger.msg("[>%d,%d]" % ((at + 1), k), 0,
                                       not (Logger.level(1)))

                            if (at_most > -1) and (at_most < cardinality):
                                sn_k = sn[at_most - 1]
                            else:
                                sn_k = FALSE()
                            bound_constr = Or(sn_k, self.region)
                            bound_constr = bound_constr if not has_next else Or(
                                bound_constr, TS.to_next(bound_constr))

                            self.config.prove = True
                            (t, status) = self.solve_safety(
                                self.hts, Or(prop, bound_constr), k,
                                max(k_min, k - step))

                            if status == True:
                                end = True
                                break

                        if (same_res_counter > 2) and (k < k_max):
                            break

                    if end:
                        break
                    k += step
            else:
                # Approach with fixed bmc k
                for at in range(cardinality):
                    Logger.msg("[%d]" % ((at + 1)), 0, not (Logger.level(1)))

                    sn_k = sn[at + 1] if at + 1 < len(sn) else FALSE()
                    bound_constr = Or(sn_k, self.region)
                    bound_constr = bound_constr if not has_next else Or(
                        bound_constr, TS.to_next(bound_constr))

                    self.config.prove = False
                    (t, status) = self.solve_safety_inc_bwd(
                        self.hts,
                        Or(prop, bound_constr),
                        k_max,
                        generalize=generalize)

                    if simplify(self.region) == TRUE():
                        break

                    if (prev_cs_count == self.cs_count):
                        same_res_counter += 1
                    else:
                        same_res_counter = 0

                    prev_cs_count = self.cs_count

                    if (prove == True) and ((same_res_counter > 1) or
                                            (at == cardinality - 1)):
                        Logger.msg("[>%d]" % ((at + 1)), 0,
                                   not (Logger.level(1)))

                        if (at_most > -1) and (at_most < cardinality):
                            sn_k = sn[at_most - 1]
                        else:
                            sn_k = FALSE()
                        bound_constr = Or(sn_k, self.region)
                        bound_constr = bound_constr if not has_next else Or(
                            bound_constr, TS.to_next(bound_constr))

                        self.config.prove = True
                        (t,
                         status) = self.solve_safety(self.hts,
                                                     Or(prop, bound_constr),
                                                     k_max, k_min)
                        if status == True:
                            break

        traces = None
        if (self.models is not None) and (simplify(self.region)
                                          not in [TRUE(), FALSE()]):
            traces = []
            for (model, time) in self.models:
                model = self._remap_model(self.hts.vars, model, time)
                trace = self.generate_trace(model, time,
                                            get_free_variables(prop))
                traces.append(trace)

        region = []
        dass = {}

        # Sorting result by size
        for ass in list(disjunctive_partition(self.region)):
            cp = list(conjunctive_partition(ass))
            size = len(cp)
            if size not in dass:
                dass[size] = []
            dass[size].append(ass)

        indexes = list(dass.keys())
        indexes.sort()
        for size in indexes:
            region += dass[size]

        if status == True:
            return (VerificationStatus.TRUE, traces, region)
        elif status is not None:
            return (VerificationStatus.FALSE, traces, region)
        else:
            return (VerificationStatus.UNK, traces, region)