3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-05-03 22:05:45 +00:00

Add simplification customization for SMTLIB2

Add the ability to customize incremental pre-processing simplification for the SMTLIB2 front-end. The main new capability is to use pre-processing tactics in incremental mode that were previously not available. The main new capabilities are
- solve-eqs
- reduce-args
- elim-unconstrained
There are several more. Documentation and exposed simplifiers are populated incrementally. The current set of supported simplifiers can be inspected by using z3 with the --simplifiers flag or referring to https://microsoft.github.io/z3guide/docs/strategies/simplifiers

Some pending features are:
- add the ability to update parameters to simplifiers similar to how tactics can be controlled using parameters.
- expose simplification solvers over the binary API.
This commit is contained in:
Nikolaj Bjorner 2023-01-30 22:38:51 -08:00
parent dd0decfe5d
commit 6022c17131
32 changed files with 370 additions and 69 deletions

View file

@ -0,0 +1,158 @@
/*++
Copyright (c) 2023 Microsoft Corporation
Module Name:
simplifier_cmds.h
Abstract:
Support for simplifier commands in SMT 2.0 front-end
Author:
Nikolaj Bjorner (nbjorner) 2023-01-30
--*/
#include<sstream>
#include "cmd_context/simplifier_cmds.h"
#include "cmd_context/cmd_context.h"
#include "cmd_context/cmd_util.h"
#include "cmd_context/parametric_cmd.h"
#include "model/model_smt2_pp.h"
#include "ast/ast_smt2_pp.h"
#include "ast/simplifiers/seq_simplifier.h"
#include "solver/simplifier_solver.h"
typedef dependent_expr_simplifier simplifier;
static simplifier_factory mk_and_then(cmd_context & ctx, sexpr * n) {
SASSERT(n->is_composite());
unsigned num_children = n->get_num_children();
if (num_children < 2)
throw cmd_exception("invalid and-then combinator, at least one argument expected", n->get_line(), n->get_pos());
if (num_children == 2)
return sexpr2simplifier(ctx, n->get_child(1));
vector<simplifier_factory> args;
for (unsigned i = 1; i < num_children; i++)
args.push_back(sexpr2simplifier(ctx, n->get_child(i)));
simplifier_factory result = [args](ast_manager& m, const params_ref& p, dependent_expr_state& st) {
scoped_ptr<seq_simplifier> s = alloc(seq_simplifier, m, p, st);
for (auto & simp : args)
s->add_simplifier(simp(m, p, st));
return s.detach();
};
return result;
}
static simplifier_factory mk_using_params(cmd_context & ctx, sexpr * n) {
SASSERT(n->is_composite());
unsigned num_children = n->get_num_children();
if (num_children < 2)
throw cmd_exception("invalid using-params combinator, at least one argument expected", n->get_line(), n->get_pos());
if (num_children == 2)
return sexpr2simplifier(ctx, n->get_child(1));
simplifier_factory t = sexpr2simplifier(ctx, n->get_child(1));
#if 0
// hoist parameter parsing code from tactic_cmds to share.
param_descrs descrs;
t->collect_param_descrs(descrs);
#endif
return t;
// return using_params(t.detach(), p);
}
simplifier_factory sexpr2simplifier(cmd_context & ctx, sexpr * n) {
if (n->is_symbol()) {
simplifier_cmd * cmd = ctx.find_simplifier_cmd(n->get_symbol());
if (cmd != nullptr)
return cmd->factory();
throw cmd_exception("invalid tactic, unknown tactic ", n->get_symbol(), n->get_line(), n->get_pos());
}
else if (n->is_composite()) {
unsigned num_children = n->get_num_children();
if (num_children == 0)
throw cmd_exception("invalid tactic, arguments expected", n->get_line(), n->get_pos());
sexpr * head = n->get_child(0);
if (!head->is_symbol())
throw cmd_exception("invalid tactic, symbol expected", n->get_line(), n->get_pos());
symbol const & cmd_name = head->get_symbol();
if (cmd_name == "and-then" || cmd_name == "then")
return mk_and_then(ctx, n);
else
throw cmd_exception("invalid tactic, unknown tactic combinator ", cmd_name, n->get_line(), n->get_pos());
}
else {
throw cmd_exception("invalid tactic, unexpected input", n->get_line(), n->get_pos());
}
}
void help_simplifier(cmd_context & ctx) {
std::ostringstream buf;
buf << "combinators:\n";
buf << "- (and-then <simplifier>+) executes the given simplifiers sequentially.\n";
// buf << "- (using-params <tactic> <attribute>*) executes the given tactic using the given attributes, where <attribute> ::= <keyword> <value>. ! is a syntax sugar for using-params.\n";
buf << "builtin simplifiers:\n";
for (simplifier_cmd* cmd : ctx.simplifiers()) {
buf << "- " << cmd->get_name() << " " << cmd->get_descr() << "\n";
auto fac = cmd->factory();
param_descrs descrs;
ast_manager& m = ctx.get_ast_manager();
default_dependent_expr_state st(m);
params_ref p;
scoped_ptr<dependent_expr_simplifier> s = fac(m, p, st);
s->collect_param_descrs(descrs);
descrs.display(buf, 4);
}
ctx.regular_stream() << '"' << escaped(buf.str()) << "\"\n";
}
ATOMIC_CMD(help_simplifier_cmd, "help-simplifier", "display the simplifier combinators and primitives.", help_simplifier(ctx););
class set_simplifier_cmd : public parametric_cmd {
protected:
sexpr * m_simplifier;
public:
set_simplifier_cmd():
parametric_cmd("set-simplifier") {}
char const * get_usage() const override { return "<tactic> (<keyword> <value>)*"; }
void prepare(cmd_context & ctx) override {
parametric_cmd::prepare(ctx);
m_simplifier = nullptr;
}
cmd_arg_kind next_arg_kind(cmd_context & ctx) const override {
if (m_simplifier == nullptr) return CPK_SEXPR;
return parametric_cmd::next_arg_kind(ctx);
}
void set_next_arg(cmd_context & ctx, sexpr * arg) override {
m_simplifier = arg;
}
char const * get_main_descr() const override { return "update main solver with simplification pre-processing."; }
void init_pdescrs(cmd_context & ctx, param_descrs & p) override {
}
void execute(cmd_context & ctx) override {
if (!m_simplifier)
throw cmd_exception("set-simplifier needs a simplifier argument");
auto simplifier_factory = sexpr2simplifier(ctx, m_simplifier);
ctx.init_manager();
auto* s = ctx.get_solver();
if (s)
ctx.set_solver(mk_simplifier_solver(s, &simplifier_factory));
}
};
void install_core_simplifier_cmds(cmd_context & ctx) {
ctx.insert(alloc(set_simplifier_cmd));
ctx.insert(alloc(help_simplifier_cmd));
}