0

This question is a follow up to the following question.

Procedural Attachment in Z3

I have a predicate (I use the name "heavier" in this case) over two integers that I need to evaluate using a custom algorithm. I have written the following piece of code to do it. But I see that the parameters that get passed into the function CMTh_reduce_app() are not actual integers, but consts of type integer. What I need is 2 integers, so that I can evaluate the predicate and return the result (The operations done in the function CMTh_reduce_app() right now are meaningless).

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdarg.h>
#include<memory.h>
#include<z3.h>
#include "z3++.h"
#include<iostream>

using namespace z3;
using namespace std;

struct _CMTheoryData {

    Z3_func_decl heavier;
  };


typedef struct _CMTheoryData CMTheoryData;
Z3_context ctx;
//Exit function
void exitf(const char* message) 
{
  fprintf(stderr,"BUG: %s.\n", message);
  exit(1);
}

//Check and print model if available
void check(Z3_context ctx)
{
    Z3_model m      = 0;
    Z3_lbool result = Z3_check_and_get_model(ctx, &m);
    switch (result) {
    case Z3_L_FALSE:
        printf("unsat\n");
        break;
    case Z3_L_UNDEF:
        printf("unknown\n");
        printf("potential model:\n%s\n", Z3_model_to_string(ctx, m));
        break;
    case Z3_L_TRUE:
        printf("sat\n%s\n", Z3_model_to_string(ctx, m));
        break;
    }
    if (m) {
        Z3_del_model(ctx, m);
    }

}

//Create logical context. Enable model generation, and set error handler

void error_handler(Z3_error_code e) 
{
    printf("Error code: %d\n", e);
    exitf("incorrect use of Z3");
}

Z3_context mk_context_custom(Z3_config cfg, Z3_error_handler err) 
{
    Z3_context ctx;

    Z3_set_param_value(cfg, "MODEL", "true");
    ctx = Z3_mk_context(cfg);
#ifdef TRACING
    Z3_trace_to_stderr(ctx);
#endif
    Z3_set_error_handler(ctx, err);

    return ctx;
}

Z3_context mk_context() 
{
    Z3_config  cfg;
    Z3_context ctx;
    cfg = Z3_mk_config();
    ctx = mk_context_custom(cfg, error_handler);
    Z3_del_config(cfg);
    return ctx;
}

//Shortcut for binary fn application
Z3_ast mk_binary_app(Z3_context ctx, Z3_func_decl f, Z3_ast x, Z3_ast y) 
{
    Z3_ast args[2] = {x, y};
    return Z3_mk_app(ctx, f, 2, args);
}

//Shortcut to create an int
Z3_ast mk_int(Z3_context ctx, int v) 
{
    Z3_sort ty = Z3_mk_int_sort(ctx);
    return Z3_mk_int(ctx, v, ty);
}

Z3_ast mk_var(Z3_context ctx, const char * name, Z3_sort ty) 
{
    Z3_symbol   s  = Z3_mk_string_symbol(ctx, name);
    return Z3_mk_const(ctx, s, ty);
}

Z3_ast mk_int_var(Z3_context ctx, const char * name) 
{
    Z3_sort ty = Z3_mk_int_sort(ctx);
    return mk_var(ctx, name, ty);
}



//Callback when final check is to be carried out
Z3_bool CMTh_final_check(Z3_theory t) {
    printf("Final check\n");
    return Z3_TRUE;
}

//Callback when theory is to be deleted
void CMTh_delete(Z3_theory t) {
    CMTheoryData * td = (CMTheoryData *)Z3_theory_get_ext_data(t);
    printf("Delete\n");
    free(td);
}

//Callback to reduce a function application(definition of custom functions, predicates)
Z3_bool CMTh_reduce_app(Z3_theory t, Z3_func_decl d, unsigned n, Z3_ast const args[], Z3_ast * result) {

    CMTheoryData * td = (CMTheoryData*)Z3_theory_get_ext_data(t);
    cout<<Z3_ast_to_string(ctx, args[0])<<' '<<Z3_ast_to_string(ctx,args[1])<<endl;
    if (d == td->heavier) {
        cout<<"Reducing the fn \'heavier\'"<<endl;
        if(Z3_is_eq_ast(ctx,mk_int(ctx, 1),args[0])||Z3_is_eq_ast(ctx,mk_int(ctx,2),args[0]))
        {

            *result = Z3_mk_true(Z3_theory_get_context(t));
            return Z3_TRUE;;
        }
        else
        {

            *result = Z3_mk_false(Z3_theory_get_context(t));
            return Z3_TRUE;;
        }
    }

    return Z3_FALSE; // failed to simplify

}



