Exemple #1
0
 def prev_frame_constraint(diag: Diagram) -> bool:
     pre_frame = self[j - 1].summary()
     return (
         logic.check_two_state_implication_all_transitions(
             self.solver, pre_frame, syntax.Not(diag.to_ast()), minimize=False
         ) is None and
         self.valid_in_initial_frame(syntax.Not(diag.to_ast()))
     )
Exemple #2
0
    def block(self, diag_or_expr: Union[Diagram, Expr], j: int,
              trace: RelaxedTrace) -> None:
        utils.logger.info(f'block({j})')

        def as_expr() -> Expr:
            return diag_or_expr.to_ast() if isinstance(
                diag_or_expr, Diagram) else diag_or_expr

        if j == 0 or (j == 1 and
                      not self.valid_in_initial_frame(syntax.Not(as_expr()))):
            utils.logger.always_print('\n'.join(
                ((t.name + ' ') if t is not None else '') + str(diag)
                for t, diag in trace))
            print(
                'abstract counterexample: the system has no universal inductive invariant proving safety'
            )
            if utils.args.checkpoint_out:
                self.store_frames(utils.args.checkpoint_out)
            raise AbstractCounterexample()

        while True:
            res, x = self.find_predecessor(j - 1, diag_or_expr)
            if res == z3.unsat:
                assert x is None or isinstance(x, MySet)
                core: Optional[MySet[int]] = x
                self.augment_core_for_init(diag_or_expr, core)
                break
            assert isinstance(x, tuple), (res, x)
            trans, cti = x
            pre_diag = cti.as_diagram(index=0)

            trace.append((trans, pre_diag))
            self.block(pre_diag, j - 1, trace)
            trace.pop()

        if isinstance(diag_or_expr, Diagram):
            diag_or_expr.minimize_from_core(core)

        def prev_frame_constraint(diag: Diagram) -> bool:
            pre_frame = self[j - 1].summary()
            return (logic.check_two_state_implication_all_transitions(
                self.solver,
                pre_frame,
                syntax.Not(diag.to_ast()),
                minimize=False) is None
                    and self.valid_in_initial_frame(syntax.Not(diag.to_ast())))

        if isinstance(diag_or_expr, Diagram):
            diag_or_expr.generalize(self.solver, prev_frame_constraint)
        e = syntax.Not(as_expr())
        utils.logger.info(f'block({j}) using {e}')
        self.add(e, j)
        k = j
        while k + 1 < len(self) and self.push_conjunct(k, e):
            utils.logger.info(f'and pushed to {k + 1}')
            k += 1
    def as_onestate_formula(self, index: Optional[int] = None) -> Expr:
        # TODO: move to class State, this shouldn't be here
        assert self.num_states == 1 or index is not None, \
            'to generate a onestate formula from a multi-state model, ' + \
            'you must specify which state you want'
        assert index is None or (0 <= index and index < self.num_states)

        if index is None:
            index = 0

        if index not in self.onestate_formula_cache:
            prog = syntax.the_program

            mut_rel_interps = self.rel_interps[index]
            mut_const_interps = self.const_interps[index]
            mut_func_interps = self.func_interps[index]

            vs: List[syntax.SortedVar] = []
            ineqs: Dict[SortDecl, List[Expr]] = {}
            rels: Dict[RelationDecl, List[Expr]] = {}
            consts: Dict[ConstantDecl, Expr] = {}
            funcs: Dict[FunctionDecl, List[Expr]] = {}
            for sort in self.univs:
                vs.extend(syntax.SortedVar(v, syntax.UninterpretedSort(sort.name))
                          for v in self.univs[sort])
                u = [syntax.Id(v) for v in self.univs[sort]]
                ineqs[sort] = [syntax.Neq(a, b) for a, b in combinations(u, 2)]
            for R, l in chain(mut_rel_interps.items(), self.immut_rel_interps.items()):
                rels[R] = []
                for tup, ans in l.items():
                    e: Expr = (
                        syntax.AppExpr(R.name, tuple(syntax.Id(col) for col in tup))
                        if tup else syntax.Id(R.name)
                    )
                    rels[R].append(e if ans else syntax.Not(e))
            for C, c in chain(mut_const_interps.items(), self.immut_const_interps.items()):
                consts[C] = syntax.Eq(syntax.Id(C.name), syntax.Id(c))
            for F, fl in chain(mut_func_interps.items(), self.immut_func_interps.items()):
                funcs[F] = [
                    syntax.Eq(syntax.AppExpr(F.name, tuple(syntax.Id(col) for col in tup)),
                              syntax.Id(res))
                    for tup, res in fl.items()
                ]

            # get a fresh variable, avoiding names of universe elements in vs
            fresh = prog.scope.fresh('x', [v.name for v in vs])

            e = syntax.Exists(tuple(vs), syntax.And(
                *chain(*ineqs.values(), *rels.values(), consts.values(), *funcs.values(), (
                    syntax.Forall((syntax.SortedVar(fresh,
                                                    syntax.UninterpretedSort(sort.name)),),
                                  syntax.Or(*(syntax.Eq(syntax.Id(fresh), syntax.Id(v))
                                              for v in self.univs[sort])))
                    for sort in self.univs
                ))))
            assert prog.scope is not None
            with prog.scope.n_states(1):
                typechecker.typecheck_expr(prog.scope, e, None)
            self.onestate_formula_cache[index] = e
        return self.onestate_formula_cache[index]
