mirror of
https://github.com/Z3Prover/z3
synced 2025-10-04 23:13:57 +00:00
Sls (#7439)
* reorg sls * sls * na * split into base and plugin * move sat_params to params directory, add op_def repair options * move sat_ddfw to sls, initiate sls-bv-plugin * porting bv-sls * adding basic plugin * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * add sls-sms solver * bv updates * updated dependencies Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * updated dependencies Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * use portable ptr-initializer Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * move definitions to cpp Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * use template<> syntax Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix compiler errors for gcc Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * Bump docker/build-push-action from 6.0.0 to 6.1.0 (#7265) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.0.0 to 6.1.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.0.0...v6.1.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * set clean shutdown for local search and re-enable local search when it parallelizes with PB solver Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * Bump docker/build-push-action from 6.1.0 to 6.2.0 (#7269) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.1.0 to 6.2.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.1.0...v6.2.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix a comment for Z3_solver_from_string (#7271) Z3_solver_from_string accepts a string buffer with solver assertions, not a string buffer with filename. * trigger the build with a comment change Signed-off-by: Lev Nachmanson <levnach@hotmail.com> * remove macro distinction #7270 * fix #7268 * kludge to address #7232, probably superseeded by planned revision to setup/pypi Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * add new ema invariant (#7288) * Bump docker/build-push-action from 6.2.0 to 6.3.0 (#7280) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.2.0 to 6.3.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v6.2.0...v6.3.0) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * merge Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix unit test build Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove shared attribute Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove stale files Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix build of unit test Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixes and rename sls-cc to sls-euf-plugin Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * testing / debugging arithmetic * updates to repair logic, mainly arithmetic * fixes to sls * evolve sls arith * bugfixes in sls-arith * fix typo Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * bug fixes * Update sls_test.cpp * fixes * fixes Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix build Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * refactor basic plugin and clause generation Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixes to ite and other Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * updates * update Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix division by 0 Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * disable fail restart Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * disable tabu when using reset moves Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * update sls_test Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * add factoring Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixes to semantics Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * re-add tabu override Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * generalize factoring Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix bug Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove restart Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * disable tabu in fallback modes Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * localize impact of factoring Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * delay factoring Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * flatten products Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * perform lookahead update + nested mul Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * disable nested mul Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * disable nested mul, use non-lookahead Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * make reset updates recursive Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * include linear moves Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * include 5% reset probability Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * separate linear update Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * separate linear update remove 20% threshold Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove linear opt Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * enable multiplier expansion, enable linear move Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * use unit coefficients for muls Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * disable non-tabu version of find_nl_moves Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove coefficient from multiplication definition Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * reorg monomials Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * add smt params to path Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * avoid negative reward Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * use reward as proxy for score Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * use reward as proxy for score Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * use exponential decay with breaks Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * use std::pow Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixes to bv Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixes to fixed Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixup repairs Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * reserve for multiplication Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixing repair Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * include bounds checks in set random * na * fixes to mul Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix mul inverse Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixes to handling signed operators Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * logging and fixes Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * gcm Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * peli Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * Add .env to gitignore to prevent environment files from being tracked * Add m_num_pelis counter to stats in sls_context * Remove m_num_pelis member from stats struct in sls_context * Enhance bv_sls_eval with improved repair and logging, refine is_bv_predicate in sls_bv_plugin * Remove verbose logging in register_term function of sls_basic_plugin and fix formatting in sls_context * Rename source files for consistency in `src/ast/sls` directory * Refactor bv_sls files to sls_bv with namespace and class name adjustments * Remove typename from member declarations in bv_fixed class * fixing conca Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * Add initial implementation of bit-vector SLS evaluation module in bv_sls_eval.cpp * Remove bv_sls_eval.cpp as part of code cleanup and refactoring * Refactor alignment of member variables in bv_plugin of sls namespace * Rename SLS engine related files to reflect their specific use for bit-vectors * Refactor SLS engine and evaluator components for bit-vector specifics and adjust memory manager alignment * Enhance bv_eval with use_current, lookahead strategies, and randomization improvements in SLS module * Refactor verbose logging and fix logic in range adjustment functions in sls bv modules * Remove commented verbose output in sls_bv_plugin.cpp during repair process * Add early return after setting fixed subterms in sls_bv_fixed.cpp * Remove redundant return statement in sls_bv_fixed.cpp * fixes to new value propagation Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * Refactor sls bv evaluation and fix logic checks for bit operations * Add array plugin support and update bv_eval in ast_sls module * Add array, model value, and user sort plugins to SLS module with enhancements in array propagation logic * Refactor array_plugin in sls to improve handling of select expressions with multiple arguments * Enhance array plugin with early termination and propagation verification, and improve euf and user sort plugins with propagation adjustments and debugging enhancements * Add support for handling 'distinct' expressions in SLS context and user sort plugin * Remove model value and user sort plugins from SLS theory * replace user plugin by euf plugin Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove extra file Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * Refactor handling of term registration and enhance distinct handling in sls_euf_plugin * Add TODO list for enhancements in sls_euf_plugin.cpp * add incremental mode * updated package * fix sls build Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * break sls build Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix build * break build again * fix build Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixes Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixing incremental Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * avoid units Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixup handling of disequality propagation Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fx Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * recover shift-weight loop Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * alternate Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * throttle save model Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * allow for alternating Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix test for new signature of flip Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * bug fixes Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * restore use of value_hash Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixes Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * adding dt plugin Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * adt Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * dt updates Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * added cycle detection Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * updated sls-datatype Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * Refactor context management, improve datatype handling, and enhance logging in sls plugins. * axiomatize dt Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * add missing factory plugins to model Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixup finite domain search Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixup finite domain search Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixes Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * redo dfs Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixing model construction for underspecified operators Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixes to occurs check Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixup interpretation building Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * saturate worklist Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * delay distinct axiom Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * adding model-based sls for datatatypes * update the interface in sls_solver to transfer phase between SAT and SLS * add value transfer option Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * rename aux functions * Track shared variables using a unit set * debugging parallel integration * fix dirty flag setting * update log level * add plugin to smt_context, factor out sls_smt_plugin functionality. * bug fixes * fixes * use common infrastructure for sls-smt * fix build Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix build Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * remove declaration of context Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * add virtual destructor Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * build warnings Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * reorder inclusion order to define smt_context before theory_sls Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * change namespace for single threaded Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * check delayed eqs before nla Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * use independent completion flag for sls to avoid conflating with genuine cancelation * validate sls-arith lemmas Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * bugfixes Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * add intblast to legacy SMT solver * fixup model generation for theory_intblast Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * na Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * mk_value needs to accept more cases where integer expression doesn't evalate Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * use th-axioms to track origins of assertions Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * add missing operator handling for bitwise operators Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * add missing operator handling for bitwise operators Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * normalizing inequality Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * add virtual destructor Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * rework elim_unconstrained * fix non-termination Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * use glue as computed without adjustment * update model generation to fix model bug Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fixes to model construction * remove package and package lock Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * fix build warning Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> * use original gai Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> --------- Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: Lev Nachmanson <levnach@hotmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sergey Bronnikov <estetus@gmail.com> Co-authored-by: Lev Nachmanson <levnach@hotmail.com> Co-authored-by: LiviaSun <33578456+ChuyueSun@users.noreply.github.com>
This commit is contained in:
parent
ecdfab81a6
commit
91dc02d862
120 changed files with 11172 additions and 4148 deletions
|
@ -15,7 +15,7 @@ z3_add_component(sat
|
|||
sat_config.cpp
|
||||
sat_cut_simplifier.cpp
|
||||
sat_cutset.cpp
|
||||
sat_ddfw.cpp
|
||||
sat_ddfw_wrapper.cpp
|
||||
sat_drat.cpp
|
||||
sat_elim_eqs.cpp
|
||||
sat_elim_vars.cpp
|
||||
|
@ -43,7 +43,6 @@ z3_add_component(sat
|
|||
params
|
||||
PYG_FILES
|
||||
sat_asymm_branch_params.pyg
|
||||
sat_params.pyg
|
||||
sat_scc_params.pyg
|
||||
sat_simplifier_params.pyg
|
||||
)
|
||||
|
|
|
@ -16,9 +16,9 @@ Author:
|
|||
Revision History:
|
||||
|
||||
--*/
|
||||
#include "params/sat_params.hpp"
|
||||
#include "sat/sat_config.h"
|
||||
#include "sat/sat_types.h"
|
||||
#include "sat/sat_params.hpp"
|
||||
#include "sat/sat_simplifier_params.hpp"
|
||||
#include "params/solver_params.hpp"
|
||||
|
||||
|
|
|
@ -1,686 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2019 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
sat_ddfw.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
DDFW Local search module for clauses
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner, Marijn Heule 2019-4-23
|
||||
|
||||
|
||||
Notes:
|
||||
|
||||
http://www.ict.griffith.edu.au/~johnt/publications/CP2006raouf.pdf
|
||||
|
||||
|
||||
Todo:
|
||||
- rephase strategy
|
||||
- experiment with backoff schemes for restarts
|
||||
- parallel sync
|
||||
--*/
|
||||
|
||||
#include "util/luby.h"
|
||||
#include "sat/sat_ddfw.h"
|
||||
#include "sat/sat_solver.h"
|
||||
#include "sat/sat_params.hpp"
|
||||
|
||||
namespace sat {
|
||||
|
||||
ddfw::~ddfw() {
|
||||
for (auto& ci : m_clauses)
|
||||
m_alloc.del_clause(ci.m_clause);
|
||||
}
|
||||
|
||||
lbool ddfw::check(unsigned sz, literal const* assumptions, parallel* p) {
|
||||
init(sz, assumptions);
|
||||
flet<parallel*> _p(m_par, p);
|
||||
if (m_plugin)
|
||||
check_with_plugin();
|
||||
else
|
||||
check_without_plugin();
|
||||
remove_assumptions();
|
||||
log();
|
||||
return m_min_sz == 0 ? l_true : l_undef;
|
||||
}
|
||||
|
||||
void ddfw::check_without_plugin() {
|
||||
while (m_limit.inc() && m_min_sz > 0) {
|
||||
if (should_reinit_weights()) do_reinit_weights();
|
||||
else if (do_flip<false>());
|
||||
else if (should_restart()) do_restart();
|
||||
else if (should_parallel_sync()) do_parallel_sync();
|
||||
else shift_weights();
|
||||
}
|
||||
}
|
||||
|
||||
void ddfw::check_with_plugin() {
|
||||
m_plugin->init_search();
|
||||
m_steps_since_progress = 0;
|
||||
unsigned steps = 0;
|
||||
while (m_min_sz > 0 && m_steps_since_progress++ <= 1500000) {
|
||||
if (should_reinit_weights()) do_reinit_weights();
|
||||
else if (steps % 5000 == 0) shift_weights(), m_plugin->on_rescale();
|
||||
else if (should_restart()) do_restart(), m_plugin->on_restart();
|
||||
else if (do_flip<true>());
|
||||
else if (do_literal_flip<true>());
|
||||
else if (should_parallel_sync()) do_parallel_sync();
|
||||
else shift_weights(), m_plugin->on_rescale();
|
||||
++steps;
|
||||
}
|
||||
m_plugin->finish_search();
|
||||
}
|
||||
|
||||
void ddfw::log() {
|
||||
double sec = m_stopwatch.get_current_seconds();
|
||||
double kflips_per_sec = (m_flips - m_last_flips) / (1000.0 * sec);
|
||||
if (m_last_flips == 0) {
|
||||
IF_VERBOSE(1, verbose_stream() << "(sat.ddfw :unsat :models :kflips/sec :flips :restarts :reinits :unsat_vars :shifts";
|
||||
if (m_par) verbose_stream() << " :par";
|
||||
verbose_stream() << ")\n");
|
||||
}
|
||||
IF_VERBOSE(1, verbose_stream() << "(sat.ddfw "
|
||||
<< std::setw(07) << m_min_sz
|
||||
<< std::setw(07) << m_models.size()
|
||||
<< std::setw(10) << kflips_per_sec
|
||||
<< std::setw(10) << m_flips
|
||||
<< std::setw(10) << m_restart_count
|
||||
<< std::setw(11) << m_reinit_count
|
||||
<< std::setw(13) << m_unsat_vars.size()
|
||||
<< std::setw(9) << m_shifts;
|
||||
if (m_par) verbose_stream() << std::setw(10) << m_parsync_count;
|
||||
verbose_stream() << ")\n");
|
||||
m_stopwatch.start();
|
||||
m_last_flips = m_flips;
|
||||
}
|
||||
|
||||
template<bool uses_plugin>
|
||||
bool ddfw::do_flip() {
|
||||
double reward = 0;
|
||||
bool_var v = pick_var<uses_plugin>(reward);
|
||||
return apply_flip<uses_plugin>(v, reward);
|
||||
}
|
||||
|
||||
template<bool uses_plugin>
|
||||
bool ddfw::apply_flip(bool_var v, double reward) {
|
||||
if (v == null_bool_var)
|
||||
return false;
|
||||
|
||||
if (reward > 0 || (reward == 0 && m_rand(100) <= m_config.m_use_reward_zero_pct)) {
|
||||
if (uses_plugin && is_external(v))
|
||||
m_plugin->flip(v);
|
||||
else
|
||||
flip(v);
|
||||
if (m_unsat.size() <= m_min_sz)
|
||||
save_best_values();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<bool uses_plugin>
|
||||
bool_var ddfw::pick_var(double& r) {
|
||||
double sum_pos = 0;
|
||||
unsigned n = 1;
|
||||
bool_var v0 = null_bool_var;
|
||||
for (bool_var v : m_unsat_vars) {
|
||||
r = uses_plugin ? plugin_reward(v) : reward(v);
|
||||
if (r > 0.0)
|
||||
sum_pos += score(r);
|
||||
else if (r == 0.0 && sum_pos == 0 && (m_rand() % (n++)) == 0)
|
||||
v0 = v;
|
||||
}
|
||||
if (sum_pos > 0) {
|
||||
double lim_pos = ((double) m_rand() / (1.0 + m_rand.max_value())) * sum_pos;
|
||||
for (bool_var v : m_unsat_vars) {
|
||||
r = uses_plugin && is_external(v) ? m_vars[v].m_last_reward : reward(v);
|
||||
if (r > 0) {
|
||||
lim_pos -= score(r);
|
||||
if (lim_pos <= 0)
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
r = 0;
|
||||
if (v0 != null_bool_var)
|
||||
return v0;
|
||||
if (m_unsat_vars.empty())
|
||||
return null_bool_var;
|
||||
return m_unsat_vars.elem_at(m_rand(m_unsat_vars.size()));
|
||||
}
|
||||
|
||||
template<bool uses_plugin>
|
||||
bool ddfw::do_literal_flip() {
|
||||
double reward = 1;
|
||||
return apply_flip<uses_plugin>(pick_literal_var<uses_plugin>(), reward);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pick a random false literal from a satisfied clause such that
|
||||
* the literal has zero break count and positive reward.
|
||||
*/
|
||||
template<bool uses_plugin>
|
||||
bool_var ddfw::pick_literal_var() {
|
||||
#if false
|
||||
unsigned sz = m_clauses.size();
|
||||
unsigned start = rand();
|
||||
for (unsigned i = 0; i < 100; ++i) {
|
||||
unsigned cl = (i + start) % sz;
|
||||
if (m_unsat.contains(cl))
|
||||
continue;
|
||||
for (auto lit : *m_clauses[cl].m_clause) {
|
||||
if (is_true(lit))
|
||||
continue;
|
||||
double r = uses_plugin ? plugin_reward(lit.var()) : reward(lit.var());
|
||||
if (r < 0)
|
||||
continue;
|
||||
//verbose_stream() << "false " << r << " " << lit << "\n";
|
||||
return lit.var();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return null_bool_var;
|
||||
}
|
||||
|
||||
void ddfw::add(unsigned n, literal const* c) {
|
||||
clause* cls = m_alloc.mk_clause(n, c, false);
|
||||
unsigned idx = m_clauses.size();
|
||||
|
||||
m_clauses.push_back(clause_info(cls, m_config.m_init_clause_weight));
|
||||
for (literal lit : *cls) {
|
||||
m_use_list.reserve(2*(lit.var()+1));
|
||||
m_vars.reserve(lit.var()+1);
|
||||
m_use_list[lit.index()].push_back(idx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the last clause that was added
|
||||
*/
|
||||
void ddfw::del() {
|
||||
auto& info = m_clauses.back();
|
||||
for (literal lit : *info.m_clause)
|
||||
m_use_list[lit.index()].pop_back();
|
||||
m_alloc.del_clause(info.m_clause);
|
||||
m_clauses.pop_back();
|
||||
if (m_unsat.contains(m_clauses.size()))
|
||||
m_unsat.remove(m_clauses.size());
|
||||
}
|
||||
|
||||
void ddfw::add(solver const& s) {
|
||||
set_seed(s.get_config().m_random_seed);
|
||||
for (auto& ci : m_clauses)
|
||||
m_alloc.del_clause(ci.m_clause);
|
||||
m_clauses.reset();
|
||||
m_use_list.reset();
|
||||
m_num_non_binary_clauses = 0;
|
||||
|
||||
unsigned trail_sz = s.init_trail_size();
|
||||
for (unsigned i = 0; i < trail_sz; ++i) {
|
||||
add(1, s.m_trail.data() + i);
|
||||
}
|
||||
unsigned sz = s.m_watches.size();
|
||||
for (unsigned l_idx = 0; l_idx < sz; ++l_idx) {
|
||||
literal l1 = ~to_literal(l_idx);
|
||||
watch_list const & wlist = s.m_watches[l_idx];
|
||||
for (watched const& w : wlist) {
|
||||
if (!w.is_binary_non_learned_clause())
|
||||
continue;
|
||||
literal l2 = w.get_literal();
|
||||
if (l1.index() > l2.index())
|
||||
continue;
|
||||
literal ls[2] = { l1, l2 };
|
||||
add(2, ls);
|
||||
}
|
||||
}
|
||||
for (clause* c : s.m_clauses) {
|
||||
add(c->size(), c->begin());
|
||||
}
|
||||
m_num_non_binary_clauses = s.m_clauses.size();
|
||||
}
|
||||
|
||||
void ddfw::add_assumptions() {
|
||||
for (unsigned i = 0; i < m_assumptions.size(); ++i)
|
||||
add(1, m_assumptions.data() + i);
|
||||
}
|
||||
|
||||
void ddfw::remove_assumptions() {
|
||||
if (m_assumptions.empty())
|
||||
return;
|
||||
for (unsigned i = 0; i < m_assumptions.size(); ++i)
|
||||
del();
|
||||
init(0, nullptr);
|
||||
}
|
||||
|
||||
void ddfw::init(unsigned sz, literal const* assumptions) {
|
||||
m_assumptions.reset();
|
||||
m_assumptions.append(sz, assumptions);
|
||||
add_assumptions();
|
||||
for (unsigned v = 0; v < num_vars(); ++v) {
|
||||
value(v) = (m_rand() % 2) == 0; // m_use_list[lit.index()].size() >= m_use_list[nlit.index()].size();
|
||||
}
|
||||
init_clause_data();
|
||||
flatten_use_list();
|
||||
|
||||
m_reinit_count = 0;
|
||||
m_reinit_next = m_config.m_reinit_base;
|
||||
|
||||
m_restart_count = 0;
|
||||
m_restart_next = m_config.m_restart_base*2;
|
||||
|
||||
m_parsync_count = 0;
|
||||
m_parsync_next = m_config.m_parsync_base;
|
||||
|
||||
m_min_sz = m_unsat.size();
|
||||
m_flips = 0;
|
||||
m_last_flips = 0;
|
||||
m_shifts = 0;
|
||||
m_stopwatch.start();
|
||||
}
|
||||
|
||||
void ddfw::reinit(solver& s, bool_vector const& phase) {
|
||||
add(s);
|
||||
add_assumptions();
|
||||
for (unsigned v = 0; v < phase.size(); ++v) {
|
||||
value(v) = phase[v];
|
||||
reward(v) = 0;
|
||||
make_count(v) = 0;
|
||||
}
|
||||
init_clause_data();
|
||||
flatten_use_list();
|
||||
}
|
||||
|
||||
void ddfw::flatten_use_list() {
|
||||
m_use_list_index.reset();
|
||||
m_flat_use_list.reset();
|
||||
for (auto const& ul : m_use_list) {
|
||||
m_use_list_index.push_back(m_flat_use_list.size());
|
||||
m_flat_use_list.append(ul);
|
||||
}
|
||||
m_use_list_index.push_back(m_flat_use_list.size());
|
||||
}
|
||||
|
||||
void ddfw::flip(bool_var v) {
|
||||
++m_flips;
|
||||
literal lit = literal(v, !value(v));
|
||||
literal nlit = ~lit;
|
||||
SASSERT(is_true(lit));
|
||||
for (unsigned cls_idx : use_list(*this, lit)) {
|
||||
clause_info& ci = m_clauses[cls_idx];
|
||||
ci.del(lit);
|
||||
double w = ci.m_weight;
|
||||
// cls becomes false: flip any variable in clause to receive reward w
|
||||
switch (ci.m_num_trues) {
|
||||
case 0: {
|
||||
m_unsat.insert_fresh(cls_idx);
|
||||
clause const& c = get_clause(cls_idx);
|
||||
for (literal l : c) {
|
||||
inc_reward(l, w);
|
||||
inc_make(l);
|
||||
}
|
||||
inc_reward(lit, w);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
dec_reward(to_literal(ci.m_trues), w);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (unsigned cls_idx : use_list(*this, nlit)) {
|
||||
clause_info& ci = m_clauses[cls_idx];
|
||||
double w = ci.m_weight;
|
||||
// the clause used to have a single true (pivot) literal, now it has two.
|
||||
// Then the previous pivot is no longer penalized for flipping.
|
||||
switch (ci.m_num_trues) {
|
||||
case 0: {
|
||||
m_unsat.remove(cls_idx);
|
||||
clause const& c = get_clause(cls_idx);
|
||||
for (literal l : c) {
|
||||
dec_reward(l, w);
|
||||
dec_make(l);
|
||||
}
|
||||
dec_reward(nlit, w);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
inc_reward(to_literal(ci.m_trues), w);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ci.add(nlit);
|
||||
}
|
||||
value(v) = !value(v);
|
||||
update_reward_avg(v);
|
||||
}
|
||||
|
||||
bool ddfw::should_reinit_weights() {
|
||||
return m_flips >= m_reinit_next;
|
||||
}
|
||||
|
||||
void ddfw::do_reinit_weights() {
|
||||
log();
|
||||
|
||||
if (m_reinit_count % 2 == 0) {
|
||||
for (auto& ci : m_clauses)
|
||||
ci.m_weight += 1;
|
||||
}
|
||||
else {
|
||||
for (auto& ci : m_clauses)
|
||||
if (ci.is_true())
|
||||
ci.m_weight = m_config.m_init_clause_weight;
|
||||
else
|
||||
ci.m_weight = m_config.m_init_clause_weight + 1;
|
||||
}
|
||||
init_clause_data();
|
||||
++m_reinit_count;
|
||||
m_reinit_next += m_reinit_count * m_config.m_reinit_base;
|
||||
}
|
||||
|
||||
void ddfw::init_clause_data() {
|
||||
for (unsigned v = 0; v < num_vars(); ++v) {
|
||||
make_count(v) = 0;
|
||||
reward(v) = 0;
|
||||
}
|
||||
m_unsat_vars.reset();
|
||||
m_unsat.reset();
|
||||
unsigned sz = m_clauses.size();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
auto& ci = m_clauses[i];
|
||||
clause const& c = get_clause(i);
|
||||
ci.m_trues = 0;
|
||||
ci.m_num_trues = 0;
|
||||
for (literal lit : c)
|
||||
if (is_true(lit))
|
||||
ci.add(lit);
|
||||
switch (ci.m_num_trues) {
|
||||
case 0:
|
||||
for (literal lit : c) {
|
||||
inc_reward(lit, ci.m_weight);
|
||||
inc_make(lit);
|
||||
}
|
||||
m_unsat.insert_fresh(i);
|
||||
break;
|
||||
case 1:
|
||||
dec_reward(to_literal(ci.m_trues), ci.m_weight);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ddfw::should_restart() {
|
||||
return m_flips >= m_restart_next;
|
||||
}
|
||||
|
||||
void ddfw::do_restart() {
|
||||
reinit_values();
|
||||
init_clause_data();
|
||||
m_restart_next += m_config.m_restart_base*get_luby(++m_restart_count);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief the higher the bias, the lower the probability to deviate from the value of the bias
|
||||
during a restart.
|
||||
bias = 0 -> flip truth value with 50%
|
||||
|bias| = 1 -> toss coin with 25% probability
|
||||
|bias| = 2 -> toss coin with 12.5% probability
|
||||
etc
|
||||
*/
|
||||
void ddfw::reinit_values() {
|
||||
for (unsigned i = 0; i < num_vars(); ++i) {
|
||||
int b = bias(i);
|
||||
if (0 == (m_rand() % (1 + abs(b))))
|
||||
value(i) = (m_rand() % 2) == 0;
|
||||
else
|
||||
value(i) = bias(i) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool ddfw::should_parallel_sync() {
|
||||
return m_par != nullptr && m_flips >= m_parsync_next;
|
||||
}
|
||||
|
||||
void ddfw::save_priorities() {
|
||||
m_probs.reset();
|
||||
for (unsigned v = 0; v < num_vars(); ++v)
|
||||
m_probs.push_back(-m_vars[v].m_reward_avg);
|
||||
}
|
||||
|
||||
void ddfw::do_parallel_sync() {
|
||||
if (m_par->from_solver(*this))
|
||||
m_par->to_solver(*this);
|
||||
|
||||
++m_parsync_count;
|
||||
m_parsync_next *= 3;
|
||||
m_parsync_next /= 2;
|
||||
}
|
||||
|
||||
void ddfw::save_model() {
|
||||
m_model.reserve(num_vars());
|
||||
for (unsigned i = 0; i < num_vars(); ++i)
|
||||
m_model[i] = to_lbool(value(i));
|
||||
save_priorities();
|
||||
if (m_plugin)
|
||||
m_plugin->on_save_model();
|
||||
}
|
||||
|
||||
|
||||
void ddfw::save_best_values() {
|
||||
if (m_unsat.size() < m_min_sz) {
|
||||
m_steps_since_progress = 0;
|
||||
if (m_unsat.size() < 50 || m_min_sz * 10 > m_unsat.size() * 11)
|
||||
save_model();
|
||||
}
|
||||
if (m_unsat.size() < m_min_sz) {
|
||||
m_models.reset();
|
||||
// skip saving the first model.
|
||||
for (unsigned v = 0; v < num_vars(); ++v) {
|
||||
int& b = bias(v);
|
||||
if (abs(b) > 3) {
|
||||
b = b > 0 ? 3 : -3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned h = value_hash();
|
||||
unsigned occs = 0;
|
||||
bool contains = m_models.find(h, occs);
|
||||
if (!contains) {
|
||||
for (unsigned v = 0; v < num_vars(); ++v)
|
||||
bias(v) += value(v) ? 1 : -1;
|
||||
if (m_models.size() > m_config.m_max_num_models)
|
||||
m_models.erase(m_models.begin()->m_key);
|
||||
}
|
||||
m_models.insert(h, occs + 1);
|
||||
if (occs > 100) {
|
||||
m_restart_next = m_flips;
|
||||
m_models.erase(h);
|
||||
}
|
||||
m_min_sz = m_unsat.size();
|
||||
}
|
||||
|
||||
unsigned ddfw::value_hash() const {
|
||||
unsigned s0 = 0, s1 = 0;
|
||||
for (auto const& vi : m_vars) {
|
||||
s0 += vi.m_value;
|
||||
s1 += s0;
|
||||
}
|
||||
return s1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
\brief Filter on whether to select a satisfied clause
|
||||
1. with some probability prefer higher weight to lesser weight.
|
||||
2. take into account number of trues ?
|
||||
3. select multiple clauses instead of just one per clause in unsat.
|
||||
*/
|
||||
|
||||
bool ddfw::select_clause(double max_weight, clause_info const& cn, unsigned& n) {
|
||||
if (cn.m_num_trues == 0 || cn.m_weight + 1e-5 < max_weight)
|
||||
return false;
|
||||
if (cn.m_weight > max_weight) {
|
||||
n = 2;
|
||||
return true;
|
||||
}
|
||||
return (m_rand() % (n++)) == 0;
|
||||
}
|
||||
|
||||
unsigned ddfw::select_max_same_sign(unsigned cf_idx) {
|
||||
auto& ci = m_clauses[cf_idx];
|
||||
unsigned cl = UINT_MAX; // clause pointer to same sign, max weight satisfied clause.
|
||||
clause const& c = *ci.m_clause;
|
||||
double max_weight = m_init_weight;
|
||||
unsigned n = 1;
|
||||
for (literal lit : c) {
|
||||
for (unsigned cn_idx : use_list(*this, lit)) {
|
||||
auto& cn = m_clauses[cn_idx];
|
||||
if (select_clause(max_weight, cn, n)) {
|
||||
cl = cn_idx;
|
||||
max_weight = cn.m_weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cl;
|
||||
}
|
||||
|
||||
void ddfw::transfer_weight(unsigned from, unsigned to, double w) {
|
||||
auto& cf = m_clauses[to];
|
||||
auto& cn = m_clauses[from];
|
||||
if (cn.m_weight < w)
|
||||
return;
|
||||
cf.m_weight += w;
|
||||
cn.m_weight -= w;
|
||||
|
||||
for (literal lit : get_clause(to))
|
||||
inc_reward(lit, w);
|
||||
if (cn.m_num_trues == 1)
|
||||
inc_reward(to_literal(cn.m_trues), w);
|
||||
}
|
||||
|
||||
unsigned ddfw::select_random_true_clause() {
|
||||
unsigned num_clauses = m_clauses.size();
|
||||
unsigned rounds = 100 * num_clauses;
|
||||
for (unsigned i = 0; i < rounds; ++i) {
|
||||
unsigned idx = (m_rand() * m_rand()) % num_clauses;
|
||||
auto & cn = m_clauses[idx];
|
||||
if (cn.is_true() && cn.m_weight >= m_init_weight)
|
||||
return idx;
|
||||
}
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
// 1% chance to disregard neighbor
|
||||
inline bool ddfw::disregard_neighbor() {
|
||||
return false; // rand() % 1000 == 0;
|
||||
}
|
||||
|
||||
double ddfw::calculate_transfer_weight(double w) {
|
||||
return (w > m_init_weight) ? m_init_weight : 1;
|
||||
}
|
||||
|
||||
void ddfw::shift_weights() {
|
||||
++m_shifts;
|
||||
for (unsigned to_idx : m_unsat) {
|
||||
SASSERT(!m_clauses[to_idx].is_true());
|
||||
unsigned from_idx = select_max_same_sign(to_idx);
|
||||
if (from_idx == UINT_MAX || disregard_neighbor())
|
||||
from_idx = select_random_true_clause();
|
||||
if (from_idx == UINT_MAX)
|
||||
continue;
|
||||
auto & cn = m_clauses[from_idx];
|
||||
SASSERT(cn.is_true());
|
||||
double w = calculate_transfer_weight(cn.m_weight);
|
||||
transfer_weight(from_idx, to_idx, w);
|
||||
}
|
||||
// DEBUG_CODE(invariant(););
|
||||
}
|
||||
|
||||
std::ostream& ddfw::display(std::ostream& out) const {
|
||||
unsigned num_cls = m_clauses.size();
|
||||
for (unsigned i = 0; i < num_cls; ++i) {
|
||||
out << get_clause(i) << " ";
|
||||
auto const& ci = m_clauses[i];
|
||||
out << ci.m_num_trues << " " << ci.m_weight << "\n";
|
||||
}
|
||||
for (unsigned v = 0; v < num_vars(); ++v) {
|
||||
out << v << ": rw " << reward(v) << "\n";
|
||||
}
|
||||
out << "unsat vars: ";
|
||||
for (bool_var v : m_unsat_vars) {
|
||||
out << v << " ";
|
||||
}
|
||||
out << "\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
void ddfw::invariant() {
|
||||
// every variable in unsat vars is in a false clause.
|
||||
for (bool_var v : m_unsat_vars) {
|
||||
bool found = false;
|
||||
for (unsigned cl : m_unsat) {
|
||||
for (literal lit : get_clause(cl)) {
|
||||
if (lit.var() == v) { found = true; break; }
|
||||
}
|
||||
if (found) break;
|
||||
}
|
||||
if (!found) IF_VERBOSE(0, verbose_stream() << "unsat var not found: " << v << "\n"; );
|
||||
VERIFY(found);
|
||||
}
|
||||
for (unsigned v = 0; v < num_vars(); ++v) {
|
||||
double v_reward = 0;
|
||||
literal lit(v, !value(v));
|
||||
for (unsigned j : m_use_list[lit.index()]) {
|
||||
clause_info const& ci = m_clauses[j];
|
||||
if (ci.m_num_trues == 1) {
|
||||
SASSERT(lit == to_literal(ci.m_trues));
|
||||
v_reward -= ci.m_weight;
|
||||
}
|
||||
}
|
||||
for (unsigned j : m_use_list[(~lit).index()]) {
|
||||
clause_info const& ci = m_clauses[j];
|
||||
if (ci.m_num_trues == 0) {
|
||||
v_reward += ci.m_weight;
|
||||
}
|
||||
}
|
||||
IF_VERBOSE(0, if (v_reward != reward(v)) verbose_stream() << v << " " << v_reward << " " << reward(v) << "\n");
|
||||
// SASSERT(reward(v) == v_reward);
|
||||
}
|
||||
DEBUG_CODE(
|
||||
for (auto const& ci : m_clauses) {
|
||||
SASSERT(ci.m_weight > 0);
|
||||
}
|
||||
for (unsigned i = 0; i < m_clauses.size(); ++i) {
|
||||
bool found = false;
|
||||
for (literal lit : get_clause(i)) {
|
||||
if (is_true(lit)) found = true;
|
||||
}
|
||||
SASSERT(found == !m_unsat.contains(i));
|
||||
}
|
||||
// every variable in a false clause is in unsat vars
|
||||
for (unsigned cl : m_unsat) {
|
||||
for (literal lit : get_clause(cl)) {
|
||||
SASSERT(m_unsat_vars.contains(lit.var()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ddfw::updt_params(params_ref const& _p) {
|
||||
sat_params p(_p);
|
||||
m_config.m_init_clause_weight = p.ddfw_init_clause_weight();
|
||||
m_config.m_use_reward_zero_pct = p.ddfw_use_reward_pct();
|
||||
m_config.m_reinit_base = p.ddfw_reinit_base();
|
||||
m_config.m_restart_base = p.ddfw_restart_base();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,300 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2019 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
sat_ddfw.h
|
||||
|
||||
Abstract:
|
||||
|
||||
DDFW Local search module for clauses
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner, Marijn Heule 2019-4-23
|
||||
|
||||
Notes:
|
||||
|
||||
http://www.ict.griffith.edu.au/~johnt/publications/CP2006raouf.pdf
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/uint_set.h"
|
||||
#include "util/rlimit.h"
|
||||
#include "util/params.h"
|
||||
#include "util/ema.h"
|
||||
#include "sat/sat_clause.h"
|
||||
#include "sat/sat_types.h"
|
||||
|
||||
namespace arith {
|
||||
class sls;
|
||||
}
|
||||
|
||||
namespace sat {
|
||||
class solver;
|
||||
class parallel;
|
||||
|
||||
class local_search_plugin {
|
||||
public:
|
||||
virtual ~local_search_plugin() {}
|
||||
virtual void init_search() = 0;
|
||||
virtual void finish_search() = 0;
|
||||
virtual void flip(bool_var v) = 0;
|
||||
virtual double reward(bool_var v) = 0;
|
||||
virtual void on_rescale() = 0;
|
||||
virtual void on_save_model() = 0;
|
||||
virtual void on_restart() = 0;
|
||||
};
|
||||
|
||||
class ddfw : public i_local_search {
|
||||
friend class arith::sls;
|
||||
public:
|
||||
struct clause_info {
|
||||
clause_info(clause* cl, double init_weight): m_weight(init_weight), m_clause(cl) {}
|
||||
double m_weight; // weight of clause
|
||||
unsigned m_trues = 0; // set of literals that are true
|
||||
unsigned m_num_trues = 0; // size of true set
|
||||
clause* m_clause;
|
||||
bool is_true() const { return m_num_trues > 0; }
|
||||
void add(literal lit) { ++m_num_trues; m_trues += lit.index(); }
|
||||
void del(literal lit) { SASSERT(m_num_trues > 0); --m_num_trues; m_trues -= lit.index(); }
|
||||
};
|
||||
|
||||
class use_list {
|
||||
ddfw& p;
|
||||
unsigned i;
|
||||
public:
|
||||
use_list(ddfw& p, literal lit) :
|
||||
p(p), i(lit.index()) {}
|
||||
unsigned const* begin() { return p.m_flat_use_list.data() + p.m_use_list_index[i]; }
|
||||
unsigned const* end() { return p.m_flat_use_list.data() + p.m_use_list_index[i + 1]; }
|
||||
unsigned size() const { return p.m_use_list_index[i + 1] - p.m_use_list_index[i]; }
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
struct config {
|
||||
config() { reset(); }
|
||||
unsigned m_use_reward_zero_pct;
|
||||
unsigned m_init_clause_weight;
|
||||
unsigned m_max_num_models;
|
||||
unsigned m_restart_base;
|
||||
unsigned m_reinit_base;
|
||||
unsigned m_parsync_base;
|
||||
double m_itau;
|
||||
void reset() {
|
||||
m_init_clause_weight = 8;
|
||||
m_use_reward_zero_pct = 15;
|
||||
m_max_num_models = (1 << 10);
|
||||
m_restart_base = 100333;
|
||||
m_reinit_base = 10000;
|
||||
m_parsync_base = 333333;
|
||||
m_itau = 0.5;
|
||||
}
|
||||
};
|
||||
|
||||
struct var_info {
|
||||
bool m_value = false;
|
||||
double m_reward = 0;
|
||||
double m_last_reward = 0;
|
||||
unsigned m_make_count = 0;
|
||||
int m_bias = 0;
|
||||
bool m_external = false;
|
||||
ema m_reward_avg = 1e-5;
|
||||
};
|
||||
|
||||
config m_config;
|
||||
reslimit m_limit;
|
||||
clause_allocator m_alloc;
|
||||
svector<clause_info> m_clauses;
|
||||
literal_vector m_assumptions;
|
||||
svector<var_info> m_vars; // var -> info
|
||||
svector<double> m_probs; // var -> probability of flipping
|
||||
svector<double> m_scores; // reward -> score
|
||||
model m_model; // var -> best assignment
|
||||
unsigned m_init_weight = 2;
|
||||
|
||||
vector<unsigned_vector> m_use_list;
|
||||
unsigned_vector m_flat_use_list;
|
||||
unsigned_vector m_use_list_index;
|
||||
|
||||
indexed_uint_set m_unsat;
|
||||
indexed_uint_set m_unsat_vars; // set of variables that are in unsat clauses
|
||||
random_gen m_rand;
|
||||
unsigned m_num_non_binary_clauses = 0;
|
||||
unsigned m_restart_count = 0, m_reinit_count = 0, m_parsync_count = 0;
|
||||
uint64_t m_restart_next = 0, m_reinit_next = 0, m_parsync_next = 0;
|
||||
uint64_t m_flips = 0, m_last_flips = 0, m_shifts = 0;
|
||||
unsigned m_min_sz = 0, m_steps_since_progress = 0;
|
||||
u_map<unsigned> m_models;
|
||||
stopwatch m_stopwatch;
|
||||
|
||||
parallel* m_par;
|
||||
local_search_plugin* m_plugin = nullptr;
|
||||
|
||||
void flatten_use_list();
|
||||
|
||||
/**
|
||||
* TBD: map reward value to a score, possibly through an exponential function, such as
|
||||
* exp(-tau/r), where tau > 0
|
||||
*/
|
||||
inline double score(double r) { return r; }
|
||||
|
||||
inline unsigned num_vars() const { return m_vars.size(); }
|
||||
|
||||
inline unsigned& make_count(bool_var v) { return m_vars[v].m_make_count; }
|
||||
|
||||
inline bool& value(bool_var v) { return m_vars[v].m_value; }
|
||||
|
||||
inline bool value(bool_var v) const { return m_vars[v].m_value; }
|
||||
|
||||
inline double& reward(bool_var v) { return m_vars[v].m_reward; }
|
||||
|
||||
inline double reward(bool_var v) const { return m_vars[v].m_reward; }
|
||||
|
||||
inline double plugin_reward(bool_var v) { return is_external(v) ? (m_vars[v].m_last_reward = m_plugin->reward(v)) : reward(v); }
|
||||
|
||||
void set_external(bool_var v) { m_vars[v].m_external = true; }
|
||||
|
||||
inline bool is_external(bool_var v) const { return m_vars[v].m_external; }
|
||||
|
||||
inline int& bias(bool_var v) { return m_vars[v].m_bias; }
|
||||
|
||||
unsigned value_hash() const;
|
||||
|
||||
inline bool is_true(literal lit) const { return value(lit.var()) != lit.sign(); }
|
||||
|
||||
inline clause const& get_clause(unsigned idx) const { return *m_clauses[idx].m_clause; }
|
||||
|
||||
inline double get_weight(unsigned idx) const { return m_clauses[idx].m_weight; }
|
||||
|
||||
inline bool is_true(unsigned idx) const { return m_clauses[idx].is_true(); }
|
||||
|
||||
void update_reward_avg(bool_var v) { m_vars[v].m_reward_avg.update(reward(v)); }
|
||||
|
||||
unsigned select_max_same_sign(unsigned cf_idx);
|
||||
|
||||
inline void inc_make(literal lit) {
|
||||
bool_var v = lit.var();
|
||||
if (make_count(v)++ == 0) m_unsat_vars.insert_fresh(v);
|
||||
}
|
||||
|
||||
inline void dec_make(literal lit) {
|
||||
bool_var v = lit.var();
|
||||
if (--make_count(v) == 0) m_unsat_vars.remove(v);
|
||||
}
|
||||
|
||||
inline void inc_reward(literal lit, double w) { reward(lit.var()) += w; }
|
||||
|
||||
inline void dec_reward(literal lit, double w) { reward(lit.var()) -= w; }
|
||||
|
||||
void check_with_plugin();
|
||||
void check_without_plugin();
|
||||
|
||||
// flip activity
|
||||
template<bool uses_plugin>
|
||||
bool do_flip();
|
||||
|
||||
template<bool uses_plugin>
|
||||
bool_var pick_var(double& reward);
|
||||
|
||||
template<bool uses_plugin>
|
||||
bool apply_flip(bool_var v, double reward);
|
||||
|
||||
template<bool uses_plugin>
|
||||
bool do_literal_flip();
|
||||
|
||||
template<bool uses_plugin>
|
||||
bool_var pick_literal_var();
|
||||
|
||||
void save_best_values();
|
||||
void save_model();
|
||||
void save_priorities();
|
||||
|
||||
// shift activity
|
||||
void shift_weights();
|
||||
inline double calculate_transfer_weight(double w);
|
||||
|
||||
// reinitialize weights activity
|
||||
bool should_reinit_weights();
|
||||
void do_reinit_weights();
|
||||
inline bool select_clause(double max_weight, clause_info const& cn, unsigned& n);
|
||||
|
||||
// restart activity
|
||||
bool should_restart();
|
||||
void do_restart();
|
||||
void reinit_values();
|
||||
|
||||
unsigned select_random_true_clause();
|
||||
|
||||
// parallel integration
|
||||
bool should_parallel_sync();
|
||||
void do_parallel_sync();
|
||||
|
||||
void log();
|
||||
|
||||
void init(unsigned sz, literal const* assumptions);
|
||||
|
||||
void init_clause_data();
|
||||
|
||||
void invariant();
|
||||
|
||||
void add(unsigned sz, literal const* c);
|
||||
|
||||
void del();
|
||||
|
||||
void add_assumptions();
|
||||
|
||||
inline void transfer_weight(unsigned from, unsigned to, double w);
|
||||
|
||||
inline bool disregard_neighbor();
|
||||
|
||||
public:
|
||||
|
||||
ddfw(): m_par(nullptr) {}
|
||||
|
||||
~ddfw() override;
|
||||
|
||||
void set(local_search_plugin* p) { m_plugin = p; }
|
||||
|
||||
lbool check(unsigned sz, literal const* assumptions, parallel* p) override;
|
||||
|
||||
void updt_params(params_ref const& p) override;
|
||||
|
||||
model const& get_model() const override { return m_model; }
|
||||
|
||||
reslimit& rlimit() override { return m_limit; }
|
||||
|
||||
void set_seed(unsigned n) override { m_rand.set_seed(n); }
|
||||
|
||||
void add(solver const& s) override;
|
||||
|
||||
bool get_value(bool_var v) const override { return value(v); }
|
||||
|
||||
std::ostream& display(std::ostream& out) const;
|
||||
|
||||
// for parallel integration
|
||||
unsigned num_non_binary_clauses() const override { return m_num_non_binary_clauses; }
|
||||
void reinit(solver& s, bool_vector const& phase) override;
|
||||
|
||||
void collect_statistics(statistics& st) const override {}
|
||||
|
||||
double get_priority(bool_var v) const override { return m_probs[v]; }
|
||||
|
||||
// access clause information and state of Boolean search
|
||||
indexed_uint_set& unsat_set() { return m_unsat; }
|
||||
|
||||
unsigned num_clauses() const { return m_clauses.size(); }
|
||||
|
||||
clause_info& get_clause_info(unsigned idx) { return m_clauses[idx]; }
|
||||
|
||||
void remove_assumptions();
|
||||
|
||||
void flip(bool_var v);
|
||||
|
||||
use_list get_use_list(literal lit) { return use_list(*this, lit); }
|
||||
|
||||
};
|
||||
}
|
||||
|
85
src/sat/sat_ddfw_wrapper.cpp
Normal file
85
src/sat/sat_ddfw_wrapper.cpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*++
|
||||
Copyright (c) 2019 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
sat_ddfw_wrapper.cpp
|
||||
|
||||
*/
|
||||
|
||||
#include "sat/sat_ddfw_wrapper.h"
|
||||
#include "sat/sat_solver.h"
|
||||
#include "sat/sat_parallel.h"
|
||||
|
||||
namespace sat {
|
||||
|
||||
lbool ddfw_wrapper::check(unsigned sz, literal const* assumptions, parallel* p) {
|
||||
flet<parallel*> _p(m_par, p);
|
||||
m_ddfw.m_parallel_sync = nullptr;
|
||||
if (m_par) {
|
||||
m_ddfw.m_parallel_sync = [&]() -> bool {
|
||||
if (should_parallel_sync()) {
|
||||
do_parallel_sync();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
};
|
||||
}
|
||||
return m_ddfw.check(sz, assumptions);
|
||||
}
|
||||
|
||||
bool ddfw_wrapper::should_parallel_sync() {
|
||||
return m_par != nullptr && m_ddfw.m_flips >= m_parsync_next;
|
||||
}
|
||||
|
||||
void ddfw_wrapper::do_parallel_sync() {
|
||||
if (m_par->from_solver(*this))
|
||||
m_par->to_solver(*this);
|
||||
|
||||
++m_parsync_count;
|
||||
m_parsync_next *= 3;
|
||||
m_parsync_next /= 2;
|
||||
}
|
||||
|
||||
|
||||
void ddfw_wrapper::reinit(solver& s, bool_vector const& phase) {
|
||||
add(s);
|
||||
m_ddfw.add_assumptions();
|
||||
for (unsigned v = 0; v < phase.size(); ++v) {
|
||||
m_ddfw.value(v) = phase[v];
|
||||
m_ddfw.reward(v) = 0;
|
||||
m_ddfw.make_count(v) = 0;
|
||||
}
|
||||
m_ddfw.init_clause_data();
|
||||
m_ddfw.flatten_use_list();
|
||||
}
|
||||
|
||||
void ddfw_wrapper::add(solver const& s) {
|
||||
m_ddfw.set_seed(s.get_config().m_random_seed);
|
||||
m_ddfw.m_clauses.reset();
|
||||
m_ddfw.m_use_list.reset();
|
||||
m_ddfw.m_num_non_binary_clauses = 0;
|
||||
|
||||
unsigned trail_sz = s.init_trail_size();
|
||||
for (unsigned i = 0; i < trail_sz; ++i) {
|
||||
m_ddfw.add(1, s.m_trail.data() + i);
|
||||
}
|
||||
unsigned sz = s.m_watches.size();
|
||||
for (unsigned l_idx = 0; l_idx < sz; ++l_idx) {
|
||||
literal l1 = ~to_literal(l_idx);
|
||||
watch_list const & wlist = s.m_watches[l_idx];
|
||||
for (watched const& w : wlist) {
|
||||
if (!w.is_binary_non_learned_clause())
|
||||
continue;
|
||||
literal l2 = w.get_literal();
|
||||
if (l1.index() > l2.index())
|
||||
continue;
|
||||
literal ls[2] = { l1, l2 };
|
||||
m_ddfw.add(2, ls);
|
||||
}
|
||||
}
|
||||
for (clause* c : s.m_clauses)
|
||||
m_ddfw.add(c->size(), c->begin());
|
||||
}
|
||||
}
|
89
src/sat/sat_ddfw_wrapper.h
Normal file
89
src/sat/sat_ddfw_wrapper.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*++
|
||||
Copyright (c) 2019 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
sat_ddfw_wrapper.h
|
||||
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/uint_set.h"
|
||||
#include "util/rlimit.h"
|
||||
#include "util/params.h"
|
||||
#include "util/ema.h"
|
||||
#include "util/sat_sls.h"
|
||||
#include "util/map.h"
|
||||
#include "ast/sls/sat_ddfw.h"
|
||||
#include "sat/sat_types.h"
|
||||
|
||||
namespace sat {
|
||||
class solver;
|
||||
class parallel;
|
||||
|
||||
|
||||
class ddfw_wrapper : public i_local_search {
|
||||
protected:
|
||||
ddfw m_ddfw;
|
||||
parallel* m_par = nullptr;
|
||||
unsigned m_parsync_count = 0;
|
||||
uint64_t m_parsync_next = 0;
|
||||
|
||||
void do_parallel_sync();
|
||||
bool should_parallel_sync();
|
||||
|
||||
public:
|
||||
|
||||
ddfw_wrapper() {}
|
||||
|
||||
~ddfw_wrapper() override {}
|
||||
|
||||
void set_plugin(local_search_plugin* p) { m_ddfw.set_plugin(p); }
|
||||
|
||||
lbool check(unsigned sz, literal const* assumptions, parallel* p) override;
|
||||
|
||||
void updt_params(params_ref const& p) override { m_ddfw.updt_params(p); }
|
||||
|
||||
model const& get_model() const override { return m_ddfw.get_model(); }
|
||||
|
||||
reslimit& rlimit() override { return m_ddfw.rlimit(); }
|
||||
|
||||
void set_seed(unsigned n) override { m_ddfw.set_seed(n); }
|
||||
|
||||
void add(solver const& s) override;
|
||||
|
||||
bool get_value(bool_var v) const override { return m_ddfw.get_value(v); }
|
||||
|
||||
std::ostream& display(std::ostream& out) const { return m_ddfw.display(out); }
|
||||
|
||||
// for parallel integration
|
||||
unsigned num_non_binary_clauses() const override { return m_ddfw.num_non_binary_clauses(); }
|
||||
|
||||
void reinit(solver& s, bool_vector const& phase) override;
|
||||
|
||||
void collect_statistics(statistics& st) const override {}
|
||||
|
||||
double get_priority(bool_var v) const override { return m_ddfw.get_priority(v); }
|
||||
|
||||
// access clause information and state of Boolean search
|
||||
indexed_uint_set& unsat_set() { return m_ddfw.unsat_set(); }
|
||||
|
||||
vector<clause_info> const& clauses() const { return m_ddfw.clauses(); }
|
||||
|
||||
clause_info& get_clause_info(unsigned idx) { return m_ddfw.get_clause_info(idx); }
|
||||
|
||||
void remove_assumptions() { m_ddfw.remove_assumptions(); }
|
||||
|
||||
void flip(bool_var v) { m_ddfw.flip(v); }
|
||||
|
||||
inline double get_reward(bool_var v) const { return m_ddfw.get_reward(v); }
|
||||
|
||||
void add(unsigned sz, literal const* c) { m_ddfw.add(sz, c); }
|
||||
|
||||
void reinit() { m_ddfw.reinit(); }
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ Notes:
|
|||
|
||||
#include "sat/sat_local_search.h"
|
||||
#include "sat/sat_solver.h"
|
||||
#include "sat/sat_params.hpp"
|
||||
#include "params/sat_params.hpp"
|
||||
#include "util/timer.h"
|
||||
|
||||
namespace sat {
|
||||
|
|
|
@ -1,128 +0,0 @@
|
|||
def_module_params('sat',
|
||||
export=True,
|
||||
description='propositional SAT solver',
|
||||
params=(max_memory_param(),
|
||||
('phase', SYMBOL, 'caching', 'phase selection strategy: always_false, always_true, basic_caching, random, caching, local_search'),
|
||||
('phase.sticky', BOOL, True, 'use sticky phase caching'),
|
||||
('search.unsat.conflicts', UINT, 400, 'period for solving for unsat (in number of conflicts)'),
|
||||
('search.sat.conflicts', UINT, 400, 'period for solving for sat (in number of conflicts)'),
|
||||
('rephase.base', UINT, 1000, 'number of conflicts per rephase '),
|
||||
('reorder.base', UINT, UINT_MAX, 'number of conflicts per random reorder '),
|
||||
('reorder.itau', DOUBLE, 4.0, 'inverse temperature for softmax'),
|
||||
('reorder.activity_scale', UINT, 100, 'scaling factor for activity update'),
|
||||
('propagate.prefetch', BOOL, True, 'prefetch watch lists for assigned literals'),
|
||||
('restart', SYMBOL, 'ema', 'restart strategy: static, luby, ema or geometric'),
|
||||
('restart.initial', UINT, 2, 'initial restart (number of conflicts)'),
|
||||
('restart.max', UINT, UINT_MAX, 'maximal number of restarts.'),
|
||||
('restart.fast', BOOL, True, 'use fast restart approach only removing less active literals.'),
|
||||
('restart.factor', DOUBLE, 1.5, 'restart increment factor for geometric strategy'),
|
||||
('restart.margin', DOUBLE, 1.1, 'margin between fast and slow restart factors. For ema'),
|
||||
('restart.emafastglue', DOUBLE, 3e-2, 'ema alpha factor for fast moving average'),
|
||||
('restart.emaslowglue', DOUBLE, 1e-5, 'ema alpha factor for slow moving average'),
|
||||
('variable_decay', UINT, 110, 'multiplier (divided by 100) for the VSIDS activity increment'),
|
||||
('inprocess.max', UINT, UINT_MAX, 'maximal number of inprocessing passes'),
|
||||
('inprocess.out', SYMBOL, '', 'file to dump result of the first inprocessing step and exit'),
|
||||
('branching.heuristic', SYMBOL, 'vsids', 'branching heuristic vsids, chb'),
|
||||
('branching.anti_exploration', BOOL, False, 'apply anti-exploration heuristic for branch selection'),
|
||||
('random_freq', DOUBLE, 0.01, 'frequency of random case splits'),
|
||||
('random_seed', UINT, 0, 'random seed'),
|
||||
('burst_search', UINT, 100, 'number of conflicts before first global simplification'),
|
||||
('enable_pre_simplify', BOOL, False, 'enable pre simplifications before the bounded search'),
|
||||
('max_conflicts', UINT, UINT_MAX, 'maximum number of conflicts'),
|
||||
('gc', SYMBOL, 'glue_psm', 'garbage collection strategy: psm, glue, glue_psm, dyn_psm'),
|
||||
('gc.initial', UINT, 20000, 'learned clauses garbage collection frequency'),
|
||||
('gc.increment', UINT, 500, 'increment to the garbage collection threshold'),
|
||||
('gc.small_lbd', UINT, 3, 'learned clauses with small LBD are never deleted (only used in dyn_psm)'),
|
||||
('gc.k', UINT, 7, 'learned clauses that are inactive for k gc rounds are permanently deleted (only used in dyn_psm)'),
|
||||
('gc.burst', BOOL, False, 'perform eager garbage collection during initialization'),
|
||||
('gc.defrag', BOOL, True, 'defragment clauses when garbage collecting'),
|
||||
('simplify.delay', UINT, 0, 'set initial delay of simplification by a conflict count'),
|
||||
('force_cleanup', BOOL, False, 'force cleanup to remove tautologies and simplify clauses'),
|
||||
('minimize_lemmas', BOOL, True, 'minimize learned clauses'),
|
||||
('dyn_sub_res', BOOL, True, 'dynamic subsumption resolution for minimizing learned clauses'),
|
||||
('core.minimize', BOOL, False, 'minimize computed core'),
|
||||
('core.minimize_partial', BOOL, False, 'apply partial (cheap) core minimization'),
|
||||
('backtrack.scopes', UINT, 100, 'number of scopes to enable chronological backtracking'),
|
||||
('backtrack.conflicts', UINT, 4000, 'number of conflicts before enabling chronological backtracking'),
|
||||
('threads', UINT, 1, 'number of parallel threads to use'),
|
||||
('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'),
|
||||
('drat.disable', BOOL, False, 'override anything that enables DRAT'),
|
||||
('smt', BOOL, False, 'use the SAT solver based incremental SMT core'),
|
||||
('smt.proof.check', BOOL, False, 'check proofs on the fly during SMT search'),
|
||||
('drat.file', SYMBOL, '', 'file to dump DRAT proofs'),
|
||||
('drat.binary', BOOL, False, 'use Binary DRAT output format'),
|
||||
('drat.check_unsat', BOOL, False, 'build up internal proof and check'),
|
||||
('drat.check_sat', BOOL, False, 'build up internal trace, check satisfying model'),
|
||||
('drat.activity', BOOL, False, 'dump variable activities'),
|
||||
('cardinality.solver', BOOL, True, 'use cardinality solver'),
|
||||
('pb.solver', SYMBOL, 'solver', 'method for handling Pseudo-Boolean constraints: circuit (arithmetical circuit), sorting (sorting circuit), totalizer (use totalizer encoding), binary_merge, segmented, solver (use native solver)'),
|
||||
('pb.min_arity', UINT, 9, 'minimal arity to compile pb/cardinality constraints to CNF'),
|
||||
('cardinality.encoding', SYMBOL, 'grouped', 'encoding used for at-most-k constraints: grouped, bimander, ordered, unate, circuit'),
|
||||
('pb.resolve', SYMBOL, 'cardinality', 'resolution strategy for boolean algebra solver: cardinality, rounding'),
|
||||
('pb.lemma_format', SYMBOL, 'cardinality', 'generate either cardinality or pb lemmas'),
|
||||
('euf', BOOL, False, 'enable euf solver (this feature is preliminary and not ready for general consumption)'),
|
||||
('ddfw_search', BOOL, False, 'use ddfw local search instead of CDCL'),
|
||||
('ddfw.init_clause_weight', UINT, 8, 'initial clause weight for DDFW local search'),
|
||||
('ddfw.use_reward_pct', UINT, 15, 'percentage to pick highest reward variable when it has reward 0'),
|
||||
('ddfw.restart_base', UINT, 100000, 'number of flips used a starting point for hesitant restart backoff'),
|
||||
('ddfw.reinit_base', UINT, 10000, 'increment basis for geometric backoff scheme of re-initialization of weights'),
|
||||
('ddfw.threads', UINT, 0, 'number of ddfw threads to run in parallel with sat solver'),
|
||||
('prob_search', BOOL, False, 'use probsat local search instead of CDCL'),
|
||||
('local_search', BOOL, False, 'use local search instead of CDCL'),
|
||||
('local_search_threads', UINT, 0, 'number of local search threads to find satisfiable solution'),
|
||||
('local_search_mode', SYMBOL, 'wsat', 'local search algorithm, either default wsat or qsat'),
|
||||
('local_search_dbg_flips', BOOL, False, 'write debug information for number of flips'),
|
||||
('binspr', BOOL, False, 'enable SPR inferences of binary propagation redundant clauses. This inprocessing step eliminates models'),
|
||||
('anf', BOOL, False, 'enable ANF based simplification in-processing'),
|
||||
('anf.delay', UINT, 2, 'delay ANF simplification by in-processing round'),
|
||||
('anf.exlin', BOOL, False, 'enable extended linear simplification'),
|
||||
('cut', BOOL, False, 'enable AIG based simplification in-processing'),
|
||||
('cut.delay', UINT, 2, 'delay cut simplification by in-processing round'),
|
||||
('cut.aig', BOOL, False, 'extract aigs (and ites) from cluases for cut simplification'),
|
||||
('cut.lut', BOOL, False, 'extract luts from clauses for cut simplification'),
|
||||
('cut.xor', BOOL, False, 'extract xors from clauses for cut simplification'),
|
||||
('cut.npn3', BOOL, False, 'extract 3 input functions from clauses for cut simplification'),
|
||||
('cut.dont_cares', BOOL, True, 'integrate dont cares with cuts'),
|
||||
('cut.redundancies', BOOL, True, 'integrate redundancy checking of cuts'),
|
||||
('cut.force', BOOL, False, 'force redoing cut-enumeration until a fixed-point'),
|
||||
('lookahead.cube.cutoff', SYMBOL, 'depth', 'cutoff type used to create lookahead cubes: depth, freevars, psat, adaptive_freevars, adaptive_psat'),
|
||||
# - depth: the maximal cutoff is fixed to the value of lookahead.cube.depth.
|
||||
# So if the value is 10, at most 1024 cubes will be generated of length 10.
|
||||
# - freevars: cutoff based on a variable fraction of lookahead.cube.freevars.
|
||||
# Cut if the number of current unassigned variables drops below a fraction of number of initial variables.
|
||||
# - psat: Let psat_heur := (Sum_{clause C} (psat.clause_base ^ {-|C|+1})) / |freevars|^psat.var_exp
|
||||
# Cut if the value of psat_heur exceeds psat.trigger
|
||||
# - adaptive_freevars: Cut if the number of current unassigned variables drops below a fraction of free variables
|
||||
# at the time of the last conflict. The fraction is increased every time the cutoff is created.
|
||||
# - adative_psat: Cut based on psat_heur in an adaptive way.
|
||||
|
||||
('lookahead.cube.fraction', DOUBLE, 0.4, 'adaptive fraction to create lookahead cubes. Used when lookahead.cube.cutoff is adaptive_freevars or adaptive_psat'),
|
||||
('lookahead.cube.depth', UINT, 1, 'cut-off depth to create cubes. Used when lookahead.cube.cutoff is depth.'),
|
||||
('lookahead.cube.freevars', DOUBLE, 0.8, 'cube free variable fraction. Used when lookahead.cube.cutoff is freevars'),
|
||||
('lookahead.cube.psat.var_exp', DOUBLE, 1, 'free variable exponent for PSAT cutoff'),
|
||||
('lookahead.cube.psat.clause_base', DOUBLE, 2, 'clause base for PSAT cutoff'),
|
||||
('lookahead.cube.psat.trigger', DOUBLE, 5, 'trigger value to create lookahead cubes for PSAT cutoff. Used when lookahead.cube.cutoff is psat'),
|
||||
('lookahead.preselect', BOOL, False, 'use pre-selection of subset of variables for branching'),
|
||||
('lookahead_simplify', BOOL, False, 'use lookahead solver during simplification'),
|
||||
('lookahead_scores', BOOL, False, 'extract lookahead scores. A utility that can only be used from the DIMACS front-end'),
|
||||
('lookahead.double', BOOL, True, 'enable double lookahead'),
|
||||
('lookahead.use_learned', BOOL, False, 'use learned clauses when selecting lookahead literal'),
|
||||
('lookahead_simplify.bca', BOOL, True, 'add learned binary clauses as part of lookahead simplification'),
|
||||
('lookahead.global_autarky', BOOL, False, 'prefer to branch on variables that occur in clauses that are reduced'),
|
||||
('lookahead.delta_fraction', DOUBLE, 1.0, 'number between 0 and 1, the smaller the more literals are selected for double lookahead'),
|
||||
('lookahead.reward', SYMBOL, 'march_cu', 'select lookahead heuristic: ternary, heule_schur (Heule Schur), heuleu (Heule Unit), unit, or march_cu'))
|
||||
# reward function used to determine which literal to cube on.
|
||||
# - ternary: reward function useful for random 3-SAT instances. Used by Heule and Knuth in March.
|
||||
# - heule_schur: reward function based on "Schur Number 5", Heule, AAAI 2018
|
||||
# The score of a literal lit is:
|
||||
# Sum_{C in Clauses | lit in C} 2 ^ (- |C|+1)
|
||||
# * Sum_{lit' in C | lit' != lit} lit_occs(~lit')
|
||||
# / | C |
|
||||
# where lit_occs(lit) is the number of clauses containing lit.
|
||||
# - heuleu: The score of a literal lit is: Sum_{C in Clauses | lit in C} 2 ^ (-|C| + 1)
|
||||
# - unit: heule_schur + also counts number of unit clauses.
|
||||
# - march_cu: default reward function used in a version of March
|
||||
# Each reward function also comes with its own variant of "mix_diff", which
|
||||
# is the function for combining reward metrics for the positive and negative variant of a literal.
|
||||
)
|
||||
|
|
@ -29,7 +29,7 @@ Revision History:
|
|||
#include "sat/sat_solver.h"
|
||||
#include "sat/sat_integrity_checker.h"
|
||||
#include "sat/sat_lookahead.h"
|
||||
#include "sat/sat_ddfw.h"
|
||||
#include "sat/sat_ddfw_wrapper.h"
|
||||
#include "sat/sat_prob.h"
|
||||
#include "sat/sat_anf_simplifier.h"
|
||||
#include "sat/sat_cut_simplifier.h"
|
||||
|
@ -1365,7 +1365,7 @@ namespace sat {
|
|||
}
|
||||
literal_vector _lits;
|
||||
scoped_limits scoped_rl(rlimit());
|
||||
m_local_search = alloc(ddfw);
|
||||
m_local_search = alloc(ddfw_wrapper);
|
||||
scoped_ls _ls(*this);
|
||||
SASSERT(m_local_search);
|
||||
m_local_search->add(*this);
|
||||
|
@ -1442,7 +1442,7 @@ namespace sat {
|
|||
lbool solver::do_ddfw_search(unsigned num_lits, literal const* lits) {
|
||||
if (m_ext) return l_undef;
|
||||
SASSERT(!m_local_search);
|
||||
m_local_search = alloc(ddfw);
|
||||
m_local_search = alloc(ddfw_wrapper);
|
||||
return invoke_local_search(num_lits, lits);
|
||||
}
|
||||
|
||||
|
@ -1485,7 +1485,7 @@ namespace sat {
|
|||
|
||||
// set up ddfw search
|
||||
for (int i = 0; i < num_ddfw; ++i) {
|
||||
ddfw* d = alloc(ddfw);
|
||||
ddfw_wrapper* d = alloc(ddfw_wrapper);
|
||||
d->updt_params(m_params);
|
||||
d->set_seed(m_config.m_random_seed + i);
|
||||
d->add(*this);
|
||||
|
@ -2932,6 +2932,7 @@ namespace sat {
|
|||
bool_var v = m_trail[i].var();
|
||||
m_best_phase[v] = m_phase[v];
|
||||
}
|
||||
set_has_new_best_phase(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -152,6 +152,7 @@ namespace sat {
|
|||
bool_vector m_phase;
|
||||
bool_vector m_best_phase;
|
||||
bool_vector m_prev_phase;
|
||||
bool m_new_best_phase = false;
|
||||
svector<char> m_assigned_since_gc;
|
||||
search_state m_search_state;
|
||||
unsigned m_search_unsat_conflicts;
|
||||
|
@ -228,7 +229,7 @@ namespace sat {
|
|||
friend class parallel;
|
||||
friend class lookahead;
|
||||
friend class local_search;
|
||||
friend class ddfw;
|
||||
friend class ddfw_wrapper;
|
||||
friend class prob;
|
||||
friend class unit_walk;
|
||||
friend struct mk_stat;
|
||||
|
@ -380,6 +381,9 @@ namespace sat {
|
|||
bool was_eliminated(literal l) const { return was_eliminated(l.var()); }
|
||||
void set_phase(literal l) override { if (l.var() < num_vars()) m_best_phase[l.var()] = m_phase[l.var()] = !l.sign(); }
|
||||
bool get_phase(bool_var b) { return m_phase.get(b, false); }
|
||||
bool get_best_phase(bool_var b) { return m_best_phase.get(b, false); }
|
||||
void set_has_new_best_phase(bool b) { m_new_best_phase = b; }
|
||||
bool has_new_best_phase() const { return m_new_best_phase; }
|
||||
void move_to_front(bool_var b);
|
||||
unsigned scope_lvl() const { return m_scope_lvl; }
|
||||
unsigned search_lvl() const { return m_search_lvl; }
|
||||
|
|
|
@ -39,7 +39,7 @@ Notes:
|
|||
#include "model/model_v2_pp.h"
|
||||
#include "model/model_evaluator.h"
|
||||
#include "sat/sat_solver.h"
|
||||
#include "sat/sat_params.hpp"
|
||||
#include "params/sat_params.hpp"
|
||||
#include "sat/smt/euf_solver.h"
|
||||
#include "sat/tactic/goal2sat.h"
|
||||
#include "sat/tactic/sat2goal.h"
|
||||
|
|
|
@ -33,7 +33,7 @@ Notes:
|
|||
#include "model/model_evaluator.h"
|
||||
#include "sat/sat_solver.h"
|
||||
#include "solver/simplifier_solver.h"
|
||||
#include "sat/sat_params.hpp"
|
||||
#include "params/sat_params.hpp"
|
||||
#include "sat/smt/euf_solver.h"
|
||||
#include "sat/tactic/goal2sat.h"
|
||||
#include "sat/tactic/sat2goal.h"
|
||||
|
@ -586,8 +586,9 @@ private:
|
|||
|
||||
void add_assumption(expr* a) {
|
||||
init_goal2sat();
|
||||
m_dep.insert(a, m_goal2sat.internalize(a));
|
||||
get_euf()->add_assertion(a);
|
||||
auto lit = m_goal2sat.internalize(a);
|
||||
m_dep.insert(a, lit);
|
||||
get_euf()->add_clause(1, &lit);
|
||||
}
|
||||
|
||||
void internalize_assumptions(expr_ref_vector const& asms) {
|
||||
|
|
|
@ -3,7 +3,6 @@ z3_add_component(sat_smt
|
|||
arith_axioms.cpp
|
||||
arith_diagnostics.cpp
|
||||
arith_internalize.cpp
|
||||
arith_sls.cpp
|
||||
arith_solver.cpp
|
||||
arith_value.cpp
|
||||
array_axioms.cpp
|
||||
|
@ -22,7 +21,6 @@ z3_add_component(sat_smt
|
|||
euf_ackerman.cpp
|
||||
euf_internalize.cpp
|
||||
euf_invariant.cpp
|
||||
euf_local_search.cpp
|
||||
euf_model.cpp
|
||||
euf_proof.cpp
|
||||
euf_proof_checker.cpp
|
||||
|
|
|
@ -547,6 +547,7 @@ namespace arith {
|
|||
}
|
||||
|
||||
void solver::new_diseq_eh(euf::th_eq const& e) {
|
||||
TRACE("artih", tout << mk_bounded_pp(e.eq(), m) << "\n");
|
||||
ensure_column(e.v1());
|
||||
ensure_column(e.v2());
|
||||
m_delayed_eqs.push_back(std::make_pair(e, false));
|
||||
|
|
|
@ -1,642 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2023 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
arith_local_search.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Local search dispatch for SMT
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2023-02-07
|
||||
|
||||
--*/
|
||||
#include "sat/sat_solver.h"
|
||||
#include "sat/smt/arith_solver.h"
|
||||
|
||||
|
||||
namespace arith {
|
||||
|
||||
sls::sls(solver& s):
|
||||
s(s), m(s.m) {}
|
||||
|
||||
void sls::reset() {
|
||||
m_bool_vars.reset();
|
||||
m_vars.reset();
|
||||
m_terms.reset();
|
||||
}
|
||||
|
||||
void sls::save_best_values() {
|
||||
for (unsigned v = 0; v < s.get_num_vars(); ++v)
|
||||
m_vars[v].m_best_value = m_vars[v].m_value;
|
||||
check_ineqs();
|
||||
if (unsat().size() == 1) {
|
||||
auto idx = *unsat().begin();
|
||||
verbose_stream() << idx << "\n";
|
||||
auto const& c = *m_bool_search->m_clauses[idx].m_clause;
|
||||
verbose_stream() << c << "\n";
|
||||
for (auto lit : c) {
|
||||
bool_var bv = lit.var();
|
||||
ineq* i = atom(bv);
|
||||
if (i)
|
||||
verbose_stream() << lit << ": " << *i << "\n";
|
||||
}
|
||||
verbose_stream() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void sls::store_best_values() {
|
||||
// first compute assignment to terms
|
||||
// then update non-basic variables in tableau.
|
||||
|
||||
if (!unsat().empty())
|
||||
return;
|
||||
|
||||
for (auto const& [t,v] : m_terms) {
|
||||
int64_t val = 0;
|
||||
lp::lar_term const& term = s.lp().get_term(t);
|
||||
for (lp::lar_term::ival const& arg : term) {
|
||||
auto t2 = arg.j();
|
||||
auto w = s.lp().local_to_external(t2);
|
||||
val += to_numeral(arg.coeff()) * m_vars[w].m_best_value;
|
||||
}
|
||||
m_vars[v].m_best_value = val;
|
||||
}
|
||||
|
||||
for (unsigned v = 0; v < s.get_num_vars(); ++v) {
|
||||
if (s.is_bool(v))
|
||||
continue;
|
||||
if (!s.lp().external_is_used(v))
|
||||
continue;
|
||||
int64_t new_value = m_vars[v].m_best_value;
|
||||
s.ensure_column(v);
|
||||
lp::lpvar vj = s.lp().external_to_local(v);
|
||||
SASSERT(vj != lp::null_lpvar);
|
||||
if (!s.lp().is_base(vj)) {
|
||||
rational new_value_(new_value, rational::i64());
|
||||
lp::impq val(new_value_, rational::zero());
|
||||
s.lp().set_value_for_nbasic_column(vj, val);
|
||||
}
|
||||
}
|
||||
|
||||
lbool r = s.make_feasible();
|
||||
VERIFY (!unsat().empty() || r == l_true);
|
||||
#if 0
|
||||
if (unsat().empty())
|
||||
s.m_num_conflicts = s.get_config().m_arith_propagation_threshold;
|
||||
#endif
|
||||
|
||||
auto check_bool_var = [&](sat::bool_var bv) {
|
||||
auto* ineq = m_bool_vars.get(bv, nullptr);
|
||||
if (!ineq)
|
||||
return;
|
||||
api_bound* b = nullptr;
|
||||
s.m_bool_var2bound.find(bv, b);
|
||||
if (!b)
|
||||
return;
|
||||
auto bound = b->get_value();
|
||||
theory_var v = b->get_var();
|
||||
if (s.get_phase(bv) == m_bool_search->get_model()[bv])
|
||||
return;
|
||||
switch (b->get_bound_kind()) {
|
||||
case lp_api::lower_t:
|
||||
verbose_stream() << "v" << v << " " << bound << " <= " << s.get_value(v) << " " << m_vars[v].m_best_value << "\n";
|
||||
break;
|
||||
case lp_api::upper_t:
|
||||
verbose_stream() << "v" << v << " " << bound << " >= " << s.get_value(v) << " " << m_vars[v].m_best_value << "\n";
|
||||
break;
|
||||
}
|
||||
int64_t value = 0;
|
||||
for (auto const& [coeff, v] : ineq->m_args) {
|
||||
value += coeff * m_vars[v].m_best_value;
|
||||
}
|
||||
ineq->m_args_value = value;
|
||||
verbose_stream() << *ineq << " dtt " << dtt(false, *ineq) << " phase " << s.get_phase(bv) << " model " << m_bool_search->get_model()[bv] << "\n";
|
||||
for (auto const& [coeff, v] : ineq->m_args)
|
||||
verbose_stream() << "v" << v << " := " << m_vars[v].m_best_value << "\n";
|
||||
s.display(verbose_stream());
|
||||
display(verbose_stream());
|
||||
UNREACHABLE();
|
||||
exit(0);
|
||||
};
|
||||
|
||||
if (unsat().empty()) {
|
||||
for (bool_var v = 0; v < s.s().num_vars(); ++v)
|
||||
check_bool_var(v);
|
||||
}
|
||||
}
|
||||
|
||||
void sls::set(sat::ddfw* d) {
|
||||
m_bool_search = d;
|
||||
reset();
|
||||
m_bool_vars.reserve(s.s().num_vars());
|
||||
add_vars();
|
||||
for (unsigned i = 0; i < d->num_clauses(); ++i)
|
||||
for (sat::literal lit : *d->get_clause_info(i).m_clause)
|
||||
init_bool_var(lit.var());
|
||||
for (unsigned v = 0; v < s.s().num_vars(); ++v)
|
||||
init_bool_var_assignment(v);
|
||||
|
||||
d->set(this);
|
||||
}
|
||||
|
||||
// distance to true
|
||||
int64_t sls::dtt(bool sign, int64_t args, ineq const& ineq) const {
|
||||
switch (ineq.m_op) {
|
||||
case ineq_kind::LE:
|
||||
if (sign) {
|
||||
if (args <= ineq.m_bound)
|
||||
return ineq.m_bound - args + 1;
|
||||
return 0;
|
||||
}
|
||||
if (args <= ineq.m_bound)
|
||||
return 0;
|
||||
return args - ineq.m_bound;
|
||||
case ineq_kind::EQ:
|
||||
if (sign) {
|
||||
if (args == ineq.m_bound)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
if (args == ineq.m_bound)
|
||||
return 0;
|
||||
return 1;
|
||||
case ineq_kind::NE:
|
||||
if (sign) {
|
||||
if (args == ineq.m_bound)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
if (args == ineq.m_bound)
|
||||
return 1;
|
||||
return 0;
|
||||
case ineq_kind::LT:
|
||||
if (sign) {
|
||||
if (args < ineq.m_bound)
|
||||
return ineq.m_bound - args;
|
||||
return 0;
|
||||
}
|
||||
if (args < ineq.m_bound)
|
||||
return 0;
|
||||
return args - ineq.m_bound + 1;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// dtt is high overhead. It walks ineq.m_args
|
||||
// m_vars[w].m_value can be computed outside and shared among calls
|
||||
// different data-structures for storing coefficients
|
||||
//
|
||||
int64_t sls::dtt(bool sign, ineq const& ineq, var_t v, int64_t new_value) const {
|
||||
for (auto const& [coeff, w] : ineq.m_args)
|
||||
if (w == v)
|
||||
return dtt(sign, ineq.m_args_value + coeff * (new_value - m_vars[v].m_value), ineq);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int64_t sls::dtt(bool sign, ineq const& ineq, int64_t coeff, int64_t old_value, int64_t new_value) const {
|
||||
return dtt(sign, ineq.m_args_value + coeff * (new_value - old_value), ineq);
|
||||
}
|
||||
|
||||
bool sls::cm(bool old_sign, ineq const& ineq, var_t v, int64_t& new_value) {
|
||||
for (auto const& [coeff, w] : ineq.m_args)
|
||||
if (w == v)
|
||||
return cm(old_sign, ineq, v, coeff, new_value);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sls::cm(bool old_sign, ineq const& ineq, var_t v, int64_t coeff, int64_t& new_value) {
|
||||
SASSERT(ineq.is_true() != old_sign);
|
||||
VERIFY(ineq.is_true() != old_sign);
|
||||
auto bound = ineq.m_bound;
|
||||
auto argsv = ineq.m_args_value;
|
||||
bool solved = false;
|
||||
int64_t delta = argsv - bound;
|
||||
auto make_eq = [&]() {
|
||||
SASSERT(delta != 0);
|
||||
if (delta < 0)
|
||||
new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff;
|
||||
else
|
||||
new_value = value(v) - (delta + abs(coeff) - 1) / coeff;
|
||||
solved = argsv + coeff * (new_value - value(v)) == bound;
|
||||
if (!solved && abs(coeff) == 1) {
|
||||
verbose_stream() << "did not solve equality " << ineq << " for " << v << "\n";
|
||||
verbose_stream() << new_value << " " << value(v) << " delta " << delta << " lhs " << (argsv + coeff * (new_value - value(v))) << " bound " << bound << "\n";
|
||||
UNREACHABLE();
|
||||
}
|
||||
return solved;
|
||||
};
|
||||
|
||||
auto make_diseq = [&]() {
|
||||
if (delta >= 0)
|
||||
delta++;
|
||||
else
|
||||
delta--;
|
||||
new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff;
|
||||
VERIFY(argsv + coeff * (new_value - value(v)) != bound);
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!old_sign) {
|
||||
switch (ineq.m_op) {
|
||||
case ineq_kind::LE:
|
||||
// args <= bound -> args > bound
|
||||
SASSERT(argsv <= bound);
|
||||
SASSERT(delta <= 0);
|
||||
--delta;
|
||||
new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff;
|
||||
VERIFY(argsv + coeff * (new_value - value(v)) > bound);
|
||||
return true;
|
||||
case ineq_kind::LT:
|
||||
// args < bound -> args >= bound
|
||||
SASSERT(argsv <= ineq.m_bound);
|
||||
SASSERT(delta <= 0);
|
||||
new_value = value(v) + (abs(delta) + abs(coeff) - 1) / coeff;
|
||||
VERIFY(argsv + coeff * (new_value - value(v)) >= bound);
|
||||
return true;
|
||||
case ineq_kind::EQ:
|
||||
return make_diseq();
|
||||
case ineq_kind::NE:
|
||||
return make_eq();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (ineq.m_op) {
|
||||
case ineq_kind::LE:
|
||||
SASSERT(argsv > ineq.m_bound);
|
||||
SASSERT(delta > 0);
|
||||
new_value = value(v) - (delta + abs(coeff) - 1) / coeff;
|
||||
VERIFY(argsv + coeff * (new_value - value(v)) <= bound);
|
||||
return true;
|
||||
case ineq_kind::LT:
|
||||
SASSERT(argsv >= ineq.m_bound);
|
||||
SASSERT(delta >= 0);
|
||||
++delta;
|
||||
new_value = value(v) - (abs(delta) + abs(coeff) - 1) / coeff;
|
||||
VERIFY(argsv + coeff * (new_value - value(v)) < bound);
|
||||
return true;
|
||||
case ineq_kind::NE:
|
||||
return make_diseq();
|
||||
case ineq_kind::EQ:
|
||||
return make_eq();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// flip on the first positive score
|
||||
// it could be changed to flip on maximal positive score
|
||||
// or flip on maximal non-negative score
|
||||
// or flip on first non-negative score
|
||||
bool sls::flip(bool sign, ineq const& ineq) {
|
||||
int64_t new_value;
|
||||
auto v = ineq.m_var_to_flip;
|
||||
if (v == UINT_MAX) {
|
||||
IF_VERBOSE(1, verbose_stream() << "no var to flip\n");
|
||||
return false;
|
||||
}
|
||||
if (!cm(sign, ineq, v, new_value)) {
|
||||
verbose_stream() << "no critical move for " << v << "\n";
|
||||
return false;
|
||||
}
|
||||
update(v, new_value);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// dscore(op) = sum_c (dts(c,alpha) - dts(c,alpha_after)) * weight(c)
|
||||
// TODO - use cached dts instead of computed dts
|
||||
// cached dts has to be updated when the score of literals are updated.
|
||||
//
|
||||
double sls::dscore(var_t v, int64_t new_value) const {
|
||||
double score = 0;
|
||||
auto const& vi = m_vars[v];
|
||||
for (auto const& [coeff, bv] : vi.m_bool_vars) {
|
||||
sat::literal lit(bv, false);
|
||||
for (auto cl : m_bool_search->get_use_list(lit))
|
||||
score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl);
|
||||
for (auto cl : m_bool_search->get_use_list(~lit))
|
||||
score += (compute_dts(cl) - dts(cl, v, new_value)) * m_bool_search->get_weight(cl);
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
//
|
||||
// cm_score is costly. It involves several cache misses.
|
||||
// Note that
|
||||
// - m_bool_search->get_use_list(lit).size() is "often" 1 or 2
|
||||
// - dtt_old can be saved
|
||||
//
|
||||
int sls::cm_score(var_t v, int64_t new_value) {
|
||||
int score = 0;
|
||||
auto& vi = m_vars[v];
|
||||
int64_t old_value = vi.m_value;
|
||||
for (auto const& [coeff, bv] : vi.m_bool_vars) {
|
||||
auto const& ineq = *atom(bv);
|
||||
bool old_sign = sign(bv);
|
||||
int64_t dtt_old = dtt(old_sign, ineq);
|
||||
int64_t dtt_new = dtt(old_sign, ineq, coeff, old_value, new_value);
|
||||
if ((dtt_old == 0) == (dtt_new == 0))
|
||||
continue;
|
||||
sat::literal lit(bv, old_sign);
|
||||
if (dtt_old == 0)
|
||||
// flip from true to false
|
||||
lit.neg();
|
||||
|
||||
// lit flips form false to true:
|
||||
for (auto cl : m_bool_search->get_use_list(lit)) {
|
||||
auto const& clause = get_clause_info(cl);
|
||||
if (!clause.is_true())
|
||||
++score;
|
||||
}
|
||||
// ignore the situation where clause contains multiple literals using v
|
||||
for (auto cl : m_bool_search->get_use_list(~lit)) {
|
||||
auto const& clause = get_clause_info(cl);
|
||||
if (clause.m_num_trues == 1)
|
||||
--score;
|
||||
}
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
int64_t sls::compute_dts(unsigned cl) const {
|
||||
int64_t d(1), d2;
|
||||
bool first = true;
|
||||
for (auto a : get_clause(cl)) {
|
||||
auto const* ineq = atom(a.var());
|
||||
if (!ineq)
|
||||
continue;
|
||||
d2 = dtt(a.sign(), *ineq);
|
||||
if (first)
|
||||
d = d2, first = false;
|
||||
else
|
||||
d = std::min(d, d2);
|
||||
if (d == 0)
|
||||
break;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
int64_t sls::dts(unsigned cl, var_t v, int64_t new_value) const {
|
||||
int64_t d(1), d2;
|
||||
bool first = true;
|
||||
for (auto lit : get_clause(cl)) {
|
||||
auto const* ineq = atom(lit.var());
|
||||
if (!ineq)
|
||||
continue;
|
||||
d2 = dtt(lit.sign(), *ineq, v, new_value);
|
||||
if (first)
|
||||
d = d2, first = false;
|
||||
else
|
||||
d = std::min(d, d2);
|
||||
if (d == 0)
|
||||
break;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
void sls::update(var_t v, int64_t new_value) {
|
||||
auto& vi = m_vars[v];
|
||||
auto old_value = vi.m_value;
|
||||
for (auto const& [coeff, bv] : vi.m_bool_vars) {
|
||||
auto& ineq = *atom(bv);
|
||||
bool old_sign = sign(bv);
|
||||
sat::literal lit(bv, old_sign);
|
||||
SASSERT(is_true(lit));
|
||||
ineq.m_args_value += coeff * (new_value - old_value);
|
||||
int64_t dtt_new = dtt(old_sign, ineq);
|
||||
if (dtt_new != 0)
|
||||
m_bool_search->flip(bv);
|
||||
SASSERT(dtt(sign(bv), ineq) == 0);
|
||||
}
|
||||
vi.m_value = new_value;
|
||||
}
|
||||
|
||||
void sls::add_vars() {
|
||||
SASSERT(m_vars.empty());
|
||||
for (unsigned v = 0; v < s.get_num_vars(); ++v) {
|
||||
int64_t value = s.is_registered_var(v) ? to_numeral(s.get_ivalue(v).x) : 0;
|
||||
auto k = s.is_int(v) ? sls::var_kind::INT : sls::var_kind::REAL;
|
||||
m_vars.push_back({ value, value, k, {} });
|
||||
}
|
||||
}
|
||||
|
||||
sls::ineq& sls::new_ineq(ineq_kind op, int64_t const& bound) {
|
||||
auto* i = alloc(ineq);
|
||||
i->m_bound = bound;
|
||||
i->m_op = op;
|
||||
return *i;
|
||||
}
|
||||
|
||||
void sls::add_arg(sat::bool_var bv, ineq& ineq, int64_t const& c, var_t v) {
|
||||
ineq.m_args.push_back({ c, v });
|
||||
ineq.m_args_value += c * value(v);
|
||||
m_vars[v].m_bool_vars.push_back({ c, bv});
|
||||
}
|
||||
|
||||
int64_t sls::to_numeral(rational const& r) {
|
||||
if (r.is_int64())
|
||||
return r.get_int64();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sls::add_args(sat::bool_var bv, ineq& ineq, lp::lpvar t, theory_var v, int64_t sign) {
|
||||
if (s.lp().column_has_term(t)) {
|
||||
lp::lar_term const& term = s.lp().get_term(t);
|
||||
m_terms.push_back({t,v});
|
||||
for (lp::lar_term::ival arg : term) {
|
||||
auto t2 = arg.j();
|
||||
auto w = s.lp().local_to_external(t2);
|
||||
add_arg(bv, ineq, sign * to_numeral(arg.coeff()), w);
|
||||
}
|
||||
}
|
||||
else
|
||||
add_arg(bv, ineq, sign, s.lp().local_to_external(t));
|
||||
}
|
||||
|
||||
void sls::init_bool_var(sat::bool_var bv) {
|
||||
if (m_bool_vars.get(bv, nullptr))
|
||||
return;
|
||||
api_bound* b = nullptr;
|
||||
s.m_bool_var2bound.find(bv, b);
|
||||
if (b) {
|
||||
auto t = b->column_index();
|
||||
rational bound = b->get_value();
|
||||
bool should_minus = false;
|
||||
sls::ineq_kind op;
|
||||
should_minus = b->get_bound_kind() == lp_api::bound_kind::lower_t;
|
||||
op = sls::ineq_kind::LE;
|
||||
if (should_minus)
|
||||
bound.neg();
|
||||
|
||||
auto& ineq = new_ineq(op, to_numeral(bound));
|
||||
|
||||
|
||||
add_args(bv, ineq, t, b->get_var(), should_minus ? -1 : 1);
|
||||
m_bool_vars.set(bv, &ineq);
|
||||
m_bool_search->set_external(bv);
|
||||
return;
|
||||
}
|
||||
|
||||
expr* e = s.bool_var2expr(bv);
|
||||
expr* l = nullptr, * r = nullptr;
|
||||
if (e && m.is_eq(e, l, r) && s.a.is_int_real(l)) {
|
||||
theory_var u = s.get_th_var(l);
|
||||
theory_var v = s.get_th_var(r);
|
||||
lp::lpvar tu = s.get_column(u);
|
||||
lp::lpvar tv = s.get_column(v);
|
||||
auto& ineq = new_ineq(sls::ineq_kind::EQ, 0);
|
||||
add_args(bv, ineq, tu, u, 1);
|
||||
add_args(bv, ineq, tv, v, -1);
|
||||
m_bool_vars.set(bv, &ineq);
|
||||
m_bool_search->set_external(bv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void sls::init_bool_var_assignment(sat::bool_var v) {
|
||||
auto* ineq = m_bool_vars.get(v, nullptr);
|
||||
if (ineq && is_true(sat::literal(v, false)) != (dtt(false, *ineq) == 0))
|
||||
m_bool_search->flip(v);
|
||||
}
|
||||
|
||||
void sls::init_search() {
|
||||
on_restart();
|
||||
}
|
||||
|
||||
void sls::finish_search() {
|
||||
store_best_values();
|
||||
}
|
||||
|
||||
void sls::flip(sat::bool_var v) {
|
||||
sat::literal lit(v, !sign(v));
|
||||
SASSERT(!is_true(lit));
|
||||
auto const* ineq = atom(v);
|
||||
if (!ineq)
|
||||
IF_VERBOSE(0, verbose_stream() << "no inequality for variable " << v << "\n");
|
||||
if (!ineq)
|
||||
return;
|
||||
SASSERT(ineq->is_true() == lit.sign());
|
||||
flip(sign(v), *ineq);
|
||||
}
|
||||
|
||||
double sls::reward(sat::bool_var v) {
|
||||
if (m_dscore_mode)
|
||||
return dscore_reward(v);
|
||||
else
|
||||
return dtt_reward(v);
|
||||
}
|
||||
|
||||
double sls::dtt_reward(sat::bool_var bv0) {
|
||||
bool sign0 = sign(bv0);
|
||||
auto* ineq = atom(bv0);
|
||||
if (!ineq)
|
||||
return -1;
|
||||
int64_t new_value;
|
||||
double max_result = -1;
|
||||
for (auto const & [coeff, x] : ineq->m_args) {
|
||||
if (!cm(sign0, *ineq, x, coeff, new_value))
|
||||
continue;
|
||||
double result = 0;
|
||||
auto old_value = m_vars[x].m_value;
|
||||
for (auto const& [coeff, bv] : m_vars[x].m_bool_vars) {
|
||||
result += m_bool_search->reward(bv);
|
||||
continue;
|
||||
bool old_sign = sign(bv);
|
||||
auto dtt_old = dtt(old_sign, *atom(bv));
|
||||
auto dtt_new = dtt(old_sign, *atom(bv), coeff, old_value, new_value);
|
||||
if ((dtt_new == 0) != (dtt_old == 0))
|
||||
result += m_bool_search->reward(bv);
|
||||
}
|
||||
if (result > max_result) {
|
||||
max_result = result;
|
||||
ineq->m_var_to_flip = x;
|
||||
}
|
||||
}
|
||||
return max_result;
|
||||
}
|
||||
|
||||
double sls::dscore_reward(sat::bool_var bv) {
|
||||
m_dscore_mode = false;
|
||||
bool old_sign = sign(bv);
|
||||
sat::literal litv(bv, old_sign);
|
||||
auto* ineq = atom(bv);
|
||||
if (!ineq)
|
||||
return 0;
|
||||
SASSERT(ineq->is_true() != old_sign);
|
||||
int64_t new_value;
|
||||
|
||||
for (auto const& [coeff, v] : ineq->m_args) {
|
||||
double result = 0;
|
||||
if (cm(old_sign, *ineq, v, coeff, new_value))
|
||||
result = dscore(v, new_value);
|
||||
// just pick first positive, or pick a max?
|
||||
if (result > 0) {
|
||||
ineq->m_var_to_flip = v;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// switch to dscore mode
|
||||
void sls::on_rescale() {
|
||||
m_dscore_mode = true;
|
||||
}
|
||||
|
||||
void sls::on_save_model() {
|
||||
save_best_values();
|
||||
}
|
||||
|
||||
void sls::on_restart() {
|
||||
for (unsigned v = 0; v < s.s().num_vars(); ++v)
|
||||
init_bool_var_assignment(v);
|
||||
|
||||
check_ineqs();
|
||||
}
|
||||
|
||||
void sls::check_ineqs() {
|
||||
|
||||
auto check_bool_var = [&](sat::bool_var bv) {
|
||||
auto const* ineq = atom(bv);
|
||||
if (!ineq)
|
||||
return;
|
||||
int64_t d = dtt(sign(bv), *ineq);
|
||||
sat::literal lit(bv, sign(bv));
|
||||
if (is_true(lit) != (d == 0)) {
|
||||
verbose_stream() << "invalid assignment " << bv << " " << *ineq << "\n";
|
||||
}
|
||||
VERIFY(is_true(lit) == (d == 0));
|
||||
};
|
||||
for (unsigned v = 0; v < s.get_num_vars(); ++v)
|
||||
check_bool_var(v);
|
||||
}
|
||||
|
||||
std::ostream& sls::display(std::ostream& out) const {
|
||||
for (bool_var bv = 0; bv < s.s().num_vars(); ++bv) {
|
||||
auto const* ineq = atom(bv);
|
||||
if (!ineq)
|
||||
continue;
|
||||
out << bv << " " << *ineq << "\n";
|
||||
}
|
||||
for (unsigned v = 0; v < s.get_num_vars(); ++v) {
|
||||
if (s.is_bool(v))
|
||||
continue;
|
||||
out << "v" << v << " := " << m_vars[v].m_value << " " << m_vars[v].m_best_value << "\n";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,169 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2020 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
arith_local_search.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Theory plugin for arithmetic local search
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2020-09-08
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/obj_pair_set.h"
|
||||
#include "ast/ast_trail.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "math/lp/indexed_value.h"
|
||||
#include "math/lp/lar_solver.h"
|
||||
#include "math/lp/nla_solver.h"
|
||||
#include "math/lp/lp_types.h"
|
||||
#include "math/lp/lp_api.h"
|
||||
#include "math/polynomial/algebraic_numbers.h"
|
||||
#include "math/polynomial/polynomial.h"
|
||||
#include "sat/smt/sat_th.h"
|
||||
#include "sat/sat_ddfw.h"
|
||||
|
||||
namespace arith {
|
||||
|
||||
class solver;
|
||||
|
||||
// local search portion for arithmetic
|
||||
class sls : public sat::local_search_plugin {
|
||||
enum class ineq_kind { EQ, LE, LT, NE };
|
||||
enum class var_kind { INT, REAL };
|
||||
typedef unsigned var_t;
|
||||
typedef unsigned atom_t;
|
||||
|
||||
struct config {
|
||||
double cb = 0.0;
|
||||
unsigned L = 20;
|
||||
unsigned t = 45;
|
||||
unsigned max_no_improve = 500000;
|
||||
double sp = 0.0003;
|
||||
};
|
||||
|
||||
struct stats {
|
||||
unsigned m_num_flips = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
// encode args <= bound, args = bound, args < bound
|
||||
struct ineq {
|
||||
vector<std::pair<int64_t, var_t>> m_args;
|
||||
ineq_kind m_op = ineq_kind::LE;
|
||||
int64_t m_bound;
|
||||
int64_t m_args_value;
|
||||
unsigned m_var_to_flip = UINT_MAX;
|
||||
|
||||
bool is_true() const {
|
||||
switch (m_op) {
|
||||
case ineq_kind::LE:
|
||||
return m_args_value <= m_bound;
|
||||
case ineq_kind::EQ:
|
||||
return m_args_value == m_bound;
|
||||
case ineq_kind::NE:
|
||||
return m_args_value != m_bound;
|
||||
default:
|
||||
return m_args_value < m_bound;
|
||||
}
|
||||
}
|
||||
std::ostream& display(std::ostream& out) const {
|
||||
bool first = true;
|
||||
for (auto const& [c, v] : m_args)
|
||||
out << (first ? "" : " + ") << c << " * v" << v, first = false;
|
||||
switch (m_op) {
|
||||
case ineq_kind::LE:
|
||||
return out << " <= " << m_bound << "(" << m_args_value << ")";
|
||||
case ineq_kind::EQ:
|
||||
return out << " == " << m_bound << "(" << m_args_value << ")";
|
||||
case ineq_kind::NE:
|
||||
return out << " != " << m_bound << "(" << m_args_value << ")";
|
||||
default:
|
||||
return out << " < " << m_bound << "(" << m_args_value << ")";
|
||||
}
|
||||
}
|
||||
};
|
||||
private:
|
||||
|
||||
struct var_info {
|
||||
int64_t m_value;
|
||||
int64_t m_best_value;
|
||||
var_kind m_kind = var_kind::INT;
|
||||
svector<std::pair<int64_t, sat::bool_var>> m_bool_vars;
|
||||
};
|
||||
|
||||
solver& s;
|
||||
ast_manager& m;
|
||||
sat::ddfw* m_bool_search = nullptr;
|
||||
stats m_stats;
|
||||
config m_config;
|
||||
scoped_ptr_vector<ineq> m_bool_vars;
|
||||
vector<var_info> m_vars;
|
||||
svector<std::pair<lp::lpvar, euf::theory_var>> m_terms;
|
||||
bool m_dscore_mode = false;
|
||||
|
||||
|
||||
indexed_uint_set& unsat() { return m_bool_search->unsat_set(); }
|
||||
unsigned num_clauses() const { return m_bool_search->num_clauses(); }
|
||||
sat::clause& get_clause(unsigned idx) { return *get_clause_info(idx).m_clause; }
|
||||
sat::clause const& get_clause(unsigned idx) const { return *get_clause_info(idx).m_clause; }
|
||||
sat::ddfw::clause_info& get_clause_info(unsigned idx) { return m_bool_search->get_clause_info(idx); }
|
||||
sat::ddfw::clause_info const& get_clause_info(unsigned idx) const { return m_bool_search->get_clause_info(idx); }
|
||||
bool is_true(sat::literal lit) { return lit.sign() != m_bool_search->get_value(lit.var()); }
|
||||
bool sign(sat::bool_var v) const { return !m_bool_search->get_value(v); }
|
||||
|
||||
void reset();
|
||||
ineq* atom(sat::bool_var bv) const { return m_bool_vars[bv]; }
|
||||
|
||||
bool flip(bool sign, ineq const& ineq);
|
||||
int64_t dtt(bool sign, ineq const& ineq) const { return dtt(sign, ineq.m_args_value, ineq); }
|
||||
int64_t dtt(bool sign, int64_t args_value, ineq const& ineq) const;
|
||||
int64_t dtt(bool sign, ineq const& ineq, var_t v, int64_t new_value) const;
|
||||
int64_t dtt(bool sign, ineq const& ineq, int64_t coeff, int64_t old_value, int64_t new_value) const;
|
||||
int64_t dts(unsigned cl, var_t v, int64_t new_value) const;
|
||||
int64_t compute_dts(unsigned cl) const;
|
||||
bool cm(bool sign, ineq const& ineq, var_t v, int64_t& new_value);
|
||||
bool cm(bool sign, ineq const& ineq, var_t v, int64_t coeff, int64_t& new_value);
|
||||
int cm_score(var_t v, int64_t new_value);
|
||||
void update(var_t v, int64_t new_value);
|
||||
double dscore_reward(sat::bool_var v);
|
||||
double dtt_reward(sat::bool_var v);
|
||||
double dscore(var_t v, int64_t new_value) const;
|
||||
void save_best_values();
|
||||
void store_best_values();
|
||||
void add_vars();
|
||||
sls::ineq& new_ineq(ineq_kind op, int64_t const& bound);
|
||||
void add_arg(sat::bool_var bv, ineq& ineq, int64_t const& c, var_t v);
|
||||
void add_args(sat::bool_var bv, ineq& ineq, lp::lpvar j, euf::theory_var v, int64_t sign);
|
||||
void init_bool_var(sat::bool_var v);
|
||||
void init_bool_var_assignment(sat::bool_var v);
|
||||
|
||||
int64_t value(var_t v) const { return m_vars[v].m_value; }
|
||||
int64_t to_numeral(rational const& r);
|
||||
|
||||
void check_ineqs();
|
||||
|
||||
std::ostream& display(std::ostream& out) const;
|
||||
|
||||
public:
|
||||
sls(solver& s);
|
||||
void set(sat::ddfw* d);
|
||||
void init_search() override;
|
||||
void finish_search() override;
|
||||
void flip(sat::bool_var v) override;
|
||||
double reward(sat::bool_var v) override;
|
||||
void on_rescale() override;
|
||||
void on_save_model() override;
|
||||
void on_restart() override;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, sls::ineq const& ineq) {
|
||||
return ineq.display(out);
|
||||
}
|
||||
}
|
|
@ -24,7 +24,6 @@ namespace arith {
|
|||
solver::solver(euf::solver& ctx, theory_id id) :
|
||||
th_euf_solver(ctx, symbol("arith"), id),
|
||||
m_model_eqs(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)),
|
||||
m_local_search(*this),
|
||||
m_resource_limit(*this),
|
||||
m_bp(*this, m_implied_bounds),
|
||||
a(m),
|
||||
|
@ -988,10 +987,10 @@ namespace arith {
|
|||
}
|
||||
|
||||
bool solver::use_nra_model() {
|
||||
return m_nla && m_nla->use_nra_model();
|
||||
return m_nla && m_use_nra_model && m_nla->use_nra_model();
|
||||
}
|
||||
|
||||
bool solver::is_eq(theory_var v1, theory_var v2) {
|
||||
bool solver::is_eq(theory_var v1, theory_var v2) {
|
||||
if (use_nra_model()) {
|
||||
return m_nla->am().eq(nl_value(v1, m_nla->tmp1()), nl_value(v2, m_nla->tmp2()));
|
||||
}
|
||||
|
@ -1006,6 +1005,8 @@ namespace arith {
|
|||
IF_VERBOSE(12, verbose_stream() << "final-check " << lp().get_status() << "\n");
|
||||
SASSERT(lp().ax_is_correct());
|
||||
|
||||
m_use_nra_model = false;
|
||||
|
||||
if (!lp().is_feasible() || lp().has_changed_columns()) {
|
||||
switch (make_feasible()) {
|
||||
case l_false:
|
||||
|
@ -1038,8 +1039,12 @@ namespace arith {
|
|||
break;
|
||||
}
|
||||
|
||||
if (!check_delayed_eqs())
|
||||
return sat::check_result::CR_CONTINUE;
|
||||
|
||||
switch (check_nla()) {
|
||||
case l_true:
|
||||
m_use_nra_model = true;
|
||||
break;
|
||||
case l_false:
|
||||
return sat::check_result::CR_CONTINUE;
|
||||
|
@ -1053,7 +1058,8 @@ namespace arith {
|
|||
++m_stats.m_assume_eqs;
|
||||
return sat::check_result::CR_CONTINUE;
|
||||
}
|
||||
if (!check_delayed_eqs())
|
||||
|
||||
if (!check_delayed_eqs())
|
||||
return sat::check_result::CR_CONTINUE;
|
||||
|
||||
if (!int_undef && !check_bv_terms())
|
||||
|
@ -1141,6 +1147,7 @@ namespace arith {
|
|||
new_eq_eh(e);
|
||||
else if (is_eq(e.v1(), e.v2())) {
|
||||
mk_diseq_axiom(e.v1(), e.v2());
|
||||
TRACE("arith", tout << mk_bounded_pp(e.eq(), m) << " " << use_nra_model() << "\n");
|
||||
found_diseq = true;
|
||||
break;
|
||||
}
|
||||
|
@ -1249,9 +1256,9 @@ namespace arith {
|
|||
for (auto ev : m_explanation)
|
||||
set_evidence(ev.ci());
|
||||
|
||||
TRACE("arith",
|
||||
TRACE("arith_conflict",
|
||||
tout << "Lemma - " << (is_conflict ? "conflict" : "propagation") << "\n";
|
||||
for (literal c : m_core) tout << c << ": " << literal2expr(c) << "\n";
|
||||
for (literal c : m_core) tout << c << ": " << literal2expr(c) << " := " << s().value(c) << "\n";
|
||||
for (auto p : m_eqs) tout << ctx.bpp(p.first) << " == " << ctx.bpp(p.second) << "\n";);
|
||||
|
||||
if (ctx.get_config().m_arith_validate)
|
||||
|
@ -1271,6 +1278,10 @@ namespace arith {
|
|||
m_core.push_back(ctx.mk_literal(m.mk_eq(eq.first->get_expr(), eq.second->get_expr())));
|
||||
for (literal& c : m_core)
|
||||
c.neg();
|
||||
|
||||
// it is possible if multiple lemmas are added at the same time.
|
||||
if (any_of(m_core, [&](literal c) { return s().value(c) == l_true; }))
|
||||
return;
|
||||
|
||||
add_redundant(m_core, explain(ty));
|
||||
}
|
||||
|
@ -1508,6 +1519,7 @@ namespace arith {
|
|||
case l_undef:
|
||||
break;
|
||||
}
|
||||
TRACE("arith", tout << "nla " << r << "\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -1521,10 +1533,13 @@ namespace arith {
|
|||
}
|
||||
for (auto const& ineq : m_nla->literals()) {
|
||||
auto lit = mk_ineq_literal(ineq);
|
||||
if (s().value(lit) == l_true)
|
||||
continue;
|
||||
ctx.mark_relevant(lit);
|
||||
s().set_phase(lit);
|
||||
// verbose_stream() << lit << ":= " << s().value(lit) << "\n";
|
||||
// force trichotomy axiom for equality literals
|
||||
if (ineq.cmp() == lp::EQ) {
|
||||
if (ineq.cmp() == lp::EQ && false) {
|
||||
nla::lemma l;
|
||||
l.push_back(ineq);
|
||||
l.push_back(nla::ineq(lp::LT, ineq.term(), ineq.rs()));
|
||||
|
|
|
@ -28,8 +28,6 @@ Author:
|
|||
#include "math/polynomial/algebraic_numbers.h"
|
||||
#include "math/polynomial/polynomial.h"
|
||||
#include "sat/smt/sat_th.h"
|
||||
#include "sat/smt/arith_sls.h"
|
||||
#include "sat/sat_ddfw.h"
|
||||
|
||||
namespace euf {
|
||||
class solver;
|
||||
|
@ -186,8 +184,6 @@ namespace arith {
|
|||
coeffs().pop_back();
|
||||
}
|
||||
};
|
||||
|
||||
sls m_local_search;
|
||||
|
||||
typedef vector<std::pair<rational, lpvar>> var_coeffs;
|
||||
vector<rational> m_columns;
|
||||
|
@ -234,6 +230,7 @@ namespace arith {
|
|||
|
||||
// non-linear arithmetic
|
||||
scoped_ptr<nla::solver> m_nla;
|
||||
bool m_use_nra_model = false;
|
||||
|
||||
// integer arithmetic
|
||||
scoped_ptr<lp::int_solver> m_lia;
|
||||
|
@ -513,8 +510,6 @@ namespace arith {
|
|||
bool enable_ackerman_axioms(euf::enode* n) const override { return !a.is_add(n->get_expr()); }
|
||||
bool has_unhandled() const override { return m_not_handled != nullptr; }
|
||||
|
||||
void set_bool_search(sat::ddfw* ddfw) override { m_local_search.set(ddfw); }
|
||||
|
||||
// bounds and equality propagation callbacks
|
||||
lp::lar_solver& lp() { return *m_solver; }
|
||||
lp::lar_solver const& lp() const { return *m_solver; }
|
||||
|
|
|
@ -118,8 +118,8 @@ namespace bv {
|
|||
}
|
||||
}
|
||||
|
||||
if (glue < max_glue)
|
||||
v.m_glue = (sz > 6 && 2*glue <= sz) ? 0 : glue;
|
||||
if (glue < max_glue)
|
||||
v.m_glue = glue; // (sz > 6 && 2 * glue <= sz) ? 0 : glue;
|
||||
}
|
||||
|
||||
void ackerman::remove(vv* p) {
|
||||
|
|
|
@ -525,8 +525,8 @@ namespace euf {
|
|||
return n;
|
||||
}
|
||||
|
||||
void solver::add_assertion(expr* f) {
|
||||
m_assertions.push_back(f);
|
||||
m_trail.push(push_back_vector(m_assertions));
|
||||
void solver::add_clause(unsigned n, sat::literal const* lits) {
|
||||
m_top_level_clauses.push_back(sat::literal_vector(n, lits));
|
||||
m_trail.push(push_back_vector(m_top_level_clauses));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2020 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
euf_local_search.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Local search dispatch for SMT
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2023-02-07
|
||||
|
||||
--*/
|
||||
#include "sat/sat_solver.h"
|
||||
#include "sat/sat_ddfw.h"
|
||||
#include "sat/smt/euf_solver.h"
|
||||
|
||||
|
||||
namespace euf {
|
||||
|
||||
lbool solver::local_search(bool_vector& phase) {
|
||||
scoped_limits scoped_rl(m.limit());
|
||||
sat::ddfw bool_search;
|
||||
bool_search.reinit(s(), phase);
|
||||
bool_search.updt_params(s().params());
|
||||
bool_search.set_seed(rand());
|
||||
scoped_rl.push_child(&(bool_search.rlimit()));
|
||||
|
||||
for (auto* th : m_solvers)
|
||||
th->set_bool_search(&bool_search);
|
||||
|
||||
bool_search.check(0, nullptr, nullptr);
|
||||
|
||||
auto const& mdl = bool_search.get_model();
|
||||
for (unsigned i = 0; i < mdl.size(); ++i)
|
||||
phase[i] = mdl[i] == l_true;
|
||||
|
||||
if (bool_search.unsat_set().empty()) {
|
||||
enable_trace("arith");
|
||||
enable_trace("sat");
|
||||
enable_trace("euf");
|
||||
TRACE("sat", s().display(tout));
|
||||
}
|
||||
|
||||
return bool_search.unsat_set().empty() ? l_true : l_undef;
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ Author:
|
|||
#include "ast/ast_ll_pp.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "smt/smt_solver.h"
|
||||
#include "sat/sat_params.hpp"
|
||||
#include "params/sat_params.hpp"
|
||||
#include "sat/smt/euf_proof_checker.h"
|
||||
#include "sat/smt/arith_theory_checker.h"
|
||||
#include "sat/smt/q_theory_checker.h"
|
||||
|
|
|
@ -55,7 +55,6 @@ namespace euf {
|
|||
m_smt_proof_checker(m, p),
|
||||
m_clause(m),
|
||||
m_expr_args(m),
|
||||
m_assertions(m),
|
||||
m_values(m)
|
||||
{
|
||||
updt_params(p);
|
||||
|
|
|
@ -100,15 +100,6 @@ namespace euf {
|
|||
scope(unsigned l) : m_var_lim(l) {}
|
||||
};
|
||||
|
||||
struct local_search_config {
|
||||
double cb = 0.0;
|
||||
unsigned L = 20;
|
||||
unsigned t = 45;
|
||||
unsigned max_no_improve = 500000;
|
||||
double sp = 0.0003;
|
||||
};
|
||||
|
||||
|
||||
size_t* to_ptr(sat::literal l) { return TAG(size_t*, reinterpret_cast<size_t*>((size_t)(l.index() << 4)), 1); }
|
||||
size_t* to_ptr(size_t jst) { return TAG(size_t*, reinterpret_cast<size_t*>(jst), 2); }
|
||||
bool is_literal(size_t* p) const { return GET_TAG(p) == 1; }
|
||||
|
@ -127,7 +118,6 @@ namespace euf {
|
|||
sat::sat_internalizer& si;
|
||||
relevancy m_relevancy;
|
||||
smt_params m_config;
|
||||
local_search_config m_ls_config;
|
||||
euf::egraph m_egraph;
|
||||
trail_stack m_trail;
|
||||
stats m_stats;
|
||||
|
@ -174,7 +164,7 @@ namespace euf {
|
|||
symbol m_smt = symbol("smt");
|
||||
expr_ref_vector m_clause;
|
||||
expr_ref_vector m_expr_args;
|
||||
expr_ref_vector m_assertions;
|
||||
vector<sat::literal_vector> m_top_level_clauses;
|
||||
|
||||
|
||||
// internalization
|
||||
|
@ -356,7 +346,6 @@ namespace euf {
|
|||
void add_assumptions(sat::literal_set& assumptions) override;
|
||||
bool tracking_assumptions() override;
|
||||
std::string reason_unknown() override { return m_reason_unknown; }
|
||||
lbool local_search(bool_vector& phase) override;
|
||||
|
||||
void propagate(literal lit, ext_justification_idx idx);
|
||||
bool propagate(enode* a, enode* b, ext_justification_idx idx);
|
||||
|
@ -485,8 +474,10 @@ namespace euf {
|
|||
bool enable_ackerman_axioms(expr* n) const;
|
||||
bool is_fixed(euf::enode* n, expr_ref& val, sat::literal_vector& explain);
|
||||
|
||||
void add_assertion(expr* f);
|
||||
expr_ref_vector const& get_assertions() { return m_assertions; }
|
||||
// void add_assertion(expr* f);
|
||||
// expr_ref_vector const& get_assertions() { return m_assertions; }
|
||||
void add_clause(unsigned n, sat::literal const* lits);
|
||||
vector <sat::literal_vector> const& top_level_clauses() const { return m_top_level_clauses; }
|
||||
model_ref get_sls_model();
|
||||
|
||||
// relevancy
|
||||
|
|
|
@ -22,6 +22,10 @@ Author:
|
|||
|
||||
namespace intblast {
|
||||
|
||||
void translator_trail::push(push_back_vector<expr_ref_vector> const& c) { ctx.push(c); }
|
||||
void translator_trail::push(push_back_vector<ptr_vector<app>> const& c) { ctx.push(c); }
|
||||
void translator_trail::push_idx(set_vector_idx_trail<expr_ref_vector> const& c) { ctx.push(c); }
|
||||
|
||||
solver::solver(euf::solver& ctx) :
|
||||
th_euf_solver(ctx, symbol("intblast"), ctx.get_manager().get_family_id("bv")),
|
||||
ctx(ctx),
|
||||
|
@ -29,9 +33,8 @@ namespace intblast {
|
|||
m(ctx.get_manager()),
|
||||
bv(m),
|
||||
a(m),
|
||||
m_translate(m),
|
||||
m_args(m),
|
||||
m_pinned(m)
|
||||
trail(ctx),
|
||||
m_translator(m, trail)
|
||||
{}
|
||||
|
||||
euf::theory_var solver::mk_var(euf::enode* n) {
|
||||
|
@ -85,49 +88,22 @@ namespace intblast {
|
|||
SASSERT(!n->is_attached_to(get_id()));
|
||||
mk_var(n);
|
||||
SASSERT(n->is_attached_to(get_id()));
|
||||
internalize_bv(a);
|
||||
m_translator.internalize_bv(a);
|
||||
return true;
|
||||
}
|
||||
|
||||
void solver::eq_internalized(euf::enode* n) {
|
||||
expr* e = n->get_expr();
|
||||
expr* x = nullptr, * y = nullptr;
|
||||
VERIFY(m.is_eq(n->get_expr(), x, y));
|
||||
SASSERT(bv.is_bv(x));
|
||||
if (!is_translated(e)) {
|
||||
ensure_translated(x);
|
||||
ensure_translated(y);
|
||||
m_args.reset();
|
||||
m_args.push_back(a.mk_sub(translated(x), translated(y)));
|
||||
set_translated(e, m.mk_eq(umod(x, 0), a.mk_int(0)));
|
||||
}
|
||||
m_preds.push_back(e);
|
||||
TRACE("bv", tout << mk_pp(e, m) << " " << mk_pp(translated(e), m) << "\n");
|
||||
ctx.push(push_back_vector(m_preds));
|
||||
}
|
||||
|
||||
void solver::set_translated(expr* e, expr* r) {
|
||||
SASSERT(r);
|
||||
SASSERT(!is_translated(e));
|
||||
m_translate.setx(e->get_id(), r);
|
||||
ctx.push(set_vector_idx_trail(m_translate, e->get_id()));
|
||||
}
|
||||
|
||||
void solver::internalize_bv(app* e) {
|
||||
ensure_translated(e);
|
||||
if (m.is_bool(e)) {
|
||||
m_preds.push_back(e);
|
||||
ctx.push(push_back_vector(m_preds));
|
||||
}
|
||||
m_translator.translate_eq(n->get_expr());
|
||||
}
|
||||
|
||||
bool solver::add_bound_axioms() {
|
||||
if (m_vars_qhead == m_vars.size())
|
||||
auto const& vars = m_translator.vars();
|
||||
if (m_vars_qhead == vars.size())
|
||||
return false;
|
||||
ctx.push(value_trail(m_vars_qhead));
|
||||
for (; m_vars_qhead < m_vars.size(); ++m_vars_qhead) {
|
||||
auto v = m_vars[m_vars_qhead];
|
||||
auto w = translated(v);
|
||||
for (; m_vars_qhead < vars.size(); ++m_vars_qhead) {
|
||||
auto v = vars[m_vars_qhead];
|
||||
auto w = m_translator.translated(v);
|
||||
auto sz = rational::power_of_two(bv.get_bv_size(v->get_sort()));
|
||||
auto lo = ctx.mk_literal(a.mk_ge(w, a.mk_int(0)));
|
||||
auto hi = ctx.mk_literal(a.mk_le(w, a.mk_int(sz - 1)));
|
||||
|
@ -140,12 +116,13 @@ namespace intblast {
|
|||
}
|
||||
|
||||
bool solver::add_predicate_axioms() {
|
||||
if (m_preds_qhead == m_preds.size())
|
||||
auto const& preds = m_translator.preds();
|
||||
if (m_preds_qhead == preds.size())
|
||||
return false;
|
||||
ctx.push(value_trail(m_preds_qhead));
|
||||
for (; m_preds_qhead < m_preds.size(); ++m_preds_qhead) {
|
||||
expr* e = m_preds[m_preds_qhead];
|
||||
expr_ref r(translated(e), m);
|
||||
for (; m_preds_qhead < preds.size(); ++m_preds_qhead) {
|
||||
expr* e = preds[m_preds_qhead];
|
||||
expr_ref r(m_translator.translated(e), m);
|
||||
ctx.get_rewriter()(r);
|
||||
auto a = expr2literal(e);
|
||||
auto b = mk_literal(r);
|
||||
|
@ -158,31 +135,7 @@ namespace intblast {
|
|||
|
||||
bool solver::unit_propagate() {
|
||||
return add_bound_axioms() || add_predicate_axioms();
|
||||
}
|
||||
void solver::ensure_translated(expr* e) {
|
||||
if (m_translate.get(e->get_id(), nullptr))
|
||||
return;
|
||||
ptr_vector<expr> todo;
|
||||
ast_fast_mark1 visited;
|
||||
todo.push_back(e);
|
||||
visited.mark(e);
|
||||
for (unsigned i = 0; i < todo.size(); ++i) {
|
||||
expr* e = todo[i];
|
||||
if (!is_app(e))
|
||||
continue;
|
||||
app* a = to_app(e);
|
||||
if (m.is_bool(e) && a->get_family_id() != bv.get_family_id())
|
||||
continue;
|
||||
for (auto arg : *a)
|
||||
if (!visited.is_marked(arg) && !m_translate.get(arg->get_id(), nullptr)) {
|
||||
visited.mark(arg);
|
||||
todo.push_back(arg);
|
||||
}
|
||||
}
|
||||
std::stable_sort(todo.begin(), todo.end(), [&](expr* a, expr* b) { return get_depth(a) < get_depth(b); });
|
||||
for (expr* e : todo)
|
||||
translate_expr(e);
|
||||
}
|
||||
}
|
||||
|
||||
lbool solver::check_axiom(sat::literal_vector const& lits) {
|
||||
sat::literal_vector core;
|
||||
|
@ -198,14 +151,10 @@ namespace intblast {
|
|||
}
|
||||
|
||||
lbool solver::check_core(sat::literal_vector const& lits, euf::enode_pair_vector const& eqs) {
|
||||
m_core.reset();
|
||||
m_vars.reset();
|
||||
m_is_plugin = false;
|
||||
m_translator.reset(false);
|
||||
m_solver = mk_smt2_solver(m, s.params(), symbol::null);
|
||||
|
||||
for (unsigned i = 0; i < m_translate.size(); ++i)
|
||||
m_translate[i] = nullptr;
|
||||
|
||||
expr_ref_vector es(m), original_es(m);
|
||||
for (auto lit : lits)
|
||||
es.push_back(ctx.literal2expr(lit));
|
||||
|
@ -222,8 +171,8 @@ namespace intblast {
|
|||
|
||||
translate(es);
|
||||
|
||||
for (auto e : m_vars) {
|
||||
auto v = translated(e);
|
||||
for (auto e : m_translator.vars()) {
|
||||
auto v = m_translator.translated(e);
|
||||
auto b = rational::power_of_two(bv.get_bv_size(e));
|
||||
m_solver->assert_expr(a.mk_le(a.mk_int(0), v));
|
||||
m_solver->assert_expr(a.mk_lt(v, a.mk_int(b)));
|
||||
|
@ -331,8 +280,8 @@ namespace intblast {
|
|||
|
||||
translate(es);
|
||||
|
||||
for (auto e : m_vars) {
|
||||
auto v = translated(e);
|
||||
for (auto e : m_translator.vars()) {
|
||||
auto v = m_translator.translated(e);
|
||||
auto b = rational::power_of_two(bv.get_bv_size(e));
|
||||
m_solver->assert_expr(a.mk_le(a.mk_int(0), v));
|
||||
m_solver->assert_expr(a.mk_lt(v, a.mk_int(b)));
|
||||
|
@ -377,7 +326,7 @@ namespace intblast {
|
|||
void solver::sorted_subterms(expr_ref_vector& es, ptr_vector<expr>& sorted) {
|
||||
expr_fast_mark1 visited;
|
||||
for (expr* e : es) {
|
||||
if (is_translated(e))
|
||||
if (m_translator.is_translated(e))
|
||||
continue;
|
||||
if (visited.is_marked(e))
|
||||
continue;
|
||||
|
@ -389,7 +338,7 @@ namespace intblast {
|
|||
if (is_app(e)) {
|
||||
app* a = to_app(e);
|
||||
for (expr* arg : *a) {
|
||||
if (!visited.is_marked(arg) && !is_translated(arg)) {
|
||||
if (!visited.is_marked(arg) && !m_translator.is_translated(arg)) {
|
||||
visited.mark(arg);
|
||||
sorted.push_back(arg);
|
||||
}
|
||||
|
@ -399,7 +348,7 @@ namespace intblast {
|
|||
else if (is_quantifier(e)) {
|
||||
quantifier* q = to_quantifier(e);
|
||||
expr* b = q->get_expr();
|
||||
if (!visited.is_marked(b) && !is_translated(b)) {
|
||||
if (!visited.is_marked(b) && !m_translator.is_translated(b)) {
|
||||
visited.mark(b);
|
||||
sorted.push_back(b);
|
||||
}
|
||||
|
@ -414,20 +363,20 @@ namespace intblast {
|
|||
sorted_subterms(es, todo);
|
||||
|
||||
for (expr* e : todo)
|
||||
translate_expr(e);
|
||||
m_translator.translate_expr(e);
|
||||
|
||||
TRACE("bv",
|
||||
for (expr* e : es)
|
||||
tout << mk_pp(e, m) << "\n->\n" << mk_pp(translated(e), m) << "\n";
|
||||
tout << mk_pp(e, m) << "\n->\n" << mk_pp(m_translator.translated(e), m) << "\n";
|
||||
);
|
||||
|
||||
for (unsigned i = 0; i < es.size(); ++i)
|
||||
es[i] = translated(es.get(i));
|
||||
es[i] = m_translator.translated(es.get(i));
|
||||
}
|
||||
|
||||
sat::check_result solver::check() {
|
||||
// ensure that bv2int is injective
|
||||
for (auto e : m_bv2int) {
|
||||
for (auto e : m_translator.bv2int()) {
|
||||
euf::enode* n = expr2enode(e);
|
||||
euf::enode* r1 = n->get_arg(0)->get_root();
|
||||
for (auto sib : euf::enode_class(n)) {
|
||||
|
@ -449,7 +398,7 @@ namespace intblast {
|
|||
}
|
||||
// ensure that int2bv respects values
|
||||
// bv2int(int2bv(x)) = x mod N
|
||||
for (auto e : m_int2bv) {
|
||||
for (auto e : m_translator.int2bv()) {
|
||||
auto n = expr2enode(e);
|
||||
auto x = n->get_arg(0)->get_expr();
|
||||
auto bv2int = bv.mk_bv2int(e);
|
||||
|
@ -469,595 +418,12 @@ namespace intblast {
|
|||
return sat::check_result::CR_DONE;
|
||||
}
|
||||
|
||||
bool solver::is_bounded(expr* x, rational const& N) {
|
||||
return any_of(m_vars, [&](expr* v) {
|
||||
return is_translated(v) && translated(v) == x && bv_size(v) <= N;
|
||||
});
|
||||
}
|
||||
|
||||
bool solver::is_non_negative(expr* bv_expr, expr* e) {
|
||||
auto N = rational::power_of_two(bv.get_bv_size(bv_expr));
|
||||
rational r;
|
||||
if (a.is_numeral(e, r))
|
||||
return r >= 0;
|
||||
if (is_bounded(e, N))
|
||||
return true;
|
||||
expr* x = nullptr, * y = nullptr;
|
||||
if (a.is_mul(e, x, y))
|
||||
return is_non_negative(bv_expr, x) && is_non_negative(bv_expr, y);
|
||||
if (a.is_add(e, x, y))
|
||||
return is_non_negative(bv_expr, x) && is_non_negative(bv_expr, y);
|
||||
return false;
|
||||
}
|
||||
|
||||
expr* solver::umod(expr* bv_expr, unsigned i) {
|
||||
expr* x = arg(i);
|
||||
rational N = bv_size(bv_expr);
|
||||
return amod(bv_expr, x, N);
|
||||
}
|
||||
|
||||
expr* solver::smod(expr* bv_expr, unsigned i) {
|
||||
expr* x = arg(i);
|
||||
auto N = bv_size(bv_expr);
|
||||
auto shift = N / 2;
|
||||
rational r;
|
||||
if (a.is_numeral(x, r))
|
||||
return a.mk_int(mod(r + shift, N));
|
||||
return amod(bv_expr, add(x, a.mk_int(shift)), N);
|
||||
}
|
||||
|
||||
expr_ref solver::mul(expr* x, expr* y) {
|
||||
expr_ref _x(x, m), _y(y, m);
|
||||
if (a.is_zero(x))
|
||||
return _x;
|
||||
if (a.is_zero(y))
|
||||
return _y;
|
||||
if (a.is_one(x))
|
||||
return _y;
|
||||
if (a.is_one(y))
|
||||
return _x;
|
||||
rational v1, v2;
|
||||
if (a.is_numeral(x, v1) && a.is_numeral(y, v2))
|
||||
return expr_ref(a.mk_int(v1 * v2), m);
|
||||
_x = a.mk_mul(x, y);
|
||||
return _x;
|
||||
}
|
||||
|
||||
expr_ref solver::add(expr* x, expr* y) {
|
||||
expr_ref _x(x, m), _y(y, m);
|
||||
if (a.is_zero(x))
|
||||
return _y;
|
||||
if (a.is_zero(y))
|
||||
return _x;
|
||||
rational v1, v2;
|
||||
if (a.is_numeral(x, v1) && a.is_numeral(y, v2))
|
||||
return expr_ref(a.mk_int(v1 + v2), m);
|
||||
_x = a.mk_add(x, y);
|
||||
return _x;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform simplifications that are claimed sound when the bit-vector interpretations of
|
||||
* mod/div always guard the mod and dividend to be non-zero.
|
||||
* Potentially shady area is for arithmetic expressions created by int2bv.
|
||||
* They will be guarded by a modulus which does not disappear.
|
||||
*/
|
||||
expr* solver::amod(expr* bv_expr, expr* x, rational const& N) {
|
||||
rational v;
|
||||
expr* r = nullptr, *c = nullptr, * t = nullptr, * e = nullptr;
|
||||
if (m.is_ite(x, c, t, e))
|
||||
r = m.mk_ite(c, amod(bv_expr, t, N), amod(bv_expr, e, N));
|
||||
else if (a.is_idiv(x, t, e) && a.is_numeral(t, v) && 0 <= v && v < N && is_non_negative(bv_expr, e))
|
||||
r = x;
|
||||
else if (a.is_mod(x, t, e) && a.is_numeral(t, v) && 0 <= v && v < N)
|
||||
r = x;
|
||||
else if (a.is_numeral(x, v))
|
||||
r = a.mk_int(mod(v, N));
|
||||
else if (is_bounded(x, N))
|
||||
r = x;
|
||||
else
|
||||
r = a.mk_mod(x, a.mk_int(N));
|
||||
return r;
|
||||
}
|
||||
|
||||
rational solver::bv_size(expr* bv_expr) {
|
||||
return rational::power_of_two(bv.get_bv_size(bv_expr->get_sort()));
|
||||
}
|
||||
|
||||
void solver::translate_expr(expr* e) {
|
||||
if (is_quantifier(e))
|
||||
translate_quantifier(to_quantifier(e));
|
||||
else if (is_var(e))
|
||||
translate_var(to_var(e));
|
||||
else {
|
||||
app* ap = to_app(e);
|
||||
if (m_is_plugin && ap->get_family_id() == basic_family_id && m.is_bool(ap)) {
|
||||
set_translated(e, e);
|
||||
return;
|
||||
}
|
||||
m_args.reset();
|
||||
for (auto arg : *ap)
|
||||
m_args.push_back(translated(arg));
|
||||
|
||||
if (ap->get_family_id() == basic_family_id)
|
||||
translate_basic(ap);
|
||||
else if (ap->get_family_id() == bv.get_family_id())
|
||||
translate_bv(ap);
|
||||
else
|
||||
translate_app(ap);
|
||||
}
|
||||
}
|
||||
|
||||
void solver::translate_quantifier(quantifier* q) {
|
||||
if (m_is_plugin) {
|
||||
set_translated(q, q);
|
||||
return;
|
||||
}
|
||||
if (is_lambda(q))
|
||||
throw default_exception("lambdas are not supported in intblaster");
|
||||
expr* b = q->get_expr();
|
||||
unsigned nd = q->get_num_decls();
|
||||
ptr_vector<sort> sorts;
|
||||
for (unsigned i = 0; i < nd; ++i) {
|
||||
auto s = q->get_decl_sort(i);
|
||||
if (bv.is_bv_sort(s)) {
|
||||
NOT_IMPLEMENTED_YET();
|
||||
sorts.push_back(a.mk_int());
|
||||
}
|
||||
else
|
||||
sorts.push_back(s);
|
||||
}
|
||||
b = translated(b);
|
||||
// TODO if sorts contain integer, then created bounds variables.
|
||||
set_translated(q, m.update_quantifier(q, b));
|
||||
}
|
||||
|
||||
void solver::translate_var(var* v) {
|
||||
if (bv.is_bv_sort(v->get_sort()))
|
||||
set_translated(v, m.mk_var(v->get_idx(), a.mk_int()));
|
||||
else
|
||||
set_translated(v, v);
|
||||
}
|
||||
|
||||
// Translate functions that are not built-in or bit-vectors.
|
||||
// Base method uses fresh functions.
|
||||
// Other method could use bv2int, int2bv axioms and coercions.
|
||||
// f(args) = bv2int(f(int2bv(args'))
|
||||
//
|
||||
|
||||
void solver::translate_app(app* e) {
|
||||
|
||||
if (m_is_plugin && m.is_bool(e)) {
|
||||
set_translated(e, e);
|
||||
return;
|
||||
}
|
||||
|
||||
bool has_bv_sort = bv.is_bv(e);
|
||||
func_decl* f = e->get_decl();
|
||||
|
||||
for (unsigned i = 0; i < m_args.size(); ++i)
|
||||
if (bv.is_bv(e->get_arg(i)))
|
||||
m_args[i] = bv.mk_int2bv(bv.get_bv_size(e->get_arg(i)), m_args.get(i));
|
||||
|
||||
if (has_bv_sort)
|
||||
m_vars.push_back(e);
|
||||
if (m_is_plugin) {
|
||||
expr* r = m.mk_app(f, m_args);
|
||||
if (has_bv_sort) {
|
||||
ctx.push(push_back_vector(m_vars));
|
||||
r = bv.mk_bv2int(r);
|
||||
}
|
||||
set_translated(e, r);
|
||||
return;
|
||||
}
|
||||
else if (has_bv_sort) {
|
||||
if (f->get_family_id() != null_family_id)
|
||||
throw default_exception("conversion for interpreted functions is not supported by intblast solver");
|
||||
func_decl* g = nullptr;
|
||||
if (!m_new_funs.find(f, g)) {
|
||||
g = m.mk_fresh_func_decl(e->get_decl()->get_name(), symbol("bv"), f->get_arity(), f->get_domain(), a.mk_int());
|
||||
m_new_funs.insert(f, g);
|
||||
}
|
||||
f = g;
|
||||
m_pinned.push_back(f);
|
||||
}
|
||||
set_translated(e, m.mk_app(f, m_args));
|
||||
}
|
||||
|
||||
void solver::translate_bv(app* e) {
|
||||
|
||||
auto bnot = [&](expr* e) {
|
||||
return a.mk_sub(a.mk_int(-1), e);
|
||||
};
|
||||
|
||||
auto band = [&](expr_ref_vector const& args) {
|
||||
expr* r = arg(0);
|
||||
for (unsigned i = 1; i < args.size(); ++i)
|
||||
r = a.mk_band(bv.get_bv_size(e), r, arg(i));
|
||||
return r;
|
||||
};
|
||||
|
||||
auto rotate_left = [&](unsigned n) {
|
||||
auto sz = bv.get_bv_size(e);
|
||||
n = n % sz;
|
||||
expr* r = arg(0);
|
||||
if (n != 0 && sz != 1) {
|
||||
// r[sz - n - 1 : 0] ++ r[sz - 1 : sz - n]
|
||||
// r * 2^(sz - n) + (r div 2^n) mod 2^(sz - n)???
|
||||
// r * A + (r div B) mod A
|
||||
auto N = bv_size(e);
|
||||
auto A = rational::power_of_two(sz - n);
|
||||
auto B = rational::power_of_two(n);
|
||||
auto hi = mul(r, a.mk_int(A));
|
||||
auto lo = amod(e, a.mk_idiv(umod(e, 0), a.mk_int(B)), A);
|
||||
r = add(hi, lo);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
expr* bv_expr = e;
|
||||
expr_ref r(m);
|
||||
auto const& args = m_args;
|
||||
switch (e->get_decl_kind()) {
|
||||
case OP_BADD:
|
||||
r = a.mk_add(args);
|
||||
break;
|
||||
case OP_BSUB:
|
||||
r = a.mk_sub(args.size(), args.data());
|
||||
break;
|
||||
case OP_BMUL:
|
||||
r = a.mk_mul(args);
|
||||
break;
|
||||
case OP_ULEQ:
|
||||
bv_expr = e->get_arg(0);
|
||||
r = a.mk_le(umod(bv_expr, 0), umod(bv_expr, 1));
|
||||
break;
|
||||
case OP_UGEQ:
|
||||
bv_expr = e->get_arg(0);
|
||||
r = a.mk_ge(umod(bv_expr, 0), umod(bv_expr, 1));
|
||||
break;
|
||||
case OP_ULT:
|
||||
bv_expr = e->get_arg(0);
|
||||
r = a.mk_lt(umod(bv_expr, 0), umod(bv_expr, 1));
|
||||
break;
|
||||
case OP_UGT:
|
||||
bv_expr = e->get_arg(0);
|
||||
r = a.mk_gt(umod(bv_expr, 0), umod(bv_expr, 1));
|
||||
break;
|
||||
case OP_SLEQ:
|
||||
bv_expr = e->get_arg(0);
|
||||
r = a.mk_le(smod(bv_expr, 0), smod(bv_expr, 1));
|
||||
break;
|
||||
case OP_SGEQ:
|
||||
bv_expr = e->get_arg(0);
|
||||
r = a.mk_ge(smod(bv_expr, 0), smod(bv_expr, 1));
|
||||
break;
|
||||
case OP_SLT:
|
||||
bv_expr = e->get_arg(0);
|
||||
r = a.mk_lt(smod(bv_expr, 0), smod(bv_expr, 1));
|
||||
break;
|
||||
case OP_SGT:
|
||||
bv_expr = e->get_arg(0);
|
||||
r = a.mk_gt(smod(bv_expr, 0), smod(bv_expr, 1));
|
||||
break;
|
||||
case OP_BNEG:
|
||||
r = a.mk_uminus(arg(0));
|
||||
break;
|
||||
case OP_CONCAT: {
|
||||
unsigned sz = 0;
|
||||
expr_ref new_arg(m);
|
||||
for (unsigned i = args.size(); i-- > 0;) {
|
||||
expr* old_arg = e->get_arg(i);
|
||||
new_arg = umod(old_arg, i);
|
||||
if (sz > 0) {
|
||||
new_arg = mul(new_arg, a.mk_int(rational::power_of_two(sz)));
|
||||
r = add(r, new_arg);
|
||||
}
|
||||
else
|
||||
r = new_arg;
|
||||
sz += bv.get_bv_size(old_arg->get_sort());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_EXTRACT: {
|
||||
unsigned lo, hi;
|
||||
expr* old_arg;
|
||||
VERIFY(bv.is_extract(e, lo, hi, old_arg));
|
||||
r = arg(0);
|
||||
if (lo > 0)
|
||||
r = a.mk_idiv(r, a.mk_int(rational::power_of_two(lo)));
|
||||
break;
|
||||
}
|
||||
case OP_BV_NUM: {
|
||||
rational val;
|
||||
unsigned sz;
|
||||
VERIFY(bv.is_numeral(e, val, sz));
|
||||
r = a.mk_int(val);
|
||||
break;
|
||||
}
|
||||
case OP_BUREM:
|
||||
case OP_BUREM_I: {
|
||||
expr* x = umod(e, 0), * y = umod(e, 1);
|
||||
r = if_eq(y, 0, x, a.mk_mod(x, y));
|
||||
break;
|
||||
}
|
||||
case OP_BUDIV:
|
||||
case OP_BUDIV_I: {
|
||||
expr* x = umod(e, 0), * y = umod(e, 1);
|
||||
r = if_eq(y, 0, a.mk_int(-1), a.mk_idiv(x, y));
|
||||
break;
|
||||
}
|
||||
case OP_BUMUL_NO_OVFL: {
|
||||
bv_expr = e->get_arg(0);
|
||||
r = a.mk_lt(mul(umod(bv_expr, 0), umod(bv_expr, 1)), a.mk_int(bv_size(bv_expr)));
|
||||
break;
|
||||
}
|
||||
case OP_BSHL: {
|
||||
if (!a.is_numeral(arg(0)) && !a.is_numeral(arg(1)))
|
||||
r = a.mk_shl(bv.get_bv_size(e), arg(0),arg(1));
|
||||
else {
|
||||
expr* x = arg(0), * y = umod(e, 1);
|
||||
r = a.mk_int(0);
|
||||
IF_VERBOSE(2, verbose_stream() << "shl " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n");
|
||||
for (unsigned i = 0; i < bv.get_bv_size(e); ++i)
|
||||
r = if_eq(y, i, mul(x, a.mk_int(rational::power_of_two(i))), r);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_BNOT:
|
||||
r = bnot(arg(0));
|
||||
break;
|
||||
case OP_BLSHR:
|
||||
if (!a.is_numeral(arg(0)) && !a.is_numeral(arg(1)))
|
||||
r = a.mk_lshr(bv.get_bv_size(e), arg(0), arg(1));
|
||||
else {
|
||||
expr* x = arg(0), * y = umod(e, 1);
|
||||
r = a.mk_int(0);
|
||||
IF_VERBOSE(2, verbose_stream() << "lshr " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n");
|
||||
for (unsigned i = 0; i < bv.get_bv_size(e); ++i)
|
||||
r = if_eq(y, i, a.mk_idiv(x, a.mk_int(rational::power_of_two(i))), r);
|
||||
}
|
||||
break;
|
||||
case OP_BASHR:
|
||||
if (!a.is_numeral(arg(1)))
|
||||
r = a.mk_ashr(bv.get_bv_size(e), arg(0), arg(1));
|
||||
else {
|
||||
|
||||
//
|
||||
// ashr(x, y)
|
||||
// if y = k & x >= 0 -> x / 2^k
|
||||
// if y = k & x < 0 -> (x / 2^k) - 2^{N-k}
|
||||
//
|
||||
unsigned sz = bv.get_bv_size(e);
|
||||
rational N = bv_size(e);
|
||||
expr* x = umod(e, 0), *y = umod(e, 1);
|
||||
expr* signx = a.mk_ge(x, a.mk_int(N / 2));
|
||||
r = m.mk_ite(signx, a.mk_int(- 1), a.mk_int(0));
|
||||
IF_VERBOSE(1, verbose_stream() << "ashr " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n");
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
expr* d = a.mk_idiv(x, a.mk_int(rational::power_of_two(i)));
|
||||
r = if_eq(y, i,
|
||||
m.mk_ite(signx, add(d, a.mk_int(- rational::power_of_two(sz-i))), d),
|
||||
r);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OP_BOR:
|
||||
// p | q := (p + q) - band(p, q)
|
||||
IF_VERBOSE(2, verbose_stream() << "bor " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n");
|
||||
r = arg(0);
|
||||
for (unsigned i = 1; i < args.size(); ++i)
|
||||
r = a.mk_sub(add(r, arg(i)), a.mk_band(bv.get_bv_size(e), r, arg(i)));
|
||||
break;
|
||||
case OP_BNAND:
|
||||
r = bnot(band(args));
|
||||
break;
|
||||
case OP_BAND:
|
||||
IF_VERBOSE(2, verbose_stream() << "band " << mk_bounded_pp(e, m) << " " << bv.get_bv_size(e) << "\n");
|
||||
r = band(args);
|
||||
break;
|
||||
case OP_BXNOR:
|
||||
case OP_BXOR: {
|
||||
// p ^ q := (p + q) - 2*band(p, q);
|
||||
unsigned sz = bv.get_bv_size(e);
|
||||
IF_VERBOSE(2, verbose_stream() << "bxor " << bv.get_bv_size(e) << "\n");
|
||||
r = arg(0);
|
||||
for (unsigned i = 1; i < args.size(); ++i) {
|
||||
expr* q = arg(i);
|
||||
r = a.mk_sub(add(r, q), mul(a.mk_int(2), a.mk_band(sz, r, q)));
|
||||
}
|
||||
if (e->get_decl_kind() == OP_BXNOR)
|
||||
r = bnot(r);
|
||||
break;
|
||||
}
|
||||
case OP_ZERO_EXT:
|
||||
bv_expr = e->get_arg(0);
|
||||
r = umod(bv_expr, 0);
|
||||
SASSERT(bv.get_bv_size(e) >= bv.get_bv_size(bv_expr));
|
||||
break;
|
||||
case OP_SIGN_EXT: {
|
||||
bv_expr = e->get_arg(0);
|
||||
r = umod(bv_expr, 0);
|
||||
SASSERT(bv.get_bv_size(e) >= bv.get_bv_size(bv_expr));
|
||||
unsigned arg_sz = bv.get_bv_size(bv_expr);
|
||||
//unsigned sz = bv.get_bv_size(e);
|
||||
// rational N = rational::power_of_two(sz);
|
||||
rational M = rational::power_of_two(arg_sz);
|
||||
expr* signbit = a.mk_ge(r, a.mk_int(M / 2));
|
||||
r = m.mk_ite(signbit, a.mk_sub(r, a.mk_int(M)), r);
|
||||
break;
|
||||
}
|
||||
case OP_INT2BV:
|
||||
m_int2bv.push_back(e);
|
||||
ctx.push(push_back_vector(m_int2bv));
|
||||
r = arg(0);
|
||||
break;
|
||||
case OP_BV2INT:
|
||||
m_bv2int.push_back(e);
|
||||
ctx.push(push_back_vector(m_bv2int));
|
||||
r = umod(e->get_arg(0), 0);
|
||||
break;
|
||||
case OP_BCOMP:
|
||||
bv_expr = e->get_arg(0);
|
||||
r = m.mk_ite(m.mk_eq(umod(bv_expr, 0), umod(bv_expr, 1)), a.mk_int(1), a.mk_int(0));
|
||||
break;
|
||||
case OP_BSMOD_I:
|
||||
case OP_BSMOD: {
|
||||
expr* x = umod(e, 0), *y = umod(e, 1);
|
||||
rational N = bv_size(e);
|
||||
expr* signx = a.mk_ge(x, a.mk_int(N/2));
|
||||
expr* signy = a.mk_ge(y, a.mk_int(N/2));
|
||||
expr* u = a.mk_mod(x, y);
|
||||
// u = 0 -> 0
|
||||
// y = 0 -> x
|
||||
// x < 0, y < 0 -> -u
|
||||
// x < 0, y >= 0 -> y - u
|
||||
// x >= 0, y < 0 -> y + u
|
||||
// x >= 0, y >= 0 -> u
|
||||
r = a.mk_uminus(u);
|
||||
r = m.mk_ite(m.mk_and(m.mk_not(signx), signy), add(u, y), r);
|
||||
r = m.mk_ite(m.mk_and(signx, m.mk_not(signy)), a.mk_sub(y, u), r);
|
||||
r = m.mk_ite(m.mk_and(m.mk_not(signx), m.mk_not(signy)), u, r);
|
||||
r = if_eq(u, 0, a.mk_int(0), r);
|
||||
r = if_eq(y, 0, x, r);
|
||||
break;
|
||||
}
|
||||
case OP_BSDIV_I:
|
||||
case OP_BSDIV: {
|
||||
// d = udiv(abs(x), abs(y))
|
||||
// y = 0, x > 0 -> 1
|
||||
// y = 0, x <= 0 -> -1
|
||||
// x = 0, y != 0 -> 0
|
||||
// x > 0, y < 0 -> -d
|
||||
// x < 0, y > 0 -> -d
|
||||
// x > 0, y > 0 -> d
|
||||
// x < 0, y < 0 -> d
|
||||
expr* x = umod(e, 0), * y = umod(e, 1);
|
||||
rational N = bv_size(e);
|
||||
expr* signx = a.mk_ge(x, a.mk_int(N / 2));
|
||||
expr* signy = a.mk_ge(y, a.mk_int(N / 2));
|
||||
x = m.mk_ite(signx, a.mk_sub(a.mk_int(N), x), x);
|
||||
y = m.mk_ite(signy, a.mk_sub(a.mk_int(N), y), y);
|
||||
expr* d = a.mk_idiv(x, y);
|
||||
r = m.mk_ite(m.mk_iff(signx, signy), d, a.mk_uminus(d));
|
||||
r = if_eq(y, 0, m.mk_ite(signx, a.mk_int(1), a.mk_int(-1)), r);
|
||||
break;
|
||||
}
|
||||
case OP_BSREM_I:
|
||||
case OP_BSREM: {
|
||||
// y = 0 -> x
|
||||
// else x - sdiv(x, y) * y
|
||||
expr* x = umod(e, 0), * y = umod(e, 1);
|
||||
rational N = bv_size(e);
|
||||
expr* signx = a.mk_ge(x, a.mk_int(N / 2));
|
||||
expr* signy = a.mk_ge(y, a.mk_int(N / 2));
|
||||
expr* absx = m.mk_ite(signx, a.mk_sub(a.mk_int(N), x), x);
|
||||
expr* absy = m.mk_ite(signy, a.mk_sub(a.mk_int(N), y), y);
|
||||
expr* d = a.mk_idiv(absx, absy);
|
||||
d = m.mk_ite(m.mk_iff(signx, signy), d, a.mk_uminus(d));
|
||||
r = a.mk_sub(x, mul(d, y));
|
||||
r = if_eq(y, 0, x, r);
|
||||
break;
|
||||
}
|
||||
case OP_ROTATE_LEFT: {
|
||||
auto n = e->get_parameter(0).get_int();
|
||||
r = rotate_left(n);
|
||||
break;
|
||||
}
|
||||
case OP_ROTATE_RIGHT: {
|
||||
unsigned sz = bv.get_bv_size(e);
|
||||
auto n = e->get_parameter(0).get_int();
|
||||
r = rotate_left(sz - n);
|
||||
break;
|
||||
}
|
||||
case OP_EXT_ROTATE_LEFT: {
|
||||
unsigned sz = bv.get_bv_size(e);
|
||||
expr* y = umod(e, 1);
|
||||
r = a.mk_int(0);
|
||||
for (unsigned i = 0; i < sz; ++i)
|
||||
r = if_eq(y, i, rotate_left(i), r);
|
||||
break;
|
||||
}
|
||||
case OP_EXT_ROTATE_RIGHT: {
|
||||
unsigned sz = bv.get_bv_size(e);
|
||||
expr* y = umod(e, 1);
|
||||
r = a.mk_int(0);
|
||||
for (unsigned i = 0; i < sz; ++i)
|
||||
r = if_eq(y, i, rotate_left(sz - i), r);
|
||||
break;
|
||||
}
|
||||
case OP_REPEAT: {
|
||||
unsigned n = e->get_parameter(0).get_int();
|
||||
expr* x = umod(e->get_arg(0), 0);
|
||||
r = x;
|
||||
rational N = bv_size(e->get_arg(0));
|
||||
rational N0 = N;
|
||||
for (unsigned i = 1; i < n; ++i)
|
||||
r = add(mul(a.mk_int(N), x), r), N *= N0;
|
||||
break;
|
||||
}
|
||||
case OP_BREDOR: {
|
||||
r = umod(e->get_arg(0), 0);
|
||||
r = m.mk_not(m.mk_eq(r, a.mk_int(0)));
|
||||
break;
|
||||
}
|
||||
case OP_BREDAND: {
|
||||
rational N = bv_size(e->get_arg(0));
|
||||
r = umod(e->get_arg(0), 0);
|
||||
r = m.mk_not(m.mk_eq(r, a.mk_int(N - 1)));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
verbose_stream() << mk_pp(e, m) << "\n";
|
||||
NOT_IMPLEMENTED_YET();
|
||||
}
|
||||
set_translated(e, r);
|
||||
}
|
||||
|
||||
expr_ref solver::if_eq(expr* n, unsigned k, expr* th, expr* el) {
|
||||
rational r;
|
||||
expr_ref _th(th, m), _el(el, m);
|
||||
if (bv.is_numeral(n, r)) {
|
||||
if (r == k)
|
||||
return expr_ref(th, m);
|
||||
else
|
||||
return expr_ref(el, m);
|
||||
}
|
||||
return expr_ref(m.mk_ite(m.mk_eq(n, a.mk_int(k)), th, el), m);
|
||||
}
|
||||
|
||||
void solver::translate_basic(app* e) {
|
||||
if (m.is_eq(e)) {
|
||||
bool has_bv_arg = any_of(*e, [&](expr* arg) { return bv.is_bv(arg); });
|
||||
if (has_bv_arg) {
|
||||
expr* bv_expr = e->get_arg(0);
|
||||
rational N = rational::power_of_two(bv.get_bv_size(bv_expr));
|
||||
if (a.is_numeral(arg(0)) || a.is_numeral(arg(1)) ||
|
||||
is_bounded(arg(0), N) || is_bounded(arg(1), N)) {
|
||||
set_translated(e, m.mk_eq(umod(bv_expr, 0), umod(bv_expr, 1)));
|
||||
}
|
||||
else {
|
||||
m_args[0] = a.mk_sub(arg(0), arg(1));
|
||||
set_translated(e, m.mk_eq(umod(bv_expr, 0), a.mk_int(0)));
|
||||
}
|
||||
}
|
||||
else
|
||||
set_translated(e, m.mk_eq(arg(0), arg(1)));
|
||||
}
|
||||
else if (m.is_ite(e))
|
||||
set_translated(e, m.mk_ite(arg(0), arg(1), arg(2)));
|
||||
else if (m_is_plugin)
|
||||
set_translated(e, e);
|
||||
else
|
||||
set_translated(e, m.mk_app(e->get_decl(), m_args));
|
||||
}
|
||||
|
||||
rational solver::get_value(expr* e) const {
|
||||
SASSERT(bv.is_bv(e));
|
||||
model_ref mdl;
|
||||
m_solver->get_model(mdl);
|
||||
expr_ref r(m);
|
||||
r = translated(e);
|
||||
r = m_translator.translated(e);
|
||||
rational val;
|
||||
if (!mdl->eval_expr(r, r, true))
|
||||
return rational::zero();
|
||||
|
@ -1099,7 +465,7 @@ namespace intblast {
|
|||
}
|
||||
|
||||
rational r, N = rational::power_of_two(bv.get_bv_size(e));
|
||||
expr* te = translated(e);
|
||||
expr* te = m_translator.translated(e);
|
||||
model_ref mdlr;
|
||||
m_solver->get_model(mdlr);
|
||||
expr_ref value(m);
|
||||
|
@ -1126,14 +492,12 @@ namespace intblast {
|
|||
else {
|
||||
expr_ref bv2int(bv.mk_bv2int(n->get_expr()), m);
|
||||
euf::enode* b2i = ctx.get_enode(bv2int);
|
||||
if (!b2i) verbose_stream() << bv2int << "\n";
|
||||
SASSERT(b2i);
|
||||
VERIFY(b2i);
|
||||
arith::arith_value av(ctx);
|
||||
rational r;
|
||||
VERIFY(av.get_value(b2i->get_expr(), r));
|
||||
value = bv.mk_numeral(r, bv.get_bv_size(n->get_expr()));
|
||||
verbose_stream() << ctx.bpp(n) << " := " << value << "\n";
|
||||
}
|
||||
values.set(n->get_root_id(), value);
|
||||
TRACE("model", tout << "add_value " << ctx.bpp(n) << " := " << value << "\n");
|
||||
|
@ -1143,11 +507,11 @@ namespace intblast {
|
|||
return;
|
||||
for (auto n : ctx.get_egraph().nodes()) {
|
||||
auto e = n->get_expr();
|
||||
if (!is_translated(e))
|
||||
if (!m_translator.is_translated(e))
|
||||
continue;
|
||||
if (!bv.is_bv(e))
|
||||
continue;
|
||||
auto t = translated(e);
|
||||
auto t = m_translator.translated(e);
|
||||
|
||||
expr_ref ei(bv.mk_bv2int(e), m);
|
||||
expr_ref ti(a.mk_mod(t, a.mk_int(rational::power_of_two(bv.get_bv_size(e)))), m);
|
||||
|
|
|
@ -38,6 +38,7 @@ Author:
|
|||
#include "solver/solver.h"
|
||||
#include "sat/smt/sat_th.h"
|
||||
#include "util/statistics.h"
|
||||
#include "ast/rewriter/bv2int_translator.h"
|
||||
|
||||
namespace euf {
|
||||
class solver;
|
||||
|
@ -45,18 +46,31 @@ namespace euf {
|
|||
|
||||
namespace intblast {
|
||||
|
||||
class translator_trail : public bv2int_translator_trail {
|
||||
euf::solver& ctx;
|
||||
public:
|
||||
translator_trail(euf::solver& ctx):ctx(ctx) {}
|
||||
void push(push_back_vector<expr_ref_vector> const& c) override;
|
||||
void push(push_back_vector<ptr_vector<app>> const& c) override;
|
||||
void push_idx(set_vector_idx_trail<expr_ref_vector> const& c) override;
|
||||
};
|
||||
|
||||
class solver : public euf::th_euf_solver {
|
||||
euf::solver& ctx;
|
||||
sat::solver& s;
|
||||
ast_manager& m;
|
||||
bv_util bv;
|
||||
arith_util a;
|
||||
translator_trail trail;
|
||||
bv2int_translator m_translator;
|
||||
|
||||
scoped_ptr<::solver> m_solver;
|
||||
obj_map<func_decl, func_decl*> m_new_funs;
|
||||
expr_ref_vector m_translate, m_args;
|
||||
ast_ref_vector m_pinned;
|
||||
|
||||
//obj_map<func_decl, func_decl*> m_new_funs;
|
||||
//expr_ref_vector m_translate, m_args;
|
||||
//ast_ref_vector m_pinned;
|
||||
sat::literal_vector m_core;
|
||||
ptr_vector<app> m_bv2int, m_int2bv;
|
||||
// ptr_vector<app> m_bv2int, m_int2bv;
|
||||
statistics m_stats;
|
||||
bool m_is_plugin = true; // when the solver is used as a plugin, then do not translate below quantifiers.
|
||||
|
||||
|
@ -66,33 +80,6 @@ namespace intblast {
|
|||
|
||||
|
||||
|
||||
bool is_translated(expr* e) const { return !!m_translate.get(e->get_id(), nullptr); }
|
||||
expr* translated(expr* e) const { expr* r = m_translate.get(e->get_id(), nullptr); SASSERT(r); return r; }
|
||||
void set_translated(expr* e, expr* r);
|
||||
expr* arg(unsigned i) { return m_args.get(i); }
|
||||
|
||||
expr* umod(expr* bv_expr, unsigned i);
|
||||
expr* smod(expr* bv_expr, unsigned i);
|
||||
bool is_bounded(expr* v, rational const& N);
|
||||
bool is_non_negative(expr* bv_expr, expr* e);
|
||||
expr_ref mul(expr* x, expr* y);
|
||||
expr_ref add(expr* x, expr* y);
|
||||
expr_ref if_eq(expr* n, unsigned k, expr* th, expr* el);
|
||||
expr* amod(expr* bv_expr, expr* x, rational const& N);
|
||||
rational bv_size(expr* bv_expr);
|
||||
|
||||
void translate_expr(expr* e);
|
||||
void translate_bv(app* e);
|
||||
void translate_basic(app* e);
|
||||
void translate_app(app* e);
|
||||
void translate_quantifier(quantifier* q);
|
||||
void translate_var(var* v);
|
||||
|
||||
void ensure_translated(expr* e);
|
||||
void internalize_bv(app* e);
|
||||
|
||||
unsigned m_vars_qhead = 0, m_preds_qhead = 0;
|
||||
ptr_vector<expr> m_vars, m_preds;
|
||||
bool add_bound_axioms();
|
||||
bool add_predicate_axioms();
|
||||
|
||||
|
@ -101,6 +88,9 @@ namespace intblast {
|
|||
void add_value_plugin(euf::enode* n, model& mdl, expr_ref_vector& values);
|
||||
void add_value_solver(euf::enode* n, model& mdl, expr_ref_vector& values);
|
||||
|
||||
unsigned m_vars_qhead = 0, m_preds_qhead = 0;
|
||||
|
||||
|
||||
public:
|
||||
solver(euf::solver& ctx);
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ Author:
|
|||
|
||||
#include "util/top_sort.h"
|
||||
#include "sat/smt/sat_smt.h"
|
||||
#include "sat/sat_ddfw.h"
|
||||
#include "ast/euf/euf_egraph.h"
|
||||
#include "model/model.h"
|
||||
#include "smt/params/smt_params.h"
|
||||
|
@ -139,10 +138,6 @@ namespace euf {
|
|||
|
||||
virtual euf::enode_pair get_justification_eq(size_t j);
|
||||
|
||||
/**
|
||||
* Local search interface
|
||||
*/
|
||||
virtual void set_bool_search(sat::ddfw* ddfw) {}
|
||||
|
||||
virtual void set_bounds_begin() {}
|
||||
|
||||
|
|
|
@ -13,154 +13,118 @@ Author:
|
|||
|
||||
Nikolaj Bjorner (nbjorner) 2024-02-21
|
||||
|
||||
|
||||
--*/
|
||||
|
||||
#include "sat/smt/sls_solver.h"
|
||||
#include "sat/smt/euf_solver.h"
|
||||
|
||||
|
||||
#include "ast/sls/sls_context.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
|
||||
namespace sls {
|
||||
|
||||
#ifdef SINGLE_THREAD
|
||||
|
||||
solver::solver(euf::solver& ctx) :
|
||||
th_euf_solver(ctx, symbol("sls"), ctx.get_manager().mk_family_id("sls"))
|
||||
{}
|
||||
{}
|
||||
|
||||
#ifdef SINGLE_THREAD
|
||||
|
||||
#else
|
||||
solver::solver(euf::solver& ctx):
|
||||
th_euf_solver(ctx, symbol("sls"), ctx.get_manager().mk_family_id("sls"))
|
||||
{}
|
||||
|
||||
solver::~solver() {
|
||||
finalize();
|
||||
}
|
||||
|
||||
void solver::finalize() {
|
||||
if (!m_completed && m_sls) {
|
||||
m_sls->cancel();
|
||||
m_thread.join();
|
||||
m_sls->collect_statistics(m_st);
|
||||
m_sls = nullptr;
|
||||
m_shared = nullptr;
|
||||
m_slsm = nullptr;
|
||||
m_units = nullptr;
|
||||
}
|
||||
params_ref solver::get_params() {
|
||||
return s().params();
|
||||
}
|
||||
|
||||
sat::check_result solver::check() {
|
||||
return sat::check_result::CR_DONE;
|
||||
void solver::initialize_value(expr* t, expr* v) {
|
||||
ctx.user_propagate_initialize_value(t, v);
|
||||
}
|
||||
|
||||
void solver::force_phase(sat::literal lit) {
|
||||
ctx.s().set_phase(lit);
|
||||
}
|
||||
|
||||
void solver::set_has_new_best_phase(bool b) {
|
||||
|
||||
}
|
||||
|
||||
bool solver::get_best_phase(sat::bool_var v) {
|
||||
return false;
|
||||
}
|
||||
|
||||
expr* solver::bool_var2expr(sat::bool_var v) {
|
||||
return ctx.bool_var2expr(v);
|
||||
}
|
||||
|
||||
void solver::set_finished() {
|
||||
ctx.s().set_canceled();
|
||||
}
|
||||
|
||||
unsigned solver::get_num_bool_vars() const {
|
||||
return s().num_vars();
|
||||
}
|
||||
|
||||
void solver::finalize() {
|
||||
if (!m_smt_plugin)
|
||||
return;
|
||||
|
||||
m_smt_plugin->finalize(m_model, m_st);
|
||||
m_model = nullptr;
|
||||
m_smt_plugin = nullptr;
|
||||
}
|
||||
|
||||
bool solver::unit_propagate() {
|
||||
force_push();
|
||||
sample_local_search();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool solver::is_unit(expr* e) {
|
||||
if (!e)
|
||||
return false;
|
||||
m.is_not(e, e);
|
||||
if (is_uninterp_const(e))
|
||||
if (m_smt_plugin && !m_checking) {
|
||||
expr_ref_vector fmls(m);
|
||||
m_checking = true;
|
||||
m_smt_plugin->check(fmls, ctx.top_level_clauses());
|
||||
return true;
|
||||
bv_util bu(m);
|
||||
expr* s;
|
||||
if (bu.is_bit2bool(e, s))
|
||||
return is_uninterp_const(s);
|
||||
return false;
|
||||
}
|
||||
if (!m_smt_plugin)
|
||||
return false;
|
||||
if (!m_smt_plugin->completed())
|
||||
return false;
|
||||
m_smt_plugin->finalize(m_model, m_st);
|
||||
m_smt_plugin = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
void solver::pop_core(unsigned n) {
|
||||
for (; m_trail_lim < s().init_trail_size(); ++m_trail_lim) {
|
||||
auto lit = s().trail_literal(m_trail_lim);
|
||||
auto e = ctx.literal2expr(lit);
|
||||
if (is_unit(e)) {
|
||||
// IF_VERBOSE(1, verbose_stream() << "add unit " << mk_pp(e, m) << "\n");
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
ast_translation tr(m, *m_shared);
|
||||
m_units->push_back(tr(e.get()));
|
||||
m_has_units = true;
|
||||
if (!m_smt_plugin)
|
||||
return;
|
||||
|
||||
unsigned scope_lvl = s().scope_lvl();
|
||||
if (s().search_lvl() == scope_lvl - n) {
|
||||
for (; m_trail_lim < s().init_trail_size(); ++m_trail_lim) {
|
||||
auto lit = s().trail_literal(m_trail_lim);
|
||||
m_smt_plugin->add_unit(lit);
|
||||
}
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
if (ctx.has_new_best_phase())
|
||||
m_smt_plugin->import_phase_from_smt();
|
||||
|
||||
#endif
|
||||
|
||||
m_smt_plugin->import_from_sls();
|
||||
}
|
||||
|
||||
void solver::init_search() {
|
||||
if (m_sls) {
|
||||
m_sls->cancel();
|
||||
m_thread.join();
|
||||
m_result = l_undef;
|
||||
m_completed = false;
|
||||
m_has_units = false;
|
||||
m_model = nullptr;
|
||||
m_units = nullptr;
|
||||
}
|
||||
// set up state for local search solver here
|
||||
|
||||
m_shared = alloc(ast_manager);
|
||||
m_slsm = alloc(ast_manager);
|
||||
m_units = alloc(expr_ref_vector, *m_shared);
|
||||
ast_translation tr(m, *m_slsm);
|
||||
|
||||
m_completed = false;
|
||||
m_result = l_undef;
|
||||
m_model = nullptr;
|
||||
m_sls = alloc(bv::sls, *m_slsm, s().params());
|
||||
|
||||
for (expr* a : ctx.get_assertions())
|
||||
m_sls->assert_expr(tr(a));
|
||||
|
||||
std::function<bool(expr*, unsigned)> eval = [&](expr* e, unsigned r) {
|
||||
return false;
|
||||
};
|
||||
|
||||
m_sls->init();
|
||||
m_sls->init_eval(eval);
|
||||
m_sls->updt_params(s().params());
|
||||
m_sls->init_unit([&]() {
|
||||
if (!m_has_units)
|
||||
return expr_ref(*m_slsm);
|
||||
expr_ref e(*m_slsm);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
if (m_units->empty())
|
||||
return expr_ref(*m_slsm);
|
||||
ast_translation tr(*m_shared, *m_slsm);
|
||||
e = tr(m_units->back());
|
||||
m_units->pop_back();
|
||||
}
|
||||
return e;
|
||||
});
|
||||
m_sls->set_model([&](model& mdl) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
ast_translation tr(*m_shared, m);
|
||||
m_model = mdl.translate(tr);
|
||||
});
|
||||
|
||||
m_thread = std::thread([this]() { run_local_search(); });
|
||||
if (m_smt_plugin)
|
||||
finalize();
|
||||
m_smt_plugin = alloc(sls::smt_plugin, *this);
|
||||
m_checking = false;
|
||||
}
|
||||
|
||||
void solver::sample_local_search() {
|
||||
if (!m_completed)
|
||||
return;
|
||||
m_thread.join();
|
||||
m_completed = false;
|
||||
m_sls->collect_statistics(m_st);
|
||||
if (m_result == l_true) {
|
||||
IF_VERBOSE(2, verbose_stream() << "(sat.sls :model-completed)\n";);
|
||||
auto mdl = m_sls->get_model();
|
||||
ast_translation tr(*m_slsm, m);
|
||||
m_model = mdl->translate(tr);
|
||||
s().set_canceled();
|
||||
}
|
||||
m_sls = nullptr;
|
||||
}
|
||||
|
||||
void solver::run_local_search() {
|
||||
m_result = (*m_sls)();
|
||||
m_completed = true;
|
||||
std::ostream& solver::display(std::ostream& out) const {
|
||||
return out << "theory-sls\n";
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ Author:
|
|||
|
||||
|
||||
#include "util/rlimit.h"
|
||||
#include "ast/sls/bv_sls.h"
|
||||
#include "ast/sls/sat_ddfw.h"
|
||||
#include "sat/smt/sat_th.h"
|
||||
|
||||
|
||||
|
@ -52,8 +52,7 @@ namespace sls {
|
|||
|
||||
#else
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include "ast/sls/sls_smt_plugin.h"
|
||||
|
||||
namespace euf {
|
||||
class solver;
|
||||
|
@ -61,24 +60,12 @@ namespace euf {
|
|||
|
||||
namespace sls {
|
||||
|
||||
class solver : public euf::th_euf_solver {
|
||||
std::atomic<lbool> m_result;
|
||||
std::atomic<bool> m_completed, m_has_units;
|
||||
std::thread m_thread;
|
||||
std::mutex m_mutex;
|
||||
// m is accessed by the main thread
|
||||
// m_slsm is accessed by the sls thread
|
||||
// m_shared is only accessed at synchronization points
|
||||
scoped_ptr<ast_manager> m_shared, m_slsm;
|
||||
scoped_ptr<bv::sls> m_sls;
|
||||
scoped_ptr<expr_ref_vector> m_units;
|
||||
class solver : public euf::th_euf_solver, public sls::smt_context {
|
||||
model_ref m_model;
|
||||
sls::smt_plugin* m_smt_plugin = nullptr;
|
||||
unsigned m_trail_lim = 0;
|
||||
statistics m_st;
|
||||
|
||||
void run_local_search();
|
||||
void sample_local_search();
|
||||
bool is_unit(expr*);
|
||||
bool m_checking = false;
|
||||
::statistics m_st;
|
||||
|
||||
public:
|
||||
solver(euf::solver& ctx);
|
||||
|
@ -97,10 +84,21 @@ namespace sls {
|
|||
sat::literal internalize(expr* e, bool sign, bool root) override { UNREACHABLE(); return sat::null_literal; }
|
||||
void internalize(expr* e) override { UNREACHABLE(); }
|
||||
void get_antecedents(sat::literal l, sat::ext_justification_idx idx, sat::literal_vector & r, bool probing) override { UNREACHABLE(); }
|
||||
sat::check_result check() override;
|
||||
std::ostream & display(std::ostream & out) const override { return out; }
|
||||
sat::check_result check() override { return sat::check_result::CR_DONE; }
|
||||
std::ostream& display(std::ostream& out) const override;
|
||||
std::ostream & display_justification(std::ostream & out, sat::ext_justification_idx idx) const override { UNREACHABLE(); return out; }
|
||||
std::ostream & display_constraint(std::ostream & out, sat::ext_constraint_idx idx) const override { UNREACHABLE(); return out; }
|
||||
|
||||
|
||||
ast_manager& get_manager() override { return m; }
|
||||
params_ref get_params() override;
|
||||
void initialize_value(expr* t, expr* v) override;
|
||||
void force_phase(sat::literal lit) override;
|
||||
void set_has_new_best_phase(bool b) override;
|
||||
bool get_best_phase(sat::bool_var v) override;
|
||||
expr* bool_var2expr(sat::bool_var v) override;
|
||||
void set_finished() override;
|
||||
unsigned get_num_bool_vars() const override;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ Notes:
|
|||
#include "sat/smt/pb_solver.h"
|
||||
#include "sat/smt/euf_solver.h"
|
||||
#include "sat/smt/sat_th.h"
|
||||
#include "sat/sat_params.hpp"
|
||||
#include "params/sat_params.hpp"
|
||||
#include<sstream>
|
||||
|
||||
struct goal2sat::imp : public sat::sat_internalizer {
|
||||
|
@ -139,10 +139,6 @@ struct goal2sat::imp : public sat::sat_internalizer {
|
|||
return m_euf && ensure_euf()->relevancy_enabled();
|
||||
}
|
||||
|
||||
bool top_level_relevant() {
|
||||
return m_top_level && relevancy_enabled();
|
||||
}
|
||||
|
||||
void mk_clause(sat::literal l1, sat::literal l2, euf::th_proof_hint* ph) {
|
||||
sat::literal lits[2] = { l1, l2 };
|
||||
mk_clause(2, lits, ph);
|
||||
|
@ -158,6 +154,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
|
|||
if (relevancy_enabled())
|
||||
ensure_euf()->add_aux(n, lits);
|
||||
m_solver.add_clause(n, lits, mk_status(ph));
|
||||
add_top_level_clause(n, lits);
|
||||
}
|
||||
|
||||
void mk_root_clause(sat::literal l) {
|
||||
|
@ -179,6 +176,7 @@ struct goal2sat::imp : public sat::sat_internalizer {
|
|||
if (relevancy_enabled())
|
||||
ensure_euf()->add_root(n, lits);
|
||||
m_solver.add_clause(n, lits, ph ? mk_status(ph) : sat::status::input());
|
||||
add_top_level_clause(n, lits);
|
||||
}
|
||||
|
||||
sat::bool_var add_var(bool is_ext, expr* n) {
|
||||
|
@ -895,7 +893,6 @@ struct goal2sat::imp : public sat::sat_internalizer {
|
|||
process(n, true);
|
||||
CTRACE("goal2sat", !m_result_stack.empty(), tout << m_result_stack << "\n";);
|
||||
SASSERT(m_result_stack.empty());
|
||||
add_assertion(n);
|
||||
}
|
||||
|
||||
void insert_dep(expr* dep0, expr* dep, bool sign) {
|
||||
|
@ -990,10 +987,12 @@ struct goal2sat::imp : public sat::sat_internalizer {
|
|||
}
|
||||
}
|
||||
|
||||
void add_assertion(expr* f) {
|
||||
void add_top_level_clause(unsigned n, sat::literal const* lits) {
|
||||
if (!m_top_level)
|
||||
return;
|
||||
auto* ext = dynamic_cast<euf::solver*>(m_solver.get_extension());
|
||||
if (ext)
|
||||
ext->add_assertion(f);
|
||||
ext->add_clause(n, lits);
|
||||
}
|
||||
|
||||
void update_model(model_ref& mdl) {
|
||||
|
|
|
@ -44,7 +44,7 @@ Notes:
|
|||
#include "sat/smt/pb_solver.h"
|
||||
#include "sat/smt/euf_solver.h"
|
||||
#include "sat/smt/sat_th.h"
|
||||
#include "sat/sat_params.hpp"
|
||||
#include "params/sat_params.hpp"
|
||||
#include<sstream>
|
||||
|
||||
sat2goal::mc::mc(ast_manager& m): m(m), m_var2expr(m) {}
|
||||
|
|
|
@ -16,13 +16,14 @@ Author:
|
|||
Notes:
|
||||
|
||||
--*/
|
||||
#include "params/sat_params.hpp"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "model/model_v2_pp.h"
|
||||
#include "tactic/tactical.h"
|
||||
#include "sat/tactic/goal2sat.h"
|
||||
#include "sat/tactic/sat2goal.h"
|
||||
#include "sat/sat_solver.h"
|
||||
#include "sat/sat_params.hpp"
|
||||
|
||||
|
||||
class sat_tactic : public tactic {
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue