Z3优化器使用C ++ API

时间:2016-07-14 15:28:53

标签: c++ optimization z3

尝试使用Z3优化器解决图分区问题时,我遇到了问题。具体来说,下面的代码将无法产生令人满意的模型:

namespace z3 {
    expr ite(context& con, expr cond, expr then_, expr else_) {
        return to_expr(con, Z3_mk_ite(con, cond, then_, else_));;
    }
}

bool smtPart(void) {

    // Graph setup

    vector<int32_t> nodes = {{ 4, 2, 1, 1 }};
    vector<tuple<node_pos_t, node_pos_t, int32_t>> edges;
    GraphType graph(nodes, edges);

    // Z3 setup
    z3::context con;
    z3::optimize opt(con);
    string n_str = "n", sub_p_str = "_p";

    // Re-usable constants
    z3::expr zero = con.int_val(0);

    // Create the sort representing the different partitions.

    const char* part_sort_names[2] = { "P0", "P1" };
    z3::func_decl_vector part_consts(con), part_preds(con);

    z3::sort part_sort =
        con.enumeration_sort("PartID",
                             2,
                             part_sort_names,
                             part_consts,
                             part_preds);

    // Create the constants that represent partition choices.

    vector<z3::expr> part_vars;
    part_vars.reserve(graph.numNodes());

    z3::expr p0_acc = zero,
             p1_acc = zero;

    typename GraphType::NodeData total_weight = typename GraphType::NodeData();
    for (const auto& node : graph.nodes()) {
        total_weight += node.data;

        ostringstream name;
        name << n_str << node.id << sub_p_str;

        z3::expr nchoice = con.constant(name.str().c_str(), part_sort);
        part_vars.push_back(nchoice);

        p0_acc = p0_acc + z3::ite(con,
                                  nchoice == part_consts[0](), 
                                  con.int_val(node.data),
                                  zero);

        p1_acc = p1_acc + z3::ite(con,
                                  nchoice == part_consts[1](),
                                  con.int_val(node.data),
                                  zero);
    }

    z3::expr imbalance = con.int_const("imbalance");
    opt.add(imbalance ==
            z3::ite(con,
                    p0_acc > p1_acc,
                    p0_acc - p1_acc,
                    p1_acc - p0_acc));

    z3::expr imbalance_limit = con.real_val(total_weight, 100);
    opt.add(imbalance <= imbalance_limit);

    z3::expr edge_cut = zero;
    for(const auto& edge : graph.edges()) {
        edge_cut = edge_cut +
                   z3::ite(con,
                           (part_vars[edge.node0().pos()] ==
                              part_vars[edge.node1().pos()]),
                           zero,
                           con.int_val(edge.data));
    }

    opt.minimize(edge_cut);
    opt.minimize(imbalance);

    z3::check_result opt_result = opt.check();

    if (opt_result == z3::check_result::sat) {
        auto mod = opt.get_model();

        size_t node_id = 0;
        for (z3::expr& npv : part_vars) {
            cout << "Node " << node_id++ << ": " << mod.eval(npv) << endl;
        }

        return true;
    } else if (opt_result == z3::check_result::unsat) {
        cerr << "Constraints are unsatisfiable." << endl;
        return false;
    } else {
        cerr << "Result is unknown." << endl;
        return false;
    }
}

如果我删除minimize命令并使用solver而不是optimize,则会找到令人满意的0不平衡模型。如果我:

,我也可以获得optimize来找到令人满意的模型
  1. 删除约束imbalance <= imbalance_limit

  2. 使不平衡限制可以缩小为整数。在此示例中,总重量为8.如果不平衡限制设置为8 / 1,8 / 8,8 / 4或8/8,优化器将找到令人满意的模型。

  3. 我试过to_real(imbalance) <= imbalance_limit无济于事。我还考虑过Z3使用错误逻辑的可能性(不包括实数理论)但我还没有找到使用C / C ++ API设置的方法。

    如果有人能告诉我为什么优化器在存在实值约束的情况下失败,或者可能建议改进我的编码,那将非常感激。提前谢谢。

2 个答案:

答案 0 :(得分:0)

您是否可以使用opt.to_string()转储状态(就在check()之前)来重现结果?这将创建一个使用优化命令在SMT-LIB2中格式化的字符串。然后更容易交换基准。您应该看到它报告的优化命令不满意,如果您注释掉优化命令,则会坐下来。

如果您能够制作错误,请使用repro在GitHub.com/z3prover/z3.git上发布问题。

如果没有,您可以在创建z3上下文并记录可重新运行的日志文件之前使用Z3_open_log。有可能(但不是那么容易)以这种方式挖掘不健全的错误。

答案 1 :(得分:0)

事实证明这是Z3中的一个错误。我在GitHub上创建了一个Issue,然后他们回复了补丁。我现在正在编译和测试修复程序,但我希望它可以正常工作。

编辑:是的,该补丁修复了命令行工具和C ++ API的问题。