Exemple #4
0
def brat_next_frame(s: Solver, prev_frame: List[Expr], bound: int,
                    inits: List[Expr], safety: Expr, bad_cache: Set[Diagram],
                    minimize: bool) -> List[Expr]:
    current_frame: List[Expr] = new_frame(s, prev_frame)

    for bad_model in bad_cache:
        if logic.check_implication(s, current_frame,
                                   [syntax.Not(bad_model.to_ast())]) is None:
            continue
        current_frame.append(
            post_image_prime_consequence(
                s,
                prev_frame,
                inits,
                bad_model,
                gen_order=utils.args.generalization_order))

    while (bad_trace := bmc_upto_bound(
            s,
            safety,
            bound,
            preconds=current_frame,
            minimize=minimize,
            relaxed_semantics=utils.args.relax_backwards)) is not None:
        bad_model = Diagram(bad_trace.as_state(0))
        utils.logger.debug("Example to block: %s" % str(bad_model))
        bad_cache.add(bad_model)
        learned_clause = post_image_prime_consequence(
            s,
            prev_frame,
            inits,
            bad_model,
            gen_order=utils.args.generalization_order)
        utils.logger.info("Learned clause: %s" % str(learned_clause))
        current_frame.append(learned_clause)
Exemple #5
0
def check_bmc(s: Solver, safety: Expr, depth: int, preconds: Optional[Iterable[Expr]] = None,
              minimize: Optional[bool] = None) -> Optional[Trace]:
    prog = syntax.the_program

    if preconds is None:
        preconds = (init.expr for init in prog.inits())

    t = s.get_translator(depth + 1)

    with s.new_frame():
        for precond in preconds:
            s.add(t.translate_expr(precond))

        s.add(t.translate_expr(syntax.New(syntax.Not(safety), depth)))

        for i in range(depth):
            s.add(t.translate_expr(New(safety, i)))
            assert_any_transition(s, t, i, allow_stutter=False)

        res = s.check()
        if res == solver.sat:
            z3m = s.model(minimize=minimize)
            m = Z3Translator.model_to_trace(z3m, depth + 1)
            return m
        elif res == solver.unknown:
            print('unknown!')
        return None
    def _read_first_order_structure(
            struct: FirstOrderStructure
    ) -> Tuple[List[syntax.SortedVar],  # vs
               Dict[SortDecl, List[Expr]],  # ineqs
               Dict[RelationDecl, List[Expr]],  # rels
               Dict[ConstantDecl, Expr],  # consts
               Dict[FunctionDecl, List[Expr]],  # funcs
               ]:
        vars_by_sort: Dict[SortDecl, List[syntax.SortedVar]] = {}
        ineqs: Dict[SortDecl, List[Expr]] = {}
        rels: Dict[RelationDecl, List[Expr]] = {}
        consts: Dict[ConstantDecl, Expr] = {}
        funcs: Dict[FunctionDecl, List[Expr]] = {}
        for sort in struct.univs:
            vars_by_sort[sort] = [
                syntax.SortedVar(v, syntax.UninterpretedSort(sort.name))
                for v in struct.univs[sort]
            ]
            u = [syntax.Id(s) for s in struct.univs[sort]]
            ineqs[sort] = [
                syntax.Neq(a, b) for a, b in itertools.combinations(u, 2)
            ]

        for R, l in struct.rel_interps.items():
            rels[R] = []
            for tup, ans in l.items():
                e: Expr
                if tup:
                    args: List[Expr] = []
                    for (col, col_sort) in zip(tup, R.arity):
                        assert isinstance(col_sort, syntax.UninterpretedSort)
                        assert col_sort.decl is not None
                        args.append(syntax.Id(col))
                    e = syntax.AppExpr(R.name, tuple(args))
                else:
                    e = syntax.Id(R.name)
                e = e if ans else syntax.Not(e)
                rels[R].append(e)
        for C, c in struct.const_interps.items():
            e = syntax.Eq(syntax.Id(C.name), syntax.Id(c))
            consts[C] = e
        for F, fl in struct.func_interps.items():
            funcs[F] = []
            for tup, res in fl.items():
                e = syntax.AppExpr(F.name,
                                   tuple(syntax.Id(col) for col in tup))
                e = syntax.Eq(e, syntax.Id(res))
                funcs[F].append(e)

        vs = list(itertools.chain(*(vs for vs in vars_by_sort.values())))

        return vs, ineqs, rels, consts, funcs
