0

Similar (but somewhat opposite) to this SO question, I do want to expose randomness if possible. That is, I want the two consecutive queries to provide different results. Is that possible? Here is my code:

void oren_example()
{
    int i;

    // context + solver
    context ctx;
    solver solver(ctx);

    // sorts
    sort int_sort     = ctx.int_sort();
    sort seq_int_sort = ctx.seq_sort(int_sort);
    sort bool_sort    = ctx.bool_sort();

    // constants
    expr two   = ctx.int_val(2);
    expr five  = ctx.int_val(5);
    expr four  = ctx.int_val(4);
    expr three = ctx.int_val(3);

    // define State sort
    const char *names[4]={"x","A","b","n"};
    sort sorts[4]={int_sort,seq_int_sort,bool_sort,int_sort};
    func_decl_vector projs(ctx);
    sort state_sort = ctx.tuple_sort("State",4,names,sorts,projs).range();

    // define an arbitrary state sigma
    expr sigma = ctx.constant("sigma",state_sort);

    // define some predicate on the state
    func_decl init = function("init",state_sort,bool_sort);
    solver.add(forall(sigma,
        init(sigma) == (
            ((projs[0](sigma))          == two  ) &&
            ((projs[1](sigma).length()) == three) &&
            ((projs[1](sigma).nth(two)) == five ) &&
            ((projs[3](sigma))          == five ))));

    for (int k=0;k<2;k++)
    {
        // create a snapshot
        solver.push();

        // find an initial state
        solver.add(init(sigma));

        // check sat + get model
        if (solver.check() == sat)
        {
            model m = solver.get_model();
            std::cout << "x = " << m.eval(projs[0](sigma)) << "\n";
            std::cout << "A = " << m.eval(projs[1](sigma)) << "\n";
            std::cout << "b = " << m.eval(projs[2](sigma)) << "\n";
            std::cout << "n = " << m.eval(projs[3](sigma)) << "\n";

            int size = m.eval(projs[1](sigma).length()).get_numeral_int();
            std::vector<int> A;
            for (i=0;i<size;i++)
            {
                A.push_back(
                    m.eval(
                        projs[1](sigma).nth(
                            ctx.int_val(i))).get_numeral_int());
            }
            std::cout << "A = { ";
            for (i=0;i<size;i++)
            {
                std::cout << A[i] << " ";
            }
            std::cout << "}\n";
        }

        // restore snapshot
        solver.pop();
    }
}

And the results are the same:

x = 2
A = (seq.++ (seq.unit 6) (seq.unit 7) (seq.unit 5))
b = false
n = 5
A = { 6 7 5 }
x = 2
A = (seq.++ (seq.unit 6) (seq.unit 7) (seq.unit 5))
b = false
n = 5
A = { 6 7 5 } // ideally this would be different than { 6 7 5 } ...

Posted now to GitHub/Z3/issues

OrenIshShalom
  • 5,974
  • 9
  • 37
  • 87

1 Answers1

1

This is typically done by adding constraints to outlaw previous models. Note that unless you add new constraints, you will not get a new solution.

If you simply want to rely on randomness after solving from scratch, try setting random seeds used by z3. There are a few of them:

$ z3 -pd | grep seed
    random_seed (unsigned int) random seed (default: 0)
    seed (unsigned int) random seed. (default: 0)
    spacer.random_seed (unsigned int) Random seed to be used by SMT solver (default: 0)
    random_seed (unsigned int) random seed for the smt solver (default: 0)
    random_seed (unsigned int) random seed (default: 0)

whether altering these will give you a significantly different (or different at all) model will depend on your initial set of constraints and which theory solvers come into play.

To set these from the C++ API, use the set_param function. Here's how you'd set them:

set_param("sat.random_seed", 50);
set_param("smt.random_seed", 50);

If you run z3 -pd it'll list all the settings you can give, per module. You can dump it to a file (z3 -pd > settings), then look at the created settings file for names that include seed to find which ones there are. Note that you have to prefix the actual names with the module they are in, as in the above example with sat and smt. You'll also find the module names in the z3 -pd output.

alias
  • 28,120
  • 2
  • 23
  • 40
  • should I set some flag? I looked up flags with `z3 --help` ... I am unfamiliar with `z3 -pd` .. what is it? – OrenIshShalom Jun 01 '20 at 08:04
  • 1
    I added an example on how to do that using the C/C++ api. – alias Jun 01 '20 at 08:13
  • I tried it, but like you suspected, I get the **same** results ... any other trick? I can keep a history of found states and make the next one different from all of them, but that seems very inefficient ... – OrenIshShalom Jun 01 '20 at 08:24
  • 2
    Adding the negation of a model is the usual approach to extract multiple models. Z3 does not support any kind of proper randomness, i.e. there is no guarantee over the distribution of solutions and even after changing the random seed you will frequently get the same solutions (e.g. if the preprocessor solves the problem, it never needs a (pseudo-)random number). – Christoph Wintersteiger Jun 01 '20 at 09:37