示例#1
0
def primitive_values():
    return [
        (common.OP_ALTERNATIVE,
            syntax.Forall(
                var='a',
                body=syntax.function(
                    syntax.Variable(name='a'),
                    syntax.function(
                        syntax.Variable(name='a'),
                        syntax.Variable(name='a'))))),
        (common.OP_SEQUENCE,
            syntax.Forall(
                var='a',
                body=syntax.Forall(
                    var='b',
                    body=syntax.function(
                        syntax.Variable(name='a'),
                        syntax.function(
                            syntax.Variable(name='b'),
                            syntax.Variable(name='b')))))),
        (common.OP_UNIFY,
            syntax.Forall(
                var='a',
                body=syntax.function(
                    syntax.Variable(name='a'),
                    syntax.function(
                        syntax.Variable(name='a'),
                        syntax.primitive_type_unit())))),

        (common.VALUE_UNIT, syntax.primitive_type_unit()),
    ]
def functions_total_axioms(prog: syntax.Program) -> List[Expr]:
    res = []

    for func in prog.functions():
        # TODO: generation of part of the formula duplicated from relaxation_action_def.
        # TODO: would be best to beef up the expression-generation library
        names: List[str] = []
        params = []
        for arg_sort in func.arity:
            arg_sort_decl = syntax.get_decl_from_sort(arg_sort)
            name = prog.scope.fresh(arg_sort_decl.name[0].upper(),
                                    also_avoid=names)
            names.append(name)
            params.append(syntax.SortedVar(name, arg_sort))
        ap_func = syntax.Apply(func.name,
                               tuple(syntax.Id(v.name) for v in params))

        name = prog.scope.fresh('y', also_avoid=names)

        ax = syntax.Forall(
            tuple(params),
            syntax.Exists((syntax.SortedVar(name, func.sort), ),
                          syntax.Eq(syntax.Id(name), ap_func)))
        with prog.scope.n_states(1):
            typechecker.typecheck_expr(prog.scope, ax, syntax.BoolSort)

        res.append(ax)

    return res
    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]
示例#4
0
def relaxed_program(prog: syntax.Program) -> syntax.Program:
    new_decls: List[syntax.Decl] = [d for d in prog.sorts()]

    actives: Dict[syntax.SortDecl, syntax.RelationDecl] = {}
    for sort in prog.sorts():
        name = prog.scope.fresh('active_' + sort.name)
        r = syntax.RelationDecl(name, arity=[syntax.UninterpretedSort(sort.name)],
                                mutable=True, derived=None, annotations=[])
        actives[sort] = r
        new_decls.append(r)

    # active relations initial conditions: always true
    for sort in prog.sorts():
        name = prog.scope.fresh(sort.name[0].upper())
        expr = syntax.Forall([syntax.SortedVar(name, None)],
                             syntax.Apply(actives[sort].name, [syntax.Id(name)]))
        new_decls.append(syntax.InitDecl(name=None, expr=expr))

    for d in prog.decls:
        if isinstance(d, syntax.SortDecl):
            pass  # already included above
        elif isinstance(d, syntax.RelationDecl):
            if d.derived_axiom is not None:
                expr = syntax.relativize_quantifiers(actives, d.derived_axiom)
                new_decls.append(syntax.RelationDecl(d.name, d.arity, d.mutable, expr,
                                                     d.annotations))
            else:
                new_decls.append(d)
        elif isinstance(d, syntax.ConstantDecl):
            new_decls.append(d)
        elif isinstance(d, syntax.FunctionDecl):
            new_decls.append(d)
        elif isinstance(d, syntax.AxiomDecl):
            new_decls.append(d)
        elif isinstance(d, syntax.InitDecl):
            new_decls.append(d)
        elif isinstance(d, syntax.DefinitionDecl):
            assert not isinstance(d.body, syntax.BlockStatement), \
                "relax does not support transitions written in imperative syntax"
            mods, expr = d.body
            expr = syntax.relativize_quantifiers(actives, expr)
            if d.is_public_transition:
                guard = syntax.relativization_guard_for_binder(actives, d.binder)
                expr = syntax.And(guard, expr)
            new_decls.append(syntax.DefinitionDecl(d.is_public_transition, d.num_states, d.name,
                                                   params=d.binder.vs, body=(mods, expr)))
        elif isinstance(d, syntax.InvariantDecl):
            expr = syntax.relativize_quantifiers(actives, d.expr)
            new_decls.append(syntax.InvariantDecl(d.name, expr=expr,
                                                  is_safety=d.is_safety, is_sketch=d.is_sketch))
        else:
            assert False, d

    new_decls.append(relaxation_action_def(prog, actives=actives, fresh=True))

    res = syntax.Program(new_decls)
    res.resolve()  # #sorrynotsorry
    return res
def relax_actives_action_chunk(scope: syntax.Scope, actives: Dict[syntax.SortDecl, syntax.RelationDecl]) \
        -> Tuple[Tuple[syntax.ModifiesClause, ...], List[Expr]]:
    new_mods = []
    new_conjs = []

    for sort, active_rel in actives.items():
        name = scope.fresh(sort.name[0].upper())
        ap = syntax.Apply(active_rel.name, (syntax.Id(name), ))
        expr = syntax.Forall((syntax.SortedVar(name, None), ),
                             syntax.Implies(syntax.New(ap), ap))
        new_conjs.append(expr)
        new_mods.append(syntax.ModifiesClause(actives[sort].name))

    return tuple(new_mods), new_conjs
示例#6
0
文件: mypyvy.py 项目: jrkoenig/mypyvy
def sandbox(s: Solver) -> None:
    ####################################################################################
    # SANDBOX for playing with relaxed traces

    import pickle
    trns: logic.Trace = pickle.load(open("paxos_trace.p", "rb"))

    diff_conjunctions = relaxed_traces.derived_rels_candidates_from_trace(
        trns, [], 1, 3)

    print("num candidate relations:", len(diff_conjunctions))
    for diffing_conjunction in diff_conjunctions:
        # print("relation:")
        # for conj in diffing_conjunction:
        #     print("\t %s" % str(conj))
        print(diffing_conjunction[1])

    derrel_name = syntax.the_program.scope.fresh("nder")
    (free_vars, def_expr) = diff_conjunctions[0]
    def_axiom = syntax.Forall(
        free_vars,
        syntax.Iff(
            syntax.Apply(derrel_name,
                         [syntax.Id(None, v.name) for v in free_vars]),
            # TODO: extract pattern
            def_expr))

    derrel = syntax.RelationDecl(
        tok=None,
        name=derrel_name,
        arity=[syntax.safe_cast_sort(var.sort) for var in free_vars],
        mutable=True,
        derived=def_axiom,
        annotations=[])

    # TODO: this irreversibly adds the relation to the context, wrap
    derrel.resolve(syntax.the_program.scope)
    syntax.the_program.decls.append(
        derrel
    )  # TODO: hack! because RelationDecl.resolve only adds to prog.scope
    s.mutable_axioms.extend([
        def_axiom
    ])  # TODO: hack! currently we register these axioms only on solver init

    print("Trying derived relation:", derrel)

    # the new decrease_domain action incorporates restrictions that derived relations remain the same on active tuples
    new_decrease_domain = relaxed_traces.relaxation_action_def(
        syntax.the_program, fresh=False)
    new_prog = relaxed_traces.replace_relaxation_action(
        syntax.the_program, new_decrease_domain)
    new_prog.resolve()
    print(new_prog)

    syntax.the_program = new_prog

    trace_decl = next(syntax.the_program.traces())
    trns2_o = bmc_trace(new_prog, trace_decl, s,
                        lambda s, ks: logic.check_solver(s, ks, minimize=True))
    assert trns2_o is not None
    trns2 = cast(logic.Trace, trns2_o)
    print(trns2)
    print()
    print(
        trns2.as_state(relaxed_traces.first_relax_step_idx(trns2) +
                       1).rel_interp[derrel])
    assert not relaxed_traces.is_rel_blocking_relax(
        trns2, relaxed_traces.first_relax_step_idx(trns2),
        ([(v, str(syntax.safe_cast_sort(v.sort)))
          for v in free_vars], def_expr))

    diff_conjunctions = list(
        filter(
            lambda candidate: relaxed_traces.is_rel_blocking_relax(
                trns2, relaxed_traces.first_relax_step_idx(trns2),
                ([(v, str(syntax.safe_cast_sort(v.sort)))
                  for v in free_vars], def_expr)), diff_conjunctions))
    print("num candidate relations:", len(diff_conjunctions))
    for diffing_conjunction in diff_conjunctions:
        # print("relation:")
        # for conj in diffing_conjunction:
        #     print("\t %s" % str(conj))
        print(diffing_conjunction)

    print()

    assert False
示例#7
0
def sandbox(s: Solver) -> None:
    ####################################################################################
    # SANDBOX for playing with relaxed traces
    import pickle
    trns: logic.Trace = pickle.load(open("paxos_trace.p", "rb"))

    diff_conjunctions = relaxed_traces.derived_rels_candidates_from_trace(
        trns, [], 2, 3)

    print("num candidate relations:", len(diff_conjunctions))
    for diffing_conjunction in diff_conjunctions:
        # print("relation:")
        # for conj in diffing_conjunction:
        #     print("\t %s" % str(conj))
        print(diffing_conjunction[1])

    derrel_name = syntax.the_program.scope.fresh("nder")
    (free_vars, def_expr) = diff_conjunctions[0]
    def_axiom = syntax.Forall(
        tuple(free_vars),
        syntax.Iff(
            syntax.Apply(derrel_name,
                         tuple(syntax.Id(v.name) for v in free_vars)),
            # TODO: extract pattern
            def_expr))

    derrel = syntax.RelationDecl(
        name=derrel_name,
        arity=tuple(syntax.safe_cast_sort(var.sort) for var in free_vars),
        mutable=True,
        derived=def_axiom)

    # TODO: this irreversibly adds the relation to the context, wrap
    typechecker.typecheck_statedecl(syntax.the_program.scope, derrel)
    syntax.the_program.decls.append(
        derrel
    )  # TODO: hack! because typecheck_statedecl only adds to prog.scope
    s.mutable_axioms.extend([
        def_axiom
    ])  # TODO: hack! currently we register these axioms only on solver init

    print("Trying derived relation:", derrel)

    # the new decrease_domain action incorporates restrictions that derived relations remain the same on active tuples
    new_decrease_domain = relaxed_traces.relaxation_action_def(
        syntax.the_program, fresh=False)
    new_prog = relaxed_traces.replace_relaxation_action(
        syntax.the_program, new_decrease_domain)
    typechecker.typecheck_program(new_prog)
    print(new_prog)

    syntax.the_program = new_prog

    # TODO: recover this, making sure the candidate blocks the trace
    # trace_decl = next(syntax.the_program.traces())
    # trns2_o = bmc_trace(new_prog, trace_decl, s, lambda s, ks: logic.check_solver(s, ks, minimize=True))
    # assert trns2_o is None

    # migrated_trace = load_relaxed_trace_from_updr_cex(syntax.the_program, s)
    import pickle
    trns2_o = pickle.load(open("migrated_trace.p", "rb"))

    trns2 = cast(logic.Trace, trns2_o)
    print(trns2)
    print()
    assert not relaxed_traces.is_rel_blocking_relax(
        trns2, ([(v, str(syntax.safe_cast_sort(v.sort)))
                 for v in free_vars], def_expr))

    # for candidate in diff_conjunctions:
    #     print("start checking")
    #     print()
    #     if str(candidate[1]) == ('exists v0:node. member(v0, v1) & left_round(v0, v2) '
    #                              '& !vote(v0, v2, v3) & active_node(v0)'):
    #         print(candidate)
    #         assert False
    #         resush = relaxed_traces.is_rel_blocking_relax_step(
    #             trns2, 11,
    #             ([(v, str(syntax.safe_cast_sort(v.sort))) for v in candidate[0]],
    #              candidate[1]))
    #         # res2 = trns2.as_state(0).eval(syntax.And(*[i.expr for i in syntax.the_program.inits()]))
    #
    #         # resush = trns2.as_state(7).eval(syntax.And(*[i.expr for i in syntax.the_program.inits()]))
    #         print(resush)
    #         assert False
    # assert False

    diff_conjunctions = list(
        filter(
            lambda candidate: relaxed_traces.is_rel_blocking_relax(
                trns2, ([(v, str(syntax.safe_cast_sort(v.sort)))
                         for v in candidate[0]], candidate[1])),
            diff_conjunctions))
    print("num candidate relations:", len(diff_conjunctions))
    for diffing_conjunction in diff_conjunctions:
        # print("relation:")
        # for conj in diffing_conjunction:
        #     print("\t %s" % str(conj))
        print(diffing_conjunction[1])

    print()

    assert False