Exemple #7
0
def itp_gen(s: Solver) -> None:
    k = utils.args.forward_depth

    prog = syntax.the_program
    safety = syntax.And(*(inv.expr for inv in prog.invs() if inv.is_safety))
    inits = [init.expr for init in prog.inits()]

    utils.logger.info("initial state: %s" % str(inits))
    utils.logger.info("proving safety property: %s" % safety)

    candidate = [safety]

    while True:
        cti = get_cti(s, syntax.And(*candidate))
        if cti is None:
            break

        pre_diag = cti[0]

        pre_diag.generalize(s,
                            lambda diag: bmc_upto_bound(
                                s, syntax.Not(diag.to_ast()), k) is None,
                            order=utils.args.generalization_order)

        e = syntax.Not(pre_diag.to_ast())

        utils.logger.info('adding new clause to the invariant: %s' % str(e))
        candidate.append(e)

    if logic.check_implication(s, inits, candidate) is not None:
        utils.logger.always_print(
            "Failure: candidate %s excludes initial states" %
            ' & '.join(str(clause) for clause in candidate))
    else:
        utils.logger.always_print("Success! Inductive invariant:")
        for clause in candidate:
            utils.logger.always_print(str(clause))
Exemple #8
0
def bmc_prime_consequence(s: Solver,
                          bound: int,
                          inits: List[Expr],
                          bad_model: Diagram,
                          relaxed_semantics: bool,
                          generalization_order: Optional[int] = None) -> Expr:
    def bmc_constraint(diag: Diagram) -> bool:
        return bmc_upto_bound(s,
                              syntax.Not(diag.to_ast()),
                              bound,
                              preconds=inits,
                              relaxed_semantics=relaxed_semantics) is None

    bad_model_copy = copy.deepcopy(bad_model)
    bad_model_copy.generalize(s, bmc_constraint, order=generalization_order)

    return syntax.Not(bad_model_copy.to_ast())
Exemple #9
0
def post_image_prime_consequence(s: Solver,
                                 prev_frame: List[Expr],
                                 inits: List[Expr],
                                 bad_model: Diagram,
                                 gen_order: Optional[int] = None) -> Expr:
    # TODO: duplicated from updr
    def prev_frame_constraint(diag: Diagram) -> bool:
        return (logic.check_two_state_implication_all_transitions(
            s, prev_frame, syntax.Not(diag.to_ast()), minimize=False) is None
                and valid_in_initial_frame(s, inits, syntax.Not(
                    diag.to_ast())))

    # TODO: unsat core first
    bad_model_copy = copy.deepcopy(bad_model)
    bad_model_copy.generalize(s, prev_frame_constraint, order=gen_order)

    return syntax.Not(bad_model_copy.to_ast())
Exemple #10
0
def negate_clause(c: Expr) -> Expr:
    if isinstance(c, syntax.Bool):
        return syntax.Bool(not c.val)
    elif isinstance(c, syntax.UnaryExpr):
        assert c.op == 'NOT'
        return c.arg
    elif isinstance(c, syntax.BinaryExpr):
        assert c.op in ['EQUAL', 'NOTEQ']
        op = 'NOTEQ' if c.op == 'EQUAL' else 'EQUAL'
        return syntax.BinaryExpr(op, c.arg1, c.arg2)
    elif isinstance(c, syntax.NaryExpr):
        assert c.op == 'OR'
        return syntax.NaryExpr('AND', [negate_clause(arg) for arg in c.args])
    elif isinstance(c, syntax.AppExpr) or isinstance(c, syntax.Id):
        return syntax.Not(c)
    else:
        assert False, f'unsupported expression {c} in negate_clause'