Z3_theory mk_cm_theory(Z3_context ctx) {
    Z3_sort heavier_domain[2];
    Z3_symbol heavier_name    = Z3_mk_string_symbol(ctx, "heavier");
    Z3_sort B             = Z3_mk_bool_sort(ctx);
    CMTheoryData * td = (CMTheoryData*)malloc(sizeof(CMTheoryData));  
    Z3_theory Th          = Z3_mk_theory(ctx, "cm_th", td);
    heavier_domain[0] = Z3_mk_int_sort(ctx); 
    heavier_domain[1] = Z3_mk_int_sort(ctx);
    td->heavier           = Z3_theory_mk_func_decl(ctx, Th, heavier_name, 2, heavier_domain, B); //context, theory, name_of_fn, number of arguments, argument type list, return type
    Z3_set_delete_callback(Th, CMTh_delete);
    Z3_set_reduce_app_callback(Th, CMTh_reduce_app);
    Z3_set_final_check_callback(Th, CMTh_final_check);
    return Th;
}

main()
{
    Z3_ast a_ast, b_ast, c_ast, f1, f3, r;

    Z3_sort i;
    Z3_pattern p;
    Z3_app bound[2];

    Z3_theory Th;
    CMTheoryData * td;
    printf("\nCustom theory example\n");
    ctx = mk_context();
    Th = mk_cm_theory(ctx);
    td = (CMTheoryData*)Z3_theory_get_ext_data(Th);

    a_ast  = mk_int_var(ctx, "a");
    b_ast  = mk_int_var(ctx, "b");

    bound[0] = (Z3_app)a_ast;


    f1=mk_binary_app(ctx, td->heavier, a_ast, b_ast);

    r= Z3_mk_exists_const(ctx, 0, 1, bound, 0, 0,f1);

    printf("assert axiom:\n%s\n", Z3_ast_to_string(ctx, r));
    Z3_assert_cnstr(ctx, r);  
    check(ctx);


}

I know the user theory plugin is not supported anymore, but I really need to get this working, so if I could get any information, it would be really helpful. I tried looking at the source code, but I didn't know where to get started with building new theories into it. So, I'd appreciate some help with the theory plugin.

Community
  • 1
  • 1
SPMP
  • 1,181
  • 1
  • 9
  • 24

1 Answers1

0

Models are not going to be accessible to you from the abstraction that the deprecated theory plugin provides. The problem is going to be that models are constructed later in the game. It would require rewriting some of the internals to accommodate this (it is not impossible, but a very fair chunk of work).

My impression is that it would be simpler to use just the basic interaction with Z3 where you declare the predicates as uninterpreted, check for SAT. Then if the current constraints are satisfiable, use the current model to evaluate arguments. If you have values, that contradict your built-in procedural attachment, then assert new facts that rule these values out (and as many other infeasible values as possible). I call this the "lazy loop approach". This interaction model corresponds to how SMT solvers can use SAT solvers without providing theory propagation (propagating truth values when new atoms are assigned). You would have to do a bit more work during conflict analysis/resolution in order to produce strong lemmas. So a hybrid between the built-in theory and the lazy loop approach may in the end work out. But before getting there I suggest to just use Z3 as is and use the current model to calculate new blocking clauses. Of course you lose something: instantiation of quantifiers will proceed somewhat eagerly and it could very well be the case that this lazy loop approach will not work well in the presence of quantifiers.

Nikolaj Bjorner
  • 8,229
  • 14
  • 15
  • Hi,Thanks for the answer. I'll try out the lazy loop approach. I am guessing incremental solving should work best here. I read in some of the other answers that future versions of Z3 might have a full fledged user theory plugin. Is it likely that we see it soon? – SPMP Jan 23 '14 at 19:16