diff --git a/.gitignore b/.gitignore index 936f977aa..710ce4b7e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,9 +9,12 @@ callgrind.out.* .z3-trace # OCaml generated files *.a +*.o *.cma *.cmo *.cmi +*.cmx +*.byte *.cmxa ocamlz3 # Java generated files @@ -22,6 +25,7 @@ ocamlz3 # Directories with generated code and documentation release/* build/* +trace/* build-dist/* dist/* src/out/* diff --git a/CMakeLists.txt b/CMakeLists.txt index 91366181d..2b75c1840 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.4) set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cxx_compiler_flags_overrides.cmake") -project(Z3 VERSION 4.12.2.0 LANGUAGES CXX C) +project(Z3 VERSION 4.12.3.0 LANGUAGES CXX C) ################################################################################ # Project version diff --git a/README-CMake.md b/README-CMake.md index 7b7381107..5845a52c3 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -90,6 +90,37 @@ CFLAGS="-m32" CXXFLAGS="-m32" CC=gcc CXX=g++ cmake ../ Note like with the ``CC`` and ``CXX`` flags this must be done on the very first invocation to CMake in the build directory. +### Adding Z3 as a dependency to a CMAKE Project + +CMake's [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) allows +the fetching and populating of an external project. This is useful when a certain version +of z3 is required that may not match with the system version. With the following code in the +cmake file of your project, z3 version 4.12.1 is downloaded to the build directory and the +cmake targets are added to the project: + +``` +FetchContent_Declare(z3 + GIT_REPOSITORY https://github.com/Z3Prover/z3 + GIT_TAG z3-4.12.1 +) +FetchContent_MakeAvailable(z3) +``` + +The header files can be added to the included directories as follows: + +``` +include_directories( ${z3_SOURCE_DIR}/src/api ) +``` + +Finally, the z3 library can be linked to a `yourTarget` using + +``` +target_link_libraries(yourTarget libz3) +``` +Note that this is `libz3` not `z3` (`libz3` refers to the library target from `src/CMakeLists.txt`). + + + ### Ninja [Ninja](https://ninja-build.org/) is a simple build system that is built for speed. diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 6cac7cc49..9e007d1a1 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -10,6 +10,9 @@ Version 4.next - native word level bit-vector solving. - introduction of simple induction lemmas to handle a limited repertoire of induction proofs. +Version 4.12.3 +============== + Version 4.12.2 ============== @@ -27,6 +30,10 @@ Version 4.12.2 and `elim-predicates` that go beyond incremental pre-processing used internally. The advantage of using `solve-eqs` during pre-processing can be significant. Incremental pre-processing simplification using `solve-eqs` and other simplifiers that change interpretations was not possible before. +- Optimize added to JS API, thanks to gbagan +- SMTLIB2 proposal for bit-vector overflow predicates added, thanks to aehyvari +- bug fixes, thanks to Clemens Eisenhofer, hgvk94, Lev Nachmanson, and others + Version 4.12.1 ============== diff --git a/doc/mk_params_doc.py b/doc/mk_params_doc.py index de5276416..021cab3c3 100644 --- a/doc/mk_params_doc.py +++ b/doc/mk_params_doc.py @@ -37,7 +37,7 @@ def help(ous): out = subprocess.Popen([z3_exe, "-pm"],stdout=subprocess.PIPE).communicate()[0] modules = ["global"] if out != None: - out = out.decode(sys.stdout.encoding) + out = out.decode(sys.getdefaultencoding()) module_re = re.compile(r"\[module\] (.*)\,") lines = out.split("\n") for line in lines: @@ -48,7 +48,7 @@ def help(ous): out = subprocess.Popen([z3_exe, "-pmmd:%s" % module],stdout=subprocess.PIPE).communicate()[0] if out == None: continue - out = out.decode(sys.stdout.encoding) + out = out.decode(sys.getdefaultencoding()) out = out.replace("\r","") ous.write(out) diff --git a/doc/mk_tactic_doc.py b/doc/mk_tactic_doc.py index a22201e27..804df2f7e 100644 --- a/doc/mk_tactic_doc.py +++ b/doc/mk_tactic_doc.py @@ -28,7 +28,7 @@ def extract_params(ous, tac): out = subprocess.Popen([z3_exe, f"-tacticsmd:{tac}"], stdout=subprocess.PIPE).communicate()[0] if not out: return - out = out.decode(sys.stdout.encoding) + out = out.decode(sys.getdefaultencoding()) if is_ws(out): return ous.write("### Parameters\n\n") diff --git a/examples/java/JavaExample.java b/examples/java/JavaExample.java index 150efd545..a27a60721 100644 --- a/examples/java/JavaExample.java +++ b/examples/java/JavaExample.java @@ -2259,6 +2259,24 @@ class JavaExample System.out.println(e1.equals(e3)); } + public void stringExample() { + System.out.println("String example"); + Context ctx = new Context(); + Expr a = ctx.mkToRe(ctx.mkString("abcd")); + Expr b = ctx.mkFullRe(ctx.mkReSort(ctx.mkStringSort())); + System.out.println(a); + System.out.println(b); + System.out.println(a.getSort()); + System.out.println(b.getSort()); + Expr c = ctx.mkConcat(ctx.mkToRe(ctx.mkString("abc")), + ctx.mkFullRe(ctx.mkReSort(ctx.mkStringSort())), + ctx.mkEmptyRe(ctx.mkReSort(ctx.mkStringSort())), + ctx.mkAllcharRe(ctx.mkReSort(ctx.mkStringSort())), + ctx.mkToRe(ctx.mkString("d"))); + System.out.println(c); + + } + public static void main(String[] args) { JavaExample p = new JavaExample(); @@ -2274,12 +2292,15 @@ class JavaExample System.out.print("Z3 Full Version String: "); System.out.println(Version.getFullVersion()); + p.stringExample(); + p.simpleExample(); { // These examples need model generation turned on. HashMap cfg = new HashMap(); cfg.put("model", "true"); Context ctx = new Context(cfg); + p.optimizeExample(ctx); p.basicTests(ctx); diff --git a/scripts/mk_nuget_task.py b/scripts/mk_nuget_task.py index e22057a0f..073b6b99a 100644 --- a/scripts/mk_nuget_task.py +++ b/scripts/mk_nuget_task.py @@ -24,8 +24,8 @@ def mk_dir(d): os_info = { 'ubuntu-latest' : ('so', 'linux-x64'), 'ubuntu-18' : ('so', 'linux-x64'), 'ubuntu-20' : ('so', 'linux-x64'), - 'glibc-2.31' : ('so', 'linux-x64'), 'glibc' : ('so', 'linux-x64'), + #'glibc-2.35' : ('so', 'linux-x64'), 'x64-win' : ('dll', 'win-x64'), 'x86-win' : ('dll', 'win-x86'), 'x64-osx' : ('dylib', 'osx-x64'), diff --git a/scripts/mk_project.py b/scripts/mk_project.py index fef785692..fe75a9ca5 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -8,7 +8,7 @@ from mk_util import * def init_version(): - set_version(4, 12, 2, 0) # express a default build version or pick up ci build version + set_version(4, 12, 3, 0) # express a default build version or pick up ci build version # Z3 Project definition def init_project_def(): diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 635b8cec1..805aea19d 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -122,7 +122,7 @@ FPMATH_ENABLED=getenv("FPMATH_ENABLED", "True") def check_output(cmd): out = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] if out != None: - enc = sys.stdout.encoding + enc = sys.getdefaultencoding() if enc != None: return out.decode(enc).rstrip('\r\n') else: return out.rstrip('\r\n') else: diff --git a/scripts/nightly.yaml b/scripts/nightly.yaml index 4925ce45d..f4573877e 100644 --- a/scripts/nightly.yaml +++ b/scripts/nightly.yaml @@ -1,7 +1,7 @@ variables: Major: '4' Minor: '12' - Patch: '2' + Patch: '3' AssemblyVersion: $(Major).$(Minor).$(Patch).$(Build.BuildId) NightlyVersion: $(AssemblyVersion)-$(Build.DefinitionName) @@ -35,6 +35,20 @@ stages: artifactName: 'MacArm64' targetPath: $(Build.ArtifactStagingDirectory) + - job: Ubuntu20 + displayName: "Ubuntu20 build" + pool: + vmImage: "ubuntu-20.04" + steps: + - script: python scripts/mk_unix_dist.py --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk + - script: git clone https://github.com/z3prover/z3test z3test + - script: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2 + - script: cp dist/*.zip $(Build.ArtifactStagingDirectory)/. + - task: PublishPipelineArtifact@0 + inputs: + artifactName: 'Ubuntu-20.04' + targetPath: $(Build.ArtifactStagingDirectory) + - job: Ubuntu displayName: "Ubuntu build" pool: @@ -512,6 +526,11 @@ stages: inputs: artifactName: 'Ubuntu' targetPath: tmp + - task: DownloadPipelineArtifact@2 + displayName: "Download Ubuntu-20.04" + inputs: + artifactName: 'Ubuntu-20.04' + targetPath: tmp - task: DownloadPipelineArtifact@2 displayName: "Download Doc" inputs: diff --git a/scripts/release.yml b/scripts/release.yml index 10ae24577..a1306d87f 100644 --- a/scripts/release.yml +++ b/scripts/release.yml @@ -6,7 +6,7 @@ trigger: none variables: - ReleaseVersion: '4.12.2' + ReleaseVersion: '4.12.3' stages: @@ -85,6 +85,35 @@ stages: artifactName: 'UbuntuBuild' targetPath: $(Build.ArtifactStagingDirectory) + - job: UbuntuBuild20 + displayName: "Ubuntu build 20" + pool: + vmImage: "ubuntu-20.04" + steps: + - task: PythonScript@0 + displayName: Build + inputs: + scriptSource: 'filepath' + scriptPath: scripts/mk_unix_dist.py + arguments: --dotnet-key=$(Build.SourcesDirectory)/resources/z3.snk + - script: git clone https://github.com/z3prover/z3test z3test + displayName: 'Clone z3test' + - task: PythonScript@0 + displayName: Test + inputs: + scriptSource: 'filepath' + scriptPath: z3test/scripts/test_benchmarks.py + arguments: build-dist/z3 z3test/regressions/smt2 + - task: CopyFiles@2 + inputs: + sourceFolder: dist + contents: '*.zip' + targetFolder: $(Build.ArtifactStagingDirectory) + - task: PublishPipelineArtifact@0 + inputs: + artifactName: 'UbuntuBuild20' + targetPath: $(Build.ArtifactStagingDirectory) + - job: UbuntuDoc displayName: "Ubuntu Doc build" pool: @@ -191,6 +220,11 @@ stages: inputs: artifact: 'UbuntuBuild' path: $(Agent.TempDirectory)\package + - task: DownloadPipelineArtifact@2 + displayName: 'Download Ubuntu20 Build' + inputs: + artifact: 'UbuntuBuild20' + path: $(Agent.TempDirectory)\package - task: DownloadPipelineArtifact@2 displayName: 'Download macOS Build' inputs: @@ -436,6 +470,11 @@ stages: pool: vmImage: "windows-latest" steps: + - task: DownloadPipelineArtifact@2 + displayName: 'Download Ubuntu Build' + inputs: + artifact: 'UbuntuBuild20' + path: $(Agent.TempDirectory) - task: DownloadPipelineArtifact@2 displayName: 'Download Ubuntu Build' inputs: diff --git a/scripts/update_api.py b/scripts/update_api.py index 62b961d67..23d044832 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -633,7 +633,72 @@ def mk_java(java_src, java_dir, package_name): java_native.write(' }\n') java_native.write(' }\n') java_native.write(' }\n') + java_native.write(""" + public static native long propagateInit(Object o, long ctx, long solver); + public static native void propagateRegisterCreated(Object o, long ctx, long solver); + public static native void propagateRegisterFixed(Object o, long ctx, long solver); + public static native void propagateRegisterEq(Object o, long ctx, long solver); + public static native void propagateRegisterDecide(Object o, long ctx, long solver); + public static native void propagateRegisterFinal(Object o, long ctx, long solver); + public static native void propagateConflict(Object o, long ctx, long solver, long javainfo, int num_fixed, long[] fixed, long num_eqs, long[] eq_lhs, long[] eq_rhs, long conseq); + public static native void propagateAdd(Object o, long ctx, long solver, long javainfo, long e); + public static native void propagateNextSplit(Object o, long ctx, long solver, long javainfo, long e, long idx, long phase); + public static native void propagateDestroy(Object o, long ctx, long solver, long javainfo); + public static abstract class UserPropagatorBase implements AutoCloseable { + protected long ctx; + protected long solver; + protected long javainfo; + + public UserPropagatorBase(long _ctx, long _solver) { + ctx = _ctx; + solver = _solver; + javainfo = propagateInit(this, ctx, solver); + } + + @Override + public void close() { + Native.propagateDestroy(this, ctx, solver, javainfo); + javainfo = 0; + solver = 0; + ctx = 0; + } + + protected final void registerCreated() { + Native.propagateRegisterCreated(this, ctx, solver); + } + + protected final void registerFixed() { + Native.propagateRegisterFixed(this, ctx, solver); + } + + protected final void registerEq() { + Native.propagateRegisterEq(this, ctx, solver); + } + + protected final void registerDecide() { + Native.propagateRegisterDecide(this, ctx, solver); + } + + protected final void registerFinal() { + Native.propagateRegisterFinal(this, ctx, solver); + } + + protected abstract void pushWrapper(); + + protected abstract void popWrapper(int number); + + protected abstract void finWrapper(); + + protected abstract void eqWrapper(long lx, long ly); + + protected abstract UserPropagatorBase freshWrapper(long lctx); + + protected abstract void createdWrapper(long le); + + protected abstract void fixedWrapper(long lvar, long lvalue); + } + """) java_native.write('\n') for name, result, params in _dotnet_decls: java_native.write(' protected static native %s INTERNAL%s(' % (type2java(result), java_method_name(name))) @@ -1825,6 +1890,7 @@ if _lib is None: else: print(" import builtins") print(" builtins.Z3_LIB_DIRS = [ '/path/to/libz3.%s' ] " % _ext) + print(_failures) raise Z3Exception("libz3.%s not found." % _ext) @@ -1836,14 +1902,14 @@ if sys.version < '3': else: def _str_to_bytes(s): if isinstance(s, str): - enc = sys.stdout.encoding + enc = sys.getdefaultencoding() return s.encode(enc if enc != None else 'latin-1') else: return s def _to_pystr(s): if s != None: - enc = sys.stdout.encoding + enc = sys.getdefaultencoding() return s.decode(enc if enc != None else 'latin-1') else: return "" diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index bc76d02bc..bc29826ff 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -120,10 +120,8 @@ extern "C" { RESET_ERROR_CODE(); // recfun::promise_def def = - mk_c(c)->recfun().get_plugin().mk_def(to_symbol(s), - domain_size, - to_sorts(domain), - to_sort(range)); + mk_c(c)->recfun().get_plugin().mk_def( + to_symbol(s), domain_size, to_sorts(domain), to_sort(range), false); func_decl* d = def.get_def()->get_decl(); mk_c(c)->save_ast_trail(d); RETURN_Z3(of_func_decl(d)); diff --git a/src/api/api_goal.cpp b/src/api/api_goal.cpp index 086e8f302..cfe0974df 100644 --- a/src/api/api_goal.cpp +++ b/src/api/api_goal.cpp @@ -198,7 +198,7 @@ extern "C" { RESET_ERROR_CODE(); std::ostringstream buffer; if (!to_goal_ref(g)->is_cnf()) { - SET_ERROR_CODE(Z3_INVALID_ARG, "If this is not what you want, then preprocess by optional bit-blasting and applying tseitin-cnf"); + SET_ERROR_CODE(Z3_INVALID_ARG, "Goal is not converted into CNF. Preprocess by optional bit-blasting and applying tseitin-cnf"); RETURN_Z3(nullptr); } to_goal_ref(g)->display_dimacs(buffer, include_names); diff --git a/src/api/api_parsers.cpp b/src/api/api_parsers.cpp index 899750ef7..94a955d4b 100644 --- a/src/api/api_parsers.cpp +++ b/src/api/api_parsers.cpp @@ -128,7 +128,8 @@ extern "C" { static Z3_ast_vector Z3_parser_context_parse_stream(Z3_context c, scoped_ptr& ctx, bool owned, std::istream& is) { Z3_TRY; - Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); + ast_manager& m = mk_c(c)->m(); + Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), m); mk_c(c)->save_object(v); std::stringstream errstrm; ctx->set_regular_stream(errstrm); @@ -147,8 +148,11 @@ extern "C" { SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str()); return of_ast_vector(v); } - for (expr* e : ctx->tracked_assertions()) - v->m_ast_vector.push_back(e); + for (auto const& [asr, an] : ctx->tracked_assertions()) + if (an) + v->m_ast_vector.push_back(m.mk_implies(an, asr)); + else + v->m_ast_vector.push_back(asr); ctx->reset_tracked_assertions(); return of_ast_vector(v); Z3_CATCH_RETURN(nullptr); diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index ca39329bb..2c19d0d9e 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -314,8 +314,11 @@ extern "C" { bool initialized = to_solver(s)->m_solver.get() != nullptr; if (!initialized) init_solver(c, s); - for (expr* e : ctx->tracked_assertions()) - to_solver(s)->assert_expr(e); + for (auto const& [asr, an] : ctx->tracked_assertions()) + if (an) + to_solver(s)->assert_expr(asr, an); + else + to_solver(s)->assert_expr(asr); ctx->reset_tracked_assertions(); to_solver_ref(s)->set_model_converter(ctx->get_model_converter()); auto* ctx_s = ctx->get_solver(); diff --git a/src/api/api_tactic.cpp b/src/api/api_tactic.cpp index 4e5156ba9..351b3d1b9 100644 --- a/src/api/api_tactic.cpp +++ b/src/api/api_tactic.cpp @@ -24,7 +24,7 @@ Revision History: #include "util/scoped_ctrl_c.h" #include "util/cancel_eh.h" #include "util/scoped_timer.h" -#include "ast/simplifiers/seq_simplifier.h" +#include "ast/simplifiers/then_simplifier.h" Z3_apply_result_ref::Z3_apply_result_ref(api::context& c, ast_manager & m): api::object(c) { } @@ -589,7 +589,7 @@ extern "C" { auto fac1 = *to_simplifier_ref(t1); auto fac2 = *to_simplifier_ref(t2); auto new_s = [fac1, fac2](auto& m, auto& p, auto& st) { - auto* r = alloc(seq_simplifier, m, p, st); + auto* r = alloc(then_simplifier, m, p, st); r->add_simplifier(fac1(m, p, st)); r->add_simplifier(fac2(m, p, st)); return r; diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 820818168..88b520147 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -151,6 +151,7 @@ namespace z3 { } + /** \brief A Context manages all other Z3 objects, global configuration options, etc. */ @@ -360,11 +361,19 @@ namespace z3 { func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range); func_decl recfun(symbol const & name, unsigned arity, sort const * domain, sort const & range); + func_decl recfun(symbol const & name, const sort_vector& domain, sort const & range); + func_decl recfun(char const * name, sort_vector const& domain, sort const & range); func_decl recfun(char const * name, unsigned arity, sort const * domain, sort const & range); func_decl recfun(char const * name, sort const & domain, sort const & range); func_decl recfun(char const * name, sort const & d1, sort const & d2, sort const & range); - void recdef(func_decl, expr_vector const& args, expr const& body); + /** + * \brief add function definition body to declaration decl. decl needs to be declared using context::. + * @param decl + * @param args + * @param body + */ + void recdef(func_decl decl, expr_vector const& args, expr const& body); func_decl user_propagate_function(symbol const& name, sort_vector const& domain, sort const& range); /** @@ -742,6 +751,7 @@ namespace z3 { func_decl_vector recognizers(); }; + /** \brief Function declaration (aka function definition). It is the signature of interpreted and uninterpreted functions in Z3. The basic building block in Z3 is the function application. @@ -762,6 +772,8 @@ namespace z3 { sort range() const { Z3_sort r = Z3_get_range(ctx(), *this); check_error(); return sort(ctx(), r); } symbol name() const { Z3_symbol s = Z3_get_decl_name(ctx(), *this); check_error(); return symbol(ctx(), s); } Z3_decl_kind decl_kind() const { return Z3_get_decl_kind(ctx(), *this); } + unsigned num_parameters() const { return Z3_get_decl_num_parameters(ctx(), *this); } + func_decl transitive_closure(func_decl const&) { Z3_func_decl tc = Z3_mk_transitive_closure(ctx(), *this); check_error(); return func_decl(ctx(), tc); @@ -2679,6 +2691,43 @@ namespace z3 { return out; } + /** + \brief class for auxiliary parameters associated with func_decl + The class is initialized with a func_decl or application expression and an index + The accessor get_expr, get_sort, ... is available depending on the value of kind(). + The caller is responsible to check that the kind of the parameter aligns with the call (get_expr etc). + + Parameters are available on some declarations to contain additional information that is not passed as + arguments when a function is applied to arguments. For example, bit-vector extraction has two + integer parameters. Array map has a function parameter. + */ + class parameter { + Z3_parameter_kind m_kind; + func_decl m_decl; + unsigned m_index; + context& ctx() const { return m_decl.ctx(); } + void check_error() const { ctx().check_error(); } + public: + parameter(func_decl const& d, unsigned idx) : m_decl(d), m_index(idx) { + if (ctx().enable_exceptions() && idx >= d.num_parameters()) + Z3_THROW(exception("parameter index is out of bounds")); + m_kind = Z3_get_decl_parameter_kind(ctx(), d, idx); + } + parameter(expr const& e, unsigned idx) : m_decl(e.decl()), m_index(idx) { + if (ctx().enable_exceptions() && idx >= m_decl.num_parameters()) + Z3_THROW(exception("parameter index is out of bounds")); + m_kind = Z3_get_decl_parameter_kind(ctx(), m_decl, idx); + } + Z3_parameter_kind kind() const { return m_kind; } + expr get_expr() const { Z3_ast a = Z3_get_decl_ast_parameter(ctx(), m_decl, m_index); check_error(); return expr(ctx(), a); } + sort get_sort() const { Z3_sort s = Z3_get_decl_sort_parameter(ctx(), m_decl, m_index); check_error(); return sort(ctx(), s); } + func_decl get_decl() const { Z3_func_decl f = Z3_get_decl_func_decl_parameter(ctx(), m_decl, m_index); check_error(); return func_decl(ctx(), f); } + symbol get_symbol() const { Z3_symbol s = Z3_get_decl_symbol_parameter(ctx(), m_decl, m_index); check_error(); return symbol(ctx(), s); } + std::string get_rational() const { Z3_string s = Z3_get_decl_rational_parameter(ctx(), m_decl, m_index); check_error(); return s; } + double get_double() const { double d = Z3_get_decl_double_parameter(ctx(), m_decl, m_index); check_error(); return d; } + int get_int() const { int i = Z3_get_decl_int_parameter(ctx(), m_decl, m_index); check_error(); return i; } + }; + class solver : public object { Z3_solver m_solver; @@ -2712,6 +2761,16 @@ namespace z3 { void set(char const * k, double v) { params p(ctx()); p.set(k, v); set(p); } void set(char const * k, symbol const & v) { params p(ctx()); p.set(k, v); set(p); } void set(char const * k, char const* v) { params p(ctx()); p.set(k, v); set(p); } + /** + \brief Create a backtracking point. + + The solver contains a stack of assertions. + + \sa Z3_solver_get_num_scopes + \sa Z3_solver_pop + + def_API('Z3_solver_push', VOID, (_in(CONTEXT), _in(SOLVER))) + */ void push() { Z3_solver_push(ctx(), m_solver); check_error(); } void pop(unsigned n = 1) { Z3_solver_pop(ctx(), m_solver, n); check_error(); } void reset() { Z3_solver_reset(ctx(), m_solver); check_error(); } @@ -3604,6 +3663,19 @@ namespace z3 { } + inline func_decl context::recfun(symbol const & name, sort_vector const& domain, sort const & range) { + check_context(domain, range); + array domain1(domain); + Z3_func_decl f = Z3_mk_rec_func_decl(m_ctx, name, domain1.size(), domain1.ptr(), range); + check_error(); + return func_decl(*this, f); + } + + inline func_decl context::recfun(char const * name, sort_vector const& domain, sort const & range) { + return recfun(str_symbol(name), domain, range); + + } + inline func_decl context::recfun(char const * name, unsigned arity, sort const * domain, sort const & range) { return recfun(str_symbol(name), arity, domain, range); } diff --git a/src/api/java/CMakeLists.txt b/src/api/java/CMakeLists.txt index 4b13a25b1..bd4338f7b 100644 --- a/src/api/java/CMakeLists.txt +++ b/src/api/java/CMakeLists.txt @@ -179,6 +179,7 @@ set(Z3_JAVA_JAR_SOURCE_FILES Tactic.java TupleSort.java UninterpretedSort.java + UserPropagatorBase.java Version.java Z3Exception.java Z3Object.java diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 0f15d9411..b5b22405c 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -227,7 +227,7 @@ public class Context implements AutoCloseable { /** * Create a new array sort. **/ - public ArraySort mkArraySort(D domain, R range) + public final ArraySort mkArraySort(D domain, R range) { checkContextMatch(domain); checkContextMatch(range); @@ -238,7 +238,7 @@ public class Context implements AutoCloseable { /** * Create a new array sort. **/ - public ArraySort mkArraySort(Sort[] domains, R range) + public final ArraySort mkArraySort(Sort[] domains, R range) { checkContextMatch(domains); checkContextMatch(range); @@ -256,7 +256,7 @@ public class Context implements AutoCloseable { /** * Create a new sequence sort **/ - public SeqSort mkSeqSort(R s) + public final SeqSort mkSeqSort(R s) { return new SeqSort<>(this, Native.mkSeqSort(nCtx(), s.getNativeObject())); } @@ -264,7 +264,7 @@ public class Context implements AutoCloseable { /** * Create a new regular expression sort **/ - public ReSort mkReSort(R s) + public final ReSort mkReSort(R s) { return new ReSort<>(this, Native.mkReSort(nCtx(), s.getNativeObject())); } @@ -286,7 +286,7 @@ public class Context implements AutoCloseable { /** * Create a new enumeration sort. **/ - public EnumSort mkEnumSort(Symbol name, Symbol... enumNames) + public final EnumSort mkEnumSort(Symbol name, Symbol... enumNames) { checkContextMatch(name); @@ -297,7 +297,7 @@ public class Context implements AutoCloseable { /** * Create a new enumeration sort. **/ - public EnumSort mkEnumSort(String name, String... enumNames) + public final EnumSort mkEnumSort(String name, String... enumNames) { return new EnumSort<>(this, mkSymbol(name), mkSymbols(enumNames)); @@ -306,7 +306,7 @@ public class Context implements AutoCloseable { /** * Create a new list sort. **/ - public ListSort mkListSort(Symbol name, R elemSort) + public final ListSort mkListSort(Symbol name, R elemSort) { checkContextMatch(name); checkContextMatch(elemSort); @@ -316,7 +316,7 @@ public class Context implements AutoCloseable { /** * Create a new list sort. **/ - public ListSort mkListSort(String name, R elemSort) + public final ListSort mkListSort(String name, R elemSort) { checkContextMatch(elemSort); return new ListSort<>(this, mkSymbol(name), elemSort); @@ -325,7 +325,7 @@ public class Context implements AutoCloseable { /** * Create a new finite domain sort. **/ - public FiniteDomainSort mkFiniteDomainSort(Symbol name, long size) + public final FiniteDomainSort mkFiniteDomainSort(Symbol name, long size) { checkContextMatch(name); @@ -335,7 +335,7 @@ public class Context implements AutoCloseable { /** * Create a new finite domain sort. **/ - public FiniteDomainSort mkFiniteDomainSort(String name, long size) + public final FiniteDomainSort mkFiniteDomainSort(String name, long size) { return new FiniteDomainSort<>(this, mkSymbol(name), size); @@ -352,7 +352,7 @@ public class Context implements AutoCloseable { * an index referring to one of the recursive datatypes that is * declared. **/ - public Constructor mkConstructor(Symbol name, Symbol recognizer, + public final Constructor mkConstructor(Symbol name, Symbol recognizer, Symbol[] fieldNames, Sort[] sorts, int[] sortRefs) { @@ -362,7 +362,7 @@ public class Context implements AutoCloseable { /** * Create a datatype constructor. **/ - public Constructor mkConstructor(String name, String recognizer, + public final Constructor mkConstructor(String name, String recognizer, String[] fieldNames, Sort[] sorts, int[] sortRefs) { return of(this, mkSymbol(name), mkSymbol(recognizer), mkSymbols(fieldNames), sorts, sortRefs); @@ -371,7 +371,7 @@ public class Context implements AutoCloseable { /** * Create a new datatype sort. **/ - public DatatypeSort mkDatatypeSort(Symbol name, Constructor[] constructors) + public final DatatypeSort mkDatatypeSort(Symbol name, Constructor[] constructors) { checkContextMatch(name); checkContextMatch(constructors); @@ -381,7 +381,7 @@ public class Context implements AutoCloseable { /** * Create a new datatype sort. **/ - public DatatypeSort mkDatatypeSort(String name, Constructor[] constructors) + public final DatatypeSort mkDatatypeSort(String name, Constructor[] constructors) { checkContextMatch(constructors); @@ -431,7 +431,7 @@ public class Context implements AutoCloseable { * that is passed in as argument is updated with value v, * the remaining fields of t are unchanged. **/ - public Expr mkUpdateField(FuncDecl field, Expr t, Expr v) + public final Expr mkUpdateField(FuncDecl field, Expr t, Expr v) throws Z3Exception { return (Expr) Expr.create(this, @@ -444,7 +444,7 @@ public class Context implements AutoCloseable { /** * Creates a new function declaration. **/ - public FuncDecl mkFuncDecl(Symbol name, Sort[] domain, R range) + public final FuncDecl mkFuncDecl(Symbol name, Sort[] domain, R range) { checkContextMatch(name); checkContextMatch(domain); @@ -452,10 +452,25 @@ public class Context implements AutoCloseable { return new FuncDecl<>(this, name, domain, range); } + public final FuncDecl mkPropagateFunction(Symbol name, Sort[] domain, R range) + { + checkContextMatch(name); + checkContextMatch(domain); + checkContextMatch(range); + long f = Native.solverPropagateDeclare( + this.nCtx(), + name.getNativeObject(), + AST.arrayLength(domain), + AST.arrayToNative(domain), + range.getNativeObject()); + return new FuncDecl<>(this, f); + } + + /** * Creates a new function declaration. **/ - public FuncDecl mkFuncDecl(Symbol name, Sort domain, R range) + public final FuncDecl mkFuncDecl(Symbol name, Sort domain, R range) { checkContextMatch(name); @@ -468,7 +483,7 @@ public class Context implements AutoCloseable { /** * Creates a new function declaration. **/ - public FuncDecl mkFuncDecl(String name, Sort[] domain, R range) + public final FuncDecl mkFuncDecl(String name, Sort[] domain, R range) { checkContextMatch(domain); @@ -479,7 +494,7 @@ public class Context implements AutoCloseable { /** * Creates a new function declaration. **/ - public FuncDecl mkFuncDecl(String name, Sort domain, R range) + public final FuncDecl mkFuncDecl(String name, Sort domain, R range) { checkContextMatch(domain); @@ -491,7 +506,7 @@ public class Context implements AutoCloseable { /** * Creates a new recursive function declaration. **/ - public FuncDecl mkRecFuncDecl(Symbol name, Sort[] domain, R range) + public final FuncDecl mkRecFuncDecl(Symbol name, Sort[] domain, R range) { checkContextMatch(name); checkContextMatch(domain); @@ -506,7 +521,7 @@ public class Context implements AutoCloseable { * MkRecFuncDecl. The body may contain recursive uses of the function or * other mutually recursive functions. */ - public void AddRecDef(FuncDecl f, Expr[] args, Expr body) + public final void AddRecDef(FuncDecl f, Expr[] args, Expr body) { checkContextMatch(f); checkContextMatch(args); @@ -521,7 +536,7 @@ public class Context implements AutoCloseable { * @see #mkFuncDecl(String,Sort,Sort) * @see #mkFuncDecl(String,Sort[],Sort) **/ - public FuncDecl mkFreshFuncDecl(String prefix, Sort[] domain, R range) + public final FuncDecl mkFreshFuncDecl(String prefix, Sort[] domain, R range) { checkContextMatch(domain); @@ -532,7 +547,7 @@ public class Context implements AutoCloseable { /** * Creates a new constant function declaration. **/ - public FuncDecl mkConstDecl(Symbol name, R range) + public final FuncDecl mkConstDecl(Symbol name, R range) { checkContextMatch(name); checkContextMatch(range); @@ -542,7 +557,7 @@ public class Context implements AutoCloseable { /** * Creates a new constant function declaration. **/ - public FuncDecl mkConstDecl(String name, R range) + public final FuncDecl mkConstDecl(String name, R range) { checkContextMatch(range); return new FuncDecl<>(this, mkSymbol(name), null, range); @@ -554,7 +569,7 @@ public class Context implements AutoCloseable { * @see #mkFuncDecl(String,Sort,Sort) * @see #mkFuncDecl(String,Sort[],Sort) **/ - public FuncDecl mkFreshConstDecl(String prefix, R range) + public final FuncDecl mkFreshConstDecl(String prefix, R range) { checkContextMatch(range); @@ -566,7 +581,7 @@ public class Context implements AutoCloseable { * @param index The de-Bruijn index of the variable * @param ty The sort of the variable **/ - public Expr mkBound(int index, R ty) + public final Expr mkBound(int index, R ty) { return (Expr) Expr.create(this, Native.mkBound(nCtx(), index, ty.getNativeObject())); @@ -590,7 +605,7 @@ public class Context implements AutoCloseable { * Creates a new Constant of sort {@code range} and named * {@code name}. **/ - public Expr mkConst(Symbol name, R range) + public final Expr mkConst(Symbol name, R range) { checkContextMatch(name); checkContextMatch(range); @@ -605,7 +620,7 @@ public class Context implements AutoCloseable { * Creates a new Constant of sort {@code range} and named * {@code name}. **/ - public Expr mkConst(String name, R range) + public final Expr mkConst(String name, R range) { return mkConst(mkSymbol(name), range); } @@ -614,7 +629,7 @@ public class Context implements AutoCloseable { * Creates a fresh Constant of sort {@code range} and a name * prefixed with {@code prefix}. **/ - public Expr mkFreshConst(String prefix, R range) + public final Expr mkFreshConst(String prefix, R range) { checkContextMatch(range); return (Expr) Expr.create(this, @@ -625,7 +640,7 @@ public class Context implements AutoCloseable { * Creates a fresh constant from the FuncDecl {@code f}. * @param f A decl of a 0-arity function **/ - public Expr mkConst(FuncDecl f) + public final Expr mkConst(FuncDecl f) { return mkApp(f, (Expr[]) null); } @@ -754,7 +769,7 @@ public class Context implements AutoCloseable { /** * Create an expression representing {@code not(a)}. **/ - public BoolExpr mkNot(Expr a) + public final BoolExpr mkNot(Expr a) { checkContextMatch(a); return new BoolExpr(this, Native.mkNot(nCtx(), a.getNativeObject())); @@ -767,7 +782,7 @@ public class Context implements AutoCloseable { * @param t2 An expression * @param t3 An expression with the same sort as {@code t2} **/ - public Expr mkITE(Expr t1, Expr t2, Expr t3) + public final Expr mkITE(Expr t1, Expr t2, Expr t3) { checkContextMatch(t1); checkContextMatch(t2); @@ -867,7 +882,7 @@ public class Context implements AutoCloseable { /** * Create an expression representing {@code -t}. **/ - public ArithExpr mkUnaryMinus(Expr t) + public final ArithExpr mkUnaryMinus(Expr t) { checkContextMatch(t); return (ArithExpr) Expr.create(this, @@ -877,7 +892,7 @@ public class Context implements AutoCloseable { /** * Create an expression representing {@code t1 / t2}. **/ - public ArithExpr mkDiv(Expr t1, Expr t2) + public final ArithExpr mkDiv(Expr t1, Expr t2) { checkContextMatch(t1); checkContextMatch(t2); @@ -914,7 +929,7 @@ public class Context implements AutoCloseable { /** * Create an expression representing {@code t1 ^ t2}. **/ - public ArithExpr mkPower(Expr t1, + public final ArithExpr mkPower(Expr t1, Expr t2) { checkContextMatch(t1); @@ -1693,7 +1708,7 @@ public class Context implements AutoCloseable { /** * Create an array constant. **/ - public ArrayExpr mkArrayConst(Symbol name, D domain, R range) + public final ArrayExpr mkArrayConst(Symbol name, D domain, R range) { return (ArrayExpr) mkConst(name, mkArraySort(domain, range)); @@ -1702,7 +1717,7 @@ public class Context implements AutoCloseable { /** * Create an array constant. **/ - public ArrayExpr mkArrayConst(String name, D domain, R range) + public final ArrayExpr mkArrayConst(String name, D domain, R range) { return (ArrayExpr) mkConst(mkSymbol(name), mkArraySort(domain, range)); @@ -1720,7 +1735,7 @@ public class Context implements AutoCloseable { * @see #mkArraySort(Sort[], R) * @see #mkStore(Expr> a, Expr i, Expr v) **/ - public Expr mkSelect(Expr> a, Expr i) + public final Expr mkSelect(Expr> a, Expr i) { checkContextMatch(a); checkContextMatch(i); @@ -1742,7 +1757,7 @@ public class Context implements AutoCloseable { * @see #mkArraySort(Sort[], R) * @see #mkStore(Expr> a, Expr i, Expr v) **/ - public Expr mkSelect(Expr> a, Expr[] args) + public final Expr mkSelect(Expr> a, Expr[] args) { checkContextMatch(a); checkContextMatch(args); @@ -1767,7 +1782,7 @@ public class Context implements AutoCloseable { * @see #mkSelect(Expr> a, Expr i) **/ - public ArrayExpr mkStore(Expr> a, Expr i, Expr v) + public final ArrayExpr mkStore(Expr> a, Expr i, Expr v) { checkContextMatch(a); checkContextMatch(i); @@ -1792,7 +1807,7 @@ public class Context implements AutoCloseable { * @see #mkSelect(Expr> a, Expr i) **/ - public ArrayExpr mkStore(Expr> a, Expr[] args, Expr v) + public final ArrayExpr mkStore(Expr> a, Expr[] args, Expr v) { checkContextMatch(a); checkContextMatch(args); @@ -1810,7 +1825,7 @@ public class Context implements AutoCloseable { * @see #mkSelect(Expr> a, Expr i) * **/ - public ArrayExpr mkConstArray(D domain, Expr v) + public final ArrayExpr mkConstArray(D domain, Expr v) { checkContextMatch(domain); checkContextMatch(v); @@ -1847,7 +1862,7 @@ public class Context implements AutoCloseable { * value, for arrays that can be represented as finite maps with a default * range value. **/ - public Expr mkTermArray(Expr> array) + public final Expr mkTermArray(Expr> array) { checkContextMatch(array); return (Expr) Expr.create(this, @@ -1857,7 +1872,7 @@ public class Context implements AutoCloseable { /** * Create Extentionality index. Two arrays are equal if and only if they are equal on the index returned by MkArrayExt. **/ - public Expr mkArrayExt(Expr> arg1, Expr> arg2) + public final Expr mkArrayExt(Expr> arg1, Expr> arg2) { checkContextMatch(arg1); checkContextMatch(arg2); @@ -1868,7 +1883,7 @@ public class Context implements AutoCloseable { /** * Create a set type. **/ - public SetSort mkSetSort(D ty) + public final SetSort mkSetSort(D ty) { checkContextMatch(ty); return new SetSort<>(this, ty); @@ -1877,7 +1892,7 @@ public class Context implements AutoCloseable { /** * Create an empty set. **/ - public ArrayExpr mkEmptySet(D domain) + public final ArrayExpr mkEmptySet(D domain) { checkContextMatch(domain); return (ArrayExpr) Expr.create(this, @@ -1887,7 +1902,7 @@ public class Context implements AutoCloseable { /** * Create the full set. **/ - public ArrayExpr mkFullSet(D domain) + public final ArrayExpr mkFullSet(D domain) { checkContextMatch(domain); return (ArrayExpr) Expr.create(this, @@ -1897,7 +1912,7 @@ public class Context implements AutoCloseable { /** * Add an element to the set. **/ - public ArrayExpr mkSetAdd(Expr> set, Expr element) + public final ArrayExpr mkSetAdd(Expr> set, Expr element) { checkContextMatch(set); checkContextMatch(element); @@ -1909,7 +1924,7 @@ public class Context implements AutoCloseable { /** * Remove an element from a set. **/ - public ArrayExpr mkSetDel(Expr> set, Expr element) + public final ArrayExpr mkSetDel(Expr> set, Expr element) { checkContextMatch(set); checkContextMatch(element); @@ -1945,7 +1960,7 @@ public class Context implements AutoCloseable { /** * Take the difference between two sets. **/ - public ArrayExpr mkSetDifference(Expr> arg1, Expr> arg2) + public final ArrayExpr mkSetDifference(Expr> arg1, Expr> arg2) { checkContextMatch(arg1); checkContextMatch(arg2); @@ -1957,7 +1972,7 @@ public class Context implements AutoCloseable { /** * Take the complement of a set. **/ - public ArrayExpr mkSetComplement(Expr> arg) + public final ArrayExpr mkSetComplement(Expr> arg) { checkContextMatch(arg); return (ArrayExpr)Expr.create(this, @@ -1967,7 +1982,7 @@ public class Context implements AutoCloseable { /** * Check for set membership. **/ - public BoolExpr mkSetMembership(Expr elem, Expr> set) + public final BoolExpr mkSetMembership(Expr elem, Expr> set) { checkContextMatch(elem); checkContextMatch(set); @@ -1979,7 +1994,7 @@ public class Context implements AutoCloseable { /** * Check for subsetness of sets. **/ - public BoolExpr mkSetSubset(Expr> arg1, Expr> arg2) + public final BoolExpr mkSetSubset(Expr> arg1, Expr> arg2) { checkContextMatch(arg1); checkContextMatch(arg2); @@ -1996,7 +2011,7 @@ public class Context implements AutoCloseable { /** * Create the empty sequence. */ - public SeqExpr mkEmptySeq(R s) + public final SeqExpr mkEmptySeq(R s) { checkContextMatch(s); return (SeqExpr) Expr.create(this, Native.mkSeqEmpty(nCtx(), s.getNativeObject())); @@ -2005,7 +2020,7 @@ public class Context implements AutoCloseable { /** * Create the singleton sequence. */ - public SeqExpr mkUnit(Expr elem) + public final SeqExpr mkUnit(Expr elem) { checkContextMatch(elem); return (SeqExpr) Expr.create(this, Native.mkSeqUnit(nCtx(), elem.getNativeObject())); @@ -2018,11 +2033,11 @@ public class Context implements AutoCloseable { { StringBuilder buf = new StringBuilder(); for (int i = 0; i < s.length(); ++i) { - int code = s.codePointAt(i); - if (code <= 32 || 127 < code) - buf.append(String.format("\\u{%x}", code)); - else - buf.append(s.charAt(i)); + int code = s.codePointAt(i); + if (code <= 32 || 127 < code) + buf.append(String.format("\\u{%x}", code)); + else + buf.append(s.charAt(i)); } return (SeqExpr) Expr.create(this, Native.mkString(nCtx(), buf.toString())); } @@ -2073,7 +2088,7 @@ public class Context implements AutoCloseable { /** * Retrieve the length of a given sequence. */ - public IntExpr mkLength(Expr> s) + public final IntExpr mkLength(Expr> s) { checkContextMatch(s); return (IntExpr) Expr.create(this, Native.mkSeqLength(nCtx(), s.getNativeObject())); @@ -2082,7 +2097,7 @@ public class Context implements AutoCloseable { /** * Check for sequence prefix. */ - public BoolExpr mkPrefixOf(Expr> s1, Expr> s2) + public final BoolExpr mkPrefixOf(Expr> s1, Expr> s2) { checkContextMatch(s1, s2); return (BoolExpr) Expr.create(this, Native.mkSeqPrefix(nCtx(), s1.getNativeObject(), s2.getNativeObject())); @@ -2091,7 +2106,7 @@ public class Context implements AutoCloseable { /** * Check for sequence suffix. */ - public BoolExpr mkSuffixOf(Expr> s1, Expr> s2) + public final BoolExpr mkSuffixOf(Expr> s1, Expr> s2) { checkContextMatch(s1, s2); return (BoolExpr)Expr.create(this, Native.mkSeqSuffix(nCtx(), s1.getNativeObject(), s2.getNativeObject())); @@ -2100,7 +2115,7 @@ public class Context implements AutoCloseable { /** * Check for sequence containment of s2 in s1. */ - public BoolExpr mkContains(Expr> s1, Expr> s2) + public final BoolExpr mkContains(Expr> s1, Expr> s2) { checkContextMatch(s1, s2); return (BoolExpr) Expr.create(this, Native.mkSeqContains(nCtx(), s1.getNativeObject(), s2.getNativeObject())); @@ -2129,7 +2144,7 @@ public class Context implements AutoCloseable { /** * Retrieve sequence of length one at index. */ - public SeqExpr mkAt(Expr> s, Expr index) + public final SeqExpr mkAt(Expr> s, Expr index) { checkContextMatch(s, index); return (SeqExpr) Expr.create(this, Native.mkSeqAt(nCtx(), s.getNativeObject(), index.getNativeObject())); @@ -2138,7 +2153,7 @@ public class Context implements AutoCloseable { /** * Retrieve element at index. */ - public Expr mkNth(Expr> s, Expr index) + public final Expr mkNth(Expr> s, Expr index) { checkContextMatch(s, index); return (Expr) Expr.create(this, Native.mkSeqNth(nCtx(), s.getNativeObject(), index.getNativeObject())); @@ -2148,7 +2163,7 @@ public class Context implements AutoCloseable { /** * Extract subsequence. */ - public SeqExpr mkExtract(Expr> s, Expr offset, Expr length) + public final SeqExpr mkExtract(Expr> s, Expr offset, Expr length) { checkContextMatch(s, offset, length); return (SeqExpr) Expr.create(this, Native.mkSeqExtract(nCtx(), s.getNativeObject(), offset.getNativeObject(), length.getNativeObject())); @@ -2157,7 +2172,7 @@ public class Context implements AutoCloseable { /** * Extract index of sub-string starting at offset. */ - public IntExpr mkIndexOf(Expr> s, Expr> substr, Expr offset) + public final IntExpr mkIndexOf(Expr> s, Expr> substr, Expr offset) { checkContextMatch(s, substr, offset); return (IntExpr)Expr.create(this, Native.mkSeqIndex(nCtx(), s.getNativeObject(), substr.getNativeObject(), offset.getNativeObject())); @@ -2166,7 +2181,7 @@ public class Context implements AutoCloseable { /** * Replace the first occurrence of src by dst in s. */ - public SeqExpr mkReplace(Expr> s, Expr> src, Expr> dst) + public final SeqExpr mkReplace(Expr> s, Expr> src, Expr> dst) { checkContextMatch(s, src, dst); return (SeqExpr) Expr.create(this, Native.mkSeqReplace(nCtx(), s.getNativeObject(), src.getNativeObject(), dst.getNativeObject())); @@ -2175,17 +2190,17 @@ public class Context implements AutoCloseable { /** * Convert a regular expression that accepts sequence s. */ - public ReExpr mkToRe(Expr> s) + public final ReExpr> mkToRe(Expr> s) { checkContextMatch(s); - return (ReExpr) Expr.create(this, Native.mkSeqToRe(nCtx(), s.getNativeObject())); + return (ReExpr>) Expr.create(this, Native.mkSeqToRe(nCtx(), s.getNativeObject())); } /** * Check for regular expression membership. */ - public BoolExpr mkInRe(Expr> s, Expr> re) + public final BoolExpr mkInRe(Expr> s, ReExpr> re) { checkContextMatch(s, re); return (BoolExpr) Expr.create(this, Native.mkSeqInRe(nCtx(), s.getNativeObject(), re.getNativeObject())); @@ -2194,7 +2209,7 @@ public class Context implements AutoCloseable { /** * Take the Kleene star of a regular expression. */ - public ReExpr mkStar(Expr> re) + public final ReExpr mkStar(Expr> re) { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkReStar(nCtx(), re.getNativeObject())); @@ -2203,7 +2218,7 @@ public class Context implements AutoCloseable { /** * Create power regular expression. */ - public ReExpr mkPower(Expr> re, int n) + public final ReExpr mkPower(Expr> re, int n) { return (ReExpr) Expr.create(this, Native.mkRePower(nCtx(), re.getNativeObject(), n)); } @@ -2211,7 +2226,7 @@ public class Context implements AutoCloseable { /** * Take the lower and upper-bounded Kleene star of a regular expression. */ - public ReExpr mkLoop(Expr> re, int lo, int hi) + public final ReExpr mkLoop(Expr> re, int lo, int hi) { return (ReExpr) Expr.create(this, Native.mkReLoop(nCtx(), re.getNativeObject(), lo, hi)); } @@ -2219,7 +2234,7 @@ public class Context implements AutoCloseable { /** * Take the lower-bounded Kleene star of a regular expression. */ - public ReExpr mkLoop(Expr> re, int lo) + public final ReExpr mkLoop(Expr> re, int lo) { return (ReExpr) Expr.create(this, Native.mkReLoop(nCtx(), re.getNativeObject(), lo, 0)); } @@ -2228,7 +2243,7 @@ public class Context implements AutoCloseable { /** * Take the Kleene plus of a regular expression. */ - public ReExpr mkPlus(Expr> re) + public final ReExpr mkPlus(Expr> re) { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkRePlus(nCtx(), re.getNativeObject())); @@ -2237,7 +2252,7 @@ public class Context implements AutoCloseable { /** * Create the optional regular expression. */ - public ReExpr mkOption(Expr> re) + public final ReExpr mkOption(Expr> re) { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkReOption(nCtx(), re.getNativeObject())); @@ -2246,7 +2261,7 @@ public class Context implements AutoCloseable { /** * Create the complement regular expression. */ - public ReExpr mkComplement(Expr> re) + public final ReExpr mkComplement(Expr> re) { checkContextMatch(re); return (ReExpr) Expr.create(this, Native.mkReComplement(nCtx(), re.getNativeObject())); @@ -2285,10 +2300,10 @@ public class Context implements AutoCloseable { /** * Create a difference regular expression. */ - public ReExpr mkDiff(Expr> a, Expr> b) + public final ReExpr mkDiff(Expr> a, Expr> b) { checkContextMatch(a, b); - return (ReExpr) Expr.create(this, Native.mkReDiff(nCtx(), a.getNativeObject(), b.getNativeObject())); + return (ReExpr) Expr.create(this, Native.mkReDiff(nCtx(), a.getNativeObject(), b.getNativeObject())); } @@ -2296,7 +2311,7 @@ public class Context implements AutoCloseable { * Create the empty regular expression. * Coresponds to re.none */ - public ReExpr mkEmptyRe(R s) + public final ReExpr mkEmptyRe(ReSort s) { return (ReExpr) Expr.create(this, Native.mkReEmpty(nCtx(), s.getNativeObject())); } @@ -2305,16 +2320,17 @@ public class Context implements AutoCloseable { * Create the full regular expression. * Corresponds to re.all */ - public ReExpr mkFullRe(R s) + public final ReExpr mkFullRe(ReSort s) { return (ReExpr) Expr.create(this, Native.mkReFull(nCtx(), s.getNativeObject())); } /** * Create regular expression that accepts all characters + * R has to be a sequence sort. * Corresponds to re.allchar */ - public ReExpr mkAllcharRe(R s) + public final ReExpr mkAllcharRe(ReSort s) { return (ReExpr) Expr.create(this, Native.mkReAllchar(nCtx(), s.getNativeObject())); } @@ -2322,10 +2338,10 @@ public class Context implements AutoCloseable { /** * Create a range expression. */ - public ReExpr mkRange(Expr> lo, Expr> hi) + public final ReExpr> mkRange(Expr> lo, Expr> hi) { checkContextMatch(lo, hi); - return (ReExpr) Expr.create(this, Native.mkReRange(nCtx(), lo.getNativeObject(), hi.getNativeObject())); + return (ReExpr>) Expr.create(this, Native.mkReRange(nCtx(), lo.getNativeObject(), hi.getNativeObject())); } /** @@ -2429,7 +2445,7 @@ public class Context implements AutoCloseable { * * @return A Term with value {@code v} and sort {@code ty} **/ - public Expr mkNumeral(String v, R ty) + public final Expr mkNumeral(String v, R ty) { checkContextMatch(ty); return (Expr) Expr.create(this, @@ -2446,7 +2462,7 @@ public class Context implements AutoCloseable { * * @return A Term with value {@code v} and type {@code ty} **/ - public Expr mkNumeral(int v, R ty) + public final Expr mkNumeral(int v, R ty) { checkContextMatch(ty); return (Expr) Expr.create(this, Native.mkInt(nCtx(), v, ty.getNativeObject())); @@ -2462,7 +2478,7 @@ public class Context implements AutoCloseable { * * @return A Term with value {@code v} and type {@code ty} **/ - public Expr mkNumeral(long v, R ty) + public final Expr mkNumeral(long v, R ty) { checkContextMatch(ty); return (Expr) Expr.create(this, @@ -2717,7 +2733,7 @@ public class Context implements AutoCloseable { * @param names names of the bound variables. * @param body the body of the quantifier. **/ - public Lambda mkLambda(Sort[] sorts, Symbol[] names, Expr body) + public final Lambda mkLambda(Sort[] sorts, Symbol[] names, Expr body) { return Lambda.of(this, sorts, names, body); } @@ -2728,7 +2744,7 @@ public class Context implements AutoCloseable { * Creates a lambda expression using a list of constants that will * form the set of bound variables. **/ - public Lambda mkLambda(Expr[] boundConstants, Expr body) + public final Lambda mkLambda(Expr[] boundConstants, Expr body) { return Lambda.of(this, boundConstants, body); } @@ -4179,7 +4195,7 @@ public class Context implements AutoCloseable { * @param index The index of the order. * @param sort The sort of the order. */ - public FuncDecl mkLinearOrder(R sort, int index) { + public final FuncDecl mkLinearOrder(R sort, int index) { return (FuncDecl) FuncDecl.create( this, Native.mkLinearOrder( @@ -4195,7 +4211,7 @@ public class Context implements AutoCloseable { * @param index The index of the order. * @param sort The sort of the order. */ - public FuncDecl mkPartialOrder(R sort, int index) { + public final FuncDecl mkPartialOrder(R sort, int index) { return (FuncDecl) FuncDecl.create( this, Native.mkPartialOrder( diff --git a/src/api/java/NativeStatic.txt b/src/api/java/NativeStatic.txt index 4693272d5..f68893e6b 100644 --- a/src/api/java/NativeStatic.txt +++ b/src/api/java/NativeStatic.txt @@ -77,3 +77,156 @@ DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_setInternalErrorHand Z3_set_error_handler((Z3_context)a0, Z3JavaErrorHandler); } + +#include + +struct JavaInfo { + JNIEnv *jenv = nullptr; + jobject jobj = nullptr; + + jmethodID push = nullptr; + jmethodID pop = nullptr; + jmethodID fresh = nullptr; + jmethodID created = nullptr; + jmethodID fixed = nullptr; + jmethodID eq = nullptr; + jmethodID final = nullptr; + + Z3_solver_callback cb = nullptr; +}; + +struct ScopedCB { + JavaInfo *info; + ScopedCB(JavaInfo *_info, Z3_solver_callback cb): info(_info) { + info->cb = cb; + } + ~ScopedCB() { + info->cb = nullptr; + } +}; + +static void push_eh(void* _p, Z3_solver_callback cb) { + JavaInfo *info = static_cast(_p); + ScopedCB scoped(info, cb); + info->jenv->CallVoidMethod(info->jobj, info->push); +} + +static void pop_eh(void* _p, Z3_solver_callback cb, unsigned int number) { + JavaInfo *info = static_cast(_p); + ScopedCB scoped(info, cb); + info->jenv->CallVoidMethod(info->jobj, info->pop, number); +} + +static void* fresh_eh(void* _p, Z3_context new_context) { + JavaInfo *info = static_cast(_p); + return info->jenv->CallObjectMethod(info->jobj, info->fresh, (jlong)new_context); +} + +static void created_eh(void* _p, Z3_solver_callback cb, Z3_ast _e) { + JavaInfo *info = static_cast(_p); + ScopedCB scoped(info, cb); + info->jenv->CallVoidMethod(info->jobj, info->created, (jlong)_e); +} + +static void fixed_eh(void* _p, Z3_solver_callback cb, Z3_ast _var, Z3_ast _value) { + JavaInfo *info = static_cast(_p); + ScopedCB scoped(info, cb); + info->jenv->CallVoidMethod(info->jobj, info->fixed, (jlong)_var, (jlong)_value); +} + +static void eq_eh(void* _p, Z3_solver_callback cb, Z3_ast _x, Z3_ast _y) { + JavaInfo *info = static_cast(_p); + ScopedCB scoped(info, cb); + info->jenv->CallVoidMethod(info->jobj, info->eq, (jlong)_x, (jlong)_y); +} + +static void final_eh(void* _p, Z3_solver_callback cb) { + JavaInfo *info = static_cast(_p); + ScopedCB scoped(info, cb); + info->jenv->CallVoidMethod(info->jobj, info->final); +} + +// TODO: implement decide +static void decide_eh(void* _p, Z3_solver_callback cb, Z3_ast* _val, unsigned* bit, Z3_lbool* is_pos) { + JavaInfo *info = static_cast(_p); + ScopedCB scoped(info, cb); + +} + +DLL_VIS JNIEXPORT jlong JNICALL Java_com_microsoft_z3_Native_propagateInit(JNIEnv *jenv, jclass cls, jobject jobj, jlong ctx, jlong solver) { + JavaInfo *info = new JavaInfo; + + info->jenv = jenv; + info->jobj = jenv->NewGlobalRef(jobj); + jclass jcls = jenv->GetObjectClass(info->jobj); + info->push = jenv->GetMethodID(jcls, "pushWrapper", "()V"); + info->pop = jenv->GetMethodID(jcls, "popWrapper", "(I)V"); + info->fresh = jenv->GetMethodID(jcls, "freshWrapper", "(J)Lcom/microsoft/z3/Native$UserPropagatorBase;"); + info->created = jenv->GetMethodID(jcls, "createdWrapper", "(J)V"); + info->fixed = jenv->GetMethodID(jcls, "fixedWrapper", "(JJ)V"); + info->eq = jenv->GetMethodID(jcls, "eqWrapper", "(JJ)V"); + info->final = jenv->GetMethodID(jcls, "finWrapper", "()V"); + + if (!info->push || !info->pop || !info->fresh || !info->created || !info->fixed || !info->eq || !info->final) { + assert(false); + } + + Z3_solver_propagate_init((Z3_context)ctx, (Z3_solver)solver, info, push_eh, pop_eh, fresh_eh); + + return (jlong)info; +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateDestroy(JNIEnv *jenv, jclass cls, jobject jobj, jlong ctx, jlong solver, jlong javainfo) { + JavaInfo *info = (JavaInfo*)javainfo; + info->jenv->DeleteGlobalRef(info->jobj); + delete info; +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateRegisterCreated(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver) { + Z3_solver_propagate_created((Z3_context)ctx, (Z3_solver)solver, created_eh); +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateRegisterFinal(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver) { + Z3_solver_propagate_final((Z3_context)ctx, (Z3_solver)solver, final_eh); +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateRegisterFixed(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver) { + Z3_solver_propagate_fixed((Z3_context)ctx, (Z3_solver)solver, fixed_eh); +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateRegisterEq(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver) { + Z3_solver_propagate_eq((Z3_context)ctx, (Z3_solver)solver, eq_eh); +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateRegisterDecide(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver) { + Z3_solver_propagate_decide((Z3_context)ctx, (Z3_solver)solver, decide_eh); +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateConflict(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver, jlong javainfo, jlong num_fixed, jlongArray fixed, jlong num_eqs, jlongArray eq_lhs, jlongArray eq_rhs, jlong conseq) { + JavaInfo *info = (JavaInfo*)javainfo; + GETLONGAELEMS(Z3_ast, fixed, _fixed); + GETLONGAELEMS(Z3_ast, eq_lhs, _eq_lhs); + GETLONGAELEMS(Z3_ast, eq_rhs, _eq_rhs); + Z3_solver_propagate_consequence((Z3_context)ctx, info->cb, num_fixed, _fixed, num_eqs, _eq_lhs, _eq_rhs, (Z3_ast)conseq); + RELEASELONGAELEMS(fixed, _fixed); + RELEASELONGAELEMS(eq_lhs, _eq_lhs); + RELEASELONGAELEMS(eq_rhs, _eq_rhs); +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateAdd(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver, jlong javainfo, jlong e) { + JavaInfo *info = (JavaInfo*)javainfo; + Z3_solver_callback cb = info->cb; + if (cb) + Z3_solver_propagate_register_cb((Z3_context)ctx, cb, (Z3_ast)e); + else if (solver) + Z3_solver_propagate_register((Z3_context)ctx, (Z3_solver)solver, (Z3_ast)e); + else { + assert(false); + } +} + +DLL_VIS JNIEXPORT void JNICALL Java_com_microsoft_z3_Native_propagateNextSplit(JNIEnv * jenv, jclass cls, jobject jobj, jlong ctx, jlong solver, jlong javainfo, long e, long idx, long phase) { + JavaInfo *info = (JavaInfo*)javainfo; + Z3_solver_callback cb = info->cb; + Z3_solver_next_split((Z3_context)ctx, cb, (Z3_ast)e, idx, Z3_lbool(phase)); +} diff --git a/src/api/java/UserPropagatorBase.java b/src/api/java/UserPropagatorBase.java new file mode 100644 index 000000000..90243ba68 --- /dev/null +++ b/src/api/java/UserPropagatorBase.java @@ -0,0 +1,97 @@ +package com.microsoft.z3; + +import com.microsoft.z3.Context; +import com.microsoft.z3.enumerations.Z3_lbool; + +public abstract class UserPropagatorBase extends Native.UserPropagatorBase { + private Context ctx; + private Solver solver; + + public UserPropagatorBase(Context _ctx, Solver _solver) { + super(_ctx.nCtx(), _solver.getNativeObject()); + ctx = _ctx; + solver = _solver; + } + + public final Context getCtx() { + return ctx; + } + + public final Solver getSolver() { + return solver; + } + + @Override + protected final void pushWrapper() { + push(); + } + + @Override + protected final void popWrapper(int number) { + pop(number); + } + + @Override + protected final void finWrapper() { + fin(); + } + + @Override + protected final void eqWrapper(long lx, long ly) { + Expr x = new Expr(ctx, lx); + Expr y = new Expr(ctx, ly); + eq(x, y); + } + + @Override + protected final UserPropagatorBase freshWrapper(long lctx) { + return fresh(new Context(lctx)); + } + + @Override + protected final void createdWrapper(long last) { + created(new Expr(ctx, last)); + } + + @Override + protected final void fixedWrapper(long lvar, long lvalue) { + Expr var = new Expr(ctx, lvar); + Expr value = new Expr(ctx, lvalue); + fixed(var, value); + } + + public abstract void push(); + + public abstract void pop(int number); + + public abstract UserPropagatorBase fresh(Context ctx); + + public void created(Expr ast) {} + + public void fixed(Expr var, Expr value) {} + + public void eq(Expr x, Expr y) {} + + public void fin() {} + + public final void add(Expr expr) { + Native.propagateAdd(this, ctx.nCtx(), solver.getNativeObject(), javainfo, expr.getNativeObject()); + } + + public final void conflict(Expr[] fixed) { + conflict(fixed, new Expr[0], new Expr[0]); + } + + public final void conflict(Expr[] fixed, Expr[] lhs, Expr[] rhs) { + AST conseq = ctx.mkBool(false); + Native.propagateConflict( + this, ctx.nCtx(), solver.getNativeObject(), javainfo, + fixed.length, AST.arrayToNative(fixed), lhs.length, AST.arrayToNative(lhs), AST.arrayToNative(rhs), conseq.getNativeObject()); + } + + public final void nextSplit(Expr e, long idx, Z3_lbool phase) { + Native.propagateNextSplit( + this, ctx.nCtx(), solver.getNativeObject(), javainfo, + e.getNativeObject(), idx, phase.toInt()); + } +} \ No newline at end of file diff --git a/src/api/js/examples/high-level/using_smtlib2.ts b/src/api/js/examples/high-level/using_smtlib2.ts new file mode 100644 index 000000000..e9275b7bf --- /dev/null +++ b/src/api/js/examples/high-level/using_smtlib2.ts @@ -0,0 +1,36 @@ +// @ts-ignore we're not going to bother with types for this +import process from 'process'; +import { init } from '../../build/node'; +import assert from 'assert'; + +(async () => { + let { Context, em } = await init(); + let z3 = Context('main'); + + const x = z3.BitVec.const('x', 256); + const y = z3.BitVec.const('y', 256); + const z = z3.BitVec.const('z', 256); + const xPlusY = x.add(y); + const xPlusZ = x.add(z); + const expr = xPlusY.mul(xPlusZ); + + const to_check = expr.eq(z3.Const('test', expr.sort)); + + const solver = new z3.Solver(); + solver.add(to_check); + const cr = await solver.check(); + console.log(cr); + assert(cr === 'sat'); + + const model = solver.model(); + let modelStr = model.sexpr(); + modelStr = modelStr.replace(/\n/g, ' '); + console.log("Model: ", modelStr); + + const exprs = z3.ast_from_string(modelStr); + console.log(exprs); + +})().catch(e => { + console.error('error', e); + process.exit(1); +}); \ No newline at end of file diff --git a/src/api/js/examples/low-level/example-raw.ts b/src/api/js/examples/low-level/example-raw.ts index 6e34b4aba..2790f9594 100644 --- a/src/api/js/examples/low-level/example-raw.ts +++ b/src/api/js/examples/low-level/example-raw.ts @@ -1,3 +1,4 @@ +// @ts-ignore we're not going to bother with types for this import process from 'process'; import { init, Z3_error_code } from '../../build/node'; diff --git a/src/api/js/package-lock.json b/src/api/js/package-lock.json index f6969a933..a46cae951 100644 --- a/src/api/js/package-lock.json +++ b/src/api/js/package-lock.json @@ -4461,14 +4461,14 @@ "dev": true }, "shiki": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", - "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.11.1.tgz", + "integrity": "sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA==", "dev": true, "requires": { "jsonc-parser": "^3.0.0", "vscode-oniguruma": "^1.6.1", - "vscode-textmate": "5.2.0" + "vscode-textmate": "^6.0.0" } }, "side-channel": { @@ -4826,16 +4826,15 @@ "dev": true }, "typedoc": { - "version": "0.22.18", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.18.tgz", - "integrity": "sha512-NK9RlLhRUGMvc6Rw5USEYgT4DVAUFk7IF7Q6MYfpJ88KnTZP7EneEa4RcP+tX1auAcz7QT1Iy0bUSZBYYHdoyA==", + "version": "0.23.16", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.16.tgz", + "integrity": "sha512-rumYsCeNRXlyuZVzefD7050n7ptL2uudsCJg50dY0v/stKniqIlRpvx/F/6expC0/Q6Dbab+g/JpZuB7Sw90FA==", "dev": true, "requires": { - "glob": "^8.0.3", "lunr": "^2.3.9", - "marked": "^4.0.16", + "marked": "^4.0.19", "minimatch": "^5.1.0", - "shiki": "^0.10.1" + "shiki": "^0.11.1" }, "dependencies": { "brace-expansion": { @@ -4847,19 +4846,6 @@ "balanced-match": "^1.0.0" } }, - "glob": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", - "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - } - }, "minimatch": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", @@ -4872,9 +4858,9 @@ } }, "typescript": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", - "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", "dev": true }, "typical": { @@ -4945,9 +4931,9 @@ "dev": true }, "vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-6.0.0.tgz", + "integrity": "sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ==", "dev": true }, "walker": { diff --git a/src/api/js/package.json b/src/api/js/package.json index 90a7bfa3d..3e79ba8f4 100644 --- a/src/api/js/package.json +++ b/src/api/js/package.json @@ -1,5 +1,6 @@ { "name": "z3-solver", + "version": "0.1.0", "keywords": [ "Z3", "theorem", @@ -26,8 +27,8 @@ "build:ts:generate": "ts-node --transpileOnly scripts/make-ts-wrapper.ts src/low-level/wrapper.__GENERATED__.ts src/low-level/types.__GENERATED__.ts", "build:wasm": "ts-node --transpileOnly ./scripts/build-wasm.ts", "clean": "rimraf build 'src/**/*.__GENERATED__.*'", - "lint": "prettier -c '{,src/,scripts/,examples}*.{js,ts}'", - "format": "prettier --write '{,src/,scripts/}*.{js,ts}'", + "lint": "prettier -c '{./,src/,scripts/,examples/}**/*.{js,ts}'", + "format": "prettier --write '{./,src/,scripts/}**/*.{js,ts}'", "test": "jest", "docs": "typedoc", "check-engine": "check-engine" @@ -53,8 +54,8 @@ "ts-expect": "^1.3.0", "ts-jest": "^28.0.3", "ts-node": "^10.8.0", - "typedoc": "^0.22.17", - "typescript": "^4.5.4" + "typedoc": "^0.23.16", + "typescript": "^4.8.4" }, "license": "MIT", "dependencies": { diff --git a/src/api/js/scripts/build-wasm.ts b/src/api/js/scripts/build-wasm.ts index 3caf643df..0f51c84d3 100644 --- a/src/api/js/scripts/build-wasm.ts +++ b/src/api/js/scripts/build-wasm.ts @@ -40,7 +40,7 @@ function spawnSync(command: string, opts: SpawnOptions = {}) { } function exportedFuncs(): string[] { - const extras = ['_set_throwy_error_handler', '_set_noop_error_handler', ...asyncFuncs.map(f => '_async_' + f)]; + const extras = ['_malloc', '_set_throwy_error_handler', '_set_noop_error_handler', ...asyncFuncs.map(f => '_async_' + f)]; // TODO(ritave): This variable is unused in original script, find out if it's important const fns: any[] = (functions as any[]).filter(f => !asyncFuncs.includes(f.name)); diff --git a/src/api/js/scripts/make-ts-wrapper.ts b/src/api/js/scripts/make-ts-wrapper.ts index 58b2ce0ae..568609467 100644 --- a/src/api/js/scripts/make-ts-wrapper.ts +++ b/src/api/js/scripts/make-ts-wrapper.ts @@ -76,6 +76,7 @@ function makeTsWrapper() { } const isInParam = (p: FuncParam) => p.kind !== undefined && ['in', 'in_array'].includes(p.kind); + function wrapFunction(fn: Func) { if (CUSTOM_IMPLEMENTATIONS.includes(fn.name)) { return null; @@ -104,7 +105,7 @@ function makeTsWrapper() { let isAsync = asyncFuncs.includes(fn.name); let trivial = - !['string', 'boolean'].includes(fn.ret) && + !['string', 'boolean', 'unsigned'].includes(fn.ret) && !fn.nullableRet && outParams.length === 0 && !inParams.some(p => p.type === 'string' || p.isArray || p.nullable); @@ -234,6 +235,7 @@ function makeTsWrapper() { function setArg() { args[outParam.idx] = memIdx === 0 ? 'outAddress' : `outAddress + ${memIdx * 4}`; } + let read, type; if (outParam.type === 'string') { read = `Mod.UTF8ToString(getOutUint(${memIdx}))`; @@ -330,11 +332,15 @@ function makeTsWrapper() { if (ret === 0) { return null; } - `.trim(); + `.trim(); + } else if (fn.ret === 'unsigned') { + infix += ` + ret = (new Uint32Array([ret]))[0]; + `.trim(); } // prettier-ignore - let invocation = `Mod.ccall('${isAsync ? 'async_' : ''}${fn.name}', '${cReturnType}', ${JSON.stringify(ctypes)}, [${args.map(toEm).join(', ')}])`; + let invocation = `Mod.ccall('${isAsync ? "async_" : ""}${fn.name}', '${cReturnType}', ${JSON.stringify(ctypes)}, [${args.map(toEm).join(", ")}])`; if (isAsync) { invocation = `await Mod.async_call(() => ${invocation})`; diff --git a/src/api/js/scripts/parse-api.ts b/src/api/js/scripts/parse-api.ts index c3292583a..a3aa81acd 100644 --- a/src/api/js/scripts/parse-api.ts +++ b/src/api/js/scripts/parse-api.ts @@ -45,7 +45,7 @@ const types = { __proto__: null, // these are function types I can't be bothered to parse - // NSB: They can be extracted automatically from z3_api.h thanks to the use + // NSB: They can be extracted automatically from z3_api.h thanks to the use // of a macro. Z3_error_handler: 'Z3_error_handler', Z3_push_eh: 'Z3_push_eh', diff --git a/src/api/js/src/high-level/high-level.test.ts b/src/api/js/src/high-level/high-level.test.ts index 70c11b875..99f95e5b4 100644 --- a/src/api/js/src/high-level/high-level.test.ts +++ b/src/api/js/src/high-level/high-level.test.ts @@ -1,8 +1,8 @@ import assert from 'assert'; import asyncToArray from 'iter-tools/methods/async-to-array'; import { init, killThreads } from '../jest'; -import { Arith, Bool, Model, Z3AssertionError, Z3HighLevel } from './types'; -import { expectType } from "ts-expect"; +import { Arith, Bool, Model, Quantifier, Z3AssertionError, Z3HighLevel, AstVector } from './types'; +import { expectType } from 'ts-expect'; /** * Generate all possible solutions from given assumptions. @@ -58,6 +58,7 @@ async function* allSolutions(...assertions: Bool[]): async function prove(conjecture: Bool): Promise { const solver = new conjecture.ctx.Solver(); + solver.set('timeout', 1000); const { Not } = solver.ctx; solver.add(Not(conjecture)); expect(await solver.check()).toStrictEqual('unsat'); @@ -113,11 +114,11 @@ describe('high-level', () => { it('test loading a solver state from a string', async () => { const { Solver, Not, Int } = api.Context('main'); const solver = new Solver(); - solver.fromString("(declare-const x Int) (assert (and (< x 2) (> x 0)))") - expect(await solver.check()).toStrictEqual('sat') - const x = Int.const('x') - solver.add(Not(x.eq(1))) - expect(await solver.check()).toStrictEqual('unsat') + solver.fromString('(declare-const x Int) (assert (and (< x 2) (> x 0)))'); + expect(await solver.check()).toStrictEqual('sat'); + const x = Int.const('x'); + solver.add(Not(x.eq(1))); + expect(await solver.check()).toStrictEqual('unsat'); }); it('disproves x = y implies g(g(x)) = g(y)', async () => { @@ -393,14 +394,13 @@ describe('high-level', () => { }); describe('arrays', () => { - it('Example 1', async () => { const Z3 = api.Context('main'); const arr = Z3.Array.const('arr', Z3.Int.sort(), Z3.Int.sort()); const [idx, val] = Z3.Int.consts('idx val'); - const conjecture = (arr.store(idx, val).select(idx).eq(val)); + const conjecture = arr.store(idx, val).select(idx).eq(val); await prove(conjecture); }); @@ -428,7 +428,7 @@ describe('high-level', () => { // and is detected at compile time // @ts-expect-error const arr3 = Array.const('arr3', BitVec.sort(1)); - }) + }); it('can do simple proofs', async () => { const { BitVec, Array, isArray, isArraySort, isConstArray, Eq, Not } = api.Context('main'); @@ -447,13 +447,6 @@ describe('high-level', () => { await prove(Eq(arr2.select(0), FIVE_VAL)); await prove(Not(Eq(arr2.select(0), BitVec.val(6, 256)))); await prove(Eq(arr2.store(idx, val).select(idx), constArr.store(idx, val).select(idx))); - - // TODO: add in Quantifiers and better typing of arrays - // await prove( - // ForAll([idx], idx.add(1).ugt(idx).and(arr.select(idx.add(1)).ugt(arr.select(idx)))).implies( - // arr.select(0).ult(arr.select(1000)) - // ) - // ); }); it('Finds arrays that differ but that sum to the same', async () => { @@ -465,18 +458,16 @@ describe('high-level', () => { const arr1 = Array.const('arr', BitVec.sort(2), BitVec.sort(32)); const arr2 = Array.const('arr2', BitVec.sort(2), BitVec.sort(32)); - const same_sum = arr1.select(0) + const same_sum = arr1 + .select(0) .add(arr1.select(1)) .add(arr1.select(2)) .add(arr1.select(3)) - .eq( - arr2.select(0) - .add(arr2.select(1)) - .add(arr2.select(2)) - .add(arr2.select(3)) - ); + .eq(arr2.select(0).add(arr2.select(1)).add(arr2.select(2)).add(arr2.select(3))); - const different = arr1.select(0).neq(arr2.select(0)) + const different = arr1 + .select(0) + .neq(arr2.select(0)) .or(arr1.select(1).neq(arr2.select(1))) .or(arr1.select(2).neq(arr2.select(2))) .or(arr1.select(3).neq(arr2.select(3))); @@ -485,11 +476,105 @@ describe('high-level', () => { const arr1Vals = [0, 1, 2, 3].map(i => model.eval(arr1.select(i)).value()); const arr2Vals = [0, 1, 2, 3].map(i => model.eval(arr2.select(i)).value()); - expect((arr1Vals.reduce((a, b) => a + b, 0n) % mod) === arr2Vals.reduce((a, b) => a + b, 0n) % mod); + expect(arr1Vals.reduce((a, b) => a + b, 0n) % mod === arr2Vals.reduce((a, b) => a + b, 0n) % mod); for (let i = 0; i < 4; i++) { expect(arr1Vals[i] !== arr2Vals[i]); } }); + + it('Array type inference', async () => { + const z3 = api.Context('main'); + + const Z3_ADDR = z3.BitVec.const('Vault_addr', 160); + const Z3_GLOBAL_STORAGE = z3.Array.const( + 'global_storage', + z3.BitVec.sort(160), + z3.Array.sort(z3.BitVec.sort(160), z3.BitVec.sort(256)), + ); + const Z3_STORAGE = Z3_GLOBAL_STORAGE.select(Z3_ADDR); + + // We are so far unable to properly infer the type of Z3_STORAGE + // expectType< + // SMTArray<'main', [BitVecSort<160>], BitVecSort<256>> + // >(Z3_STORAGE); + }); + }); + + describe('quantifiers', () => { + it('Basic Universal', async () => { + const Z3 = api.Context('main'); + + const [x, y] = Z3.Int.consts('x y'); + + const conjecture = Z3.ForAll([x, y], x.neq(y).implies(x.lt(y).or(x.gt(y)))); + expect(Z3.isBool(conjecture)).toBeTruthy(); + expect(conjecture.var_name(0)).toBe('x'); + expect(conjecture.var_sort(0).eqIdentity(Z3.Int.sort())).toBeTruthy(); + expect(conjecture.var_name(1)).toBe('y'); + expect(conjecture.var_sort(1).eqIdentity(Z3.Int.sort())).toBeTruthy(); + await prove(conjecture); + }); + + it('Basic Existential', async () => { + const Z3 = api.Context('main'); + + const [x, y, z] = Z3.Int.consts('x y z'); + + const quantifier = Z3.Exists([z], Z3.Not(z.lt(x)).and(Z3.Not(z.gt(y)))); + expect(Z3.isBool(quantifier)).toBeTruthy(); + expect(quantifier.var_name(0)).toBe('z'); + expect(quantifier.var_sort(0).eqIdentity(Z3.Int.sort())).toBeTruthy(); + + const conjecture = Z3.Not(x.gt(y)).implies(quantifier); // Can be trivially discovered with z = x or x = y + await prove(conjecture); + }); + + it('Basic Lambda', async () => { + const Z3 = api.Context('main'); + + const [x, y] = Z3.Int.consts('x y z'); + const L = Z3.Lambda([x, y], x.add(y)); + expect(Z3.isArraySort(L.sort)).toBeTruthy(); + expect(Z3.isArray(L)).toBeFalsy(); + expect(L.var_name(0)).toBe('x'); + expect(L.var_sort(0).eqIdentity(Z3.Int.sort())).toBeTruthy(); + expect(L.var_name(1)).toBe('y'); + expect(L.var_sort(1).eqIdentity(Z3.Int.sort())).toBeTruthy(); + + const conjecture = L.select(Z3.Int.val(2), Z3.Int.val(5)).eq(Z3.Int.val(7)); + await prove(conjecture); + }); + + it('Loading Quantifier Preserves Type', async () => { + const Z3 = api.Context('main'); + + const [x, y, z] = Z3.Int.consts('x y z'); + const quantifier = Z3.Exists([z], Z3.Not(z.lt(x)).and(Z3.Not(z.gt(y)))); + expect(Z3.isBool(quantifier)).toBeTruthy(); + + const solver = new Z3.Solver(); + solver.add(quantifier); + + const dumped_str = solver.toString(); + + const solver2 = new Z3.Solver(); + solver2.fromString(dumped_str); + const quantifier2 = solver2.assertions().get(0) as unknown as Quantifier; + expect(Z3.isBool(quantifier2)).toBeTruthy(); + expect(quantifier2.var_name(0)).toBe('z'); + }); + }); + + describe('uninterpreted functions', () => { + it('Type Inference', async () => { + const Z3 = api.Context('main'); + + const f = Z3.Function.declare('f', Z3.Int.sort(), Z3.Bool.sort()); + const input = Z3.Int.val(6); + const output = f.call(input); + expectType(output); + expect(output.sort.eqIdentity(Z3.Bool.sort())).toBeTruthy(); + }); }); describe('Solver', () => { @@ -543,10 +628,11 @@ describe('high-level', () => { describe('AstVector', () => { it('can use basic methods', async () => { - const { Solver, AstVector, Int } = api.Context('main'); + const Z3 = api.Context('main'); + const { Solver, Int } = Z3; const solver = new Solver(); - const vector = new AstVector(); + const vector = new Z3.AstVector(); for (let i = 0; i < 5; i++) { vector.push(Int.const(`int__${i}`)); } @@ -559,4 +645,104 @@ describe('high-level', () => { expect(await solver.check()).toStrictEqual('sat'); }); }); + + describe('Substitution', () => { + it('basic variable substitution', async () => { + const { Int, substitute } = api.Context('main'); + const x = Int.const('x'); + const y = Int.const('y'); + const z = Int.const('z'); + + const expr = x.add(y); + const subst = substitute(expr, [x, z]); + expect(subst.eqIdentity(z.add(y))).toBeTruthy(); + }); + + it('term substitution', async () => { + const { Int, substitute } = api.Context('main'); + const x = Int.const('x'); + const y = Int.const('y'); + const z = Int.const('z'); + + const expr = x.add(y).mul(Int.val(1).sub(x.add(y))); + const subst = substitute(expr, [x.add(y), z]); + expect(subst.eqIdentity(z.mul(Int.val(1).sub(z)))).toBeTruthy(); + }); + }); + + describe('Model', () => { + it('Assigning constants', async () => { + const { Int, Model } = api.Context('main'); + const m = new Model(); + + const [x, y] = Int.consts('x y'); + + m.updateValue(x, Int.val(6)); + m.updateValue(y, Int.val(12)); + + expect(m.eval(x.add(y)).eqIdentity(Int.val(18))).toBeTruthy(); + }); + + it('Creating Func Interpretations', async () => { + const { Int, Function, Model } = api.Context('main'); + const m = new Model(); + + const f = Function.declare('f', Int.sort(), Int.sort(), Int.sort()); + + const f_interp = m.addFuncInterp(f, 0); + f_interp.addEntry([Int.val(1), Int.val(2)], Int.val(3)); + f_interp.addEntry([Int.val(4), Int.val(5)], Int.val(6)); + + expect(m.eval(f.call(1, 2)).eqIdentity(Int.val(3))).toBeTruthy(); + expect(m.eval(f.call(4, 5)).eqIdentity(Int.val(6))).toBeTruthy(); + expect(m.eval(f.call(0, 0)).eqIdentity(Int.val(0))).toBeTruthy(); + }); + }); + + describe('optimize', () => { + it("maximization problem over reals", async () => { + const { Real, Optimize } = api.Context('main'); + + const opt = new Optimize(); + const x = Real.const('x'); + const y = Real.const('y'); + const z = Real.const('z'); + + opt.add(x.ge(0), y.ge(0), z.ge(0)); + opt.add(x.le(1), y.le(1), z.le(1)); + opt.maximize(x.mul(7).add(y.mul(9)).sub(z.mul(3))) + + const result = await opt.check() + expect(result).toStrictEqual('sat'); + const model = opt.model(); + expect(model.eval(x).eqIdentity(Real.val(1))).toBeTruthy(); + expect(model.eval(y).eqIdentity(Real.val(1))).toBeTruthy(); + expect(model.eval(z).eqIdentity(Real.val(0))).toBeTruthy(); + }); + + it("minimization problem over integers using addSoft", async () => { + const { Int, Optimize } = api.Context('main'); + + const opt = new Optimize(); + const x = Int.const('x'); + const y = Int.const('y'); + const z = Int.const('z'); + + opt.add(x.ge(0), y.ge(0)); + opt.add(x.le(1), y.le(1)); + opt.addSoft(x.eq(1), 2); + opt.addSoft(y.eq(1), 1); + opt.add(z.eq(x.mul(5).add(y.mul(5)))); + opt.add(z.le(5)); + opt.minimize(z); + + const result = await opt.check() + expect(result).toStrictEqual('sat'); + const model = opt.model(); + expect(model.eval(x).eqIdentity(Int.val(1))).toBeTruthy(); + expect(model.eval(y).eqIdentity(Int.val(0))).toBeTruthy(); + expect(model.eval(z).eqIdentity(Int.val(5))).toBeTruthy(); + }); + }); + }); diff --git a/src/api/js/src/high-level/high-level.ts b/src/api/js/src/high-level/high-level.ts index 59a0b1e36..0869dbd7b 100644 --- a/src/api/js/src/high-level/high-level.ts +++ b/src/api/js/src/high-level/high-level.ts @@ -31,13 +31,20 @@ import { Z3_symbol, Z3_symbol_kind, Z3_tactic, + Z3_pattern, + Z3_app, + Z3_params, + Z3_func_entry, + Z3_optimize, } from '../low-level'; import { AnyAst, AnyExpr, AnySort, Arith, - ArithSort, ArrayIndexType, + ArithSort, + ArrayIndexType, + CoercibleToArrayIndexType, Ast, AstMap, AstMapCtor, @@ -48,11 +55,12 @@ import { BitVecSort, Bool, BoolSort, - CheckSatResult, CoercibleFromMap, + CheckSatResult, + CoercibleToMap, CoercibleRational, CoercibleToBitVec, CoercibleToExpr, - CoercibleToExprMap, + CoercibleFromMap, Context, ContextCtor, Expr, @@ -61,7 +69,11 @@ import { FuncInterp, IntNum, Model, + Optimize, + Pattern, Probe, + Quantifier, + BodyT, RatNum, SMTArray, SMTArraySort, @@ -71,6 +83,9 @@ import { Tactic, Z3Error, Z3HighLevel, + CoercibleToArith, + NonEmptySortArray, + FuncEntry, } from './types'; import { allSatisfy, assert, assertExhaustive } from './utils'; @@ -81,18 +96,19 @@ const asyncMutex = new Mutex(); function isCoercibleRational(obj: any): obj is CoercibleRational { // prettier-ignore const r = ( - (obj !== null && - (typeof obj === 'object' || typeof obj === 'function')) && - (obj.numerator !== null && - (typeof obj.numerator === 'number' || typeof obj.numerator === 'bigint')) && - (obj.denominator !== null && - (typeof obj.denominator === 'number' || typeof obj.denominator === 'bigint')) - ); - r && assert( - (typeof obj!.numerator !== 'number' || Number.isSafeInteger(obj!.numerator)) && - (typeof obj!.denominator !== 'number' || Number.isSafeInteger(obj!.denominator)), - 'Fraction numerator and denominator must be integers', - ); + (obj !== null && + (typeof obj === 'object' || typeof obj === 'function')) && + (obj.numerator !== null && + (typeof obj.numerator === 'number' || typeof obj.numerator === 'bigint')) && + (obj.denominator !== null && + (typeof obj.denominator === 'number' || typeof obj.denominator === 'bigint')) + ); + r && + assert( + (typeof obj!.numerator !== 'number' || Number.isSafeInteger(obj!.numerator)) && + (typeof obj!.denominator !== 'number' || Number.isSafeInteger(obj!.denominator)), + 'Fraction numerator and denominator must be integers', + ); return r; } @@ -152,9 +168,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { function createContext(name: Name, options?: Record): Context { const cfg = Z3.mk_config(); if (options != null) { - Object.entries(options).forEach( - ([key, value]) => check(Z3.set_param_value(cfg, key, value.toString())) - ); + Object.entries(options).forEach(([key, value]) => check(Z3.set_param_value(cfg, key, value.toString()))); } const contextPtr = Z3.mk_context_rc(cfg); Z3.set_ast_print_mode(contextPtr, Z3_ast_print_mode.Z3_PRINT_SMTLIB2_COMPLIANT); @@ -171,7 +185,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } - function check(val: T) { + function check(val: T): T { throwIfError(); return val; } @@ -199,6 +213,26 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + function _toParams(key: string, value: any): Z3_params { + const params = Z3.mk_params(contextPtr); + Z3.params_inc_ref(contextPtr, params); + // If value is a boolean + if (typeof value === 'boolean') { + Z3.params_set_bool(contextPtr, params, _toSymbol(key), value); + } else if (typeof value === 'number') { + // If value is a uint + if (Number.isInteger(value)) { + check(Z3.params_set_uint(contextPtr, params, _toSymbol(key), value)); + } else { + // If value is a double + check(Z3.params_set_double(contextPtr, params, _toSymbol(key), value)); + } + } else if (typeof value === 'string') { + check(Z3.params_set_symbol(contextPtr, params, _toSymbol(key), _toSymbol(value))); + } + return params; + } + function _toAst(ast: Z3_ast): AnyAst { switch (check(Z3.get_ast_kind(contextPtr, ast))) { case Z3_ast_kind.Z3_SORT_AST: @@ -229,13 +263,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel { function _toExpr(ast: Z3_ast): AnyExpr { const kind = check(Z3.get_ast_kind(contextPtr, ast)); if (kind === Z3_ast_kind.Z3_QUANTIFIER_AST) { - if (Z3.is_quantifier_forall(contextPtr, ast)) - return new BoolImpl(ast); - if (Z3.is_quantifier_exists(contextPtr, ast)) - return new BoolImpl(ast); - if (Z3.is_lambda(contextPtr, ast)) - return new ExprImpl(ast); - assert(false); + if (Z3.is_lambda(contextPtr, ast)) { + return new LambdaImpl(ast); + } + return new NonLambdaQuantifierImpl(ast); } const sortKind = check(Z3.get_sort_kind(contextPtr, Z3.get_sort(contextPtr, ast))); switch (sortKind) { @@ -325,6 +356,12 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return r; } + function isFuncInterp(obj: unknown): obj is FuncInterp { + const r = obj instanceof FuncInterpImpl; + r && _assertContext(obj); + return r; + } + function isApp(obj: unknown): boolean { if (!isExpr(obj)) { return false; @@ -352,7 +389,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } function isBool(obj: unknown): obj is Bool { - const r = obj instanceof BoolImpl; + const r = obj instanceof ExprImpl && obj.sort.kind() === Z3_sort_kind.Z3_BOOL_SORT; r && _assertContext(obj); return r; } @@ -389,6 +426,12 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return isAppOf(obj, Z3_decl_kind.Z3_OP_DISTINCT); } + function isQuantifier(obj: unknown): obj is Quantifier { + const r = obj instanceof QuantifierImpl; + r && _assertContext(obj); + return r; + } + function isArith(obj: unknown): obj is Arith { const r = obj instanceof ArithImpl; r && _assertContext(obj); @@ -531,8 +574,8 @@ export function createApi(Z3: Z3Core): Z3HighLevel { // expression simplification // /////////////////////////////// - async function simplify(e: Expr) { - const result = await Z3.simplify(contextPtr, e.ast) + async function simplify(e: Expr): Promise> { + const result = await Z3.simplify(contextPtr, e.ast); return _toExpr(check(result)); } @@ -543,7 +586,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel { declare: (name: string) => new SortImpl(Z3.mk_uninterpreted_sort(contextPtr, _toSymbol(name))), }; const Function = { - declare: (name: string, ...signature: FuncDeclSignature) => { + declare: [], RangeSort extends Sort>( + name: string, + ...signature: [...DomainSort, RangeSort] + ): FuncDecl => { const arity = signature.length - 1; const rng = signature[arity]; _assertContext(rng); @@ -552,9 +598,11 @@ export function createApi(Z3: Z3Core): Z3HighLevel { _assertContext(signature[i]); dom.push(signature[i].ptr); } - return new FuncDeclImpl(Z3.mk_func_decl(contextPtr, _toSymbol(name), dom, rng.ptr)); + return new FuncDeclImpl(Z3.mk_func_decl(contextPtr, _toSymbol(name), dom, rng.ptr)); }, - fresh: (...signature: FuncDeclSignature) => { + fresh: [], RangeSort extends Sort>( + ...signature: [...DomainSort, RangeSort] + ): FuncDecl => { const arity = signature.length - 1; const rng = signature[arity]; _assertContext(rng); @@ -563,7 +611,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { _assertContext(signature[i]); dom.push(signature[i].ptr); } - return new FuncDeclImpl(Z3.mk_fresh_func_decl(contextPtr, 'f', dom, rng.ptr)); + return new FuncDeclImpl(Z3.mk_fresh_func_decl(contextPtr, 'f', dom, rng.ptr)); }, }; const RecFunc = { @@ -701,7 +749,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { }, }; const Array = { - sort, ...AnySort[]], RangeSort extends AnySort>( + sort, RangeSort extends AnySort>( ...sig: [...DomainSort, RangeSort] ): SMTArraySort { const arity = sig.length - 1; @@ -711,16 +759,23 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return new ArraySortImpl(Z3.mk_array_sort(contextPtr, d.ptr, r.ptr)); } const dom = sig.slice(0, arity); - return new ArraySortImpl(Z3.mk_array_sort_n(contextPtr, dom.map(s => s.ptr), r.ptr)); - }, - const, ...AnySort[]], RangeSort extends AnySort>( - name: string, ...sig: [...DomainSort, RangeSort] - ): SMTArray { - return new ArrayImpl( - check(Z3.mk_const(contextPtr, _toSymbol(name), Array.sort(...sig).ptr)) + return new ArraySortImpl( + Z3.mk_array_sort_n( + contextPtr, + dom.map(s => s.ptr), + r.ptr, + ), ); }, - consts, ...AnySort[]], RangeSort extends AnySort>( + const, RangeSort extends AnySort>( + name: string, + ...sig: [...DomainSort, RangeSort] + ): SMTArray { + return new ArrayImpl( + check(Z3.mk_const(contextPtr, _toSymbol(name), Array.sort(...sig).ptr)), + ); + }, + consts, RangeSort extends AnySort>( names: string | string[], ...sig: [...DomainSort, RangeSort] ): SMTArray[] { @@ -731,13 +786,11 @@ export function createApi(Z3: Z3Core): Z3HighLevel { }, K, RangeSort extends AnySort>( domain: DomainSort, - value: SortToExprMap + value: SortToExprMap, ): SMTArray { - return new ArrayImpl<[DomainSort], RangeSort>( - check(Z3.mk_const_array(contextPtr, domain.ptr, value.ptr)) - ); - } - } + return new ArrayImpl<[DomainSort], RangeSort>(check(Z3.mk_const_array(contextPtr, domain.ptr, value.ptr))); + }, + }; //////////////// // Operations // @@ -747,7 +800,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { condition: Bool | boolean, onTrue: OnTrueRef, onFalse: OnFalseRef, - ): CoercibleToExprMap; + ): CoercibleFromMap; function If( condition: Bool | Probe | boolean, onTrue: CoercibleToExpr | Tactic, @@ -812,6 +865,13 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return new BoolImpl(check(Z3.mk_implies(contextPtr, a.ptr, b.ptr))); } + function Iff(a: Bool | boolean, b: Bool | boolean): Bool { + a = from(a) as Bool; + b = from(b) as Bool; + _assertContext(a, b); + return new BoolImpl(check(Z3.mk_iff(contextPtr, a.ptr, b.ptr))); + } + function Eq(a: CoercibleToExpr, b: CoercibleToExpr): Bool { a = from(a); b = from(b); @@ -897,6 +957,84 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + function ForAll>( + quantifiers: ArrayIndexType, + body: Bool, + weight: number = 1, + ): NonLambdaQuantifierImpl { + // Verify all quantifiers are constants + if (!allSatisfy(quantifiers, isConst)) { + throw new Error('Quantifier variables must be constants'); + } + + return new NonLambdaQuantifierImpl( + check( + Z3.mk_quantifier_const_ex( + contextPtr, + true, + weight, + _toSymbol(''), + _toSymbol(''), + quantifiers.map(q => q.ptr as unknown as Z3_app), // The earlier check verifies these are all apps + [], + [], + body.ptr, + ), + ), + ); + } + + function Exists>( + quantifiers: ArrayIndexType, + body: Bool, + weight: number = 1, + ): NonLambdaQuantifierImpl { + // Verify all quantifiers are constants + if (!allSatisfy(quantifiers, isConst)) { + throw new Error('Quantifier variables must be constants'); + } + + return new NonLambdaQuantifierImpl( + check( + Z3.mk_quantifier_const_ex( + contextPtr, + false, + weight, + _toSymbol(''), + _toSymbol(''), + quantifiers.map(q => q.ptr as unknown as Z3_app), // The earlier check verifies these are all apps + [], + [], + body.ptr, + ), + ), + ); + } + + function Lambda, RangeSort extends Sort>( + quantifiers: ArrayIndexType, + expr: SortToExprMap, + ): LambdaImpl { + // TODO(walden): For some reason LambdaImpl leads to type issues + // and Typescript won't build. I'm not sure why since the types seem to all match + // up. For now, we just use any for the domain sort + + // Verify all quantifiers are constants + if (!allSatisfy(quantifiers, isConst)) { + throw new Error('Quantifier variables must be constants'); + } + + return new LambdaImpl( + check( + Z3.mk_lambda_const( + contextPtr, + quantifiers.map(q => q.ptr as unknown as Z3_app), + expr.ptr, + ), + ), + ); + } + function ToReal(expr: Arith | bigint): Arith { expr = from(expr) as Arith; _assertContext(expr); @@ -961,6 +1099,101 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return new TacticImpl(check(Z3.tactic_cond(contextPtr, probe.ptr, onTrue.ptr, onFalse.ptr))); } + function LT(a: Arith, b: CoercibleToArith): Bool { + return new BoolImpl(check(Z3.mk_lt(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function GT(a: Arith, b: CoercibleToArith): Bool { + return new BoolImpl(check(Z3.mk_gt(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function LE(a: Arith, b: CoercibleToArith): Bool { + return new BoolImpl(check(Z3.mk_le(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function GE(a: Arith, b: CoercibleToArith): Bool { + return new BoolImpl(check(Z3.mk_ge(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function ULT(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvult(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function UGT(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvugt(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function ULE(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvule(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function UGE(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvuge(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function SLT(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvslt(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function SGT(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvsgt(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function SLE(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvsle(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function SGE(a: BitVec, b: CoercibleToBitVec): Bool { + return new BoolImpl(check(Z3.mk_bvsge(contextPtr, a.ast, a.sort.cast(b).ast))); + } + + function Extract(hi: number, lo: number, val: BitVec): BitVec { + return new BitVecImpl(check(Z3.mk_extract(contextPtr, hi, lo, val.ast))); + } + + function Select, RangeSort extends Sort>( + array: SMTArray, + ...indices: CoercibleToArrayIndexType + ): SortToExprMap { + const args = indices.map((arg, i) => array.domain_n(i).cast(arg as any)); + if (args.length === 1) { + return _toExpr(check(Z3.mk_select(contextPtr, array.ast, args[0].ast))) as SortToExprMap; + } + const _args = args.map(arg => arg.ast); + return _toExpr(check(Z3.mk_select_n(contextPtr, array.ast, _args))) as SortToExprMap; + } + + function Store, RangeSort extends Sort>( + array: SMTArray, + ...indicesAndValue: [ + ...CoercibleToArrayIndexType, + CoercibleToMap, Name>, + ] + ): SMTArray { + const args = indicesAndValue.map((arg, i) => { + if (i === indicesAndValue.length - 1) { + return array.range().cast(arg as any) as SortToExprMap; + } + return array.domain_n(i).cast(arg as any); + }); + if (args.length <= 1) { + throw new Error('Array store requires both index and value arguments'); + } + if (args.length === 2) { + return _toExpr(check(Z3.mk_store(contextPtr, array.ast, args[0].ast, args[1].ast))) as SMTArray< + Name, + DomainSort, + RangeSort + >; + } + const _idxs = args.slice(0, args.length - 1).map(arg => arg.ast); + return _toExpr(check(Z3.mk_store_n(contextPtr, array.ast, _idxs, args[args.length - 1].ast))) as SMTArray< + Name, + DomainSort, + RangeSort + >; + } + class AstImpl implements Ast { declare readonly __typename: Ast['__typename']; readonly ctx: Context; @@ -1023,6 +1256,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel { cleanup.register(this, () => Z3.solver_dec_ref(contextPtr, myPtr)); } + set(key: string, value: any): void { + Z3.solver_set_params(contextPtr, this.ptr, _toParams(key, value)); + } + push() { Z3.solver_push(contextPtr, this.ptr); } @@ -1092,6 +1329,102 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + class OptimizeImpl implements Optimize { + declare readonly __typename: Optimize['__typename']; + + readonly ptr: Z3_optimize; + readonly ctx: Context; + + constructor(ptr: Z3_optimize = Z3.mk_optimize(contextPtr)) { + this.ctx = ctx; + let myPtr: Z3_optimize; + myPtr = ptr; + this.ptr = myPtr; + Z3.optimize_inc_ref(contextPtr, myPtr); + cleanup.register(this, () => Z3.optimize_dec_ref(contextPtr, myPtr)); + } + + set(key: string, value: any): void { + Z3.optimize_set_params(contextPtr, this.ptr, _toParams(key, value)); + } + + push() { + Z3.optimize_push(contextPtr, this.ptr); + } + + pop() { + Z3.optimize_pop(contextPtr, this.ptr); + } + + add(...exprs: (Bool | AstVector>)[]) { + _flattenArgs(exprs).forEach(expr => { + _assertContext(expr); + check(Z3.optimize_assert(contextPtr, this.ptr, expr.ast)); + }); + } + + addSoft(expr: Bool, weight: number | bigint | string | CoercibleRational, id: number | string = "") { + if (isCoercibleRational(weight)) { + weight = `${weight.numerator}/${weight.denominator}`; + } + check(Z3.optimize_assert_soft(contextPtr, this.ptr, expr.ast, weight.toString(), _toSymbol(id))) + } + + addAndTrack(expr: Bool, constant: Bool | string) { + if (typeof constant === 'string') { + constant = Bool.const(constant); + } + assert(isConst(constant), 'Provided expression that is not a constant to addAndTrack'); + check(Z3.optimize_assert_and_track(contextPtr, this.ptr, expr.ast, constant.ast)); + } + + assertions(): AstVector> { + return new AstVectorImpl(check(Z3.optimize_get_assertions(contextPtr, this.ptr))); + } + + maximize(expr: Arith) { + check(Z3.optimize_maximize(contextPtr, this.ptr, expr.ast)); + } + + minimize(expr: Arith) { + check(Z3.optimize_minimize(contextPtr, this.ptr, expr.ast)); + } + + async check(...exprs: (Bool | AstVector>)[]): Promise { + const assumptions = _flattenArgs(exprs).map(expr => { + _assertContext(expr); + return expr.ast; + }); + const result = await asyncMutex.runExclusive(() => + check(Z3.optimize_check(contextPtr, this.ptr, assumptions)), + ); + switch (result) { + case Z3_lbool.Z3_L_FALSE: + return 'unsat'; + case Z3_lbool.Z3_L_TRUE: + return 'sat'; + case Z3_lbool.Z3_L_UNDEF: + return 'unknown'; + default: + assertExhaustive(result); + } + } + + model() { + return new ModelImpl(check(Z3.optimize_get_model(contextPtr, this.ptr))); + } + + toString() { + return check(Z3.optimize_to_string(contextPtr, this.ptr)); + } + + fromString(s: string) { + Z3.optimize_from_string(contextPtr, this.ptr, s); + throwIfError(); + } + } + + class ModelImpl implements Model { declare readonly __typename: Model['__typename']; readonly ctx: Context; @@ -1110,20 +1443,20 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return this.values(); } - * entries(): IterableIterator<[number, FuncDecl]> { + *entries(): IterableIterator<[number, FuncDecl]> { const length = this.length(); for (let i = 0; i < length; i++) { yield [i, this.get(i)]; } } - * keys(): IterableIterator { + *keys(): IterableIterator { for (const [key] of this.entries()) { yield key; } } - * values(): IterableIterator> { + *values(): IterableIterator> { for (const [, value] of this.entries()) { yield value; } @@ -1160,7 +1493,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { get(sort: Sort): AstVector>; get( i: number | FuncDecl | Expr | Sort, - to?: number + to?: number, ): FuncDecl | FuncInterp | Expr | AstVector> | FuncDecl[] { assert(to === undefined || typeof i === 'number'); if (typeof i === 'number') { @@ -1200,6 +1533,41 @@ export function createApi(Z3: Z3Core): Z3HighLevel { assert(false, 'Number, declaration or constant expected'); } + updateValue(decl: FuncDecl | Expr, a: Ast | FuncInterp): void { + _assertContext(decl); + _assertContext(a); + if (isExpr(decl)) { + decl = decl.decl(); + } + if (isFuncDecl(decl) && decl.arity() !== 0 && isFuncInterp(a)) { + const funcInterp = this.addFuncInterp(decl, a.elseValue() as Expr); + for (let i = 0; i < a.numEntries(); i++) { + const e = a.entry(i); + const n = e.numArgs(); + const args = global.Array(n).map((_, i) => e.argValue(i)); + funcInterp.addEntry(args, e.value()); + } + return; + } + if (!isFuncDecl(decl) || decl.arity() !== 0) { + throw new Z3Error('Expecting 0-ary function or constant expression'); + } + if (!isAst(a)) { + throw new Z3Error('Only func declarations can be assigned to func interpretations'); + } + check(Z3.add_const_interp(contextPtr, this.ptr, decl.ptr, a.ast)); + } + + addFuncInterp[] = Sort[], RangeSort extends Sort = Sort>( + decl: FuncDecl, + defaultValue: CoercibleToMap, Name>, + ): FuncInterp { + const fi = check( + Z3.add_func_interp(contextPtr, this.ptr, decl.ptr, decl.range().cast(defaultValue).ptr as Z3_ast), + ); + return new FuncInterpImpl(fi); + } + private getInterp(expr: FuncDecl | Expr): Expr | FuncInterp | null { assert(isFuncDecl(expr) || isConst(expr), 'Declaration expected'); if (isConst(expr)) { @@ -1228,6 +1596,30 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + class FuncEntryImpl implements FuncEntry { + declare readonly __typename: FuncEntry['__typename']; + + readonly ctx: Context; + + constructor(readonly ptr: Z3_func_entry) { + this.ctx = ctx; + Z3.func_entry_inc_ref(contextPtr, ptr); + cleanup.register(this, () => Z3.func_entry_dec_ref(contextPtr, ptr)); + } + + numArgs() { + return check(Z3.func_entry_get_num_args(contextPtr, this.ptr)); + } + + argValue(i: number): Expr { + return _toExpr(check(Z3.func_entry_get_arg(contextPtr, this.ptr, i))); + } + + value(): Expr { + return _toExpr(check(Z3.func_entry_get_value(contextPtr, this.ptr))); + } + } + class FuncInterpImpl implements FuncInterp { declare readonly __typename: FuncInterp['__typename']; readonly ctx: Context; @@ -1237,6 +1629,33 @@ export function createApi(Z3: Z3Core): Z3HighLevel { Z3.func_interp_inc_ref(contextPtr, ptr); cleanup.register(this, () => Z3.func_interp_dec_ref(contextPtr, ptr)); } + + elseValue(): Expr { + return _toExpr(check(Z3.func_interp_get_else(contextPtr, this.ptr))); + } + + numEntries(): number { + return check(Z3.func_interp_get_num_entries(contextPtr, this.ptr)); + } + + arity(): number { + return check(Z3.func_interp_get_arity(contextPtr, this.ptr)); + } + + entry(i: number): FuncEntry { + return new FuncEntryImpl(check(Z3.func_interp_get_entry(contextPtr, this.ptr, i))); + } + + addEntry(args: Expr[], value: Expr): void { + const argsVec = new AstVectorImpl(); + for (const arg of args) { + argsVec.push(arg); + } + _assertContext(argsVec); + _assertContext(value); + assert(this.arity() === argsVec.length(), "Number of arguments in entry doesn't match function arity"); + check(Z3.func_interp_add_entry(contextPtr, this.ptr, argsVec.ptr, value.ptr as Z3_ast)); + } } class SortImpl extends AstImpl implements Sort { @@ -1275,10 +1694,13 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } - class FuncDeclImpl extends AstImpl implements FuncDecl { + class FuncDeclImpl[], RangeSort extends Sort> + extends AstImpl + implements FuncDecl + { declare readonly __typename: FuncDecl['__typename']; - get ast() { + get ast(): Z3_ast { return Z3.func_decl_to_ast(contextPtr, this.ptr); } @@ -1290,20 +1712,20 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return Z3.get_arity(contextPtr, this.ptr); } - domain(i: number) { + domain(i: T): DomainSort[T] { assert(i < this.arity(), 'Index out of bounds'); return _toSort(Z3.get_domain(contextPtr, this.ptr, i)); } - range() { - return _toSort(Z3.get_range(contextPtr, this.ptr)); + range(): RangeSort { + return _toSort(Z3.get_range(contextPtr, this.ptr)) as RangeSort; } kind() { return Z3.get_decl_kind(contextPtr, this.ptr); } - params(): (number | string | Z3_symbol | Sort | Expr | FuncDecl)[] { + params(): (number | string | Sort | Expr | FuncDecl)[] { const n = Z3.get_decl_num_parameters(contextPtr, this.ptr); const result = []; for (let i = 0; i < n; i++) { @@ -1319,7 +1741,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { result.push(check(Z3.get_decl_rational_parameter(contextPtr, this.ptr, i))); break; case Z3_parameter_kind.Z3_PARAMETER_SYMBOL: - result.push(check(Z3.get_decl_symbol_parameter(contextPtr, this.ptr, i))); + result.push(_fromSymbol(check(Z3.get_decl_symbol_parameter(contextPtr, this.ptr, i)))); break; case Z3_parameter_kind.Z3_PARAMETER_SORT: result.push(new SortImpl(check(Z3.get_decl_sort_parameter(contextPtr, this.ptr, i)))); @@ -1337,21 +1759,26 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return result; } - call(...args: CoercibleToExpr[]) { + call(...args: CoercibleToArrayIndexType): SortToExprMap { assert(args.length === this.arity(), `Incorrect number of arguments to ${this}`); return _toExpr( - check(Z3.mk_app( - contextPtr, - this.ptr, - args.map((arg, i) => { - return this.domain(i).cast(arg).ast; - }), - )), - ); + check( + Z3.mk_app( + contextPtr, + this.ptr, + args.map((arg, i) => { + return this.domain(i).cast(arg as any).ast; + }), + ), + ), + ) as SortToExprMap; } } - class ExprImpl = AnySort> extends AstImpl implements Expr { + class ExprImpl = AnySort> + extends AstImpl + implements Expr + { declare readonly __typename: Expr['__typename']; get sort(): S { @@ -1364,13 +1791,19 @@ export function createApi(Z3: Z3Core): Z3HighLevel { neq(other: CoercibleToExpr): Bool { return new BoolImpl( - check(Z3.mk_distinct( - contextPtr, - [this, other].map(expr => from(expr).ast), - )), + check( + Z3.mk_distinct( + contextPtr, + [this, other].map(expr => from(expr).ast), + ), + ), ); } + name() { + return this.decl().name(); + } + params() { return this.decl().params(); } @@ -1404,6 +1837,16 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + class PatternImpl implements Pattern { + declare readonly __typename: Pattern['__typename']; + readonly ctx: Context; + + constructor(readonly ptr: Z3_pattern) { + this.ctx = ctx; + // TODO: implement rest of Pattern + } + } + class BoolSortImpl extends SortImpl implements BoolSort { declare readonly __typename: BoolSort['__typename']; @@ -1425,7 +1868,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } class BoolImpl extends ExprImpl> implements Bool { - declare readonly __typename: Bool['__typename']; + declare readonly __typename: 'Bool' | 'NonLambdaQuantifier'; not(): Bool { return Not(this); @@ -1446,6 +1889,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel { implies(other: Bool | boolean): Bool { return Implies(this, other); } + + iff(other: Bool | boolean): Bool { + return Iff(this, other); + } } class ProbeImpl implements Probe { @@ -1482,12 +1929,12 @@ export function createApi(Z3: Z3Core): Z3HighLevel { class ArithSortImpl extends SortImpl implements ArithSort { declare readonly __typename: ArithSort['__typename']; - cast(other: bigint | number): IntNum | RatNum; + cast(other: bigint | number | string): IntNum | RatNum; cast(other: CoercibleRational | RatNum): RatNum; cast(other: IntNum): IntNum; cast(other: Bool | Arith): Arith; cast(other: CoercibleToExpr): never; - cast(other: CoercibleToExpr): Arith | RatNum | IntNum { + cast(other: CoercibleToExpr | string): Arith | RatNum | IntNum { const sortTypeStr = isIntSort(this) ? 'IntSort' : 'RealSort'; if (isExpr(other)) { const otherS = other.sort; @@ -1519,51 +1966,164 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + function Sum(arg0: Arith, ...args: CoercibleToArith[]): Arith; + function Sum( + arg0: BitVec, + ...args: CoercibleToBitVec[] + ): BitVec; + function Sum>(arg0: T, ...args: CoercibleToMap[]): T { + if (arg0 instanceof BitVecImpl) { + // Assert only 2 + if (args.length !== 1) { + throw new Error('BitVec add only supports 2 arguments'); + } + return new BitVecImpl( + check(Z3.mk_bvadd(contextPtr, arg0.ast, arg0.sort.cast(args[0]).ast)), + ) as unknown as T; + } else { + assert(arg0 instanceof ArithImpl); + return new ArithImpl( + check(Z3.mk_add(contextPtr, [arg0.ast].concat(args.map(arg => arg0.sort.cast(arg).ast)))), + ) as unknown as T; + } + } + + function Sub(arg0: Arith, ...args: CoercibleToArith[]): Arith; + function Sub( + arg0: BitVec, + ...args: CoercibleToBitVec[] + ): BitVec; + function Sub>(arg0: T, ...args: CoercibleToMap[]): T { + if (arg0 instanceof BitVecImpl) { + // Assert only 2 + if (args.length !== 1) { + throw new Error('BitVec sub only supports 2 arguments'); + } + return new BitVecImpl( + check(Z3.mk_bvsub(contextPtr, arg0.ast, arg0.sort.cast(args[0]).ast)), + ) as unknown as T; + } else { + assert(arg0 instanceof ArithImpl); + return new ArithImpl( + check(Z3.mk_sub(contextPtr, [arg0.ast].concat(args.map(arg => arg0.sort.cast(arg).ast)))), + ) as unknown as T; + } + } + + function Product(arg0: Arith, ...args: CoercibleToArith[]): Arith; + function Product( + arg0: BitVec, + ...args: CoercibleToBitVec[] + ): BitVec; + function Product>(arg0: T, ...args: CoercibleToMap[]): T { + if (arg0 instanceof BitVecImpl) { + // Assert only 2 + if (args.length !== 1) { + throw new Error('BitVec mul only supports 2 arguments'); + } + return new BitVecImpl( + check(Z3.mk_bvmul(contextPtr, arg0.ast, arg0.sort.cast(args[0]).ast)), + ) as unknown as T; + } else { + assert(arg0 instanceof ArithImpl); + return new ArithImpl( + check(Z3.mk_mul(contextPtr, [arg0.ast].concat(args.map(arg => arg0.sort.cast(arg).ast)))), + ) as unknown as T; + } + } + + function Div(arg0: Arith, arg1: CoercibleToArith): Arith; + function Div( + arg0: BitVec, + arg1: CoercibleToBitVec, + ): BitVec; + function Div>(arg0: T, arg1: CoercibleToMap): T { + if (arg0 instanceof BitVecImpl) { + return new BitVecImpl( + check(Z3.mk_bvsdiv(contextPtr, arg0.ast, arg0.sort.cast(arg1).ast)), + ) as unknown as T; + } else { + assert(arg0 instanceof ArithImpl); + return new ArithImpl(check(Z3.mk_div(contextPtr, arg0.ast, arg0.sort.cast(arg1).ast))) as unknown as T; + } + } + + function BUDiv( + arg0: BitVec, + arg1: CoercibleToBitVec, + ): BitVec { + return new BitVecImpl( + check(Z3.mk_bvudiv(contextPtr, arg0.ast, arg0.sort.cast(arg1).ast)), + ) as unknown as BitVec; + } + + function Neg(a: Arith): Arith; + function Neg(a: BitVec): BitVec; + function Neg>(a: T): T { + if (a instanceof BitVecImpl) { + return new BitVecImpl(check(Z3.mk_bvneg(contextPtr, a.ast))) as unknown as T; + } else { + assert(a instanceof ArithImpl); + return new ArithImpl(check(Z3.mk_unary_minus(contextPtr, a.ast))) as unknown as T; + } + } + + function Mod(a: Arith, b: CoercibleToArith): Arith; + function Mod(a: BitVec, b: CoercibleToBitVec): BitVec; + function Mod>(a: T, b: CoercibleToMap): T { + if (a instanceof BitVecImpl) { + return new BitVecImpl(check(Z3.mk_bvsrem(contextPtr, a.ast, a.sort.cast(b).ast))) as unknown as T; + } else { + assert(a instanceof ArithImpl); + return new ArithImpl(check(Z3.mk_mod(contextPtr, a.ast, a.sort.cast(b).ast))) as unknown as T; + } + } + class ArithImpl extends ExprImpl> implements Arith { declare readonly __typename: Arith['__typename']; - add(other: Arith | number | bigint | string | CoercibleRational) { - return new ArithImpl(check(Z3.mk_add(contextPtr, [this.ast, this.sort.cast(other).ast]))); + add(other: CoercibleToArith) { + return Sum(this, other); } - mul(other: Arith | number | bigint | string | CoercibleRational) { - return new ArithImpl(check(Z3.mk_mul(contextPtr, [this.ast, this.sort.cast(other).ast]))); + mul(other: CoercibleToArith) { + return Product(this, other); } - sub(other: Arith | number | bigint | string | CoercibleRational) { - return new ArithImpl(check(Z3.mk_sub(contextPtr, [this.ast, this.sort.cast(other).ast]))); + sub(other: CoercibleToArith) { + return Sub(this, other); } - pow(exponent: Arith | number | bigint | string | CoercibleRational) { + pow(exponent: CoercibleToArith) { return new ArithImpl(check(Z3.mk_power(contextPtr, this.ast, this.sort.cast(exponent).ast))); } - div(other: Arith | number | bigint | string | CoercibleRational) { - return new ArithImpl(check(Z3.mk_div(contextPtr, this.ast, this.sort.cast(other).ast))); + div(other: CoercibleToArith) { + return Div(this, other); } - mod(other: Arith | number | bigint | string | CoercibleRational) { - return new ArithImpl(check(Z3.mk_mod(contextPtr, this.ast, this.sort.cast(other).ast))); + mod(other: CoercibleToArith) { + return Mod(this, other); } neg() { - return new ArithImpl(check(Z3.mk_unary_minus(contextPtr, this.ast))); + return Neg(this); } - le(other: Arith | number | bigint | string | CoercibleRational) { - return new BoolImpl(check(Z3.mk_le(contextPtr, this.ast, this.sort.cast(other).ast))); + le(other: CoercibleToArith) { + return LE(this, other); } - lt(other: Arith | number | bigint | string | CoercibleRational) { - return new BoolImpl(check(Z3.mk_lt(contextPtr, this.ast, this.sort.cast(other).ast))); + lt(other: CoercibleToArith) { + return LT(this, other); } - gt(other: Arith | number | bigint | string | CoercibleRational) { - return new BoolImpl(check(Z3.mk_gt(contextPtr, this.ast, this.sort.cast(other).ast))); + gt(other: CoercibleToArith) { + return GT(this, other); } - ge(other: Arith | number | bigint | string | CoercibleRational) { - return new BoolImpl(check(Z3.mk_ge(contextPtr, this.ast, this.sort.cast(other).ast))); + ge(other: CoercibleToArith) { + return GE(this, other); } } @@ -1644,27 +2204,27 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } add(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvadd(contextPtr, this.ast, this.sort.cast(other).ast))); + return Sum(this, other); } mul(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvmul(contextPtr, this.ast, this.sort.cast(other).ast))); + return Product(this, other); } sub(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvsub(contextPtr, this.ast, this.sort.cast(other).ast))); + return Sub(this, other); } sdiv(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvsdiv(contextPtr, this.ast, this.sort.cast(other).ast))); + return Div(this, other); } udiv(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvudiv(contextPtr, this.ast, this.sort.cast(other).ast))); + return BUDiv(this, other); } smod(other: CoercibleToBitVec): BitVec { - return new BitVecImpl(check(Z3.mk_bvsmod(contextPtr, this.ast, this.sort.cast(other).ast))); + return Mod(this, other); } urem(other: CoercibleToBitVec): BitVec { @@ -1676,7 +2236,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } neg(): BitVec { - return new BitVecImpl(check(Z3.mk_bvneg(contextPtr, this.ast))); + return Neg(this); } or(other: CoercibleToBitVec): BitVec { @@ -1723,8 +2283,8 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return new BitVecImpl(check(Z3.mk_bvnot(contextPtr, this.ast))); } - extract(high: number, low: number): BitVec { - return new BitVecImpl(check(Z3.mk_extract(contextPtr, high, low, this.ast))); + extract(high: number, low: number): BitVec { + return Extract(high, low, this); } signExt(count: number): BitVec { @@ -1740,35 +2300,35 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } sle(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvsle(contextPtr, this.ast, this.sort.cast(other).ast))); + return SLE(this, other); } ule(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvule(contextPtr, this.ast, this.sort.cast(other).ast))); + return ULE(this, other); } slt(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvslt(contextPtr, this.ast, this.sort.cast(other).ast))); + return SLT(this, other); } ult(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvult(contextPtr, this.ast, this.sort.cast(other).ast))); + return ULT(this, other); } sge(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvsge(contextPtr, this.ast, this.sort.cast(other).ast))); + return SGE(this, other); } uge(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvuge(contextPtr, this.ast, this.sort.cast(other).ast))); + return UGE(this, other); } sgt(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvsgt(contextPtr, this.ast, this.sort.cast(other).ast))); + return SGT(this, other); } ugt(other: CoercibleToBitVec): Bool { - return new BoolImpl(check(Z3.mk_bvugt(contextPtr, this.ast, this.sort.cast(other).ast))); + return UGT(this, other); } redAnd(): BitVec { @@ -1840,10 +2400,10 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } - class ArraySortImpl, ...AnySort[]] = [Sort, ...Sort[]], - RangeSort extends AnySort = Sort> + class ArraySortImpl, RangeSort extends Sort> extends SortImpl - implements SMTArraySort { + implements SMTArraySort + { declare readonly __typename: SMTArraySort['__typename']; domain(): DomainSort[0] { @@ -1857,15 +2417,13 @@ export function createApi(Z3: Z3Core): Z3HighLevel { range(): RangeSort { return _toSort(check(Z3.get_array_sort_range(contextPtr, this.ptr))) as RangeSort; } - } - class ArrayImpl< - DomainSort extends [AnySort, ...AnySort[]] = [Sort, ...Sort[]], - RangeSort extends AnySort = Sort - > extends ExprImpl> - implements SMTArray { - declare readonly __typename: SMTArray['__typename']; + class ArrayImpl, RangeSort extends Sort> + extends ExprImpl> + implements SMTArray + { + declare readonly __typename: 'Array' | 'Lambda'; domain(): DomainSort[0] { return this.sort.domain(); @@ -1879,35 +2437,145 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return this.sort.range(); } - select(...indices: ArrayIndexType): SortToExprMap { - const args = indices.map((arg, i) => this.domain_n(i).cast(arg as any)); - if (args.length === 1) { - return _toExpr(check(Z3.mk_select(contextPtr, this.ast, args[0].ast))) as SortToExprMap; - } - const _args = args.map(arg => arg.ast); - return _toExpr(check(Z3.mk_select_n(contextPtr, this.ast, _args))) as SortToExprMap; + select(...indices: CoercibleToArrayIndexType): SortToExprMap { + return Select(this, ...indices) as SortToExprMap; } store( ...indicesAndValue: [ - ...ArrayIndexType, - CoercibleFromMap, Name> + ...CoercibleToArrayIndexType, + CoercibleToMap, Name>, ] ): SMTArray { - const args = indicesAndValue.map((arg, i) => { - if (i === indicesAndValue.length - 1) { - return this.range().cast(arg as CoercibleFromMap, Name>); - } - return this.domain_n(i).cast(arg as any); - }); - if (args.length <= 1) { - throw new Z3Error("Array store requires both index and value arguments"); - } - if (args.length === 2) { - return _toExpr(check(Z3.mk_store(contextPtr, this.ast, args[0].ast, args[1].ast))) as SMTArray; - } - const _idxs = args.slice(0, args.length - 1).map(arg => arg.ast); - return _toExpr(check(Z3.mk_store_n(contextPtr, this.ast, _idxs, args[args.length - 1].ast))) as SMTArray; + return Store(this, ...indicesAndValue); + } + } + + class QuantifierImpl< + QVarSorts extends NonEmptySortArray, + QSort extends BoolSort | SMTArraySort, + > + extends ExprImpl + implements Quantifier + { + declare readonly __typename: Quantifier['__typename']; + + is_forall(): boolean { + return Z3.is_quantifier_forall(contextPtr, this.ast); + } + + is_exists(): boolean { + return Z3.is_quantifier_exists(contextPtr, this.ast); + } + + is_lambda(): boolean { + return Z3.is_lambda(contextPtr, this.ast); + } + + weight(): number { + return Z3.get_quantifier_weight(contextPtr, this.ast); + } + + num_patterns(): number { + return Z3.get_quantifier_num_patterns(contextPtr, this.ast); + } + + pattern(i: number): Pattern { + return new PatternImpl(check(Z3.get_quantifier_pattern_ast(contextPtr, this.ast, i))); + } + + num_no_patterns(): number { + return Z3.get_quantifier_num_no_patterns(contextPtr, this.ast); + } + + no_pattern(i: number): Expr { + return _toExpr(check(Z3.get_quantifier_no_pattern_ast(contextPtr, this.ast, i))); + } + + body(): BodyT { + return _toExpr(check(Z3.get_quantifier_body(contextPtr, this.ast))) as any; + } + + num_vars(): number { + return Z3.get_quantifier_num_bound(contextPtr, this.ast); + } + + var_name(i: number): string | number { + return _fromSymbol(Z3.get_quantifier_bound_name(contextPtr, this.ast, i)); + } + + var_sort(i: T): QVarSorts[T] { + return _toSort(check(Z3.get_quantifier_bound_sort(contextPtr, this.ast, i))); + } + + children(): [BodyT] { + return [this.body()]; + } + } + + class NonLambdaQuantifierImpl> + extends QuantifierImpl> + implements Quantifier>, Bool + { + declare readonly __typename: 'NonLambdaQuantifier'; + + not(): Bool { + return Not(this); + } + + and(other: Bool | boolean): Bool { + return And(this, other); + } + + or(other: Bool | boolean): Bool { + return Or(this, other); + } + + xor(other: Bool | boolean): Bool { + return Xor(this, other); + } + + implies(other: Bool | boolean): Bool { + return Implies(this, other); + } + + iff(other: Bool | boolean): Bool { + return Iff(this, other); + } + } + + // isBool will return false which is unlike the python API (but like the C API) + class LambdaImpl, RangeSort extends Sort> + extends QuantifierImpl> + implements + Quantifier>, + SMTArray + { + declare readonly __typename: 'Lambda'; + + domain(): DomainSort[0] { + return this.sort.domain(); + } + + domain_n(i: T): DomainSort[T] { + return this.sort.domain_n(i); + } + + range(): RangeSort { + return this.sort.range(); + } + + select(...indices: CoercibleToArrayIndexType): SortToExprMap { + return Select(this, ...indices); + } + + store( + ...indicesAndValue: [ + ...CoercibleToArrayIndexType, + CoercibleToMap, Name>, + ] + ): SMTArray { + return Store(this, ...indicesAndValue); } } @@ -1929,20 +2597,20 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return this.values(); } - * entries(): IterableIterator<[number, Item]> { + *entries(): IterableIterator<[number, Item]> { const length = this.length(); for (let i = 0; i < length; i++) { yield [i, this.get(i)]; } } - * keys(): IterableIterator { + *keys(): IterableIterator { for (let [key] of this.entries()) { yield key; } } - * values(): IterableIterator { + *values(): IterableIterator { for (let [, value] of this.entries()) { yield value; } @@ -2027,7 +2695,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return Z3.ast_map_size(contextPtr, this.ptr); } - * entries(): IterableIterator<[Key, Value]> { + *entries(): IterableIterator<[Key, Value]> { for (const key of this.keys()) { yield [key, this.get(key)]; } @@ -2037,7 +2705,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { return new AstVectorImpl(Z3.ast_map_keys(contextPtr, this.ptr)); } - * values(): IterableIterator { + *values(): IterableIterator { for (const [_, value] of this.entries()) { yield value; } @@ -2068,6 +2736,31 @@ export function createApi(Z3: Z3Core): Z3HighLevel { } } + function substitute(t: Expr, ...substitutions: [Expr, Expr][]): Expr { + _assertContext(t); + const from: Z3_ast[] = []; + const to: Z3_ast[] = []; + for (const [f, t] of substitutions) { + _assertContext(f); + _assertContext(t); + from.push(f.ast); + to.push(t.ast); + } + return _toExpr(check(Z3.substitute(contextPtr, t.ast, from, to))); + } + + function ast_from_string(s: string): Ast { + const sort_names: Z3_symbol[] = []; + const sorts: Z3_sort[] = []; + const decl_names: Z3_symbol[] = []; + const decls: Z3_func_decl[] = []; + const v = new AstVectorImpl(check(Z3.parse_smtlib2_string(contextPtr, s, sort_names, sorts, decl_names, decls))); + if (v.length() !== 1) { + throw new Error('Expected exactly one AST. Instead got ' + v.length() + ': ' + v.sexpr()); + } + return v.get(0); + } + const ctx: Context = { ptr: contextPtr, name, @@ -2076,6 +2769,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { // Classes // ///////////// Solver: SolverImpl, + Optimize: OptimizeImpl, Model: ModelImpl, Tactic: TacticImpl, AstVector: AstVectorImpl as AstVectorCtor, @@ -2089,6 +2783,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { isAst, isSort, isFuncDecl, + isFuncInterp, isApp, isConst, isExpr, @@ -2103,6 +2798,7 @@ export function createApi(Z3: Z3Core): Z3HighLevel { isNot, isEq, isDistinct, + isQuantifier, isArith, isArithSort, isInt, @@ -2147,11 +2843,15 @@ export function createApi(Z3: Z3Core): Z3HighLevel { FreshConst, Var, Implies, + Iff, Eq, Xor, Not, And, Or, + ForAll, + Exists, + Lambda, ToReal, ToInt, IsInt, @@ -2161,6 +2861,36 @@ export function createApi(Z3: Z3Core): Z3HighLevel { Int2BV, Concat, Cond, + LT, + GT, + LE, + GE, + ULT, + UGT, + ULE, + UGE, + SLT, + SGT, + SLE, + SGE, + Sum, + Sub, + Product, + Div, + BUDiv, + Neg, + Mod, + Select, + Store, + Extract, + + substitute, + simplify, + + ///////////// + // Loading // + ///////////// + ast_from_string, }; cleanup.register(ctx, () => Z3.del_context(contextPtr)); return ctx; diff --git a/src/api/js/src/high-level/types.ts b/src/api/js/src/high-level/types.ts index a6cb01e79..6f3630a6d 100644 --- a/src/api/js/src/high-level/types.ts +++ b/src/api/js/src/high-level/types.ts @@ -5,13 +5,14 @@ import { Z3_context, Z3_decl_kind, Z3_func_decl, + Z3_func_entry, Z3_func_interp, Z3_model, Z3_probe, Z3_solver, + Z3_optimize, Z3_sort, Z3_sort_kind, - Z3_symbol, Z3_tactic, } from '../low-level'; @@ -21,7 +22,7 @@ export type AnySort = | BoolSort | ArithSort | BitVecSort - | SMTArraySort, ...AnySort[]], AnySort>; + | SMTArraySort; /** @hidden */ export type AnyExpr = | Expr @@ -31,53 +32,64 @@ export type AnyExpr = | RatNum | BitVec | BitVecNum - | SMTArray, ...AnySort[]], AnySort>; + | SMTArray; /** @hidden */ export type AnyAst = AnyExpr | AnySort | FuncDecl; /** @hidden */ -export type SortToExprMap, Name extends string = 'main'> = - S extends BoolSort - ? Bool - : S extends ArithSort - ? Arith - : S extends BitVecSort - ? BitVec - : S extends SMTArraySort - ? SMTArray - : S extends Sort - ? Expr - : never; +export type SortToExprMap, Name extends string = 'main'> = S extends BoolSort + ? Bool + : S extends ArithSort + ? Arith + : S extends BitVecSort + ? BitVec + : S extends SMTArraySort + ? SMTArray + : S extends Sort + ? Expr + : never; /** @hidden */ -export type CoercibleToExprMap, Name extends string = 'main'> = - S extends bigint - ? ArithSort - : S extends number | CoercibleRational - ? RatNum - : S extends boolean - ? Bool - : S extends Expr - ? S - : never; +export type CoercibleFromMap, Name extends string = 'main'> = S extends bigint + ? Arith + : S extends number | CoercibleRational + ? RatNum + : S extends boolean + ? Bool + : S extends Expr + ? S + : never; /** @hidden */ -export type CoercibleFromMap, Name extends string = 'main'> = - S extends Bool - ? (boolean | Bool) - : S extends IntNum - ? (bigint | number | IntNum) - : S extends RatNum - ? (bigint | number | CoercibleRational | RatNum) - : S extends Arith - ? (bigint | number | CoercibleRational | Arith) - : S extends BitVec - ? (number | BitVec) - : S extends SMTArray - ? SMTArray - : S extends Expr - ? Expr - : never; +export type CoercibleToBitVec = + | bigint + | number + | BitVec; + +export type CoercibleRational = { numerator: bigint | number; denominator: bigint | number }; + +/** @hidden */ +export type CoercibleToExpr = number | bigint | boolean | CoercibleRational | Expr; + +/** @hidden */ +export type CoercibleToArith = number | string | bigint | CoercibleRational | Arith; + +/** @hidden */ +export type CoercibleToMap, Name extends string = 'main'> = T extends Bool + ? boolean | Bool + : T extends IntNum + ? bigint | number | IntNum + : T extends RatNum + ? bigint | number | CoercibleRational | RatNum + : T extends Arith + ? CoercibleToArith + : T extends BitVec + ? CoercibleToBitVec + : T extends SMTArray + ? SMTArray + : T extends Expr + ? Expr + : never; /** * Used to create a Real constant @@ -97,16 +109,10 @@ export type CoercibleFromMap, Name extends string = 'mai * @see {@link Context.from} * @category Global */ -export type CoercibleRational = { numerator: bigint | number; denominator: bigint | number }; -/** @hidden */ -export type CoercibleToExpr = number | bigint | boolean | CoercibleRational | Expr; +export class Z3Error extends Error {} -export class Z3Error extends Error { -} - -export class Z3AssertionError extends Z3Error { -} +export class Z3AssertionError extends Z3Error {} /** @category Global */ export type CheckSatResult = 'sat' | 'unsat' | 'unknown'; @@ -149,6 +155,9 @@ export interface Context { /** @category Functions */ isFuncDecl(obj: unknown): obj is FuncDecl; + /** @category Functions */ + isFuncInterp(obj: unknown): obj is FuncInterp; + /** @category Functions */ isApp(obj: unknown): boolean; @@ -191,6 +200,9 @@ export interface Context { /** @category Functions */ isDistinct(obj: unknown): boolean; + /** @category Functions */ + isQuantifier(obj: unknown): obj is Quantifier; + /** @category Functions */ isArith(obj: unknown): obj is Arith; @@ -225,10 +237,10 @@ export interface Context { isBitVecVal(obj: unknown): obj is BitVecNum; /** @category Functions */ - isArraySort(obj: unknown): obj is SMTArraySort, ...AnySort[]], AnySort>; + isArraySort(obj: unknown): obj is SMTArraySort; /** @category Functions */ - isArray(obj: unknown): obj is SMTArray, ...AnySort[]], AnySort>; + isArray(obj: unknown): obj is SMTArray; /** @category Functions */ isConstArray(obj: unknown): boolean; @@ -306,6 +318,9 @@ export interface Context { * @category Classes */ readonly Solver: new (logic?: string) => Solver; + + readonly Optimize: new () => Optimize; + /** * Creates an empty Model * @see {@link Solver.model} for common usage of Model @@ -315,7 +330,11 @@ export interface Context { /** @category Classes */ readonly AstVector: new = AnyAst>() => AstVector; /** @category Classes */ - readonly AstMap: new = AnyAst, Value extends Ast = AnyAst>() => AstMap; + readonly AstMap: new = AnyAst, Value extends Ast = AnyAst>() => AstMap< + Name, + Key, + Value + >; /** @category Classes */ readonly Tactic: new (name: string) => Tactic; @@ -363,7 +382,7 @@ export interface Context { condition: Bool | boolean, onTrue: OnTrueRef, onFalse: OnFalseRef, - ): CoercibleToExprMap; + ): CoercibleFromMap; /** @category Operations */ Distinct(...args: CoercibleToExpr[]): Bool; @@ -371,6 +390,9 @@ export interface Context { /** @category Operations */ Implies(a: Bool | boolean, b: Bool | boolean): Bool; + /** @category Operations */ + Iff(a: Bool | boolean, b: Bool | boolean): Bool; + /** @category Operations */ Eq(a: CoercibleToExpr, b: CoercibleToExpr): Bool; @@ -407,6 +429,28 @@ export interface Context { /** @category Operations */ Or(...args: Probe[]): Probe; + // Quantifiers + + /** @category Operations */ + ForAll>( + quantifiers: ArrayIndexType, + body: Bool, + weight?: number, + ): Quantifier> & Bool; + + /** @category Operations */ + Exists>( + quantifiers: ArrayIndexType, + body: Bool, + weight?: number, + ): Quantifier> & Bool; + + /** @category Operations */ + Lambda, RangeSort extends Sort>( + quantifiers: ArrayIndexType, + expr: SortToExprMap, + ): Quantifier> & SMTArray; + // Arithmetic /** @category Operations */ ToReal(expr: Arith | bigint): Arith; @@ -437,7 +481,7 @@ export interface Context { * // a**(1/2) * ``` * @category Operations */ - Sqrt(a: Arith | number | bigint | string | CoercibleRational): Arith; + Sqrt(a: CoercibleToArith): Arith; /** * Returns a Z3 expression representing cubic root of a @@ -449,7 +493,7 @@ export interface Context { * // a**(1/3) * ``` * @category Operations */ - Cbrt(a: Arith | number | bigint | string | CoercibleRational): Arith; + Cbrt(a: CoercibleToArith): Arith; // Bit Vectors /** @category Operations */ @@ -462,7 +506,102 @@ export interface Context { Concat(...bitvecs: BitVec[]): BitVec; /** @category Operations */ - Cond(probe: Probe, onTrue: Tactic, onFalse: Tactic): Tactic + Cond(probe: Probe, onTrue: Tactic, onFalse: Tactic): Tactic; + + // Arith + + /** @category Operations */ + LT(a: Arith, b: CoercibleToArith): Bool; + + /** @category Operations */ + GT(a: Arith, b: CoercibleToArith): Bool; + + /** @category Operations */ + LE(a: Arith, b: CoercibleToArith): Bool; + + /** @category Operations */ + GE(a: Arith, b: CoercibleToArith): Bool; + + // Bit Vectors + + /** @category Operations */ + ULT(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + UGT(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + ULE(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + UGE(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + SLT(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + SGT(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + SGE(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + SLE(a: BitVec, b: CoercibleToBitVec): Bool; + + /** @category Operations */ + Sum(arg0: Arith, ...args: CoercibleToArith[]): Arith; + + Sum(arg0: BitVec, ...args: CoercibleToBitVec[]): BitVec; + + Sub(arg0: Arith, ...args: CoercibleToArith[]): Arith; + + Sub(arg0: BitVec, ...args: CoercibleToBitVec[]): BitVec; + + Product(arg0: Arith, ...args: CoercibleToArith[]): Arith; + + Product(arg0: BitVec, ...args: CoercibleToBitVec[]): BitVec; + + Div(arg0: Arith, arg1: CoercibleToArith): Arith; + + Div(arg0: BitVec, arg1: CoercibleToBitVec): BitVec; + + BUDiv(arg0: BitVec, arg1: CoercibleToBitVec): BitVec; + + Neg(a: Arith): Arith; + + Neg(a: BitVec): BitVec; + + Mod(a: Arith, b: CoercibleToArith): Arith; + + Mod(a: BitVec, b: CoercibleToBitVec): BitVec; + + // Arrays + + /** @category Operations */ + Select, RangeSort extends Sort = Sort>( + array: SMTArray, + ...indices: CoercibleToArrayIndexType + ): SortToExprMap; + + /** @category Operations */ + Store, RangeSort extends Sort = Sort>( + array: SMTArray, + ...indicesAndValue: [ + ...CoercibleToArrayIndexType, + CoercibleToMap, Name>, + ] + ): SMTArray; + + /** @category Operations */ + Extract(hi: number, lo: number, val: BitVec): BitVec; + + /** @category Operations */ + ast_from_string(s: string): Ast; + + /** @category Operations */ + substitute(t: Expr, ...substitutions: [Expr, Expr][]): Expr; + + simplify(expr: Expr): Promise>; } export interface Ast { @@ -490,7 +629,7 @@ export interface Ast { /** @hidden */ export interface SolverCtor { - new(): Solver; + new (): Solver; } export interface Solver { @@ -500,10 +639,11 @@ export interface Solver { readonly ctx: Context; readonly ptr: Z3_solver; - /* TODO(ritave): Decide on how to discern between integer and float parameters set(key: string, value: any): void; - set(params: Record): void; - */ + + /* TODO(ritave): Decide on how to discern between integer and float parameters + set(params: Record): void; + */ push(): void; pop(num?: number): void; @@ -525,9 +665,42 @@ export interface Solver { model(): Model; } +export interface Optimize { + /** @hidden */ + readonly __typename: 'Optimize'; + + readonly ctx: Context; + readonly ptr: Z3_optimize; + + set(key: string, value: any): void; + + push(): void; + + pop(num?: number): void; + + add(...exprs: (Bool | AstVector>)[]): void; + + addSoft(expr: Bool, weight: number | bigint | string | CoercibleRational, id?: number | string): void; + + addAndTrack(expr: Bool, constant: Bool | string): void; + + assertions(): AstVector>; + + fromString(s: string): void; + + maximize(expr: Arith): void; + + minimize(expr: Arith): void; + + check(...exprs: (Bool | AstVector>)[]): Promise; + + model(): Model; +} + + /** @hidden */ export interface ModelCtor { - new(): Model; + new (): Model; } export interface Model extends Iterable> { @@ -566,6 +739,13 @@ export interface Model extends Iterable): Expr; get(sort: Sort): AstVector>; + + updateValue(decl: FuncDecl | Expr, a: Ast | FuncInterp): void; + + addFuncInterp[] = Sort[], RangeSort extends Sort = Sort>( + decl: FuncDecl, + defaultValue: CoercibleToMap, Name>, + ): FuncInterp; } /** @@ -608,6 +788,23 @@ export interface Sort extends Ast { name(): string | number; } +/** + * @category Functions + */ +export interface FuncEntry { + /** @hidden */ + readonly __typename: 'FuncEntry'; + + readonly ctx: Context; + readonly ptr: Z3_func_entry; + + numArgs(): number; + + argValue(i: number): Expr; + + value(): Expr; +} + /** * @category Functions */ @@ -617,6 +814,16 @@ export interface FuncInterp { readonly ctx: Context; readonly ptr: Z3_func_interp; + + elseValue(): Expr; + + numEntries(): number; + + arity(): number; + + entry(i: number): FuncEntry; + + addEntry(args: Expr[], value: Expr): void; } /** @hidden */ @@ -639,9 +846,14 @@ export interface FuncDeclCreation { * @param name Name of the function * @param signature The domains, and last parameter - the range of the function */ - declare(name: string, ...signature: FuncDeclSignature): FuncDecl; + declare[], RangeSort extends Sort>( + name: string, + ...signature: [...DomainSort, RangeSort] + ): FuncDecl; - fresh(...signature: FuncDeclSignature): FuncDecl; + fresh[], RangeSort extends Sort>( + ...signature: [...DomainSort, RangeSort] + ): FuncDecl; } /** @@ -656,7 +868,11 @@ export interface RecFuncCreation { /** * @category Functions */ -export interface FuncDecl extends Ast { +export interface FuncDecl< + Name extends string = 'main', + DomainSort extends Sort[] = Sort[], + RangeSort extends Sort = Sort, +> extends Ast { /** @hidden */ readonly __typename: 'FuncDecl'; @@ -664,21 +880,26 @@ export interface FuncDecl extends Ast; + domain(i: T): DomainSort[T]; - range(): Sort; + range(): RangeSort; kind(): Z3_decl_kind; - params(): (number | string | Z3_symbol | Sort | Expr | FuncDecl)[]; + params(): (number | string | Sort | Expr | FuncDecl)[]; - call(...args: CoercibleToExpr[]): AnyExpr; + call(...args: CoercibleToArrayIndexType): SortToExprMap; } export interface Expr = AnySort, Ptr = unknown> extends Ast { /** @hidden */ - readonly __typename: 'Expr' | Bool['__typename'] | Arith['__typename'] | BitVec['__typename'] | SMTArray['__typename']; + readonly __typename: + | 'Expr' + | Bool['__typename'] + | Arith['__typename'] + | BitVec['__typename'] + | SMTArray['__typename']; get sort(): S; @@ -688,6 +909,8 @@ export interface Expr = AnySo params(): ReturnType['params']>; + name(): ReturnType['name']>; + decl(): FuncDecl; numArgs(): number; @@ -725,7 +948,7 @@ export interface BoolCreation { /** @category Booleans */ export interface Bool extends Expr, Z3_ast> { /** @hidden */ - readonly __typename: 'Bool'; + readonly __typename: 'Bool' | 'NonLambdaQuantifier'; not(): Bool; @@ -738,6 +961,13 @@ export interface Bool extends Expr | boolean): Bool; } +// TODO: properly implement pattern +/** @category Quantifiers */ +export interface Pattern { + /** @hidden */ + readonly __typename: 'Pattern'; +} + /** * A Sort that represents Integers or Real numbers * @category Arithmetic @@ -798,17 +1028,17 @@ export interface Arith extends Expr | number | bigint | string): Arith; + add(other: CoercibleToArith): Arith; /** * Multiplies two numbers together */ - mul(other: Arith | number | bigint | string): Arith; + mul(other: CoercibleToArith): Arith; /** * Subtract second number from the first one */ - sub(other: Arith | number | bigint | string): Arith; + sub(other: CoercibleToArith): Arith; /** * Applies power to the number @@ -820,12 +1050,12 @@ export interface Arith extends Expr | number | bigint | string): Arith; + pow(exponent: CoercibleToArith): Arith; /** * Divides the number by the second one */ - div(other: Arith | number | bigint | string): Arith; + div(other: CoercibleToArith): Arith; /** * Returns a number modulo second one @@ -837,7 +1067,7 @@ export interface Arith extends Expr | number | bigint | string): Arith; + mod(other: CoercibleToArith): Arith; /** * Returns a negation of the number @@ -847,22 +1077,22 @@ export interface Arith extends Expr | number | bigint | string): Bool; + le(other: CoercibleToArith): Bool; /** * Returns whether the number is less than the second one (`<`) */ - lt(other: Arith | number | bigint | string): Bool; + lt(other: CoercibleToArith): Bool; /** * Returns whether the number is greater than the second one (`>`) */ - gt(other: Arith | number | bigint | string): Bool; + gt(other: CoercibleToArith): Bool; /** * Returns whether the number is greater or equal than the second one (`>=`) */ - ge(other: Arith | number | bigint | string): Bool; + ge(other: CoercibleToArith): Bool; } /** @@ -939,12 +1169,6 @@ export interface BitVecSort): Expr; } -/** @hidden */ -export type CoercibleToBitVec = - | bigint - | number - | BitVec; - /** @category Bit Vectors */ export interface BitVecCreation { sort(bits: Bits): BitVecSort; @@ -1213,10 +1437,11 @@ export interface BitVecNum, ...AnySort[]] = [Sort, ...Sort[]], +export interface SMTArraySort< + Name extends string = 'main', + DomainSort extends NonEmptySortArray = [Sort, ...Sort[]], RangeSort extends AnySort = AnySort, - > extends Sort { +> extends Sort { /** @hidden */ readonly __typename: 'ArraySort'; @@ -1236,36 +1461,47 @@ export interface SMTArraySort { - sort, ...AnySort[]], RangeSort extends AnySort>( + sort, RangeSort extends Sort>( ...sig: [...DomainSort, RangeSort] ): SMTArraySort; - const, ...AnySort[]], RangeSort extends AnySort>( - name: string, ...sig: [...DomainSort, RangeSort] + const, RangeSort extends Sort>( + name: string, + ...sig: [...DomainSort, RangeSort] ): SMTArray; - consts, ...AnySort[]], RangeSort extends AnySort>( + consts, RangeSort extends Sort>( names: string | string[], ...sig: [...DomainSort, RangeSort] ): SMTArray[]; K, RangeSort extends AnySort>( domain: DomainSort, - value: SortToExprMap + value: SortToExprMap, ): SMTArray; } -export type ArrayIndexType, ...AnySort[]] = [Sort, ...Sort[]]> = [...{ - [Index in keyof DomainSort]: DomainSort[Index] extends AnySort ? - CoercibleFromMap, Name> : - DomainSort[Index]; -}] +export type NonEmptySortArray = [Sort, ...Array>]; + +export type ArrayIndexType[]> = [ + ...{ + [Key in keyof DomainSort]: DomainSort[Key] extends AnySort + ? SortToExprMap + : DomainSort[Key]; + }, +]; + +export type CoercibleToArrayIndexType[]> = [ + ...{ + [Key in keyof DomainSort]: DomainSort[Key] extends AnySort + ? CoercibleToMap, Name> + : DomainSort[Key]; + }, +]; /** * Represents Array expression @@ -1274,13 +1510,13 @@ export type ArrayIndexType, ...AnySort[]] = [Sort, ...Sort[]], - RangeSort extends AnySort = AnySort> - extends Expr, Z3_ast> { - +export interface SMTArray< + Name extends string = 'main', + DomainSort extends NonEmptySortArray = [Sort, ...Sort[]], + RangeSort extends Sort = Sort, +> extends Expr, Z3_ast> { /** @hidden */ - readonly __typename: 'Array'; + readonly __typename: 'Array' | 'Lambda'; domain(): DomainSort[0]; @@ -1288,7 +1524,7 @@ export interface SMTArray): SortToExprMap; + select(...indices: CoercibleToArrayIndexType): SortToExprMap; /** * value should be coercible to RangeSort @@ -1297,11 +1533,60 @@ export interface SMTArray, - CoercibleFromMap, Name> + ...CoercibleToArrayIndexType, + CoercibleToMap, Name>, ] ): SMTArray; +} +/** + * Defines the expression type of the body of a quantifier expression + * + * @category Quantifiers + */ +export type BodyT< + Name extends string = 'main', + QVarSorts extends NonEmptySortArray = [Sort, ...Sort[]], + QSort extends BoolSort | SMTArraySort = BoolSort | SMTArraySort, +> = QSort extends BoolSort + ? Bool + : QSort extends SMTArray + ? SortToExprMap + : never; + +/** @category Quantifiers */ +export interface Quantifier< + Name extends string = 'main', + QVarSorts extends NonEmptySortArray = [Sort, ...Sort[]], + QSort extends BoolSort | SMTArraySort = BoolSort | SMTArraySort, +> extends Expr { + readonly __typename: 'NonLambdaQuantifier' | 'Lambda'; + + is_forall(): boolean; + + is_exists(): boolean; + + is_lambda(): boolean; + + weight(): number; + + num_patterns(): number; + + pattern(i: number): Pattern; + + num_no_patterns(): number; + + no_pattern(i: number): Expr; + + body(): BodyT; + + num_vars(): number; + + var_name(i: number): string | number; + + var_sort(i: T): QVarSorts[T]; + + children(): [BodyT]; } export interface Probe { @@ -1314,7 +1599,7 @@ export interface Probe { /** @hidden */ export interface TacticCtor { - new(name: string): Tactic; + new (name: string): Tactic; } export interface Tactic { @@ -1327,7 +1612,7 @@ export interface Tactic { /** @hidden */ export interface AstVectorCtor { - new = AnyAst>(): AstVector; + new = AnyAst>(): AstVector; } /** @@ -1378,7 +1663,7 @@ export interface AstVector /** @hidden */ export interface AstMapCtor { - new = AnyAst, Value extends Ast = AnyAst>(): AstMap; + new = AnyAst, Value extends Ast = AnyAst>(): AstMap; } /** @@ -1402,8 +1687,11 @@ export interface AstMapCtor { * // 0 * ``` */ -export interface AstMap = AnyAst, Value extends Ast = AnyAst> - extends Iterable<[Key, Value]> { +export interface AstMap< + Name extends string = 'main', + Key extends Ast = AnyAst, + Value extends Ast = AnyAst, +> extends Iterable<[Key, Value]> { /** @hidden */ readonly __typename: 'AstMap'; diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index ac21902fe..98807bedd 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1253,7 +1253,9 @@ struct let mk_re_sort = Z3native.mk_re_sort let is_re_sort = Z3native.is_re_sort let mk_string_sort = Z3native.mk_string_sort + let mk_char_sort = Z3native.mk_char_sort let is_string_sort = Z3native.is_string_sort + let is_char_sort = Z3native.is_char_sort let mk_string = Z3native.mk_string let is_string = Z3native.is_string let get_string = Z3native.get_string @@ -1274,6 +1276,10 @@ struct let mk_str_le = Z3native.mk_str_le let mk_str_lt = Z3native.mk_str_lt let mk_int_to_str = Z3native.mk_int_to_str + let mk_string_to_code = Z3native.mk_string_to_code + let mk_string_from_code = Z3native.mk_string_from_code + let mk_ubv_to_str = Z3native.mk_ubv_to_str + let mk_sbv_to_str = Z3native.mk_sbv_to_str let mk_seq_to_re = Z3native.mk_seq_to_re let mk_seq_in_re = Z3native.mk_seq_in_re let mk_re_plus = Z3native.mk_re_plus @@ -1287,6 +1293,12 @@ struct let mk_re_complement = Z3native.mk_re_complement let mk_re_empty = Z3native.mk_re_empty let mk_re_full = Z3native.mk_re_full + let mk_char = Z3native.mk_char + let mk_char_le = Z3native.mk_char_le + let mk_char_to_int = Z3native.mk_char_to_int + let mk_char_to_bv = Z3native.mk_char_to_bv + let mk_char_from_bv = Z3native.mk_char_from_bv + let mk_char_is_digit = Z3native.mk_char_is_digit end module FloatingPoint = @@ -1542,7 +1554,7 @@ struct let to_string (x:func_entry) = let a = get_args x in - let f c p = (p ^ (Expr.to_string c) ^ ", ") in + let f c p = ((Expr.to_string c) ^ ", " ^ p) in "[" ^ List.fold_right f a ((Expr.to_string (get_value x)) ^ "]") end diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index 27b0992ca..53e92b491 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -1881,9 +1881,15 @@ sig (** create string sort *) val mk_string_sort : context -> Sort.sort + (** create char sort *) + val mk_char_sort : context -> Sort.sort + (** test if sort is a string sort (a sequence of 8-bit bit-vectors) *) val is_string_sort : context -> Sort.sort -> bool + (** test if sort is a char sort *) + val is_char_sort : context -> Sort.sort -> bool + (** create a string literal *) val mk_string : context -> string -> Expr.expr @@ -1936,6 +1942,7 @@ sig (** retrieve integer expression encoded in string *) val mk_str_to_int : context -> Expr.expr -> Expr.expr + (** compare strings less-than-or-equal *) val mk_str_le : context -> Expr.expr -> Expr.expr -> Expr.expr @@ -1945,6 +1952,18 @@ sig (** convert an integer expression to a string *) val mk_int_to_str : context -> Expr.expr -> Expr.expr + (** [mk_string_to_code ctx s] convert a unit length string [s] to integer code *) + val mk_string_to_code : context -> Expr.expr -> Expr.expr + + (** [mk_string_from_code ctx c] convert code [c] to a string *) + val mk_string_from_code : context -> Expr.expr -> Expr.expr + + (** [mk_ubv_to_str ctx ubv] convert a unsigned bitvector [ubv] to a string *) + val mk_ubv_to_str : context -> Expr.expr -> Expr.expr + + (** [mk_sbv_to_str ctx sbv] convert a signed bitvector [sbv] to a string *) + val mk_sbv_to_str : context -> Expr.expr -> Expr.expr + (** create regular expression that accepts the argument sequence *) val mk_seq_to_re : context -> Expr.expr -> Expr.expr @@ -1984,6 +2003,24 @@ sig (** the regular expression that accepts all sequences *) val mk_re_full : context -> Sort.sort -> Expr.expr + (** [mk_char ctx i] converts an integer to a character *) + val mk_char : context -> int -> Expr.expr + + (** [mk_char_le ctx lc rc] compares two characters *) + val mk_char_le : context -> Expr.expr -> Expr.expr -> Expr.expr + + (** [mk_char_to_int ctx c] converts the character [c] to an integer *) + val mk_char_to_int : context -> Expr.expr -> Expr.expr + + (** [mk_char_to_bv ctx c] converts the character [c] to a bitvector *) + val mk_char_to_bv : context -> Expr.expr -> Expr.expr + + (** [mk_char_from_bv ctx bv] converts the bitvector [bv] to a character *) + val mk_char_from_bv : context -> Expr.expr -> Expr.expr + + (** [mk_char_is_digit ctx c] checks if the character [c] is a digit *) + val mk_char_is_digit: context -> Expr.expr -> Expr.expr + end (** Floating-Point Arithmetic *) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 92bb2aa24..6b79dd1fe 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -3173,12 +3173,8 @@ def _to_int_str(val): return "1" else: return "0" - elif _is_int(val): + else: return str(val) - elif isinstance(val, str): - return val - if z3_debug(): - _z3_assert(False, "Python value cannot be used as a Z3 integer") def IntVal(val, ctx=None): @@ -11343,7 +11339,7 @@ def Range(lo, hi, ctx=None): return ReRef(Z3_mk_re_range(lo.ctx_ref(), lo.ast, hi.ast), lo.ctx) def Diff(a, b, ctx=None): - """Create the difference regular epression + """Create the difference regular expression """ return ReRef(Z3_mk_re_diff(a.ctx_ref(), a.ast, b.ast), a.ctx) diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp index a67464a92..4778caf89 100644 --- a/src/ast/arith_decl_plugin.cpp +++ b/src/ast/arith_decl_plugin.cpp @@ -365,7 +365,6 @@ inline func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, bool is_real) { case OP_MOD: return m_i_mod_decl; case OP_DIV0: return m_manager->mk_func_decl(symbol("/0"), m_real_decl, m_real_decl, m_real_decl, func_decl_info(m_family_id, OP_DIV0)); case OP_IDIV0: return m_manager->mk_func_decl(symbol("div0"), m_int_decl, m_int_decl, m_int_decl, func_decl_info(m_family_id, OP_IDIV0)); - case OP_REM0: return m_manager->mk_func_decl(symbol("rem0"), m_int_decl, m_int_decl, m_int_decl, func_decl_info(m_family_id, OP_REM0)); case OP_MOD0: return m_manager->mk_func_decl(symbol("mod0"), m_int_decl, m_int_decl, m_int_decl, func_decl_info(m_family_id, OP_MOD0)); case OP_POWER0: if (is_real) { @@ -612,7 +611,6 @@ void arith_decl_plugin::get_op_names(svector& op_names, symbol con op_names.push_back(builtin_name("euler", OP_E)); op_names.push_back(builtin_name("/0",OP_DIV0)); op_names.push_back(builtin_name("div0",OP_IDIV0)); - op_names.push_back(builtin_name("rem0",OP_REM0)); op_names.push_back(builtin_name("mod0",OP_MOD0)); } } @@ -821,7 +819,7 @@ bool arith_util::is_considered_uninterpreted(func_decl* f, unsigned n, expr* con } if (is_decl_of(f, arith_family_id, OP_REM) && n == 2 && is_numeral(args[1], r) && r.is_zero()) { sort* rs[2] = { mk_int(), mk_int() }; - f_out = m_manager.mk_func_decl(arith_family_id, OP_REM0, 0, nullptr, 2, rs, mk_int()); + f_out = m_manager.mk_func_decl(arith_family_id, OP_MOD0, 0, nullptr, 2, rs, mk_int()); return true; } if (is_decl_of(f, arith_family_id, OP_POWER) && n == 2 && is_numeral(args[1], r) && r.is_zero() && is_numeral(args[0], r) && r.is_zero()) { @@ -857,7 +855,7 @@ func_decl* arith_util::mk_idiv0() { func_decl* arith_util::mk_rem0() { sort* rs[2] = { mk_int(), mk_int() }; - return m_manager.mk_func_decl(arith_family_id, OP_REM0, 0, nullptr, 2, rs, mk_int()); + return m_manager.mk_func_decl(arith_family_id, OP_MOD0, 0, nullptr, 2, rs, mk_int()); } func_decl* arith_util::mk_mod0() { @@ -942,7 +940,6 @@ bool arith_util::is_underspecified(expr* e) const { case OP_MOD: case OP_DIV0: case OP_IDIV0: - case OP_REM0: case OP_MOD0: return true; default: diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index 93d0edf2f..5dbf3e8cf 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -50,7 +50,6 @@ enum arith_op_kind { OP_IDIVIDES, OP_REM, OP_MOD, - OP_REM0, OP_MOD0, OP_TO_REAL, OP_TO_INT, @@ -216,7 +215,6 @@ public: case OP_U_ACOS: case OP_DIV0: case OP_IDIV0: - case OP_REM0: case OP_MOD0: case OP_POWER0: return true; @@ -270,7 +268,7 @@ public: bool is_div0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_DIV0); } bool is_idiv0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_IDIV0); } - bool is_rem0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_REM0); } + bool is_rem0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_MOD0); } bool is_mod0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_MOD0); } bool is_power0(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_POWER0); } bool is_power(func_decl const * n) const { return is_decl_of(n, arith_family_id, OP_POWER); } @@ -296,7 +294,7 @@ public: bool is_mod(expr const * n) const { return is_app_of(n, arith_family_id, OP_MOD); } bool is_rem(expr const * n) const { return is_app_of(n, arith_family_id, OP_REM); } bool is_mod0(expr const * n) const { return is_app_of(n, arith_family_id, OP_MOD0); } - bool is_rem0(expr const * n) const { return is_app_of(n, arith_family_id, OP_REM0); } + bool is_rem0(expr const * n) const { return is_app_of(n, arith_family_id, OP_MOD0); } bool is_to_real(expr const * n) const { return is_app_of(n, arith_family_id, OP_TO_REAL); } bool is_to_int(expr const * n) const { return is_app_of(n, arith_family_id, OP_TO_INT); } bool is_is_int(expr const * n) const { return is_app_of(n, arith_family_id, OP_IS_INT); } @@ -355,7 +353,7 @@ public: MATCH_BINARY(is_div); MATCH_BINARY(is_idiv); MATCH_BINARY(is_mod0); - MATCH_BINARY(is_rem0); + // MATCH_BINARY(is_rem0); MATCH_BINARY(is_div0); MATCH_BINARY(is_idiv0); MATCH_BINARY(is_power); @@ -465,7 +463,7 @@ public: app * mk_mod(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_MOD, arg1, arg2); } app * mk_div0(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_DIV0, arg1, arg2); } app * mk_idiv0(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_IDIV0, arg1, arg2); } - app * mk_rem0(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_REM0, arg1, arg2); } + app * mk_rem0(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_MOD0, arg1, arg2); } app * mk_mod0(expr * arg1, expr * arg2) { return m_manager.mk_app(arith_family_id, OP_MOD0, arg1, arg2); } app * mk_to_real(expr * arg1) { return m_manager.mk_app(arith_family_id, OP_TO_REAL, arg1); } app * mk_to_int(expr * arg1) { return m_manager.mk_app(arith_family_id, OP_TO_INT, arg1); } diff --git a/src/ast/array_decl_plugin.cpp b/src/ast/array_decl_plugin.cpp index 7c2b357c2..6778bec7c 100644 --- a/src/ast/array_decl_plugin.cpp +++ b/src/ast/array_decl_plugin.cpp @@ -315,13 +315,13 @@ func_decl * array_decl_plugin::mk_store(unsigned arity, sort * const * domain) { func_decl * array_decl_plugin::mk_array_ext(unsigned arity, sort * const * domain, unsigned i) { if (arity != 2 || domain[0] != domain[1]) { - UNREACHABLE(); + m_manager->raise_exception("incorrect arguments passed to array-ext"); return nullptr; } sort * s = domain[0]; unsigned num_parameters = s->get_num_parameters(); if (num_parameters == 0 || i >= num_parameters - 1) { - UNREACHABLE(); + m_manager->raise_exception("incorrect arguments passed to array-ext"); return nullptr; } sort * r = to_sort(s->get_parameter(i).get_ast()); diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index dee55cf98..7f9542fe4 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -2322,6 +2322,14 @@ func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const return d; } +bool ast_manager::is_parametric_function(func_decl* f, func_decl *& g) const { + // is-as-array + // is-map + // is-transitive-closure + return false; +} + + sort * ast_manager::mk_fresh_sort(char const * prefix) { string_buffer<32> buffer; buffer << prefix << "!" << m_fresh_id; @@ -3296,7 +3304,7 @@ proof * ast_manager::mk_redundant_del(expr* e) { return mk_clause_trail_elem(nullptr, e, PR_REDUNDANT_DEL); } -proof * ast_manager::mk_clause_trail(unsigned n, proof* const* ps) { +proof * ast_manager::mk_clause_trail(unsigned n, expr* const* ps) { ptr_buffer args; args.append(n, (expr**) ps); return mk_app(basic_family_id, PR_CLAUSE_TRAIL, 0, nullptr, args.size(), args.data()); diff --git a/src/ast/ast.h b/src/ast/ast.h index 2400e05a6..e0ae7b92f 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -180,13 +180,13 @@ public: */ void del_eh(ast_manager & m, family_id fid); - int get_int() const { return std::get(m_val); } - ast * get_ast() const { return std::get(m_val); } - symbol get_symbol() const { return std::get(m_val); } - rational const & get_rational() const { return *std::get(m_val); } - zstring const& get_zstring() const { return *std::get(m_val); } - double get_double() const { return std::get(m_val); } - unsigned get_ext_id() const { return std::get(m_val); } + int get_int() const { SASSERT(is_int()); return std::get(m_val); } + ast * get_ast() const { SASSERT(is_ast()); return std::get(m_val); } + symbol get_symbol() const { SASSERT(is_symbol()); return std::get(m_val); } + rational const & get_rational() const { SASSERT(is_rational()); return *std::get(m_val); } + zstring const& get_zstring() const { SASSERT(is_zstring()); return *std::get(m_val); } + double get_double() const { SASSERT(is_double()); return std::get(m_val); } + unsigned get_ext_id() const { SASSERT(is_external()); return std::get(m_val); } bool operator==(parameter const & p) const; bool operator!=(parameter const & p) const { return !operator==(p); } @@ -1924,6 +1924,8 @@ public: return mk_fresh_func_decl(symbol(prefix), symbol::null, arity, domain, range, skolem); } + bool is_parametric_function(func_decl* f, func_decl *& g) const; + app * mk_fresh_const(char const * prefix, sort * s, bool skolem = true) { return mk_const(mk_fresh_func_decl(prefix, 0, nullptr, s, skolem)); } @@ -2335,7 +2337,7 @@ public: proof * mk_th_assumption_add(proof* pr, expr* e); proof * mk_th_lemma_add(proof* pr, expr* e); proof * mk_redundant_del(expr* e); - proof * mk_clause_trail(unsigned n, proof* const* ps); + proof * mk_clause_trail(unsigned n, expr* const* ps); proof * mk_def_axiom(expr * ax); proof * mk_unit_resolution(unsigned num_proofs, proof * const * proofs); diff --git a/src/ast/ast_pp_util.cpp b/src/ast/ast_pp_util.cpp index c0608522f..2b7e72a91 100644 --- a/src/ast/ast_pp_util.cpp +++ b/src/ast/ast_pp_util.cpp @@ -43,11 +43,11 @@ void ast_pp_util::display_decls(std::ostream& out) { for (unsigned i = m_sorts; i < n; ++i) pp.display_sort_decl(out, coll.get_sorts()[i], seen); m_sorts = n; - + n = coll.get_num_decls(); for (unsigned i = m_decls; i < n; ++i) { func_decl* f = coll.get_func_decls()[i]; - if (f->get_family_id() == null_family_id && !m_removed.contains(f)) + if (coll.should_declare(f) && !m_removed.contains(f)) ast_smt2_pp(out, f, m_env) << "\n"; } m_decls = n; @@ -80,7 +80,7 @@ void ast_pp_util::display_skolem_decls(std::ostream& out) { unsigned n = coll.get_num_decls(); for (unsigned i = m_decls; i < n; ++i) { func_decl* f = coll.get_func_decls()[i]; - if (f->get_family_id() == null_family_id && !m_removed.contains(f) && f->is_skolem()) + if (coll.should_declare(f) && !m_removed.contains(f) && f->is_skolem()) ast_smt2_pp(out, f, m_env) << "\n"; } m_decls = n; diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index 74bb871ec..bfa262410 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -121,8 +121,10 @@ format * smt2_pp_environment::pp_fdecl_params(format * fname, func_decl * f) { std::string str = f->get_parameter(i).get_rational().to_string(); fs.push_back(mk_string(get_manager(), str)); } - else - fs.push_back(pp_fdecl_ref(to_func_decl(f->get_parameter(i).get_ast()))); + else { + unsigned len; + fs.push_back(pp_fdecl_name(to_func_decl(f->get_parameter(i).get_ast()), len)); + } } return mk_seq1(get_manager(), fs.begin(), fs.end(), f2f(), "_"); } diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index f40b8a554..bea669438 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -34,6 +34,7 @@ Revision History: #include "ast/for_each_ast.h" #include "ast/decl_collector.h" #include "math/polynomial/algebraic_numbers.h" +#include "ast/pp_params.hpp" // --------------------------------------- @@ -911,7 +912,9 @@ ast_smt_pp::ast_smt_pp(ast_manager& m): void ast_smt_pp::display_expr_smt2(std::ostream& strm, expr* n, unsigned indent, unsigned num_var_names, char const* const* var_names) { ptr_vector ql; smt_renaming rn; - smt_printer p(strm, m_manager, ql, rn, m_logic, false, m_simplify_implies, indent, num_var_names, var_names); + pp_params params; + bool no_lets = params.no_lets(); + smt_printer p(strm, m_manager, ql, rn, m_logic, no_lets, m_simplify_implies, indent, num_var_names, var_names); p(n); } diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index 351d037cb..4d38327a5 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -118,9 +118,22 @@ void bv_decl_plugin::finalize() { DEC_REF(m_bv_redand); DEC_REF(m_bv_comp); + DEC_REF(m_bv_mul_no_ovfl); + DEC_REF(m_bv_smul_no_ovfl); + DEC_REF(m_bv_smul_no_udfl); + DEC_REF(m_bv_mul_ovfl); DEC_REF(m_bv_smul_ovfl); - DEC_REF(m_bv_smul_udfl); + + DEC_REF(m_bv_neg_ovfl); + + DEC_REF(m_bv_uadd_ovfl); + DEC_REF(m_bv_sadd_ovfl); + + DEC_REF(m_bv_usub_ovfl); + DEC_REF(m_bv_ssub_ovfl); + + DEC_REF(m_bv_sdiv_ovfl); DEC_REF(m_bv_shl); DEC_REF(m_bv_lshr); @@ -245,6 +258,16 @@ func_decl * bv_decl_plugin::mk_bv2int(unsigned bv_size, unsigned num_parameters, return m_bv2int[bv_size]; } +func_decl * bv_decl_plugin::mk_unary_pred(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size) { + force_ptr_array_size(decls, bv_size+1); + + if (decls[bv_size] == 0) { + decls[bv_size] = m_manager->mk_func_decl(symbol(name), get_bv_sort(bv_size), m_manager->mk_bool_sort(), func_decl_info(m_family_id, k)); + m_manager->inc_ref(decls[bv_size]); + } + return decls[bv_size]; +} + func_decl * bv_decl_plugin::mk_pred(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size) { force_ptr_array_size(decls, bv_size + 1); @@ -289,6 +312,7 @@ func_decl * bv_decl_plugin::mk_comp(unsigned bv_size) { func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned bv_size) { switch (k) { case OP_BNEG: return mk_unary(m_bv_neg, k, "bvneg", bv_size); + case OP_BNEG_OVFL: return mk_unary_pred(m_bv_neg_ovfl, k, "bvnego", bv_size); case OP_BADD: return mk_binary(m_bv_add, k, "bvadd", bv_size, true); case OP_BSUB: return mk_binary(m_bv_sub, k, "bvsub", bv_size, false); case OP_BMUL: return mk_binary(m_bv_mul, k, "bvmul", bv_size, true); @@ -327,9 +351,16 @@ func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned bv_size) { case OP_BREDOR: return mk_reduction(m_bv_redor, k, "bvredor", bv_size); case OP_BREDAND: return mk_reduction(m_bv_redand, k, "bvredand", bv_size); case OP_BCOMP: return mk_comp(bv_size); - case OP_BUMUL_NO_OVFL: return mk_pred(m_bv_mul_ovfl, k, "bvumul_noovfl", bv_size); - case OP_BSMUL_NO_OVFL: return mk_pred(m_bv_smul_ovfl, k, "bvsmul_noovfl", bv_size); - case OP_BSMUL_NO_UDFL: return mk_pred(m_bv_smul_udfl, k, "bvsmul_noudfl", bv_size); + case OP_BUMUL_NO_OVFL: return mk_pred(m_bv_mul_no_ovfl, k, "bvumul_noovfl", bv_size); + case OP_BSMUL_NO_OVFL: return mk_pred(m_bv_smul_no_ovfl, k, "bvsmul_noovfl", bv_size); + case OP_BSMUL_NO_UDFL: return mk_pred(m_bv_smul_no_udfl, k, "bvsmul_noudfl", bv_size); + case OP_BUMUL_OVFL: return mk_pred(m_bv_mul_ovfl, k, "bvumulo", bv_size); + case OP_BSMUL_OVFL: return mk_pred(m_bv_smul_ovfl, k, "bvsmulo", bv_size); + case OP_BSDIV_OVFL: return mk_pred(m_bv_sdiv_ovfl, k, "bvsdivo", bv_size); + case OP_BUADD_OVFL: return mk_pred(m_bv_uadd_ovfl, k, "bvuaddo", bv_size); + case OP_BSADD_OVFL: return mk_pred(m_bv_sadd_ovfl, k, "bvsaddo", bv_size); + case OP_BUSUB_OVFL: return mk_pred(m_bv_usub_ovfl, k, "bvusubo", bv_size); + case OP_BSSUB_OVFL: return mk_pred(m_bv_ssub_ovfl, k, "bvssubo", bv_size); case OP_BSHL: return mk_binary(m_bv_shl, k, "bvshl", bv_size, false); case OP_BLSHR: return mk_binary(m_bv_lshr, k, "bvlshr", bv_size, false); @@ -681,10 +712,18 @@ void bv_decl_plugin::get_op_names(svector & op_names, symbol const op_names.push_back(builtin_name("bit1",OP_BIT1)); op_names.push_back(builtin_name("bit0",OP_BIT0)); op_names.push_back(builtin_name("bvneg",OP_BNEG)); + op_names.push_back(builtin_name("bvnego", OP_BNEG_OVFL)); op_names.push_back(builtin_name("bvadd",OP_BADD)); + op_names.push_back(builtin_name("bvuaddo",OP_BUADD_OVFL)); + op_names.push_back(builtin_name("bvsaddo",OP_BSADD_OVFL)); op_names.push_back(builtin_name("bvsub",OP_BSUB)); + op_names.push_back(builtin_name("bvusubo",OP_BUSUB_OVFL)); + op_names.push_back(builtin_name("bvssubo",OP_BSSUB_OVFL)); op_names.push_back(builtin_name("bvmul",OP_BMUL)); + op_names.push_back(builtin_name("bvumulo",OP_BUMUL_OVFL)); + op_names.push_back(builtin_name("bvsmulo",OP_BSMUL_OVFL)); op_names.push_back(builtin_name("bvsdiv",OP_BSDIV)); + op_names.push_back(builtin_name("bvsdivo",OP_BSDIV_OVFL)); op_names.push_back(builtin_name("bvudiv",OP_BUDIV)); op_names.push_back(builtin_name("bvsrem",OP_BSREM)); op_names.push_back(builtin_name("bvurem",OP_BUREM)); diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h index fc7e35245..51faca7ed 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -93,6 +93,19 @@ enum bv_op_kind { OP_BSMUL_NO_OVFL, // no signed multiplication overflow predicate OP_BSMUL_NO_UDFL, // no signed multiplication underflow predicate + OP_BUMUL_OVFL, // unsigned multiplication overflow predicate (negation of OP_BUMUL_NO_OVFL) + OP_BSMUL_OVFL, // signed multiplication over/underflow predicate + + OP_BSDIV_OVFL, // signed division overflow perdicate + + OP_BNEG_OVFL, // negation overflow predicate + + OP_BUADD_OVFL, // unsigned addition overflow predicate + OP_BSADD_OVFL, // signed addition overflow predicate + + OP_BUSUB_OVFL, // unsigned subtraction overflow predicate + OP_BSSUB_OVFL, // signed subtraction overflow predicate + OP_BIT2BOOL, // predicate OP_MKBV, // bools to bv OP_INT2BV, @@ -189,9 +202,22 @@ protected: ptr_vector m_bv_redand; ptr_vector m_bv_comp; - ptr_vector m_bv_mul_ovfl; - ptr_vector m_bv_smul_ovfl; - ptr_vector m_bv_smul_udfl; + ptr_vector m_bv_mul_no_ovfl; + ptr_vector m_bv_smul_no_ovfl; + ptr_vector m_bv_smul_no_udfl; + + ptr_vector m_bv_mul_ovfl; + ptr_vector m_bv_smul_ovfl; + + ptr_vector m_bv_sdiv_ovfl; + + ptr_vector m_bv_neg_ovfl; + + ptr_vector m_bv_uadd_ovfl; + ptr_vector m_bv_sadd_ovfl; + + ptr_vector m_bv_usub_ovfl; + ptr_vector m_bv_ssub_ovfl; ptr_vector m_bv_shl; ptr_vector m_bv_lshr; @@ -213,6 +239,7 @@ protected: func_decl * mk_unary(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); func_decl * mk_pred(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); + func_decl * mk_unary_pred(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); func_decl * mk_reduction(ptr_vector & decls, decl_kind k, char const * name, unsigned bv_size); func_decl * mk_comp(unsigned bv_size); bool get_bv_size(sort * t, int & result); @@ -490,9 +517,19 @@ public: app * mk_bv2int(expr* e); + // TODO: all these binary ops commute (right?) but it'd be more logical to swap `n` & `m` in the `return` app * mk_bvsmul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_OVFL, n, m); } app * mk_bvsmul_no_udfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_UDFL, n, m); } app * mk_bvumul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUMUL_NO_OVFL, n, m); } + app * mk_bvsmul_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_OVFL, n, m); } + app * mk_bvumul_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUMUL_OVFL, n, m); } + app * mk_bvsdiv_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSDIV_OVFL, m, n); } + app * mk_bvneg_ovfl(expr* m) { return m_manager.mk_app(get_fid(), OP_BNEG_OVFL, m); } + app * mk_bvuadd_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUADD_OVFL, n, m); } + app * mk_bvsadd_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSADD_OVFL, n, m); } + app * mk_bvusub_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BUSUB_OVFL, m, n); } + app * mk_bvssub_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSSUB_OVFL, m, n); } + app * mk_bit2bool(expr* e, unsigned idx) { parameter p(idx); return m_manager.mk_app(get_fid(), OP_BIT2BOOL, 1, &p, 1, &e); } private: diff --git a/src/ast/converters/expr_inverter.cpp b/src/ast/converters/expr_inverter.cpp index abf3125c6..0ee3e130d 100644 --- a/src/ast/converters/expr_inverter.cpp +++ b/src/ast/converters/expr_inverter.cpp @@ -19,6 +19,7 @@ Author: #include "ast/ast_ll_pp.h" #include "ast/ast_util.h" #include "ast/arith_decl_plugin.h" +#include "ast/seq_decl_plugin.h" #include "ast/converters/expr_inverter.h" class basic_expr_inverter : public iexpr_inverter { @@ -742,6 +743,54 @@ public: }; +class seq_expr_inverter : public iexpr_inverter { + seq_util seq; +public: + seq_expr_inverter(ast_manager& m) : iexpr_inverter(m), seq(m) {} + + family_id get_fid() const override { return seq.get_family_id(); } + + bool operator()(func_decl* f, unsigned num, expr* const* args, expr_ref& r) override { + switch (f->get_decl_kind()) { + case _OP_STRING_CONCAT: + case OP_SEQ_CONCAT: { + expr* x, *y; + + if (uncnstr(args[0]) && num == 2 && + args[1]->get_ref_count() == 1 && + seq.str.is_concat(args[1], x, y) && + uncnstr(x)) { + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) { + add_def(args[0], seq.str.mk_empty(args[0]->get_sort())); + add_def(x, r); + } + r = seq.str.mk_concat(r, y); + return true; + } + + if (!uncnstr(num, args)) + return false; + mk_fresh_uncnstr_var_for(f, r); + if (m_mc) { + add_def(args[0], r); + for (unsigned i = 1; i < num; ++i) + add_def(args[i], seq.str.mk_empty(args[0]->get_sort())); + } + return true; + } + default: + return false; + + } + } + + bool mk_diff(expr* t, expr_ref& r) override { + return false; + } + +}; + expr_inverter::~expr_inverter() { for (auto* v : m_inverters) @@ -796,6 +845,7 @@ expr_inverter::expr_inverter(ast_manager& m): iexpr_inverter(m) { add(alloc(array_expr_inverter, m, *this)); add(alloc(dt_expr_inverter, m)); add(alloc(basic_expr_inverter, m, *this)); + add(alloc(seq_expr_inverter, m)); } diff --git a/src/ast/decl_collector.cpp b/src/ast/decl_collector.cpp index 7786a79d4..5619f546b 100644 --- a/src/ast/decl_collector.cpp +++ b/src/ast/decl_collector.cpp @@ -24,7 +24,7 @@ Revision History: void decl_collector::visit_sort(sort * n) { SASSERT(!m_visited.is_marked(n)); family_id fid = n->get_family_id(); - if (m().is_uninterp(n)) + if (m.is_uninterp(n)) m_sorts.push_back(n); else if (fid == m_dt_fid) { m_sorts.push_back(n); @@ -43,7 +43,7 @@ void decl_collector::visit_sort(sort * n) { } bool decl_collector::is_bool(sort * s) { - return m().is_bool(s); + return m.is_bool(s); } void decl_collector::visit_func(func_decl * n) { @@ -51,10 +51,10 @@ void decl_collector::visit_func(func_decl * n) { if (!m_visited.is_marked(n)) { family_id fid = n->get_family_id(); - if (fid == null_family_id) + if (should_declare(n)) m_decls.push_back(n); else if (fid == m_rec_fid) { - recfun::util u(m()); + recfun::util u(m); if (u.has_def(n)) { m_rec_decls.push_back(n); m_todo.push_back(u.get_def(n).get_rhs()); @@ -69,12 +69,17 @@ void decl_collector::visit_func(func_decl * n) { } } +bool decl_collector::should_declare(func_decl* f) const { + return f->get_family_id() == null_family_id || m.is_model_value(f); +} + + decl_collector::decl_collector(ast_manager & m): - m_manager(m), + m(m), m_trail(m), m_dt_util(m), m_ar_util(m) { - m_basic_fid = m_manager.get_basic_family_id(); + m_basic_fid = m.get_basic_family_id(); m_dt_fid = m_dt_util.get_family_id(); recfun::util rec_util(m); m_rec_fid = rec_util.get_family_id(); @@ -83,7 +88,7 @@ decl_collector::decl_collector(ast_manager & m): void decl_collector::visit(ast* n) { if (m_visited.is_marked(n)) return; - datatype_util util(m()); + datatype_util util(m); m_todo.push_back(n); while (!m_todo.empty()) { n = m_todo.back(); diff --git a/src/ast/decl_collector.h b/src/ast/decl_collector.h index 31cabe796..a7f79d008 100644 --- a/src/ast/decl_collector.h +++ b/src/ast/decl_collector.h @@ -26,7 +26,7 @@ Revision History: #include "ast/array_decl_plugin.h" class decl_collector { - ast_manager & m_manager; + ast_manager & m; lim_svector m_sorts; lim_svector m_decls; lim_svector m_rec_decls; @@ -48,10 +48,10 @@ class decl_collector { void collect_deps(top_sort& st); void collect_deps(sort* s, sort_set& set); - public: decl_collector(ast_manager & m); - ast_manager & m() { return m_manager; } + + bool should_declare(func_decl* f) const; void reset() { m_sorts.reset(); m_decls.reset(); m_visited.reset(); m_trail.reset(); } void visit_func(func_decl* n); diff --git a/src/ast/display_dimacs.cpp b/src/ast/display_dimacs.cpp index 6b2ef6658..9440987ea 100644 --- a/src/ast/display_dimacs.cpp +++ b/src/ast/display_dimacs.cpp @@ -47,6 +47,8 @@ struct dimacs_pp { } for (unsigned j = 0; j < num_lits; j++) { expr * l = lits[j]; + if (m.is_false(l)) + continue; if (m.is_not(l)) l = to_app(l)->get_arg(0); if (!is_uninterp_const(l)) @@ -101,6 +103,12 @@ struct dimacs_pp { } for (unsigned j = 0; j < num_lits; j++) { expr * l = lits[j]; + if (m.is_false(l)) + continue; + if (m.is_true(l)) { + out << "1 -1 "; + continue; + } if (m.is_not(l)) { out << "-"; l = to_app(l)->get_arg(0); diff --git a/src/ast/euf/euf_egraph.cpp b/src/ast/euf/euf_egraph.cpp index 7939c23f2..ac60a98ba 100644 --- a/src/ast/euf/euf_egraph.cpp +++ b/src/ast/euf/euf_egraph.cpp @@ -67,6 +67,8 @@ namespace euf { } enode_bool_pair egraph::insert_table(enode* p) { + TRACE("euf", tout << bpp(p) << "\n"); + //SASSERT(!m_table.contains_ptr(p)); auto rc = m_table.insert(p); p->m_cg = rc.first; return rc; @@ -280,6 +282,7 @@ namespace euf { if (!m.is_bool(n->get_sort())) return; if (enable_merge_tf != n->merge_tf()) { + TRACE("euf", tout << "set tf " << enable_merge_tf << " " << bpp(n) << "\n"); n->set_merge_tf(enable_merge_tf); m_updates.push_back(update_record(n, update_record::toggle_merge_tf())); } @@ -487,6 +490,7 @@ namespace euf { } void egraph::remove_parents(enode* r) { + TRACE("euf", tout << bpp(r) << "\n"); for (enode* p : enode_parents(r)) { if (p->is_marked1()) continue; @@ -496,6 +500,7 @@ namespace euf { SASSERT(m_table.contains_ptr(p)); p->mark1(); erase_from_table(p); + CTRACE("euf", m_table.contains_ptr(p), tout << bpp(p) << "\n"; display(tout)); SASSERT(!m_table.contains_ptr(p)); } else if (p->is_equality()) diff --git a/src/ast/for_each_expr.cpp b/src/ast/for_each_expr.cpp index 1e7b6da3b..832c1d0bc 100644 --- a/src/ast/for_each_expr.cpp +++ b/src/ast/for_each_expr.cpp @@ -44,6 +44,49 @@ unsigned get_num_exprs(expr * n) { return get_num_exprs(n, visited); } + +void get_num_internal_exprs(unsigned_vector& counts, ptr_vector& todo, expr * n) { + counts.reserve(n->get_id() + 1); + unsigned& rc = counts[n->get_id()]; + if (rc > 0) { + --rc; + return; + } + rc = n->get_ref_count() - 1; + unsigned i = todo.size(); + todo.push_back(n); + for (; i < todo.size(); ++i) { + n = todo[i]; + if (!is_app(n)) + continue; + for (expr* arg : *to_app(n)) { + unsigned id = arg->get_id(); + counts.reserve(id + 1); + unsigned& rc = counts[id]; + if (rc > 0) { + --rc; + continue; + } + rc = arg->get_ref_count() - 1; + todo.push_back(arg); + } + } +} + +unsigned count_internal_nodes(unsigned_vector& counts, ptr_vector& todo) { + unsigned internal_nodes = 0; + for (expr* t : todo) { + if (counts[t->get_id()] == 0) + ++internal_nodes; + else + counts[t->get_id()] = 0; + } + todo.reset(); + return internal_nodes; + +} + + namespace has_skolem_functions_ns { struct found {}; struct proc { diff --git a/src/ast/for_each_expr.h b/src/ast/for_each_expr.h index 2d5ed05ae..0ba0dc992 100644 --- a/src/ast/for_each_expr.h +++ b/src/ast/for_each_expr.h @@ -163,10 +163,13 @@ struct for_each_expr_proc : public EscapeProc { unsigned get_num_exprs(expr * n); unsigned get_num_exprs(expr * n, expr_mark & visited); unsigned get_num_exprs(expr * n, expr_fast_mark1 & visited); +void get_num_internal_exprs(unsigned_vector& counts, ptr_vector& todo, expr * n); +unsigned count_internal_nodes(unsigned_vector& counts, ptr_vector& todo); bool has_skolem_functions(expr * n); // pre-order traversal of subterms + class subterms { bool m_include_bound = false; expr_ref_vector m_es; diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index a35956454..b04445d16 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -149,7 +149,7 @@ class skolemizer { p = nullptr; if (m_proofs_enabled) { if (q->get_kind() == forall_k) - p = m.mk_skolemization(mk_not(m, q), mk_not(m, r)); + p = m.mk_skolemization(mk_not(m, q), m.mk_not(r)); else p = m.mk_skolemization(q, r); } diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp index d751a1388..2fd2b4c82 100644 --- a/src/ast/pattern/pattern_inference.cpp +++ b/src/ast/pattern/pattern_inference.cpp @@ -405,6 +405,44 @@ bool pattern_inference_cfg::pattern_weight_lt::operator()(expr * n1, expr * n2) return num_free_vars1 > num_free_vars2 || (num_free_vars1 == num_free_vars2 && i1.m_size < i2.m_size); } + +app* pattern_inference_cfg::mk_pattern(app* candidate) { + auto has_var_arg = [&](expr* e) { + if (!is_app(e)) + return false; + for (expr* arg : *to_app(e)) + if (is_var(arg)) + return true; + return false; + }; + if (has_var_arg(candidate)) + return m.mk_pattern(candidate); + m_args.reset(); + for (expr* arg : *candidate) { + if (!is_app(arg)) + return m.mk_pattern(candidate); + m_args.push_back(to_app(arg)); + } + for (unsigned i = 0; i < m_args.size(); ++i) { + app* arg = m_args[i]; + if (has_var_arg(arg)) + continue; + m_args[i] = m_args.back(); + --i; + m_args.pop_back(); + + if (is_ground(arg)) + continue; + + for (expr* e : *to_app(arg)) { + if (!is_app(e)) + return m.mk_pattern(candidate); + m_args.push_back(to_app(e)); + } + } + return m.mk_pattern(m_args.size(), m_args.data()); +} + /** \brief Create unary patterns (single expressions that contain all bound variables). If a candidate does not contain all bound @@ -418,7 +456,7 @@ void pattern_inference_cfg::candidates2unary_patterns(ptr_vector const & ca expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate); info const & i = e->get_data().m_value; if (i.m_free_vars.num_elems() == m_num_bindings) { - app * new_pattern = m.mk_pattern(candidate); + app * new_pattern = mk_pattern(candidate); result.push_back(new_pattern); } else { diff --git a/src/ast/pattern/pattern_inference.h b/src/ast/pattern/pattern_inference.h index bb4cf4238..da905dca4 100644 --- a/src/ast/pattern/pattern_inference.h +++ b/src/ast/pattern/pattern_inference.h @@ -188,6 +188,9 @@ class pattern_inference_cfg : public default_rewriter_cfg { ptr_vector m_pre_patterns; expr_pattern_match m_database; + ptr_buffer m_args; + app* mk_pattern(app* candidate); + void candidates2unary_patterns(ptr_vector const & candidate_patterns, ptr_vector & remaining_candidate_patterns, app_ref_buffer & result); diff --git a/src/ast/pp_params.pyg b/src/ast/pp_params.pyg index 6b43cbea3..c833b624a 100644 --- a/src/ast/pp_params.pyg +++ b/src/ast/pp_params.pyg @@ -6,6 +6,7 @@ def_module_params('pp', ('max_width', UINT, 80, 'max. width in pretty printer'), ('max_ribbon', UINT, 80, 'max. ribbon (width - indentation) in pretty printer'), ('max_depth', UINT, 5, 'max. term depth (when pretty printing SMT2 terms/formulas)'), + ('no_lets', BOOL, False, 'dont print lets in low level SMT printer'), ('min_alias_size', UINT, 10, 'min. size for creating an alias for a shared term (when pretty printing SMT2 terms/formulas)'), ('decimal', BOOL, False, 'pretty print real numbers using decimal notation (the output may be truncated). Z3 adds a ? if the value is not precise'), ('decimal_precision', UINT, 10, 'maximum number of decimal places to be used when pp.decimal=true'), diff --git a/src/ast/recfun_decl_plugin.cpp b/src/ast/recfun_decl_plugin.cpp index 84d68d782..7a3e9521d 100644 --- a/src/ast/recfun_decl_plugin.cpp +++ b/src/ast/recfun_decl_plugin.cpp @@ -492,7 +492,7 @@ namespace recfun { def* plugin::mk_def(replace& subst, bool is_macro, symbol const& name, unsigned n, sort ** params, sort * range, unsigned n_vars, var ** vars, expr * rhs) { - promise_def d = mk_def(name, n, params, range); + promise_def d = mk_def(name, n, params, range, false); SASSERT(! m_defs.contains(d.get_def()->get_decl())); set_definition(subst, d, is_macro, n_vars, vars, rhs); return d.get_def(); @@ -581,7 +581,7 @@ namespace recfun { } symbol fresh_name("fold-rec-" + std::to_string(m().mk_fresh_id())); - auto pd = mk_def(fresh_name, n, domain.data(), max_expr->get_sort()); + auto pd = mk_def(fresh_name, n, domain.data(), max_expr->get_sort(), false); func_decl* f = pd.get_def()->get_decl(); expr_ref new_body(m().mk_app(f, n, args.data()), m()); set_definition(subst, pd, false, n, vars, max_expr); diff --git a/src/ast/recfun_decl_plugin.h b/src/ast/recfun_decl_plugin.h index 66544bec7..442bdadbd 100644 --- a/src/ast/recfun_decl_plugin.h +++ b/src/ast/recfun_decl_plugin.h @@ -192,9 +192,9 @@ namespace recfun { void get_op_names(svector & op_names, symbol const & logic) override; - promise_def mk_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated = false); + promise_def mk_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated); - promise_def ensure_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated = false); + promise_def ensure_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated); void set_definition(replace& r, promise_def & d, bool is_macro, unsigned n_vars, var * const * vars, expr * rhs); @@ -248,6 +248,7 @@ namespace recfun { bool is_defined(expr * e) const { return is_app_of(e, m_fid, OP_FUN_DEFINED); } bool is_defined(func_decl* f) const { return is_decl_of(f, m_fid, OP_FUN_DEFINED); } bool is_generated(func_decl* f) const { return is_defined(f) && f->get_parameter(0).get_int() == 1; } + bool is_macro(func_decl* f) { return is_defined(f) && get_def(f).is_macro(); } bool is_num_rounds(expr * e) const { return is_app_of(e, m_fid, OP_NUM_ROUNDS); } bool owns_app(app * e) const { return e->get_family_id() == m_fid; } diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index 7cade3bfa..ed36562ca 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -16,6 +16,7 @@ Author: Notes: --*/ + #include "params/arith_rewriter_params.hpp" #include "ast/rewriter/arith_rewriter.h" #include "ast/rewriter/poly_rewriter_def.h" @@ -1046,19 +1047,21 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu set_curr_sort(arg1->get_sort()); numeral v1, v2; bool is_int; - if (m_util.is_numeral(arg1, v1, is_int) && m_util.is_numeral(arg2, v2, is_int) && !v2.is_zero()) { + bool is_num1 = m_util.is_numeral(arg1, v1, is_int); + bool is_num2 = m_util.is_numeral(arg2, v2, is_int); + if (is_num1 && is_num2 && !v2.is_zero()) { result = m_util.mk_numeral(div(v1, v2), is_int); return BR_DONE; } - if (m_util.is_numeral(arg2, v2, is_int) && v2.is_one()) { + if (is_num2 && v2.is_one()) { result = arg1; return BR_DONE; } - if (m_util.is_numeral(arg2, v2, is_int) && v2.is_minus_one()) { + if (is_num2 && v2.is_minus_one()) { result = m_util.mk_mul(m_util.mk_int(-1), arg1); return BR_REWRITE1; } - if (m_util.is_numeral(arg2, v2, is_int) && v2.is_zero()) { + if (is_num2 && v2.is_zero()) { return BR_FAILED; } if (arg1 == arg2) { @@ -1066,7 +1069,7 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu result = m.mk_ite(m.mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1)); return BR_REWRITE3; } - if (m_util.is_numeral(arg2, v2, is_int) && v2.is_pos() && m_util.is_add(arg1)) { + if (is_num2 && v2.is_pos() && m_util.is_add(arg1)) { expr_ref_buffer args(m); bool change = false; rational add(0); @@ -1092,7 +1095,14 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu expr_ref zero(m_util.mk_int(0), m); result = m.mk_ite(m.mk_eq(zero, arg2), m_util.mk_idiv(arg1, zero), result); return BR_REWRITE_FULL; - } + } +#if 0 + expr* x = nullptr, *y = nullptr, *z = nullptr; + if (is_num2 && m_util.is_idiv(arg1, x, y) && m_util.is_numeral(y, v1) && v1 > 0 && v2 > 0) { + result = m_util.mk_idiv(x, m_util.mk_numeral(v1*v2, is_int)); + return BR_DONE; + } +#endif return BR_FAILED; } diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 392b2e681..95c0950d8 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -20,6 +20,7 @@ Notes: #include "params/bool_rewriter_params.hpp" #include "ast/rewriter/rewriter_def.h" #include "ast/ast_lt.h" +#include "ast/for_each_expr.h" #include void bool_rewriter::updt_params(params_ref const & _p) { @@ -32,6 +33,7 @@ void bool_rewriter::updt_params(params_ref const & _p) { m_blast_distinct = p.blast_distinct(); m_blast_distinct_threshold = p.blast_distinct_threshold(); m_ite_extra_rules = p.ite_extra_rules(); + m_hoist.set_elim_and(m_elim_and); } void bool_rewriter::get_param_descrs(param_descrs & r) { @@ -269,13 +271,26 @@ br_status bool_rewriter::mk_nflat_or_core(unsigned num_args, expr * const * args #if 1 br_status st; - st = m_hoist.mk_or(buffer.size(), buffer.data(), result); + expr_ref r(m()); + st = m_hoist.mk_or(buffer.size(), buffer.data(), r); + if (st != BR_FAILED) { + m_counts1.reserve(m().get_num_asts() + 1); + m_counts2.reserve(m().get_num_asts() + 1); + get_num_internal_exprs(m_counts1, m_todo1, r); + for (unsigned i = 0; i < num_args; ++i) + get_num_internal_exprs(m_counts2, m_todo2, args[i]); + unsigned count1 = count_internal_nodes(m_counts1, m_todo1); + unsigned count2 = count_internal_nodes(m_counts2, m_todo2); + if (count1 > count2) + st = BR_FAILED; + } + if (st != BR_FAILED) + result = r; if (st == BR_DONE) return BR_REWRITE1; if (st != BR_FAILED) return st; #endif - if (s) { ast_lt lt; std::sort(buffer.begin(), buffer.end(), lt); diff --git a/src/ast/rewriter/bool_rewriter.h b/src/ast/rewriter/bool_rewriter.h index 8f2221a8c..0693e94ba 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -62,6 +62,8 @@ class bool_rewriter { unsigned m_local_ctx_limit; unsigned m_local_ctx_cost; bool m_elim_ite; + ptr_vector m_todo1, m_todo2; + unsigned_vector m_counts1, m_counts2; br_status mk_flat_and_core(unsigned num_args, expr * const * args, expr_ref & result); br_status mk_flat_or_core(unsigned num_args, expr * const * args, expr_ref & result); @@ -81,7 +83,9 @@ class bool_rewriter { void push_new_arg(expr* arg, expr_ref_vector& new_args, expr_fast_mark1& neg_lits, expr_fast_mark2& pos_lits); public: - bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_hoist(m), m_local_ctx_cost(0) { updt_params(p); } + bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_hoist(m), m_local_ctx_cost(0) { + updt_params(p); + } ast_manager & m() const { return m_manager; } family_id get_fid() const { return m().get_basic_family_id(); } bool is_eq(expr * t) const { return m().is_eq(t); } diff --git a/src/ast/rewriter/bv_bounds_base.h b/src/ast/rewriter/bv_bounds_base.h index 840cf6e3e..8542e5921 100644 --- a/src/ast/rewriter/bv_bounds_base.h +++ b/src/ast/rewriter/bv_bounds_base.h @@ -185,7 +185,7 @@ namespace bv { m_args.push_back(arg); continue; } - if (!m_bv.is_extract(arg) && m_bound.find(arg, b)) { + if (!m_bv.is_extract(arg) && m_bound.find(arg, b) && b.lo() <= b.hi()) { unsigned num_bits = b.hi().get_num_bits(); unsigned bv_size = m_bv.get_bv_size(arg); if (0 < num_bits && num_bits < bv_size) { @@ -202,6 +202,7 @@ namespace bv { if (simplified) { result = m.mk_app(to_app(t)->get_decl(), m_args); + TRACE("bv", tout << mk_pp(t, m) << " -> " << result << "\n"); return true; } diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 6f1479195..82bbefce3 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -94,6 +94,10 @@ br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons case OP_BNEG: SASSERT(num_args == 1); return mk_uminus(args[0], result); + case OP_BNEG_OVFL: + SASSERT(num_args == 1); + return mk_bvneg_overflow(args[0], result); + case OP_BSHL: SASSERT(num_args == 2); return mk_bv_shl(args[0], args[1], result); @@ -200,6 +204,20 @@ br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons return mk_bvsmul_no_overflow(num_args, args, false, result); case OP_BUMUL_NO_OVFL: return mk_bvumul_no_overflow(num_args, args, result); + case OP_BSMUL_OVFL: + return mk_bvsmul_overflow(num_args, args, result); + case OP_BUMUL_OVFL: + return mk_bvumul_overflow(num_args, args, result); + case OP_BSDIV_OVFL: + return mk_bvsdiv_overflow(num_args, args, result); + case OP_BUADD_OVFL: + return mk_bvuadd_overflow(num_args, args, result); + case OP_BSADD_OVFL: + return mk_bvsadd_over_underflow(num_args, args, result); + case OP_BUSUB_OVFL: + return mk_bvusub_underflow(num_args, args, result); + case OP_BSSUB_OVFL: + return mk_bvssub_overflow(num_args, args, result); default: return BR_FAILED; } @@ -1555,7 +1573,7 @@ br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_re if (eq_args) { if (m.is_ite(new_args.back(), x, y, z)) { ptr_buffer args1, args2; - for (expr* arg : new_args) + for (unsigned i = 0; i < new_args.size(); ++i) args1.push_back(y), args2.push_back(z); result = m.mk_ite(x, m_util.mk_concat(args1), m_util.mk_concat(args2)); return BR_REWRITE2; @@ -2925,6 +2943,21 @@ br_status bv_rewriter::mk_distinct(unsigned num_args, expr * const * args, expr_ return BR_DONE; } +br_status bv_rewriter::mk_bvsmul_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + result = m.mk_or( + m.mk_not(m_util.mk_bvsmul_no_ovfl(args[0], args[1])), + m.mk_not(m_util.mk_bvsmul_no_udfl(args[0], args[1])) + ); + return BR_REWRITE_FULL; +} + +br_status bv_rewriter::mk_bvumul_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + result = m.mk_not(m_util.mk_bvumul_no_ovfl(args[0], args[1])); + return BR_REWRITE2; +} + br_status bv_rewriter::mk_bvsmul_no_overflow(unsigned num, expr * const * args, bool is_overflow, expr_ref & result) { SASSERT(num == 2); unsigned bv_sz; @@ -2984,5 +3017,95 @@ br_status bv_rewriter::mk_bvumul_no_overflow(unsigned num, expr * const * args, return BR_FAILED; } +br_status bv_rewriter::mk_bvneg_overflow(expr * const arg, expr_ref & result) { + unsigned int sz = get_bv_size(arg); + auto maxUnsigned = mk_numeral(rational::power_of_two(sz)-1, sz); + result = m.mk_eq(arg, maxUnsigned); + return BR_REWRITE3; +} + +br_status bv_rewriter::mk_bvuadd_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + unsigned sz = get_bv_size(args[0]); + auto a1 = mk_zero_extend(1, args[0]); + auto a2 = mk_zero_extend(1, args[1]); + auto r = mk_bv_add(a1, a2); + auto extract = m_mk_extract(sz, sz, r); + result = m.mk_eq(extract, mk_one(1)); + return BR_REWRITE_FULL; +} + +br_status bv_rewriter::mk_bvsadd_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + unsigned sz = get_bv_size(args[0]); + auto zero = mk_zero(sz); + auto r = mk_bv_add(args[0], args[1]); + auto l1 = m_util.mk_slt(zero, args[0]); + auto l2 = m_util.mk_slt(zero, args[1]); + auto args_pos = m.mk_and(l1, l2); + auto non_pos_sum = m_util.mk_sle(r, zero); + result = m.mk_and(args_pos, non_pos_sum); + return BR_REWRITE_FULL; +} + +br_status bv_rewriter::mk_bvsadd_underflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + unsigned sz = get_bv_size(args[0]); + auto zero = mk_zero(sz); + auto r = mk_bv_add(args[0], args[1]); + auto l1 = m_util.mk_slt(args[0], zero); + auto l2 = m_util.mk_slt(args[1], zero); + auto args_neg = m.mk_and(l1, l2); + expr_ref non_neg_sum{m}; + auto res_rewrite = mk_sge(r, zero, non_neg_sum); + SASSERT(res_rewrite != BR_FAILED); (void)res_rewrite; + result = m.mk_and(args_neg, non_neg_sum); + return BR_REWRITE_FULL; +} + +br_status bv_rewriter::mk_bvsadd_over_underflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + expr_ref l1{m}; + expr_ref l2{m}; + (void)mk_bvsadd_overflow(2, args, l1); + (void)mk_bvsadd_underflow(2, args, l2); + result = m.mk_or(l1, l2); + return BR_REWRITE_FULL; +} + +br_status bv_rewriter::mk_bvusub_underflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + br_status status = mk_ult(args[0], args[1], result); + SASSERT(status != BR_FAILED); + return status; +} + +br_status bv_rewriter::mk_bvssub_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + auto sz = get_bv_size(args[0]); + auto minSigned = mk_numeral(-rational::power_of_two(sz-1), sz); + expr_ref bvsaddo {m}; + expr * args2[2] = { args[0], m_util.mk_bv_neg(args[1]) }; + auto bvsaddo_stat = mk_bvsadd_overflow(2, args2, bvsaddo); + SASSERT(bvsaddo_stat != BR_FAILED); (void)bvsaddo_stat; + auto first_arg_ge_zero = m_util.mk_sle(mk_zero(sz), args[0]); + result = m.mk_ite(m.mk_eq(args[1], minSigned), first_arg_ge_zero, bvsaddo); + return BR_REWRITE_FULL; +} +br_status bv_rewriter::mk_bvsdiv_overflow(unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 2); + SASSERT(get_bv_size(args[0]) == get_bv_size(args[1])); + auto sz = get_bv_size(args[1]); + auto minSigned = mk_numeral(-rational::power_of_two(sz-1), sz); + auto minusOne = mk_numeral(rational::power_of_two(sz) - 1, sz); + result = m.mk_and(m.mk_eq(args[0], minSigned), m.mk_eq(args[1], minusOne)); + return BR_REWRITE_FULL; +} template class poly_rewriter; diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h index 25bd65b61..09e7996c2 100644 --- a/src/ast/rewriter/bv_rewriter.h +++ b/src/ast/rewriter/bv_rewriter.h @@ -139,6 +139,22 @@ class bv_rewriter : public poly_rewriter { br_status mk_mkbv(unsigned num, expr * const * args, expr_ref & result); br_status mk_bvsmul_no_overflow(unsigned num, expr * const * args, bool is_overflow, expr_ref & result); br_status mk_bvumul_no_overflow(unsigned num, expr * const * args, expr_ref & result); + + br_status mk_bvsmul_overflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvumul_overflow(unsigned num, expr * const * args, expr_ref & result); + + br_status mk_bvsdiv_overflow(unsigned num, expr * const * args, expr_ref & result); + + br_status mk_bvneg_overflow(expr * const arg, expr_ref & result); + + br_status mk_bvuadd_overflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvsadd_overflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvsadd_underflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvsadd_over_underflow(unsigned num, expr * const * args, expr_ref & result); + + br_status mk_bvusub_underflow(unsigned num, expr * const * args, expr_ref & result); + br_status mk_bvssub_overflow(unsigned num, expr * const * args, expr_ref & result); + bool is_minus_one_times_t(expr * arg); void mk_t1_add_t2_eq_c(expr * t1, expr * t2, expr * c, expr_ref & result); diff --git a/src/ast/rewriter/der.cpp b/src/ast/rewriter/der.cpp index fd79529f9..0e28cf6f6 100644 --- a/src/ast/rewriter/der.cpp +++ b/src/ast/rewriter/der.cpp @@ -197,9 +197,10 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) { m_pos2var.reserve(num_args, -1); - // Find all disequalities + // Find all equalities/disequalities for (unsigned i = 0; i < num_args; i++) { - is_eq = is_forall(q) ? is_var_diseq(literals.get(i), num_decls, v, t) : is_var_eq(literals.get(i), num_decls, v, t); + expr* arg = literals.get(i); + is_eq = is_forall(q) ? is_var_diseq(arg, num_decls, v, t) : is_var_eq(arg, num_decls, v, t); if (is_eq) { unsigned idx = v->get_idx(); if (m_map.get(idx, nullptr) == nullptr) { diff --git a/src/ast/rewriter/elim_bounds.cpp b/src/ast/rewriter/elim_bounds.cpp index de23537ad..27c34463d 100644 --- a/src/ast/rewriter/elim_bounds.cpp +++ b/src/ast/rewriter/elim_bounds.cpp @@ -22,6 +22,7 @@ Revision History: #include "ast/used_vars.h" #include "util/obj_hashtable.h" +#include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/var_subst.h" #include "ast/rewriter/elim_bounds.h" #include "ast/ast_pp.h" diff --git a/src/ast/rewriter/hoist_rewriter.cpp b/src/ast/rewriter/hoist_rewriter.cpp index 40ad4604a..72a764bfa 100644 --- a/src/ast/rewriter/hoist_rewriter.cpp +++ b/src/ast/rewriter/hoist_rewriter.cpp @@ -17,16 +17,36 @@ Author: #include "ast/rewriter/hoist_rewriter.h" +#include "ast/rewriter/bool_rewriter.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" - hoist_rewriter::hoist_rewriter(ast_manager & m, params_ref const & p): - m(m), m_args1(m), m_args2(m), m_subst(m) { + m(m), m_args1(m), m_args2(m), m_refs(m), m_subst(m) { updt_params(p); } +expr_ref hoist_rewriter::mk_and(expr_ref_vector const& args) { + if (m_elim_and) { + expr_ref_vector negs(m); + for (expr* a : args) + if (m.is_false(a)) + return expr_ref(m.mk_false(), m); + else if (m.is_true(a)) + continue; + else + negs.push_back(::mk_not(m, a)); + return ::mk_not(mk_or(negs)); + } + else + return ::mk_and(args); +} + +expr_ref hoist_rewriter::mk_or(expr_ref_vector const& args) { + return ::mk_or(args); +} + br_status hoist_rewriter::mk_or(unsigned num_args, expr * const * es, expr_ref & result) { if (num_args < 2) return BR_FAILED; @@ -144,28 +164,26 @@ unsigned hoist_rewriter::mk_var(expr* e) { } expr_ref hoist_rewriter::hoist_predicates(obj_hashtable const& preds, unsigned num_args, expr* const* es) { - expr_ref result(m); - expr_ref_vector args(m), fmls(m); + expr_ref_vector args(m), args1(m), fmls(m); for (unsigned i = 0; i < num_args; ++i) { - VERIFY(is_and(es[i], &m_args1)); + VERIFY(is_and(es[i], &args1)); fmls.reset(); - for (expr* e : m_args1) + for (expr* e : args1) if (!preds.contains(e)) fmls.push_back(e); - args.push_back(::mk_and(fmls)); + args.push_back(mk_and(fmls)); } fmls.reset(); - fmls.push_back(::mk_or(args)); + fmls.push_back(mk_or(args)); for (auto* p : preds) fmls.push_back(p); - result = ::mk_and(fmls); - return result; + return mk_and(fmls); } br_status hoist_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { switch (f->get_decl_kind()) { - case OP_OR: + case OP_OR: return mk_or(num_args, args, result); default: return BR_FAILED; @@ -173,6 +191,33 @@ br_status hoist_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * c } bool hoist_rewriter::is_and(expr * e, expr_ref_vector* args) { +#if 0 + if (!args) + return m.is_and(e) || (m.is_not(e, e) && m.is_or(e)); + expr_fast_mark1 visited; + args->reset(); + args->push_back(e); + m_refs.reset(); + for (unsigned i = 0; i < args->size(); ++i) { + e = args->get(i); + if (visited.is_marked(e)) + goto drop; + m_refs.push_back(e); + visited.mark(e, true); + if (m.is_and(e)) + args->append(to_app(e)->get_num_args(), to_app(e)->get_args()); + else if (m.is_not(e, e) && m.is_or(e)) + for (expr* arg : *to_app(e)) + args->push_back(::mk_not(m, arg)); + else + continue; + drop: + (*args)[i] = args->back(); + args->pop_back(); + --i; + } + return args->size() > 1; +#else if (m.is_and(e)) { if (args) { args->reset(); @@ -185,9 +230,11 @@ bool hoist_rewriter::is_and(expr * e, expr_ref_vector* args) { args->reset(); for (expr* arg : *to_app(e)) args->push_back(::mk_not(m, arg)); + TRACE("hoist", tout << args << " " << * args << "\n"); } return true; } +#endif return false; } diff --git a/src/ast/rewriter/hoist_rewriter.h b/src/ast/rewriter/hoist_rewriter.h index cc83bfa56..b64325584 100644 --- a/src/ast/rewriter/hoist_rewriter.h +++ b/src/ast/rewriter/hoist_rewriter.h @@ -25,9 +25,11 @@ Notes: #include "util/union_find.h" #include "util/obj_hashtable.h" +class bool_rewriter; + class hoist_rewriter { ast_manager & m; - expr_ref_vector m_args1, m_args2; + expr_ref_vector m_args1, m_args2, m_refs; obj_hashtable m_preds1, m_preds2; basic_union_find m_uf1, m_uf2, m_uf0; ptr_vector m_es; @@ -37,8 +39,11 @@ class hoist_rewriter { obj_map m_expr2var; ptr_vector m_var2expr; expr_mark m_mark; + bool m_elim_and = false; bool is_and(expr* e, expr_ref_vector* args); + expr_ref mk_and(expr_ref_vector const& args); + expr_ref mk_or(expr_ref_vector const& args); bool is_var(expr* e) { return m_expr2var.contains(e); } expr* mk_expr(unsigned v) { return m_var2expr[v]; } @@ -48,6 +53,7 @@ class hoist_rewriter { expr_ref hoist_predicates(obj_hashtable const& p, unsigned num_args, expr* const* args); + public: hoist_rewriter(ast_manager & m, params_ref const & p = params_ref()); family_id get_fid() const { return m.get_basic_family_id(); } @@ -56,6 +62,7 @@ public: static void get_param_descrs(param_descrs & r) {} br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result); br_status mk_or(unsigned num_args, expr * const * args, expr_ref & result); + void set_elim_and(bool b) { m_elim_and = b; } }; struct hoist_rewriter_cfg : public default_rewriter_cfg { diff --git a/src/ast/rewriter/maximize_ac_sharing.cpp b/src/ast/rewriter/maximize_ac_sharing.cpp index a8bee62d5..b5ebb9f1c 100644 --- a/src/ast/rewriter/maximize_ac_sharing.cpp +++ b/src/ast/rewriter/maximize_ac_sharing.cpp @@ -92,7 +92,7 @@ br_status maximize_ac_sharing::reduce_app(func_decl * f, unsigned num_args, expr else { result = m.mk_app(f, numeral, _args[0]); } - TRACE("ac_sharing_detail", tout << "result: " << mk_pp(result, m) << "\n";); + TRACE("ac_sharing_detail", tout << "result: " << result << "\n";); return BR_DONE; } } diff --git a/src/ast/rewriter/pb2bv_rewriter.cpp b/src/ast/rewriter/pb2bv_rewriter.cpp index d3dd8ae76..98c7a4c5e 100644 --- a/src/ast/rewriter/pb2bv_rewriter.cpp +++ b/src/ast/rewriter/pb2bv_rewriter.cpp @@ -825,16 +825,17 @@ struct pb2bv_rewriter::imp { if (a->get_family_id() == au.get_family_id()) { switch (a->get_decl_kind()) { case OP_ADD: - for (unsigned i = 0; i < sz; ++i) { - if (!is_pb(a->get_arg(i), mul)) return false; - } + for (unsigned i = 0; i < sz; ++i) + if (!is_pb(a->get_arg(i), mul)) + return false; return true; case OP_SUB: { - if (!is_pb(a->get_arg(0), mul)) return false; + if (!is_pb(a->get_arg(0), mul)) + return false; r = -mul; - for (unsigned i = 1; i < sz; ++i) { - if (!is_pb(a->get_arg(1), r)) return false; - } + for (unsigned i = 1; i < sz; ++i) + if (!is_pb(a->get_arg(i), r)) + return false; return true; } case OP_UMINUS: diff --git a/src/ast/rewriter/push_app_ite.cpp b/src/ast/rewriter/push_app_ite.cpp index 0282aa602..b993c29a5 100644 --- a/src/ast/rewriter/push_app_ite.cpp +++ b/src/ast/rewriter/push_app_ite.cpp @@ -18,6 +18,7 @@ Revision History: --*/ #include "ast/rewriter/push_app_ite.h" +#include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" diff --git a/src/ast/rewriter/rewriter.h b/src/ast/rewriter/rewriter.h index a04c96f4f..cd89bb2f9 100644 --- a/src/ast/rewriter/rewriter.h +++ b/src/ast/rewriter/rewriter.h @@ -347,7 +347,7 @@ public: Config & cfg() { return m_cfg; } Config const & cfg() const { return m_cfg; } - ~rewriter_tpl() override; + ~rewriter_tpl() override {}; void reset(); void cleanup(); diff --git a/src/ast/rewriter/rewriter_def.h b/src/ast/rewriter/rewriter_def.h index e9b9c316a..44bed09c6 100644 --- a/src/ast/rewriter/rewriter_def.h +++ b/src/ast/rewriter/rewriter_def.h @@ -640,10 +640,6 @@ rewriter_tpl::rewriter_tpl(ast_manager & m, bool proof_gen, Config & cfg m_pr2(m) { } -template -rewriter_tpl::~rewriter_tpl() { -} - template void rewriter_tpl::reset() { m_cfg.reset(); diff --git a/src/ast/rewriter/seq_axioms.cpp b/src/ast/rewriter/seq_axioms.cpp index c7dde763f..4d7da4d7f 100644 --- a/src/ast/rewriter/seq_axioms.cpp +++ b/src/ast/rewriter/seq_axioms.cpp @@ -513,8 +513,8 @@ namespace seq { !contains(t, s) => i = -1 |t| = 0 => |s| = 0 or i = -1 - |t| = 0 & |s| = 0 => i = 0 |t| != 0 & contains(t, s) => t = xsy & i = len(x) + |s| = 0 => i = len(t) |s| = 0 or s = s_head*s_tail |s| = 0 or !contains(s_tail*y, s) @@ -540,7 +540,7 @@ namespace seq { add_clause(cnt, i_eq_m1); add_clause(~t_eq_empty, s_eq_empty, i_eq_m1); - add_clause(~t_eq_empty, ~s_eq_empty, i_eq_0); + add_clause(~s_eq_empty, mk_eq(i, mk_len(t))); add_clause(t_eq_empty, ~cnt, mk_seq_eq(t, xsy)); add_clause(t_eq_empty, ~cnt, mk_eq(i, mk_len(x))); add_clause(s_eq_empty, mk_eq(s, mk_concat(s_head, s_tail))); @@ -928,7 +928,6 @@ namespace seq { e1 < e2 => prefix(e1, e2) or e1 = xcy e1 < e2 => prefix(e1, e2) or c < d e1 < e2 => prefix(e1, e2) or e2 = xdz - e1 < e2 => e1 != e2 !(e1 < e2) => prefix(e2, e1) or e2 = xdz !(e1 < e2) => prefix(e2, e1) or d < c !(e1 < e2) => prefix(e2, e1) or e1 = xcy @@ -938,6 +937,7 @@ namespace seq { e1 < e2 or e1 = e2 or e2 < e1 !(e1 = e2) or !(e2 < e1) !(e1 < e2) or !(e2 < e1) + */ void axioms::lt_axiom(expr* n) { expr* _e1 = nullptr, *_e2 = nullptr; @@ -948,6 +948,7 @@ namespace seq { sort* char_sort = nullptr; VERIFY(seq.is_seq(s, char_sort)); expr_ref lt = expr_ref(n, m); + expr_ref gt = expr_ref(seq.str.mk_lex_lt(e2, e1), m); expr_ref x = m_sk.mk("str.<.x", e1, e2); expr_ref y = m_sk.mk("str.<.y", e1, e2); expr_ref z = m_sk.mk("str.<.z", e1, e2); @@ -969,6 +970,7 @@ namespace seq { add_clause(lt, pref21, ltdc); add_clause(lt, pref21, e2xdz); add_clause(~eq, ~lt); + add_clause(eq, lt, gt); } /** @@ -1235,7 +1237,7 @@ namespace seq { seq.str.is_string(x)) { expr_ref len(n, m); m_rewrite(len); - SASSERT(n != len); + SASSERT(m.limit().is_canceled() || n != len); add_clause(mk_eq(len, n)); } else { diff --git a/src/ast/rewriter/seq_eq_solver.cpp b/src/ast/rewriter/seq_eq_solver.cpp index 6d8734bc9..c6778c45e 100644 --- a/src/ast/rewriter/seq_eq_solver.cpp +++ b/src/ast/rewriter/seq_eq_solver.cpp @@ -190,8 +190,8 @@ namespace seq { expr_ref digit = m_ax.sk().mk_digit2int(u); add_consequence(m_ax.mk_ge(digit, 1)); } - expr_ref y(seq.str.mk_concat(es, es[0]->get_sort()), m); - ctx.add_solution(seq.str.mk_itos(n), y); + expr_ref y(seq.str.mk_concat(es, es[0]->get_sort()), m); + ctx.add_solution(seq.str.mk_itos(n), y); return true; } diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index eed36af81..24df51845 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -5024,12 +5024,14 @@ br_status seq_rewriter::mk_re_star(expr* a, expr_ref& result) { * (re.range c_1 c_n) */ br_status seq_rewriter::mk_re_range(expr* lo, expr* hi, expr_ref& result) { - zstring s; + zstring slo, shi; unsigned len = 0; bool is_empty = false; - if (str().is_string(lo, s) && s.length() != 1) + if (str().is_string(lo, slo) && slo.length() != 1) is_empty = true; - if (str().is_string(hi, s) && s.length() != 1) + if (str().is_string(hi, shi) && shi.length() != 1) + is_empty = true; + if (slo.length() == 1 && shi.length() == 1 && slo[0] > shi[0]) is_empty = true; len = min_length(lo).second; if (len > 1) @@ -5246,7 +5248,17 @@ br_status seq_rewriter::reduce_re_is_empty(expr* r, expr_ref& result) { else if (re().is_range(r, r1, r2) && str().is_string(r1, s1) && str().is_string(r2, s2) && s1.length() == 1 && s2.length() == 1) { - result = m().mk_bool_val(s1[0] <= s2[0]); + result = m().mk_bool_val(s1[0] > s2[0]); + return BR_DONE; + } + else if (re().is_range(r, r1, r2) && + str().is_string(r1, s1) && s1.length() != 1) { + result = m().mk_true(); + return BR_DONE; + } + else if (re().is_range(r, r1, r2) && + str().is_string(r2, s2) && s2.length() != 1) { + result = m().mk_true(); return BR_DONE; } else if ((re().is_loop(r, r1, lo) || @@ -5307,6 +5319,7 @@ br_status seq_rewriter::mk_le_core(expr * l, expr * r, expr_ref & result) { } br_status seq_rewriter::mk_eq_core(expr * l, expr * r, expr_ref & result) { + TRACE("seq", tout << mk_pp(l, m()) << " == " << mk_pp(r, m()) << "\n"); expr_ref_vector res(m()); expr_ref_pair_vector new_eqs(m()); if (m_util.is_re(l)) { @@ -5518,6 +5531,7 @@ bool seq_rewriter::reduce_eq(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_ reduce_front(ls, rs, eqs) && reduce_itos(ls, rs, eqs) && reduce_itos(rs, ls, eqs) && + reduce_value_clash(ls, rs, eqs) && reduce_by_length(ls, rs, eqs) && reduce_subsequence(ls, rs, eqs) && reduce_non_overlap(ls, rs, eqs) && @@ -5943,6 +5957,47 @@ bool seq_rewriter::reduce_non_overlap(expr_ref_vector& ls, expr_ref_vector& rs, return true; } + +/** + * partial check for value clash. + * checks that elements that do not occur in + * other sequence are non-values. + * The check could be extended to take non-value + * characters (units) into account. + */ +bool seq_rewriter::reduce_value_clash(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& eqs) { + ptr_buffer es; + + if (ls.empty() || rs.empty()) + return true; + es.append(ls.size(), ls.data()); + auto remove = [&](expr* r) { + for (unsigned i = 0; i < es.size(); ++i) { + if (r == es[i]) { + es[i] = es.back(); + es.pop_back(); + return true; + } + } + return false; + }; + auto is_unit_value = [&](expr* r) { + return m().is_value(r) && str().is_unit(r); + }; + for (expr* r : rs) { + if (remove(r)) + continue; + if (!is_unit_value(r)) + return true; + } + if (es.empty()) + return true; + for (expr* e : es) + if (!is_unit_value(e)) + return true; + return false; +} + bool seq_rewriter::reduce_subsequence(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& eqs) { if (ls.size() > rs.size()) diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index 26dc00675..92a6a17fa 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -340,6 +340,7 @@ class seq_rewriter { bool is_sequence(expr* e, expr_ref_vector& seq); bool is_sequence(eautomaton& aut, expr_ref_vector& seq); bool get_lengths(expr* e, expr_ref_vector& lens, rational& pos); + bool reduce_value_clash(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& new_eqs); bool reduce_back(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& new_eqs); bool reduce_front(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_pair_vector& new_eqs); void remove_empty_and_concats(expr_ref_vector& es); diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index c6ab22636..9278ae5ae 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -31,6 +31,7 @@ Notes: #include "ast/rewriter/seq_rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/rewriter/var_subst.h" +#include "ast/rewriter/der.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" @@ -54,6 +55,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { recfun_rewriter m_rec_rw; arith_util m_a_util; bv_util m_bv_util; + der m_der; expr_safe_replace m_rep; expr_ref_vector m_pinned; // substitution support @@ -821,6 +823,26 @@ struct th_rewriter_cfg : public default_rewriter_cfg { TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << result << " " << result_pr << "\n" ;); + proof_ref p2(m()); + expr_ref r(m()); + + bool der_change = false; + if (is_quantifier(result) && to_quantifier(result)->get_num_patterns() == 0) { + m_der(to_quantifier(result), r, p2); + der_change = result.get() != r.get(); + if (m().proofs_enabled() && der_change) + result_pr = m().mk_transitivity(result_pr, p2); + result = r; + } + + if (der_change) { + th_rewriter rw(m()); + rw(result, r, p2); + if (m().proofs_enabled() && result.get() != r.get()) + result_pr = m().mk_transitivity(result_pr, p2); + result = r; + } + SASSERT(old_q->get_sort() == result->get_sort()); return true; } @@ -839,6 +861,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { m_rec_rw(m), m_a_util(m), m_bv_util(m), + m_der(m), m_rep(m), m_pinned(m), m_used_dependencies(m) { @@ -944,17 +967,41 @@ void th_rewriter::reset() { } void th_rewriter::operator()(expr_ref & term) { - expr_ref result(term.get_manager()); - m_imp->operator()(term, result); - term = std::move(result); + expr_ref result(term.get_manager()); + try { + m_imp->operator()(term, result); + term = std::move(result); + } + catch (...) { + if (!term.get_manager().inc()) + return; + throw; + } } void th_rewriter::operator()(expr * t, expr_ref & result) { - m_imp->operator()(t, result); + try { + m_imp->operator()(t, result); + } + catch (...) { + result = t; + if (!result.get_manager().inc()) + return; + throw; + } } void th_rewriter::operator()(expr * t, expr_ref & result, proof_ref & result_pr) { - m_imp->operator()(t, result, result_pr); + try { + m_imp->operator()(t, result, result_pr); + } + catch (...) { + result = t; + result_pr = nullptr; + if (!result.get_manager().inc()) + return; + throw; + } } expr_ref th_rewriter::operator()(expr * n, unsigned num_bindings, expr * const * bindings) { diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 54ef58e32..9292a7be6 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -663,19 +663,21 @@ void seq_decl_plugin::add_map_sig() { m_sigs[OP_SEQ_MAP] = alloc(psig, m, "seq.map", 2, 2, arrABseqA, seqB); m_sigs[OP_SEQ_MAPI] = alloc(psig, m, "seq.mapi", 2, 3, arrIABintTseqA, seqB); m_sigs[OP_SEQ_FOLDL] = alloc(psig, m, "seq.fold_left", 2, 3, arrBAB_BseqA, B); - m_sigs[OP_SEQ_FOLDLI] = alloc(psig, m, "seq.fold_leftli", 2, 4, arrIBABintTBseqA, B); + m_sigs[OP_SEQ_FOLDLI] = alloc(psig, m, "seq.fold_lefti", 2, 4, arrIBABintTBseqA, B); } void seq_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { init(); for (unsigned i = 0; i < m_sigs.size(); ++i) { - if (m_sigs[i]) - op_names.push_back(builtin_name(m_sigs[i]->m_name.str(), i)); + if (m_sigs[i]) + op_names.push_back(builtin_name(m_sigs[i]->m_name.str(), i)); } op_names.push_back(builtin_name("seq.map", OP_SEQ_MAP)); op_names.push_back(builtin_name("seq.mapi", OP_SEQ_MAPI)); op_names.push_back(builtin_name("seq.foldl", OP_SEQ_FOLDL)); op_names.push_back(builtin_name("seq.foldli", OP_SEQ_FOLDLI)); + op_names.push_back(builtin_name("seq.fold_lefti", OP_SEQ_FOLDLI)); + op_names.push_back(builtin_name("seq.fold_left", OP_SEQ_FOLDL)); op_names.push_back(builtin_name("str.in.re", _OP_STRING_IN_REGEXP)); op_names.push_back(builtin_name("str.in-re", _OP_STRING_IN_REGEXP)); op_names.push_back(builtin_name("str.to.re", _OP_STRING_TO_REGEXP)); diff --git a/src/ast/simplifiers/dependent_expr_state.cpp b/src/ast/simplifiers/dependent_expr_state.cpp index d784e6fe4..7d00708ac 100644 --- a/src/ast/simplifiers/dependent_expr_state.cpp +++ b/src/ast/simplifiers/dependent_expr_state.cpp @@ -81,7 +81,7 @@ void dependent_expr_state::freeze_recfun() { ast_mark visited; for (func_decl* f : rec.get_rec_funs()) { auto& d = rec.get_def(f); - if (!d.is_macro()) + if (!d.is_macro() && d.get_rhs()) freeze_terms(d.get_rhs(), false, visited); } m_trail.push(value_trail(m_num_recfun)); diff --git a/src/ast/simplifiers/dominator_simplifier.h b/src/ast/simplifiers/dominator_simplifier.h index b412f6db0..048f6991b 100644 --- a/src/ast/simplifiers/dominator_simplifier.h +++ b/src/ast/simplifiers/dominator_simplifier.h @@ -41,7 +41,7 @@ class dominator_simplifier : public dependent_expr_simplifier { bool is_subexpr(expr * a, expr * b); expr_ref get_cached(expr* t) { expr* r = nullptr; if (!m_result.find(t, r)) r = t; return expr_ref(r, m); } - void cache(expr *t, expr* r) { m_result.insert(t, r); m_trail.push_back(r); } + void cache(expr *t, expr* r) { m_result.insert(t, r); m_trail.push_back(r); m_trail.push_back(t); } void reset_cache() { m_result.reset(); } ptr_vector const & tree(expr * e); diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp index 41877202a..231858897 100644 --- a/src/ast/simplifiers/elim_unconstrained.cpp +++ b/src/ast/simplifiers/elim_unconstrained.cpp @@ -52,9 +52,9 @@ monotonicity or reflexivity rules. #include "ast/simplifiers/elim_unconstrained.h" elim_unconstrained::elim_unconstrained(ast_manager& m, dependent_expr_state& fmls) : - dependent_expr_simplifier(m, fmls), m_inverter(m), m_lt(*this), m_heap(1024, m_lt), m_trail(m) { + dependent_expr_simplifier(m, fmls), m_inverter(m), m_lt(*this), m_heap(1024, m_lt), m_trail(m), m_args(m) { std::function is_var = [&](expr* e) { - return is_uninterp_const(e) && !m_fmls.frozen(e) && get_node(e).m_refcount <= 1; + return is_uninterp_const(e) && !m_fmls.frozen(e) && is_node(e) && get_node(e).m_refcount <= 1; }; m_inverter.set_is_var(is_var); } @@ -73,7 +73,7 @@ void elim_unconstrained::eliminate() { node& n = get_node(v); if (n.m_refcount == 0) continue; - if (n.m_refcount > 1) + if (n.m_refcount > 1) return; if (n.m_parents.empty()) { @@ -90,10 +90,10 @@ void elim_unconstrained::eliminate() { unsigned sz = m_args.size(); for (expr* arg : *to_app(t)) m_args.push_back(reconstruct_term(get_node(arg))); - bool inverted = m_inverter(t->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz, r); + bool inverted = m_inverter(t->get_decl(), t->get_num_args(), m_args.data() + sz, r); proof_ref pr(m); if (inverted && m_enable_proofs) { - expr * s = m.mk_app(t->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz); + expr * s = m.mk_app(t->get_decl(), t->get_num_args(), m_args.data() + sz); expr * eq = m.mk_eq(s, r); proof * pr1 = m.mk_def_intro(eq); proof * pr = m.mk_apply_def(s, r, pr1); @@ -114,7 +114,7 @@ void elim_unconstrained::eliminate() { gc(e); invalidate_parents(e); freeze_rec(r); - + m_root.setx(r->get_id(), e->get_id(), UINT_MAX); get_node(e).m_term = r; get_node(e).m_proof = pr; @@ -291,7 +291,7 @@ expr_ref elim_unconstrained::reconstruct_term(node& n0) { unsigned sz0 = todo.size(); if (is_app(t)) { for (expr* arg : *to_app(t)) - if (get_node(arg).m_dirty) + if (get_node(arg).m_dirty || !get_node(arg).m_term) todo.push_back(arg); if (todo.size() != sz0) continue; @@ -300,18 +300,20 @@ expr_ref elim_unconstrained::reconstruct_term(node& n0) { for (expr* arg : *to_app(t)) m_args.push_back(get_node(arg).m_term); n.m_term = m.mk_app(to_app(t)->get_decl(), to_app(t)->get_num_args(), m_args.data() + sz); + m_args.shrink(sz); } else if (is_quantifier(t)) { expr* body = to_quantifier(t)->get_expr(); node& n2 = get_node(body); - if (n2.m_dirty) { + if (n2.m_dirty || !n2.m_term) { todo.push_back(body); continue; } n.m_term = m.update_quantifier(to_quantifier(t), n2.m_term); } m_trail.push_back(n.m_term); + m_root.setx(n.m_term->get_id(), n.m_term->get_id(), UINT_MAX); todo.pop_back(); n.m_dirty = false; } diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h index 19af099d0..5dced90d0 100644 --- a/src/ast/simplifiers/elim_unconstrained.h +++ b/src/ast/simplifiers/elim_unconstrained.h @@ -22,6 +22,8 @@ Author: class elim_unconstrained : public dependent_expr_simplifier { + friend class seq_simplifier; + struct node { unsigned m_refcount = 0; expr* m_term = nullptr; @@ -46,13 +48,15 @@ class elim_unconstrained : public dependent_expr_simplifier { var_lt m_lt; heap m_heap; expr_ref_vector m_trail; - ptr_vector m_args; + expr_ref_vector m_args; stats m_stats; unsigned_vector m_root; bool m_created_compound = false; bool m_enable_proofs = false; bool is_var_lt(int v1, int v2) const; + bool is_node(unsigned n) const { return m_nodes.size() > n; } + bool is_node(expr* t) const { return is_node(t->get_id()); } node& get_node(unsigned n) { return m_nodes[n]; } node const& get_node(unsigned n) const { return m_nodes[n]; } node& get_node(expr* t) { return m_nodes[root(t)]; } diff --git a/src/ast/simplifiers/eliminate_predicates.cpp b/src/ast/simplifiers/eliminate_predicates.cpp index 6c1eccea0..0dfb5a87e 100644 --- a/src/ast/simplifiers/eliminate_predicates.cpp +++ b/src/ast/simplifiers/eliminate_predicates.cpp @@ -174,7 +174,7 @@ bool eliminate_predicates::can_be_quasi_macro_head(expr* _head, unsigned num_bou // then replace (f x y z) by (if (= z (+ x y)) s (f' x y z)) // -void eliminate_predicates::insert_quasi_macro(app* head, expr* body, clause const& cl) { +void eliminate_predicates::insert_quasi_macro(app* head, expr* body, clause& cl) { expr_ref _body(body, m); uint_set indices; expr_ref_vector args(m), eqs(m); @@ -205,9 +205,10 @@ void eliminate_predicates::insert_quasi_macro(app* head, expr* body, clause cons // forall vars . f(args) = if eqs then body else f'(args) f1 = m.mk_fresh_func_decl(f->get_name(), symbol::null, sorts.size(), sorts.data(), f->get_range()); + lhs = m.mk_app(f, args); rhs = m.mk_ite(mk_and(eqs), body, m.mk_app(f1, args)); - insert_macro(lhs, rhs, cl.m_dep); + insert_macro(lhs, rhs, cl); } @@ -310,6 +311,12 @@ bool eliminate_predicates::is_macro_safe(expr* e) { return true; } +void eliminate_predicates::insert_macro(app* head, expr* def, clause& cl) { + insert_macro(head, def, cl.m_dep); + TRACE("elim_predicates", tout << "remove " << cl << "\n"); + cl.m_alive = false; +} + void eliminate_predicates::insert_macro(app* head, expr* def, expr_dependency* dep) { unsigned num = head->get_num_args(); ptr_buffer vars, subst_args; @@ -334,7 +341,7 @@ void eliminate_predicates::insert_macro(app* head, expr* def, expr_dependency* d auto* info = alloc(macro_def, _head, _def, _dep); m_macros.insert(head->get_decl(), info); m_fmls.model_trail().push(head->get_decl(), _def, _dep, {}); // augment with definition for head - m_is_macro.mark(head->get_decl(), true); + m_is_macro.mark(head->get_decl(), true); TRACE("elim_predicates", tout << "insert " << _head << " " << _def << "\n"); ++m_stats.m_num_macros; } @@ -367,26 +374,22 @@ void eliminate_predicates::try_find_macro(clause& cl) { // (= (f x) t) if (cl.is_unit() && !cl.sign(0) && m.is_eq(cl.atom(0), x, y)) { if (can_be_def(x, y)) { - insert_macro(to_app(x), y, cl.m_dep); - cl.m_alive = false; + insert_macro(to_app(x), y, cl); return; } if (can_be_def(y, x)) { - insert_macro(to_app(y), x, cl.m_dep); - cl.m_alive = false; + insert_macro(to_app(y), x, cl); return; } } // not (= (p x) t) -> (p x) = (not t) if (cl.is_unit() && cl.sign(0) && m.is_iff(cl.atom(0), x, y)) { if (can_be_def(x, y)) { - insert_macro(to_app(x), m.mk_not(y), cl.m_dep); - cl.m_alive = false; + insert_macro(to_app(x), m.mk_not(y), cl); return; } if (can_be_def(y, x)) { - insert_macro(to_app(y), m.mk_not(x), cl.m_dep); - cl.m_alive = false; + insert_macro(to_app(y), m.mk_not(x), cl); return; } } @@ -413,8 +416,7 @@ void eliminate_predicates::try_find_macro(clause& cl) { m_fmls.model_trail().hide(fn); // hide definition of fn k = m.mk_app(fn, f->get_num_args(), f->get_args()); def = m.mk_ite(cond, t, k); - insert_macro(f, def, cl.m_dep); - cl.m_alive = false; + insert_macro(f, def, cl); fml = m.mk_not(m.mk_eq(k, t)); clause* new_cl = init_clause(fml, cl.m_dep, UINT_MAX); add_use_list(*new_cl); @@ -531,8 +533,7 @@ void eliminate_predicates::try_find_macro(clause& cl) { expr_ref y1 = subtract(y, to_app(x), i); if (inv) y1 = uminus(y1); - insert_macro(to_app(arg), y1, cl.m_dep); - cl.m_alive = false; + insert_macro(to_app(arg), y1, cl); return true; } next: @@ -572,7 +573,7 @@ void eliminate_predicates::try_find_macro(clause& cl) { !occurs(x->get_decl(), y); }; - if (cl.is_unit() && m.is_eq(cl.atom(0), x, y)) { + if (cl.is_unit() && m.is_eq(cl.atom(0), x, y) && !cl.m_bound.empty()) { if (!cl.sign(0) && can_be_qdef(x, y)) { insert_quasi_macro(to_app(x), y, cl); return; @@ -590,7 +591,7 @@ void eliminate_predicates::try_find_macro(clause& cl) { return; } } - if (cl.is_unit()) { + if (cl.is_unit() && !cl.m_bound.empty()) { expr* body = cl.sign(0) ? m.mk_false() : m.mk_true(); expr* x = cl.atom(0); if (can_be_qdef(x, body)) { @@ -736,6 +737,7 @@ void eliminate_predicates::update_model(func_decl* p) { } rewrite(def); + TRACE("elim_predicates", tout << "insert " << p->get_name() << " " << def << "\n"); m_fmls.model_trail().push(p, def, dep, deleted); } @@ -1008,8 +1010,6 @@ void eliminate_predicates::reset() { void eliminate_predicates::reduce() { - if (!m_fmls.has_quantifiers()) - return; reset(); init_clauses(); find_definitions(); diff --git a/src/ast/simplifiers/eliminate_predicates.h b/src/ast/simplifiers/eliminate_predicates.h index af0ede119..013c7cf65 100644 --- a/src/ast/simplifiers/eliminate_predicates.h +++ b/src/ast/simplifiers/eliminate_predicates.h @@ -111,9 +111,10 @@ private: bool try_find_binary_definition(func_decl* p, app_ref& head, expr_ref& def, expr_dependency_ref& dep); void try_resolve_definition(func_decl* p); void insert_macro(app* head, expr* def, expr_dependency* dep); + void insert_macro(app* head, expr* def, clause& cl); expr_ref bind_free_variables_in_def(clause& cl, app* head, expr* def); bool can_be_macro_head(expr* head, unsigned num_bound); - void insert_quasi_macro(app* head, expr* body, clause const& cl); + void insert_quasi_macro(app* head, expr* body, clause& cl); bool can_be_quasi_macro_head(expr* head, unsigned num_bound); bool is_macro_safe(expr* e); void try_find_macro(clause& cl); diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp index 65fe8374a..fbc6fbb02 100644 --- a/src/ast/simplifiers/solve_eqs.cpp +++ b/src/ast/simplifiers/solve_eqs.cpp @@ -262,6 +262,47 @@ namespace euf { } } + void solve_eqs::collect_num_occs(expr * t, expr_fast_mark1 & visited) { + ptr_buffer stack; + + auto visit = [&](expr* arg) { + if (is_uninterp_const(arg)) + m_num_occs.insert_if_not_there(arg, 0)++; + if (!visited.is_marked(arg) && is_app(arg)) { + visited.mark(arg, true); + stack.push_back(to_app(arg)); + } + }; + + visit(t); + + while (!stack.empty()) { + app * t = stack.back(); + stack.pop_back(); + for (expr* arg : *t) + visit(arg); + } + } + + void solve_eqs::collect_num_occs() { + if (m_config.m_max_occs == UINT_MAX) + return; // no need to compute num occs + m_num_occs.reset(); + expr_fast_mark1 visited; + for (unsigned i : indices()) + collect_num_occs(m_fmls[i].fml(), visited); + } + + // Check if the number of occurrences of t is below the specified threshold :solve-eqs-max-occs + bool solve_eqs::check_occs(expr * t) const { + if (m_config.m_max_occs == UINT_MAX) + return true; + unsigned num = 0; + m_num_occs.find(t, num); + TRACE("solve_eqs_check_occs", tout << mk_ismt2_pp(t, m) << " num_occs: " << num << " max: " << m_config.m_max_occs << "\n";); + return num <= m_config.m_max_occs; + } + void solve_eqs::save_subst(vector const& old_fmls) { if (!m_subst->empty()) m_fmls.model_trail().push(m_subst.detach(), old_fmls); diff --git a/src/ast/simplifiers/solve_eqs.h b/src/ast/simplifiers/solve_eqs.h index c8fbe3a40..dde42dd94 100644 --- a/src/ast/simplifiers/solve_eqs.h +++ b/src/ast/simplifiers/solve_eqs.h @@ -56,10 +56,12 @@ namespace euf { expr_mark m_unsafe_vars; // expressions that cannot be replaced ptr_vector m_todo; expr_mark m_visited; + obj_map m_num_occs; + bool is_var(expr* e) const { return e->get_id() < m_var2id.size() && m_var2id[e->get_id()] != UINT_MAX; } unsigned var2id(expr* v) const { return m_var2id[v->get_id()]; } - bool can_be_var(expr* e) const { return is_uninterp_const(e) && !m_unsafe_vars.is_marked(e); } + bool can_be_var(expr* e) const { return is_uninterp_const(e) && !m_unsafe_vars.is_marked(e) && check_occs(e); } void get_eqs(dep_eq_vector& eqs); void filter_unsafe_vars(); void extract_subst(); @@ -67,6 +69,9 @@ namespace euf { void normalize(); void apply_subst(vector& old_fmls); void save_subst(vector const& old_fmls); + void collect_num_occs(expr * t, expr_fast_mark1 & visited); + void collect_num_occs(); + bool check_occs(expr* t) const; public: diff --git a/src/ast/simplifiers/seq_simplifier.h b/src/ast/simplifiers/then_simplifier.h similarity index 94% rename from src/ast/simplifiers/seq_simplifier.h rename to src/ast/simplifiers/then_simplifier.h index a5ef91d7c..6ee8b9412 100644 --- a/src/ast/simplifiers/seq_simplifier.h +++ b/src/ast/simplifiers/then_simplifier.h @@ -3,7 +3,7 @@ Copyright (c) 2022 Microsoft Corporation Module Name: - seq_simplifier.h + then_simplifier.h Abstract: @@ -21,7 +21,7 @@ Author: #include "ast/simplifiers/dependent_expr_state.h" -class seq_simplifier : public dependent_expr_simplifier { +class then_simplifier : public dependent_expr_simplifier { scoped_ptr_vector m_simplifiers; struct collect_stats { @@ -53,7 +53,7 @@ class seq_simplifier : public dependent_expr_simplifier { public: - seq_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): + then_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls): dependent_expr_simplifier(m, fmls) { } diff --git a/src/ast/special_relations_decl_plugin.cpp b/src/ast/special_relations_decl_plugin.cpp index 7ed5e8346..24a756bf7 100644 --- a/src/ast/special_relations_decl_plugin.cpp +++ b/src/ast/special_relations_decl_plugin.cpp @@ -47,6 +47,7 @@ func_decl * special_relations_decl_plugin::mk_func_decl( if (!m_manager->is_bool(range)) { m_manager->raise_exception("range type is expected to be Boolean for special relations"); } + m_has_special_relation = true; func_decl_info info(m_family_id, k, num_parameters, parameters); symbol name; switch(k) { @@ -54,7 +55,11 @@ func_decl * special_relations_decl_plugin::mk_func_decl( case OP_SPECIAL_RELATION_LO: name = m_lo; break; case OP_SPECIAL_RELATION_PLO: name = m_plo; break; case OP_SPECIAL_RELATION_TO: name = m_to; break; - case OP_SPECIAL_RELATION_TC: name = m_tc; break; + case OP_SPECIAL_RELATION_TC: + name = m_tc; + if (num_parameters != 1 || !parameters[0].is_ast() || !is_func_decl(parameters[0].get_ast())) + m_manager->raise_exception("parameter to transitive closure should be a function declaration"); + break; } return m_manager->mk_func_decl(name, arity, domain, range, info); } diff --git a/src/ast/special_relations_decl_plugin.h b/src/ast/special_relations_decl_plugin.h index daff802e7..c422cbcdc 100644 --- a/src/ast/special_relations_decl_plugin.h +++ b/src/ast/special_relations_decl_plugin.h @@ -37,6 +37,7 @@ class special_relations_decl_plugin : public decl_plugin { symbol m_plo; symbol m_to; symbol m_tc; + bool m_has_special_relation = false; public: special_relations_decl_plugin(); @@ -50,6 +51,8 @@ public: void get_op_names(svector & op_names, symbol const & logic) override; sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override { return nullptr; } + + bool has_special_relation() const { return m_has_special_relation; } }; enum sr_property { @@ -71,15 +74,19 @@ class special_relations_util { ast_manager& m; mutable family_id m_fid; func_decl* mk_rel_decl(func_decl* f, decl_kind k) { + SASSERT(f); parameter p(f); SASSERT(f->get_arity() == 2); return m.mk_func_decl(fid(), k, 1, &p, 2, f->get_domain(), f->get_range()); } family_id fid() const { - if (null_family_id == m_fid) m_fid = m.get_family_id("specrels"); + if (null_family_id == m_fid) + m_fid = m.get_family_id("specrels"); return m_fid; } public: special_relations_util(ast_manager& m) : m(m), m_fid(null_family_id) { } + + bool has_special_relation() const { return static_cast(m.get_plugin(m.mk_family_id("specrels")))->has_special_relation(); } bool is_special_relation(func_decl* f) const { return f->get_family_id() == fid(); } bool is_special_relation(app* e) const { return is_special_relation(e->get_decl()); } @@ -99,6 +106,12 @@ public: bool is_to(expr const * e) const { return is_app_of(e, fid(), OP_SPECIAL_RELATION_TO); } bool is_tc(expr const * e) const { return is_app_of(e, fid(), OP_SPECIAL_RELATION_TC); } + bool is_lo(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_LO); } + bool is_po(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_PO); } + bool is_plo(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_PLO); } + bool is_to(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_TO); } + bool is_tc(func_decl const * e) const { return is_decl_of(e, fid(), OP_SPECIAL_RELATION_TC); } + app * mk_lo (expr * arg1, expr * arg2) { return m.mk_app( fid(), OP_SPECIAL_RELATION_LO, arg1, arg2); } app * mk_po (expr * arg1, expr * arg2) { return m.mk_app( fid(), OP_SPECIAL_RELATION_PO, arg1, arg2); } app * mk_plo(expr * arg1, expr * arg2) { return m.mk_app( fid(), OP_SPECIAL_RELATION_PLO, arg1, arg2); } diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 89733a7ea..8d1e375d1 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -361,7 +361,7 @@ void cmd_context::insert_macro(symbol const& s, unsigned arity, sort*const* doma vars.push_back(m().mk_var(i, domain[i])); rvars.push_back(m().mk_var(i, domain[arity - i - 1])); } - recfun::promise_def d = p.ensure_def(s, arity, domain, t->get_sort()); + recfun::promise_def d = p.ensure_def(s, arity, domain, t->get_sort(), false); // recursive functions have opposite calling convention from macros! var_subst sub(m(), true); @@ -984,7 +984,7 @@ recfun::decl::plugin& cmd_context::get_recfun_plugin() { recfun::promise_def cmd_context::decl_rec_fun(const symbol &name, unsigned int arity, sort *const *domain, sort *range) { SASSERT(logic_has_recfun()); - return get_recfun_plugin().mk_def(name, arity, domain, range); + return get_recfun_plugin().mk_def(name, arity, domain, range, false); } void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector const& ids, expr* rhs) { @@ -1237,7 +1237,10 @@ bool cmd_context::try_mk_pdecl_app(symbol const & s, unsigned num_args, expr * c if (num_args != 1) return false; - for (auto* a : dt.plugin().get_accessors(s)) { + if (!dt.is_datatype(args[0]->get_sort())) + return false; + + for (auto* a : dt.plugin().get_accessors(s)) { fn = a->instantiate(args[0]->get_sort()); r = m().mk_app(fn, num_args, args); return true; @@ -1979,23 +1982,28 @@ void cmd_context::complete_model(model_ref& md) const { } } - for (auto kd : m_func_decls) { - symbol const & k = kd.m_key; - func_decls & v = kd.m_value; + for (auto& [k, v] : m_func_decls) { IF_VERBOSE(12, verbose_stream() << "(model.completion " << k << ")\n"; ); for (unsigned i = 0; i < v.get_num_entries(); i++) { func_decl * f = v.get_entry(i); - if (!md->has_interpretation(f)) { - sort * range = f->get_range(); - expr * some_val = m().get_some_value(range); - if (f->get_arity() > 0) { - func_interp * fi = alloc(func_interp, m(), f->get_arity()); - fi->set_else(some_val); - md->register_decl(f, fi); - } - else - md->register_decl(f, some_val); + + if (md->has_interpretation(f)) + continue; + macro_decls decls; + expr* body = nullptr; + + if (m_macros.find(k, decls)) + body = decls.find(f->get_arity(), f->get_domain()); + sort * range = f->get_range(); + if (!body) + body = m().get_some_value(range); + if (f->get_arity() > 0) { + func_interp * fi = alloc(func_interp, m(), f->get_arity()); + fi->set_else(body); + md->register_decl(f, fi); } + else + md->register_decl(f, body); } } } @@ -2201,21 +2209,18 @@ void cmd_context::display_statistics(bool show_total_time, double total_time) { } -expr_ref_vector cmd_context::tracked_assertions() { - expr_ref_vector result(m()); +vector> cmd_context::tracked_assertions() { + vector> result; if (assertion_names().size() == assertions().size()) { for (unsigned i = 0; i < assertions().size(); ++i) { expr* an = assertion_names()[i]; expr* asr = assertions()[i]; - if (an) - result.push_back(m().mk_implies(an, asr)); - else - result.push_back(asr); + result.push_back({ asr, an }); } } else { for (expr * e : assertions()) - result.push_back(e); + result.push_back({ e, nullptr}); } return result; } diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index f5a247794..b034a9ffc 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -523,7 +523,7 @@ public: ptr_vector const& assertions() const { return m_assertions; } ptr_vector const& assertion_names() const { return m_assertion_names; } - expr_ref_vector tracked_assertions(); + vector> tracked_assertions(); void reset_tracked_assertions(); /** diff --git a/src/cmd_context/simplifier_cmds.cpp b/src/cmd_context/simplifier_cmds.cpp index 0050dc548..ac8d3e839 100644 --- a/src/cmd_context/simplifier_cmds.cpp +++ b/src/cmd_context/simplifier_cmds.cpp @@ -21,7 +21,7 @@ Author: #include "cmd_context/parametric_cmd.h" #include "model/model_smt2_pp.h" #include "ast/ast_smt2_pp.h" -#include "ast/simplifiers/seq_simplifier.h" +#include "ast/simplifiers/then_simplifier.h" #include "solver/simplifier_solver.h" typedef dependent_expr_simplifier simplifier; @@ -37,7 +37,7 @@ static simplifier_factory mk_and_then(cmd_context & ctx, sexpr * n) { for (unsigned i = 1; i < num_children; i++) args.push_back(sexpr2simplifier(ctx, n->get_child(i))); simplifier_factory result = [args](ast_manager& m, const params_ref& p, dependent_expr_state& st) { - scoped_ptr s = alloc(seq_simplifier, m, p, st); + scoped_ptr s = alloc(then_simplifier, m, p, st); for (auto & simp : args) s->add_simplifier(simp(m, p, st)); return s.detach(); diff --git a/src/math/interval/interval.h b/src/math/interval/interval.h index fd9f5a246..0d036a16e 100644 --- a/src/math/interval/interval.h +++ b/src/math/interval/interval.h @@ -72,6 +72,7 @@ public: void set_upper_is_open(interval & a, bool v) { a.m_upper_open = v; } void set_lower_is_inf(interval & a, bool v) { a.m_lower_inf = v; } void set_upper_is_inf(interval & a, bool v) { a.m_upper_inf = v; } + // Reference to numeral manager numeral_manager & m() const { return m_manager; } @@ -184,6 +185,14 @@ public: bool upper_is_open(interval const & a) const { return m_c.upper_is_open(a); } bool lower_is_inf(interval const & a) const { return m_c.lower_is_inf(a); } bool upper_is_inf(interval const & a) const { return m_c.upper_is_inf(a); } + bool is_empty(interval const& a) const { + if (lower_is_inf(a) || upper_is_inf(a)) + return false; + ext_numeral_kind lk = lower_kind(a), uk = upper_kind(a); + if (lower_is_open(a) || upper_is_open(a)) + return !(::lt(m(), lower(a), lk, upper(a), uk)); + return ::lt(m(), upper(a), uk, lower(a), lk); + } bool lower_is_neg(interval const & a) const { return ::is_neg(m(), lower(a), lower_kind(a)); } bool lower_is_pos(interval const & a) const { return ::is_pos(m(), lower(a), lower_kind(a)); } diff --git a/src/math/interval/interval_def.h b/src/math/interval/interval_def.h index bbce66420..9918f39a1 100644 --- a/src/math/interval/interval_def.h +++ b/src/math/interval/interval_def.h @@ -681,7 +681,7 @@ void interval_manager::set(interval & t, interval const & s) { } set_lower_is_open(t, lower_is_open(s)); set_upper_is_open(t, upper_is_open(s)); - SASSERT(check_invariant(t)); + SASSERT(is_empty(t) || check_invariant(t)); } template @@ -813,7 +813,7 @@ void interval_manager::add(interval const & a, interval const & b, interval & set_upper_is_inf(c, new_u_kind == EN_PLUS_INFINITY); set_lower_is_open(c, lower_is_open(a) || lower_is_open(b)); set_upper_is_open(c, upper_is_open(a) || upper_is_open(b)); - SASSERT(check_invariant(c)); + SASSERT(is_empty(a) || is_empty(b) || check_invariant(c)); } template diff --git a/src/math/lp/CMakeLists.txt b/src/math/lp/CMakeLists.txt index 9f0fae6bc..dd72f36ee 100644 --- a/src/math/lp/CMakeLists.txt +++ b/src/math/lp/CMakeLists.txt @@ -1,10 +1,7 @@ z3_add_component(lp SOURCES - binary_heap_priority_queue.cpp - binary_heap_upair_queue.cpp core_solver_pretty_printer.cpp dense_matrix.cpp - eta_matrix.cpp emonics.cpp factorization.cpp factorization_factory_imp.cpp @@ -19,14 +16,8 @@ z3_add_component(lp lar_solver.cpp lar_core_solver.cpp lp_core_solver_base.cpp - lp_dual_core_solver.cpp - lp_dual_simplex.cpp lp_primal_core_solver.cpp - lp_primal_simplex.cpp lp_settings.cpp - lp_solver.cpp - lu.cpp - lp_utils.cpp matrix.cpp mon_eq.cpp monomial_bounds.cpp @@ -45,10 +36,6 @@ z3_add_component(lp nra_solver.cpp permutation_matrix.cpp random_updater.cpp - row_eta_matrix.cpp - scaler.cpp - square_dense_submatrix.cpp - square_sparse_matrix.cpp static_matrix.cpp COMPONENT_DEPENDENCIES util diff --git a/src/math/lp/binary_heap_priority_queue.cpp b/src/math/lp/binary_heap_priority_queue.cpp deleted file mode 100644 index bbe735e58..000000000 --- a/src/math/lp/binary_heap_priority_queue.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/numeric_pair.h" -#include "math/lp/binary_heap_priority_queue_def.h" -namespace lp { -template binary_heap_priority_queue::binary_heap_priority_queue(unsigned int); -template unsigned binary_heap_priority_queue::dequeue(); -template void binary_heap_priority_queue::enqueue(unsigned int, int const&); -template void binary_heap_priority_queue::enqueue(unsigned int, double const&); -template void binary_heap_priority_queue::enqueue(unsigned int, mpq const&); -template void binary_heap_priority_queue::remove(unsigned int); -template unsigned binary_heap_priority_queue >::dequeue(); -template unsigned binary_heap_priority_queue::dequeue(); -template unsigned binary_heap_priority_queue::dequeue(); -template void binary_heap_priority_queue >::enqueue(unsigned int, numeric_pair const&); -template void binary_heap_priority_queue >::resize(unsigned int); -template void lp::binary_heap_priority_queue::resize(unsigned int); -template binary_heap_priority_queue::binary_heap_priority_queue(unsigned int); -template void binary_heap_priority_queue::resize(unsigned int); -template unsigned binary_heap_priority_queue::dequeue(); -template void binary_heap_priority_queue::enqueue(unsigned int, unsigned int const&); -template void binary_heap_priority_queue::remove(unsigned int); -template void lp::binary_heap_priority_queue::resize(unsigned int); -} diff --git a/src/math/lp/binary_heap_priority_queue.h b/src/math/lp/binary_heap_priority_queue.h deleted file mode 100644 index 1ea8363ef..000000000 --- a/src/math/lp/binary_heap_priority_queue.h +++ /dev/null @@ -1,83 +0,0 @@ - -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/vector.h" -#include "util/debug.h" -#include "math/lp/lp_utils.h" -namespace lp { -// the elements with the smallest priority are dequeued first -template -class binary_heap_priority_queue { - vector m_priorities; - - // indexing for A starts from 1 - vector m_heap; // keeps the elements of the queue - vector m_heap_inverse; // o = m_heap[m_heap_inverse[o]] - unsigned m_heap_size; - // is is the child place in heap - void swap_with_parent(unsigned i); - void put_at(unsigned i, unsigned h); - void decrease_priority(unsigned o, T newPriority); -public: -#ifdef Z3DEBUG - bool is_consistent() const; -#endif -public: - void remove(unsigned o); - unsigned size() const { return m_heap_size; } - binary_heap_priority_queue(): m_heap(1), m_heap_size(0) {} // the empty constructror - // n is the initial queue capacity. - // The capacity will be enlarged each time twice if needed - binary_heap_priority_queue(unsigned n); - - void clear() { - for (unsigned i = 0; i < m_heap_size; i++) { - unsigned o = m_heap[i+1]; - m_heap_inverse[o] = -1; - } - m_heap_size = 0; - } - - void resize(unsigned n); - void put_to_heap(unsigned i, unsigned o); - - void enqueue_new(unsigned o, const T& priority); - - // This method can work with an element that is already in the queue. - // In this case the priority will be changed and the queue adjusted. - void enqueue(unsigned o, const T & priority); - void change_priority_for_existing(unsigned o, const T & priority); - T get_priority(unsigned o) const { return m_priorities[o]; } - bool is_empty() const { return m_heap_size == 0; } - - /// return the first element of the queue and removes it from the queue - unsigned dequeue_and_get_priority(T & priority); - void fix_heap_under(unsigned i); - void put_the_last_at_the_top_and_fix_the_heap(); - /// return the first element of the queue and removes it from the queue - unsigned dequeue(); - unsigned peek() const { - lp_assert(m_heap_size > 0); - return m_heap[1]; - } - void print(std::ostream & out); -}; -} diff --git a/src/math/lp/binary_heap_priority_queue_def.h b/src/math/lp/binary_heap_priority_queue_def.h deleted file mode 100644 index 0e640d949..000000000 --- a/src/math/lp/binary_heap_priority_queue_def.h +++ /dev/null @@ -1,214 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "util/vector.h" -#include "math/lp/binary_heap_priority_queue.h" -namespace lp { -// "i" is the child's place in the heap -template void binary_heap_priority_queue::swap_with_parent(unsigned i) { - unsigned parent = m_heap[i >> 1]; - put_at(i >> 1, m_heap[i]); - put_at(i, parent); -} - -template void binary_heap_priority_queue::put_at(unsigned i, unsigned h) { - m_heap[i] = h; - m_heap_inverse[h] = i; -} - -template void binary_heap_priority_queue::decrease_priority(unsigned o, T newPriority) { - m_priorities[o] = newPriority; - int i = m_heap_inverse[o]; - while (i > 1) { - if (m_priorities[m_heap[i]] < m_priorities[m_heap[i >> 1]]) - swap_with_parent(i); - else - break; - i >>= 1; - } -} - -#ifdef Z3DEBUG -template bool binary_heap_priority_queue::is_consistent() const { - for (int i = 0; i < m_heap_inverse.size(); i++) { - int i_index = m_heap_inverse[i]; - lp_assert(i_index <= static_cast(m_heap_size)); - lp_assert(i_index == -1 || m_heap[i_index] == i); - } - for (unsigned i = 1; i < m_heap_size; i++) { - unsigned ch = i << 1; - for (int k = 0; k < 2; k++) { - if (ch > m_heap_size) break; - if (!(m_priorities[m_heap[i]] <= m_priorities[m_heap[ch]])){ - return false; - } - ch++; - } - } - return true; -} -#endif - -template void binary_heap_priority_queue::remove(unsigned o) { - T priority_of_o = m_priorities[o]; - int o_in_heap = m_heap_inverse[o]; - if (o_in_heap == -1) { - return; // nothing to do - } - lp_assert(static_cast(o_in_heap) <= m_heap_size); - if (static_cast(o_in_heap) < m_heap_size) { - put_at(o_in_heap, m_heap[m_heap_size--]); - if (m_priorities[m_heap[o_in_heap]] > priority_of_o) { - fix_heap_under(o_in_heap); - } else { // we need to propagate the m_heap[o_in_heap] up - unsigned i = o_in_heap; - while (i > 1) { - unsigned ip = i >> 1; - if (m_priorities[m_heap[i]] < m_priorities[m_heap[ip]]) - swap_with_parent(i); - else - break; - i = ip; - } - } - } else { - lp_assert(static_cast(o_in_heap) == m_heap_size); - m_heap_size--; - } - m_heap_inverse[o] = -1; - // lp_assert(is_consistent()); -} -// n is the initial queue capacity. -// The capacity will be enlarged two times automatically if needed -template binary_heap_priority_queue::binary_heap_priority_queue(unsigned n) : - m_priorities(n), - m_heap(n + 1), // because the indexing for A starts from 1 - m_heap_inverse(n, -1), - m_heap_size(0) -{ } - - -template void binary_heap_priority_queue::resize(unsigned n) { - m_priorities.resize(n); - m_heap.resize(n + 1); - m_heap_inverse.resize(n, -1); -} - -template void binary_heap_priority_queue::put_to_heap(unsigned i, unsigned o) { - m_heap[i] = o; - m_heap_inverse[o] = i; -} - -template void binary_heap_priority_queue::enqueue_new(unsigned o, const T& priority) { - m_heap_size++; - int i = m_heap_size; - lp_assert(o < m_priorities.size()); - m_priorities[o] = priority; - put_at(i, o); - while (i > 1 && m_priorities[m_heap[i >> 1]] > priority) { - swap_with_parent(i); - i >>= 1; - } -} -// This method can work with an element that is already in the queue. -// In this case the priority will be changed and the queue adjusted. -template void binary_heap_priority_queue::enqueue(unsigned o, const T & priority) { - if (o >= m_priorities.size()) { - if (o == 0) - resize(2); - else - resize(o << 1); // make the size twice larger - } - - if (m_heap_inverse[o] == -1) - enqueue_new(o, priority); - else - change_priority_for_existing(o, priority); -} - -template void binary_heap_priority_queue::change_priority_for_existing(unsigned o, const T & priority) { - if (m_priorities[o] > priority) { - decrease_priority(o, priority); - } else { - m_priorities[o] = priority; - fix_heap_under(m_heap_inverse[o]); - } -} - - -/// return the first element of the queue and removes it from the queue -template unsigned binary_heap_priority_queue::dequeue_and_get_priority(T & priority) { - lp_assert(m_heap_size != 0); - int ret = m_heap[1]; - priority = m_priorities[ret]; - put_the_last_at_the_top_and_fix_the_heap(); - return ret; -} - -template void binary_heap_priority_queue::fix_heap_under(unsigned i) { - while (true) { - unsigned smallest = i; - unsigned l = i << 1; - if (l <= m_heap_size && m_priorities[m_heap[l]] < m_priorities[m_heap[i]]) - smallest = l; - unsigned r = l + 1; - if (r <= m_heap_size && m_priorities[m_heap[r]] < m_priorities[m_heap[smallest]]) - smallest = r; - if (smallest != i) - swap_with_parent(smallest); - else - break; - i = smallest; - } -} - -template void binary_heap_priority_queue::put_the_last_at_the_top_and_fix_the_heap() { - if (m_heap_size > 1) { - put_at(1, m_heap[m_heap_size--]); - fix_heap_under(1); - } else { - m_heap_size--; - } -} -/// return the first element of the queue and removes it from the queue -template unsigned binary_heap_priority_queue::dequeue() { - lp_assert(m_heap_size > 0); - int ret = m_heap[1]; - put_the_last_at_the_top_and_fix_the_heap(); - m_heap_inverse[ret] = -1; - return ret; -} -template void binary_heap_priority_queue::print(std::ostream & out) { - vector index; - vector prs; - while (size()) { - T prior; - int j = dequeue_and_get_priority(prior); - index.push_back(j); - prs.push_back(prior); - out << "(" << j << ", " << prior << ")"; - } - out << std::endl; - // restore the queue - for (int i = 0; i < index.size(); i++) - enqueue(index[i], prs[i]); -} -} diff --git a/src/math/lp/binary_heap_upair_queue.cpp b/src/math/lp/binary_heap_upair_queue.cpp deleted file mode 100644 index f1ff1d639..000000000 --- a/src/math/lp/binary_heap_upair_queue.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/binary_heap_upair_queue_def.h" -namespace lp { -template binary_heap_upair_queue::binary_heap_upair_queue(unsigned int); -template binary_heap_upair_queue::binary_heap_upair_queue(unsigned int); -template unsigned binary_heap_upair_queue::dequeue_available_spot(); -template unsigned binary_heap_upair_queue::dequeue_available_spot(); -template void binary_heap_upair_queue::enqueue(unsigned int, unsigned int, int const&); -template void binary_heap_upair_queue::remove(unsigned int, unsigned int); -template void binary_heap_upair_queue::remove(unsigned int, unsigned int); -template void binary_heap_upair_queue::dequeue(unsigned int&, unsigned int&); -template void binary_heap_upair_queue::enqueue(unsigned int, unsigned int, unsigned int const&); -template void binary_heap_upair_queue::dequeue(unsigned int&, unsigned int&); -} diff --git a/src/math/lp/binary_heap_upair_queue.h b/src/math/lp/binary_heap_upair_queue.h deleted file mode 100644 index ce4542803..000000000 --- a/src/math/lp/binary_heap_upair_queue.h +++ /dev/null @@ -1,65 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include -#include -#include -#include "util/vector.h" -#include -#include -#include "math/lp/binary_heap_priority_queue.h" - - -typedef std::pair upair; - -namespace lp { -template -class binary_heap_upair_queue { - binary_heap_priority_queue m_q; - std::unordered_map m_pairs_to_index; - svector m_pairs; // inverse to index - svector m_available_spots; -public: - binary_heap_upair_queue(unsigned size); - - unsigned dequeue_available_spot(); - bool is_empty() const { return m_q.is_empty(); } - - unsigned size() const {return m_q.size(); } - - bool contains(unsigned i, unsigned j) const { return m_pairs_to_index.find(std::make_pair(i, j)) != m_pairs_to_index.end(); - } - - void remove(unsigned i, unsigned j); - bool ij_index_is_new(unsigned ij_index) const; - void enqueue(unsigned i, unsigned j, const T & priority); - void dequeue(unsigned & i, unsigned &j); - T get_priority(unsigned i, unsigned j) const; -#ifdef Z3DEBUG - bool pair_to_index_is_a_bijection() const; - bool available_spots_are_correct() const; - bool is_correct() const { - return m_q.is_consistent() && pair_to_index_is_a_bijection() && available_spots_are_correct(); - } -#endif - void resize(unsigned size) { m_q.resize(size); } -}; -} diff --git a/src/math/lp/binary_heap_upair_queue_def.h b/src/math/lp/binary_heap_upair_queue_def.h deleted file mode 100644 index 65485a6eb..000000000 --- a/src/math/lp/binary_heap_upair_queue_def.h +++ /dev/null @@ -1,126 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include "math/lp/lp_utils.h" -#include "math/lp/binary_heap_upair_queue.h" -namespace lp { -template binary_heap_upair_queue::binary_heap_upair_queue(unsigned size) : m_q(size), m_pairs(size) { - for (unsigned i = 0; i < size; i++) - m_available_spots.push_back(i); -} - -template unsigned -binary_heap_upair_queue::dequeue_available_spot() { - lp_assert(m_available_spots.empty() == false); - unsigned ret = m_available_spots.back(); - m_available_spots.pop_back(); - return ret; -} - -template void binary_heap_upair_queue::remove(unsigned i, unsigned j) { - upair p(i, j); - auto it = m_pairs_to_index.find(p); - if (it == m_pairs_to_index.end()) - return; // nothing to do - m_q.remove(it->second); - m_available_spots.push_back(it->second); - m_pairs_to_index.erase(it); -} - - -template bool binary_heap_upair_queue::ij_index_is_new(unsigned ij_index) const { - for (auto it : m_pairs_to_index) { - if (it.second == ij_index) - return false; - } - return true; -} - -template void binary_heap_upair_queue::enqueue(unsigned i, unsigned j, const T & priority) { - upair p(i, j); - auto it = m_pairs_to_index.find(p); - unsigned ij_index; - if (it == m_pairs_to_index.end()) { - // it is a new pair, let us find a spot for it - if (m_available_spots.empty()) { - // we ran out of empty spots - unsigned size_was = static_cast(m_pairs.size()); - unsigned new_size = size_was << 1; - for (unsigned i = size_was; i < new_size; i++) - m_available_spots.push_back(i); - m_pairs.resize(new_size); - } - ij_index = dequeue_available_spot(); - // lp_assert(ij_indexsecond; - } - m_q.enqueue(ij_index, priority); -} - -template void binary_heap_upair_queue::dequeue(unsigned & i, unsigned &j) { - lp_assert(!m_q.is_empty()); - unsigned ij_index = m_q.dequeue(); - upair & p = m_pairs[ij_index]; - i = p.first; - j = p.second; - m_available_spots.push_back(ij_index); - m_pairs_to_index.erase(p); -} - - -template T binary_heap_upair_queue::get_priority(unsigned i, unsigned j) const { - auto it = m_pairs_to_index.find(std::make_pair(i, j)); - if (it == m_pairs_to_index.end()) - return T(0xFFFFFF); // big number - return m_q.get_priority(it->second); -} - -#ifdef Z3DEBUG -template bool binary_heap_upair_queue::pair_to_index_is_a_bijection() const { - std::set tmp; - for (auto p : m_pairs_to_index) { - unsigned j = p.second; - unsigned size = tmp.size(); - tmp.insert(j); - if (tmp.size() == size) - return false; - } - return true; -} - -template bool binary_heap_upair_queue::available_spots_are_correct() const { - std::set tmp; - for (auto p : m_available_spots){ - tmp.insert(p); - } - if (tmp.size() != m_available_spots.size()) - return false; - for (auto it : m_pairs_to_index) - if (tmp.find(it.second) != tmp.end()) - return false; - return true; -} -#endif -} diff --git a/src/math/lp/bound_analyzer_on_row.h b/src/math/lp/bound_analyzer_on_row.h index 6084145c2..0008a0ee9 100644 --- a/src/math/lp/bound_analyzer_on_row.h +++ b/src/math/lp/bound_analyzer_on_row.h @@ -54,32 +54,40 @@ public : {} - static void analyze_row(const C & row, + static unsigned analyze_row(const C & row, unsigned bj, // basis column for the row const numeric_pair& rs, unsigned row_or_term_index, B & bp) { bound_analyzer_on_row a(row, bj, rs, row_or_term_index, bp); - a.analyze(); + return a.analyze(); } private: - void analyze() { + unsigned analyze() { + unsigned num_prop = 0; for (const auto & c : m_row) { if ((m_column_of_l == -2) && (m_column_of_u == -2)) - return; + return 0; analyze_bound_on_var_on_coeff(c.var(), c.coeff()); } + ++num_prop; if (m_column_of_u >= 0) limit_monoid_u_from_below(); else if (m_column_of_u == -1) limit_all_monoids_from_below(); + else + --num_prop; + ++num_prop; if (m_column_of_l >= 0) limit_monoid_l_from_above(); else if (m_column_of_l == -1) limit_all_monoids_from_above(); + else + --num_prop; + return num_prop; } bool bound_is_available(unsigned j, bool lower_bound) { diff --git a/src/math/lp/breakpoint.h b/src/math/lp/breakpoint.h deleted file mode 100644 index 40fab293f..000000000 --- a/src/math/lp/breakpoint.h +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once - -namespace lp { -enum breakpoint_type { - low_break, upper_break, fixed_break -}; -template -struct breakpoint { - unsigned m_j; // the basic column - breakpoint_type m_type; - X m_delta; - breakpoint(){} - breakpoint(unsigned j, X delta, breakpoint_type type):m_j(j), m_type(type), m_delta(delta) {} -}; -} diff --git a/src/math/lp/conversion_helper.h b/src/math/lp/conversion_helper.h deleted file mode 100644 index feb999743..000000000 --- a/src/math/lp/conversion_helper.h +++ /dev/null @@ -1,58 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -namespace lp { -template -struct conversion_helper { - static V get_lower_bound(const column_info & ci) { - return V(ci.get_lower_bound(), ci.lower_bound_is_strict()? 1 : 0); - } - - static V get_upper_bound(const column_info & ci) { - return V(ci.get_upper_bound(), ci.upper_bound_is_strict()? -1 : 0); - } -}; - -template<> -struct conversion_helper { - static double get_upper_bound(const column_info & ci) { - if (!ci.upper_bound_is_strict()) - return ci.get_upper_bound().get_double(); - double eps = 0.00001; - if (!ci.lower_bound_is_set()) - return ci.get_upper_bound().get_double() - eps; - eps = std::min((ci.get_upper_bound() - ci.get_lower_bound()).get_double() / 1000, eps); - return ci.get_upper_bound().get_double() - eps; - } - - static double get_lower_bound(const column_info & ci) { - if (!ci.lower_bound_is_strict()) - return ci.get_lower_bound().get_double(); - double eps = 0.00001; - if (!ci.upper_bound_is_set()) - return ci.get_lower_bound().get_double() + eps; - eps = std::min((ci.get_upper_bound() - ci.get_lower_bound()).get_double() / 1000, eps); - return ci.get_lower_bound().get_double() + eps; - } - -}; - -} diff --git a/src/math/lp/core_solver_pretty_printer.cpp b/src/math/lp/core_solver_pretty_printer.cpp index 74d2f6048..18bef8303 100644 --- a/src/math/lp/core_solver_pretty_printer.cpp +++ b/src/math/lp/core_solver_pretty_printer.cpp @@ -19,9 +19,6 @@ Revision History: --*/ #include "math/lp/numeric_pair.h" #include "math/lp/core_solver_pretty_printer_def.h" -template lp::core_solver_pretty_printer::core_solver_pretty_printer(const lp::lp_core_solver_base &, std::ostream & out); -template void lp::core_solver_pretty_printer::print(); -template lp::core_solver_pretty_printer::~core_solver_pretty_printer(); template lp::core_solver_pretty_printer::core_solver_pretty_printer(const lp::lp_core_solver_base &, std::ostream & out); template void lp::core_solver_pretty_printer::print(); template lp::core_solver_pretty_printer::~core_solver_pretty_printer(); diff --git a/src/math/lp/core_solver_pretty_printer.h b/src/math/lp/core_solver_pretty_printer.h index 3c0563c32..5bf29d511 100644 --- a/src/math/lp/core_solver_pretty_printer.h +++ b/src/math/lp/core_solver_pretty_printer.h @@ -59,7 +59,7 @@ class core_solver_pretty_printer { unsigned m_artificial_start; indexed_vector m_w_buff; indexed_vector m_ed_buff; - vector m_exact_column_norms; + public: core_solver_pretty_printer(const lp_core_solver_base & core_solver, std::ostream & out); @@ -85,14 +85,7 @@ public: } unsigned get_column_width(unsigned column); - - unsigned regular_cell_width(unsigned row, unsigned column, const std::string & name) { - return regular_cell_string(row, column, name).size(); - } - - std::string regular_cell_string(unsigned row, unsigned column, std::string name); - - + void set_coeff(vector& row, vector & row_signs, unsigned col, const T & t, string name); void print_x(); @@ -105,13 +98,7 @@ public: void print_lows(); void print_upps(); - - string get_exact_column_norm_string(unsigned col) { - return T_to_string(m_exact_column_norms[col]); - } - - void print_exact_norms(); - + void print_approx_norms(); void print(); diff --git a/src/math/lp/core_solver_pretty_printer_def.h b/src/math/lp/core_solver_pretty_printer_def.h index 23417b691..27aa6c75d 100644 --- a/src/math/lp/core_solver_pretty_printer_def.h +++ b/src/math/lp/core_solver_pretty_printer_def.h @@ -37,9 +37,8 @@ core_solver_pretty_printer::core_solver_pretty_printer(const lp_core_solve m_signs(core_solver.m_A.row_count(), vector(core_solver.m_A.column_count(), " ")), m_costs(ncols(), ""), m_cost_signs(ncols(), " "), - m_rs(ncols(), zero_of_type()), - m_w_buff(core_solver.m_w), - m_ed_buff(core_solver.m_ed) { + m_rs(ncols(), zero_of_type()) + { m_lower_bounds_title = "low"; m_upp_bounds_title = "upp"; m_exact_norm_title = "exact cn"; @@ -59,22 +58,13 @@ core_solver_pretty_printer::core_solver_pretty_printer(const lp_core_solve } template void core_solver_pretty_printer::init_costs() { - if (!m_core_solver.use_tableau()) { - vector local_y(m_core_solver.m_m()); - m_core_solver.solve_yB(local_y); - for (unsigned i = 0; i < ncols(); i++) { - if (m_core_solver.m_basis_heading[i] < 0) { - T t = m_core_solver.m_costs[i] - m_core_solver.m_A.dot_product_with_column(local_y, i); - set_coeff(m_costs, m_cost_signs, i, t, m_core_solver.column_name(i)); - } - } - } else { + for (unsigned i = 0; i < ncols(); i++) { if (m_core_solver.m_basis_heading[i] < 0) { set_coeff(m_costs, m_cost_signs, i, m_core_solver.m_d[i], m_core_solver.column_name(i)); } } - } + } template core_solver_pretty_printer::~core_solver_pretty_printer() { @@ -89,15 +79,7 @@ template void core_solver_pretty_printer::init_rs } } -template T core_solver_pretty_printer::current_column_norm() { - T ret = zero_of_type(); - for (auto i : m_core_solver.m_ed.m_index) - ret += m_core_solver.m_ed[i] * m_core_solver.m_ed[i]; - return ret; -} - template void core_solver_pretty_printer::init_m_A_and_signs() { - if (numeric_traits::precise() && m_core_solver.m_settings.use_tableau()) { for (unsigned column = 0; column < ncols(); column++) { vector t(nrows(), zero_of_type()); for (const auto & c : m_core_solver.m_A.m_columns[column]){ @@ -124,24 +106,7 @@ template void core_solver_pretty_printer::init_m_ name); m_rs[row] += t[row] * m_core_solver.m_x[column]; } - } - } else { - for (unsigned column = 0; column < ncols(); column++) { - m_core_solver.solve_Bd(column, m_ed_buff, m_w_buff); // puts the result into m_core_solver.m_ed - string name = m_core_solver.column_name(column); - for (unsigned row = 0; row < nrows(); row ++) { - set_coeff( - m_A[row], - m_signs[row], - column, - m_ed_buff[row], - name); - m_rs[row] += m_ed_buff[row] * m_core_solver.m_x[column]; - } - if (!m_core_solver.use_tableau()) - m_exact_column_norms.push_back(current_column_norm() + T(1)); // a conversion missing 1 -> T - } - } + } } template void core_solver_pretty_printer::init_column_widths() { @@ -174,7 +139,7 @@ template void core_solver_pretty_printer::adjust_ case column_type::free_column: break; default: - lp_assert(false); + UNREACHABLE(); break; } } @@ -190,21 +155,10 @@ template unsigned core_solver_pretty_printer:: ge w = cellw; } } - if (!m_core_solver.use_tableau()) { - w = std::max(w, (unsigned)T_to_string(m_exact_column_norms[column]).size()); - if (!m_core_solver.m_column_norms.empty()) - w = std::max(w, (unsigned)T_to_string(m_core_solver.m_column_norms[column]).size()); - } + return w; } -template std::string core_solver_pretty_printer::regular_cell_string(unsigned row, unsigned /* column */, std::string name) { - T t = fabs(m_core_solver.m_ed[row]); - if ( t == 1) return name; - return T_to_string(t) + name; -} - - template void core_solver_pretty_printer::set_coeff(vector& row, vector & row_signs, unsigned col, const T & t, string name) { if (numeric_traits::is_zero(t)) { return; @@ -315,41 +269,15 @@ template void core_solver_pretty_printer::print_u m_out << std::endl; } -template void core_solver_pretty_printer::print_exact_norms() { - if (m_core_solver.use_tableau()) return; - int blanks = m_title_width + 1 - static_cast(m_exact_norm_title.size()); - m_out << m_exact_norm_title; - print_blanks_local(blanks, m_out); - for (unsigned i = 0; i < ncols(); i++) { - string s = get_exact_column_norm_string(i); - int blanks = m_column_widths[i] - static_cast(s.size()); - print_blanks_local(blanks, m_out); - m_out << s << " "; - } - m_out << std::endl; -} template void core_solver_pretty_printer::print_approx_norms() { - if (m_core_solver.use_tableau()) return; - int blanks = m_title_width + 1 - static_cast(m_approx_norm_title.size()); - m_out << m_approx_norm_title; - print_blanks_local(blanks, m_out); - for (unsigned i = 0; i < ncols(); i++) { - string s = T_to_string(m_core_solver.m_column_norms[i]); - int blanks = m_column_widths[i] - static_cast(s.size()); - print_blanks_local(blanks, m_out); - m_out << s << " "; - } - m_out << std::endl; + return; } template void core_solver_pretty_printer::print() { for (unsigned i = 0; i < nrows(); i++) { print_row(i); } - print_exact_norms(); - if (!m_core_solver.m_column_norms.empty()) - print_approx_norms(); m_out << std::endl; if (m_core_solver.inf_set().size()) { m_out << "inf columns: "; diff --git a/src/math/lp/dense_matrix.cpp b/src/math/lp/dense_matrix.cpp index f12e688d3..25fc65a5d 100644 --- a/src/math/lp/dense_matrix.cpp +++ b/src/math/lp/dense_matrix.cpp @@ -21,11 +21,6 @@ Revision History: #include "math/lp/dense_matrix_def.h" #ifdef Z3DEBUG #include "util/vector.h" -template lp::dense_matrix lp::operator*(lp::matrix&, lp::matrix&); -template void lp::dense_matrix::apply_from_left(vector &); -template lp::dense_matrix::dense_matrix(lp::matrix const*); -template lp::dense_matrix::dense_matrix(unsigned int, unsigned int); -template lp::dense_matrix& lp::dense_matrix::operator=(lp::dense_matrix const&); template lp::dense_matrix::dense_matrix(unsigned int, unsigned int); template lp::dense_matrix >::dense_matrix(lp::matrix > const*); template void lp::dense_matrix >::apply_from_left(vector&); @@ -35,6 +30,5 @@ template lp::dense_matrix >::dense_matrix(uns template lp::dense_matrix >& lp::dense_matrix >::operator=(lp::dense_matrix > const&); template lp::dense_matrix > lp::operator* >(lp::matrix >&, lp::matrix >&); template void lp::dense_matrix >::apply_from_right( vector< lp::mpq> &); -template void lp::dense_matrix::apply_from_right(vector &); template void lp::dense_matrix::apply_from_left(vector&); #endif diff --git a/src/math/lp/dense_matrix.h b/src/math/lp/dense_matrix.h index 342fc7aeb..fcc85cdd1 100644 --- a/src/math/lp/dense_matrix.h +++ b/src/math/lp/dense_matrix.h @@ -90,11 +90,7 @@ public: void set_elem(unsigned i, unsigned j, const T& val) { m_values[i * m_n + j] = val; } - // This method pivots row i to row i0 by muliplying row i by - // alpha and adding it to row i0. - void pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, - const double & pivot_epsilon); - + // This method pivots void swap_columns(unsigned a, unsigned b); void swap_rows(unsigned a, unsigned b); diff --git a/src/math/lp/dense_matrix_def.h b/src/math/lp/dense_matrix_def.h index 8eb9ad5dd..e850a9acd 100644 --- a/src/math/lp/dense_matrix_def.h +++ b/src/math/lp/dense_matrix_def.h @@ -150,17 +150,6 @@ template void dense_matrix::apply_from_left_to_X( } } -// This method pivots row i to row i0 by muliplying row i by -// alpha and adding it to row i0. -template void dense_matrix::pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, - const double & pivot_epsilon) { - for (unsigned j = 0; j < m_n; j++) { - m_values[i0 * m_n + j] += m_values[i * m_n + j] * alpha; - if (fabs(m_values[i0 + m_n + j]) < pivot_epsilon) { - m_values[i0 + m_n + j] = numeric_traits::zero();; - } - } -} template void dense_matrix::swap_columns(unsigned a, unsigned b) { for (unsigned i = 0; i < m_m; i++) { diff --git a/src/math/lp/emonics.cpp b/src/math/lp/emonics.cpp index 21bfe6792..bcdb81dd8 100644 --- a/src/math/lp/emonics.cpp +++ b/src/math/lp/emonics.cpp @@ -68,8 +68,8 @@ void emonics::pop(unsigned n) { TRACE("nla_solver_mons", tout << "pop: " << n << "\n";); SASSERT(invariant()); for (unsigned i = 0; i < n; ++i) { - m_u_f_stack.pop_scope(1); m_ve.pop(1); + m_u_f_stack.pop_scope(1); } SASSERT(invariant()); SASSERT(monics_are_canonized()); diff --git a/src/math/lp/emonics.h b/src/math/lp/emonics.h index d26d96ad6..e4f4f4848 100644 --- a/src/math/lp/emonics.h +++ b/src/math/lp/emonics.h @@ -81,8 +81,8 @@ class emonics { } }; - union_find m_u_f; trail_stack m_u_f_stack; + union_find m_u_f; mutable svector m_find_key; // the key used when looking for a monic with the specific variables var_eqs& m_ve; mutable vector m_monics; // set of monics @@ -125,8 +125,8 @@ public: other calls to push/pop to the var_eqs should take place. */ emonics(var_eqs& ve): - m_u_f(*this), m_u_f_stack(), + m_u_f(*this), m_ve(ve), m_visited(0), m_cg_hash(*this), diff --git a/src/math/lp/eta_matrix.cpp b/src/math/lp/eta_matrix.cpp deleted file mode 100644 index 77f538426..000000000 --- a/src/math/lp/eta_matrix.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "util/vector.h" -#include "math/lp/numeric_pair.h" -#include "math/lp/eta_matrix_def.h" -#ifdef Z3DEBUG -template double lp::eta_matrix::get_elem(unsigned int, unsigned int) const; -template lp::mpq lp::eta_matrix::get_elem(unsigned int, unsigned int) const; -template lp::mpq lp::eta_matrix >::get_elem(unsigned int, unsigned int) const; -#endif -template void lp::eta_matrix::apply_from_left(vector&, lp::lp_settings&); -template void lp::eta_matrix::apply_from_right(vector&); -template void lp::eta_matrix::conjugate_by_permutation(lp::permutation_matrix&); -template void lp::eta_matrix::apply_from_left(vector&, lp::lp_settings&); -template void lp::eta_matrix::apply_from_right(vector&); -template void lp::eta_matrix::conjugate_by_permutation(lp::permutation_matrix&); -template void lp::eta_matrix >::apply_from_left(vector >&, lp::lp_settings&); -template void lp::eta_matrix >::apply_from_right(vector&); -template void lp::eta_matrix >::conjugate_by_permutation(lp::permutation_matrix >&); -template void lp::eta_matrix::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); -template void lp::eta_matrix::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); -template void lp::eta_matrix >::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); -template void lp::eta_matrix >::apply_from_right(lp::indexed_vector&); -template void lp::eta_matrix::apply_from_right(lp::indexed_vector&); -template void lp::eta_matrix::apply_from_right(lp::indexed_vector&); diff --git a/src/math/lp/eta_matrix.h b/src/math/lp/eta_matrix.h deleted file mode 100644 index 14fe3ffea..000000000 --- a/src/math/lp/eta_matrix.h +++ /dev/null @@ -1,98 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "math/lp/tail_matrix.h" -#include "math/lp/permutation_matrix.h" -namespace lp { - -// This is the sum of a unit matrix and a one-column matrix -template -class eta_matrix - : public tail_matrix { -#ifdef Z3DEBUG - unsigned m_length; -#endif - unsigned m_column_index; -public: - sparse_vector m_column_vector; - T m_diagonal_element; -#ifdef Z3DEBUG - eta_matrix(unsigned column_index, unsigned length): -#else - eta_matrix(unsigned column_index): -#endif - -#ifdef Z3DEBUG - m_length(length), -#endif - m_column_index(column_index) {} - - bool is_dense() const override { return false; } - - void print(std::ostream & out) { - print_matrix(*this, out); - } - - bool is_unit() { - return m_column_vector.size() == 0 && m_diagonal_element == 1; - } - - bool set_diagonal_element(T const & diagonal_element) { - m_diagonal_element = diagonal_element; - return !lp_settings::is_eps_small_general(diagonal_element, 1e-12); - } - - const T & get_diagonal_element() const { - return m_diagonal_element; - } - - void apply_from_left(vector & w, lp_settings & ) override; - - template - void apply_from_left_local(indexed_vector & w, lp_settings & settings); - - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override { - apply_from_left_local(w, settings); - } - - - void push_back(unsigned row_index, T val ) { - lp_assert(row_index != m_column_index); - m_column_vector.push_back(row_index, val); - } - - void apply_from_right(vector & w) override; - void apply_from_right(indexed_vector & w) override; - -#ifdef Z3DEBUG - T get_elem(unsigned i, unsigned j) const override; - unsigned row_count() const override { return m_length; } - unsigned column_count() const override { return m_length; } - void set_number_of_rows(unsigned m) override { m_length = m; } - void set_number_of_columns(unsigned n) override { m_length = n; } -#endif - void divide_by_diagonal_element() { - m_column_vector.divide(m_diagonal_element); - } - void conjugate_by_permutation(permutation_matrix & p); -}; -} diff --git a/src/math/lp/eta_matrix_def.h b/src/math/lp/eta_matrix_def.h deleted file mode 100644 index a6c908572..000000000 --- a/src/math/lp/eta_matrix_def.h +++ /dev/null @@ -1,151 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "math/lp/eta_matrix.h" -namespace lp { - -// This is the sum of a unit matrix and a one-column matrix -template -void eta_matrix::apply_from_left(vector & w, lp_settings & ) { - auto & w_at_column_index = w[m_column_index]; - for (auto & it : m_column_vector.m_data) { - w[it.first] += w_at_column_index * it.second; - } - w_at_column_index /= m_diagonal_element; -} -template -template -void eta_matrix:: -apply_from_left_local(indexed_vector & w, lp_settings & settings) { - const L w_at_column_index = w[m_column_index]; - if (is_zero(w_at_column_index)) return; - - if (settings.abs_val_is_smaller_than_drop_tolerance(w[m_column_index] /= m_diagonal_element)) { - w[m_column_index] = zero_of_type(); - w.erase_from_index(m_column_index); - } - - for (auto & it : m_column_vector.m_data) { - unsigned i = it.first; - if (is_zero(w[i])) { - L v = w[i] = w_at_column_index * it.second; - if (settings.abs_val_is_smaller_than_drop_tolerance(v)) { - w[i] = zero_of_type(); - continue; - } - w.m_index.push_back(i); - } else { - L v = w[i] += w_at_column_index * it.second; - if (settings.abs_val_is_smaller_than_drop_tolerance(v)) { - w[i] = zero_of_type(); - w.erase_from_index(i); - } - } - } -} -template -void eta_matrix::apply_from_right(vector & w) { -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // auto clone_w = clone_vector(w, get_number_of_rows()); - // deb.apply_from_right(clone_w); -#endif - T t = w[m_column_index] / m_diagonal_element; - for (auto & it : m_column_vector.m_data) { - t += w[it.first] * it.second; - } - w[m_column_index] = t; -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(clone_w, w, get_number_of_rows())); - // delete clone_w; -#endif -} -template -void eta_matrix::apply_from_right(indexed_vector & w) { - if (w.m_index.empty()) - return; -#ifdef Z3DEBUG - // vector wcopy(w.m_data); - // apply_from_right(wcopy); -#endif - T & t = w[m_column_index]; - t /= m_diagonal_element; - bool was_in_index = (!numeric_traits::is_zero(t)); - - for (auto & it : m_column_vector.m_data) { - t += w[it.first] * it.second; - } - - if (numeric_traits::precise() ) { - if (!numeric_traits::is_zero(t)) { - if (!was_in_index) - w.m_index.push_back(m_column_index); - } else { - if (was_in_index) - w.erase_from_index(m_column_index); - } - } else { - if (!lp_settings::is_eps_small_general(t, 1e-14)) { - if (!was_in_index) - w.m_index.push_back(m_column_index); - } else { - if (was_in_index) - w.erase_from_index(m_column_index); - t = zero_of_type(); - } - } - -#ifdef Z3DEBUG - // lp_assert(w.is_OK()); - // lp_assert(vectors_are_equal(wcopy, w.m_data)); -#endif -} -#ifdef Z3DEBUG -template -T eta_matrix::get_elem(unsigned i, unsigned j) const { - if (j == m_column_index){ - if (i == j) { - return 1 / m_diagonal_element; - } - return m_column_vector[i]; - } - - return i == j ? numeric_traits::one() : numeric_traits::zero(); -} -#endif -template -void eta_matrix::conjugate_by_permutation(permutation_matrix & p) { - // this = p * this * p(-1) -#ifdef Z3DEBUG - // auto rev = p.get_reverse(); - // auto deb = ((*this) * rev); - // deb = p * deb; -#endif - m_column_index = p.get_rev(m_column_index); - for (auto & pair : m_column_vector.m_data) { - pair.first = p.get_rev(pair.first); - } -#ifdef Z3DEBUG - // lp_assert(deb == *this); -#endif -} -} diff --git a/src/math/lp/general_matrix.h b/src/math/lp/general_matrix.h index 749fa30dc..a4f6693a2 100644 --- a/src/math/lp/general_matrix.h +++ b/src/math/lp/general_matrix.h @@ -114,9 +114,6 @@ public: } } - void copy_column_to_indexed_vector(unsigned entering, indexed_vector &w ) const { - lp_assert(false); // not implemented - } general_matrix operator*(const general_matrix & m) const { lp_assert(m.row_count() == column_count()); general_matrix ret(row_count(), m.column_count()); @@ -172,24 +169,7 @@ public: return r; } - // bool create_upper_triangle(general_matrix& m, vector& x) { - // for (unsigned i = 1; i < m.row_count(); i++) { - // lp_assert(false); // to be continued - // } - // } - - // bool solve_A_x_equal_b(const general_matrix& m, vector& x, const vector& b) const { - // auto m_copy = m; - // // for square matrices - // lp_assert(row_count() == b.size()); - // lp_assert(x.size() == column_count()); - // lp_assert(row_count() == column_count()); - // x = b; - // create_upper_triangle(copy_of_m, x); - // solve_on_triangle(copy_of_m, x); - // } - // - + void transpose_rows(unsigned i, unsigned l) { lp_assert(i != l); m_row_permutation.transpose_from_right(i, l); diff --git a/src/math/lp/gomory.cpp b/src/math/lp/gomory.cpp index 2d187c9f4..2ecbc49ac 100644 --- a/src/math/lp/gomory.cpp +++ b/src/math/lp/gomory.cpp @@ -377,10 +377,21 @@ bool gomory::is_gomory_cut_target(const row_strip& row) { } int gomory::find_basic_var() { - int result = -1; unsigned n = 0; + int result = -1; unsigned min_row_size = UINT_MAX; - // Prefer smaller row size + +#if 0 + result = lia.select_int_infeasible_var(); + + if (result == -1) + return result; + + const row_strip& row = lra.get_row(lia.row_of_basic_column(result)); + if (is_gomory_cut_target(row)) + return result; + result = -1; +#endif for (unsigned j : lra.r_basis()) { if (!lia.column_is_int_inf(j)) @@ -389,6 +400,7 @@ int gomory::find_basic_var() { if (!is_gomory_cut_target(row)) continue; IF_VERBOSE(20, lia.display_row_info(verbose_stream(), lia.row_of_basic_column(j))); + // Prefer smaller row size if (min_row_size == UINT_MAX || 2*row.size() < min_row_size || (4*row.size() < 5*min_row_size && lia.random() % (++n) == 0)) { diff --git a/src/math/lp/hnf_cutter.cpp b/src/math/lp/hnf_cutter.cpp index c1f93c9d8..3c4ea10ab 100644 --- a/src/math/lp/hnf_cutter.cpp +++ b/src/math/lp/hnf_cutter.cpp @@ -248,9 +248,8 @@ branch y_i >= ceil(y0_i) is impossible. bool hnf_cutter::init_terms_for_hnf_cut() { clear(); - for (unsigned i = 0; i < lra.terms().size() && !is_full(); i++) { + for (unsigned i = 0; i < lra.terms().size() && !is_full(); i++) try_add_term_to_A_for_hnf(tv::term(i)); - } return hnf_has_var_with_non_integral_value(); } diff --git a/src/math/lp/indexed_value.h b/src/math/lp/indexed_value.h index c48376470..92d8f2adf 100644 --- a/src/math/lp/indexed_value.h +++ b/src/math/lp/indexed_value.h @@ -43,15 +43,4 @@ public: m_value = val; } }; -#ifdef Z3DEBUG -template -bool check_vector_for_small_values(indexed_vector & w, lp_settings & settings) { - for (unsigned i : w.m_index) { - const X & v = w[i]; - if ((!is_zero(v)) && settings.abs_val_is_smaller_than_drop_tolerance(v)) - return false; - } - return true; -} -#endif } diff --git a/src/math/lp/indexed_vector.cpp b/src/math/lp/indexed_vector.cpp index d7cde2e1d..fe0892541 100644 --- a/src/math/lp/indexed_vector.cpp +++ b/src/math/lp/indexed_vector.cpp @@ -20,10 +20,6 @@ Revision History: #include "util/vector.h" #include "math/lp/indexed_vector_def.h" namespace lp { -template void indexed_vector::clear(); -template void indexed_vector::clear_all(); -template void indexed_vector::erase_from_index(unsigned int); -template void indexed_vector::set_value(const double&, unsigned int); template void indexed_vector::clear(); template void indexed_vector::clear(); template void indexed_vector::clear_all(); @@ -32,22 +28,8 @@ template void indexed_vector::resize(unsigned int); template void indexed_vector::resize(unsigned int); template void indexed_vector::set_value(const mpq&, unsigned int); template void indexed_vector::set_value(const unsigned&, unsigned int); -#ifdef Z3DEBUG -template bool indexed_vector::is_OK() const; -template bool indexed_vector::is_OK() const; -template bool indexed_vector::is_OK() const; -template bool indexed_vector >::is_OK() const; -#endif template void lp::indexed_vector< lp::mpq>::print(std::basic_ostream > &); -template void lp::indexed_vector::print(std::basic_ostream > &); template void lp::indexed_vector >::print(std::ostream&); } -// template void lp::print_vector(vector const&, std::ostream&); -// template void lp::print_vector(vector const&, std::ostream&); -// template void lp::print_vector(vector const&, std::ostream&); -// template void lp::print_vector >(vector> const&, std::ostream&); -template void lp::indexed_vector::resize(unsigned int); -// template void lp::print_vector< lp::mpq>(vector< lp::mpq> const &, std::basic_ostream > &); -// template void lp::print_vector >(vector> const&, std::ostream&); template void lp::indexed_vector >::erase_from_index(unsigned int); diff --git a/src/math/lp/indexed_vector.h b/src/math/lp/indexed_vector.h index 017a25f6b..9f3119e9a 100644 --- a/src/math/lp/indexed_vector.h +++ b/src/math/lp/indexed_vector.h @@ -99,47 +99,9 @@ public: return m_data[i]; } - void clean_up() { -#if 0==1 - for (unsigned k = 0; k < m_index.size(); k++) { - unsigned i = m_index[k]; - T & v = m_data[i]; - if (lp_settings::is_eps_small_general(v, 1e-14)) { - v = zero_of_type(); - m_index.erase(m_index.begin() + k--); - } - } -#endif - vector index_copy; - for (unsigned i : m_index) { - T & v = m_data[i]; - if (!lp_settings::is_eps_small_general(v, 1e-14)) { - index_copy.push_back(i); - } else if (!numeric_traits::is_zero(v)) { - v = zero_of_type(); - } - } - m_index = index_copy; - } - void erase_from_index(unsigned j); - - void add_value_at_index_with_drop_tolerance(unsigned j, const T& val_to_add) { - T & v = m_data[j]; - bool was_zero = is_zero(v); - v += val_to_add; - if (lp_settings::is_eps_small_general(v, 1e-14)) { - v = zero_of_type(); - if (!was_zero) { - erase_from_index(j); - } - } else { - if (was_zero) - m_index.push_back(j); - } - } - + void add_value_at_index(unsigned j, const T& val_to_add) { T & v = m_data[j]; bool was_zero = is_zero(v); @@ -153,18 +115,6 @@ public: } } - void restore_index_and_clean_from_data() { - m_index.resize(0); - for (unsigned i = 0; i < m_data.size(); i++) { - T & v = m_data[i]; - if (lp_settings::is_eps_small_general(v, 1e-14)) { - v = zero_of_type(); - } else { - m_index.push_back(i); - } - } - } - struct ival { unsigned m_var; const T & m_coeff; @@ -215,9 +165,6 @@ public: } -#ifdef Z3DEBUG - bool is_OK() const; -#endif void print(std::ostream & out); }; } diff --git a/src/math/lp/indexed_vector_def.h b/src/math/lp/indexed_vector_def.h index 0e25ee271..034325088 100644 --- a/src/math/lp/indexed_vector_def.h +++ b/src/math/lp/indexed_vector_def.h @@ -24,14 +24,6 @@ Revision History: #include "math/lp/lp_settings.h" namespace lp { -template -void print_sparse_vector(const vector & t, std::ostream & out) { - for (unsigned i = 0; i < t.size(); i++) { - if (is_zero(t[i]))continue; - out << "[" << i << "] = " << t[i] << ", "; - } - out << std::endl; -} void print_vector_as_doubles(const vector & t, std::ostream & out) { for (unsigned i = 0; i < t.size(); i++) @@ -43,7 +35,7 @@ template void indexed_vector::resize(unsigned data_size) { clear(); m_data.resize(data_size, numeric_traits::zero()); - lp_assert(is_OK()); + } template @@ -72,33 +64,6 @@ void indexed_vector::erase_from_index(unsigned j) { m_index.erase(it); } -#ifdef Z3DEBUG -template -bool indexed_vector::is_OK() const { - return true; - const double drop_eps = 1e-14; - for (unsigned i = 0; i < m_data.size(); i++) { - if (!is_zero(m_data[i]) && lp_settings::is_eps_small_general(m_data[i], drop_eps)) { - return false; - } - if (lp_settings::is_eps_small_general(m_data[i], drop_eps) != (std::find(m_index.begin(), m_index.end(), i) == m_index.end())) { - return false; - } - } - - std::unordered_set s; - for (unsigned i : m_index) { - //no duplicates!!! - if (s.find(i) != s.end()) - return false; - s.insert(i); - if (i >= m_data.size()) - return false; - } - - return true; -} -#endif template void indexed_vector::print(std::ostream & out) { out << "m_index " << std::endl; diff --git a/src/math/lp/indexer_of_constraints.h b/src/math/lp/indexer_of_constraints.h deleted file mode 100644 index 9bda22bc1..000000000 --- a/src/math/lp/indexer_of_constraints.h +++ /dev/null @@ -1,45 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - Nikolaj Bjorner (nbjorner) - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "math/lp/binary_heap_priority_queue.h" -namespace lp { - -class indexer_of_constraints { - binary_heap_priority_queue m_queue_of_released_indices; - unsigned m_max; -public: - indexer_of_constraints() :m_max(0) {} - unsigned get_new_index() { - unsigned ret; - if (m_queue_of_released_indices.is_empty()) { - ret = m_max++; - } - else { - ret = m_queue_of_released_indices.dequeue(); - } - return ret; - }; - void release_index(unsigned i) { - m_queue_of_released_indices.enqueue(i, i); - }; - unsigned max() const { return m_max; } -}; -} diff --git a/src/math/lp/int_branch.cpp b/src/math/lp/int_branch.cpp index fc7b5c3ec..da34f77fd 100644 --- a/src/math/lp/int_branch.cpp +++ b/src/math/lp/int_branch.cpp @@ -52,16 +52,22 @@ lia_move int_branch::create_branch_on_column(int j) { int int_branch::find_inf_int_base_column() { + +#if 0 + return lia.select_int_infeasible_var(); +#endif + int result = -1; mpq range; mpq new_range; - mpq small_range_thresold(1024); + mpq small_value(1024); unsigned n = 0; lar_core_solver & lcs = lra.m_mpq_lar_core_solver; unsigned prev_usage = 0; // to quiet down the compile unsigned k = 0; unsigned usage; unsigned j; + // this loop looks for a column with the most usages, but breaks when // a column with a small span of bounds is found for (; k < lra.r_basis().size(); k++) { @@ -69,12 +75,13 @@ int int_branch::find_inf_int_base_column() { if (!lia.column_is_int_inf(j)) continue; usage = lra.usage_in_terms(j); - if (lia.is_boxed(j) && (range = lcs.m_r_upper_bounds()[j].x - lcs.m_r_lower_bounds()[j].x - rational(2*usage)) <= small_range_thresold) { + if (lia.is_boxed(j) && (range = lcs.m_r_upper_bounds()[j].x - lcs.m_r_lower_bounds()[j].x - rational(2*usage)) <= small_value) { result = j; k++; n = 1; break; } + if (n == 0 || usage > prev_usage) { result = j; prev_usage = usage; diff --git a/src/math/lp/int_solver.cpp b/src/math/lp/int_solver.cpp index 3aaf8f29e..6c34ce16f 100644 --- a/src/math/lp/int_solver.cpp +++ b/src/math/lp/int_solver.cpp @@ -344,7 +344,6 @@ bool int_solver::get_freedom_interval_for_column(unsigned j, bool & inf_l, impq set_upper(u, inf_u, upper_bound(j) - xj); - lp_assert(settings().use_tableau()); const auto & A = lra.A_r(); TRACE("random_update", tout << "m = " << m << "\n";); @@ -633,4 +632,73 @@ bool int_solver::non_basic_columns_are_at_bounds() const { return true; } +int int_solver::select_int_infeasible_var() { + int result = -1; + mpq range; + mpq new_range; + mpq small_value(1024); + unsigned n = 0; + lar_core_solver & lcs = lra.m_mpq_lar_core_solver; + unsigned prev_usage = 0; // to quiet down the compile + unsigned k = 0; + unsigned usage; + unsigned j; + + enum state { small_box, is_small_value, any_value, not_found }; + state st = not_found; + + // 1. small box + // 2. small value + // 3. any value + for (; k < lra.r_basis().size(); k++) { + j = lra.r_basis()[k]; + if (!column_is_int_inf(j)) + continue; + usage = lra.usage_in_terms(j); + if (is_boxed(j) && (new_range = lcs.m_r_upper_bounds()[j].x - lcs.m_r_lower_bounds()[j].x - rational(2*usage)) <= small_value) { + SASSERT(!is_fixed(j)); + if (st != small_box) { + n = 0; + st = small_box; + } + if (n == 0 || new_range < range) { + result = j; + range = new_range; + n = 1; + } + else if (new_range == range && (random() % (++n) == 0)) { + result = j; + } + continue; + } + if (st == small_box) + continue; + impq const& value = get_value(j); + if (abs(value.x) < small_value || + (has_upper(j) && small_value > upper_bound(j).x - value.x) || + (has_lower(j) && small_value > value.x - lower_bound(j).x)) { + if (st != is_small_value) { + n = 0; + st = is_small_value; + } + if (random() % (++n) == 0) + result = j; + } + if (st == is_small_value) + continue; + SASSERT(st == not_found || st == any_value); + st = any_value; + if (n == 0 /*|| usage > prev_usage*/) { + result = j; + prev_usage = usage; + n = 1; + } + else if (usage > 0 && /*usage == prev_usage && */ (random() % (++n) == 0)) + result = j; + } + + return result; +} + + } diff --git a/src/math/lp/int_solver.h b/src/math/lp/int_solver.h index c348f27f2..822e1cf1e 100644 --- a/src/math/lp/int_solver.h +++ b/src/math/lp/int_solver.h @@ -129,5 +129,8 @@ public: void find_feasible_solution(); lia_move hnf_cut(); void patch_nbasic_column(unsigned j) { m_patcher.patch_nbasic_column(j); } + + int select_int_infeasible_var(); + }; } diff --git a/src/math/lp/lar_constraints.h b/src/math/lp/lar_constraints.h index 8e6311683..f8cffbe57 100644 --- a/src/math/lp/lar_constraints.h +++ b/src/math/lp/lar_constraints.h @@ -44,7 +44,7 @@ inline std::string lconstraint_kind_string(lconstraint_kind t) { case EQ: return std::string("="); case NE: return std::string("!="); } - lp_unreachable(); + UNREACHABLE(); return std::string(); // it is unreachable } diff --git a/src/math/lp/lar_core_solver.h b/src/math/lp/lar_core_solver.h index bcd33966f..06ef4d50b 100644 --- a/src/math/lp/lar_core_solver.h +++ b/src/math/lp/lar_core_solver.h @@ -12,24 +12,17 @@ Author: #include "math/lp/lp_core_solver_base.h" #include #include "math/lp/indexed_vector.h" -#include "math/lp/binary_heap_priority_queue.h" -#include "math/lp/breakpoint.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/stacked_vector.h" -#include "math/lp/lar_solution_signature.h" #include "util/stacked_value.h" namespace lp { class lar_core_solver { - // m_sign_of_entering is set to 1 if the entering variable needs - // to grow and is set to -1 otherwise - int m_sign_of_entering_delta; vector> m_infeasible_linear_combination; int m_infeasible_sum_sign; // todo: get rid of this field vector> m_right_sides_dummy; vector m_costs_dummy; - vector m_d_right_sides_dummy; - vector m_d_costs_dummy; + public: stacked_value m_stacked_simplex_strategy; stacked_vector m_column_types; @@ -42,23 +35,9 @@ public: vector m_r_basis; vector m_r_nbasis; vector m_r_heading; - stacked_vector m_r_columns_nz; - stacked_vector m_r_rows_nz; - // d - solver fields, for doubles - vector m_d_x; // the solution in doubles - vector m_d_lower_bounds; - vector m_d_upper_bounds; - static_matrix m_d_A; - stacked_vector m_d_pushed_basis; - vector m_d_basis; - vector m_d_nbasis; - vector m_d_heading; - lp_primal_core_solver> m_r_solver; // solver in rational numbers - - lp_primal_core_solver m_d_solver; // solver in doubles lar_core_solver( lp_settings & settings, @@ -66,6 +45,7 @@ public: ); lp_settings & settings() { return m_r_solver.m_settings;} + const lp_settings & settings() const { return m_r_solver.m_settings;} int get_infeasible_sum_sign() const { return m_infeasible_sum_sign; } @@ -79,8 +59,7 @@ public: column_type get_column_type(unsigned j) { return m_column_types[j];} - void calculate_pivot_row(unsigned i); - + void print_pivot_row(std::ostream & out, unsigned row_index) const { for (unsigned j : m_r_solver.m_pivot_row.m_index) { if (numeric_traits::is_pos(m_r_solver.m_pivot_row.m_data[j])) @@ -96,21 +75,9 @@ public: m_r_solver.print_column_bound_info(m_r_solver.m_basis[row_index], out); } - - void advance_on_sorted_breakpoints(unsigned entering); - - void change_slope_on_breakpoint(unsigned entering, breakpoint> * b, mpq & slope_at_entering); - - bool row_is_infeasible(unsigned row); - - bool row_is_evidence(unsigned row); - - bool find_evidence_row(); - + void prefix_r(); - void prefix_d(); - unsigned m_m() const { return m_r_A.row_count(); } unsigned m_n() const { return m_r_A.column_count(); } @@ -122,8 +89,6 @@ public: template int get_sign(const L & v) { return v > zero_of_type() ? 1 : (v < zero_of_type() ? -1 : 0); } - void fill_evidence(unsigned row); - unsigned get_number_of_non_ints() const; void solve(); @@ -136,422 +101,40 @@ public: void fill_not_improvable_zero_sum(); - void pop_basis(unsigned k) { - if (!settings().use_tableau()) { - m_r_pushed_basis.pop(k); - m_r_basis = m_r_pushed_basis(); - m_r_solver.init_basis_heading_and_non_basic_columns_vector(); - m_d_pushed_basis.pop(k); - m_d_basis = m_d_pushed_basis(); - m_d_solver.init_basis_heading_and_non_basic_columns_vector(); - } else { - m_d_basis = m_r_basis; - m_d_nbasis = m_r_nbasis; - m_d_heading = m_r_heading; - } - } - void push() { lp_assert(m_r_solver.basis_heading_is_correct()); - lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); lp_assert(m_column_types.size() == m_r_A.column_count()); m_stacked_simplex_strategy = settings().simplex_strategy(); m_stacked_simplex_strategy.push(); m_column_types.push(); // rational - if (!settings().use_tableau()) - m_r_A.push(); m_r_lower_bounds.push(); m_r_upper_bounds.push(); - if (!settings().use_tableau()) { - push_vector(m_r_pushed_basis, m_r_basis); - push_vector(m_r_columns_nz, m_r_solver.m_columns_nz); - push_vector(m_r_rows_nz, m_r_solver.m_rows_nz); - } - m_d_A.push(); - if (!settings().use_tableau()) - push_vector(m_d_pushed_basis, m_d_basis); + + } - template - void push_vector(stacked_vector & pushed_vector, const vector & vector) { - lp_assert(pushed_vector.size() <= vector.size()); - for (unsigned i = 0; i < vector.size();i++) { - if (i == pushed_vector.size()) { - pushed_vector.push_back(vector[i]); - } else { - pushed_vector[i] = vector[i]; - } - } - pushed_vector.push(); - } - - void pop_markowitz_counts(unsigned k) { - m_r_columns_nz.pop(k); - m_r_rows_nz.pop(k); - m_r_solver.m_columns_nz.resize(m_r_columns_nz.size()); - m_r_solver.m_rows_nz.resize(m_r_rows_nz.size()); - for (unsigned i = 0; i < m_r_columns_nz.size(); i++) - m_r_solver.m_columns_nz[i] = m_r_columns_nz[i]; - for (unsigned i = 0; i < m_r_rows_nz.size(); i++) - m_r_solver.m_rows_nz[i] = m_r_rows_nz[i]; - } - - void pop(unsigned k) { // rationals - if (!settings().use_tableau()) - m_r_A.pop(k); m_r_lower_bounds.pop(k); m_r_upper_bounds.pop(k); m_column_types.pop(k); - delete m_r_solver.m_factorization; - m_r_solver.m_factorization = nullptr; m_r_x.resize(m_r_A.column_count()); m_r_solver.m_costs.resize(m_r_A.column_count()); m_r_solver.m_d.resize(m_r_A.column_count()); - if(!settings().use_tableau()) - pop_markowitz_counts(k); - m_d_A.pop(k); - // doubles - delete m_d_solver.m_factorization; - m_d_solver.m_factorization = nullptr; - m_d_x.resize(m_d_A.column_count()); - pop_basis(k); m_stacked_simplex_strategy.pop(k); settings().set_simplex_strategy(m_stacked_simplex_strategy); lp_assert(m_r_solver.basis_heading_is_correct()); - lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); - } - - bool need_to_presolve_with_double_solver() const { - return settings().simplex_strategy() == simplex_strategy_enum::lu; - } - - template - bool is_zero_vector(const vector & b) { - for (const L & m: b) - if (!is_zero(m)) return false; - return true; - } - - - bool update_xj_and_get_delta(unsigned j, non_basic_column_value_position pos_type, numeric_pair & delta) { - auto & x = m_r_x[j]; - switch (pos_type) { - case at_lower_bound: - if (x == m_r_solver.m_lower_bounds[j]) - return false; - delta = m_r_solver.m_lower_bounds[j] - x; - m_r_solver.m_x[j] = m_r_solver.m_lower_bounds[j]; - break; - case at_fixed: - case at_upper_bound: - if (x == m_r_solver.m_upper_bounds[j]) - return false; - delta = m_r_solver.m_upper_bounds[j] - x; - x = m_r_solver.m_upper_bounds[j]; - break; - case free_of_bounds: { - return false; - } - case not_at_bound: - switch (m_column_types[j]) { - case column_type::free_column: - return false; - case column_type::upper_bound: - delta = m_r_solver.m_upper_bounds[j] - x; - x = m_r_solver.m_upper_bounds[j]; - break; - case column_type::lower_bound: - delta = m_r_solver.m_lower_bounds[j] - x; - x = m_r_solver.m_lower_bounds[j]; - break; - case column_type::boxed: - if (x > m_r_solver.m_upper_bounds[j]) { - delta = m_r_solver.m_upper_bounds[j] - x; - x += m_r_solver.m_upper_bounds[j]; - } else { - delta = m_r_solver.m_lower_bounds[j] - x; - x = m_r_solver.m_lower_bounds[j]; - } - break; - case column_type::fixed: - delta = m_r_solver.m_lower_bounds[j] - x; - x = m_r_solver.m_lower_bounds[j]; - break; - - default: - lp_assert(false); - } - break; - default: - lp_unreachable(); - } - m_r_solver.remove_column_from_inf_set(j); - return true; } - void prepare_solver_x_with_signature_tableau(const lar_solution_signature & signature) { - lp_assert(m_r_solver.inf_set_is_correct()); - for (auto &t : signature) { - unsigned j = t.first; - if (m_r_heading[j] >= 0) - continue; - auto pos_type = t.second; - numeric_pair delta; - if (!update_xj_and_get_delta(j, pos_type, delta)) - continue; - for (const auto & cc : m_r_solver.m_A.m_columns[j]){ - unsigned i = cc.var(); - unsigned jb = m_r_solver.m_basis[i]; - m_r_solver.add_delta_to_x_and_track_feasibility(jb, - delta * m_r_solver.m_A.get_val(cc)); - } - CASSERT("A_off", m_r_solver.A_mult_x_is_off() == false); - } - lp_assert(m_r_solver.inf_set_is_correct()); - } - - - template - void prepare_solver_x_with_signature(const lar_solution_signature & signature, lp_primal_core_solver & s) { - for (auto &t : signature) { - unsigned j = t.first; - lp_assert(m_r_heading[j] < 0); - auto pos_type = t.second; - switch (pos_type) { - case at_lower_bound: - s.m_x[j] = s.m_lower_bounds[j]; - break; - case at_fixed: - case at_upper_bound: - s.m_x[j] = s.m_upper_bounds[j]; - break; - case free_of_bounds: { - s.m_x[j] = zero_of_type(); - continue; - } - case not_at_bound: - switch (m_column_types[j]) { - case column_type::free_column: - lp_assert(false); // unreachable - case column_type::upper_bound: - s.m_x[j] = s.m_upper_bounds[j]; - break; - case column_type::lower_bound: - s.m_x[j] = s.m_lower_bounds[j]; - break; - case column_type::boxed: - if (settings().random_next() % 2) { - s.m_x[j] = s.m_lower_bounds[j]; - } else { - s.m_x[j] = s.m_upper_bounds[j]; - } - break; - case column_type::fixed: - s.m_x[j] = s.m_lower_bounds[j]; - break; - default: - lp_assert(false); - } - break; - default: - lp_unreachable(); - } - } - - lp_assert(is_zero_vector(s.m_b)); - s.solve_Ax_eq_b(); - } - - template - void catch_up_in_lu_in_reverse(const vector & trace_of_basis_change, lp_primal_core_solver & cs) { - // recover the previous working basis - for (unsigned i = trace_of_basis_change.size(); i > 0; i-= 2) { - unsigned entering = trace_of_basis_change[i-1]; - unsigned leaving = trace_of_basis_change[i-2]; - cs.change_basis_unconditionally(entering, leaving); - } - cs.init_lu(); - } - - //basis_heading is the basis heading of the solver owning trace_of_basis_change - // here we compact the trace as we go to avoid unnecessary column changes - template - void catch_up_in_lu(const vector & trace_of_basis_change, const vector & basis_heading, lp_primal_core_solver & cs) { - if (cs.m_factorization == nullptr || cs.m_factorization->m_refactor_counter + trace_of_basis_change.size()/2 >= 200) { - for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { - unsigned entering = trace_of_basis_change[i]; - unsigned leaving = trace_of_basis_change[i+1]; - cs.change_basis_unconditionally(entering, leaving); - } - if (cs.m_factorization != nullptr) { - delete cs.m_factorization; - cs.m_factorization = nullptr; - } - } else { - indexed_vector w(cs.m_A.row_count()); - // the queues of delayed indices - std::queue entr_q, leav_q; - auto * l = cs.m_factorization; - lp_assert(l->get_status() == LU_status::OK); - for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { - unsigned entering = trace_of_basis_change[i]; - unsigned leaving = trace_of_basis_change[i+1]; - bool good_e = basis_heading[entering] >= 0 && cs.m_basis_heading[entering] < 0; - bool good_l = basis_heading[leaving] < 0 && cs.m_basis_heading[leaving] >= 0; - if (!good_e && !good_l) continue; - if (good_e && !good_l) { - while (!leav_q.empty() && cs.m_basis_heading[leav_q.front()] < 0) - leav_q.pop(); - if (!leav_q.empty()) { - leaving = leav_q.front(); - leav_q.pop(); - } else { - entr_q.push(entering); - continue; - } - } else if (!good_e && good_l) { - while (!entr_q.empty() && cs.m_basis_heading[entr_q.front()] >= 0) - entr_q.pop(); - if (!entr_q.empty()) { - entering = entr_q.front(); - entr_q.pop(); - } else { - leav_q.push(leaving); - continue; - } - } - lp_assert(cs.m_basis_heading[entering] < 0); - lp_assert(cs.m_basis_heading[leaving] >= 0); - if (l->get_status() == LU_status::OK) { - l->prepare_entering(entering, w); // to init vector w - l->replace_column(zero_of_type(), w, cs.m_basis_heading[leaving]); - } - cs.change_basis_unconditionally(entering, leaving); - } - if (l->get_status() != LU_status::OK) { - delete l; - cs.m_factorization = nullptr; - } - } - if (cs.m_factorization == nullptr) { - if (numeric_traits::precise()) - init_factorization(cs.m_factorization, cs.m_A, cs.m_basis, settings()); - } - } - - bool no_r_lu() const { - return m_r_solver.m_factorization == nullptr || m_r_solver.m_factorization->get_status() == LU_status::Degenerated; - } - - void solve_on_signature_tableau(const lar_solution_signature & signature, const vector & changes_of_basis) { - r_basis_is_OK(); - lp_assert(settings().use_tableau()); - bool r = catch_up_in_lu_tableau(changes_of_basis, m_d_solver.m_basis_heading); - - if (!r) { // it is the case where m_d_solver gives a degenerated basis - prepare_solver_x_with_signature_tableau(signature); // still are going to use the signature partially - m_r_solver.find_feasible_solution(); - m_d_basis = m_r_basis; - m_d_heading = m_r_heading; - m_d_nbasis = m_r_nbasis; - delete m_d_solver.m_factorization; - m_d_solver.m_factorization = nullptr; - } else { - prepare_solver_x_with_signature_tableau(signature); - m_r_solver.start_tracing_basis_changes(); - m_r_solver.find_feasible_solution(); - if (settings().get_cancel_flag()) - return; - m_r_solver.stop_tracing_basis_changes(); - // and now catch up in the double solver - lp_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); - catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver); - } - lp_assert(r_basis_is_OK()); - } - - bool adjust_x_of_column(unsigned j) { - /* - if (m_r_solver.m_basis_heading[j] >= 0) { - return false; - } - - if (m_r_solver.column_is_feasible(j)) { - return false; - } - - m_r_solver.snap_column_to_bound_tableau(j); - lp_assert(m_r_solver.column_is_feasible(j)); - m_r_solver.m_inf_set.erase(j); - */ - lp_assert(false); - return true; - } - - - bool catch_up_in_lu_tableau(const vector & trace_of_basis_change, const vector & basis_heading) { - lp_assert(r_basis_is_OK()); - // the queues of delayed indices - std::queue entr_q, leav_q; - for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { - unsigned entering = trace_of_basis_change[i]; - unsigned leaving = trace_of_basis_change[i+1]; - bool good_e = basis_heading[entering] >= 0 && m_r_solver.m_basis_heading[entering] < 0; - bool good_l = basis_heading[leaving] < 0 && m_r_solver.m_basis_heading[leaving] >= 0; - if (!good_e && !good_l) continue; - if (good_e && !good_l) { - while (!leav_q.empty() && m_r_solver.m_basis_heading[leav_q.front()] < 0) - leav_q.pop(); - if (!leav_q.empty()) { - leaving = leav_q.front(); - leav_q.pop(); - } else { - entr_q.push(entering); - continue; - } - } else if (!good_e && good_l) { - while (!entr_q.empty() && m_r_solver.m_basis_heading[entr_q.front()] >= 0) - entr_q.pop(); - if (!entr_q.empty()) { - entering = entr_q.front(); - entr_q.pop(); - } else { - leav_q.push(leaving); - continue; - } - } - lp_assert(m_r_solver.m_basis_heading[entering] < 0); - lp_assert(m_r_solver.m_basis_heading[leaving] >= 0); - m_r_solver.change_basis_unconditionally(entering, leaving); - if(!m_r_solver.pivot_column_tableau(entering, m_r_solver.m_basis_heading[entering])) { - // unroll the last step - m_r_solver.change_basis_unconditionally(leaving, entering); -#ifdef Z3DEBUG - bool t = -#endif - m_r_solver.pivot_column_tableau(leaving, m_r_solver.m_basis_heading[leaving]); -#ifdef Z3DEBUG - lp_assert(t); -#endif - return false; - } - } - lp_assert(r_basis_is_OK()); - return true; - } - - bool r_basis_is_OK() const { #ifdef Z3DEBUG - if (!m_r_solver.m_settings.use_tableau()) - return true; + for (unsigned j : m_r_solver.m_basis) { lp_assert(m_r_solver.m_A.m_columns[j].size() == 1); } @@ -565,139 +148,7 @@ public: return true; } - void solve_on_signature(const lar_solution_signature & signature, const vector & changes_of_basis) { - SASSERT(!settings().use_tableau()); - if (m_r_solver.m_factorization == nullptr) { - for (unsigned j = 0; j < changes_of_basis.size(); j+=2) { - unsigned entering = changes_of_basis[j]; - unsigned leaving = changes_of_basis[j + 1]; - m_r_solver.change_basis_unconditionally(entering, leaving); - } - init_factorization(m_r_solver.m_factorization, m_r_A, m_r_basis, settings()); - } else { - catch_up_in_lu(changes_of_basis, m_d_solver.m_basis_heading, m_r_solver); - } - - if (no_r_lu()) { // it is the case where m_d_solver gives a degenerated basis, we need to roll back - catch_up_in_lu_in_reverse(changes_of_basis, m_r_solver); - m_r_solver.find_feasible_solution(); - m_d_basis = m_r_basis; - m_d_heading = m_r_heading; - m_d_nbasis = m_r_nbasis; - delete m_d_solver.m_factorization; - m_d_solver.m_factorization = nullptr; - } else { - prepare_solver_x_with_signature(signature, m_r_solver); - m_r_solver.start_tracing_basis_changes(); - m_r_solver.find_feasible_solution(); - if (settings().get_cancel_flag()) - return; - m_r_solver.stop_tracing_basis_changes(); - // and now catch up in the double solver - lp_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); - catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver); - } - } - - void create_double_matrix(static_matrix & A) { - for (unsigned i = 0; i < m_r_A.row_count(); i++) { - auto & row = m_r_A.m_rows[i]; - for (row_cell & c : row) { - A.add_new_element(i, c.var(), c.coeff().get_double()); - } - } - } - - void fill_basis_d( - vector& basis_d, - vector& heading_d, - vector& nbasis_d){ - basis_d = m_r_basis; - heading_d = m_r_heading; - nbasis_d = m_r_nbasis; - } - - template - void extract_signature_from_lp_core_solver(const lp_primal_core_solver & solver, lar_solution_signature & signature) { - signature.clear(); - lp_assert(signature.size() == 0); - for (unsigned j = 0; j < solver.m_basis_heading.size(); j++) { - if (solver.m_basis_heading[j] < 0) { - signature[j] = solver.get_non_basic_column_value_position(j); - } - } - } - - void get_bounds_for_double_solver() { - unsigned n = m_n(); - m_d_lower_bounds.resize(n); - m_d_upper_bounds.resize(n); - double delta = find_delta_for_strict_boxed_bounds().get_double(); - if (delta > 0.000001) - delta = 0.000001; - for (unsigned j = 0; j < n; j++) { - if (lower_bound_is_set(j)) { - const auto & lb = m_r_solver.m_lower_bounds[j]; - m_d_lower_bounds[j] = lb.x.get_double() + delta * lb.y.get_double(); - } - if (upper_bound_is_set(j)) { - const auto & ub = m_r_solver.m_upper_bounds[j]; - m_d_upper_bounds[j] = ub.x.get_double() + delta * ub.y.get_double(); - lp_assert(!lower_bound_is_set(j) || (m_d_upper_bounds[j] >= m_d_lower_bounds[j])); - } - } - } - - void scale_problem_for_doubles( - static_matrix& A, - vector & lower_bounds, - vector & upper_bounds) { - vector column_scale_vector; - vector right_side_vector(A.column_count()); - settings().reps_in_scaler = 5; - scaler scaler(right_side_vector, - A, - settings().scaling_minimum, - settings().scaling_maximum, - column_scale_vector, - settings()); - if (! scaler.scale()) { - // the scale did not succeed, unscaling - A.clear(); - create_double_matrix(A); - } else { - for (unsigned j = 0; j < A.column_count(); j++) { - if (m_r_solver.column_has_upper_bound(j)) { - upper_bounds[j] /= column_scale_vector[j]; - } - if (m_r_solver.column_has_lower_bound(j)) { - lower_bounds[j] /= column_scale_vector[j]; - } - } - } - - } - // returns the trace of basis changes - vector find_solution_signature_with_doubles(lar_solution_signature & signature) { - if (m_d_solver.m_factorization == nullptr || m_d_solver.m_factorization->get_status() != LU_status::OK) { - vector ret; - return ret; - } - get_bounds_for_double_solver(); - - extract_signature_from_lp_core_solver(m_r_solver, signature); - prepare_solver_x_with_signature(signature, m_d_solver); - m_d_solver.start_tracing_basis_changes(); - m_d_solver.find_feasible_solution(); - if (settings().get_cancel_flag()) - return vector(); - - m_d_solver.stop_tracing_basis_changes(); - extract_signature_from_lp_core_solver(m_d_solver, signature); - return m_d_solver.m_trace_of_basis_change_vector; - } - - + bool lower_bound_is_set(unsigned j) const { switch (m_column_types[j]) { case column_type::free_column: @@ -708,7 +159,7 @@ public: case column_type::fixed: return true; default: - lp_assert(false); + UNREACHABLE(); } return false; } @@ -723,7 +174,7 @@ public: case column_type::fixed: return true; default: - lp_assert(false); + UNREACHABLE(); } return false; } @@ -762,10 +213,6 @@ public: return delta; } - void init_column_row_nz_for_r_solver() { - m_r_solver.init_column_row_non_zeroes(); - } - bool column_is_fixed(unsigned j) const { return m_column_types()[j] == column_type::fixed || ( m_column_types()[j] == column_type::boxed && diff --git a/src/math/lp/lar_core_solver_def.h b/src/math/lp/lar_core_solver_def.h index 939a05114..550b6fe36 100644 --- a/src/math/lp/lar_core_solver_def.h +++ b/src/math/lp/lar_core_solver_def.h @@ -14,7 +14,6 @@ Revision History: #include #include "util/vector.h" #include "math/lp/lar_core_solver.h" -#include "math/lp/lar_solution_signature.h" namespace lp { lar_core_solver::lar_core_solver( lp_settings & settings, @@ -31,78 +30,26 @@ lar_core_solver::lar_core_solver( m_r_lower_bounds(), m_r_upper_bounds(), settings, - column_names), - m_d_solver(m_d_A, - m_d_right_sides_dummy, - m_d_x, - m_d_basis, - m_d_nbasis, - m_d_heading, - m_d_costs_dummy, - m_column_types(), - m_d_lower_bounds, - m_d_upper_bounds, - settings, column_names) { } - - -void lar_core_solver::calculate_pivot_row(unsigned i) { - m_r_solver.calculate_pivot_row(i); -} - - void lar_core_solver::prefix_r() { - if (!m_r_solver.m_settings.use_tableau()) { - m_r_solver.m_copy_of_xB.resize(m_r_solver.m_n()); - m_r_solver.m_ed.resize(m_r_solver.m_m()); - m_r_solver.m_pivot_row.resize(m_r_solver.m_n()); - m_r_solver.m_pivot_row_of_B_1.resize(m_r_solver.m_m()); - m_r_solver.m_w.resize(m_r_solver.m_m()); - m_r_solver.m_y.resize(m_r_solver.m_m()); - m_r_solver.m_rows_nz.resize(m_r_solver.m_m(), 0); - m_r_solver.m_columns_nz.resize(m_r_solver.m_n(), 0); - init_column_row_nz_for_r_solver(); - } - - m_r_solver.m_b.resize(m_r_solver.m_m()); + + // m_r_solver.m_b.resize(m_r_solver.m_m()); if (m_r_solver.m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) { - if(m_r_solver.m_settings.use_breakpoints_in_feasibility_search) - m_r_solver.m_breakpoint_indices_queue.resize(m_r_solver.m_n()); m_r_solver.m_costs.resize(m_r_solver.m_n()); m_r_solver.m_d.resize(m_r_solver.m_n()); - m_r_solver.set_using_infeas_costs(true); } } -void lar_core_solver::prefix_d() { - m_d_solver.m_b.resize(m_d_solver.m_m()); - m_d_solver.m_breakpoint_indices_queue.resize(m_d_solver.m_n()); - m_d_solver.m_copy_of_xB.resize(m_d_solver.m_n()); - m_d_solver.m_costs.resize(m_d_solver.m_n()); - m_d_solver.m_d.resize(m_d_solver.m_n()); - m_d_solver.m_ed.resize(m_d_solver.m_m()); - m_d_solver.m_pivot_row.resize(m_d_solver.m_n()); - m_d_solver.m_pivot_row_of_B_1.resize(m_d_solver.m_m()); - m_d_solver.m_w.resize(m_d_solver.m_m()); - m_d_solver.m_y.resize(m_d_solver.m_m()); - m_d_solver.m_steepest_edge_coefficients.resize(m_d_solver.m_n()); - m_d_solver.m_column_norms.clear(); - m_d_solver.m_column_norms.resize(m_d_solver.m_n(), 2); - m_d_solver.clear_inf_set(); - m_d_solver.resize_inf_set(m_d_solver.m_n()); -} void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() { - CASSERT("A_off", m_r_solver.A_mult_x_is_off() == false); unsigned bj = m_r_basis[m_r_solver.m_inf_row_index_for_tableau]; m_infeasible_sum_sign = m_r_solver.inf_sign_of_column(bj); m_infeasible_linear_combination.clear(); - for (auto & rc : m_r_solver.m_A.m_rows[m_r_solver.m_inf_row_index_for_tableau]) { - m_infeasible_linear_combination.push_back(std::make_pair(rc.coeff(), rc.var())); - } + for (auto & rc : m_r_solver.m_A.m_rows[m_r_solver.m_inf_row_index_for_tableau]) + m_infeasible_linear_combination.push_back(std::make_pair(rc.coeff(), rc.var())); } void lar_core_solver::fill_not_improvable_zero_sum() { @@ -111,30 +58,27 @@ void lar_core_solver::fill_not_improvable_zero_sum() { return; } // reusing the existing mechanism for row_feasibility_loop - m_infeasible_sum_sign = m_r_solver.m_settings.use_breakpoints_in_feasibility_search? -1 : 1; + m_infeasible_sum_sign = 1; m_infeasible_linear_combination.clear(); for (auto j : m_r_solver.m_basis) { const mpq & cost_j = m_r_solver.m_costs[j]; - if (!numeric_traits::is_zero(cost_j)) { - m_infeasible_linear_combination.push_back(std::make_pair(cost_j, j)); - } + if (!numeric_traits::is_zero(cost_j)) + m_infeasible_linear_combination.push_back(std::make_pair(cost_j, j)); } // m_costs are expressed by m_d ( additional costs), substructing the latter gives 0 for (unsigned j = 0; j < m_r_solver.m_n(); j++) { if (m_r_solver.m_basis_heading[j] >= 0) continue; const mpq & d_j = m_r_solver.m_d[j]; - if (!numeric_traits::is_zero(d_j)) { - m_infeasible_linear_combination.push_back(std::make_pair(-d_j, j)); - } + if (!numeric_traits::is_zero(d_j)) + m_infeasible_linear_combination.push_back(std::make_pair(-d_j, j)); } } unsigned lar_core_solver::get_number_of_non_ints() const { unsigned n = 0; - for (auto & x : m_r_solver.m_x) { - if (x.is_int() == false) - n++; - } + for (auto & x : m_r_solver.m_x) + if (!x.is_int()) + n++; return n; } @@ -149,38 +93,16 @@ void lar_core_solver::solve() { return; } ++settings().stats().m_need_to_solve_inf; - CASSERT("A_off", !m_r_solver.A_mult_x_is_off()); - lp_assert((!settings().use_tableau()) || r_basis_is_OK()); - if (need_to_presolve_with_double_solver()) { - TRACE("lar_solver", tout << "presolving\n";); - prefix_d(); - lar_solution_signature solution_signature; - vector changes_of_basis = find_solution_signature_with_doubles(solution_signature); - if (m_d_solver.get_status() == lp_status::TIME_EXHAUSTED) { - m_r_solver.set_status(lp_status::TIME_EXHAUSTED); - return; - } - if (settings().use_tableau()) - solve_on_signature_tableau(solution_signature, changes_of_basis); - else - solve_on_signature(solution_signature, changes_of_basis); - - lp_assert(!settings().use_tableau() || r_basis_is_OK()); - } else { - if (!settings().use_tableau()) { - TRACE("lar_solver", tout << "no tablau\n";); - bool snapped = m_r_solver.snap_non_basic_x_to_bound(); - lp_assert(m_r_solver.non_basic_columns_are_set_correctly()); - if (snapped) - m_r_solver.solve_Ax_eq_b(); - } - if (m_r_solver.m_look_for_feasible_solution_only) //todo : should it be set? - m_r_solver.find_feasible_solution(); - else { - m_r_solver.solve(); - } - lp_assert(!settings().use_tableau() || r_basis_is_OK()); + lp_assert( r_basis_is_OK()); + + + if (m_r_solver.m_look_for_feasible_solution_only) //todo : should it be set? + m_r_solver.find_feasible_solution(); + else { + m_r_solver.solve(); } + lp_assert(r_basis_is_OK()); + switch (m_r_solver.get_status()) { case lp_status::INFEASIBLE: diff --git a/src/math/lp/lar_solution_signature.h b/src/math/lp/lar_solution_signature.h deleted file mode 100644 index 5b8bfae48..000000000 --- a/src/math/lp/lar_solution_signature.h +++ /dev/null @@ -1,28 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "util/debug.h" -#include "math/lp/lp_settings.h" -#include -namespace lp { -typedef std::unordered_map lar_solution_signature; -} diff --git a/src/math/lp/lar_solver.cpp b/src/math/lp/lar_solver.cpp index 46ed0b5a9..0eb65e197 100644 --- a/src/math/lp/lar_solver.cpp +++ b/src/math/lp/lar_solver.cpp @@ -1,16 +1,13 @@ -#include "math/lp/lar_solver.h" -#include "smt/params/smt_params_helper.hpp" - /* Copyright (c) 2017 Microsoft Corporation Author: Nikolaj Bjorner, Lev Nachmanson */ -namespace lp { +#include "math/lp/lar_solver.h" +#include "smt/params/smt_params_helper.hpp" - ////////////////// methods //////////////////////////////// - static_matrix& lar_solver::A_d() { return m_mpq_lar_core_solver.m_d_A; } - static_matrix const& lar_solver::A_d() const { return m_mpq_lar_core_solver.m_d_A; } + +namespace lp { lp_settings& lar_solver::settings() { return m_settings; } @@ -18,7 +15,6 @@ namespace lp { statistics& lar_solver::stats() { return m_settings.stats(); } - void lar_solver::updt_params(params_ref const& _p) { smt_params_helper p(_p); set_track_pivoted_rows(p.arith_bprop_on_pivoted_rows()); @@ -42,15 +38,12 @@ namespace lp { } lar_solver::~lar_solver() { - for (auto t : m_terms) delete t; } - bool lar_solver::use_lu() const { return m_settings.simplex_strategy() == simplex_strategy_enum::lu; } - + bool lar_solver::sizes_are_correct() const { - lp_assert(strategy_is_undecided() || !m_mpq_lar_core_solver.need_to_presolve_with_double_solver() || A_r().column_count() == A_d().column_count()); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_column_types.size()); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_x.size()); @@ -142,10 +135,9 @@ namespace lp { bool lar_solver::row_has_a_big_num(unsigned i) const { - for (const auto& c : A_r().m_rows[i]) { + for (const auto& c : A_r().m_rows[i]) if (c.coeff().is_big()) return true; - } return false; } @@ -199,9 +191,11 @@ namespace lp { stats().m_max_rows = A_r().row_count(); if (strategy_is_undecided()) decide_on_strategy_and_adjust_initial_state(); - + auto strategy_was = settings().simplex_strategy(); + settings().set_simplex_strategy(simplex_strategy_enum::tableau_rows); m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = true; auto ret = solve(); + settings().set_simplex_strategy(strategy_was); return ret; } @@ -229,9 +223,6 @@ namespace lp { evidence.add_pair(ul.lower_bound_witness(), -numeric_traits::one()); } - - unsigned lar_solver::get_total_iterations() const { return m_mpq_lar_core_solver.m_r_solver.total_iterations(); } - void lar_solver::push() { m_simplex_strategy = m_settings.simplex_strategy(); m_simplex_strategy.push(); @@ -253,19 +244,14 @@ namespace lp { set.erase(j); } - void lar_solver::shrink_inf_set_after_pop(unsigned n, u_set& set) { - clean_popped_elements(n, set); - set.resize(n); - } - + void lar_solver::pop(unsigned k) { TRACE("lar_solver", tout << "k = " << k << std::endl;); m_crossed_bounds_column.pop(k); unsigned n = m_columns_to_ul_pairs.peek_size(k); m_var_register.shrink(n); - if (m_settings.use_tableau()) - pop_tableau(); + pop_tableau(); lp_assert(A_r().column_count() == n); TRACE("lar_solver_details", for (unsigned j = 0; j < n; j++) { @@ -286,9 +272,9 @@ namespace lp { unsigned m = A_r().row_count(); clean_popped_elements(m, m_rows_with_changed_bounds); clean_inf_set_of_r_solver_after_pop(); - lp_assert(m_settings.simplex_strategy() == simplex_strategy_enum::undecided || - (!use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); - + lp_assert( + m_settings.simplex_strategy() == simplex_strategy_enum::undecided || + m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); m_constraints.pop(k); m_term_count.pop(k); @@ -302,7 +288,7 @@ namespace lp { m_simplex_strategy.pop(k); m_settings.set_simplex_strategy(m_simplex_strategy); lp_assert(sizes_are_correct()); - lp_assert((!m_settings.use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); m_usage_in_terms.pop(k); set_status(lp_status::UNKNOWN); } @@ -371,7 +357,6 @@ namespace lp { m_basic_columns_with_changed_cost.resize(m_mpq_lar_core_solver.m_r_x.size()); move_non_basic_columns_to_bounds(false); auto& rslv = m_mpq_lar_core_solver.m_r_solver; - rslv.set_using_infeas_costs(false); lp_assert(costs_are_zeros_for_r_solver()); lp_assert(reduced_costs_are_zeroes_for_r_solver()); rslv.m_costs.resize(A_r().column_count(), zero_of_type()); @@ -480,12 +465,9 @@ namespace lp { m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::OPTIMAL); return ret; - case simplex_strategy_enum::lu: - lp_assert(false); // not implemented - return false; - + default: - lp_unreachable(); // wrong mode + UNREACHABLE(); // wrong mode } return false; } @@ -509,11 +491,11 @@ namespace lp { lp_status lar_solver::maximize_term(unsigned j_or_term, impq& term_max) { TRACE("lar_solver", print_values(tout);); + lar_term term = get_term_to_maximize(j_or_term); if (term.is_empty()) { return lp_status::UNBOUNDED; } - impq prev_value; auto backup = m_mpq_lar_core_solver.m_r_x; if (m_mpq_lar_core_solver.m_r_solver.calc_current_x_is_feasible_include_non_basis()) { @@ -582,7 +564,6 @@ namespace lp { void lar_solver::pop_core_solver_params(unsigned k) { A_r().pop(k); - A_d().pop(k); } @@ -620,33 +601,20 @@ namespace lp { else { const lar_term& term = *m_terms[tv::unmask_term(t.second)]; - for (auto p : term) { + for (auto p : term) register_monoid_in_map(coeffs, t.first * p.coeff(), p.column()); - } } } - for (auto& p : coeffs) - if (!is_zero(p.second)) - left_side.push_back(std::make_pair(p.second, p.first)); + for (auto& [v, c] : coeffs) + if (!is_zero(c)) + left_side.push_back(std::make_pair(c, v)); } void lar_solver::insert_row_with_changed_bounds(unsigned rid) { m_rows_with_changed_bounds.insert(rid); } - void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column(unsigned j) { - if (A_r().row_count() != m_column_buffer.data_size()) - m_column_buffer.resize(A_r().row_count()); - else - m_column_buffer.clear(); - lp_assert(m_column_buffer.size() == 0 && m_column_buffer.is_OK()); - - m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); - for (unsigned i : m_column_buffer.m_index) - insert_row_with_changed_bounds(i); - } - void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j) { @@ -654,24 +622,17 @@ namespace lp { insert_row_with_changed_bounds(rc.var()); } - bool lar_solver::use_tableau() const { return m_settings.use_tableau(); } - + bool lar_solver::use_tableau_costs() const { return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; } - - void lar_solver::adjust_x_of_column(unsigned j) { - lp_assert(false); - } - bool lar_solver::row_is_correct(unsigned i) const { numeric_pair r = zero_of_type>(); for (const auto& c : A_r().m_rows[i]) { r += c.coeff() * m_mpq_lar_core_solver.m_r_x[c.var()]; } CTRACE("lar_solver", !is_zero(r), tout << "row = " << i << ", j = " << m_mpq_lar_core_solver.m_r_basis[i] << "\n"; - print_row(A_r().m_rows[i], tout); tout << " = " << r << "\n"; - ); + print_row(A_r().m_rows[i], tout); tout << " = " << r << "\n"); return is_zero(r); } @@ -698,27 +659,15 @@ namespace lp { } void lar_solver::change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair& delta) { - if (use_tableau()) { - for (const auto& c : A_r().m_columns[j]) { - unsigned bj = m_mpq_lar_core_solver.m_r_basis[c.var()]; - if (tableau_with_costs()) { - m_basic_columns_with_changed_cost.insert(bj); - } - m_mpq_lar_core_solver.m_r_solver.add_delta_to_x_and_track_feasibility(bj, -A_r().get_val(c) * delta); - TRACE("change_x_del", - tout << "changed basis column " << bj << ", it is " << - (m_mpq_lar_core_solver.m_r_solver.column_is_feasible(bj) ? "feas" : "inf") << std::endl;); - - } - } - else { - m_column_buffer.clear(); - m_column_buffer.resize(A_r().row_count()); - m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); - for (unsigned i : m_column_buffer.m_index) { - unsigned bj = m_mpq_lar_core_solver.m_r_basis[i]; - m_mpq_lar_core_solver.m_r_solver.add_delta_to_x_and_track_feasibility(bj, -m_column_buffer[i] * delta); - } + for (const auto& c : A_r().m_columns[j]) { + unsigned bj = m_mpq_lar_core_solver.m_r_basis[c.var()]; + if (tableau_with_costs()) { + m_basic_columns_with_changed_cost.insert(bj); + } + m_mpq_lar_core_solver.m_r_solver.add_delta_to_x_and_track_feasibility(bj, -A_r().get_val(c) * delta); + TRACE("change_x_del", + tout << "changed basis column " << bj << ", it is " << + (m_mpq_lar_core_solver.m_r_solver.column_is_feasible(bj) ? "feas" : "inf") << std::endl;); } } @@ -741,17 +690,11 @@ namespace lp { } } - void lar_solver::detect_rows_with_changed_bounds_for_column(unsigned j) { - if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { + if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) insert_row_with_changed_bounds(m_mpq_lar_core_solver.m_r_heading[j]); - return; - } - - if (use_tableau()) - detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); - else - detect_rows_of_bound_change_column_for_nbasic_column(j); + else + detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); } void lar_solver::detect_rows_with_changed_bounds() { @@ -759,39 +702,18 @@ namespace lp { detect_rows_with_changed_bounds_for_column(j); } - void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds() { - for (auto j : m_columns_with_changed_bounds) - update_x_and_inf_costs_for_column_with_changed_bounds(j); - } - void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds_tableau() { for (auto j : m_columns_with_changed_bounds) update_x_and_inf_costs_for_column_with_changed_bounds(j); - - if (tableau_with_costs()) { - if (m_mpq_lar_core_solver.m_r_solver.using_infeas_costs()) { - for (unsigned j : m_basic_columns_with_changed_cost) - m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); - lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); - } - } } void lar_solver::solve_with_core_solver() { - if (!use_tableau()) - add_last_rows_to_lu(m_mpq_lar_core_solver.m_r_solver); - if (m_mpq_lar_core_solver.need_to_presolve_with_double_solver()) { - add_last_rows_to_lu(m_mpq_lar_core_solver.m_d_solver); - } m_mpq_lar_core_solver.prefix_r(); if (costs_are_used()) { m_basic_columns_with_changed_cost.resize(m_mpq_lar_core_solver.m_r_x.size()); } - if (use_tableau()) - update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); - else - update_x_and_inf_costs_for_columns_with_changed_bounds(); + update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); m_mpq_lar_core_solver.solve(); set_status(m_mpq_lar_core_solver.m_r_solver.get_status()); lp_assert(((stats().m_make_feasible% 100) != 0) || m_status != lp_status::OPTIMAL || all_constraints_hold()); @@ -823,44 +745,6 @@ namespace lp { return r; } - - template - void lar_solver::add_last_rows_to_lu(lp_primal_core_solver& s) { - auto& f = s.m_factorization; - if (f != nullptr) { - auto columns_to_replace = f->get_set_of_columns_to_replace_for_add_last_rows(s.m_basis_heading); - if (f->m_refactor_counter + columns_to_replace.size() >= 200 || f->has_dense_submatrix()) { - delete f; - f = nullptr; - } - else { - f->add_last_rows_to_B(s.m_basis_heading, columns_to_replace); - } - } - if (f == nullptr) { - init_factorization(f, s.m_A, s.m_basis, m_settings); - if (f->get_status() != LU_status::OK) { - delete f; - f = nullptr; - } - } - - } - - bool lar_solver::x_is_correct() const { - if (m_mpq_lar_core_solver.m_r_x.size() != A_r().column_count()) { - return false; - } - for (unsigned i = 0; i < A_r().row_count(); i++) { - numeric_pair delta = A_r().dot_product_with_row(i, m_mpq_lar_core_solver.m_r_x); - if (!delta.is_zero()) { - return false; - } - } - return true;; - - } - bool lar_solver::var_is_registered(var_index vj) const { if (tv::is_term(vj)) { return tv::unmask_term(vj) < m_terms.size(); @@ -869,44 +753,6 @@ namespace lp { } - void lar_solver::fill_last_row_of_A_r(static_matrix>& A, const lar_term* ls) { - lp_assert(A.row_count() > 0); - lp_assert(A.column_count() > 0); - unsigned last_row = A.row_count() - 1; - lp_assert(A.m_rows[last_row].size() == 0); - for (auto t : *ls) { - lp_assert(!is_zero(t.coeff())); - var_index j = t.column(); - A.set(last_row, j, -t.coeff()); - } - unsigned basis_j = A.column_count() - 1; - A.set(last_row, basis_j, mpq(1)); - } - - template - void lar_solver::create_matrix_A(static_matrix& matr) { - lp_assert(false); // not implemented - /* - unsigned m = number_or_nontrivial_left_sides(); - unsigned n = m_vec_of_canonic_left_sides.size(); - if (matr.row_count() == m && matr.column_count() == n) - return; - matr.init_empty_matrix(m, n); - copy_from_mpq_matrix(matr); - */ - } - - template - void lar_solver::copy_from_mpq_matrix(static_matrix& matr) { - matr.m_rows.resize(A_r().row_count()); - matr.m_columns.resize(A_r().column_count()); - for (unsigned i = 0; i < matr.row_count(); i++) { - for (auto& it : A_r().m_rows[i]) { - matr.set(i, it.var(), convert_struct::convert(it.coeff())); - } - } - } - bool lar_solver::all_constrained_variables_are_registered(const vector>& left_side) { for (auto it : left_side) { if (!var_is_registered(it.second)) @@ -943,33 +789,11 @@ namespace lp { case GT: return left_side_val > constr.rhs(); case EQ: return left_side_val == constr.rhs(); default: - lp_unreachable(); + UNREACHABLE(); } return false; // it is unreachable } - bool lar_solver::the_relations_are_of_same_type(const vector>& evidence, lconstraint_kind& the_kind_of_sum) const { - unsigned n_of_G = 0, n_of_L = 0; - bool strict = false; - for (auto& it : evidence) { - mpq coeff = it.first; - constraint_index con_ind = it.second; - lconstraint_kind kind = coeff.is_pos() ? - m_constraints[con_ind].kind() : - flip_kind(m_constraints[con_ind].kind()); - if (kind == GT || kind == LT) - strict = true; - if (kind == GE || kind == GT) - n_of_G++; - else if (kind == LE || kind == LT) - n_of_L++; - } - the_kind_of_sum = n_of_G ? GE : (n_of_L ? LE : EQ); - if (strict) - the_kind_of_sum = static_cast((static_cast(the_kind_of_sum) / 2)); - - return n_of_G == 0 || n_of_L == 0; - } void lar_solver::register_in_map(std::unordered_map& coeffs, const lar_base_constraint& cn, const mpq& a) { for (auto& it : cn.coeffs()) { @@ -1020,7 +844,7 @@ namespace lp { case EQ: lp_assert(rs != zero_of_type()); break; default: - lp_assert(false); + UNREACHABLE(); return false; } #endif @@ -1351,12 +1175,6 @@ namespace lp { insert_row_with_changed_bounds(r.var()); } - - - void lar_solver::pivot_fixed_vars_from_basis() { - m_mpq_lar_core_solver.m_r_solver.pivot_fixed_vars_from_basis(); - } - void lar_solver::pop() { pop(1); } @@ -1406,7 +1224,6 @@ namespace lp { A_r().m_rows.pop_back(); A_r().m_columns.pop_back(); CASSERT("check_static_matrix", A_r().is_correct()); - slv.m_b.pop_back(); } void lar_solver::remove_last_column_from_A() { @@ -1514,14 +1331,6 @@ namespace lp { for (unsigned j : became_feas) m_mpq_lar_core_solver.m_r_solver.remove_column_from_inf_set(j); - - if (use_tableau_costs()) { - for (unsigned j : became_feas) - m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); - for (unsigned j : basic_columns_with_changed_cost) - m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); - lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); - } } bool lar_solver::model_is_int_feasible() const { @@ -1569,6 +1378,66 @@ namespace lp { return m_mpq_lar_core_solver.column_is_free(j); } + // column is at lower or upper bound, lower and upper bound are different. + // the lower/upper bound is not strict. + // the LP obtained by making the bound strict is infeasible + // -> the column has to be fixed + bool lar_solver::is_fixed_at_bound(column_index const& j) { + if (column_is_fixed(j)) + return false; + mpq val; + if (!has_value(j, val)) + return false; + lp::lconstraint_kind k; + if (column_has_upper_bound(j) && + get_upper_bound(j).x == val) { + verbose_stream() << "check upper " << j << "\n"; + push(); + if (column_is_int(j)) + k = LE, val -= 1; + else + k = LT; + auto ci = mk_var_bound(j, k, val); + update_column_type_and_bound(j, k, val, ci); + auto st = find_feasible_solution(); + pop(1); + return st == lp_status::INFEASIBLE; + } + if (column_has_lower_bound(j) && + get_lower_bound(j).x == val) { + verbose_stream() << "check lower " << j << "\n"; + push(); + if (column_is_int(j)) + k = GE, val += 1; + else + k = GT; + auto ci = mk_var_bound(j, k, val); + update_column_type_and_bound(j, k, val, ci); + auto st = find_feasible_solution(); + pop(1); + return st == lp_status::INFEASIBLE; + } + + return false; + } + + bool lar_solver::has_fixed_at_bound() { + verbose_stream() << "has-fixed-at-bound\n"; + unsigned num_fixed = 0; + for (unsigned j = 0; j < A_r().m_columns.size(); ++j) { + auto ci = column_index(j); + if (is_fixed_at_bound(ci)) { + ++num_fixed; + verbose_stream() << "fixed " << j << "\n"; + } + } + verbose_stream() << "num fixed " << num_fixed << "\n"; + if (num_fixed > 0) + find_feasible_solution(); + return num_fixed > 0; + } + + // below is the initialization functionality of lar_solver bool lar_solver::strategy_is_undecided() const { @@ -1627,31 +1496,9 @@ namespace lp { register_new_ext_var_index(ext_j, is_int); m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); increase_by_one_columns_with_changed_bounds(); - add_new_var_to_core_fields_for_mpq(false); // false for not adding a row - if (use_lu()) - add_new_var_to_core_fields_for_doubles(false); + add_new_var_to_core_fields_for_mpq(false); // false for not adding a row } - - void lar_solver::add_new_var_to_core_fields_for_doubles(bool register_in_basis) { - unsigned j = A_d().column_count(); - A_d().add_column(); - lp_assert(m_mpq_lar_core_solver.m_d_x.size() == j); - // lp_assert(m_mpq_lar_core_solver.m_d_lower_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later - m_mpq_lar_core_solver.m_d_x.resize(j + 1); - m_mpq_lar_core_solver.m_d_lower_bounds.resize(j + 1); - m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1); - lp_assert(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method - if (register_in_basis) { - A_d().add_row(); - m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size()); - m_mpq_lar_core_solver.m_d_basis.push_back(j); - } - else { - m_mpq_lar_core_solver.m_d_heading.push_back(-static_cast(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1); - m_mpq_lar_core_solver.m_d_nbasis.push_back(j); - } - } - + void lar_solver::add_new_var_to_core_fields_for_mpq(bool register_in_basis) { unsigned j = A_r().column_count(); TRACE("add_var", tout << "j = " << j << std::endl;); @@ -1687,24 +1534,21 @@ namespace lp { #if Z3DEBUG_CHECK_UNIQUE_TERMS bool lar_solver::term_coeffs_are_ok(const vector>& coeffs) { - for (const auto& p : coeffs) { + for (const auto& p : coeffs) if (column_is_real(p.second)) return true; - } mpq g; bool g_is_set = false; for (const auto& p : coeffs) { - if (!p.first.is_int()) { + if (!p.first.is_int()) return false; - } if (!g_is_set) { g_is_set = true; g = p.first; } - else { + else g = gcd(g, p.first); - } } if (g == one_of_type()) return true; @@ -1716,8 +1560,6 @@ namespace lp { m_terms.push_back(t); } - - // terms bool lar_solver::all_vars_are_registered(const vector>& coeffs) { for (const auto& p : coeffs) { @@ -1732,20 +1574,17 @@ namespace lp { std::set seen_terms; for (auto p : *t) { auto j = p.column(); - if (this->column_corresponds_to_term(j)) { + if (this->column_corresponds_to_term(j)) seen_terms.insert(j); - } } while (!seen_terms.empty()) { unsigned j = *seen_terms.begin(); seen_terms.erase(j); auto tj = this->m_var_register.local_to_external(j); auto& ot = this->get_term(tj); - for (auto p : ot){ - if (this->column_corresponds_to_term(p.column())) { + for (auto p : ot) + if (this->column_corresponds_to_term(p.column())) seen_terms.insert(p.column()); - } - } t->subst_by_term(ot, j); } } @@ -1763,15 +1602,14 @@ namespace lp { SASSERT(m_terms.size() == m_term_register.size()); unsigned adjusted_term_index = m_terms.size() - 1; var_index ret = tv::mask_term(adjusted_term_index); - if (use_tableau() && !coeffs.empty()) { + if (!coeffs.empty()) { add_row_from_term_no_constraint(m_terms.back(), ret); if (m_settings.bound_propagation()) insert_row_with_changed_bounds(A_r().row_count() - 1); } lp_assert(m_var_register.size() == A_r().column_count()); - if (m_need_register_terms) { + if (m_need_register_terms) register_normalized_term(*t, A_r().column_count() - 1); - } return ret; } @@ -1784,44 +1622,32 @@ namespace lp { ul_pair ul(true); // to mark this column as associated_with_row m_columns_to_ul_pairs.push_back(ul); add_basic_var_to_core_fields(); - if (use_tableau()) { - A_r().fill_last_row_with_pivoting(*term, + + A_r().fill_last_row_with_pivoting(*term, j, - m_mpq_lar_core_solver.m_r_solver.m_basis_heading); - m_mpq_lar_core_solver.m_r_solver.m_b.resize(A_r().column_count(), zero_of_type()); - } - else { - fill_last_row_of_A_r(A_r(), term); - } + m_mpq_lar_core_solver.m_r_solver.m_basis_heading); + + m_mpq_lar_core_solver.m_r_solver.update_x(j, get_basic_var_value_from_row(A_r().row_count() - 1)); - if (use_lu()) - fill_last_row_of_A_d(A_d(), term); for (lar_term::ival c : *term) { unsigned j = c.column(); - while (m_usage_in_terms.size() <= j) { + while (m_usage_in_terms.size() <= j) m_usage_in_terms.push_back(0); - } m_usage_in_terms[j] = m_usage_in_terms[j] + 1; } - } void lar_solver::add_basic_var_to_core_fields() { - bool use_lu = m_mpq_lar_core_solver.need_to_presolve_with_double_solver(); - lp_assert(!use_lu || A_r().column_count() == A_d().column_count()); m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); increase_by_one_columns_with_changed_bounds(); m_incorrect_columns.increase_size_by_one(); m_rows_with_changed_bounds.increase_size_by_one(); add_new_var_to_core_fields_for_mpq(true); - if (use_lu) - add_new_var_to_core_fields_for_doubles(true); + } bool lar_solver::bound_is_integer_for_integer_column(unsigned j, const mpq& right_side) const { - if (!column_is_int(j)) - return true; - return right_side.is_int(); + return !column_is_int(j) || right_side.is_int(); } constraint_index lar_solver::add_var_bound_check_on_equal(var_index j, lconstraint_kind kind, const mpq& right_side, var_index& equal_var) { @@ -2005,59 +1831,26 @@ namespace lp { void lar_solver::decide_on_strategy_and_adjust_initial_state() { lp_assert(strategy_is_undecided()); - if (m_columns_to_ul_pairs.size() > m_settings.column_number_threshold_for_using_lu_in_lar_solver) { - m_settings.set_simplex_strategy(simplex_strategy_enum::lu); - } - else { - m_settings.set_simplex_strategy(simplex_strategy_enum::tableau_rows); // todo: when to switch to tableau_costs? - } + + m_settings.set_simplex_strategy(simplex_strategy_enum::tableau_rows); // todo: when to switch to tableau_costs? + adjust_initial_state(); } void lar_solver::adjust_initial_state() { switch (m_settings.simplex_strategy()) { - case simplex_strategy_enum::lu: - adjust_initial_state_for_lu(); - break; case simplex_strategy_enum::tableau_rows: adjust_initial_state_for_tableau_rows(); break; case simplex_strategy_enum::tableau_costs: - lp_assert(false); // not implemented + UNREACHABLE(); // not implemented case simplex_strategy_enum::undecided: adjust_initial_state_for_tableau_rows(); break; } } - void lar_solver::adjust_initial_state_for_lu() { - copy_from_mpq_matrix(A_d()); - unsigned n = A_d().column_count(); - m_mpq_lar_core_solver.m_d_x.resize(n); - m_mpq_lar_core_solver.m_d_lower_bounds.resize(n); - m_mpq_lar_core_solver.m_d_upper_bounds.resize(n); - m_mpq_lar_core_solver.m_d_heading = m_mpq_lar_core_solver.m_r_heading; - m_mpq_lar_core_solver.m_d_basis = m_mpq_lar_core_solver.m_r_basis; - - /* - unsigned j = A_d().column_count(); - A_d().add_column(); - lp_assert(m_mpq_lar_core_solver.m_d_x.size() == j); - // lp_assert(m_mpq_lar_core_solver.m_d_lower_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later - m_mpq_lar_core_solver.m_d_x.resize(j + 1 ); - m_mpq_lar_core_solver.m_d_lower_bounds.resize(j + 1); - m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1); - lp_assert(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method - if (register_in_basis) { - A_d().add_row(); - m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size()); - m_mpq_lar_core_solver.m_d_basis.push_back(j); - }else { - m_mpq_lar_core_solver.m_d_heading.push_back(- static_cast(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1); - m_mpq_lar_core_solver.m_d_nbasis.push_back(j); - }*/ - } - + void lar_solver::adjust_initial_state_for_tableau_rows() { for (unsigned i = 0; i < m_terms.size(); i++) { if (m_var_register.external_is_used(tv::mask_term(i))) @@ -2066,24 +1859,7 @@ namespace lp { } } - // this fills the last row of A_d and sets the basis column: -1 in the last column of the row - void lar_solver::fill_last_row_of_A_d(static_matrix& A, const lar_term* ls) { - lp_assert(A.row_count() > 0); - lp_assert(A.column_count() > 0); - unsigned last_row = A.row_count() - 1; - lp_assert(A.m_rows[last_row].empty()); - - for (auto t : *ls) { - lp_assert(!is_zero(t.coeff())); - var_index j = t.column(); - A.set(last_row, j, -t.coeff().get_double()); - } - - unsigned basis_j = A.column_count() - 1; - A.set(last_row, basis_j, -1); - lp_assert(A.is_correct()); - } - + void lar_solver::update_column_type_and_bound_with_ub(unsigned j, lp::lconstraint_kind kind, const mpq& right_side, unsigned constraint_index) { SASSERT(column_has_upper_bound(j)); if (column_has_lower_bound(j)) { @@ -2156,7 +1932,7 @@ namespace lp { } default: - lp_unreachable(); + UNREACHABLE(); } if (m_mpq_lar_core_solver.m_r_upper_bounds[j] == m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; @@ -2210,7 +1986,7 @@ namespace lp { } default: - lp_unreachable(); + UNREACHABLE(); } } @@ -2260,7 +2036,7 @@ namespace lp { } default: - lp_unreachable(); + UNREACHABLE(); } } void lar_solver::update_bound_with_no_ub_no_lb(var_index j, lconstraint_kind kind, const mpq& right_side, constraint_index ci) { @@ -2301,7 +2077,7 @@ namespace lp { } default: - lp_unreachable(); + UNREACHABLE(); } } @@ -2536,10 +2312,6 @@ namespace lp { return true; } - void lar_solver::pivot_column_tableau(unsigned j, unsigned row_index) { - m_mpq_lar_core_solver.m_r_solver.pivot_column_tableau(j, row_index); - m_mpq_lar_core_solver.m_r_solver.change_basis(j, r_basis()[row_index]); - } } // namespace lp diff --git a/src/math/lp/lar_solver.h b/src/math/lp/lar_solver.h index f13231610..182ef0be3 100644 --- a/src/math/lp/lar_solver.h +++ b/src/math/lp/lar_solver.h @@ -31,14 +31,12 @@ #include "math/lp/lar_constraints.h" #include "math/lp/lar_core_solver.h" #include "math/lp/numeric_pair.h" -#include "math/lp/scaler.h" #include "math/lp/lp_primal_core_solver.h" #include "math/lp/random_updater.h" #include "util/stacked_value.h" #include "math/lp/stacked_vector.h" #include "math/lp/implied_bound.h" #include "math/lp/bound_analyzer_on_row.h" -#include "math/lp/conversion_helper.h" #include "math/lp/int_solver.h" #include "math/lp/nra_solver.h" #include "math/lp/lp_types.h" @@ -113,8 +111,6 @@ class lar_solver : public column_namer { // end of fields ////////////////// methods //////////////////////////////// - static_matrix & A_d(); - static_matrix const & A_d() const; static bool valid_index(unsigned j) { return static_cast(j) >= 0;} const lar_term & get_term(unsigned j) const; @@ -125,7 +121,6 @@ class lar_solver : public column_namer { bool term_is_int(const lar_term * t) const; bool term_is_int(const vector> & coeffs) const; void add_non_basic_var_to_core_fields(unsigned ext_j, bool is_int); - void add_new_var_to_core_fields_for_doubles(bool register_in_basis); void add_new_var_to_core_fields_for_mpq(bool register_in_basis); mpq adjust_bound_for_int(lpvar j, lconstraint_kind&, const mpq&); @@ -134,7 +129,6 @@ class lar_solver : public column_namer { var_index add_term_undecided(const vector> & coeffs); bool term_coeffs_are_ok(const vector> & coeffs); void push_term(lar_term* t); - void add_row_for_term(const lar_term * term, unsigned term_ext_index); void add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index); void add_basic_var_to_core_fields(); bool compare_values(impq const& lhs, lconstraint_kind k, const mpq & rhs); @@ -163,39 +157,28 @@ class lar_solver : public column_namer { unsigned row_of_basic_column(unsigned) const; void decide_on_strategy_and_adjust_initial_state(); void adjust_initial_state(); - void adjust_initial_state_for_lu(); void adjust_initial_state_for_tableau_rows(); - void fill_last_row_of_A_d(static_matrix & A, const lar_term* ls); - bool use_lu() const; bool sizes_are_correct() const; bool implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const; - template - void analyze_new_bounds_on_row_tableau( - unsigned row_index, - lp_bound_propagator & bp ) { - - if (A_r().m_rows[row_index].size() > settings().max_row_length_for_bound_propagation - || row_has_a_big_num(row_index)) - return; - lp_assert(use_tableau()); - - bound_analyzer_on_row, lp_bound_propagator>::analyze_row(A_r().m_rows[row_index], - null_ci, - zero_of_type>(), - row_index, - bp - ); - } void substitute_basis_var_in_terms_for_row(unsigned i); + template - void calculate_implied_bounds_for_row(unsigned i, lp_bound_propagator & bp) { - SASSERT(use_tableau()); - analyze_new_bounds_on_row_tableau(i, bp); + unsigned calculate_implied_bounds_for_row(unsigned row_index, lp_bound_propagator & bp) { + + if (A_r().m_rows[row_index].size() > settings().max_row_length_for_bound_propagation || row_has_a_big_num(row_index)) + return 0; + + return bound_analyzer_on_row, lp_bound_propagator>::analyze_row( + A_r().m_rows[row_index], + null_ci, + zero_of_type>(), + row_index, + bp); } + static void clean_popped_elements(unsigned n, u_set& set); - static void shrink_inf_set_after_pop(unsigned n, u_set & set); bool maximize_term_on_tableau(const lar_term & term, impq &term_max); bool costs_are_zeros_for_r_solver() const; @@ -209,12 +192,9 @@ class lar_solver : public column_namer { void set_lower_bound_witness(var_index j, constraint_index ci); void substitute_terms_in_linear_expression( const vector>& left_side_with_terms, vector> &left_side) const; - void detect_rows_of_bound_change_column_for_nbasic_column(unsigned j); + void detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j); - bool use_tableau() const; bool use_tableau_costs() const; - void detect_rows_of_column_with_bound_change(unsigned j); - void adjust_x_of_column(unsigned j); bool tableau_with_costs() const; bool costs_are_used() const; void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair & delta); @@ -224,27 +204,15 @@ class lar_solver : public column_namer { void detect_rows_with_changed_bounds_for_column(unsigned j); void detect_rows_with_changed_bounds(); - void update_x_and_inf_costs_for_columns_with_changed_bounds(); void update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); void solve_with_core_solver(); numeric_pair get_basic_var_value_from_row(unsigned i); - template - void add_last_rows_to_lu(lp_primal_core_solver & s); - bool x_is_correct() const; - void fill_last_row_of_A_r(static_matrix> & A, const lar_term * ls); - template - void create_matrix_A(static_matrix & matr); - template - void copy_from_mpq_matrix(static_matrix & matr); - bool try_to_set_fixed(column_info & ci); bool all_constrained_variables_are_registered(const vector>& left_side); bool all_constraints_hold() const; bool constraint_holds(const lar_base_constraint & constr, std::unordered_map & var_map) const; - bool the_relations_are_of_same_type(const vector> & evidence, lconstraint_kind & the_kind_of_sum) const; static void register_in_map(std::unordered_map & coeffs, const lar_base_constraint & cn, const mpq & a); static void register_monoid_in_map(std::unordered_map & coeffs, const mpq & a, unsigned j); bool the_left_sides_sum_to_zero(const vector> & evidence) const; - bool the_right_sides_do_not_sum_to_zero(const vector> & evidence); bool explanation_is_correct(explanation&) const; bool inf_explanation_is_correct() const; mpq sum_of_right_sides_of_explanation(explanation &) const; @@ -254,7 +222,6 @@ class lar_solver : public column_namer { int inf_sign) const; mpq get_left_side_val(const lar_base_constraint & cns, const std::unordered_map & var_map) const; void fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector& column_list); - void pivot_fixed_vars_from_basis(); bool column_represents_row_in_tableau(unsigned j); void make_sure_that_the_bottom_right_elem_not_zero_in_tableau(unsigned i, unsigned j); void remove_last_row_and_column_from_tableau(unsigned j); @@ -264,27 +231,16 @@ class lar_solver : public column_namer { void remove_last_column_from_tableau(); void pop_tableau(); void clean_inf_set_of_r_solver_after_pop(); - void shrink_explanation_to_minimum(vector> & explanation) const; inline bool column_value_is_integer(unsigned j) const { return get_column_value(j).is_int(); } bool model_is_int_feasible() const; - inline - indexed_vector & get_column_in_lu_mode(unsigned j) { - m_column_buffer.clear(); - m_column_buffer.resize(A_r().row_count()); - m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); - return m_column_buffer; - } + bool bound_is_integer_for_integer_column(unsigned j, const mpq & right_side) const; inline lar_core_solver & get_core_solver() { return m_mpq_lar_core_solver; } - void catch_up_in_updating_int_solver(); var_index to_column(unsigned ext_j) const; void fix_terms_with_rounded_columns(); - void update_delta_for_terms(const impq & delta, unsigned j, const vector&); - void fill_vars_to_terms(vector> & vars_to_terms); bool remove_from_basis(unsigned); lar_term get_term_to_maximize(unsigned ext_j) const; bool sum_first_coords(const lar_term& t, mpq & val) const; - void collect_rounded_rows_to_fix(); void register_normalized_term(const lar_term&, lpvar); void deregister_normalized_term(const lar_term&); @@ -300,10 +256,7 @@ public: return m_fixed_var_table_int; } - map, default_eq>& fixed_var_table_int() { - return m_fixed_var_table_int; - } - + const map, default_eq>& fixed_var_table_real() const { return m_fixed_var_table_real; } @@ -329,9 +282,7 @@ public: inline void set_column_value_test(unsigned j, const impq& v) { set_column_value(j, v); } - - unsigned get_total_iterations() const; - + var_index add_named_var(unsigned ext_j, bool is_integer, const std::string&); lp_status maximize_term(unsigned j_or_term, impq &term_max); @@ -383,9 +334,9 @@ public: void mark_rows_for_bound_prop(lpvar j); template void propagate_bounds_for_touched_rows(lp_bound_propagator & bp) { - SASSERT(use_tableau()); + unsigned num_prop = 0; for (unsigned i : m_rows_with_changed_bounds) { - calculate_implied_bounds_for_row(i, bp); + num_prop += calculate_implied_bounds_for_row(i, bp); if (settings().get_cancel_flag()) return; } @@ -405,8 +356,20 @@ public: } m_rows_with_changed_bounds.clear(); } + + template + void check_missed_propagations(lp_bound_propagator & bp) { + for (unsigned i = 0; i < A_r().row_count(); i++) + if (!m_rows_with_changed_bounds.contains(i)) + if (0 < calculate_implied_bounds_for_row(i, bp)) { + verbose_stream() << i << ": " << get_row(i) << "\n"; + } + } + + bool is_fixed_at_bound(column_index const& j); + bool has_fixed_at_bound(); - bool is_fixed(column_index const& j) const { return column_is_fixed(j); } + bool is_fixed(column_index const& j) const { return column_is_fixed(j); } inline column_index to_column_index(unsigned v) const { return column_index(external_to_column_index(v)); } bool external_is_used(unsigned) const; void pop(unsigned k); @@ -436,8 +399,8 @@ public: void change_basic_columns_dependend_on_a_given_nb_column_report(unsigned j, const numeric_pair & delta, const ChangeReport& after) { - if (use_tableau()) { - for (const auto & c : A_r().m_columns[j]) { + + for (const auto & c : A_r().m_columns[j]) { unsigned bj = m_mpq_lar_core_solver.m_r_basis[c.var()]; if (tableau_with_costs()) { m_basic_columns_with_changed_cost.insert(bj); @@ -447,20 +410,8 @@ public: TRACE("change_x_del", tout << "changed basis column " << bj << ", it is " << ( m_mpq_lar_core_solver.m_r_solver.column_is_feasible(bj)? "feas":"inf") << std::endl;); - - } - } else { - NOT_IMPLEMENTED_YET(); - m_column_buffer.clear(); - m_column_buffer.resize(A_r().row_count()); - m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); - for (unsigned i : m_column_buffer.m_index) { - unsigned bj = m_mpq_lar_core_solver.m_r_basis[i]; - m_mpq_lar_core_solver.m_r_solver.add_delta_to_x_and_track_feasibility(bj, -m_column_buffer[i] * delta); - } - } - } + } template void set_value_for_nbasic_column_report(unsigned j, @@ -567,8 +518,6 @@ public: return m_mpq_lar_core_solver.lower_bound(j); } - void pivot_column_tableau(unsigned j, unsigned row_index); - inline const impq & column_upper_bound(unsigned j) const { return m_mpq_lar_core_solver.upper_bound(j); } diff --git a/src/math/lp/lar_term.h b/src/math/lp/lar_term.h index 3ef424e24..fc73f949f 100644 --- a/src/math/lp/lar_term.h +++ b/src/math/lp/lar_term.h @@ -179,7 +179,7 @@ public: return p.coeff().is_one(); } } - lp_unreachable(); + UNREACHABLE(); return false; } diff --git a/src/math/lp/lia_move.h b/src/math/lp/lia_move.h index 65da5826e..ca61d7b7a 100644 --- a/src/math/lp/lia_move.h +++ b/src/math/lp/lia_move.h @@ -45,7 +45,7 @@ inline std::string lia_move_to_string(lia_move m) { case lia_move::unsat: return "unsat"; default: - lp_assert(false); + UNREACHABLE(); }; return "strange"; } diff --git a/src/math/lp/lp_bound_propagator.h b/src/math/lp/lp_bound_propagator.h index 14f646fc1..dba93398e 100644 --- a/src/math/lp/lp_bound_propagator.h +++ b/src/math/lp/lp_bound_propagator.h @@ -578,8 +578,12 @@ public: ); bool added = m_imp.add_eq(je, ke, exp, is_fixed); - if (added) - lp().stats().m_offset_eqs++; + if (added) { + if (is_fixed) + lp().stats().m_fixed_eqs++; + else + lp().stats().m_offset_eqs++; + } return added; } diff --git a/src/math/lp/lp_core_solver_base.cpp b/src/math/lp/lp_core_solver_base.cpp index 83da68d9d..f1ae95ea0 100644 --- a/src/math/lp/lp_core_solver_base.cpp +++ b/src/math/lp/lp_core_solver_base.cpp @@ -23,75 +23,24 @@ Revision History: #include "util/vector.h" #include #include "math/lp/lp_core_solver_base_def.h" -template bool lp::lp_core_solver_base::A_mult_x_is_off() const; -template bool lp::lp_core_solver_base::A_mult_x_is_off_on_index(const vector &) const; -template bool lp::lp_core_solver_base::basis_heading_is_correct() const; -template void lp::lp_core_solver_base::calculate_pivot_row_of_B_1(unsigned int); -template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); -template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; -template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); -template bool lp::lp_core_solver_base::find_x_by_solving(); -template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; -template lp::non_basic_column_value_position lp::lp_core_solver_base >::get_non_basic_column_value_position(unsigned int) const; -template lp::non_basic_column_value_position lp::lp_core_solver_base::get_non_basic_column_value_position(unsigned int) const; -template void lp::lp_core_solver_base::init_reduced_costs_for_one_iteration(); -template lp::lp_core_solver_base::lp_core_solver_base( - lp::static_matrix&, vector&, - vector&, - vector &, vector &, - vector&, - vector&, - lp::lp_settings&, const column_namer&, const vector&, - const vector&, - const vector&); - -template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); -template void lp::lp_core_solver_base::restore_x(unsigned int, double const&); -template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); -template void lp::lp_core_solver_base::snap_xN_to_bounds_and_free_columns_to_zeroes(); -template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_free_columns_to_zeroes(); -template void lp::lp_core_solver_base::solve_Ax_eq_b(); -template void lp::lp_core_solver_base::solve_Bd(unsigned int); -template void lp::lp_core_solver_base::solve_Bd(unsigned int, lp::indexed_vector&, lp::indexed_vector&) const; -template void lp::lp_core_solver_base>::solve_Bd(unsigned int, indexed_vector&); -template void lp::lp_core_solver_base::solve_yB(vector&) const; -template bool lp::lp_core_solver_base::update_basis_and_x(int, int, double const&); -template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const double&); -template bool lp::lp_core_solver_base::A_mult_x_is_off() const; -template bool lp::lp_core_solver_base::A_mult_x_is_off_on_index(const vector &) const; template bool lp::lp_core_solver_base::basis_heading_is_correct() const ; -template void lp::lp_core_solver_base::calculate_pivot_row_of_B_1(unsigned int); -template void lp::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template bool lp::lp_core_solver_base::column_is_dual_feasible(unsigned int) const; -template void lp::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); -template bool lp::lp_core_solver_base::find_x_by_solving(); -template void lp::lp_core_solver_base::init_reduced_costs_for_one_iteration(); template bool lp::lp_core_solver_base::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); -template void lp::lp_core_solver_base::restore_x(unsigned int, lp::mpq const&); -template void lp::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); -template void lp::lp_core_solver_base::solve_Ax_eq_b(); -template void lp::lp_core_solver_base::solve_Bd(unsigned int); -template void lp::lp_core_solver_base::solve_yB(vector&) const; -template bool lp::lp_core_solver_base::update_basis_and_x(int, int, lp::mpq const&); template void lp::lp_core_solver_base::add_delta_to_entering(unsigned int, const lp::mpq&); -template void lp::lp_core_solver_base >::calculate_pivot_row_of_B_1(unsigned int); -template void lp::lp_core_solver_base >::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); template void lp::lp_core_solver_base >::init(); template void lp::lp_core_solver_base >::init_basis_heading_and_non_basic_columns_vector(); -template void lp::lp_core_solver_base >::init_reduced_costs_for_one_iteration(); -template lp::lp_core_solver_base >::lp_core_solver_base(lp::static_matrix >&, vector >&, vector&, vector &, vector &, vector >&, vector&, lp::lp_settings&, const column_namer&, const vector&, +template lp::lp_core_solver_base >::lp_core_solver_base(lp::static_matrix >&, + // vector >&, + vector&, vector &, vector &, vector >&, vector&, lp::lp_settings&, const column_namer&, const vector&, const vector >&, const vector >&); template bool lp::lp_core_solver_base >::print_statistics_with_cost_and_check_that_the_time_is_over(lp::numeric_pair, std::ostream&); -template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_fill_xB(); -template void lp::lp_core_solver_base >::solve_Ax_eq_b(); -template void lp::lp_core_solver_base >::solve_Bd(unsigned int); -template bool lp::lp_core_solver_base >::update_basis_and_x(int, int, lp::numeric_pair const&); + template void lp::lp_core_solver_base >::add_delta_to_entering(unsigned int, const lp::numeric_pair&); template lp::lp_core_solver_base::lp_core_solver_base( lp::static_matrix&, - vector&, + //vector&, vector&, vector &, vector &, vector&, @@ -102,49 +51,20 @@ template lp::lp_core_solver_base::lp_core_solver_base( const vector&, const vector&); template bool lp::lp_core_solver_base >::print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream &); -template std::string lp::lp_core_solver_base::column_name(unsigned int) const; -template void lp::lp_core_solver_base::pretty_print(std::ostream & out); -template void lp::lp_core_solver_base::restore_state(double*, double*); -template void lp::lp_core_solver_base::save_state(double*, double*); template std::string lp::lp_core_solver_base::column_name(unsigned int) const; template void lp::lp_core_solver_base::pretty_print(std::ostream & out); -template void lp::lp_core_solver_base::restore_state(lp::mpq*, lp::mpq*); -template void lp::lp_core_solver_base::save_state(lp::mpq*, lp::mpq*); template std::string lp::lp_core_solver_base >::column_name(unsigned int) const; template void lp::lp_core_solver_base >::pretty_print(std::ostream & out); -template void lp::lp_core_solver_base >::restore_state(lp::mpq*, lp::mpq*); -template void lp::lp_core_solver_base >::save_state(lp::mpq*, lp::mpq*); -template void lp::lp_core_solver_base >::solve_yB(vector&) const; -template void lp::lp_core_solver_base::init_lu(); -template void lp::lp_core_solver_base::init_lu(); -template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; -template int lp::lp_core_solver_base >::pivots_in_column_and_row_are_different(int, int) const; -template int lp::lp_core_solver_base::pivots_in_column_and_row_are_different(int, int) const; -template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base::calc_current_x_is_feasible_include_non_basis(void)const; template bool lp::lp_core_solver_base >::calc_current_x_is_feasible_include_non_basis() const; -template void lp::lp_core_solver_base >::pivot_fixed_vars_from_basis(); -template bool lp::lp_core_solver_base::column_is_feasible(unsigned int) const; template bool lp::lp_core_solver_base::column_is_feasible(unsigned int) const; // template void lp::lp_core_solver_base >::print_linear_combination_of_column_indices(vector, std::allocator > > const&, std::ostream&) const; template bool lp::lp_core_solver_base >::column_is_feasible(unsigned int) const; -template bool lp::lp_core_solver_base >::snap_non_basic_x_to_bound(); -template void lp::lp_core_solver_base >::init_lu(); -template bool lp::lp_core_solver_base >::A_mult_x_is_off_on_index(vector const&) const; -template bool lp::lp_core_solver_base >::find_x_by_solving(); -template void lp::lp_core_solver_base >::restore_x(unsigned int, lp::numeric_pair const&); template bool lp::lp_core_solver_base>::pivot_column_tableau(unsigned int, unsigned int); -template bool lp::lp_core_solver_base::pivot_column_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base::pivot_column_tableau(unsigned int, unsigned int); template void lp::lp_core_solver_base >::transpose_rows_tableau(unsigned int, unsigned int); template bool lp::lp_core_solver_base >::inf_set_is_correct() const; -template bool lp::lp_core_solver_base::inf_set_is_correct() const; template bool lp::lp_core_solver_base::inf_set_is_correct() const; -template bool lp::lp_core_solver_base >::infeasibility_costs_are_correct() const; -template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; -template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; -template void lp::lp_core_solver_base >::calculate_pivot_row(unsigned int); template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int); -template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int, lp::numeric_pair const&); -template void lp::lp_core_solver_base::solve_Bd(unsigned int, lp::indexed_vector&, lp::indexed_vector&) const; -template void lp::lp_core_solver_base >::solve_Bd(unsigned int, lp::indexed_vector&, lp::indexed_vector&) const; + + diff --git a/src/math/lp/lp_core_solver_base.h b/src/math/lp/lp_core_solver_base.h index 5cde4485d..fb0c28507 100644 --- a/src/math/lp/lp_core_solver_base.h +++ b/src/math/lp/lp_core_solver_base.h @@ -25,11 +25,21 @@ Revision History: #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/numeric_pair.h" #include "math/lp/static_matrix.h" -#include "math/lp/lu.h" #include "math/lp/permutation_matrix.h" #include "math/lp/column_namer.h" +#include "math/lp/u_set.h" + namespace lp { +template +X dot_product(const vector & a, const vector & b) { + lp_assert(a.size() == b.size()); + auto r = zero_of_type(); + for (unsigned i = 0; i < a.size(); i++) { + r += a[i] * b[i]; + } + return r; +} template // X represents the type of the x variable and the bounds class lp_core_solver_base { @@ -53,44 +63,31 @@ public: bool current_x_is_infeasible() const { return m_inf_set.size() != 0; } private: u_set m_inf_set; - bool m_using_infeas_costs; public: const u_set& inf_set() const { return m_inf_set; } u_set& inf_set() { return m_inf_set; } void inf_set_increase_size_by_one() { m_inf_set.increase_size_by_one(); } bool inf_set_contains(unsigned j) const { return m_inf_set.contains(j); } - unsigned inf_set_size() const { return m_inf_set.size(); } - bool using_infeas_costs() const { return m_using_infeas_costs; } - void set_using_infeas_costs(bool val) { m_using_infeas_costs = val; } - vector m_columns_nz; // m_columns_nz[i] keeps an approximate value of non zeroes the i-th column - vector m_rows_nz; // m_rows_nz[i] keeps an approximate value of non zeroes in the i-th row - indexed_vector m_pivot_row_of_B_1; // the pivot row of the reverse of B + unsigned inf_set_size() const { return m_inf_set.size(); } indexed_vector m_pivot_row; // this is the real pivot row of the simplex tableu static_matrix & m_A; // the matrix A - vector & m_b; // the right side + // vector const & m_b; // the right side vector & m_basis; vector& m_nbasis; vector& m_basis_heading; - vector & m_x; // a feasible solution, the fist time set in the constructor + vector & m_x; // a feasible solution, the first time set in the constructor vector & m_costs; lp_settings & m_settings; - lu> * m_factorization = nullptr; - vector m_y; // the buffer for yB = cb - // a device that is able to solve Bx=c, xB=d, and change the basis + const column_namer & m_column_names; - indexed_vector m_w; // the vector featuring in 24.3 of the Chvatal book vector m_d; // the vector of reduced costs - indexed_vector m_ed; // the solution of B*m_ed = a const vector & m_column_types; const vector & m_lower_bounds; - const vector & m_upper_bounds; - vector m_column_norms; // the approximate squares of column norms that help choosing a profitable column - vector m_copy_of_xB; + const vector & m_upper_bounds; unsigned m_basis_sort_counter; - vector m_steepest_edge_coefficients; vector m_trace_of_basis_change_vector; // the even positions are entering, the odd positions are leaving bool m_tracing_basis_changes; - u_set* m_pivoted_rows; + u_set* m_pivoted_rows; bool m_look_for_feasible_solution_only; void start_tracing_basis_changes() { @@ -118,7 +115,7 @@ public: unsigned m_n() const { return m_A.column_count(); } // the number of columns in the matrix m_A lp_core_solver_base(static_matrix & A, - vector & b, // the right side vector + //vector & b, // the right side vector vector & basis, vector & nbasis, vector & heading, @@ -134,7 +131,7 @@ public: void init(); virtual ~lp_core_solver_base() { - delete m_factorization; + } vector & non_basis() { @@ -149,46 +146,12 @@ public: lp_status get_status() const{ return m_status; } - - void fill_cb(T * y) const; - - void fill_cb(vector & y) const; - - void solve_yB(vector & y) const; - - void solve_Bd(unsigned entering, indexed_vector & d_buff, indexed_vector& w_buff) const; - - void solve_Bd(unsigned entering); - - void solve_Bd(unsigned entering, indexed_vector & column); - void pretty_print(std::ostream & out); - void save_state(T * w_buffer, T * d_buffer); - - void restore_state(T * w_buffer, T * d_buffer); - X get_cost() const { return dot_product(m_costs, m_x); } - void copy_m_w(T * buffer); - - void restore_m_w(T * buffer); - - // needed for debugging - void copy_m_ed(T * buffer); - - void restore_m_ed(T * buffer); - - bool A_mult_x_is_off() const; - - bool A_mult_x_is_off_on_index(const vector & index) const; - // from page 182 of Istvan Maros's book - void calculate_pivot_row_of_B_1(unsigned pivot_row); - - void calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row); - void add_delta_to_entering(unsigned entering, const X & delta); const X & get_var_value(unsigned j) const { @@ -207,13 +170,10 @@ public: void set_total_iterations(unsigned s) { m_total_iterations = s; } - void set_non_basic_x_to_correct_bounds(); - bool at_bound(const X &x, const X & bound) const { return !below_bound(x, bound) && !above_bound(x, bound); } - bool need_to_pivot_to_basis_tableau() const { unsigned m = m_A.row_count(); for (unsigned i = 0; i < m; i++) { @@ -235,11 +195,7 @@ public: if (m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) return true; CASSERT("check_static_matrix", m_A.is_correct()); - if (m_using_infeas_costs) { - if (infeasibility_costs_are_correct() == false) { - return false; - } - } + unsigned n = m_A.column_count(); for (unsigned j = 0; j < n; j++) { @@ -262,19 +218,17 @@ public: } bool below_bound(const X & x, const X & bound) const { - return precise()? x < bound : below_bound_numeric(x, bound, m_settings.primal_feasibility_tolerance); + return x < bound ; } bool above_bound(const X & x, const X & bound) const { - return precise()? x > bound : above_bound_numeric(x, bound, m_settings.primal_feasibility_tolerance); + return x > bound ; } bool x_below_low_bound(unsigned p) const { return below_bound(m_x[p], m_lower_bounds[p]); } - bool infeasibility_costs_are_correct() const; - bool infeasibility_cost_is_correct_for_column(unsigned j) const; bool x_above_lower_bound(unsigned p) const { return above_bound(m_x[p], m_lower_bounds[p]); @@ -284,7 +238,6 @@ public: return below_bound(m_x[p], m_upper_bounds[p]); } - bool x_above_upper_bound(unsigned p) const { return above_bound(m_x[p], m_upper_bounds[p]); } @@ -310,15 +263,10 @@ public: bool d_is_not_positive(unsigned j) const; - bool time_is_over(); void rs_minus_Anx(vector & rs); - bool find_x_by_solving(); - - bool update_basis_and_x(int entering, int leaving, X const & tt); - bool basis_has_no_doubles() const; bool non_basis_has_no_doubles() const; @@ -328,79 +276,19 @@ public: bool basis_heading_is_correct() const; - void restore_x_and_refactor(int entering, int leaving, X const & t); - - void restore_x(unsigned entering, X const & t); - - void fill_reduced_costs_from_m_y_by_rows(); - - void copy_rs_to_xB(vector & rs); virtual bool lower_bounds_are_set() const { return false; } X lower_bound_value(unsigned j) const { return m_lower_bounds[j]; } X upper_bound_value(unsigned j) const { return m_upper_bounds[j]; } column_type get_column_type(unsigned j) const {return m_column_types[j]; } - bool pivot_row_element_is_too_small_for_ratio_test(unsigned j) { - return m_settings.abs_val_is_smaller_than_pivot_tolerance(m_pivot_row[j]); - } - + X bound_span(unsigned j) const { return m_upper_bounds[j] - m_lower_bounds[j]; } std::string column_name(unsigned column) const; - void copy_right_side(vector & rs); - - void add_delta_to_xB(vector & del); - - void find_error_in_BxB(vector& rs); - - // recalculates the projection of x to B, such that Ax = b, whereab is the right side - void solve_Ax_eq_b(); - - bool snap_non_basic_x_to_bound() { - bool ret = false; - for (unsigned j : non_basis()) - ret = snap_column_to_bound(j) || ret; - return ret; - } - - - - bool snap_column_to_bound(unsigned j) { - switch (m_column_types[j]) { - case column_type::fixed: - if (x_is_at_bound(j)) - break; - m_x[j] = m_lower_bounds[j]; - return true; - case column_type::boxed: - if (x_is_at_bound(j)) - break; // we should preserve x if possible - // snap randomly - if (m_settings.random_next() % 2 == 1) - m_x[j] = m_lower_bounds[j]; - else - m_x[j] = m_upper_bounds[j]; - return true; - case column_type::lower_bound: - if (x_is_at_lower_bound(j)) - break; - m_x[j] = m_lower_bounds[j]; - return true; - case column_type::upper_bound: - if (x_is_at_upper_bound(j)) - break; - m_x[j] = m_upper_bounds[j]; - return true; - default: - break; - } - return false; - } - bool make_column_feasible(unsigned j, numeric_pair & delta) { bool ret = false; lp_assert(m_basis_heading[j] < 0); @@ -445,21 +333,7 @@ public: } - - void snap_non_basic_x_to_bound_and_free_to_zeroes(); - void snap_xN_to_bounds_and_fill_xB(); - - void snap_xN_to_bounds_and_free_columns_to_zeroes(); - - void init_reduced_costs_for_one_iteration(); - - non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; - - void init_lu(); - int pivots_in_column_and_row_are_different(int entering, int leaving) const; - void pivot_fixed_vars_from_basis(); bool remove_from_basis(unsigned j); - bool remove_from_basis(unsigned j, const impq&); bool pivot_column_general(unsigned j, unsigned j_basic, indexed_vector & w); void init_basic_part_of_basis_heading() { unsigned m = m_basis.size(); @@ -531,31 +405,6 @@ public: change_basis_unconditionally(leaving, entering); } - bool non_basic_column_is_set_correctly(unsigned j) const { - if (j >= this->m_n()) - return false; - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - if (!this->x_is_at_bound(j)) - return false; - break; - case column_type::lower_bound: - if (!this->x_is_at_lower_bound(j)) - return false; - break; - case column_type::upper_bound: - if (!this->x_is_at_upper_bound(j)) - return false; - break; - case column_type::free_column: - break; - default: - lp_assert(false); - break; - } - return true; - } bool non_basic_columns_are_set_correctly() const { for (unsigned j : this->m_nbasis) if (!column_is_feasible(j)) { @@ -615,13 +464,11 @@ public: out << "[-oo, oo]"; break; default: - lp_assert(false); + UNREACHABLE(); } return out << "\n"; } - bool column_is_free(unsigned j) const { return this->m_column_types[j] == column_type::free_column; } - bool column_is_fixed(unsigned j) const { return this->m_column_types[j] == column_type::fixed; } @@ -654,16 +501,6 @@ public: } } - // only check for basic columns - bool calc_current_x_is_feasible() const { - unsigned i = this->m_m(); - while (i--) { - if (!column_is_feasible(m_basis[i])) - return false; - } - return true; - } - void transpose_rows_tableau(unsigned i, unsigned ii); void pivot_to_reduced_costs_tableau(unsigned i, unsigned j); @@ -671,13 +508,10 @@ public: bool pivot_column_tableau(unsigned j, unsigned row_index); bool divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col); - bool precise() const { return numeric_traits::precise(); } - simplex_strategy_enum simplex_strategy() const { return m_settings.simplex_strategy(); } - bool use_tableau() const { return m_settings.use_tableau(); } template static void swap(vector &v, unsigned i, unsigned j) { @@ -767,7 +601,7 @@ public: return m_iters_with_no_cost_growing; } - void calculate_pivot_row(unsigned i); + unsigned get_base_column_in_row(unsigned row_index) const { return m_basis[row_index]; } diff --git a/src/math/lp/lp_core_solver_base_def.h b/src/math/lp/lp_core_solver_base_def.h index f85de1111..8619c926e 100644 --- a/src/math/lp/lp_core_solver_base_def.h +++ b/src/math/lp/lp_core_solver_base_def.h @@ -28,7 +28,7 @@ namespace lp { template lp_core_solver_base:: lp_core_solver_base(static_matrix & A, - vector & b, // the right side vector + // vector & b, // the right side vector vector & basis, vector & nbasis, vector & heading, @@ -43,29 +43,20 @@ lp_core_solver_base(static_matrix & A, m_iters_with_no_cost_growing(0), m_status(lp_status::FEASIBLE), m_inf_set(A.column_count()), - m_using_infeas_costs(false), - m_pivot_row_of_B_1(A.row_count()), m_pivot_row(A.column_count()), m_A(A), - m_b(b), m_basis(basis), m_nbasis(nbasis), m_basis_heading(heading), m_x(x), m_costs(costs), m_settings(settings), - m_y(m_m()), m_column_names(column_names), - m_w(m_m()), m_d(m_n()), - m_ed(m_m()), m_column_types(column_types), m_lower_bounds(lower_bound_values), m_upper_bounds(upper_bound_values), - m_column_norms(m_n()), - m_copy_of_xB(m_m()), m_basis_sort_counter(0), - m_steepest_edge_coefficients(A.column_count()), m_tracing_basis_changes(false), m_pivoted_rows(nullptr), m_look_for_feasible_solution_only(false) { @@ -82,8 +73,7 @@ allocate_basis_heading() { // the rest of initialization will be handled by the template void lp_core_solver_base:: init() { allocate_basis_heading(); - if (m_settings.use_lu()) - init_factorization(m_factorization, m_A, m_basis, m_settings); + } // i is the pivot row, and j is the pivot column @@ -102,26 +92,6 @@ pivot_to_reduced_costs_tableau(unsigned i, unsigned j) { } -template void lp_core_solver_base:: -fill_cb(T * y) const { - for (unsigned i = 0; i < m_m(); i++) { - y[i] = m_costs[m_basis[i]]; - } -} - - -template void lp_core_solver_base:: -fill_cb(vector & y) const { - for (unsigned i = 0; i < m_m(); i++) { - y[i] = m_costs[m_basis[i]]; - } -} - -template void lp_core_solver_base:: -solve_yB(vector & y) const { - fill_cb(y); // now y = cB, that is the projection of costs to basis - m_factorization->solve_yB_with_error_check(y, m_basis); -} // template void lp_core_solver_base:: // update_index_of_ed() { @@ -132,35 +102,9 @@ solve_yB(vector & y) const { // m_index_of_ed.push_back(i); // } // } -template void lp_core_solver_base::solve_Bd(unsigned entering, indexed_vector & column) { - lp_assert(!m_settings.use_tableau()); - if (m_factorization == nullptr) { - init_factorization(m_factorization, m_A, m_basis, m_settings); - } - m_factorization->solve_Bd_faster(entering, column); -} -template void lp_core_solver_base::solve_Bd(unsigned , indexed_vector& , indexed_vector &) const { - NOT_IMPLEMENTED_YET(); -} -template void lp_core_solver_base:: -solve_Bd(unsigned entering) { - lp_assert(m_ed.is_OK()); - m_factorization->solve_Bd(entering, m_ed, m_w); - if (this->precise()) - m_columns_nz[entering] = m_ed.m_index.size(); - lp_assert(m_ed.is_OK()); - lp_assert(m_w.is_OK()); -#ifdef Z3DEBUG - // auto B = get_B(*m_factorization, m_basis); - // vector a(m_m()); - // m_A.copy_column_to_vector(entering, a); - // vector cd(m_ed.m_data); - // B.apply_from_left(cd, m_settings); - // lp_assert(vectors_are_equal(cd , a)); -#endif -} + template void lp_core_solver_base:: pretty_print(std::ostream & out) { @@ -168,162 +112,11 @@ pretty_print(std::ostream & out) { pp.print(); } -template void lp_core_solver_base:: -save_state(T * w_buffer, T * d_buffer) { - copy_m_w(w_buffer); - copy_m_ed(d_buffer); -} - -template void lp_core_solver_base:: -restore_state(T * w_buffer, T * d_buffer) { - restore_m_w(w_buffer); - restore_m_ed(d_buffer); -} - -template void lp_core_solver_base:: -copy_m_w(T * buffer) { - unsigned i = m_m(); - while (i --) { - buffer[i] = m_w[i]; - } -} - -template void lp_core_solver_base:: -restore_m_w(T * buffer) { - m_w.m_index.clear(); - unsigned i = m_m(); - while (i--) { - if (!is_zero(m_w[i] = buffer[i])) - m_w.m_index.push_back(i); - } -} - -// needed for debugging -template void lp_core_solver_base:: -copy_m_ed(T * buffer) { - unsigned i = m_m(); - while (i --) { - buffer[i] = m_ed[i]; - } -} - -template void lp_core_solver_base:: -restore_m_ed(T * buffer) { - unsigned i = m_m(); - while (i --) { - m_ed[i] = buffer[i]; - } -} - -template bool lp_core_solver_base:: -A_mult_x_is_off() const { - lp_assert(m_x.size() == m_A.column_count()); - if (numeric_traits::precise()) { - for (unsigned i = 0; i < m_m(); i++) { - X delta = m_b[i] - m_A.dot_product_with_row(i, m_x); - if (delta != numeric_traits::zero()) { - return true; - } - } - return false; - } - T feps = convert_struct::convert(m_settings.refactor_tolerance); - X one = convert_struct::convert(1.0); - for (unsigned i = 0; i < m_m(); i++) { - X delta = abs(m_b[i] - m_A.dot_product_with_row(i, m_x)); - X eps = feps * (one + T(0.1) * abs(m_b[i])); - - if (delta > eps) { -#if 0 - LP_OUT(m_settings, "x is off (" - << "m_b[" << i << "] = " << m_b[i] << " " - << "left side = " << m_A.dot_product_with_row(i, m_x) << ' ' - << "delta = " << delta << ' ' - << "iters = " << total_iterations() << ")" << std::endl); -#endif - return true; - } - } - return false; -} -template bool lp_core_solver_base:: -A_mult_x_is_off_on_index(const vector & index) const { - lp_assert(m_x.size() == m_A.column_count()); - if (numeric_traits::precise()) return false; -#if RUN_A_MULT_X_IS_OFF_FOR_PRECESE - for (unsigned i : index) { - X delta = m_b[i] - m_A.dot_product_with_row(i, m_x); - if (delta != numeric_traits::zero()) { - return true; - } - } - return false; -#endif - // todo(levnach) run on m_ed.m_index only !!!!! - T feps = convert_struct::convert(m_settings.refactor_tolerance); - X one = convert_struct::convert(1.0); - for (unsigned i : index) { - X delta = abs(m_b[i] - m_A.dot_product_with_row(i, m_x)); - X eps = feps * (one + T(0.1) * abs(m_b[i])); - - if (delta > eps) { -#if 0 - LP_OUT(m_settings, "x is off (" - << "m_b[" << i << "] = " << m_b[i] << " " - << "left side = " << m_A.dot_product_with_row(i, m_x) << ' ' - << "delta = " << delta << ' ' - << "iters = " << total_iterations() << ")" << std::endl); -#endif - return true; - } - } - return false; -} - -// from page 182 of Istvan Maros's book -template void lp_core_solver_base:: -calculate_pivot_row_of_B_1(unsigned pivot_row) { - lp_assert(! use_tableau()); - lp_assert(m_pivot_row_of_B_1.is_OK()); - m_pivot_row_of_B_1.clear(); - m_pivot_row_of_B_1.set_value(numeric_traits::one(), pivot_row); - lp_assert(m_pivot_row_of_B_1.is_OK()); - m_factorization->solve_yB_with_error_check_indexed(m_pivot_row_of_B_1, m_basis_heading, m_basis, m_settings); - lp_assert(m_pivot_row_of_B_1.is_OK()); -} - - -template void lp_core_solver_base:: -calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row) { - m_pivot_row.clear(); - - for (unsigned i : m_pivot_row_of_B_1.m_index) { - const T & pi_1 = m_pivot_row_of_B_1[i]; - if (numeric_traits::is_zero(pi_1)) { - continue; - } - for (auto & c : m_A.m_rows[i]) { - unsigned j = c.var(); - if (m_basis_heading[j] < 0) { - m_pivot_row.add_value_at_index_with_drop_tolerance(j, c.coeff() * pi_1); - } - } - } - if (precise()) { - m_rows_nz[pivot_row] = m_pivot_row.m_index.size(); - } -} template void lp_core_solver_base:: add_delta_to_entering(unsigned entering, const X& delta) { m_x[entering] += delta; - if (!use_tableau()) - for (unsigned i : m_ed.m_index) { - if (!numeric_traits::precise()) - m_copy_of_xB[i] = m_x[m_basis[i]]; - m_x[m_basis[i]] -= delta * m_ed[i]; - } - else + for (const auto & c : m_A.m_columns[entering]) { unsigned i = c.var(); m_x[m_basis[i]] -= delta * m_A.get_val(c); @@ -336,7 +129,7 @@ print_statistics(char const* str, X cost, std::ostream & out) { if (str!= nullptr) out << str << " "; out << "iterations = " << (total_iterations() - 1) << ", cost = " << T_to_string(cost) - << ", nonzeros = " << (m_factorization != nullptr? m_factorization->get_number_of_nonzeroes() : m_A.number_of_non_zeroes()) << std::endl; + << ", nonzeros = " << m_A.number_of_non_zeroes() << std::endl; } template bool lp_core_solver_base:: @@ -370,26 +163,6 @@ print_statistics_with_cost_and_check_that_the_time_is_over(X cost, std::ostream return time_is_over(); } -template void lp_core_solver_base:: -set_non_basic_x_to_correct_bounds() { - for (unsigned j : non_basis()) { - switch (m_column_types[j]) { - case column_type::boxed: - m_x[j] = m_d[j] < 0? m_upper_bounds[j]: m_lower_bounds[j]; - break; - case column_type::lower_bound: - m_x[j] = m_lower_bounds[j]; - lp_assert(column_is_dual_feasible(j)); - break; - case column_type::upper_bound: - m_x[j] = m_upper_bounds[j]; - lp_assert(column_is_dual_feasible(j)); - break; - default: - break; - } - } -} template bool lp_core_solver_base:: column_is_dual_feasible(unsigned j) const { switch (m_column_types[j]) { @@ -400,29 +173,24 @@ column_is_dual_feasible(unsigned j) const { case column_type::lower_bound: return x_is_at_lower_bound(j) && d_is_not_negative(j); case column_type::upper_bound: - lp_assert(false); // impossible case + UNREACHABLE(); + break; case column_type::free_column: return numeric_traits::is_zero(m_d[j]); default: - lp_unreachable(); + UNREACHABLE(); } - lp_unreachable(); + UNREACHABLE(); return false; } template bool lp_core_solver_base:: d_is_not_negative(unsigned j) const { - if (numeric_traits::precise()) { - return m_d[j] >= numeric_traits::zero(); - } - return m_d[j] > -T(0.00001); + return m_d[j] >= numeric_traits::zero(); } template bool lp_core_solver_base:: d_is_not_positive(unsigned j) const { - if (numeric_traits::precise()) { - return m_d[j] <= numeric_traits::zero(); - } - return m_d[j] < T(0.00001); + return m_d[j] <= numeric_traits::zero(); } @@ -441,7 +209,7 @@ template void lp_core_solver_base:: rs_minus_Anx(vector & rs) { unsigned row = m_m(); while (row--) { - auto &rsv = rs[row] = m_b[row]; + auto& rsv = rs[row] = zero_of_type(); //m_b[row]; for (auto & it : m_A.m_rows[row]) { unsigned j = it.var(); if (m_basis_heading[j] < 0) { @@ -451,45 +219,22 @@ rs_minus_Anx(vector & rs) { } } -template bool lp_core_solver_base:: -find_x_by_solving() { - solve_Ax_eq_b(); - bool ret= !A_mult_x_is_off(); - return ret; -} - template bool lp_core_solver_base::column_is_feasible(unsigned j) const { const X& x = this->m_x[j]; switch (this->m_column_types[j]) { case column_type::fixed: case column_type::boxed: - if (this->above_bound(x, this->m_upper_bounds[j])) { - return false; - } else if (this->below_bound(x, this->m_lower_bounds[j])) { - return false; - } else { - return true; - } - break; + return !this->above_bound(x, this->m_upper_bounds[j]) && + !this->below_bound(x, this->m_lower_bounds[j]); case column_type::lower_bound: - if (this->below_bound(x, this->m_lower_bounds[j])) { - return false; - } else { - return true; - } - break; + return !this->below_bound(x, this->m_lower_bounds[j]); case column_type::upper_bound: - if (this->above_bound(x, this->m_upper_bounds[j])) { - return false; - } else { - return true; - } - break; + return !this->above_bound(x, this->m_upper_bounds[j]); case column_type::free_column: return true; break; default: - lp_unreachable(); + UNREACHABLE(); } return false; // it is unreachable } @@ -517,70 +262,9 @@ template bool lp_core_solver_base::inf_set_is_cor return true; } -template bool lp_core_solver_base:: -update_basis_and_x(int entering, int leaving, X const & tt) { - - if (!is_zero(tt)) { - add_delta_to_entering(entering, tt); - if ((!numeric_traits::precise()) && A_mult_x_is_off_on_index(m_ed.m_index) && !find_x_by_solving()) { - init_factorization(m_factorization, m_A, m_basis, m_settings); - if (!find_x_by_solving()) { - restore_x(entering, tt); - if(A_mult_x_is_off()) { - m_status = lp_status::FLOATING_POINT_ERROR; - m_iters_with_no_cost_growing++; - return false; - } - - init_factorization(m_factorization, m_A, m_basis, m_settings); - m_iters_with_no_cost_growing++; - if (m_factorization->get_status() != LU_status::OK) { - std::stringstream s; - // s << "failing refactor on off_result for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations(); - m_status = lp_status::FLOATING_POINT_ERROR; - return false; - } - return false; - } - } - } - - bool refactor = m_factorization->need_to_refactor(); - if (!refactor) { - const T & pivot = this->m_pivot_row[entering]; // m_ed[m_factorization->basis_heading(leaving)] is the same but the one that we are using is more precise - m_factorization->replace_column(pivot, m_w, m_basis_heading[leaving]); - if (m_factorization->get_status() == LU_status::OK) { - change_basis(entering, leaving); - return true; - } - } - // need to refactor == true - change_basis(entering, leaving); - init_lu(); - if (m_factorization->get_status() != LU_status::OK) { - if (m_look_for_feasible_solution_only && !precise()) { - m_status = lp_status::UNSTABLE; - delete m_factorization; - m_factorization = nullptr; - return false; - } - // LP_OUT(m_settings, "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations() << std::endl); - restore_x_and_refactor(entering, leaving, tt); - if (m_status == lp_status::FLOATING_POINT_ERROR) - return false; - CASSERT("A_off", !A_mult_x_is_off()); - m_iters_with_no_cost_growing++; - // LP_OUT(m_settings, "rolled back after failing of init_factorization()" << std::endl); - m_status = lp_status::UNSTABLE; - return false; - } - return true; -} - template bool lp_core_solver_base:: divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) { - lp_assert(numeric_traits::precise()); int pivot_index = -1; auto & row = m_A.m_rows[pivot_row]; unsigned size = row.size(); @@ -598,7 +282,7 @@ divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) { if (is_zero(coeff)) return false; - this->m_b[pivot_row] /= coeff; + // this->m_b[pivot_row] /= coeff; for (unsigned j = 0; j < size; j++) { auto & c = row[j]; if (c.var() != pivot_col) { @@ -662,259 +346,60 @@ basis_has_no_doubles() const { template bool lp_core_solver_base:: non_basis_has_no_doubles() const { std::set bm; - for (auto j : m_nbasis) { - bm.insert(j); - } + for (auto j : m_nbasis) + bm.insert(j); return bm.size() == m_nbasis.size(); } template bool lp_core_solver_base:: basis_is_correctly_represented_in_heading() const { - for (unsigned i = 0; i < m_m(); i++) { + for (unsigned i = 0; i < m_m(); i++) if (m_basis_heading[m_basis[i]] != static_cast(i)) - return false; - } + return false; return true; } template bool lp_core_solver_base:: non_basis_is_correctly_represented_in_heading() const { - for (unsigned i = 0; i < m_nbasis.size(); i++) { + for (unsigned i = 0; i < m_nbasis.size(); i++) if (m_basis_heading[m_nbasis[i]] != - static_cast(i) - 1) return false; - } - for (unsigned j = 0; j < m_A.column_count(); j++) { - if (m_basis_heading[j] >= 0) { + + for (unsigned j = 0; j < m_A.column_count(); j++) + if (m_basis_heading[j] >= 0) lp_assert(static_cast(m_basis_heading[j]) < m_A.row_count() && m_basis[m_basis_heading[j]] == j); - } - } + return true; } template bool lp_core_solver_base:: basis_heading_is_correct() const { - if ( m_A.column_count() > 10 ) { // for the performance reason + if ( m_A.column_count() > 10 ) // for the performance reason return true; - } + lp_assert(m_basis_heading.size() == m_A.column_count()); lp_assert(m_basis.size() == m_A.row_count()); lp_assert(m_nbasis.size() <= m_A.column_count() - m_A.row_count()); // for the dual the size of non basis can be smaller - if (!basis_has_no_doubles()) { + + if (!basis_has_no_doubles()) return false; - } - - if (!non_basis_has_no_doubles()) { + + if (!non_basis_has_no_doubles()) return false; - } + + if (!basis_is_correctly_represented_in_heading()) + return false; - if (!basis_is_correctly_represented_in_heading()) { + if (!non_basis_is_correctly_represented_in_heading()) return false; - } - - if (!non_basis_is_correctly_represented_in_heading()) { - return false; - } - - + return true; } -template void lp_core_solver_base:: -restore_x_and_refactor(int entering, int leaving, X const & t) { - this->restore_basis_change(entering, leaving); - restore_x(entering, t); - init_factorization(m_factorization, m_A, m_basis, m_settings); - if (m_factorization->get_status() == LU_status::Degenerated) { - LP_OUT(m_settings, "cannot refactor" << std::endl); - m_status = lp_status::FLOATING_POINT_ERROR; - return; - } - // solve_Ax_eq_b(); - if (A_mult_x_is_off()) { - LP_OUT(m_settings, "cannot restore solution" << std::endl); - m_status = lp_status::FLOATING_POINT_ERROR; - return; - } -} - -template void lp_core_solver_base:: -restore_x(unsigned entering, X const & t) { - if (is_zero(t)) return; - m_x[entering] -= t; - for (unsigned i : m_ed.m_index) { - m_x[m_basis[i]] = m_copy_of_xB[i]; - } -} - -template void lp_core_solver_base:: -fill_reduced_costs_from_m_y_by_rows() { - unsigned j = m_n(); - while (j--) { - if (m_basis_heading[j] < 0) - m_d[j] = m_costs[j]; - else - m_d[j] = numeric_traits::zero(); - } - - unsigned i = m_m(); - while (i--) { - const T & y = m_y[i]; - if (is_zero(y)) continue; - for (row_cell & c : m_A.m_rows[i]) { - j = c.var(); - if (m_basis_heading[j] < 0) { - m_d[j] -= y * c.coeff(); - } - } - } -} - -template void lp_core_solver_base:: -copy_rs_to_xB(vector & rs) { - unsigned j = m_m(); - while (j--) { - m_x[m_basis[j]] = rs[j]; - } -} - template std::string lp_core_solver_base:: column_name(unsigned column) const { return m_column_names.get_variable_name(column); } -template void lp_core_solver_base:: -copy_right_side(vector & rs) { - unsigned i = m_m(); - while (i --) { - rs[i] = m_b[i]; - } -} - -template void lp_core_solver_base:: -add_delta_to_xB(vector & del) { - unsigned i = m_m(); - while (i--) { - this->m_x[this->m_basis[i]] -= del[i]; - } -} - -template void lp_core_solver_base:: -find_error_in_BxB(vector& rs){ - unsigned row = m_m(); - while (row--) { - auto &rsv = rs[row]; - for (auto & it : m_A.m_rows[row]) { - unsigned j = it.var(); - if (m_basis_heading[j] >= 0) { - rsv -= m_x[j] * it.coeff(); - } - } - } -} - -// recalculates the projection of x to B, such that Ax = b -template void lp_core_solver_base:: -solve_Ax_eq_b() { - if (numeric_traits::precise()) { - vector rs(m_m()); - rs_minus_Anx(rs); - m_factorization->solve_By(rs); - copy_rs_to_xB(rs); - } else { - vector rs(m_m()); - rs_minus_Anx(rs); - vector rrs = rs; // another copy of rs - m_factorization->solve_By(rs); - copy_rs_to_xB(rs); - find_error_in_BxB(rrs); - m_factorization->solve_By(rrs); - add_delta_to_xB(rrs); - } -} - - - - -template void lp_core_solver_base:: -snap_non_basic_x_to_bound_and_free_to_zeroes() { - for (unsigned j : non_basis()) { - lp_assert(j < m_x.size()); - switch (m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - case column_type::lower_bound: - m_x[j] = m_lower_bounds[j]; - break; - case column_type::upper_bound: - m_x[j] = m_upper_bounds[j]; - break; - default: - m_x[j] = zero_of_type(); - break; - } - } -} -template void lp_core_solver_base:: -snap_xN_to_bounds_and_fill_xB() { - snap_non_basic_x_to_bound(); - solve_Ax_eq_b(); -} - -template void lp_core_solver_base:: -snap_xN_to_bounds_and_free_columns_to_zeroes() { - snap_non_basic_x_to_bound_and_free_to_zeroes(); - solve_Ax_eq_b(); -} - -template void lp_core_solver_base:: -init_reduced_costs_for_one_iteration() { - solve_yB(m_y); - fill_reduced_costs_from_m_y_by_rows(); -} - -template non_basic_column_value_position lp_core_solver_base:: -get_non_basic_column_value_position(unsigned j) const { - switch (m_column_types[j]) { - case column_type::fixed: - return x_is_at_lower_bound(j)? at_fixed : not_at_bound; - case column_type::free_column: - return free_of_bounds; - case column_type::boxed: - return x_is_at_lower_bound(j)? at_lower_bound :( - x_is_at_upper_bound(j)? at_upper_bound: - not_at_bound - ); - case column_type::lower_bound: - return x_is_at_lower_bound(j)? at_lower_bound : not_at_bound; - case column_type::upper_bound: - return x_is_at_upper_bound(j)? at_upper_bound : not_at_bound; - default: - lp_unreachable(); - } - lp_unreachable(); - return at_lower_bound; -} - -template void lp_core_solver_base::init_lu() { - init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_settings); -} - -template int lp_core_solver_base::pivots_in_column_and_row_are_different(int entering, int leaving) const { - const T & column_p = this->m_ed[this->m_basis_heading[leaving]]; - const T & row_p = this->m_pivot_row[entering]; - if (is_zero(column_p) || is_zero(row_p)) return true; // pivots cannot be zero - // the pivots have to have the same sign - if (column_p < 0) { - if (row_p > 0) - return 2; - } else { // column_p > 0 - if (row_p < 0) - return 2; - } - T diff_normalized = abs((column_p - row_p) / (numeric_traits::one() + abs(row_p))); - if ( !this->m_settings.abs_val_is_smaller_than_harris_tolerance(diff_normalized / T(10))) - return 1; - return 0; -} template void lp_core_solver_base::transpose_rows_tableau(unsigned i, unsigned j) { transpose_basis(i, j); m_A.transpose_rows(i, j); @@ -924,51 +409,14 @@ template bool lp_core_solver_base::pivot_column_g lp_assert(m_basis_heading[j] < 0); lp_assert(m_basis_heading[j_basic] >= 0); unsigned row_index = m_basis_heading[j_basic]; - if (m_settings.m_simplex_strategy == simplex_strategy_enum::lu) { - if (m_factorization->need_to_refactor()) { - init_lu(); - } - else { - m_factorization->prepare_entering(j, w); // to init vector w - m_factorization->replace_column(zero_of_type(), w, row_index); - } - if (m_factorization->get_status() != LU_status::OK) { - init_lu(); - return false; - } - else { - change_basis(j, j_basic); - } - } - else { // the tableau case - if (pivot_column_tableau(j, row_index)) - change_basis(j, j_basic); - else return false; - } + // the tableau case + if (pivot_column_tableau(j, row_index)) + change_basis(j, j_basic); + else return false; + return true; } -template void lp_core_solver_base::pivot_fixed_vars_from_basis() { - // run over basis and non-basis at the same time - indexed_vector w(m_basis.size()); // the buffer - unsigned i = 0; // points to basis - for (; i < m_basis.size(); i++) { - unsigned basic_j = m_basis[i]; - - if (get_column_type(basic_j) != column_type::fixed) continue; - T a; - unsigned j; - for (auto &c : m_A.m_rows[i]) { - j = c.var(); - if (j == basic_j) - continue; - if (get_column_type(j) != column_type::fixed) { - if (pivot_column_general(j, basic_j, w)) - break; - } - } - } -} template bool lp_core_solver_base::remove_from_basis(unsigned basic_j) { indexed_vector w(m_basis.size()); // the buffer @@ -982,91 +430,5 @@ template bool lp_core_solver_base::remove_from_ba return false; } -template bool lp_core_solver_base::remove_from_basis(unsigned basic_j, const impq& val) { - indexed_vector w(m_basis.size()); // the buffer - unsigned i = m_basis_heading[basic_j]; - for (auto &c : m_A.m_rows[i]) { - if (c.var() == basic_j) - continue; - if (pivot_column_general(c.var(), basic_j, w)) - return true; - } - return false; -} - - -template bool -lp_core_solver_base::infeasibility_costs_are_correct() const { - if (! this->m_using_infeas_costs) - return true; - lp_assert(costs_on_nbasis_are_zeros()); - for (unsigned j :this->m_basis) { - if (!infeasibility_cost_is_correct_for_column(j)) { - TRACE("lar_solver", tout << "incorrect cost for column " << j << std::endl;); - return false; - } - if (!is_zero(m_d[j])) { - TRACE("lar_solver", tout << "non zero inf cost for basis j = " << j << std::endl;); - return false; - } - } - return true; -} - -template bool -lp_core_solver_base::infeasibility_cost_is_correct_for_column(unsigned j) const { - T r = (!this->m_settings.use_breakpoints_in_feasibility_search)? -one_of_type(): one_of_type(); - - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - if (this->x_above_upper_bound(j)) { - return (this->m_costs[j] == r); - } - if (this->x_below_low_bound(j)) { - return (this->m_costs[j] == -r); - } - return is_zero(this->m_costs[j]); - - case column_type::lower_bound: - if (this->x_below_low_bound(j)) { - return this->m_costs[j] == -r; - } - return is_zero(this->m_costs[j]); - - case column_type::upper_bound: - if (this->x_above_upper_bound(j)) { - return this->m_costs[j] == r; - } - return is_zero(this->m_costs[j]); - case column_type::free_column: - return is_zero(this->m_costs[j]); - default: - lp_assert(false); - return true; - } -} - -template -void lp_core_solver_base::calculate_pivot_row(unsigned i) { - lp_assert(!use_tableau()); - lp_assert(m_pivot_row.is_OK()); - m_pivot_row_of_B_1.clear(); - m_pivot_row_of_B_1.resize(m_m()); - m_pivot_row.clear(); - m_pivot_row.resize(m_n()); - if (m_settings.use_tableau()) { - unsigned basic_j = m_basis[i]; - for (auto & c : m_A.m_rows[i]) { - if (c.var() != basic_j) - m_pivot_row.set_value(c.coeff(), c.var()); - } - return; - } - - calculate_pivot_row_of_B_1(i); - calculate_pivot_row_when_pivot_row_of_B1_is_ready(i); -} - } diff --git a/src/math/lp/lp_dual_core_solver.cpp b/src/math/lp/lp_dual_core_solver.cpp deleted file mode 100644 index 8cf45f7c6..000000000 --- a/src/math/lp/lp_dual_core_solver.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include -#include -#include "util/vector.h" -#include -#include "math/lp/lp_dual_core_solver_def.h" -template void lp::lp_dual_core_solver::start_with_initial_basis_and_make_it_dual_feasible(); -template void lp::lp_dual_core_solver::solve(); -template lp::lp_dual_core_solver::lp_dual_core_solver(lp::static_matrix&, vector&, - vector&, - vector&, - vector&, - vector &, - vector &, - vector&, - vector&, - vector&, - vector&, - lp::lp_settings&, const lp::column_namer&); -template void lp::lp_dual_core_solver::start_with_initial_basis_and_make_it_dual_feasible(); -template void lp::lp_dual_core_solver::solve(); -template void lp::lp_dual_core_solver::restore_non_basis(); -template void lp::lp_dual_core_solver::restore_non_basis(); -template void lp::lp_dual_core_solver::revert_to_previous_basis(); -template void lp::lp_dual_core_solver::revert_to_previous_basis(); diff --git a/src/math/lp/lp_dual_core_solver.h b/src/math/lp/lp_dual_core_solver.h deleted file mode 100644 index f4aa4b44d..000000000 --- a/src/math/lp/lp_dual_core_solver.h +++ /dev/null @@ -1,212 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "math/lp/static_matrix.h" -#include "math/lp/lp_core_solver_base.h" -#include -#include -#include -#include -#include "util/vector.h" - -namespace lp { -template -class lp_dual_core_solver:public lp_core_solver_base { -public: - vector & m_can_enter_basis; - int m_r; // the row of the leaving column - int m_p; // leaving column; that is m_p = m_basis[m_r] - T m_delta; // the offset of the leaving basis variable - int m_sign_of_alpha_r; // see page 27 - T m_theta_D; - T m_theta_P; - int m_q; - // todo : replace by a vector later - std::set m_breakpoint_set; // it is F in "Progress in the dual simplex method ..." - std::set m_flipped_boxed; - std::set m_tight_set; // it is the set of all breakpoints that become tight when m_q becomes tight - vector m_a_wave; - vector m_betas; // m_betas[i] is approximately a square of the norm of the i-th row of the reverse of B - T m_harris_tolerance; - std::set m_forbidden_rows; - - lp_dual_core_solver(static_matrix & A, - vector & can_enter_basis, - vector & b, // the right side vector - vector & x, // the number of elements in x needs to be at least as large as the number of columns in A - vector & basis, - vector & nbasis, - vector & heading, - vector & costs, - vector & column_type_array, - vector & lower_bound_values, - vector & upper_bound_values, - lp_settings & settings, - const column_namer & column_names): - lp_core_solver_base(A, - b, - basis, - nbasis, - heading, - x, - costs, - settings, - column_names, - column_type_array, - lower_bound_values, - upper_bound_values), - m_can_enter_basis(can_enter_basis), - m_a_wave(this->m_m()), - m_betas(this->m_m()) { - m_harris_tolerance = numeric_traits::precise()? numeric_traits::zero() : T(this->m_settings.harris_feasibility_tolerance); - this->solve_yB(this->m_y); - this->init_basic_part_of_basis_heading(); - fill_non_basis_with_only_able_to_enter_columns(); - } - - void init_a_wave_by_zeros(); - - void fill_non_basis_with_only_able_to_enter_columns() { - auto & nb = this->m_nbasis; - nb.reset(); - unsigned j = this->m_n(); - while (j--) { - if (this->m_basis_heading[j] >= 0 || !m_can_enter_basis[j]) continue; - nb.push_back(j); - this->m_basis_heading[j] = - static_cast(nb.size()); - } - } - - void restore_non_basis(); - - bool update_basis(int entering, int leaving); - - void recalculate_xB_and_d(); - - void recalculate_d(); - - void init_betas(); - - void adjust_xb_for_changed_xn_and_init_betas(); - - void start_with_initial_basis_and_make_it_dual_feasible(); - - bool done(); - - T get_edge_steepness_for_lower_bound(unsigned p); - - T get_edge_steepness_for_upper_bound(unsigned p); - - T pricing_for_row(unsigned i); - - void pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows); - - bool advance_on_known_p(); - - int define_sign_of_alpha_r(); - - bool can_be_breakpoint(unsigned j); - - void fill_breakpoint_set(); - - void DSE_FTran(); - T get_delta(); - - void restore_d(); - - bool d_is_correct(); - - void xb_minus_delta_p_pivot_column(); - - void update_betas(); - - void apply_flips(); - - void snap_xN_column_to_bounds(unsigned j); - - void snap_xN_to_bounds(); - - void init_beta_precisely(unsigned i); - - void init_betas_precisely(); - - // step 7 of the algorithm from Progress - bool basis_change_and_update(); - - void revert_to_previous_basis(); - - non_basic_column_value_position m_entering_boundary_position; - bool update_basis_and_x_local(int entering, int leaving, X const & tt); - void recover_leaving(); - - bool problem_is_dual_feasible() const; - - bool snap_runaway_nonbasic_column(unsigned); - - bool snap_runaway_nonbasic_columns(); - - unsigned get_number_of_rows_to_try_for_leaving(); - - void update_a_wave(const T & del, unsigned j) { - this->m_A.add_column_to_vector(del, j, & m_a_wave[0]); - } - - bool delta_keeps_the_sign(int initial_delta_sign, const T & delta); - - void set_status_to_tentative_dual_unbounded_or_dual_unbounded(); - - // it is positive if going from low bound to upper bound and negative if going from upper bound to low bound - T signed_span_of_boxed(unsigned j) { - return this->x_is_at_lower_bound(j)? this->bound_span(j): - this->bound_span(j); - } - - void add_tight_breakpoints_and_q_to_flipped_set(); - - T delta_lost_on_flips_of_tight_breakpoints(); - - bool tight_breakpoinst_are_all_boxed(); - - T calculate_harris_delta_on_breakpoint_set(); - - void fill_tight_set_on_harris_delta(const T & harris_delta ); - - void find_q_on_tight_set(); - - void find_q_and_tight_set(); - - void erase_tight_breakpoints_and_q_from_breakpoint_set(); - - bool ratio_test(); - - void process_flipped(); - void update_d_and_xB(); - - void calculate_beta_r_precisely(); - // see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms" - - void update_xb_after_bound_flips(); - - void one_iteration(); - - void solve(); - - bool lower_bounds_are_set() const override { return true; } -}; -} diff --git a/src/math/lp/lp_dual_core_solver_def.h b/src/math/lp/lp_dual_core_solver_def.h deleted file mode 100644 index b42d644af..000000000 --- a/src/math/lp/lp_dual_core_solver_def.h +++ /dev/null @@ -1,751 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include -#include "util/vector.h" -#include "math/lp/lp_dual_core_solver.h" - -namespace lp { - -template void lp_dual_core_solver::init_a_wave_by_zeros() { - unsigned j = this->m_m(); - while (j--) { - m_a_wave[j] = numeric_traits::zero(); - } -} - -template void lp_dual_core_solver::restore_non_basis() { - auto & nb = this->m_nbasis; - nb.reset(); - unsigned j = this->m_n(); - while (j--) { - if (this->m_basis_heading[j] >= 0 ) continue; - if (m_can_enter_basis[j]) { - lp_assert(std::find(nb.begin(), nb.end(), j) == nb.end()); - nb.push_back(j); - this->m_basis_heading[j] = - static_cast(nb.size()); - } - } -} - -template bool lp_dual_core_solver::update_basis(int entering, int leaving) { - // the second argument is the element of the entering column from the pivot row - its value should be equal to the low diagonal element of the bump after all pivoting is done - if (this->m_refactor_counter++ < 200) { - this->m_factorization->replace_column(this->m_ed[this->m_factorization->basis_heading(leaving)], this->m_w); - if (this->m_factorization->get_status() == LU_status::OK) { - this->m_factorization->change_basis(entering, leaving); - return true; - } - } - // need to refactor - this->m_factorization->change_basis(entering, leaving); - init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings); - this->m_refactor_counter = 0; - if (this->m_factorization->get_status() != LU_status::OK) { - LP_OUT(this->m_settings, "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << this->total_iterations() << std::endl); - this->m_iters_with_no_cost_growing++; - return false; - } - return true; -} - -template void lp_dual_core_solver::recalculate_xB_and_d() { - this->solve_Ax_eq_b(); - recalculate_d(); -} - -template void lp_dual_core_solver::recalculate_d() { - this->solve_yB(this->m_y); - this->fill_reduced_costs_from_m_y_by_rows(); -} - -template void lp_dual_core_solver::init_betas() { - // todo : look at page 194 of Progress in the dual simplex algorithm for solving large scale LP problems : techniques for a fast and stable implementation - // the current implementation is not good enough: todo - unsigned i = this->m_m(); - while (i--) { - m_betas[i] = 1; - } -} - -template void lp_dual_core_solver::adjust_xb_for_changed_xn_and_init_betas() { - this->solve_Ax_eq_b(); - init_betas(); -} - -template void lp_dual_core_solver::start_with_initial_basis_and_make_it_dual_feasible() { - this->set_non_basic_x_to_correct_bounds(); // It is not an efficient version, see 3.29, - // however this version does not require that m_x is the solution of Ax = 0 beforehand - adjust_xb_for_changed_xn_and_init_betas(); -} - -template bool lp_dual_core_solver::done() { - if (this->get_status() == lp_status::OPTIMAL) { - return true; - } - - return false; // todo, need to be more cases -} - -template T lp_dual_core_solver::get_edge_steepness_for_lower_bound(unsigned p) { - lp_assert(this->m_basis_heading[p] >= 0 && static_cast(this->m_basis_heading[p]) < this->m_m()); - T del = this->m_x[p] - this->m_lower_bounds[p]; - del *= del; - return del / this->m_betas[this->m_basis_heading[p]]; -} - -template T lp_dual_core_solver::get_edge_steepness_for_upper_bound(unsigned p) { - lp_assert(this->m_basis_heading[p] >= 0 && static_cast(this->m_basis_heading[p]) < this->m_m()); - T del = this->m_x[p] - this->m_upper_bounds[p]; - del *= del; - return del / this->m_betas[this->m_basis_heading[p]]; -} - -template T lp_dual_core_solver::pricing_for_row(unsigned i) { - unsigned p = this->m_basis[i]; - switch (this->m_column_types[p]) { - case column_type::fixed: - case column_type::boxed: - if (this->x_below_low_bound(p)) { - T del = get_edge_steepness_for_lower_bound(p); - return del; - } - if (this->x_above_upper_bound(p)) { - T del = get_edge_steepness_for_upper_bound(p); - return del; - } - return numeric_traits::zero(); - case column_type::lower_bound: - if (this->x_below_low_bound(p)) { - T del = get_edge_steepness_for_lower_bound(p); - return del; - } - return numeric_traits::zero(); - break; - case column_type::upper_bound: - if (this->x_above_upper_bound(p)) { - T del = get_edge_steepness_for_upper_bound(p); - return del; - } - return numeric_traits::zero(); - break; - case column_type::free_column: - lp_assert(numeric_traits::is_zero(this->m_d[p])); - return numeric_traits::zero(); - default: - lp_unreachable(); - } - lp_unreachable(); - return numeric_traits::zero(); -} - -template void lp_dual_core_solver::pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows) { - m_r = -1; - T steepest_edge_max = numeric_traits::zero(); - unsigned initial_offset_in_rows = offset_in_rows; - unsigned i = offset_in_rows; - unsigned rows_left = number_of_rows_to_try; - do { - if (m_forbidden_rows.find(i) != m_forbidden_rows.end()) { - if (++i == this->m_m()) { - i = 0; - } - continue; - } - T se = pricing_for_row(i); - if (se > steepest_edge_max) { - steepest_edge_max = se; - m_r = i; - if (rows_left > 0) { - rows_left--; - } - } - if (++i == this->m_m()) { - i = 0; - } - } while (i != initial_offset_in_rows && rows_left); - if (m_r == -1) { - if (this->get_status() != lp_status::UNSTABLE) { - this->set_status(lp_status::OPTIMAL); - } - } else { - m_p = this->m_basis[m_r]; - m_delta = get_delta(); - if (advance_on_known_p()){ - m_forbidden_rows.clear(); - return; - } - // failure in advance_on_known_p - if (this->get_status() == lp_status::FLOATING_POINT_ERROR) { - return; - } - this->set_status(lp_status::UNSTABLE); - m_forbidden_rows.insert(m_r); - } -} - - // this calculation is needed for the steepest edge update, - // it hijackes m_pivot_row_of_B_1 for this purpose since we will need it anymore to the end of the cycle -template void lp_dual_core_solver::DSE_FTran() { // todo, see algorithm 7 from page 35 - this->m_factorization->solve_By_for_T_indexed_only(this->m_pivot_row_of_B_1, this->m_settings); -} - -template bool lp_dual_core_solver::advance_on_known_p() { - if (done()) { - return true; - } - this->calculate_pivot_row_of_B_1(m_r); - this->calculate_pivot_row_when_pivot_row_of_B1_is_ready(m_r); - if (!ratio_test()) { - return true; - } - calculate_beta_r_precisely(); - this->solve_Bd(m_q); // FTRAN - int pivot_compare_result = this->pivots_in_column_and_row_are_different(m_q, m_p); - if (!pivot_compare_result){;} - else if (pivot_compare_result == 2) { // the sign is changed, cannot continue - lp_unreachable(); // not implemented yet - } else { - lp_assert(pivot_compare_result == 1); - this->init_lu(); - } - DSE_FTran(); - return basis_change_and_update(); -} - -template int lp_dual_core_solver::define_sign_of_alpha_r() { - switch (this->m_column_types[m_p]) { - case column_type::boxed: - case column_type::fixed: - if (this->x_below_low_bound(m_p)) { - return -1; - } - if (this->x_above_upper_bound(m_p)) { - return 1; - } - lp_unreachable(); - case column_type::lower_bound: - if (this->x_below_low_bound(m_p)) { - return -1; - } - lp_unreachable(); - case column_type::upper_bound: - if (this->x_above_upper_bound(m_p)) { - return 1; - } - lp_unreachable(); - default: - lp_unreachable(); - } - lp_unreachable(); - return 0; -} - -template bool lp_dual_core_solver::can_be_breakpoint(unsigned j) { - if (this->pivot_row_element_is_too_small_for_ratio_test(j)) return false; - switch (this->m_column_types[j]) { - case column_type::lower_bound: - lp_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_lower_bounds[j])); - return m_sign_of_alpha_r * this->m_pivot_row[j] > 0; - case column_type::upper_bound: - lp_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_upper_bounds[j])); - return m_sign_of_alpha_r * this->m_pivot_row[j] < 0; - case column_type::boxed: - { - bool lower_bound = this->x_is_at_lower_bound(j); - bool grawing = m_sign_of_alpha_r * this->m_pivot_row[j] > 0; - return lower_bound == grawing; - } - case column_type::fixed: // is always dual feasible so we ignore it - return false; - case column_type::free_column: - return true; - default: - return false; - } -} - -template void lp_dual_core_solver::fill_breakpoint_set() { - m_breakpoint_set.clear(); - for (unsigned j : this->non_basis()) { - if (can_be_breakpoint(j)) { - m_breakpoint_set.insert(j); - } - } -} - -// template void lp_dual_core_solver::FTran() { -// this->solve_Bd(m_q); -// } - -template T lp_dual_core_solver::get_delta() { - switch (this->m_column_types[m_p]) { - case column_type::boxed: - if (this->x_below_low_bound(m_p)) { - return this->m_x[m_p] - this->m_lower_bounds[m_p]; - } - if (this->x_above_upper_bound(m_p)) { - return this->m_x[m_p] - this->m_upper_bounds[m_p]; - } - lp_unreachable(); - case column_type::lower_bound: - if (this->x_below_low_bound(m_p)) { - return this->m_x[m_p] - this->m_lower_bounds[m_p]; - } - lp_unreachable(); - case column_type::upper_bound: - if (this->x_above_upper_bound(m_p)) { - return get_edge_steepness_for_upper_bound(m_p); - } - lp_unreachable(); - case column_type::fixed: - return this->m_x[m_p] - this->m_upper_bounds[m_p]; - default: - lp_unreachable(); - } - lp_unreachable(); - return zero_of_type(); -} - -template void lp_dual_core_solver::restore_d() { - this->m_d[m_p] = numeric_traits::zero(); - for (auto j : this->non_basis()) { - this->m_d[j] += m_theta_D * this->m_pivot_row[j]; - } -} - -template bool lp_dual_core_solver::d_is_correct() { - this->solve_yB(this->m_y); - for (auto j : this->non_basis()) { - T d = this->m_costs[j] - this->m_A.dot_product_with_column(this->m_y, j); - if (numeric_traits::get_double(abs(d - this->m_d[j])) >= 0.001) { - LP_OUT(this->m_settings, "total_iterations = " << this->total_iterations() << std::endl - << "d[" << j << "] = " << this->m_d[j] << " but should be " << d << std::endl); - return false; - } - } - return true; -} - -template void lp_dual_core_solver::xb_minus_delta_p_pivot_column() { - unsigned i = this->m_m(); - while (i--) { - this->m_x[this->m_basis[i]] -= m_theta_P * this->m_ed[i]; - } -} - -template void lp_dual_core_solver::update_betas() { // page 194 of Progress ... todo - once in a while betas have to be reinitialized - T one_over_arq = numeric_traits::one() / this->m_pivot_row[m_q]; - T beta_r = this->m_betas[m_r] = std::max(T(0.0001), (m_betas[m_r] * one_over_arq) * one_over_arq); - T k = -2 * one_over_arq; - unsigned i = this->m_m(); - while (i--) { - if (static_cast(i) == m_r) continue; - T a = this->m_ed[i]; - m_betas[i] += a * (a * beta_r + k * this->m_pivot_row_of_B_1[i]); - if (m_betas[i] < T(0.0001)) - m_betas[i] = T(0.0001); - } -} - -template void lp_dual_core_solver::apply_flips() { - for (unsigned j : m_flipped_boxed) { - lp_assert(this->x_is_at_bound(j)); - if (this->x_is_at_lower_bound(j)) { - this->m_x[j] = this->m_upper_bounds[j]; - } else { - this->m_x[j] = this->m_lower_bounds[j]; - } - } -} - -template void lp_dual_core_solver::snap_xN_column_to_bounds(unsigned j) { - switch (this->m_column_type[j]) { - case column_type::fixed: - this->m_x[j] = this->m_lower_bounds[j]; - break; - case column_type::boxed: - if (this->x_is_at_lower_bound(j)) { - this->m_x[j] = this->m_lower_bounds[j]; - } else { - this->m_x[j] = this->m_upper_bounds[j]; - } - break; - case column_type::lower_bound: - this->m_x[j] = this->m_lower_bounds[j]; - break; - case column_type::upper_bound: - this->m_x[j] = this->m_upper_bounds[j]; - break; - case column_type::free_column: - break; - default: - lp_unreachable(); - } -} - -template void lp_dual_core_solver::snap_xN_to_bounds() { - for (auto j : this->non_basis()) { - snap_xN_column_to_bounds(j); - } -} - -template void lp_dual_core_solver::init_beta_precisely(unsigned i) { - vector vec(this->m_m(), numeric_traits::zero()); - vec[i] = numeric_traits::one(); - this->m_factorization->solve_yB_with_error_check(vec, this->m_basis); - T beta = numeric_traits::zero(); - for (T & v : vec) { - beta += v * v; - } - this->m_betas[i] =beta; -} - -template void lp_dual_core_solver::init_betas_precisely() { - unsigned i = this->m_m(); - while (i--) { - init_beta_precisely(i); - } -} - -// step 7 of the algorithm from Progress -template bool lp_dual_core_solver::basis_change_and_update() { - update_betas(); - update_d_and_xB(); - // m_theta_P = m_delta / this->m_ed[m_r]; - m_theta_P = m_delta / this->m_pivot_row[m_q]; - // xb_minus_delta_p_pivot_column(); - apply_flips(); - if (!this->update_basis_and_x(m_q, m_p, m_theta_P)) { - init_betas_precisely(); - return false; - } - - if (snap_runaway_nonbasic_column(m_p)) { - if (!this->find_x_by_solving()) { - revert_to_previous_basis(); - this->iters_with_no_cost_growing()++; - return false; - } - } - - if (!problem_is_dual_feasible()) { - // todo : shift the costs!!!! - revert_to_previous_basis(); - this->iters_with_no_cost_growing()++; - return false; - } - - lp_assert(d_is_correct()); - return true; -} - -template void lp_dual_core_solver::recover_leaving() { - switch (m_entering_boundary_position) { - case at_lower_bound: - case at_fixed: - this->m_x[m_q] = this->m_lower_bounds[m_q]; - break; - case at_upper_bound: - this->m_x[m_q] = this->m_upper_bounds[m_q]; - break; - case free_of_bounds: - this->m_x[m_q] = zero_of_type(); - default: - lp_unreachable(); - } -} - -template void lp_dual_core_solver::revert_to_previous_basis() { - LP_OUT(this->m_settings, "revert to previous basis on ( " << m_p << ", " << m_q << ")" << std::endl); - this->change_basis_unconditionally(m_p, m_q); - init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_settings); - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); // complete failure - return; - } - recover_leaving(); - if (!this->find_x_by_solving()) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return; - } - recalculate_xB_and_d(); - init_betas_precisely(); -} - -// returns true if the column has been snapped -template bool lp_dual_core_solver::snap_runaway_nonbasic_column(unsigned j) { - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::lower_bound: - if (!this->x_is_at_lower_bound(j)) { - this->m_x[j] = this->m_lower_bounds[j]; - return true; - } - break; - case column_type::boxed: - { - bool closer_to_lower_bound = abs(this->m_lower_bounds[j] - this->m_x[j]) < abs(this->m_upper_bounds[j] - this->m_x[j]); - if (closer_to_lower_bound) { - if (!this->x_is_at_lower_bound(j)) { - this->m_x[j] = this->m_lower_bounds[j]; - return true; - } - } else { - if (!this->x_is_at_upper_bound(j)) { - this->m_x[j] = this->m_lower_bounds[j]; - return true; - } - } - } - break; - case column_type::upper_bound: - if (!this->x_is_at_upper_bound(j)) { - this->m_x[j] = this->m_upper_bounds[j]; - return true; - } - break; - default: - break; - } - return false; -} - - -template bool lp_dual_core_solver::problem_is_dual_feasible() const { - for (unsigned j : this->non_basis()){ - if (!this->column_is_dual_feasible(j)) { - return false; - } - } - return true; -} - -template unsigned lp_dual_core_solver::get_number_of_rows_to_try_for_leaving() { - unsigned s = this->m_m(); - if (this->m_m() > 300) { - s = (unsigned)((s / 100.0) * this->m_settings.percent_of_entering_to_check); - } - return this->m_settings.random_next() % s + 1; -} - -template bool lp_dual_core_solver::delta_keeps_the_sign(int initial_delta_sign, const T & delta) { - if (numeric_traits::precise()) - return ((delta > numeric_traits::zero()) && (initial_delta_sign == 1)) || - ((delta < numeric_traits::zero()) && (initial_delta_sign == -1)); - - double del = numeric_traits::get_double(delta); - return ( (del > this->m_settings.zero_tolerance) && (initial_delta_sign == 1)) || - ((del < - this->m_settings.zero_tolerance) && (initial_delta_sign == -1)); -} - -template void lp_dual_core_solver::set_status_to_tentative_dual_unbounded_or_dual_unbounded() { - if (this->get_status() == lp_status::TENTATIVE_DUAL_UNBOUNDED) { - this->set_status(lp_status::DUAL_UNBOUNDED); - } else { - this->set_status(lp_status::TENTATIVE_DUAL_UNBOUNDED); - } -} - -template void lp_dual_core_solver::add_tight_breakpoints_and_q_to_flipped_set() { - m_flipped_boxed.insert(m_q); - for (auto j : m_tight_set) { - m_flipped_boxed.insert(j); - } -} - -template T lp_dual_core_solver::delta_lost_on_flips_of_tight_breakpoints() { - T ret = abs(this->bound_span(m_q) * this->m_pivot_row[m_q]); - for (auto j : m_tight_set) { - ret += abs(this->bound_span(j) * this->m_pivot_row[j]); - } - return ret; -} - -template bool lp_dual_core_solver::tight_breakpoinst_are_all_boxed() { - if (this->m_column_types[m_q] != column_type::boxed) return false; - for (auto j : m_tight_set) { - if (this->m_column_types[j] != column_type::boxed) return false; - } - return true; -} - -template T lp_dual_core_solver::calculate_harris_delta_on_breakpoint_set() { - bool first_time = true; - T ret = zero_of_type(); - lp_assert(m_breakpoint_set.size() > 0); - for (auto j : m_breakpoint_set) { - T t; - if (this->x_is_at_lower_bound(j)) { - t = abs((std::max(this->m_d[j], numeric_traits::zero()) + m_harris_tolerance) / this->m_pivot_row[j]); - } else { - t = abs((std::min(this->m_d[j], numeric_traits::zero()) - m_harris_tolerance) / this->m_pivot_row[j]); - } - if (first_time) { - ret = t; - first_time = false; - } else if (t < ret) { - ret = t; - } - } - return ret; -} - -template void lp_dual_core_solver::fill_tight_set_on_harris_delta(const T & harris_delta ){ - m_tight_set.clear(); - for (auto j : m_breakpoint_set) { - if (this->x_is_at_lower_bound(j)) { - if (abs(std::max(this->m_d[j], numeric_traits::zero()) / this->m_pivot_row[j]) <= harris_delta){ - m_tight_set.insert(j); - } - } else { - if (abs(std::min(this->m_d[j], numeric_traits::zero() ) / this->m_pivot_row[j]) <= harris_delta){ - m_tight_set.insert(j); - } - } - } -} - -template void lp_dual_core_solver::find_q_on_tight_set() { - m_q = -1; - T max_pivot; - for (auto j : m_tight_set) { - T r = abs(this->m_pivot_row[j]); - if (m_q != -1) { - if (r > max_pivot) { - max_pivot = r; - m_q = j; - } - } else { - max_pivot = r; - m_q = j; - } - } - m_tight_set.erase(m_q); - lp_assert(m_q != -1); -} - -template void lp_dual_core_solver::find_q_and_tight_set() { - T harris_del = calculate_harris_delta_on_breakpoint_set(); - fill_tight_set_on_harris_delta(harris_del); - find_q_on_tight_set(); - m_entering_boundary_position = this->get_non_basic_column_value_position(m_q); -} - -template void lp_dual_core_solver::erase_tight_breakpoints_and_q_from_breakpoint_set() { - m_breakpoint_set.erase(m_q); - for (auto j : m_tight_set) { - m_breakpoint_set.erase(j); - } -} - -template bool lp_dual_core_solver::ratio_test() { - m_sign_of_alpha_r = define_sign_of_alpha_r(); - fill_breakpoint_set(); - m_flipped_boxed.clear(); - int initial_delta_sign = m_delta >= numeric_traits::zero()? 1: -1; - do { - if (m_breakpoint_set.empty()) { - set_status_to_tentative_dual_unbounded_or_dual_unbounded(); - return false; - } - this->set_status(lp_status::FEASIBLE); - find_q_and_tight_set(); - if (!tight_breakpoinst_are_all_boxed()) break; - T del = m_delta - delta_lost_on_flips_of_tight_breakpoints() * initial_delta_sign; - if (!delta_keeps_the_sign(initial_delta_sign, del)) break; - if (m_tight_set.size() + 1 == m_breakpoint_set.size()) { - break; // deciding not to flip since we might get stuck without finding m_q, the column entering the basis - } - // we can flip m_q together with the tight set and look for another breakpoint candidate for m_q and another tight set - add_tight_breakpoints_and_q_to_flipped_set(); - m_delta = del; - erase_tight_breakpoints_and_q_from_breakpoint_set(); - } while (true); - m_theta_D = this->m_d[m_q] / this->m_pivot_row[m_q]; - return true; -} - -template void lp_dual_core_solver::process_flipped() { - init_a_wave_by_zeros(); - for (auto j : m_flipped_boxed) { - update_a_wave(signed_span_of_boxed(j), j); - } -} -template void lp_dual_core_solver::update_d_and_xB() { - for (auto j : this->non_basis()) { - this->m_d[j] -= m_theta_D * this->m_pivot_row[j]; - } - this->m_d[m_p] = - m_theta_D; - if (!m_flipped_boxed.empty()) { - process_flipped(); - update_xb_after_bound_flips(); - } -} - -template void lp_dual_core_solver::calculate_beta_r_precisely() { - T t = numeric_traits::zero(); - unsigned i = this->m_m(); - while (i--) { - T b = this->m_pivot_row_of_B_1[i]; - t += b * b; - } - m_betas[m_r] = t; -} -// see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms" - -template void lp_dual_core_solver::update_xb_after_bound_flips() { - this->m_factorization->solve_By(m_a_wave); - unsigned i = this->m_m(); - while (i--) { - this->m_x[this->m_basis[i]] -= m_a_wave[i]; - } -} - -template void lp_dual_core_solver::one_iteration() { - unsigned number_of_rows_to_try = get_number_of_rows_to_try_for_leaving(); - unsigned offset_in_rows = this->m_settings.random_next() % this->m_m(); - if (this->get_status() == lp_status::TENTATIVE_DUAL_UNBOUNDED) { - number_of_rows_to_try = this->m_m(); - } else { - this->set_status(lp_status::FEASIBLE); - } - pricing_loop(number_of_rows_to_try, offset_in_rows); - lp_assert(problem_is_dual_feasible()); -} - -template void lp_dual_core_solver::solve() { // see the page 35 - lp_assert(d_is_correct()); - lp_assert(problem_is_dual_feasible()); - lp_assert(this->basis_heading_is_correct()); - //this->set_total_iterations(0); - this->iters_with_no_cost_growing() = 0; - do { - if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over("", *this->m_settings.get_message_ostream())){ - return; - } - one_iteration(); - } while (this->get_status() != lp_status::FLOATING_POINT_ERROR && this->get_status() != lp_status::DUAL_UNBOUNDED && this->get_status() != lp_status::OPTIMAL && - this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements - ); -} -} diff --git a/src/math/lp/lp_dual_simplex.cpp b/src/math/lp/lp_dual_simplex.cpp deleted file mode 100644 index aaf612f56..000000000 --- a/src/math/lp/lp_dual_simplex.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/lp_dual_simplex_def.h" -template lp::mpq lp::lp_dual_simplex::get_current_cost() const; -template void lp::lp_dual_simplex::find_maximal_solution(); -template double lp::lp_dual_simplex::get_current_cost() const; -template void lp::lp_dual_simplex::find_maximal_solution(); diff --git a/src/math/lp/lp_dual_simplex.h b/src/math/lp/lp_dual_simplex.h deleted file mode 100644 index 75ef87492..000000000 --- a/src/math/lp/lp_dual_simplex.h +++ /dev/null @@ -1,93 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/vector.h" -#include "math/lp/lp_utils.h" -#include "math/lp/lp_solver.h" -#include "math/lp/lp_dual_core_solver.h" -namespace lp { - -template -class lp_dual_simplex: public lp_solver { - lp_dual_core_solver * m_core_solver; - vector m_b_copy; - vector m_lower_bounds; // We don't have a convention here that all low bounds are zeros. At least it does not hold for the first stage solver - vector m_column_types_of_core_solver; - vector m_column_types_of_logicals; - vector m_can_enter_basis; -public: - ~lp_dual_simplex() override { - delete m_core_solver; - } - - lp_dual_simplex() : m_core_solver(nullptr) {} - - - void decide_on_status_after_stage1(); - - void fix_logical_for_stage2(unsigned j); - - void fix_structural_for_stage2(unsigned j); - - void unmark_boxed_and_fixed_columns_and_fix_structural_costs(); - - void restore_right_sides(); - - void solve_for_stage2(); - - void fill_x_with_zeros(); - - void stage1(); - - void stage2(); - - void fill_first_stage_solver_fields(); - - column_type get_column_type(unsigned j); - - void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j); - - void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j); - - void fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); - - void set_type_for_logical(unsigned j, column_type col_type) { - this->m_column_types_of_logicals[j - this->number_of_core_structurals()] = col_type; - } - - void fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, - unsigned & slack_var, - unsigned & artificial); - - void augment_matrix_A_and_fill_x_and_allocate_some_fields(); - - - - void copy_m_b_aside_and_set_it_to_zeros(); - - void find_maximal_solution() override; - - T get_column_value(unsigned column) const override { - return this->get_column_value_with_core_solver(column, m_core_solver); - } - - T get_current_cost() const override; -}; -} diff --git a/src/math/lp/lp_dual_simplex_def.h b/src/math/lp/lp_dual_simplex_def.h deleted file mode 100644 index 8af9d87c1..000000000 --- a/src/math/lp/lp_dual_simplex_def.h +++ /dev/null @@ -1,376 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "math/lp/lp_dual_simplex.h" -namespace lp{ - -template void lp_dual_simplex::decide_on_status_after_stage1() { - switch (m_core_solver->get_status()) { - case lp_status::OPTIMAL: - if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { - this->m_status = lp_status::FEASIBLE; - } else { - this->m_status = lp_status::UNBOUNDED; - } - break; - case lp_status::DUAL_UNBOUNDED: - lp_unreachable(); - case lp_status::TIME_EXHAUSTED: - this->m_status = lp_status::TIME_EXHAUSTED; - break; - case lp_status::FLOATING_POINT_ERROR: - this->m_status = lp_status::FLOATING_POINT_ERROR; - break; - default: - lp_unreachable(); - } -} - -template void lp_dual_simplex::fix_logical_for_stage2(unsigned j) { - lp_assert(j >= this->number_of_core_structurals()); - switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) { - case column_type::lower_bound: - m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::lower_bound; - m_can_enter_basis[j] = true; - break; - case column_type::fixed: - this->m_upper_bounds[j] = m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::fixed; - m_can_enter_basis[j] = false; - break; - default: - lp_unreachable(); - } -} - -template void lp_dual_simplex::fix_structural_for_stage2(unsigned j) { - column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; - switch (ci->get_column_type()) { - case column_type::lower_bound: - m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::lower_bound; - m_can_enter_basis[j] = true; - break; - case column_type::fixed: - case column_type::upper_bound: - lp_unreachable(); - case column_type::boxed: - this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; - m_lower_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::boxed; - m_can_enter_basis[j] = true; - break; - case column_type::free_column: - m_can_enter_basis[j] = true; - m_column_types_of_core_solver[j] = column_type::free_column; - break; - default: - lp_unreachable(); - } - // T cost_was = this->m_costs[j]; - this->set_scaled_cost(j); -} - -template void lp_dual_simplex::unmark_boxed_and_fixed_columns_and_fix_structural_costs() { - unsigned j = this->m_A->column_count(); - while (j-- > this->number_of_core_structurals()) { - fix_logical_for_stage2(j); - } - j = this->number_of_core_structurals(); - while (j--) { - fix_structural_for_stage2(j); - } -} - -template void lp_dual_simplex::restore_right_sides() { - unsigned i = this->m_A->row_count(); - while (i--) { - this->m_b[i] = m_b_copy[i]; - } -} - -template void lp_dual_simplex::solve_for_stage2() { - m_core_solver->restore_non_basis(); - m_core_solver->solve_yB(m_core_solver->m_y); - m_core_solver->fill_reduced_costs_from_m_y_by_rows(); - m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); - m_core_solver->set_status(lp_status::FEASIBLE); - m_core_solver->solve(); - switch (m_core_solver->get_status()) { - case lp_status::OPTIMAL: - this->m_status = lp_status::OPTIMAL; - break; - case lp_status::DUAL_UNBOUNDED: - this->m_status = lp_status::INFEASIBLE; - break; - case lp_status::TIME_EXHAUSTED: - this->m_status = lp_status::TIME_EXHAUSTED; - break; - case lp_status::FLOATING_POINT_ERROR: - this->m_status = lp_status::FLOATING_POINT_ERROR; - break; - default: - lp_unreachable(); - } - this->m_second_stage_iterations = m_core_solver->total_iterations(); - this->m_total_iterations = (this->m_first_stage_iterations + this->m_second_stage_iterations); -} - -template void lp_dual_simplex::fill_x_with_zeros() { - unsigned j = this->m_A->column_count(); - while (j--) { - this->m_x[j] = numeric_traits::zero(); - } -} - -template void lp_dual_simplex::stage1() { - lp_assert(m_core_solver == nullptr); - this->m_x.resize(this->m_A->column_count(), numeric_traits::zero()); - if (this->m_settings.get_message_ostream() != nullptr) - this->print_statistics_on_A(*this->m_settings.get_message_ostream()); - m_core_solver = new lp_dual_core_solver( - *this->m_A, - m_can_enter_basis, - this->m_b, // the right side vector - this->m_x, - this->m_basis, - this->m_nbasis, - this->m_heading, - this->m_costs, - this->m_column_types_of_core_solver, - this->m_lower_bounds, - this->m_upper_bounds, - this->m_settings, - *this); - m_core_solver->fill_reduced_costs_from_m_y_by_rows(); - m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); - if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { - // skipping stage 1 - m_core_solver->set_status(lp_status::OPTIMAL); - m_core_solver->set_total_iterations(0); - } else { - m_core_solver->solve(); - } - decide_on_status_after_stage1(); - this->m_first_stage_iterations = m_core_solver->total_iterations(); -} - -template void lp_dual_simplex::stage2() { - unmark_boxed_and_fixed_columns_and_fix_structural_costs(); - restore_right_sides(); - solve_for_stage2(); -} - -template void lp_dual_simplex::fill_first_stage_solver_fields() { - unsigned slack_var = this->number_of_core_structurals(); - unsigned artificial = this->number_of_core_structurals() + this->m_slacks; - - for (unsigned row = 0; row < this->row_count(); row++) { - fill_first_stage_solver_fields_for_row_slack_and_artificial(row, slack_var, artificial); - } - fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); -} - -template column_type lp_dual_simplex::get_column_type(unsigned j) { - lp_assert(j < this->m_A->column_count()); - if (j >= this->number_of_core_structurals()) { - return m_column_types_of_logicals[j - this->number_of_core_structurals()]; - } - return this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]->get_column_type(); -} - -template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) { - // see 4.7 in the dissertation of Achim Koberstein - lp_assert(this->m_core_solver_columns_to_external_columns.find(j) != - this->m_core_solver_columns_to_external_columns.end()); - - T free_bound = T(1e4); // see 4.8 - unsigned jj = this->m_core_solver_columns_to_external_columns[j]; - lp_assert(this->m_map_from_var_index_to_column_info.find(jj) != this->m_map_from_var_index_to_column_info.end()); - column_info * ci = this->m_map_from_var_index_to_column_info[jj]; - switch (ci->get_column_type()) { - case column_type::upper_bound: { - std::stringstream s; - s << "unexpected bound type " << j << " " - << column_type_to_string(get_column_type(j)); - throw_exception(s.str()); - break; - } - case column_type::lower_bound: { - m_can_enter_basis[j] = true; - this->set_scaled_cost(j); - this->m_lower_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = numeric_traits::one(); - break; - } - case column_type::free_column: { - m_can_enter_basis[j] = true; - this->set_scaled_cost(j); - this->m_upper_bounds[j] = free_bound; - this->m_lower_bounds[j] = -free_bound; - break; - } - case column_type::boxed: - m_can_enter_basis[j] = false; - this->m_costs[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = this->m_lower_bounds[j] = numeric_traits::zero(); // is it needed? - break; - default: - lp_unreachable(); - } - m_column_types_of_core_solver[j] = column_type::boxed; -} - -template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) { - this->m_costs[j] = 0; - lp_assert(get_column_type(j) != column_type::upper_bound); - if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::lower_bound))) { - m_column_types_of_core_solver[j] = column_type::boxed; - this->m_lower_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = numeric_traits::one(); - } else { - m_column_types_of_core_solver[j] = column_type::fixed; - this->m_lower_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = numeric_traits::zero(); - } -} - -template void lp_dual_simplex::fill_costs_and_bounds_and_column_types_for_the_first_stage_solver() { - unsigned j = this->m_A->column_count(); - while (j-- > this->number_of_core_structurals()) { // go over logicals here - fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(j); - } - j = this->number_of_core_structurals(); - while (j--) { - fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(j); - } -} - -template void lp_dual_simplex::fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, - unsigned & slack_var, - unsigned & artificial) { - lp_assert(row < this->row_count()); - auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; - // we need to bring the program to the form Ax = b - T rs = this->m_b[row]; - switch (constraint.m_relation) { - case Equal: // no slack variable here - set_type_for_logical(artificial, column_type::fixed); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::zero(); - (*this->m_A)(row, artificial) = numeric_traits::one(); - artificial++; - break; - - case Greater_or_equal: - set_type_for_logical(slack_var, column_type::lower_bound); - (*this->m_A)(row, slack_var) = - numeric_traits::one(); - if (rs > 0) { - // adding one artificial - set_type_for_logical(artificial, column_type::fixed); - (*this->m_A)(row, artificial) = numeric_traits::one(); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::zero(); - artificial++; - } else { - // we can put a slack_var into the basis, and avoid adding an artificial variable - this->m_basis[row] = slack_var; - this->m_costs[slack_var] = numeric_traits::zero(); - } - slack_var++; - break; - case Less_or_equal: - // introduce a non-negative slack variable - set_type_for_logical(slack_var, column_type::lower_bound); - (*this->m_A)(row, slack_var) = numeric_traits::one(); - if (rs < 0) { - // adding one artificial - set_type_for_logical(artificial, column_type::fixed); - (*this->m_A)(row, artificial) = - numeric_traits::one(); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::zero(); - artificial++; - } else { - // we can put slack_var into the basis, and avoid adding an artificial variable - this->m_basis[row] = slack_var; - this->m_costs[slack_var] = numeric_traits::zero(); - } - slack_var++; - break; - } -} - -template void lp_dual_simplex::augment_matrix_A_and_fill_x_and_allocate_some_fields() { - this->count_slacks_and_artificials(); - this->m_A->add_columns_at_the_end(this->m_slacks + this->m_artificials); - unsigned n = this->m_A->column_count(); - this->m_column_types_of_core_solver.resize(n); - m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials); - this->m_costs.resize(n); - this->m_upper_bounds.resize(n); - this->m_lower_bounds.resize(n); - m_can_enter_basis.resize(n); - this->m_basis.resize(this->m_A->row_count()); -} - - - -template void lp_dual_simplex::copy_m_b_aside_and_set_it_to_zeros() { - for (unsigned i = 0; i < this->m_b.size(); i++) { - m_b_copy.push_back(this->m_b[i]); - this->m_b[i] = numeric_traits::zero(); // preparing for the first stage - } -} - -template void lp_dual_simplex::find_maximal_solution(){ - if (this->problem_is_empty()) { - this->m_status = lp_status::EMPTY; - return; - } - - this->flip_costs(); // do it for now, todo ( remove the flipping) - - this->cleanup(); - if (this->m_status == lp_status::INFEASIBLE) { - return; - } - this->fill_matrix_A_and_init_right_side(); - this->fill_m_b(); - this->scale(); - augment_matrix_A_and_fill_x_and_allocate_some_fields(); - fill_first_stage_solver_fields(); - copy_m_b_aside_and_set_it_to_zeros(); - stage1(); - if (this->m_status == lp_status::FEASIBLE) { - stage2(); - } -} - - -template T lp_dual_simplex::get_current_cost() const { - T ret = numeric_traits::zero(); - for (auto it : this->m_map_from_var_index_to_column_info) { - ret += this->get_column_cost_value(it.first, it.second); - } - return -ret; // we flip costs for now -} -} diff --git a/src/math/lp/lp_primal_core_solver.cpp b/src/math/lp/lp_primal_core_solver.cpp index 8a4359806..efbfd27e1 100644 --- a/src/math/lp/lp_primal_core_solver.cpp +++ b/src/math/lp/lp_primal_core_solver.cpp @@ -27,17 +27,11 @@ Revision History: #include "math/lp/lp_primal_core_solver_tableau_def.h" namespace lp { -template void lp_primal_core_solver::find_feasible_solution(); template void lp::lp_primal_core_solver >::find_feasible_solution(); -template unsigned lp_primal_core_solver::solve(); -template unsigned lp_primal_core_solver::solve_with_tableau(); template unsigned lp_primal_core_solver::solve(); template unsigned lp_primal_core_solver >::solve(); -template void lp::lp_primal_core_solver::clear_breakpoints(); template bool lp::lp_primal_core_solver::update_basis_and_x_tableau(int, int, lp::mpq const&); -template bool lp::lp_primal_core_solver::update_basis_and_x_tableau(int, int, double const&); template bool lp::lp_primal_core_solver >::update_basis_and_x_tableau(int, int, lp::numeric_pair const&); -template void lp::lp_primal_core_solver >::update_inf_cost_for_column_tableau(unsigned); } diff --git a/src/math/lp/lp_primal_core_solver.h b/src/math/lp/lp_primal_core_solver.h index 3abf9dbc0..207428985 100644 --- a/src/math/lp/lp_primal_core_solver.h +++ b/src/math/lp/lp_primal_core_solver.h @@ -19,1024 +19,656 @@ Revision History: --*/ #pragma once -#include -#include -#include -#include -#include -#include "util/vector.h" -#include -#include -#include -#include -#include "math/lp/lu.h" -#include "math/lp/lp_solver.h" -#include "math/lp/static_matrix.h" #include "math/lp/core_solver_pretty_printer.h" #include "math/lp/lp_core_solver_base.h" -#include "math/lp/breakpoint.h" -#include "math/lp/binary_heap_priority_queue.h" +#include "math/lp/static_matrix.h" #include "math/lp/u_set.h" +#include "util/vector.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace lp { -// This core solver solves (Ax=b, lower_bound_values \leq x \leq upper_bound_values, maximize costs*x ) -// The right side b is given implicitly by x and the basis +// This core solver solves (Ax=b, lower_bound_values \leq x \leq +// upper_bound_values, maximize costs*x ) The right side b is given implicitly +// by x and the basis template -class lp_primal_core_solver:public lp_core_solver_base { +class lp_primal_core_solver : public lp_core_solver_base { public: - // m_sign_of_entering is set to 1 if the entering variable needs - // to grow and is set to -1 otherwise - unsigned m_column_norm_update_counter; - T m_enter_price_eps; - int m_sign_of_entering_delta; - vector> m_breakpoints; - binary_heap_priority_queue m_breakpoint_indices_queue; - indexed_vector m_beta; // see Swietanowski working vector beta for column norms - T m_epsilon_of_reduced_cost; - vector m_costs_backup; - T m_converted_harris_eps; - unsigned m_inf_row_index_for_tableau; - bool m_bland_mode_tableau; - u_set m_left_basis_tableau; - unsigned m_bland_mode_threshold; - unsigned m_left_basis_repeated; - vector m_leaving_candidates; - // T m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); - std::list m_non_basis_list; - void sort_non_basis(); - void sort_non_basis_rational(); - int choose_entering_column(unsigned number_of_benefitial_columns_to_go_over); - int choose_entering_column_tableau(); - int choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over); - int find_leaving_and_t_with_breakpoints(unsigned entering, X & t); - // int find_inf_row() { - // // mimicing CLP : todo : use a heap - // int j = -1; - // for (unsigned k : this->m_inf_set.m_index) { - // if (k < static_cast(j)) - // j = static_cast(k); - // } - // if (j == -1) - // return -1; - // return this->m_basis_heading[j]; - // #if 0 - // vector choices; - // unsigned len = 100000000; - // for (unsigned j : this->m_inf_set.m_index) { - // int i = this->m_basis_heading[j]; - // lp_assert(i >= 0); - // unsigned row_len = this->m_A.m_rows[i].size(); - // if (row_len < len) { - // choices.clear(); - // choices.push_back(i); - // len = row_len; - // if (m_settings.random_next() % 10) break; - // } else if (row_len == len) { - // choices.push_back(i); - // if (m_settings.random_next() % 10) break; - // } - // } + int m_sign_of_entering_delta; + vector m_costs_backup; + unsigned m_inf_row_index_for_tableau; + bool m_bland_mode_tableau; + u_set m_left_basis_tableau; + unsigned m_bland_mode_threshold; + unsigned m_left_basis_repeated; + vector m_leaving_candidates; - // if (choices.size() == 0) - // return -1; + std::list m_non_basis_list; + void sort_non_basis(); + int choose_entering_column_tableau(); - // if (choices.size() == 1) - // return choices[0]; - - // unsigned k = this->m_settings.random_next() % choices.size(); - // return choices[k]; - // #endif - // } - - - bool column_is_benefitial_for_entering_basis_on_sign_row_strategy(unsigned j, int sign) const { - // sign = 1 means the x of the basis column of the row has to grow to become feasible, when the coeff before j is neg, or x - has to diminish when the coeff is pos - // we have xbj = -aj * xj - lp_assert(this->m_basis_heading[j] < 0); - lp_assert(this->column_is_feasible(j)); - switch (this->m_column_types[j]) { - case column_type::free_column: return true; - case column_type::fixed: return false; - case column_type::lower_bound: - if (sign < 0) - return true; - return !this->x_is_at_lower_bound(j); - case column_type::upper_bound: - if (sign > 0) - return true; - return !this->x_is_at_upper_bound(j); - case column_type::boxed: - if (sign < 0) - return !this->x_is_at_lower_bound(j); - return !this->x_is_at_upper_bound(j); - } - - lp_assert(false); // cannot be here - return false; + bool needs_to_grow(unsigned bj) const { + lp_assert(!this->column_is_feasible(bj)); + switch (this->m_column_types[bj]) { + case column_type::free_column: + return false; + case column_type::fixed: + case column_type::lower_bound: + case column_type::boxed: + return this->x_below_low_bound(bj); + default: + return false; } - + UNREACHABLE(); // unreachable + return false; + } - bool needs_to_grow(unsigned bj) const { - lp_assert(!this->column_is_feasible(bj)); - switch(this->m_column_types[bj]) { - case column_type::free_column: - return false; - case column_type::fixed: - case column_type::lower_bound: - case column_type::boxed: - return this-> x_below_low_bound(bj); - default: - return false; - } - lp_assert(false); // unreachable - return false; + int inf_sign_of_column(unsigned bj) const { + lp_assert(!this->column_is_feasible(bj)); + switch (this->m_column_types[bj]) { + case column_type::free_column: + return 0; + case column_type::lower_bound: + return 1; + case column_type::fixed: + case column_type::boxed: + return this->x_above_upper_bound(bj) ? -1 : 1; + default: + return -1; } + UNREACHABLE(); // unreachable + return 0; + } - int inf_sign_of_column(unsigned bj) const { - lp_assert(!this->column_is_feasible(bj)); - switch(this->m_column_types[bj]) { - case column_type::free_column: - return 0; - case column_type::lower_bound: - return 1; - case column_type::fixed: - case column_type::boxed: - return this->x_above_upper_bound(bj)? -1: 1; - default: - return -1; - } - lp_assert(false); // unreachable - return 0; - + bool monoid_can_decrease(const row_cell &rc) const { + unsigned j = rc.var(); + lp_assert(this->column_is_feasible(j)); + switch (this->m_column_types[j]) { + case column_type::free_column: + return true; + case column_type::fixed: + return false; + case column_type::lower_bound: + if (is_pos(rc.coeff())) { + return this->x_above_lower_bound(j); + } + + return true; + case column_type::upper_bound: + if (is_pos(rc.coeff())) { + return true; + } + + return this->x_below_upper_bound(j); + case column_type::boxed: + if (is_pos(rc.coeff())) { + return this->x_above_lower_bound(j); + } + + return this->x_below_upper_bound(j); + default: + return false; } - + UNREACHABLE(); // unreachable + return false; + } - bool monoid_can_decrease(const row_cell & rc) const { - unsigned j = rc.var(); - lp_assert(this->column_is_feasible(j)); - switch (this->m_column_types[j]) { - case column_type::free_column: - return true; - case column_type::fixed: - return false; - case column_type::lower_bound: - if (is_pos(rc.coeff())) { - return this->x_above_lower_bound(j); - } + bool monoid_can_increase(const row_cell &rc) const { + unsigned j = rc.var(); + lp_assert(this->column_is_feasible(j)); + switch (this->m_column_types[j]) { + case column_type::free_column: + return true; + case column_type::fixed: + return false; + case column_type::lower_bound: + if (is_neg(rc.coeff())) { + return this->x_above_lower_bound(j); + } - return true; - case column_type::upper_bound: - if (is_pos(rc.coeff())) { - return true; - } + return true; + case column_type::upper_bound: + if (is_neg(rc.coeff())) { + return true; + } - return this->x_below_upper_bound(j); - case column_type::boxed: - if (is_pos(rc.coeff())) { - return this->x_above_lower_bound(j); - } + return this->x_below_upper_bound(j); + case column_type::boxed: + if (is_neg(rc.coeff())) { + return this->x_above_lower_bound(j); + } - return this->x_below_upper_bound(j); - default: - return false; - } - lp_assert(false); // unreachable - return false; + return this->x_below_upper_bound(j); + default: + return false; } + UNREACHABLE(); // unreachable + return false; + } - bool monoid_can_increase(const row_cell & rc) const { - unsigned j = rc.var(); - lp_assert(this->column_is_feasible(j)); - switch (this->m_column_types[j]) { - case column_type::free_column: - return true; - case column_type::fixed: - return false; - case column_type::lower_bound: - if (is_neg(rc.coeff())) { - return this->x_above_lower_bound(j); - } - - return true; - case column_type::upper_bound: - if (is_neg(rc.coeff())) { - return true; - } - - return this->x_below_upper_bound(j); - case column_type::boxed: - if (is_neg(rc.coeff())) { - return this->x_above_lower_bound(j); - } - - return this->x_below_upper_bound(j); - default: - return false; - } - lp_assert(false); // unreachable - return false; + unsigned get_number_of_basic_vars_that_might_become_inf( + unsigned j) const { // consider looking at the signs here: todo + unsigned r = 0; + for (const auto &cc : this->m_A.m_columns[j]) { + unsigned k = this->m_basis[cc.var()]; + if (this->m_column_types[k] != column_type::free_column) + r++; } + return r; + } - unsigned get_number_of_basic_vars_that_might_become_inf(unsigned j) const { // consider looking at the signs here: todo - unsigned r = 0; - for (const auto & cc : this->m_A.m_columns[j]) { - unsigned k = this->m_basis[cc.var()]; - if (this->m_column_types[k] != column_type::free_column) - r++; - } - return r; - } - - - int find_beneficial_column_in_row_tableau_rows_bland_mode(int i, T & a_ent) { - int j = -1; - unsigned bj = this->m_basis[i]; - bool bj_needs_to_grow = needs_to_grow(bj); - for (const row_cell& rc : this->m_A.m_rows[i]) { - if (rc.var() == bj) - continue; - if (bj_needs_to_grow) { - if (!monoid_can_decrease(rc)) - continue; - } else { - if (!monoid_can_increase(rc)) - continue; - } - if (rc.var() < static_cast(j) ) { - j = rc.var(); - a_ent = rc.coeff(); - } - } - if (j == -1) { - m_inf_row_index_for_tableau = i; - } - - return j; - } - - int find_beneficial_column_in_row_tableau_rows(int i, T & a_ent) { - if (m_bland_mode_tableau) - return find_beneficial_column_in_row_tableau_rows_bland_mode(i, a_ent); - // a short row produces short infeasibility explanation and benefits at least one pivot operation - int choice = -1; - int nchoices = 0; - unsigned num_of_non_free_basics = 1000000; - unsigned len = 100000000; - unsigned bj = this->m_basis[i]; - bool bj_needs_to_grow = needs_to_grow(bj); - for (unsigned k = 0; k < this->m_A.m_rows[i].size(); k++) { - const row_cell& rc = this->m_A.m_rows[i][k]; - unsigned j = rc.var(); - if (j == bj) - continue; - if (bj_needs_to_grow) { - if (!monoid_can_decrease(rc)) - continue; - } else { - if (!monoid_can_increase(rc)) - continue; - } - unsigned damage = get_number_of_basic_vars_that_might_become_inf(j); - if (damage < num_of_non_free_basics) { - num_of_non_free_basics = damage; - len = this->m_A.m_columns[j].size(); - choice = k; - nchoices = 1; - } else if (damage == num_of_non_free_basics && - this->m_A.m_columns[j].size() <= len && (this->m_settings.random_next() % (++nchoices))) { - choice = k; - len = this->m_A.m_columns[j].size(); - } - } - - - if (choice == -1) { - m_inf_row_index_for_tableau = i; - return -1; - } - const row_cell& rc = this->m_A.m_rows[i][choice]; + int find_beneficial_column_in_row_tableau_rows_bland_mode(int i, T &a_ent) { + int j = -1; + unsigned bj = this->m_basis[i]; + bool bj_needs_to_grow = needs_to_grow(bj); + for (const row_cell &rc : this->m_A.m_rows[i]) { + if (rc.var() == bj) + continue; + if (bj_needs_to_grow) { + if (!monoid_can_decrease(rc)) + continue; + } else { + if (!monoid_can_increase(rc)) + continue; + } + if (rc.var() < static_cast(j)) { + j = rc.var(); a_ent = rc.coeff(); - return rc.var(); + } } - static X positive_infinity() { - return convert_struct::convert(std::numeric_limits::max()); + if (j == -1) { + m_inf_row_index_for_tableau = i; } - bool get_harris_theta(X & theta); + return j; + } - void restore_harris_eps() { m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); } - void zero_harris_eps() { m_converted_harris_eps = zero_of_type(); } - int find_leaving_on_harris_theta(X const & harris_theta, X & t); - bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited); - bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t); - int find_leaving_and_t(unsigned entering, X & t); - int find_leaving_and_t_precise(unsigned entering, X & t); - int find_leaving_and_t_tableau(unsigned entering, X & t); - - void limit_theta(const X & lim, X & theta, bool & unlimited) { - if (unlimited) { - theta = lim; - unlimited = false; - } else { - theta = std::min(lim, theta); - } + int find_beneficial_column_in_row_tableau_rows(int i, T &a_ent) { + if (m_bland_mode_tableau) + return find_beneficial_column_in_row_tableau_rows_bland_mode(i, a_ent); + // a short row produces short infeasibility explanation and benefits at + // least one pivot operation + int choice = -1; + int nchoices = 0; + unsigned num_of_non_free_basics = 1000000; + unsigned len = 100000000; + unsigned bj = this->m_basis[i]; + bool bj_needs_to_grow = needs_to_grow(bj); + for (unsigned k = 0; k < this->m_A.m_rows[i].size(); k++) { + const row_cell &rc = this->m_A.m_rows[i][k]; + unsigned j = rc.var(); + if (j == bj) + continue; + if (bj_needs_to_grow) { + if (!monoid_can_decrease(rc)) + continue; + } else { + if (!monoid_can_increase(rc)) + continue; + } + unsigned damage = get_number_of_basic_vars_that_might_become_inf(j); + if (damage < num_of_non_free_basics) { + num_of_non_free_basics = damage; + len = this->m_A.m_columns[j].size(); + choice = k; + nchoices = 1; + } else if (damage == num_of_non_free_basics && + this->m_A.m_columns[j].size() <= len && + (this->m_settings.random_next() % (++nchoices))) { + choice = k; + len = this->m_A.m_columns[j].size(); + } } - void limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m < 0 && this->m_column_types[j] == column_type::upper_bound); - limit_inf_on_upper_bound_m_neg(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); + if (choice == -1) { + m_inf_row_index_for_tableau = i; + return -1; } + const row_cell &rc = this->m_A.m_rows[i][choice]; + a_ent = rc.coeff(); + return rc.var(); + } + bool try_jump_to_another_bound_on_entering(unsigned entering, const X &theta, + X &t, bool &unlimited); + bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X &t); + int find_leaving_and_t_tableau(unsigned entering, X &t); - void limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m < 0 && this->m_column_types[j] == column_type::lower_bound); - limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_lower_bounds[j], theta, unlimited); + void limit_theta(const X &lim, X &theta, bool &unlimited) { + if (unlimited) { + theta = lim; + unlimited = false; + } else { + theta = std::min(lim, theta); } + } + void limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m < 0 && this->m_column_types[j] == column_type::upper_bound); + limit_inf_on_upper_bound_m_neg(m, this->m_x[j], this->m_upper_bounds[j], + theta, unlimited); + } - void limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m > 0 && this->m_column_types[j] == column_type::lower_bound); - limit_inf_on_lower_bound_m_pos(m, this->m_x[j], this->m_lower_bounds[j], theta, unlimited); - } + void limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m < 0 && this->m_column_types[j] == column_type::lower_bound); + limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_lower_bounds[j], theta, + unlimited); + } - void limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m > 0 && this->m_column_types[j] == column_type::upper_bound); - limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); - }; + void limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m > 0 && this->m_column_types[j] == column_type::lower_bound); + limit_inf_on_lower_bound_m_pos(m, this->m_x[j], this->m_lower_bounds[j], + theta, unlimited); + } - X harris_eps_for_bound(const X & bound) const { return ( convert_struct::convert(1) + abs(bound)/10) * m_converted_harris_eps/3; - } + void limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m > 0 && this->m_column_types[j] == column_type::upper_bound); + limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, + unlimited); + }; - void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector & leavings, T m, X & t, T & abs_of_d_of_leaving); + void get_bound_on_variable_and_update_leaving_precisely( + unsigned j, vector &leavings, T m, X &t, + T &abs_of_d_of_leaving); - vector m_lower_bounds_dummy; // needed for the base class only - - X get_max_bound(vector & b); + X get_max_bound(vector &b); #ifdef Z3DEBUG - void check_Ax_equal_b(); - void check_the_bounds(); - void check_bound(unsigned i); - void check_correctness(); + void check_Ax_equal_b(); + void check_the_bounds(); + void check_bound(unsigned i); + void check_correctness(); #endif - // from page 183 of Istvan Maros's book - // the basis structures have not changed yet - void update_reduced_costs_from_pivot_row(unsigned entering, unsigned leaving); + // from page 183 of Istvan Maros's book + // the basis structures have not changed yet + void update_reduced_costs_from_pivot_row(unsigned entering, unsigned leaving); - // return 0 if the reduced cost at entering is close enough to the refreshed - // 1 if it is way off, and 2 if it is unprofitable - int refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering); + // return 0 if the reduced cost at entering is close enough to the refreshed + // 1 if it is way off, and 2 if it is unprofitable + int refresh_reduced_cost_at_entering_and_check_that_it_is_off( + unsigned entering); - void backup_and_normalize_costs(); + void backup_and_normalize_costs(); - void init_run(); + void advance_on_entering_and_leaving_tableau(int entering, int leaving, X &t); + void advance_on_entering_equal_leaving_tableau(int entering, X &t); - void calc_working_vector_beta_for_column_norms(); + bool need_to_switch_costs() const { + if (this->m_settings.simplex_strategy() == + simplex_strategy_enum::tableau_rows) + return false; + // lp_assert(calc_current_x_is_feasible() == + // current_x_is_feasible()); + return this->current_x_is_feasible() == this->using_infeas_costs(); + } - void advance_on_entering_and_leaving(int entering, int leaving, X & t); - void advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t); - void advance_on_entering_equal_leaving(int entering, X & t); - void advance_on_entering_equal_leaving_tableau(int entering, X & t); + void advance_on_entering_tableau(int entering); - bool need_to_switch_costs() const { - if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) - return false; - // lp_assert(calc_current_x_is_feasible() == current_x_is_feasible()); - return this->current_x_is_feasible() == this->using_infeas_costs(); + void push_forward_offset_in_non_basis(unsigned &offset_in_nb); + + unsigned get_number_of_non_basic_column_to_try_for_enter(); + + // returns the number of iterations + unsigned solve(); + + void find_feasible_solution(); + + // bool is_tiny() const {return this->m_m < 10 && this->m_n < 20;} + + void one_iteration_tableau(); + + // this version assumes that the leaving already has the right value, and does + // not update it + void update_x_tableau_rows(unsigned entering, unsigned leaving, + const X &delta) { + this->add_delta_to_x(entering, delta); + for (const auto &c : this->m_A.m_columns[entering]) { + if (leaving != this->m_basis[c.var()]) { + this->add_delta_to_x_and_track_feasibility( + this->m_basis[c.var()], -delta * this->m_A.get_val(c)); + } + } + } + + void update_basis_and_x_tableau_rows(int entering, int leaving, X const &tt) { + lp_assert(entering != leaving); + update_x_tableau_rows(entering, leaving, tt); + this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); + this->change_basis(entering, leaving); + } + + void advance_on_entering_and_leaving_tableau_rows(int entering, int leaving, + const X &theta) { + update_basis_and_x_tableau_rows(entering, leaving, theta); + this->track_column_feasibility(entering); + } + + int find_smallest_inf_column() { + int j = -1; + for (unsigned k : this->inf_set()) { + if (k < static_cast(j)) { + j = k; + } + } + return j; + } + + const X &get_val_for_leaving(unsigned j) const { + lp_assert(!this->column_is_feasible(j)); + switch (this->m_column_types[j]) { + case column_type::fixed: + case column_type::upper_bound: + return this->m_upper_bounds[j]; + case column_type::lower_bound: + return this->m_lower_bounds[j]; + break; + case column_type::boxed: + if (this->x_above_upper_bound(j)) + return this->m_upper_bounds[j]; + else + return this->m_lower_bounds[j]; + break; + default: + UNREACHABLE(); + return this->m_lower_bounds[j]; + } + } + + void one_iteration_tableau_rows() { + int leaving = find_smallest_inf_column(); + if (leaving == -1) { + this->set_status(lp_status::OPTIMAL); + return; } + SASSERT(this->column_is_base(leaving)); - void advance_on_entering(int entering); - void advance_on_entering_tableau(int entering); - void advance_on_entering_precise(int entering); - void push_forward_offset_in_non_basis(unsigned & offset_in_nb); - - unsigned get_number_of_non_basic_column_to_try_for_enter(); - - void print_column_norms(std::ostream & out); - - // returns the number of iterations - unsigned solve(); - - lu> * factorization() {return this->m_factorization;} - - void delete_factorization(); - - // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" - void init_column_norms(); - - T calculate_column_norm_exactly(unsigned j); - - void update_or_init_column_norms(unsigned entering, unsigned leaving); - - // following Swietanowski - A new steepest ... - void update_column_norms(unsigned entering, unsigned leaving); - - T calculate_norm_of_entering_exactly(); - - void find_feasible_solution(); - - // bool is_tiny() const {return this->m_m < 10 && this->m_n < 20;} - - void one_iteration(); - void one_iteration_tableau(); - - // this version assumes that the leaving already has the right value, and does not update it - void update_x_tableau_rows(unsigned entering, unsigned leaving, const X& delta) { - this->add_delta_to_x(entering, delta); - if (!this->using_infeas_costs()) { - for (const auto & c : this->m_A.m_columns[entering]) { - if (leaving != this->m_basis[c.var()]) { - this->add_delta_to_x_and_track_feasibility(this->m_basis[c.var()], - delta * this->m_A.get_val(c)); - } - } - } else { // using_infeas_costs() == true - lp_assert(this->column_is_feasible(entering)); - lp_assert(this->m_costs[entering] == zero_of_type()); - // m_d[entering] can change because of the cost change for basic columns. - for (const auto & c : this->m_A.m_columns[entering]) { - unsigned j = this->m_basis[c.var()]; - if (j != leaving) - this->add_delta_to_x(j, -delta * this->m_A.get_val(c)); - update_inf_cost_for_column_tableau(j); - if (is_zero(this->m_costs[j])) - this->remove_column_from_inf_set(j); - else - this->insert_column_into_inf_set(j); - } + if (!m_bland_mode_tableau) { + if (m_left_basis_tableau.contains(leaving)) { + if (++m_left_basis_repeated > m_bland_mode_threshold) { + m_bland_mode_tableau = true; } + } else { + m_left_basis_tableau.insert(leaving); + } } - - void update_basis_and_x_tableau_rows(int entering, int leaving, X const & tt) { - lp_assert(this->use_tableau()); - lp_assert(entering != leaving); - update_x_tableau_rows(entering, leaving, tt); - this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); - this->change_basis(entering, leaving); + T a_ent; + int entering = find_beneficial_column_in_row_tableau_rows( + this->m_basis_heading[leaving], a_ent); + if (entering == -1) { + this->set_status(lp_status::INFEASIBLE); + return; + } + const X &new_val_for_leaving = get_val_for_leaving(leaving); + X theta = (this->m_x[leaving] - new_val_for_leaving) / a_ent; + this->m_x[leaving] = new_val_for_leaving; + this->remove_column_from_inf_set(leaving); + advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta); + if (this->current_x_is_feasible()) + this->set_status(lp_status::OPTIMAL); + } + + void decide_on_status_when_cannot_find_entering() { + this->set_status(this->current_x_is_feasible() ? lp_status::OPTIMAL + : lp_status::INFEASIBLE); + } + + void limit_theta_on_basis_column_for_feas_case_m_neg_no_check( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m < 0); + limit_theta((this->m_lower_bounds[j] - this->m_x[j]) / m, theta, unlimited); + if (theta < zero_of_type()) + theta = zero_of_type(); + } + + bool limit_inf_on_bound_m_neg(const T &m, const X &x, const X &bound, + X &theta, bool &unlimited) { + // x gets smaller + lp_assert(m < 0); + if (this->below_bound(x, bound)) + return false; + if (this->above_bound(x, bound)) { + limit_theta((bound - x) / m, theta, unlimited); + } else { + theta = zero_of_type(); + unlimited = false; + } + return true; + } + + bool limit_inf_on_bound_m_pos(const T &m, const X &x, const X &bound, + X &theta, bool &unlimited) { + // x gets larger + lp_assert(m > 0); + if (this->above_bound(x, bound)) + return false; + if (this->below_bound(x, bound)) { + limit_theta((bound - x) / m, theta, unlimited); + } else { + theta = zero_of_type(); + unlimited = false; } - - void advance_on_entering_and_leaving_tableau_rows(int entering, int leaving, const X &theta ) { - update_basis_and_x_tableau_rows(entering, leaving, theta); - this->track_column_feasibility(entering); + return true; + } + + void limit_inf_on_lower_bound_m_pos(const T &m, const X &x, const X &bound, + X &theta, bool &unlimited) { + // x gets larger + lp_assert(m > 0); + if (this->below_bound(x, bound)) { + limit_theta((bound - x) / m, theta, unlimited); } + } - int find_smallest_inf_column() { - int j = -1; - for (unsigned k : this->inf_set()) { - if (k < static_cast(j)) { - j = k; - } - } - return j; + void limit_inf_on_upper_bound_m_neg(const T &m, const X &x, const X &bound, + X &theta, bool &unlimited) { + // x gets smaller + lp_assert(m < 0); + if (this->above_bound(x, bound)) { + limit_theta((bound - x) / m, theta, unlimited); } + } - const X& get_val_for_leaving(unsigned j) const { - lp_assert(!this->column_is_feasible(j)); - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::upper_bound: - return this->m_upper_bounds[j]; - case column_type::lower_bound: - return this->m_lower_bounds[j]; - break; - case column_type::boxed: - if (this->x_above_upper_bound(j)) - return this->m_upper_bounds[j]; - else - return this->m_lower_bounds[j]; - break; - default: - UNREACHABLE(); - return this->m_lower_bounds[j]; - } + void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, + const T &m, + X &theta, + bool &unlimited) { + const X &x = this->m_x[j]; + const X &lbound = this->m_lower_bounds[j]; + + if (this->below_bound(x, lbound)) { + limit_theta((lbound - x) / m, theta, unlimited); + } else { + const X &ubound = this->m_upper_bounds[j]; + if (this->below_bound(x, ubound)) { + limit_theta((ubound - x) / m, theta, unlimited); + } else if (!this->above_bound(x, ubound)) { + theta = zero_of_type(); + unlimited = false; + } } - - - void one_iteration_tableau_rows() { - int leaving = find_smallest_inf_column(); - if (leaving == -1) { - this->set_status(lp_status::OPTIMAL); - return; - } - - SASSERT(this->column_is_base(leaving)); - - if (!m_bland_mode_tableau) { - if (m_left_basis_tableau.contains(leaving)) { - if (++m_left_basis_repeated > m_bland_mode_threshold) { - m_bland_mode_tableau = true; - } - } else { - m_left_basis_tableau.insert(leaving); - } - } - T a_ent; - int entering = find_beneficial_column_in_row_tableau_rows(this->m_basis_heading[leaving], a_ent); - if (entering == -1) { - this->set_status(lp_status::INFEASIBLE); - return; - } - const X& new_val_for_leaving = get_val_for_leaving(leaving); - X theta = (this->m_x[leaving] - new_val_for_leaving) / a_ent; - this->m_x[leaving] = new_val_for_leaving; - this->remove_column_from_inf_set(leaving); - advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta ); - if (this->current_x_is_feasible()) - this->set_status(lp_status::OPTIMAL); + } + + void limit_theta_on_basis_column_for_inf_case_m_neg_boxed(unsigned j, + const T &m, + X &theta, + bool &unlimited) { + // lp_assert(m < 0 && this->m_column_type[j] == column_type::boxed); + const X &x = this->m_x[j]; + const X &ubound = this->m_upper_bounds[j]; + if (this->above_bound(x, ubound)) { + limit_theta((ubound - x) / m, theta, unlimited); + } else { + const X &lbound = this->m_lower_bounds[j]; + if (this->above_bound(x, lbound)) { + limit_theta((lbound - x) / m, theta, unlimited); + } else if (!this->below_bound(x, lbound)) { + theta = zero_of_type(); + unlimited = false; + } } + } - void fill_breakpoints_array(unsigned entering); - - void try_add_breakpoint_in_row(unsigned i); - - void clear_breakpoints(); - - void change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering); - void advance_on_sorted_breakpoints(unsigned entering); - - void update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta); - - void decide_on_status_when_cannot_find_entering() { - lp_assert(!need_to_switch_costs()); - this->set_status(this->current_x_is_feasible()? lp_status::OPTIMAL: lp_status::INFEASIBLE); + void limit_theta_on_basis_column_for_feas_case_m_pos_no_check( + unsigned j, const T &m, X &theta, bool &unlimited) { + lp_assert(m > 0); + limit_theta((this->m_upper_bounds[j] - this->m_x[j]) / m, theta, unlimited); + if (theta < zero_of_type()) { + theta = zero_of_type(); } + } - // void limit_theta_on_basis_column_for_feas_case_m_neg(unsigned j, const T & m, X & theta) { - // lp_assert(m < 0); - // lp_assert(this->m_column_type[j] == lower_bound || this->m_column_type[j] == boxed); - // const X & eps = harris_eps_for_bound(this->m_lower_bounds[j]); - // if (this->above_bound(this->m_x[j], this->m_lower_bounds[j])) { - // theta = std::min((this->m_lower_bounds[j] -this->m_x[j] - eps) / m, theta); - // if (theta < zero_of_type()) theta = zero_of_type(); - // } - // } - - void limit_theta_on_basis_column_for_feas_case_m_neg_no_check(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m < 0); - const X& eps = harris_eps_for_bound(this->m_lower_bounds[j]); - limit_theta((this->m_lower_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited); - if (theta < zero_of_type()) theta = zero_of_type(); - } - - bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { - // x gets smaller - lp_assert(m < 0); - if (numeric_traits::precise()) { - if (this->below_bound(x, bound)) return false; - if (this->above_bound(x, bound)) { - limit_theta((bound - x) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } + // j is a basic column or the entering, in any case x[j] has to stay feasible. + // m is the multiplier. updating t in a way that holds the following + // x[j] + t * m >= this->m_lower_bounds[j]( if m < 0 ) + // or + // x[j] + t * m <= this->m_upper_bounds[j] ( if m > 0) + void limit_theta_on_basis_column(unsigned j, T m, X &theta, bool &unlimited) { + switch (this->m_column_types[j]) { + case column_type::free_column: + break; + case column_type::upper_bound: + if (this->current_x_is_feasible()) { + if (m > 0) + limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, + unlimited); + } else { // inside of feasibility_loop + if (m > 0) + limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound( + j, m, theta, unlimited); + else + limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound( + j, m, theta, unlimited); + } + break; + case column_type::lower_bound: + if (this->current_x_is_feasible()) { + if (m < 0) + limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, + unlimited); + } else { + if (m < 0) + limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound( + j, m, theta, unlimited); + else + limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound( + j, m, theta, unlimited); + } + break; + // case fixed: + // if (get_this->current_x_is_feasible()) { + // theta = zero_of_type(); + // break; + // } + // if (m < 0) + // limit_theta_on_basis_column_for_inf_case_m_neg_fixed(j, m, + // theta); + // else + // limit_theta_on_basis_column_for_inf_case_m_pos_fixed(j, m, + // theta); + // break; + case column_type::fixed: + case column_type::boxed: + if (this->current_x_is_feasible()) { + if (m > 0) { + limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, + unlimited); } else { - const X& eps = harris_eps_for_bound(bound); - if (this->below_bound(x, bound)) return false; - if (this->above_bound(x, bound)) { - limit_theta((bound - x - eps) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } + limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, + unlimited); } - return true; - } - - bool limit_inf_on_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { - // x gets larger - lp_assert(m > 0); - if (numeric_traits::precise()) { - if (this->above_bound(x, bound)) return false; - if (this->below_bound(x, bound)) { - limit_theta((bound - x) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } + } else { + if (m > 0) { + limit_theta_on_basis_column_for_inf_case_m_pos_boxed(j, m, theta, + unlimited); } else { - const X& eps = harris_eps_for_bound(bound); - if (this->above_bound(x, bound)) return false; - if (this->below_bound(x, bound)) { - limit_theta((bound - x + eps) / m, theta, unlimited); - } else { - theta = zero_of_type(); - unlimited = false; - } + limit_theta_on_basis_column_for_inf_case_m_neg_boxed(j, m, theta, + unlimited); } - return true; + } + + break; + default: + UNREACHABLE(); } - - void limit_inf_on_lower_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { - if (numeric_traits::precise()) { - // x gets larger - lp_assert(m > 0); - if (this->below_bound(x, bound)) { - limit_theta((bound - x) / m, theta, unlimited); - } - } - else { - // x gets larger - lp_assert(m > 0); - const X& eps = harris_eps_for_bound(bound); - if (this->below_bound(x, bound)) { - limit_theta((bound - x + eps) / m, theta, unlimited); - } - } + if (!unlimited && theta < zero_of_type()) { + theta = zero_of_type(); } + } - void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { - // x gets smaller - lp_assert(m < 0); - const X& eps = harris_eps_for_bound(bound); - if (this->above_bound(x, bound)) { - limit_theta((bound - x - eps) / m, theta, unlimited); - } + bool column_is_benefitial_for_entering_basis(unsigned j) const; + void init_infeasibility_costs(); + void print_column(unsigned j, std::ostream &out); + + void print_bound_info_and_x(unsigned j, std::ostream &out); + + bool basis_column_is_set_correctly(unsigned j) const { + return this->m_A.m_columns[j].size() == 1; + } + + bool basis_columns_are_set_correctly() const { + for (unsigned j : this->m_basis) + if (!basis_column_is_set_correctly(j)) + return false; + + return this->m_basis_heading.size() == this->m_A.column_count() && + this->m_basis.size() == this->m_A.row_count(); + } + + void init_run_tableau(); + void update_x_tableau(unsigned entering, const X &delta); + // the delta is between the old and the new cost (old - new) + void update_reduced_cost_for_basic_column_cost_change(const T &delta, + unsigned j) { + lp_assert(this->m_basis_heading[j] >= 0); + unsigned i = static_cast(this->m_basis_heading[j]); + for (const row_cell &rc : this->m_A.m_rows[i]) { + unsigned k = rc.var(); + if (k == j) + continue; + this->m_d[k] += delta * rc.coeff(); } + } - void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - // lp_assert(m > 0 && this->m_column_type[j] == column_type::boxed); - const X & x = this->m_x[j]; - const X & lbound = this->m_lower_bounds[j]; - - if (this->below_bound(x, lbound)) { - const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); - limit_theta((lbound - x + eps) / m, theta, unlimited); - } else { - const X & ubound = this->m_upper_bounds[j]; - if (this->below_bound(x, ubound)){ - const X& eps = harris_eps_for_bound(ubound); - limit_theta((ubound - x + eps) / m, theta, unlimited); - } else if (!this->above_bound(x, ubound)) { - theta = zero_of_type(); - unlimited = false; - } - } - } - - void limit_theta_on_basis_column_for_inf_case_m_neg_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - // lp_assert(m < 0 && this->m_column_type[j] == column_type::boxed); - const X & x = this->m_x[j]; - const X & ubound = this->m_upper_bounds[j]; - if (this->above_bound(x, ubound)) { - const X& eps = harris_eps_for_bound(ubound); - limit_theta((ubound - x - eps) / m, theta, unlimited); - } else { - const X & lbound = this->m_lower_bounds[j]; - if (this->above_bound(x, lbound)){ - const X& eps = harris_eps_for_bound(lbound); - limit_theta((lbound - x - eps) / m, theta, unlimited); - } else if (!this->below_bound(x, lbound)) { - theta = zero_of_type(); - unlimited = false; - } - } - } - void limit_theta_on_basis_column_for_feas_case_m_pos(unsigned j, const T & m, X & theta, bool & unlimited) { - lp_assert(m > 0); - const T& eps = harris_eps_for_bound(this->m_upper_bounds[j]); - if (this->below_bound(this->m_x[j], this->m_upper_bounds[j])) { - limit_theta((this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); - if (theta < zero_of_type()) { - theta = zero_of_type(); - unlimited = false; - } - } - } - - void limit_theta_on_basis_column_for_feas_case_m_pos_no_check(unsigned j, const T & m, X & theta, bool & unlimited ) { - lp_assert(m > 0); - const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); - limit_theta( (this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); - if (theta < zero_of_type()) { - theta = zero_of_type(); - } - } - - // j is a basic column or the entering, in any case x[j] has to stay feasible. - // m is the multiplier. updating t in a way that holds the following - // x[j] + t * m >= this->m_lower_bounds[j]- harris_feasibility_tolerance ( if m < 0 ) - // or - // x[j] + t * m <= this->m_upper_bounds[j] + harris_feasibility_tolerance ( if m > 0) - void limit_theta_on_basis_column(unsigned j, T m, X & theta, bool & unlimited) { - switch (this->m_column_types[j]) { - case column_type::free_column: break; - case column_type::upper_bound: - if (this->current_x_is_feasible()) { - if (m > 0) - limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, unlimited); - } else { // inside of feasibility_loop - if (m > 0) - limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(j, m, theta, unlimited); - else - limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(j, m, theta, unlimited); - } - break; - case column_type::lower_bound: - if (this->current_x_is_feasible()) { - if (m < 0) - limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited); - } else { - if (m < 0) - limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound(j, m, theta, unlimited); - else - limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound(j, m, theta, unlimited); - } - break; - // case fixed: - // if (get_this->current_x_is_feasible()) { - // theta = zero_of_type(); - // break; - // } - // if (m < 0) - // limit_theta_on_basis_column_for_inf_case_m_neg_fixed(j, m, theta); - // else - // limit_theta_on_basis_column_for_inf_case_m_pos_fixed(j, m, theta); - // break; - case column_type::fixed: - case column_type::boxed: - if (this->current_x_is_feasible()) { - if (m > 0) { - limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, unlimited); - } else { - limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited); - } - } else { - if (m > 0) { - limit_theta_on_basis_column_for_inf_case_m_pos_boxed(j, m, theta, unlimited); - } else { - limit_theta_on_basis_column_for_inf_case_m_neg_boxed(j, m, theta, unlimited); - } - } - - break; - default: - lp_unreachable(); - } - if (!unlimited && theta < zero_of_type()) { - theta = zero_of_type(); - } - } - - - bool column_is_benefitial_for_entering_basis(unsigned j) const; - bool column_is_benefitial_for_entering_basis_precise(unsigned j) const; - - bool column_is_benefitial_for_entering_on_breakpoints(unsigned j) const; - - - bool can_enter_basis(unsigned j); - bool done(); - void init_infeasibility_costs(); - - void init_infeasibility_cost_for_column(unsigned j); - T get_infeasibility_cost_for_column(unsigned j) const; - void init_infeasibility_costs_for_changed_basis_only(); - - void print_column(unsigned j, std::ostream & out); - void add_breakpoint(unsigned j, X delta, breakpoint_type type); - - // j is the basic column, x is the value at x[j] - // d is the coefficient before m_entering in the row with j as the basis column - void try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value); - template - bool same_sign_with_entering_delta(const L & a) { - return (a > zero_of_type() && m_sign_of_entering_delta > 0) || (a < zero_of_type() && m_sign_of_entering_delta < 0); - } - - void init_reduced_costs(); - - bool lower_bounds_are_set() const override { return true; } - - int advance_on_sorted_breakpoints(unsigned entering, X & t); - - std::string break_type_to_string(breakpoint_type type); - - void print_breakpoint(const breakpoint * b, std::ostream & out); - - void print_bound_info_and_x(unsigned j, std::ostream & out); - - void init_infeasibility_after_update_x_if_inf(unsigned leaving) { - if (this->using_infeas_costs()) { - init_infeasibility_costs_for_changed_basis_only(); - this->m_costs[leaving] = zero_of_type(); - this->remove_column_from_inf_set(leaving); - } - } - - void init_inf_set() { - this->clear_inf_set(); - for (unsigned j = 0; j < this->m_n(); j++) { - if (this->m_basis_heading[j] < 0) - continue; - if (!this->column_is_feasible(j)) - this->insert_column_into_inf_set(j); - } - } - - int get_column_out_of_bounds_delta_sign(unsigned j) { - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - if (this->x_below_low_bound(j)) - return -1; - if (this->x_above_upper_bound(j)) - return 1; - break; - case column_type::lower_bound: - if (this->x_below_low_bound(j)) - return -1; - break; - case column_type::upper_bound: - if (this->x_above_upper_bound(j)) - return 1; - break; - case column_type::free_column: - return 0; - default: - lp_assert(false); - } - return 0; - } - - void init_column_row_non_zeroes() { - this->m_columns_nz.resize(this->m_A.column_count()); - this->m_rows_nz.resize(this->m_A.row_count()); - for (unsigned i = 0; i < this->m_A.column_count(); i++) { - if (this->m_columns_nz[i] == 0) - this->m_columns_nz[i] = this->m_A.m_columns[i].size(); - } - for (unsigned i = 0; i < this->m_A.row_count(); i++) { - if (this->m_rows_nz[i] == 0) - this->m_rows_nz[i] = this->m_A.m_rows[i].size(); - } - } - - - int x_at_bound_sign(unsigned j) { - switch (this->m_column_types[j]) { - case column_type::fixed: - return 0; - case column_type::boxed: - if (this->x_is_at_lower_bound(j)) - return 1; - return -1; - break; - case column_type::lower_bound: - return 1; - break; - case column_type::upper_bound: - return -1; - break; - default: - lp_assert(false); - } - return 0; - - } - - unsigned solve_with_tableau(); - - bool basis_column_is_set_correctly(unsigned j) const { - return this->m_A.m_columns[j].size() == 1; - - } - - bool basis_columns_are_set_correctly() const { - for (unsigned j : this->m_basis) - if(!basis_column_is_set_correctly(j)) - return false; - - return this->m_basis_heading.size() == this->m_A.column_count() && this->m_basis.size() == this->m_A.row_count(); - } - - void init_run_tableau(); - void update_x_tableau(unsigned entering, const X & delta); - void update_inf_cost_for_column_tableau(unsigned j); - -// the delta is between the old and the new cost (old - new) - void update_reduced_cost_for_basic_column_cost_change(const T & delta, unsigned j) { - lp_assert(this->m_basis_heading[j] >= 0); - unsigned i = static_cast(this->m_basis_heading[j]); - for (const row_cell & rc : this->m_A.m_rows[i]) { - unsigned k = rc.var(); - if (k == j) - continue; - this->m_d[k] += delta * rc.coeff(); - } - } - - bool update_basis_and_x_tableau(int entering, int leaving, X const & tt); - void init_reduced_costs_tableau(); - void init_tableau_rows() { - m_bland_mode_tableau = false; - m_left_basis_tableau.clear(); - m_left_basis_tableau.resize(this->m_A.column_count()); - m_left_basis_repeated = 0; - } -// stage1 constructor - lp_primal_core_solver(static_matrix & A, - vector & b, // the right side vector - vector & x, // the number of elements in x needs to be at least as large as the number of columns in A - vector & basis, - vector & nbasis, - vector & heading, - vector & costs, - const vector & column_type_array, - const vector & lower_bound_values, - const vector & upper_bound_values, - lp_settings & settings, - const column_namer& column_names): - lp_core_solver_base(A, b, - basis, - nbasis, - heading, - x, - costs, - settings, - column_names, - column_type_array, - lower_bound_values, - upper_bound_values), - m_beta(A.row_count()), - m_epsilon_of_reduced_cost(T(1)/T(10000000)), + bool update_basis_and_x_tableau(int entering, int leaving, X const &tt); + void init_reduced_costs_tableau(); + void init_tableau_rows() { + m_bland_mode_tableau = false; + m_left_basis_tableau.clear(); + m_left_basis_tableau.resize(this->m_A.column_count()); + m_left_basis_repeated = 0; + } + // stage1 constructor + lp_primal_core_solver( + static_matrix &A, + vector &b, // the right side vector + vector &x, // the number of elements in x needs to be at least as large + // as the number of columns in A + vector &basis, vector &nbasis, vector &heading, + vector &costs, const vector &column_type_array, + const vector &lower_bound_values, const vector &upper_bound_values, + lp_settings &settings, const column_namer &column_names) + : lp_core_solver_base(A, // b, + basis, nbasis, heading, x, costs, settings, + column_names, column_type_array, + lower_bound_values, upper_bound_values), m_bland_mode_threshold(1000) { + this->set_status(lp_status::UNKNOWN); + } - if (!(numeric_traits::precise())) { - m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); - } else { - m_converted_harris_eps = zero_of_type(); - } - this->set_status(lp_status::UNKNOWN); - } - - // constructor - lp_primal_core_solver(static_matrix & A, - vector & b, // the right side vector - vector & x, // the number of elements in x needs to be at least as large as the number of columns in A - vector & basis, - vector & nbasis, - vector & heading, - vector & costs, - const vector & column_type_array, - const vector & upper_bound_values, - lp_settings & settings, - const column_namer& column_names): - lp_core_solver_base(A, b, - basis, - nbasis, - heading, - x, - costs, - settings, - column_names, - column_type_array, - m_lower_bounds_dummy, - upper_bound_values), - m_beta(A.row_count()), - m_converted_harris_eps(convert_struct::convert(this->m_settings.harris_feasibility_tolerance)) { - lp_assert(initial_x_is_correct()); - m_lower_bounds_dummy.resize(A.column_count(), zero_of_type()); - m_enter_price_eps = numeric_traits::precise() ? numeric_traits::zero() : T(1e-5); -#ifdef Z3DEBUG - // check_correctness(); -#endif - } - - bool initial_x_is_correct() { - std::set basis_set; - for (unsigned i = 0; i < this->m_A.row_count(); i++) { - basis_set.insert(this->m_basis[i]); - } - for (unsigned j = 0; j < this->m_n(); j++) { - if (this->column_has_lower_bound(j) && this->m_x[j] < numeric_traits::zero()) { - LP_OUT(this->m_settings, "low bound for variable " << j << " does not hold: this->m_x[" << j << "] = " << this->m_x[j] << " is negative " << std::endl); - return false; - } - - if (this->column_has_upper_bound(j) && this->m_x[j] > this->m_upper_bounds[j]) { - LP_OUT(this->m_settings, "upper bound for " << j << " does not hold: " << this->m_upper_bounds[j] << ">" << this->m_x[j] << std::endl); - return false; - } - - if (basis_set.find(j) != basis_set.end()) continue; - if (this->m_column_types[j] == column_type::lower_bound) { - if (numeric_traits::zero() != this->m_x[j]) { - LP_OUT(this->m_settings, "only low bound is set for " << j << " but low bound value " << numeric_traits::zero() << " is not equal to " << this->m_x[j] << std::endl); - return false; - } - } - if (this->m_column_types[j] == column_type::boxed) { - if (this->m_upper_bounds[j] != this->m_x[j] && !numeric_traits::is_zero(this->m_x[j])) { - return false; - } - } - } - return true; - } - - - friend core_solver_pretty_printer; + friend core_solver_pretty_printer; }; -} +} // namespace lp diff --git a/src/math/lp/lp_primal_core_solver_def.h b/src/math/lp/lp_primal_core_solver_def.h index 3818b589a..c3c545fdd 100644 --- a/src/math/lp/lp_primal_core_solver_def.h +++ b/src/math/lp/lp_primal_core_solver_def.h @@ -26,28 +26,20 @@ Revision History: #include #include #include "math/lp/lp_primal_core_solver.h" +#include "math/lp/dense_matrix.h" namespace lp { // This core solver solves (Ax=b, lower_bound_values \leq x \leq upper_bound_values, maximize costs*x ) // The right side b is given implicitly by x and the basis template -void lp_primal_core_solver::sort_non_basis_rational() { - lp_assert(numeric_traits::precise()); - if (this->m_settings.use_tableau()) { - std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { +void lp_primal_core_solver::sort_non_basis() { + std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { unsigned ca = this->m_A.number_of_non_zeroes_in_column(a); unsigned cb = this->m_A.number_of_non_zeroes_in_column(b); if (ca == 0 && cb != 0) return false; return ca < cb; }); - } else { - std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { - unsigned ca = this->m_columns_nz[a]; - unsigned cb = this->m_columns_nz[b]; - if (ca == 0 && cb != 0) return false; - return ca < cb; - });} - + m_non_basis_list.clear(); // reinit m_basis_heading for (unsigned j = 0; j < this->m_nbasis.size(); j++) { @@ -58,102 +50,9 @@ void lp_primal_core_solver::sort_non_basis_rational() { } -template -void lp_primal_core_solver::sort_non_basis() { - if (numeric_traits::precise()) { - sort_non_basis_rational(); - return; - } - for (unsigned j : this->m_nbasis) { - T const & da = this->m_d[j]; - this->m_steepest_edge_coefficients[j] = da * da / this->m_column_norms[j]; - } - std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { - return this->m_steepest_edge_coefficients[a] > this->m_steepest_edge_coefficients[b]; - }); - m_non_basis_list.clear(); - // reinit m_basis_heading - for (unsigned j = 0; j < this->m_nbasis.size(); j++) { - unsigned col = this->m_nbasis[j]; - this->m_basis_heading[col] = - static_cast(j) - 1; - m_non_basis_list.push_back(col); - } -} - -template -bool lp_primal_core_solver::column_is_benefitial_for_entering_on_breakpoints(unsigned j) const { - bool ret; - const T & d = this->m_d[j]; - switch (this->m_column_types[j]) { - case column_type::lower_bound: - lp_assert(this->x_is_at_lower_bound(j)); - ret = d < -m_epsilon_of_reduced_cost; - break; - case column_type::upper_bound: - lp_assert(this->x_is_at_upper_bound(j)); - ret = d > m_epsilon_of_reduced_cost; - break; - case column_type::fixed: - ret = false; - break; - case column_type::boxed: - { - bool lower_bound = this->x_is_at_lower_bound(j); - lp_assert(lower_bound || this->x_is_at_upper_bound(j)); - ret = (lower_bound && d < -m_epsilon_of_reduced_cost) || ((!lower_bound) && d > m_epsilon_of_reduced_cost); - } - break; - case column_type::free_column: - ret = d > m_epsilon_of_reduced_cost || d < - m_epsilon_of_reduced_cost; - break; - default: - lp_unreachable(); - ret = false; - break; - } - return ret; -} template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsigned j) const { - if (numeric_traits::precise()) - return column_is_benefitial_for_entering_basis_precise(j); - if (this->using_infeas_costs() && this->m_settings.use_breakpoints_in_feasibility_search) - return column_is_benefitial_for_entering_on_breakpoints(j); - const T& dj = this->m_d[j]; - switch (this->m_column_types[j]) { - case column_type::fixed: break; - case column_type::free_column: - if (dj > m_epsilon_of_reduced_cost || dj < -m_epsilon_of_reduced_cost) - return true; - break; - case column_type::lower_bound: - if (dj > m_epsilon_of_reduced_cost) return true;; - break; - case column_type::upper_bound: - if (dj < -m_epsilon_of_reduced_cost) return true; - break; - case column_type::boxed: - if (dj > m_epsilon_of_reduced_cost) { - if (this->m_x[j] < this->m_upper_bounds[j] - this->bound_span(j)/2) - return true; - break; - } else if (dj < - m_epsilon_of_reduced_cost) { - if (this->m_x[j] > this->m_lower_bounds[j] + this->bound_span(j)/2) - return true; - } - break; - default: - lp_unreachable(); - break; - } - return false; -} -template -bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precise(unsigned j) const { - lp_assert (numeric_traits::precise()); - if (this->using_infeas_costs() && this->m_settings.use_breakpoints_in_feasibility_search) - return column_is_benefitial_for_entering_on_breakpoints(j); const T& dj = this->m_d[j]; TRACE("lar_solver", tout << "dj=" << dj << "\n";); switch (this->m_column_types[j]) { @@ -185,175 +84,12 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precis } break; default: - lp_unreachable(); + UNREACHABLE(); break; } return false; } -template -int lp_primal_core_solver::choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) - lp_assert(numeric_traits::precise()); - if (number_of_benefitial_columns_to_go_over == 0) - return -1; - if (this->m_basis_sort_counter == 0) { - sort_non_basis(); - this->m_basis_sort_counter = 20; - } - else { - this->m_basis_sort_counter--; - } - unsigned j_nz = this->m_m() + 1; // this number is greater than the max column size - std::list::iterator entering_iter = m_non_basis_list.end(); - for (auto non_basis_iter = m_non_basis_list.begin(); number_of_benefitial_columns_to_go_over && non_basis_iter != m_non_basis_list.end(); ++non_basis_iter) { - unsigned j = *non_basis_iter; - if (!column_is_benefitial_for_entering_basis(j)) - continue; - - // if we are here then j is a candidate to enter the basis - unsigned t = this->m_columns_nz[j]; - if (t < j_nz) { - j_nz = t; - entering_iter = non_basis_iter; - if (number_of_benefitial_columns_to_go_over) - number_of_benefitial_columns_to_go_over--; - } else if (t == j_nz && this->m_settings.random_next() % 2 == 0) { - entering_iter = non_basis_iter; - } - }// while (number_of_benefitial_columns_to_go_over && initial_offset_in_non_basis != offset_in_nb); - if (entering_iter == m_non_basis_list.end()) - return -1; - unsigned entering = *entering_iter; - m_sign_of_entering_delta = this->m_d[entering] > 0 ? 1 : -1; - if (this->using_infeas_costs() && this->m_settings.use_breakpoints_in_feasibility_search) - m_sign_of_entering_delta = -m_sign_of_entering_delta; - m_non_basis_list.erase(entering_iter); - m_non_basis_list.push_back(entering); - return entering; -} - - -template -int lp_primal_core_solver::choose_entering_column(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) - if (numeric_traits::precise()) - return choose_entering_column_presize(number_of_benefitial_columns_to_go_over); - if (number_of_benefitial_columns_to_go_over == 0) - return -1; - if (this->m_basis_sort_counter == 0) { - sort_non_basis(); - this->m_basis_sort_counter = 20; - } else { - this->m_basis_sort_counter--; - } - T steepest_edge = zero_of_type(); - std::list::iterator entering_iter = m_non_basis_list.end(); - for (auto non_basis_iter= m_non_basis_list.begin(); number_of_benefitial_columns_to_go_over && non_basis_iter != m_non_basis_list.end(); ++non_basis_iter) { - unsigned j = *non_basis_iter; - if (!column_is_benefitial_for_entering_basis(j)) - continue; - - // if we are here then j is a candidate to enter the basis - T dj = this->m_d[j]; - T t = dj * dj / this->m_column_norms[j]; - if (t > steepest_edge) { - steepest_edge = t; - entering_iter = non_basis_iter; - if (number_of_benefitial_columns_to_go_over) - number_of_benefitial_columns_to_go_over--; - } - }// while (number_of_benefitial_columns_to_go_over && initial_offset_in_non_basis != offset_in_nb); - if (entering_iter != m_non_basis_list.end()) { - unsigned entering = *entering_iter; - m_sign_of_entering_delta = this->m_d[entering] > 0? 1 : -1; - if (this->using_infeas_costs() && this->m_settings.use_breakpoints_in_feasibility_search) - m_sign_of_entering_delta = - m_sign_of_entering_delta; - m_non_basis_list.erase(entering_iter); - m_non_basis_list.push_back(entering); - return entering; - } - return -1; -} - -template int lp_primal_core_solver::advance_on_sorted_breakpoints(unsigned entering, X &t) { - T slope_at_entering = this->m_d[entering]; - breakpoint * last_bp = nullptr; - lp_assert(m_breakpoint_indices_queue.is_empty()==false); - while (m_breakpoint_indices_queue.is_empty() == false) { - unsigned bi = m_breakpoint_indices_queue.dequeue(); - breakpoint *b = &m_breakpoints[bi]; - change_slope_on_breakpoint(entering, b, slope_at_entering); - last_bp = b; - if (slope_at_entering * m_sign_of_entering_delta > - m_epsilon_of_reduced_cost) { // the slope started to increase infeasibility - break; - } else { - if ((numeric_traits::precise() == false) || ( numeric_traits::is_zero(slope_at_entering) && this->m_settings.random_next() % 2 == 0)) { - // it is not cost beneficial to advance the delta more, so just break to increase the randomness - break; - } - } - } - lp_assert (last_bp != nullptr); - t = last_bp->m_delta; - return last_bp->m_j; -} - - -template int -lp_primal_core_solver::find_leaving_and_t_with_breakpoints(unsigned entering, X & t){ - lp_assert(this->precise() == false); - fill_breakpoints_array(entering); - return advance_on_sorted_breakpoints(entering, t); -} - -template bool lp_primal_core_solver::get_harris_theta(X & theta) { - lp_assert(this->m_ed.is_OK()); - bool unlimited = true; - for (unsigned i : this->m_ed.m_index) { - if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue; - limit_theta_on_basis_column(this->m_basis[i], - this->m_ed[i] * m_sign_of_entering_delta, theta, unlimited); - if (!unlimited && is_zero(theta)) break; - } - return unlimited; -} - - -template int lp_primal_core_solver:: -find_leaving_on_harris_theta(X const & harris_theta, X & t) { - int leaving = -1; - T pivot_abs_max = zero_of_type(); - // we know already that there is no bound flip on entering - // we also know that harris_theta is limited, so we will find a leaving - zero_harris_eps(); - unsigned steps = this->m_ed.m_index.size(); - unsigned k = this->m_settings.random_next() % steps; - unsigned initial_k = k; - do { - unsigned i = this->m_ed.m_index[k]; - const T & ed = this->m_ed[i]; - if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(ed)) { - if (++k == steps) - k = 0; - continue; - } - X ratio; - unsigned j = this->m_basis[i]; - bool unlimited = true; - limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, ratio, unlimited); - if ((!unlimited) && ratio <= harris_theta) { - if (leaving == -1 || abs(ed) > pivot_abs_max) { - t = ratio; - leaving = j; - pivot_abs_max = abs(ed); - } - } - if (++k == steps) k = 0; - } while (k != initial_k); - if (!this->precise()) - restore_harris_eps(); - return leaving; -} - - template bool lp_primal_core_solver::try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, @@ -411,83 +147,6 @@ try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t ) { return true; } -template int lp_primal_core_solver::find_leaving_and_t_precise(unsigned entering, X & t) { - if (this->m_settings.use_breakpoints_in_feasibility_search && !this->current_x_is_feasible()) - return find_leaving_and_t_with_breakpoints(entering, t); - bool unlimited = true; - unsigned steps = this->m_ed.m_index.size(); - unsigned k = this->m_settings.random_next() % steps; - unsigned initial_k = k; - unsigned row_min_nz = this->m_n() + 1; - m_leaving_candidates.clear(); - do { - unsigned i = this->m_ed.m_index[k]; - const T & ed = this->m_ed[i]; - lp_assert(!numeric_traits::is_zero(ed)); - unsigned j = this->m_basis[i]; - limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, t, unlimited); - if (!unlimited) { - m_leaving_candidates.push_back(j); - row_min_nz = this->m_rows_nz[i]; - } - if (++k == steps) k = 0; - } while (unlimited && k != initial_k); - if (unlimited) { - if (try_jump_to_another_bound_on_entering_unlimited(entering, t)) - return entering; - return -1; - } - - X ratio; - while (k != initial_k) { - unsigned i = this->m_ed.m_index[k]; - const T & ed = this->m_ed[i]; - lp_assert(!numeric_traits::is_zero(ed)); - unsigned j = this->m_basis[i]; - unlimited = true; - limit_theta_on_basis_column(j, -ed * m_sign_of_entering_delta, ratio, unlimited); - if (unlimited) { - if (++k == steps) k = 0; - continue; - } - unsigned i_nz = this->m_rows_nz[i]; - if (ratio < t) { - t = ratio; - m_leaving_candidates.clear(); - m_leaving_candidates.push_back(j); - row_min_nz = this->m_rows_nz[i]; - } else if (ratio == t && i_nz < row_min_nz) { - m_leaving_candidates.clear(); - m_leaving_candidates.push_back(j); - row_min_nz = this->m_rows_nz[i]; - } else if (ratio == t && i_nz == row_min_nz) { - m_leaving_candidates.push_back(j); - } - if (++k == steps) k = 0; - } - - ratio = t; - unlimited = false; - if (try_jump_to_another_bound_on_entering(entering, t, ratio, unlimited)) { - t = ratio; - return entering; - } - k = this->m_settings.random_next() % m_leaving_candidates.size(); - return m_leaving_candidates[k]; -} - - -template int lp_primal_core_solver::find_leaving_and_t(unsigned entering, X & t) { - if (this->m_settings.use_breakpoints_in_feasibility_search && !this->current_x_is_feasible()) - return find_leaving_and_t_with_breakpoints(entering, t); - X theta = zero_of_type(); - bool unlimited = get_harris_theta(theta); - lp_assert(unlimited || theta >= zero_of_type()); - if (try_jump_to_another_bound_on_entering(entering, theta, t, unlimited)) return entering; - if (unlimited) - return -1; - return find_leaving_on_harris_theta(theta, t); -} @@ -538,15 +197,6 @@ lp_primal_core_solver::get_bound_on_variable_and_update_leaving_precisely( } } -template X lp_primal_core_solver::get_max_bound(vector & b) { - X ret = zero_of_type(); - for (auto & v : b) { - X a = abs(v); - if (a > ret) ret = a; - } - return ret; -} - #ifdef Z3DEBUG template void lp_primal_core_solver::check_Ax_equal_b() { dense_matrix d(this->m_A); @@ -571,272 +221,14 @@ template void lp_primal_core_solver::check_cor } #endif -// from page 183 of Istvan Maros's book -// the basis structures have not changed yet -template -void lp_primal_core_solver::update_reduced_costs_from_pivot_row(unsigned entering, unsigned leaving) { - // the basis heading has changed already -#ifdef Z3DEBUG - auto & basis_heading = this->m_basis_heading; - lp_assert(basis_heading[entering] >= 0 && static_cast(basis_heading[entering]) < this->m_m()); - lp_assert(basis_heading[leaving] < 0); -#endif - T pivot = this->m_pivot_row[entering]; - T dq = this->m_d[entering]/pivot; - for (auto j : this->m_pivot_row.m_index) { - // for (auto j : this->m_nbasis) - if (this->m_basis_heading[j] >= 0) continue; - if (j != leaving) - this->m_d[j] -= dq * this->m_pivot_row[j]; - } - this->m_d[leaving] = -dq; - if (this->current_x_is_infeasible() && !this->m_settings.use_breakpoints_in_feasibility_search) { - this->m_d[leaving] -= this->m_costs[leaving]; - this->m_costs[leaving] = zero_of_type(); - } - this->m_d[entering] = numeric_traits::zero(); -} - -// return 0 if the reduced cost at entering is close enough to the refreshed -// 1 if it is way off, and 2 if it is unprofitable -template int lp_primal_core_solver::refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering) { - if (numeric_traits::precise()) return 0; - T reduced_at_entering_was = this->m_d[entering]; // can benefit from going over non-zeros of m_ed - lp_assert(abs(reduced_at_entering_was) > m_epsilon_of_reduced_cost); - T refreshed_cost = this->m_costs[entering]; - unsigned i = this->m_m(); - while (i--) refreshed_cost -= this->m_costs[this->m_basis[i]] * this->m_ed[i]; - this->m_d[entering] = refreshed_cost; - T delta = abs(reduced_at_entering_was - refreshed_cost); - if (delta * 2 > abs(reduced_at_entering_was)) { - // this->m_status = UNSTABLE; - if (reduced_at_entering_was > m_epsilon_of_reduced_cost) { - if (refreshed_cost <= zero_of_type()) - return 2; // abort entering - } else { - if (refreshed_cost > -m_epsilon_of_reduced_cost) - return 2; // abort entering - } - return 1; // go on with this entering - } else { - if (reduced_at_entering_was > m_epsilon_of_reduced_cost) { - if (refreshed_cost <= zero_of_type()) - return 2; // abort entering - } else { - if (refreshed_cost > -m_epsilon_of_reduced_cost) - return 2; // abort entering - } - } - return 0; -} template void lp_primal_core_solver::backup_and_normalize_costs() { if (this->m_look_for_feasible_solution_only) return; // no need to backup cost, since we are going to use only feasibility costs - if (numeric_traits::precise() ) { - m_costs_backup = this->m_costs; - } else { - T cost_max = std::max(max_abs_in_vector(this->m_costs), T(1)); - lp_assert(m_costs_backup.size() == 0); - for (unsigned j = 0; j < this->m_costs.size(); j++) - m_costs_backup.push_back(this->m_costs[j] /= cost_max); - } -} - -template void lp_primal_core_solver::init_run() { - this->m_basis_sort_counter = 0; // to initiate the sort of the basis - // this->set_total_iterations(0); - this->iters_with_no_cost_growing() = 0; - init_inf_set(); - if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) - return; - this->set_using_infeas_costs(false); - if (this->m_settings.backup_costs) - backup_and_normalize_costs(); - m_epsilon_of_reduced_cost = numeric_traits::precise()? zero_of_type(): T(1)/T(10000000); - m_breakpoint_indices_queue.resize(this->m_n()); - init_reduced_costs(); - if (!numeric_traits::precise()) { - this->m_column_norm_update_counter = 0; - init_column_norms(); - } else { - if (this->m_columns_nz.size() != this->m_n()) - init_column_row_non_zeroes(); - } + m_costs_backup = this->m_costs; } -template void lp_primal_core_solver::calc_working_vector_beta_for_column_norms(){ - lp_assert(numeric_traits::precise() == false); - lp_assert(this->m_ed.is_OK()); - lp_assert(m_beta.is_OK()); - m_beta = this->m_ed; - this->m_factorization->solve_yB_with_error_check_indexed(m_beta, this->m_basis_heading, this->m_basis, this->m_settings); -} - -template -void lp_primal_core_solver::advance_on_entering_equal_leaving(int entering, X & t) { - CASSERT("A_off", !this->A_mult_x_is_off() ); - this->add_delta_to_entering(entering, t * m_sign_of_entering_delta); - if (this->A_mult_x_is_off_on_index(this->m_ed.m_index) && !this->find_x_by_solving()) { - this->init_lu(); - if (!this->find_x_by_solving()) { - this->restore_x(entering, t * m_sign_of_entering_delta); - this->iters_with_no_cost_growing()++; - LP_OUT(this->m_settings, "failing in advance_on_entering_equal_leaving for entering = " << entering << std::endl); - return; - } - } - if (this->using_infeas_costs()) { - lp_assert(is_zero(this->m_costs[entering])); - init_infeasibility_costs_for_changed_basis_only(); - } - if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) - return; - - if (need_to_switch_costs() ||!this->current_x_is_feasible()) { - init_reduced_costs(); - } - this->iters_with_no_cost_growing() = 0; -} - -template void lp_primal_core_solver::advance_on_entering_and_leaving(int entering, int leaving, X & t) { - lp_assert(entering >= 0 && m_non_basis_list.back() == static_cast(entering)); - lp_assert(this->using_infeas_costs() || t >= zero_of_type()); - lp_assert(leaving >= 0 && entering >= 0); - lp_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes - if (entering == leaving) { - advance_on_entering_equal_leaving(entering, t); - return; - } - unsigned pivot_row = this->m_basis_heading[leaving]; - this->calculate_pivot_row_of_B_1(pivot_row); - this->calculate_pivot_row_when_pivot_row_of_B1_is_ready(pivot_row); - - int pivot_compare_result = this->pivots_in_column_and_row_are_different(entering, leaving); - if (!pivot_compare_result){;} - else if (pivot_compare_result == 2) { // the sign is changed, cannot continue - this->set_status(lp_status::UNSTABLE); - this->iters_with_no_cost_growing()++; - return; - } else { - lp_assert(pivot_compare_result == 1); - this->init_lu(); - if (this->m_factorization == nullptr || this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::UNSTABLE); - this->iters_with_no_cost_growing()++; - return; - } - } - if (!numeric_traits::precise()) - calc_working_vector_beta_for_column_norms(); - if (this->current_x_is_feasible() || !this->m_settings.use_breakpoints_in_feasibility_search) { - if (m_sign_of_entering_delta == -1) - t = -t; - } - if (!this->update_basis_and_x(entering, leaving, t)) { - if (this->get_status() == lp_status::FLOATING_POINT_ERROR) - return; - if (this->m_look_for_feasible_solution_only) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return; - } - init_reduced_costs(); - return; - } - - if (!is_zero(t)) { - this->iters_with_no_cost_growing() = 0; - init_infeasibility_after_update_x_if_inf(leaving); - } - - if (this->current_x_is_feasible()) { - this->set_status(lp_status::FEASIBLE); - if (this->m_look_for_feasible_solution_only) - return; - } - if (numeric_traits::precise() == false) - update_or_init_column_norms(entering, leaving); - - - if (need_to_switch_costs()) { - init_reduced_costs(); - } else { - update_reduced_costs_from_pivot_row(entering, leaving); - } - lp_assert(!need_to_switch_costs()); - std::list::iterator it = m_non_basis_list.end(); - it--; - * it = static_cast(leaving); -} - - -template void lp_primal_core_solver::advance_on_entering_precise(int entering) { - lp_assert(numeric_traits::precise()); - lp_assert(entering > -1); - this->solve_Bd(entering); - X t; - int leaving = find_leaving_and_t_precise(entering, t); - if (leaving == -1) { - TRACE("lar_solver", tout << "non-leaving\n";); - this->set_status(lp_status::UNBOUNDED); - return; - } - advance_on_entering_and_leaving(entering, leaving, t); -} - -template void lp_primal_core_solver::advance_on_entering(int entering) { - if (numeric_traits::precise()) { - advance_on_entering_precise(entering); - return; - } - lp_assert(entering > -1); - this->solve_Bd(entering); - int refresh_result = refresh_reduced_cost_at_entering_and_check_that_it_is_off(entering); - if (refresh_result) { - if (this->m_look_for_feasible_solution_only) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return; - } - - this->init_lu(); - init_reduced_costs(); - if (refresh_result == 2) { - this->iters_with_no_cost_growing()++; - return; - } - } - X t; - int leaving = find_leaving_and_t(entering, t); - if (leaving == -1){ - if (!this->current_x_is_feasible()) { - lp_assert(!numeric_traits::precise()); // we cannot have unbounded with inf costs - - // if (m_look_for_feasible_solution_only) { - // this->m_status = INFEASIBLE; - // return; - // } - - - if (this->get_status() == lp_status::UNSTABLE) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return; - } - init_infeasibility_costs(); - this->set_status(lp_status::UNSTABLE); - - return; - } - if (this->get_status() == lp_status::TENTATIVE_UNBOUNDED) { - this->set_status(lp_status::UNBOUNDED); - } else { - this->set_status(lp_status::TENTATIVE_UNBOUNDED); - } - TRACE("lar_solver", tout << this->get_status() << "\n";); - return; - } - advance_on_entering_and_leaving(entering, leaving, t); -} template void lp_primal_core_solver::push_forward_offset_in_non_basis(unsigned & offset_in_nb) { if (++offset_in_nb == this->m_nbasis.size()) @@ -856,193 +248,7 @@ template unsigned lp_primal_core_solver::get_num return std::max(static_cast(this->m_settings.random_next() % ret), 1u); } -template void lp_primal_core_solver::print_column_norms(std::ostream & out) { - out << " column norms " << std::endl; - for (unsigned j = 0; j < this->m_n(); j++) { - out << this->m_column_norms[j] << " "; - } - out << std::endl; - } -// returns the number of iterations -template unsigned lp_primal_core_solver::solve() { - TRACE("lar_solver", tout << "solve " << this->get_status() << "\n";); - if (numeric_traits::precise() && this->m_settings.use_tableau()) - return solve_with_tableau(); - - init_run(); - if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) { - this->set_status(lp_status::FEASIBLE); - return 0; - } - - if ((!numeric_traits::precise()) && this->A_mult_x_is_off()) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return 0; - } - do { - if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->using_infeas_costs()? "inf" : "feas"), * this->m_settings.get_message_ostream())) { - return this->total_iterations(); - } - one_iteration(); - - TRACE("lar_solver", tout << "one iteration: " << this->get_status() << "\n";); - lp_assert(!this->using_infeas_costs() || this->costs_on_nbasis_are_zeros()); - switch (this->get_status()) { - case lp_status::OPTIMAL: // double check that we are at optimum - case lp_status::INFEASIBLE: - if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) - break; - if (!numeric_traits::precise()) { - if(this->m_look_for_feasible_solution_only) - break; - this->init_lu(); - - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status (lp_status::FLOATING_POINT_ERROR); - break; - } - init_reduced_costs(); - if (choose_entering_column(1) == -1) { - decide_on_status_when_cannot_find_entering(); - break; - } - this->set_status(lp_status::UNKNOWN); - } else { // precise case - if (this->m_look_for_feasible_solution_only) { // todo: keep the reduced costs correct all the time! - init_reduced_costs(); - if (choose_entering_column(1) == -1) { - decide_on_status_when_cannot_find_entering(); - break; - } - this->set_status(lp_status::UNKNOWN); - } - } - break; - case lp_status::TENTATIVE_UNBOUNDED: - this->init_lu(); - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - break; - } - - init_reduced_costs(); - break; - case lp_status::UNBOUNDED: - if (this->current_x_is_infeasible()) { - init_reduced_costs(); - this->set_status(lp_status::UNKNOWN); - } - break; - - case lp_status::UNSTABLE: - lp_assert(! (numeric_traits::precise())); - this->init_lu(); - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - break; - } - init_reduced_costs(); - break; - - default: - break; // do nothing - } - } while (this->get_status() != lp_status::FLOATING_POINT_ERROR - && - this->get_status() != lp_status::UNBOUNDED - && - this->get_status() != lp_status::OPTIMAL - && - this->get_status() != lp_status::INFEASIBLE - && - this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements - && - !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)); - - lp_assert(this->get_status() == lp_status::FLOATING_POINT_ERROR - || - this->current_x_is_feasible() == false - || - this->calc_current_x_is_feasible_include_non_basis()); - return this->total_iterations(); -} - -template void lp_primal_core_solver::delete_factorization() { - if (this->m_factorization != nullptr) { - delete this->m_factorization; - this->m_factorization = nullptr; - } -} - -// according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" -template void lp_primal_core_solver::init_column_norms() { - lp_assert(numeric_traits::precise() == false); - for (unsigned j = 0; j < this->m_n(); j++) { - this->m_column_norms[j] = T(static_cast(this->m_A.m_columns[j].size() + 1)) - - + T(static_cast(this->m_settings.random_next() % 10000)) / T(100000); - } -} - -// debug only -template T lp_primal_core_solver::calculate_column_norm_exactly(unsigned j) { - lp_assert(numeric_traits::precise() == false); - indexed_vector w(this->m_m()); - this->m_A.copy_column_to_vector(j, w); - vector d(this->m_m()); - this->m_factorization->solve_Bd_when_w_is_ready(d, w); - T ret = zero_of_type(); - for (auto v : d) - ret += v*v; - return ret+1; -} - -template void lp_primal_core_solver::update_or_init_column_norms(unsigned entering, unsigned leaving) { - lp_assert(numeric_traits::precise() == false); - lp_assert(m_column_norm_update_counter <= this->m_settings.column_norms_update_frequency); - if (m_column_norm_update_counter == this->m_settings.column_norms_update_frequency) { - m_column_norm_update_counter = 0; - init_column_norms(); - } else { - m_column_norm_update_counter++; - update_column_norms(entering, leaving); - } -} - -// following Swietanowski - A new steepest ... -template void lp_primal_core_solver::update_column_norms(unsigned entering, unsigned leaving) { - lp_assert(numeric_traits::precise() == false); - T pivot = this->m_pivot_row[entering]; - T g_ent = calculate_norm_of_entering_exactly() / pivot / pivot; - if (!numeric_traits::precise()) { - if (g_ent < T(0.000001)) - g_ent = T(0.000001); - } - this->m_column_norms[leaving] = g_ent; - - for (unsigned j : this->m_pivot_row.m_index) { - if (j == leaving) - continue; - const T & t = this->m_pivot_row[j]; - T s = this->m_A.dot_product_with_column(m_beta.m_data, j); - T k = -2 / pivot; - T tp = t/pivot; - if (this->m_column_types[j] != column_type::fixed) { // a fixed columns do not enter the basis, we don't use the norm of a fixed column - this->m_column_norms[j] = std::max(this->m_column_norms[j] + t * (t * g_ent + k * s), // see Istvan Maros, page 196 - 1 + tp * tp); - } - } -} - -template T lp_primal_core_solver::calculate_norm_of_entering_exactly() { - T r = numeric_traits::one(); - for (auto i : this->m_ed.m_index) { - T t = this->m_ed[i]; - r += t * t; - } - return r; -} // calling it stage1 is too cryptic template void lp_primal_core_solver::find_feasible_solution() { @@ -1052,177 +258,9 @@ template void lp_primal_core_solver::find_feas solve(); } -template void lp_primal_core_solver::one_iteration() { - unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter(); - int entering = choose_entering_column(number_of_benefitial_columns_to_go_over); - if (entering == -1) { - decide_on_status_when_cannot_find_entering(); - } - else { - advance_on_entering(entering); - } -} - -template void lp_primal_core_solver::update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta) { - if (entering != leaving) - this->update_basis_and_x(entering, leaving, delta); - else - this->update_x(entering, delta); -} - - -template void lp_primal_core_solver::clear_breakpoints() { - m_breakpoints.clear(); - m_breakpoint_indices_queue.clear(); -} - -template void lp_primal_core_solver::fill_breakpoints_array(unsigned entering) { - clear_breakpoints(); - for (unsigned i : this->m_ed.m_index) - try_add_breakpoint_in_row(i); - - if (this->m_column_types[entering] == column_type::boxed) { - if (m_sign_of_entering_delta < 0) - add_breakpoint(entering, - this->bound_span(entering), low_break); - else - add_breakpoint(entering, this->bound_span(entering), upper_break); - } -} -template bool lp_primal_core_solver::done() { - if (this->get_status() == lp_status::OPTIMAL || this->get_status() == lp_status::FLOATING_POINT_ERROR) return true; - if (this->get_status() == lp_status::INFEASIBLE) { - return true; - } - if (this->m_iters_with_no_cost_growing >= this->m_settings.max_number_of_iterations_with_no_improvements) { - this->set_status(lp_status::CANCELLED); - return true; - } - return false; -} - -template -void lp_primal_core_solver::init_infeasibility_costs_for_changed_basis_only() { - for (unsigned i : this->m_ed.m_index) - init_infeasibility_cost_for_column(this->m_basis[i]); - this->set_using_infeas_costs(true); -} - - -template -void lp_primal_core_solver::init_infeasibility_costs() { - lp_assert(this->m_x.size() >= this->m_n()); - lp_assert(this->m_column_types.size() >= this->m_n()); - for (unsigned j = this->m_n(); j--;) - init_infeasibility_cost_for_column(j); - this->set_using_infeas_costs(true); -} - -template T -lp_primal_core_solver::get_infeasibility_cost_for_column(unsigned j) const { - if (this->m_basis_heading[j] < 0) { - return zero_of_type(); - } - T ret; - // j is a basis column - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - if (this->x_above_upper_bound(j)) { - ret = 1; - } else if (this->x_below_low_bound(j)) { - ret = -1; - } else { - ret = numeric_traits::zero(); - } - break; - case column_type::lower_bound: - if (this->x_below_low_bound(j)) { - ret = -1; - } else { - ret = numeric_traits::zero(); - } - break; - case column_type::upper_bound: - if (this->x_above_upper_bound(j)) { - ret = 1; - } else { - ret = numeric_traits::zero(); - } - break; - case column_type::free_column: - ret = numeric_traits::zero(); - break; - default: - lp_assert(false); - ret = numeric_traits::zero(); // does not matter - break; - } - - if (!this->m_settings.use_breakpoints_in_feasibility_search) { - ret = - ret; - } - return ret; -} - - -// changed m_inf_set too! -template void -lp_primal_core_solver::init_infeasibility_cost_for_column(unsigned j) { - - // If j is a breakpoint column, then we set the cost zero. - // When anylyzing an entering column candidate we update the cost of the breakpoints columns to get the left or the right derivative if the infeasibility function - // set zero cost for each non-basis column - if (this->m_basis_heading[j] < 0) { - this->m_costs[j] = numeric_traits::zero(); - this->remove_column_from_inf_set(j); - return; - } - // j is a basis column - switch (this->m_column_types[j]) { - case column_type::fixed: - case column_type::boxed: - if (this->x_above_upper_bound(j)) { - this->m_costs[j] = 1; - } else if (this->x_below_low_bound(j)) { - this->m_costs[j] = -1; - } else { - this->m_costs[j] = numeric_traits::zero(); - } - break; - case column_type::lower_bound: - if (this->x_below_low_bound(j)) { - this->m_costs[j] = -1; - } else { - this->m_costs[j] = numeric_traits::zero(); - } - break; - case column_type::upper_bound: - if (this->x_above_upper_bound(j)) { - this->m_costs[j] = 1; - } else { - this->m_costs[j] = numeric_traits::zero(); - } - break; - case column_type::free_column: - this->m_costs[j] = numeric_traits::zero(); - break; - default: - lp_assert(false); - break; - } - - if (numeric_traits::is_zero(this->m_costs[j])) { - this->remove_column_from_inf_set(j); - } else { - this->insert_column_into_inf_set(j); - } - if (!this->m_settings.use_breakpoints_in_feasibility_search) { - this->m_costs[j] = - this->m_costs[j]; - } -} template void lp_primal_core_solver::print_column(unsigned j, std::ostream & out) { @@ -1242,131 +280,10 @@ template void lp_primal_core_solver::print_column out << "( _" << this->m_x[j] << "_)" << std::endl; break; default: - lp_unreachable(); + UNREACHABLE(); } } -template void lp_primal_core_solver::add_breakpoint(unsigned j, X delta, breakpoint_type type) { - m_breakpoints.push_back(breakpoint(j, delta, type)); - m_breakpoint_indices_queue.enqueue(m_breakpoint_indices_queue.size(), abs(delta)); -} - -// j is the basic column, x is the value at x[j] -// d is the coefficient before m_entering in the row with j as the basis column -template void lp_primal_core_solver::try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value) { - X diff = x - break_value; - if (is_zero(diff)) { - switch (break_type) { - case low_break: - if (!same_sign_with_entering_delta(d)) - return; // no breakpoint - break; - case upper_break: - if (same_sign_with_entering_delta(d)) - return; // no breakpoint - break; - default: break; - } - add_breakpoint(j, zero_of_type(), break_type); - return; - } - auto delta_j = diff / d; - if (same_sign_with_entering_delta(delta_j)) - add_breakpoint(j, delta_j, break_type); -} - -template std::string lp_primal_core_solver::break_type_to_string(breakpoint_type type) { - switch (type){ - case low_break: return "low_break"; - case upper_break: return "upper_break"; - case fixed_break: return "fixed_break"; - default: - lp_assert(false); - break; - } - return "type is not found"; -} - -template void lp_primal_core_solver::print_breakpoint(const breakpoint * b, std::ostream & out) { - out << "(" << this->column_name(b->m_j) << "," << break_type_to_string(b->m_type) << "," << T_to_string(b->m_delta) << ")" << std::endl; - print_bound_info_and_x(b->m_j, out); -} - -template -void lp_primal_core_solver::init_reduced_costs() { - lp_assert(!this->use_tableau()); - if (this->current_x_is_infeasible() && !this->using_infeas_costs()) { - init_infeasibility_costs(); - } else if (this->current_x_is_feasible() && this->using_infeas_costs()) { - if (this->m_look_for_feasible_solution_only) - return; - this->m_costs = m_costs_backup; - this->set_using_infeas_costs(false); - } - - this->init_reduced_costs_for_one_iteration(); -} - -template void lp_primal_core_solver::change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering) { - if (b->m_j == entering) { - lp_assert(b->m_type != fixed_break && (!is_zero(b->m_delta))); - slope_at_entering += m_sign_of_entering_delta; - return; - } - - lp_assert(this->m_basis_heading[b->m_j] >= 0); - unsigned i_row = this->m_basis_heading[b->m_j]; - const T & d = - this->m_ed[i_row]; - if (numeric_traits::is_zero(d)) return; - - T delta = m_sign_of_entering_delta * abs(d); - switch (b->m_type) { - case fixed_break: - if (is_zero(b->m_delta)) { - slope_at_entering += delta; - } else { - slope_at_entering += 2 * delta; - } - break; - case low_break: - case upper_break: - slope_at_entering += delta; - break; - default: - lp_assert(false); - } -} - - -template void lp_primal_core_solver::try_add_breakpoint_in_row(unsigned i) { - lp_assert(i < this->m_m()); - const T & d = this->m_ed[i]; // the coefficient before m_entering in the i-th row - if (d == 0) return; // the change of x[m_entering] will not change the corresponding basis x - unsigned j = this->m_basis[i]; - const X & x = this->m_x[j]; - switch (this->m_column_types[j]) { - case column_type::fixed: - try_add_breakpoint(j, x, d, fixed_break, this->m_lower_bounds[j]); - break; - case column_type::boxed: - try_add_breakpoint(j, x, d, low_break, this->m_lower_bounds[j]); - try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]); - break; - case column_type::lower_bound: - try_add_breakpoint(j, x, d, low_break, this->m_lower_bounds[j]); - break; - case column_type::upper_bound: - try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]); - break; - case column_type::free_column: - break; - default: - lp_assert(false); - break; - } -} - - template void lp_primal_core_solver::print_bound_info_and_x(unsigned j, std::ostream & out) { out << "type of " << this->column_name(j) << " is " << column_type_to_string(this->m_column_types[j]) << std::endl; out << "x[" << this->column_name(j) << "] = " << this->m_x[j] << std::endl; @@ -1385,7 +302,7 @@ template void lp_primal_core_solver::print_bound_ out << "inf, inf" << std::endl; break; default: - lp_assert(false); + UNREACHABLE(); break; } } diff --git a/src/math/lp/lp_primal_core_solver_tableau_def.h b/src/math/lp/lp_primal_core_solver_tableau_def.h index 46297a63e..898abb152 100644 --- a/src/math/lp/lp_primal_core_solver_tableau_def.h +++ b/src/math/lp/lp_primal_core_solver_tableau_def.h @@ -43,19 +43,11 @@ template void lp_primal_core_solver::advance_on_e } advance_on_entering_and_leaving_tableau(entering, leaving, t); } -/* -template int lp_primal_core_solver::choose_entering_column_tableau_rows() { - int i = find_inf_row(); - if (i == -1) - return -1; - return find_shortest_beneficial_column_in_row(i); - } -*/ + template int lp_primal_core_solver::choose_entering_column_tableau() { //this moment m_y = cB * B(-1) unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter(); - lp_assert(numeric_traits::precise()); if (number_of_benefitial_columns_to_go_over == 0) return -1; if (this->m_basis_sort_counter == 0) { @@ -88,31 +80,23 @@ template int lp_primal_core_solver::choose_enteri return -1; unsigned entering = *entering_iter; m_sign_of_entering_delta = this->m_d[entering] > 0 ? 1 : -1; - if (this->using_infeas_costs() && this->m_settings.use_breakpoints_in_feasibility_search) - m_sign_of_entering_delta = -m_sign_of_entering_delta; m_non_basis_list.erase(entering_iter); m_non_basis_list.push_back(entering); return entering; } - - - template -unsigned lp_primal_core_solver::solve_with_tableau() { +unsigned lp_primal_core_solver::solve() { + TRACE("lar_solver", tout << "solve " << this->get_status() << "\n";); init_run_tableau(); if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) { this->set_status(lp_status::FEASIBLE); return 0; } - if ((!numeric_traits::precise()) && this->A_mult_x_is_off()) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - return 0; - } do { - if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->using_infeas_costs()? "inf t" : "feas t"), * this->m_settings.get_message_ostream())) { + if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over( "feas t", * this->m_settings.get_message_ostream())) { return this->total_iterations(); } if (this->m_settings.use_tableau_rows()) { @@ -122,60 +106,17 @@ unsigned lp_primal_core_solver::solve_with_tableau() { } TRACE("lar_solver", tout << "one iteration tableau " << this->get_status() << "\n";); switch (this->get_status()) { - case lp_status::OPTIMAL: // double check that we are at optimum - case lp_status::INFEASIBLE: - if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) - break; - if (!numeric_traits::precise()) { - if(this->m_look_for_feasible_solution_only) - break; - this->init_lu(); - - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - break; - } - init_reduced_costs(); - if (choose_entering_column(1) == -1) { - decide_on_status_when_cannot_find_entering(); - break; - } - this->set_status(lp_status::UNKNOWN); - } else { // precise case - if ((!this->infeasibility_costs_are_correct())) { - init_reduced_costs_tableau(); // forcing recalc - if (choose_entering_column_tableau() == -1) { - decide_on_status_when_cannot_find_entering(); - break; - } - this->set_status(lp_status::UNKNOWN); - } - } + case lp_status::OPTIMAL: // check again that we are at optimum break; case lp_status::TENTATIVE_UNBOUNDED: - this->init_lu(); - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - break; - } - - init_reduced_costs(); + UNREACHABLE(); break; case lp_status::UNBOUNDED: - if (this->current_x_is_infeasible()) { - init_reduced_costs_tableau(); - this->set_status(lp_status::UNKNOWN); - } + lp_assert (this->current_x_is_feasible()); break; case lp_status::UNSTABLE: - lp_assert(! (numeric_traits::precise())); - this->init_lu(); - if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(lp_status::FLOATING_POINT_ERROR); - break; - } - init_reduced_costs(); + UNREACHABLE(); break; default: @@ -188,8 +129,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { this->set_status(lp_status::CANCELLED); break; // from the loop } - } while (this->get_status() != lp_status::FLOATING_POINT_ERROR - && + } while ( this->get_status() != lp_status::UNBOUNDED && this->get_status() != lp_status::OPTIMAL @@ -199,8 +139,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) ); - lp_assert(this->get_status() == lp_status::FLOATING_POINT_ERROR - || + lp_assert( this->get_status() == lp_status::CANCELLED || this->current_x_is_feasible() == false @@ -210,24 +149,22 @@ unsigned lp_primal_core_solver::solve_with_tableau() { } template void lp_primal_core_solver::advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t) { - CASSERT("A_off", this->A_mult_x_is_off() == false); lp_assert(leaving >= 0 && entering >= 0); lp_assert((this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) || m_non_basis_list.back() == static_cast(entering)); - lp_assert(this->using_infeas_costs() || !is_neg(t)); + lp_assert(!is_neg(t)); lp_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes if (entering == leaving) { advance_on_entering_equal_leaving_tableau(entering, t); return; } if (!is_zero(t)) { - if (this->current_x_is_feasible() || !this->m_settings.use_breakpoints_in_feasibility_search ) { + if (this->current_x_is_feasible() ) { if (m_sign_of_entering_delta == -1) t = -t; } this->update_basis_and_x_tableau(entering, leaving, t); - CASSERT("A_off", this->A_mult_x_is_off() == false); this->iters_with_no_cost_growing() = 0; } else { this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); @@ -238,11 +175,6 @@ template void lp_primal_core_solver::advance_on_en return; if (this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) { - if (need_to_switch_costs()) { - this->init_reduced_costs_tableau(); - } - - lp_assert(!need_to_switch_costs()); std::list::iterator it = m_non_basis_list.end(); it--; * it = static_cast(leaving); @@ -251,14 +183,11 @@ template void lp_primal_core_solver::advance_on_en template void lp_primal_core_solver::advance_on_entering_equal_leaving_tableau(int entering, X & t) { - CASSERT("A_off", !this->A_mult_x_is_off() ); this->update_x_tableau(entering, t * m_sign_of_entering_delta); if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) return; - if (need_to_switch_costs()) { - init_reduced_costs_tableau(); - } + this->iters_with_no_cost_growing() = 0; } template int lp_primal_core_solver::find_leaving_and_t_tableau(unsigned entering, X & t) { @@ -323,7 +252,6 @@ template int lp_primal_core_solver::find_leaving_ return m_leaving_candidates[k]; } template void lp_primal_core_solver::init_run_tableau() { - CASSERT("A_off", this->A_mult_x_is_off() == false); lp_assert(basis_columns_are_set_correctly()); this->m_basis_sort_counter = 0; // to initiate the sort of the basis // this->set_total_iterations(0); @@ -333,13 +261,7 @@ template void lp_primal_core_solver::init_run_tab return; if (this->m_settings.backup_costs) backup_and_normalize_costs(); - m_epsilon_of_reduced_cost = numeric_traits::precise() ? zero_of_type() : T(1) / T(10000000); - if (this->m_settings.use_breakpoints_in_feasibility_search) - m_breakpoint_indices_queue.resize(this->m_n()); - if (!numeric_traits::precise()) { - this->m_column_norm_update_counter = 0; - init_column_norms(); - } + if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) init_tableau_rows(); lp_assert(this->reduced_costs_are_correct_tableau()); @@ -348,62 +270,25 @@ template void lp_primal_core_solver::init_run_tab template bool lp_primal_core_solver:: update_basis_and_x_tableau(int entering, int leaving, X const & tt) { - lp_assert(this->use_tableau()); lp_assert(entering != leaving); update_x_tableau(entering, tt); this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); this->change_basis(entering, leaving); return true; } + template void lp_primal_core_solver:: update_x_tableau(unsigned entering, const X& delta) { this->add_delta_to_x(entering, delta); - if (!this->using_infeas_costs()) { - for (const auto & c : this->m_A.m_columns[entering]) { - unsigned i = c.var(); - this->add_delta_to_x_and_track_feasibility(this->m_basis[i], - delta * this->m_A.get_val(c)); - } - } else { // using_infeas_costs() == true - lp_assert(this->column_is_feasible(entering)); - lp_assert(this->m_costs[entering] == zero_of_type()); - // m_d[entering] can change because of the cost change for basic columns. - for (const auto & c : this->m_A.m_columns[entering]) { - unsigned i = c.var(); - unsigned j = this->m_basis[i]; - this->add_delta_to_x(j, -delta * this->m_A.get_val(c)); - update_inf_cost_for_column_tableau(j); - if (is_zero(this->m_costs[j])) - this->remove_column_from_inf_set(j); - else - this->insert_column_into_inf_set(j); - } + for (const auto & c : this->m_A.m_columns[entering]) { + unsigned i = c.var(); + this->add_delta_to_x_and_track_feasibility(this->m_basis[i], - delta * this->m_A.get_val(c)); } - CASSERT("A_off", this->A_mult_x_is_off() == false); } -template void lp_primal_core_solver:: -update_inf_cost_for_column_tableau(unsigned j) { - lp_assert(this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows); - - lp_assert(this->using_infeas_costs()); - - T new_cost = get_infeasibility_cost_for_column(j); - T delta = this->m_costs[j] - new_cost; - if (is_zero(delta)) - return; - this->m_costs[j] = new_cost; - update_reduced_cost_for_basic_column_cost_change(delta, j); -} template void lp_primal_core_solver::init_reduced_costs_tableau() { - if (this->current_x_is_infeasible() && !this->using_infeas_costs()) { - init_infeasibility_costs(); - } else if (this->current_x_is_feasible() && this->using_infeas_costs()) { - if (this->m_look_for_feasible_solution_only) - return; - this->m_costs = m_costs_backup; - this->set_using_infeas_costs(false); - } + unsigned size = this->m_basis_heading.size(); for (unsigned j = 0; j < size; j++) { if (this->m_basis_heading[j] >= 0) diff --git a/src/math/lp/lp_primal_simplex.cpp b/src/math/lp/lp_primal_simplex.cpp deleted file mode 100644 index 634f52900..000000000 --- a/src/math/lp/lp_primal_simplex.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include -#include -#include "util/vector.h" -#include -#include "math/lp/lp_primal_simplex_def.h" -template bool lp::lp_primal_simplex::bounds_hold(std::unordered_map, std::equal_to, std::allocator > > const&); -template bool lp::lp_primal_simplex::row_constraints_hold(std::unordered_map, std::equal_to, std::allocator > > const&); -template double lp::lp_primal_simplex::get_current_cost() const; -template double lp::lp_primal_simplex::get_column_value(unsigned int) const; -template lp::lp_primal_simplex::~lp_primal_simplex(); -template lp::lp_primal_simplex::~lp_primal_simplex(); -template lp::mpq lp::lp_primal_simplex::get_current_cost() const; -template lp::mpq lp::lp_primal_simplex::get_column_value(unsigned int) const; -template void lp::lp_primal_simplex::find_maximal_solution(); -template void lp::lp_primal_simplex::find_maximal_solution(); diff --git a/src/math/lp/lp_primal_simplex.h b/src/math/lp/lp_primal_simplex.h deleted file mode 100644 index 77e12d088..000000000 --- a/src/math/lp/lp_primal_simplex.h +++ /dev/null @@ -1,106 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/vector.h" -#include -#include -#include -#include "math/lp/lp_utils.h" -#include "math/lp/column_info.h" -#include "math/lp/lp_primal_core_solver.h" -#include "math/lp/lp_solver.h" -namespace lp { -template -class lp_primal_simplex: public lp_solver { - lp_primal_core_solver * m_core_solver; - vector m_lower_bounds; -private: - unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); } - - void fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns); - - void init_buffer(unsigned k, vector & r); - - void refactor(); - - void set_scaled_costs(); -public: - lp_primal_simplex(): m_core_solver(nullptr) {} - - column_info * get_or_create_column_info(unsigned column); - - void set_status(lp_status status) { - this->m_status = status; - } - - lp_status get_status() { - return this->m_status; - } - - void fill_acceptable_values_for_x(); - - - void set_zero_bound(bool * bound_is_set, T * bounds, unsigned i); - - void fill_costs_and_x_for_first_stage_solver_for_row( - int row, - unsigned & slack_var, - unsigned & artificial); - - - - - void set_core_solver_bounds(); - - void find_maximal_solution() override; - - void fill_A_x_and_basis_for_stage_one_total_inf(); - - void fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row); - - void solve_with_total_inf(); - - - ~lp_primal_simplex() override; - - bool bounds_hold(std::unordered_map const & solution); - - T get_row_value(unsigned i, std::unordered_map const & solution, std::ostream * out); - - bool row_constraint_holds(unsigned i, std::unordered_map const & solution, std::ostream * out); - - bool row_constraints_hold(std::unordered_map const & solution); - - - T * get_array_from_map(std::unordered_map const & solution); - - bool solution_is_feasible(std::unordered_map const & solution) { - return bounds_hold(solution) && row_constraints_hold(solution); - } - - T get_column_value(unsigned column) const override { - return this->get_column_value_with_core_solver(column, m_core_solver); - } - - T get_current_cost() const override; - - -}; -} diff --git a/src/math/lp/lp_primal_simplex_def.h b/src/math/lp/lp_primal_simplex_def.h deleted file mode 100644 index 7ffe819b2..000000000 --- a/src/math/lp/lp_primal_simplex_def.h +++ /dev/null @@ -1,367 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include "util/vector.h" -#include "math/lp/lp_primal_simplex.h" - -namespace lp { -template void lp_primal_simplex::fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns) { - unsigned slack_var = original_number_of_columns; - unsigned artificial = original_number_of_columns + this->m_slacks; - - for (unsigned row = 0; row < this->row_count(); row++) { - fill_costs_and_x_for_first_stage_solver_for_row(row, slack_var, artificial); - } -} - -template void lp_primal_simplex::init_buffer(unsigned k, vector & r) { - for (unsigned i = 0; i < k; i++) { - r[i] = 0; - } - r[k] = 1; - for (unsigned i = this->row_count() -1; i > k; i--) { - r[i] = 0; - } -} - -template void lp_primal_simplex::refactor() { - m_core_solver->init_lu(); - if (m_core_solver->factorization()->get_status() != LU_status::OK) { - throw_exception("cannot refactor"); - } -} - -template void lp_primal_simplex::set_scaled_costs() { - unsigned j = this->number_of_core_structurals(); - while (j-- > 0) { - this->set_scaled_cost(j); - } -} - -template column_info * lp_primal_simplex::get_or_create_column_info(unsigned column) { - auto it = this->m_columns.find(column); - return (it == this->m_columns.end())? ( this->m_columns[column] = new column_info) : it->second; -} - -template void lp_primal_simplex::fill_acceptable_values_for_x() { - for (auto t : this->m_core_solver_columns_to_external_columns) { - this->m_x[t.first] = numeric_traits::zero(); - } -} - - -template void lp_primal_simplex::set_zero_bound(bool * bound_is_set, T * bounds, unsigned i) { - bound_is_set[i] = true; - bounds[i] = numeric_traits::zero(); -} - -template void lp_primal_simplex::fill_costs_and_x_for_first_stage_solver_for_row( - int row, - unsigned & slack_var, - unsigned & artificial) { - lp_assert(row >= 0 && row < this->row_count()); - auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; - // we need to bring the program to the form Ax = b - T rs = this->m_b[row]; - T artificial_cost = - numeric_traits::one(); - switch (constraint.m_relation) { - case Equal: // no slack variable here - this->m_column_types[artificial] = column_type::lower_bound; - this->m_costs[artificial] = artificial_cost; // we are maximizing, so the artificial, which is non-negatiive, will be pushed to zero - this->m_basis[row] = artificial; - if (rs >= 0) { - (*this->m_A)(row, artificial) = numeric_traits::one(); - this->m_x[artificial] = rs; - } else { - (*this->m_A)(row, artificial) = - numeric_traits::one(); - this->m_x[artificial] = - rs; - } - artificial++; - break; - - case Greater_or_equal: - this->m_column_types[slack_var] = column_type::lower_bound; - (*this->m_A)(row, slack_var) = - numeric_traits::one(); - - if (rs > 0) { - lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); - // adding one artificial - this->m_column_types[artificial] = column_type::lower_bound; - (*this->m_A)(row, artificial) = numeric_traits::one(); - this->m_costs[artificial] = artificial_cost; - this->m_basis[row] = artificial; - this->m_x[artificial] = rs; - artificial++; - } else { - // we can put a slack_var into the basis, and atemplate void lp_primal_simplex::adding an artificial variable - this->m_basis[row] = slack_var; - this->m_x[slack_var] = - rs; - } - slack_var++; - break; - case Less_or_equal: - // introduce a non-negative slack variable - this->m_column_types[slack_var] = column_type::lower_bound; - (*this->m_A)(row, slack_var) = numeric_traits::one(); - - if (rs < 0) { - // adding one artificial - lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); - this->m_column_types[artificial] = column_type::lower_bound; - (*this->m_A)(row, artificial) = - numeric_traits::one(); - this->m_costs[artificial] = artificial_cost; - this->m_x[artificial] = - rs; - this->m_basis[row] = artificial++; - } else { - // we can put slack_var into the basis, and atemplate void lp_primal_simplex::adding an artificial variable - this->m_basis[row] = slack_var; - this->m_x[slack_var] = rs; - } - slack_var++; - break; - } -} - - - - - -template void lp_primal_simplex::set_core_solver_bounds() { - unsigned total_vars = this->m_A->column_count() + this->m_slacks + this->m_artificials; - this->m_column_types.resize(total_vars); - this->m_upper_bounds.resize(total_vars); - for (auto cit : this->m_map_from_var_index_to_column_info) { - column_info * ci = cit.second; - unsigned j = ci->get_column_index(); - if (!is_valid(j)) - continue; // the variable is not mapped to a column - switch (this->m_column_types[j] = ci->get_column_type()){ - case column_type::fixed: - this->m_upper_bounds[j] = numeric_traits::zero(); - break; - case column_type::boxed: - this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; - break; - - default: break; // do nothing - } - } -} - - -template void lp_primal_simplex::find_maximal_solution() { - if (this->problem_is_empty()) { - this->m_status = lp_status::EMPTY; - return; - } - - this->cleanup(); - this->fill_matrix_A_and_init_right_side(); - if (this->m_status == lp_status::INFEASIBLE) { - return; - } - this->m_x.resize(this->m_A->column_count()); - this->fill_m_b(); - this->scale(); - fill_acceptable_values_for_x(); - this->count_slacks_and_artificials(); - set_core_solver_bounds(); - solve_with_total_inf(); -} - -template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf() { - for (unsigned row = 0; row < this->row_count(); row++) - fill_A_x_and_basis_for_stage_one_total_inf_for_row(row); -} - -template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row) { - lp_assert(row < this->row_count()); - auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row); - lp_assert(ext_row_it != this->m_core_solver_rows_to_external_rows.end()); - unsigned ext_row = ext_row_it->second; - auto constr_it = this->m_constraints.find(ext_row); - lp_assert(constr_it != this->m_constraints.end()); - auto & constraint = constr_it->second; - unsigned j = this->m_A->column_count(); // j is a slack variable - this->m_A->add_column(); - // we need to bring the program to the form Ax = b - this->m_basis[row] = j; - switch (constraint.m_relation) { - case Equal: - this->m_x[j] = this->m_b[row]; - (*this->m_A)(row, j) = numeric_traits::one(); - this->m_column_types[j] = column_type::fixed; - this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); - break; - - case Greater_or_equal: - this->m_x[j] = - this->m_b[row]; - (*this->m_A)(row, j) = - numeric_traits::one(); - this->m_column_types[j] = column_type::lower_bound; - this->m_upper_bounds[j] = zero_of_type(); - break; - case Less_or_equal: - this->m_x[j] = this->m_b[row]; - (*this->m_A)(row, j) = numeric_traits::one(); - this->m_column_types[j] = column_type::lower_bound; - this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); - break; - default: - lp_unreachable(); - } -} - -template void lp_primal_simplex::solve_with_total_inf() { - int total_vars = this->m_A->column_count() + this->row_count(); - if (total_vars == 0) { - this->m_status = lp_status::OPTIMAL; - return; - } - m_lower_bounds.clear(); - m_lower_bounds.resize(total_vars, zero_of_type()); // low bounds are shifted ot zero - this->m_x.resize(total_vars, numeric_traits::zero()); - this->m_basis.resize(this->row_count()); - this->m_costs.clear(); - this->m_costs.resize(total_vars, zero_of_type()); - fill_A_x_and_basis_for_stage_one_total_inf(); - if (this->m_settings.get_message_ostream() != nullptr) - this->print_statistics_on_A(*this->m_settings.get_message_ostream()); - set_scaled_costs(); - - m_core_solver = new lp_primal_core_solver(*this->m_A, - this->m_b, - this->m_x, - this->m_basis, - this->m_nbasis, - this->m_heading, - this->m_costs, - this->m_column_types, - m_lower_bounds, - this->m_upper_bounds, - this->m_settings, *this); - m_core_solver->solve(); - this->set_status(m_core_solver->get_status()); - this->m_total_iterations = m_core_solver->total_iterations(); -} - - -template lp_primal_simplex::~lp_primal_simplex() { - delete m_core_solver; -} - -template bool lp_primal_simplex::bounds_hold(std::unordered_map const & solution) { - for (auto it : this->m_map_from_var_index_to_column_info) { - auto sol_it = solution.find(it.second->get_name()); - if (sol_it == solution.end()) { - std::stringstream s; - s << "cannot find column " << it.first << " in solution"; - throw_exception(s.str() ); - } - - if (!it.second->bounds_hold(sol_it->second)) { - it.second->bounds_hold(sol_it->second); - return false; - } - } - return true; -} - -template T lp_primal_simplex::get_row_value(unsigned i, std::unordered_map const & solution, std::ostream * out) { - auto it = this->m_A_values.find(i); - if (it == this->m_A_values.end()) { - std::stringstream s; - s << "cannot find row " << i; - throw_exception(s.str() ); - } - T ret = numeric_traits::zero(); - for (auto & pair : it->second) { - auto cit = this->m_map_from_var_index_to_column_info.find(pair.first); - lp_assert(cit != this->m_map_from_var_index_to_column_info.end()); - column_info * ci = cit->second; - auto sol_it = solution.find(ci->get_name()); - lp_assert(sol_it != solution.end()); - T column_val = sol_it->second; - if (out != nullptr) { - (*out) << pair.second << "(" << ci->get_name() << "=" << column_val << ") "; - } - ret += pair.second * column_val; - } - if (out != nullptr) { - (*out) << " = " << ret << std::endl; - } - return ret; -} - -template bool lp_primal_simplex::row_constraint_holds(unsigned i, std::unordered_map const & solution, std::ostream *out) { - T row_val = get_row_value(i, solution, out); - auto & constraint = this->m_constraints[i]; - T rs = constraint.m_rs; - bool print = out != nullptr; - switch (constraint.m_relation) { - case Equal: - if (fabs(numeric_traits::get_double(row_val - rs)) > 0.00001) { - if (print) { - (*out) << "should be = " << rs << std::endl; - } - return false; - } - return true; - case Greater_or_equal: - if (numeric_traits::get_double(row_val - rs) < -0.00001) { - if (print) { - (*out) << "should be >= " << rs << std::endl; - } - return false; - } - return true;; - - case Less_or_equal: - if (numeric_traits::get_double(row_val - rs) > 0.00001) { - if (print) { - (*out) << "should be <= " << rs << std::endl; - } - return false; - } - return true;; - } - lp_unreachable(); - return false; // it is unreachable -} - -template bool lp_primal_simplex::row_constraints_hold(std::unordered_map const & solution) { - for (auto it : this->m_A_values) { - if (!row_constraint_holds(it.first, solution, nullptr)) { - row_constraint_holds(it.first, solution, nullptr); - return false; - } - } - return true; -} - -template T lp_primal_simplex::get_current_cost() const { - T ret = numeric_traits::zero(); - for (auto it : this->m_map_from_var_index_to_column_info) { - ret += this->get_column_cost_value(it.first, it.second); - } - return ret; -} -} diff --git a/src/math/lp/lp_settings.cpp b/src/math/lp/lp_settings.cpp index 592a98983..b72b837fd 100644 --- a/src/math/lp/lp_settings.cpp +++ b/src/math/lp/lp_settings.cpp @@ -21,7 +21,6 @@ Revision History: #include "util/vector.h" #include "smt/params/smt_params_helper.hpp" #include "math/lp/lp_settings_def.h" -template bool lp::vectors_are_equal(vector const&, vector const&); template bool lp::vectors_are_equal(vector const&, vector const&); void lp::lp_settings::updt_params(params_ref const& _p) { diff --git a/src/math/lp/lp_settings.h b/src/math/lp/lp_settings.h index 2245f6f4e..c213333e0 100644 --- a/src/math/lp/lp_settings.h +++ b/src/math/lp/lp_settings.h @@ -55,8 +55,7 @@ inline std::ostream& operator<<(std::ostream& out, column_type const& t) { enum class simplex_strategy_enum { undecided = 3, tableau_rows = 0, - tableau_costs = 1, - lu = 2 + tableau_costs = 1 }; std::string column_type_to_string(column_type t); @@ -70,7 +69,6 @@ enum class lp_status { DUAL_UNBOUNDED, OPTIMAL, FEASIBLE, - FLOATING_POINT_ERROR, TIME_EXHAUSTED, EMPTY, UNSTABLE, @@ -80,9 +78,8 @@ enum class lp_status { // when the ratio of the vector length to domain size to is greater than the return value we switch to solve_By_for_T_indexed_only template unsigned ratio_of_index_size_to_all_size() { - if (numeric_traits::precise()) return 10; - return 120; + } const char* lp_status_to_string(lp_status status); @@ -93,9 +90,6 @@ inline std::ostream& operator<<(std::ostream& out, lp_status status) { lp_status lp_status_from_string(std::string status); -enum non_basic_column_value_position { at_lower_bound, at_upper_bound, at_fixed, free_of_bounds, not_at_bound }; - -template bool is_epsilon_small(const X & v, const double& eps); // forward definition class lp_resource_limit { public: @@ -127,6 +121,7 @@ struct statistics { unsigned m_grobner_calls; unsigned m_grobner_conflicts; unsigned m_offset_eqs; + unsigned m_fixed_eqs; statistics() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } void collect_statistics(::statistics& st) const { @@ -148,6 +143,7 @@ struct statistics { st.update("arith-grobner-calls", m_grobner_calls); st.update("arith-grobner-conflicts", m_grobner_conflicts); st.update("arith-offset-eqs", m_offset_eqs); + st.update("arith-fixed-eqs", m_fixed_eqs); } }; @@ -167,11 +163,11 @@ private: }; default_lp_resource_limit m_default_resource_limit; - lp_resource_limit* m_resource_limit; + lp_resource_limit* m_resource_limit = nullptr; // used for debug output - std::ostream* m_debug_out; + std::ostream* m_debug_out = nullptr; // used for messages, for example, the computation progress messages - std::ostream* m_message_out; + std::ostream* m_message_out = nullptr; statistics m_stats; random_gen m_rand; @@ -182,66 +178,40 @@ public: unsigned nlsat_delay() const { return m_nlsat_delay; } bool int_run_gcd_test() const { return m_int_run_gcd_test; } bool& int_run_gcd_test() { return m_int_run_gcd_test; } - unsigned reps_in_scaler { 20 }; - // when the absolute value of an element is less than pivot_epsilon - // in pivoting, we treat it as a zero - double pivot_epsilon { 0.00000001 }; - // see Chatal, page 115 - double positive_price_epsilon { 1e-7 }; - // a quotation "if some choice of the entering variable leads to an eta matrix - // whose diagonal element in the eta column is less than e2 (entering_diag_epsilon) in magnitude, the this choice is rejected ... - double entering_diag_epsilon { 1e-8 }; - int c_partial_pivoting { 10 }; // this is the constant c from page 410 - unsigned depth_of_rook_search { 4 }; - bool using_partial_pivoting { true }; - // dissertation of Achim Koberstein - // if Bx - b is different at any component more that refactor_epsilon then we refactor - double refactor_tolerance { 1e-4 }; - double pivot_tolerance { 1e-6 }; - double zero_tolerance { 1e-12 }; - double drop_tolerance { 1e-14 }; - double tolerance_for_artificials { 1e-4 }; - double can_be_taken_to_basis_tolerance { 0.00001 }; - - unsigned percent_of_entering_to_check { 5 }; // we try to find a profitable column in a percentage of the columns - bool use_scaling { true }; - double scaling_maximum { 1.0 }; - double scaling_minimum { 0.5 }; - double harris_feasibility_tolerance { 1e-7 }; // page 179 of Istvan Maros - double ignore_epsilon_of_harris { 10e-5 }; - unsigned max_number_of_iterations_with_no_improvements { 2000000 }; - double time_limit; // the maximum time limit of the total run time in seconds - // dual section - double dual_feasibility_tolerance { 1e-7 }; // page 71 of the PhD thesis of Achim Koberstein - double primal_feasibility_tolerance { 1e-7 }; // page 71 of the PhD thesis of Achim Koberstein - double relative_primal_feasibility_tolerance { 1e-9 }; // page 71 of the PhD thesis of Achim Koberstein + unsigned reps_in_scaler = 20; + int c_partial_pivoting = 10; // this is the constant c from page 410 + unsigned depth_of_rook_search = 4; + bool using_partial_pivoting = true; + + unsigned percent_of_entering_to_check = 5; // we try to find a profitable column in a percentage of the columns + bool use_scaling = true; + unsigned max_number_of_iterations_with_no_improvements = 2000000; + double time_limit; // the maximum time limit of the total run time in seconds // end of dual section - bool m_bound_propagation { true }; - bool presolve_with_double_solver_for_lar { true }; + bool m_bound_propagation = true; + bool presolve_with_double_solver_for_lar = true; simplex_strategy_enum m_simplex_strategy; - int report_frequency { 1000 }; - bool print_statistics { false }; - unsigned column_norms_update_frequency { 12000 }; - bool scale_with_ratio { true }; - double density_threshold { 0.7 }; - bool use_breakpoints_in_feasibility_search { false }; - unsigned max_row_length_for_bound_propagation { 300 }; - bool backup_costs { true }; - unsigned column_number_threshold_for_using_lu_in_lar_solver { 4000 }; - unsigned m_int_gomory_cut_period { 4 }; - unsigned m_int_find_cube_period { 4 }; + int report_frequency = 1000; + bool print_statistics = false; + unsigned column_norms_update_frequency = 12000; + bool scale_with_ratio = true; + unsigned max_row_length_for_bound_propagation = 300; + bool backup_costs = true; + unsigned column_number_threshold_for_using_lu_in_lar_solver = 4000; + unsigned m_int_gomory_cut_period = 4; + unsigned m_int_find_cube_period = 4; private: - unsigned m_hnf_cut_period { 4 }; - bool m_int_run_gcd_test { true }; + unsigned m_hnf_cut_period = 4; + bool m_int_run_gcd_test = true; public: - unsigned limit_on_rows_for_hnf_cutter { 75 }; - unsigned limit_on_columns_for_hnf_cutter { 150 }; + unsigned limit_on_rows_for_hnf_cutter = 75; + unsigned limit_on_columns_for_hnf_cutter = 150; private: unsigned m_nlsat_delay; - bool m_enable_hnf { true }; - bool m_print_external_var_name { false }; - bool m_propagate_eqs { false }; + bool m_enable_hnf = true; + bool m_print_external_var_name = false; + bool m_propagate_eqs = false; public: bool print_external_var_name() const { return m_print_external_var_name; } bool propagate_eqs() const { return m_propagate_eqs;} @@ -274,84 +244,12 @@ public: std::ostream* get_debug_ostream() { return m_debug_out; } std::ostream* get_message_ostream() { return m_message_out; } statistics& stats() { return m_stats; } - statistics const& stats() const { return m_stats; } - - template static bool is_eps_small_general(const T & t, const double & eps) { - return (!numeric_traits::precise())? is_epsilon_small(t, eps) : numeric_traits::is_zero(t); - } - - template - bool abs_val_is_smaller_than_dual_feasibility_tolerance(T const & t) { - return is_eps_small_general(t, dual_feasibility_tolerance); - } - - template - bool abs_val_is_smaller_than_primal_feasibility_tolerance(T const & t) { - return is_eps_small_general(t, primal_feasibility_tolerance); - } - - template - bool abs_val_is_smaller_than_can_be_taken_to_basis_tolerance(T const & t) { - return is_eps_small_general(t, can_be_taken_to_basis_tolerance); - } - - template - bool abs_val_is_smaller_than_drop_tolerance(T const & t) const { - return is_eps_small_general(t, drop_tolerance); - } - - - template - bool abs_val_is_smaller_than_zero_tolerance(T const & t) { - return is_eps_small_general(t, zero_tolerance); - } - - template - bool abs_val_is_smaller_than_refactor_tolerance(T const & t) { - return is_eps_small_general(t, refactor_tolerance); - } - - - template - bool abs_val_is_smaller_than_pivot_tolerance(T const & t) { - return is_eps_small_general(t, pivot_tolerance); - } - - template - bool abs_val_is_smaller_than_harris_tolerance(T const & t) { - return is_eps_small_general(t, harris_feasibility_tolerance); - } - - template - bool abs_val_is_smaller_than_ignore_epslilon_for_harris(T const & t) { - return is_eps_small_general(t, ignore_epsilon_of_harris); - } - - template - bool abs_val_is_smaller_than_artificial_tolerance(T const & t) { - return is_eps_small_general(t, tolerance_for_artificials); - } + statistics const& stats() const { return m_stats; } + // the method of lar solver to use - simplex_strategy_enum simplex_strategy() const { - return m_simplex_strategy; - } - - void set_simplex_strategy(simplex_strategy_enum s) { - m_simplex_strategy = s; - } - - bool use_lu() const { - return m_simplex_strategy == simplex_strategy_enum::lu; - } - - bool use_tableau() const { - return m_simplex_strategy == simplex_strategy_enum::tableau_rows || - m_simplex_strategy == simplex_strategy_enum::tableau_costs; - } - - bool use_tableau_rows() const { - return m_simplex_strategy == simplex_strategy_enum::tableau_rows; - } + simplex_strategy_enum simplex_strategy() const { return m_simplex_strategy; } + void set_simplex_strategy(simplex_strategy_enum s) { m_simplex_strategy = s; } + bool use_tableau_rows() const { return m_simplex_strategy == simplex_strategy_enum::tableau_rows; } #ifdef Z3DEBUG static unsigned ddd; // used for debugging @@ -382,13 +280,6 @@ inline std::string T_to_string(const mpq & t) { return strs.str(); } -template -bool val_is_smaller_than_eps(T const & t, double const & eps) { - if (!numeric_traits::precise()) { - return numeric_traits::get_double(t) < eps; - } - return t <= numeric_traits::zero(); -} template bool vectors_are_equal(T * a, vector &b, unsigned n); diff --git a/src/math/lp/lp_settings_def.h b/src/math/lp/lp_settings_def.h index 58b37a19d..a19558949 100644 --- a/src/math/lp/lp_settings_def.h +++ b/src/math/lp/lp_settings_def.h @@ -31,7 +31,7 @@ std::string column_type_to_string(column_type t) { case column_type::lower_bound: return "lower_bound"; case column_type::upper_bound: return "upper_bound"; case column_type::free_column: return "free_column"; - default: lp_unreachable(); + default: UNREACHABLE(); } return "unknown"; // it is unreachable } @@ -45,13 +45,12 @@ const char* lp_status_to_string(lp_status status) { case lp_status::DUAL_UNBOUNDED: return "DUAL_UNBOUNDED"; case lp_status::OPTIMAL: return "OPTIMAL"; case lp_status::FEASIBLE: return "FEASIBLE"; - case lp_status::FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR"; case lp_status::TIME_EXHAUSTED: return "TIME_EXHAUSTED"; case lp_status::EMPTY: return "EMPTY"; case lp_status::UNSTABLE: return "UNSTABLE"; case lp_status::CANCELLED: return "CANCELLED"; default: - lp_unreachable(); + UNREACHABLE(); } return "UNKNOWN"; // it is unreachable } @@ -62,29 +61,21 @@ lp_status lp_status_from_string(std::string status) { if (status == "UNBOUNDED") return lp_status::UNBOUNDED; if (status == "OPTIMAL") return lp_status::OPTIMAL; if (status == "FEASIBLE") return lp_status::FEASIBLE; - if (status == "FLOATING_POINT_ERROR") return lp_status::FLOATING_POINT_ERROR; if (status == "TIME_EXHAUSTED") return lp_status::TIME_EXHAUSTED; if (status == "EMPTY") return lp_status::EMPTY; - lp_unreachable(); + UNREACHABLE(); return lp_status::UNKNOWN; // it is unreachable } template bool vectors_are_equal(T * a, vector &b, unsigned n) { - if (numeric_traits::precise()) { for (unsigned i = 0; i < n; i ++){ if (!numeric_traits::is_zero(a[i] - b[i])) { return false; } } - } else { - for (unsigned i = 0; i < n; i ++){ - if (std::abs(numeric_traits::get_double(a[i] - b[i])) > 0.000001) { - return false; - } - } - } + return true; } @@ -93,27 +84,12 @@ template bool vectors_are_equal(const vector & a, const vector &b) { unsigned n = static_cast(a.size()); if (n != b.size()) return false; - if (numeric_traits::precise()) { for (unsigned i = 0; i < n; i ++){ if (!numeric_traits::is_zero(a[i] - b[i])) { return false; } } - } else { - for (unsigned i = 0; i < n; i ++){ - double da = numeric_traits::get_double(a[i]); - double db = numeric_traits::get_double(b[i]); - double amax = std::max(fabs(da), fabs(db)); - if (amax > 1) { - da /= amax; - db /= amax; - } - - if (fabs(da - db) > 0.000001) { - return false; - } - } - } + return true; } #ifdef Z3DEBUG diff --git a/src/math/lp/lp_solver.cpp b/src/math/lp/lp_solver.cpp deleted file mode 100644 index fc9514098..000000000 --- a/src/math/lp/lp_solver.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "math/lp/lp_solver_def.h" -template void lp::lp_solver::add_constraint(lp::lp_relation, double, unsigned int); -template void lp::lp_solver::cleanup(); -template void lp::lp_solver::count_slacks_and_artificials(); -template void lp::lp_solver::fill_m_b(); -template void lp::lp_solver::fill_matrix_A_and_init_right_side(); -template void lp::lp_solver::flip_costs(); -template double lp::lp_solver::get_column_cost_value(unsigned int, lp::column_info*) const; -template int lp::lp_solver::get_column_index_by_name(std::string) const; -template double lp::lp_solver::get_column_value_with_core_solver(unsigned int, lp::lp_core_solver_base*) const; -template lp::column_info* lp::lp_solver::get_or_create_column_info(unsigned int); -template void lp::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); -template void lp::lp_solver::print_statistics_on_A(std::ostream & out); -template bool lp::lp_solver::problem_is_empty(); -template void lp::lp_solver::scale(); -template void lp::lp_solver::set_scaled_cost(unsigned int); -template lp::lp_solver::~lp_solver(); -template void lp::lp_solver::add_constraint(lp::lp_relation, lp::mpq, unsigned int); -template void lp::lp_solver::cleanup(); -template void lp::lp_solver::count_slacks_and_artificials(); -template void lp::lp_solver::fill_m_b(); -template void lp::lp_solver::fill_matrix_A_and_init_right_side(); -template void lp::lp_solver::flip_costs(); -template lp::mpq lp::lp_solver::get_column_cost_value(unsigned int, lp::column_info*) const; -template int lp::lp_solver::get_column_index_by_name(std::string) const; -template lp::mpq lp::lp_solver::get_column_value_by_name(std::string) const; -template lp::mpq lp::lp_solver::get_column_value_with_core_solver(unsigned int, lp::lp_core_solver_base*) const; -template lp::column_info* lp::lp_solver::get_or_create_column_info(unsigned int); -template void lp::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); -template void lp::lp_solver::print_statistics_on_A(std::ostream & out); -template bool lp::lp_solver::problem_is_empty(); -template void lp::lp_solver::scale(); -template void lp::lp_solver::set_scaled_cost(unsigned int); -template lp::lp_solver::~lp_solver(); -template double lp::lp_solver::get_column_value_by_name(std::string) const; diff --git a/src/math/lp/lp_solver.h b/src/math/lp/lp_solver.h deleted file mode 100644 index ab16a686f..000000000 --- a/src/math/lp/lp_solver.h +++ /dev/null @@ -1,260 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include -#include -#include -#include "util/vector.h" -#include "math/lp/lp_settings.h" -#include "math/lp/column_info.h" -#include "math/lp/static_matrix.h" -#include "math/lp/lp_core_solver_base.h" -#include "math/lp/scaler.h" -#include "math/lp/bound_analyzer_on_row.h" -namespace lp { -enum lp_relation { - Less_or_equal, - Equal, - Greater_or_equal -}; - -template -struct lp_constraint { - X m_rs; // right side of the constraint - lp_relation m_relation; - lp_constraint() {} // empty constructor - lp_constraint(T rs, lp_relation relation): m_rs(rs), m_relation(relation) {} -}; - - -template -class lp_solver : public column_namer { - column_info * get_or_create_column_info(unsigned column); - -protected: - T get_column_cost_value(unsigned j, column_info * ci) const; -public: - unsigned m_total_iterations; - static_matrix* m_A; // this is the matrix of constraints - vector m_b; // the right side vector - unsigned m_first_stage_iterations; - unsigned m_second_stage_iterations; - std::unordered_map> m_constraints; - std::unordered_map*> m_map_from_var_index_to_column_info; - std::unordered_map > m_A_values; - std::unordered_map m_names_to_columns; // don't have to use it - std::unordered_map m_external_rows_to_core_solver_rows; - std::unordered_map m_core_solver_rows_to_external_rows; - std::unordered_map m_core_solver_columns_to_external_columns; - vector m_column_scale; - std::unordered_map m_name_map; - unsigned m_artificials; - unsigned m_slacks; - vector m_column_types; - vector m_costs; - vector m_x; - vector m_upper_bounds; - vector m_basis; - vector m_nbasis; - vector m_heading; - - - lp_status m_status; - - lp_settings m_settings; - lp_solver(): - m_A(nullptr), // this is the matrix of constraints - m_first_stage_iterations (0), - m_second_stage_iterations (0), - m_artificials (0), - m_slacks (0), - m_status(lp_status::UNKNOWN) - {} - - unsigned row_count() const { return this->m_A->row_count(); } - - void add_constraint(lp_relation relation, T right_side, unsigned row_index); - - void set_cost_for_column(unsigned column, T column_cost) { - get_or_create_column_info(column)->set_cost(column_cost); - } - std::string get_variable_name(unsigned j) const override; - - void set_row_column_coefficient(unsigned row, unsigned column, T const & val) { - m_A_values[row][column] = val; - } - // returns the current cost - virtual T get_current_cost() const = 0; - // do not have to call it - void give_symbolic_name_to_column(std::string name, unsigned column); - - virtual T get_column_value(unsigned column) const = 0; - - T get_column_value_by_name(std::string name) const; - - // returns -1 if not found - virtual int get_column_index_by_name(std::string name) const; - - void set_lower_bound(unsigned i, T bound) { - column_info *ci = get_or_create_column_info(i); - ci->set_lower_bound(bound); - } - - void set_upper_bound(unsigned i, T bound) { - column_info *ci = get_or_create_column_info(i); - ci->set_upper_bound(bound); - } - - void unset_lower_bound(unsigned i) { - get_or_create_column_info(i)->unset_lower_bound(); - } - - void unset_upper_bound(unsigned i) { - get_or_create_column_info(i)->unset_upper_bound(); - } - - void set_fixed_value(unsigned i, T val) { - column_info *ci = get_or_create_column_info(i); - ci->set_fixed_value(val); - } - - void unset_fixed_value(unsigned i) { - get_or_create_column_info(i)->unset_fixed(); - } - - lp_status get_status() const { - return m_status; - } - - void set_status(lp_status st) { - m_status = st; - } - - - ~lp_solver() override; - - void flip_costs(); - - virtual void find_maximal_solution() = 0; - void set_time_limit(unsigned time_limit_in_seconds) { - m_settings.time_limit = time_limit_in_seconds; - } - - -protected: - bool problem_is_empty(); - - void scale(); - - - void print_rows_scale_stats(std::ostream & out); - - void print_columns_scale_stats(std::ostream & out); - - void print_row_scale_stats(unsigned i, std::ostream & out); - - void print_column_scale_stats(unsigned j, std::ostream & out); - - void print_scale_stats(std::ostream & out); - - void get_max_abs_in_row(std::unordered_map & row_map); - - void pin_vars_down_on_row(std::unordered_map & row) { - pin_vars_on_row_with_sign(row, - numeric_traits::one()); - } - - void pin_vars_up_on_row(std::unordered_map & row) { - pin_vars_on_row_with_sign(row, numeric_traits::one()); - } - - void pin_vars_on_row_with_sign(std::unordered_map & row, T sign ); - - bool get_minimal_row_value(std::unordered_map & row, T & lower_bound); - - bool get_maximal_row_value(std::unordered_map & row, T & lower_bound); - - bool row_is_zero(std::unordered_map & row); - - bool row_e_is_obsolete(std::unordered_map & row, unsigned row_index); - - bool row_ge_is_obsolete(std::unordered_map & row, unsigned row_index); - - bool row_le_is_obsolete(std::unordered_map & row, unsigned row_index); - - // analyse possible max and min values that are derived from var boundaries - // Let us say that the we have a "ge" constraint, and the min value is equal to the rs. - // Then we know what values of the variables are. For each positive coeff of the row it has to be - // the low boundary of the var and for a negative - the upper. - - // this routing also pins the variables to the boundaries - bool row_is_obsolete(std::unordered_map & row, unsigned row_index ); - - void remove_fixed_or_zero_columns(); - - void remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row); - - unsigned try_to_remove_some_rows(); - - void cleanup(); - - void map_external_rows_to_core_solver_rows(); - - void map_external_columns_to_core_solver_columns(); - - unsigned number_of_core_structurals() { - return static_cast(m_core_solver_columns_to_external_columns.size()); - } - - void restore_column_scales_to_one() { - for (unsigned i = 0; i < m_column_scale.size(); i++) m_column_scale[i] = numeric_traits::one(); - } - - void unscale(); - - void fill_A_from_A_values(); - - void fill_matrix_A_and_init_right_side(); - - void count_slacks_and_artificials(); - - void count_slacks_and_artificials_for_row(unsigned i); - - T lower_bound_shift_for_row(unsigned i); - - void fill_m_b(); - - T get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const; - void set_scaled_cost(unsigned j); - void print_statistics_on_A(std::ostream & out) { - out << "extended A[" << this->m_A->row_count() << "," << this->m_A->column_count() << "]" << std::endl; - } - -public: - lp_settings & settings() { return m_settings;} - void print_model(std::ostream & s) const { - s << "objective = " << get_current_cost() << std::endl; - s << "column values\n"; - for (auto & it : m_names_to_columns) { - s << it.first << " = " << get_column_value(it.second) << std::endl; - } - } -}; -} diff --git a/src/math/lp/lp_solver_def.h b/src/math/lp/lp_solver_def.h deleted file mode 100644 index 191832a24..000000000 --- a/src/math/lp/lp_solver_def.h +++ /dev/null @@ -1,571 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include -#include "util/vector.h" -#include "math/lp/lp_solver.h" -namespace lp { -template column_info * lp_solver::get_or_create_column_info(unsigned column) { - auto it = m_map_from_var_index_to_column_info.find(column); - return (it == m_map_from_var_index_to_column_info.end())? (m_map_from_var_index_to_column_info[column] = new column_info()) : it->second; -} - -template -std::string lp_solver::get_variable_name(unsigned j) const { // j here is the core solver index - if (!m_settings.print_external_var_name()) - return std::string("j")+T_to_string(j); - auto it = this->m_core_solver_columns_to_external_columns.find(j); - if (it == this->m_core_solver_columns_to_external_columns.end()) - return std::string("x")+T_to_string(j); - unsigned external_j = it->second; - auto t = this->m_map_from_var_index_to_column_info.find(external_j); - if (t == this->m_map_from_var_index_to_column_info.end()) { - return std::string("x") +T_to_string(external_j); - } - return t->second->get_name(); -} - -template T lp_solver::get_column_cost_value(unsigned j, column_info * ci) const { - if (ci->is_fixed()) { - return ci->get_cost() * ci->get_fixed_value(); - } - return ci->get_cost() * get_column_value(j); -} -template void lp_solver::add_constraint(lp_relation relation, T right_side, unsigned row_index) { - lp_assert(m_constraints.find(row_index) == m_constraints.end()); - lp_constraint cs(right_side, relation); - m_constraints[row_index] = cs; -} - -template void lp_solver::give_symbolic_name_to_column(std::string name, unsigned column) { - auto it = m_map_from_var_index_to_column_info.find(column); - column_info *ci; - if (it == m_map_from_var_index_to_column_info.end()){ - m_map_from_var_index_to_column_info[column] = ci = new column_info; - } else { - ci = it->second; - } - ci->set_name(name); - m_names_to_columns[name] = column; -} - - -template T lp_solver::get_column_value_by_name(std::string name) const { - auto it = m_names_to_columns.find(name); - if (it == m_names_to_columns.end()) { - std::stringstream s; - s << "get_column_value_by_name " << name; - throw_exception(s.str()); - } - return get_column_value(it -> second); -} - -// returns -1 if not found -template int lp_solver::get_column_index_by_name(std::string name) const { - auto t = m_names_to_columns.find(name); - if (t == m_names_to_columns.end()) { - return -1; - } - return t->second; -} - - -template lp_solver::~lp_solver(){ - delete m_A; - for (auto t : m_map_from_var_index_to_column_info) { - delete t.second; - } -} - -template void lp_solver::flip_costs() { - for (auto t : m_map_from_var_index_to_column_info) { - column_info *ci = t.second; - ci->set_cost(-ci->get_cost()); - } -} - -template bool lp_solver::problem_is_empty() { - for (auto & c : m_A_values) - if (!c.second.empty()) - return false; - return true; -} - -template void lp_solver::scale() { - if (numeric_traits::precise() || m_settings.use_scaling == false) { - m_column_scale.clear(); - m_column_scale.resize(m_A->column_count(), one_of_type()); - return; - } - - T smin = T(m_settings.scaling_minimum); - T smax = T(m_settings.scaling_maximum); - - scaler scaler(m_b, *m_A, smin, smax, m_column_scale, this->m_settings); - if (!scaler.scale()) { - unscale(); - } -} - - -template void lp_solver::print_rows_scale_stats(std::ostream & out) { - out << "rows max" << std::endl; - for (unsigned i = 0; i < m_A->row_count(); i++) { - print_row_scale_stats(i, out); - } - out << std::endl; -} - -template void lp_solver::print_columns_scale_stats(std::ostream & out) { - out << "columns max" << std::endl; - for (unsigned i = 0; i < m_A->column_count(); i++) { - print_column_scale_stats(i, out); - } - out << std::endl; -} - -template void lp_solver::print_row_scale_stats(unsigned i, std::ostream & out) { - out << "(" << std::min(m_A->get_min_abs_in_row(i), abs(m_b[i])) << " "; - out << std::max(m_A->get_max_abs_in_row(i), abs(m_b[i])) << ")"; -} - -template void lp_solver::print_column_scale_stats(unsigned j, std::ostream & out) { - out << "(" << m_A->get_min_abs_in_row(j) << " "; - out << m_A->get_max_abs_in_column(j) << ")"; -} - -template void lp_solver::print_scale_stats(std::ostream & out) { - print_rows_scale_stats(out); - print_columns_scale_stats(out); -} - -template void lp_solver::get_max_abs_in_row(std::unordered_map & row_map) { - T ret = numeric_traits::zero(); - for (auto jp : row_map) { - T ac = numeric_traits::abs(jp->second); - if (ac > ret) { - ret = ac; - } - } - return ret; -} - -template void lp_solver::pin_vars_on_row_with_sign(std::unordered_map & row, T sign ) { - for (auto t : row) { - unsigned j = t.first; - column_info * ci = m_map_from_var_index_to_column_info[j]; - T a = t.second; - if (a * sign > numeric_traits::zero()) { - lp_assert(ci->upper_bound_is_set()); - ci->set_fixed_value(ci->get_upper_bound()); - } else { - lp_assert(ci->lower_bound_is_set()); - ci->set_fixed_value(ci->get_lower_bound()); - } - } -} - -template bool lp_solver::get_minimal_row_value(std::unordered_map & row, T & lower_bound) { - lower_bound = numeric_traits::zero(); - for (auto & t : row) { - T a = t.second; - column_info * ci = m_map_from_var_index_to_column_info[t.first]; - if (a > numeric_traits::zero()) { - if (ci->lower_bound_is_set()) { - lower_bound += ci->get_lower_bound() * a; - } else { - return false; - } - } else { - if (ci->upper_bound_is_set()) { - lower_bound += ci->get_upper_bound() * a; - } else { - return false; - } - } - } - return true; -} - -template bool lp_solver::get_maximal_row_value(std::unordered_map & row, T & lower_bound) { - lower_bound = numeric_traits::zero(); - for (auto & t : row) { - T a = t.second; - column_info * ci = m_map_from_var_index_to_column_info[t.first]; - if (a < numeric_traits::zero()) { - if (ci->lower_bound_is_set()) { - lower_bound += ci->get_lower_bound() * a; - } else { - return false; - } - } else { - if (ci->upper_bound_is_set()) { - lower_bound += ci->get_upper_bound() * a; - } else { - return false; - } - } - } - return true; -} - -template bool lp_solver::row_is_zero(std::unordered_map & row) { - for (auto & t : row) { - if (!is_zero(t.second)) - return false; - } - return true; -} - -template bool lp_solver::row_e_is_obsolete(std::unordered_map & row, unsigned row_index) { - T rs = m_constraints[row_index].m_rs; - if (row_is_zero(row)) { - if (!is_zero(rs)) - m_status = lp_status::INFEASIBLE; - return true; - } - - T lower_bound; - bool lb = get_minimal_row_value(row, lower_bound); - if (lb) { - T diff = lower_bound - rs; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ - // lower_bound > rs + m_settings.refactor_epsilon - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_down_on_row(row); - return true; - } - } - - T upper_bound; - bool ub = get_maximal_row_value(row, upper_bound); - if (ub) { - T diff = rs - upper_bound; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { - // upper_bound < rs - m_settings.refactor_tolerance - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_up_on_row(row); - return true; - } - } - - return false; -} - -template bool lp_solver::row_ge_is_obsolete(std::unordered_map & row, unsigned row_index) { - T rs = m_constraints[row_index].m_rs; - if (row_is_zero(row)) { - if (rs > zero_of_type()) - m_status = lp_status::INFEASIBLE; - return true; - } - - T upper_bound; - if (get_maximal_row_value(row, upper_bound)) { - T diff = rs - upper_bound; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { - // upper_bound < rs - m_settings.refactor_tolerance - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_up_on_row(row); - return true; - } - } - - return false; -} - -template bool lp_solver::row_le_is_obsolete(std::unordered_map & row, unsigned row_index) { - T lower_bound; - T rs = m_constraints[row_index].m_rs; - if (row_is_zero(row)) { - if (rs < zero_of_type()) - m_status = lp_status::INFEASIBLE; - return true; - } - - if (get_minimal_row_value(row, lower_bound)) { - T diff = lower_bound - rs; - if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ - // lower_bound > rs + m_settings.refactor_tolerance - m_status = lp_status::INFEASIBLE; - return true; - } - if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ - pin_vars_down_on_row(row); - return true; - } - } - - return false; -} - -// analyse possible max and min values that are derived from var boundaries -// Let us say that the we have a "ge" constraint, and the min value is equal to the rs. -// Then we know what values of the variables are. For each positive coeff of the row it has to be -// the low boundary of the var and for a negative - the upper. - -// this routing also pins the variables to the boundaries -template bool lp_solver::row_is_obsolete(std::unordered_map & row, unsigned row_index ) { - auto & constraint = m_constraints[row_index]; - switch (constraint.m_relation) { - case lp_relation::Equal: - return row_e_is_obsolete(row, row_index); - - case lp_relation::Greater_or_equal: - return row_ge_is_obsolete(row, row_index); - - case lp_relation::Less_or_equal: - return row_le_is_obsolete(row, row_index); - } - lp_unreachable(); - return false; // it is unreachable -} - -template void lp_solver::remove_fixed_or_zero_columns() { - for (auto & i_row : m_A_values) { - remove_fixed_or_zero_columns_from_row(i_row.first, i_row.second); - } -} - -template void lp_solver::remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row) { - auto & constraint = m_constraints[i]; - vector removed; - for (auto & col : row) { - unsigned j = col.first; - lp_assert(m_map_from_var_index_to_column_info.find(j) != m_map_from_var_index_to_column_info.end()); - column_info * ci = m_map_from_var_index_to_column_info[j]; - if (ci->is_fixed()) { - removed.push_back(j); - T aj = col.second; - constraint.m_rs -= aj * ci->get_fixed_value(); - } else { - if (numeric_traits::is_zero(col.second)){ - removed.push_back(j); - } - } - } - - for (auto j : removed) { - row.erase(j); - } -} - -template unsigned lp_solver::try_to_remove_some_rows() { - vector rows_to_delete; - for (auto & t : m_A_values) { - if (row_is_obsolete(t.second, t.first)) { - rows_to_delete.push_back(t.first); - } - - if (m_status == lp_status::INFEASIBLE) { - return 0; - } - } - if (!rows_to_delete.empty()) { - for (unsigned k : rows_to_delete) { - m_A_values.erase(k); - } - } - remove_fixed_or_zero_columns(); - return static_cast(rows_to_delete.size()); -} - -template void lp_solver::cleanup() { - int n = 0; // number of deleted rows - int d; - while ((d = try_to_remove_some_rows()) > 0) - n += d; - - if (n == 1) { - LP_OUT(m_settings, "deleted one row" << std::endl); - } else if (n) { - LP_OUT(m_settings, "deleted " << n << " rows" << std::endl); - } -} - -template void lp_solver::map_external_rows_to_core_solver_rows() { - unsigned size = 0; - for (auto & row : m_A_values) { - m_external_rows_to_core_solver_rows[row.first] = size; - m_core_solver_rows_to_external_rows[size] = row.first; - size++; - } -} - -template void lp_solver::map_external_columns_to_core_solver_columns() { - unsigned size = 0; - for (auto & row : m_A_values) { - for (auto & col : row.second) { - if (col.second == numeric_traits::zero() || m_map_from_var_index_to_column_info[col.first]->is_fixed()) { - throw_exception("found fixed column"); - } - unsigned j = col.first; - auto column_info_it = m_map_from_var_index_to_column_info.find(j); - lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); - - auto j_column = column_info_it->second->get_column_index(); - if (!is_valid(j_column)) { // j is a newcomer - m_map_from_var_index_to_column_info[j]->set_column_index(size); - m_core_solver_columns_to_external_columns[size++] = j; - } - } - } -} - -template void lp_solver::unscale() { - delete m_A; - m_A = nullptr; - fill_A_from_A_values(); - restore_column_scales_to_one(); - fill_m_b(); -} - -template void lp_solver::fill_A_from_A_values() { - m_A = new static_matrix(static_cast(m_A_values.size()), number_of_core_structurals()); - for (auto & t : m_A_values) { - auto row_it = m_external_rows_to_core_solver_rows.find(t.first); - lp_assert(row_it != m_external_rows_to_core_solver_rows.end()); - unsigned row = row_it->second; - for (auto k : t.second) { - auto column_info_it = m_map_from_var_index_to_column_info.find(k.first); - lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); - column_info *ci = column_info_it->second; - unsigned col = ci->get_column_index(); - lp_assert(is_valid(col)); - bool col_is_flipped = m_map_from_var_index_to_column_info[k.first]->is_flipped(); - if (!col_is_flipped) { - (*m_A)(row, col) = k.second; - } else { - (*m_A)(row, col) = - k.second; - } - } - } -} - -template void lp_solver::fill_matrix_A_and_init_right_side() { - map_external_rows_to_core_solver_rows(); - map_external_columns_to_core_solver_columns(); - lp_assert(m_A == nullptr); - fill_A_from_A_values(); - m_b.resize(m_A->row_count()); -} - -template void lp_solver::count_slacks_and_artificials() { - for (int i = row_count() - 1; i >= 0; i--) { - count_slacks_and_artificials_for_row(i); - } -} - -template void lp_solver::count_slacks_and_artificials_for_row(unsigned i) { - lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); - auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[i]]; - switch (constraint.m_relation) { - case Equal: - m_artificials++; - break; - case Greater_or_equal: - m_slacks++; - if (this->m_b[i] > 0) { - m_artificials++; - } - break; - case Less_or_equal: - m_slacks++; - if (this->m_b[i] < 0) { - m_artificials++; - } - break; - } -} - -template T lp_solver::lower_bound_shift_for_row(unsigned i) { - T ret = numeric_traits::zero(); - - auto row = this->m_A_values.find(i); - if (row == this->m_A_values.end()) { - throw_exception("cannot find row"); - } - for (auto col : row->second) { - ret += col.second * this->m_map_from_var_index_to_column_info[col.first]->get_shift(); - } - return ret; -} - -template void lp_solver::fill_m_b() { - for (int i = this->row_count() - 1; i >= 0; i--) { - lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); - unsigned external_i = this->m_core_solver_rows_to_external_rows[i]; - auto & constraint = this->m_constraints[external_i]; - this->m_b[i] = constraint.m_rs - lower_bound_shift_for_row(external_i); - } -} - -template T lp_solver::get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const { - auto cit = this->m_map_from_var_index_to_column_info.find(column); - if (cit == this->m_map_from_var_index_to_column_info.end()) { - return numeric_traits::zero(); - } - - column_info * ci = cit->second; - - if (ci->is_fixed()) { - return ci->get_fixed_value(); - } - - unsigned cj = ci->get_column_index(); - if (cj != static_cast(-1)) { - T v = core_solver->get_var_value(cj) * this->m_column_scale[cj]; - if (ci->is_free()) { - return v; - } - if (!ci->is_flipped()) { - return v + ci->get_lower_bound(); - } - - // the flipped case when there is only upper bound - return -v + ci->get_upper_bound(); // - } - - return numeric_traits::zero(); // returns zero for out of boundary columns -} - -template void lp_solver::set_scaled_cost(unsigned j) { - // grab original costs but modify it with the column scales - lp_assert(j < this->m_column_scale.size()); - column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; - T cost = ci->get_cost(); - if (ci->is_flipped()){ - cost *= T(-1); - } - lp_assert(ci->is_fixed() == false); - this->m_costs[j] = cost * this->m_column_scale[j]; -} -} diff --git a/src/math/lp/lp_utils.cpp b/src/math/lp/lp_utils.cpp deleted file mode 100644 index 9ce3b9894..000000000 --- a/src/math/lp/lp_utils.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/lp_utils.h" -#ifdef lp_for_z3 -namespace lp { -double numeric_traits::g_zero = 0.0; -double numeric_traits::g_one = 1.0; -} -#endif - diff --git a/src/math/lp/lp_utils.h b/src/math/lp/lp_utils.h index 40c5f0632..3c1383cb3 100644 --- a/src/math/lp/lp_utils.h +++ b/src/math/lp/lp_utils.h @@ -141,7 +141,6 @@ inline void throw_exception(std::string && str) { typedef z3_exception exception; #define lp_assert(_x_) { SASSERT(_x_); } -inline void lp_unreachable() { lp_assert(false); } template inline X zero_of_type() { return numeric_traits::zero(); } template inline X one_of_type() { return numeric_traits::one(); } template inline bool is_zero(const X & v) { return numeric_traits::is_zero(v); } @@ -153,9 +152,6 @@ template inline X ceil_ratio(const X & a, const X & b) { return num template inline X floor_ratio(const X & a, const X & b) { return numeric_traits::floor_ratio(a, b); } -template inline bool precise() { return numeric_traits::precise(); } - - // returns true if a factor of b template bool is_proper_factor(const T & a, const T & b) { diff --git a/src/math/lp/lu.cpp b/src/math/lp/lu.cpp deleted file mode 100644 index 6c9bcc5f6..000000000 --- a/src/math/lp/lu.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include -#include -#include "util/vector.h" -#include "util/debug.h" -#include "math/lp/lu_def.h" -namespace lp { -template double dot_product(vector const&, vector const&); -template lu>::lu(static_matrix const&, vector&, lp_settings&); -template void lu>::push_matrix_to_tail(tail_matrix*); -template void lu>::replace_column(double, indexed_vector&, unsigned); -template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); -template lu>::~lu(); -template void lu>::push_matrix_to_tail(tail_matrix*); -template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); -template lu>::~lu(); -template void lu>::push_matrix_to_tail(tail_matrix*); -template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); -template lu>::~lu(); -template mpq dot_product(vector const&, vector const&); -template void init_factorization> - (lu>*&, static_matrix&, vector&, lp_settings&); -template void init_factorization> - (lu>*&, static_matrix&, vector&, lp_settings&); -template void init_factorization>(lu >*&, static_matrix&, vector&, lp_settings&); -template void print_matrix>(square_sparse_matrix&, std::ostream & out); -template void print_matrix>(static_matrix&, std::ostream&); -template void print_matrix >(static_matrix&, std::ostream&); -template void print_matrix>(static_matrix&, std::ostream & out); -#ifdef Z3DEBUG -template bool lu>::is_correct(const vector& basis); -template bool lu>::is_correct( vector const &); -template dense_matrix get_B>(lu>&, const vector& basis); -template dense_matrix get_B>(lu>&, vector const&); - -#endif - -template bool lu>::pivot_the_row(int); // NOLINT -template void lu>::init_vector_w(unsigned int, indexed_vector&); -template void lu>::solve_By(vector&); -template void lu>::solve_By_when_y_is_ready_for_X(vector&); -template void lu>::solve_yB_with_error_check(vector&, const vector& basis); -template void lu>::solve_yB_with_error_check_indexed(indexed_vector&, vector const&, const vector & basis, const lp_settings&); -template void lu>::replace_column(mpq, indexed_vector&, unsigned); -template void lu>::solve_By(vector&); -template void lu>::solve_By_when_y_is_ready_for_X(vector&); -template void lu>::solve_yB_with_error_check(vector&, const vector& basis); -template void lu>::solve_yB_with_error_check_indexed(indexed_vector&, vector< int > const&, const vector & basis, const lp_settings&); -template void lu >::solve_yB_with_error_check_indexed(indexed_vector&, vector< int > const&, const vector & basis, const lp_settings&); -template void lu >::init_vector_w(unsigned int, indexed_vector&); -template void lu >::replace_column(mpq, indexed_vector&, unsigned); -template void lu >::solve_Bd_faster(unsigned int, indexed_vector&); -template void lu >::solve_By(vector&); -template void lu >::solve_By_when_y_is_ready_for_X(vector&); -template void lu >::solve_yB_with_error_check(vector&, const vector& basis); -template void lu>::solve_By(indexed_vector&); -template void lu>::solve_By(indexed_vector&); -template void lu>::solve_yB_indexed(indexed_vector&); -template void lu >::solve_yB_indexed(indexed_vector&); -template void lu>::solve_By_for_T_indexed_only(indexed_vector&, lp_settings const&); -template void lu>::solve_By_for_T_indexed_only(indexed_vector&, lp_settings const&); -#ifdef Z3DEBUG -template void print_matrix>(tail_matrix&, std::ostream&); -#endif -} diff --git a/src/math/lp/lu.h b/src/math/lp/lu.h deleted file mode 100644 index aca59065d..000000000 --- a/src/math/lp/lu.h +++ /dev/null @@ -1,383 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - for matrix B we have - t0*...*tn-1*B = Q*U*R - here ti are matrices corresponding to pivot operations, - including columns and rows swaps, - or a multiplication matrix row by a number - Q, R - permutations and U is an upper triangular matrix -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once - -#include "util/vector.h" -#include "util/debug.h" -#include -#include -#include "math/lp/square_sparse_matrix.h" -#include "math/lp/static_matrix.h" -#include -#include "math/lp/numeric_pair.h" -#include -#include -#include "math/lp/row_eta_matrix.h" -#include "math/lp/square_dense_submatrix.h" -#include "math/lp/dense_matrix.h" -namespace lp { -template // print the nr x nc submatrix at the top left corner -void print_submatrix(square_sparse_matrix & m, unsigned mr, unsigned nc); - -template -void print_matrix(M &m, std::ostream & out); - -template -X dot_product(const vector & a, const vector & b) { - lp_assert(a.size() == b.size()); - auto r = zero_of_type(); - for (unsigned i = 0; i < a.size(); i++) { - r += a[i] * b[i]; - } - return r; -} - - -template -class one_elem_on_diag: public tail_matrix { - unsigned m_i; - T m_val; -public: - one_elem_on_diag(unsigned i, T val) : m_i(i), m_val(val) { -#ifdef Z3DEBUG - m_one_over_val = numeric_traits::one() / m_val; -#endif - } - - bool is_dense() const override { return false; } - - one_elem_on_diag(const one_elem_on_diag & o); - -#ifdef Z3DEBUG - unsigned m_m; - unsigned m_n; - void set_number_of_rows(unsigned m) override { m_m = m; m_n = m; } - void set_number_of_columns(unsigned n) override { m_m = n; m_n = n; } - T m_one_over_val; - - T get_elem (unsigned i, unsigned j) const override; - - unsigned row_count() const override { return m_m; } // not defined } - unsigned column_count() const override { return m_m; } // not defined } -#endif - void apply_from_left(vector & w, lp_settings &) override { - w[m_i] /= m_val; - } - - void apply_from_right(vector & w) override { - w[m_i] /= m_val; - } - - void apply_from_right(indexed_vector & w) override { - if (is_zero(w.m_data[m_i])) - return; - auto & v = w.m_data[m_i] /= m_val; - if (lp_settings::is_eps_small_general(v, 1e-14)) { - w.erase_from_index(m_i); - v = zero_of_type(); - } - } - - - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override; - - void conjugate_by_permutation(permutation_matrix & p) { - // this = p * this * p(-1) -#ifdef Z3DEBUG - // auto rev = p.get_reverse(); - // auto deb = ((*this) * rev); - // deb = p * deb; -#endif - m_i = p.apply_reverse(m_i); - -#ifdef Z3DEBUG - // lp_assert(*this == deb); -#endif - } -}; // end of one_elem_on_diag - -enum class LU_status { OK, Degenerated}; - -// This class supports updates of the columns of B, and solves systems Bx=b,and yB=c -// Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5 -template -class lu { - LU_status m_status; -public: - typedef typename M::coefftype T; - typedef typename M::argtype X; - - // the fields - unsigned m_dim; - const M & m_A; - permutation_matrix m_Q; - permutation_matrix m_R; - permutation_matrix m_r_wave; - square_sparse_matrix m_U; - square_dense_submatrix* m_dense_LU; - - vector *> m_tail; - lp_settings & m_settings; - bool m_failure; - indexed_vector m_row_eta_work_vector; - indexed_vector m_w_for_extension; - indexed_vector m_y_copy; - indexed_vector m_ii; //to optimize the work with the m_index fields - unsigned m_refactor_counter; - // constructor - // if A is an m by n matrix then basis has length m and values in [0,n); the values are all different - // they represent the set of m columns - lu(const M & A, - vector& basis, - lp_settings & settings); - lu(const M & A, lp_settings&); - void debug_test_of_basis(const M & A, vector & basis); - void solve_Bd_when_w_is_ready(vector & d, indexed_vector& w ); - void solve_By(indexed_vector & y); - - void solve_By(vector & y); - - void solve_By_for_T_indexed_only(indexed_vector& y, const lp_settings &); - - template - void solve_By_when_y_is_ready(indexed_vector & y); - void solve_By_when_y_is_ready_for_X(vector & y); - void solve_By_when_y_is_ready_for_T(vector & y, vector & index); - void print_indexed_vector(indexed_vector & w, std::ofstream & f); - - void print_matrix_compact(std::ostream & f); - - void print(indexed_vector & w, const vector& basis); - void solve_Bd(unsigned a_column, vector & d, indexed_vector & w); - void solve_Bd(unsigned a_column, indexed_vector & d, indexed_vector & w); - void solve_Bd_faster(unsigned a_column, indexed_vector & d); // d is the right side on the input and the solution at the exit - - void solve_yB(vector& y); - - void solve_yB_indexed(indexed_vector& y); - - void add_delta_to_solution_indexed(indexed_vector& y); - - void add_delta_to_solution(const vector& yc, vector& y); - - - void find_error_of_yB(vector& yc, const vector& y, - const vector& basis); - - void find_error_of_yB_indexed(const indexed_vector& y, - const vector& heading, const lp_settings& settings); - - - void solve_yB_with_error_check(vector & y, const vector& basis); - - void solve_yB_with_error_check_indexed(indexed_vector & y, const vector& heading, const vector & basis, const lp_settings &); - - void apply_Q_R_to_U(permutation_matrix & r_wave); - - - LU_status get_status() { return m_status; } - - void set_status(LU_status status) { - m_status = status; - } - - ~lu(); - - void init_vector_y(vector & y); - - void perform_transformations_on_w(indexed_vector& w); - - void init_vector_w(unsigned entering, indexed_vector & w); - void apply_lp_list_to_w(indexed_vector & w); - void apply_lp_list_to_y(vector& y); - - void swap_rows(int j, int k); - - void swap_columns(int j, int pivot_column); - - void push_matrix_to_tail(tail_matrix* tm) { - m_tail.push_back(tm); - } - - bool pivot_the_row(int row); - - eta_matrix * get_eta_matrix_for_pivot(unsigned j); - // we're processing the column j now - eta_matrix * get_eta_matrix_for_pivot(unsigned j, square_sparse_matrix& copy_of_U); - - // see page 407 of Chvatal - unsigned transform_U_to_V_by_replacing_column(indexed_vector & w, unsigned leaving_column_of_U); - -#ifdef Z3DEBUG - void check_vector_w(unsigned entering); - - void check_apply_matrix_to_vector(matrix *lp, T *w); - - void check_apply_lp_lists_to_w(T * w); - - // provide some access operators for testing - permutation_matrix & Q() { return m_Q; } - permutation_matrix & R() { return m_R; } - matrix & U() { return m_U; } - unsigned tail_size() { return m_tail.size(); } - - tail_matrix * get_lp_matrix(unsigned i) { - return m_tail[i]; - } - - T B_(unsigned i, unsigned j, const vector& basis) { - return m_A[i][basis[j]]; - } - - unsigned dimension() { return m_dim; } - -#endif - - - unsigned get_number_of_nonzeroes() { - return m_U.get_number_of_nonzeroes(); - } - - - void process_column(int j); - - bool is_correct(const vector& basis); - bool is_correct(); - - -#ifdef Z3DEBUG - dense_matrix tail_product(); - dense_matrix get_left_side(const vector& basis); - dense_matrix get_left_side(); - - dense_matrix get_right_side(); -#endif - - // needed for debugging purposes - void copy_w(T *buffer, indexed_vector & w); - - // needed for debugging purposes - void restore_w(T *buffer, indexed_vector & w); - bool all_columns_and_rows_are_active(); - - bool too_dense(unsigned j) const; - - void pivot_in_dense_mode(unsigned i); - - void create_initial_factorization(); - - void calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix & r_wave); - - void scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump); - - bool diagonal_element_is_off(T /* diag_element */) { return false; } - - void pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump); - // see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last - // row at the same time - row_eta_matrix *get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking); - - void replace_column(T pivot_elem, indexed_vector & w, unsigned leaving_column_of_U); - - void calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump); - - void calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element); - - void prepare_entering(unsigned entering, indexed_vector & w) { - init_vector_w(entering, w); - } - bool need_to_refactor() { return m_refactor_counter >= 200; } - - void adjust_dimension_with_matrix_A() { - lp_assert(m_A.row_count() >= m_dim); - m_dim = m_A.row_count(); - m_U.resize(m_dim); - m_Q.resize(m_dim); - m_R.resize(m_dim); - m_row_eta_work_vector.resize(m_dim); - } - - - std::unordered_set get_set_of_columns_to_replace_for_add_last_rows(const vector & heading) const { - std::unordered_set columns_to_replace; - unsigned m = m_A.row_count(); - unsigned m_prev = m_U.dimension(); - - lp_assert(m_A.column_count() == heading.size()); - - for (unsigned i = m_prev; i < m; i++) { - for (const row_cell & c : m_A.m_rows[i]) { - int h = heading[c.var()]; - if (h < 0) { - continue; - } - columns_to_replace.insert(c.var()); - } - } - return columns_to_replace; - } - - void add_last_rows_to_B(const vector & heading, const std::unordered_set & columns_to_replace) { - unsigned m = m_A.row_count(); - lp_assert(m_A.column_count() == heading.size()); - adjust_dimension_with_matrix_A(); - m_w_for_extension.resize(m); - // At this moment the LU is correct - // for B extended by only by ones at the diagonal in the lower right corner - - for (unsigned j :columns_to_replace) { - lp_assert(heading[j] >= 0); - replace_column_with_only_change_at_last_rows(j, heading[j]); - if (get_status() == LU_status::Degenerated) - break; - } - } - // column j is a basis column, and there is a change in the last rows - void replace_column_with_only_change_at_last_rows(unsigned j, unsigned column_to_change_in_U) { - init_vector_w(j, m_w_for_extension); - replace_column(zero_of_type(), m_w_for_extension, column_to_change_in_U); - } - - bool has_dense_submatrix() const { - for (auto m : m_tail) - if (m->is_dense()) - return true; - return false; - } - -}; // end of lu - -template -void init_factorization(lu* & factorization, M & m_A, vector & m_basis, lp_settings &m_settings); - -#ifdef Z3DEBUG -template -dense_matrix get_B(lu& f, const vector& basis); - -template -dense_matrix get_B(lu& f); -#endif -} diff --git a/src/math/lp/lu_def.h b/src/math/lp/lu_def.h deleted file mode 100644 index 80c9cdf0e..000000000 --- a/src/math/lp/lu_def.h +++ /dev/null @@ -1,992 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include -#include -#include "util/vector.h" -#include -#include "util/debug.h" -#include "math/lp/lu.h" -namespace lp { -template // print the nr x nc submatrix at the top left corner -void print_submatrix(square_sparse_matrix & m, unsigned mr, unsigned nc, std::ostream & out) { - vector> A; - vector widths; - for (unsigned i = 0; i < m.row_count() && i < mr ; i++) { - A.push_back(vector()); - for (unsigned j = 0; j < m.column_count() && j < nc; j++) { - A[i].push_back(T_to_string(static_cast(m(i, j)))); - } - } - - for (unsigned j = 0; j < m.column_count() && j < nc; j++) { - widths.push_back(get_width_of_column(j, A)); - } - - print_matrix_with_widths(A, widths, out); -} - -template -void print_matrix(M &m, std::ostream & out) { - vector> A; - vector widths; - for (unsigned i = 0; i < m.row_count(); i++) { - A.push_back(vector()); - for (unsigned j = 0; j < m.column_count(); j++) { - A[i].push_back(T_to_string(m[i][j])); - } - } - - for (unsigned j = 0; j < m.column_count(); j++) { - widths.push_back(get_width_of_column(j, A)); - } - - print_matrix_with_widths(A, widths, out); -} - -template -one_elem_on_diag::one_elem_on_diag(const one_elem_on_diag & o) { - m_i = o.m_i; - m_val = o.m_val; -#ifdef Z3DEBUG - m_m = m_n = o.m_m; - m_one_over_val = numeric_traits::one() / o.m_val; -#endif -} - -#ifdef Z3DEBUG -template -T one_elem_on_diag::get_elem(unsigned i, unsigned j) const { - if (i == j){ - if (j == m_i) { - return m_one_over_val; - } - return numeric_traits::one(); - } - - return numeric_traits::zero(); -} -#endif -template -void one_elem_on_diag::apply_from_left_to_T(indexed_vector & w, lp_settings & settings) { - T & t = w[m_i]; - if (numeric_traits::is_zero(t)) { - return; - } - t /= m_val; - if (numeric_traits::precise()) return; - if (settings.abs_val_is_smaller_than_drop_tolerance(t)) { - w.erase_from_index(m_i); - t = numeric_traits::zero(); - } -} - -// This class supports updates of the columns of B, and solves systems Bx=b,and yB=c -// Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5 -template -lu::lu(const M& A, - vector& basis, - lp_settings & settings): - m_status(LU_status::OK), - m_dim(A.row_count()), - m_A(A), - m_Q(m_dim), - m_R(m_dim), - m_r_wave(m_dim), - m_U(A, basis), // create the square matrix that eventually will be factorized - m_settings(settings), - m_failure(false), - m_row_eta_work_vector(A.row_count()), - m_refactor_counter(0) { - lp_assert(!(numeric_traits::precise() && settings.use_tableau())); -#ifdef Z3DEBUG - debug_test_of_basis(A, basis); -#endif - ++m_settings.stats().m_num_factorizations; - create_initial_factorization(); -#ifdef Z3DEBUG - // lp_assert(check_correctness()); -#endif -} -template -lu::lu(const M& A, - lp_settings & settings): - m_status(LU_status::OK), - m_dim(A.row_count()), - m_A(A), - m_Q(m_dim), - m_R(m_dim), - m_r_wave(m_dim), - m_U(A), // create the square matrix that eventually will be factorized - m_settings(settings), - m_failure(false), - m_row_eta_work_vector(A.row_count()), - m_refactor_counter(0) { - lp_assert(A.row_count() == A.column_count()); - create_initial_factorization(); -#ifdef Z3DEBUG - lp_assert(is_correct()); -#endif -} -template -void lu::debug_test_of_basis( M const & A, vector & basis) { - std::set set; - for (unsigned i = 0; i < A.row_count(); i++) { - lp_assert(basis[i]< A.column_count()); - set.insert(basis[i]); - } - lp_assert(set.size() == A.row_count()); -} - -template -void lu::solve_By(indexed_vector & y) { - lp_assert(false); // not implemented - // init_vector_y(y); - // solve_By_when_y_is_ready(y); - } - - -template -void lu::solve_By(vector & y) { - init_vector_y(y); - solve_By_when_y_is_ready_for_X(y); -} - -template -void lu::solve_By_when_y_is_ready_for_X(vector & y) { - if (numeric_traits::precise()) { - m_U.solve_U_y(y); - m_R.apply_reverse_from_left_to_X(y); // see 24.3 from Chvatal - return; - } - m_U.double_solve_U_y(y); - m_R.apply_reverse_from_left_to_X(y); // see 24.3 from Chvatal - unsigned i = m_dim; - while (i--) { - if (is_zero(y[i])) continue; - if (m_settings.abs_val_is_smaller_than_drop_tolerance(y[i])){ - y[i] = zero_of_type(); - } - } -} - -template -void lu::solve_By_when_y_is_ready_for_T(vector & y, vector & index) { - if (numeric_traits::precise()) { - m_U.solve_U_y(y); - m_R.apply_reverse_from_left_to_T(y); // see 24.3 from Chvatal - unsigned j = m_dim; - while (j--) { - if (!is_zero(y[j])) - index.push_back(j); - } - return; - } - m_U.double_solve_U_y(y); - m_R.apply_reverse_from_left_to_T(y); // see 24.3 from Chvatal - unsigned i = m_dim; - while (i--) { - if (is_zero(y[i])) continue; - if (m_settings.abs_val_is_smaller_than_drop_tolerance(y[i])){ - y[i] = zero_of_type(); - } else { - index.push_back(i); - } - } -} - -template -void lu::solve_By_for_T_indexed_only(indexed_vector & y, const lp_settings & settings) { - if (numeric_traits::precise()) { - vector active_rows; - m_U.solve_U_y_indexed_only(y, settings, active_rows); - m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal - return; - } - m_U.double_solve_U_y(y, m_settings); - m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal -} - -template -void lu::print_matrix_compact(std::ostream & f) { - f << "matrix_start" << std::endl; - f << "nrows " << m_A.row_count() << std::endl; - f << "ncolumns " << m_A.column_count() << std::endl; - for (unsigned i = 0; i < m_A.row_count(); i++) { - auto & row = m_A.m_rows[i]; - f << "row " << i << std::endl; - for (auto & t : row) { - f << "column " << t.m_j << " value " << t.m_value << std::endl; - } - f << "row_end" << std::endl; - } - f << "matrix_end" << std::endl; -} -template -void lu< M>::print(indexed_vector & w, const vector& basis) { - std::string dump_file_name("/tmp/lu"); - remove(dump_file_name.c_str()); - std::ofstream f(dump_file_name); - if (!f.is_open()) { - LP_OUT(m_settings, "cannot open file " << dump_file_name << std::endl); - return; - } - LP_OUT(m_settings, "writing lu dump to " << dump_file_name << std::endl); - print_matrix_compact(f); - print_vector(basis, f); - print_indexed_vector(w, f); - f.close(); -} -template -void lu< M>::solve_Bd(unsigned a_column, indexed_vector & d, indexed_vector & w) { - init_vector_w(a_column, w); - - if (w.m_index.size() * ratio_of_index_size_to_all_size() < d.m_data.size()) { // this const might need some tuning - d = w; - solve_By_for_T_indexed_only(d, m_settings); - } else { - d.m_data = w.m_data; - d.m_index.clear(); - solve_By_when_y_is_ready_for_T(d.m_data, d.m_index); - } -} - -template -void lu< M>::solve_Bd_faster(unsigned a_column, indexed_vector & d) { // puts the a_column into d - init_vector_w(a_column, d); - solve_By_for_T_indexed_only(d, m_settings); -} - -template -void lu< M>::solve_yB(vector& y) { - // first solve yU = cb*R(-1) - m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1) - m_U.solve_y_U(y); // got y*U=cb*R(-1) - m_Q.apply_reverse_from_right_to_T(y); // - for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) { -#ifdef Z3DEBUG - (*e)->set_number_of_columns(m_dim); -#endif - (*e)->apply_from_right(y); - } -} - -template -void lu< M>::solve_yB_indexed(indexed_vector& y) { - lp_assert(y.is_OK()); - // first solve yU = cb*R(-1) - m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1) - lp_assert(y.is_OK()); - m_U.solve_y_U_indexed(y, m_settings); // got y*U=cb*R(-1) - lp_assert(y.is_OK()); - m_Q.apply_reverse_from_right_to_T(y); - lp_assert(y.is_OK()); - for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) { -#ifdef Z3DEBUG - (*e)->set_number_of_columns(m_dim); -#endif - (*e)->apply_from_right(y); - lp_assert(y.is_OK()); - } -} - -template -void lu< M>::add_delta_to_solution(const vector& yc, vector& y){ - unsigned i = static_cast(y.size()); - while (i--) - y[i]+=yc[i]; -} - -template -void lu< M>::add_delta_to_solution_indexed(indexed_vector& y) { - // the delta sits in m_y_copy, put result into y - lp_assert(y.is_OK()); - lp_assert(m_y_copy.is_OK()); - m_ii.clear(); - m_ii.resize(y.data_size()); - for (unsigned i : y.m_index) - m_ii.set_value(1, i); - for (unsigned i : m_y_copy.m_index) { - y.m_data[i] += m_y_copy[i]; - if (m_ii[i] == 0) - m_ii.set_value(1, i); - } - lp_assert(m_ii.is_OK()); - y.m_index.clear(); - - for (unsigned i : m_ii.m_index) { - T & v = y.m_data[i]; - if (!lp_settings::is_eps_small_general(v, 1e-14)) - y.m_index.push_back(i); - else if (!numeric_traits::is_zero(v)) - v = zero_of_type(); - } - - lp_assert(y.is_OK()); -} - -template -void lu< M>::find_error_of_yB(vector& yc, const vector& y, const vector& m_basis) { - unsigned i = m_dim; - while (i--) { - yc[i] -= m_A.dot_product_with_column(y, m_basis[i]); - } -} - -template -void lu< M>::find_error_of_yB_indexed(const indexed_vector& y, const vector& heading, const lp_settings& settings) { -#if 0 == 1 - // it is a non efficient version - indexed_vector yc = m_y_copy; - yc.m_index.clear(); - lp_assert(!numeric_traits::precise()); - { - - vector d_basis(y.m_data.size()); - for (unsigned j = 0; j < heading.size(); j++) { - if (heading[j] >= 0) { - d_basis[heading[j]] = j; - } - } - - - unsigned i = m_dim; - while (i--) { - T & v = yc.m_data[i] -= m_A.dot_product_with_column(y.m_data, d_basis[i]); - if (settings.abs_val_is_smaller_than_drop_tolerance(v)) - v = zero_of_type(); - else - yc.m_index.push_back(i); - } - } -#endif - lp_assert(m_ii.is_OK()); - m_ii.clear(); - m_ii.resize(y.data_size()); - lp_assert(m_y_copy.is_OK()); - // put the error into m_y_copy - for (auto k : y.m_index) { - auto & row = m_A.m_rows[k]; - const T & y_k = y.m_data[k]; - for (auto & c : row) { - unsigned j = c.var(); - int hj = heading[j]; - if (hj < 0) continue; - if (m_ii.m_data[hj] == 0) - m_ii.set_value(1, hj); - m_y_copy.m_data[hj] -= c.coeff() * y_k; - } - } - // add the index of m_y_copy to m_ii - for (unsigned i : m_y_copy.m_index) { - if (m_ii.m_data[i] == 0) - m_ii.set_value(1, i); - } - - // there is no guarantee that m_y_copy is OK here, but its index - // is contained in m_ii index - m_y_copy.m_index.clear(); - // setup the index of m_y_copy - for (auto k : m_ii.m_index) { - T& v = m_y_copy.m_data[k]; - if (settings.abs_val_is_smaller_than_drop_tolerance(v)) - v = zero_of_type(); - else { - m_y_copy.set_value(v, k); - } - } - lp_assert(m_y_copy.is_OK()); - -} - - - - -// solves y*B = y -// y is the input -template -void lu< M>::solve_yB_with_error_check_indexed(indexed_vector & y, const vector& heading, const vector & basis, const lp_settings & settings) { - if (numeric_traits::precise()) { - if (y.m_index.size() * ratio_of_index_size_to_all_size() * 3 < m_A.column_count()) { - solve_yB_indexed(y); - } else { - solve_yB(y.m_data); - y.restore_index_and_clean_from_data(); - } - return; - } - lp_assert(m_y_copy.is_OK()); - lp_assert(y.is_OK()); - if (y.m_index.size() * ratio_of_index_size_to_all_size() < m_A.column_count()) { - m_y_copy = y; - solve_yB_indexed(y); - lp_assert(y.is_OK()); - if (y.m_index.size() * ratio_of_index_size_to_all_size() >= m_A.column_count()) { - find_error_of_yB(m_y_copy.m_data, y.m_data, basis); - solve_yB(m_y_copy.m_data); - add_delta_to_solution(m_y_copy.m_data, y.m_data); - y.restore_index_and_clean_from_data(); - m_y_copy.clear_all(); - } else { - find_error_of_yB_indexed(y, heading, settings); // this works with m_y_copy - solve_yB_indexed(m_y_copy); - add_delta_to_solution_indexed(y); - } - lp_assert(m_y_copy.is_OK()); - } else { - solve_yB_with_error_check(y.m_data, basis); - y.restore_index_and_clean_from_data(); - } -} - - -// solves y*B = y -// y is the input -template -void lu< M>::solve_yB_with_error_check(vector & y, const vector& basis) { - if (numeric_traits::precise()) { - solve_yB(y); - return; - } - auto & yc = m_y_copy.m_data; - yc =y; // copy y aside - solve_yB(y); - find_error_of_yB(yc, y, basis); - solve_yB(yc); - add_delta_to_solution(yc, y); - m_y_copy.clear_all(); -} -template -void lu< M>::apply_Q_R_to_U(permutation_matrix & r_wave) { - m_U.multiply_from_right(r_wave); - m_U.multiply_from_left_with_reverse(r_wave); -} - - -// Solving yB = cb to find the entering variable, -// where cb is the cost vector projected to B. -// The result is stored in cb. - -// solving Bd = a ( to find the column d of B^{-1} A_N corresponding to the entering -// variable -template -lu< M>::~lu(){ - for (auto t : m_tail) { - delete t; - } -} -template -void lu< M>::init_vector_y(vector & y) { - apply_lp_list_to_y(y); - m_Q.apply_reverse_from_left_to_X(y); -} - -template -void lu< M>::perform_transformations_on_w(indexed_vector& w) { - apply_lp_list_to_w(w); - m_Q.apply_reverse_from_left(w); - // TBD does not compile: lp_assert(numeric_traits::precise() || check_vector_for_small_values(w, m_settings)); -} - -// see Chvatal 24.3 -template -void lu< M>::init_vector_w(unsigned entering, indexed_vector & w) { - w.clear(); - m_A.copy_column_to_indexed_vector(entering, w); // w = a, the column - perform_transformations_on_w(w); -} -template -void lu< M>::apply_lp_list_to_w(indexed_vector & w) { - for (unsigned i = 0; i < m_tail.size(); i++) { - m_tail[i]->apply_from_left_to_T(w, m_settings); - // TBD does not compile: lp_assert(check_vector_for_small_values(w, m_settings)); - } -} -template -void lu< M>::apply_lp_list_to_y(vector& y) { - for (unsigned i = 0; i < m_tail.size(); i++) { - m_tail[i]->apply_from_left(y, m_settings); - } -} -template -void lu< M>::swap_rows(int j, int k) { - if (j != k) { - m_Q.transpose_from_left(j, k); - m_U.swap_rows(j, k); - } -} - -template -void lu< M>::swap_columns(int j, int pivot_column) { - if (j == pivot_column) - return; - m_R.transpose_from_right(j, pivot_column); - m_U.swap_columns(j, pivot_column); -} -template -bool lu< M>::pivot_the_row(int row) { - eta_matrix * eta_matrix = get_eta_matrix_for_pivot(row); - if (get_status() != LU_status::OK) { - return false; - } - - if (eta_matrix == nullptr) { - m_U.shorten_active_matrix(row, nullptr); - return true; - } - if (!m_U.pivot_with_eta(row, eta_matrix, m_settings)) - return false; - eta_matrix->conjugate_by_permutation(m_Q); - push_matrix_to_tail(eta_matrix); - return true; -} -// we're processing the column j now -template -eta_matrix * lu< M>::get_eta_matrix_for_pivot(unsigned j) { - eta_matrix *ret; - if(!m_U.fill_eta_matrix(j, &ret)) { - set_status(LU_status::Degenerated); - } - return ret; -} -// we're processing the column j now -template -eta_matrix * lu::get_eta_matrix_for_pivot(unsigned j, square_sparse_matrix& copy_of_U) { - eta_matrix *ret; - copy_of_U.fill_eta_matrix(j, &ret); - return ret; -} - -// see page 407 of Chvatal -template -unsigned lu::transform_U_to_V_by_replacing_column(indexed_vector & w, - unsigned leaving_column) { - unsigned column_to_replace = m_R.apply_reverse(leaving_column); - m_U.replace_column(column_to_replace, w, m_settings); - return column_to_replace; -} - -#ifdef Z3DEBUG -template -void lu::check_vector_w(unsigned entering) { - T * w = new T[m_dim]; - m_A.copy_column_to_vector(entering, w); - check_apply_lp_lists_to_w(w); - delete [] w; -} -template -void lu::check_apply_matrix_to_vector(matrix *lp, T *w) { - if (lp != nullptr) { - lp -> set_number_of_rows(m_dim); - lp -> set_number_of_columns(m_dim); - apply_to_vector(*lp, w); - } -} - -template -void lu::check_apply_lp_lists_to_w(T * w) { - for (unsigned i = 0; i < m_tail.size(); i++) { - check_apply_matrix_to_vector(m_tail[i], w); - } - permutation_matrix qr = m_Q.get_reverse(); - apply_to_vector(qr, w); - for (int i = m_dim - 1; i >= 0; i--) { - lp_assert(abs(w[i] - w[i]) < 0.0000001); - } -} - -#endif -template -void lu::process_column(int j) { - unsigned pi, pj; - bool success = m_U.get_pivot_for_column(pi, pj, m_settings.c_partial_pivoting, j); - if (!success) { - // LP_OUT(m_settings, "get_pivot returned false: cannot find the pivot for column " << j << std::endl); - m_failure = true; - return; - } - - if (static_cast(pi) == -1) { - // LP_OUT(m_settings, "cannot find the pivot for column " << j << std::endl); - m_failure = true; - return; - } - swap_columns(j, pj); - swap_rows(j, pi); - if (!pivot_the_row(j)) { - // LP_OUT(m_settings, "pivot_the_row(" << j << ") failed" << std::endl); - m_failure = true; - } -} -template -bool lu::is_correct(const vector& basis) { -#ifdef Z3DEBUG - if (get_status() != LU_status::OK) { - return false; - } - dense_matrix left_side = get_left_side(basis); - dense_matrix right_side = get_right_side(); - return left_side == right_side; -#else - return true; -#endif -} - -template -bool lu::is_correct() { -#ifdef Z3DEBUG - if (get_status() != LU_status::OK) { - return false; - } - dense_matrix left_side = get_left_side(); - dense_matrix right_side = get_right_side(); - return left_side == right_side; -#else - return true; -#endif -} - - -#ifdef Z3DEBUG -template -dense_matrix lu::tail_product() { - lp_assert(tail_size() > 0); - dense_matrix left_side = permutation_matrix(m_dim); - for (unsigned i = 0; i < tail_size(); i++) { - matrix* lp = get_lp_matrix(i); - lp->set_number_of_rows(m_dim); - lp->set_number_of_columns(m_dim); - left_side = ((*lp) * left_side); - } - return left_side; -} -template -dense_matrix lu::get_left_side(const vector& basis) { - dense_matrix left_side = get_B(*this, basis); - for (unsigned i = 0; i < tail_size(); i++) { - matrix* lp = get_lp_matrix(i); - lp->set_number_of_rows(m_dim); - lp->set_number_of_columns(m_dim); - left_side = ((*lp) * left_side); - } - return left_side; -} -template -dense_matrix lu::get_left_side() { - dense_matrix left_side = get_B(*this); - for (unsigned i = 0; i < tail_size(); i++) { - matrix* lp = get_lp_matrix(i); - lp->set_number_of_rows(m_dim); - lp->set_number_of_columns(m_dim); - left_side = ((*lp) * left_side); - } - return left_side; -} -template -dense_matrix lu::get_right_side() { - auto ret = U() * R(); - ret = Q() * ret; - return ret; -} -#endif - -// needed for debugging purposes -template -void lu::copy_w(T *buffer, indexed_vector & w) { - unsigned i = m_dim; - while (i--) { - buffer[i] = w[i]; - } -} - -// needed for debugging purposes -template -void lu::restore_w(T *buffer, indexed_vector & w) { - unsigned i = m_dim; - while (i--) { - w[i] = buffer[i]; - } -} -template -bool lu::all_columns_and_rows_are_active() { - unsigned i = m_dim; - while (i--) { - lp_assert(m_U.col_is_active(i)); - lp_assert(m_U.row_is_active(i)); - } - return true; -} -template -bool lu::too_dense(unsigned j) const { - unsigned r = m_dim - j; - if (r < 5) - return false; - // if (j * 5 < m_dim * 4) // start looking for dense only at the bottom of the rows - // return false; - // return r * r * m_settings.density_threshold <= m_U.get_number_of_nonzeroes_below_row(j); - return r * r * m_settings.density_threshold <= m_U.get_n_of_active_elems(); -} -template -void lu::pivot_in_dense_mode(unsigned i) { - int j = m_dense_LU->find_pivot_column_in_row(i); - if (j == -1) { - m_failure = true; - return; - } - if (i != static_cast(j)) { - swap_columns(i, j); - m_dense_LU->swap_columns(i, j); - } - m_dense_LU->pivot(i, m_settings); -} -template -void lu::create_initial_factorization(){ - m_U.prepare_for_factorization(); - unsigned j; - for (j = 0; j < m_dim; j++) { - process_column(j); - if (m_failure) { - set_status(LU_status::Degenerated); - return; - } - if (too_dense(j)) { - break; - } - } - if (j == m_dim) { - // TBD does not compile: lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); - // lp_assert(is_correct()); - // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); - return; - } - j++; - m_dense_LU = new square_dense_submatrix(&m_U, j); - for (; j < m_dim; j++) { - pivot_in_dense_mode(j); - if (m_failure) { - set_status(LU_status::Degenerated); - return; - } - } - m_dense_LU->update_parent_matrix(m_settings); - lp_assert(m_dense_LU->is_L_matrix()); - m_dense_LU->conjugate_by_permutation(m_Q); - push_matrix_to_tail(m_dense_LU); - m_refactor_counter = 0; - // lp_assert(is_correct()); - // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); -} - -template -void lu::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix & r_wave) { - if (bump_start > bump_end) { - set_status(LU_status::Degenerated); - return; - } - if (bump_start == bump_end) { - return; - } - - r_wave[bump_start] = bump_end; // sending the offensive column to the end of the bump - - for ( unsigned i = bump_start + 1 ; i <= bump_end; i++ ) { - r_wave[i] = i - 1; - } - - m_U.multiply_from_right(r_wave); - m_U.multiply_from_left_with_reverse(r_wave); -} -template -void lu::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) { - vector> & last_row_vec = m_U.get_row_values(m_U.adjust_row(lowest_row_of_the_bump)); - for (auto & iv : last_row_vec) { - if (is_zero(iv.m_value)) continue; - lp_assert(!m_settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value)); - unsigned adjusted_col = m_U.adjust_column_inverse(iv.m_index); - if (adjusted_col < lowest_row_of_the_bump) { - m_row_eta_work_vector.set_value(-iv.m_value, adjusted_col); - } else { - m_row_eta_work_vector.set_value(iv.m_value, adjusted_col); // preparing to calculate the real value in the matrix - } - } -} - -template -void lu::pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump) { - // we have the system right side at m_row_eta_work_vector now - // solve the system column wise - for (unsigned j = replaced_column; j < lowest_row_of_the_bump; j++) { - T v = m_row_eta_work_vector[j]; - if (numeric_traits::is_zero(v)) continue; // this column does not contribute to the solution - unsigned aj = m_U.adjust_row(j); - vector> & row = m_U.get_row_values(aj); - for (auto & iv : row) { - unsigned col = m_U.adjust_column_inverse(iv.m_index); - lp_assert(col >= j || numeric_traits::is_zero(iv.m_value)); - if (col == j) continue; - if (numeric_traits::is_zero(iv.m_value)) { - continue; - } - // the -v is for solving the system ( to zero the last row), and +v is for pivoting - T delta = col < lowest_row_of_the_bump? -v * iv.m_value: v * iv.m_value; - lp_assert(numeric_traits::is_zero(delta) == false); - - - - // m_row_eta_work_vector.add_value_at_index_with_drop_tolerance(col, delta); - if (numeric_traits::is_zero(m_row_eta_work_vector[col])) { - if (!m_settings.abs_val_is_smaller_than_drop_tolerance(delta)){ - m_row_eta_work_vector.set_value(delta, col); - } - } else { - T t = (m_row_eta_work_vector[col] += delta); - if (m_settings.abs_val_is_smaller_than_drop_tolerance(t)){ - m_row_eta_work_vector[col] = numeric_traits::zero(); - auto it = std::find(m_row_eta_work_vector.m_index.begin(), m_row_eta_work_vector.m_index.end(), col); - if (it != m_row_eta_work_vector.m_index.end()) - m_row_eta_work_vector.m_index.erase(it); - } - } - } - } -} -// see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last -// row at the same time -template -row_eta_matrix *lu::get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking) { - if (replaced_column == lowest_row_of_the_bump) return nullptr; - scan_last_row_to_work_vector(lowest_row_of_the_bump); - pivot_and_solve_the_system(replaced_column, lowest_row_of_the_bump); - if (numeric_traits::precise() == false && !is_zero(pivot_elem_for_checking)) { - T denom = std::max(T(1), abs(pivot_elem_for_checking)); - if ( - !m_settings.abs_val_is_smaller_than_pivot_tolerance((m_row_eta_work_vector[lowest_row_of_the_bump] - pivot_elem_for_checking) / denom)) { - set_status(LU_status::Degenerated); - // LP_OUT(m_settings, "diagonal element is off" << std::endl); - return nullptr; - } - } -#ifdef Z3DEBUG - auto ret = new row_eta_matrix(replaced_column, lowest_row_of_the_bump, m_dim); -#else - auto ret = new row_eta_matrix(replaced_column, lowest_row_of_the_bump); -#endif - - for (auto j : m_row_eta_work_vector.m_index) { - if (j < lowest_row_of_the_bump) { - auto & v = m_row_eta_work_vector[j]; - if (!is_zero(v)) { - if (!m_settings.abs_val_is_smaller_than_drop_tolerance(v)){ - ret->push_back(j, v); - } - v = numeric_traits::zero(); - } - } - } // now the lowest_row_of_the_bump contains the rest of the row to the right of the bump with correct values - return ret; -} - -template -void lu::replace_column(T pivot_elem_for_checking, indexed_vector & w, unsigned leaving_column_of_U){ - m_refactor_counter++; - unsigned replaced_column = transform_U_to_V_by_replacing_column( w, leaving_column_of_U); - unsigned lowest_row_of_the_bump = m_U.lowest_row_in_column(replaced_column); - m_r_wave.init(m_dim); - calculate_r_wave_and_update_U(replaced_column, lowest_row_of_the_bump, m_r_wave); - auto row_eta = get_row_eta_matrix_and_set_row_vector(replaced_column, lowest_row_of_the_bump, pivot_elem_for_checking); - - if (get_status() == LU_status::Degenerated) { - m_row_eta_work_vector.clear_all(); - return; - } - m_Q.multiply_by_permutation_from_right(m_r_wave); - m_R.multiply_by_permutation_reverse_from_left(m_r_wave); - if (row_eta != nullptr) { - row_eta->conjugate_by_permutation(m_Q); - push_matrix_to_tail(row_eta); - } - calculate_Lwave_Pwave_for_bump(replaced_column, lowest_row_of_the_bump); - // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); - // lp_assert(w.is_OK() && m_row_eta_work_vector.is_OK()); -} -template -void lu::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){ - T diagonal_elem; - if (replaced_column < lowest_row_of_the_bump) { - diagonal_elem = m_row_eta_work_vector[lowest_row_of_the_bump]; - // lp_assert(m_row_eta_work_vector.is_OK()); - m_U.set_row_from_work_vector_and_clean_work_vector_not_adjusted(m_U.adjust_row(lowest_row_of_the_bump), m_row_eta_work_vector, m_settings); - } else { - diagonal_elem = m_U(lowest_row_of_the_bump, lowest_row_of_the_bump); // todo - get it more efficiently - } - if (m_settings.abs_val_is_smaller_than_pivot_tolerance(diagonal_elem)) { - set_status(LU_status::Degenerated); - return; - } - - calculate_Lwave_Pwave_for_last_row(lowest_row_of_the_bump, diagonal_elem); - // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); -} - -template -void lu::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) { - auto l = new one_elem_on_diag(lowest_row_of_the_bump, diagonal_element); -#ifdef Z3DEBUG - l->set_number_of_columns(m_dim); -#endif - push_matrix_to_tail(l); - m_U.divide_row_by_constant(lowest_row_of_the_bump, diagonal_element, m_settings); - l->conjugate_by_permutation(m_Q); -} - -template -void init_factorization(lu* & factorization, M & m_A, vector & m_basis, lp_settings &m_settings) { - if (factorization != nullptr) - delete factorization; - factorization = new lu(m_A, m_basis, m_settings); - // if (factorization->get_status() != LU_status::OK) - // LP_OUT(m_settings, "failing in init_factorization" << std::endl); -} - -#ifdef Z3DEBUG -template -dense_matrix get_B(lu& f, const vector& basis) { - lp_assert(basis.size() == f.dimension()); - lp_assert(basis.size() == f.m_U.dimension()); - dense_matrix B(f.dimension(), f.dimension()); - for (unsigned i = 0; i < f.dimension(); i++) - for (unsigned j = 0; j < f.dimension(); j++) - B.set_elem(i, j, f.B_(i, j, basis)); - - return B; -} -template -dense_matrix get_B(lu& f) { - dense_matrix B(f.dimension(), f.dimension()); - for (unsigned i = 0; i < f.dimension(); i++) - for (unsigned j = 0; j < f.dimension(); j++) - B.set_elem(i, j, f.m_A[i][j]); - - return B; -} -#endif -} diff --git a/src/math/lp/matrix.cpp b/src/math/lp/matrix.cpp index 5367c74d0..1ea2da263 100644 --- a/src/math/lp/matrix.cpp +++ b/src/math/lp/matrix.cpp @@ -22,10 +22,8 @@ Revision History: #include "math/lp/static_matrix.h" #include #ifdef Z3DEBUG -template bool lp::matrix::is_equal(lp::matrix const&); template bool lp::matrix >::is_equal(lp::matrix > const&); template bool lp::matrix::is_equal(lp::matrix const&); #endif -template void lp::print_matrix(lp::matrix const*, std::ostream & out); template void lp::print_matrix >(lp::matrix > const *, std::basic_ostream > &); template void lp::print_matrix(lp::matrix const*, std::ostream&); diff --git a/src/math/lp/matrix_def.h b/src/math/lp/matrix_def.h index 95810bd5a..e3ac08f7e 100644 --- a/src/math/lp/matrix_def.h +++ b/src/math/lp/matrix_def.h @@ -32,16 +32,9 @@ bool matrix::is_equal(const matrix& other) { for (unsigned j = 0; j < column_count(); j++) { auto a = get_elem(i, j); auto b = other.get_elem(i, j); - if (numeric_traits::precise()) { - if (a != b) return false; - } else if (fabs(numeric_traits::get_double(a - b)) > 0.000001) { - // cout << "returning false from operator== of matrix comparison" << endl; - // cout << "this matrix is " << endl; - // print_matrix(*this); - // cout << "other matrix is " << endl; - // print_matrix(other); - return false; - } + + if (a != b) return false; + } } return true; diff --git a/src/math/lp/mps_reader.h b/src/math/lp/mps_reader.h deleted file mode 100644 index 8093954b1..000000000 --- a/src/math/lp/mps_reader.h +++ /dev/null @@ -1,891 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once - -// reads an MPS file representing a Mixed Integer Program -#include -#include -#include -#include "util/vector.h" -#include -#include -#include -#include -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" -#include "math/lp/lar_solver.h" -#include "math/lp/lp_utils.h" -#include "math/lp/lp_solver.h" -namespace lp { -inline bool my_white_space(const char & a) { - return a == ' ' || a == '\t'; -} -inline size_t number_of_whites(const std::string & s) { - size_t i = 0; - for(;i < s.size(); i++) - if (!my_white_space(s[i])) return i; - return i; -} -inline size_t number_of_whites_from_end(const std::string & s) { - size_t ret = 0; - for(int i = static_cast(s.size()) - 1;i >= 0; i--) - if (my_white_space(s[i])) ret++;else break; - - return ret; -} - - - // trim from start -inline std::string <rim(std::string &s) { - s.erase(0, number_of_whites(s)); - return s; -} - - - - - // trim from end -inline std::string &rtrim(std::string &s) { - // s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); - s.erase(s.end() - number_of_whites_from_end(s), s.end()); - return s; -} - // trim from both ends -inline std::string &trim(std::string &s) { - return ltrim(rtrim(s)); -} - -inline std::string trim(std::string const &r) { - std::string s = r; - return ltrim(rtrim(s)); -} - - -inline vector string_split(const std::string &source, const char *delimiter, bool keep_empty) { - vector results; - size_t prev = 0; - size_t next = 0; - while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) { - if (keep_empty || (next - prev != 0)) { - results.push_back(source.substr(prev, next - prev)); - } - prev = next + 1; - } - if (prev < source.size()) { - results.push_back(source.substr(prev)); - } - return results; -} - -inline vector split_and_trim(const std::string &line) { - auto split = string_split(line, " \t", false); - vector ret; - for (auto s : split) { - ret.push_back(trim(s)); - } - return ret; -} - -template -class mps_reader { - enum row_type { Cost, Less_or_equal, Greater_or_equal, Equal }; - struct bound { - T m_low; - T m_upper; - bool m_low_is_set; - bool m_upper_is_set; - bool m_value_is_fixed; - T m_fixed_value; - bool m_free; - // constructor - bound() : m_low(numeric_traits::zero()), - m_low_is_set(true), - m_upper_is_set(false), - m_value_is_fixed(false), - m_free(false) {} // it seems all mps files I have seen have the default low value 0 on a variable - }; - - struct column { - std::string m_name; - bound * m_bound; - unsigned m_index; - column(const std::string &name, unsigned index): m_name(name), - m_bound(nullptr), - m_index(index) { - } - }; - - struct row { - row_type m_type; - std::string m_name; - std::unordered_map m_row_columns; - unsigned m_index; - T m_right_side; - T m_range; - row(row_type type, const std::string &name, unsigned index) : - m_type(type), - m_name(name), - m_index(index), - m_right_side(zero_of_type()), - m_range(zero_of_type()) - { - } - }; - - bool m_is_OK; - std::string m_file_name; - std::unordered_map m_rows; - std::unordered_map m_columns; - std::unordered_map m_names_to_var_index; - std::string m_line; - std::string m_name; - std::string m_cost_row_name; - std::ifstream m_file_stream; - // needed to adjust the index row - unsigned m_cost_line_count; - unsigned m_line_number; - std::ostream * m_message_stream; - - void set_m_ok_to_false() { - *m_message_stream << "setting m_is_OK to false" << std::endl; - m_is_OK = false; - } - - std::string get_string_from_position(unsigned offset) { - unsigned i = offset; - for (; i < m_line.size(); i++){ - if (m_line[i] == ' ') - break; - } - lp_assert(m_line.size() >= offset); - lp_assert(m_line.size() >> i); - lp_assert(i >= offset); - return m_line.substr(offset, i - offset); - } - - void set_boundary_for_column(unsigned col, bound * b, lp_solver * solver){ - if (b == nullptr) { - solver->set_lower_bound(col, numeric_traits::zero()); - return; - } - - if (b->m_free) { - return; - } - if (b->m_low_is_set) { - solver->set_lower_bound(col, b->m_low); - } - if (b->m_upper_is_set) { - solver->set_upper_bound(col, b->m_upper); - } - - if (b->m_value_is_fixed) { - solver->set_fixed_value(col, b->m_fixed_value); - } - } - - bool all_white_space() { - for (unsigned i = 0; i < m_line.size(); i++) { - char c = m_line[i]; - if (c != ' ' && c != '\t') { - return false; - } - } - return true; - } - - void read_line() { - while (m_is_OK) { - if (!getline(m_file_stream, m_line)) { - m_line_number++; - set_m_ok_to_false(); - *m_message_stream << "cannot read from file" << std::endl; - } - m_line_number++; - if (!m_line.empty() && m_line[0] != '*' && !all_white_space()) - break; - } - } - - void read_name() { - do { - read_line(); - if (m_line.find("NAME") != 0) { - continue; - } - m_line = m_line.substr(4); - m_name = trim(m_line); - break; - } while (m_is_OK); - } - - void read_rows() { - // look for start of the rows - read_line(); - do { - if (static_cast(m_line.find("ROWS")) >= 0) { - break; - } - } while (m_is_OK); - do { - read_line(); - if (m_line.find("COLUMNS") == 0) { - break; - } - add_row(); - } while (m_is_OK); - } - - void read_column_by_columns(const std::string & column_name, std::string column_data) { - // uph, let us try to work with columns - if (column_data.size() >= 22) { - std::string ss = column_data.substr(0, 8); - std::string row_name = trim(ss); - auto t = m_rows.find(row_name); - - if (t == m_rows.end()) { - *m_message_stream << "cannot find " << row_name << std::endl; - goto fail; - } else { - row * row = t->second; - row->m_row_columns[column_name] = numeric_traits::from_string(column_data.substr(8)); - if (column_data.size() > 24) { - column_data = column_data.substr(25); - if (column_data.size() >= 22) { - read_column_by_columns(column_name, column_data); - } - } - } - } else { - fail: - set_m_ok_to_false(); - *m_message_stream << "cannot understand this line\n" - "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - void read_column(const std::string & column_name, const std::string & column_data){ - auto tokens = split_and_trim(column_data); - for (unsigned i = 0; i < tokens.size() - 1; i+= 2) { - auto row_name = tokens[i]; - if (row_name == "'MARKER'") return; // it is the integrality marker, no real data here - auto t = m_rows.find(row_name); - if (t == m_rows.end()) { - read_column_by_columns(column_name, column_data); - return; - } - row *r = t->second; - r->m_row_columns[column_name] = numeric_traits::from_string(tokens[i + 1]); - } - } - - void read_columns(){ - std::string column_name; - do { - read_line(); - if (m_line.find("RHS") == 0) { - break; - } - if (m_line.size() < 22) { - (*m_message_stream) << "line is too short for a column" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - std::string column_name_tmp = trim(m_line.substr(4, 8)); - if (!column_name_tmp.empty()) { - column_name = column_name_tmp; - } - auto col_it = m_columns.find(column_name); - mps_reader::column * col; - if (col_it == m_columns.end()) { - col = new mps_reader::column(column_name, static_cast(m_columns.size())); - m_columns[column_name] = col; - // (*m_message_stream) << column_name << '[' << col->m_index << ']'<< std::endl; - } else { - col = col_it->second; - } - read_column(column_name, m_line.substr(14)); - } while (m_is_OK); - } - - void read_rhs() { - do { - read_line(); - if (m_line.find("BOUNDS") == 0 || m_line.find("ENDATA") == 0 || m_line.find("RANGES") == 0) { - break; - } - fill_rhs(); - } while (m_is_OK); - } - - - void fill_rhs_by_columns(std::string rhsides) { - // uph, let us try to work with columns - if (rhsides.size() >= 22) { - std::string ss = rhsides.substr(0, 8); - std::string row_name = trim(ss); - auto t = m_rows.find(row_name); - - if (t == m_rows.end()) { - (*m_message_stream) << "cannot find " << row_name << std::endl; - goto fail; - } else { - row * row = t->second; - row->m_right_side = numeric_traits::from_string(rhsides.substr(8)); - if (rhsides.size() > 24) { - rhsides = rhsides.substr(25); - if (rhsides.size() >= 22) { - fill_rhs_by_columns(rhsides); - } - } - } - } else { - fail: - set_m_ok_to_false(); - (*m_message_stream) << "cannot understand this line" << std::endl; - (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - void fill_rhs() { - if (m_line.size() < 14) { - (*m_message_stream) << "line is too short" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - std::string rhsides = m_line.substr(14); - vector splitted_line = split_and_trim(rhsides); - - for (unsigned i = 0; i < splitted_line.size() - 1; i += 2) { - auto t = m_rows.find(splitted_line[i]); - if (t == m_rows.end()) { - fill_rhs_by_columns(rhsides); - return; - } - row * row = t->second; - row->m_right_side = numeric_traits::from_string(splitted_line[i + 1]); - } - } - - void read_bounds() { - if (m_line.find("BOUNDS") != 0) { - return; - } - - do { - read_line(); - if (m_line[0] != ' ') { - break; - } - create_or_update_bound(); - } while (m_is_OK); - } - - void read_ranges() { - if (m_line.find("RANGES") != 0) { - return; - } - do { - read_line(); - auto sl = split_and_trim(m_line); - if (sl.size() < 2) { - break; - } - read_range(sl); - } while (m_is_OK); - } - - - void read_bound_by_columns(const std::string & colstr) { - if (colstr.size() < 14) { - (*m_message_stream) << "line is too short" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - // uph, let us try to work with columns - if (colstr.size() >= 22) { - std::string ss = colstr.substr(0, 8); - std::string column_name = trim(ss); - auto t = m_columns.find(column_name); - - if (t == m_columns.end()) { - (*m_message_stream) << "cannot find " << column_name << std::endl; - goto fail; - } else { - vector bound_string; - bound_string.push_back(column_name); - if (colstr.size() > 14) { - bound_string.push_back(colstr.substr(14)); - } - mps_reader::column * col = t->second; - bound * b = col->m_bound; - if (b == nullptr) { - col->m_bound = b = new bound(); - } - update_bound(b, bound_string); - } - } else { - fail: - set_m_ok_to_false(); - (*m_message_stream) << "cannot understand this line" << std::endl; - (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - void update_bound(bound * b, vector bound_string) { - /* - UP means an upper bound is applied to the variable. A bound of type LO means a lower bound is applied. A bound type of FX ("fixed") means that the variable has upper and lower bounds equal to a single value. A bound type of FR ("free") means the variable has neither lower nor upper bounds and so can take on negative values. A variation on that is MI for free negative, giving an upper bound of 0 but no lower bound. Bound type PL is for a free positive for zero to plus infinity, but as this is the normal default, it is seldom used. There are also bound types for use in MIP models - BV for binary, being 0 or 1. UI for upper integer and LI for lower integer. SC stands for semi-continuous and indicates that the variable may be zero, but if not must be equal to at least the value given. - */ - - std::string bound_type = get_string_from_position(1); - if (bound_type == "BV") { - b->m_upper_is_set = true; - b->m_upper = 1; - return; - } - - if (bound_type == "UP" || bound_type == "UI" || bound_type == "LIMITMAX") { - if (bound_string.size() <= 1){ - set_m_ok_to_false(); - return; - } - b->m_upper_is_set = true; - b->m_upper= numeric_traits::from_string(bound_string[1]); - } else if (bound_type == "LO" || bound_type == "LI") { - if (bound_string.size() <= 1){ - set_m_ok_to_false(); - return; - } - - b->m_low_is_set = true; - b->m_low = numeric_traits::from_string(bound_string[1]); - } else if (bound_type == "FR") { - b->m_free = true; - } else if (bound_type == "FX") { - if (bound_string.size() <= 1){ - set_m_ok_to_false(); - return; - } - - b->m_value_is_fixed = true; - b->m_fixed_value = numeric_traits::from_string(bound_string[1]); - } else if (bound_type == "PL") { - b->m_low_is_set = true; - b->m_low = 0; - } else if (bound_type == "MI") { - b->m_upper_is_set = true; - b->m_upper = 0; - } else { - (*m_message_stream) << "unexpected bound type " << bound_type << " at line " << m_line_number << std::endl; - set_m_ok_to_false(); - throw; - } - } - - void create_or_update_bound() { - const unsigned name_offset = 14; - lp_assert(m_line.size() >= 14); - vector bound_string = split_and_trim(m_line.substr(name_offset, m_line.size())); - - if (bound_string.empty()) { - set_m_ok_to_false(); - (*m_message_stream) << "error at line " << m_line_number << std::endl; - throw m_line; - } - - std::string name = bound_string[0]; - auto it = m_columns.find(name); - if (it == m_columns.end()){ - read_bound_by_columns(m_line.substr(14)); - return; - } - mps_reader::column * col = it->second; - bound * b = col->m_bound; - if (b == nullptr) { - col->m_bound = b = new bound(); - } - update_bound(b, bound_string); - } - - - - void read_range_by_columns(std::string rhsides) { - if (m_line.size() < 14) { - (*m_message_stream) << "line is too short" << std::endl; - (*m_message_stream) << m_line << std::endl; - (*m_message_stream) << "line number is " << m_line_number << std::endl; - set_m_ok_to_false(); - return; - } - // uph, let us try to work with columns - if (rhsides.size() >= 22) { - std::string ss = rhsides.substr(0, 8); - std::string row_name = trim(ss); - auto t = m_rows.find(row_name); - - if (t == m_rows.end()) { - (*m_message_stream) << "cannot find " << row_name << std::endl; - goto fail; - } else { - row * row = t->second; - row->m_range = numeric_traits::from_string(rhsides.substr(8)); - maybe_modify_current_row_and_add_row_for_range(row); - if (rhsides.size() > 24) { - rhsides = rhsides.substr(25); - if (rhsides.size() >= 22) { - read_range_by_columns(rhsides); - } - } - } - } else { - fail: - set_m_ok_to_false(); - (*m_message_stream) << "cannot understand this line" << std::endl; - (*m_message_stream) << "line = " << m_line << ", line number is " << m_line_number << std::endl; - return; - } - } - - - void read_range(vector & splitted_line){ - for (unsigned i = 1; i < splitted_line.size() - 1; i += 2) { - auto it = m_rows.find(splitted_line[i]); - if (it == m_rows.end()) { - read_range_by_columns(m_line.substr(14)); - return; - } - row * row = it->second; - row->m_range = numeric_traits::from_string(splitted_line[i + 1]); - maybe_modify_current_row_and_add_row_for_range(row); - } - } - - void maybe_modify_current_row_and_add_row_for_range(row * row_with_range) { - unsigned index= static_cast(m_rows.size() - m_cost_line_count); - std::string row_name = row_with_range->m_name + "_range"; - row * other_bound_range_row; - switch (row_with_range->m_type) { - case row_type::Greater_or_equal: - m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index); - other_bound_range_row->m_right_side = row_with_range->m_right_side + abs(row_with_range->m_range); - break; - case row_type::Less_or_equal: - m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index); - other_bound_range_row->m_right_side = row_with_range->m_right_side - abs(row_with_range->m_range); - break; - case row_type::Equal: - if (row_with_range->m_range > 0) { - row_with_range->m_type = row_type::Greater_or_equal; // the existing row type change - m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index); - } else { // row->m_range < 0; - row_with_range->m_type = row_type::Less_or_equal; // the existing row type change - m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index); - } - other_bound_range_row->m_right_side = row_with_range->m_right_side + row_with_range->m_range; - break; - default: - (*m_message_stream) << "unexpected bound type " << row_with_range->m_type << " at line " << m_line_number << std::endl; - set_m_ok_to_false(); - throw; - } - - for (auto s : row_with_range->m_row_columns) { - lp_assert(m_columns.find(s.first) != m_columns.end()); - other_bound_range_row->m_row_columns[s.first] = s.second; - } - } - - void add_row() { - if (m_line.length() < 2) { - return; - } - - m_line = trim(m_line); - char c = m_line[0]; - m_line = m_line.substr(1); - m_line = trim(m_line); - add_row(c); - } - - void add_row(char c) { - unsigned index= static_cast(m_rows.size() - m_cost_line_count); - switch (c) { - case 'E': - m_rows[m_line] = new row(row_type::Equal, m_line, index); - break; - case 'L': - m_rows[m_line] = new row(row_type::Less_or_equal, m_line, index); - break; - case 'G': - m_rows[m_line] = new row(row_type::Greater_or_equal, m_line, index); - break; - case 'N': - m_rows[m_line] = new row(row_type::Cost, m_line, index); - m_cost_row_name = m_line; - m_cost_line_count++; - break; - } - } - unsigned range_count() { - unsigned ret = 0; - for (auto s : m_rows) { - if (s.second->m_range != 0) { - ret++; - } - } - return ret; - } - - /* - If rhs is a constraint's right-hand-side value and range is the constraint's range value, then the range interval is defined according to the following table: - sense interval - G [rhs, rhs + |range|] - L [rhs - |range|, rhs] - E [rhs, rhs + |range|] if range > 0, - [rhs - |range|, rhs] if range < 0 - where |range| is range's absolute value. - */ - - lp_relation get_relation_from_row(row_type rt) { - switch (rt) { - case mps_reader::Less_or_equal: return lp_relation::Less_or_equal; - case mps_reader::Greater_or_equal: return lp_relation::Greater_or_equal; - case mps_reader::Equal: return lp_relation::Equal; - default: - (*m_message_stream) << "Unexpected rt " << rt << std::endl; - set_m_ok_to_false(); - throw; - } - } - - unsigned solver_row_count() { - return m_rows.size() - m_cost_line_count + range_count(); - } - - void fill_solver_on_row(row * row, lp_solver *solver) { - if (row->m_name != m_cost_row_name) { - solver->add_constraint(get_relation_from_row(row->m_type), row->m_right_side, row->m_index); - for (auto s : row->m_row_columns) { - lp_assert(m_columns.find(s.first) != m_columns.end()); - solver->set_row_column_coefficient(row->m_index, m_columns[s.first]->m_index, s.second); - } - } else { - set_solver_cost(row, solver); - } - } - - T abs(T & t) { return t < numeric_traits::zero() ? -t: t; } - - void fill_solver_on_rows(lp_solver * solver) { - for (auto row_it : m_rows) { - fill_solver_on_row(row_it.second, solver); - } - } - - - void fill_solver_on_columns(lp_solver * solver){ - for (auto s : m_columns) { - mps_reader::column * col = s.second; - unsigned index = col->m_index; - set_boundary_for_column(index, col->m_bound, solver); - // optional call - solver->give_symbolic_name_to_column(col->m_name, col->m_index); - } - } - - void fill_solver(lp_solver *solver) { - fill_solver_on_rows(solver); - fill_solver_on_columns(solver); - } - - void set_solver_cost(row * row, lp_solver *solver) { - for (auto s : row->m_row_columns) { - std::string name = s.first; - lp_assert(m_columns.find(name) != m_columns.end()); - mps_reader::column * col = m_columns[name]; - solver->set_cost_for_column(col->m_index, s.second); - } - } - -public: - - void set_message_stream(std::ostream * o) { - lp_assert(o != nullptr); - m_message_stream = o; - } - vector column_names() { - vector v; - for (auto s : m_columns) { - v.push_back(s.first); - } - return v; - } - - ~mps_reader() { - for (auto s : m_rows) { - delete s.second; - } - for (auto s : m_columns) { - auto col = s.second; - delete col->m_bound; - delete col; - } - } - - mps_reader(const std::string & file_name): - m_is_OK(true), - m_file_name(file_name), - m_file_stream(file_name), - m_cost_line_count(0), - m_line_number(0), - m_message_stream(& std::cout) {} - void read() { - if (!m_file_stream.is_open()){ - set_m_ok_to_false(); - return; - } - - read_name(); - read_rows(); - read_columns(); - read_rhs(); - if (m_line.find("BOUNDS") == 0) { - read_bounds(); - read_ranges(); - } else if (m_line.find("RANGES") == 0) { - read_ranges(); - read_bounds(); - } - } - - bool is_ok() { - return m_is_OK; - } - - lp_solver * create_solver(bool dual) { - lp_solver * solver = dual? (lp_solver*)new lp_dual_simplex() : new lp_primal_simplex(); - fill_solver(solver); - return solver; - } - - lconstraint_kind get_lar_relation_from_row(row_type rt) { - switch (rt) { - case Less_or_equal: return LE; - case Greater_or_equal: return GE; - case Equal: return EQ; - default: - (*m_message_stream) << "Unexpected rt " << rt << std::endl; - set_m_ok_to_false(); - throw; - } - } - - unsigned get_var_index(std::string s) { - auto it = m_names_to_var_index.find(s); - if (it != m_names_to_var_index.end()) - return it->second; - unsigned ret = static_cast(m_names_to_var_index.size()); - m_names_to_var_index[s] = ret; - return ret; - } - - void fill_lar_solver_on_row(row * row, lar_solver *solver, int row_index) { - if (row->m_name != m_cost_row_name) { - auto kind = get_lar_relation_from_row(row->m_type); - vector> ls; - for (auto s : row->m_row_columns) { - var_index i = solver->add_var(get_var_index(s.first), false); - ls.push_back(std::make_pair(s.second, i)); - } - unsigned j = solver->add_term(ls, row_index); - solver->add_var_bound(j, kind, row->m_right_side); - } else { - // ignore the cost row - } - } - - - void fill_lar_solver_on_rows(lar_solver * solver) { - int row_index = 0; - for (auto row_it : m_rows) { - fill_lar_solver_on_row(row_it.second, solver, row_index++); - } - } - - void create_low_constraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index, false); - solver->add_var_bound(i, GE, b->m_low); - } - - void create_upper_constraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index, false); - solver->add_var_bound(i, LE, b->m_upper); - } - - void create_equality_contraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index, false); - solver->add_var_bound(i, LE, b->m_fixed_value); - solver->add_var_bound(i, GE, b->m_fixed_value); - } - - void fill_lar_solver_on_columns(lar_solver * solver) { - for (auto s : m_columns) { - mps_reader::column * col = s.second; - solver->add_var(col->m_index, false); - auto b = col->m_bound; - if (b == nullptr) return; - - if (b->m_free) continue; - - if (b->m_low_is_set) { - create_low_constraint_for_var(col, b, solver); - } - if (b->m_upper_is_set) { - create_upper_constraint_for_var(col, b, solver); - } - if (b->m_value_is_fixed) { - create_equality_contraint_for_var(col, b, solver); - } - } - } - - - void fill_lar_solver(lar_solver * solver) { - fill_lar_solver_on_columns(solver); - fill_lar_solver_on_rows(solver); - } - - lar_solver * create_lar_solver() { - lar_solver * solver = new lar_solver(); - fill_lar_solver(solver); - return solver; - } -}; -} diff --git a/src/math/lp/nla_core.cpp b/src/math/lp/nla_core.cpp index 386ad296d..4d1cc6edb 100644 --- a/src/math/lp/nla_core.cpp +++ b/src/math/lp/nla_core.cpp @@ -1745,6 +1745,13 @@ bool core::influences_nl_var(lpvar j) const { return false; } +void core::set_use_nra_model(bool m) { + if (m != m_use_nra_model) { + trail().push(value_trail(m_use_nra_model)); + m_use_nra_model = m; + } +} + void core::collect_statistics(::statistics & st) { st.update("arith-nla-explanations", m_stats.m_nla_explanations); st.update("arith-nla-lemmas", m_stats.m_nla_lemmas); diff --git a/src/math/lp/nla_core.h b/src/math/lp/nla_core.h index 016ff6e9d..938bcbe83 100644 --- a/src/math/lp/nla_core.h +++ b/src/math/lp/nla_core.h @@ -418,7 +418,7 @@ public: bool var_is_big(lpvar) const; bool has_real(const factorization&) const; bool has_real(const monic& m) const; - void set_use_nra_model(bool m) { m_use_nra_model = m; } + void set_use_nra_model(bool m); bool use_nra_model() const { return m_use_nra_model; } void collect_statistics(::statistics&); private: diff --git a/src/math/lp/nla_divisions.cpp b/src/math/lp/nla_divisions.cpp index 91b674d58..cbb30d9d9 100644 --- a/src/math/lp/nla_divisions.cpp +++ b/src/math/lp/nla_divisions.cpp @@ -58,10 +58,11 @@ namespace nla { auto monotonicity1 = [&](auto x1, auto& x1val, auto y1, auto& y1val, auto& q1, auto& q1val, auto x2, auto& x2val, auto y2, auto& y2val, auto& q2, auto& q2val) { - if (y1val >= y2val && y2val > 0 && x1val <= x2val && q1val > q2val) { - new_lemma lemma(c, "y1 >= y2 > 0 & x1 <= x2 => x1/y1 <= x2/y2"); + if (y1val >= y2val && y2val > 0 && 0 <= x1val && x1val <= x2val && q1val > q2val) { + new_lemma lemma(c, "y1 >= y2 > 0 & 0 <= x1 <= x2 => x1/y1 <= x2/y2"); lemma |= ineq(term(y1, rational(-1), y2), llc::LT, 0); lemma |= ineq(y2, llc::LE, 0); + lemma |= ineq(x1, llc::LT, 0); lemma |= ineq(term(x1, rational(-1), x2), llc::GT, 0); lemma |= ineq(term(q1, rational(-1), q2), llc::LE, 0); return true; diff --git a/src/math/lp/nla_powers.cpp b/src/math/lp/nla_powers.cpp index 3af31a488..f389aad93 100644 --- a/src/math/lp/nla_powers.cpp +++ b/src/math/lp/nla_powers.cpp @@ -79,8 +79,11 @@ am().set(rval, am_value(r)); namespace nla { lbool powers::check(lpvar r, lpvar x, lpvar y, vector& lemmas) { + TRACE("nla", tout << r << " == " << x << "^" << y << "\n"); if (x == null_lpvar || y == null_lpvar || r == null_lpvar) return l_undef; + if (lp::tv::is_term(x) || lp::tv::is_term(y) || lp::tv::is_term(r)) + return l_undef; core& c = m_core; if (c.use_nra_model()) @@ -143,17 +146,24 @@ namespace nla { auto r2val = power(xval, yval.get_unsigned()); if (rval == r2val) return l_true; - if (xval > 0 && r2val < rval) { - SASSERT(yval > 0); + if (c.random() % 2 == 0) { + new_lemma lemma(c, "x == x0, y == y0 => r = x0^y0"); + lemma |= ineq(x, llc::NE, xval); + lemma |= ineq(y, llc::NE, yval); + lemma |= ineq(r, llc::EQ, r2val); + return l_false; + } + if (yval > 0 && r2val > rval) { new_lemma lemma(c, "x >= x0 > 0, y >= y0 > 0 => r >= x0^y0"); lemma |= ineq(x, llc::LT, xval); lemma |= ineq(y, llc::LT, yval); lemma |= ineq(r, llc::GE, r2val); return l_false; } - if (xval > 0 && r2val < rval) { - new_lemma lemma(c, "x >= x0 > 0, y <= y0 => r <= x0^y0"); - lemma |= ineq(x, llc::LT, xval); + if (r2val < rval) { + new_lemma lemma(c, "0 < x <= x0, y <= y0 => r <= x0^y0"); + lemma |= ineq(x, llc::LE, rational::zero()); + lemma |= ineq(x, llc::GT, xval); lemma |= ineq(y, llc::GT, yval); lemma |= ineq(r, llc::LE, r2val); return l_false; diff --git a/src/math/lp/nra_solver.cpp b/src/math/lp/nra_solver.cpp index 56a84d1f0..1f4e0b76a 100644 --- a/src/math/lp/nra_solver.cpp +++ b/src/math/lp/nra_solver.cpp @@ -171,7 +171,7 @@ struct solver::imp { lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); break; default: - lp_assert(false); // unreachable + UNREACHABLE(); // unreachable } m_nlsat->mk_clause(1, &lit, a); } diff --git a/src/math/lp/numeric_pair.h b/src/math/lp/numeric_pair.h index 9f7e27c18..251274006 100644 --- a/src/math/lp/numeric_pair.h +++ b/src/math/lp/numeric_pair.h @@ -45,7 +45,6 @@ template class numeric_traits {}; template <> class numeric_traits { public: - static bool precise() { return true; } static unsigned zero() { return 0; } static unsigned one() { return 1; } static bool is_zero(unsigned v) { return v == 0; } @@ -56,7 +55,6 @@ public: template <> class numeric_traits { public: - static bool precise() { return true; } static int zero() { return 0; } static int one() { return 1; } static bool is_zero(int v) { return v == 0; } @@ -71,7 +69,6 @@ public: template <> class numeric_traits { public: - static bool precise() { return false; } static double g_zero; static double const &zero() { return g_zero; } static double g_one; @@ -88,7 +85,6 @@ public: template<> class numeric_traits { public: - static bool precise() { return true; } static rational const & zero() { return rational::zero(); } static rational const & one() { return rational::one(); } static bool is_zero(const rational & v) { return v.is_zero(); } @@ -111,21 +107,8 @@ public: template struct convert_struct { static X convert(const Y & y){ return X(y);} - static bool is_epsilon_small(const X & x, const double & y) { return std::abs(numeric_traits::get_double(x)) < y; } - static bool below_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false;} - static bool above_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false; } -}; - - -template <> -struct convert_struct { - static double convert(const mpq & q) {return q.get_double();} -}; - - -template <> -struct convert_struct { - static mpq convert(unsigned q) {return mpq(q);} + static bool below_bound_numeric(const X &, const X &, const Y &) { /*UNREACHABLE();*/ return false;} + static bool above_bound_numeric(const X &, const X &, const Y &) { /*UNREACHABLE();*/ return false; } }; @@ -207,7 +190,7 @@ struct numeric_pair { } numeric_pair operator/(const numeric_pair &) const { - // lp_unreachable(); + // UNREACHABLE(); } @@ -216,7 +199,7 @@ struct numeric_pair { } numeric_pair operator*(const numeric_pair & /*a*/) const { - // lp_unreachable(); + // UNREACHABLE(); } numeric_pair& operator+=(const numeric_pair & a) { @@ -251,8 +234,6 @@ struct numeric_pair { return numeric_pair(-x, -y); } - static bool precize() { return lp::numeric_traits::precize();} - bool is_zero() const { return x.is_zero() && y.is_zero(); } bool is_pos() const { return x.is_pos() || (x.is_zero() && y.is_pos());} @@ -294,16 +275,14 @@ numeric_pair operator/(const numeric_pair & r, const X & a) { return numeric_pair(r.x / a, r.y / a); } -// template bool precise() { return numeric_traits::precise();} -template double get_double(const lp::numeric_pair & ) { /* lp_unreachable(); */ return 0;} +template double get_double(const lp::numeric_pair & ) { /* UNREACHABLE(); */ return 0;} template class numeric_traits> { public: - static bool precise() { return numeric_traits::precise();} static lp::numeric_pair zero() { return lp::numeric_pair(numeric_traits::zero(), numeric_traits::zero()); } static bool is_zero(const lp::numeric_pair & v) { return numeric_traits::is_zero(v.x) && numeric_traits::is_zero(v.y); } static double get_double(const lp::numeric_pair & v){ return numeric_traits::get_double(v.x); } // just return the double of the first coordinate - static double one() { /*lp_unreachable();*/ return 0;} + static double one() { /*UNREACHABLE();*/ return 0;} static bool is_pos(const numeric_pair &p) { return numeric_traits::is_pos(p.x) || (numeric_traits::is_zero(p.x) && numeric_traits::is_pos(p.y)); @@ -317,83 +296,8 @@ public: } }; -template <> -struct convert_struct> { - static double convert(const numeric_pair & q) {return q.x;} -}; - typedef numeric_pair impq; -template bool is_epsilon_small(const X & v, const double& eps); // forward definition { return convert_struct::is_epsilon_small(v, eps);} - -template -struct convert_struct, double> { - static numeric_pair convert(const double & q) { - return numeric_pair(convert_struct::convert(q), numeric_traits::zero()); - } - static bool is_epsilon_small(const numeric_pair & p, const double & eps) { - return convert_struct::is_epsilon_small(p.x, eps) && convert_struct::is_epsilon_small(p.y, eps); - } - static bool below_bound_numeric(const numeric_pair &, const numeric_pair &, const double &) { - // lp_unreachable(); - return false; - } - static bool above_bound_numeric(const numeric_pair &, const numeric_pair &, const double &) { - // lp_unreachable(); - return false; - } -}; -template <> -struct convert_struct, double> { - static numeric_pair convert(const double & q) { - return numeric_pair(q, 0.0); - } - static bool is_epsilon_small(const numeric_pair & p, const double & eps) { - return std::abs(p.x) < eps && std::abs(p.y) < eps; - } - - static int compare_on_coord(const double & x, const double & bound, const double eps) { - if (bound == 0) return (x < - eps)? -1: (x > eps? 1 : 0); // it is an important special case - double relative = (bound > 0)? - eps: eps; - return (x < bound * (1.0 + relative) - eps)? -1 : ((x > bound * (1.0 - relative) + eps)? 1 : 0); - } - - static bool below_bound_numeric(const numeric_pair & x, const numeric_pair & bound, const double & eps) { - int r = compare_on_coord(x.x, bound.x, eps); - if (r == 1) return false; - if (r == -1) return true; - // the first coordinates are almost the same - return compare_on_coord(x.y, bound.y, eps) == -1; - } - - static bool above_bound_numeric(const numeric_pair & x, const numeric_pair & bound, const double & eps) { - int r = compare_on_coord(x.x, bound.x, eps); - if (r == -1) return false; - if (r == 1) return true; - // the first coordinates are almost the same - return compare_on_coord(x.y, bound.y, eps) == 1; - } -}; - -template <> -struct convert_struct { - static bool is_epsilon_small(const double& x, const double & eps) { - return x < eps && x > -eps; - } - static double convert(const double & y){ return y;} - static bool below_bound_numeric(const double & x, const double & bound, const double & eps) { - if (bound == 0) return x < - eps; - double relative = (bound > 0)? - eps: eps; - return x < bound * (1.0 + relative) - eps; - } - static bool above_bound_numeric(const double & x, const double & bound, const double & eps) { - if (bound == 0) return x > eps; - double relative = (bound > 0)? eps: - eps; - return x > bound * (1.0 + relative) + eps; - } -}; - -template bool is_epsilon_small(const X & v, const double &eps) { return convert_struct::is_epsilon_small(v, eps);} template bool below_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct::below_bound_numeric(x, bound, eps);} template bool above_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct::above_bound_numeric(x, bound, eps);} template T floor(const numeric_pair & r) { diff --git a/src/math/lp/permutation_matrix.cpp b/src/math/lp/permutation_matrix.cpp index 28319c2ee..be4b7335c 100644 --- a/src/math/lp/permutation_matrix.cpp +++ b/src/math/lp/permutation_matrix.cpp @@ -21,50 +21,11 @@ Revision History: #include "util/vector.h" #include "math/lp/permutation_matrix_def.h" #include "math/lp/numeric_pair.h" -template void lp::permutation_matrix::apply_from_right(vector&); -template void lp::permutation_matrix::init(unsigned int); template void lp::permutation_matrix::init(unsigned int); template void lp::permutation_matrix>::init(unsigned int); -template bool lp::permutation_matrix::is_identity() const; -template void lp::permutation_matrix::multiply_by_permutation_from_left(lp::permutation_matrix&); -template void lp::permutation_matrix::multiply_by_permutation_reverse_from_left(lp::permutation_matrix&); -template void lp::permutation_matrix::multiply_by_reverse_from_right(lp::permutation_matrix&); -template lp::permutation_matrix::permutation_matrix(unsigned int, vector const&); -template void lp::permutation_matrix::transpose_from_left(unsigned int, unsigned int); -template void lp::permutation_matrix::apply_from_right(vector&); -template bool lp::permutation_matrix::is_identity() const; -template void lp::permutation_matrix::multiply_by_permutation_from_left(lp::permutation_matrix&); -template void lp::permutation_matrix::multiply_by_permutation_from_right(lp::permutation_matrix&); -template void lp::permutation_matrix::multiply_by_permutation_reverse_from_left(lp::permutation_matrix&); -template void lp::permutation_matrix::multiply_by_reverse_from_right(lp::permutation_matrix&); template lp::permutation_matrix::permutation_matrix(unsigned int); template void lp::permutation_matrix::transpose_from_left(unsigned int, unsigned int); template void lp::permutation_matrix::transpose_from_right(unsigned int, unsigned int); -template void lp::permutation_matrix >::apply_from_right(vector&); -template bool lp::permutation_matrix >::is_identity() const; -template void lp::permutation_matrix >::multiply_by_permutation_from_left(lp::permutation_matrix >&); -template void lp::permutation_matrix >::multiply_by_permutation_from_right(lp::permutation_matrix >&); -template void lp::permutation_matrix >::multiply_by_permutation_reverse_from_left(lp::permutation_matrix >&); -template void lp::permutation_matrix >::multiply_by_reverse_from_right(lp::permutation_matrix >&); template lp::permutation_matrix >::permutation_matrix(unsigned int); template void lp::permutation_matrix >::transpose_from_left(unsigned int, unsigned int); -template void lp::permutation_matrix >::transpose_from_right(unsigned int, unsigned int); -template void lp::permutation_matrix::apply_reverse_from_left(lp::indexed_vector&); -template void lp::permutation_matrix::apply_reverse_from_left_to_T(vector&); -template void lp::permutation_matrix::apply_reverse_from_right_to_T(vector&); -template void lp::permutation_matrix::transpose_from_right(unsigned int, unsigned int); -template void lp::permutation_matrix::apply_reverse_from_left(lp::indexed_vector&); -template void lp::permutation_matrix::apply_reverse_from_left_to_T(vector&); -template void lp::permutation_matrix::apply_reverse_from_right_to_T(vector&); -template void lp::permutation_matrix >::apply_reverse_from_left(lp::indexed_vector&); -template void lp::permutation_matrix >::apply_reverse_from_left_to_T(vector&); -template void lp::permutation_matrix >::apply_reverse_from_right_to_T(vector&); -template void lp::permutation_matrix::multiply_by_permutation_from_right(lp::permutation_matrix&); -template lp::permutation_matrix::permutation_matrix(unsigned int); -template void lp::permutation_matrix::apply_reverse_from_left_to_X(vector &); -template void lp::permutation_matrix< lp::mpq, lp::mpq>::apply_reverse_from_left_to_X(vector &); -template void lp::permutation_matrix< lp::mpq, lp::numeric_pair< lp::mpq> >::apply_reverse_from_left_to_X(vector> &); -template void lp::permutation_matrix::apply_reverse_from_right_to_T(lp::indexed_vector&); -template void lp::permutation_matrix::apply_reverse_from_right_to_T(lp::indexed_vector&); -template void lp::permutation_matrix >::apply_reverse_from_right_to_T(lp::indexed_vector&); diff --git a/src/math/lp/permutation_matrix.h b/src/math/lp/permutation_matrix.h index 8ec78c14a..a3fff4f7f 100644 --- a/src/math/lp/permutation_matrix.h +++ b/src/math/lp/permutation_matrix.h @@ -22,24 +22,19 @@ Revision History: #include #include "util/debug.h" #include -#include "math/lp/sparse_vector.h" #include "math/lp/indexed_vector.h" #include "math/lp/lp_settings.h" #include "math/lp/matrix.h" -#include "math/lp/tail_matrix.h" namespace lp { -#ifdef Z3DEBUG - inline bool is_even(int k) { return (k/2)*2 == k; } -#endif - template -class permutation_matrix : public tail_matrix { +template +class permutation_matrix +#ifdef Z3DEBUG + : public matrix +#endif +{ vector m_permutation; vector m_rev; - vector m_work_array; - vector m_T_buffer; - vector m_X_buffer; - class ref { permutation_matrix & m_p; @@ -64,42 +59,15 @@ class permutation_matrix : public tail_matrix { // create a unit permutation of the given length void init(unsigned length); unsigned get_rev(unsigned i) { return m_rev[i]; } - bool is_dense() const override { return false; } -#ifdef Z3DEBUG - permutation_matrix get_inverse() const { - return permutation_matrix(size(), m_rev); - } + +#ifdef Z3DEBUG void print(std::ostream & out) const; #endif ref operator[](unsigned i) { return ref(*this, i); } unsigned operator[](unsigned i) const { return m_permutation[i]; } - - void apply_from_left(vector & w, lp_settings &) override; - - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override; - - void apply_from_right(vector & w) override; - - void apply_from_right(indexed_vector & w) override; - template - void copy_aside(vector & t, vector & tmp_index, indexed_vector & w); - - template - void clear_data(indexed_vector & w); - - template - void apply_reverse_from_left(indexed_vector & w); - - void apply_reverse_from_left_to_T(vector & w); - void apply_reverse_from_left_to_X(vector & w); - - void apply_reverse_from_right_to_T(vector & w); - void apply_reverse_from_right_to_T(indexed_vector & w); - void apply_reverse_from_right_to_X(vector & w); - void set_val(unsigned i, unsigned pi) { lp_assert(i < size() && pi < size()); m_permutation[i] = pi; m_rev[pi] = i; } @@ -117,18 +85,6 @@ class permutation_matrix : public tail_matrix { void set_number_of_rows(unsigned /*m*/) override { } void set_number_of_columns(unsigned /*n*/) override { } #endif - void multiply_by_permutation_from_left(permutation_matrix & p); - - // this is multiplication in the matrix sense - void multiply_by_permutation_from_right(permutation_matrix & p); - - void multiply_by_reverse_from_right(permutation_matrix & q); - - void multiply_by_permutation_reverse_from_left(permutation_matrix & r); - - void shrink_by_one_identity(); - - bool is_identity() const; unsigned size() const { return static_cast(m_rev.size()); } @@ -136,8 +92,6 @@ class permutation_matrix : public tail_matrix { unsigned old_size = m_permutation.size(); m_permutation.resize(size); m_rev.resize(size); - m_T_buffer.resize(size); - m_X_buffer.resize(size); for (unsigned i = old_size; i < size; i++) { m_permutation[i] = m_rev[i] = i; } diff --git a/src/math/lp/permutation_matrix_def.h b/src/math/lp/permutation_matrix_def.h index 703830ffc..c86fef4f4 100644 --- a/src/math/lp/permutation_matrix_def.h +++ b/src/math/lp/permutation_matrix_def.h @@ -22,13 +22,13 @@ Revision History: #include "util/vector.h" #include "math/lp/permutation_matrix.h" namespace lp { -template permutation_matrix::permutation_matrix(unsigned length): m_permutation(length), m_rev(length), m_T_buffer(length), m_X_buffer(length) { +template permutation_matrix::permutation_matrix(unsigned length): m_permutation(length), m_rev(length) { for (unsigned i = 0; i < length; i++) { // do not change the direction of the loop because of the vectorization bug in clang3.3 m_permutation[i] = m_rev[i] = i; } } -template permutation_matrix::permutation_matrix(unsigned length, vector const & values): m_permutation(length), m_rev(length) , m_T_buffer(length), m_X_buffer(length) { +template permutation_matrix::permutation_matrix(unsigned length, vector const & values): m_permutation(length), m_rev(length) { for (unsigned i = 0; i < length; i++) { set_val(i, values[i]); } @@ -37,8 +37,6 @@ template permutation_matrix::permutation_matrix(u template void permutation_matrix::init(unsigned length) { m_permutation.resize(length); m_rev.resize(length); - m_T_buffer.resize(length); - m_X_buffer.resize(length); for (unsigned i = 0; i < length; i++) { m_permutation[i] = m_rev[i] = i; } @@ -59,213 +57,6 @@ template void permutation_matrix::print(std::ostr } #endif -template -void permutation_matrix::apply_from_left(vector & w, lp_settings & ) { -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // L * deb_w = clone_vector(w, row_count()); - // deb.apply_from_left(deb_w); -#endif - lp_assert(m_X_buffer.size() == w.size()); - unsigned i = size(); - while (i-- > 0) { - m_X_buffer[i] = w[m_permutation[i]]; - } - i = size(); - while (i-- > 0) { - w[i] = m_X_buffer[i]; - } -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(deb_w, w, row_count())); - // delete [] deb_w; -#endif -} - -template -void permutation_matrix::apply_from_left_to_T(indexed_vector & w, lp_settings & ) { - vector t(w.m_index.size()); - vector tmp_index(w.m_index.size()); - copy_aside(t, tmp_index, w); // todo: is it too much copying - clear_data(w); - // set the new values - for (unsigned i = static_cast(t.size()); i > 0;) { - i--; - unsigned j = m_rev[tmp_index[i]]; - w[j] = t[i]; - w.m_index[i] = j; - } -} - -template void permutation_matrix::apply_from_right(vector & w) { -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // T * deb_w = clone_vector(w, row_count()); - // deb.apply_from_right(deb_w); -#endif - lp_assert(m_T_buffer.size() == w.size()); - for (unsigned i = 0; i < size(); i++) { - m_T_buffer[i] = w[m_rev[i]]; - } - - for (unsigned i = 0; i < size(); i++) { - w[i] = m_T_buffer[i]; - } -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(deb_w, w, row_count())); - // delete [] deb_w; -#endif -} - -template void permutation_matrix::apply_from_right(indexed_vector & w) { -#ifdef Z3DEBUG - vector wcopy(w.m_data); - apply_from_right(wcopy); -#endif - vector buffer(w.m_index.size()); - vector index_copy(w.m_index); - for (unsigned i = 0; i < w.m_index.size(); i++) { - buffer[i] = w.m_data[w.m_index[i]]; - } - w.clear(); - - for (unsigned i = 0; i < index_copy.size(); i++) { - unsigned j = index_copy[i]; - unsigned pj = m_permutation[j]; - w.set_value(buffer[i], pj); - } - lp_assert(w.is_OK()); -#ifdef Z3DEBUG - lp_assert(vectors_are_equal(wcopy, w.m_data)); -#endif -} - - -template template -void permutation_matrix::copy_aside(vector & t, vector & tmp_index, indexed_vector & w) { - for (unsigned i = static_cast(t.size()); i > 0;) { - i--; - unsigned j = w.m_index[i]; - t[i] = w[j]; // copy aside all non-zeroes - tmp_index[i] = j; // and the indices too - } -} - -template template -void permutation_matrix::clear_data(indexed_vector & w) { - // clear old non-zeroes - for (unsigned i = static_cast(w.m_index.size()); i > 0;) { - i--; - unsigned j = w.m_index[i]; - w[j] = zero_of_type(); - } -} - -template template -void permutation_matrix::apply_reverse_from_left(indexed_vector & w) { - // the result will be w = p(-1) * w -#ifdef Z3DEBUG - // dense_matrix deb(get_reverse()); - // L * deb_w = clone_vector(w.m_data, row_count()); - // deb.apply_from_left(deb_w); -#endif - vector t(w.m_index.size()); - vector tmp_index(w.m_index.size()); - - copy_aside(t, tmp_index, w); - clear_data(w); - - // set the new values - for (unsigned i = static_cast(t.size()); i > 0;) { - i--; - unsigned j = m_permutation[tmp_index[i]]; - w[j] = t[i]; - w.m_index[i] = j; - } -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(deb_w, w.m_data, row_count())); - // delete [] deb_w; -#endif -} - -template -void permutation_matrix::apply_reverse_from_left_to_T(vector & w) { - // the result will be w = p(-1) * w - lp_assert(m_T_buffer.size() == w.size()); - unsigned i = size(); - while (i-- > 0) { - m_T_buffer[m_permutation[i]] = w[i]; - } - i = size(); - while (i-- > 0) { - w[i] = m_T_buffer[i]; - } -} -template -void permutation_matrix::apply_reverse_from_left_to_X(vector & w) { - // the result will be w = p(-1) * w - lp_assert(m_X_buffer.size() == w.size()); - unsigned i = size(); - while (i-- > 0) { - m_X_buffer[m_permutation[i]] = w[i]; - } - i = size(); - while (i-- > 0) { - w[i] = m_X_buffer[i]; - } -} - -template -void permutation_matrix::apply_reverse_from_right_to_T(vector & w) { - // the result will be w = w * p(-1) - lp_assert(m_T_buffer.size() == w.size()); - unsigned i = size(); - while (i-- > 0) { - m_T_buffer[i] = w[m_permutation[i]]; - } - i = size(); - while (i-- > 0) { - w[i] = m_T_buffer[i]; - } -} - -template -void permutation_matrix::apply_reverse_from_right_to_T(indexed_vector & w) { - // the result will be w = w * p(-1) -#ifdef Z3DEBUG - // vector wcopy(w.m_data); - // apply_reverse_from_right_to_T(wcopy); -#endif - lp_assert(w.is_OK()); - vector tmp; - vector tmp_index(w.m_index); - for (auto i : w.m_index) { - tmp.push_back(w[i]); - } - w.clear(); - - for (unsigned k = 0; k < tmp_index.size(); k++) { - unsigned j = tmp_index[k]; - w.set_value(tmp[k], m_rev[j]); - } - - // lp_assert(w.is_OK()); - // lp_assert(vectors_are_equal(w.m_data, wcopy)); -} - - -template -void permutation_matrix::apply_reverse_from_right_to_X(vector & w) { - // the result will be w = w * p(-1) - lp_assert(m_X_buffer.size() == w.size()); - unsigned i = size(); - while (i-- > 0) { - m_X_buffer[i] = w[m_permutation[i]]; - } - i = size(); - while (i-- > 0) { - w[i] = m_X_buffer[i]; - } -} template void permutation_matrix::transpose_from_left(unsigned i, unsigned j) { // the result will be this = (i,j)*this @@ -285,55 +76,5 @@ template void permutation_matrix::transpose_from_ set_val(j, pi); } -template void permutation_matrix::multiply_by_permutation_from_left(permutation_matrix & p) { - m_work_array = m_permutation; - lp_assert(p.size() == size()); - unsigned i = size(); - while (i-- > 0) { - set_val(i, m_work_array[p[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation - } -} - -// this is multiplication in the matrix sense -template void permutation_matrix::multiply_by_permutation_from_right(permutation_matrix & p) { - m_work_array = m_permutation; - lp_assert(p.size() == size()); - unsigned i = size(); - while (i-- > 0) - set_val(i, p[m_work_array[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation - -} - -template void permutation_matrix::multiply_by_reverse_from_right(permutation_matrix & q){ // todo : condensed permutations ? - lp_assert(q.size() == size()); - m_work_array = m_permutation; - // the result is this = this*q(-1) - unsigned i = size(); - while (i-- > 0) { - set_val(i, q.m_rev[m_work_array[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation - } -} - -template void permutation_matrix::multiply_by_permutation_reverse_from_left(permutation_matrix & r){ // todo : condensed permutations? - // the result is this = r(-1)*this - m_work_array = m_permutation; - // the result is this = this*q(-1) - unsigned i = size(); - while (i-- > 0) { - set_val(i, m_work_array[r.m_rev[i]]); - } -} - - -template bool permutation_matrix::is_identity() const { - unsigned i = size(); - while (i-- > 0) { - if (m_permutation[i] != i) { - return false; - } - } - return true; -} - } diff --git a/src/math/lp/row_eta_matrix.cpp b/src/math/lp/row_eta_matrix.cpp deleted file mode 100644 index 6fafb83ed..000000000 --- a/src/math/lp/row_eta_matrix.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "util/vector.h" -#include "math/lp/row_eta_matrix_def.h" -#include "math/lp/lu.h" -namespace lp { -template void row_eta_matrix::conjugate_by_permutation(permutation_matrix&); -template void row_eta_matrix >::conjugate_by_permutation(permutation_matrix >&); -template void row_eta_matrix::conjugate_by_permutation(permutation_matrix&); -#ifdef Z3DEBUG -template mpq row_eta_matrix::get_elem(unsigned int, unsigned int) const; -template mpq row_eta_matrix >::get_elem(unsigned int, unsigned int) const; -template double row_eta_matrix::get_elem(unsigned int, unsigned int) const; -#endif -template void row_eta_matrix::apply_from_left(vector&, lp_settings&); -template void row_eta_matrix::apply_from_right(vector&); -template void row_eta_matrix::apply_from_right(indexed_vector&); -template void row_eta_matrix >::apply_from_left(vector>&, lp_settings&); -template void row_eta_matrix >::apply_from_right(vector&); -template void row_eta_matrix >::apply_from_right(indexed_vector&); -template void row_eta_matrix::apply_from_left(vector&, lp_settings&); -template void row_eta_matrix::apply_from_right(vector&); -template void row_eta_matrix::apply_from_right(indexed_vector&); -template void row_eta_matrix::apply_from_left_to_T(indexed_vector&, lp_settings&); -template void row_eta_matrix::apply_from_left_local_to_T(indexed_vector&, lp_settings&); -template void row_eta_matrix >::apply_from_left_to_T(indexed_vector&, lp_settings&); -template void row_eta_matrix >::apply_from_left_local_to_T(indexed_vector&, lp_settings&); -template void row_eta_matrix::apply_from_left_to_T(indexed_vector&, lp_settings&); -template void row_eta_matrix::apply_from_left_local_to_T(indexed_vector&, lp_settings&); -} diff --git a/src/math/lp/row_eta_matrix.h b/src/math/lp/row_eta_matrix.h deleted file mode 100644 index 50c500f00..000000000 --- a/src/math/lp/row_eta_matrix.h +++ /dev/null @@ -1,89 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "util/debug.h" -#include -#include "math/lp/sparse_vector.h" -#include "math/lp/indexed_vector.h" -#include "math/lp/permutation_matrix.h" -namespace lp { - // This is the sum of a unit matrix and a lower triangular matrix - // with non-zero elements only in one row -template -class row_eta_matrix - : public tail_matrix { -#ifdef Z3DEBUG - unsigned m_dimension; -#endif - unsigned m_row_start; - unsigned m_row; - sparse_vector m_row_vector; -public: -#ifdef Z3DEBUG - row_eta_matrix(unsigned row_start, unsigned row, unsigned dim): -#else - row_eta_matrix(unsigned row_start, unsigned row): -#endif - -#ifdef Z3DEBUG - m_dimension(dim), -#endif - m_row_start(row_start), m_row(row) { - } - - bool is_dense() const override { return false; } - - void print(std::ostream & out) { - print_matrix(*this, out); - } - - const T & get_diagonal_element() const { - return m_row_vector.m_data[m_row]; - } - - void apply_from_left(vector & w, lp_settings &) override; - - void apply_from_left_local_to_T(indexed_vector & w, lp_settings & settings); - void apply_from_left_local_to_X(indexed_vector & w, lp_settings & settings); - - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override { - apply_from_left_local_to_T(w, settings); - } - - void push_back(unsigned row_index, T val ) { - lp_assert(row_index != m_row); - m_row_vector.push_back(row_index, val); - } - - void apply_from_right(vector & w) override; - void apply_from_right(indexed_vector & w) override; - - void conjugate_by_permutation(permutation_matrix & p); -#ifdef Z3DEBUG - T get_elem(unsigned row, unsigned col) const override; - unsigned row_count() const override { return m_dimension; } - unsigned column_count() const override { return m_dimension; } - void set_number_of_rows(unsigned m) override { m_dimension = m; } - void set_number_of_columns(unsigned n) override { m_dimension = n; } -#endif -}; // end of row_eta_matrix -} diff --git a/src/math/lp/row_eta_matrix_def.h b/src/math/lp/row_eta_matrix_def.h deleted file mode 100644 index faac5c6fe..000000000 --- a/src/math/lp/row_eta_matrix_def.h +++ /dev/null @@ -1,188 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "util/vector.h" -#include "math/lp/row_eta_matrix.h" -namespace lp { -template -void row_eta_matrix::apply_from_left(vector & w, lp_settings &) { - // #ifdef Z3DEBUG - // dense_matrix deb(*this); - // auto clone_w = clone_vector(w, m_dimension); - // deb.apply_from_left(clone_w, settings); - // #endif - - auto & w_at_row = w[m_row]; - for (auto & it : m_row_vector.m_data) { - w_at_row += w[it.first] * it.second; - } - // w[m_row] = w_at_row; - // #ifdef Z3DEBUG - // lp_assert(vectors_are_equal(clone_w, w, m_dimension)); - // delete [] clone_w; - // #endif -} - -template -void row_eta_matrix::apply_from_left_local_to_T(indexed_vector & w, lp_settings & settings) { - auto w_at_row = w[m_row]; - bool was_zero_at_m_row = is_zero(w_at_row); - - for (auto & it : m_row_vector.m_data) { - w_at_row += w[it.first] * it.second; - } - - if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){ - if (was_zero_at_m_row) { - w.m_index.push_back(m_row); - } - w[m_row] = w_at_row; - } else if (!was_zero_at_m_row){ - w[m_row] = zero_of_type(); - auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row); - w.m_index.erase(it); - } - // TBD: lp_assert(check_vector_for_small_values(w, settings)); -} - -template -void row_eta_matrix::apply_from_left_local_to_X(indexed_vector & w, lp_settings & settings) { - auto w_at_row = w[m_row]; - bool was_zero_at_m_row = is_zero(w_at_row); - - for (auto & it : m_row_vector.m_data) { - w_at_row += w[it.first] * it.second; - } - - if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){ - if (was_zero_at_m_row) { - w.m_index.push_back(m_row); - } - w[m_row] = w_at_row; - } else if (!was_zero_at_m_row){ - w[m_row] = zero_of_type(); - auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row); - w.m_index.erase(it); - } - // TBD: does not compile lp_assert(check_vector_for_small_values(w, settings)); -} - -template -void row_eta_matrix::apply_from_right(vector & w) { - const T & w_row = w[m_row]; - if (numeric_traits::is_zero(w_row)) return; -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // auto clone_w = clone_vector(w, m_dimension); - // deb.apply_from_right(clone_w); -#endif - for (auto & it : m_row_vector.m_data) { - w[it.first] += w_row * it.second; - } -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(clone_w, w, m_dimension)); - // delete clone_w; -#endif -} - -template -void row_eta_matrix::apply_from_right(indexed_vector & w) { - lp_assert(w.is_OK()); - const T & w_row = w[m_row]; - if (numeric_traits::is_zero(w_row)) return; -#ifdef Z3DEBUG - // vector wcopy(w.m_data); - // apply_from_right(wcopy); -#endif - if (numeric_traits::precise()) { - for (auto & it : m_row_vector.m_data) { - unsigned j = it.first; - bool was_zero = numeric_traits::is_zero(w[j]); - const T & v = w[j] += w_row * it.second; - - if (was_zero) { - if (!numeric_traits::is_zero(v)) - w.m_index.push_back(j); - } else { - if (numeric_traits::is_zero(v)) - w.erase_from_index(j); - } - } - } else { // the non precise version - const double drop_eps = 1e-14; - for (auto & it : m_row_vector.m_data) { - unsigned j = it.first; - bool was_zero = numeric_traits::is_zero(w[j]); - T & v = w[j] += w_row * it.second; - - if (was_zero) { - if (!lp_settings::is_eps_small_general(v, drop_eps)) - w.m_index.push_back(j); - else - v = zero_of_type(); - } else { - if (lp_settings::is_eps_small_general(v, drop_eps)) { - w.erase_from_index(j); - v = zero_of_type(); - } - } - } - } -#ifdef Z3DEBUG - // lp_assert(vectors_are_equal(wcopy, w.m_data)); - -#endif -} - -template -void row_eta_matrix::conjugate_by_permutation(permutation_matrix & p) { - // this = p * this * p(-1) -#ifdef Z3DEBUG - // auto rev = p.get_reverse(); - // auto deb = ((*this) * rev); - // deb = p * deb; -#endif - m_row = p.apply_reverse(m_row); - // copy aside the column indices - vector columns; - for (auto & it : m_row_vector.m_data) - columns.push_back(it.first); - for (unsigned i = static_cast(columns.size()); i-- > 0;) - m_row_vector.m_data[i].first = p.get_rev(columns[i]); -#ifdef Z3DEBUG - // lp_assert(deb == *this); -#endif -} -#ifdef Z3DEBUG -template -T row_eta_matrix::get_elem(unsigned row, unsigned col) const { - if (row == m_row){ - if (col == row) { - return numeric_traits::one(); - } - return m_row_vector[col]; - } - - return col == row ? numeric_traits::one() : numeric_traits::zero(); -} -#endif -} - diff --git a/src/math/lp/scaler.cpp b/src/math/lp/scaler.cpp deleted file mode 100644 index 46330e2a1..000000000 --- a/src/math/lp/scaler.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "math/lp/scaler_def.h" -template bool lp::scaler::scale(); -template bool lp::scaler::scale(); diff --git a/src/math/lp/scaler.h b/src/math/lp/scaler.h deleted file mode 100644 index dd21b5c28..000000000 --- a/src/math/lp/scaler.h +++ /dev/null @@ -1,94 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include -#include -#include /* printf, fopen */ -#include /* exit, EXIT_FAILURE */ -#include "math/lp/lp_utils.h" -#include "math/lp/static_matrix.h" -namespace lp { -// for scaling an LP -template -class scaler { - vector & m_b; // right side - static_matrix &m_A; // the constraint matrix - const T & m_scaling_minimum; - const T & m_scaling_maximum; - vector& m_column_scale; - lp_settings & m_settings; -public: - // constructor - scaler(vector & b, static_matrix &A, const T & scaling_minimum, const T & scaling_maximum, vector & column_scale, - lp_settings & settings): - m_b(b), - m_A(A), - m_scaling_minimum(scaling_minimum), - m_scaling_maximum(scaling_maximum), - m_column_scale(column_scale), - m_settings(settings) { - lp_assert(m_column_scale.size() == 0); - m_column_scale.resize(m_A.column_count(), numeric_traits::one()); - } - - T right_side_balance(); - - T get_balance() { return m_A.get_balance(); } - - T A_min() const; - - T A_max() const; - - T get_A_ratio() const; - - T get_max_ratio_on_rows() const; - - T get_max_ratio_on_columns() const; - - void scale_rows_with_geometric_mean(); - - void scale_columns_with_geometric_mean(); - - void scale_once_for_ratio(); - - bool scale_with_ratio(); - - void bring_row_maximums_to_one(); - - void bring_column_maximums_to_one(); - - void bring_rows_and_columns_maximums_to_one(); - - bool scale_with_log_balance(); - // Returns true if and only if the scaling was successful. - // It is the caller responsibility to restore the matrix - bool scale(); - - void scale_rows(); - - void scale_row(unsigned i); - - void scale_column(unsigned i); - - void scale_columns(); -}; -} diff --git a/src/math/lp/scaler_def.h b/src/math/lp/scaler_def.h deleted file mode 100644 index 8604a67a1..000000000 --- a/src/math/lp/scaler_def.h +++ /dev/null @@ -1,270 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include -#include "math/lp/scaler.h" -#include "math/lp/numeric_pair.h" -namespace lp { -// for scaling an LP -template T scaler::right_side_balance() { - T ret = zero_of_type(); - unsigned i = m_A.row_count(); - while (i--) { - T rs = abs(convert_struct::convert(m_b[i])); - if (!is_zero(rs)) { - numeric_traits::log(rs); - ret += rs * rs; - } - } - return ret; -} - -template T scaler::A_min() const { - T min = zero_of_type(); - for (unsigned i = 0; i < m_A.row_count(); i++) { - T t = m_A.get_min_abs_in_row(i); - min = i == 0 ? t : std::min(t, min); - } - return min; -} - -template T scaler::A_max() const { - T max = zero_of_type(); - for (unsigned i = 0; i < m_A.row_count(); i++) { - T t = m_A.get_max_abs_in_row(i); - max = i == 0? t : std::max(t, max); - } - return max; -} - -template T scaler::get_A_ratio() const { - T min = A_min(); - T max = A_max(); - lp_assert(!m_settings.abs_val_is_smaller_than_zero_tolerance(min)); - T ratio = max / min; - return ratio; -} - -template T scaler::get_max_ratio_on_rows() const { - T ret = T(1); - unsigned i = m_A.row_count(); - while (i--) { - T den = m_A.get_min_abs_in_row(i); - lp_assert(!m_settings.abs_val_is_smaller_than_zero_tolerance(den)); - T t = m_A.get_max_abs_in_row(i)/ den; - if (t > ret) - ret = t; - } - return ret; -} - -template T scaler::get_max_ratio_on_columns() const { - T ret = T(1); - unsigned i = m_A.column_count(); - while (i--) { - T den = m_A.get_min_abs_in_column(i); - if (m_settings.abs_val_is_smaller_than_zero_tolerance(den)) - continue; // got a zero column - T t = m_A.get_max_abs_in_column(i)/den; - if (t > ret) - ret = t; - } - return ret; -} - -template void scaler::scale_rows_with_geometric_mean() { - unsigned i = m_A.row_count(); - while (i--) { - T max = m_A.get_max_abs_in_row(i); - T min = m_A.get_min_abs_in_row(i); - lp_assert(max > zero_of_type() && min > zero_of_type()); - if (is_zero(max) || is_zero(min)) - continue; - T gm = T(sqrt(numeric_traits::get_double(max*min))); - if (m_settings.is_eps_small_general(gm, 0.01)) { - continue; - } - m_A.multiply_row(i, one_of_type() / gm); - m_b[i] /= gm; - } -} - -template void scaler::scale_columns_with_geometric_mean() { - unsigned i = m_A.column_count(); - while (i--) { - T max = m_A.get_max_abs_in_column(i); - T min = m_A.get_min_abs_in_column(i); - T den = T(sqrt(numeric_traits::get_double(max*min))); - if (m_settings.is_eps_small_general(den, 0.01)) - continue; // got a zero column - T gm = T(1)/ den; - T cs = m_column_scale[i] * gm; - if (m_settings.is_eps_small_general(cs, 0.1)) - continue; - m_A.multiply_column(i, gm); - m_column_scale[i] = cs; - } -} - -template void scaler::scale_once_for_ratio() { - T max_ratio_on_rows = get_max_ratio_on_rows(); - T max_ratio_on_columns = get_max_ratio_on_columns(); - bool scale_rows_first = max_ratio_on_rows > max_ratio_on_columns; - // if max_ratio_on_columns is the largest then the rows are in worse shape than columns - if (scale_rows_first) { - scale_rows_with_geometric_mean(); - scale_columns_with_geometric_mean(); - } else { - scale_columns_with_geometric_mean(); - scale_rows_with_geometric_mean(); - } -} - -template bool scaler::scale_with_ratio() { - T ratio = get_A_ratio(); - // The ratio is greater than or equal to one. We would like to diminish it and bring it as close to 1 as possible - unsigned reps = m_settings.reps_in_scaler; - do { - scale_once_for_ratio(); - T new_r = get_A_ratio(); - if (new_r >= T(0.9) * ratio) - break; - } while (reps--); - - bring_rows_and_columns_maximums_to_one(); - return true; -} - -template void scaler::bring_row_maximums_to_one() { - unsigned i = m_A.row_count(); - while (i--) { - T t = m_A.get_max_abs_in_row(i); - if (m_settings.abs_val_is_smaller_than_zero_tolerance(t)) continue; - m_A.multiply_row(i, one_of_type() / t); - m_b[i] /= t; - } -} - -template void scaler::bring_column_maximums_to_one() { - unsigned i = m_A.column_count(); - while (i--) { - T max = m_A.get_max_abs_in_column(i); - if (m_settings.abs_val_is_smaller_than_zero_tolerance(max)) continue; - T t = T(1) / max; - m_A.multiply_column(i, t); - m_column_scale[i] *= t; - } -} - -template void scaler::bring_rows_and_columns_maximums_to_one() { - if (get_max_ratio_on_rows() > get_max_ratio_on_columns()) { - bring_row_maximums_to_one(); - bring_column_maximums_to_one(); - } else { - bring_column_maximums_to_one(); - bring_row_maximums_to_one(); - } -} - -template bool scaler::scale_with_log_balance() { - T balance = get_balance(); - T balance_before_scaling = balance; - // todo : analyze the scale order : rows-columns, or columns-rows. Iterate if needed - for (int i = 0; i < 10; i++) { - scale_rows(); - scale_columns(); - T nb = get_balance(); - if (nb < T(0.9) * balance) { - balance = nb; - } else { - balance = nb; - break; - } - } - return balance <= balance_before_scaling; -} -// Returns true if and only if the scaling was successful. -// It is the caller responsibility to restore the matrix -template bool scaler::scale() { - if (numeric_traits::precise()) return true; - if (m_settings.scale_with_ratio) - return scale_with_ratio(); - return scale_with_log_balance(); -} - -template void scaler::scale_rows() { - for (unsigned i = 0; i < m_A.row_count(); i++) - scale_row(i); -} - -template void scaler::scale_row(unsigned i) { - T row_max = std::max(m_A.get_max_abs_in_row(i), abs(convert_struct::convert(m_b[i]))); - T alpha = numeric_traits::one(); - if (numeric_traits::is_zero(row_max)) { - return; - } - if (row_max < m_scaling_minimum) { - do { - alpha *= T(2); - row_max *= T(2); - } while (row_max < m_scaling_minimum); - m_A.multiply_row(i, alpha); - m_b[i] *= alpha; - } else if (row_max > m_scaling_maximum) { - do { - alpha /= T(2); - row_max /= T(2); - } while (row_max > m_scaling_maximum); - m_A.multiply_row(i, alpha); - m_b[i] *= alpha; - } -} - -template void scaler::scale_column(unsigned i) { - T column_max = m_A.get_max_abs_in_column(i); - T alpha = numeric_traits::one(); - - if (numeric_traits::is_zero(column_max)){ - return; // the column has zeros only - } - if (column_max < m_scaling_minimum) { - do { - alpha *= T(2); - column_max *= T(2); - } while (column_max < m_scaling_minimum); - } else if (column_max > m_scaling_maximum) { - do { - alpha /= T(2); - column_max /= T(2); - } while (column_max > m_scaling_maximum); - } else { - return; - } - m_A.multiply_column(i, alpha); - m_column_scale[i] = alpha; -} - -template void scaler::scale_columns() { - for (unsigned i = 0; i < m_A.column_count(); i++) { - scale_column(i); - } -} -} diff --git a/src/math/lp/sparse_vector.h b/src/math/lp/sparse_vector.h deleted file mode 100644 index 1c27a8d96..000000000 --- a/src/math/lp/sparse_vector.h +++ /dev/null @@ -1,53 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include -#include "util/debug.h" -#include "math/lp/lp_utils.h" -#include "math/lp/lp_settings.h" -namespace lp { - -template -class sparse_vector { -public: - vector> m_data; - void push_back(unsigned index, T val) { - m_data.push_back(std::make_pair(index, val)); - } -#ifdef Z3DEBUG - T operator[] (unsigned i) const { - for (auto &t : m_data) { - if (t.first == i) return t.second; - } - return numeric_traits::zero(); - } -#endif - void divide(T const & a) { - lp_assert(!lp_settings::is_eps_small_general(a, 1e-12)); - for (auto & t : m_data) { t.second /= a; } - } - - unsigned size() const { - return m_data.size(); - } -}; -} diff --git a/src/math/lp/square_dense_submatrix.cpp b/src/math/lp/square_dense_submatrix.cpp deleted file mode 100644 index 4d9fcec13..000000000 --- a/src/math/lp/square_dense_submatrix.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "util/vector.h" -#include "math/lp/square_dense_submatrix_def.h" -template void lp::square_dense_submatrix::init(lp::square_sparse_matrix*, unsigned int); -template lp::square_dense_submatrix::square_dense_submatrix(lp::square_sparse_matrix*, unsigned int); -template void lp::square_dense_submatrix::update_parent_matrix(lp::lp_settings&); -template bool lp::square_dense_submatrix::is_L_matrix() const; -template void lp::square_dense_submatrix::conjugate_by_permutation(lp::permutation_matrix&); -template int lp::square_dense_submatrix::find_pivot_column_in_row(unsigned int) const; -template void lp::square_dense_submatrix::pivot(unsigned int, lp::lp_settings&); -template lp::square_dense_submatrix >::square_dense_submatrix(lp::square_sparse_matrix >*, unsigned int); -template void lp::square_dense_submatrix >::update_parent_matrix(lp::lp_settings&); -template bool lp::square_dense_submatrix >::is_L_matrix() const; -template void lp::square_dense_submatrix >::conjugate_by_permutation(lp::permutation_matrix >&); -template int lp::square_dense_submatrix >::find_pivot_column_in_row(unsigned int) const; -template void lp::square_dense_submatrix >::pivot(unsigned int, lp::lp_settings&); -#ifdef Z3DEBUG -template double lp::square_dense_submatrix::get_elem(unsigned int, unsigned int) const; -#endif -template void lp::square_dense_submatrix::apply_from_right(vector&); - -template void lp::square_dense_submatrix::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); -template void lp::square_dense_submatrix::apply_from_left_to_vector(vector&); -template lp::square_dense_submatrix::square_dense_submatrix(lp::square_sparse_matrix*, unsigned int); -template void lp::square_dense_submatrix::update_parent_matrix(lp::lp_settings&); -template bool lp::square_dense_submatrix::is_L_matrix() const; -template void lp::square_dense_submatrix::conjugate_by_permutation(lp::permutation_matrix&); -template int lp::square_dense_submatrix::find_pivot_column_in_row(unsigned int) const; -template void lp::square_dense_submatrix::pivot(unsigned int, lp::lp_settings&); diff --git a/src/math/lp/square_dense_submatrix.h b/src/math/lp/square_dense_submatrix.h deleted file mode 100644 index 308f9eadc..000000000 --- a/src/math/lp/square_dense_submatrix.h +++ /dev/null @@ -1,225 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "math/lp/permutation_matrix.h" -#include -#include "math/lp/static_matrix.h" -#include -#include -#include -#include -#include -#include "math/lp/indexed_value.h" -#include "math/lp/indexed_vector.h" -#include -#include "math/lp/lp_settings.h" -#include "math/lp/eta_matrix.h" -#include "math/lp/binary_heap_upair_queue.h" -#include "math/lp/square_sparse_matrix.h" -namespace lp { -template -class square_dense_submatrix : public tail_matrix { - // the submatrix uses the permutations of the parent matrix to access the elements - struct ref { - unsigned m_i_offset; - square_dense_submatrix & m_s; - ref(unsigned i, square_dense_submatrix & s) : - m_i_offset((i - s.m_index_start) * s.m_dim), m_s(s){} - T & operator[] (unsigned j) { - lp_assert(j >= m_s.m_index_start); - return m_s.m_v[m_i_offset + m_s.adjust_column(j) - m_s.m_index_start]; - } - const T & operator[] (unsigned j) const { - lp_assert(j >= m_s.m_index_start); - return m_s.m_v[m_i_offset + m_s.adjust_column(j) - m_s.m_index_start]; - } - }; -public: - unsigned m_index_start; - unsigned m_dim; - vector m_v; - square_sparse_matrix * m_parent; - permutation_matrix m_row_permutation; - indexed_vector m_work_vector; -public: - permutation_matrix m_column_permutation; - bool is_active() const { return m_parent != nullptr; } - - square_dense_submatrix() {} - - square_dense_submatrix (square_sparse_matrix *parent_matrix, unsigned index_start); - - void init(square_sparse_matrix *parent_matrix, unsigned index_start); - - bool is_dense() const override { return true; } - - ref operator[] (unsigned i) { - lp_assert(i >= m_index_start); - lp_assert(i < m_parent->dimension()); - return ref(i, *this); - } - - int find_pivot_column_in_row(unsigned i) const; - - void swap_columns(unsigned i, unsigned j) { - if (i != j) - m_column_permutation.transpose_from_left(i, j); - } - - unsigned adjust_column(unsigned col) const{ - if (col >= m_column_permutation.size()) - return col; - return m_column_permutation.apply_reverse(col); - } - - unsigned adjust_column_inverse(unsigned col) const{ - if (col >= m_column_permutation.size()) - return col; - return m_column_permutation[col]; - } - unsigned adjust_row(unsigned row) const{ - if (row >= m_row_permutation.size()) - return row; - return m_row_permutation[row]; - } - - unsigned adjust_row_inverse(unsigned row) const{ - if (row >= m_row_permutation.size()) - return row; - return m_row_permutation.apply_reverse(row); - } - - void pivot(unsigned i, lp_settings & settings); - - void pivot_row_to_row(unsigned i, unsigned row, lp_settings & settings);; - - void divide_row_by_pivot(unsigned i); - - void update_parent_matrix(lp_settings & settings); - - void update_existing_or_delete_in_parent_matrix_for_row(unsigned i, lp_settings & settings); - - void push_new_elements_to_parent_matrix(lp_settings & settings); - - template - L row_by_vector_product(unsigned i, const vector & v); - - template - L column_by_vector_product(unsigned j, const vector & v); - - template - L row_by_indexed_vector_product(unsigned i, const indexed_vector & v); - - template - void apply_from_left_local(indexed_vector & w, lp_settings & settings); - - template - void apply_from_left_to_vector(vector & w); - - bool is_L_matrix() const; - - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override { - apply_from_left_local(w, settings); - } - - - - void apply_from_right(indexed_vector & w) override { -#if 1==0 - indexed_vector wcopy = w; - apply_from_right(wcopy.m_data); - wcopy.m_index.clear(); - if (numeric_traits::precise()) { - for (unsigned i = 0; i < m_parent->dimension(); i++) { - if (!is_zero(wcopy.m_data[i])) - wcopy.m_index.push_back(i); - } - } else { - for (unsigned i = 0; i < m_parent->dimension(); i++) { - T & v = wcopy.m_data[i]; - if (!lp_settings::is_eps_small_general(v, 1e-14)){ - wcopy.m_index.push_back(i); - } else { - v = zero_of_type(); - } - } - } - lp_assert(wcopy.is_OK()); - apply_from_right(w.m_data); - w.m_index.clear(); - if (numeric_traits::precise()) { - for (unsigned i = 0; i < m_parent->dimension(); i++) { - if (!is_zero(w.m_data[i])) - w.m_index.push_back(i); - } - } else { - for (unsigned i = 0; i < m_parent->dimension(); i++) { - T & v = w.m_data[i]; - if (!lp_settings::is_eps_small_general(v, 1e-14)){ - w.m_index.push_back(i); - } else { - v = zero_of_type(); - } - } - } -#else - lp_assert(w.is_OK()); - lp_assert(m_work_vector.is_OK()); - m_work_vector.resize(w.data_size()); - m_work_vector.clear(); - lp_assert(m_work_vector.is_OK()); - unsigned end = m_index_start + m_dim; - for (unsigned k : w.m_index) { - // find j such that k = adjust_row_inverse(j) - unsigned j = adjust_row(k); - if (j < m_index_start || j >= end) { - m_work_vector.set_value(w[k], adjust_column_inverse(j)); - } else { // j >= m_index_start and j < end - unsigned offset = (j - m_index_start) * m_dim; // this is the row start - const T& wv = w[k]; - for (unsigned col = m_index_start; col < end; col++, offset ++) { - unsigned adj_col = adjust_column_inverse(col); - m_work_vector.add_value_at_index(adj_col, m_v[offset] * wv); - } - } - } - m_work_vector.clean_up(); - lp_assert(m_work_vector.is_OK()); - w = m_work_vector; -#endif - } - void apply_from_left(vector & w, lp_settings & /*settings*/) override { - apply_from_left_to_vector(w);// , settings); - } - - void apply_from_right(vector & w) override; - -#ifdef Z3DEBUG - T get_elem (unsigned i, unsigned j) const override; - unsigned row_count() const override { return m_parent->row_count();} - unsigned column_count() const override { return row_count();} - void set_number_of_rows(unsigned) override {} - void set_number_of_columns(unsigned) override {} -#endif - void conjugate_by_permutation(permutation_matrix & q); -}; -} diff --git a/src/math/lp/square_dense_submatrix_def.h b/src/math/lp/square_dense_submatrix_def.h deleted file mode 100644 index 3a9006b4d..000000000 --- a/src/math/lp/square_dense_submatrix_def.h +++ /dev/null @@ -1,370 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "util/vector.h" -#include "math/lp/square_dense_submatrix.h" -namespace lp { -template -square_dense_submatrix::square_dense_submatrix (square_sparse_matrix *parent_matrix, unsigned index_start) : - m_index_start(index_start), - m_dim(parent_matrix->dimension() - index_start), - m_v(m_dim * m_dim), - m_parent(parent_matrix), - m_row_permutation(m_parent->dimension()), - m_column_permutation(m_parent->dimension()) { - int row_offset = - static_cast(m_index_start); - for (unsigned i = index_start; i < parent_matrix->dimension(); i++) { - unsigned row = parent_matrix->adjust_row(i); - for (auto & iv : parent_matrix->get_row_values(row)) { - unsigned j = parent_matrix->adjust_column_inverse(iv.m_index); - lp_assert(j>= m_index_start); - m_v[row_offset + j] = iv.m_value; - } - row_offset += m_dim; - } -} - -template void square_dense_submatrix::init(square_sparse_matrix *parent_matrix, unsigned index_start) { - m_index_start = index_start; - m_dim = parent_matrix->dimension() - index_start; - m_v.resize(m_dim * m_dim); - m_parent = parent_matrix; - m_column_permutation.init(m_parent->dimension()); - for (unsigned i = index_start; i < parent_matrix->dimension(); i++) { - unsigned row = parent_matrix->adjust_row(i); - for (auto & iv : parent_matrix->get_row_values(row)) { - unsigned j = parent_matrix->adjust_column_inverse(iv.m_index); - (*this)[i][j] = iv.m_value; - } - } -} - -template int square_dense_submatrix::find_pivot_column_in_row(unsigned i) const { - int j = -1; - T max = zero_of_type(); - lp_assert(i >= m_index_start); - unsigned row_start = (i - m_index_start) * m_dim; - for (unsigned k = i; k < m_parent->dimension(); k++) { - unsigned col = adjust_column(k); // this is where the column is in the row - unsigned offs = row_start + col - m_index_start; - T t = abs(m_v[offs]); - if (t > max) { - j = k; - max = t; - } - } - return j; -} - -template void square_dense_submatrix::pivot(unsigned i, lp_settings & settings) { - divide_row_by_pivot(i); - for (unsigned k = i + 1; k < m_parent->dimension(); k++) - pivot_row_to_row(i, k, settings); -} - -template void square_dense_submatrix::pivot_row_to_row(unsigned i, unsigned row, lp_settings & settings) { - lp_assert(i < row); - unsigned pj = adjust_column(i); // the pivot column - unsigned pjd = pj - m_index_start; - unsigned pivot_row_offset = (i-m_index_start)*m_dim; - T pivot = m_v[pivot_row_offset + pjd]; - unsigned row_offset= (row-m_index_start)*m_dim; - T m = m_v[row_offset + pjd]; - lp_assert(!is_zero(pivot)); - m_v[row_offset + pjd] = -m * pivot; // creating L matrix - for (unsigned j = m_index_start; j < m_parent->dimension(); j++) { - if (j == pj) { - pivot_row_offset++; - row_offset++; - continue; - } - auto t = m_v[row_offset] - m_v[pivot_row_offset] * m; - if (settings.abs_val_is_smaller_than_drop_tolerance(t)) { - m_v[row_offset] = zero_of_type(); - } else { - m_v[row_offset] = t; - } - row_offset++; pivot_row_offset++; - // at the same time we pivot the L too - } -} - -template void square_dense_submatrix::divide_row_by_pivot(unsigned i) { - unsigned pj = adjust_column(i); // the pivot column - unsigned irow_offset = (i - m_index_start) * m_dim; - T pivot = m_v[irow_offset + pj - m_index_start]; - lp_assert(!is_zero(pivot)); - for (unsigned k = m_index_start; k < m_parent->dimension(); k++) { - if (k == pj){ - m_v[irow_offset++] = one_of_type() / pivot; // creating the L matrix diagonal - continue; - } - m_v[irow_offset++] /= pivot; - } -} - -template void square_dense_submatrix::update_parent_matrix(lp_settings & settings) { - for (unsigned i = m_index_start; i < m_parent->dimension(); i++) - update_existing_or_delete_in_parent_matrix_for_row(i, settings); - push_new_elements_to_parent_matrix(settings); - for (unsigned i = m_index_start; i < m_parent->dimension(); i++) - m_parent->set_max_in_row(m_parent->adjust_row(i)); -} - -template void square_dense_submatrix::update_existing_or_delete_in_parent_matrix_for_row(unsigned i, lp_settings & settings) { - bool diag_updated = false; - unsigned ai = m_parent->adjust_row(i); - auto & row_vals = m_parent->get_row_values(ai); - for (unsigned k = 0; k < row_vals.size(); k++) { - auto & iv = row_vals[k]; - unsigned j = m_parent->adjust_column_inverse(iv.m_index); - if (j < i) { - m_parent->remove_element(row_vals, iv); - k--; - } else if (i == j) { - m_parent->m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = one_of_type()); - diag_updated = true; - } else { // j > i - T & v = (*this)[i][j]; - if (settings.abs_val_is_smaller_than_drop_tolerance(v)) { - m_parent->remove_element(row_vals, iv); - k--; - } else { - m_parent->m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = v); - v = zero_of_type(); // only new elements are left above the diagonal - } - } - } - if (!diag_updated) { - unsigned aj = m_parent->adjust_column(i); - m_parent->add_new_element(ai, aj, one_of_type()); - } -} - -template void square_dense_submatrix::push_new_elements_to_parent_matrix(lp_settings & settings) { - for (unsigned i = m_index_start; i < m_parent->dimension() - 1; i++) { - unsigned ai = m_parent->adjust_row(i); - for (unsigned j = i + 1; j < m_parent->dimension(); j++) { - T & v = (*this)[i][j]; - if (!settings.abs_val_is_smaller_than_drop_tolerance(v)) { - unsigned aj = m_parent->adjust_column(j); - m_parent->add_new_element(ai, aj, v); - } - v = zero_of_type(); // leave only L elements now - } - } -} -template -template -L square_dense_submatrix::row_by_vector_product(unsigned i, const vector & v) { - lp_assert(i >= m_index_start); - - unsigned row_in_subm = i - m_index_start; - unsigned row_offset = row_in_subm * m_dim; - L r = zero_of_type(); - for (unsigned j = 0; j < m_dim; j++) - r += m_v[row_offset + j] * v[adjust_column_inverse(m_index_start + j)]; - return r; -} - -template -template -L square_dense_submatrix::column_by_vector_product(unsigned j, const vector & v) { - lp_assert(j >= m_index_start); - - unsigned offset = j - m_index_start; - L r = zero_of_type(); - for (unsigned i = 0; i < m_dim; i++, offset += m_dim) - r += m_v[offset] * v[adjust_row_inverse(m_index_start + i)]; - return r; -} -template -template -L square_dense_submatrix::row_by_indexed_vector_product(unsigned i, const indexed_vector & v) { - lp_assert(i >= m_index_start); - - unsigned row_in_subm = i - m_index_start; - unsigned row_offset = row_in_subm * m_dim; - L r = zero_of_type(); - for (unsigned j = 0; j < m_dim; j++) - r += m_v[row_offset + j] * v[adjust_column_inverse(m_index_start + j)]; - return r; -} -template -template -void square_dense_submatrix::apply_from_left_local(indexed_vector & w, lp_settings & settings) { -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // vector deb_w(w.m_data.size()); - // for (unsigned i = 0; i < w.m_data.size(); i++) - // deb_w[i] = w[i]; - - // deb.apply_from_left(deb_w); -#endif // use indexed vector here - -#ifndef DO_NOT_USE_INDEX - vector t(m_parent->dimension(), zero_of_type()); - for (auto k : w.m_index) { - unsigned j = adjust_column(k); // k-th element will contribute only to column j - if (j < m_index_start || j >= this->m_index_start + this->m_dim) { // it is a unit matrix outside - t[adjust_row_inverse(j)] = w[k]; - } else { - const L & v = w[k]; - for (unsigned i = 0; i < m_dim; i++) { - unsigned row = adjust_row_inverse(m_index_start + i); - unsigned offs = i * m_dim + j - m_index_start; - t[row] += m_v[offs] * v; - } - } - } - w.m_index.clear(); - for (unsigned i = 0; i < m_parent->dimension(); i++) { - const L & v = t[i]; - if (!settings.abs_val_is_smaller_than_drop_tolerance(v)){ - w.m_index.push_back(i); - w.m_data[i] = v; - } else { - w.m_data[i] = zero_of_type(); - } - } -#else - vector t(m_parent->dimension()); - for (unsigned i = 0; i < m_index_start; i++) { - t[adjust_row_inverse(i)] = w[adjust_column_inverse(i)]; - } - for (unsigned i = m_index_start; i < m_parent->dimension(); i++){ - t[adjust_row_inverse(i)] = row_by_indexed_vector_product(i, w); - } - for (unsigned i = 0; i < m_parent->dimension(); i++) { - w.set_value(t[i], i); - } - for (unsigned i = 0; i < m_parent->dimension(); i++) { - const L & v = t[i]; - if (!is_zero(v)) - w.m_index.push_back(i); - w.m_data[i] = v; - } -#endif -#ifdef Z3DEBUG - // cout << "w final" << endl; - // print_vector(w.m_data); - // lp_assert(vectors_are_equal(deb_w, w.m_data)); - // lp_assert(w.is_OK()); -#endif -} - -template -template -void square_dense_submatrix::apply_from_left_to_vector(vector & w) { - // lp_settings & settings) { - // dense_matrix deb(*this); - // vector deb_w(w); - // deb.apply_from_left_to_X(deb_w, settings); - // // cout << "deb" << endl; - // // print_matrix(deb); - // // cout << "w" << endl; - // // print_vector(w.m_data); - // // cout << "deb_w" << endl; - // // print_vector(deb_w); - vector t(m_parent->dimension()); - for (unsigned i = 0; i < m_index_start; i++) { - t[adjust_row_inverse(i)] = w[adjust_column_inverse(i)]; - } - for (unsigned i = m_index_start; i < m_parent->dimension(); i++){ - t[adjust_row_inverse(i)] = row_by_vector_product(i, w); - } - for (unsigned i = 0; i < m_parent->dimension(); i++) { - w[i] = t[i]; - } -#ifdef Z3DEBUG - // cout << "w final" << endl; - // print_vector(w.m_data); - // lp_assert(vectors_are_equal(deb_w, w)); -#endif -} - -template bool square_dense_submatrix::is_L_matrix() const { -#ifdef Z3DEBUG - lp_assert(m_row_permutation.is_identity()); - for (unsigned i = 0; i < m_parent->dimension(); i++) { - if (i < m_index_start) { - lp_assert(m_column_permutation[i] == i); - continue; - } - unsigned row_offs = (i-m_index_start)*m_dim; - for (unsigned k = 0; k < m_dim; k++) { - unsigned j = m_index_start + k; - unsigned jex = adjust_column_inverse(j); - if (jex > i) { - lp_assert(is_zero(m_v[row_offs + k])); - } else if (jex == i) { - lp_assert(!is_zero(m_v[row_offs + k])); - } - } - } -#endif - return true; -} - -template void square_dense_submatrix::apply_from_right(vector & w) { -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // vector deb_w(w); - // deb.apply_from_right(deb_w); -#endif - vector t(w.size()); - - for (unsigned j = 0; j < m_index_start; j++) { - t[adjust_column_inverse(j)] = w[adjust_row_inverse(j)]; - } - unsigned end = m_index_start + m_dim; - for (unsigned j = end; j < m_parent->dimension(); j++) { - t[adjust_column_inverse(j)] = w[adjust_row_inverse(j)]; - } - for (unsigned j = m_index_start; j < end; j++) { - t[adjust_column_inverse(j)] = column_by_vector_product(j, w); - } - w = t; -#ifdef Z3DEBUG - // lp_assert(vector_are_equal(deb_w, w)); -#endif -} - - - - -#ifdef Z3DEBUG - -template T square_dense_submatrix::get_elem (unsigned i, unsigned j) const { - i = adjust_row(i); - j = adjust_column(j); - if (i < m_index_start || j < m_index_start) - return i == j? one_of_type() : zero_of_type(); - unsigned offs = (i - m_index_start)* m_dim + j - m_index_start; - return m_v[offs]; -} - -#endif -template void square_dense_submatrix::conjugate_by_permutation(permutation_matrix & q) { - m_row_permutation.multiply_by_permutation_from_left(q); - m_column_permutation.multiply_by_reverse_from_right(q); -} -} diff --git a/src/math/lp/square_sparse_matrix.cpp b/src/math/lp/square_sparse_matrix.cpp deleted file mode 100644 index 35d38e529..000000000 --- a/src/math/lp/square_sparse_matrix.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "util/vector.h" -#include "math/lp/lp_settings.h" -#include "math/lp/lu.h" -#include "math/lp/square_sparse_matrix_def.h" -#include "math/lp/dense_matrix.h" -namespace lp { -template double square_sparse_matrix::dot_product_with_row(unsigned int, vector const&) const; -template void square_sparse_matrix::add_new_element(unsigned int, unsigned int, const double&); -template void square_sparse_matrix::divide_row_by_constant(unsigned int, const double&, lp_settings&); -template bool square_sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); -template const double & square_sparse_matrix::get(unsigned int, unsigned int) const; -template unsigned square_sparse_matrix::get_number_of_nonzeroes() const; -template bool square_sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); -template unsigned square_sparse_matrix::lowest_row_in_column(unsigned int); -template bool square_sparse_matrix::pivot_row_to_row(unsigned int, const double&, unsigned int, lp_settings&); -template bool square_sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); -template void square_sparse_matrix::prepare_for_factorization(); -template void square_sparse_matrix::remove_element(vector >&, indexed_value&); -template void square_sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); -template void square_sparse_matrix::set(unsigned int, unsigned int, double); -template void square_sparse_matrix::set_max_in_row(vector >&); -template bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); -template bool square_sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); -template void square_sparse_matrix::solve_y_U(vector&) const; -template square_sparse_matrix::square_sparse_matrix(unsigned int, unsigned); -template void square_sparse_matrix::add_new_element(unsigned int, unsigned int, const mpq&); -template void square_sparse_matrix::divide_row_by_constant(unsigned int, const mpq&, lp_settings&); -template bool square_sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); -template mpq const & square_sparse_matrix::get(unsigned int, unsigned int) const; -template unsigned square_sparse_matrix::get_number_of_nonzeroes() const; -template bool square_sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); -template unsigned square_sparse_matrix::lowest_row_in_column(unsigned int); -template bool square_sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); -template void square_sparse_matrix::prepare_for_factorization(); -template void square_sparse_matrix::remove_element(vector> &, indexed_value&); -template void square_sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); -template void square_sparse_matrix::set_max_in_row(vector>&); -template bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); -template bool square_sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); -template void square_sparse_matrix::solve_y_U(vector&) const; -template void square_sparse_matrix>::add_new_element(unsigned int, unsigned int, const mpq&); -template void square_sparse_matrix>::divide_row_by_constant(unsigned int, const mpq&, lp_settings&); -template bool square_sparse_matrix>::fill_eta_matrix(unsigned int, eta_matrix >**); -template const mpq & square_sparse_matrix>::get(unsigned int, unsigned int) const; -template unsigned square_sparse_matrix>::get_number_of_nonzeroes() const; -template bool square_sparse_matrix>::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); -template unsigned square_sparse_matrix>::lowest_row_in_column(unsigned int); -template bool square_sparse_matrix>::pivot_with_eta(unsigned int, eta_matrix >*, lp_settings&); -template void square_sparse_matrix>::prepare_for_factorization(); -template void square_sparse_matrix>::remove_element(vector>&, indexed_value&); -template void square_sparse_matrix>::replace_column(unsigned int, indexed_vector&, lp_settings&); -template void square_sparse_matrix>::set_max_in_row(vector>&); -template bool square_sparse_matrix>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); -template bool square_sparse_matrix>::shorten_active_matrix(unsigned int, eta_matrix >*); -template void square_sparse_matrix>::solve_y_U(vector&) const; -template void square_sparse_matrix::double_solve_U_y(indexed_vector&, const lp_settings &); -template void square_sparse_matrix::double_solve_U_y(indexed_vector&, const lp_settings&); -template void square_sparse_matrix>::double_solve_U_y(indexed_vector&, const lp_settings&); -template void square_sparse_matrix >::double_solve_U_y >(indexed_vector>&, const lp_settings&); -template void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector&, const lp_settings&, vector &); -template void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector&, const lp_settings &, vector &); -#ifdef Z3DEBUG -template bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; -template bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; -template bool square_sparse_matrix >::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; -#endif - -template void square_sparse_matrix >::solve_U_y_indexed_only(indexed_vector&, const lp_settings &, vector &); -template void square_sparse_matrix::solve_U_y(vector&); -template void square_sparse_matrix::double_solve_U_y(vector&); -template void square_sparse_matrix::solve_U_y(vector&); -template void square_sparse_matrix::double_solve_U_y(vector&); -template void square_sparse_matrix >::solve_U_y >(vector >&); -template void square_sparse_matrix >::double_solve_U_y >(vector >&); -template void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); -template double square_sparse_matrix::dot_product_with_row(unsigned int, indexed_vector const&) const; -template void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); -template mpq square_sparse_matrix::dot_product_with_row(unsigned int, indexed_vector const&) const; -template void square_sparse_matrix >::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); -template mpq square_sparse_matrix >::dot_product_with_row(unsigned int, indexed_vector const&) const; -template void square_sparse_matrix >::find_error_in_solution_U_y_indexed >(indexed_vector >&, indexed_vector >&, const vector &); -template numeric_pair square_sparse_matrix >::dot_product_with_row >(unsigned int, indexed_vector > const&) const; -template void square_sparse_matrix::extend_and_sort_active_rows(vector const&, vector&); - -template void square_sparse_matrix >::extend_and_sort_active_rows(vector const&, vector&); - -template void square_sparse_matrix >::solve_U_y(vector&); -template void square_sparse_matrix >::double_solve_U_y(vector&); -template void square_sparse_matrix< mpq,numeric_pair< mpq> >::set(unsigned int,unsigned int, mpq); -template void square_sparse_matrix::solve_y_U_indexed(indexed_vector&, const lp_settings & ); -template void square_sparse_matrix::solve_y_U_indexed(indexed_vector&, const lp_settings &); -template void square_sparse_matrix >::solve_y_U_indexed(indexed_vector&, const lp_settings &); - -template square_sparse_matrix::square_sparse_matrix(static_matrix const&, vector&); -template square_sparse_matrix::square_sparse_matrix (static_matrix const&, vector&); -template square_sparse_matrix >::square_sparse_matrix(static_matrix > const&, vector&); -} -template void lp::square_sparse_matrix::copy_from_input_on_basis >(lp::static_matrix const&, vector&); -template void lp::square_sparse_matrix::copy_from_input_on_basis >(lp::static_matrix const&, vector&); diff --git a/src/math/lp/square_sparse_matrix.h b/src/math/lp/square_sparse_matrix.h deleted file mode 100644 index 5637af99f..000000000 --- a/src/math/lp/square_sparse_matrix.h +++ /dev/null @@ -1,433 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "math/lp/permutation_matrix.h" -#include "math/lp/static_matrix.h" -#include -#include -#include -#include -#include -#include "math/lp/indexed_value.h" -#include "math/lp/indexed_vector.h" -#include -#include "math/lp/lp_settings.h" -#include "math/lp/eta_matrix.h" -#include "math/lp/binary_heap_upair_queue.h" -#include "math/lp/numeric_pair.h" -#include "math/lp/u_set.h" -namespace lp { -// it is a square matrix -template -class square_sparse_matrix - : public matrix -{ - struct col_header { - unsigned m_shortened_markovitz; - vector> m_values; // the actual column values - - col_header(): m_shortened_markovitz(0) {} - - void shorten_markovich_by_one() { - m_shortened_markovitz++; - } - - void zero_shortened_markovitz() { - m_shortened_markovitz = 0; - } - }; - - unsigned m_n_of_active_elems; - binary_heap_upair_queue m_pivot_queue; -public: - vector>> m_rows; - vector m_columns; - permutation_matrix m_row_permutation; - permutation_matrix m_column_permutation; - // m_work_pivot_vector[j] = offset of elementh of j-th column in the row we are pivoting to - // if the column is not present then m_work_pivot_vector[j] is -1 - vector m_work_pivot_vector; - vector m_processed; - unsigned get_n_of_active_elems() const { return m_n_of_active_elems; } - -#ifdef Z3DEBUG - // dense_matrix m_dense; -#endif - /* - the rule is: row i is mapped to m_row_permutation[i] and - column j is mapped to m_column_permutation.apply_reverse(j) - */ - - unsigned adjust_row(unsigned row) const{ - return m_row_permutation[row]; - } - - unsigned adjust_column(unsigned col) const{ - return m_column_permutation.apply_reverse(col); - } - - unsigned adjust_row_inverse(unsigned row) const{ - return m_row_permutation.apply_reverse(row); - } - - unsigned adjust_column_inverse(unsigned col) const{ - return m_column_permutation[col]; - } - - template - void copy_column_from_input(unsigned input_column, const M& A, unsigned j); - template - void copy_column_from_input_with_possible_zeros(const M& A, unsigned j); - - template - void copy_from_input(const M& A); - template - void copy_from_input_on_basis(const M& A, vector & basis); - -public: - - // constructors - template - square_sparse_matrix(const M &A, vector& basis); - - template - square_sparse_matrix(const M &A); - - square_sparse_matrix(unsigned dim, unsigned); // the second parameter is needed to distinguish this - // constructor from the one above - - - - class ref_matrix_element { - square_sparse_matrix & m_matrix; - unsigned m_row; - unsigned m_col; - public: - ref_matrix_element(square_sparse_matrix & m, unsigned row, unsigned col):m_matrix(m), m_row(row), m_col(col) {} - ref_matrix_element & operator=(T const & v) { m_matrix.set( m_row, m_col, v); return *this; } - ref_matrix_element & operator=(ref_matrix_element const & v) { m_matrix.set(m_row, m_col, v.m_matrix.get(v.m_row, v.m_col)); return *this; } - operator T () const { return m_matrix.get(m_row, m_col); } - }; - - class ref_row { - square_sparse_matrix & m_matrix; - unsigned m_row; - public: - ref_row(square_sparse_matrix & m, unsigned row) : m_matrix(m), m_row(row) {} - ref_matrix_element operator[](unsigned col) const { return ref_matrix_element(m_matrix, m_row, col); } - }; - - void set_with_no_adjusting_for_row(unsigned row, unsigned col, T val); - void set_with_no_adjusting_for_col(unsigned row, unsigned col, T val); - - void set_with_no_adjusting(unsigned row, unsigned col, T val); - - void set(unsigned row, unsigned col, T val); - - T const & get_not_adjusted(unsigned row, unsigned col) const; - T const & get(unsigned row, unsigned col) const; - - ref_row operator[](unsigned row) { return ref_row(*this, row); } - - ref_matrix_element operator()(unsigned row, unsigned col) { return ref_matrix_element(*this, row, col); } - - T operator() (unsigned row, unsigned col) const { return get(row, col); } - - vector> & get_row_values(unsigned row) { - return m_rows[row]; - } - - vector> const & get_row_values(unsigned row) const { - return m_rows[row]; - } - - vector> & get_column_values(unsigned col) { - return m_columns[col].m_values; - } - - vector> const & get_column_values(unsigned col) const { - return m_columns[col].m_values; - } - - unsigned dimension() const {return static_cast(m_row_permutation.size());} - - unsigned row_count() const override {return dimension();} - unsigned column_count() const override {return dimension();} - - void init_row_headers(); - - void init_column_headers(); - - unsigned lowest_row_in_column(unsigned j); - - indexed_value & column_iv_other(indexed_value & iv) { - return m_rows[iv.m_index][iv.m_other]; - } - - indexed_value & row_iv_other(indexed_value & iv) { - return m_columns[iv.m_index].m_values[iv.m_other]; - } - - void remove_element(vector> & row_vals, unsigned row_offset, vector> & column_vals, unsigned column_offset); - - void remove_element(vector> & row_chunk, indexed_value & row_el_iv); - - void put_max_index_to_0(vector> & row_vals, unsigned max_index); - - void set_max_in_row(unsigned row) { - set_max_in_row(m_rows[row]); - } - - - void set_max_in_row(vector> & row_vals); - - bool pivot_with_eta(unsigned i, eta_matrix *eta_matrix, lp_settings & settings); - - void scan_row_to_work_vector_and_remove_pivot_column(unsigned row, unsigned pivot_column); - - // This method pivots row i to row i0 by muliplying row i by - // alpha and adding it to row i0. - // After pivoting the row i0 has a max abs value set correctly at the beginning of m_start, - // Returns false if the resulting row is all zeroes, and true otherwise - bool pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, lp_settings & settings ); - - // set the max val as well - // returns false if the resulting row is all zeroes, and true otherwise - bool set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned i0, indexed_vector & work_vec, - lp_settings & settings); - - - // set the max val as well - // returns false if the resulting row is all zeroes, and true otherwise - bool set_row_from_work_vector_and_clean_work_vector(unsigned i0); - - void remove_zero_elements_and_set_data_on_existing_elements(unsigned row); - - // work_vec here has not adjusted column indices - void remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(unsigned row, indexed_vector & work_vec, lp_settings & settings); - - void multiply_from_right(permutation_matrix& p) { - // m_dense = m_dense * p; - m_column_permutation.multiply_by_permutation_from_right(p); - // lp_assert(*this == m_dense); - } - - void multiply_from_left(permutation_matrix& p) { - // m_dense = p * m_dense; - m_row_permutation.multiply_by_permutation_from_left(p); - // lp_assert(*this == m_dense); - } - - void multiply_from_left_with_reverse(permutation_matrix& p) { - // m_dense = p * m_dense; - m_row_permutation.multiply_by_permutation_reverse_from_left(p); - // lp_assert(*this == m_dense); - } - - // adding delta columns at the end of the matrix - void add_columns_at_the_end(unsigned delta); - - void delete_column(int i); - - void swap_columns(unsigned a, unsigned b) { - m_column_permutation.transpose_from_left(a, b); - } - - void swap_rows(unsigned a, unsigned b) { - m_row_permutation.transpose_from_right(a, b); - // m_dense.swap_rows(a, b); - // lp_assert(*this == m_dense); - } - - void divide_row_by_constant(unsigned i, const T & t, lp_settings & settings); - - bool close(T a, T b) { - return // (numeric_traits::precise() && numeric_traits::is_zero(a - b)) - // || - fabs(numeric_traits::get_double(a - b)) < 0.0000001; - } - - // solving x * this = y, and putting the answer into y - // the matrix here has to be upper triangular - void solve_y_U(vector & y) const; - - // solving x * this = y, and putting the answer into y - // the matrix here has to be upper triangular - void solve_y_U_indexed(indexed_vector & y, const lp_settings &); - - // fills the indices for such that y[i] can be not a zero - // sort them so the smaller indices come first - void fill_reachable_indices(std::set & rset, T *y); - - template - void find_error_in_solution_U_y(vector& y_orig, vector & y); - - template - void find_error_in_solution_U_y_indexed(indexed_vector& y_orig, indexed_vector & y, const vector& sorted_active_rows); - - template - void add_delta_to_solution(const vector& del, vector & y); - - template - void add_delta_to_solution(const indexed_vector& del, indexed_vector & y); - - template - void double_solve_U_y(indexed_vector& y, const lp_settings & settings); - - template - void double_solve_U_y(vector& y); - // solving this * x = y, and putting the answer into y - // the matrix here has to be upper triangular - template - void solve_U_y(vector & y); - // solving this * x = y, and putting the answer into y - // the matrix here has to be upper triangular - template - void solve_U_y_indexed_only(indexed_vector & y, const lp_settings&, vector & sorted_active_rows ); - - T get_elem(unsigned i, unsigned j) const override { return get(i, j); } - unsigned get_number_of_rows() const { return dimension(); } - unsigned get_number_of_columns() const { return dimension(); } - void set_number_of_rows(unsigned /*m*/) override { } - void set_number_of_columns(unsigned /*n*/) override { } - template - L dot_product_with_row (unsigned row, const vector & y) const; - - template - L dot_product_with_row (unsigned row, const indexed_vector & y) const; - - unsigned get_number_of_nonzeroes() const; - - bool get_non_zero_column_in_row(unsigned i, unsigned *j) const; - - void remove_element_that_is_not_in_w(vector> & column_vals, indexed_value & col_el_iv); - - - // w contains the new column - // the old column inside of the matrix has not been changed yet - void remove_elements_that_are_not_in_w_and_update_common_elements(unsigned column_to_replace, indexed_vector & w); - - void add_new_element(unsigned row, unsigned col, const T& val); - - // w contains the "rest" of the new column; all common elements of w and the old column has been zeroed - // the old column inside of the matrix has not been changed yet - void add_new_elements_of_w_and_clear_w(unsigned column_to_replace, indexed_vector & w, lp_settings & settings); - - void replace_column(unsigned column_to_replace, indexed_vector & w, lp_settings &settings); - - unsigned pivot_score(unsigned i, unsigned j); - - void enqueue_domain_into_pivot_queue(); - - void set_max_in_rows(); - - void zero_shortened_markovitz_numbers(); - - void prepare_for_factorization(); - - void recover_pivot_queue(vector & rejected_pivots); - - int elem_is_too_small(unsigned i, unsigned j, int c_partial_pivoting); - - bool remove_row_from_active_pivots_and_shorten_columns(unsigned row); - - void remove_pivot_column(unsigned row); - - void update_active_pivots(unsigned row); - - bool shorten_active_matrix(unsigned row, eta_matrix *eta_matrix); - - unsigned pivot_score_without_shortened_counters(unsigned i, unsigned j, unsigned k); -#ifdef Z3DEBUG - bool can_improve_score_for_row(unsigned row, unsigned score, T const & c_partial_pivoting, unsigned k); - bool really_best_pivot(unsigned i, unsigned j, T const & c_partial_pivoting, unsigned k); - void print_active_matrix(unsigned k, std::ostream & out); -#endif - bool pivot_queue_is_correct_for_row(unsigned i, unsigned k); - - bool pivot_queue_is_correct_after_pivoting(int k); - - bool get_pivot_for_column(unsigned &i, unsigned &j, int c_partial_pivoting, unsigned k); - - bool elem_is_too_small(vector> & row_chunk, indexed_value & iv, int c_partial_pivoting); - - unsigned number_of_non_zeroes_in_row(unsigned row) const { - return static_cast(m_rows[row].size()); - } - - unsigned number_of_non_zeroes_in_column(unsigned col) const { - return m_columns[col].m_values.size(); - } - - bool shorten_columns_by_pivot_row(unsigned i, unsigned pivot_column); - - bool col_is_active(unsigned j, unsigned pivot) { - return adjust_column_inverse(j) > pivot; - } - - bool row_is_active(unsigned i, unsigned pivot) { - return adjust_row_inverse(i) > pivot; - } - - bool fill_eta_matrix(unsigned j, eta_matrix ** eta); -#ifdef Z3DEBUG - bool is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings & settings) const; - - bool is_upper_triangular_until(unsigned k) const; - void check_column_vs_rows(unsigned col); - - void check_row_vs_columns(unsigned row); - - void check_rows_vs_columns(); - - void check_columns_vs_rows(); - - void check_matrix(); -#endif - void create_graph_G(const vector & active_rows, vector & sorted_active_rows); - void process_column_recursively(unsigned i, vector & sorted_rows); - void extend_and_sort_active_rows(const vector & active_rows, vector & sorted_active_rows); - void process_index_recursively_for_y_U(unsigned j, vector & sorted_rows); - void resize(unsigned new_dim) { - unsigned old_dim = dimension(); - lp_assert(new_dim >= old_dim); - for (unsigned j = old_dim; j < new_dim; j++) { - m_rows.push_back(vector>()); - m_columns.push_back(col_header()); - } - m_pivot_queue.resize(new_dim); - m_row_permutation.resize(new_dim); - m_column_permutation.resize(new_dim); - m_work_pivot_vector.resize(new_dim); - m_processed.resize(new_dim); - for (unsigned j = old_dim; j < new_dim; j++) { - add_new_element(j, j, numeric_traits::one()); - } - } -#ifdef Z3DEBUG -vector get_full_row(unsigned i) const; -#endif - unsigned pivot_queue_size() const { return m_pivot_queue.size(); } -}; -}; - - diff --git a/src/math/lp/square_sparse_matrix_def.h b/src/math/lp/square_sparse_matrix_def.h deleted file mode 100644 index 3533ba066..000000000 --- a/src/math/lp/square_sparse_matrix_def.h +++ /dev/null @@ -1,1301 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once - -#include "util/vector.h" -#include "math/lp/square_sparse_matrix.h" -#include -#include -namespace lp { -template -template -void square_sparse_matrix::copy_column_from_input(unsigned input_column, const M& A, unsigned j) { - vector> & new_column_vector = m_columns[j].m_values; - for (auto c : A.column(input_column)) { - unsigned col_offset = static_cast(new_column_vector.size()); - vector> & row_vector = m_rows[c.var()]; - unsigned row_offset = static_cast(row_vector.size()); - new_column_vector.push_back(indexed_value(c.coeff(), c.var(), row_offset)); - row_vector.push_back(indexed_value(c.coeff(), j, col_offset)); - m_n_of_active_elems++; - } -} - -template -template -void square_sparse_matrix::copy_column_from_input_with_possible_zeros(const M& A, unsigned j) { - vector> & new_column_vector = m_columns[j].m_values; - for (auto c : A.column(j)) { - if (is_zero(c.coeff())) - continue; - unsigned col_offset = static_cast(new_column_vector.size()); - vector> & row_vector = m_rows[c.var()]; - unsigned row_offset = static_cast(row_vector.size()); - new_column_vector.push_back(indexed_value(c.coeff(), c.var(), row_offset)); - row_vector.push_back(indexed_value(c.coeff(), j, col_offset)); - m_n_of_active_elems++; - } -} - -template -template -void square_sparse_matrix::copy_from_input_on_basis(const M & A, vector & basis) { - unsigned m = A.row_count(); - for (unsigned j = m; j-- > 0;) { - copy_column_from_input(basis[j], A, j); - } -} -template -template -void square_sparse_matrix::copy_from_input(const M & A) { - unsigned m = A.row_count(); - for (unsigned j = m; j-- > 0;) { - copy_column_from_input_with_possible_zeros(A, j); - } -} - -// constructor that copies columns of the basis from A -template -template -square_sparse_matrix::square_sparse_matrix(const M &A, vector & basis) : - m_n_of_active_elems(0), - m_pivot_queue(A.row_count()), - m_row_permutation(A.row_count()), - m_column_permutation(A.row_count()), - m_work_pivot_vector(A.row_count(), -1), - m_processed(A.row_count()) { - init_row_headers(); - init_column_headers(); - copy_from_input_on_basis(A, basis); -} - -template -template -square_sparse_matrix::square_sparse_matrix(const M &A) : - m_n_of_active_elems(0), - m_pivot_queue(A.row_count()), - m_row_permutation(A.row_count()), - m_column_permutation(A.row_count()), - m_work_pivot_vector(A.row_count(), -1), - m_processed(A.row_count()) { - init_row_headers(); - init_column_headers(); - copy_from_input(A); -} - - -template -void square_sparse_matrix::set_with_no_adjusting_for_row(unsigned row, unsigned col, T val) { // should not be used in efficient code - vector> & row_vec = m_rows[row]; - for (auto & iv : row_vec) { - if (iv.m_index == col) { - iv.set_value(val); - return; - } - } - // have not found the column between the indices - row_vec.push_back(indexed_value(val, col, -1)); -} - -template -void square_sparse_matrix::set_with_no_adjusting_for_col(unsigned row, unsigned col, T val) { // should not be used in efficient code - vector> & col_vec = m_columns[col].m_values; - for (auto & iv : col_vec) { - if (iv.m_index == row) { - iv.set_value(val); - return; - } - } - // have not found the column between the indices - col_vec.push_back(indexed_value(val, row, -1)); -} - - -template -void square_sparse_matrix::set_with_no_adjusting(unsigned row, unsigned col, T val) { // should not be used in efficient code - set_with_no_adjusting_for_row(row, col, val); - set_with_no_adjusting_for_col(row, col, val); -} - -template -void square_sparse_matrix::set(unsigned row, unsigned col, T val) { // should not be used in efficient code - lp_assert(row < dimension() && col < dimension()); - // m_dense.set_elem(row, col, val); - row = adjust_row(row); - col = adjust_column(col); - set_with_no_adjusting(row, col, val); - // lp_assert(*this == m_dense); -} - -template -T const & square_sparse_matrix::get_not_adjusted(unsigned row, unsigned col) const { - for (indexed_value const & iv : m_rows[row]) { - if (iv.m_index == col) { - return iv.m_value; - } - } - return numeric_traits::zero(); -} - -template -T const & square_sparse_matrix::get(unsigned row, unsigned col) const { // should not be used in efficient code - row = adjust_row(row); - auto & row_chunk = m_rows[row]; - col = adjust_column(col); - for (indexed_value const & iv : row_chunk) { - if (iv.m_index == col) { - return iv.m_value; - } - } - return numeric_traits::zero(); -} - -// constructor creating a zero matrix of dim*dim -template -square_sparse_matrix::square_sparse_matrix(unsigned dim, unsigned ) : - m_pivot_queue(dim), // dim will be the initial size of the queue - m_row_permutation(dim), - m_column_permutation(dim), - m_work_pivot_vector(dim, -1), - m_processed(dim) { - init_row_headers(); - init_column_headers(); - } - -template -void square_sparse_matrix::init_row_headers() { - for (unsigned l = 0; l < m_row_permutation.size(); l++) { - m_rows.push_back(vector>()); - } -} - -template -void square_sparse_matrix::init_column_headers() { // we always have only square square_sparse_matrix - for (unsigned l = 0; l < m_row_permutation.size(); l++) { - m_columns.push_back(col_header()); - } -} - -template -unsigned square_sparse_matrix::lowest_row_in_column(unsigned j) { - auto & mc = get_column_values(adjust_column(j)); - unsigned ret = 0; - for (auto & iv : mc) { - unsigned row = adjust_row_inverse(iv.m_index); - if (row > ret) { - ret = row; - } - } - return ret; -} - -template -void square_sparse_matrix::remove_element(vector> & row_vals, unsigned row_offset, vector> & column_vals, unsigned column_offset) { - if (column_offset != column_vals.size() - 1) { - auto & column_iv = column_vals[column_offset] = column_vals.back(); // copy from the tail - column_iv_other(column_iv).m_other = column_offset; - if (row_offset != row_vals.size() - 1) { - auto & row_iv = row_vals[row_offset] = row_vals.back(); // copy from the tail - row_iv_other(row_iv).m_other = row_offset; - } - } else if (row_offset != row_vals.size() - 1) { - auto & row_iv = row_vals[row_offset] = row_vals.back(); // copy from the tail - row_iv_other(row_iv).m_other = row_offset; - } - // do nothing - just decrease the sizes - column_vals.pop_back(); - row_vals.pop_back(); - m_n_of_active_elems--; // the value is correct only when refactoring -} - -template -void square_sparse_matrix::remove_element(vector> & row_chunk, indexed_value & row_el_iv) { - auto & column_chunk = get_column_values(row_el_iv.m_index); - indexed_value & col_el_iv = column_chunk[row_el_iv.m_other]; - remove_element(row_chunk, col_el_iv.m_other, column_chunk, row_el_iv.m_other); -} - -template -void square_sparse_matrix::put_max_index_to_0(vector> & row_vals, unsigned max_index) { - if (max_index == 0) return; - indexed_value * max_iv = & row_vals[max_index]; - indexed_value * start_iv = & row_vals[0]; - // update the "other" columns elements which are bound to the start_iv and max_iv - m_columns[max_iv->m_index].m_values[max_iv->m_other].m_other = 0; - m_columns[start_iv->m_index].m_values[start_iv->m_other].m_other = max_index; - - // swap the elements - indexed_value t = * max_iv; - * max_iv = * start_iv; - * start_iv = t; -} - -template -void square_sparse_matrix::set_max_in_row(vector> & row_vals) { - if (row_vals.empty()) - return; - T max_val = abs(row_vals[0].m_value); - unsigned max_index = 0; - for (unsigned i = 1; i < row_vals.size(); i++) { - T iabs = abs(row_vals[i].m_value); - if (iabs > max_val) { - max_val = iabs; - max_index = i; - } - } - put_max_index_to_0(row_vals, max_index); -} - -template -bool square_sparse_matrix::pivot_with_eta(unsigned i, eta_matrix *eta_matrix, lp_settings & settings) { - const T& pivot = eta_matrix->get_diagonal_element(); - for (auto & it : eta_matrix->m_column_vector.m_data) { - if (!pivot_row_to_row(i, it.second, it.first, settings)) { - return false; - } - } - - divide_row_by_constant(i, pivot, settings); - if (!shorten_active_matrix(i, eta_matrix)) { - return false; - } - - return true; -} - -// returns the offset of the pivot column in the row -template -void square_sparse_matrix::scan_row_to_work_vector_and_remove_pivot_column(unsigned row, unsigned pivot_column) { - auto & rvals = m_rows[row]; - unsigned size = rvals.size(); - for (unsigned j = 0; j < size; j++) { - auto & iv = rvals[j]; - if (iv.m_index != pivot_column) { - m_work_pivot_vector[iv.m_index] = j; - } else { - remove_element(rvals, iv); - j--; - size--; - } - } -} - -#ifdef Z3DEBUG -template -vector square_sparse_matrix::get_full_row(unsigned i) const { - vector r; - for (unsigned j = 0; j < column_count(); j++) - r.push_back(get(i, j)); - return r; -} -#endif - - - -// This method pivots row i to row i0 by muliplying row i by -// alpha and adding it to row i0. -// After pivoting the row i0 has a max abs value set correctly at the beginning of m_start, -// Returns false if the resulting row is all zeroes, and true otherwise -template -bool square_sparse_matrix::pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, lp_settings & settings ) { - lp_assert(i < dimension() && i0 < dimension()); - lp_assert(i != i0); - unsigned pivot_col = adjust_column(i); - i = adjust_row(i); - i0 = adjust_row(i0); - vector became_zeros; - // the offset of element of the pivot column in row i0 - scan_row_to_work_vector_and_remove_pivot_column(i0, pivot_col); - auto & i0_row_vals = m_rows[i0]; - // run over the pivot row and update row i0 - unsigned prev_size_i0 = i0_row_vals.size(); - for (const auto & iv : m_rows[i]) { - unsigned j = iv.m_index; - if (j == pivot_col) continue; - T alv = alpha * iv.m_value; - int j_offs = m_work_pivot_vector[j]; - if (j_offs == -1) { // it is a new element - if (!settings.abs_val_is_smaller_than_drop_tolerance(alv)) { - add_new_element(i0, j, alv); - } - } - else { - auto & row_el_iv = i0_row_vals[j_offs]; - row_el_iv.m_value += alv; - if (settings.abs_val_is_smaller_than_drop_tolerance(row_el_iv.m_value)) { - became_zeros.push_back(j_offs); - ensure_increasing(became_zeros); - } - else { - m_columns[j].m_values[row_el_iv.m_other].set_value(row_el_iv.m_value); - } - } - } - - - // clean the work vector - for (unsigned k = 0; k < prev_size_i0; k++) { - m_work_pivot_vector[i0_row_vals[k].m_index] = -1; - } - - for (unsigned k = became_zeros.size(); k-- > 0; ) { - unsigned j = became_zeros[k]; - remove_element(i0_row_vals, i0_row_vals[j]); - if (i0_row_vals.empty()) - return false; - } - - if (numeric_traits::precise() == false) - set_max_in_row(i0_row_vals); - - return !i0_row_vals.empty(); -} - - - -// set the max val as well -// returns false if the resulting row is all zeroes, and true otherwise -template -bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned i0, indexed_vector & work_vec, - lp_settings & settings) { - remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(i0, work_vec, settings); - // all non-zero elements in m_work_pivot_vector are new - for (unsigned j : work_vec.m_index) { - if (numeric_traits::is_zero(work_vec[j])) { - continue; - } - lp_assert(!settings.abs_val_is_smaller_than_drop_tolerance(work_vec[j])); - add_new_element(i0, adjust_column(j), work_vec[j]); - work_vec[j] = numeric_traits::zero(); - } - work_vec.m_index.clear(); - auto & row_vals = m_rows[i0]; - if (row_vals.empty()) { - return false; - } - set_max_in_row(row_vals); // it helps to find larger pivots - return true; -} - - - -template -void square_sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements(unsigned row) { - auto & row_vals = m_rows[row]; - for (unsigned k = static_cast(row_vals.size()); k-- > 0;) { // we cannot simply run the iterator since we are removing - // elements from row_vals - auto & row_el_iv = row_vals[k]; - unsigned j = row_el_iv.m_index; - T & wj = m_work_pivot_vector[j]; - if (is_zero(wj)) { - remove_element(row_vals, row_el_iv); - } else { - m_columns[j].m_values[row_el_iv.m_other].set_value(wj); - row_el_iv.set_value(wj); - wj = zero_of_type(); - } - } -} - -// work_vec here has not adjusted column indices -template -void square_sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(unsigned row, indexed_vector & work_vec, lp_settings & settings) { - auto & row_vals = m_rows[row]; - for (unsigned k = static_cast(row_vals.size()); k-- > 0;) { // we cannot simply run the iterator since we are removing - // elements from row_vals - auto & row_el_iv = row_vals[k]; - unsigned j = row_el_iv.m_index; - unsigned rj = adjust_column_inverse(j); - T val = work_vec[rj]; - if (settings.abs_val_is_smaller_than_drop_tolerance(val)) { - remove_element(row_vals, row_el_iv); - lp_assert(numeric_traits::is_zero(val)); - } else { - m_columns[j].m_values[row_el_iv.m_other].set_value(row_el_iv.m_value = val); - work_vec[rj] = numeric_traits::zero(); - } - } -} - - -// adding delta columns at the end of the matrix -template -void square_sparse_matrix::add_columns_at_the_end(unsigned delta) { - for (unsigned i = 0; i < delta; i++) { - col_header col_head; - m_columns.push_back(col_head); - } - m_column_permutation.enlarge(delta); -} - -template -void square_sparse_matrix::delete_column(int i) { - lp_assert(i < dimension()); - for (auto cell = m_columns[i].m_head; cell != nullptr;) { - auto next_cell = cell->m_down; - kill_cell(cell); - cell = next_cell; - } -} - -template -void square_sparse_matrix::divide_row_by_constant(unsigned i, const T & t, lp_settings & settings) { - lp_assert(!settings.abs_val_is_smaller_than_zero_tolerance(t)); - i = adjust_row(i); - for (auto & iv : m_rows[i]) { - T &v = iv.m_value; - v /= t; - if (settings.abs_val_is_smaller_than_drop_tolerance(v)){ - v = numeric_traits::zero(); - } - m_columns[iv.m_index].m_values[iv.m_other].set_value(v); - } -} - - -// solving x * this = y, and putting the answer into y -// the matrix here has to be upper triangular -template -void square_sparse_matrix::solve_y_U(vector & y) const { // works by rows -#ifdef Z3DEBUG - // T * rs = clone_vector(y, dimension()); -#endif - unsigned end = dimension(); - for (unsigned i = 0; i + 1 < end; i++) { - // all y[i] has correct values already - const T & yv = y[i]; - if (numeric_traits::is_zero(yv)) continue; - auto & mc = get_row_values(adjust_row(i)); - for (auto & c : mc) { - unsigned col = adjust_column_inverse(c.m_index); - if (col != i) { - y[col] -= c.m_value * yv; - } - } - } -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // T * clone_y = clone_vector(y, dimension()); - // deb.apply_from_right(clone_y); - // lp_assert(vectors_are_equal(rs, clone_y, dimension())); - // delete [] clone_y; - // delete [] rs; -#endif -} - -// solving x * this = y, and putting the answer into y -// the matrix here has to be upper triangular -template -void square_sparse_matrix::solve_y_U_indexed(indexed_vector & y, const lp_settings & settings) { -#if 0 && Z3DEBUG - vector ycopy(y.m_data); - if (numeric_traits::precise() == false) - solve_y_U(ycopy); -#endif - vector sorted_active_columns; - extend_and_sort_active_rows(y.m_index, sorted_active_columns); - for (unsigned k = sorted_active_columns.size(); k-- > 0; ) { - unsigned j = sorted_active_columns[k]; - auto & yj = y[j]; - for (auto & c: m_columns[adjust_column(j)].m_values) { - unsigned i = adjust_row_inverse(c.m_index); - if (i == j) continue; - yj -= y[i] * c.m_value; - } - } - y.m_index.clear(); - for (auto j : sorted_active_columns) { - if (!settings.abs_val_is_smaller_than_drop_tolerance(y[j])) - y.m_index.push_back(j); - else if (!numeric_traits::precise()) - y.m_data[j] = zero_of_type(); - } - - lp_assert(y.is_OK()); -#if 0 && Z3DEBUG - if (numeric_traits::precise() == false) - lp_assert(vectors_are_equal(ycopy, y.m_data)); -#endif -} - - -// fills the indices for such that y[i] can be not a zero -// sort them so the smaller indices come first -// void fill_reachable_indices(std::set & rset, T *y) { -// std::queue q; -// int m = dimension(); -// for (int i = m - 1; i >= 0; i--) { -// if (!numeric_traits::is_zero(y[i])){ -// for (cell * c = m_columns[adjust_column(i)].m_head; c != nullptr; c = c->m_down) { -// unsigned row = adjust_row_inverse(c->m_i); -// q.push(row); -// } -// } -// } -// while (!q.empty()) { -// unsigned i = q.front(); -// q.pop(); -// for (cell * c = m_columns[adjust_column(i)].m_head; c != nullptr; c = c->m_down) { -// unsigned row = adjust_row_inverse(c->m_i); -// if (rset.find(row) == rset.end()){ -// rset.insert(row); -// q.push(row); -// } -// } -// } -// } - -template -template -void square_sparse_matrix::find_error_in_solution_U_y(vector& y_orig, vector & y) { - unsigned i = dimension(); - while (i--) { - y_orig[i] -= dot_product_with_row(i, y); - } -} - -template -template -void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector& y_orig, indexed_vector & y, const vector& sorted_active_rows) { - for (unsigned i: sorted_active_rows) - y_orig.add_value_at_index(i, -dot_product_with_row(i, y)); // cannot round up here!!! - // y_orig can contain very small values -} - - -template -template -void square_sparse_matrix::add_delta_to_solution(const vector& del, vector & y) { - unsigned i = dimension(); - while (i--) { - y[i] += del[i]; - } -} -template -template -void square_sparse_matrix::add_delta_to_solution(const indexed_vector& del, indexed_vector & y) { -// lp_assert(del.is_OK()); - // lp_assert(y.is_OK()); - for (auto i : del.m_index) { - y.add_value_at_index(i, del[i]); - } -} -template -template -void square_sparse_matrix::double_solve_U_y(indexed_vector& y, const lp_settings & settings){ - lp_assert(y.is_OK()); - indexed_vector y_orig(y); // copy y aside - vector active_rows; - solve_U_y_indexed_only(y, settings, active_rows); - lp_assert(y.is_OK()); - find_error_in_solution_U_y_indexed(y_orig, y, active_rows); - // y_orig contains the error now - if (y_orig.m_index.size() * ratio_of_index_size_to_all_size() < 32 * dimension()) { - active_rows.clear(); - solve_U_y_indexed_only(y_orig, settings, active_rows); - add_delta_to_solution(y_orig, y); - y.clean_up(); - } else { // the dense version - solve_U_y(y_orig.m_data); - add_delta_to_solution(y_orig.m_data, y.m_data); - y.restore_index_and_clean_from_data(); - } - lp_assert(y.is_OK()); -} -template -template -void square_sparse_matrix::double_solve_U_y(vector& y){ - vector y_orig(y); // copy y aside - solve_U_y(y); - find_error_in_solution_U_y(y_orig, y); - // y_orig contains the error now - solve_U_y(y_orig); - add_delta_to_solution(y_orig, y); -} - -// solving this * x = y, and putting the answer into y -// the matrix here has to be upper triangular -template -template -void square_sparse_matrix::solve_U_y(vector & y) { // it is a column wise version -#ifdef Z3DEBUG - // T * rs = clone_vector(y, dimension()); -#endif - - for (unsigned j = dimension(); j--; ) { - const L & yj = y[j]; - if (is_zero(yj)) continue; - for (const auto & iv : m_columns[adjust_column(j)].m_values) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i != j) { - y[i] -= iv.m_value * yj; - } - } - } -#ifdef Z3DEBUG - // dense_matrix deb(*this); - // T * clone_y = clone_vector(y, dimension()); - // deb.apply_from_left(clone_y); - // lp_assert(vectors_are_equal(rs, clone_y, dimension())); -#endif -} -template -void square_sparse_matrix::process_index_recursively_for_y_U(unsigned j, vector & sorted_active_rows) { - lp_assert(m_processed[j] == false); - m_processed[j]=true; - auto & row = m_rows[adjust_row(j)]; - for (auto & c : row) { - unsigned i = adjust_column_inverse(c.m_index); - if (i == j) continue; - if (!m_processed[i]) { - process_index_recursively_for_y_U(i, sorted_active_rows); - } - } - sorted_active_rows.push_back(j); -} - -template -void square_sparse_matrix::process_column_recursively(unsigned j, vector & sorted_active_rows) { - lp_assert(m_processed[j] == false); - auto & mc = m_columns[adjust_column(j)].m_values; - for (auto & iv : mc) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i == j) continue; - if (!m_processed[i]) { - process_column_recursively(i, sorted_active_rows); - } - } - m_processed[j]=true; - sorted_active_rows.push_back(j); -} - - -template -void square_sparse_matrix::create_graph_G(const vector & index_or_right_side, vector & sorted_active_rows) { - for (auto i : index_or_right_side) { - if (m_processed[i]) continue; - process_column_recursively(i, sorted_active_rows); - } - - for (auto i : sorted_active_rows) { - m_processed[i] = false; - } -} - - -template -void square_sparse_matrix::extend_and_sort_active_rows(const vector & index_or_right_side, vector & sorted_active_rows) { - for (auto i : index_or_right_side) { - if (m_processed[i]) continue; - process_index_recursively_for_y_U(i, sorted_active_rows); - } - - for (auto i : sorted_active_rows) { - m_processed[i] = false; - } -} - - -template -template -void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector & y, const lp_settings & settings, vector & sorted_active_rows) { // it is a column wise version - create_graph_G(y.m_index, sorted_active_rows); - - for (auto k = sorted_active_rows.size(); k-- > 0;) { - unsigned j = sorted_active_rows[k]; - const L & yj = y[j]; - if (is_zero(yj)) continue; - auto & mc = m_columns[adjust_column(j)].m_values; - for (auto & iv : mc) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i != j) { - y[i] -= iv.m_value * yj; - } - } - } - y.m_index.clear(); - for (auto j : sorted_active_rows) { - if (!settings.abs_val_is_smaller_than_drop_tolerance(y[j])) - y.m_index.push_back(j); - else if (!numeric_traits::precise()) - y[j] = zero_of_type(); - } - - lp_assert(y.is_OK()); -#ifdef Z3DEBUG - // dense_matrix deb(this); - // vector clone_y(y.m_data); - // deb.apply_from_left(clone_y); - // lp_assert(vectors_are_equal(rs, clone_y)); -#endif -} - -template -template -L square_sparse_matrix::dot_product_with_row (unsigned row, const vector & y) const { - L ret = zero_of_type(); - auto & mc = get_row_values(adjust_row(row)); - for (auto & c : mc) { - unsigned col = m_column_permutation[c.m_index]; - ret += c.m_value * y[col]; - } - return ret; -} - -template -template -L square_sparse_matrix::dot_product_with_row (unsigned row, const indexed_vector & y) const { - L ret = zero_of_type(); - auto & mc = get_row_values(adjust_row(row)); - for (auto & c : mc) { - unsigned col = m_column_permutation[c.m_index]; - ret += c.m_value * y[col]; - } - return ret; -} - - -template -unsigned square_sparse_matrix::get_number_of_nonzeroes() const { - unsigned ret = 0; - for (unsigned i = dimension(); i--; ) { - ret += number_of_non_zeroes_in_row(i); - } - return ret; -} - -template -bool square_sparse_matrix::get_non_zero_column_in_row(unsigned i, unsigned *j) const { - // go over the i-th row - auto & mc = get_row_values(adjust_row(i)); - if (mc.size() > 0) { - *j = m_column_permutation[mc[0].m_index]; - return true; - } - return false; -} - -template -void square_sparse_matrix::remove_element_that_is_not_in_w(vector> & column_vals, indexed_value & col_el_iv) { - auto & row_chunk = m_rows[col_el_iv.m_index]; - indexed_value & row_el_iv = row_chunk[col_el_iv.m_other]; - unsigned index_in_row = col_el_iv.m_other; - remove_element(row_chunk, col_el_iv.m_other, column_vals, row_el_iv.m_other); - if (index_in_row == 0) - set_max_in_row(row_chunk); -} - - -// w contains the new column -// the old column inside of the matrix has not been changed yet -template -void square_sparse_matrix::remove_elements_that_are_not_in_w_and_update_common_elements(unsigned column_to_replace, indexed_vector & w) { - // -------------------------------- - // column_vals represents the old column - auto & column_vals = m_columns[column_to_replace].m_values; - for (unsigned k = static_cast(column_vals.size()); k-- > 0;) { - indexed_value & col_el_iv = column_vals[k]; - unsigned i = col_el_iv.m_index; - T &w_data_at_i = w[adjust_row_inverse(i)]; - if (numeric_traits::is_zero(w_data_at_i)) { - remove_element_that_is_not_in_w(column_vals, col_el_iv); - } else { - auto& row_chunk = m_rows[i]; - unsigned index_in_row = col_el_iv.m_other; - if (index_in_row == 0) { - bool look_for_max = abs(w_data_at_i) < abs(row_chunk[0].m_value); - row_chunk[0].set_value(col_el_iv.m_value = w_data_at_i); - if (look_for_max) - set_max_in_row(i); - } else { - row_chunk[index_in_row].set_value(col_el_iv.m_value = w_data_at_i); - if (abs(w_data_at_i) > abs(row_chunk[0].m_value)) - put_max_index_to_0(row_chunk, index_in_row); - } - w_data_at_i = numeric_traits::zero(); - } - } -} - -template -void square_sparse_matrix::add_new_element(unsigned row, unsigned col, const T& val) { - auto & row_vals = m_rows[row]; - auto & col_vals = m_columns[col].m_values; - unsigned row_el_offs = static_cast(row_vals.size()); - unsigned col_el_offs = static_cast(col_vals.size()); - row_vals.push_back(indexed_value(val, col, col_el_offs)); - col_vals.push_back(indexed_value(val, row, row_el_offs)); - m_n_of_active_elems++; -} - -// w contains the "rest" of the new column; all common elements of w and the old column has been zeroed -// the old column inside of the matrix has not been changed yet -template -void square_sparse_matrix::add_new_elements_of_w_and_clear_w(unsigned column_to_replace, indexed_vector & w, lp_settings & settings) { - for (unsigned i : w.m_index) { - T w_at_i = w[i]; - if (numeric_traits::is_zero(w_at_i)) continue; // was dealt with already - if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_i)) { - unsigned ai = adjust_row(i); - add_new_element(ai, column_to_replace, w_at_i); - auto & row_chunk = m_rows[ai]; - lp_assert(row_chunk.size() > 0); - if (abs(w_at_i) > abs(row_chunk[0].m_value)) - put_max_index_to_0(row_chunk, static_cast(row_chunk.size()) - 1); - } - w[i] = numeric_traits::zero(); - } - w.m_index.clear(); -} - -template -void square_sparse_matrix::replace_column(unsigned column_to_replace, indexed_vector & w, lp_settings &settings) { - column_to_replace = adjust_column(column_to_replace); - remove_elements_that_are_not_in_w_and_update_common_elements(column_to_replace, w); - add_new_elements_of_w_and_clear_w(column_to_replace, w, settings); -} - -template -unsigned square_sparse_matrix::pivot_score(unsigned i, unsigned j) { - // It goes like this (rnz-1)(cnz-1) is the Markovitz number, that is the max number of - // new non zeroes we can obtain after the pivoting. - // In addition we will get another cnz - 1 elements in the eta matrix created for this pivot, - // which gives rnz(cnz-1). For example, is 0 for a column singleton, but not for - // a row singleton ( which is not a column singleton). - - auto col_header = m_columns[j]; - - return static_cast(get_row_values(i).size() * (col_header.m_values.size() - col_header.m_shortened_markovitz - 1)); -} - -template -void square_sparse_matrix::enqueue_domain_into_pivot_queue() { - lp_assert(m_pivot_queue.size() == 0); - for (unsigned i = 0; i < dimension(); i++) { - auto & rh = m_rows[i]; - unsigned rnz = static_cast(rh.size()); - for (auto iv : rh) { - unsigned j = iv.m_index; - m_pivot_queue.enqueue(i, j, rnz * (static_cast(m_columns[j].m_values.size()) - 1)); - } - } -} - -template -void square_sparse_matrix::set_max_in_rows() { - unsigned i = dimension(); - while (i--) - set_max_in_row(i); -} - - -template -void square_sparse_matrix::zero_shortened_markovitz_numbers() { - for (auto & ch : m_columns) - ch.zero_shortened_markovitz(); -} - -template -void square_sparse_matrix::prepare_for_factorization() { - zero_shortened_markovitz_numbers(); - set_max_in_rows(); - enqueue_domain_into_pivot_queue(); -} - -template -void square_sparse_matrix::recover_pivot_queue(vector & rejected_pivots) { - for (auto p : rejected_pivots) { - m_pivot_queue.enqueue(p.first, p.second, pivot_score(p.first, p.second)); - } -} - -template -int square_sparse_matrix::elem_is_too_small(unsigned i, unsigned j, int c_partial_pivoting) { - auto & row_chunk = m_rows[i]; - - if (j == row_chunk[0].m_index) { - return 0; // the max element is at the head - } - T max = abs(row_chunk[0].m_value); - for (unsigned k = 1; k < row_chunk.size(); k++) { - auto &iv = row_chunk[k]; - if (iv.m_index == j) - return abs(iv.m_value) * c_partial_pivoting < max ? 1: 0; - } - return 2; // the element became zero but it still sits in the active pivots? -} - -template -bool square_sparse_matrix::remove_row_from_active_pivots_and_shorten_columns(unsigned row) { - unsigned arow = adjust_row(row); - for (auto & iv : m_rows[arow]) { - m_pivot_queue.remove(arow, iv.m_index); - m_n_of_active_elems--; // the value is correct only when refactoring - if (adjust_column_inverse(iv.m_index) <= row) - continue; // this column will be removed anyway - auto & col = m_columns[iv.m_index]; - - col.shorten_markovich_by_one(); - if (col.m_values.size() <= col.m_shortened_markovitz) - return false; // got a zero column - } - return true; -} - -template -void square_sparse_matrix::remove_pivot_column(unsigned row) { - unsigned acol = adjust_column(row); - for (const auto & iv : m_columns[acol].m_values) - if (adjust_row_inverse(iv.m_index) >= row) - m_pivot_queue.remove(iv.m_index, acol); -} - -template -void square_sparse_matrix::update_active_pivots(unsigned row) { - unsigned arow = adjust_row(row); - for (const auto & iv : m_rows[arow]) { - col_header & ch = m_columns[iv.m_index]; - int cols = static_cast(ch.m_values.size()) - ch.m_shortened_markovitz - 1; - lp_assert(cols >= 0); - for (const auto &ivc : ch.m_values) { - unsigned i = ivc.m_index; - if (adjust_row_inverse(i) <= row) continue; // the i is not an active row - m_pivot_queue.enqueue(i, iv.m_index, static_cast(m_rows[i].size())*cols); - } - } -} - -template -bool square_sparse_matrix::shorten_active_matrix(unsigned row, eta_matrix *eta_matrix) { - if (!remove_row_from_active_pivots_and_shorten_columns(row)) - return false; - remove_pivot_column(row); - // need to know the max priority of the queue here - update_active_pivots(row); - if (eta_matrix == nullptr) return true; - // it looks like double work, but the pivot scores have changed for all rows - // touched by eta_matrix - for (auto & it : eta_matrix->m_column_vector.m_data) { - unsigned row = adjust_row(it.first); - const auto & row_values = m_rows[row]; - unsigned rnz = static_cast(row_values.size()); - for (auto & iv : row_values) { - const col_header& ch = m_columns[iv.m_index]; - int cnz = static_cast(ch.m_values.size()) - ch.m_shortened_markovitz - 1; - lp_assert(cnz >= 0); - m_pivot_queue.enqueue(row, iv.m_index, rnz * cnz); - } - } - - return true; -} - -template -unsigned square_sparse_matrix::pivot_score_without_shortened_counters(unsigned i, unsigned j, unsigned k) { - auto &cols = m_columns[j].m_values; - unsigned cnz = cols.size(); - for (auto & iv : cols) { - if (adjust_row_inverse(iv.m_index) < k) - cnz--; - } - lp_assert(cnz > 0); - return m_rows[i].m_values.size() * (cnz - 1); -} -#ifdef Z3DEBUG -template -bool square_sparse_matrix::can_improve_score_for_row(unsigned row, unsigned score, T const & c_partial_pivoting, unsigned k) { - unsigned arow = adjust_row(row); - auto & row_vals = m_rows[arow].m_values; - auto & begin_iv = row_vals[0]; - T row_max = abs(begin_iv.m_value); - lp_assert(adjust_column_inverse(begin_iv.m_index) >= k); - if (pivot_score_without_shortened_counters(arow, begin_iv.m_index, k) < score) { - print_active_matrix(k); - return true; - } - for (unsigned jj = 1; jj < row_vals.size(); jj++) { - auto & iv = row_vals[jj]; - lp_assert(adjust_column_inverse(iv.m_index) >= k); - lp_assert(abs(iv.m_value) <= row_max); - if (c_partial_pivoting * abs(iv.m_value) < row_max) continue; - if (pivot_score_without_shortened_counters(arow, iv.m_index, k) < score) { - print_active_matrix(k); - return true; - } - } - return false; -} - -template -bool square_sparse_matrix::really_best_pivot(unsigned i, unsigned j, T const & c_partial_pivoting, unsigned k) { - unsigned queue_pivot_score = pivot_score_without_shortened_counters(i, j, k); - for (unsigned ii = k; ii < dimension(); ii++) { - lp_assert(!can_improve_score_for_row(ii, queue_pivot_score, c_partial_pivoting, k)); - } - return true; -} -template -void square_sparse_matrix::print_active_matrix(unsigned k, std::ostream & out) { - out << "active matrix for k = " << k << std::endl; - if (k >= dimension()) { - out << "empty" << std::endl; - return; - } - unsigned dim = dimension() - k; - dense_matrix b(dim, dim); - for (unsigned i = 0; i < dim; i++) - for (unsigned j = 0; j < dim; j++ ) - b.set_elem(i, j, zero_of_type()); - for (int i = k; i < dimension(); i++) { - unsigned col = adjust_column(i); - for (auto &iv : get_column_values(col)) { - unsigned row = iv.m_index; - unsigned row_ex = this->adjust_row_inverse(row); - if (row_ex < k) continue; - auto v = this->get_not_adjusted(row, col); - b.set_elem(row_ex - k, i -k, v); - } - } - print_matrix(b, out); -} - -template -bool square_sparse_matrix::pivot_queue_is_correct_for_row(unsigned i, unsigned k) { - unsigned arow = adjust_row(i); - for (auto & iv : m_rows[arow].m_values) { - lp_assert(pivot_score_without_shortened_counters(arow, iv.m_index, k + 1) == - m_pivot_queue.get_priority(arow, iv.m_index)); - } - return true; -} - -template -bool square_sparse_matrix::pivot_queue_is_correct_after_pivoting(int k) { - for (unsigned i = k + 1; i < dimension(); i++ ) - lp_assert(pivot_queue_is_correct_for_row(i, k)); - lp_assert(m_pivot_queue.is_correct()); - return true; -} -#endif - -template -bool square_sparse_matrix::get_pivot_for_column(unsigned &i, unsigned &j, int c_partial_pivoting, unsigned k) { - vector pivots_candidates_that_are_too_small; - while (!m_pivot_queue.is_empty()) { - m_pivot_queue.dequeue(i, j); - unsigned i_inv = adjust_row_inverse(i); - if (i_inv < k) continue; - unsigned j_inv = adjust_column_inverse(j); - if (j_inv < k) continue; - int _small = elem_is_too_small(i, j, c_partial_pivoting); - if (!_small) { -#ifdef Z3DEBUG - // if (!really_best_pivot(i, j, c_partial_pivoting, k)) { - // print_active_matrix(k); - // lp_assert(false); - // } -#endif - recover_pivot_queue(pivots_candidates_that_are_too_small); - i = i_inv; - j = j_inv; - return true; - } - if (_small != 2) { // 2 means that the pair is not in the matrix - pivots_candidates_that_are_too_small.push_back(std::make_pair(i, j)); - } - } - recover_pivot_queue(pivots_candidates_that_are_too_small); - return false; -} - -template -bool square_sparse_matrix::elem_is_too_small(vector> & row_chunk, indexed_value & iv, int c_partial_pivoting) { - if (&iv == &row_chunk[0]) { - return false; // the max element is at the head - } - T val = abs(iv.m_value); - T max = abs(row_chunk[0].m_value); - return val * c_partial_pivoting < max; -} - -template -bool square_sparse_matrix::shorten_columns_by_pivot_row(unsigned i, unsigned pivot_column) { - vector> & row_chunk = get_row_values(i); - - for (indexed_value & iv : row_chunk) { - unsigned j = iv.m_index; - if (j == pivot_column) { - lp_assert(!col_is_active(j)); - continue; - } - m_columns[j].shorten_markovich_by_one(); - - if (m_columns[j].m_shortened_markovitz >= get_column_values(j).size()) { // got the zero column under the row! - return false; - } - } - return true; -} - -template -bool square_sparse_matrix::fill_eta_matrix(unsigned j, eta_matrix ** eta) { - const vector> & col_chunk = get_column_values(adjust_column(j)); - bool is_unit = true; - for (const auto & iv : col_chunk) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i > j) { - is_unit = false; - break; - } - if (i == j && iv.m_value != 1) { - is_unit = false; - break; - } - } - - if (is_unit) { - *eta = nullptr; - return true; - } - -#ifdef Z3DEBUG - *eta = new eta_matrix(j, dimension()); -#else - *eta = new eta_matrix(j); -#endif - for (const auto & iv : col_chunk) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i < j) { - continue; - } - if (i > j) { - (*eta)->push_back(i, - iv.m_value); - } else { // i == j - if ( !(*eta)->set_diagonal_element(iv.m_value)) { - delete *eta; - *eta = nullptr; - return false; - } - - } - } - - (*eta)->divide_by_diagonal_element(); - return true; -} -#ifdef Z3DEBUG -template -bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings & settings) const { - for (unsigned i = 0; i < dimension(); i++) { - vector> const & row_chunk = get_row_values(i); - lp_assert(row_chunk.size()); - T const & max = abs(row_chunk[0].m_value); - unsigned ai = adjust_row_inverse(i); - for (auto & iv : row_chunk) { - lp_assert(abs(iv.m_value) <= max); - unsigned aj = adjust_column_inverse(iv.m_index); - if (!(ai <= aj || numeric_traits::is_zero(iv.m_value))) - return false; - if (aj == ai) { - if (iv.m_value != 1) { - return false; - } - } - if (settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value) && (!is_zero(iv.m_value))) - return false; - } - } - return true; -} - -template -bool square_sparse_matrix::is_upper_triangular_until(unsigned k) const { - for (unsigned j = 0; j < dimension() && j < k; j++) { - unsigned aj = adjust_column(j); - auto & col = get_column_values(aj); - for (auto & iv : col) { - unsigned row = adjust_row_inverse(iv.m_index); - if (row > j) - return false; - } - } - return true; -} - -template -void square_sparse_matrix::check_column_vs_rows(unsigned col) { - auto & mc = get_column_values(col); - for (indexed_value & column_iv : mc) { - indexed_value & row_iv = column_iv_other(column_iv); - if (row_iv.m_index != col) { - lp_assert(false); - } - - if (& row_iv_other(row_iv) != &column_iv) { - lp_assert(false); - } - - if (row_iv.m_value != column_iv.m_value) { - lp_assert(false); - } - } -} - -template -void square_sparse_matrix::check_row_vs_columns(unsigned row) { - auto & mc = get_row_values(row); - for (indexed_value & row_iv : mc) { - indexed_value & column_iv = row_iv_other(row_iv); - - if (column_iv.m_index != row) { - lp_assert(false); - } - - if (& row_iv != & column_iv_other(column_iv)) { - lp_assert(false); - } - - if (row_iv.m_value != column_iv.m_value) { - lp_assert(false); - } - } -} - -template -void square_sparse_matrix::check_rows_vs_columns() { - for (unsigned i = 0; i < dimension(); i++) { - check_row_vs_columns(i); - } -} - -template -void square_sparse_matrix::check_columns_vs_rows() { - for (unsigned i = 0; i < dimension(); i++) { - check_column_vs_rows(i); - } -} -template -void square_sparse_matrix::check_matrix() { - check_rows_vs_columns(); - check_columns_vs_rows(); -} -#endif -} - diff --git a/src/math/lp/static_matrix.cpp b/src/math/lp/static_matrix.cpp index 571e9b1d0..efb6e07cf 100644 --- a/src/math/lp/static_matrix.cpp +++ b/src/math/lp/static_matrix.cpp @@ -23,42 +23,19 @@ Revision History: #include #include "math/lp/static_matrix_def.h" #include "math/lp/lp_core_solver_base.h" -#include "math/lp/lp_dual_core_solver.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/lp_primal_core_solver.h" -#include "math/lp/scaler.h" #include "math/lp/lar_solver.h" namespace lp { -template void static_matrix::add_columns_at_the_end(unsigned int); -template void static_matrix::clear(); -#ifdef Z3DEBUG -template bool static_matrix::is_correct() const; -#endif -template void static_matrix::copy_column_to_indexed_vector(unsigned int, indexed_vector&) const; - -template double static_matrix::get_balance() const; -template std::set> static_matrix::get_domain(); template std::set> lp::static_matrix::get_domain(); template std::set> lp::static_matrix >::get_domain(); -template double static_matrix::get_elem(unsigned int, unsigned int) const; -template double static_matrix::get_max_abs_in_column(unsigned int) const; -template double static_matrix::get_min_abs_in_column(unsigned int) const; -template double static_matrix::get_min_abs_in_row(unsigned int) const; -template void static_matrix::init_empty_matrix(unsigned int, unsigned int); -template void static_matrix::init_row_columns(unsigned int, unsigned int); -template static_matrix::ref & static_matrix::ref::operator=(double const&); -template void static_matrix::set(unsigned int, unsigned int, double const&); -template static_matrix::static_matrix(unsigned int, unsigned int); template void static_matrix::add_column_to_vector(mpq const&, unsigned int, mpq*) const; template void static_matrix::add_columns_at_the_end(unsigned int); template bool static_matrix::is_correct() const; -template void static_matrix::copy_column_to_indexed_vector(unsigned int, indexed_vector&) const; template mpq static_matrix::get_balance() const; template mpq static_matrix::get_elem(unsigned int, unsigned int) const; template mpq static_matrix::get_max_abs_in_column(unsigned int) const; template mpq static_matrix::get_max_abs_in_row(unsigned int) const; -template double static_matrix::get_max_abs_in_row(unsigned int) const; template mpq static_matrix::get_min_abs_in_column(unsigned int) const; template mpq static_matrix::get_min_abs_in_row(unsigned int) const; template void static_matrix::init_row_columns(unsigned int, unsigned int); @@ -69,13 +46,11 @@ template static_matrix::static_matrix(unsigned int, unsigned int); #ifdef Z3DEBUG template bool static_matrix >::is_correct() const; #endif -template void static_matrix >::copy_column_to_indexed_vector(unsigned int, indexed_vector&) const; template mpq static_matrix >::get_elem(unsigned int, unsigned int) const; template void static_matrix >::init_empty_matrix(unsigned int, unsigned int); template void static_matrix >::set(unsigned int, unsigned int, mpq const&); -template bool lp::static_matrix::pivot_row_to_row_given_cell(unsigned int, column_cell &, unsigned int); template bool lp::static_matrix::pivot_row_to_row_given_cell(unsigned int, column_cell& , unsigned int); template bool lp::static_matrix >::pivot_row_to_row_given_cell(unsigned int, column_cell&, unsigned int); template void lp::static_matrix >::remove_element(vector, true, unsigned int>&, lp::row_cell&); diff --git a/src/math/lp/static_matrix.h b/src/math/lp/static_matrix.h index a42926853..f79ff36ac 100644 --- a/src/math/lp/static_matrix.h +++ b/src/math/lp/static_matrix.h @@ -12,7 +12,6 @@ Author: #include #include #include -#include "math/lp/sparse_vector.h" #include "math/lp/indexed_vector.h" #include "math/lp/permutation_matrix.h" #include @@ -169,8 +168,6 @@ public: std::set> get_domain(); - void copy_column_to_indexed_vector(unsigned j, indexed_vector & v) const; - T get_max_abs_in_row(unsigned row) const; void add_column_to_vector (const T & a, unsigned j, T * v) const { for (const auto & it : m_columns[j]) { @@ -223,7 +220,7 @@ public: virtual void set_number_of_columns(unsigned /*n*/) { } #endif - T get_max_val_in_row(unsigned /* i */) const { lp_unreachable(); } + T get_max_val_in_row(unsigned /* i */) const { UNREACHABLE(); } T get_balance() const; @@ -344,7 +341,6 @@ public: void fill_last_row_with_pivoting(const term& row, unsigned bj, // the index of the basis column const vector & basis_heading) { - lp_assert(numeric_traits::precise()); lp_assert(row_count() > 0); m_work_vector.resize(column_count()); T a; @@ -360,7 +356,6 @@ public: for (auto p : row) { fill_last_row_with_pivoting_loop_block(p.column().index(), basis_heading); } - lp_assert(m_work_vector.is_OK()); unsigned last_row = row_count() - 1; for (unsigned j : m_work_vector.m_index) { diff --git a/src/math/lp/static_matrix_def.h b/src/math/lp/static_matrix_def.h index af2eac360..76c1dec54 100644 --- a/src/math/lp/static_matrix_def.h +++ b/src/math/lp/static_matrix_def.h @@ -174,14 +174,6 @@ std::set> static_matrix::get_domain() { return ret; } -template void static_matrix::copy_column_to_indexed_vector (unsigned j, indexed_vector & v) const { - lp_assert(j < m_columns.size()); - for (auto & it : m_columns[j]) { - const T& val = get_val(it); - if (!is_zero(val)) - v.set_value(val, it.var()); - } -} template T static_matrix::get_max_abs_in_row(unsigned row) const { T ret = numeric_traits::zero(); diff --git a/src/math/lp/tail_matrix.h b/src/math/lp/tail_matrix.h deleted file mode 100644 index 9fa1a4a47..000000000 --- a/src/math/lp/tail_matrix.h +++ /dev/null @@ -1,50 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include "math/lp/indexed_vector.h" -#include "math/lp/matrix.h" -#include "math/lp/lp_settings.h" -// These matrices appear at the end of the list - -namespace lp { -template -class tail_matrix -#ifdef Z3DEBUG - : public matrix -#endif -{ -public: - virtual void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) = 0; - virtual void apply_from_left(vector & w, lp_settings & settings) = 0; - virtual void apply_from_right(vector & w) = 0; - virtual void apply_from_right(indexed_vector & w) = 0; - virtual ~tail_matrix() = default; - virtual bool is_dense() const = 0; - struct ref_row { - const tail_matrix & m_A; - unsigned m_row; - ref_row(const tail_matrix& m, unsigned row): m_A(m), m_row(row) {} - T operator[](unsigned j) const { return m_A.get_elem(m_row, j);} - }; - ref_row operator[](unsigned i) const { return ref_row(*this, i);} -}; -} diff --git a/src/math/subpaving/tactic/expr2subpaving.cpp b/src/math/subpaving/tactic/expr2subpaving.cpp index 3afbc1ecb..e2c43d12b 100644 --- a/src/math/subpaving/tactic/expr2subpaving.cpp +++ b/src/math/subpaving/tactic/expr2subpaving.cpp @@ -311,7 +311,6 @@ struct expr2subpaving::imp { case OP_REM: case OP_IRRATIONAL_ALGEBRAIC_NUM: case OP_DIV0: - case OP_REM0: case OP_MOD0: case OP_IDIV0: throw default_exception("you must apply arithmetic purifier before internalizing expressions into the subpaving module."); diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index b180a8a1f..ebb22f079 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -146,7 +146,8 @@ void func_interp::set_else(expr * e) { ptr_vector args; while (e && is_fi_entry_expr(e, args)) { - insert_entry(args.data(), to_app(e)->get_arg(1)); + if (!get_entry(args.data())) + insert_entry(args.data(), to_app(e)->get_arg(1)); e = to_app(e)->get_arg(2); } diff --git a/src/model/model_core.cpp b/src/model/model_core.cpp index a23fc9800..222247c07 100644 --- a/src/model/model_core.cpp +++ b/src/model/model_core.cpp @@ -81,7 +81,7 @@ void model_core::register_decl(func_decl * d, func_interp * fi) { } func_interp* model_core::update_func_interp(func_decl* d, func_interp* fi) { - TRACE("model", tout << "register " << d->get_name() << "\n";); + TRACE("model_verbose", tout << "register " << d->get_name() << "\n";); SASSERT(d->get_arity() > 0); SASSERT(&fi->m() == &m); diff --git a/src/model/model_smt2_pp.cpp b/src/model/model_smt2_pp.cpp index df1a8b734..b5ac3fbad 100644 --- a/src/model/model_smt2_pp.cpp +++ b/src/model/model_smt2_pp.cpp @@ -195,12 +195,10 @@ static void pp_funs(std::ostream & out, ast_printer_context & ctx, model_core co ptr_buffer func_decls; sort_fun_decls(m, md, func_decls); for (func_decl * f : func_decls) { - if (recfun_util.is_defined(f) && !recfun_util.is_generated(f)) { + if (recfun_util.is_defined(f) && !recfun_util.is_generated(f)) continue; - } - if (!m.is_considered_uninterpreted(f)) { + if (!m.is_considered_uninterpreted(f)) continue; - } func_interp * f_i = md.get_func_interp(f); SASSERT(f->get_arity() == f_i->get_arity()); format_ref body(fm(m)); diff --git a/src/model/value_factory.cpp b/src/model/value_factory.cpp index 2b412f850..30fa82caf 100644 --- a/src/model/value_factory.cpp +++ b/src/model/value_factory.cpp @@ -28,13 +28,13 @@ value_factory::value_factory(ast_manager & m, family_id fid): value_factory::~value_factory() { } -basic_factory::basic_factory(ast_manager & m): - value_factory(m, m.get_basic_family_id()) { +basic_factory::basic_factory(ast_manager & m, unsigned seed): + value_factory(m, m.get_basic_family_id()), m_rand(seed) { } expr * basic_factory::get_some_value(sort * s) { - if (m_manager.is_bool(s)) - return m_manager.mk_false(); + if (m_manager.is_bool(s)) + return (m_rand() % 2 == 0) ? m_manager.mk_false() : m_manager.mk_true(); return nullptr; } diff --git a/src/model/value_factory.h b/src/model/value_factory.h index cf56439d9..20c383efe 100644 --- a/src/model/value_factory.h +++ b/src/model/value_factory.h @@ -60,8 +60,9 @@ public: }; class basic_factory : public value_factory { + random_gen m_rand; public: - basic_factory(ast_manager & m); + basic_factory(ast_manager & m, unsigned seed); expr * get_some_value(sort * s) override; @@ -193,9 +194,8 @@ public: while (!is_new) { result = mk_value(next, s, is_new); next++; - if (has_max && next > max_size + start) { - return nullptr; - } + if (has_max && next > max_size + start) + return nullptr; } SASSERT(result != 0); return result; diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index c9d2c7797..230a452df 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -577,6 +577,7 @@ namespace datalog { m_rule_properties.check_uninterpreted_free(); m_rule_properties.check_nested_free(); m_rule_properties.check_infinite_sorts(); + m_rule_properties.check_background_free(); break; case SPACER_ENGINE: m_rule_properties.collect(r); @@ -584,6 +585,7 @@ namespace datalog { m_rule_properties.check_for_negated_predicates(); m_rule_properties.check_uninterpreted_free(); m_rule_properties.check_quantifier_free(exists_k); + m_rule_properties.check_background_free(); break; case BMC_ENGINE: m_rule_properties.collect(r); @@ -598,13 +600,16 @@ namespace datalog { m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); + m_rule_properties.check_background_free(); break; case CLP_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); + m_rule_properties.check_background_free(); break; case DDNF_ENGINE: + m_rule_properties.check_background_free(); break; case LAST_ENGINE: default: diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index 623f287f7..401d8e816 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -324,17 +324,14 @@ namespace datalog { if (cycle_len < 2) return; auto aux = container[permutation_cycle[0]]; - verbose_stream() << "xx " << cycle_len << "\n"; for (unsigned i = 1; i < cycle_len; i++) container[permutation_cycle[i-1]] = container[permutation_cycle[i]]; container[permutation_cycle[cycle_len-1]] = aux; } template void permute_by_cycle(ref_vector & container, unsigned cycle_len, const unsigned * permutation_cycle) { - if (cycle_len<2) { + if (cycle_len < 2) return; - } - verbose_stream() << "ptr\n"; T * aux = container.get(permutation_cycle[0]); for (unsigned i=1; i < cycle_len; i++) { container.set(permutation_cycle[i-1], container.get(permutation_cycle[i])); diff --git a/src/muz/base/rule_properties.cpp b/src/muz/base/rule_properties.cpp index 7632a0c2f..239fa73b6 100644 --- a/src/muz/base/rule_properties.cpp +++ b/src/muz/base/rule_properties.cpp @@ -139,6 +139,12 @@ void rule_properties::check_nested_free() { } } +void rule_properties::check_background_free() { + if (m_ctx.get_num_assertions() > 0) + throw default_exception("engine does not support background assertions"); +} + + void rule_properties::check_existential_tail() { ast_mark visited; ptr_vector todo, tocheck; diff --git a/src/muz/base/rule_properties.h b/src/muz/base/rule_properties.h index 896b1bb16..a7ef9a0de 100644 --- a/src/muz/base/rule_properties.h +++ b/src/muz/base/rule_properties.h @@ -68,6 +68,7 @@ namespace datalog { void check_for_negated_predicates(); void check_nested_free(); void check_infinite_sorts(); + void check_background_free(); bool is_monotone() { return m_is_monotone; } void operator()(var* n); void operator()(quantifier* n); diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 4c5c13e34..38b1c7149 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -114,18 +114,10 @@ void pob::inherit(pob const &p) { m_desired_level = std::max(m_desired_level, p.m_desired_level); m_open = p.m_open; m_use_farkas = p.m_use_farkas; - - m_is_conjecture = p.m_is_conjecture; - m_enable_local_gen = p.m_enable_local_gen; - m_enable_concretize = p.m_enable_concretize; - m_is_subsume = p.m_is_subsume; - m_enable_expand_bnd_gen = p.m_enable_expand_bnd_gen; - m_weakness = p.m_weakness; m_derivation = nullptr; - m_gas = p.m_gas; } void pob::close () { diff --git a/src/muz/spacer/spacer_convex_closure.cpp b/src/muz/spacer/spacer_convex_closure.cpp index 476821643..e313c972b 100644 --- a/src/muz/spacer/spacer_convex_closure.cpp +++ b/src/muz/spacer/spacer_convex_closure.cpp @@ -22,21 +22,23 @@ Notes: #include "ast/rewriter/th_rewriter.h" namespace { + +#ifdef Z3DEBUG bool is_int_matrix(const spacer::spacer_matrix &matrix) { - rational val; - for (unsigned i = 0, rows = matrix.num_rows(); i < rows; i++) { + for (unsigned i = 0, rows = matrix.num_rows(); i < rows; i++) for (unsigned j = 0, cols = matrix.num_cols(); j < cols; j++) - if (!matrix.get(i, j).is_int()) return false; - } + if (!matrix.get(i, j).is_int()) + return false; return true; } bool is_sorted(const vector &data) { - for (unsigned i = 0; i < data.size() - 1; i++) { - if (!(data[i] >= data[i + 1])) return false; - } + for (unsigned i = 0; i < data.size() - 1; i++) + if (!(data[i] >= data[i + 1])) + return false; return true; } +#endif /// Check whether all elements of \p data are congruent modulo \p m bool is_congruent_mod(const vector &data, const rational &m) { diff --git a/src/muz/spacer/spacer_global_generalizer.cpp b/src/muz/spacer/spacer_global_generalizer.cpp index 8c0d7aa6f..55bc4eec7 100644 --- a/src/muz/spacer/spacer_global_generalizer.cpp +++ b/src/muz/spacer/spacer_global_generalizer.cpp @@ -170,14 +170,14 @@ void lemma_global_generalizer::subsumer::mk_col_names(const lemma_cluster &lc) { m_col_names.reserve(sub.get_num_bindings()); for (unsigned j = 0, sz = sub.get_num_bindings(); j < sz; j++) { - // get var id (sub is in reverse order) - sub.get_binding(sz - 1 - j, v, r); + sub.get_binding(j, v, r); auto *sort = r.get_expr()->get_sort(); - - if (!m_col_names.get(j) || m_col_names.get(j)->get_sort() != sort) { + auto i = v.first; + SASSERT(0 <= i && i < sz); + if (!m_col_names.get(i) || m_col_names.get(i)->get_sort() != sort) { // create a fresh skolem constant for the jth variable // reuse variables if they are already here and have matching sort - m_col_names[j] = m.mk_fresh_const("mrg_cvx!!", sort); + m_col_names[i] = m.mk_fresh_const("mrg_cvx!!", sort); } } @@ -210,10 +210,13 @@ void lemma_global_generalizer::subsumer::setup_cvx_closure( is_first = false; } + unsigned i; for (unsigned j = 0; j < n_vars; j++) { - sub.get_binding(n_vars - 1 - j, v, r); + sub.get_binding(j, v, r); + i = v.first; + SASSERT(0 <= i && i < n_vars); if (is_numeral(r.get_expr(), num)) { - m_col_lcm[j] = lcm(m_col_lcm.get(j), abs(denominator(num))); + m_col_lcm[i] = lcm(m_col_lcm.get(i), abs(denominator(num))); } } } @@ -229,14 +232,17 @@ void lemma_global_generalizer::subsumer::setup_cvx_closure( cc.set_col_var(j, mk_rat_mul(m_col_lcm.get(j), m_col_names.get(j))); vector row; + unsigned i; for (const auto &lemma : lemmas) { row.reset(); + row.reserve(n_vars); const substitution &sub = lemma.get_sub(); for (unsigned j = 0, sz = sub.get_num_bindings(); j < sz; j++) { - sub.get_binding(sz - 1 - j, v, r); + sub.get_binding(j, v, r); + i = v.first; VERIFY(is_numeral(r.get_expr(), num)); - row.push_back(m_col_lcm.get(j) * num); + row[i] = m_col_lcm.get(i) * num; } // -- add normalized row to convex closure diff --git a/src/muz/transforms/dl_mk_slice.cpp b/src/muz/transforms/dl_mk_slice.cpp index 25888cb68..c13509224 100644 --- a/src/muz/transforms/dl_mk_slice.cpp +++ b/src/muz/transforms/dl_mk_slice.cpp @@ -467,15 +467,15 @@ namespace datalog { void mk_slice::solve_vars(rule& r, uint_set& used_vars, uint_set& parameter_vars) { expr_ref_vector conjs = get_tail_conjs(r); for (expr * e : conjs) { - expr_ref r(m); + expr_ref rhs(m); unsigned v = 0; - if (is_eq(e, v, r) && is_output(v) && m_var_is_sliceable[v]) { + if (is_eq(e, v, rhs) && is_output(v) && m_var_is_sliceable[v]) { TRACE("dl", tout << "is_eq: " << mk_pp(e, m) << " " << (m_solved_vars[v].get()?"solved":"new") << "\n";); add_var(v); if (!m_solved_vars[v].get()) { TRACE("dl", tout << v << " is solved\n";); - add_free_vars(parameter_vars, r); - m_solved_vars[v] = r; + add_free_vars(parameter_vars, rhs); + m_solved_vars[v] = rhs; } else { TRACE("dl", tout << v << " is used\n";); @@ -666,10 +666,8 @@ namespace datalog { } else { SASSERT(m.is_value(arg)); - if (!is_output) { - TRACE("dl", tout << "input " << i << " in " << p->get_decl()->get_name() << "\n";); - bv.unset(i); - } + TRACE("dl", tout << i << " in " << p->get_decl()->get_name() << " is a value, unable to slice\n";); + bv.unset(i); } } } diff --git a/src/opt/maxsmt.cpp b/src/opt/maxsmt.cpp index 3d0834472..e684c64d6 100644 --- a/src/opt/maxsmt.cpp +++ b/src/opt/maxsmt.cpp @@ -178,14 +178,14 @@ namespace opt { maxsmt::maxsmt(maxsat_context& c, unsigned index): m(c.get_manager()), m_c(c), m_index(index), m_answer(m) {} - lbool maxsmt::operator()() { + lbool maxsmt::operator()(bool committed) { lbool is_sat = l_undef; m_msolver = nullptr; opt_params optp(m_params); symbol const& maxsat_engine = m_c.maxsat_engine(); IF_VERBOSE(1, verbose_stream() << "(maxsmt)\n";); TRACE("opt_verbose", s().display(tout << "maxsmt\n") << "\n";); - if (optp.maxlex_enable() && is_maxlex(m_soft)) + if (!committed && optp.maxlex_enable() && is_maxlex(m_soft)) m_msolver = mk_maxlex(m_c, m_index, m_soft); else if (m_soft.empty() || maxsat_engine == symbol("maxres") || maxsat_engine == symbol::null) m_msolver = mk_maxres(m_c, m_index, m_soft); @@ -213,7 +213,8 @@ namespace opt { try { is_sat = (*m_msolver)(); } - catch (z3_exception&) { + catch (z3_exception& ex) { + IF_VERBOSE(1, verbose_stream() << ex.msg() << "\n"); is_sat = l_undef; } if (is_sat != l_false) { @@ -401,7 +402,7 @@ namespace opt { for (auto const& p : soft) { maxsmt.add(p.first, p.second); } - lbool r = maxsmt(); + lbool r = maxsmt(true); if (r == l_true) { svector labels; maxsmt.get_model(m_model, labels); diff --git a/src/opt/maxsmt.h b/src/opt/maxsmt.h index ac39d7891..17306b222 100644 --- a/src/opt/maxsmt.h +++ b/src/opt/maxsmt.h @@ -137,7 +137,7 @@ namespace opt { params_ref m_params; public: maxsmt(maxsat_context& c, unsigned id); - lbool operator()(); + lbool operator()(bool committed); void updt_params(params_ref& p); void add(expr* f, rational const& w); unsigned size() const { return m_soft.size(); } diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 5895643bd..11eddc2eb 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -453,8 +453,8 @@ namespace opt { lbool context::execute_maxsat(symbol const& id, bool committed, bool scoped) { model_ref tmp; maxsmt& ms = *m_maxsmts.find(id); - if (scoped) get_solver().push(); - lbool result = ms(); + if (scoped) get_solver().push(); + lbool result = ms(committed); if (result != l_false && (ms.get_model(tmp, m_labels), tmp.get())) { ms.get_model(m_model, m_labels); } diff --git a/src/opt/wmax.cpp b/src/opt/wmax.cpp index 1fb6c05dd..9a7d1a3ca 100644 --- a/src/opt/wmax.cpp +++ b/src/opt/wmax.cpp @@ -53,7 +53,7 @@ namespace opt { TRACE("opt", tout << "weighted maxsat\n";); scoped_ensure_theory wth(*this); reset(); - if (init()) + if (!init()) return l_undef; lbool is_sat = l_true; diff --git a/src/params/solver_params.pyg b/src/params/solver_params.pyg index 1c2be3213..0912b4c7f 100644 --- a/src/params/solver_params.pyg +++ b/src/params/solver_params.pyg @@ -10,6 +10,7 @@ def_module_params('solver', ('axioms2files', BOOL, False, 'print negated theory axioms to separate files during search'), ('proof.log', SYMBOL, '', 'log clause proof trail into a file'), ('proof.check', BOOL, True, 'check proof logs'), + ('proof.check_rup', BOOL, True, 'check proof RUP inference in proof logs'), ('proof.save', BOOL, False, 'save proof log into a proof object that can be extracted using (get-proof)'), ('proof.trim', BOOL, False, 'trim and save proof into a proof object that an be extracted using (get-proof)'), )) diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 9f61d48ca..081a8c838 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -372,8 +372,8 @@ namespace smt2 { return true; } catch (scanner_exception & ex) { - SASSERT(ex.has_pos()); - error(ex.line(), ex.pos(), ex.msg()); + if (ex.has_pos()) + error(ex.line(), ex.pos(), ex.msg()); ++num_errors; } } @@ -2640,8 +2640,6 @@ namespace smt2 { check_lparen_next("invalid get-value command, '(' expected"); while (!curr_is_rparen()) { parse_expr(); - if (!is_ground(expr_stack().back())) - throw cmd_exception("invalid get-value term, term must be ground and must not contain quantifiers"); m_cached_strings.push_back(m_scanner.cached_str(cache_it, m_cache_end)); cache_it = m_cache_end; } @@ -2680,7 +2678,7 @@ namespace smt2 { m_ctx.regular_stream() << "\n "; m_ctx.regular_stream() << "(" << m_cached_strings[i] << " "; m_ctx.display(m_ctx.regular_stream(), v); - m_ctx.regular_stream() << ")"; + m_ctx.regular_stream() << ")"; } m_ctx.regular_stream() << ")" << std::endl; expr_stack().shrink(spos); diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index 0a9e803a9..73516f66d 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -200,7 +200,6 @@ namespace sat { m_drat_check_sat = p.drat_check_sat(); m_drat_file = p.drat_file(); m_smt_proof_check = p.smt_proof_check(); - m_smt_proof_check_rup = p.smt_proof_check_rup(); m_drat_disable = p.drat_disable(); m_drat = !m_drat_disable && p.threads() == 1 && diff --git a/src/sat/sat_config.h b/src/sat/sat_config.h index f8c0775b1..a47f041b0 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -180,7 +180,6 @@ namespace sat { bool m_drat_binary; symbol m_drat_file; bool m_smt_proof_check; - bool m_smt_proof_check_rup; bool m_drat_check_unsat; bool m_drat_check_sat; bool m_drat_activity; diff --git a/src/sat/sat_ddfw.cpp b/src/sat/sat_ddfw.cpp index f1493232c..ca274be51 100644 --- a/src/sat/sat_ddfw.cpp +++ b/src/sat/sat_ddfw.cpp @@ -9,7 +9,7 @@ DDFW Local search module for clauses - Author: + Author: Nikolaj Bjorner, Marijn Heule 2019-4-23 @@ -33,35 +33,58 @@ namespace sat { ddfw::~ddfw() { - for (auto& ci : m_clauses) { - m_alloc.del_clause(ci.m_clause); - } + 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 _p(m_par, p); - while (m_limit.inc() && m_min_sz > 0) { - if (should_reinit_weights()) do_reinit_weights(); - else if (do_flip()) ; - else if (should_restart()) do_restart(); - else if (should_parallel_sync()) do_parallel_sync(); - else shift_weights(); - } + 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()); + 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()); + else if (do_literal_flip()); + 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(0, verbose_stream() << "(sat.ddfw :unsat :models :kflips/sec :flips :restarts :reinits :unsat_vars :shifts"; + 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(0, verbose_stream() << "(sat.ddfw " + 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 @@ -76,60 +99,98 @@ namespace sat { m_last_flips = m_flips; } + template bool ddfw::do_flip() { - bool_var v = pick_var(); - if (reward(v) > 0 || (reward(v) == 0 && m_rand(100) <= m_config.m_use_reward_zero_pct)) { - flip(v); - if (m_unsat.size() <= m_min_sz) save_best_values(); + double reward = 0; + bool_var v = pick_var(reward); + return apply_flip(v, reward); + } + + template + 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; } - bool_var ddfw::pick_var() { + template + 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) { - double r = reward(v); - if (r > 0.0) { - sum_pos += score(r); - } - else if (r == 0.0 && sum_pos == 0 && (m_rand() % (n++)) == 0) { - v0 = v; - } + 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) { - double r = reward(v); + 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) { - if (m_par) update_reward_avg(v); - return v; - } + if (lim_pos <= 0) + return v; } } } - if (v0 != null_bool_var) { + 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())); } - /** - * TBD: map reward value to a score, possibly through an exponential function, such as - * exp(-tau/r), where tau > 0 - */ - double ddfw::mk_score(double r) { - return r; + template + bool ddfw::do_literal_flip() { + double reward = 1; + return apply_flip(pick_literal_var(), reward); } + /* + * Pick a random false literal from a satisfied clause such that + * the literal has zero break count and positive reward. + */ + template + 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)); @@ -138,10 +199,22 @@ namespace sat { } } + /** + * 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) { - for (auto& ci : m_clauses) { + 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; @@ -171,9 +244,16 @@ namespace sat { } void ddfw::add_assumptions() { - for (unsigned i = 0; i < m_assumptions.size(); ++i) { - add(1, m_assumptions.data() + i); - } + 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) { @@ -202,16 +282,14 @@ namespace sat { m_shifts = 0; m_stopwatch.start(); } - - void ddfw::reinit(solver& s) { + + void ddfw::reinit(solver& s, bool_vector const& phase) { add(s); add_assumptions(); - if (s.m_best_phase_size > 0) { - for (unsigned v = 0; v < num_vars(); ++v) { - value(v) = s.m_best_phase[v]; - reward(v) = 0; - make_count(v) = 0; - } + 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(); @@ -227,7 +305,6 @@ namespace sat { 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)); @@ -281,6 +358,7 @@ namespace sat { ci.add(nlit); } value(v) = !value(v); + update_reward_avg(v); } bool ddfw::should_reinit_weights() { @@ -291,19 +369,15 @@ namespace sat { log(); if (m_reinit_count % 2 == 0) { - for (auto& ci : m_clauses) { - ci.m_weight += 1; - } + 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; - } - } + 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; @@ -323,11 +397,9 @@ namespace sat { 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); - } - } + for (literal lit : c) + if (is_true(lit)) + ci.add(lit); switch (ci.m_num_trues) { case 0: for (literal lit : c) { @@ -348,7 +420,7 @@ namespace sat { bool ddfw::should_restart() { return m_flips >= m_restart_next; } - + void ddfw::do_restart() { reinit_values(); init_clause_data(); @@ -366,12 +438,10 @@ namespace sat { 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; - } + if (0 == (m_rand() % (1 + abs(b)))) + value(i) = (m_rand() % 2) == 0; + else + value(i) = bias(i) > 0; } } @@ -379,36 +449,36 @@ namespace sat { 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)) { - // Sum exp(xi) / exp(a) = Sum exp(xi - a) - double max_avg = 0; - for (unsigned v = 0; v < num_vars(); ++v) { - max_avg = std::max(max_avg, (double)m_vars[v].m_reward_avg); - } - double sum = 0; - for (unsigned v = 0; v < num_vars(); ++v) { - sum += exp(m_config.m_itau * (m_vars[v].m_reward_avg - max_avg)); - } - if (sum == 0) { - sum = 0.01; - } - m_probs.reset(); - for (unsigned v = 0; v < num_vars(); ++v) { - m_probs.push_back(exp(m_config.m_itau * (m_vars[v].m_reward_avg - max_avg)) / sum); - } + 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.empty()) { - m_model.reserve(num_vars()); - for (unsigned i = 0; i < num_vars(); ++i) - m_model[i] = to_lbool(value(i)); + 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(); @@ -420,13 +490,20 @@ namespace sat { } } } + unsigned h = value_hash(); + unsigned occs = 0; + bool contains = m_models.find(h, occs); if (!m_models.contains(h)) { - for (unsigned v = 0; v < num_vars(); ++v) + for (unsigned v = 0; v < num_vars(); ++v) bias(v) += value(v) ? 1 : -1; - m_models.insert(h); - if (m_models.size() > m_config.m_max_num_models) - m_models.erase(*m_models.begin()); + 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(); } @@ -514,8 +591,7 @@ namespace sat { void ddfw::shift_weights() { ++m_shifts; for (unsigned to_idx : m_unsat) { - auto& cf = m_clauses[to_idx]; - SASSERT(!cf.is_true()); + 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(); diff --git a/src/sat/sat_ddfw.h b/src/sat/sat_ddfw.h index ed9936f0a..988365285 100644 --- a/src/sat/sat_ddfw.h +++ b/src/sat/sat_ddfw.h @@ -27,12 +27,29 @@ #include "sat/sat_clause.h" #include "sat/sat_types.h" +namespace arith { + class sls; +} + namespace sat { class solver; class parallel; - class ddfw : public i_local_search { + 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 @@ -44,6 +61,19 @@ namespace sat { 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; @@ -68,8 +98,10 @@ namespace sat { 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; }; @@ -95,27 +127,20 @@ namespace sat { 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; - hashtable> m_models; + unsigned m_min_sz = 0, m_steps_since_progress = 0; + u_map m_models; stopwatch m_stopwatch; parallel* m_par; - - 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]; } - }; + local_search_plugin* m_plugin = nullptr; void flatten_use_list(); - double mk_score(double r); - - inline double score(double r) { return r; } // TBD: { for (unsigned sz = m_scores.size(); sz <= r; ++sz) m_scores.push_back(mk_score(sz)); return m_scores[r]; } + /** + * 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(); } @@ -129,6 +154,12 @@ namespace sat { 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; @@ -159,11 +190,28 @@ namespace sat { inline void dec_reward(literal lit, double w) { reward(lit.var()) -= w; } + void check_with_plugin(); + void check_without_plugin(); + // flip activity + template bool do_flip(); - bool_var pick_var(); - void flip(bool_var v); + + template + bool_var pick_var(double& reward); + + template + bool apply_flip(bool_var v, double reward); + + template + bool do_literal_flip(); + + template + bool_var pick_literal_var(); + void save_best_values(); + void save_model(); + void save_priorities(); // shift activity void shift_weights(); @@ -195,6 +243,8 @@ namespace sat { void add(unsigned sz, literal const* c); + void del(); + void add_assumptions(); inline void transfer_weight(unsigned from, unsigned to, double w); @@ -207,6 +257,8 @@ namespace sat { ~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; @@ -225,11 +277,25 @@ namespace sat { // for parallel integration unsigned num_non_binary_clauses() const override { return m_num_non_binary_clauses; } - void reinit(solver& s) override; + 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); } + }; } diff --git a/src/sat/sat_extension.h b/src/sat/sat_extension.h index 1f451e085..b82df81fe 100644 --- a/src/sat/sat_extension.h +++ b/src/sat/sat_extension.h @@ -127,6 +127,7 @@ namespace sat { virtual void add_assumptions(literal_set& ext_assumptions) {} virtual bool tracking_assumptions() { return false; } virtual bool enable_self_propagate() const { return false; } + virtual lbool local_search(bool_vector& phase) { return l_undef; } virtual bool extract_pb(std::function& card, std::function& pb) { diff --git a/src/sat/sat_local_search.cpp b/src/sat/sat_local_search.cpp index 61ddd13d8..c3cb0fb37 100644 --- a/src/sat/sat_local_search.cpp +++ b/src/sat/sat_local_search.cpp @@ -353,19 +353,13 @@ namespace sat { DEBUG_CODE(verify_unsat_stack();); } - local_search::local_search() : - m_is_unsat(false), - m_initializing(false), - m_par(nullptr) { + local_search::local_search() { } - void local_search::reinit(solver& s) { - import(s, true); - if (s.m_best_phase_size > 0) { - for (unsigned i = num_vars(); i-- > 0; ) { - set_phase(i, s.m_best_phase[i]); - } - } + void local_search::reinit(solver& s, bool_vector const& phase) { + import(s, true); + for (unsigned i = phase.size(); i-- > 0; ) + set_phase(i, phase[i]); } void local_search::import(solver const& s, bool _init) { @@ -378,11 +372,10 @@ namespace sat { m_vars.reserve(s.num_vars()); m_config.set_config(s.get_config()); - if (m_config.phase_sticky()) { - unsigned v = 0; + unsigned v = 0; + if (m_config.phase_sticky()) for (var_info& vi : m_vars) - vi.m_bias = s.m_phase[v++] ? 98 : 2; - } + vi.m_bias = s.m_phase[v++] ? 98 : 2; // copy units unsigned trail_sz = s.init_trail_size(); @@ -422,9 +415,8 @@ namespace sat { if (ext && (!ext->is_pb() || !ext->extract_pb(card, pb))) throw default_exception("local search is incomplete with extensions beyond PB"); - if (_init) { - init(); - } + if (_init) + init(); } local_search::~local_search() { diff --git a/src/sat/sat_local_search.h b/src/sat/sat_local_search.h index e46d4b009..b62234522 100644 --- a/src/sat/sat_local_search.h +++ b/src/sat/sat_local_search.h @@ -133,21 +133,21 @@ namespace sat { vector m_constraints; // all constraints literal_vector m_assumptions; // temporary assumptions literal_vector m_prop_queue; // propagation queue - unsigned m_num_non_binary_clauses; - bool m_is_pb; - bool m_is_unsat; + unsigned m_num_non_binary_clauses = 0; + bool m_is_pb = false; + bool m_is_unsat = false; unsigned_vector m_unsat_stack; // store all the unsat constraints unsigned_vector m_index_in_unsat_stack; // which position is a constraint in the unsat_stack // configuration changed decreasing variables (score>0 and conf_change==true) bool_var_vector m_goodvar_stack; - bool m_initializing; + bool m_initializing = false; // information about solution - unsigned m_best_unsat; - double m_best_unsat_rate; - double m_last_best_unsat_rate; + unsigned m_best_unsat = 0; + double m_best_unsat_rate = 0; + double m_last_best_unsat_rate = 0; // for non-known instance, set as maximal int m_best_known_value = INT_MAX; // best known value for this instance @@ -159,7 +159,7 @@ namespace sat { reslimit m_limit; random_gen m_rand; - parallel* m_par; + parallel* m_par = nullptr; model m_model; inline int score(bool_var v) const { return m_vars[v].m_score; } @@ -248,7 +248,7 @@ namespace sat { void set_seed(unsigned n) override { config().set_random_seed(n); } - void reinit(solver& s) override; + void reinit(solver& s, bool_vector const& phase) override; // used by unit-walk void set_phase(bool_var v, bool f); diff --git a/src/sat/sat_parallel.cpp b/src/sat/sat_parallel.cpp index 2f7a19558..cdb13706f 100644 --- a/src/sat/sat_parallel.cpp +++ b/src/sat/sat_parallel.cpp @@ -214,14 +214,17 @@ namespace sat { } - bool parallel::_to_solver(solver& s) { - if (m_priorities.empty()) { - return false; - } + void parallel::_to_solver(solver& s) { + return; +#if 0 + if (m_priorities.empty()) + return; + for (bool_var v = 0; v < m_priorities.size(); ++v) { s.update_activity(v, m_priorities[v]); } - return true; + s.m_activity_inc = 128; +#endif } void parallel::from_solver(solver& s) { @@ -229,16 +232,19 @@ namespace sat { _from_solver(s); } - bool parallel::to_solver(solver& s) { + void parallel::to_solver(solver& s) { lock_guard lock(m_mux); - return _to_solver(s); + _to_solver(s); } void parallel::_to_solver(i_local_search& s) { + return; +#if 0 m_priorities.reset(); for (bool_var v = 0; m_solver_copy && v < m_solver_copy->num_vars(); ++v) { m_priorities.push_back(s.get_priority(v)); } +#endif } bool parallel::_from_solver(i_local_search& s) { @@ -246,7 +252,7 @@ namespace sat { m_consumer_ready = true; if (m_solver_copy) { copied = true; - s.reinit(*m_solver_copy.get()); + s.reinit(*m_solver_copy.get(), m_solver_copy->m_best_phase); } return copied; } diff --git a/src/sat/sat_parallel.h b/src/sat/sat_parallel.h index 68266760b..65ae09183 100644 --- a/src/sat/sat_parallel.h +++ b/src/sat/sat_parallel.h @@ -51,7 +51,7 @@ namespace sat { bool enable_add(clause const& c) const; void _get_clauses(solver& s); void _from_solver(solver& s); - bool _to_solver(solver& s); + void _to_solver(solver& s); bool _from_solver(i_local_search& s); void _to_solver(i_local_search& s); @@ -102,7 +102,7 @@ namespace sat { // exchange from solver state to local search and back. void from_solver(solver& s); - bool to_solver(solver& s); + void to_solver(solver& s); bool from_solver(i_local_search& s); void to_solver(i_local_search& s); diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index 89ae19a72..ba4dc3282 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -2,7 +2,7 @@ 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'), + ('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)'), @@ -48,8 +48,7 @@ def_module_params('sat', ('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 SMT proof while it is created'), - ('smt.proof.check_rup', BOOL, True, 'apply forward RUP proof checking'), + ('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'), diff --git a/src/sat/sat_prob.h b/src/sat/sat_prob.h index 305e76b8b..691758154 100644 --- a/src/sat/sat_prob.h +++ b/src/sat/sat_prob.h @@ -58,7 +58,7 @@ namespace sat { clause_vector m_clause_db; svector m_clauses; bool_vector m_values, m_best_values; - unsigned m_best_min_unsat{ 0 }; + unsigned m_best_min_unsat = 0; vector m_use_list; unsigned_vector m_flat_use_list; unsigned_vector m_use_list_index; @@ -67,9 +67,9 @@ namespace sat { indexed_uint_set m_unsat; random_gen m_rand; unsigned_vector m_breaks; - uint64_t m_flips{ 0 }; - uint64_t m_next_restart{ 0 }; - unsigned m_restart_count{ 0 }; + uint64_t m_flips = 0; + uint64_t m_next_restart = 0; + unsigned m_restart_count = 0; stopwatch m_stopwatch; model m_model; @@ -139,6 +139,8 @@ namespace sat { void add(solver const& s) override; model const& get_model() const override { return m_model; } + + double get_priority(bool_var v) const override { return 0; } std::ostream& display(std::ostream& out) const; @@ -148,7 +150,7 @@ namespace sat { void collect_statistics(statistics& st) const override {} - void reinit(solver& s) override { UNREACHABLE(); } + void reinit(solver& s, bool_vector const& phase) override { UNREACHABLE(); } }; } diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index f9edbe928..159453350 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1283,6 +1283,11 @@ namespace sat { return l_undef; } + if (m_config.m_phase == PS_LOCAL_SEARCH && m_ext) { + bounded_local_search(); + // exit(0); + } + log_stats(); if (m_config.m_max_conflicts > 0 && m_config.m_burst_search > 0) { m_restart_threshold = m_config.m_burst_search; @@ -1341,6 +1346,18 @@ namespace sat { }; void solver::bounded_local_search() { + if (m_ext) { + IF_VERBOSE(0, verbose_stream() << "WARNING: local search with theories is in testing mode\n"); + do_restart(true); + lbool r = m_ext->local_search(m_best_phase); + verbose_stream() << r << "\n"; + if (r == l_true) { + m_conflicts_since_restart = 0; + m_conflicts_since_gc = 0; + m_next_simplify = std::max(m_next_simplify, m_conflicts_since_init + 1); + } + return; + } literal_vector _lits; scoped_limits scoped_rl(rlimit()); m_local_search = alloc(ddfw); @@ -1350,11 +1367,44 @@ namespace sat { m_local_search->updt_params(m_params); m_local_search->set_seed(m_rand()); scoped_rl.push_child(&(m_local_search->rlimit())); - m_local_search->rlimit().push(500000); - m_local_search->reinit(*this); - m_local_search->check(_lits.size(), _lits.data(), nullptr); - for (unsigned i = 0; i < m_phase.size(); ++i) - m_best_phase[i] = m_local_search->get_value(i); + + m_local_search_lim.inc(num_clauses()); + m_local_search->rlimit().push(m_local_search_lim.limit); + + m_local_search->reinit(*this, m_best_phase); + lbool r = m_local_search->check(_lits.size(), _lits.data(), nullptr); + auto const& mdl = m_local_search->get_model(); + if (mdl.size() == m_best_phase.size()) { + for (unsigned i = 0; i < m_best_phase.size(); ++i) + m_best_phase[i] = l_true == mdl[i]; + + if (r == l_true) { + m_conflicts_since_restart = 0; + m_conflicts_since_gc = 0; + m_next_simplify = std::max(m_next_simplify, m_conflicts_since_init + 1); + } + do_restart(true); +#if 0 + // move higher priority variables to front + // eg., move the first 10% variables to front + svector> priorities(mdl.size()); + for (unsigned i = 0; i < mdl.size(); ++i) + priorities[i] = { m_local_search->get_priority(i), i }; + std::sort(priorities.begin(), priorities.end(), [](auto& x, auto& y) { return x.first > y.first; }); + for (unsigned i = priorities.size() / 10; i-- > 0; ) + move_to_front(priorities[i].second); +#endif + + + if (l_true == r) { + for (clause const* cp : m_clauses) { + bool is_true = any_of(*cp, [&](auto lit) { return lit.sign() != m_best_phase[lit.var()]; }); + if (!is_true) { + verbose_stream() << "clause is false " << *cp << "\n"; + } + } + } + } } @@ -1670,6 +1720,8 @@ namespace sat { push(); m_stats.m_decision++; + CTRACE("sat", m_best_phase[next] != guess(next), tout << "phase " << phase << " " << m_best_phase[next] << " " << guess(next) << "\n"); + if (phase == l_undef) phase = guess(next) ? l_true: l_false; @@ -1680,15 +1732,15 @@ namespace sat { m_case_split_queue.unassign_var_eh(next); next_lit = literal(next, false); } - + if (phase == l_undef) is_pos = guess(next); else is_pos = phase == l_true; - + if (!is_pos) next_lit.neg(); - + TRACE("sat_decide", tout << scope_lvl() << ": next-case-split: " << next_lit << "\n";); assign_scoped(next_lit); return true; @@ -1905,10 +1957,13 @@ namespace sat { m_search_sat_conflicts = m_config.m_search_sat_conflicts; m_search_next_toggle = m_search_unsat_conflicts; m_best_phase_size = 0; + + m_reorder.lo = m_config.m_reorder_base; + m_rephase.base = m_config.m_rephase_base; m_rephase_lim = 0; m_rephase_inc = 0; - m_reorder_lim = m_config.m_reorder_base; - m_reorder_inc = 0; + m_local_search_lim.base = 500; + m_conflicts_since_restart = 0; m_force_conflict_analysis = false; m_restart_threshold = m_config.m_restart_initial; @@ -1923,6 +1978,7 @@ namespace sat { m_next_simplify = m_config.m_simplify_delay; m_min_d_tk = 1.0; m_search_lvl = 0; + if (m_learned.size() <= 2*m_clauses.size()) m_conflicts_since_gc = 0; m_restart_next_out = 0; @@ -2025,9 +2081,7 @@ namespace sat { if (m_par) { m_par->from_solver(*this); - if (m_par->to_solver(*this)) { - m_activity_inc = 128; - } + m_par->to_solver(*this); } if (m_config.m_binspr && !inconsistent()) { @@ -2909,6 +2963,7 @@ namespace sat { bool solver::should_rephase() { return m_conflicts_since_init > m_rephase_lim; +// return m_rephase.should_apply(m_conflicts_since_init); } void solver::do_rephase() { @@ -2922,7 +2977,7 @@ namespace sat { case PS_FROZEN: break; case PS_BASIC_CACHING: - switch (m_rephase_lim % 4) { + switch (m_rephase.count % 4) { case 0: for (auto& p : m_phase) p = (m_rand() % 2) == 0; break; @@ -2939,14 +2994,19 @@ namespace sat { case PS_SAT_CACHING: if (m_search_state == s_sat) for (unsigned i = 0; i < m_phase.size(); ++i) - m_phase[i] = m_best_phase[i]; + m_phase[i] = m_best_phase[i]; break; case PS_RANDOM: for (auto& p : m_phase) p = (m_rand() % 2) == 0; break; case PS_LOCAL_SEARCH: - if (m_search_state == s_sat) - bounded_local_search(); + if (m_search_state == s_sat) { + if (m_rand() % 2 == 0) + bounded_local_search(); + for (unsigned i = 0; i < m_phase.size(); ++i) + m_phase[i] = m_best_phase[i]; + } + break; default: UNREACHABLE(); @@ -2954,10 +3014,11 @@ namespace sat { } m_rephase_inc += m_config.m_rephase_base; m_rephase_lim += m_rephase_inc; + m_rephase.inc(m_conflicts_since_init, num_clauses()); } bool solver::should_reorder() { - return m_conflicts_since_init > m_reorder_lim; + return m_reorder.should_apply(m_conflicts_since_init); } void solver::do_reorder() { @@ -3001,8 +3062,7 @@ namespace sat { update_activity(v, m_rand(10)/10.0); } #endif - m_reorder_inc += m_config.m_reorder_base; - m_reorder_lim += m_reorder_inc; + m_reorder.inc(m_conflicts_since_init, num_clauses()); } void solver::updt_phase_counters() { diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 524f6b06d..3a437855e 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -159,10 +159,11 @@ namespace sat { unsigned m_search_next_toggle; unsigned m_phase_counter; unsigned m_best_phase_size; + backoff m_local_search_lim; unsigned m_rephase_lim; unsigned m_rephase_inc; - unsigned m_reorder_lim; - unsigned m_reorder_inc; + backoff m_rephase; + backoff m_reorder; var_queue m_case_split_queue; unsigned m_qhead; unsigned m_scope_lvl; @@ -237,6 +238,7 @@ namespace sat { friend class lut_finder; friend class npn3_finder; friend class proof_trim; + friend struct backoff; public: solver(params_ref const & p, reslimit& l); ~solver() override; @@ -332,6 +334,7 @@ namespace sat { s.m_checkpoint_enabled = true; } }; + unsigned select_watch_lit(clause const & cls, unsigned starting_at) const; unsigned select_learned_watch_lit(clause const & cls) const; bool simplify_clause(unsigned & num_lits, literal * lits) const; diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 75351f053..8bf665ebf 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -701,6 +701,10 @@ public: ensure_euf()->user_propagate_register_created(r); } + void user_propagate_register_decide(user_propagator::decide_eh_t& r) override { + ensure_euf()->user_propagate_register_decide(r); + } + private: diff --git a/src/sat/sat_solver/sat_smt_setup.h b/src/sat/sat_solver/sat_smt_setup.h new file mode 100644 index 000000000..c78eb72c8 --- /dev/null +++ b/src/sat/sat_solver/sat_smt_setup.h @@ -0,0 +1,90 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation + +Module Name: + + sat_smt_setup.h + +Author: + + Nikolaj Bjorner (nbjorner) 2023-01-17 + +--*/ +#pragma once + +#include "ast/ast.h" +#include "smt/params/smt_params.h" +#include "sat/sat_config.h" +#include "ast/simplifiers/dependent_expr_state.h" + +struct static_features; + +namespace sat_smt { + + void setup_sat_config(smt_params const& p, sat::config& config); + + class setup { + ast_manager& m; + dependent_expr_state& m_st; + smt_params& m_params; + symbol m_logic; + bool m_already_configured = false; + + void setup_auto_config(); + void setup_default(); + // + // setup_() methods do not depend on static features of the formula. So, they are safe to use + // even in an incremental setting. + // + // setup_(static_features & st) can only be used if the logical context will perform a single + // check. + // + void setup_QF_DT(); + void setup_QF_UF(); + void setup_QF_UF(static_features const & st); + void setup_QF_RDL(); + void setup_QF_RDL(static_features & st); + void setup_QF_IDL(); + void setup_QF_IDL(static_features & st); + void setup_QF_UFIDL(); + void setup_QF_UFIDL(static_features & st); + void setup_QF_LRA(); + void setup_QF_LRA(static_features const & st); + void setup_QF_LIA(); + void setup_QF_LIRA(static_features const& st); + void setup_QF_LIA(static_features const & st); + void setup_QF_UFLIA(); + void setup_QF_UFLIA(static_features & st); + void setup_QF_UFLRA(); + void setup_QF_BV(); + void setup_QF_AUFBV(); + void setup_QF_AX(); + void setup_QF_AX(static_features const & st); + void setup_QF_AUFLIA(); + void setup_QF_AUFLIA(static_features const & st); + void setup_QF_FP(); + void setup_QF_FPBV(); + void setup_QF_S(); + void setup_LRA(); + void setup_CSP(); + void setup_AUFLIA(bool simple_array = true); + void setup_AUFLIA(static_features const & st); + void setup_AUFLIRA(bool simple_array = true); + void setup_UFNIA(); + void setup_UFLRA(); + void setup_AUFLIAp(); + void setup_AUFNIRA(); + void setup_QF_BVRE(); + void setup_unknown(); + void setup_unknown(static_features & st); + + public: + setup(ast_manager& m, dependent_expr_state& st, smt_params & params); + void setk_already_configured() { m_already_configured = true; } + bool already_configured() const { return m_already_configured; } + symbol const & get_logic() const { return m_logic; } + void operator()(); + }; +}; + + diff --git a/src/sat/sat_solver/sat_smt_solver.cpp b/src/sat/sat_solver/sat_smt_solver.cpp index 82aeded6f..a5dd9b415 100644 --- a/src/sat/sat_solver/sat_smt_solver.cpp +++ b/src/sat/sat_solver/sat_smt_solver.cpp @@ -568,6 +568,10 @@ public: ensure_euf()->user_propagate_register_created(r); } + void user_propagate_register_decide(user_propagator::decide_eh_t& r) override { + ensure_euf()->user_propagate_register_decide(r); + } + private: void add_assumption(expr* a) { diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index c92a8bbeb..427b6fb70 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -85,12 +85,12 @@ namespace sat { virtual void updt_params(params_ref const& p) = 0; virtual void set_seed(unsigned s) = 0; virtual lbool check(unsigned sz, literal const* assumptions, parallel* par) = 0; - virtual void reinit(solver& s) = 0; + virtual void reinit(solver& s, bool_vector const& phase) = 0; virtual unsigned num_non_binary_clauses() const = 0; virtual reslimit& rlimit() = 0; virtual model const& get_model() const = 0; virtual void collect_statistics(statistics& st) const = 0; - virtual double get_priority(bool_var v) const { return 0; } + virtual double get_priority(bool_var v) const = 0; virtual bool get_value(bool_var v) const { return true; } }; @@ -136,6 +136,40 @@ namespace sat { std::ostream& operator<<(std::ostream& out, sat::status const& st); std::ostream& operator<<(std::ostream& out, sat::status_pp const& p); + /** + * Special cases of kissat style general backoff calculation. + * The version here calculates + * limit := value*log(C)^2*n*log(n) + * (effort calculation in kissat is based on ticks not clauses) + * + * respectively + * limit := conflicts + value*log(C)^2*n*log(n) + */ + struct backoff { + unsigned base = 1; + unsigned lo = 0; + unsigned hi = UINT_MAX; + unsigned limit = 0; + unsigned count = 0; + + bool should_apply(unsigned n) const { + return limit <= n && lo <= n && n <= hi; + } + + void inc(unsigned num_clauses) { + count++; + unsigned d = base * count * log2(count + 1); + unsigned cl = log2(num_clauses + 2); + limit = cl * cl * d; + } + + void inc(unsigned num_conflicts, unsigned num_clauses) { + inc(num_clauses); + limit += num_conflicts; + } + + }; + }; diff --git a/src/sat/smt/CMakeLists.txt b/src/sat/smt/CMakeLists.txt index 00c41d4be..448e75d26 100644 --- a/src/sat/smt/CMakeLists.txt +++ b/src/sat/smt/CMakeLists.txt @@ -3,6 +3,7 @@ z3_add_component(sat_smt arith_axioms.cpp arith_diagnostics.cpp arith_internalize.cpp + arith_sls.cpp arith_solver.cpp array_axioms.cpp array_diagnostics.cpp @@ -21,6 +22,7 @@ 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 diff --git a/src/sat/smt/arith_diagnostics.cpp b/src/sat/smt/arith_diagnostics.cpp index 8ead3d980..a3e48256d 100644 --- a/src/sat/smt/arith_diagnostics.cpp +++ b/src/sat/smt/arith_diagnostics.cpp @@ -23,6 +23,17 @@ Author: namespace arith { + + void arith_proof_hint_builder::set_type(euf::solver& ctx, hint_type ty) { + ctx.push(value_trail(m_eq_tail)); + ctx.push(value_trail(m_lit_tail)); + m_ty = ty; + reset(); + } + + arith_proof_hint* arith_proof_hint_builder::mk(euf::solver& s) { + return new (s.get_region()) arith_proof_hint(m_ty, m_num_le, m_lit_head, m_lit_tail, m_eq_head, m_eq_tail); + } std::ostream& solver::display(std::ostream& out) const { lp().display(out); diff --git a/src/sat/smt/arith_internalize.cpp b/src/sat/smt/arith_internalize.cpp index d35f79954..60ca9651a 100644 --- a/src/sat/smt/arith_internalize.cpp +++ b/src/sat/smt/arith_internalize.cpp @@ -107,10 +107,12 @@ namespace arith { e = a.mk_idiv0(x, y); } else if (a.is_rem(n, x, y)) { - e = a.mk_rem0(x, y); + n = a.mk_rem(x, a.mk_int(0)); + e = a.mk_rem0(x, a.mk_int(0)); } else if (a.is_mod(n, x, y)) { - e = a.mk_mod0(x, y); + n = a.mk_mod(x, a.mk_int(0)); + e = a.mk_mod0(x, a.mk_int(0)); } else if (a.is_power(n, x, y)) { e = a.mk_power0(x, y); diff --git a/src/sat/smt/arith_sls.cpp b/src/sat/smt/arith_sls.cpp new file mode 100644 index 000000000..4fe153289 --- /dev/null +++ b/src/sat/smt/arith_sls.cpp @@ -0,0 +1,650 @@ +/*++ +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 = s.lp().column2tv(arg.column()); + auto w = s.lp().local_to_external(t2.id()); + val += to_numeral(arg.coeff()) * m_vars[w].m_best_value; + } + if (v == 52) { + verbose_stream() << "update v" << v << " := " << val << "\n"; + for (lp::lar_term::ival const& arg : term) { + auto t2 = s.lp().column2tv(arg.column()); + auto w = s.lp().local_to_external(t2.id()); + verbose_stream() << "v" << w << " := " << m_vars[w].m_best_value << " * " << to_numeral(arg.coeff()) << "\n"; + } + } + 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::column_index vj = s.lp().to_column_index(v); + SASSERT(!vj.is_null()); + if (!s.lp().is_base(vj.index())) { + rational new_value_(new_value, rational::i64()); + lp::impq val(new_value_, rational::zero()); + s.lp().set_value_for_nbasic_column(vj.index(), 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::tv t, theory_var v, int64_t sign) { + if (t.is_term()) { + 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 = s.lp().column2tv(arg.column()); + auto w = s.lp().local_to_external(t2.id()); + add_arg(bv, ineq, sign * to_numeral(arg.coeff()), w); + } + } + else + add_arg(bv, ineq, sign, s.lp().local_to_external(t.id())); + } + + 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->tv(); + 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::tv tu = s.get_tv(u); + lp::tv tv = s.get_tv(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; + } + +} diff --git a/src/sat/smt/arith_sls.h b/src/sat/smt/arith_sls.h new file mode 100644 index 000000000..09a56c84e --- /dev/null +++ b/src/sat/smt/arith_sls.h @@ -0,0 +1,170 @@ +/*++ +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> 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> m_bool_vars; + }; + + solver& s; + ast_manager& m; + sat::ddfw* m_bool_search = nullptr; + stats m_stats; + config m_config; + scoped_ptr_vector m_bool_vars; + vector m_vars; + svector> 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::tv t, 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); + ~sls() override {} + 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); + } +} diff --git a/src/sat/smt/arith_solver.cpp b/src/sat/smt/arith_solver.cpp index 35e0795b7..bd5dd315f 100644 --- a/src/sat/smt/arith_solver.cpp +++ b/src/sat/smt/arith_solver.cpp @@ -24,6 +24,7 @@ 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), a(m), @@ -100,8 +101,7 @@ namespace arith { return false; switch (lbl) { - case l_false: - TRACE("arith", tout << "propagation conflict\n";); + case l_false: get_infeasibility_explanation_and_set_conflict(); break; case l_true: @@ -381,9 +381,9 @@ namespace arith { void solver::assert_bound(bool is_true, api_bound& b) { - TRACE("arith", tout << b << "\n";); lp::constraint_index ci = b.get_constraint(is_true); lp().activate(ci); + TRACE("arith", tout << b << " " << is_infeasible() << "\n";); if (is_infeasible()) return; lp::lconstraint_kind k = bound2constraint_kind(b.is_int(), b.get_bound_kind(), is_true); @@ -1065,6 +1065,7 @@ namespace arith { TRACE("pcs", tout << lp().constraints();); auto status = lp().find_feasible_solution(); TRACE("arith_verbose", display(tout);); + TRACE("arith", tout << status << "\n"); switch (status) { case lp::lp_status::INFEASIBLE: return l_false; @@ -1201,7 +1202,7 @@ namespace arith { TRACE("arith", tout << "Lemma - " << (is_conflict ? "conflict" : "propagation") << "\n"; - for (literal c : m_core) tout << literal2expr(c) << "\n"; + for (literal c : m_core) tout << c << ": " << literal2expr(c) << "\n"; for (auto p : m_eqs) tout << ctx.bpp(p.first) << " == " << ctx.bpp(p.second) << "\n";); if (is_conflict) { diff --git a/src/sat/smt/arith_solver.h b/src/sat/smt/arith_solver.h index a13ef6684..68d5f8025 100644 --- a/src/sat/smt/arith_solver.h +++ b/src/sat/smt/arith_solver.h @@ -19,9 +19,7 @@ Author: #include "util/obj_pair_set.h" #include "ast/ast_trail.h" #include "ast/arith_decl_plugin.h" -#include "math/lp/lp_solver.h" -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" + #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" @@ -30,6 +28,8 @@ 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; @@ -78,12 +78,7 @@ namespace arith { m_eq_tail++; } public: - void set_type(euf::solver& ctx, hint_type ty) { - ctx.push(value_trail(m_eq_tail)); - ctx.push(value_trail(m_lit_tail)); - m_ty = ty; - reset(); - } + void set_type(euf::solver& ctx, hint_type ty); void set_num_le(unsigned n) { m_num_le = n; } void add_eq(euf::enode* a, euf::enode* b) { add(a, b, true); } void add_diseq(euf::enode* a, euf::enode* b) { add(a, b, false); } @@ -96,15 +91,13 @@ namespace arith { } std::pair const& lit(unsigned i) const { return m_literals[i]; } std::tuple const& eq(unsigned i) const { return m_eqs[i]; } - arith_proof_hint* mk(euf::solver& s) { - return new (s.get_region()) arith_proof_hint(m_ty, m_num_le, m_lit_head, m_lit_tail, m_eq_head, m_eq_tail); - } + arith_proof_hint* mk(euf::solver& s); }; - class solver : public euf::th_euf_solver { friend struct arith_proof_hint; + friend class sls; struct scope { unsigned m_bounds_lim; @@ -144,7 +137,7 @@ namespace arith { }; int_hashtable m_model_eqs; - bool m_new_eq { false }; + bool m_new_eq = false; // temporary values kept during internalization @@ -197,6 +190,8 @@ namespace arith { coeffs().pop_back(); } }; + + sls m_local_search; typedef vector> var_coeffs; vector m_columns; @@ -233,10 +228,10 @@ namespace arith { unsigned m_asserted_qhead = 0; svector > m_assume_eq_candidates; - unsigned m_assume_eq_head{ 0 }; + unsigned m_assume_eq_head = 0; lp::u_set m_tmp_var_set; - unsigned m_num_conflicts{ 0 }; + unsigned m_num_conflicts = 0; lp_api::stats m_stats; svector m_scopes; @@ -515,6 +510,8 @@ 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; } @@ -523,4 +520,7 @@ namespace arith { void consume(rational const& v, lp::constraint_index j); bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const; }; + + + } diff --git a/src/sat/smt/euf_local_search.cpp b/src/sat/smt/euf_local_search.cpp new file mode 100644 index 000000000..ca450e513 --- /dev/null +++ b/src/sat/smt/euf_local_search.cpp @@ -0,0 +1,50 @@ +/*++ +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; + } +} diff --git a/src/sat/smt/euf_model.cpp b/src/sat/smt/euf_model.cpp index 0fd021d70..b117ac1e3 100644 --- a/src/sat/smt/euf_model.cpp +++ b/src/sat/smt/euf_model.cpp @@ -67,7 +67,7 @@ namespace euf { m_qmodel = mdl; } - void solver::update_model(model_ref& mdl) { + void solver::update_model(model_ref& mdl, bool validate) { TRACE("model", tout << "create model\n";); if (m_qmodel) { mdl = m_qmodel; @@ -87,7 +87,8 @@ namespace euf { for (auto* mb : m_solvers) mb->finalize_model(*mdl); TRACE("model", tout << "created model " << *mdl << "\n";); - validate_model(*mdl); + if (validate) + validate_model(*mdl); } bool solver::include_func_interp(func_decl* f) { diff --git a/src/sat/smt/euf_proof_checker.cpp b/src/sat/smt/euf_proof_checker.cpp index 2d4f67cd2..a538b2a80 100644 --- a/src/sat/smt/euf_proof_checker.cpp +++ b/src/sat/smt/euf_proof_checker.cpp @@ -28,7 +28,7 @@ Author: #include "sat/smt/bv_theory_checker.h" #include "sat/smt/distinct_theory_checker.h" #include "sat/smt/tseitin_theory_checker.h" - +#include "params/solver_params.hpp" namespace euf { @@ -388,8 +388,8 @@ namespace euf { m_sat_solver.updt_params(m_params); m_drat.updt_config(); m_rup = symbol("rup"); - sat_params sp(m_params); - m_check_rup = sp.smt_proof_check_rup(); + solver_params sp(m_params); + m_check_rup = sp.proof_check_rup(); } void smt_proof_checker::ensure_solver() { diff --git a/src/sat/smt/euf_proof_checker.h b/src/sat/smt/euf_proof_checker.h index 9a84015e4..d84e4d19f 100644 --- a/src/sat/smt/euf_proof_checker.h +++ b/src/sat/smt/euf_proof_checker.h @@ -35,7 +35,7 @@ namespace euf { virtual bool check(app* jst) = 0; virtual expr_ref_vector clause(app* jst) = 0; virtual void register_plugins(theory_checker& pc) = 0; - virtual bool vc(app* jst, expr_ref_vector const& clause, expr_ref_vector& v) { v.reset(); v.append(this->clause(jst)); return false; } + virtual bool vc(app* jst, expr_ref_vector const& clause, expr_ref_vector& v) { v.append(this->clause(jst)); return false; } }; class theory_checker { diff --git a/src/sat/smt/euf_solver.cpp b/src/sat/smt/euf_solver.cpp index 8efb433ff..0ae56beb3 100644 --- a/src/sat/smt/euf_solver.cpp +++ b/src/sat/smt/euf_solver.cpp @@ -349,6 +349,20 @@ namespace euf { si.uncache(literal(v, true)); } + bool solver::decide(bool_var& var, lbool& phase) { + for (auto const& th : m_solvers) + if (th->decide(var, phase)) + return true; + return false; + } + + bool solver::get_case_split(bool_var& var, lbool& phase) { + for (auto const& th : m_solvers) + if (th->get_case_split(var, phase)) + return true; + return false; + } + void solver::asserted(literal l) { m_relevancy.asserted(l); if (!m_relevancy.is_relevant(l)) @@ -478,8 +492,13 @@ namespace euf { m_ackerman->cg_conflict_eh(a, b); switch (s().value(lit)) { case l_true: - if (n->merge_tf() && !m.is_value(n->get_root()->get_expr())) - m_egraph.merge(n, ante, to_ptr(lit)); + if (!n->merge_tf()) + break; + if (m.is_value(n->get_root()->get_expr())) + break; + if (!ante) + ante = mk_true(); + m_egraph.merge(n, ante, to_ptr(lit)); break; case l_undef: case l_false: diff --git a/src/sat/smt/euf_solver.h b/src/sat/smt/euf_solver.h index 5aa05d79c..6bfa75046 100644 --- a/src/sat/smt/euf_solver.h +++ b/src/sat/smt/euf_solver.h @@ -100,6 +100,14 @@ 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)(l.index() << 4)), 1); } size_t* to_ptr(size_t jst) { return TAG(size_t*, reinterpret_cast(jst), 2); } @@ -119,6 +127,7 @@ 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; @@ -251,7 +260,7 @@ namespace euf { constraint& mk_constraint(constraint*& c, constraint::kind_t k); constraint& conflict_constraint() { return mk_constraint(m_conflict, constraint::kind_t::conflict); } constraint& eq_constraint() { return mk_constraint(m_eq, constraint::kind_t::eq); } - constraint& lit_constraint(enode* n); + constraint& lit_constraint(enode* n); // user propagator void check_for_user_propagator() { @@ -339,6 +348,7 @@ 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); @@ -359,6 +369,8 @@ namespace euf { void add_explain(size_t* p) { m_explain.push_back(p); } void reset_explain() { m_explain.reset(); } void set_eliminated(bool_var v) override; + bool decide(bool_var& var, lbool& phase) override; + bool get_case_split(bool_var& var, lbool& phase) override; void asserted(literal l) override; sat::check_result check() override; void push() override; @@ -486,7 +498,7 @@ namespace euf { // model construction void save_model(model_ref& mdl); - void update_model(model_ref& mdl); + void update_model(model_ref& mdl, bool validate); obj_map const& values2root(); void model_updated(model_ref& mdl); expr* node2value(enode* n) const; @@ -531,6 +543,10 @@ namespace euf { check_for_user_propagator(); m_user_propagator->register_created(ceh); } + void user_propagate_register_decide(user_propagator::decide_eh_t& ceh) { + check_for_user_propagator(); + m_user_propagator->register_decide(ceh); + } void user_propagate_register_expr(expr* e) { check_for_user_propagator(); m_user_propagator->add_expr(e); @@ -552,4 +568,3 @@ namespace euf { inline std::ostream& operator<<(std::ostream& out, euf::solver const& s) { return s.display(out); } - diff --git a/src/sat/smt/pb_internalize.cpp b/src/sat/smt/pb_internalize.cpp index 1a83dbc87..abbd79c44 100644 --- a/src/sat/smt/pb_internalize.cpp +++ b/src/sat/smt/pb_internalize.cpp @@ -41,6 +41,11 @@ namespace pb { SASSERT(m_pb.is_pb(e)); app* t = to_app(e); rational k = m_pb.get_k(t); + if (!root && is_app(e)) { + sat::literal lit = si.get_cached(to_app(e)); + if (lit != sat::null_literal) + return sign ? ~lit : lit; + } switch (t->get_decl_kind()) { case OP_AT_MOST_K: return convert_at_most_k(t, k, root, sign); @@ -119,7 +124,7 @@ namespace pb { else { bool_var v = s().add_var(true); literal lit(v, sign); - add_pb_ge(v, sign, wlits, k.get_unsigned()); + add_pb_ge(v, false, wlits, k.get_unsigned()); TRACE("ba", tout << "root: " << root << " lit: " << lit << "\n";); return lit; } @@ -146,7 +151,7 @@ namespace pb { else { sat::bool_var v = s().add_var(true); sat::literal lit(v, sign); - add_pb_ge(v, sign, wlits, k.get_unsigned()); + add_pb_ge(v, false, wlits, k.get_unsigned()); TRACE("goal2sat", tout << "root: " << root << " lit: " << lit << "\n";); return lit; } diff --git a/src/sat/smt/pb_solver.cpp b/src/sat/smt/pb_solver.cpp index fed6aaf7c..496042f49 100644 --- a/src/sat/smt/pb_solver.cpp +++ b/src/sat/smt/pb_solver.cpp @@ -2039,7 +2039,7 @@ namespace pb { for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) simplify(*m_constraints[i]); for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) simplify(*m_learned[i]); init_use_lists(); - remove_unused_defs(); + // remove_unused_defs(); set_non_external(); elim_pure(); for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) subsumption(*m_constraints[i]); @@ -2528,8 +2528,13 @@ namespace pb { } void solver::remove_unused_defs() { - if (incremental_mode()) return; + if (incremental_mode()) + return; // remove constraints where indicator literal isn't used. + NOT_IMPLEMENTED_YET(); + // TODO: #6675 + // need to add this inequality to the model reconstruction + // stack in order to produce correct models. for (constraint* cp : m_constraints) { constraint& c = *cp; literal lit = c.lit(); @@ -2853,19 +2858,20 @@ namespace pb { void solver::subsumes(pbc& p1, literal lit) { for (constraint* c : m_cnstr_use_list[lit.index()]) { - if (c == &p1 || c->was_removed()) continue; - bool s = false; + if (c == &p1 || c->was_removed() || c->lit() != sat::null_literal) + continue; + bool sub = false; switch (c->tag()) { case pb::tag_t::card_t: - s = subsumes(p1, c->to_card()); + sub = subsumes(p1, c->to_card()); break; case pb::tag_t::pb_t: - s = subsumes(p1, c->to_pb()); + sub = subsumes(p1, c->to_pb()); break; default: break; } - if (s) { + if (sub) { ++m_stats.m_num_pb_subsumes; set_non_learned(p1); remove_constraint(*c, "subsumed"); diff --git a/src/sat/smt/q_mbi.cpp b/src/sat/smt/q_mbi.cpp index 21830c162..c66f1b3a2 100644 --- a/src/sat/smt/q_mbi.cpp +++ b/src/sat/smt/q_mbi.cpp @@ -357,8 +357,7 @@ namespace q { return expr_ref(m); } else if (!(*p)(*m_model, vars, fmls)) { - TRACE("q", tout << "theory projection failed\n"); - return expr_ref(m); + TRACE("q", tout << "theory projection failed - use value\n"); } } for (app* v : vars) { @@ -636,7 +635,7 @@ namespace q { if (m_model) return; m_model = alloc(model, m); - ctx.update_model(m_model); + ctx.update_model(m_model, false); } void mbqi::init_solver() { diff --git a/src/sat/smt/sat_internalizer.h b/src/sat/smt/sat_internalizer.h index 5be20c4e0..7b1d932ec 100644 --- a/src/sat/smt/sat_internalizer.h +++ b/src/sat/smt/sat_internalizer.h @@ -26,6 +26,7 @@ namespace sat { virtual literal internalize(expr* e) = 0; virtual bool_var to_bool_var(expr* e) = 0; virtual bool_var add_bool_var(expr* e) = 0; + virtual literal get_cached(app* t) const = 0; virtual bool is_cached(app* t, literal l) const = 0; virtual void cache(app* t, literal l) = 0; virtual void uncache(literal l) = 0; diff --git a/src/sat/smt/sat_th.h b/src/sat/smt/sat_th.h index a3b81a08d..e226566b8 100644 --- a/src/sat/smt/sat_th.h +++ b/src/sat/smt/sat_th.h @@ -18,6 +18,7 @@ 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" @@ -136,6 +137,17 @@ namespace euf { sat::status status() const { return sat::status::th(false, get_id()); } + /** + * Local search interface + */ + virtual void set_bool_search(sat::ddfw* ddfw) {} + + virtual void set_bounds_begin() {} + + virtual void set_bounds_end(unsigned num_literals) {} + + virtual void set_bounds(enode* n) {} + }; class th_proof_hint : public sat::proof_hint { diff --git a/src/sat/smt/user_solver.cpp b/src/sat/smt/user_solver.cpp index 99e4eeeb1..34f2b10b4 100644 --- a/src/sat/smt/user_solver.cpp +++ b/src/sat/smt/user_solver.cpp @@ -21,7 +21,7 @@ Author: namespace user_solver { solver::solver(euf::solver& ctx) : - th_euf_solver(ctx, symbol("user"), ctx.get_manager().mk_family_id("user")) + th_euf_solver(ctx, symbol(user_propagator::plugin::name()), ctx.get_manager().mk_family_id(user_propagator::plugin::name())) {} solver::~solver() { @@ -92,24 +92,24 @@ namespace user_solver { euf::enode* original_enode = bool_var2enode(var); - if (!is_attached_to_var(original_enode)) + if (!original_enode || !is_attached_to_var(original_enode)) return false; unsigned new_bit = 0; // ignored; currently no bv-support - expr* e = bool_var2expr(var); + expr* e = original_enode->get_expr(); m_decide_eh(m_user_context, this, &e, &new_bit, &phase); euf::enode* new_enode = ctx.get_enode(e); - if (original_enode == new_enode) + if (original_enode == new_enode || new_enode->bool_var() == sat::null_bool_var) return false; var = new_enode->bool_var(); return true; } - bool solver::get_case_split(sat::bool_var& var, lbool &phase){ + bool solver::get_case_split(sat::bool_var& var, lbool& phase){ if (!m_next_split_expr) return false; @@ -123,9 +123,12 @@ namespace user_solver { void solver::asserted(sat::literal lit) { if (!m_fixed_eh) return; - force_push(); auto* n = bool_var2enode(lit.var()); euf::theory_var v = n->get_th_var(get_id()); + if (!m_id2justification.get(v, sat::literal_vector()).empty()) + // the core merged variables. We already issued the respective fixed callback for an equated variable + return; + force_push(); sat::literal_vector lits; lits.push_back(lit); m_id2justification.setx(v, lits, sat::literal_vector()); diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 865c5f15d..267888804 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -276,11 +276,16 @@ struct goal2sat::imp : public sat::sat_internalizer { m_cache_trail.push_back(t); } + sat::literal get_cached(app* t) const override { + sat::literal lit = sat::null_literal; + m_app2lit.find(t, lit); + return lit; + } + bool is_cached(app* t, sat::literal l) const override { - if (!m_app2lit.contains(t)) - return false; - SASSERT(m_app2lit[t] == l); - return true; + sat::literal lit = get_cached(t); + SASSERT(lit == sat::null_literal || l == lit); + return l == lit; } void convert_atom(expr * t, bool root, bool sign) { @@ -987,7 +992,7 @@ struct goal2sat::imp : public sat::sat_internalizer { void update_model(model_ref& mdl) { auto* ext = dynamic_cast(m_solver.get_extension()); if (ext) - ext->update_model(mdl); + ext->update_model(mdl, true); } void user_push() { diff --git a/src/shell/CMakeLists.txt b/src/shell/CMakeLists.txt index d9b74f162..c0e9c8505 100644 --- a/src/shell/CMakeLists.txt +++ b/src/shell/CMakeLists.txt @@ -28,7 +28,6 @@ add_executable(shell opt_frontend.cpp smtlib_frontend.cpp z3_log_frontend.cpp - lp_frontend.cpp # FIXME: shell should really link against libz3 but it can't due to requiring # use of some hidden symbols. Also libz3 has the ``api_dll`` component which # we don't want (I think). diff --git a/src/shell/lp_frontend.cpp b/src/shell/lp_frontend.cpp deleted file mode 100644 index 8d6425533..000000000 --- a/src/shell/lp_frontend.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/*++ -Copyright (c) 2016 Microsoft Corporation - -Author: - - Lev Nachmanson 2016-10-27 - ---*/ - -#include "math/lp/lp_settings.h" -#include "math/lp/mps_reader.h" -#include "util/timeout.h" -#include "util/cancel_eh.h" -#include "util/scoped_timer.h" -#include "util/rlimit.h" -#include "util/gparams.h" -#include "util/mutex.h" -#include -#include -#include "smt/params/smt_params_helper.hpp" - -namespace { -static mutex *display_stats_mux = new mutex; - -static lp::lp_solver* g_solver = nullptr; - -static void display_statistics() { - lock_guard lock(*display_stats_mux); - if (g_solver && g_solver->settings().print_statistics) { - // TBD display relevant information about statistics - } -} - -static void STD_CALL on_ctrl_c(int) { - signal (SIGINT, SIG_DFL); - display_statistics(); - raise(SIGINT); -} - -static void on_timeout() { - display_statistics(); - _Exit(0); -} - -struct front_end_resource_limit : public lp::lp_resource_limit { - reslimit& m_reslim; - - front_end_resource_limit(reslimit& lim): - m_reslim(lim) - {} - - bool get_cancel_flag() override { return !m_reslim.inc(); } -}; - -void run_solver(smt_params_helper & params, char const * mps_file_name) { - - reslimit rlim; - unsigned timeout = gparams::get_ref().get_uint("timeout", 0); - unsigned rlimit = gparams::get_ref().get_uint("rlimit", 0); - front_end_resource_limit lp_limit(rlim); - - scoped_rlimit _rlimit(rlim, rlimit); - cancel_eh eh(rlim); - scoped_timer timer(timeout, &eh); - - std::string fn(mps_file_name); - lp::mps_reader reader(fn); - reader.set_message_stream(&std::cout); // can be redirected - reader.read(); - if (!reader.is_ok()) { - std::cerr << "cannot process " << mps_file_name << std::endl; - return; - } - lp::lp_solver * solver = reader.create_solver(false); // false - to create the primal solver - solver->settings().set_resource_limit(lp_limit); - g_solver = solver; - if (params.arith_min()) { - solver->flip_costs(); - } - solver->settings().set_message_ostream(&std::cout); - solver->settings().report_frequency = params.arith_rep_freq(); - solver->settings().print_statistics = params.arith_print_stats(); - solver->settings().set_simplex_strategy(lp:: simplex_strategy_enum::lu); - - solver->find_maximal_solution(); - - *(solver->settings().get_message_ostream()) << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp::lp_status::OPTIMAL) { - if (params.arith_min()) { - solver->flip_costs(); - } - solver->print_model(std::cout); - } - - display_statistics(); - g_solver = nullptr; - delete solver; -} -} - -unsigned read_mps_file(char const * mps_file_name) { - signal(SIGINT, on_ctrl_c); - register_on_timeout_proc(on_timeout); - smt_params_helper p; - param_descrs r; - p.collect_param_descrs(r); - run_solver(p, mps_file_name); - return 0; -} diff --git a/src/shell/lp_frontend.h b/src/shell/lp_frontend.h deleted file mode 100644 index b24be811f..000000000 --- a/src/shell/lp_frontend.h +++ /dev/null @@ -1,7 +0,0 @@ -/* - Copyright (c) 2013 Microsoft Corporation. All rights reserved. - - Author: Lev Nachmanson -*/ -#pragma once -unsigned read_mps_file(char const * mps_file_name); diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 4c26d91d9..26325d3d0 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -37,14 +37,13 @@ Revision History: #include "util/gparams.h" #include "util/env_params.h" #include "util/file_path.h" -#include "shell/lp_frontend.h" #include "shell/drat_frontend.h" #if defined( _WINDOWS ) && defined( __MINGW32__ ) && ( defined( __GNUG__ ) || defined( __clang__ ) ) #include #endif -typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_MPS, IN_DRAT } input_kind; +typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_DRAT } input_kind; static char const * g_input_file = nullptr; static char const * g_drat_input_file = nullptr; @@ -377,10 +376,6 @@ int STD_CALL main(int argc, char ** argv) { else if (strcmp(ext, "smt2") == 0) { g_input_kind = IN_SMTLIB_2; } - else if (strcmp(ext, "mps") == 0 || strcmp(ext, "sif") == 0 || - strcmp(ext, "MPS") == 0 || strcmp(ext, "SIF") == 0) { - g_input_kind = IN_MPS; - } } } switch (g_input_kind) { @@ -406,9 +401,6 @@ int STD_CALL main(int argc, char ** argv) { case IN_Z3_LOG: replay_z3_log(g_input_file); break; - case IN_MPS: - return_value = read_mps_file(g_input_file); - break; case IN_DRAT: return_value = read_drat(g_drat_input_file); break; diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index 44a435041..3804b7228 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -3958,7 +3958,7 @@ namespace { void relevant_eh(enode * n, bool lazy) override { TRACE("trigger_bug", tout << "relevant_eh:\n" << mk_ismt2_pp(n->get_expr(), m) << "\n"; tout << "mam: " << this << "\n";); - TRACE("mam", tout << "relevant_eh: #" << n->get_owner_id() << "\n";); + TRACE("mam", tout << "relevant_eh: #" << enode_pp(n, m_context) << "\n";); if (n->has_lbl_hash()) update_lbls(n, n->get_lbl_hash()); diff --git a/src/smt/params/preprocessor_params.cpp b/src/smt/params/preprocessor_params.cpp index 180242f85..b6b80a34e 100644 --- a/src/smt/params/preprocessor_params.cpp +++ b/src/smt/params/preprocessor_params.cpp @@ -30,6 +30,7 @@ void preprocessor_params::updt_local_params(params_ref const & _p) { m_elim_unconstrained = p.elim_unconstrained(); m_solve_eqs = p.solve_eqs(); m_ng_lift_ite = static_cast(p.q_lift_ite()); + m_bound_simplifier = p.bound_simplifier(); } void preprocessor_params::updt_params(params_ref const & p) { @@ -63,4 +64,5 @@ void preprocessor_params::display(std::ostream & out) const { DISPLAY_PARAM(m_max_bv_sharing); DISPLAY_PARAM(m_pre_simplifier); DISPLAY_PARAM(m_nlquant_elim); + DISPLAY_PARAM(m_bound_simplifier); } diff --git a/src/smt/params/preprocessor_params.h b/src/smt/params/preprocessor_params.h index 55a55980b..d53cbbe8f 100644 --- a/src/smt/params/preprocessor_params.h +++ b/src/smt/params/preprocessor_params.h @@ -49,6 +49,7 @@ struct preprocessor_params : public pattern_inference_params, bool m_max_bv_sharing = true; bool m_pre_simplifier = true; bool m_nlquant_elim = false; + bool m_bound_simplifier = true; public: preprocessor_params(params_ref const & p = params_ref()): diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index ea288e742..22ef86ff7 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -21,6 +21,7 @@ def_module_params(module_name='smt', ('elim_unconstrained', BOOL, True, 'pre-processing: eliminate unconstrained subterms'), ('solve_eqs', BOOL, True, 'pre-processing: solve equalities'), ('propagate_values', BOOL, True, 'pre-processing: propagate values'), + ('bound_simplifier', BOOL, True, 'apply bounds simplification during pre-processing'), ('pull_nested_quantifiers', BOOL, False, 'pre-processing: pull nested quantifiers'), ('refine_inj_axioms', BOOL, True, 'pre-processing: refine injectivity axioms'), ('candidate_models', BOOL, False, 'create candidate models even when quantifier or theory reasoning is incomplete'), diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp index d1614f521..f79f7851f 100644 --- a/src/smt/proto_model/proto_model.cpp +++ b/src/smt/proto_model/proto_model.cpp @@ -32,7 +32,7 @@ proto_model::proto_model(ast_manager & m, params_ref const & p): model_core(m), m_eval(*this), m_rewrite(m) { - register_factory(alloc(basic_factory, m)); + register_factory(alloc(basic_factory, m, m.get_num_asts())); m_user_sort_factory = alloc(user_sort_factory, m); register_factory(m_user_sort_factory); m_model_partial = model_params(p).partial(); @@ -288,42 +288,33 @@ bool proto_model::is_finite(sort * s) const { } expr * proto_model::get_some_value(sort * s) { - if (m.is_uninterp(s)) { - return m_user_sort_factory->get_some_value(s); - } - else if (value_factory * f = get_factory(s->get_family_id())) { - return f->get_some_value(s); - } - else { + if (m.is_uninterp(s)) + return m_user_sort_factory->get_some_value(s); + else if (value_factory * f = get_factory(s->get_family_id())) + return f->get_some_value(s); + else // there is no factory for the family id, then assume s is uninterpreted. - return m_user_sort_factory->get_some_value(s); - } + return m_user_sort_factory->get_some_value(s); } bool proto_model::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { - if (m.is_uninterp(s)) { - return m_user_sort_factory->get_some_values(s, v1, v2); - } - else if (value_factory * f = get_factory(s->get_family_id())) { - return f->get_some_values(s, v1, v2); - } - else { - return false; - } + if (m.is_uninterp(s)) + return m_user_sort_factory->get_some_values(s, v1, v2); + else if (value_factory * f = get_factory(s->get_family_id())) + return f->get_some_values(s, v1, v2); + else + return false; } expr * proto_model::get_fresh_value(sort * s) { - if (m.is_uninterp(s)) { - return m_user_sort_factory->get_fresh_value(s); - } - else if (value_factory * f = get_factory(s->get_family_id())) { - return f->get_fresh_value(s); - } - else { + if (m.is_uninterp(s)) + return m_user_sort_factory->get_fresh_value(s); + else if (value_factory * f = get_factory(s->get_family_id())) + return f->get_fresh_value(s); + else // Use user_sort_factory if the theory has no support for model construnction. // This is needed when dummy theories are used for arithmetic or arrays. - return m_user_sort_factory->get_fresh_value(s); - } + return m_user_sort_factory->get_fresh_value(s); } void proto_model::register_value(expr * n) { diff --git a/src/smt/smt_case_split_queue.cpp b/src/smt/smt_case_split_queue.cpp index 9f5bdb0d4..711e3de3a 100644 --- a/src/smt/smt_case_split_queue.cpp +++ b/src/smt/smt_case_split_queue.cpp @@ -964,7 +964,7 @@ namespace { } void display(std::ostream & out) override { - if (m_queue.empty() && m_queue2.empty()) + if (m_queue.empty()) return; out << "case-splits:\n"; display_core(out, m_queue, m_head, 1); diff --git a/src/smt/smt_clause_proof.cpp b/src/smt/smt_clause_proof.cpp index 521510b59..3bb2a1fdf 100644 --- a/src/smt/smt_clause_proof.cpp +++ b/src/smt/smt_clause_proof.cpp @@ -233,7 +233,7 @@ namespace smt { TRACE("context", tout << "get-proof " << ctx.get_fparams().m_clause_proof << "\n";); if (!ctx.get_fparams().m_clause_proof) return proof_ref(m); - proof_ref_vector ps(m); + expr_ref_vector ps(m); for (auto& info : m_trail) { expr_ref fact = mk_or(info.m_clause); proof* pr = info.m_proof; diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index d84535a9c..82f56ab76 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -3024,7 +3024,8 @@ namespace smt { SASSERT(is_well_sorted(m, e)); TRACE("begin_assert_expr", tout << mk_pp(e, m) << " " << mk_pp(pr, m) << "\n";); TRACE("begin_assert_expr_ll", tout << mk_ll_pp(e, m) << "\n";); - pop_to_base_lvl(); + if (!m_searching) + pop_to_base_lvl(); if (pr == nullptr) m_asserted_formulas.assert_expr(e); else diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index 40e789204..a6088fdf7 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -704,14 +704,20 @@ namespace smt { for (clause* cp : m_lemmas) if (cp->get_num_literals() == 2) ++bin_lemmas; + auto num_units = [&]() { + if (m_scopes.empty()) + return m_assigned_literals.size(); + else + return m_scopes[0].m_assigned_literals_lim; + }; std::stringstream strm; strm << "(smt.stats " << std::setw(4) << m_stats.m_num_restarts << " " << std::setw(6) << m_stats.m_num_conflicts << " " << std::setw(6) << m_stats.m_num_decisions << " " << std::setw(6) << m_stats.m_num_propagations << " " - << std::setw(5) << (m_aux_clauses.size() + bin_clauses) << "/" << bin_clauses << " " - << std::setw(5) << m_lemmas.size(); if (bin_lemmas > 0) strm << "/" << bin_lemmas << " "; + << std::setw(5) << (m_aux_clauses.size() + bin_clauses) << "/" << bin_clauses << "/" << num_units() + << std::setw(7) << m_lemmas.size(); if (bin_lemmas > 0) strm << "/" << bin_lemmas << " "; strm << std::setw(5) << m_stats.m_num_simplifications << " " << std::setw(4) << m_stats.m_num_del_clauses << " " << std::setw(7) << mem_stat() << ")\n"; @@ -739,8 +745,8 @@ namespace smt { m_last_position_log = m_stats.m_num_restarts; // restarts decisions clauses simplifications memory // conflicts propagations lemmas deletions - int adjust[9] = { -3, -3, -3, -3, -3, -3, -4, -4, -1 }; - char const* tag[9] = { ":restarts ", ":conflicts ", ":decisions ", ":propagations ", ":clauses/bin ", ":lemmas ", ":simplify ", ":deletions", ":memory" }; + int adjust[9] = { -3, -3, -3, -3, -3, -4, -4, -4, -1 }; + char const* tag[9] = { ":restarts ", ":conflicts ", ":decisions ", ":propagations ", ":clauses/bin/units ", ":lemmas ", ":simplify ", ":deletions", ":memory" }; std::stringstream l1, l2; l1 << "(smt.stats "; diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 3a7b95e2c..68879b8ac 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -1536,7 +1536,7 @@ namespace smt { fml = mk_or(fmls); m_lemma_visitor.collect(fml); m_lemma_visitor.display_skolem_decls(std::cout); - m_lemma_visitor.display_assert(std::cout, fml.get(), true); + m_lemma_visitor.display_assert(std::cout, fml.get(), false); } } diff --git a/src/smt/smt_model_checker.cpp b/src/smt/smt_model_checker.cpp index e55508853..2d2350494 100644 --- a/src/smt/smt_model_checker.cpp +++ b/src/smt/smt_model_checker.cpp @@ -28,6 +28,7 @@ Revision History: #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" #include "ast/array_decl_plugin.h" +#include "ast/special_relations_decl_plugin.h" #include "ast/ast_smt2_pp.h" #include "smt/smt_model_checker.h" #include "smt/smt_context.h" @@ -358,7 +359,7 @@ namespace smt { TRACE("model_checker", tout << "[complete] model-checker result: " << to_sat_str(r) << "\n";); if (r != l_true) { - return r == l_false; // quantifier is satisfied by m_curr_model + return is_safe_for_mbqi(q) && r == l_false; // quantifier is satisfied by m_curr_model } model_ref complete_cex; @@ -398,6 +399,26 @@ namespace smt { return false; } + bool model_checker::is_safe_for_mbqi(quantifier * q) const { + special_relations_util sp(m); + if (!sp.has_special_relation()) + return true; + ast_fast_mark1 visited; + struct proc { + special_relations_util& sp; + bool found = false; + proc(special_relations_util& sp):sp(sp) {} + void operator()(app* f) { + found |= sp.is_special_relation(f); + } + void operator()(expr* e) {} + }; + proc p(sp); + quick_for_each_expr(p, visited, q); + return !p.found; + } + + void model_checker::init_aux_context() { if (!m_fparams) { m_fparams = alloc(smt_params, m_context->get_fparams()); diff --git a/src/smt/smt_model_checker.h b/src/smt/smt_model_checker.h index 6332a890e..46d51f9a0 100644 --- a/src/smt/smt_model_checker.h +++ b/src/smt/smt_model_checker.h @@ -87,6 +87,7 @@ namespace smt { expr_mark m_visited; bool contains_model_value(expr * e); void add_instance(quantifier * q, expr_ref_vector const & bindings, unsigned max_generation, expr * def); + bool is_safe_for_mbqi(quantifier * q) const; public: model_checker(ast_manager & m, qi_params const & p, model_finder & mf); diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 854ddb9e0..7699fc3a5 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -802,7 +802,7 @@ namespace smt { setup_dl(); setup_seq_str(st); setup_fpa(); - if (st.m_has_sr) setup_special_relations(); + setup_special_relations(); } void setup::setup_unknown(static_features & st) { @@ -818,7 +818,7 @@ namespace smt { setup_seq_str(st); setup_fpa(); setup_recfuns(); - if (st.m_has_sr) setup_special_relations(); + setup_special_relations(); return; } diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 34a76d955..92aa4baec 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -41,7 +41,7 @@ Revision History: namespace smt { struct theory_arith_stats { - unsigned m_conflicts, m_add_rows, m_pivots, m_diseq_cs, m_gomory_cuts, m_branches, m_gcd_tests, m_patches, m_patches_succ; + unsigned m_conflicts, m_add_rows, m_pivots, m_diseq_cs, m_gomory_cuts, m_branches, m_gcd_tests, m_gcd_conflicts, m_patches, m_patches_succ; unsigned m_assert_lower, m_assert_upper, m_assert_diseq, m_core2th_eqs, m_core2th_diseqs; unsigned m_th2core_eqs, m_th2core_diseqs, m_bound_props, m_offset_eqs, m_fixed_eqs, m_offline_eqs; unsigned m_max_min; @@ -452,18 +452,18 @@ namespace smt { svector m_var_pos; // temporary array used in add_rows atoms m_atoms; // set of theory atoms ptr_vector m_asserted_bounds; // set of asserted bounds - unsigned m_asserted_qhead; + unsigned m_asserted_qhead = 0; ptr_vector m_new_atoms; // new bound atoms that have yet to be internalized. svector m_nl_monomials; // non linear monomials svector m_nl_propagated; // non linear monomials that became linear v_dependency_manager m_dep_manager; // for tracking bounds during non-linear reasoning vector m_row_vars; // variables in a given row. Used during internalization to detect repeated variables. - unsigned m_row_vars_top; + unsigned m_row_vars_top = 0; var_heap m_to_patch; // heap containing all variables v s.t. m_value[v] does not satisfy bounds of v. nat_set m_left_basis; // temporary: set of variables that already left the basis in make_feasible - bool m_blands_rule; + bool m_blands_rule = false; svector m_update_trail_stack; // temporary trail stack used to restore the last feasible assignment. nat_set m_in_update_trail_stack; // set of variables in m_update_trail_stack @@ -473,11 +473,11 @@ namespace smt { inf_numeral m_tmp; random_gen m_random; - unsigned m_num_conflicts; + unsigned m_num_conflicts = 0; - unsigned m_branch_cut_counter; + unsigned m_branch_cut_counter = 0; bool m_eager_gcd; // true if gcd should be applied at every add_row - unsigned m_final_check_idx; + unsigned m_final_check_idx = 0; // backtracking @@ -676,7 +676,7 @@ namespace smt { See also m_changed_assignment flag. */ - bool m_liberal_final_check; + bool m_liberal_final_check = true; final_check_status final_check_core(); final_check_status final_check_eh() override; @@ -734,7 +734,7 @@ namespace smt { // Assignment management // // ----------------------------------- - bool m_changed_assignment; //!< auxiliary variable set to true when the assignment is changed. + bool m_changed_assignment = false; //!< auxiliary variable set to true when the assignment is changed. void save_value(theory_var v); void discard_update_trail(); void restore_assignment(); @@ -790,11 +790,11 @@ namespace smt { void mark_row_for_bound_prop(unsigned r1); void mark_rows_for_bound_prop(theory_var v); void is_row_useful_for_bound_prop(row const & r, int & lower_idx, int & upper_idx) const; - void imply_bound_for_monomial(row const & r, int idx, bool lower); - void imply_bound_for_all_monomials(row const & r, bool lower); + unsigned imply_bound_for_monomial(row const & r, int idx, bool lower); + unsigned imply_bound_for_all_monomials(row const & r, bool lower); void explain_bound(row const & r, int idx, bool lower, inf_numeral & delta, antecedents & antecedents); - void mk_implied_bound(row const & r, unsigned idx, bool lower, theory_var v, bound_kind kind, inf_numeral const & k); + unsigned mk_implied_bound(row const & r, unsigned idx, bool lower, theory_var v, bound_kind kind, inf_numeral const & k); void assign_bound_literal(literal l, row const & r, unsigned idx, bool lower, inf_numeral & delta); void propagate_bounds(); @@ -821,7 +821,7 @@ namespace smt { var_set m_tmp_var_set; var_set m_tmp_var_set2; svector > m_assume_eq_candidates; - unsigned m_assume_eq_head; + unsigned m_assume_eq_head = 0; bool random_update(theory_var v); void mutate_assignment(); bool assume_eqs_core(); @@ -953,10 +953,10 @@ namespace smt { // // ----------------------------------- typedef int_hashtable > row_set; - bool m_model_depends_on_computed_epsilon; - unsigned m_nl_rounds; - bool m_nl_gb_exhausted; - unsigned m_nl_strategy_idx; // for fairness + bool m_model_depends_on_computed_epsilon = false; + unsigned m_nl_rounds = 0; + bool m_nl_gb_exhausted = false; + unsigned m_nl_strategy_idx = 0; // for fairness expr_ref_vector m_nl_new_exprs; typedef obj_map var2num_occs; var2num_occs m_var2num_occs; diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 3a5c86207..159758c80 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -47,11 +47,15 @@ namespace smt { else if (m_util.is_idiv(n)) { e = m_util.mk_idiv0(n->get_arg(0), n->get_arg(1)); } - else if (m_util.is_rem(n)) { - e = m_util.mk_rem0(n->get_arg(0), n->get_arg(1)); + else if (m_util.is_rem(n)) { + expr* z = m_util.mk_int(0); + e = m_util.mk_rem0(n->get_arg(0), z); + n = m_util.mk_rem(n->get_arg(0), z); } - else if (m_util.is_mod(n)) { - e = m_util.mk_mod0(n->get_arg(0), n->get_arg(1)); + else if (m_util.is_mod(n)) { + expr* z = m_util.mk_int(0); + e = m_util.mk_mod0(n->get_arg(0), z); + n = m_util.mk_mod(n->get_arg(0), z); } else if (m_util.is_power(n)) { e = m_util.mk_power0(n->get_arg(0), n->get_arg(1)); @@ -154,7 +158,6 @@ namespace smt { case OP_MOD: case OP_DIV0: case OP_IDIV0: - case OP_REM0: case OP_MOD0: return true; default: @@ -1732,23 +1735,11 @@ namespace smt { m_util(m), m_arith_eq_solver(m), m_arith_eq_adapter(*this, m_util), - m_asserted_qhead(0), - m_row_vars_top(0), m_to_patch(1024), - m_blands_rule(false), m_random(ctx.get_fparams().m_arith_random_seed), - m_num_conflicts(0), - m_branch_cut_counter(0), m_eager_gcd(m_params.m_arith_eager_gcd), - m_final_check_idx(0), m_antecedents_index(0), m_var_value_table(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)), - m_liberal_final_check(true), - m_changed_assignment(false), - m_assume_eq_head(0), - m_model_depends_on_computed_epsilon(false), - m_nl_rounds(0), - m_nl_gb_exhausted(false), m_nl_new_exprs(m), m_bound_watch(null_bool_var) { } @@ -2700,8 +2691,9 @@ namespace smt { Then this bound is used to produce a bound for the monomial variable. */ template - void theory_arith::imply_bound_for_monomial(row const & r, int idx, bool is_lower) { + unsigned theory_arith::imply_bound_for_monomial(row const & r, int idx, bool is_lower) { row_entry const & entry = r[idx]; + unsigned count = 0; if (m_unassigned_atoms[entry.m_var] > 0) { inf_numeral implied_k; typename vector::const_iterator it = r.begin_entries(); @@ -2723,7 +2715,7 @@ namespace smt { tout << "implying lower bound for v" << entry.m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, entry.m_var);); - mk_implied_bound(r, idx, is_lower, entry.m_var, B_LOWER, implied_k); + count += mk_implied_bound(r, idx, is_lower, entry.m_var, B_LOWER, implied_k); } } else { @@ -2734,10 +2726,11 @@ namespace smt { tout << "implying upper bound for v" << entry.m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, entry.m_var);); - mk_implied_bound(r, idx, is_lower, entry.m_var, B_UPPER, implied_k); + count += mk_implied_bound(r, idx, is_lower, entry.m_var, B_UPPER, implied_k); } } } + return count; } /** @@ -2748,7 +2741,7 @@ namespace smt { for the monomial variables. */ template - void theory_arith::imply_bound_for_all_monomials(row const & r, bool is_lower) { + unsigned theory_arith::imply_bound_for_all_monomials(row const & r, bool is_lower) { // Traverse the row once and compute // bb = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_j > 0} -a_j * upper(x_j)) If is_lower = true // bb = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_j < 0} -a_j * upper(x_j)) If is_lower = false @@ -2761,6 +2754,7 @@ namespace smt { } } + unsigned count = 0; inf_numeral implied_k; typename vector::const_iterator it = r.begin(); typename vector::const_iterator end = r.end(); @@ -2784,7 +2778,7 @@ namespace smt { tout << "implying lower bound for v" << it->m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, it->m_var);); - mk_implied_bound(r, idx, is_lower, it->m_var, B_LOWER, implied_k); + count += mk_implied_bound(r, idx, is_lower, it->m_var, B_LOWER, implied_k); } } else { @@ -2796,11 +2790,12 @@ namespace smt { tout << "implying upper bound for v" << it->m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, it->m_var);); - mk_implied_bound(r, idx, is_lower, it->m_var, B_UPPER, implied_k); + count += mk_implied_bound(r, idx, is_lower, it->m_var, B_UPPER, implied_k); } } } } + return count; } /** @@ -2922,10 +2917,11 @@ namespace smt { } template - void theory_arith::mk_implied_bound(row const & r, unsigned idx, bool is_lower, theory_var v, bound_kind kind, inf_numeral const & k) { + unsigned theory_arith::mk_implied_bound(row const & r, unsigned idx, bool is_lower, theory_var v, bound_kind kind, inf_numeral const & k) { atoms const & as = m_var_occs[v]; inf_numeral const & epsilon = get_epsilon(v); inf_numeral delta; + unsigned count = 0; for (atom* a : as) { bool_var bv = a->get_bool_var(); literal l(bv); @@ -2942,6 +2938,7 @@ namespace smt { TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", v" << v << " >= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(l, r, idx, is_lower, delta); + ++count; } // v <= k k < k2 |- v < k2 |- not v >= k2 if (kind == B_UPPER && k < k2) { @@ -2958,6 +2955,7 @@ namespace smt { TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", not v" << v << " >= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(~l, r, idx, is_lower, delta); + ++count; } } } @@ -2973,6 +2971,7 @@ namespace smt { TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", not v" << v << " <= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(~l, r, idx, is_lower, delta); + ++count; } } // v <= k k <= k2 |- v <= k2 @@ -2984,10 +2983,12 @@ namespace smt { TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", v" << v << " <= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(l, r, idx, is_lower, delta); + ++count; } } } } + return count; } @@ -2998,11 +2999,17 @@ namespace smt { antecedents ante(*this); explain_bound(r, idx, is_lower, delta, ante); + TRACE("propagate_bounds", ante.display(tout) << " --> "; ctx.display_detailed_literal(tout, l); tout << "\n";); + + + TRACE("arith", tout << ctx.get_scope_level() << "\n"; + ctx.display_detailed_literal(tout, l) << "\n"); + if (ante.lits().size() < small_lemma_size() && ante.eqs().empty()) { literal_vector & lits = m_tmp_literal_vector2; lits.reset(); @@ -3031,6 +3038,7 @@ namespace smt { template void theory_arith::propagate_bounds() { TRACE("propagate_bounds_detail", display(tout);); + unsigned num_prop = 0, count = 0; for (unsigned r_idx : m_to_check) { row & r = m_rows[r_idx]; if (r.get_base_var() != null_theory_var) { @@ -3039,15 +3047,21 @@ namespace smt { int upper_idx; is_row_useful_for_bound_prop(r, lower_idx, upper_idx); + ++num_prop; if (lower_idx >= 0) - imply_bound_for_monomial(r, lower_idx, true); + count += imply_bound_for_monomial(r, lower_idx, true); else if (lower_idx == -1) - imply_bound_for_all_monomials(r, true); - + count += imply_bound_for_all_monomials(r, true); + else + --num_prop; + + ++num_prop; if (upper_idx >= 0) - imply_bound_for_monomial(r, upper_idx, false); + count += imply_bound_for_monomial(r, upper_idx, false); else if (upper_idx == -1) - imply_bound_for_all_monomials(r, false); + count += imply_bound_for_all_monomials(r, false); + else + --num_prop; // sneaking cheap eq detection in this loop propagate_cheap_eq(r_idx); @@ -3063,6 +3077,7 @@ namespace smt { #endif } } + TRACE("arith_eq", tout << "done\n";); m_to_check.reset(); m_in_to_check.reset(); @@ -3377,7 +3392,7 @@ namespace smt { } template - void theory_arith::pop_scope_eh(unsigned num_scopes) { + void theory_arith::pop_scope_eh(unsigned num_scopes) { CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); @@ -3397,7 +3412,6 @@ namespace smt { restore_unassigned_atoms(s.m_unassigned_atoms_trail_lim); m_asserted_bounds.shrink(s.m_asserted_bounds_lim); m_asserted_qhead = s.m_asserted_qhead_old; - TRACE("arith_pop_scope_bug", tout << "num_vars: " << get_num_vars() << ", num_old_vars: " << get_old_num_vars(num_scopes) << "\n";); restore_nl_propagated_flag(s.m_nl_propagated_lim); m_nl_monomials.shrink(s.m_nl_monomials_lim); del_atoms(s.m_atoms_lim); diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index 699ebd5d2..c9bc9f31a 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -514,9 +514,11 @@ namespace smt { // SASSERT(m_value[x_i].is_rational()); // infinitesimals are not used for integer variables SASSERT(!m_value[x_i].is_int()); // the base variable is not assigned to an integer value. - if (constrain_free_vars(r) || !is_gomory_cut_target(r)) { + bool cfv = constrain_free_vars(r); + + if (cfv || !is_gomory_cut_target(r)) { TRACE("gomory_cut", tout << "failed to apply gomory cut:\n"; - tout << "constrain_free_vars(r): " << constrain_free_vars(r) << "\n";); + tout << "constrain_free_vars(r): " << cfv << "\n";); return false; } @@ -752,6 +754,7 @@ namespace smt { if (!(consts / gcds).is_int()) { TRACE("gcd_test", tout << "row failed the GCD test:\n"; display_row_info(tout, r);); antecedents ante(*this); + m_stats.m_gcd_conflicts++; collect_fixed_var_justifications(r, ante); context & ctx = get_context(); ctx.set_conflict( @@ -831,6 +834,7 @@ namespace smt { numeral u1 = floor(u/gcds); if (u1 < l1) { + m_stats.m_gcd_conflicts++; TRACE("gcd_test", tout << "row failed the extended GCD test:\n"; display_row_info(tout, r);); collect_fixed_var_justifications(r, ante); context & ctx = get_context(); diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index 3aa34cbc7..f44516cad 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -892,6 +892,7 @@ bool theory_arith::propagate_linear_monomial(theory_var v) { } tout << "\n";); + return true; } @@ -2264,8 +2265,10 @@ typename theory_arith::gb_result theory_arith::compute_grobner(svector return GB_FAIL; if (get_gb_eqs_and_look_for_conflict(eqs, gb)) return GB_PROGRESS; + if (scan_for_linear(eqs, gb)) + return GB_NEW_EQ; } - while(scan_for_linear(eqs, gb) && m_params.m_nl_arith_gb_perturbate && + while(m_params.m_nl_arith_gb_perturbate && (!m_nl_gb_exhausted) && try_to_modify_eqs(eqs, gb, next_weight)); return GB_FAIL; } diff --git a/src/smt/theory_arith_pp.h b/src/smt/theory_arith_pp.h index d10bfca55..b0d43bc00 100644 --- a/src/smt/theory_arith_pp.h +++ b/src/smt/theory_arith_pp.h @@ -37,6 +37,7 @@ namespace smt { st.update("arith assume eqs", m_stats.m_assume_eqs); st.update("arith offset eqs", m_stats.m_offset_eqs); st.update("arith gcd tests", m_stats.m_gcd_tests); + st.update("arith gcd conflicts", m_stats.m_gcd_conflicts); st.update("arith ineq splits", m_stats.m_branches); st.update("arith gomory cuts", m_stats.m_gomory_cuts); st.update("arith branch int", m_stats.m_branch_infeasible_int); @@ -82,8 +83,9 @@ namespace smt { template void theory_arith::display_row(std::ostream & out, row const & r, bool compact) const { - - out << "(v" << r.get_base_var() << ") : "; + + column const & c = m_columns[r.get_base_var()]; + out << "(v" << r.get_base_var() << " r" << c[0].m_row_id << ") : "; bool first = true; for (auto const& e : r) { if (!e.is_dead()) { diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp index 120e12418..079c2f62e 100644 --- a/src/smt/theory_array_full.cpp +++ b/src/smt/theory_array_full.cpp @@ -252,6 +252,8 @@ namespace smt { else if (m.is_lambda_def(n->get_decl())) { instantiate_default_lambda_def_axiom(n); d->m_lambdas.push_back(n); + m_lambdas.push_back(n); + ctx.push_trail(push_back_vector(m_lambdas)); } return r; } @@ -830,6 +832,12 @@ namespace smt { return true; } } + for (enode* n : m_lambdas) + for (enode* p : n->get_parents()) + if (!is_default(p) && !ctx.is_beta_redex(p, n)) { + TRACE("array", tout << "lambda is not a beta redex " << enode_pp(p, ctx) << "\n"); + return true; + } return false; } diff --git a/src/smt/theory_array_full.h b/src/smt/theory_array_full.h index 210426e10..98e53627c 100644 --- a/src/smt/theory_array_full.h +++ b/src/smt/theory_array_full.h @@ -86,6 +86,7 @@ namespace smt { bool has_unitary_domain(app* array_term); std::pair mk_epsilon(sort* s); enode_vector m_as_array; + enode_vector m_lambdas; bool has_non_beta_as_array(); bool instantiate_select_const_axiom(enode* select, enode* cnst); diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index b3a6e77ff..7adab35f4 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -811,6 +811,7 @@ namespace smt { init_bits(e, bits); } + MK_UNARY(internalize_neg, mk_neg); MK_UNARY(internalize_not, mk_not); MK_UNARY(internalize_redand, mk_redand); MK_UNARY(internalize_redor, mk_redor); @@ -895,6 +896,7 @@ namespace smt { } switch (term->get_decl_kind()) { case OP_BV_NUM: internalize_num(term); return true; + case OP_BNEG: internalize_neg(term); return true; case OP_BADD: internalize_add(term); return true; case OP_BSUB: internalize_sub(term); return true; case OP_BMUL: internalize_mul(term); return true; @@ -944,6 +946,9 @@ namespace smt { internalize_bv2int(term); } return params().m_bv_enable_int2bv2int; + case OP_BSREM: return false; + case OP_BUREM: return false; + case OP_BSMOD: return false; default: TRACE("bv_op", tout << "unsupported operator: " << mk_ll_pp(term, m) << "\n";); UNREACHABLE(); diff --git a/src/smt/theory_bv.h b/src/smt/theory_bv.h index 588f19d89..73d659c68 100644 --- a/src/smt/theory_bv.h +++ b/src/smt/theory_bv.h @@ -196,6 +196,7 @@ namespace smt { void internalize_ext_rotate_right(app * n); void internalize_and(app * n); void internalize_or(app * n); + void internalize_neg(app * n); void internalize_not(app * n); void internalize_nand(app * n); void internalize_nor(app * n); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index c5b706fde..c6bd12f03 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -19,9 +19,6 @@ --*/ #include "util/stopwatch.h" -#include "math/lp/lp_solver.h" -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/nla_solver.h" @@ -48,6 +45,7 @@ #include "ast/ast_ll_pp.h" #include "util/cancel_eh.h" #include "util/scoped_timer.h" +#include "util/distribution.h" typedef lp::var_index lpvar; @@ -318,11 +316,13 @@ class theory_lra::imp { else if (a.is_idiv(n, x, y)) { e = a.mk_idiv0(x, y); } - else if (a.is_rem(n, x, y)) { - e = a.mk_rem0(x, y); + else if (a.is_rem(n, x, y)) { + n = a.mk_rem(x, a.mk_int(0)); + e = a.mk_rem0(x, a.mk_int(0)); } else if (a.is_mod(n, x, y)) { - e = a.mk_mod0(x, y); + n = a.mk_mod(x, a.mk_int(0)); + e = a.mk_mod0(x, a.mk_int(0)); } else if (a.is_power(n, x, y)) { e = a.mk_power0(x, y); @@ -439,6 +439,9 @@ class theory_lra::imp { if (ctx().relevancy()) ctx().add_relevancy_dependency(n, mod); if (m_nla && !a.is_numeral(n2)) { // shortcut to create non-linear division axioms. + internalize_term(to_app(n)); + internalize_term(to_app(n1)); + internalize_term(to_app(n2)); theory_var q = mk_var(n); theory_var x = mk_var(n1); theory_var y = mk_var(n2); @@ -446,6 +449,9 @@ class theory_lra::imp { } if (a.is_numeral(n2) && a.is_bounded(n1)) { ensure_nla(); + internalize_term(to_app(n)); + internalize_term(to_app(n1)); + internalize_term(to_app(n2)); theory_var q = mk_var(n); theory_var x = mk_var(n1); theory_var y = mk_var(n2); @@ -466,7 +472,7 @@ class theory_lra::imp { st.to_ensure_var().push_back(n1); st.to_ensure_var().push_back(n2); } - else if (a.is_idiv0(n, n1, n2) || a.is_mod0(n, n1, n2) || a.is_rem0(n, n1, n2)) { + else if (a.is_idiv0(n, n1, n2) || a.is_mod0(n, n1, n2)) { st.to_ensure_var().push_back(n1); st.to_ensure_var().push_back(n2); } @@ -814,12 +820,13 @@ class theory_lra::imp { } lpvar get_lpvar(expr* e) { - return get_lpvar(get_enode(e)); + theory_var v = mk_var(e); + m_solver->register_existing_terms(); + return register_theory_var_in_lar_solver(v); } lpvar get_lpvar(enode* n) { - ensure_column(n); - return n ? get_lpvar(n->get_th_var(get_id())) : lp::null_lpvar; + return get_lpvar(n->get_expr()); } lpvar get_lpvar(theory_var v) const { @@ -1134,6 +1141,17 @@ public: expr_ref zero(a.mk_real(0), m); mk_axiom(~mk_literal(a.mk_le(p, zero))); } + bool can_be_underspecified = false; + if (a.is_numeral(x, r) && r == 0 && (!a.is_numeral(y, r) || r == 0)) + can_be_underspecified = true; + if (!a.is_extended_numeral(x, r) && + !a.is_extended_numeral(y, r)) + can_be_underspecified = true; + if (can_be_underspecified) { + literal lit = th.mk_eq(p, a.mk_power0(x, y), false); + ctx().mark_as_relevant(lit); + ctx().assign(lit, nullptr); + } } // n < 0 || rem(a, n) = mod(a, n) @@ -1299,7 +1317,6 @@ public: expr_ref abs_q(m.mk_ite(a.mk_ge(q, zero), q, a.mk_uminus(q)), m); expr_ref mone(a.mk_int(-1), m); expr_ref modmq(a.mk_sub(mod, abs_q), m); - ctx().get_rewriter()(modmq); literal eqz = mk_literal(m.mk_eq(q, zero)); literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero)); literal mod_lt_q = mk_literal(a.mk_le(modmq, mone)); @@ -1514,21 +1531,23 @@ public: } } TRACE("arith", - for (theory_var v = 0; v < sz; ++v) { - if (th.is_relevant_and_shared(get_enode(v))) { + for (theory_var v = 0; v < sz; ++v) + if (th.is_relevant_and_shared(get_enode(v))) tout << "v" << v << " "; - } - } tout << "\n"; ); if (!vars.empty()) { lp().random_update(vars.size(), vars.data()); } } - bool assume_eqs() { + bool assume_eqs() { + if (delayed_assume_eqs()) + return true; + TRACE("arith", display(tout);); random_update(); m_model_eqs.reset(); + theory_var sz = static_cast(th.get_num_vars()); unsigned old_sz = m_assume_eq_candidates.size(); unsigned num_candidates = 0; @@ -1577,8 +1596,10 @@ public: CTRACE("arith", is_eq(v1, v2) && n1->get_root() != n2->get_root(), tout << "assuming eq: v" << v1 << " = v" << v2 << "\n";); - if (is_eq(v1, v2) && n1->get_root() != n2->get_root() && th.assume_eq(n1, n2)) + if (is_eq(v1, v2) && n1->get_root() != n2->get_root() && th.assume_eq(n1, n2)) { + ++m_stats.m_assume_eqs; return true; + } } return false; } @@ -1596,8 +1617,12 @@ public: final_check_status eval_power(expr* e) { expr* x, * y; + rational r; VERIFY(a.is_power(e, x, y)); - + if (a.is_numeral(x, r) && r == 0 && a.is_numeral(y, r) && r == 0) + return FC_DONE; + if (!m_nla) + return FC_GIVEUP; switch (m_nla->check_power(get_lpvar(e), get_lpvar(x), get_lpvar(y), m_nla_lemma_vector)) { case l_true: return FC_DONE; @@ -1616,9 +1641,14 @@ public: final_check_status eval_unsupported(expr* e) { if (a.is_power(e)) return eval_power(e); + if (a.is_power0(e)) + return FC_DONE; return FC_GIVEUP; } + unsigned m_final_check_idx = 0; + distribution m_dist { 0 }; + final_check_status final_check_eh() { if (propagate_core()) return FC_CONTINUE; @@ -1629,53 +1659,95 @@ public: if (!lp().is_feasible() || lp().has_changed_columns()) { is_sat = make_feasible(); } + bool giveup = false; final_check_status st = FC_DONE; - + m_final_check_idx = 0; // remove to experiment. + unsigned old_idx = m_final_check_idx; switch (is_sat) { case l_true: TRACE("arith", display(tout)); - switch (check_lia()) { - case l_true: - break; - case l_false: - return FC_CONTINUE; - case l_undef: - TRACE("arith", tout << "check-lia giveup\n";); - if (ctx().get_fparams().m_arith_ignore_int) + // if (lp().has_fixed_at_bound()) // explain and propagate. + +#if 0 + m_dist.reset(); + m_dist.push(0, 1); + m_dist.push(1, 1); + m_dist.push(2, 1); + + for (auto idx : m_dist) { + if (!m.inc()) return FC_GIVEUP; - st = FC_CONTINUE; - break; + + switch (idx) { + case 0: + if (assume_eqs()) + st = FC_CONTINUE; + break; + case 1: + st = check_nla(); + break; + case 2: + st = check_lia(); + break; + default: + UNREACHABLE(); + break; + } + switch (st) { + case FC_DONE: + break; + case FC_CONTINUE: + return st; + case FC_GIVEUP: + giveup = true; + break; + } } - switch (check_nla()) { - case l_true: - break; - case l_false: - return FC_CONTINUE; - case l_undef: - TRACE("arith", tout << "check-nra giveup\n";); - st = FC_GIVEUP; - break; - } +#else - if (delayed_assume_eqs()) { - ++m_stats.m_assume_eqs; - return FC_CONTINUE; - } - if (assume_eqs()) { - ++m_stats.m_assume_eqs; - return FC_CONTINUE; + do { + if (!m.inc()) + return FC_GIVEUP; + + switch (m_final_check_idx) { + case 0: + if (assume_eqs()) + st = FC_CONTINUE; + break; + case 1: + st = check_lia(); + break; + case 2: + st = check_nla(); + break; + } + m_final_check_idx = (m_final_check_idx + 1) % 3; + switch (st) { + case FC_DONE: + break; + case FC_CONTINUE: + return st; + case FC_GIVEUP: + giveup = true; + break; + } } + while (old_idx != m_final_check_idx); +#endif + + if (giveup) + return FC_GIVEUP; for (expr* e : m_not_handled) { if (!ctx().is_relevant(e)) continue; - st = FC_DONE; switch (eval_unsupported(e)) { case FC_CONTINUE: st = FC_CONTINUE; break; case FC_GIVEUP: + TRACE("arith", tout << "give up " << mk_pp(e, m) << "\n"); if (st != FC_CONTINUE) st = FC_GIVEUP; break; @@ -1894,21 +1966,19 @@ public: visitor.display_asserts(out, fmls, true); out << "(check-sat)\n"; } - - lbool check_lia() { + + final_check_status check_lia() { TRACE("arith",); if (!m.inc()) { TRACE("arith", tout << "canceled\n";); - return l_undef; + return FC_CONTINUE; } - lbool lia_check = l_undef; auto cr = m_lia->check(&m_explanation); if (cr != lp::lia_move::sat && ctx().get_fparams().m_arith_ignore_int) - return l_undef; + return FC_GIVEUP; switch (cr) { case lp::lia_move::sat: - lia_check = l_true; break; case lp::lia_move::branch: { @@ -1931,13 +2001,12 @@ public: // TBD: ctx().force_phase(ctx().get_literal(b)); // at this point we have a new unassigned atom that the // SAT core assigns a value to - lia_check = l_false; ++m_stats.m_branch; - break; + return FC_CONTINUE; } case lp::lia_move::cut: { if (ctx().get_fparams().m_arith_ignore_int) - return l_undef; + return FC_GIVEUP; TRACE("arith", tout << "cut\n";); ++m_stats.m_gomory_cuts; // m_explanation implies term <= k @@ -1959,28 +2028,28 @@ public: ctx().display_lemma_as_smt_problem(tout << "new cut:\n", m_core.size(), m_core.data(), m_eqs.size(), m_eqs.data(), lit); display(tout);); assign(lit, m_core, m_eqs, m_params); - lia_check = l_false; - break; + return FC_CONTINUE; } case lp::lia_move::conflict: TRACE("arith", tout << "conflict\n";); // ex contains unsat core set_conflict(); - return l_false; + return FC_CONTINUE; case lp::lia_move::undef: TRACE("arith", tout << "lia undef\n";); - lia_check = l_undef; - break; + return FC_CONTINUE; case lp::lia_move::continue_with_check: - lia_check = l_undef; - break; + return FC_CONTINUE; default: UNREACHABLE(); } - if (lia_check != l_false && !check_idiv_bounds()) - return l_false; + if (!check_idiv_bounds()) + return FC_CONTINUE; - return lia_check; + if (assume_eqs()) + return FC_CONTINUE; + + return FC_DONE; } nla::lemma m_lemma; @@ -2017,36 +2086,31 @@ public: set_conflict_or_lemma(core, false); } - lbool check_nla_continue() { + final_check_status check_nla_continue() { m_a1 = nullptr; m_a2 = nullptr; lbool r = m_nla->check(m_nla_lemma_vector); switch (r) { - case l_false: { + case l_false: for (const nla::lemma & l : m_nla_lemma_vector) - false_case_of_check_nla(l); - break; - } + false_case_of_check_nla(l); + return FC_CONTINUE; case l_true: - if (assume_eqs()) { - return l_false; - } - break; - case l_undef: - break; + return assume_eqs()? FC_CONTINUE: FC_DONE; + default: + return FC_GIVEUP; } - return r; } - lbool check_nla() { + final_check_status check_nla() { if (!m.inc()) { TRACE("arith", tout << "canceled\n";); - return l_undef; + return FC_GIVEUP; } CTRACE("arith",!m_nla, tout << "no nla\n";); if (!m_nla) - return l_true; + return FC_DONE; if (!m_nla->need_check()) - return l_true; + return FC_DONE; return check_nla_continue(); } @@ -2168,7 +2232,6 @@ public: set_evidence(j, m_core, m_eqs); m_explanation.add_pair(j, v); } - void propagate_bounds_with_lp_solver() { if (!should_propagate()) @@ -2182,13 +2245,16 @@ public: if (is_infeasible()) { get_infeasibility_explanation_and_set_conflict(); + // verbose_stream() << "unsat\n"; } else { + unsigned count = 0, prop = 0; for (auto& ib : m_bp.ibounds()) { m.inc(); if (ctx().inconsistent()) break; - propagate_lp_solver_bound(ib); + ++prop; + count += propagate_lp_solver_bound(ib); } } } @@ -2209,12 +2275,14 @@ public: return false; } - void propagate_lp_solver_bound(const lp::implied_bound& be) { + +#if 0 + unsigned propagate_lp_solver_bound_dry_run(const lp::implied_bound& be) { lpvar vi = be.m_j; theory_var v = lp().local_to_external(vi); if (v == null_theory_var) - return; + return 0; TRACE("arith", tout << "v" << v << " " << be.kind() << " " << be.m_bound << "\n";); @@ -2222,20 +2290,58 @@ public: if (m_unassigned_bounds[v] == 0 && !should_refine_bounds()) { TRACE("arith", tout << "return\n";); - return; + return 0; } lp_bounds const& bounds = m_bounds[v]; bool first = true; + unsigned count = 0; for (unsigned i = 0; i < bounds.size(); ++i) { api_bound* b = bounds[i]; - if (ctx().get_assignment(b->get_lit()) != l_undef) { + if (ctx().get_assignment(b->get_lit()) != l_undef) continue; - } literal lit = is_bound_implied(be.kind(), be.m_bound, *b); - if (lit == null_literal) { + if (lit == null_literal) continue; - } TRACE("arith", tout << lit << " bound: " << *b << " first: " << first << "\n";); + ctx().display_literal_verbose(verbose_stream() << "miss ", lit) << "\n"; + display(verbose_stream()); + TRACE("arith", ctx().display_literal_verbose(tout << "miss ", lit) << "\n"); + exit(0); + + ++count; + } + return count; + } +#endif + + unsigned propagate_lp_solver_bound(const lp::implied_bound& be) { + lpvar vi = be.m_j; + theory_var v = lp().local_to_external(vi); + + if (v == null_theory_var) + return 0; + + TRACE("arith", tout << "v" << v << " " << be.kind() << " " << be.m_bound << "\n";); + + reserve_bounds(v); + + if (m_unassigned_bounds[v] == 0 && !should_refine_bounds()) { + TRACE("arith", tout << "return\n";); + return 0; + } + lp_bounds const& bounds = m_bounds[v]; + bool first = true; + unsigned count = 0; + for (unsigned i = 0; i < bounds.size(); ++i) { + api_bound* b = bounds[i]; + if (ctx().get_assignment(b->get_lit()) != l_undef) + continue; + literal lit = is_bound_implied(be.kind(), be.m_bound, *b); + if (lit == null_literal) + continue; + TRACE("arith", tout << lit << " bound: " << *b << " first: " << first << "\n";); + + ++count; lp().settings().stats().m_num_of_implied_bounds ++; if (first) { @@ -2254,6 +2360,8 @@ public: display_evidence(tout, m_explanation); lp().print_implied_bound(be, tout); ); + + DEBUG_CODE( for (auto& lit : m_core) { VERIFY(ctx().get_assignment(lit) == l_true); @@ -2263,7 +2371,9 @@ public: } if (should_refine_bounds() && first) - refine_bound(v, be); + refine_bound(v, be); + + return count; } void refine_bound(theory_var v, const lp::implied_bound& be) { @@ -2887,7 +2997,7 @@ public: propagate_eqs(b.tv(), ci, k, b, value.get_rational()); } #if 0 - if (propagation_mode() != BP_NONE) + if (should_propagate()) lp().mark_rows_for_bound_prop(b.tv().id()); #endif } @@ -3125,7 +3235,7 @@ public: return l_false; TRACE("arith", tout << "status treated as inconclusive: " << status << "\n";); // TENTATIVE_UNBOUNDED, UNBOUNDED, TENTATIVE_DUAL_UNBOUNDED, DUAL_UNBOUNDED, - // FLOATING_POINT_ERROR, TIME_EXAUSTED, EMPTY, UNSTABLE + // TIME_EXAUSTED, EMPTY, UNSTABLE return l_undef; } @@ -3908,5 +4018,6 @@ void theory_lra::setup() { } template class lp::lp_bound_propagator; template void lp::lar_solver::propagate_bounds_for_touched_rows(lp::lp_bound_propagator&); +template void lp::lar_solver::check_missed_propagations(lp::lp_bound_propagator&); template void lp::lar_solver::explain_implied_bound(const lp::implied_bound&, lp::lp_bound_propagator&); -template void lp::lar_solver::calculate_implied_bounds_for_row(unsigned int, lp::lp_bound_propagator&); +template unsigned lp::lar_solver::calculate_implied_bounds_for_row(unsigned int, lp::lp_bound_propagator&); diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index db076ef8c..718d5c65a 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -1604,7 +1604,7 @@ namespace smt { std::cout << B << "\n"; } #endif - SASSERT(is_sat != l_true); + VERIFY(is_sat != l_true); return true; } diff --git a/src/smt/theory_special_relations.cpp b/src/smt/theory_special_relations.cpp index 9113f189e..ddddfbc00 100644 --- a/src/smt/theory_special_relations.cpp +++ b/src/smt/theory_special_relations.cpp @@ -71,7 +71,7 @@ namespace smt { ensure_var(v1); ensure_var(v2); literal_vector ls; - ls.push_back(l); + ls.push_back(l); return m_graph.add_non_strict_edge(v1, v2, ls) && m_graph.add_non_strict_edge(v2, v1, ls); } @@ -130,6 +130,7 @@ namespace smt { } bool theory_special_relations::internalize_term(app * term) { + verbose_stream() << mk_pp(term, m) << "\n"; return false; } @@ -156,9 +157,8 @@ namespace smt { } theory_var theory_special_relations::mk_var(expr* e) { - if (!ctx.e_internalized(e)) { + if (!ctx.e_internalized(e)) ctx.internalize(e, false); - } enode * n = ctx.get_enode(e); theory_var v = n->get_th_var(get_id()); if (null_theory_var == v) { @@ -405,6 +405,12 @@ namespace smt { TRACE("special_relations", tout << "already: " << a.v2() << " <= " << a.v1() << "\n";); continue; } + if (a.v1() == a.v2()) { + r.m_explanation.reset(); + r.m_explanation.push_back(a.explanation()); + set_conflict(r); + return l_false; + } // the nodes visited from v1 become target for v2 if (r.m_graph.reachable(a.v2(), visited, target, w)) { // @@ -582,18 +588,18 @@ namespace smt { lbool theory_special_relations::final_check_po(relation& r) { for (atom* ap : r.m_asserted_atoms) { atom& a = *ap; - if (!a.phase() && r.m_uf.find(a.v1()) == r.m_uf.find(a.v2())) { - // v1 !-> v2 - // find v1 -> v3 -> v4 -> v2 path - r.m_explanation.reset(); - unsigned timestamp = r.m_graph.get_timestamp(); - bool found_path = r.m_graph.find_shortest_reachable_path(a.v1(), a.v2(), timestamp, r); - if (found_path) { - TRACE("special_relations", tout << "check po conflict\n";); - r.m_explanation.push_back(a.explanation()); - set_conflict(r); - return l_false; - } + if (a.phase()) + continue; + // v1 !-> v2 + // find v1 -> v3 -> v4 -> v2 path + r.m_explanation.reset(); + unsigned timestamp = r.m_graph.get_timestamp(); + bool found_path = a.v1() == a.v2() || r.m_graph.find_shortest_reachable_path(a.v1(), a.v2(), timestamp, r); + if (found_path) { + TRACE("special_relations", tout << "check po conflict\n";); + r.m_explanation.push_back(a.explanation()); + set_conflict(r); + return l_false; } } return l_true; @@ -601,9 +607,8 @@ namespace smt { void theory_special_relations::propagate() { if (m_can_propagate) { - for (auto const& kv : m_relations) { + for (auto const& kv : m_relations) propagate(*kv.m_value); - } m_can_propagate = false; } } @@ -1124,12 +1129,12 @@ namespace smt { } void theory_special_relations::display(std::ostream & out) const { - if (m_relations.empty()) return; + if (m_relations.empty()) + return; out << "Theory Special Relations\n"; display_var2enode(out); - for (auto const& kv : m_relations) { + for (auto const& kv : m_relations) kv.m_value->display(*this, out); - } } void theory_special_relations::collect_asserted_po_atoms(vector>& atoms) const { diff --git a/src/smt/theory_user_propagator.cpp b/src/smt/theory_user_propagator.cpp index f19f933f2..8eeaf4382 100644 --- a/src/smt/theory_user_propagator.cpp +++ b/src/smt/theory_user_propagator.cpp @@ -92,6 +92,9 @@ void theory_user_propagator::propagate_cb( expr_ref _conseq(conseq, m); ctx.get_rewriter()(conseq, _conseq); + if (!ctx.get_manager().is_true(_conseq) && !ctx.get_manager().is_false(_conseq)) + ctx.mark_as_relevant((expr*)_conseq); + if (ctx.lit_internalized(_conseq) && ctx.get_assignment(ctx.get_literal(_conseq)) == l_true) return; m_prop.push_back(prop_info(num_fixed, fixed_ids, num_eqs, eq_lhs, eq_rhs, _conseq)); diff --git a/src/solver/check_sat_result.cpp b/src/solver/check_sat_result.cpp index e946dd430..c0f3979aa 100644 --- a/src/solver/check_sat_result.cpp +++ b/src/solver/check_sat_result.cpp @@ -41,8 +41,10 @@ void check_sat_result::set_reason_unknown(event_handler& eh) { proof* check_sat_result::get_proof() { if (!m_log.empty() && !m_proof) { - app* last = m_log.back(); - m_log.push_back(to_app(m.get_fact(last))); + SASSERT(is_app(m_log.back())); + app* last = to_app(m_log.back()); + expr* fact = m.get_fact(last); + m_log.push_back(fact); m_proof = m.mk_clause_trail(m_log.size(), m_log.data()); } if (m_proof) diff --git a/src/solver/check_sat_result.h b/src/solver/check_sat_result.h index 936f6d3df..2269a1444 100644 --- a/src/solver/check_sat_result.h +++ b/src/solver/check_sat_result.h @@ -40,7 +40,7 @@ Notes: class check_sat_result { protected: ast_manager& m; - proof_ref_vector m_log; + expr_ref_vector m_log; proof_ref m_proof; unsigned m_ref_count = 0; lbool m_status = l_undef; diff --git a/src/solver/simplifier_solver.cpp b/src/solver/simplifier_solver.cpp index a717c4932..d70d232e4 100644 --- a/src/solver/simplifier_solver.cpp +++ b/src/solver/simplifier_solver.cpp @@ -23,7 +23,7 @@ Author: #include "ast/ast_util.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/simplifiers/dependent_expr_state.h" -#include "ast/simplifiers/seq_simplifier.h" +#include "ast/simplifiers/then_simplifier.h" #include "solver/solver.h" #include "solver/simplifier_solver.h" #include "solver/solver_preprocess.h" @@ -91,7 +91,7 @@ class simplifier_solver : public solver { solver_ref s; vector m_fmls; dep_expr_state m_preprocess_state; - seq_simplifier m_preprocess; + then_simplifier m_preprocess; expr_ref_vector m_assumptions; model_converter_ref m_mc; bool m_inconsistent = false; @@ -365,6 +365,7 @@ public: void user_propagate_register_diseq(user_propagator::eq_eh_t& diseq_eh) override { s->user_propagate_register_diseq(diseq_eh); } void user_propagate_register_expr(expr* e) override { m_preprocess_state.freeze(e); s->user_propagate_register_expr(e); } void user_propagate_register_created(user_propagator::created_eh_t& r) override { s->user_propagate_register_created(r); } + void user_propagate_register_decide(user_propagator::decide_eh_t& r) override { s->user_propagate_register_decide(r); } }; diff --git a/src/solver/solver_preprocess.cpp b/src/solver/solver_preprocess.cpp index 38b0584b5..9cac4b835 100644 --- a/src/solver/solver_preprocess.cpp +++ b/src/solver/solver_preprocess.cpp @@ -39,12 +39,13 @@ Notes: #include "ast/simplifiers/push_ite.h" #include "ast/simplifiers/elim_term_ite.h" #include "ast/simplifiers/flatten_clauses.h" +#include "ast/simplifiers/bound_simplifier.h" #include "ast/simplifiers/cnf_nnf.h" #include "smt/params/smt_params.h" #include "solver/solver_preprocess.h" #include "qe/lite/qe_lite_tactic.h" -void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dependent_expr_state& st) { +void init_preprocess(ast_manager& m, params_ref const& p, then_simplifier& s, dependent_expr_state& st) { smt_params smtp(p); s.add_simplifier(alloc(rewriter_simplifier, m, p, st)); @@ -59,6 +60,7 @@ void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dep if (smtp.m_refine_inj_axiom) s.add_simplifier(alloc(refine_inj_axiom_simplifier, m, p, st)); if (smtp.m_bv_size_reduce) s.add_simplifier(alloc(bv::slice, m, st)); if (smtp.m_distribute_forall) s.add_simplifier(alloc(distribute_forall_simplifier, m, p, st)); + if (smtp.m_bound_simplifier) s.add_simplifier(alloc(bound_simplifier, m, p, st)); if (smtp.m_eliminate_bounds) s.add_simplifier(alloc(elim_bounds_simplifier, m, p, st)); if (smtp.m_simplify_bit2int) s.add_simplifier(alloc(bit2int_simplifier, m, p, st)); if (smtp.m_bb_quantifiers) s.add_simplifier(alloc(bv::elim_simplifier, m, p, st)); diff --git a/src/solver/solver_preprocess.h b/src/solver/solver_preprocess.h index c0dfc42f3..6f610d59d 100644 --- a/src/solver/solver_preprocess.h +++ b/src/solver/solver_preprocess.h @@ -19,7 +19,7 @@ Author: #pragma once -#include "ast/simplifiers/seq_simplifier.h" +#include "ast/simplifiers/then_simplifier.h" -void init_preprocess(ast_manager& m, params_ref const& p, seq_simplifier& s, dependent_expr_state& st); +void init_preprocess(ast_manager& m, params_ref const& p, then_simplifier& s, dependent_expr_state& st); diff --git a/src/tactic/aig/aig_tactic.cpp b/src/tactic/aig/aig_tactic.cpp index 9c5390c16..8027e6484 100644 --- a/src/tactic/aig/aig_tactic.cpp +++ b/src/tactic/aig/aig_tactic.cpp @@ -103,6 +103,7 @@ public: new_f = conj; g->assert_expr(new_f); } + g->elim_true(); } void operator()(goal_ref const & g, goal_ref_buffer & result) override { diff --git a/src/tactic/arith/bound_simplifier_tactic.h b/src/tactic/arith/bound_simplifier_tactic.h deleted file mode 100644 index b61a10004..000000000 --- a/src/tactic/arith/bound_simplifier_tactic.h +++ /dev/null @@ -1,42 +0,0 @@ -/*++ -Copyright (c) 2023 Microsoft Corporation - -Module Name: - - bound_simplifier_tactic.h - -Author: - - Nikolaj Bjorner (nbjorner) 2023-01-22 - -Tactic Documentation: - -## Tactic bound-simplifier - -### Short Description - -Tactic for simplifying arithmetical expressions modulo bounds - -### Long Description - -The tactic is used to eliminate occurrences of modulus expressions when it is known that terms are within the bounds -of the modulus. - - ---*/ -#pragma once - -#include "util/params.h" -#include "tactic/tactic.h" -#include "tactic/dependent_expr_state_tactic.h" -#include "ast/simplifiers/bound_simplifier.h" - -inline tactic* mk_bound_simplifier_tactic(ast_manager& m, params_ref const& p = params_ref()) { - return alloc(dependent_expr_state_tactic, m, p, - [](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bound_simplifier, m, p, s); }); -} - -/* - ADD_TACTIC("bound-simplifier", "Simplify arithmetical expressions modulo bounds.", "mk_bound_simplifier_tactic(m, p)") - ADD_SIMPLIFIER("bound-simplifier", "Simplify arithmetical expressions modulo bounds.", "alloc(bound_simplifier, m, p, s)") -*/ diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index b8b4334f4..a372a1f8b 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -24,6 +24,7 @@ Notes: #include "ast/recfun_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/datatype_decl_plugin.h" +#include "ast/seq_decl_plugin.h" #include "tactic/core/collect_occs.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_ll_pp.h" @@ -44,6 +45,7 @@ class elim_uncnstr_tactic : public tactic { bv_util m_bv_util; array_util m_ar_util; datatype_util m_dt_util; + seq_util m_seq_util; app_ref_vector m_fresh_vars; obj_map m_cache; app_ref_vector m_cache_domain; @@ -60,6 +62,7 @@ class elim_uncnstr_tactic : public tactic { m_bv_util(m), m_ar_util(m), m_dt_util(m), + m_seq_util(m), m_fresh_vars(m), m_cache_domain(m), m_max_memory(max_memory), @@ -792,6 +795,44 @@ class elim_uncnstr_tactic : public tactic { } return nullptr; } + + // x ++ y -> z, x -> z, y -> eps + app * process_seq_app(func_decl * f, unsigned num, expr * const * args) { + switch (f->get_decl_kind()) { + case _OP_STRING_CONCAT: + case OP_SEQ_CONCAT: { + app * r = nullptr; + expr* x, *y; + if (uncnstr(args[0]) && num == 2 && + args[1]->get_ref_count() == 1 && + m_seq_util.str.is_concat(args[1], x, y) && + uncnstr(x)) { + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + + if (m_mc) { + add_def(args[0], r); + add_def(x, m_seq_util.str.mk_empty(args[0]->get_sort())); + } + r = m_seq_util.str.mk_concat(r, y); + return r; + + } + if (!uncnstr(num, args)) + return nullptr; + if (!mk_fresh_uncnstr_var_for(f, num, args, r)) + return r; + + expr_ref id(m_seq_util.str.mk_empty(args[0]->get_sort()), m()); + add_defs(num, args, r, id); + + return r; + } + default: + return nullptr; + } + } + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (num == 0) @@ -817,7 +858,8 @@ class elim_uncnstr_tactic : public tactic { u = process_array_app(f, num, args); else if (fid == m_dt_util.get_family_id()) u = process_datatype_app(f, num, args); - + else if (fid == m_seq_util.get_family_id()) + u = process_seq_app(f, num, args); if (u == nullptr) return BR_FAILED; diff --git a/src/tactic/core/eliminate_predicates_tactic.h b/src/tactic/core/eliminate_predicates_tactic.h index c2eb90742..51bb4a6c3 100644 --- a/src/tactic/core/eliminate_predicates_tactic.h +++ b/src/tactic/core/eliminate_predicates_tactic.h @@ -30,7 +30,7 @@ resolution. the predicate `p` occurs once positively. All negative occurrences of `p` are resolved against this positive occurrence. The result of resolution is a set of equalities between arguments to `p`. The function `f` is replaced by a partial solution. -``` +```z3 (declare-fun f (Int Int Int) Int) (declare-fun p (Int) Bool) (declare-const a Int) diff --git a/src/tactic/core/symmetry_reduce_tactic.cpp b/src/tactic/core/symmetry_reduce_tactic.cpp index e94e83679..9ad12d504 100644 --- a/src/tactic/core/symmetry_reduce_tactic.cpp +++ b/src/tactic/core/symmetry_reduce_tactic.cpp @@ -47,13 +47,13 @@ public: }; class ac_rewriter { - ast_manager& m_manager; + ast_manager& m; public: - ac_rewriter(ast_manager& m): m_manager(m) {} + ac_rewriter(ast_manager& m): m(m) {} br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if ((f->is_associative() && f->is_commutative()) || - m_manager.is_distinct(f)) { + m.is_distinct(f)) { ptr_buffer buffer; buffer.append(num_args, args); std::sort(buffer.begin(), buffer.end(), ast_lt_proc()); @@ -62,13 +62,13 @@ public: change = (args[i] != buffer[i]); } if (change) { - result = m().mk_app(f, num_args, buffer.begin()); + result = m.mk_app(f, num_args, buffer.begin()); return BR_DONE; } } else if (f->is_commutative() && num_args == 2 && args[0]->get_id() > args[1]->get_id()) { expr* args2[2] = { args[1], args[0] }; - result = m().mk_app(f, num_args, args2); + result = m.mk_app(f, num_args, args2); return BR_DONE; } return BR_FAILED; @@ -76,10 +76,8 @@ public: void mk_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { if (mk_app_core(f, num_args, args, result) == BR_FAILED) - result = m().mk_app(f, num_args, args); + result = m.mk_app(f, num_args, args); } -private: - ast_manager& m() const { return m_manager; } }; @@ -110,13 +108,12 @@ class symmetry_reduce_tactic::imp { typedef ptr_vector term_set; typedef obj_map app_map; typedef u_map > inv_app_map; - ast_manager& m_manager; + ast_manager& m; ac_rewriter_star m_rewriter; scoped_ptr m_replace; - ast_manager& m() const { return m_manager; } public: - imp(ast_manager& m) : m_manager(m), m_rewriter(m) { + imp(ast_manager& m) : m(m), m_rewriter(m) { m_replace = mk_default_expr_replacer(m, false); } @@ -124,10 +121,10 @@ public: void operator()(goal & g) { if (g.inconsistent()) - return; + return; tactic_report report("symmetry-reduce", g); vector > P; - expr_ref fml(m()); + expr_ref fml(m); to_formula(g, fml); app_map occs; compute_occurrences(fml, occs); @@ -149,11 +146,13 @@ public: app* c = select_const(consts, cts); if (!c) break; cts.push_back(c); - expr* mem = mk_member(t, cts); + expr_ref mem = mk_member(t, cts); g.assert_expr(mem); num_sym_break_preds++; - TRACE("symmetry_reduce", tout << "member predicate: " << mk_pp(mem, m()) << "\n";); - fml = m().mk_and(fml.get(), mem); + + TRACE("symmetry_reduce", tout << "member predicate: " << mem << "\n";); + + fml = m.mk_and(fml.get(), mem); normalize(fml); } } @@ -167,7 +166,7 @@ private: for (unsigned i = 0; i < g.size(); ++i) { conjs.push_back(g.form(i)); } - fml = m().mk_and(conjs.size(), conjs.data()); + fml = m.mk_and(conjs.size(), conjs.data()); normalize(fml); } @@ -184,28 +183,17 @@ private: // compute_siblings(fml, coloring); compute_inv_app(coloring, inv_color); - inv_app_map::iterator it = inv_color.begin(), end = inv_color.end(); - for (; it != end; ++it) { - if (it->m_value.size() < 2) { + for (auto const& [k, v] : inv_color) { + if (v.size() < 2) continue; - } - VERIFY(occs.find(it->m_value[0], num_occs)); - if (num_occs < 2) { + VERIFY(occs.find(v[0], num_occs)); + if (num_occs < 2) continue; - } - bool is_const = true; - for (unsigned j = 0; is_const && j < it->m_value.size(); ++j) { - is_const = it->m_value[j]->get_num_args() == 0; - } - if (!is_const) { + bool is_const = all_of(v, [&](app* a) { return a->get_num_args() == 0; }); + if (!is_const) continue; - } - P.push_back(it->m_value); - TRACE("symmetry_reduce", - for (unsigned i = 0; i < it->m_value.size(); ++i) { - tout << mk_pp(it->m_value[i], m()) << " "; - } - tout << "\n";); + P.push_back(v); + TRACE("symmetry_reduce", for (app * a : v) tout << mk_pp(a, m) << " "; tout << "\n";); } } @@ -426,14 +414,14 @@ private: tout << "Not symmetric: "; } for (unsigned i = 0; i < p.size(); ++i) { - tout << mk_pp(p[i], m()) << " "; + tout << mk_pp(p[i], m) << " "; } tout << "\n";); return result; } bool check_swap(expr* fml, app* t1, app* t2) { - expr_substitution sub(m()); + expr_substitution sub(m); sub.insert(t1, t2); sub.insert(t2, t1); m_replace->set_substitution(&sub); @@ -441,7 +429,7 @@ private: } bool check_cycle(expr* fml, permutation& p) { - expr_substitution sub(m()); + expr_substitution sub(m); for (unsigned i = 0; i + 1 < p.size(); ++i) { sub.insert(p[i], p[i+1]); } @@ -451,15 +439,15 @@ private: } bool check_substitution(expr* t) { - expr_ref r(m()); + expr_ref r(m); (*m_replace)(t, r); normalize(r); return t == r.get(); } void normalize(expr_ref& r) { - proof_ref pr(m()); - expr_ref result(m()); + proof_ref pr(m); + expr_ref result(m); m_rewriter(r.get(), result, pr); r = result; } @@ -473,7 +461,7 @@ private: while (!todo.empty()) { fml = todo.back(); todo.pop_back(); - if (m().is_and(fml)) { + if (m.is_and(fml)) { todo.append(to_app(fml)->get_num_args(), to_app(fml)->get_args()); } else if (is_range_restriction(fml, p, t)) { @@ -482,13 +470,13 @@ private: } } bool is_range_restriction(expr* form, term_set const& C, app*& t) { - if (!m().is_or(form)) return false; + if (!m.is_or(form)) return false; unsigned sz = to_app(form)->get_num_args(); t = nullptr; for (unsigned i = 0; i < sz; ++i) { expr* e = to_app(form)->get_arg(i); expr* e1, *e2; - if (!m().is_eq(e, e1, e2)) return false; + if (!m.is_eq(e, e1, e2)) return false; if (!is_app(e1) || !is_app(e2)) return false; app* a1 = to_app(e1), *a2 = to_app(e2); if (C.contains(a1) && (t == nullptr || t == a2)) { @@ -515,13 +503,9 @@ private: num_occurrences(app_map& occs): m_occs(occs) {} void operator()(app* n) { m_occs.insert_if_not_there(n, 0); - unsigned sz = n->get_num_args(); - for (unsigned i = 0; i < sz; ++i) { - expr* arg = n->get_arg(i); - if (is_app(arg)) { - m_occs.insert_if_not_there(to_app(arg), 0)++; - } - } + for (expr* arg : *n) + if (is_app(arg)) + m_occs.insert_if_not_there(to_app(arg), 0)++; } void operator()(quantifier * n) {} void operator()(var * n) {} @@ -540,7 +524,7 @@ private: unsigned weight = 0, weight1 = 0; VERIFY(occs.find(t, weight)); unsigned cts_delta = compute_cts_delta(t, cts, consts); - TRACE("symmetry_reduce", tout << mk_pp(t, m()) << " " << weight << " " << cts_delta << "\n";); + TRACE("symmetry_reduce", tout << mk_pp(t, m) << " " << weight << " " << cts_delta << "\n";); for (unsigned i = 1; i < T.size(); ++i) { app* t1 = T[i]; VERIFY(occs.find(t1, weight1)); @@ -548,7 +532,7 @@ private: continue; } unsigned cts_delta1 = compute_cts_delta(t1, cts, consts); - TRACE("symmetry_reduce", tout << mk_pp(t1, m()) << " " << weight1 << " " << cts_delta1 << "\n";); + TRACE("symmetry_reduce", tout << mk_pp(t1, m) << " " << weight1 << " " << cts_delta1 << "\n";); if ((t->get_num_args() == t1->get_num_args() && (weight1 > weight || cts_delta1 < cts_delta)) || t->get_num_args() > t1->get_num_args()) { cts_delta = cts_delta1; @@ -577,15 +561,15 @@ private: member_of mem(consts, cts); for_each_expr(mem, t); TRACE("symmetry_reduce", - tout << "Term: " << mk_pp(t, m()) << "\n"; + tout << "Term: " << mk_pp(t, m) << "\n"; tout << "Support set: "; for (unsigned i = 0; i < consts.size(); ++i) { - tout << mk_pp(consts[i], m()) << " "; + tout << mk_pp(consts[i], m) << " "; } tout << "\n"; tout << "Constants: "; for (unsigned i = 0; i < cts.size(); ++i) { - tout << mk_pp(cts[i], m()) << " "; + tout << mk_pp(cts[i], m) << " "; } tout << "\n"; ); @@ -606,15 +590,14 @@ private: app* select_const(term_set const& A, term_set const& B) { unsigned j; for (j = 0; j < A.size() && B.contains(A[j]); ++j); - return (j == A.size())?0:A[j]; + return (j == A.size())? nullptr:A[j]; } - expr* mk_member(app* t, term_set const& C) { - expr_ref_vector eqs(m()); - for (unsigned i = 0; i < C.size(); ++i) { - eqs.push_back(m().mk_eq(t, C[i])); - } - return mk_or(m(), eqs.size(), eqs.data()); + expr_ref mk_member(app* t, term_set const& C) { + expr_ref_vector eqs(m); + for (expr* e : C) + eqs.push_back(m.mk_eq(t, e)); + return mk_or(eqs); } }; diff --git a/src/tactic/core/tseitin_cnf_tactic.cpp b/src/tactic/core/tseitin_cnf_tactic.cpp index bd2f58b44..411b8aa6e 100644 --- a/src/tactic/core/tseitin_cnf_tactic.cpp +++ b/src/tactic/core/tseitin_cnf_tactic.cpp @@ -141,6 +141,7 @@ class tseitin_cnf_tactic : public tactic { sign = !sign; goto start; case OP_OR: + // case OP_AND: l = nullptr; m_cache.find(to_app(n), l); SASSERT(l != 0); @@ -187,6 +188,7 @@ class tseitin_cnf_tactic : public tactic { goto start; } case OP_OR: + // case OP_AND: visited = false; push_frame(to_app(n)); return; @@ -197,10 +199,10 @@ class tseitin_cnf_tactic : public tactic { push_frame(to_app(n)); } return; - case OP_AND: case OP_XOR: case OP_IMPLIES: case OP_DISTINCT: + case OP_AND: throw_op_not_handled(); default: return; @@ -617,6 +619,43 @@ class tseitin_cnf_tactic : public tactic { } return DONE; } + + mres match_and(app * t, bool first, bool root) { + if (!m.is_and(t)) + return NO; + if (first) { + bool visited = true; + for (expr* a : *t) + visit(a, visited); + if (!visited) + return CONT; + } + expr_ref_buffer lits(m); + expr_ref l(m), nl(m); + app_ref k(m), nk(m); + if (root) { + for (expr* arg : *t) { + get_lit(arg, false, l); + expr* lits[1] = { l }; + mk_clause(1, lits); + } + } + else { + k = mk_fresh(); + nk = m.mk_not(k); + cache_result(t, k); + + for (expr* arg : *t) { + get_lit(arg, false, l); + mk_clause(nk, l); + inv(l, nl); + lits.push_back(nl); + } + lits.push_back(k); + mk_clause(lits.size(), lits.data()); + } + return DONE; + } mres match_or(app * t, bool first, bool root) { if (!m.is_or(t)) @@ -778,6 +817,7 @@ class tseitin_cnf_tactic : public tactic { fr.m_first = false; TRY(match_or_3and); TRY(match_or); + TRY(match_and); TRY(match_iff3); // TRY(match_iff_or); TRY(match_iff); diff --git a/src/tactic/goal.cpp b/src/tactic/goal.cpp index b6fe76f6a..23e3ff969 100644 --- a/src/tactic/goal.cpp +++ b/src/tactic/goal.cpp @@ -692,28 +692,23 @@ bool goal::is_cnf() const { for (unsigned i = 0; i < size(); i++) { expr * f = form(i); if (m_manager.is_or(f)) { - for (expr* lit : *to_app(f)) { - if (!is_literal(lit)) { + for (expr* lit : *to_app(f)) + if (!is_literal(lit)) return false; - } - } - return true; } - if (!is_literal(f)) { + if (!is_literal(f)) return false; - } } return true; } bool goal::is_literal(expr* f) const { m_manager.is_not(f, f); - if (!is_app(f)) return false; - if (to_app(f)->get_family_id() == m_manager.get_basic_family_id()) { + if (!is_app(f)) + return false; + if (to_app(f)->get_family_id() == m_manager.get_basic_family_id()) for (expr* arg : *to_app(f)) - if (m_manager.is_bool(arg)) { + if (m_manager.is_bool(arg)) return false; - } - } return true; } diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index ba5841461..27de8b3af 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -29,6 +29,7 @@ add_executable(test-z3 datalog_parser.cpp ddnf.cpp diff_logic.cpp + distribution.cpp dl_context.cpp dl_product_relation.cpp dl_query.cpp diff --git a/src/test/api.cpp b/src/test/api.cpp index ccbbef6ea..560dd1121 100644 --- a/src/test/api.cpp +++ b/src/test/api.cpp @@ -88,7 +88,7 @@ void test_bvneg() { static bool cb_called = false; static void my_cb(Z3_context, Z3_error_code) { - cb_called = true; + cb_called = true; } static void test_mk_distinct() { @@ -101,6 +101,7 @@ static void test_mk_distinct() { Z3_ast args[] = { Z3_mk_int64(ctx, 0, bv8), Z3_mk_int64(ctx, 0, bv32) }; Z3_ast d = Z3_mk_distinct(ctx, 2, args); ENSURE(cb_called); + VERIFY(!d); Z3_del_config(cfg); Z3_del_context(ctx); diff --git a/src/test/bit_blaster.cpp b/src/test/bit_blaster.cpp index 8c4cd0903..fa623f767 100644 --- a/src/test/bit_blaster.cpp +++ b/src/test/bit_blaster.cpp @@ -28,13 +28,9 @@ void mk_bits(ast_manager & m, char const * prefix, unsigned sz, expr_ref_vector sort_ref b(m); b = m.mk_bool_sort(); for (unsigned i = 0; i < sz; ++i) { - char buffer[128]; -#ifdef _WINDOWS - sprintf_s(buffer, Z3_ARRAYSIZE(buffer), "%s%d.smt", prefix, i); -#else - sprintf(buffer, "%s%d.smt", prefix, i); -#endif - r.push_back(m.mk_const(symbol(buffer), b)); + std::stringstream ous; + ous << prefix << i << ".smt2"; + r.push_back(m.mk_const(symbol(ous.str()), b)); } } diff --git a/src/test/distribution.cpp b/src/test/distribution.cpp new file mode 100644 index 000000000..c67757737 --- /dev/null +++ b/src/test/distribution.cpp @@ -0,0 +1,45 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + distribution.cpp + +Abstract: + + Test distribution + +Author: + + Nikolaj Bjorner (nbjorner) 2023-04-13 + + +--*/ +#include "util/distribution.h" +#include + +static void tst1() { + distribution dist(1); + dist.push(1, 3); + dist.push(2, 1); + dist.push(3, 1); + dist.push(4, 1); + + unsigned counts[4] = { 0, 0, 0, 0 }; + for (unsigned i = 0; i < 1000; ++i) + counts[dist.choose()-1]++; + for (unsigned i = 1; i <= 4; ++i) + std::cout << "count " << i << ": " << counts[i-1] << "\n"; + + for (unsigned i = 0; i < 5; ++i) { + std::cout << "enum "; + for (auto j : dist) + std::cout << j << " "; + std::cout << "\n"; + } + +} + +void tst_distribution() { + tst1(); +} diff --git a/src/test/interval.cpp b/src/test/interval.cpp index f289871de..289265949 100644 --- a/src/test/interval.cpp +++ b/src/test/interval.cpp @@ -24,6 +24,7 @@ Revision History: #include "util/debug.h" #include "util/rlimit.h" #include +#include template class interval_manager; typedef im_default_config::interval interval; @@ -199,16 +200,12 @@ static void mk_random_interval(T & cfg, interval & a, unsigned magnitude) { #define BUFFER_SZ 256 static int g_problem_id = 0; -static char g_buffer[BUFFER_SZ]; -char const * get_next_file_name() { -#ifdef _WINDOWS - sprintf_s(g_buffer, BUFFER_SZ, "interval_lemma_%d.smt2", g_problem_id); -#else - sprintf(g_buffer, "interval_lemma_%d.smt2", g_problem_id); -#endif +std::string get_next_file_name() { + std::stringstream ous; + ous << "interval_lemma_" << g_problem_id << ".smt2"; g_problem_id++; - return g_buffer; + return ous.str(); } static void display_lemmas(unsynch_mpq_manager & nm, char const * result_term, diff --git a/src/test/lp/gomory_test.h b/src/test/lp/gomory_test.h index 890ff90e3..c64c01036 100644 --- a/src/test/lp/gomory_test.h +++ b/src/test/lp/gomory_test.h @@ -130,7 +130,7 @@ struct gomory_test { void report_conflict_from_gomory_cut(mpq &k) { - lp_assert(false); + UNREACHABLE(); } void adjust_term_and_k_for_some_ints_case_gomory(lar_term& t, mpq& k, mpq &lcm_den) { diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index c82cdd0a4..9120d64cf 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -33,16 +33,12 @@ #include #include #include "math/lp/lp_utils.h" -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/mps_reader.h" #include "test/lp/smt_reader.h" -#include "math/lp/binary_heap_priority_queue.h" #include "test/lp/argument_parser.h" #include "test/lp/test_file_reader.h" #include "math/lp/indexed_value.h" #include "math/lp/lar_solver.h" #include "math/lp/numeric_pair.h" -#include "math/lp/binary_heap_upair_queue.h" #include "util/stacked_value.h" #include "math/lp/u_set.h" #include "util/stopwatch.h" @@ -50,8 +46,6 @@ #include "test/lp/gomory_test.h" #include "math/lp/matrix.h" #include "math/lp/hnf.h" -#include "math/lp/square_sparse_matrix_def.h" -#include "math/lp/lu_def.h" #include "math/lp/general_matrix.h" #include "math/lp/lp_bound_propagator.h" #include "math/lp/nla_solver.h" @@ -59,6 +53,72 @@ #include "math/lp/cross_nested.h" #include "math/lp/int_cube.h" #include "math/lp/emonics.h" +#include "math/lp/static_matrix.h" + +bool my_white_space(const char & a) { + return a == ' ' || a == '\t'; +} +size_t number_of_whites(const std::string & s) { + size_t i = 0; + for(;i < s.size(); i++) + if (!my_white_space(s[i])) return i; + return i; +} +size_t number_of_whites_from_end(const std::string & s) { + size_t ret = 0; + for(int i = static_cast(s.size()) - 1;i >= 0; i--) + if (my_white_space(s[i])) ret++;else break; + + return ret; +} + + +std::string <rim(std::string &s) { + s.erase(0, number_of_whites(s)); + return s; +} + + + + + // trim from end +inline std::string &rtrim(std::string &s) { + // s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + s.erase(s.end() - number_of_whites_from_end(s), s.end()); + return s; +} + // trim from both ends +inline std::string &trim(std::string &s) { + return ltrim(rtrim(s)); +} + + +vector string_split(const std::string &source, const char *delimiter, bool keep_empty) { + vector results; + size_t prev = 0; + size_t next = 0; + while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) { + if (keep_empty || (next - prev != 0)) { + results.push_back(source.substr(prev, next - prev)); + } + prev = next + 1; + } + if (prev < source.size()) { + results.push_back(source.substr(prev)); + } + return results; +} + +vector split_and_trim(const std::string &line) { + auto split = string_split(line, " \t", false); + vector ret; + for (auto s : split) { + ret.push_back(trim(s)); + } + return ret; +} + + namespace nla { void test_horner(); void test_monics(); @@ -326,122 +386,6 @@ struct simple_column_namer:public column_namer }; -template -void test_matrix(square_sparse_matrix & a) { - auto m = a.dimension(); - - // copy a to b in the reversed order - square_sparse_matrix b(m, m); - std::cout << "copy b to a"<< std::endl; - for (int row = m - 1; row >= 0; row--) - for (int col = m - 1; col >= 0; col --) { - b(row, col) = (T const&) a(row, col); - } - - - std::cout << "zeroing b in the reverse order"<< std::endl; - for (int row = m - 1; row >= 0; row--) - for (int col = m - 1; col >= 0; col --) - b.set(row, col, T(0)); - - - - for (unsigned row = 0; row < m; row ++) - for (unsigned col = 0; col < m; col ++) - a.set(row, col, T(0)); - - - unsigned i = my_random() % m; - unsigned j = my_random() % m; - - auto t = T(1); - - a.set(i, j, t); - - lp_assert(a.get(i, j) == t); - - unsigned j1; - if (j < m - 1) { - j1 = m - 1; - a.set(i, j1, T(2)); - } -} - -void tst1() { - std::cout << "testing the minimal matrix with 1 row and 1 column" << std::endl; - square_sparse_matrix m0(1, 1); - m0.set(0, 0, 1); - // print_matrix(m0); - m0.set(0, 0, 0); - // print_matrix(m0); - test_matrix(m0); - - unsigned rows = 2; - square_sparse_matrix m(rows, rows); - std::cout << "setting m(0,1)=" << std::endl; - - m.set(0, 1, 11); - m.set(0, 0, 12); - - // print_matrix(m); - - test_matrix(m); - - square_sparse_matrix m1(2, 2); - m1.set(0, 0, 2); - m1.set(1, 0, 3); - // print_matrix(m1); - std::cout << " zeroing matrix 2 by 2" << std::endl; - m1.set(0, 0, 0); - m1.set(1, 0, 0); - // print_matrix(m1); - - test_matrix(m1); - - - std::cout << "printing zero matrix 3 by 1" << std::endl; - square_sparse_matrix m2(3, 3); - // print_matrix(m2); - - m2.set(0, 0, 1); - m2.set(2, 0, 2); - std::cout << "printing matrix 3 by 1 with a gap" << std::endl; - // print_matrix(m2); - - test_matrix(m2); - - square_sparse_matrix m10by9(10, 10); - m10by9.set(0, 1, 1); - - m10by9(0, 1) = 4; - - double test = m10by9(0, 1); - - std::cout << "got " << test << std::endl; - - - m10by9.set(0, 8, 8); - m10by9.set(3, 4, 7); - m10by9.set(3, 2, 5); - m10by9.set(3, 8, 99); - m10by9.set(3, 2, 6); - m10by9.set(1, 8, 9); - m10by9.set(4, 0, 40); - m10by9.set(0, 0, 10); - - std::cout << "printing matrix 10 by 9" << std::endl; - // print_matrix(m10by9); - - - test_matrix(m10by9); - std::cout <<"zeroing m10by9\n"; -#ifdef Z3DEBUG - for (unsigned int i = 0; i < m10by9.dimension(); i++) - for (unsigned int j = 0; j < m10by9.column_count(); j++) - m10by9.set(i, j, 0); -#endif - // print_matrix(m10by9); -} vector allocate_basis_heading(unsigned count) { // the rest of initialization will be handled by lu_QR vector basis_heading(count, -1); @@ -484,862 +428,8 @@ void change_basis(unsigned entering, unsigned leaving, vector& basis, nbasis[place_in_non_basis] = leaving; } - - -#ifdef Z3DEBUG -void test_small_lu(lp_settings & settings) { - std::cout << " test_small_lu" << std::endl; - static_matrix m(3, 6); - vector basis(3); - basis[0] = 0; - basis[1] = 1; - basis[2] = 3; - - m(0, 0) = 1; m(0, 2)= 3.9; m(2, 3) = 11; m(0, 5) = -3; - m(1, 1) = 4; m(1, 4) = 7; - m(2, 0) = 1.8; m(2, 2) = 5; m(2, 4) = 2; m(2, 5) = 8; - -#ifdef Z3DEBUG - print_matrix(m, std::cout); -#endif - vector heading = allocate_basis_heading(m.column_count()); - vector non_basic_columns; - init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - lu> l(m, basis, settings); - lp_assert(l.is_correct(basis)); - indexed_vector w(m.row_count()); - std::cout << "entering 2, leaving 0" << std::endl; - l.prepare_entering(2, w); // to init vector w - l.replace_column(0, w, heading[0]); - change_basis(2, 0, basis, non_basic_columns, heading); - // #ifdef Z3DEBUG - // std::cout << "we were factoring " << std::endl; - // print_matrix(get_B(l)); - // #endif - lp_assert(l.is_correct(basis)); - std::cout << "entering 4, leaving 3" << std::endl; - l.prepare_entering(4, w); // to init vector w - l.replace_column(0, w, heading[3]); - change_basis(4, 3, basis, non_basic_columns, heading); - std::cout << "we were factoring " << std::endl; -#ifdef Z3DEBUG - { - auto bl = get_B(l, basis); - print_matrix(&bl, std::cout); - } -#endif - lp_assert(l.is_correct(basis)); - - std::cout << "entering 5, leaving 1" << std::endl; - l.prepare_entering(5, w); // to init vector w - l.replace_column(0, w, heading[1]); - change_basis(5, 1, basis, non_basic_columns, heading); - std::cout << "we were factoring " << std::endl; -#ifdef Z3DEBUG - { - auto bl = get_B(l, basis); - print_matrix(&bl, std::cout); - } -#endif - lp_assert(l.is_correct(basis)); - std::cout << "entering 3, leaving 2" << std::endl; - l.prepare_entering(3, w); // to init vector w - l.replace_column(0, w, heading[2]); - change_basis(3, 2, basis, non_basic_columns, heading); - std::cout << "we were factoring " << std::endl; -#ifdef Z3DEBUG - { - auto bl = get_B(l, basis); - print_matrix(&bl, std::cout); - } -#endif - lp_assert(l.is_correct(basis)); - - m.add_row(); - m.add_column(); - m.add_row(); - m.add_column(); - for (unsigned i = 0; i < m.column_count(); i++) { - m(3, i) = i; - m(4, i) = i * i; // to make the rows linearly independent - } - unsigned j = m.column_count() ; - basis.push_back(j-2); - heading.push_back(basis.size() - 1); - basis.push_back(j-1); - heading.push_back(basis.size() - 1); - - auto columns_to_replace = l.get_set_of_columns_to_replace_for_add_last_rows(heading); - l.add_last_rows_to_B(heading, columns_to_replace); - lp_assert(l.is_correct(basis)); -} - -#endif - -void fill_long_row(square_sparse_matrix &m, int i) { - int n = m.dimension(); - for (int j = 0; j < n; j ++) { - m (i, (j + i) % n) = j * j; - } -} - -void fill_long_row(static_matrix &m, int i) { - int n = m.column_count(); - for (int j = 0; j < n; j ++) { - m (i, (j + i) % n) = j * j; - } -} - - -void fill_long_row_exp(square_sparse_matrix &m, int i) { - int n = m.dimension(); - - for (int j = 0; j < n; j ++) { - m(i, j) = my_random() % 20; - } -} - -void fill_long_row_exp(static_matrix &m, int i) { - int n = m.column_count(); - - for (int j = 0; j < n; j ++) { - m(i, j) = my_random() % 20; - } -} - -void fill_larger_square_sparse_matrix_exp(square_sparse_matrix & m){ - for ( unsigned i = 0; i < m.dimension(); i++ ) - fill_long_row_exp(m, i); -} - -void fill_larger_square_sparse_matrix_exp(static_matrix & m){ - for ( unsigned i = 0; i < m.row_count(); i++ ) - fill_long_row_exp(m, i); -} - - -void fill_larger_square_sparse_matrix(square_sparse_matrix & m){ - for ( unsigned i = 0; i < m.dimension(); i++ ) - fill_long_row(m, i); -} - -void fill_larger_square_sparse_matrix(static_matrix & m){ - for ( unsigned i = 0; i < m.row_count(); i++ ) - fill_long_row(m, i); -} - - int perm_id = 0; -#ifdef Z3DEBUG -void test_larger_lu_exp(lp_settings & settings) { - std::cout << " test_larger_lu_exp" << std::endl; - static_matrix m(6, 12); - vector basis(6); - basis[0] = 1; - basis[1] = 3; - basis[2] = 0; - basis[3] = 4; - basis[4] = 5; - basis[5] = 6; - - - fill_larger_square_sparse_matrix_exp(m); - // print_matrix(m); - vector heading = allocate_basis_heading(m.column_count()); - vector non_basic_columns; - init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - lu> l(m, basis, settings); - - dense_matrix left_side = l.get_left_side(basis); - dense_matrix right_side = l.get_right_side(); - lp_assert(left_side == right_side); - int leaving = 3; - int entering = 8; - for (unsigned i = 0; i < m.row_count(); i++) { - std::cout << static_cast(m(i, entering)) << std::endl; - } - - indexed_vector w(m.row_count()); - - l.prepare_entering(entering, w); - l.replace_column(0, w, heading[leaving]); - change_basis(entering, leaving, basis, non_basic_columns, heading); - lp_assert(l.is_correct(basis)); - - l.prepare_entering(11, w); // to init vector w - l.replace_column(0, w, heading[0]); - change_basis(11, 0, basis, non_basic_columns, heading); - lp_assert(l.is_correct(basis)); -} - -void test_larger_lu_with_holes(lp_settings & settings) { - std::cout << " test_larger_lu_with_holes" << std::endl; - static_matrix m(8, 9); - vector basis(8); - for (unsigned i = 0; i < m.row_count(); i++) { - basis[i] = i; - } - m(0, 0) = 1; m(0, 1) = 2; m(0, 2) = 3; m(0, 3) = 4; m(0, 4) = 5; m(0, 8) = 99; - /* */ m(1, 1) =- 6; m(1, 2) = 7; m(1, 3) = 8; m(1, 4) = 9; - /* */ m(2, 2) = 10; - /* */ m(3, 2) = 11; m(3, 3) = -12; - /* */ m(4, 2) = 13; m(4, 3) = 14; m(4, 4) = 15; - // the rest of the matrix is denser - m(5, 4) = 28; m(5, 5) = -18; m(5, 6) = 19; m(5, 7) = 25; - /* */ m(6, 5) = 20; m(6, 6) = -21; - /* */ m(7, 5) = 22; m(7, 6) = 23; m(7, 7) = 24; m(7, 8) = 88; - print_matrix(m, std::cout); - vector heading = allocate_basis_heading(m.column_count()); - vector non_basic_columns; - init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - lu> l(m, basis, settings); - std::cout << "printing factorization" << std::endl; - for (int i = l.tail_size() - 1; i >=0; i--) { - auto lp = l.get_lp_matrix(i); - lp->set_number_of_columns(m.row_count()); - lp->set_number_of_rows(m.row_count()); - print_matrix( *lp, std::cout); - } - - dense_matrix left_side = l.get_left_side(basis); - dense_matrix right_side = l.get_right_side(); - if (!(left_side == right_side)) { - std::cout << "different sides" << std::endl; - } - - indexed_vector w(m.row_count()); - l.prepare_entering(8, w); // to init vector w - l.replace_column(0, w, heading[0]); - change_basis(8, 0, basis, non_basic_columns, heading); - lp_assert(l.is_correct(basis)); -} - - -void test_larger_lu(lp_settings& settings) { - std::cout << " test_larger_lu" << std::endl; - static_matrix m(6, 12); - vector basis(6); - basis[0] = 1; - basis[1] = 3; - basis[2] = 0; - basis[3] = 4; - basis[4] = 5; - basis[5] = 6; - - - fill_larger_square_sparse_matrix(m); - print_matrix(m, std::cout); - - vector heading = allocate_basis_heading(m.column_count()); - vector non_basic_columns; - init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - auto l = lu> (m, basis, settings); - // std::cout << "printing factorization" << std::endl; - // for (int i = lu.tail_size() - 1; i >=0; i--) { - // auto lp = lu.get_lp_matrix(i); - // lp->set_number_of_columns(m.row_count()); - // lp->set_number_of_rows(m.row_count()); - // print_matrix(* lp); - // } - - dense_matrix left_side = l.get_left_side(basis); - dense_matrix right_side = l.get_right_side(); - if (!(left_side == right_side)) { - std::cout << "left side" << std::endl; - print_matrix(&left_side, std::cout); - std::cout << "right side" << std::endl; - print_matrix(&right_side, std::cout); - - std::cout << "different sides" << std::endl; - std::cout << "initial factorization is incorrect" << std::endl; - exit(1); - } - indexed_vector w(m.row_count()); - l.prepare_entering(9, w); // to init vector w - l.replace_column(0, w, heading[0]); - change_basis(9, 0, basis, non_basic_columns, heading); - lp_assert(l.is_correct(basis)); -} - - -void test_lu(lp_settings & settings) { - test_small_lu(settings); - test_larger_lu(settings); - test_larger_lu_with_holes(settings); - test_larger_lu_exp(settings); -} -#endif - - - - - - -void init_b(vector & b, square_sparse_matrix & m, vector& x) { - for (unsigned i = 0; i < m.dimension(); i++) { - b.push_back(m.dot_product_with_row(i, x)); - } -} - -void init_b(vector & b, static_matrix & m, vector & x) { - for (unsigned i = 0; i < m.row_count(); i++) { - b.push_back(m.dot_product_with_row(i, x)); - } -} - - -void test_lp_0() { - std::cout << " test_lp_0 " << std::endl; - static_matrix m_(3, 7); - m_(0, 0) = 3; m_(0, 1) = 2; m_(0, 2) = 1; m_(0, 3) = 2; m_(0, 4) = 1; - m_(1, 0) = 1; m_(1, 1) = 1; m_(1, 2) = 1; m_(1, 3) = 1; m_(1, 5) = 1; - m_(2, 0) = 4; m_(2, 1) = 3; m_(2, 2) = 3; m_(2, 3) = 4; m_(2, 6) = 1; - vector x_star(7); - x_star[0] = 225; x_star[1] = 117; x_star[2] = 420; - x_star[3] = x_star[4] = x_star[5] = x_star[6] = 0; - vector b; - init_b(b, m_, x_star); - vector basis(3); - basis[0] = 0; basis[1] = 1; basis[2] = 2; - vector costs(7); - costs[0] = 19; - costs[1] = 13; - costs[2] = 12; - costs[3] = 17; - costs[4] = 0; - costs[5] = 0; - costs[6] = 0; - - vector column_types(7, column_type::lower_bound); - vector upper_bound_values; - lp_settings settings; - simple_column_namer cn; - vector nbasis; - vector heading; - - lp_primal_core_solver lpsolver(m_, b, x_star, basis, nbasis, heading, costs, column_types, upper_bound_values, settings, cn); - - lpsolver.solve(); -} - -void test_lp_1() { - std::cout << " test_lp_1 " << std::endl; - static_matrix m(4, 7); - m(0, 0) = 1; m(0, 1) = 3; m(0, 2) = 1; m(0, 3) = 1; - m(1, 0) = -1; m(1, 2) = 3; m(1, 4) = 1; - m(2, 0) = 2; m(2, 1) = -1; m(2, 2) = 2; m(2, 5) = 1; - m(3, 0) = 2; m(3, 1) = 3; m(3, 2) = -1; m(3, 6) = 1; -#ifdef Z3DEBUG - print_matrix(m, std::cout); -#endif - vector x_star(7); - x_star[0] = 0; x_star[1] = 0; x_star[2] = 0; - x_star[3] = 3; x_star[4] = 2; x_star[5] = 4; x_star[6] = 2; - - vector basis(4); - basis[0] = 3; basis[1] = 4; basis[2] = 5; basis[3] = 6; - - vector b; - b.push_back(3); - b.push_back(2); - b.push_back(4); - b.push_back(2); - - vector costs(7); - costs[0] = 5; - costs[1] = 5; - costs[2] = 3; - costs[3] = 0; - costs[4] = 0; - costs[5] = 0; - costs[6] = 0; - - - - vector column_types(7, column_type::lower_bound); - vector upper_bound_values; - - std::cout << "calling lp\n"; - lp_settings settings; - simple_column_namer cn; - - vector nbasis; - vector heading; - - lp_primal_core_solver lpsolver(m, b, - x_star, - basis, - nbasis, heading, - costs, - column_types, upper_bound_values, settings, cn); - - lpsolver.solve(); -} - - -void test_lp_primal_core_solver() { - test_lp_0(); - test_lp_1(); -} - - -#ifdef Z3DEBUG -template -void test_swap_rows_with_permutation(square_sparse_matrix& m){ - std::cout << "testing swaps" << std::endl; - unsigned dim = m.row_count(); - dense_matrix original(&m); - permutation_matrix q(dim); - print_matrix(m, std::cout); - lp_assert(original == q * m); - for (int i = 0; i < 100; i++) { - unsigned row1 = my_random() % dim; - unsigned row2 = my_random() % dim; - if (row1 == row2) continue; - std::cout << "swap " << row1 << " " << row2 << std::endl; - m.swap_rows(row1, row2); - q.transpose_from_left(row1, row2); - lp_assert(original == q * m); - print_matrix(m, std::cout); - std::cout << std::endl; - } -} -#endif -template -void fill_matrix(square_sparse_matrix& m); // forward definition -#ifdef Z3DEBUG -template -void test_swap_cols_with_permutation(square_sparse_matrix& m){ - std::cout << "testing swaps" << std::endl; - unsigned dim = m.row_count(); - dense_matrix original(&m); - permutation_matrix q(dim); - print_matrix(m, std::cout); - lp_assert(original == q * m); - for (int i = 0; i < 100; i++) { - unsigned row1 = my_random() % dim; - unsigned row2 = my_random() % dim; - if (row1 == row2) continue; - std::cout << "swap " << row1 << " " << row2 << std::endl; - m.swap_rows(row1, row2); - q.transpose_from_right(row1, row2); - lp_assert(original == q * m); - print_matrix(m, std::cout); - std::cout << std::endl; - } -} - - -template -void test_swap_rows(square_sparse_matrix& m, unsigned i0, unsigned i1){ - std::cout << "test_swap_rows(" << i0 << "," << i1 << ")" << std::endl; - square_sparse_matrix mcopy(m.dimension(), 0); - for (unsigned i = 0; i < m.dimension(); i++) - for (unsigned j = 0; j < m.dimension(); j++) { - mcopy(i, j)= m(i, j); - } - std::cout << "swapping rows "<< i0 << "," << i1 << std::endl; - m.swap_rows(i0, i1); - - for (unsigned j = 0; j < m.dimension(); j++) { - lp_assert(mcopy(i0, j) == m(i1, j)); - lp_assert(mcopy(i1, j) == m(i0, j)); - } -} -template -void test_swap_columns(square_sparse_matrix& m, unsigned i0, unsigned i1){ - std::cout << "test_swap_columns(" << i0 << "," << i1 << ")" << std::endl; - square_sparse_matrix mcopy(m.dimension(), 0); // the second argument does not matter - for (unsigned i = 0; i < m.dimension(); i++) - for (unsigned j = 0; j < m.dimension(); j++) { - mcopy(i, j)= m(i, j); - } - m.swap_columns(i0, i1); - - for (unsigned j = 0; j < m.dimension(); j++) { - lp_assert(mcopy(j, i0) == m(j, i1)); - lp_assert(mcopy(j, i1) == m(j, i0)); - } - - for (unsigned i = 0; i < m.dimension(); i++) { - if (i == i0 || i == i1) - continue; - for (unsigned j = 0; j < m.dimension(); j++) { - lp_assert(mcopy(j, i)== m(j, i)); - } - } -} -#endif - -template -void fill_matrix(square_sparse_matrix& m){ - int v = 0; - for (int i = m.dimension() - 1; i >= 0; i--) { - for (int j = m.dimension() - 1; j >=0; j--){ - m(i, j) = v++; - } - } -} - -void test_pivot_like_swaps_and_pivot(){ - square_sparse_matrix m(10, 10); - fill_matrix(m); - // print_matrix(m); - // pivot at 2,7 - m.swap_columns(0, 7); - // print_matrix(m); - m.swap_rows(2, 0); - // print_matrix(m); - for (unsigned i = 1; i < m.dimension(); i++) { - m(i, 0) = 0; - } - // print_matrix(m); - - // say pivot at 3,4 - m.swap_columns(1, 4); - // print_matrix(m); - m.swap_rows(1, 3); - // print_matrix(m); - - vector row; - double alpha = 2.33; - unsigned pivot_row = 1; - unsigned target_row = 2; - unsigned pivot_row_0 = 3; - double beta = 3.1; - m(target_row, 3) = 0; - m(target_row, 5) = 0; - m(pivot_row, 6) = 0; -#ifdef Z3DEBUG - print_matrix(m, std::cout); -#endif - - for (unsigned j = 0; j < m.dimension(); j++) { - row.push_back(m(target_row, j) + alpha * m(pivot_row, j) + beta * m(pivot_row_0, j)); - } - - for (auto & t : row) { - std::cout << t << ","; - } - std::cout << std::endl; - lp_settings settings; - m.pivot_row_to_row(pivot_row, alpha, target_row, settings); - m.pivot_row_to_row(pivot_row_0, beta, target_row, settings); - // print_matrix(m); - for (unsigned j = 0; j < m.dimension(); j++) { - lp_assert(abs(row[j] - m(target_row, j)) < 0.00000001); - } -} - -#ifdef Z3DEBUG -void test_swap_rows() { - square_sparse_matrix m(10, 10); - fill_matrix(m); - // print_matrix(m); - test_swap_rows(m, 3, 5); - - test_swap_rows(m, 1, 3); - - - test_swap_rows(m, 1, 3); - - test_swap_rows(m, 1, 7); - - test_swap_rows(m, 3, 7); - - test_swap_rows(m, 0, 7); - - m(0, 4) = 1; - // print_matrix(m); - test_swap_rows(m, 0, 7); - - // go over some corner cases - square_sparse_matrix m0(2, 2); - test_swap_rows(m0, 0, 1); - m0(0, 0) = 3; - test_swap_rows(m0, 0, 1); - m0(1, 0) = 3; - test_swap_rows(m0, 0, 1); - - - square_sparse_matrix m1(10, 10); - test_swap_rows(m1, 0, 1); - m1(0, 0) = 3; - test_swap_rows(m1, 0, 1); - m1(1, 0) = 3; - m1(0, 3) = 5; - m1(1, 3) = 4; - m1(1, 8) = 8; - m1(1, 9) = 8; - - test_swap_rows(m1, 0, 1); - - square_sparse_matrix m2(3, 3); - test_swap_rows(m2, 0, 1); - m2(0, 0) = 3; - test_swap_rows(m2, 0, 1); - m2(2, 0) = 3; - test_swap_rows(m2, 0, 2); -} - -void fill_uniformly(square_sparse_matrix & m, unsigned dim) { - int v = 0; - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - m(i, j) = v++; - } - } -} - -void fill_uniformly(dense_matrix & m, unsigned dim) { - int v = 0; - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - m.set_elem(i, j, v++); - } - } -} - -void square_sparse_matrix_with_permutations_test() { - unsigned dim = 4; - square_sparse_matrix m(dim, dim); - fill_uniformly(m, dim); - dense_matrix dm(dim, dim); - fill_uniformly(dm, dim); - dense_matrix dm0(dim, dim); - fill_uniformly(dm0, dim); - permutation_matrix q0(dim); - q0[0] = 1; - q0[1] = 0; - q0[2] = 3; - q0[3] = 2; - permutation_matrix q1(dim); - q1[0] = 1; - q1[1] = 2; - q1[2] = 3; - q1[3] = 0; - permutation_matrix p0(dim); - p0[0] = 1; - p0[1] = 0; - p0[2] = 3; - p0[3] = 2; - permutation_matrix p1(dim); - p1[0] = 1; - p1[1] = 2; - p1[2] = 3; - p1[3] = 0; - - m.multiply_from_left(q0); - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - lp_assert(m(i, j) == dm0.get_elem(q0[i], j)); - } - } - - auto q0_dm = q0 * dm; - lp_assert(m == q0_dm); - - m.multiply_from_left(q1); - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], j)); - } - } - - - auto q1_q0_dm = q1 * q0_dm; - - lp_assert(m == q1_q0_dm); - - m.multiply_from_right(p0); - - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p0[j])); - } - } - - auto q1_q0_dm_p0 = q1_q0_dm * p0; - - lp_assert(m == q1_q0_dm_p0); - - m.multiply_from_right(p1); - - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p1[p0[j]])); - } - } - - auto q1_q0_dm_p0_p1 = q1_q0_dm_p0 * p1; - lp_assert(m == q1_q0_dm_p0_p1); - - m.multiply_from_right(p1); - for (unsigned i = 0; i < dim; i++) { - for (unsigned j = 0; j < dim; j++) { - lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p1[p1[p0[j]]])); - } - } - auto q1_q0_dm_p0_p1_p1 = q1_q0_dm_p0_p1 * p1; - - lp_assert(m == q1_q0_dm_p0_p1_p1); -} - -void test_swap_columns() { - square_sparse_matrix m(10, 10); - fill_matrix(m); - // print_matrix(m); - - test_swap_columns(m, 3, 5); - - test_swap_columns(m, 1, 3); - - test_swap_columns(m, 1, 3); - - // print_matrix(m); - test_swap_columns(m, 1, 7); - - test_swap_columns(m, 3, 7); - - test_swap_columns(m, 0, 7); - - test_swap_columns(m, 0, 7); - - // go over some corner cases - square_sparse_matrix m0(2, 2); - test_swap_columns(m0, 0, 1); - m0(0, 0) = 3; - test_swap_columns(m0, 0, 1); - m0(0, 1) = 3; - test_swap_columns(m0, 0, 1); - - - square_sparse_matrix m1(10, 10); - test_swap_columns(m1, 0, 1); - m1(0, 0) = 3; - test_swap_columns(m1, 0, 1); - m1(0, 1) = 3; - m1(3, 0) = 5; - m1(3, 1) = 4; - m1(8, 1) = 8; - m1(9, 1) = 8; - - test_swap_columns(m1, 0, 1); - - square_sparse_matrix m2(3, 3); - test_swap_columns(m2, 0, 1); - m2(0, 0) = 3; - test_swap_columns(m2, 0, 1); - m2(0, 2) = 3; - test_swap_columns(m2, 0, 2); -} - - - -void test_swap_operations() { - test_swap_rows(); - test_swap_columns(); -} - -void test_dense_matrix() { - dense_matrix d(3, 2); - d.set_elem(0, 0, 1); - d.set_elem(1, 1, 2); - d.set_elem(2, 0, 3); - // print_matrix(d); - - dense_matrix unit(2, 2); - d.set_elem(0, 0, 1); - d.set_elem(1, 1, 1); - - dense_matrix c = d * unit; - - // print_matrix(d); - - dense_matrix perm(3, 3); - perm.set_elem(0, 1, 1); - perm.set_elem(1, 0, 1); - perm.set_elem(2, 2, 1); - auto c1 = perm * d; - // print_matrix(c1); - - - dense_matrix p2(2, 2); - p2.set_elem(0, 1, 1); - p2.set_elem(1, 0, 1); - auto c2 = d * p2; -} -#endif - - - -vector> vector_of_permutations() { - vector> ret; - { - permutation_matrix p0(5); - p0[0] = 1; p0[1] = 2; p0[2] = 3; p0[3] = 4; - p0[4] = 0; - ret.push_back(p0); - } - { - permutation_matrix p0(5); - p0[0] = 2; p0[1] = 0; p0[2] = 1; p0[3] = 4; - p0[4] = 3; - ret.push_back(p0); - } - return ret; -} - -void test_apply_reverse_from_right_to_perm(permutation_matrix & l) { - permutation_matrix p(5); - p[0] = 4; p[1] = 2; p[2] = 0; p[3] = 3; - p[4] = 1; - - permutation_matrix pclone(5); - pclone[0] = 4; pclone[1] = 2; pclone[2] = 0; pclone[3] = 3; - pclone[4] = 1; - - p.multiply_by_reverse_from_right(l); -#ifdef Z3DEBUG - auto rev = l.get_inverse(); - auto rs = pclone * rev; - lp_assert(p == rs) -#endif - } - -void test_apply_reverse_from_right() { - auto vec = vector_of_permutations(); - for (unsigned i = 0; i < vec.size(); i++) { - test_apply_reverse_from_right_to_perm(vec[i]); - } -} - -void test_permutations() { - std::cout << "test permutations" << std::endl; - test_apply_reverse_from_right(); - vector v; v.resize(5, 0); - v[1] = 1; - v[3] = 3; - permutation_matrix p(5); - p[0] = 4; p[1] = 2; p[2] = 0; p[3] = 3; - p[4] = 1; - - indexed_vector vi(5); - vi.set_value(1, 1); - vi.set_value(3, 3); - - p.apply_reverse_from_right_to_T(v); - p.apply_reverse_from_right_to_T(vi); - lp_assert(vectors_are_equal(v, vi.m_data)); - lp_assert(vi.is_OK()); -} - -void lp_solver_test() { - // lp_revised_solver lp_revised; - // lp_revised.get_minimal_solution(); -} - bool get_int_from_args_parser(const char * option, argument_parser & args_parser, unsigned & n) { std::string s = args_parser.get_option_value(option); if (!s.empty()) { @@ -1359,320 +449,15 @@ bool get_double_from_args_parser(const char * option, argument_parser & args_par } -void update_settings(argument_parser & args_parser, lp_settings& settings) { - unsigned n; - settings.m_simplex_strategy = simplex_strategy_enum::lu; - if (get_int_from_args_parser("--rep_frq", args_parser, n)) - settings.report_frequency = n; - else - settings.report_frequency = args_parser.option_is_used("--mpq")? 80: 1000; - - settings.print_statistics = true; - - if (get_int_from_args_parser("--percent_for_enter", args_parser, n)) - settings.percent_of_entering_to_check = n; - if (get_int_from_args_parser("--partial_pivot", args_parser, n)) { - std::cout << "setting partial pivot constant to " << n << std::endl; - settings.c_partial_pivoting = n; - } - if (get_int_from_args_parser("--density", args_parser, n)) { - double density = static_cast(n) / 100.0; - std::cout << "setting density to " << density << std::endl; - settings.density_threshold = density; - } - if (get_int_from_args_parser("--maxng", args_parser, n)) - settings.max_number_of_iterations_with_no_improvements = n; - double d; - if (get_double_from_args_parser("--harris_toler", args_parser, d)) { - std::cout << "setting harris_feasibility_tolerance to " << d << std::endl; - settings.harris_feasibility_tolerance = d; - } - if (get_int_from_args_parser("--random_seed", args_parser, n)) { - settings.set_random_seed(n); - } - if (get_int_from_args_parser("--simplex_strategy", args_parser, n)) { - settings.set_simplex_strategy(static_cast(n)); - } -} - -template -void setup_solver(unsigned time_limit, bool look_for_min, argument_parser & args_parser, lp_solver * solver) { - if (time_limit > 0) - solver->set_time_limit(time_limit); - - if (look_for_min) - solver->flip_costs(); - - update_settings(args_parser, solver->settings()); -} - -bool values_are_one_percent_close(double a, double b); - -void print_x(mps_reader & reader, lp_solver * solver) { - for (const auto & name : reader.column_names()) { - std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; - } - std::cout << std::endl; -} - -void compare_solutions(mps_reader & reader, lp_solver * solver, lp_solver * solver0) { - for (const auto & name : reader.column_names()) { - double a = solver->get_column_value_by_name(name); - double b = solver0->get_column_value_by_name(name); - if (!values_are_one_percent_close(a, b)) { - std::cout << "different values for " << name << ":" << a << " and " << b << std::endl; - } - } -} -void solve_mps_double(std::string file_name, bool look_for_min, unsigned time_limit, bool dual, bool compare_with_primal, argument_parser & args_parser) { - mps_reader reader(file_name); - reader.read(); - if (!reader.is_ok()) { - std::cout << "cannot process " << file_name << std::endl; - return; - } - - lp_solver * solver = reader.create_solver(dual); - setup_solver(time_limit, look_for_min, args_parser, solver); - stopwatch sw; - sw.start(); - if (dual) { - std::cout << "solving for dual" << std::endl; - } - solver->find_maximal_solution(); - sw.stop(); - double span = sw.get_seconds(); - std::cout << "Status: " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp_status::OPTIMAL) { - if (reader.column_names().size() < 20) { - print_x(reader, solver); - } - double cost = solver->get_current_cost(); - if (look_for_min) { - cost = -cost; - } - std::cout << "cost = " << cost << std::endl; - } - std::cout << "processed in " << span / 1000.0 << " seconds, running for " << solver->m_total_iterations << " iterations" << " one iter for " << (double)span/solver->m_total_iterations << " ms" << std::endl; - if (compare_with_primal) { - auto * primal_solver = reader.create_solver(false); - setup_solver(time_limit, look_for_min, args_parser, primal_solver); - primal_solver->find_maximal_solution(); - if (solver->get_status() != primal_solver->get_status()) { - std::cout << "statuses are different: dual " << lp_status_to_string(solver->get_status()) << " primal = " << lp_status_to_string(primal_solver->get_status()) << std::endl; - } else { - if (solver->get_status() == lp_status::OPTIMAL) { - double cost = solver->get_current_cost(); - if (look_for_min) { - cost = -cost; - } - double primal_cost = primal_solver->get_current_cost(); - if (look_for_min) { - primal_cost = -primal_cost; - } - std::cout << "primal cost = " << primal_cost << std::endl; - if (!values_are_one_percent_close(cost, primal_cost)) { - compare_solutions(reader, primal_solver, solver); - print_x(reader, primal_solver); - std::cout << "dual cost is " << cost << ", but primal cost is " << primal_cost << std::endl; - lp_assert(false); - } - } - } - delete primal_solver; - } - delete solver; -} -void solve_mps_rational(std::string file_name, bool look_for_min, unsigned time_limit, bool dual, argument_parser & args_parser) { - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - auto * solver = reader.create_solver(dual); - setup_solver(time_limit, look_for_min, args_parser, solver); - stopwatch sw; - sw.start(); - solver->find_maximal_solution(); - std::cout << "Status: " << lp_status_to_string(solver->get_status()) << std::endl; - - if (solver->get_status() == lp_status::OPTIMAL) { - // for (auto name: reader.column_names()) { - // std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; - // } - lp::mpq cost = solver->get_current_cost(); - if (look_for_min) { - cost = -cost; - } - std::cout << "cost = " << cost.get_double() << std::endl; - } - std::cout << "processed in " << sw.get_current_seconds() / 1000.0 << " seconds, running for " << solver->m_total_iterations << " iterations" << std::endl; - delete solver; - } else { - std::cout << "cannot process " << file_name << std::endl; - } -} void get_time_limit_and_max_iters_from_parser(argument_parser & args_parser, unsigned & time_limit); // forward definition -void solve_mps(std::string file_name, bool look_for_min, unsigned time_limit, bool solve_for_rational, bool dual, bool compare_with_primal, argument_parser & args_parser) { - if (!solve_for_rational) { - std::cout << "solving " << file_name << std::endl; - solve_mps_double(file_name, look_for_min, time_limit, dual, compare_with_primal, args_parser); - } - else { - std::cout << "solving " << file_name << " in rationals " << std::endl; - solve_mps_rational(file_name, look_for_min, time_limit, dual, args_parser); - } -} - -void solve_mps(std::string file_name, argument_parser & args_parser) { - bool look_for_min = args_parser.option_is_used("--min"); - unsigned time_limit; - bool solve_for_rational = args_parser.option_is_used("--mpq"); - bool dual = args_parser.option_is_used("--dual"); - bool compare_with_primal = args_parser.option_is_used("--compare_with_primal"); - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - solve_mps(file_name, look_for_min, time_limit, solve_for_rational, dual, compare_with_primal, args_parser); -} - -void solve_mps_in_rational(std::string file_name, bool dual, argument_parser & /*args_parser*/) { - std::cout << "solving " << file_name << std::endl; - - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - auto * solver = reader.create_solver(dual); - solver->find_maximal_solution(); - std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp_status::OPTIMAL) { - if (reader.column_names().size() < 20) { - for (const auto & name : reader.column_names()) { - std::cout << name << "=" << solver->get_column_value_by_name(name).get_double() << ' '; - } - } - std::cout << std::endl << "cost = " << numeric_traits::get_double(solver->get_current_cost()) << std::endl; - } - delete solver; - } else { - std::cout << "cannot process " << file_name << std::endl; - } -} - -void test_upair_queue() { - int n = 10; - binary_heap_upair_queue q(2); - std::unordered_map m; - for (int k = 0; k < 100; k++) { - int i = my_random()%n; - int j = my_random()%n; - q.enqueue(i, j, my_random()%n); - } - - q.remove(5, 5); - - while (!q.is_empty()) { - unsigned i, j; - q.dequeue(i, j); - } -} - -void test_binary_priority_queue() { - std::cout << "testing binary_heap_priority_queue..."; - auto q = binary_heap_priority_queue(10); - q.enqueue(2, 2); - q.enqueue(1, 1); - q.enqueue(9, 9); - q.enqueue(8, 8); - q.enqueue(5, 25); - q.enqueue(3, 3); - q.enqueue(4, 4); - q.enqueue(7, 30); - q.enqueue(6, 6); - q.enqueue(0, 0); - q.enqueue(5, 5); - q.enqueue(7, 7); - - for (unsigned i = 0; i < 10; i++) { - unsigned de = q.dequeue(); - lp_assert(i == de); - std::cout << de << std::endl; - } - q.enqueue(2, 2); - q.enqueue(1, 1); - q.enqueue(9, 9); - q.enqueue(8, 8); - q.enqueue(5, 5); - q.enqueue(3, 3); - q.enqueue(4, 4); - q.enqueue(7, 2); - q.enqueue(0, 1); - q.enqueue(6, 6); - q.enqueue(7, 7); - q.enqueue(33, 1000); - q.enqueue(20, 0); - q.dequeue(); - q.remove(33); - q.enqueue(0, 0); -#ifdef Z3DEBUG - unsigned t = 0; - while (q.size() > 0) { - unsigned d =q.dequeue(); - lp_assert(t++ == d); - std::cout << d << std::endl; - } -#endif - test_upair_queue(); - std::cout << " done" << std::endl; -} - -bool solution_is_feasible(std::string file_name, const std::unordered_map & solution) { - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - lp_primal_simplex * solver = static_cast *>(reader.create_solver(false)); - return solver->solution_is_feasible(solution); - } - return false; -} -void solve_mps_with_known_solution(std::string file_name, std::unordered_map * solution, lp_status status, bool dual) { - std::cout << "solving " << file_name << std::endl; - mps_reader reader(file_name); - reader.read(); - if (reader.is_ok()) { - auto * solver = reader.create_solver(dual); - solver->find_maximal_solution(); - std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (status != solver->get_status()){ - std::cout << "status should be " << lp_status_to_string(status) << std::endl; - lp_assert(status == solver->get_status()); - throw "status is wrong"; - } - if (solver->get_status() == lp_status::OPTIMAL) { - std::cout << "cost = " << solver->get_current_cost() << std::endl; - if (solution != nullptr) { - for (auto it : *solution) { - if (fabs(it.second - solver->get_column_value_by_name(it.first)) >= 0.000001) { - std::cout << "expected:" << it.first << "=" << - it.second <<", got " << solver->get_column_value_by_name(it.first) << std::endl; - } - lp_assert(fabs(it.second - solver->get_column_value_by_name(it.first)) < 0.000001); - } - } - if (reader.column_names().size() < 20) { - for (const auto & name : reader.column_names()) { - std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; - } - std::cout << std::endl; - } - } - delete solver; - } else { - std::cout << "cannot process " << file_name << std::endl; - } -} + + int get_random_rows() { return 5 + my_random() % 2; @@ -1686,55 +471,6 @@ int get_random_int() { return -1 + my_random() % 2; // (1.0 + RAND_MAX); } -void add_random_row(lp_primal_simplex * solver, int cols, int row) { - solver->add_constraint(lp_relation::Greater_or_equal, 1, row); - for (int i = 0; i < cols; i++) { - solver->set_row_column_coefficient(row, i, get_random_int()); - } -} - -void add_random_cost(lp_primal_simplex * solver, int cols) { - for (int i = 0; i < cols; i++) { - solver->set_cost_for_column(i, get_random_int()); - } -} - -lp_primal_simplex * generate_random_solver() { - int rows = get_random_rows(); - int cols = get_random_columns(); - auto * solver = new lp_primal_simplex(); - for (int i = 0; i < rows; i++) { - add_random_row(solver, cols, i); - } - add_random_cost(solver, cols); - return solver; -} - - - -void random_test_on_i(unsigned i) { - if (i % 1000 == 0) { - std::cout << "."; - } - srand(i); - auto *solver = generate_random_solver(); - solver->find_maximal_solution(); - // std::cout << lp_status_to_string(solver->get_status()) << std::endl; - delete solver; -} - -void random_test() { - for (unsigned i = 0; i < std::numeric_limits::max(); i++) { - try { - random_test_on_i(i); - } - catch (const char * error) { - std::cout << "i = " << i << ", throwing at ' " << error << "'" << std::endl; - break; - } - } -} - #ifndef _WINDOWS void fill_file_names(vector &file_names, std::set & minimums) { char *home_dir = getenv("HOME"); @@ -1896,140 +632,9 @@ void find_dir_and_file_name(std::string a, std::string & dir, std::string& fn) { // std::cout << "fn = " << fn << std::endl; } -void process_test_file(std::string test_dir, std::string test_file_name, argument_parser & args_parser, std::string out_dir, unsigned max_iters, unsigned time_limit, unsigned & successes, unsigned & failures, unsigned & inconclusives); -void solve_some_mps(argument_parser & args_parser) { - unsigned max_iters = UINT_MAX, time_limit = UINT_MAX; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - unsigned successes = 0; - unsigned failures = 0; - unsigned inconclusives = 0; - std::set minimums; - vector file_names; - fill_file_names(file_names, minimums); - bool solve_for_rational = args_parser.option_is_used("--mpq"); - bool dual = args_parser.option_is_used("--dual"); - bool compare_with_primal = args_parser.option_is_used("--compare_with_primal"); - bool compare_with_glpk = args_parser.option_is_used("--compare_with_glpk"); - if (compare_with_glpk) { - std::string out_dir = args_parser.get_option_value("--out_dir"); - if (out_dir.size() == 0) { - out_dir = "/tmp/test"; - } - test_out_dir(out_dir); - for (auto& a : file_names) { - try { - std::string file_dir; - std::string file_name; - find_dir_and_file_name(a, file_dir, file_name); - process_test_file(file_dir, file_name, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); - } - catch(const char *s){ - std::cout<< "exception: "<< s << std::endl; - } - } - std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; - return; - } - if (!solve_for_rational) { - solve_mps(file_names[6], false, time_limit, false, dual, compare_with_primal, args_parser); - solve_mps_with_known_solution(file_names[3], nullptr, lp_status::INFEASIBLE, dual); // chvatal: 135(d) - std::unordered_map sol; - sol["X1"] = 0; - sol["X2"] = 6; - sol["X3"] = 0; - sol["X4"] = 15; - sol["X5"] = 2; - sol["X6"] = 1; - sol["X7"] = 1; - sol["X8"] = 0; - solve_mps_with_known_solution(file_names[9], &sol, lp_status::OPTIMAL, dual); - solve_mps_with_known_solution(file_names[0], &sol, lp_status::OPTIMAL, dual); - sol.clear(); - sol["X1"] = 25.0/14.0; - // sol["X2"] = 0; - // sol["X3"] = 0; - // sol["X4"] = 0; - // sol["X5"] = 0; - // sol["X6"] = 0; - // sol["X7"] = 9.0/14.0; - solve_mps_with_known_solution(file_names[5], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) - solve_mps_with_known_solution(file_names[4], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) - solve_mps_with_known_solution(file_names[2], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(c) - solve_mps_with_known_solution(file_names[1], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(b) - solve_mps(file_names[8], false, time_limit, false, dual, compare_with_primal, args_parser); - // return; - for (auto& s : file_names) { - try { - solve_mps(s, minimums.find(s) != minimums.end(), time_limit, false, dual, compare_with_primal, args_parser); - } - catch(const char *s){ - std::cout<< "exception: "<< s << std::endl; - } - } - } else { - // unsigned i = 0; - for (auto& s : file_names) { - // if (i++ > 9) return; - try { - solve_mps_in_rational(s, dual, args_parser); - } - catch(const char *s){ - std::cout<< "exception: "<< s << std::endl; - } - } - } -} #endif -void solve_rational() { - lp_primal_simplex solver; - solver.add_constraint(lp_relation::Equal, lp::mpq(7), 0); - solver.add_constraint(lp_relation::Equal, lp::mpq(-3), 1); - - // setting the cost - int cost[] = {-3, -1, -1, 2, -1, 1, 1, -4}; - std::string var_names[8] = {"x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8"}; - - for (unsigned i = 0; i < 8; i++) { - solver.set_cost_for_column(i, lp::mpq(cost[i])); - solver.give_symbolic_name_to_column(var_names[i], i); - } - - int row0[] = {1, 0, 3, 1, -5, -2 , 4, -6}; - for (unsigned i = 0; i < 8; i++) { - solver.set_row_column_coefficient(0, i, lp::mpq(row0[i])); - } - - int row1[] = {0, 1, -2, -1, 4, 1, -3, 5}; - for (unsigned i = 0; i < 8; i++) { - solver.set_row_column_coefficient(1, i, lp::mpq(row1[i])); - } - - int bounds[] = {8, 6, 4, 15, 2, 10, 10, 3}; - for (unsigned i = 0; i < 8; i++) { - solver.set_lower_bound(i, lp::mpq(0)); - solver.set_upper_bound(i, lp::mpq(bounds[i])); - } - - std::unordered_map expected_sol; - expected_sol["x1"] = lp::mpq(0); - expected_sol["x2"] = lp::mpq(6); - expected_sol["x3"] = lp::mpq(0); - expected_sol["x4"] = lp::mpq(15); - expected_sol["x5"] = lp::mpq(2); - expected_sol["x6"] = lp::mpq(1); - expected_sol["x7"] = lp::mpq(1); - expected_sol["x8"] = lp::mpq(0); - solver.find_maximal_solution(); - lp_assert(solver.get_status() == lp_status::OPTIMAL); -#ifdef Z3DEBUG - for (const auto & it : expected_sol) { - (void)it; - lp_assert(it.second == solver.get_column_value_by_name(it.first)); - } -#endif -} std::string read_line(bool & end, std::ifstream & file) { @@ -2047,102 +652,6 @@ bool contains(std::string const & s, char const * pattern) { } -std::unordered_map * get_solution_from_glpsol_output(std::string & file_name) { - std::ifstream file(file_name); - if (!file.is_open()){ - std::cerr << "cannot open " << file_name << std::endl; - return nullptr; - } - std::string s; - bool end; - do { - s = read_line(end, file); - if (end) { - std::cerr << "unexpected file end " << file_name << std::endl; - return nullptr; - } - if (contains(s, "Column name")){ - break; - } - } while (true); - - read_line(end, file); - if (end) { - std::cerr << "unexpected file end " << file_name << std::endl; - return nullptr; - } - - auto ret = new std::unordered_map(); - - do { - s = read_line(end, file); - if (end) { - std::cerr << "unexpected file end " << file_name << std::endl; - return nullptr; - } - auto split = string_split(s, " \t", false); - if (split.empty()) { - return ret; - } - - lp_assert(split.size() > 3); - (*ret)[split[1]] = atof(split[3].c_str()); - } while (true); -} - - - -void test_init_U() { - static_matrix m(3, 7); - m(0, 0) = 10; m(0, 1) = 11; m(0, 2) = 12; m(0, 3) = 13; m(0, 4) = 14; - m(1, 0) = 20; m(1, 1) = 21; m(1, 2) = 22; m(1, 3) = 23; m(1, 5) = 24; - m(2, 0) = 30; m(2, 1) = 31; m(2, 2) = 32; m(2, 3) = 33; m(2, 6) = 34; -#ifdef Z3DEBUG - print_matrix(m, std::cout); -#endif - vector basis(3); - basis[0] = 1; - basis[1] = 2; - basis[2] = 4; - - square_sparse_matrix u(m, basis); - - for (unsigned i = 0; i < 3; i++) { - for (unsigned j = 0; j < 3; j ++) { - lp_assert(m(i, basis[j]) == u(i, j)); - } - } - - // print_matrix(m); - // print_matrix(u); -} - -void test_replace_column() { - square_sparse_matrix m(10, 10); - fill_matrix(m); - m.swap_columns(0, 7); - m.swap_columns(6, 3); - m.swap_rows(2, 0); - for (unsigned i = 1; i < m.dimension(); i++) { - m(i, 0) = 0; - } - - indexed_vector w(m.dimension()); - for (unsigned i = 0; i < m.dimension(); i++) { - w.set_value(i % 3, i); - } - - lp_settings settings; - - for (unsigned column_to_replace = 0; column_to_replace < m.dimension(); column_to_replace ++) { - m.replace_column(column_to_replace, w, settings); - for (unsigned i = 0; i < m.dimension(); i++) { - lp_assert(abs(w[i] - m(i, column_to_replace)) < 0.00000001); - } - } -} - - void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("-monics", "test emonics"); @@ -2162,12 +671,6 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("-gomory", "gomory"); parser.add_option_with_help_string("-intd", "test integer_domain"); parser.add_option_with_help_string("-xyz_sample", "run a small interactive scenario"); - parser.add_option_with_after_string_with_help("--density", "the percentage of non-zeroes in the matrix below which it is not dense"); - parser.add_option_with_after_string_with_help("--harris_toler", "harris tolerance"); - parser.add_option_with_help_string("--test_swaps", "test row swaps with a permutation"); - parser.add_option_with_help_string("--test_perm", "test permutations"); - parser.add_option_with_after_string_with_help("--checklu", "the file name for lu checking"); - parser.add_option_with_after_string_with_help("--partial_pivot", "the partial pivot constant, a number somewhere between 10 and 100"); parser.add_option_with_after_string_with_help("--percent_for_enter", "which percent of columns check for entering column"); parser.add_option_with_help_string("--totalinf", "minimizes the total infeasibility instead of diminishing infeasibility of the rows"); parser.add_option_with_after_string_with_help("--rep_frq", "the report frequency, in how many iterations print the cost and other info "); @@ -2182,20 +685,14 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_after_string_with_help("--time_limit", "time limit in seconds"); parser.add_option_with_help_string("--mpq", "solve for rational numbers"); parser.add_option_with_after_string_with_help("--simplex_strategy", "sets simplex strategy for rational number"); - parser.add_option_with_help_string("--test_lu", "test the work of the factorization"); - parser.add_option_with_help_string("--test_small_lu", "test the work of the factorization on a smallish matrix"); - parser.add_option_with_help_string("--test_larger_lu", "test the work of the factorization"); - parser.add_option_with_help_string("--test_larger_lu_with_holes", "test the work of the factorization"); parser.add_option_with_help_string("--test_lp_0", "solve a small lp"); parser.add_option_with_help_string("--solve_some_mps", "solves a list of mps problems"); parser.add_option_with_after_string_with_help("--test_file_directory", "loads files from the directory for testing"); - parser.add_option_with_help_string("--compare_with_glpk", "compares the results by running glpsol"); parser.add_option_with_after_string_with_help("--out_dir", "setting the output directory for tests, if not set /tmp is used"); parser.add_option_with_help_string("--dual", "using the dual simplex solver"); parser.add_option_with_help_string("--compare_with_primal", "using the primal simplex solver for comparison"); parser.add_option_with_help_string("--lar", "test lar_solver"); parser.add_option_with_after_string_with_help("--maxng", "max iterations without progress"); - parser.add_option_with_help_string("-tbq", "test binary queue"); parser.add_option_with_help_string("--randomize_lar", "test randomize functionality"); parser.add_option_with_help_string("--smap", "test stacked_map"); parser.add_option_with_help_string("--term", "simple term test"); @@ -2360,237 +857,9 @@ std::string get_status(std::string file_name) { throw 0; } -// returns true if the costs should be compared too -bool compare_statuses(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures) { - std::string glpk_status = get_status(glpk_out_file_name); - std::string lp_tst_status = get_status(lp_out_file_name); - - if (glpk_status != lp_tst_status) { - if (glpk_status == "UNDEFINED" && (lp_tst_status == "UNBOUNDED" || lp_tst_status == "INFEASIBLE")) { - successes++; - return false; - } else { - std::cout << "glpsol and lp_tst disagree: glpsol status is " << glpk_status; - std::cout << " but lp_tst status is " << lp_tst_status << std::endl; - failures++; - return false; - } - } - return lp_tst_status == "OPTIMAL"; -} - -double get_glpk_cost(std::string file_name) { - std::ifstream f(file_name); - if (!f.is_open()) { - std::cout << "cannot open " << file_name << std::endl; - throw 0; - } - std::string str; - while (getline(f, str)) { - if (str.find("Objective") != std::string::npos) { - vector tokens = split_and_trim(str); - if (tokens.size() != 5) { - std::cout << "unexpected Objective std::string " << str << std::endl; - throw 0; - } - return atof(tokens[3].c_str()); - } - } - std::cout << "cannot find the Objective line in " << file_name << std::endl; - throw 0; -} - -double get_lp_tst_cost(std::string file_name) { - std::ifstream f(file_name); - if (!f.is_open()) { - std::cout << "cannot open " << file_name << std::endl; - throw 0; - } - std::string str; - std::string cost_string; - while (getline(f, str)) { - if (str.find("cost") != std::string::npos) { - cost_string = str; - } - } - if (cost_string.empty()) { - std::cout << "cannot find the cost line in " << file_name << std::endl; - throw 0; - } - - vector tokens = split_and_trim(cost_string); - if (tokens.size() != 3) { - std::cout << "unexpected cost string " << cost_string << std::endl; - throw 0; - } - return atof(tokens[2].c_str()); -} - -bool values_are_one_percent_close(double a, double b) { - double maxval = std::max(fabs(a), fabs(b)); - if (maxval < 0.000001) { - return true; - } - - double one_percent = maxval / 100; - return fabs(a - b) <= one_percent; -} - -// returns true if both are optimal -void compare_costs(std::string glpk_out_file_name, - std::string lp_out_file_name, - unsigned & successes, - unsigned & failures) { - double a = get_glpk_cost(glpk_out_file_name); - double b = get_lp_tst_cost(lp_out_file_name); - - if (values_are_one_percent_close(a, b)) { - successes++; - } else { - failures++; - std::cout << "glpsol cost is " << a << " lp_tst cost is " << b << std::endl; - } -} -void compare_with_glpk(std::string glpk_out_file_name, std::string lp_out_file_name, unsigned & successes, unsigned & failures, std::string /*lp_file_name*/) { -#ifdef CHECK_GLPK_SOLUTION - std::unordered_map * solution_table = get_solution_from_glpsol_output(glpk_out_file_name); - if (solution_is_feasible(lp_file_name, *solution_table)) { - std::cout << "glpk solution is feasible" << std::endl; - } else { - std::cout << "glpk solution is infeasible" << std::endl; - } - delete solution_table; -#endif - if (compare_statuses(glpk_out_file_name, lp_out_file_name, successes, failures)) { - compare_costs(glpk_out_file_name, lp_out_file_name, successes, failures); - } -} -void test_lar_on_file(std::string file_name, argument_parser & args_parser); - -void process_test_file(std::string test_dir, std::string test_file_name, argument_parser & args_parser, std::string out_dir, unsigned max_iters, unsigned time_limit, unsigned & successes, unsigned & failures, unsigned & inconclusives) { - bool use_mpq = args_parser.option_is_used("--mpq"); - bool minimize = args_parser.option_is_used("--min"); - std::string full_lp_tst_out_name = out_dir + "/" + create_output_file_name(minimize, test_file_name, use_mpq); - - std::string input_file_name = test_dir + "/" + test_file_name; - if (input_file_name[input_file_name.size() - 1] == '~') { - // std::cout << "ignoring " << input_file_name << std::endl; - return; - } - std::cout <<"processing " << input_file_name << std::endl; - - std::ofstream out(full_lp_tst_out_name); - if (!out.is_open()) { - std::cout << "cannot open file " << full_lp_tst_out_name << std::endl; - throw 0; - } - std::streambuf *coutbuf = std::cout.rdbuf(); // save old buffer - std::cout.rdbuf(out.rdbuf()); // redirect std::cout to dir_entry->d_name! - bool dual = args_parser.option_is_used("--dual"); - try { - if (args_parser.option_is_used("--lar")) - test_lar_on_file(input_file_name, args_parser); - else - solve_mps(input_file_name, minimize, time_limit, use_mpq, dual, false, args_parser); - } - catch(...) { - std::cout << "catching the failure" << std::endl; - failures++; - std::cout.rdbuf(coutbuf); // reset to standard output again - return; - } - std::cout.rdbuf(coutbuf); // reset to standard output again - - if (args_parser.option_is_used("--compare_with_glpk")) { - std::string glpk_out_file_name = out_dir + "/" + create_output_file_name_for_glpsol(minimize, std::string(test_file_name)); - int glpk_exit_code = run_glpk(input_file_name, glpk_out_file_name, minimize, time_limit); - if (glpk_exit_code != 0) { - std::cout << "glpk failed" << std::endl; - inconclusives++; - } else { - compare_with_glpk(glpk_out_file_name, full_lp_tst_out_name, successes, failures, input_file_name); - } - } -} -/* - int my_readdir(DIR *dirp, struct dirent * - #ifndef LEAN_WINDOWS - entry - #endif - , struct dirent **result) { - #ifdef LEAN_WINDOWS - *result = readdir(dirp); // NOLINT - return *result != nullptr? 0 : 1; - #else - return readdir_r(dirp, entry, result); - #endif - } -*/ -/* - vector> get_file_list_of_dir(std::string test_file_dir) { - DIR *dir; - if ((dir = opendir(test_file_dir.c_str())) == nullptr) { - std::cout << "Cannot open directory " << test_file_dir << std::endl; - throw 0; - } - vector> ret; - struct dirent entry; - struct dirent* result; - int return_code; - for (return_code = my_readdir(dir, &entry, &result); - #ifndef LEAN_WINDOWS - result != nullptr && - #endif - return_code == 0; - return_code = my_readdir(dir, &entry, &result)) { - DIR *tmp_dp = opendir(result->d_name); - struct stat file_record; - if (tmp_dp == nullptr) { - std::string s = test_file_dir+ "/" + result->d_name; - int stat_ret = stat(s.c_str(), & file_record); - if (stat_ret!= -1) { - ret.push_back(make_pair(result->d_name, file_record.st_size)); - } else { - perror("stat"); - exit(1); - } - } else { - closedir(tmp_dp); - } - } - closedir(dir); - return ret; - } -*/ -/* - struct file_size_comp { - unordered_map& m_file_sizes; - file_size_comp(unordered_map& fs) :m_file_sizes(fs) {} - int operator()(std::string a, std::string b) { - std::cout << m_file_sizes.size() << std::endl; - std::cout << a << std::endl; - std::cout << b << std::endl; - - auto ls = m_file_sizes.find(a); - std::cout << "fa" << std::endl; - auto rs = m_file_sizes.find(b); - std::cout << "fb" << std::endl; - if (ls != m_file_sizes.end() && rs != m_file_sizes.end()) { - std::cout << "fc " << std::endl; - int r = (*ls < *rs? -1: (*ls > *rs)? 1 : 0); - std::cout << "calc r " << std::endl; - return r; - } else { - std::cout << "sc " << std::endl; - return 0; - } - } - }; - -*/ struct sort_pred { bool operator()(const std::pair &left, const std::pair &right) { return left.second < right.second; @@ -2598,121 +867,11 @@ struct sort_pred { }; -void test_files_from_directory(std::string test_file_dir, argument_parser & args_parser) { - /* - std::cout << "loading files from directory \"" << test_file_dir << "\"" << std::endl; - std::string out_dir = args_parser.get_option_value("--out_dir"); - if (out_dir.size() == 0) { - out_dir = "/tmp/test"; - } - DIR *out_dir_p = opendir(out_dir.c_str()); - if (out_dir_p == nullptr) { - std::cout << "Cannot open output directory \"" << out_dir << "\"" << std::endl; - return; - } - closedir(out_dir_p); - vector> files = get_file_list_of_dir(test_file_dir); - std::sort(files.begin(), files.end(), sort_pred()); - unsigned max_iters, time_limit; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - unsigned successes = 0, failures = 0, inconclusives = 0; - for (auto & t : files) { - process_test_file(test_file_dir, t.first, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); - } - std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; - */ -} -std::unordered_map get_solution_map(lp_solver * lps, mps_reader & reader) { - std::unordered_map ret; - for (const auto & it : reader.column_names()) { - ret[it] = lps->get_column_value_by_name(it); - } - return ret; -} -void run_lar_solver(argument_parser & args_parser, lar_solver * solver, mps_reader * reader) { - std::string maxng = args_parser.get_option_value("--maxng"); - if (!maxng.empty()) { - solver->settings().max_number_of_iterations_with_no_improvements = atoi(maxng.c_str()); - } - if (args_parser.option_is_used("-pd")){ - solver->settings().presolve_with_double_solver_for_lar = true; - } - - if (args_parser.option_is_used("--compare_with_primal")){ - if (reader == nullptr) { - std::cout << "cannot compare with primal, the reader is null " << std::endl; - return; - } - auto * lps = reader->create_solver(false); - lps->find_maximal_solution(); - std::unordered_map sol = get_solution_map(lps, *reader); - std::cout << "status = " << lp_status_to_string(solver->get_status()) << std::endl; - return; - } - stopwatch sw; - sw.start(); - lp_status status = solver->solve(); - std::cout << "status is " << lp_status_to_string(status) << ", processed for " << sw.get_current_seconds() <<" seconds, and " << solver->get_total_iterations() << " iterations" << std::endl; - if (solver->get_status() == lp_status::INFEASIBLE) { - explanation evidence; - solver->get_infeasibility_explanation(evidence); - } - if (args_parser.option_is_used("--randomize_lar")) { - if (solver->get_status() != lp_status::OPTIMAL) { - std::cout << "cannot check randomize on an infeazible problem" << std::endl; - return; - } - std::cout << "checking randomize" << std::endl; - vector all_vars; - for (unsigned j = 0; j < solver->number_of_vars(); j++) - all_vars.push_back(j); - - unsigned m = all_vars.size(); - if (m > 100) - m = 100; - - var_index *vars = new var_index[m]; - for (unsigned i = 0; i < m; i++) - vars[i]=all_vars[i]; - - solver->random_update(m, vars); - delete []vars; - } -} -lar_solver * create_lar_solver_from_file(std::string file_name, argument_parser & args_parser) { - if (args_parser.option_is_used("--smt")) { - smt_reader reader(file_name); - reader.read(); - if (!reader.is_ok()){ - std::cout << "cannot process " << file_name << std::endl; - return nullptr; - } - return reader.create_lar_solver(); - } - mps_reader reader(file_name); - reader.read(); - if (!reader.is_ok()) { - std::cout << "cannot process " << file_name << std::endl; - return nullptr; - } - return reader.create_lar_solver(); -} -void test_lar_on_file(std::string file_name, argument_parser & args_parser) { - lar_solver * solver = create_lar_solver_from_file(file_name, args_parser); - mps_reader reader(file_name); - mps_reader * mps_reader = nullptr; - reader.read(); - if (reader.is_ok()) { - mps_reader = & reader; - run_lar_solver(args_parser, solver, mps_reader); - } - delete solver; -} vector get_file_names_from_file_list(std::string filelist) { std::ifstream file(filelist); @@ -2733,23 +892,6 @@ vector get_file_names_from_file_list(std::string filelist) { return ret; } -void test_lar_solver(argument_parser & args_parser) { - - std::string file_name = args_parser.get_option_value("--file"); - if (!file_name.empty()) { - test_lar_on_file(file_name, args_parser); - return; - } - - std::string file_list = args_parser.get_option_value("--filelist"); - if (!file_list.empty()) { - for (const std::string & fn : get_file_names_from_file_list(file_list)) - test_lar_on_file(fn, args_parser); - return; - } - - std::cout << "give option --file or --filelist to test_lar_solver\n"; -} void test_numeric_pair() { numeric_pair a; @@ -2787,131 +929,6 @@ void get_matrix_dimensions(std::ifstream & f, unsigned & m, unsigned & n) { n = atoi(r[1].c_str()); } -void read_row_cols(unsigned i, static_matrix& A, std::ifstream & f) { - do { - std::string line; - getline(f, line); - if (line== "row_end") - break; - auto r = split_and_trim(line); - lp_assert(r.size() == 4); - unsigned j = atoi(r[1].c_str()); - double v = atof(r[3].c_str()); - A.set(i, j, v); - } while (true); -} - -bool read_row(static_matrix & A, std::ifstream & f) { - std::string line; - getline(f, line); - if (static_cast(line.find("row")) == -1) - return false; - auto r = split_and_trim(line); - if (r[0] != "row") - std::cout << "wrong row line" << line << std::endl; - unsigned i = atoi(r[1].c_str()); - read_row_cols(i, A, f); - return true; -} - -void read_rows(static_matrix& A, std::ifstream & f) { - while (read_row(A, f)) {} -} - -void read_basis(vector & basis, std::ifstream & f) { - std::cout << "reading basis" << std::endl; - std::string line; - getline(f, line); - lp_assert(line == "basis_start"); - do { - getline(f, line); - if (line == "basis_end") - break; - unsigned j = atoi(line.c_str()); - basis.push_back(j); - } while (true); -} - -void read_indexed_vector(indexed_vector & v, std::ifstream & f) { - std::string line; - getline(f, line); - lp_assert(line == "vector_start"); - do { - getline(f, line); - if (line == "vector_end") break; - auto r = split_and_trim(line); - unsigned i = atoi(r[0].c_str()); - double val = atof(r[1].c_str()); - v.set_value(val, i); - std::cout << "setting value " << i << " = " << val << std::endl; - } while (true); -} - -void check_lu_from_file(std::string lufile_name) { - std::ifstream f(lufile_name); - if (!f.is_open()) { - std::cout << "cannot open file " << lufile_name << std::endl; - } - unsigned m, n; - get_matrix_dimensions(f, m, n); - std::cout << "init matrix " << m << " by " << n << std::endl; - static_matrix A(m, n); - read_rows(A, f); - vector basis; - read_basis(basis, f); - indexed_vector v(m); - // read_indexed_vector(v, f); - f.close(); - vector basis_heading; - lp_settings settings; - vector non_basic_columns; - lu> lsuhl(A, basis, settings); - indexed_vector d(A.row_count()); - unsigned entering = 26; - lsuhl.solve_Bd(entering, d, v); -#ifdef Z3DEBUG - auto B = get_B(lsuhl, basis); - vector a(m); - A.copy_column_to_vector(entering, a); - indexed_vector cd(d); - B.apply_from_left(cd.m_data, settings); - lp_assert(vectors_are_equal(cd.m_data , a)); -#endif -} - -void test_square_dense_submatrix() { - std::cout << "testing square_dense_submatrix" << std::endl; - unsigned parent_dim = 7; - square_sparse_matrix parent(parent_dim, 0); - fill_matrix(parent); - unsigned index_start = 3; - square_dense_submatrix d; - d.init(&parent, index_start); - for (unsigned i = index_start; i < parent_dim; i++) - for (unsigned j = index_start; j < parent_dim; j++) - d[i][j] = i*3+j*2; -#ifdef Z3DEBUG - unsigned dim = parent_dim - index_start; - dense_matrix m(dim, dim); - for (unsigned i = index_start; i < parent_dim; i++) - for (unsigned j = index_start; j < parent_dim; j++) - m[i-index_start][j-index_start] = d[i][j]; - print_matrix(&m, std::cout); -#endif - for (unsigned i = index_start; i < parent_dim; i++) - for (unsigned j = index_start; j < parent_dim; j++) - d[i][j] = d[j][i]; -#ifdef Z3DEBUG - for (unsigned i = index_start; i < parent_dim; i++) - for (unsigned j = index_start; j < parent_dim; j++) - m[i-index_start][j-index_start] = d[i][j]; - - print_matrix(&m, std::cout); - std::cout << std::endl; -#endif -} - - void print_st(lp_status status) { std::cout << lp_status_to_string(status) << std::endl; @@ -3348,7 +1365,7 @@ void test_gomory_cut_0() { if (j == 2) return zero_of_type(); if (j == 3) return mpq(3); - lp_assert(false); + UNREACHABLE(); return zero_of_type(); }, [](unsigned j) { // at_low_p @@ -3358,7 +1375,7 @@ void test_gomory_cut_0() { return true; if (j == 3) return true; - lp_assert(false); + UNREACHABLE(); return false; }, [](unsigned j) { // at_upper @@ -3368,31 +1385,31 @@ void test_gomory_cut_0() { return true; if (j == 3) return false; - lp_assert(false); + UNREACHABLE(); return false; }, [](unsigned j) { // lower_bound if (j == 1) { - lp_assert(false); //unlimited from below + UNREACHABLE(); //unlimited from below return impq(0); } if (j == 2) return impq(0); if (j == 3) return impq(3); - lp_assert(false); + UNREACHABLE(); return impq(0); }, [](unsigned j) { // upper if (j == 1) { - lp_assert(false); //unlimited from above + UNREACHABLE(); //unlimited from above return impq(0); } if (j == 2) return impq(0); if (j == 3) return impq(10); - lp_assert(false); + UNREACHABLE(); return impq(0); }, [] (unsigned) { return 0; }, @@ -3420,7 +1437,7 @@ void test_gomory_cut_1() { return mpq(4363334, 2730001); if (j == 3) return mpq(1); - lp_assert(false); + UNREACHABLE(); return zero_of_type(); }, [](unsigned j) { // at_low_p @@ -3430,7 +1447,7 @@ void test_gomory_cut_1() { return false; if (j == 3) return true; - lp_assert(false); + UNREACHABLE(); return false; }, [](unsigned j) { // at_upper @@ -3440,19 +1457,19 @@ void test_gomory_cut_1() { return false; if (j == 3) return true; - lp_assert(false); + UNREACHABLE(); return false; }, [](unsigned j) { // lower_bound if (j == 1) { - lp_assert(false); //unlimited from below + UNREACHABLE(); //unlimited from below return impq(0); } if (j == 2) return impq(1); if (j == 3) return impq(1); - lp_assert(false); + UNREACHABLE(); return impq(0); }, [](unsigned j) { // upper @@ -3463,7 +1480,7 @@ void test_gomory_cut_1() { return impq(3333); if (j == 3) return impq(10000); - lp_assert(false); + UNREACHABLE(); return impq(0); }, [] (unsigned) { return 0; }, @@ -3934,22 +1951,6 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } - if (args_parser.option_is_used("--test_mpq")) { - test_rationals(); - return finalize(0); - } - - if (args_parser.option_is_used("--test_mpq_np")) { - test_rationals_no_numeric_pairs(); - return finalize(0); - } - - if (args_parser.option_is_used("--test_mpq_np_plus")) { - test_rationals_no_numeric_pairs_plus(); - return finalize(0); - } - - if (args_parser.option_is_used("--test_int_set")) { test_int_set(); @@ -3959,146 +1960,10 @@ void test_lp_local(int argn, char**argv) { test_bound_propagation(); return finalize(0); } - - std::string lufile = args_parser.get_option_value("--checklu"); - if (!lufile.empty()) { - check_lu_from_file(lufile); - return finalize(0); - } - -#ifdef Z3DEBUG - if (args_parser.option_is_used("--test_swaps")) { - square_sparse_matrix m(10, 0); - fill_matrix(m); - test_swap_rows_with_permutation(m); - test_swap_cols_with_permutation(m); - return finalize(0); - } -#endif - if (args_parser.option_is_used("--test_perm")) { - test_permutations(); - return finalize(0); - } - if (args_parser.option_is_used("--test_file_directory")) { - test_files_from_directory(args_parser.get_option_value("--test_file_directory"), args_parser); - return finalize(0); - } - std::string file_list = args_parser.get_option_value("--filelist"); - if (!file_list.empty()) { - for (const std::string & fn : get_file_names_from_file_list(file_list)) - solve_mps(fn, args_parser); - return finalize(0); - } - - if (args_parser.option_is_used("-tbq")) { - test_binary_priority_queue(); - ret = 0; - return finalize(ret); - } - -#ifdef Z3DEBUG - lp_settings settings; - update_settings(args_parser, settings); - if (args_parser.option_is_used("--test_lu")) { - test_lu(settings); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--test_small_lu")) { - test_small_lu(settings); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--lar")){ - std::cout <<"calling test_lar_solver" << std::endl; - test_lar_solver(args_parser); - return finalize(0); - } - - - - if (args_parser.option_is_used("--test_larger_lu")) { - test_larger_lu(settings); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--test_larger_lu_with_holes")) { - test_larger_lu_with_holes(settings); - ret = 0; - return finalize(ret); - } -#endif - if (args_parser.option_is_used("--eti")) { - test_evidence_for_total_inf_simple(args_parser); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--maximize_term")) { - test_maximize_term(); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--test_lp_0")) { - test_lp_0(); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--smap")) { - test_stacked(); - ret = 0; - return finalize(ret); - } - if (args_parser.option_is_used("--term")) { - test_term(); - ret = 0; - return finalize(ret); - } - unsigned time_limit; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit); - bool dual = args_parser.option_is_used("--dual"); - bool solve_for_rational = args_parser.option_is_used("--mpq"); - std::string file_name = args_parser.get_option_value("--file"); - if (!file_name.empty()) { - solve_mps(file_name, args_parser.option_is_used("--min"), time_limit, solve_for_rational, dual, args_parser.option_is_used("--compare_with_primal"), args_parser); - ret = 0; - return finalize(ret); - } - - if (args_parser.option_is_used("--solve_some_mps")) { -#ifndef _WINDOWS - solve_some_mps(args_parser); -#endif - ret = 0; - return finalize(ret); - } - // lp::ccc = 0; - return finalize(0); - test_init_U(); - test_replace_column(); -#ifdef Z3DEBUG - square_sparse_matrix_with_permutations_test(); - test_dense_matrix(); - test_swap_operations(); - test_permutations(); - test_pivot_like_swaps_and_pivot(); -#endif - tst1(); - std::cout << "done with LP tests\n"; return finalize(0); // has_violations() ? 1 : 0); } } void tst_lp(char ** argv, int argc, int& i) { lp::test_lp_local(argc - 2, argv + 2); } -#ifdef Z3DEBUG -namespace lp { -template void print_matrix(general_matrix&, std::ostream&); -} -#endif diff --git a/src/test/lp/smt_reader.h b/src/test/lp/smt_reader.h index 2ab0c1ea6..7843d5714 100644 --- a/src/test/lp/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -20,18 +20,14 @@ Revision History: #pragma once -// reads an MPS file representing a Mixed Integer Program #include #include #include -#include "math/lp/lp_primal_simplex.h" -#include "math/lp/lp_dual_simplex.h" #include "math/lp/lar_solver.h" #include #include #include #include -#include "math/lp/mps_reader.h" #include "math/lp/ul_pair.h" #include "math/lp/lar_constraints.h" #include @@ -276,7 +272,7 @@ namespace lp { } else if (el.m_head == "+") { add_sum(c, el.m_elems); } else { - lp_assert(false); // unexpected input + UNREACHABLE(); // unexpected input } } diff --git a/src/test/lp/test_file_reader.h b/src/test/lp/test_file_reader.h index 8f461ea1c..36b273740 100644 --- a/src/test/lp/test_file_reader.h +++ b/src/test/lp/test_file_reader.h @@ -27,7 +27,6 @@ Revision History: #include #include #include "math/lp/lp_utils.h" -#include "math/lp/lp_solver.h" namespace lp { diff --git a/src/test/main.cpp b/src/test/main.cpp index 0dce9000e..bf839bd64 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -268,4 +268,5 @@ int main(int argc, char ** argv) { TST(mod_interval); TST(viable); TST(totalizer); + TST(distribution); } diff --git a/src/test/no_overflow.cpp b/src/test/no_overflow.cpp index dd826bad8..c7124a5ce 100644 --- a/src/test/no_overflow.cpp +++ b/src/test/no_overflow.cpp @@ -529,8 +529,8 @@ void test_div(unsigned bvsize) { Z3_del_context(ctx); } -typedef Z3_ast (Z3_API *NO_OVFL_ARITH_FUNC)(Z3_context ctx, Z3_ast t1, Z3_ast t2, bool is_signed); -typedef Z3_ast (Z3_API *ARITH_FUNC)(Z3_context ctx, Z3_ast t1, Z3_ast t2); +typedef Z3_ast (*NO_OVFL_ARITH_FUNC)(Z3_context ctx, Z3_ast t1, Z3_ast t2, bool is_signed); +typedef Z3_ast (*ARITH_FUNC)(Z3_context ctx, Z3_ast t1, Z3_ast t2); typedef enum { OVFL_FUNC, UDFL_FUNC } overflow_type; diff --git a/src/test/qe_arith.cpp b/src/test/qe_arith.cpp index 859d7f2e5..2e170979a 100644 --- a/src/test/qe_arith.cpp +++ b/src/test/qe_arith.cpp @@ -377,6 +377,9 @@ static void add_random_ineq( case opt::t_mod: NOT_IMPLEMENTED_YET(); break; + default: + NOT_IMPLEMENTED_YET(); + break; } fmls.push_back(fml); mbo.add_constraint(vars, rational(coeff), rel); diff --git a/src/util/distribution.h b/src/util/distribution.h new file mode 100644 index 000000000..0ed63d510 --- /dev/null +++ b/src/util/distribution.h @@ -0,0 +1,104 @@ +/*++ +Copyright (c) 2023 Microsoft Corporation + +Module Name: + + distribution.h + +Abstract: + + Probabiltiy distribution + +Author: + + Nikolaj Bjorner (nbjorner) 2023-4-12 + +Notes: + + Distribution class works by pushing identifiers with associated scores. + After they have been pushed, you can access a random element using choose + or you can enumerate the elements in random order, sorted by the score probability. + Only one iterator can be active at a time because the iterator reshuffles the registered elements. + The requirement is not checked or enforced. + +--*/ +#pragma once + +#include "vector.h" + +class distribution { + + random_gen m_random; + svector> m_elems; + unsigned m_sum = 0; + + unsigned choose(unsigned sum) { + unsigned s = m_random(sum); + unsigned idx = 0; + for (auto const& [j, score] : m_elems) { + if (s < score) + return idx; + s -= score; + ++idx; + } + UNREACHABLE(); + return 0; + } + +public: + + distribution(unsigned seed): m_random(seed) {} + + void reset() { + m_elems.reset(); + m_sum = 0; + } + + bool empty() const { + return m_elems.empty(); + } + + void push(unsigned id, unsigned score) { + SASSERT(score > 0); + if (score > 0) { + m_elems.push_back({id, score}); + m_sum += score; + } + } + + /** + \brief choose an element at random with probability proportional to the score + relative to the sum of scores of other. + */ + unsigned choose() { + return m_elems[choose(m_sum)].first; + } + + class iterator { + distribution& d; + unsigned m_sz = 0; + unsigned m_sum = 0; + unsigned m_index = 0; + void next_index() { + if (0 != m_sz) + m_index = d.choose(m_sum); + } + public: + iterator(distribution& d, bool start): d(d), m_sz(start?d.m_elems.size():0), m_sum(d.m_sum) { + next_index(); + } + unsigned operator*() const { return d.m_elems[m_index].first; } + iterator operator++() { + m_sum -= d.m_elems[m_index].second; + --m_sz; + std::swap(d.m_elems[m_index], d.m_elems[m_sz]); + next_index(); + return *this; + } + bool operator==(iterator const& other) const { return m_sz == other.m_sz; } + bool operator!=(iterator const& other) const { return m_sz != other.m_sz; } + }; + + iterator begin() { return iterator(*this, true); } + iterator end() { return iterator(*this, false); } +}; diff --git a/src/util/rlimit.cpp b/src/util/rlimit.cpp index f71d2764a..ecc527681 100644 --- a/src/util/rlimit.cpp +++ b/src/util/rlimit.cpp @@ -87,6 +87,8 @@ void reslimit::push_child(reslimit* r) { void reslimit::pop_child() { lock_guard lock(*g_rlimit_mux); + m_count += m_children.back()->m_count; + m_children.back()->m_count = 0; m_children.pop_back(); } diff --git a/src/util/tptr.h b/src/util/tptr.h index 806b7637c..50e9417fe 100644 --- a/src/util/tptr.h +++ b/src/util/tptr.h @@ -19,26 +19,28 @@ Revision History: #pragma once +#include #include "util/machine.h" +#include #define TAG_SHIFT PTR_ALIGNMENT #define ALIGNMENT_VALUE (1 << PTR_ALIGNMENT) #define TAG_MASK (ALIGNMENT_VALUE - 1) #define PTR_MASK (~TAG_MASK) -#define ALIGN(T, PTR) reinterpret_cast(((reinterpret_cast(PTR) >> PTR_ALIGNMENT) + \ - static_cast((reinterpret_cast(PTR) & TAG_MASK) != 0)) << PTR_ALIGNMENT) +#define ALIGN(T, PTR) reinterpret_cast(((reinterpret_cast(PTR) >> PTR_ALIGNMENT) + \ + static_cast((reinterpret_cast(PTR) & TAG_MASK) != 0)) << PTR_ALIGNMENT) -#define UNTAG(T, PTR) reinterpret_cast(reinterpret_cast(PTR) & PTR_MASK) +#define UNTAG(T, PTR) reinterpret_cast(reinterpret_cast(PTR) & PTR_MASK) -#define TAG(T, PTR, TAG_VAL) reinterpret_cast(reinterpret_cast(PTR) | static_cast(TAG_VAL)) +#define TAG(T, PTR, TAG_VAL) reinterpret_cast(reinterpret_cast(PTR) | static_cast(TAG_VAL)) -#define GET_TAG(PTR) (reinterpret_cast(PTR) & TAG_MASK) +#define GET_TAG(PTR) (reinterpret_cast(PTR) & TAG_MASK) -#define BOXINT(T, VAL) reinterpret_cast(static_cast(VAL) << PTR_ALIGNMENT) +#define BOXINT(T, VAL) reinterpret_cast(static_cast(VAL) << PTR_ALIGNMENT) -#define BOXTAGINT(T, VAL, TAG_VAL) reinterpret_cast((static_cast(VAL) << PTR_ALIGNMENT) | static_cast(TAG_VAL)) +#define BOXTAGINT(T, VAL, TAG_VAL) reinterpret_cast((static_cast(VAL) << PTR_ALIGNMENT) | static_cast(TAG_VAL)) -#define UNBOXINT(PTR) static_cast(reinterpret_cast(PTR) >> PTR_ALIGNMENT) +#define UNBOXINT(PTR) static_cast(reinterpret_cast(PTR) >> PTR_ALIGNMENT) diff --git a/src/util/zstring.cpp b/src/util/zstring.cpp index 570510458..eaa5bb5ee 100644 --- a/src/util/zstring.cpp +++ b/src/util/zstring.cpp @@ -78,7 +78,7 @@ zstring::zstring(char const* s) { m_buffer.push_back(ch); } else { - m_buffer.push_back(*s); + m_buffer.push_back((unsigned char)*s); ++s; } }