Exemple #11
0
def diagram_trace_to_explicitly_relaxed_trace(trace: RelaxedTrace, safety: Sequence[syntax.InvariantDecl]) -> None:
    relaxed_prog = relaxed_program(syntax.the_program)

    with syntax.prog_context(relaxed_prog):
        s = Solver()

        end_expr = syntax.Not(syntax.And(*(invd.expr for invd in safety)))
        with syntax.the_program.scope.n_states(1):
            end_expr.resolve(syntax.the_program.scope, syntax.BoolSort)
        trace_decl = diagram_trace_to_explicitly_relaxed_trace_decl(trace, end_expr)
        with syntax.the_program.scope.n_states(1):
            trace_decl.resolve(syntax.the_program.scope)

        print(trace_decl)

        res = bmc_trace(relaxed_prog, trace_decl, s, lambda slvr, ks: logic.check_solver(slvr, ks, minimize=True))
        print(res)
        assert False
Exemple #12
0
def is_rel_blocking_relax(trns: Trace, idx: int,
                          derived_rel: Tuple[List[Tuple[syntax.SortedVar, str]], Expr]) -> bool:
    # TODO: probably can obtain the sort from the sortedvar when not using scapy
    free_vars, derived_relation_formula = derived_rel
    free_vars_active_clause = syntax.And(*(active_var(v.name, sort_name) for (v, sort_name) in free_vars))

    diffing_formula = syntax.Exists([v for (v, _) in free_vars],
                                    syntax.And(syntax.Old(syntax.And(free_vars_active_clause,
                                                                     derived_relation_formula)),
                                               syntax.And(free_vars_active_clause,
                                                          syntax.Not(derived_relation_formula))))

    with syntax.the_program.scope.two_state(twostate=True):  # TODO: what is this doing? probably misusing
        diffing_formula.resolve(syntax.the_program.scope, syntax.BoolSort)

    res = trns.eval_double_vocab(diffing_formula, idx)
    assert isinstance(res, bool)
    return cast(bool, res)
Exemple #13
0
def negate_clause(c: Expr) -> Expr:
    if isinstance(c, syntax.Bool):
        return syntax.Bool(not c.val)
    elif isinstance(c, syntax.UnaryExpr):
        assert c.op == 'NOT'
        return c.arg
    elif isinstance(c, syntax.BinaryExpr):
        assert c.op in ['EQUAL', 'NOTEQ']
        op = 'NOTEQ' if c.op == 'EQUAL' else 'EQUAL'
        return syntax.BinaryExpr(op, c.arg1, c.arg2)
    elif isinstance(c, syntax.NaryExpr):
        assert c.op == 'OR'
        return syntax.NaryExpr('AND', tuple(negate_clause(arg) for arg in c.args))
    elif isinstance(c, syntax.AppExpr) or isinstance(c, syntax.Id):
        return syntax.Not(c)
    elif isinstance(c, syntax.QuantifierExpr):
        assert c.quant == 'FORALL'
        return syntax.QuantifierExpr('EXISTS', c.get_vs(), negate_clause(c.body))
    else:
        assert False, f'unsupported expression {c} in negate_clause'
Exemple #14
0
def is_rel_blocking_relax_step(
        trns: Trace, idx: int,
        derived_rel: Tuple[List[Tuple[syntax.SortedVar, str]], Expr]
) -> bool:
    # TODO: probably can obtain the sort from the sortedvar when not using pickle
    free_vars, derived_relation_formula = derived_rel
    free_vars_active_clause = syntax.And(*(active_var(v.name, sort_name) for (v, sort_name) in free_vars))

    diffing_formula = syntax.Exists([v for (v, _) in free_vars],
                                    syntax.And(syntax.And(free_vars_active_clause,
                                                          derived_relation_formula),
                                               syntax.New(syntax.And(free_vars_active_clause,
                                                                     syntax.Not(derived_relation_formula)))))

    with syntax.the_program.scope.fresh_stack():
        with syntax.the_program.scope.n_states(2):
            diffing_formula.resolve(syntax.the_program.scope, syntax.BoolSort)

    res = trns.eval(diffing_formula, idx)
    assert isinstance(res, bool)
    return cast(bool, res)