示例#8
0
def load_relaxed_trace_from_updr_cex(prog: Program, s: Solver) -> logic.Trace:
    import xml.dom.minidom  # type: ignore
    collection = xml.dom.minidom.parse(
        "paxos_derived_trace.xml").documentElement

    components: List[syntax.TraceComponent] = []

    xml_decls = reversed(collection.childNodes)
    seen_first = False

    for elm in xml_decls:
        if isinstance(elm, xml.dom.minidom.Text):  # type: ignore
            continue
        if elm.tagName == 'state':
            diagram = parser.parse_expr(elm.childNodes[0].data)
            typechecker.typecheck_expr(prog.scope, diagram, syntax.BoolSort)
            assert isinstance(
                diagram, syntax.QuantifierExpr) and diagram.quant == 'EXISTS'
            active_clauses = [
                relaxed_traces.active_var(v.name, str(v.sort))
                for v in diagram.get_vs()
            ]

            if not seen_first:
                # restrict the domain to be subdomain of the diagram's existentials
                seen_first = True
                import itertools  # type: ignore
                for sort, vars in itertools.groupby(
                        diagram.get_vs(),
                        lambda v: v.sort):  # TODO; need to sort first
                    free_var = syntax.SortedVar(
                        syntax.the_program.scope.fresh("v_%s" % str(sort)),
                        None)

                    # TODO: diagram simplification omits them from the exists somewhere
                    consts = list(
                        filter(lambda c: c.sort == sort, prog.constants()))
                    els: Sequence[Union[syntax.SortedVar, syntax.ConstantDecl]]
                    els = list(vars)
                    els += consts
                    restrict_domain = syntax.Forall(
                        (free_var, ),
                        syntax.Or(*(syntax.Eq(syntax.Id(free_var.name),
                                              syntax.Id(v.name))
                                    for v in els)))
                    active_clauses += [restrict_domain]

            diagram_active = syntax.Exists(
                diagram.get_vs(), syntax.And(diagram.body, *active_clauses))
            typechecker.typecheck_expr(prog.scope, diagram_active,
                                       syntax.BoolSort)

            components.append(syntax.AssertDecl(expr=diagram_active))
        elif elm.tagName == 'action':
            action_name = elm.childNodes[0].data.split()[0]
            tcall = syntax.TransitionCalls(
                calls=[syntax.TransitionCall(target=action_name, args=None)])
            components.append(syntax.TraceTransitionDecl(transition=tcall))
        else:
            assert False, "unknown xml tagName"

    trace_decl = syntax.TraceDecl(components=components, sat=True)
    migrated_trace = bmc_trace(
        prog,
        trace_decl,
        s,
        lambda s, ks: logic.check_solver(s, ks, minimize=True),
        log=False)

    assert migrated_trace is not None
    import pickle
    pickle.dump(migrated_trace, open("migrated_trace.p", "wb"))
    return migrated_trace