Exemple #15
0
def itp_gen(s: Solver) -> None:
    k = 4

    prog = syntax.the_program
    safety = syntax.And(*(inv.expr for inv in prog.invs() if inv.is_safety))
    inits = [init.expr for init in prog.inits()]

    utils.logger.info("initial state: %s" % str(inits))
    utils.logger.info("proving safety property: %s" % safety)

    candidate = [safety]
    while True:
        cti = get_cti(s, syntax.And(*candidate))
        if cti is None:
            break

        pre_diag = cti[0]

        with logic.BoundedReachabilityCheck(s, syntax.the_program,
                                            k) as bmc_checker:
            core = bmc_checker.check_and_core(pre_diag)
        pre_diag.minimize_from_core(core)

        pre_diag.generalize(
            s, lambda diag: generalize_cex_omission_checker(s, diag, k))

        e = syntax.Not(pre_diag.to_ast())

        utils.logger.info('adding new clause to the invariant: %s' % str(e))
        candidate.append(e)

    res = logic.check_implication(s, inits, candidate)
    if res is not None:
        utils.logger.always_print(
            "Failure: candidate %s excludes initial states" %
            ' & '.join(str(clause) for clause in candidate))
    else:
        utils.logger.always_print("Success! Inductive invariant:")
        for clause in candidate:
            utils.logger.always_print(str(clause))
Exemple #16
0
                             key=lambda b: (b.known_absent_until_frame, b.num_steps_to_bad),
                             default=None)

            if bstate_min is None or (min_frame_no := bstate_min.known_absent_until_frame) == len(self) - 1:
                break

            if isinstance(state := bstate_min.state_or_expr, State):
                eval_res = state.eval(syntax.And(*(self[min_frame_no + 1].summary())))
                assert isinstance(eval_res, bool)
                if eval_res:
                    return bstate_min
            else:
                expr = state
                res = logic.check_implication(self.solver,
                                              self[min_frame_no + 1].summary(),
                                              [syntax.Not(expr)],
                                              minimize=False)
                if res is not None:
                    return bstate_min

            bstate_min.known_absent_until_frame += 1

        utils.logger.info('no existing states to block. looking for a new state.')

        f = self.fs[-1]
        if len(self.safeties) == 0 or (res := logic.check_implication(self.solver, f.summary(), self.safeties)) is None:
            utils.logger.info('frontier is safe. nothing new to block either.')
            return None

        state = Z3Translator.model_to_trace(res, 1).as_state(0)
        assert len(self) >= 2
Exemple #17
0
 def bmc_constraint(diag: Diagram) -> bool:
     return bmc_upto_bound(s,
                           syntax.Not(diag.to_ast()),
                           bound,
                           preconds=inits,
                           relaxed_semantics=relaxed_semantics) is None
Exemple #18
0
 def valid_in_initial_frame(self, s: Solver, p: Phase,
                            diag: Diagram) -> Optional[z3.ModelRef]:
     return logic.check_implication(s, self.fs[0].summary_of(p),
                                    [syntax.Not(diag.to_ast())])
Exemple #19
0
 def as_expr(self, els_trans: Callable[[str],str]) -> Expr:
     fact_free_vars = syntax.Apply(self._rel.name, [syntax.Id(None, els_trans(e)) for e in self._els])
     if not self._is_positive():
         fact_free_vars = syntax.Not(fact_free_vars)
     return fact_free_vars
Exemple #20
0
    def block(self,
              diag: Diagram,
              j: int,
              p: Phase,
              trace: Optional[List[Tuple[Optional[PhaseTransition],
                                         Union[Diagram, Expr]]]] = None,
              safety_goal: bool = True) -> Union[Blocked, CexFound]:
        if trace is None:
            trace = []
        if j == 0 or (j == 1 and self.valid_in_initial_frame(
                self.solver, p, diag) is not None):
            if safety_goal:
                utils.logger.always_print('\n'.join(
                    ((t.pp() + ' ') if t is not None else '') + str(diag)
                    for t, diag in trace))
                print('abstract counterexample')
                raise Exception('abstract counterexample')
            else:
                if utils.logger.isEnabledFor(logging.DEBUG):
                    utils.logger.debug('failed to block diagram')
                    # utils.logger.debug(str(diag))
                return CexFound()

        # print fs
        while True:
            with utils.LogTag(utils.logger, 'block-attempt'):
                if utils.logger.isEnabledFor(logging.DEBUG):
                    utils.logger.debug('blocking diagram in frame %s' % j)
                    utils.logger.debug(str(diag))

                    self.print_frame(j - 1, lvl=logging.DEBUG)
                res, x = self.find_predecessor(self[j - 1], p, diag)
                if res == z3.unsat:
                    utils.logger.debug('no predecessor: blocked!')
                    assert x is None or isinstance(x, MySet)
                    core: Optional[MySet[int]] = x
                    self.augment_core_for_init(p, diag, core)
                    break
                assert isinstance(x, tuple), (res, x)
                trans, (pre_phase, pre_diag) = x

                trace.append((trans, pre_diag))
                ans = self.block(pre_diag, j - 1, pre_phase, trace,
                                 safety_goal)
                if not isinstance(ans, Blocked):
                    return ans
                trace.pop()

        if utils.logger.isEnabledFor(logging.DEBUG) and core is not None:
            utils.logger.debug('core %s' % core)
            utils.logger.debug('unminimized diag\n%s' % diag)

        diag.minimize_from_core(core)
        diag.generalize(self.solver, self[j - 1],
                        self.automaton.transitions_to_grouped_by_src(p),
                        p == self.automaton.init_phase(), j)

        e = syntax.Not(diag.to_ast())

        if utils.logger.isEnabledFor(logging.DEBUG):
            utils.logger.debug(
                'adding new clause to frames 0 through %d phase %s' %
                (j, p.name()))
        if utils.logger.isEnabledFor(logging.INFO):
            utils.logger.info("[%d] %s" % (j, str(e)))

        self.add(p, e, j)
        utils.logger.debug("Done blocking")

        return Blocked()
Exemple #21
0
 def prev_frame_constraint(diag: Diagram) -> bool:
     return (logic.check_two_state_implication_all_transitions(
         s, prev_frame, syntax.Not(diag.to_ast()), minimize=False) is None
             and valid_in_initial_frame(s, inits, syntax.Not(
                 diag.to_ast())))
Exemple #22
0
    def block(self,
              diag: Diagram,
              j: int,
              p: Phase,
              trace: Optional[RelaxedTrace] = None,
              safety_goal: bool = True) -> Union[Blocked, CexFound]:
        if trace is None:
            trace = []
        if j == 0 or (j == 1 and self.valid_in_initial_frame(
                self.solver, p, diag) is not None):
            if safety_goal:
                utils.logger.always_print('\n'.join(
                    ((t.pp() + ' ') if t is not None else '') + str(diag)
                    for t, diag in trace))
                print(
                    'abstract counterexample: the system has no universal inductive invariant proving safety'
                )
                # TODO: placeholder for analyzing relaxed trace
                # import relaxed_traces
                # print(relaxed_traces.diagram_trace_to_explicitly_relaxed_trace(trace, phases.phase_safety(p)))
                if utils.args.checkpoint_out:
                    self.store_frames(utils.args.checkpoint_out)
                raise AbstractCounterexample()
            else:
                if utils.logger.isEnabledFor(logging.DEBUG):
                    utils.logger.debug('failed to block diagram')
                return CexFound()

        while True:
            with utils.LogTag(utils.logger, 'block-attempt'):
                if utils.logger.isEnabledFor(logging.DEBUG):
                    utils.logger.debug('blocking diagram in frame %s' % j)
                    utils.logger.debug(str(diag))

                    self.print_frame(j - 1, lvl=logging.DEBUG)
                res, x = self.find_predecessor(self[j - 1], p, diag)
                if res == z3.unsat:
                    utils.logger.debug('no predecessor: blocked!')
                    assert x is None or isinstance(x, MySet)
                    core: Optional[MySet[int]] = x
                    self.augment_core_for_init(p, diag, core)
                    break
                assert isinstance(x, tuple), (res, x)
                trans, (pre_phase, pre_diag) = x

                trace.append((trans, pre_diag))
                ans = self.block(pre_diag, j - 1, pre_phase, trace,
                                 safety_goal)
                if not isinstance(ans, Blocked):
                    return ans
                trace.pop()

        if utils.logger.isEnabledFor(logging.DEBUG) and core is not None:
            utils.logger.debug('core %s' % core)
            utils.logger.debug('unminimized diag\n%s' % diag)

        diag.minimize_from_core(core)
        diag.generalize(self.solver, self[j - 1],
                        self.automaton.transitions_to_grouped_by_src(p),
                        p == self.automaton.init_phase(), j)

        e = syntax.Not(diag.to_ast())

        if utils.logger.isEnabledFor(logging.DEBUG):
            utils.logger.debug(
                'adding new clause to frames 0 through %d phase %s' %
                (j, p.name()))
        if utils.logger.isEnabledFor(logging.INFO):
            utils.logger.info("[%d] %s" % (j, str(e)))

        self.add(p, e, j)
        utils.logger.debug("Done blocking")

        return Blocked()