示例#9
0
def relaxation_action_def(prog: syntax.Program,
                          actives: Optional[Dict[syntax.SortDecl, syntax.RelationDecl]]=None,
                          fresh: bool=True)  \
                            -> syntax.DefinitionDecl:
    decrease_name = (prog.scope.fresh('decrease_domain') if fresh else 'decrease_domain')
    mods = []
    conjs: List[Expr] = []
    if actives is None:
        actives = active_rel_by_sort(prog)

    # a conjunct allowing each domain to decrease
    for sort in prog.sorts():
        name = prog.scope.fresh(sort.name[0].upper())
        ap = syntax.Apply(actives[sort].name, [syntax.Id(None, name)])
        expr = syntax.Forall([syntax.SortedVar(None, name, None)],
                             syntax.Implies(ap, syntax.Old(ap)))
        conjs.append(expr)
        mods.append(syntax.ModifiesClause(None, actives[sort].name))

    # constants are active
    for const in prog.constants():
        conjs.append(syntax.Apply(actives[syntax.get_decl_from_sort(const.sort)].name,
                                  [syntax.Id(None, const.name)]))

    # functions map active to active
    for func in prog.functions():
        names: List[str] = []
        func_conjs = []
        for arg_sort in func.arity:
            arg_sort_decl = syntax.get_decl_from_sort(arg_sort)
            name = prog.scope.fresh(arg_sort_decl.name[0].upper(),
                                    also_avoid=names)
            names.append(name)
            func_conjs.append(syntax.Apply(actives[arg_sort_decl].name, [syntax.Id(None, name)]))
        ap_func = syntax.Old(syntax.Apply(func.name, [syntax.Id(None, name) for name in names]))
        active_func = syntax.Apply(actives[syntax.get_decl_from_sort(func.sort)].name, [ap_func])
        conjs.append(syntax.Forall([syntax.SortedVar(None, name, None) for name in names],
                                   syntax.Implies(syntax.And(*func_conjs), active_func)))

    # (relativized) axioms hold after relaxation
    for axiom in prog.axioms():
        if not syntax.is_universal(axiom.expr):
            conjs.append(syntax.relativize_quantifiers(actives, axiom.expr))

    # derived relations have the same interpretation on the active domain
    for rel in prog.derived_relations():
        names = []
        rel_conjs = []
        for arg_sort in rel.arity:
            arg_sort_decl = syntax.get_decl_from_sort(arg_sort)
            name = prog.scope.fresh(arg_sort_decl.name[0].upper(),
                                    also_avoid=names)
            names.append(name)
            rel_conjs.append(syntax.Apply(actives[arg_sort_decl].name, [syntax.Id(None, name)]))
        ap_rel = syntax.Apply(rel.name, [syntax.Id(None, name) for name in names])
        conjs.append(syntax.Forall([syntax.SortedVar(None, name, None) for name in names],
                                   syntax.Implies(syntax.And(*rel_conjs),
                                                  syntax.Iff(ap_rel, syntax.Old(ap_rel)))))

    return syntax.DefinitionDecl(None, public=True, twostate=True, name=decrease_name,
                                           params=[], body=(mods, syntax.And(*conjs)))
def relaxed_program(prog: syntax.Program) -> syntax.Program:
    new_decls: List[syntax.Decl] = [d for d in prog.sorts()]

    actives: Dict[syntax.SortDecl, syntax.RelationDecl] = {}
    for sort in prog.sorts():
        name = prog.scope.fresh('active_' + sort.name)
        r = syntax.RelationDecl(name,
                                arity=(syntax.UninterpretedSort(sort.name), ),
                                mutable=True)
        actives[sort] = r
        new_decls.append(r)

    # active relations initial conditions: always true
    for sort in prog.sorts():
        name = prog.scope.fresh(sort.name[0].upper())
        expr = syntax.Forall((syntax.SortedVar(name, None), ),
                             syntax.Apply(actives[sort].name,
                                          (syntax.Id(name), )))
        new_decls.append(syntax.InitDecl(name=None, expr=expr))

    for d in prog.decls:
        if isinstance(d, syntax.SortDecl):
            pass  # already included above
        elif isinstance(d, syntax.RelationDecl):
            if d.derived_axiom is not None:
                expr = syntax.relativize_quantifiers(actives, d.derived_axiom)
                new_decls.append(
                    syntax.RelationDecl(d.name,
                                        d.arity,
                                        d.mutable,
                                        expr,
                                        annotations=d.annotations))
            else:
                new_decls.append(d)
        elif isinstance(d, syntax.ConstantDecl):
            new_decls.append(d)
        elif isinstance(d, syntax.FunctionDecl):
            new_decls.append(d)
        elif isinstance(d, syntax.AxiomDecl):
            new_decls.append(d)
        elif isinstance(d, syntax.InitDecl):
            new_decls.append(d)
        elif isinstance(d, syntax.DefinitionDecl):
            relativized_def = relativize_decl(d,
                                              actives,
                                              prog.scope,
                                              inline_relax_actives=False)
            new_decls.append(relativized_def)
        elif isinstance(d, syntax.InvariantDecl):
            expr = syntax.relativize_quantifiers(actives, d.expr)
            new_decls.append(
                syntax.InvariantDecl(d.name,
                                     expr=expr,
                                     is_safety=d.is_safety,
                                     is_sketch=d.is_sketch))
        else:
            assert False, d

    new_decls.append(relaxation_action_def(prog, actives=actives, fresh=True))

    res = syntax.Program(new_decls)
    typechecker.typecheck_program(res)  # #sorrynotsorry
    return res
def relaxation_action_def(prog: syntax.Program,
                          actives: Optional[Dict[syntax.SortDecl,
                                                 syntax.RelationDecl]] = None,
                          fresh: bool = True) -> syntax.DefinitionDecl:
    decrease_name = (prog.scope.fresh('decrease_domain')
                     if fresh else 'decrease_domain')
    mods: Tuple[syntax.ModifiesClause, ...] = ()
    conjs: List[Expr] = []
    if actives is None:
        actives = active_rel_by_sort(prog)

    # a conjunct allowing each domain to decrease
    new_mods, new_conjs = relax_actives_action_chunk(prog.scope, actives)
    mods += new_mods
    conjs += new_conjs

    # constants are active
    for const in prog.constants():
        conjs.append(
            syntax.New(
                syntax.Apply(
                    actives[syntax.get_decl_from_sort(const.sort)].name,
                    (syntax.Id(const.name), ))))

    # functions map active to active
    for func in prog.functions():
        names: List[str] = []
        func_conjs = []
        for arg_sort in func.arity:
            arg_sort_decl = syntax.get_decl_from_sort(arg_sort)
            name = prog.scope.fresh(arg_sort_decl.name[0].upper(),
                                    also_avoid=names)
            names.append(name)
            func_conjs.append(
                syntax.New(
                    syntax.Apply(actives[arg_sort_decl].name,
                                 (syntax.Id(name), ))))
        ap_func = syntax.Apply(func.name,
                               tuple(syntax.Id(name) for name in names))
        name = prog.scope.fresh('y', also_avoid=names)
        active_func = syntax.Let(
            syntax.SortedVar(name, func.sort), ap_func,
            syntax.New(
                syntax.Apply(
                    actives[syntax.get_decl_from_sort(func.sort)].name,
                    (syntax.Id(name), ))))
        conjs.append(
            syntax.Forall(
                tuple(syntax.SortedVar(name, None) for name in names),
                syntax.Implies(syntax.And(*func_conjs), active_func)))

    # (relativized) axioms hold after relaxation
    for axiom in prog.axioms():
        if not syntax.is_universal(axiom.expr):
            conjs.append(syntax.relativize_quantifiers(actives, axiom.expr))

    # derived relations have the same interpretation on the active domain
    for rel in prog.derived_relations():
        names = []
        rel_conjs = []
        for arg_sort in rel.arity:
            arg_sort_decl = syntax.get_decl_from_sort(arg_sort)
            name = prog.scope.fresh(arg_sort_decl.name[0].upper(),
                                    also_avoid=names)
            names.append(name)
            rel_conjs.append(
                syntax.Apply(actives[arg_sort_decl].name, (syntax.Id(name), )))
        ap_rel = syntax.Apply(rel.name,
                              tuple(syntax.Id(name) for name in names))
        conjs.append(
            syntax.Forall(
                tuple(syntax.SortedVar(name, None) for name in names),
                syntax.Implies(syntax.And(*rel_conjs),
                               syntax.Iff(syntax.New(ap_rel), ap_rel))))

    return syntax.DefinitionDecl(is_public_transition=True,
                                 num_states=2,
                                 name=decrease_name,
                                 params=(),
                                 mods=mods,
                                 expr=syntax.And(*conjs))