diff --git a/.gitmodules b/.gitmodules index 6ad08ddfd..e69de29bb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,22 +0,0 @@ -[submodule "clasp"] - path = clasp - url = https://github.com/potassco/clasp.git - branch = dev -[submodule "third_party/catch"] - path = third_party/catch - url = ../../catchorg/Catch2.git -[submodule "third_party/ordered-map"] - path = third_party/ordered-map - url = ../../Tessil/ordered-map.git -[submodule "third_party/hopscotch-map"] - path = third_party/hopscotch-map - url = ../../Tessil/hopscotch-map.git -[submodule "third_party/sparse-map"] - path = third_party/sparse-map - url = ../../Tessil/sparse-map.git -[submodule "third_party/optional"] - path = third_party/optional - url = ../../TartanLlama/optional.git -[submodule "third_party/variant"] - path = third_party/variant - url = ../../mpark/variant.git diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 46e586a80..000000000 --- a/TODO.md +++ /dev/null @@ -1,68 +0,0 @@ -# clingo 5 -- address `_` in negated literals - - use `_` or maybe `*` -- add sort-constraint - - `order(B,A) :- (A, B) = #sort{ X : p(X) }.` - - `order(A,B) :- ((_,A), (_,B)) = #sort{ K,X : p(X), key(X,K) }.` -- profiling -- sorting via conditional literals became less efficient with the latest implementation in some cases -- projection is disabled in non-monotone constructs for now - it could be enabled again if equivalences are used for affected atoms -- remove CSP support -- shifting of disjunctions - -# constraints -- integrate constraint variables tighter into the gringo language - - any term can contain csp variables - - use [HT\_LC](http://www.cs.uni-potsdam.de/wv/pdfformat/cakaossc16a.pdf) as basis -- csp terms could be supported in aggregates too -- when the value of a csp term is required rewriting has to happen - - `p(f($X+3))` becomes `p(f(Aux)), Aux = $X+3` -- supporting csp terms in functions symbols in build-ins is more tricky - - `f($X+3) < g($Y+1)` - - a translation as above is unsafe - - the literal is a tautology - -# memory management -- I think that the proper way to handle symbols is to use reference counting - - the c api would have to put the burden of reference counting on the user - - the APIs can provide reference counted symbols -- symbols should only be owned in key locations where the reference count is - increased - - for example a domain owns a symbol and can also take care of flyweighting - it -- touching the counters should be avoided as much as possible during grounding - - during the backtraking based instantiation a very limited set of - intermediate symbols is created - - these intermediate symbols can be freed upon backtracking (if they have not - been added to a domain) - - the temporary symbols may refer to owned symbols without increasing their - reference count - - this has the advantage that insertion into hash tables and touching of - reference counts only happens when a symbol is commited into a domain -- implementing this should not be terribly difficult but affects *a lot* of - code - -# misc -- **enlarge test suites** -- incremental programs - - atm indexes have to be cleared and recreated afterwards - - it might be a good idea to optimize this and reuse indices later on - - for now just clear them to not have them dangling around -- missing features in view of the ASP standard - - queries -- assignment rewriting - - enqueue: `expr(X,Z,Val):-expr(X,Y,Val_1)?,sing_term(Y,Z,Val_2)?,Val=(Val_1+Val_2),#X0=(Val_1+Val_2),#X0=Val.` - - handle assignments in a more clever way... -- it would be nice to block grounding of rules if one index is empty - (maybe even delaying the filling of indices if one index is empty) -- indices could be specialized to handle zero-ary predicates more efficiently - - there could be one domain for all zero-ary predicates -- detect ground rules and implement more clever groundig -- domains - - using a value as representation is wasteful - - uses one unordered\_map to much -- predicate indices - - using a valvec as key is wasteful - - uses one unordered\_map too much -- on large instances both optimizations should safe a lot of memory diff --git a/clasp b/clasp deleted file mode 160000 index 4c708a73f..000000000 --- a/clasp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4c708a73fdcb78049e9c3bad863a3f6eab437a07 diff --git a/clasp/.gitignore b/clasp/.gitignore new file mode 100644 index 000000000..17a5534f5 --- /dev/null +++ b/clasp/.gitignore @@ -0,0 +1,4 @@ +*.swp +build* +.vscode* +CMakeLists.txt.user diff --git a/clasp/.travis.yml b/clasp/.travis.yml new file mode 100644 index 000000000..3b62046be --- /dev/null +++ b/clasp/.travis.yml @@ -0,0 +1,63 @@ +sudo: false +language: cpp +matrix: + include: + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - george-edison55-precise-backports + packages: + - g++-4.9 + - cmake + - cmake-data + env: + - COMPILER='g++-4.9' + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - george-edison55-precise-backports + packages: + - g++-5 + - cmake + - cmake-data + env: + - COMPILER='g++-5' + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - george-edison55-precise-backports + packages: + - g++-8 + - cmake + - cmake-data + env: + - COMPILER='g++-8' + - os: osx + osx_image: xcode8 + env: + - COMPILER='clang++' + +install: + - export CMAKE=cmake + - export CXX=$COMPILER + - $CMAKE --version + - $CXX --version +script: + - mkdir $CXX-mt && cd $CXX-mt + - $CMAKE -DCMAKE_CXX_COMPILER=$CXX -DCLASP_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-Wall -Wextra" ../ + - make -j3 && make test CTEST_OUTPUT_ON_FAILURE=1 + - cd ../ + - mkdir $CXX-st && cd $CXX-st + - $CMAKE -DCMAKE_CXX_COMPILER=$CXX -DCLASP_BUILD_TESTS=ON -DCLASP_BUILD_WITH_THREADS=OFF -DCMAKE_CXX_FLAGS="-Wall -Wextra" ../ + - make -j3 && make test CTEST_OUTPUT_ON_FAILURE=1 + - cd ../ + diff --git a/clasp/CHANGES b/clasp/CHANGES new file mode 100644 index 000000000..3a48ddc5c --- /dev/null +++ b/clasp/CHANGES @@ -0,0 +1,756 @@ +clasp 3.3.9: Saturday, 10th September 2022 + * fixed issue https://github.com/potassco/clasp/issues/80 + * fixed issue https://github.com/potassco/clasp/issues/81 + * fixed issue https://github.com/potassco/clasp/issues/84 + * fixed issue https://github.com/potassco/clasp/issues/85 + * refined overflow checks in LogicProgram + +clasp 3.3.8: Tuesday, 3rd May 2022 + * fixed issue https://github.com/potassco/clasp/issues/75 + * fixed issue https://github.com/potassco/clasp/issues/76 + * fixed issue https://github.com/potassco/clingcon/issues/85 + * updated catch2 (https://github.com/potassco/clasp/issues/73) + * adjusted TextOutput (https://github.com/potassco/clingo/issues/355) + * moved clasp app to cli part of library + +clasp 3.3.7: Tuesday, 16th November 2021 + * fixed issue https://github.com/potassco/clingo/issues/342 + * fixed issue https://github.com/potassco/clingcon/issues/81 + * fixed issue https://github.com/potassco/clasp/issues/72 + * fixed issue https://github.com/potassco/clasp/issues/71 + +clasp 3.3.6: Sunday, 18th April 2021 + * fixed a regression in preprocessing of disjunctions + * fixed issue https://github.com/potassco/clasp/issues/48 + * fixed issue https://github.com/potassco/clasp/issues/51 + * fixed issue https://github.com/potassco/clasp/issues/52 + * fixed issue https://github.com/potassco/clasp/issues/54 + * fixed issue https://github.com/potassco/clasp/issues/55 + * fixed issue https://github.com/potassco/clasp/issues/57 + * fixed issue https://github.com/potassco/clasp/issues/65 + * fixed issue https://github.com/potassco/clasp/issues/65 + * fixed issue https://github.com/potassco/clasp/issues/69 + * fixed an issue in optimal model enumeration + (see: https://github.com/potassco/clingcon/issues/58) + * added support for single shot solving while keeping the logic program + (https://github.com/potassco/clasp/issues/64) + * added support for getting final conflict + (https://github.com/potassco/clasp/issues/59) + * added lower bounds to clingo statistics + * added support for accessing trail from Potassco::Assignment + (https://github.com/potassco/clasp/issues/49) + +clasp 3.3.5: Friday, 23rd August 2019 + * added support for clingo heuristic + * fixed issue https://github.com/potassco/clasp/issues/36 + * fixed issue https://github.com/potassco/clasp/issues/39 + * fixed issue https://github.com/potassco/clasp/issues/41 + * fixed issue https://github.com/potassco/clasp/issues/45 + * fixed a couple of warnings + +clasp 3.3.4: Wednesday, 27th June 2018 + * fixed: https://github.com/potassco/clasp/pull/22 + * fixed: https://github.com/potassco/clasp/issues/16 + * fixed: https://github.com/potassco/clasp/issues/18 + * fixed: https://github.com/potassco/clasp/issues/33 + * fixed: https://github.com/potassco/clasp/issues/34 + * fixed: possible infinite loop in detection of problem type from input. + * fixed: Atoms of incremental programs not always marked as frozen. + * added internal interface for user-defined statistics + (https://github.com/potassco/clasp/pull/23) + * extended ClingoPropagatorInit to support both global and per-solver watches. + +clasp 3.3.3: Sunday, 5th November 2017 + * fixed: possible race condition in Windows alarm handling (libpotassco) + * fixed: state not fully reset in incremental acyc check + * fixed: facade progress state not reset after solving step + * fixed: https://github.com/potassco/clasp/issues/11 + * fixed: https://github.com/potassco/clasp/issues/13 + * https://github.com/potassco/clasp/issues/12 + +clasp 3.3.2: Saturday, 29th July 2017 + * fixed: missing lower bound output in unsat-core optimization for bounds < 0 + * fixed: conflict clauses not always tagged with assumptions in unsat-core optimization + * fixed: invalid reallocation of vector during theory propagation + * fixed: https://github.com/potassco/clingo/issues/45 + * fixed: https://github.com/potassco/clasp/issues/10 + * added workaround for bug #81365 in gcc 7.1 + (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81365) + * added support for as terminator for minweight constraint in dimacs input + +clasp 3.3.1: Wednesday, May 3rd 2017 (internal only) + * added support for partial checks in clingo propagator + +clasp 3.3.0: Friday, 28th April 2017 + * switched to MIT license + * added support for unsatisfiable core shrinking via option "--opt-usc-shrink" + * added support for core-guided optimization algorithms K and ONE + * CLI changes: + - changed syntax of option "--opt-strategy" (old syntax is still supported but deprecated) + - First argument selects the overall strategy, i.e. model- or core-guided optimization + - Second argument selects the algorithm, e.g. "K" or "OLL" for core-guided optimization + - Further arguments enable additional tactics, e.g. "disjoint" for disjoint core + preprocessing in core-guided optimization + - option "--pre" now prints aspif by default (use "--pre=smodels" for smodels) + - merged option "--opt-bound" into "--opt-mode" + - merged option "--counter-bump" into "--counter-restarts" + - replaced option "--opt-sat" with "--parse-maxsat" + - merged option "--dist-mode" into "--distribute" + - merged option "--del-protect" into "--update-lbd" + - replaced numbers with named constants in various options + - added support for specifying bitmask arguments as list of named constants, e.g. + "--forget-on-step=varScores,signs,lemmaScores" instead of "--forget-on-step=7" + * Integration/clingo: + - removed dependency to Intel TBB (multithreading now only uses C++11 threads) + - switched to CMake for building clasp + - moved libprogram_opts to libpotassco, which is now a submodule of clasp + - added support for step-specific true var to simplify implementation of multishot propagators + - simplified ClaspFacade solving interface + +clasp 3.2.3: Monday, 24th April 2017 + * fixed: possible crash on Cygwin-32 because of non-increasing wall clock time + * fixed: PB constraints not correctly initialized if added on levels > 0 + * fixed regression: unsupported atoms in minimize statements not added to program + * fixed regression in enum mode "domRec" in case of complementary atoms + * fixed spurious backtracking on integrating sat clauses from theory propagator + * fixed regression: model heuristic ("--opt-heuristic=2") not applied + +clasp 3.2.2: Monday, 23rd January 2017 + * added experimental support for MiniCard's CNF+ format + * added missing stats key "summary.winner" for getting id of winner thread from clingo + * fixed regression in sat/pb reader + * fixed regression in handling of unsatisfiable optimization problems + * fixed regression in component-wise shifting and preprocessing of disjunctive rules + * fixed: ClaspFacade::startSolve() could produce duplicate and/or miss models + * fixed: failed to handle empty clauses added from theory propagator + * fixed: invalid logged lemmas due to buggy minimization in Solver::resolveToFlagged() + * fixed: LogicProgram::dispose() must reset pointers + * fixed compilation problems on Cygwin and Alpha + +clasp 3.2.1: Thursday, 13th October 2016 + * added support for adding variables from theory propagator + * fixed output of "--pre" wrt to externals in incremental setting + * fixed regression in projective enumeration in incremental setting + * fixed regression in handling of minimize statements containing only zero-weighted literals + * fixed possible invalid access to empty input vector in ClaspAppBase::run() + * added workaround to fix emcc code-gen bug + * fixed type mismatch and warnings when compiled with x64 and _DEBUG + * fixed compilation problems on MacOS and ARM + +clasp 3.2.0: Thursday, 8th September 2016 + * added experimental support for new asp format and changed "--pre" option to use it + * added propagator for acyclicity constraint (#edge-directive) + * added option "--parse-ext" for enabling input extentions: + - acyc constraints in smodels, dimacs, and opb format, + - objective function in dimacs format, + - projection, assumption, output, heuristic directives in dimacs and opb format + * added stratification heuristic for weights in core-guided optimization enabled via + "--opt-strategy=usc,{8-15}" + * added support for parallel solving via C++11 threads instead of Intel TBB (see README) + * added ClaspFacade::startSolve() for synchronous (possibly single-threaded) model generation + * added option "--vsids-acids" for enabling average conflict-index decision scoring in vsids + * added option "--vsids-progress" for enabling Glucose-style decreasing decay scheme + * added option "--block-restarts" for enabling Glucose-style blocking of restarts + * added option "--del-protect" for enabling Glucose-style temporary freezing of learnt nogods + * option "--dom-mod" now also supports modifiers "factor" and "init" and also + applies to problems in dimacs and opb format + * added support for updating heuristic options in multishot solving + (init-moms, score-res, score-other, berk-huang, dom-mod) + * added support for config inheritance in portfolio configurations + * changed default configuration of tester in disjunctive asp solving + * improved handling of extended rules in computation of loop nogoods + * improved handling of equivalent and complementary atoms in domain heuristic + * option "--nant" now also applies to lookback heuristics + * option "--lemma-out" now resolves lemmas to problem variables + - added "--lemma-out-dom" for setting variable domain to either input or output + - added "--lemma-out-txt" for logging lemmas in ground lp format + - added "--lemma-out-max" for limiting number of logged lemmas + * replaced "--sign-def=4" with "--sign-def-disj=" for setting default sign of disjunctive atoms + * made lower bound updates in parallel optimization lock-free + * switched to a more compact representation of input facts in smodels format + * replaced old symbol table with simple output list + +clasp 3.1.5: Friday, 5th August 2016 (updated: 11th August 2016) + * fixed: projective enumeration failed to work with option "--no-lookback" + * fixed: invalid models in parallel disjunctive solving due to unsynchronized simplifications + * fixed: Json output sometimes failed to report correct number of optimal models + * fixed: SAT-frontend must retain pure literals for cautious/brave reasoning + * fixed: ClaspFacade::start() sometimes failed to reset SharedContext + * fixed: Sat-Preprocessor failed to propagate resolution of empty clause + * fixed: simplifications after end of incremental step not always propagated to all threads + * fixed too strong assertion in eq-preprocessor + +clasp 3.1.4: Thursday, 10th December 2015 + * fixed: "--pre" failed to handle choice/disjunctive heads defined by extended bodies + * fixed: "--backprop" failed to backpropagte certain constraints when used with "--eq=0" + * fixed: potential deadlock during shutdown/join() in async solving + * fixed: DomainHeuristic failed to handle nested heuristic predicates + * fixed: invalid delete in DomainHeuristic destructor + * fixed: configuration sometimes failed to initialize solver id + * rewrote Clause::updateWatch() loop to workaround bug #67892 in gcc 5 + (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67892) + +clasp 3.1.3: Friday, 31th July 2015 + * fixed: possible livelock in multi-threaded enumeration of optimal models + * fixed: parser for _heuristic atoms failed to handle atoms containing strings + * fixed: over-satisfied PB constraints not always handled correctly + * fixed: incorrect handling of aux vars in Solver::numWatches() + * fixed: C++11 isnan issue on Mac + +clasp 3.1.2: Tuesday, 5th May 2015 + * fixed: Option "--heuristic" failed to accept decay factor for domain heuristic + * fixed: LogicProgram::getDisjFor() failed to handle hash collisions correctly + * fixed: facts in disjunctions not always correctly handled + * fixed: domain heuristic failed to distinguish atom names sharing a common prefix + * fixed: domain heuristic failed to handle atoms with complementary literals correctly + * fixed: LogicProgram::write() must not output gamma rules and/or atoms that are false + * fixed regression in handling of sign modifier in domain heuristic + * fixed regression in backpropagation during preprocessing + +clasp 3.1.1: Wednesday, 12th November 2014 + * LparseReader now handles "external statement" + * fixed: low-level config interface failed to correctly handle successive changes of "opt-bound" + * fixed: wrong completition nogoods with option "--no-gamma" + * fixed regression in rule simplification (regression in 3.0.x) + * fixed regression in "--configuration=jumpy" + * fixed regression in handling of modifiers true/false in domain heuristic + +clasp 3.1.0: Friday, 15th August 2014 + * added new strategies for unsatisfiable-core based optimization + * added alternative distribution mode via thread-local queues (option " --dist-mode") + * generators now propagate top-level assignment to testers + * removed optimization options from default configurations + * CLI changes: + - option "--sat-prepro" now expects the algorithm followed by an optional list of limits + - option "--opt-strategy" now takes two arguments; the first mandatory argument selects the + major strategy (i.e. branch and bound or unsat-core), while the optional second argument + controls fine tuning of said strategy + - combined options "--dom-mod" and "--dom-pref" and added enum mode "domRec" for + enabling subset optimization via domain heuristic + - added "--forget-on-step=2" for discarding previously set variable phases and changed + value for discarding nogood activities and learnt nogoods to 4 and 8, respectively + - replaced option "--hparam" (and its aliases) with second argument for "--heuristic" + - replaced "--berk-once" with more general "--score-res" + - renamed option "--number" to "--models" + * Integration/clingo: + - added support for open (i.e. unassigned) externals + - added support for adding constraints via model callback + - added low-level interface for accessing a clasp configuration + - cleaned up some stats keys and replaced "camelCase" with "snake_case" in keys + +clasp 3.0.6: Monday, 21st July 2014 + * fixed: parallel unsat-core based enumeration of optimal models could skip valid models + * fixed: possible race condition in signal handling + * fixed: ABA-problem in parallel cb-enumerator could lead to duplicate models (regression in 3.0.x) + * fixed: "--share=auto" (default) failed to enable physical sharing in mt-mode (regression in 3.0.x) + * fixed: eq over complementary atoms not always correctly handled (regression in 3.0.x) + * fixed: incremental domain heuristic failed to apply values to atoms from previous steps + * fixed: eq over bodies of size 1 not always correctly handled + * fixed: typo in description of "--quiet" + +clasp 3.0.5: Monday, 19th May 2014 + * added support for ThreadTime::getTime() on MacOS X + * removed pointless "--del-init" option from config tweety + * fixed: non-hcf components not always correctly added in incremental disjunctive programs (clingo) + * fixed: empty soft clauses not correctly handled in MaxSAT frontend + * fixed: unfounded sets from 2nd level test not always correctly handled + * fixed: json output must not print NaN + * fixed: asp options not correctly set on configuration update (from clingo) + * fixed: regression in handling of (incremental) projection + +clasp 3.0.4: Thursday, 24th April 2014 + * replaced "s SATISFIABLE" with "s UNKNOWN" when called with option "--opt-sat" + * improved handling of weight constraints containing incremental assumptions + * fixed: typo in output of "--help" + * fixed: unsatisfiable-core based optimization sometimes fails to assign all active assumptions + * fixed: superfluous rules for facts not always correctly handled + * fixed: assigned values for external atoms not always carried over to next step + * fixed: clasp sometimes fails on incremental problems if "--lookahead" is used with "--forget-on-step" + +clasp 3.0.3: Friday, 28th March 2014 + * fixed: AsyncResult destructor fails if called after destruction of ClaspFacade + * fixed: projective enumeration sometimes fails when used with option "--restart-on-model" + * fixed: regression in handling of negative lower bound in optimization + +clasp 3.0.2: Monday, 17th March 2014 + * patch 1 (March 19th, 2014): fixed regression in projective enumeration + * added ClaspFacade::startSolveAsync() for enumerating models in an iterator-like fashion + * added "--forget-on-step" for simulating iclingo's "--ilearnt" and "--iheuristic" + * added "--dom-mod=6" for simulating hclasp's subset minimization + * fixed: DefaultUnfoundedCheck sometimes fails with assertion in incremental setting + * fixed: wcnf frontend fails if top weight >= 2^31 + * fixed: unit cores not always correctly handled in unsatisfiable-core based optimization + * fixed: negative lower bound not correctly handled in hierarchical optimization + * fixed: projection not correctly handled in incremental setting + * fixed: clasp sometimes reports bogus timing values when interrupted via Ctrl-C on Windows + * fixed: lookahead fails on non-asp problems if combined with option "--nant" + * fixed: wrong/missing models because of incorrect backpropagation with option "--backprop" + +clasp 3.0.1: Friday, 21th February 2014 + * added support for disabling "--opt-bound" via "--opt-bound=no" + * added support for passing on/off values for option flags on config update + * fixed: multiple occurrences of literal with negative weight not correctly merged + (see also: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=739628) + * fixed: negative literals not correctly formatted in PB output + * fixed: excess bounds in "--opt-bound" not correctly handled + * fixed: negatable otions like "[no]-init-moms" not recognized + * fixed libprogram_opts: string to long long conversion fails + +clasp 3.0.0: Friday, 14th February 2014 + 1. Integrated support for disjunctive solving from claspD-2 + 2. Integrated domain heuristic from hclasp via option "--heuristic=domain" + * added "--dom-pref" and "--dom-mod" for applying domain modifications to certain subsets of atoms + 3. Optimization + * replaced global "--opt-hierarch" option with more general per thread option "--opt-strategy" + to enable competition-based optimization during parallel solving + * integrated unsatisfiable-core based optimization from unclasp via "--opt-strategy={4,5}" + * replaced "--opt-ignore", "--opt-all", "--opt-best" with option "--opt-mode" and + added new reasoning mode for enumerating optimal models via option "--opt-mode=optN" + * renamed "--opt-value" to "--opt-bound" + 4. Misc + * added a new configuration "tweety" and made it the default for asp problems + * removed option "--portfolio" in favour of "--configuration=" + * added second argument to "--contraction" for replacing long nogoods with decision sequence/all uip clause + * added option "--out-hide-aux" for hiding atoms starting with "_" in models + * added option "--out-atomf" for setting atom output format + * added "--outf=3" for disabling output + * simplified deletion options + 5. Internals & Integration with clingo + * implemented new configuration class + * moved option handling and output classes to libclasp + * simplified/cleaned up major interfaces + * added support for asynchronous solving + * added support for incremental/volatile enumeration/optimization + * added support for adding/removing aux variables during solving + * added low-level interface for accessing statistics + +clasp 2.1.5: Tuesday, 28th January 2014 + * fixed a bug in the handling of unary projection nogoods + * fixed a bug in the interplay between lookahead and minimization + +clasp 2.1.4: Tuesday, 26th November 2013 + * fixed a possible crash bug in handling of conditional constraints + * fixed a regression in handling of physically shared clauses + * fixed compilation issues with clang-500.2.79 on Mac + +clasp 2.1.3: Friday, 12th April 2013 + * fixed a regression in the handling of choice rules in the unfounded set checker + +clasp 2.1.2: Friday, 5th April 2013 + * updated "--outf=1" to ASP-Comp'13 format and added corresponding exit codes + * fixed: Wrong/missing models because of incorrect handling of recursive aggregates + * fixed: Counter implication restart on root level could introduce an invalid conflict + * fixed: clasp does not compile in C++11 mode + * fixed regression: Inconsistent behaviour in dimacs front-end when running + with or without option "--number=1" +clasp 2.1.1: Thursday, 22th November 2012 + * fixed regression: MaxSAT-frontend must not assert pure literals / relaxation variables + * fixed: spurious warning when using option "--no-lookback" + * fixed: "--shuffle" and "--counter-restarts" not working as intended with hierarchical optimization + * fixed: Backslashes in JSON output not correctly quoted + +clasp 2.1.0: Monday, 27th August 2012 + * restructured help and revised options and their defaults + * added "--help[={1..3}]" for showing basic, advanced, or all options + * added "--configuration" for selecting between prefabricated default configurations + * revised deletion options and grouped them via common prefix "--del-" + * revised thread options and added options for size-/topology-based lemma exchange + * added support for dynamic restarts via "--restarts=D," + * added "--sign-def" for setting default sign used in decision heuristic + * added "--sign-fix" for disabling sign-heuristic + * added "--init-moms" for enabling/disabling initialization of heuristic with MOMS-score + * added "--fast-exit" for forcing app exit without cleanup + * added "--update-act" for enabling LBD-based activity bumping + * added "--update-mode" for configuring when update/integrate messages are handled in parallel-search + * added "--learn-explicit" to disable usage of implication graph for learnt constraints + * added "--share" for configuring physical constraint sharing + * added "--init-watches" for controlling initialization of watched literals + * added "--counter-restarts" and "--counter-bump" for enabling counter implication restarts + * added "--lemma-out-lbd" for restricting lemmas written via "--lemma-out" + * added "--lemma-in-lbd" for setting initial lbd of lemmas read via "--lemma-in" + * replaced "--loops-in-heu" with more general "--score-other" + * replaced "--rand-watches" with more general "--init-watches" + * replaced "--initial-lookahead" with "--lookahead=[,]" + * replaced "--recursive-str" with "--strengthen=[,]" + * replaced "--copy-problem" with more general "--share" + * removed options "--brave", "--cautious", "--solution-recording": all superseded by "--enum-mode=" + * option "--restarts" now always requires a type; + and the "limit" parameter now sets the (initial) length of the sequence + * disbaled signal handling during printing of models + * fixed: Overflow on parsing large opt-values (issue 3528852) + * fixed: Parallel bt-enumerator enumerated duplicate models under certain conditions + * fixed: Sat-Preprocessor failed to expand models correctly under very rare conditions + * fixed: Erroneous interplay between "otfs=2" and "reverse-arcs" could lead to the + unjustified removal of problem constraints + * fixed: Incorrect ordering-predicate in MinimizeConstraint could lead to wrong optima + * fixed: Solve loop failed to handle restart on total assignment correctly resulting + in an infinite loop on some optimization problems + * fixed: Duplicate key in JSON-Output of lemma stats + +clasp 2.0.6: Tuesday, 3rd April 2012 + * added better error detection to portfolio parser and fixed a bug + in the initialization of thread configs + * first attempt at decoupling learnt db growing from restart schedule using + independent grow schedule configurable via (hidden) option "--dgrowS" + * added "--update-lbd=2" to enable usage of "strict" lbds + * added (experimental) support for counter implication restarts and dynamic restarts + via hidden options + * fixed bugs in code for shared clause integration that could led to unpleasant + results ranging from duplicate/missing models in enumeration to crashing if + option "--otfs" was used + +clasp 2.0.5: Sunday, 29th January 2012 + * fixed: Input parser for opb and (w)cnf failed to read 64bit integers. + * fixed: Outer bound of inner-outer-restarts sometimes remained constant because of integer arithmetic. + * fixed: Minimize constraint must not update optimum in enumeration-mode (opt-all). + Bug introduced in version 2.0.3 + * fixed: Minimize constraint must not update initial (user-set) bound until a model is found. + Bug introduced in version 2.0.3 + * fixed: Right hand side of minimize constraint not correctly initialized if "--opt-hierarch" was used. + * fixed: SharedMinimizeData assumed monotonicity of individual levels - could fail + after merging complementary literals in multi-level minimize constraints. + Added extra "adjustment"-values to represent values resulting from such merges. + +clasp 2.0.4: Tuesday, 29th November 2011 + * some cleanup in help output + * moved general ASP-specific options to separate group + * moved "--opt-sat" to basic options + * simplified default command-line and made "input-dependent" defaults more explicit + * updated interface spec of ProgramBuilder to make it more robust w.r.t incremental update + * fixed parsing of "--global-restarts" + * fixed: default value for option "--rand-watches" was not applied + * fixed: Enumerator did not broadcast last model/optimum of current GP in parallel search + * fixed: clauses simplified to binary/ternary are no longer counted twice in stats + * fixed: eq-preprocessor sometimes did not reorder head list after merging two bodies + * fixed: dimacs front-end failed to read empty dimacs file + +clasp 2.0.3: Tuesday, 23rd August 2011 + * added option "--enum-mode" for setting enumeration algorithm and + deprecated superseded options "--solution-recording", "--brave", and "cautious" + * made "--enum-mode=record" the default for optimization + * added default portfolio settable via "--portfolio=default" + * added support for arithmetic restarts + * moved "--opt-heuristic" to "Search Options" to allow for different settings in portfolios + * fixed: clasp crashes with --pre (issue 3393095) + * fixed: value given via "--opt-val" not checked against initial inconsistency + * fixed: command line options not applied to portfolio configurations + * fixed: parallel enumeration of models did not always respect "--number" + * fixed: possible deadlock in parallel hierarchical optimization + * fixed: failed to always propagate literals enqueued by PostPropagator::isModel() + * fixed: possible crash bug because ReasonStore32::value_type did not decode stored data + +clasp 2.0.2: Thursday, 30th June 2011 + * applied fixes from version 1.3.9 + * fixed problem in call to linker (issue 3324430) + * fixed a bug in "otfs" which could not handle unsimplified lemmas + * fixed a potential invalid memory access in ImplicationList::hasLearnt() + * fixed potential underflow in output of solved guiding paths + * fixed a problem in UnitHeuristic which failed to handle stop conflicts in parallel solving + * fixed various exception-related issues in parallel solving and made + it more robust in the face of exceptions + * re-enabled optimized unit clause handling for MaxSAT + +clasp 2.0.1: Wednesday, 27th April 2011 + * fixed various (spurious) warnings + * updated ProgramOptions to circumvent tellg() bug in g++-4.{4,5,6} under Debian + (see also: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=623850) + * fixed some issues in build scripts (many thanks to Thomas Krennwallner) + * configure.sh no longer sets RPATH unless "--set-rpath" is given + * check for libtbb now also works on Mac OS + * added code for finding TBB in default paths + * fixed argument order in calls to compiler + * removed spaces in specification of include/library directories + +clasp 2.0.0: Thursday, 21st April 2011 + 1. Support for parallel (multithreaded) solving (requires Intel(R) Threading Building Blocks) + * configurable number of threads via option "--threads" + * splitting-based search via guiding path and dynamic load-balancing + * global restarts via option "--global-restarts" to escape from bad initial splits + * competition-based search via freely configurable portfolios (option "--portfolio") + * combination of splitting-based and portfolio-based search (option "--force-gp") + * configurable lemma exchange via Distributor interface and options "--distribute" and "--integrate" + * support for parallel enumeration, optimization, and computation of brave-/cautious consequences + 2. Optimization + * hierarchical (multi-level) optimization via option "--opt-hierarch=1" + * hierarchical optimization with varying step lengths via option "--opt-hierarch={2,3}" + * changed switch "--opt-heu" to option "--opt-heuristic={1,2,3}", where + * 1: use optimize statements in sign heuristic + * 2: use optimize statements in model heuristic + * 3: use optimize statements in both sign and model heuristic + * changed switch "--opt-all" to option "--opt-all=value"; now enumerates + all models with optimize value less than or equal to given value + 3. New language front-ends + * added support for MaxSAT via optimization (option "--opt-sat") + * added support for weighted partial MaxSAT problems in wcnf format + * added support for weighted boolean optimization (WBO) problems + * added support for non-linear constraints to pseudo-Boolean parser + 4. New output handling + * option "--outf={0,1,2}" for selecting between (0) default output format, (1) competition output format, + and (2) output in JSON format + * option "--quiet" now accepts a pair for configuring printing of models and optimize values + * added verbosity level 3 to enable printing of progress messages + * new (hidden) option "--ifs" for setting separator used when printing models + 5. Preprocessing & Learning + * added (optional) support for blocked clause elimination to SAT-preprocessor + * added option "--reverse-arcs" for ManySAT-like conflict analysis with "reverse arcs" + * added option "--otfs" for enabling "on the fly subsumption" + * added (optional) support for secondary nogood reduction schedule via option "--dsched" + * added option "--dinit=min,max" for limiting initial learnt db size to range [min,max] + * added option "--dglue=x" to prevent deletion of nogoods with lbd <= x + * added option "--update-lbd" for enabling dynamic updates of lbds of learnt nogoods + 6. Internals + * reimplemented constraints to enable sharing (and cloning); forced clear distinction between + read-only, shared, and thread-local data + * separated read-only program dependency graph from thread-local unfounded set checker so that + the former can be shared between multiple threads + * added SharedContext object for hosting information to be shared between solving threads + * added shareable weight constraints + * updated clause representation and added a shared clause constraint + * splitted binary-/ternary implication graph into shared read-only static and fine-grained lock-protected dynamic part + * implemented lock-free MultiQueue as first concrete nogood distributor (passive, global distribution) + * implemented linear-time recursive-strengthen algorithm + * switched to new version of ProgramOptions library + * added support for "tagged" knowledge, i.e. nogoods tagged with an initial assumption + * separated local (solver & search related) from global (enumeration & preprocessing) options in ClaspFacade + * implemented more cache-efficient left_right_sequence for storing and distinguishing between clause + and general watches + * added (hidden) option "--dfrac" to set fraction of nogoods to delete on reduction + * added new deletion algorithms and hidden option "--dalgo" for selecting between them + * added hidden option "--dscore" for selecting between different nogood activity scoring schemes + * added (hidden) option "--berk-once" for switching between multiset and set-based scoring in BerkMin + +clasp 1.3.6: Friday, 19th November 2010 + * added missing copyright information to tests and examples + * fixed a bug in preprocessing of weight rules where only literals but not their weights were swapped + * fixed: lookahead accessed unitialized memory in incremental setting (concerns iclingo only) + * fixed: inner-outer restart sequence always used hard-coded grow-factor + +clasp 1.3.5: Friday, 24th September 2010 + * Removed warning for minimization under projection if safe + * fixed inconsequent output format in time and statistics output + * fixed bug in output of choice rules with empty head + * fixed: truth-value of complementary eq-atom not correctly set + * fixed: SAT-preprocessor must not reuse clause ids from previous incremental steps + +clasp 1.3.4: Monday, 14th June 2010 + * moved claspre functionality to clasp (run configure with "--with-claspre" to enable it) + * added "--search-limit" to limit search to a certain number of conflicts or restarts + * calling clasp with "--stats=2" now prints jump statistics + * long loop nogoods are now replaced with decision sequence if the latter is considerably shorter + * sat-based preprocessing is now again enabled by default if input format is dimacs or OPB + * fixed: option "--rand-prob" not always correctly parsed + * fixed: lookahead not correctly initialized in incremental setting (only concerns iclingo) + * fixed: enumerators not always correctly terminated when solving under assumptions + * fixed: sat-output failed to '0'-terminate models + * fixed: "--opt-value" did not work with negative numbers + * simplified some code to avoid internal compiler error in gcc-3.4.3 + +clasp 1.3.3: Thursday, 1st April 2010 (April Fools' Day) + * added "--trans-ext=card" and "--trans-ext=integ" for transforming cardinality rules resp. extended integrity constraints + * added "--lemma-out=" and "--lemma-in=" for writing lemmas to resp. reading lemmas from + * clasp now prints suboptimal models in PB-mode if called with "--verbosity=2" + * added more timing statistics + * application now reports actual average size of learnt lemmas instead of average contracted size + * fixed: "--del=no" did not fully disable nogood deletion + * fixed: "--opt-value" not checked against initial sum of minimize constraint + * fixed: dimacs parser must not assign undefined literals to false if more than one model is requested + * fixed: possible double-deletion in option "--pre" + * fixed: unfounded set checker sometimes too lazy w.r.t heads of recursive extended rules + * fixed a minor bug in eq-preprocessing + * changed some code to avoid code generation bug in Visual C++ 2008 x64 + see: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=526921 + +clasp 1.3.2: Friday, 15th January 2010 + * fixed a 64-bit portability problem; clasp should now also run under Mac OS X 10.6 + * fixed option "--pre" + * fixed: SAT-preprocessor not correctly initialized on SAT-problems with more than 4 Mio. clauses + +clasp 1.3.1: Tuesday, 8th December 2009 + * changed some internal interface to simplify integration of clasp + in clingo/iclingo/clingcon + * fixed a couple of bugs in the eq-preprocessing + * fixed a bug in transformation of weight rules in incremental setting + * fixed a crash bug in experimental "--backprop" preprocessor + * fixed a bug that made clasp exit with code S_UNKNOWN (0) instead of S_UNSAT (20) + on unsat problems (computation was correct, though) + * fixed a command-line problem: option "--recursive-str" wrongly required an argument + * fixed a small problem in PB-frontend that led to wrong optima + +clasp 1.3.0: Tuesday, 20th October 2009 + * added an OPB front-end for solving linear Pseudo-Boolean constraint problems + and removed switch "--dimacs": the input format (Smodels-input, dimacs, or OPB) + is now automatically detected + * added switch "--opt-ignore" and "--opt-heu" for ignoring optimize statements during search, + resp. for setting the preferred value of literals occurring in minimize statements to false + * added switch "--estimate" for initializing size of learnt DB based on estimated problem complexity + * added switch "--reset-restart" for resetting restart schedule after each model found + * added switch "--asp09" for enabling ASP'09 competition output format + * added switch "--pre" for only printing preprocessed program + * added switch "--backprop" for enabling backpropagation in ASP-preprocessor. Note: EXPERIMENTAL FEATURE + * added option "--time-limit=" for setting a solving time limit of seconds + * added option "--verbose=" for controlling status messages, where + "--verbose=0" disables all status messages + "--verbose=1" is the default and prints basic status information + "--verbose=2" prints more status messages + * option "--quiet" no longer controls status messages; it only disables printing of models + * replaced option "--lookback=" with switch "--no-lookback" + * option "--save-progress" now takes an int to enable progress saving only on jumps >= > 0 + * default heuristic now disables MOMs-based top-level heuristic on large problems + * removed auto_lookahead option + * internal: added better interfaces for users of clasp library (iClingo, Clingcon, ...) + * fixed an optimization that violated gcc's strict aliasing rules and could led to crashes + * fixed a bug in the interplay of projective enumeration and sat-preprocessing that could + led to the enumeration of duplicate models (w.r.t the projection variables) + * fixed two bugs w.r.t optimization: first, clasp sometimes reported unknown optimum + although last model was optimal. Second, reported optima for minimize statements + containing facts were too low (optimality of models was correct, though) + * fixed a lookahead bug which occurred only together with sat-preprocessing + * fixed a small glitch in the recursive nogood minimization (switch "--recursive-str") + +clasp 1.2.1: Tuesday, 14th April 2009 + * fixed a crash bug in handling of weight rules with bound 0 (introduced in 1.2.0) + * added warning for minimization under projection + * reverted behaviour of "--number" to pre 1.2.0, i.e. + "--number" is again considered during optimization/computation of consequences + * removed spurious warning when "--number" is not explicitly set on optimization + * disabled printing of cautious consequences and optimization for UNSAT problems + * clasp now also prints current optimum on interrupt + +clasp 1.2.0: Tuesday, 10th March 2009 + * added "--project" for enabling projective enumeration + * added "--solution-recording" for enabling alternative enumeration mode based on (solution) nogoods + * added "--restart-on-model" for restarting search after each model (randomize enumeration) + * changed "--initial-lookahead" to "--initial-lookahead=", where gives the number of choices + to be selected by lookahead. If 0, lookahead is only used during preprocessing. + * removed "--opt-restart", which is now superseded by "--restart-on-model" + * added support for minimization during consequence computation + * added new (platform specific) timer which won't overflow after merely 36 minutes + * improved algorithm for handling recursive weight constraints + * fixed a bug where B+ atoms were falsely removed during preprocessing + * fixed a crash bug where clasp created bogus minimize statements for trivially UNSAT programs + * fixed a bug in the unfounded set checker where multiple SCCs were not always handled correctly + (bug was introduced in version 1.1.0) + * fixed ProgramBuilder::writeProgram so that it now correctly writes integrity constraints (internal) + +clasp 1.1.3: Monday, 24th November 2008 + * fixed handling of zero weighted atoms in minimize statements + * fixed a bug that caused "--deletion=no" to constantly delete all learnt nogoods + +clasp 1.1.2: Saturday, 25th October 2008 + * fixed a bug in the unfounded set checker (bug introduced in version 1.1.0) + +clasp 1.1.1: Monday, 13th October 2008 + * revised option names, help text, and error messages + * fixed a bug in the preprocessing/simplification of weight rules + * fixed several bugs in the handling of incremental programs + +clasp 1.1.0: Thursday, 31th July 2008 + * added "--sat-prepro" for SatElite-like preprocessing + * improved eq-Preprocessing and updated "--eq" to reflect new iterative algorithm + * added "--cautious" and "--brave" for cautious resp. brave reasoning + * added new optimization mode "--opt-rand": similar to optimize-one but restarts after each enumerated model + * added "--local-restarts" for local restarts as described in "Local Restarts" by Ryvchin and Strichman + * added new inner-outer restart strategy from PicoSat; quadratic-scheme remains default for now + * added "--expensiveccm" for expensive conflict clause minimization a la MiniSat; old Beame-scheme remains default for now + * replaced old VSIDS heuristic with MiniSat-like VSIDS heuristic + * changed growth strategy of learnt db + * changed default value of "--contraction" to 250 + * added support for incremental program update (internal) + +clasp 1.0.5: Monday, 31th December 2007 + * added "--minimization=all" for conflict clause minimization over all antecedent types + * changed default of "--minimization" from "bin" to "all". + * added "--save-progress" for RSat-like progress saving + * added "--optimize-value" for setting initial value(s) of optimize function + * optimized implementation of cardinality-/weight-rules + * simplified and improved implementation of ProgramBuilder and preprocessing + * unfounded set checker is now also backtrack-free for extended rules + * if "--rand-watches" is not used, watches in clauses are initialized to the two least watched literals + * fixed a bug regarding lookahead: if lookahead was not used as heuristic, literal dependencies were + not cleared; failed-literal detection skipped literals and thus did not always find all conflicts. + * fixed a bug that led to wrong answers/crashes if "loops=no" was used + * fixed a bug in preprocessing of minimize rules + +clasp 1.0.4: Wednesday, 22th August 2007 (updated: 24th August 2007) + * improved equivalence-preprocessing w.r.t bodies of size > 1 and slightly decreased memory usage + * improved look-back heuristics + * changed default value of "--trans-ext" to "no", i.e. extended rules are now handled natively by default + * simplified output and added "--stats" for enabling printing of extended statistics + * implemented non-recursive version of Tarjan's SCC algorithm to prevent stack overflows on large SCCs + * fixed another bug in handling of minimize rules + * fixed a bug in the command-line interface that led to a crash when an unknown heuristic was given + * fixed a bug concerning preprocessing/simplification of weight rules + * 08/24/2007: fixed a bug in preprocessing of cardinality/weight rules containing duplicate literals + * 10/25/2007: fixed a bug in preprocessing of satisfied bodies + * 10/27/2007: fixed a bug where weight constraints were inadvertently handled as cardinality constraints whenever the second weight equaled 1 + * 11/09/2007: fixed a bug in preprocessing of unsupported bodies + +clasp 1.0.3: Monday, 16th July 2007 + * added a new parameter "no" to "--loops" which disables learning of loop formulas + * improved performance of the equivalence-preprocessing algorithm on certain inputs + * adapted lookahead so that it can distinguish equivalent variables + * fixed a bug in the dimacs front-end that led to incorrect results on certain inputs + * if "--loops=shared" was used, learnt loop formulas were not added to the learnt db and thus not subject to nogood deletion + * fixed a bug that led to an infinite loop on certain optimization problems (bug was introduced in version 1.0.2) + * added signal handler to intercept standard signals like SIGTERM + +clasp 1.0.2: Friday, 15th June 2007 + * added "--eq" for equivalence-based preprocessing; added "--eq" to default command line + * "--contraction" expects threshold length for dynamic compression of long learnt clauses (default: 60) + * added a second (optional) grow factor to "--deletion" that is applied after every reduction + * added "--reduce-on-restart" that removes some of the learnt nogoods on every restart + * added "--optimize" for computing either one or all optimal solutions + * fixed a bug that led to a crash when "--evaluate-loops=false" was used together with + the default heuristic + * fixed a bug that led to a crash on certain problems when lookahead was used + in lookback mode + + +clasp 1.0.1: Wednesday, 28th March 2007 + * some fixes to make clasp compile on gcc 4.1 and gcc 4.2 + * fixed a bug in the RNG that led to array-out-of-bounds accesses using certain + standard library implementations + * fixed a bug in the (native) handling of constraint-rules - clasp treated + their bodies as sets and spuriously removed duplicate atoms + * fixed a bug in the handling of compute-statements - clasp failed to produce + a conflict if the compute-statement forced an unsupported atom to be true. + +clasp 1.0: Wednesday, 7th March 2007 + * added native support for extended rules (disabled by default) + * added support for optimize statements + * added "--dimacs" for reading CNF-problems in dimacs-format + * added "--contraction": dynamic clause compression for very long learnt clauses + * added "--minimize": (weak) conflict clause minimization + * added "--lookahead" for (configurable) failed-literal-detection + * added "--rand-watches" for random initialization of watches + * added "--rand-prop" for support of occasional random-choices + * added "--supp-models" for computation of supported models + * added "--bounded-restarts" for allowing (bounded) restarts afer solution was found + * new and more dynamic VSIDS heuristic + * new smodels-like lookahead heuristic + * changed nogood deletion heuristic + * removed -s option (replaced with --initial-lookahead) + * renamed --no-learn option (now: --lookback=no) + +clasp Asp-Comp: Revision: Wednesday, 20th December 2006 + * scaled-down version for the ASP-competition + +clasp RC4: Wednesday, 25th October 2006 + * added new restart strategies + * addad a randomization strategy similar to satzoo's "Burst of random variable orders" + * added an option to initialize the random number generator + * fixed bug in LparseReader that caused an infinite loop on bad input + * fixed implementation of BerkMin heuristic + * fixed potential memory-leak in clause deletion code + +clasp RC3: Friday, 29th September 2006 + * transformation of extended rules to normal rules + * some low-level stuff to improve performance + +clasp RC2: Monday, 19th June 2006 + * version produced for paper + * major bug fixes + +clasp RC1: Friday, 16th June 2006 + * initial version + diff --git a/clasp/CMakeLists.txt b/clasp/CMakeLists.txt new file mode 100644 index 000000000..4b7e64dd4 --- /dev/null +++ b/clasp/CMakeLists.txt @@ -0,0 +1,157 @@ +cmake_minimum_required(VERSION 3.1) +project(CLASP VERSION 3.3.9 LANGUAGES CXX) +# Enable folders in IDEs like Visual Studio +set_property(GLOBAL PROPERTY USE_FOLDERS ON) +if (POLICY CMP0063) + cmake_policy(SET CMP0063 NEW) +endif() +if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "No build type selected - using 'Release'") + set(CMAKE_BUILD_TYPE "Release") +endif() +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +include(GNUInstallDirs) + +# Configuration options +option(CLASP_BUILD_APP "whether or not to build the clasp application" ON) +option(CLASP_BUILD_STATIC "whether or not to link statically (if supported)" OFF) +option(CLASP_BUILD_TESTS "whether or not to build clasp unit tests" OFF) +option(CLASP_BUILD_EXAMPLES "whether or not to build examples" OFF) +option(CLASP_BUILD_WITH_THREADS "whether or not to build clasp with threading support (requires C++11)" ON) +option(CLASP_INSTALL_LIB "whether or not to install libclasp" OFF) +option(CLASP_INSTALL_VERSIONED "whether to use a versioned install layout" OFF) +option(CLASP_USE_LOCAL_LIB_POTASSCO "whether to use the libpotassco submodule" ON) + +if (NOT MSVC) + if (NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + endif() + if (NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + endif() + if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + endif() +else() + set(VC_RELEASE_LINK_OPTIONS /LTCG) + SET(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${VC_RELEASE_LINK_OPTIONS}") + SET(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${VC_RELEASE_LINK_OPTIONS}") + SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${VC_RELEASE_LINK_OPTIONS}") + SET(CMAKE_STATIC_LINKER_FLAGS_RELEASE "${CMAKE_STATIC_LINKER_FLAGS_RELEASE} ${VC_RELEASE_LINK_OPTIONS}") + if (CLASP_BUILD_STATIC) + # force static runtime + string(REGEX REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") + endif() +endif() +if (CLASP_INSTALL_VERSIONED) + set(clasp_include_dest "clasp-${CLASP_VERSION}") + set(clasp_library_dest "clasp-${CLASP_VERSION}") + set(cmake_dest "clasp-${CLASP_VERSION}/cmake") +else() + set(clasp_include_dest ".") + set(clasp_library_dest ".") + set(cmake_dest "cmake/Clasp") +endif() + +if (CLASP_INSTALL_LIB AND NOT CMAKE_INSTALL_LIBDIR) + message(STATUS "LIBDIR no set - using lib") + set(CMAKE_INSTALL_LIBDIR lib) +endif() + + +# C++11 is required for building with threads +if (CLASP_BUILD_WITH_THREADS) + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS ON) + # some versions of findThreads will fail if C is not enabled + enable_language(C) + find_package(Threads REQUIRED) + + # Add libatomic if necessary + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_USE_PTHREADS_INIT) + include (CheckCXXSourceCompiles) + set (OLD_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) + set (OLD_CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + list(APPEND CMAKE_REQUIRED_FLAGS "-std=c++11") + list(APPEND CMAKE_REQUIRED_LIBRARIES Threads::Threads) + check_cxx_source_compiles(" +#include +#include +std::atomic x (0); +int main() { + uint64_t i = x.load(std::memory_order_relaxed); + return 0; +} +" CLASP_HAS_WORKING_LIBATOMIC) + set (CMAKE_REQUIRED_FLAGS ${OLD_CMAKE_REQUIRED_FLAGS}) + set (CMAKE_REQUIRED_LIBRARIES ${OLD_CMAKE_REQUIRED_LIBRARIES}) + if (NOT CLASP_HAS_WORKING_LIBATOMIC) + check_library_exists(atomic __atomic_fetch_add_4 "" CLASP_HAS_LIBATOMIC) + if (CLASP_HAS_LIBATOMIC) + set_property(TARGET Threads::Threads APPEND PROPERTY INTERFACE_LINK_LIBRARIES "atomic") + endif() + endif() + endif() +endif() + +# Check for or build external dependency +if (NOT CLASP_USE_LOCAL_LIB_POTASSCO) + find_package(Potassco 1.0 REQUIRED CONFIG) +else() + if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/libpotassco/CMakeLists.txt) + message(STATUS "Potassco is not installed - fetching submodule") + execute_process(COMMAND git submodule update --init WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_QUIET) + else() + message(STATUS "Potassco is not installed - using local copy") + endif() + set(LIB_POTASSCO_BUILD_APP ${CLASP_BUILD_APP} CACHE BOOL "") + set(LIB_POTASSCO_INSTALL_LIB ${CLASP_INSTALL_LIB} CACHE BOOL "") + add_subdirectory(libpotassco) +endif() + +# Build clasp library +add_subdirectory(src) + +# Build optional targets +if(CLASP_BUILD_TESTS) + enable_testing() + add_subdirectory(tests) +endif() +# optional doc target +find_package(Doxygen) +if(DOXYGEN_FOUND) + set(doxyfile "${CMAKE_CURRENT_SOURCE_DIR}/doc/api/clasp.doxy") + add_custom_target(doc_clasp + COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/doc/api" + COMMENT "Generating documentation..." + VERBATIM) + set_target_properties(doc_clasp PROPERTIES FOLDER doc) +endif() + +if(CLASP_BUILD_APP) + add_subdirectory(app) +endif() + +if(CLASP_BUILD_EXAMPLES) + add_subdirectory(examples) +endif() + +# Export +if (CLASP_INSTALL_LIB) + include(CMakePackageConfigHelpers) + configure_package_config_file( + ${PROJECT_SOURCE_DIR}/cmake/ClaspConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/ClaspConfig.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/${cmake_dest}) + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/ClaspConfigVersion.cmake + COMPATIBILITY SameMajorVersion) + install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/ClaspConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/ClaspConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/${cmake_dest}) + install(EXPORT ClaspTargets DESTINATION "${CMAKE_INSTALL_LIBDIR}/${cmake_dest}") +endif() diff --git a/clasp/LICENSE b/clasp/LICENSE new file mode 100644 index 000000000..bf8b467a4 --- /dev/null +++ b/clasp/LICENSE @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2015-2017 Benjamin Kaufmann + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/clasp/README.md b/clasp/README.md new file mode 100644 index 000000000..39e595ded --- /dev/null +++ b/clasp/README.md @@ -0,0 +1,128 @@ +# clasp + +clasp is an answer set solver for (extended) normal and disjunctive logic programs. +It is part of the [Potassco](https://potassco.org) project for *Answer Set Programming* (ASP). +The primary algorithm of clasp relies on conflict-driven nogood learning, +a technique that proved very successful for satisfiability checking (SAT). +clasp has been genuinely developed for answer set solving but can +also be applied as a (Max-)SAT or PB solver or as a C++ library in another program. +It provides different reasoning modes and other advanced features including: + + - [Enumeration][enum] and [Optimization][opt] of ([Projected][proj]) Solutions, + - Cautious and Brave Reasoning, + - [Advanced Disjunctive Solving][claspD2], + - [Parallel (multithreaded) solving][claspmt], + - [Domain heuristic][hclasp] modifications, + - [Unsatisfiable-core based optimization][unclasp], + - [ASP/SAT/PB modulo acyclicity][acyc], + - Different input formats including [smodels][smodels], [aspif][aspif], [dimacs][dimacs] and [opb][opb]. + +Detailed information (including a User's manual), source code, +and pre-compiled binaries are available at: http://potassco.org/ + +## LICENSE + clasp is distributed under the MIT License. + + See LICENSE for details regarding the license. + +## PACKAGE CONTENTS + LICENSE - The MIT License + CHANGES - Major changes between versions + README.md - This file + CMakeLists.txt - Configuration file for building clasp with CMake + cmake/ - Module directory for additional CMake scripts + app/ - Source code directory of the command-line interface + clasp/ - Header directory of the clasp library + src/ - Source code directory of the clasp library + tests/ - Unit tests of the clasp library + examples/ - Examples using the clasp library + libpotassco/ - Directory of the potassco library + tools/ - Some additional files + + +## BUILDING & INSTALLING + The preferred way to build clasp is to use [CMake][cmake] version 3.1 or later + together with a C++ compiler that supports C++11. + + The following options can be used to configure the build: + + CLASP_BUILD_APP : whether or not to build the clasp application + CLASP_BUILD_TESTS : whether or not to build clasp unit tests + CLASP_BUILD_EXAMPLES : whether or not to build examples + CLASP_BUILD_WITH_THREADS: whether or not to build clasp with threading support + (requires C++11) + + For example, to build clasp in release mode in directory ``: + + cmake -H. -B + cmake --build + + To install clasp afterwards: + + cmake --build --target install + + To set the installation prefix, run + `cmake` with option `-DCMAKE_INSTALL_PREFIX=`. + + Finally, you can always skip installation and simply copy the + clasp executable to a directory of your choice. + +## DOCUMENTATION + A User's Guide is available from http://potassco.org/ + + Source code documentation can be generated with [Doxygen][doxygen]. + Either explicitly: + + cd libclasp/doc/api + doxygen clasp.doxy + + or via the `doc_clasp` target when using cmake. + +## USAGE + clasp reads problem instances either from stdin, e.g + + cat problem | clasp + + or from a given file, e.g + + clasp problem + + Type + + clasp --help + + to get a basic overview of options supported by clasp or + + clasp --help={2,3} + + for a more detailed list. + + In addition to printing status information, clasp also + provides information about the computation via its exit status. + The exit status is either one or a combination of: + + 0 : search was not started because of some option (e.g. '--help') + 1 : search was interrupted + 10 : problem was found to be satisfiable + 20 : problem was proved to be unsatisfiable + + Exit codes 1 and 11 indicate that search was interrupted before + the final result was computed. Exit code 30 indicates that either + all models were found (enumeration), optimality was proved (optimization), + or all consequences were computed (cautious/brave reasoning). + Finally, exit codes greater than 32 are used to signal errors. + +[enum]: https://www.cs.uni-potsdam.de/wv/publications/#DBLP:conf/lpnmr/GebserKNS07 +[proj]: https://www.cs.uni-potsdam.de/wv/publications/#DBLP:conf/cpaior/GebserKS09 +[opt]: https://www.cs.uni-potsdam.de/wv/publications/#DBLP:journals/tplp/GebserKS11 +[claspmt]: https://www.cs.uni-potsdam.de/wv/publications/#DBLP:journals/tplp/GebserKS12 +[claspD2]: https://www.cs.uni-potsdam.de/wv/publications/#DBLP:conf/ijcai/GebserKS13 +[hclasp]: https://www.cs.uni-potsdam.de/wv/publications/#DBLP:conf/aaai/GebserKROSW13 +[unclasp]: https://www.cs.uni-potsdam.de/wv/publications/#DBLP:conf/iclp/AndresKMS12 +[acyc]: https://www.cs.uni-potsdam.de/wv/publications/#DBLP:journals/fuin/BomansonGJKS16 +[aspif]: https://www.cs.uni-potsdam.de/wv/publications/#DBLP:conf/iclp/GebserKKOSW16x +[smodels]: http://www.tcs.hut.fi/Software/smodels/lparse.ps +[dimacs]: http://www.satcompetition.org/2009/format-benchmarks2009.html +[opb]: https://www.cril.univ-artois.fr/PB09/solver_req.html +[doxygen]: https://www.stack.nl/~dimitri/doxygen/ +[cmake]: https://cmake.org/ diff --git a/clasp/app/CMakeLists.txt b/clasp/app/CMakeLists.txt new file mode 100644 index 000000000..976eab7a5 --- /dev/null +++ b/clasp/app/CMakeLists.txt @@ -0,0 +1,22 @@ +set(files + main.cpp) +add_executable(clasp ${files}) +set_target_properties(clasp PROPERTIES FOLDER exe) +if (NOT CMAKE_INSTALL_BINDIR) + message(STATUS "BINDIR not set - using bin") + set(CMAKE_INSTALL_BINDIR "bin") +endif() +if (CLASP_BUILD_STATIC AND UNIX AND NOT APPLE) + if (CLASP_BUILD_WITH_THREADS) + string(CONCAT refs "-Wl,-u,pthread_cancel,-u,pthread_cond_broadcast," + "-u,pthread_cond_destroy,-u,pthread_cond_signal," + "-u,pthread_cond_timedwait,-u,pthread_cond_wait," + "-u,pthread_create,-u,pthread_detach,-u,pthread_join," + "-u,pthread_equal") + target_link_libraries(clasp ${refs}) + endif() + target_link_libraries(clasp "-static") +endif() +target_link_libraries(clasp libclasp) + +install(TARGETS clasp EXPORT clasp DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/clasp/app/main.cpp b/clasp/app/main.cpp new file mode 100644 index 000000000..f44176c60 --- /dev/null +++ b/clasp/app/main.cpp @@ -0,0 +1,47 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#include +///////////////////////////////////////////////////////////////////////////////////////// +// main - entry point +///////////////////////////////////////////////////////////////////////////////////////// +// #define CHECK_HEAP +#if defined (_MSC_VER) && defined(CHECK_HEAP) && _MSC_VER >= 1200 +#include +#endif +int main(int argc, char** argv) { +#if defined (_MSC_VER) && defined (CHECK_HEAP) && _MSC_VER >= 1200 + _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | + CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF | + _CRTDBG_CHECK_ALWAYS_DF); + + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR ); + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR ); +#endif + Clasp::Cli::ClaspApp app; + return app.main(argc, argv); +} diff --git a/clasp/clasp/asp_preprocessor.h b/clasp/clasp/asp_preprocessor.h new file mode 100644 index 000000000..577d04598 --- /dev/null +++ b/clasp/clasp/asp_preprocessor.h @@ -0,0 +1,131 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +#ifndef CLASP_PREPROCESSOR_H_INCLUDED +#define CLASP_PREPROCESSOR_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif +#include +#include +namespace Clasp { namespace Asp { + +/** + * \addtogroup asp + */ +//@{ + +//! Preprocesses (i.e. simplifies) a logic program. +/*! + * Preprocesses (i.e. simplifies) a logic program and associates variables with + * the nodes of the simplified logic program. + */ +class Preprocessor { +public: + Preprocessor() : prg_(0), dfs_(true) {} + //! Possible eq-preprocessing types. + enum EqType { + no_eq, //!< No eq-preprocessing, associate a new var with each supported atom and body. + full_eq //!< Check for all kinds of equivalences between atoms and bodies. + }; + + const LogicProgram* program() const { return prg_; } + LogicProgram* program() { return prg_; } + + //! Starts preprocessing of the logic program. + /*! + * Computes the maximum consequences of prg and associates a variable + * with each supported atom and body. + * \param prg The logic program to preprocess. + * \param t Type of eq-preprocessing. + * \param maxIters If t == full_eq, maximal number of iterations during eq preprocessing. + * \param dfs If t == full_eq, classify in df-order (true) or bf-order (false). + */ + bool preprocess(LogicProgram& prg, EqType t, uint32 maxIters, bool dfs = true) { + prg_ = &prg; + dfs_ = dfs; + type_ = t; + return t == full_eq + ? preprocessEq(maxIters) + : preprocessSimple(); + } + + bool eq() const { return type_ == full_eq; } + Var getRootAtom(Literal p) const { return p.id() < litToNode_.size() ? litToNode_[p.id()] : varMax; } + void setRootAtom(Literal p, uint32 atomId) { + if (p.id() >= litToNode_.size()) litToNode_.resize(p.id()+1, varMax); + litToNode_[p.id()] = atomId; + } +private: + Preprocessor(const Preprocessor&); + Preprocessor& operator=(const Preprocessor&); + bool preprocessEq(uint32 maxIters); + bool preprocessSimple(); + // ------------------------------------------------------------------------ + typedef PrgHead* const * HeadIter; + typedef std::pair HeadRange; + // Eq-Preprocessing + struct BodyExtra { + BodyExtra() : known(0), mBody(0), bSeen(0) {} + uint32 known :30; // Number of predecessors already classified, only used for bodies + uint32 mBody : 1; // A flag for marking bodies + uint32 bSeen : 1; // First time we see this body? + }; + bool classifyProgram(const VarVec& supportedBodies); + ValueRep simplifyClassifiedProgram(const HeadRange& atoms, bool more, VarVec& supported); + PrgBody* addBodyVar(uint32 bodyId); + bool addHeadsToUpper(PrgBody* body); + bool addHeadToUpper(PrgHead* head, PrgEdge support); + bool propagateAtomVar(PrgAtom*, PrgEdge source); + bool propagateAtomValue(PrgAtom*, ValueRep val, PrgEdge source); + bool mergeEqBodies(PrgBody* b, Var rootId, bool equalLits); + bool hasRootLiteral(PrgBody* b) const; + bool superfluous(PrgBody* b) const; + ValueRep simplifyHead(PrgHead* h, bool reclassify); + ValueRep simplifyBody(PrgBody* b, bool reclassify, VarVec& supported); + uint32 nextBodyId(VarVec::size_type& idx) { + if (follow_.empty() || idx == follow_.size()) { return varMax; } + if (dfs_) { + uint32 id = follow_.back(); + follow_.pop_back(); + return id; + } + return follow_[idx++];; + } + // ------------------------------------------------------------------------ + typedef PodVector::type BodyData; + LogicProgram* prg_; // program to preprocess + VarVec follow_; // bodies yet to classify + BodyData bodyInfo_; // information about the program nodes + VarVec litToNode_;// the roots of our equivalence classes + uint32 pass_; // current iteration number + uint32 maxPass_; // force stop after maxPass_ iterations + EqType type_; // type of eq-preprocessing + bool dfs_; // classify bodies in DF or BF order +}; +//@} +} } +#endif + diff --git a/clasp/clasp/cb_enumerator.h b/clasp/clasp/cb_enumerator.h new file mode 100644 index 000000000..8d74177a6 --- /dev/null +++ b/clasp/clasp/cb_enumerator.h @@ -0,0 +1,70 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_CB_ENUMERATOR_H +#define CLASP_CB_ENUMERATOR_H + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +namespace Clasp { + +//! Enumerator for computing the brave/cautious consequences of a logic program. +/*! + * \ingroup enumerator + */ +class CBConsequences : public Enumerator { +public: + enum Type { + Brave = Model::Brave, + Cautious = Model::Cautious, + }; + enum Algo { Default, Query }; + /*! + * \param type Type of consequences to compute. + * \param a Type of algorithm to apply if type is Cautious. + */ + explicit CBConsequences(Type type, Algo a = Default); + ~CBConsequences(); + int modelType() const { return type_; } + bool exhaustive()const { return true; } + bool supportsSplitting(const SharedContext& problem) const; + int unsatType() const; +private: + class CBFinder; + class QueryFinder; + class SharedConstraint; + ConPtr doInit(SharedContext& ctx, SharedMinimizeData* m, int numModels); + void addLit(SharedContext& ctx, Literal p); + void addCurrent(Solver& s, LitVec& con, ValueVec& m, uint32 rootL = 0); + LitVec cons_; + SharedConstraint* shared_; + Type type_; + Algo algo_; +}; + +} +#endif diff --git a/clasp/clasp/clasp_facade.h b/clasp/clasp/clasp_facade.h new file mode 100644 index 000000000..70122ffd2 --- /dev/null +++ b/clasp/clasp/clasp_facade.h @@ -0,0 +1,477 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_CLASP_FACADE_H_INCLUDED +#define CLASP_CLASP_FACADE_H_INCLUDED +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#if CLASP_HAS_THREADS +#include +namespace Clasp { + //! Options for controlling enumeration and solving. + struct SolveOptions : Clasp::mt::ParallelSolveOptions, EnumOptions {}; +} +#else +namespace Clasp { + struct SolveOptions : Clasp::BasicSolveOptions, EnumOptions {}; +} +#endif + +/*! + * \file + * \brief High-level API + * + * This file provides a facade around the clasp library. + * I.e. a simplified interface for (multishot) solving a problem using + * some configuration (set of parameters). + * \ingroup facade + */ +namespace Clasp { +///////////////////////////////////////////////////////////////////////////////////////// +// Configuration +///////////////////////////////////////////////////////////////////////////////////////// +/*! + * \defgroup facade Facade + * \brief Simplified interface for (multishot) solving. + * + * @{ + */ +//! Configuration object for configuring solving via the ClaspFacade. +class ClaspConfig : public BasicSatConfig { +public: + //! Interface for injecting user-provided configurations. + class Configurator { + public: + virtual ~Configurator(); + virtual void prepare(SharedContext&); + virtual bool applyConfig(Solver& s) = 0; + virtual void unfreeze(SharedContext&); + }; + typedef BasicSatConfig UserConfig; + typedef Solver** SolverIt; + typedef Asp::LogicProgram::AspOptions AspOptions; + ClaspConfig(); + ~ClaspConfig(); + // Base interface + void prepare(SharedContext&); + void reset(); + Configuration* config(const char*); + //! Adds an unfounded set checker to the given solver if necessary. + /*! + * If asp.suppMod is false and the problem in s is a non-tight asp-problem, + * the function adds an unfounded set checker to s. + */ + bool addPost(Solver& s) const; + // own interface + UserConfig* testerConfig() const { return tester_; } + UserConfig* addTesterConfig(); + //! Registers c as additional callback for when addPost() is called. + /*! + * \param c Additional configuration to apply. + * \param ownership If Ownership_t::Acquire, ownership of c is transferred to the configuration object. + * \param once Whether c should be called once in the first step or also in each subsequent step. + */ + void addConfigurator(Configurator* c, Ownership_t::Type ownership = Ownership_t::Retain, bool once = true); + void unfreeze(SharedContext&); + SolveOptions solve; /*!< Options for solve algorithm and enumerator. */ + AspOptions asp; /*!< Options for asp preprocessing. */ + ParserOptions parse; /*!< Options for input parser. */ +private: + struct Impl; + ClaspConfig(const ClaspConfig&); + ClaspConfig& operator=(const ClaspConfig&); + UserConfig* tester_; + Impl* impl_; +}; +///////////////////////////////////////////////////////////////////////////////////////// +// ClaspFacade +///////////////////////////////////////////////////////////////////////////////////////// +//! Result of a solving step. +struct SolveResult { + //! Possible solving results. + enum Base { + UNKNOWN = 0, //!< Satisfiability unknown - a given solve limit was hit. + SAT = 1, //!< Problem is satisfiable (a model was found). + UNSAT = 2, //!< Problem is unsatisfiable. + }; + //! Additional flags applicable to a solve result. + enum Ext { + EXT_EXHAUST = 4, //!< Search space is exhausted. + EXT_INTERRUPT= 8, //!< The run was interrupted from outside. + }; + bool sat() const { return *this == SAT; } + bool unsat() const { return *this == UNSAT; } + bool unknown() const { return *this == UNKNOWN; } + bool exhausted() const { return (flags & EXT_EXHAUST) != 0; } + bool interrupted()const { return (flags & EXT_INTERRUPT) != 0; } + operator Base() const { return static_cast(flags & 3u);} + uint8 flags; //!< Set of Base and Ext flags. + uint8 signal; //!< Term signal or 0. +}; + +//! A bitmask type for representing supported solve modes. +struct SolveMode_t { + //! Named constants. + POTASSCO_ENUM_CONSTANTS(SolveMode_t, + Default = 0, /**< Solve synchronously in current thread. */ + Async = 1, /**< Solve asynchronously in worker thread. */ + Yield = 2, /**< Yield models one by one via handle. */ + AsyncYield); + friend inline SolveMode_t operator|(SolveMode_t::E x, SolveMode_t::E y) { return SolveMode_t(static_cast(x)|static_cast(y)); } + friend inline SolveMode_t operator|(SolveMode_t x, SolveMode_t::E y) { return SolveMode_t(static_cast(x)|static_cast(y)); } + friend inline SolveMode_t operator|(SolveMode_t::E x, SolveMode_t y) { return SolveMode_t(static_cast(x)|static_cast(y)); } +}; +//! Provides a simplified interface to the services of the clasp library. +class ClaspFacade : public ModelHandler { + struct SolveData; + struct SolveStrategy; +public: + //! A handle to a possibly asynchronously computed SolveResult. + class SolveHandle { + public: + typedef SolveResult Result; + typedef const Model* ModelRef; + typedef const LitVec* CoreRef; + explicit SolveHandle(SolveStrategy*); + SolveHandle(const SolveHandle&); + ~SolveHandle(); + SolveHandle& operator=(SolveHandle temp) { swap(*this, temp); return *this; } + friend void swap(SolveHandle& lhs, SolveHandle& rhs) { std::swap(lhs.strat_, rhs.strat_); } + /*! + * \name Blocking functions + * @{ */ + //! Waits until a result is ready and returns it. + Result get() const; + //! Returns an unsat core if get() returned unsat under assumptions. + CoreRef unsatCore() const; + //! Waits until a result is ready and returns it if it is a model. + /*! + * \note If the corresponding solve operation was not started with + * SolveMode_t::Yield, the function always returns 0. + * \note A call to resume() invalidates the returned model and starts + * the search for the next model. + */ + ModelRef model() const; + //! Waits until a result is ready. + void wait() const; + //! Waits for a result but for at most sec seconds. + bool waitFor(double sec)const; + //! Tries to cancel the active operation. + void cancel() const; + //! Behaves like resume() followed by return model() != 0. + bool next() const; + //@} + /*! + * \name Non-blocking functions + * @{ */ + //! Tests whether a result is ready. + bool ready() const; + //! Tests whether the operation was interrupted and if so returns the interruption signal. + int interrupted() const; + //! Tests whether a result is ready and has a stored exception. + bool error() const; + //! Tests whether the operation is still active. + bool running() const; + //! Releases ownership of the active model and schedules search for the next model. + void resume() const; + //@} + private: + SolveStrategy* strat_; + }; + typedef SolveResult Result; + typedef Potassco::AbstractStatistics AbstractStatistics; + //! Type summarizing one or more solving steps. + struct Summary { + typedef const ClaspFacade* FacadePtr; + void init(ClaspFacade& f); + //! Logic program elements added in the current step or 0 if not an asp problem. + const Asp::LpStats* lpStep() const; + //! Logic program stats or 0 if not an asp problem. + const Asp::LpStats* lpStats() const; + //! Active problem. + const SharedContext& ctx() const { return facade->ctx; } + /*! + * \name Result functions + * Solve and enumeration result - not accumulated. + * @{ + */ + bool sat() const { return result.sat(); } + bool unsat() const { return result.unsat(); } + bool complete() const { return result.exhausted(); } + bool optimum() const { return costs() && (complete() || model()->opt); } + const Model* model() const; + const LitVec* unsatCore() const; + const char* consequences() const; /**< Cautious/brave reasoning active? */ + bool optimize() const; /**< Optimization active? */ + const SumVec* costs() const; /**< Models have associated costs? */ + uint64 optimal() const; /**< Number of optimal models found. */ + bool hasLower() const; + SumVec lower() const; + //@} + //! Visits this summary object. + void accept(StatsVisitor& out) const; + FacadePtr facade; //!< Facade object of this run. + double totalTime; //!< Total wall clock time. + double cpuTime; //!< Total cpu time. + double solveTime; //!< Wall clock time for solving. + double unsatTime; //!< Wall clock time to prove unsat. + double satTime; //!< Wall clock time to first model. + uint64 numEnum; //!< Total models enumerated. + uint64 numOptimal;//!< Optimal models enumerated. + uint32 step; //!< Step number (multishot solving). + Result result; //!< Result of step. + }; + ClaspFacade(); + ~ClaspFacade(); + + /*! + * \name Query functions + * Functions for checking the state of this object. + * @{ */ + //! Returns whether the problem is still valid. + bool ok() const { return program() ? program()->ok() : ctx.ok(); } + //! Returns whether the active step is ready for solving. + bool prepared() const; + //! Returns whether the active step is currently being solved. + bool solving() const; + //! Returns whether the active step has been solved, i.e., has a result. + bool solved() const; + //! Returns whether solving of the active step was interrupted. + bool interrupted() const; + //! Returns the summary of the active step. + const Summary& summary() const { return step_; } + //! Returns the summary of the active (accu = false) or all steps. + const Summary& summary(bool accu) const; + //! Returns solving statistics or throws std::logic_error if solving() is true. + AbstractStatistics*getStats() const; + //! Returns the active configuration. + const ClaspConfig* config() const { return config_;} + //! Returns the current solving step (starts at 0). + int step() const { return (int)step_.step;} + //! Returns the result of the active step (unknown if run is not yet completed). + Result result() const { return step_.result; } + //! Returns the active program or 0 if it was already released. + ProgramBuilder* program() const { return builder_.get(); } + //! Returns whether program updates are enabled. + bool incremental() const; + //! Returns the active enumerator or 0 if there is none. + Enumerator* enumerator() const; + //@} + + //! Event type used to signal that a new step has started. + struct StepStart : Event_t { + explicit StepStart(const ClaspFacade& f) : Event_t(subsystem_facade, verbosity_quiet), facade(&f) {} + const ClaspFacade* facade; + }; + //! Event type used to signal that a solve step has terminated. + struct StepReady : Event_t { + explicit StepReady(const Summary& x) : Event_t(subsystem_facade, verbosity_quiet), summary(&x) {} + const Summary* summary; + }; + + SharedContext ctx; //!< Context-object used to store problem. + + /*! + * \name Start functions + * Functions for defining a problem. + * Calling one of the start functions discards any previous problem + * and emits a StepStart event. + * @{ */ + //! Starts definition of an ASP-problem. + Asp::LogicProgram& startAsp(ClaspConfig& config, bool enableProgramUpdates = false); + //! Starts definition of a SAT-problem. + SatBuilder& startSat(ClaspConfig& config); + //! Starts definition of a PB-problem. + PBBuilder& startPB(ClaspConfig& config); + //! Starts definition of a problem of type t. + ProgramBuilder& start(ClaspConfig& config, ProblemType t); + //! Starts definition of a problem given in stream. + ProgramBuilder& start(ClaspConfig& config, std::istream& stream); + //! Enables support for program updates if supported by the program. + /*! + * \pre program() != 0 and not prepared(). + * \return true if program updates are supported. Otherwise, false. + */ + bool enableProgramUpdates(); + //! Enables support for (asynchronous) solve interrupts. + void enableSolveInterrupts(); + //! Disables program disposal in non-incremental mode after problem has been prepared for solving. + /*! + * \pre program() != 0 and not prepared(). + */ + void keepProgram(); + //! Tries to detect the problem type from the given input stream. + static ProblemType detectProblemType(std::istream& str); + //! Tries to read the next program part from the stream passed to start(). + /*! + * \return false if nothing was read because the stream is exhausted, solving was interrupted, + * or the problem is unconditionally unsat. + */ + bool read(); + + //@} + + /*! + * \name Solve functions + * Functions for solving a problem. + * @{ */ + + enum EnumMode { enum_volatile, enum_static }; + + //! Finishes the definition of a problem and prepares it for solving. + /*! + * \pre !solving() + * \post prepared() || !ok() + * \param m Mode to be used for handling enumeration-related knowledge. + * If m is enum_volatile, enumeration knowledge is learnt under an + * assumption that is retracted on program update. Otherwise, + * no special assumption is used and enumeration-related knowledge + * might become unretractable. + * \note If solved() is true, prepare() first starts a new solving step. + */ + void prepare(EnumMode m = enum_volatile); + + //! Solves the current problem. + /*! + * If prepared() is false, the function first calls prepare() to prepare the problem for solving. + * \pre !solving() + * \post solved() + * \param a A list of unit-assumptions under which solving should operate. + * \param eh An optional event handler that is notified on each model and + * once the solve operation has completed. + */ + Result solve(const LitVec& a = LitVec(), EventHandler* eh = 0); + Result solve(EventHandler* eh) { return solve(LitVec(), eh); } + + //! Solves the current problem using the given solve mode. + /*! + * If prepared() is false, the function first calls prepare() to prepare the problem for solving. + * \pre !solving() + * \param mode The solve mode to use. + * \param a A list of unit-assumptions under which solving should operate. + * \param eh An optional event handler that is notified on each model and + * once the solve operation has completed. + * \throws std::logic_error if mode contains SolveMode_t::Async but thread support is disabled. + * \throws std::runtime_error if mode contains SolveMode_t::Async but solve is unable to start a thread. + * + * \note If mode contains SolveMode_t::Async, the optional event handler is notified in the + * context of the asynchronous thread. + * + * \note If mode contains SolveMode_t::Yield, models are signaled one by one via the + * returned handle object. + * It is the caller's responsibility to finish the solve operation, + * either by extracting models until SolveHandle::model() returns 0, or + * by calling SolveHandle::cancel(). + * + * To iterate over models one by one use a loop like: + * \code + * SolveMode_t p = ... + * for (auto it = facade.solve(p|SolveMode_t::Yield); it.model(); it.resume()) { + * printModel(*it.model()); + * } + * \endcode + */ + SolveHandle solve(SolveMode_t mode, const LitVec& a = LitVec(), EventHandler* eh = 0); + + //! Tries to interrupt the active solve operation. + /*! + * The function sends the given signal to the active solve operation. + * If no solve operation is active (i.e. solving() is false), the signal + * is queued and applied to the next solve operation. + * + * \param sig The signal to raise or 0, to re-raises a previously queued signal. + * \return false if no operation was interrupted, because + * there is no active solve operation, + * or the operation does not support interrupts, + * or sig was 0 and there was no queued signal. + * + * \see enableSolveInterrupts() + */ + bool interrupt(int sig); + + //! Forces termination of the current solving step. + /*! + * \post solved() + * \return summary(true) + */ + const Summary& shutdown(); + + //! Starts update of the active problem. + /*! + * \pre solving() is false and program updates are enabled (incremental() is true). + * \post !solved() + * \param updateConfig If true, the function applies any configuration changes. + * \param sigQ An action to be performed for any queued signal. + * The default is to apply the signal to the next solve operation, while + * SIGN_IGN can be used to discard queued signals. + */ + ProgramBuilder& update(bool updateConfig, void (*sigQ)(int)); + ProgramBuilder& update(bool updateConfig = false); + //@} +private: + struct Statistics; + typedef SingleOwnerPtr BuilderPtr; + typedef SingleOwnerPtr SolvePtr; + typedef SingleOwnerPtr SummaryPtr; + typedef SingleOwnerPtr StatsPtr; + void init(ClaspConfig& cfg, bool discardProblem); + void initBuilder(ProgramBuilder* in); + bool isAsp() const { return program() && type_ == Problem_t::Asp; } + void discardProblem(); + void startStep(uint32 num); + Result stopStep(int signal, bool complete); + void updateStats(); + bool onModel(const Solver& s, const Model& m); + void doUpdate(ProgramBuilder* p, bool updateConfig, void (*sig)(int)); + ProblemType type_; + Summary step_; + LitVec assume_; + ClaspConfig* config_; + BuilderPtr builder_; + SummaryPtr accu_; + StatsPtr stats_; // statistics: only if requested + SolvePtr solve_; // NOTE: last so that it is destroyed first; +}; + +/** + * \example example2.cpp + * This is an example of how to use the ClaspFacade class for basic solving. + * + * \example example3.cpp + * This is an example of how to use the ClaspFacade class for generator-based solving. + */ + +//!@} + +} +#endif diff --git a/clasp/clasp/claspfwd.h b/clasp/clasp/claspfwd.h new file mode 100644 index 000000000..a8a5ea380 --- /dev/null +++ b/clasp/clasp/claspfwd.h @@ -0,0 +1,77 @@ +// +// Copyright (c) 2013-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_CLASP_FWD_H_INCLUDED +#define CLASP_CLASP_FWD_H_INCLUDED +/*! + * \file + * \brief Forward declarations of important clasp and potassco types. + */ + +namespace Potassco { +class TheoryAtom; +class TheoryTerm; +class TheoryData; +template struct Span; +struct Heuristic_t; +class BufferedStream; +class AbstractStatistics; +} +//! Root namespace for all types and functions of libclasp. +namespace Clasp { +class SharedContext; +class MinimizeBuilder; +class SharedMinimizeData; +class Configuration; +class Constraint; +class ConstraintInfo; +class Solver; +struct Model; +//! Supported problem types. +struct Problem_t { + enum Type {Sat = 0, Pb = 1, Asp = 2}; +}; +typedef Problem_t::Type ProblemType; +class ProgramBuilder; +class ProgramParser; +class SatBuilder; +class PBBuilder; +class ExtDepGraph; +class ConstString; +typedef Potassco::Span StrView; +typedef Potassco::Heuristic_t DomModType; +//! Namespace for types and functions used to define ASP programs. +namespace Asp { +class LogicProgram; +class Preprocessor; +class LpStats; +class PrgAtom; +class PrgBody; +class PrgDisj; +class PrgHead; +class PrgNode; +class PrgDepGraph; +struct PrgEdge; +}} + +#endif diff --git a/clasp/clasp/clause.h b/clasp/clasp/clause.h new file mode 100644 index 000000000..ebc74aeb1 --- /dev/null +++ b/clasp/clasp/clause.h @@ -0,0 +1,543 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_CLAUSE_H_INCLUDED +#define CLASP_CLAUSE_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif + +#include +namespace Clasp { + +//! An array of literals that can be shared between threads. +/*! + * \ingroup shared_con + */ +class SharedLiterals { +public: + //! Creates a shareable (ref-counted) object containing the literals in lits. + /*! + * \note The reference count is set to numRefs. + */ + static SharedLiterals* newShareable(const LitVec& lits, ConstraintType t, uint32 numRefs = 1) { + return newShareable(!lits.empty() ? &lits[0]:0, static_cast(lits.size()), t, numRefs); + } + static SharedLiterals* newShareable(const Literal* lits, uint32 size, ConstraintType t, uint32 numRefs = 1); + + //! Returns a pointer to the beginning of the literal array. + const Literal* begin() const { return lits_; } + //! Returns a pointer to the end of the literal array. + const Literal* end() const { return lits_+size(); } + //! Returns the number of literals in the array. + uint32 size() const { return size_type_ >> 2; } + //! Returns the type of constraint from which the literals originated. + ConstraintType type() const { return ConstraintType( size_type_ & uint32(3) ); } + //! Simplifies the literals w.r.t to the assignment in s. + /*! + * Returns the number of non-false literals in this object or 0 if + * the array contains a true literal. + * \note If the object is currently not shared, simplify() removes + * all false literals from the array. + */ + uint32 simplify(Solver& s); + + void release() { release(1); } + void release(uint32 numRefs); + SharedLiterals* share(); + bool unique() const { return refCount_ <= 1; } + uint32 refCount() const { return refCount_; } +private: + void destroy(); + SharedLiterals(const Literal* lits, uint32 size, ConstraintType t, uint32 numRefs); + SharedLiterals(const SharedLiterals&); + SharedLiterals& operator=(const SharedLiterals&); + typedef Clasp::Atomic_t::type RCType; + RCType refCount_; + uint32 size_type_; +POTASSCO_WARNING_BEGIN_RELAXED + Literal lits_[0]; +POTASSCO_WARNING_END_RELAXED +}; + +//! A helper-class for creating/adding clauses. +/*! + * \ingroup constraint + * This class simplifies clause creation. It hides the special handling of + * short, and shared clauses. It also makes sure that learnt clauses watch + * the literals from the highest decision levels. + */ +class ClauseCreator { +public: + typedef ConstraintInfo ClauseInfo; + //! Creates a new ClauseCreator object. + /*! + * \param s the Solver in which to store created clauses. + */ + explicit ClauseCreator(Solver* s = 0); + //! Sets the solver in which created clauses are stored. + void setSolver(Solver& s) { solver_ = &s; } + //! Adds additional flags to be applied in end(). + void addDefaultFlags(uint32 f) { flags_ |= f; } + //! Reserve space for a clause of size s. + void reserve(LitVec::size_type s) { literals_.reserve(s); } + //! Discards the current clause. + void clear() { literals_.clear(); } + + //! Status of a clause. + /*! + * For a clause with literals [l1,...,ln], status is one of: + */ + enum Status { + // BASE STATUS + status_open = 0, //!< Clause is neither sat, unsat, or unit. + status_sat = 1, //!< At least one literal is true. + status_unsat = 2, //!< All literals are false. + status_unit = 4, //!< All but one literal false. + // COMPLEX STATUS + status_sat_asserting= 5, //!< Sat but literal is implied on lower dl. + status_asserting = 6, //!< Unsat but literal is implied on second highest dl. + status_subsumed = 9, //!< Sat and one literal is true on level 0. + status_empty = 10, //!< Unsat and all literals are false on level 0. + }; + //! A type for storing the result of a clause insertion operation. + struct Result { + explicit Result(ClauseHead* loc = 0, Status st = status_open) + : local(loc) + , status(st) {} + ClauseHead* local; + Status status; + //! Returns false is clause is conflicting w.r.t current assignment. + bool ok() const { return (status & status_unsat) == 0; } + //! Returns true if the clause implies a literal (possibly after backtracking). + bool unit() const { return (status & status_unit) != 0; } + operator bool() const { return ok(); } + }; + //! Starts the creation of a new clause. + /*! + * \pre s.decisionLevel() == 0 || t != Constraint_t::Static + */ + ClauseCreator& start(ConstraintType t = Constraint_t::Static); + //! Sets the initial activity of the clause under construction. + ClauseCreator& setActivity(uint32 a) { extra_.setActivity(a); return *this; } + //! Sets the initial literal block distance of the clause under construction. + ClauseCreator& setLbd(uint32 lbd) { extra_.setLbd(lbd); return *this; } + //! Adds the literal p to the clause under construction. + ClauseCreator& add(const Literal& p) { literals_.push_back(p); return *this; } + //! Removes subsumed lits and orders first lits w.r.t watch order. + ClauseRep prepare(bool fullSimplify); + //! Returns the current size of the clause under construction. + uint32 size() const { return (uint32)literals_.size(); } + //! Returns the literal at the given idx. + Literal& operator[](uint32 i) { return literals_[i]; } + Literal operator[](uint32 i) const { return literals_[i]; } + //! Returns the literals of the clause under construction. + const LitVec& lits() const { return literals_; } + LitVec& lits() { return literals_; } + //! Returns the clause's type. + ConstraintType type() const { return extra_.type(); } + //! Returns the aux info of the clause under construction. + ConstraintInfo info() const { return extra_; } + //! Creates a new clause object for the clause under construction. + /*! + * \pre The clause does not contain duplicate/complementary literals or + * flags contains clause_force_simplify. + * + * \note If the clause to be added is empty, end() fails and s.hasConflict() is set to true. + * \see Result ClauseCreator::create(Solver& s, LitVec& lits, uint32 flags, const ClauseInfo& info); + */ + Result end(uint32 flags = clause_not_sat | clause_not_conflict); + + /*! + * \name Factory functions + * Functions for creating and integrating clauses. + */ + //@{ + //! Flags controlling clause creation and integration. + enum CreateFlag { + // REPRESENTATION + clause_no_add = 1, //!< Do not add clause to solver db. + clause_explicit = 2, //!< Force creation of explicit clause even if size <= 3. + // STATUS + clause_not_sat = 4, //!< Do not add clause if it is satisfied (but not asserting) w.r.t current assignment. + clause_not_root_sat = 8, //!< Do not add clause if it is satisfied w.r.t the root assignment. + clause_not_conflict = 16, //!< Do not add clause if it is conflicting w.r.t the current assignment. + // INTEGRATE + clause_no_release = 32, //!< Do not call release on shared literals. + clause_int_lbd = 64, //!< Compute lbd when integrating asserting clauses. + // PREPARE + clause_no_prepare = 128,//!< Assume clause is already ordered w.r.t watches. + clause_force_simplify= 256,//!< Call simplify() on create. + clause_no_heuristic = 512,//!< Do not notify heuristic about new clause. + // WATCH MODE - only for problem clauses + clause_watch_first =1024,//!< Watch first free literals. + clause_watch_rand =2048,//!< Watch rand literals. + clause_watch_least =4096,//!< Watch least watched literals. + }; + //! Returns the status of the given clause w.r.t s. + static Status status(const Solver& s, const Literal* clause_begin, const Literal* clause_end); + static Status status(const Solver& s, const ClauseRep& c); + + //! Returns an abstraction of p's decision level that can be used to order literals. + /*! + * The function returns a value, s.th + * order(any true literal) > order(any free literal) > order(any false literal). + * Furthermore, for equally assigned literals p and q, order(p) > order(q), iff + * level(p) > level(q). + */ + static uint32 watchOrder(const Solver& s, Literal p); + + //! Prepares the clause given in lits. + /*! + * A prepared clause [l1...ln] with n >= 2 is a clause that, + * - does not contain any duplicate or complementary literals, and + * - does not contain any subsumed literals (i.e. literals assigned on decision level 0), and + * - is partially ordered w.r.t watchOrder(), i.e., watchOrder(l1) >= watchOrder(l2), and there + * is no lj, j > 2, s.th. watchOrder(lj) > watchOrder(l2) + * . + * + * Removes subsumed literals from lits and reorders lits s.th. + * the first literals are valid watches. Furthermore, + * if flags contains clause_force_simplify, + * duplicate literals are removed from lits and tautologies are + * replaced with the single literal True. + */ + static ClauseRep prepare(Solver& s, LitVec& lits, uint32 flags, const ClauseInfo& info = ClauseInfo()); + + //! Creates a clause from the literals given in lits. + /*! + * \param s The solver to which the clause should be added. + * \param lits The literals of the clause. + * \param flags Flag set controlling creation (see ClauseCreator::CreateFlag). + * \param info Initial information (e.g. type) for the new clause. + * + * \pre !s.hasConflict() and s.decisionLevel() == 0 or extra.learnt() + * \pre lits is fully prepared or flags contains suitable prepare flags. + * + * \note + * If the given clause is unit (or asserting), the unit-resulting literal is + * asserted on the (numerical) lowest level possible but the new information + * is not immediately propagated, i.e. on return queueSize() may be greater than 0. + * + * \note + * The local representation of the clause is always attached to the solver + * but only added to the solver if clause_no_add is not contained in flags. + * Otherwise, the returned clause is owned by the caller + * and it is the caller's responsibility to manage it. Furthermore, + * learnt statistics are *not* updated automatically in that case. + * + * \see prepare() + */ + static Result create(Solver& s, LitVec& lits, uint32 flags, const ClauseInfo& info = ClauseInfo()); + + /*! + * \overload + */ + static Result create(Solver& s, const ClauseRep& rep, uint32 flags); + + //! Integrates the given clause into the current search of s. + /*! + * \pre the assignment in s is not conflicting + * \param s The solver in which the clause should be integrated. + * \param clause The clause to be integrated. + * \param flags A set of flags controlling integration (see ClauseCreator::CreateFlag). + * \param t Constraint type to use for the local representation. + * + * \note + * The function behaves similar to ClauseCreator::create() with the exception that + * it does not add local representations for implicit clauses (i.e. size <= 3) + * unless flags contains clause_explicit. + * In that case, an explicit representation is created. + * Implicit representations can only be created via ClauseCreator::create(). + * + * \note + * The function acts as a sink for the given clause (i.e. it decreases its reference count) + * unless flags contains clause_no_release. + * + * \note integrate() is intended to be called in a post propagator. + * To integrate a set of clauses F, one would use a loop like this: + * \code + * bool MyPostProp::propagate(Solver& s) { + * bool r = true; + * while (!F.empty() && r) { + * SharedLiterals* C = f.pop(); + * r = integrate(s, C, ...).ok; + * } + * return r; + * \endcode + */ + static Result integrate(Solver& s, SharedLiterals* clause, uint32 flags, ConstraintType t); + + /*! + * \overload + */ + static Result integrate(Solver& s, SharedLiterals* clause, uint32 flags); + //@} +private: + static ClauseRep prepare(Solver& s, const Literal* in, uint32 inSize, const ClauseInfo& e, uint32 flags, Literal* out, uint32 outMax = UINT32_MAX); + static Result create_prepared(Solver& s, const ClauseRep& pc, uint32 flags); + static ClauseHead* newProblemClause(Solver& s, const ClauseRep& clause, uint32 flags); + static ClauseHead* newLearntClause(Solver& s, const ClauseRep& clause, uint32 flags); + static ClauseHead* newUnshared(Solver& s, SharedLiterals* clause, const Literal* w, const ClauseInfo& e); + static bool ignoreClause(const Solver& s, const ClauseRep& cl, Status st, uint32 modeFlags); + Solver* solver_; // solver in which new clauses are stored + LitVec literals_; // literals of the new clause + ClauseInfo extra_; // extra info + uint32 flags_; // default flags to be used in end() +}; + +//! Class for representing a clause in a solver. +/*! + * \ingroup constraint + */ +class Clause : public ClauseHead { +public: + typedef Constraint::PropResult PropResult; + + //! Allocates memory for storing a (learnt) clause with nLits literals. + static void* alloc(Solver& s, uint32 mLits, bool learnt); + + //! Creates a new clause from the clause given in rep. + /*! + * \param s Solver in which the new clause is to be used. + * \param rep The raw representation of the clause. + * + * \pre The clause given in lits is prepared and contains at least two literals. + * \note The clause must be destroyed using Clause::destroy. + * \see ClauseCreator::prepare() + */ + static ClauseHead* newClause(Solver& s, const ClauseRep& rep) { + return newClause(alloc(s, rep.size, rep.info.learnt()), s, rep); + } + //! Creates a new clause object in mem. + /*! + * \pre mem points to a memory block that was allocated via Clause::alloc(). + */ + static ClauseHead* newClause(void* mem, Solver& s, const ClauseRep& rep); + + //! Creates a new contracted clause from the clause given in rep. + /*! + * A contracted clause consists of an active head and a tail of false literals. + * Propagation is restricted to the head. + * The tail is only needed to compute reasons from assignments. + * + * \param s Solver in which the new clause is to be used. + * \param rep The raw representation of the clause. + * \param tailPos The starting index of the tail (first literal that should be temporarily removed from the clause). + * \param extend Extend head part of clause as tail literals become free? + */ + static ClauseHead* newContractedClause(Solver& s, const ClauseRep& rep, uint32 tailPos, bool extend); + + //! Creates a new local surrogate for shared_lits to be used in the given solver. + /*! + * \param s The solver in which this clause will be used. + * \param lits The shared literals of this clause. + * \param e Initial meta data for the new (local) clause. + * \param head Watches and cache literal for the new (local) clause. + * \param addRef Increment ref count of lits. + */ + static ClauseHead* newShared(Solver& s, SharedLiterals* lits, const InfoType& e, const Literal head[3], bool addRef = true); + + // Constraint-Interface + + Constraint* cloneAttach(Solver& other); + + /*! + * For a clause [x y p] the reason for p is ~x and ~y. + * \pre *this previously asserted p + * \note if the clause is a learnt clause, calling reason increases + * the clause's activity. + */ + void reason(Solver& s, Literal p, LitVec& lits); + + bool minimize(Solver& m, Literal p, CCMinRecursive* r); + + bool isReverseReason(const Solver& s, Literal p, uint32 maxL, uint32 maxN); + + //! Returns true if clause is SAT. + /*! + * Removes from the clause all literals that are false. + */ + bool simplify(Solver& s, bool = false); + + //! Destroys the clause and frees its memory. + void destroy(Solver* s = 0, bool detach = false); + + // LearntConstraint interface + + //! Returns type() if the clause is currently not satisfied and t.inSet(type()). + uint32 isOpen(const Solver& s, const TypeSet& t, LitVec& freeLits); + + // clause interface + BoolPair strengthen(Solver& s, Literal p, bool allowToShort); + void detach(Solver&); + uint32 size() const; + void toLits(LitVec& out) const; + bool contracted() const; + bool isSmall() const; + bool strengthened() const; + uint32 computeAllocSize() const; +private: + Clause(Solver& s, const ClauseRep& rep, uint32 tail = UINT32_MAX, bool extend = false); + Clause(Solver& s, const Clause& other); + typedef std::pair LitRange; + void undoLevel(Solver& s); + bool updateWatch(Solver& s, uint32 pos); + Literal* end() { return head_+local_.size(); } + Literal* removeFromTail(Solver& s, Literal* it, Literal* end); + Literal* small(); + LitRange tail(); +}; + +//! Constraint for Loop-Formulas. +/*! + * \ingroup constraint + * Special purpose constraint for loop formulas of the form: L v B1 v ... v Bm, + * where L is an unfounded set represented as a set of atom literals {~a1, ..., ~an}. + * Representing such a loop formula explicitly as n clauses + * - (1) ~a1 v B1 v ... v Bm + * - ... + * - (n) ~an v B1 v ... v Bm + * . + * is wasteful because each clause contains the same set of bodies. + * + * The idea behind LoopFormula is to treat L as a "macro-literal" + * with the following properties: + * - isTrue(L), iff for all ai isTrue(~ai) + * - isFalse(L), iff for some ai isFalse(~ai) + * - L is watchable, iff not isFalse(L) + * - Watching L means watching all ai. + * - setting L to true means setting all ai to false. + * Using this convention the TWL-algo can be implemented as in a clause. + * + * \par Implementation: + * - The literal-array is divided into two parts, an "active clause" part and an atom part. + * - The "active clause" contains one atom and all bodies: [~ai B1 ... Bj] + * - The atom part contains all atoms: [~a1 ... ~an] + * - Two of the literals of the "active clause" are watched (again: watching an atom means watching all atoms) + * - If a watched atom becomes true, it is copied into the "active clause" and the TWL-algo starts. + */ +class LoopFormula : public Constraint { +public: + //! Creates a new loop-constraint for the given atoms. + /*! + * \param s Solver in which the new constraint is to be used. + * \param c1 First clause of the new constraint. + * \param atoms Set of atoms in the loop. + * \param nAtoms Number of atoms in the loop. + * \param updateHeuristic Whether to notify heuristic about new constraint. + * + * \pre The clause given in c1 is prepared and c1.size > 1 and c1.lits[0] is a literal in atoms. + * \see ClauseCreator::prepare() + */ + static LoopFormula* newLoopFormula(Solver& s, const ClauseRep& c1, const Literal* atoms, uint32 nAtoms, bool updateHeuristic = true); + + //! Returns the number of literals in the loop-formula. + uint32 size() const; + + // Constraint interface + Constraint* cloneAttach(Solver&) { return 0; } + PropResult propagate(Solver& s, Literal p, uint32& data); + void reason(Solver&, Literal p, LitVec& lits); + bool minimize(Solver& s, Literal p, CCMinRecursive* ccMin); + bool simplify(Solver& s, bool = false); + void destroy(Solver* = 0, bool = false); + + // LearntConstraint interface + bool locked(const Solver& s) const; + + uint32 isOpen(const Solver& s, const TypeSet& t, LitVec& freeLits); + + //! Returns the loop-formula's activity. + /*! + * The activity of a loop-formula is increased, whenever reason() is called. + */ + ScoreType activity() const { return act_; } + + //! Halves the loop-formula's activity. + void decreaseActivity() { act_.reduce(); } + void resetActivity() { act_.reset(); } + + //! Returns Constraint_t::Loop. + ConstraintType type() const { return Constraint_t::Loop; } +private: + LoopFormula(Solver& s, const ClauseRep& c1, const Literal* atoms, uint32 nAtoms, bool heu); + void detach(Solver& s); + bool otherIsSat(const Solver& s); + Literal* begin() { return lits_ + 1; } + Literal* xBegin(){ return lits_ + end_ + 1; } + Literal* xEnd() { return lits_ + size_; } + ScoreType act_; // activity of constraint + uint32 end_; // position of second sentinel + uint32 size_:30; // size of lits_ + uint32 str_ : 1; // removed literal(s) during simplify? + uint32 xPos_: 1; // position of ai in lits_ or 0 if no atom + uint32 other_; // stores the position of a literal that was recently true +POTASSCO_WARNING_BEGIN_RELAXED + Literal lits_[0]; // S ai B1...Bm S a1...an +POTASSCO_WARNING_END_RELAXED +}; + +namespace mt { + +//! Stores the local part of a shared clause. +/*! + * \ingroup constraint + * The local part of a shared clause consists of a + * clause head and and a pointer to the shared literals. + * Since the local part is owned by a particular solver + * it can be safely modified. Destroying a SharedLitsClause + * means destroying the local part and decreasing the + * shared literals' reference count. + */ +class SharedLitsClause : public ClauseHead { +public: + //! Creates a new SharedLitsClause to be used in the given solver. + /*! + * \param s The solver in which this clause will be used. + * \param shared_lits The shared literals of this clause. + * \param e Initial meta data for the new (local) clause. + * \param lits Watches and cache literal for the new (local) clause. + * \param addRef Increment ref count of shared_lits. + */ + static ClauseHead* newClause(Solver& s, SharedLiterals* shared_lits, const InfoType& e, const Literal* lits, bool addRef = true); + + Constraint* cloneAttach(Solver& other); + void reason(Solver& s, Literal p, LitVec& out); + bool minimize(Solver& s, Literal p, CCMinRecursive* rec); + bool isReverseReason(const Solver& s, Literal p, uint32 maxL, uint32 maxN); + bool simplify(Solver& s, bool); + void destroy(Solver* s, bool detach); + uint32 isOpen(const Solver& s, const TypeSet& t, LitVec& freeLits); + uint32 size() const; + void toLits(LitVec& out) const; +private: + SharedLitsClause(Solver& s, SharedLiterals* x, const Literal* lits, const InfoType&, bool addRef); + bool updateWatch(Solver& s, uint32 pos); + BoolPair strengthen(Solver& s, Literal p, bool allowToShort); +}; +} + +} +#endif diff --git a/clasp/clasp/cli/clasp_app.h b/clasp/clasp/cli/clasp_app.h new file mode 100644 index 000000000..28222b25f --- /dev/null +++ b/clasp/clasp/cli/clasp_app.h @@ -0,0 +1,217 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_CLI_CLASP_APP_H_INCLUDED +#define CLASP_CLI_CLASP_APP_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace Potassco { class StringBuilder; } +namespace Clasp { namespace Cli { +///////////////////////////////////////////////////////////////////////////////////////// +// clasp exit codes +///////////////////////////////////////////////////////////////////////////////////////// +enum ExitCode { + E_UNKNOWN = 0, /*!< Satisfiablity of problem not knwon; search not started. */ + E_INTERRUPT = 1, /*!< Run was interrupted. */ + E_SAT = 10, /*!< At least one model was found. */ + E_EXHAUST = 20, /*!< Search-space was completely examined. */ + E_MEMORY = 33, /*!< Run was interrupted by out of memory exception. */ + E_ERROR = 65, /*!< Run was interrupted by internal error. */ + E_NO_RUN = 128 /*!< Search not started because of syntax or command line error.*/ +}; +///////////////////////////////////////////////////////////////////////////////////////// +// clasp app helpers +///////////////////////////////////////////////////////////////////////////////////////// +class WriteCnf { +public: + WriteCnf(const std::string& outFile); + ~WriteCnf(); + void writeHeader(uint32 numVars, uint32 numCons); + void write(Var maxVar, const ShortImplicationsGraph& g); + void write(ClauseHead* h); + void write(Literal unit); + void close(); + bool unary(Literal, Literal) const; + bool binary(Literal, Literal, Literal) const; +private: + WriteCnf(const WriteCnf&); + WriteCnf& operator=(const WriteCnf&); + FILE* str_; + LitVec lits_; +}; +class LemmaLogger { +public: + struct Options { + Options() : logMax(UINT32_MAX), lbdMax(UINT32_MAX), domOut(false), logText(false) {} + uint32 logMax; // log at most logMax lemmas + uint32 lbdMax; // only log lemmas with lbd <= lbdMax + bool domOut; // only log lemmas that can be expressed over out variables + bool logText; // log lemmas in ground lp format + }; + LemmaLogger(const std::string& outFile, const Options& opts); + ~LemmaLogger(); + void startStep(ProgramBuilder& prg, bool inc); + void add(const Solver& s, const LitVec& cc, const ConstraintInfo& info); + void close(); +private: + typedef PodVector::type Var2Idx; + typedef Atomic_t::type Counter; + LemmaLogger(const LemmaLogger&); + LemmaLogger& operator=(const LemmaLogger&); + void formatAspif(const LitVec& cc, uint32 lbd, Potassco::StringBuilder& out) const; + void formatText(const LitVec& cc, const OutputTable& tab, uint32 lbd, Potassco::StringBuilder& out) const; + FILE* str_; + Potassco::LitVec solver2asp_; + Var2Idx solver2NameIdx_; + ProblemType inputType_; + Options options_; + int step_; + Counter logged_; +}; +///////////////////////////////////////////////////////////////////////////////////////// +// clasp specific application options +///////////////////////////////////////////////////////////////////////////////////////// +struct ClaspAppOptions { + typedef LemmaLogger::Options LogOptions; + ClaspAppOptions(); + typedef std::vector StringSeq; + static bool mappedOpts(ClaspAppOptions*, const std::string&, const std::string&); + void initOptions(Potassco::ProgramOptions::OptionContext& root); + bool validateOptions(const Potassco::ProgramOptions::ParsedOptions& parsed); + StringSeq input; // list of input files - only first used! + std::string lemmaLog; // optional file name for writing learnt lemmas + std::string lemmaIn; // optional file name for reading learnt lemmas + std::string hccOut; // optional file name for writing scc programs + std::string outAtom; // optional format string for atoms + uint32 outf; // output format + int compute; // force literal compute to true + LogOptions lemma; // options for lemma logging + char ifs; // output field separator + bool hideAux; // output aux atoms? + uint8 quiet[3]; // configure printing of models, optimization values, and call steps + int8 onlyPre; // run preprocessor and exit + bool printPort; // print portfolio and exit + enum OutputFormat { out_def = 0, out_comp = 1, out_json = 2, out_none = 3 }; +}; +///////////////////////////////////////////////////////////////////////////////////////// +// clasp application base +///////////////////////////////////////////////////////////////////////////////////////// +// Base class for applications using the clasp library. +class ClaspAppBase : public Potassco::Application, public Clasp::EventHandler { +public: + typedef ClaspFacade::Summary RunSummary; + typedef Potassco::ProgramOptions::PosOption PosOption; +protected: + using Potassco::Application::run; + ClaspAppBase(); + ~ClaspAppBase(); + // ------------------------------------------------------------------------------------------- + // Functions to be implemented by subclasses + virtual ProblemType getProblemType() = 0; + virtual void run(ClaspFacade& clasp) = 0; + virtual Output* createOutput(ProblemType f); + virtual void storeCommandArgs(const Potassco::ProgramOptions::ParsedValues& values); + // ------------------------------------------------------------------------------------------- + // Helper functions that subclasses might call during run + void handleStartOptions(ClaspFacade& clasp); + bool handlePostGroundOptions(ProgramBuilder& prg); + bool handlePreSolveOptions(ClaspFacade& clasp); + // ------------------------------------------------------------------------------------------- + // Application functions + virtual const int* getSignals() const; + virtual HelpOpt getHelpOption() const { return HelpOpt("Print {1=basic|2=more|3=full} help and exit", 3); } + virtual PosOption getPositional() const { return parsePositional; } + virtual void initOptions(Potassco::ProgramOptions::OptionContext& root); + virtual void validateOptions(const Potassco::ProgramOptions::OptionContext& root, const Potassco::ProgramOptions::ParsedOptions& parsed, const Potassco::ProgramOptions::ParsedValues& values); + virtual void setup(); + virtual void run(); + virtual void shutdown(); + virtual bool onSignal(int); + virtual void printHelp(const Potassco::ProgramOptions::OptionContext& root); + virtual void printVersion(); + static bool parsePositional(const std::string& s, std::string& out); + // ------------------------------------------------------------------------------------------- + // Event handler + virtual void onEvent(const Event& ev); + virtual bool onModel(const Solver& s, const Model& m); + virtual bool onUnsat(const Solver& s, const Model& m); + // ------------------------------------------------------------------------------------------- + // Status information & output + int exitCode(const RunSummary& sol) const; + void printTemplate() const; + void printDefaultConfigs() const; + void printConfig(ConfigKey k) const; + void printLibClaspVersion() const; + void printLicense() const; + std::istream& getStream(bool reopen = false) const; + // ------------------------------------------------------------------------------------------- + // Functions called in handlePreSolveOptions() + void writeNonHcfs(const PrgDepGraph& graph) const; + typedef Potassco::ProgramReader LemmaReader; + typedef SingleOwnerPtr OutPtr; + typedef SingleOwnerPtr ClaspPtr; + typedef SingleOwnerPtr LogPtr; + typedef SingleOwnerPtr LemmaPtr; + ClaspCliConfig claspConfig_; + ClaspAppOptions claspAppOpts_; + ClaspPtr clasp_; + OutPtr out_; + LogPtr logger_; + LemmaPtr lemmaIn_; + unsigned fpuMode_; +}; +///////////////////////////////////////////////////////////////////////////////////////// +// clasp application +///////////////////////////////////////////////////////////////////////////////////////// +// Standalone clasp application. +class ClaspApp : public ClaspAppBase { +public: + ClaspApp(); + const char* getName() const { return "clasp"; } + const char* getVersion() const { return CLASP_VERSION; } + const char* getUsage() const { + return + "[number] [options] [file]\n" + "Compute at most models (0=all) of the instance given in "; + } +protected: + virtual ProblemType getProblemType(); + virtual void run(ClaspFacade& clasp); + virtual void printHelp(const Potassco::ProgramOptions::OptionContext& root); +private: + ClaspApp(const ClaspApp&); + ClaspApp& operator=(const ClaspApp&); +}; +}} +#endif diff --git a/clasp/clasp/cli/clasp_cli_configs.inl b/clasp/clasp/cli/clasp_cli_configs.inl new file mode 100644 index 000000000..6db3f22bd --- /dev/null +++ b/clasp/clasp/cli/clasp_cli_configs.inl @@ -0,0 +1,95 @@ +// +// Copyright (c) 2013-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +/*! + * \file + * \brief Supermacros for defining clasp's default configurations. + * \code + * CONFIG(sId, name, "", "", "") + * \endcode + * + * A configuration consists of a solver id and a name followed by three option strings: + * - "" : options that are always part of the configuration + * - "": options to add if configuration is used as a stand-alone configuration (e.g. global options) + * - "" : options to add if configuration is used in a portfolio + * . + * \note The solver id is used to identify a configuration in the default portfolio. + */ + +#if !defined(CONFIG) || (!defined(CLASP_CLI_DEFAULT_CONFIGS) && !defined(CLASP_CLI_AUX_CONFIGS)) +#error Invalid include context +#endif + +//! Named default configurations accessible via option "--configuration=". +#if defined(CLASP_CLI_DEFAULT_CONFIGS) +CLASP_CLI_DEFAULT_CONFIGS +CONFIG(0, tweety\ + , "--heuristic=Vsids,92 --restarts=L,60 --deletion=basic,50 --del-max=2000000 --del-estimate=1 --del-cfl=+,2000,100,20 --del-grow=0 --del-glue=2,0"\ + " --strengthen=recursive,all --otfs=2 --init-moms --score-other=all --update-lbd=less --save-progress=160 --init-watches=least --local-restarts --loops=shared"\ + , "--eq=3 --trans-ext=dynamic"\ + , "--opt-strat=bb,hier") +CONFIG(1, trendy\ + , "--heuristic=Vsids --restarts=D,100,0.7 --deletion=basic,50 --del-init=3.0,500,19500 --del-grow=1.1,20.0,x,100,1.5 --del-cfl=+,10000,2000 --del-glue=2"\ + " --strengthen=recursive --update-lbd=less --otfs=2 --save-p=75 --counter-restarts=3,1023 --reverse-arcs=2 --contraction=250 --loops=common"\ + , "--sat-p=2,iter=20,occ=25,time=240 --trans-ext=dynamic"\ + , "--opt-heu=sign --opt-strat=usc,disjoint") +CONFIG(2, frumpy\ + , "--heuristic=Berkmin --restarts=x,100,1.5 --deletion=basic,75 --del-init=3.0,200,40000 --del-max=400000 --contraction=250 --loops=common --save-p=180"\ + " --del-grow=1.1 --strengthen=local --sign-def-disj=pos"\ + , "--eq=5"\ + , "--restart-on-model --opt-heu=model") +CONFIG(3, crafty\ + , "--restarts=x,128,1.5 --deletion=basic,75 --del-init=10.0,1000,9000 --del-grow=1.1,20.0 --del-cfl=+,10000,1000 --del-glue=2 --otfs=2"\ + " --reverse-arcs=1 --counter-restarts=3,9973 --contraction=250"\ + , "--sat-p=2,iter=10,occ=25,time=240 --trans-ext=dynamic --backprop --heuristic=Vsids --save-p=180"\ + , "--heuristic=domain --dom-mod=neg,opt --opt-strat=bb,hier") +CONFIG(4, jumpy\ + , "--heuristic=Vsids --restarts=L,100 --deletion=basic,75,mixed --del-init=3.0,1000,20000 --del-grow=1.1,25,x,100,1.5 --del-cfl=x,10000,1.1 --del-glue=2"\ + " --update-lbd=glucose --strengthen=recursive --otfs=2 --save-p=70"\ + , "--sat-p=2,iter=20,occ=25,time=240 --trans-ext=dynamic"\ + , "--restart-on-model --opt-heu=sign,model --opt-strat=bb,inc") +CONFIG(5, handy\ + , "--heuristic=Vsids --restarts=D,100,0.7 --deletion=sort,50,mixed --del-max=200000 --del-init=20.0,1000,14000 --del-cfl=+,4000,600 --del-glue=2 --update-lbd=less"\ + " --strengthen=recursive --otfs=2 --save-p=20 --contraction=600 --loops=distinct --counter-restarts=7,1023 --reverse-arcs=2"\ + , "--sat-p=2,iter=10,occ=25,time=240 --trans-ext=dynamic --backprop"\ + , "") +#undef CLASP_CLI_DEFAULT_CONFIGS +#endif +//! Auxiliary configurations accessible via default portfolio ("--configuration=many"). +#if defined(CLASP_CLI_AUX_CONFIGS) +CLASP_CLI_AUX_CONFIGS +CONFIG(6, s6, "--heuristic=Berkmin,512 --restarts=x,100,1.5 --deletion=basic,75 --del-init=3.0,200,40000 --del-max=400000 --contraction=250 --loops=common --del-grow=1.1,25 --otfs=2 --reverse-arcs=2 --strengthen=recursive --init-w=least --lookahead=atom,10", "", "") +CONFIG(7, s7, "--heuristic=Vsids --reverse-arcs=1 --otfs=1 --local-restarts --save-progress=0 --contraction=250 --counter-restart=7,200 --restarts=x,100,1.5 --del-init=3.0,800,-1 --deletion=basic,60 --strengthen=local --del-grow=1.0,1.0 --del-glue=4 --del-cfl=+,4000,300,100", "", "") +CONFIG(8, s8, "--heuristic=Vsids --restarts=L,256 --counter-restart=3,9973 --strengthen=recursive --update-lbd=less --del-glue=2 --otfs=2 --deletion=ipSort,75,mixed --del-init=20.0,1000,19000", "", "") +CONFIG(9, s9, "--heuristic=Berkmin,512 --restarts=F,16000 --lookahead=atom,50", "", "") +CONFIG(10, s10, "--heuristic=Vmtf --strengthen=no --contr=0 --restarts=x,100,1.3 --del-init=3.0,800,9200", "", "") +CONFIG(11, s11, "--heuristic=Vsids --strengthen=recursive --restarts=x,100,1.5,15 --contraction=0", "", "") +CONFIG(12, s12, "--heuristic=Vsids --restarts=L,128 --save-p --otfs=1 --init-w=least --contr=0 --opt-heu=sign,model", "", "") +CONFIG(13, s13, "--heuristic=Berkmin,512 --restarts=x,100,1.5,6 --local-restarts --init-w=least --contr=0", "", "") +CONFIG(14, nolearn, "--no-lookback --heuristic=Unit --lookahead=atom --deletion=no --restarts=no", "", "") +CONFIG(15, tester, "--heuristic=Vsids --restarts=D,100,0.7 --deletion=sort,50,mixed --del-max=200000 --del-init=20.0,1000,14000 --del-cfl=+,4000,600 --del-glue=2 --update-lbd=less"\ + " --strengthen=recursive --otfs=2 --save-p=20 --contraction=600 --counter-restarts=7,1023 --reverse-arcs=2"\ + , "--sat-p=2,iter=10,occ=25,time=240", "") +#undef CLASP_CLI_AUX_CONFIGS +#endif +#undef CONFIG diff --git a/clasp/clasp/cli/clasp_cli_options.inl b/clasp/clasp/cli/clasp_cli_options.inl new file mode 100644 index 000000000..8ba00baac --- /dev/null +++ b/clasp/clasp/cli/clasp_cli_options.inl @@ -0,0 +1,574 @@ +// +// Copyright (c) 2013-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +/*! + * \file + * \brief Supermacros for describing clasp's options. + * \code + * OPTION(key, "ext", ARG(...), "help", set, get) + * \endcode + * An option consists of: + * - a key (valid and unique c identifier in 'snake_case') + * - a key extension ("[!][,][,@]") as understood by ProgramOptions::OptionInitHelper + * - an arg description (ARG macro) as understood by ProgramOptions::Value + * - a description (string) + * - a set action to be executed when a value (string) for the option is found in a source + * - a get action to be executed when the current value for an option is requested + * . + * + * \note In the implementation of ClaspCliConfig, each key is mapped to an enumeration constant and + * the stringified version of key (i.e. \#key) is used to identify options. + * Furthermore, the key is also used for generating command-line option names. + * As a convention, compound keys using 'snake_case' to separate words + * are mapped to dash-separated command-line option names. + * E.g. an \ is mapped to the command-line option "option-like-this". + * + * \note ClaspCliConfig assumes a certain option order. In particular, context options shall + * precede all solver/search options, which in turn shall precede global asp/solving options. + * + * \note The following set actions may be used: + * - STORE(obj): converts the string value to the type of obj and stores the result in obj. + * - STORE_U(E, n): converts the string value to type E and stores it as unsigned int in n. + * - STORE_LEQ(n, max): converts the string value to an unsinged int and stores the result in n if it is <= max. + * - STORE_FLAG(n): converts the string value to a bool and stores the result in n as either 0 or 1. + * - STORE_OR_FILL(n): converts the string value to an unsinged int t and sets n to std::min(t, maxValue(n)). + * - FUN(arg): anonymous function of type bool (ArgStream& arg), where arg provides the following interface: + * - arg.ok() : returns whether arg is still valid + * - arg.empty() : returns whether arg is empty (i.e. all tokens were extracted) + * - arg.off() : returns whether arg contains a single token representing a valid off value + * - arg >> x : extracts and converts the current token and stores it in x. Sets failbit if conversion fails. + * - arg >> opt(x): extracts an optional argument, shorthand for (!arg.empty() ? arg>>obj : arg) + * . + * + * \note The following get actions may be used: + * - GET_FUN(str) : anonymous function of type void (OutputStream& str) + * - GET(obj...) : shorthand for GET_FUN(str) { (str << obj)...; } + * - GET_IF(C, obj): shorthand for GET_FUN(str) { ITE(C, str << obj, str << off); } + * . + * + * \note The following primitives may be used in the set/get arguments: + * - off : singleton object representing a valid off value ("no", "off", "false", "0") + * - ITE(C, x, y) : if-then-else expression, i.e. behaves like (C ? x : y). + * - SET(x, y) : shorthand for (x=y) == y. + * - SET_LEQ(x, v, m) : shorthand for (x <= m && SET(x, v)). + * - SET_GEQ(x, v, m) : shorthand for (x >= m && SET(x, v)). + * - SET_OR_FILL(x, v) : behaves like SET(x, min(v, maxValue(x))) + * - SET_OR_ZERO(x,v) : behaves like ITE(v <= maxValue(x), SET(x, v), SET(x, 0)). + * . + */ +#if !defined(OPTION) || defined(SELF) || !defined(CLASP_HAS_THREADS) +#error Invalid include context +#endif + +#if !defined(GROUP_BEGIN) +#define GROUP_BEGIN(X) +#endif + +#if !defined(GROUP_END) +#define GROUP_END(X) +#endif + +//! Options for configuring a SharedContext object stored in a Clasp::ContextParams object. +#if defined(CLASP_CONTEXT_OPTIONS) +#define SELF CLASP_CONTEXT_OPTIONS +GROUP_BEGIN(SELF) +OPTION(share, "!,@1", ARG_EXT(defaultsTo("auto")->state(Value::value_defaulted), DEFINE_ENUM_MAPPING(ContextParams::ShareMode, \ + MAP("no" , ContextParams::share_no) , MAP("all", ContextParams::share_all),\ + MAP("auto", ContextParams::share_auto), MAP("problem", ContextParams::share_problem),\ + MAP("learnt", ContextParams::share_learnt))),\ + "Configure physical sharing of constraints [%D]\n"\ + " %A: {auto|problem|learnt|all}", FUN(arg) {ContextParams::ShareMode x; return arg>>x && SET(SELF.shareMode, (uint32)x);}, GET((ContextParams::ShareMode)SELF.shareMode)) +OPTION(learn_explicit, ",@2" , ARG(flag()), "Do not use Short Implication Graph for learning", STORE_FLAG(SELF.shortMode), GET(SELF.shortMode)) +OPTION(sat_prepro , "!,@1", ARG(arg("")->implicit("2")), \ + "Run SatELite-like preprocessing (Implicit: %I)\n" \ + " %A: [,...]\n" \ + " : Set preprocessing level to \n" \ + " 1: Variable elimination with subsumption (VE)\n" \ + " 2: VE with limited blocked clause elimination (BCE)\n" \ + " 3: Full BCE followed by VE\n" \ + " : [=] (0=no limit)\n" \ + " iter : Set iteration limit to [0]\n" \ + " occ : Set variable occurrence limit to [0]\n" \ + " time : Set time limit to seconds [0]\n" \ + " frozen: Set frozen variables limit to %% [0]\n" \ + " size : Set size limit to *1000 clauses [4000]", STORE(SELF.satPre), GET(SELF.satPre)) +GROUP_END(SELF) +#undef CLASP_CONTEXT_OPTIONS +#undef SELF +#endif + +//! Global options only valid in facade. +#if defined(CLASP_GLOBAL_OPTIONS) +#define SELF CLASP_GLOBAL_OPTIONS +GROUP_BEGIN(SELF) +OPTION(stats, ",s", ARG(implicit("1")->arg("[,]")), "Enable {1=basic|2=full} statistics ( for tester)",\ + FUN(arg) { uint32 s = 0; uint32 t = 0;\ + return (arg.off() || (arg >> s >> opt(t) && s > 0)) + && SET(SELF.stats, s) && ((!SELF.testerConfig() && t == 0) || SET(SELF.addTesterConfig()->stats, t)); + },\ + GET_FUN(str) { ITE(!SELF.testerConfig() || !SELF.testerConfig()->stats, str << SELF.stats, str << SELF.stats << SELF.testerConfig()->stats); }) +OPTION(parse_ext, "!", ARG(flag()), "Enable extensions in non-aspif input",\ + FUN(arg) { bool b = false; return (arg.off() || arg >> b) && (SELF.parse.assign(ParserOptions::parse_full, b), true); }, \ + GET((SELF.parse.anyOf(ParserOptions::parse_full)))) +OPTION(parse_maxsat, "!", ARG(flag()), "Treat dimacs input as MaxSAT problem", \ + FUN(arg) { bool b = false; return (arg.off() || arg >> b) && (SELF.parse.assign(ParserOptions::parse_maxsat, b), true); }, \ + GET(static_cast(SELF.parse.isEnabled(ParserOptions::parse_maxsat)))) +GROUP_END(SELF) +#undef CLASP_GLOBAL_OPTIONS +#undef SELF +#endif + +//! Solver options (see SolverParams). +#if defined(CLASP_SOLVER_OPTIONS) +#define SELF CLASP_SOLVER_OPTIONS +GROUP_BEGIN(SELF) +OPTION(opt_strategy , "" , ARG_EXT(arg(""),\ + DEFINE_ENUM_MAPPING(OptParams::Type, MAP("bb", OptParams::type_bb), MAP("usc", OptParams::type_usc))\ + DEFINE_ENUM_MAPPING(OptParams::BBAlgo, MAP("lin", OptParams::bb_lin), MAP("hier", OptParams::bb_hier), MAP("inc", OptParams::bb_inc), MAP("dec", OptParams::bb_dec))\ + DEFINE_ENUM_MAPPING(OptParams::UscAlgo, MAP("oll", OptParams::usc_oll), MAP("one", OptParams::usc_one), MAP("k", OptParams::usc_k), MAP("pmres", OptParams::usc_pmr))\ + DEFINE_ENUM_MAPPING(OptParams::UscOption, MAP("disjoint", OptParams::usc_disjoint), MAP("succinct", OptParams::usc_succinct), MAP("stratify", OptParams::usc_stratify))),\ + "Configure optimization strategy\n" \ + " %A: {bb|usc}[,]\n" \ + " bb : Model-guided optimization with [lin]\n" \ + " lin : Basic lexicographical descent\n" \ + " hier: Hierarchical (highest priority criteria first) descent \n" \ + " inc : Hierarchical descent with exponentially increasing steps\n" \ + " dec : Hierarchical descent with exponentially decreasing steps\n" \ + " usc: Core-guided optimization with : [,]\n" \ + " : Relaxation algorithm {oll|one|k|pmres} [oll]\n" \ + " oll : Use strategy from unclasp\n" \ + " one : Add one cardinality constraint per core\n" \ + " k[,]: Add cardinality constraints of bounded size ([0]=dynamic)\n" \ + " pmres : Add clauses of size 3\n" \ + " : Tactics |\n" \ + " disjoint: Disjoint-core preprocessing (1)\n" \ + " succinct: No redundant (symmetry) constraints (2)\n" \ + " stratify: Stratification heuristic for handling weights (4)", \ + STORE(SELF.opt), GET(SELF.opt)) +OPTION(opt_usc_shrink, "", ARG_EXT(arg(""), DEFINE_ENUM_MAPPING(OptParams::UscTrim, \ + MAP("lin", OptParams::usc_trim_lin), MAP("rgs", OptParams::usc_trim_rgs), MAP("min", OptParams::usc_trim_min),\ + MAP("exp", OptParams::usc_trim_exp), MAP("inv", OptParams::usc_trim_inv), MAP("bin", OptParams::usc_trim_bin))),\ + "Enable core-shrinking in core-guided optimization\n" \ + " %A: [, (0=no limit)]\n" \ + " : Use algorithm {lin|inv|bin|rgs|exp|min}\n" \ + " lin : Forward linear search unsat\n" \ + " inv : Inverse linear search not unsat\n" \ + " bin : Binary search\n" \ + " rgs : Repeated geometric sequence until unsat\n" \ + " exp : Exponential search until unsat\n" \ + " min : Linear search for subset minimal core\n" \ + " : Limit solve calls to 2^ conflicts [10]",\ + FUN(arg) {\ + OptParams::UscTrim t = (OptParams::UscTrim)0; uint32 n = 0; \ + return (arg.off() || arg >> t >> opt(n=10)) && SET(SELF.opt.trim, uint32(t)) && SET(SELF.opt.tLim, uint32(n)); },\ + GET_IF(SELF.opt.trim, (OptParams::UscTrim)SELF.opt.trim, SELF.opt.tLim)) +OPTION(opt_heuristic, "", ARG_EXT(arg(""), DEFINE_ENUM_MAPPING(OptParams::Heuristic, \ + MAP("sign", OptParams::heu_sign), MAP("model", OptParams::heu_model))),\ + "Use opt. in heuristics",\ + FUN(arg) { Set h; return (arg.off() || arg >> h) && SET(SELF.opt.heus, h.value());},\ + GET(Set(SELF.opt.heus))) +OPTION(restart_on_model, "!", ARG(flag()), "Restart after each model\n", STORE_FLAG(SELF.restartOnModel), GET(SELF.restartOnModel)) +OPTION(lookahead , "!", ARG_EXT(implicit("atom"), DEFINE_ENUM_MAPPING(VarType, \ + MAP("atom", Var_t::Atom), MAP("body", Var_t::Body), MAP("hybrid", Var_t::Hybrid))),\ + "Configure failed-literal detection (fld)\n" \ + " %A: [,] / Implicit: %I\n" \ + " : Run fld via {atom|body|hybrid} lookahead\n" \ + " : Disable fld after applications ([0]=no limit)\n" \ + " --lookahead=atom is default if --no-lookback is used\n", FUN(arg) { \ + VarType type = Var_t::Atom; uint32 limit = (SELF.lookOps = 0u);\ + return ITE(arg.off(), SET(SELF.lookType, 0u), arg>>type>>opt(limit) && SET(SELF.lookType, (uint32)type)) && SET_OR_ZERO(SELF.lookOps, limit);},\ + GET_IF(SELF.lookType, (VarType)SELF.lookType, SELF.lookOps)) +OPTION(heuristic, "", ARG_EXT(arg(""), DEFINE_ENUM_MAPPING(Heuristic_t::Type, \ + MAP("berkmin", Heuristic_t::Berkmin), MAP("vmtf" , Heuristic_t::Vmtf), \ + MAP("vsids" , Heuristic_t::Vsids) , MAP("domain", Heuristic_t::Domain), \ + MAP("unit" , Heuristic_t::Unit) , MAP("auto", Heuristic_t::Default), MAP("none" , Heuristic_t::None))), \ + "Configure decision heuristic\n" \ + " %A: {Berkmin|Vmtf|Vsids|Domain|Unit|None}[,]\n" \ + " Berkmin: Use BerkMin-like heuristic (Check last nogoods [0]=all)\n" \ + " Vmtf : Use Siege-like heuristic (Move literals to the front [8])\n" \ + " Vsids : Use Chaff-like heuristic (Use 1.0/0. as decay factor [95])\n"\ + " Domain : Use domain knowledge in Vsids-like heuristic\n"\ + " Unit : Use Smodels-like heuristic (Default if --no-lookback)\n" \ + " None : Select the first free variable", FUN(arg) { Heuristic_t::Type h = Heuristic_t::Berkmin; uint32 n = 0u; \ + return arg>>h>>opt(n) && SET(SELF.heuId, (uint32)h) && (Heuristic_t::isLookback(h) || !n) && SET_OR_FILL(SELF.heuristic.param, n);},\ + GET((Heuristic_t::Type)SELF.heuId, SELF.heuristic.param)) +OPTION(init_moms , "!,@2", ARG(flag()) , "Initialize heuristic with MOMS-score", STORE_FLAG(SELF.heuristic.moms), GET(SELF.heuristic.moms)) +OPTION(score_res , ",@2" , ARG_EXT(arg(""), DEFINE_ENUM_MAPPING(HeuParams::Score, \ + MAP("auto", HeuParams::score_auto), MAP("min", HeuParams::score_min), MAP("set", HeuParams::score_set), MAP("multiset", HeuParams::score_multi_set))),\ + "Resolution score {auto|min|set|multiset}", STORE_U(HeuParams::Score, SELF.heuristic.score), GET(static_cast(SELF.heuristic.score))) +OPTION(score_other, ",@2" , ARG_EXT(arg(""), DEFINE_ENUM_MAPPING(HeuParams::ScoreOther, \ + MAP("auto", HeuParams::other_auto), MAP("no", HeuParams::other_no), MAP("loop", HeuParams::other_loop), MAP("all", HeuParams::other_all))),\ + "Score other learnt nogoods: {auto|no|loop|all}", STORE_U(HeuParams::ScoreOther, SELF.heuristic.other), GET(static_cast(SELF.heuristic.other))) +OPTION(sign_def , ",@1" , ARG_EXT(arg(""),\ + DEFINE_ENUM_MAPPING(SolverStrategies::SignHeu, MAP("asp", SolverStrategies::sign_atom), MAP("pos", SolverStrategies::sign_pos), MAP("neg", SolverStrategies::sign_neg), MAP("rnd", SolverStrategies::sign_rnd))),\ + "Default sign: {asp|pos|neg|rnd}", STORE_U(SolverStrategies::SignHeu, SELF.signDef), GET((SolverStrategies::SignHeu)SELF.signDef)) +OPTION(sign_fix , "!,@2", ARG(flag()) , "Disable sign heuristics and use default signs only", STORE_FLAG(SELF.signFix), GET(SELF.signFix)) +OPTION(berk_huang , "!,@2", ARG(flag()) , "Enable Huang-scoring in Berkmin", STORE_FLAG(SELF.heuristic.huang), GET(SELF.heuristic.huang)) +OPTION(vsids_acids, "!,@2", ARG(flag()) , "Enable acids-scheme in Vsids/Domain", STORE_FLAG(SELF.heuristic.acids), GET(SELF.heuristic.acids)) +OPTION(vsids_progress, ",@2", NO_ARG, "Enable dynamic decaying scheme in Vsids/Domain\n"\ + " %A: [,][,]|(0=disable)\n"\ + " : Set initial decay factor to 1.0/0.\n"\ + " : Set decay update to /100.0 [1]\n"\ + " : Decrease decay every conflicts [5000]", \ + FUN(arg) { uint32 n = 80; uint32 i = 1; uint32 c = 5000; \ + return ITE(arg.off(), SET(SELF.heuristic.decay.init, 0), (arg >> n >> opt(i) >> opt(c))\ + && SET(SELF.heuristic.decay.init, n) && SET_LEQ(SELF.heuristic.decay.bump, i, 100) && SET(SELF.heuristic.decay.freq, c));}, \ + GET_IF(SELF.heuristic.decay.init, SELF.heuristic.decay.init, SELF.heuristic.decay.bump, SELF.heuristic.decay.freq)) +OPTION(nant , "!,@2", ARG(flag()) , "Prefer negative antecedents of P in heuristic", STORE_FLAG(SELF.heuristic.nant), GET(SELF.heuristic.nant)) +OPTION(dom_mod , ",@1" , ARG_EXT(arg(""), DEFINE_ENUM_MAPPING(HeuParams::DomMod, \ + MAP("level", HeuParams::mod_level), MAP("pos", HeuParams::mod_spos), MAP("true", HeuParams::mod_true),\ + MAP("neg", HeuParams::mod_sneg), MAP("false", HeuParams::mod_false), MAP("init", HeuParams::mod_init), MAP("factor", HeuParams::mod_factor))\ + DEFINE_ENUM_MAPPING(HeuParams::DomPref, MAP("all", HeuParams::pref_atom), MAP("scc", HeuParams::pref_scc), MAP("hcc", HeuParams::pref_hcc),\ + MAP("disj", HeuParams::pref_disj), MAP("opt", HeuParams::pref_min), MAP("show", HeuParams::pref_show))),\ + "Default modification for domain heuristic\n"\ + " %A: (no|[,])\n"\ + " : Modifier {level|pos|true|neg|false|init|factor}\n"\ + " : Apply to (all | ) atoms", \ + FUN(arg) { HeuParams::DomMod modK; unsigned modN = 0; Set k; bool ok = true;\ + if (!arg.off()) { ok = ITE(arg.peek() >= 'A', arg >> modK && SET(modN, uint32(modK)), arg >> modN && modN > 0u && modN < 8u); }\ + return ok && (arg.off() || arg >> opt(k)) && SET(SELF.heuristic.domMod, modN) && SET(SELF.heuristic.domPref, k.value());},\ + GET_FUN(str) { Set mod(SELF.heuristic.domMod); Set pick(SELF.heuristic.domPref); \ + ITE(mod.value() && pick.value(), str << mod << pick, str << mod); }) +OPTION(save_progress, "", ARG(implicit("1")->arg("")), "Use RSat-like progress saving on backjumps > %A", STORE_OR_FILL(SELF.saveProgress), GET(SELF.saveProgress)) +OPTION(init_watches , ",@2", ARG_EXT(arg(""), DEFINE_ENUM_MAPPING(SolverStrategies::WatchInit, \ + MAP("rnd", SolverStrategies::watch_rand), MAP("first", SolverStrategies::watch_first), MAP("least", SolverStrategies::watch_least))),\ + "Watched literal initialization: {rnd|first|least}", STORE_U(SolverStrategies::WatchInit, SELF.initWatches), GET(static_cast(SELF.initWatches))) +OPTION(update_mode , ",@2", ARG_EXT(arg(""), DEFINE_ENUM_MAPPING(SolverStrategies::UpdateMode, \ + MAP("propagate", SolverStrategies::update_on_propagate), MAP("conflict", SolverStrategies::update_on_conflict))),\ + "Process messages on {propagate|conflict}", STORE_U(SolverStrategies::UpdateMode, SELF.upMode), GET(static_cast(SELF.upMode))) +OPTION(acyc_prop, ",@2", ARG(implicit("1")->arg("{0..1}")), "Use backward inference in acyc propagation", \ + FUN(arg) { uint32 x; return arg>>x && SET_LEQ(SELF.acycFwd, (1u-x), 1u); }, GET(1u-SELF.acycFwd)) +OPTION(seed , "" , ARG(arg("")),"Set random number generator's seed to %A", STORE(SELF.seed), GET(SELF.seed)) +OPTION(no_lookback , "" , ARG(flag()), "Disable all lookback strategies\n", STORE_FLAG(SELF.search),GET(static_cast(SELF.search == SolverStrategies::no_learning))) +OPTION(forget_on_step, "" , ARG_EXT(arg(""), DEFINE_ENUM_MAPPING(SolverParams::Forget, \ + MAP("varScores", SolverParams::forget_heuristic), MAP("signs", SolverParams::forget_signs), MAP("lemmaScores", SolverParams::forget_activities), MAP("lemmas", SolverParams::forget_learnts))),\ + "Configure forgetting on (incremental) step\n"\ + " %A: |\n",\ + FUN(arg) { Set s; return (arg.off() || arg >> s) && SET(SELF.forgetSet, s.value()); },\ + GET(Set(SELF.forgetSet))) +OPTION(strengthen , "!" , ARG_EXT(arg(""),\ + DEFINE_ENUM_MAPPING(SolverStrategies::CCMinType, MAP("local", SolverStrategies::cc_local), MAP("recursive", SolverStrategies::cc_recursive))\ + DEFINE_ENUM_MAPPING(SolverStrategies::CCMinAntes, MAP("all", SolverStrategies::all_antes), MAP("short", SolverStrategies::short_antes), MAP("binary", SolverStrategies::binary_antes))), \ + "Use MiniSAT-like conflict nogood strengthening\n" \ + " %A: [,][,]\n" \ + " : Use {local|recursive} self-subsumption check\n" \ + " : Follow {all|short|binary} antecedents [all]\n" \ + " : Bump activities of antecedents [yes]", FUN(arg) {\ + SolverStrategies::CCMinType m = SolverStrategies::cc_local; SolverStrategies::CCMinAntes t = SolverStrategies::no_antes; bool b = true; \ + return (arg.off() || arg>>m>>opt(t = SolverStrategies::all_antes)>>opt(b)) && SET(SELF.ccMinAntes, (uint32)t) && SET(SELF.ccMinRec, (uint32)m) && SET(SELF.ccMinKeepAct, uint32(!b)); }, \ + GET_IF(SELF.ccMinAntes != SolverStrategies::no_antes, (SolverStrategies::CCMinType)SELF.ccMinRec, (SolverStrategies::CCMinAntes)SELF.ccMinAntes, ITE(SELF.ccMinKeepAct, "no", "yes"))) +OPTION(otfs , "" , ARG(implicit("1")->arg("{0..2}")), "Enable {1=partial|2=full} on-the-fly subsumption", STORE_LEQ(SELF.otfs, 2u), GET(SELF.otfs)) +OPTION(update_lbd , "!,@2" , ARG_EXT(arg(""), DEFINE_ENUM_MAPPING(SolverStrategies::LbdMode, \ + MAP("less", SolverStrategies::lbd_updated_less), MAP("glucose", SolverStrategies::lbd_update_glucose), MAP("pseudo", SolverStrategies::lbd_update_pseudo))),\ + "Configure LBD updates during conflict resolution\n" \ + " %A: [,]\n" \ + " less : update to X = new LBD iff X < previous LBD\n" \ + " glucose: update to X = new LBD iff X+1 < previous LBD\n" \ + " pseudo : update to X = new LBD+1 iff X < previous LBD\n" \ + " : Protect updated nogoods on next reduce if X <= ",\ + FUN(arg) { SolverStrategies::LbdMode n = SolverStrategies::lbd_fixed; uint32 m = 0; \ + return (arg.off() || (arg >> n >> opt(m) && n > 0)) && SET(SELF.updateLbd, uint32(n)) && SET_LEQ(search->reduce.strategy.protect, m, uint32(Clasp::LBD_MAX));},\ + GET_IF(SELF.updateLbd, (SolverStrategies::LbdMode)SELF.updateLbd, search->reduce.strategy.protect)) +OPTION(update_act , ",@2", ARG(flag()), "Enable LBD-based activity bumping", STORE_FLAG(SELF.bumpVarAct), GET(SELF.bumpVarAct)) +OPTION(reverse_arcs, "" , ARG(implicit("1")->arg("{0..3}")), "Enable ManySAT-like inverse-arc learning", STORE_LEQ(SELF.reverseArcs, 3u), GET(SELF.reverseArcs)) +OPTION(contraction , "!,@2", ARG_EXT(arg(""),\ + DEFINE_ENUM_MAPPING(SolverStrategies::CCRepMode, MAP("no", SolverStrategies::cc_no_replace), MAP("decisionSeq", SolverStrategies::cc_rep_decision), MAP("allUIP", SolverStrategies::cc_rep_uip), MAP("dynamic", SolverStrategies::cc_rep_dynamic))),\ + "Configure handling of long learnt nogoods\n" + " %A: [,]\n"\ + " : Contract nogoods if size > (0=disable)\n"\ + " : Nogood replacement {no|decisionSeq|allUIP|dynamic} [no]\n", FUN(arg) { uint32 n = 0; SolverStrategies::CCRepMode r = SolverStrategies::cc_no_replace;\ + return (arg.off() || (arg>>n>>opt(r) && n != 0u)) && SET_OR_FILL(SELF.compress, n) && SET(SELF.ccRepMode, uint32(r));},\ + GET_IF(SELF.compress, SELF.compress, (SolverStrategies::CCRepMode)SELF.ccRepMode)) +OPTION(loops, "" , ARG_EXT(arg(""), DEFINE_ENUM_MAPPING(DefaultUnfoundedCheck::ReasonStrategy,\ + MAP("common" , DefaultUnfoundedCheck::common_reason) , MAP("shared", DefaultUnfoundedCheck::shared_reason), \ + MAP("distinct", DefaultUnfoundedCheck::distinct_reason), MAP("no", DefaultUnfoundedCheck::only_reason))),\ + "Configure learning of loop nogoods\n" \ + " %A: {common|distinct|shared|no}\n" \ + " common : Create loop nogoods for atoms in an unfounded set\n" \ + " distinct: Create distinct loop nogood for each atom in an unfounded set\n" \ + " shared : Create loop formula for a whole unfounded set\n" \ + " no : Do not learn loop formulas\n", FUN(arg) {DefaultUnfoundedCheck::ReasonStrategy x; return arg>>x && SET(SELF.loopRep, (uint32)x);},\ + GET(static_cast(SELF.loopRep))) +GROUP_END(SELF) +#undef CLASP_SOLVER_OPTIONS +#undef SELF +#endif + +//! Search-related options (see SolveParams). +#if defined(CLASP_SEARCH_OPTIONS) +#define SELF CLASP_SEARCH_OPTIONS +GROUP_BEGIN(SELF) +OPTION(partial_check, "", ARG(implicit("50")), "Configure partial stability tests\n" \ + " %A:

[,] / Implicit: %I\n" \ + "

: Partial check skip percentage\n" \ + " : Init/update value for high bound ([0]=umax)", FUN(arg) {\ + uint32 p = 0; uint32 h = 0; \ + return (arg.off() || (arg>>p>>opt(h) && p)) && SET_LEQ(SELF.fwdCheck.highPct, p, 100u) && SET_OR_ZERO(SELF.fwdCheck.highStep, h);},\ + GET_IF(SELF.fwdCheck.highPct, SELF.fwdCheck.highPct, SELF.fwdCheck.highStep)) +OPTION(sign_def_disj, ",@2", ARG(arg("")), "Default sign for atoms in disjunctions", STORE_U(SolverStrategies::SignHeu, SELF.fwdCheck.signDef), GET((SolverStrategies::SignHeu)SELF.fwdCheck.signDef)) +OPTION(rand_freq, "!", ARG(arg("

")), "Make random decisions with probability %A", FUN(arg) {\ + double f = 0.0; \ + return (arg.off() || arg>>f) && SET_R(SELF.randProb, (float)f, 0.0f, 1.0f);}, GET(SELF.randProb)) +OPTION(rand_prob, "", ARG(arg("[,]")), "Do random searches with [=100] conflicts",\ + FUN(arg) { uint32 n1 = 0; uint32 n2 = 100;\ + return (arg.off() || (arg>>n1>>opt(n2) && n1)) && SET_OR_FILL(SELF.randRuns, n1) && SET_OR_FILL(SELF.randConf, n2);},\ + GET_IF(SELF.randRuns, SELF.randRuns,SELF.randConf)) +#undef SELF +//! Options for configuring the restart strategy of a solver. +#define SELF CLASP_SEARCH_RESTART_OPTIONS +#if defined(NOTIFY_SUBGROUPS) +GROUP_BEGIN(SELF) +#endif +OPTION(restarts, "!,r", ARG(arg("")), "Configure restart policy\n" \ + " %A: ,[,][,]\n" \ + " F, : Run fixed sequence of conflicts\n" \ + " L, : Run Luby et al.'s sequence with unit length \n" \ + " x,,: Run geometric seq. of *(^i) conflicts ( >= 1.0)\n" \ + " +,,: Run arithmetic seq. of +(*i) conflicts ()\n"\ + " ...,: Repeat seq. every +j restarts ( != F)\n" \ + " D,,: Restart based on moving LBD average over last conflicts\n" \ + " Mavg(,LBD)* > avg(LBD)\n" \ + " use conflict level average if > 0 and avg(LBD) > \n"\ + " no|0 : Disable restarts", FUN(arg) { return ITE(arg.off(), (SELF.disable(),true), \ + arg>>SELF.sched && SET(SELF.dynRestart, uint32(SELF.sched.type == ScheduleStrategy::User)));}, GET(SELF.sched)) +OPTION(reset_restarts , ",@2", ARG_EXT(arg(""), DEFINE_ENUM_MAPPING(RestartParams::SeqUpdate, \ + MAP("no",RestartParams::seq_continue), MAP("repeat", RestartParams::seq_repeat), MAP("disable", RestartParams::seq_disable))),\ + "Update restart seq. on model {no|repeat|disable}",\ + STORE_U(RestartParams::SeqUpdate, SELF.upRestart), GET(static_cast(SELF.upRestart))) +OPTION(local_restarts , "!" , ARG(flag()), "Use Ryvchin et al.'s local restarts", STORE_FLAG(SELF.cntLocal), GET(SELF.cntLocal)) +OPTION(counter_restarts, "" , ARG(arg("")), "Use counter implication restarts\n" \ + " %A: ([,] | {0|no})\n" \ + " : Interval in number of restarts\n" \ + " : Bump factor applied to indegrees", \ + FUN(arg) { uint32 n = 0; uint32 m = SELF.counterBump; \ + return (arg.off() || (arg >> n >> opt(m) && n > 0)) && SET_OR_FILL(SELF.counterRestart, n) && SET_OR_FILL(SELF.counterBump, m); },\ + GET_IF(SELF.counterRestart, SELF.counterRestart, SELF.counterBump)) +OPTION(block_restarts , "" , ARG(arg("")), "Use glucose-style blocking restarts\n" \ + " %A: [,][,]\n" \ + " : Window size for moving average (0=disable blocking)\n" \ + " : Block restart if assignment > average * [1.4]\n" \ + " : Disable blocking for the first conflicts [10000]\n", FUN(arg) { \ + uint32 n = 0; double R = 0.0; uint32 x = 0;\ + return (arg.off() || (arg>>n>>opt(R = 1.4)>>opt(x = 10000) && n && R >= 1.0 && R <= 5.0))\ + && SET(SELF.blockWindow, n) && SET(SELF.blockScale, (float)R) && SET_OR_FILL(SELF.blockFirst, x);},\ + GET_IF(SELF.blockWindow, SELF.blockWindow, SELF.blockScale, SELF.blockFirst)) +OPTION(shuffle , "!" , ARG(arg(",")), "Shuffle problem after +(*i) restarts\n", FUN(arg) { uint32 n1 = 0; uint32 n2 = 0;\ + return (arg.off() || (arg>>n1>>opt(n2) && n1)) && SET_OR_FILL(SELF.shuffle, n1) && SET_OR_FILL(SELF.shuffleNext, n2);},\ + GET_IF(SELF.shuffle, SELF.shuffle, SELF.shuffleNext)) +#if defined(NOTIFY_SUBGROUPS) +GROUP_END(SELF) +#endif +#undef SELF +#undef CLASP_SEARCH_RESTART_OPTIONS +//! Options for configuring the deletion strategy of a solver. +#define SELF CLASP_SEARCH_REDUCE_OPTIONS +#if defined(NOTIFY_SUBGROUPS) +GROUP_BEGIN(SELF) +#endif +OPTION(deletion , "!,d", ARG_EXT(defaultsTo("basic,75,activity")->state(Value::value_defaulted), DEFINE_ENUM_MAPPING(ReduceStrategy::Algorithm,\ + MAP("basic", ReduceStrategy::reduce_linear), MAP("sort", ReduceStrategy::reduce_stable),\ + MAP("ipSort", ReduceStrategy::reduce_sort) , MAP("ipHeap", ReduceStrategy::reduce_heap))\ + DEFINE_ENUM_MAPPING(ReduceStrategy::Score, MAP("activity",ReduceStrategy::score_act), MAP("lbd", ReduceStrategy::score_lbd), MAP("mixed", ReduceStrategy::score_both))),\ + "Configure deletion algorithm [%D]\n" \ + " %A: [,][,]\n" \ + " : Use {basic|sort|ipSort|ipHeap} algorithm\n" \ + " : Delete at most %% of nogoods on reduction [75]\n" \ + " : Use {activity|lbd|mixed} nogood scores [activity]\n" \ + " no : Disable nogood deletion", FUN(arg){\ + ReduceStrategy::Algorithm algo = ReduceStrategy::reduce_linear; uint32 n; ReduceStrategy::Score sc;\ + return ITE(arg.off(), (SELF.disable(), true), arg>>algo>>opt(n = 75)>>opt(sc = ReduceStrategy::score_act)\ + && SET(SELF.strategy.algo, (uint32)algo) && SET_R(SELF.strategy.fReduce, n, 1, 100) && SET(SELF.strategy.score, (uint32)sc));},\ + GET_IF(SELF.strategy.fReduce, (ReduceStrategy::Algorithm)SELF.strategy.algo, SELF.strategy.fReduce,(ReduceStrategy::Score)SELF.strategy.score)) +OPTION(del_grow , "!", NO_ARG, "Configure size-based deletion policy\n" \ + " %A: [,][,] ( >= 1.0)\n" \ + " : Keep at most T = X*(^i) learnt nogoods with X being the\n"\ + " initial limit and i the number of times fired\n" \ + " : Stop growth once T > P* (0=no limit) [3.0]\n" \ + " : Set grow schedule () [grow on restart]", FUN(arg){ double f; double g; ScheduleStrategy sc = ScheduleStrategy::def();\ + return ITE(arg.off(), (SELF.growSched = ScheduleStrategy::none(), SELF.fGrow = 0.0f, true),\ + arg>>f>>opt(g = 3.0)>>opt(sc) && SET_R(SELF.fGrow, (float)f, 1.0f, FLT_MAX) && SET_R(SELF.fMax, (float)g, 0.0f, FLT_MAX)\ + && (sc.defaulted() || sc.type != ScheduleStrategy::User) && (SELF.growSched=sc, true));},\ + GET_FUN(str) { if (SELF.fGrow == 0.0f) str<")), "Configure conflict-based deletion policy\n" \ + " %A: ,... (see restarts)", FUN(arg){\ + return ITE(arg.off(), (SELF.cflSched=ScheduleStrategy::none()).disabled(), arg>>SELF.cflSched && SELF.cflSched.type != ScheduleStrategy::User);}, GET(SELF.cflSched)) +OPTION(del_init , "" , ARG(defaultsTo("3.0")->state(Value::value_defaulted)), "Configure initial deletion limit\n"\ + " %A: [,,] ( > 0)\n" \ + " : Set initial limit to P=estimated problem size/ [%D]\n" \ + " ,: Clamp initial limit to the range [,+]" , FUN(arg) { double f; uint32 lo = 10; uint32 hi = UINT32_MAX;\ + return arg>>f>>opt(lo)>>opt(hi) && f > 0.0 && (SELF.fInit = float(1.0 / f)) > 0 && SET_OR_FILL(SELF.initRange.lo, lo) && SET_OR_FILL(SELF.initRange.hi, (uint64(hi)+SELF.initRange.lo));},\ + GET_IF(SELF.fInit, 1.0/SELF.fInit, SELF.initRange.lo, SELF.initRange.hi - SELF.initRange.lo)) +OPTION(del_estimate, "", ARG(arg("0..3")->implicit("1")), "Use estimated problem complexity in limits", STORE_LEQ(SELF.strategy.estimate, 3u), GET(SELF.strategy.estimate)) +OPTION(del_max , "!", ARG(arg(",")), "Keep at most learnt nogoods taking up to MB", FUN(arg) { uint32 n = UINT32_MAX; uint32 mb = 0; \ + return (arg.off() || arg>>n>>opt(mb)) && SET_GEQ(SELF.maxRange, n, 1u) && SET(SELF.memMax, mb);}, GET(SELF.maxRange, SELF.memMax)) +OPTION(del_glue , "", NO_ARG, "Configure glue clause handling\n" \ + " %A: [,]\n" \ + " : Do not delete nogoods with LBD <= \n" \ + " : Count (0) or ignore (1) glue clauses in size limit [0]", FUN(arg) {uint32 lbd; uint32 m = 0; \ + return arg>>lbd>>opt(m) && SET(SELF.strategy.glue, lbd) && SET(SELF.strategy.noGlue, m);}, GET(SELF.strategy.glue, SELF.strategy.noGlue)) +OPTION(del_on_restart, "", ARG(arg("")), "Delete %A%% of learnt nogoods on each restart", STORE_LEQ(SELF.strategy.fRestart, 100u), GET(SELF.strategy.fRestart)) +#if defined(NOTIFY_SUBGROUPS) +GROUP_END(SELF) +#endif +#undef SELF +#undef CLASP_SEARCH_REDUCE_OPTIONS +GROUP_END(CLASP_SEARCH_OPTIONS) +#undef CLASP_SEARCH_OPTIONS +#endif + +//! ASP-front-end options stored in an Clasp::Asp::LogicProgram::AspOptions object. +#if defined(CLASP_ASP_OPTIONS) +#define SELF CLASP_ASP_OPTIONS +GROUP_BEGIN(SELF) +OPTION(trans_ext , "!", ARG_EXT(arg(""), DEFINE_ENUM_MAPPING(Asp::LogicProgram::ExtendedRuleMode,\ + MAP("no" , Asp::LogicProgram::mode_native) , MAP("all" , Asp::LogicProgram::mode_transform),\ + MAP("choice", Asp::LogicProgram::mode_transform_choice), MAP("card", Asp::LogicProgram::mode_transform_card),\ + MAP("weight", Asp::LogicProgram::mode_transform_weight), MAP("scc" , Asp::LogicProgram::mode_transform_scc),\ + MAP("integ" , Asp::LogicProgram::mode_transform_integ) , MAP("dynamic", Asp::LogicProgram::mode_transform_dynamic))),\ + "Configure handling of extended rules\n"\ + " %A: {all|choice|card|weight|integ|dynamic}\n"\ + " all : Transform all extended rules to basic rules\n"\ + " choice : Transform choice rules, but keep cardinality and weight rules\n"\ + " card : Transform cardinality rules, but keep choice and weight rules\n"\ + " weight : Transform cardinality and weight rules, but keep choice rules\n"\ + " scc : Transform \"recursive\" cardinality and weight rules\n"\ + " integ : Transform cardinality integrity constraints\n"\ + " dynamic: Transform \"simple\" extended rules, but keep more complex ones", STORE(SELF.erMode), GET((Asp::LogicProgram::ExtendedRuleMode)SELF.erMode)) +OPTION(eq, "", ARG(arg("")), "Configure equivalence preprocessing\n" \ + " Run for at most %A iterations (-1=run to fixpoint)", STORE_OR_FILL(SELF.iters), GET(SELF.iters)) +OPTION(backprop ,"!,@1", ARG(flag()), "Use backpropagation in ASP-preprocessing", STORE_FLAG(SELF.backprop), GET(SELF.backprop)) +OPTION(supp_models , ",@1", ARG(flag()), "Compute supported models", STORE_FLAG(SELF.suppMod), GET(SELF.suppMod)) +OPTION(no_ufs_check, ",@1", ARG(flag()), "Disable unfounded set check", STORE_FLAG(SELF.noSCC), GET(SELF.noSCC)) +OPTION(no_gamma , ",@1", ARG(flag()), "Do not add gamma rules for non-hcf disjunctions", STORE_FLAG(SELF.noGamma), GET(SELF.noGamma)) +OPTION(eq_dfs , ",@2", ARG(flag()), "Enable df-order in eq-preprocessing", STORE_FLAG(SELF.dfOrder), GET(SELF.dfOrder)) +OPTION(dlp_old_map , ",@3", ARG(flag()), "Enable old mapping for disjunctive LPs", STORE_FLAG(SELF.oldMap), GET(SELF.oldMap)) +GROUP_END(SELF) +#undef CLASP_ASP_OPTIONS +#undef SELF +#endif + +//! Options for the solving algorithm (see Clasp::SolveOptions) +#if defined(CLASP_SOLVE_OPTIONS) +#define SELF CLASP_SOLVE_OPTIONS +GROUP_BEGIN(SELF) +OPTION(solve_limit , ",@1", ARG(arg("[,]")), "Stop search after conflicts or restarts\n", FUN(arg) {\ + uint32 n = UINT32_MAX; uint32 m = UINT32_MAX;\ + return ((arg.off() && arg.peek() != '0') || arg>>n>>opt(m)) && (SELF.limit=SolveLimits(n == UINT32_MAX ? UINT64_MAX : n, m == UINT32_MAX ? UINT64_MAX : m), true);},\ + GET((uint32)Range(0u,UINT32_MAX).clamp(SELF.limit.conflicts),(uint32)Range(0u,UINT32_MAX).clamp(SELF.limit.restarts))) +#if CLASP_HAS_THREADS +OPTION(parallel_mode, ",t", ARG_EXT(arg(""), DEFINE_ENUM_MAPPING(SolveOptions::Algorithm::SearchMode,\ + MAP("compete", SolveOptions::Algorithm::mode_compete), MAP("split", SolveOptions::Algorithm::mode_split))),\ + "Run parallel search with given number of threads\n" \ + " %A: [,]\n" \ + " : Number of threads to use in search\n"\ + " : Run competition or splitting based search [compete]\n", FUN(arg){\ + uint32 n; SolveOptions::Algorithm::SearchMode mode;\ + return arg>>n>>opt(mode = SolveOptions::Algorithm::mode_compete) && SET_R(SELF.algorithm.threads, n, 1u, 64u) && SET(SELF.algorithm.mode, mode);},\ + GET(SELF.algorithm.threads, (SolveOptions::Algorithm::SearchMode)SELF.algorithm.mode)) +OPTION(global_restarts, ",@1", ARG(arg("")), "Configure global restart policy\n" \ + " %A: [,]\n" \ + " : Maximal number of global restarts (0=disable)\n" \ + " : Restart schedule [x,100,1.5] ()\n", FUN(arg) {\ + return ITE(arg.off(), (SELF.restarts = SolveOptions::GRestarts(), true), arg>>SELF.restarts.maxR>>opt(SELF.restarts.sched = ScheduleStrategy())\ + && SELF.restarts.maxR && SELF.restarts.sched.type != ScheduleStrategy::User);},\ + GET_IF(SELF.restarts.maxR, SELF.restarts.maxR, SELF.restarts.sched)) +OPTION(distribute, "!,@1", ARG_EXT(defaultsTo("conflict,global,4"), \ + DEFINE_ENUM_MAPPING(Distributor::Policy::Types, MAP("all", Distributor::Policy::all), MAP("short", Distributor::Policy::implicit), MAP("conflict", Distributor::Policy::conflict), MAP("loop" , Distributor::Policy::loop))\ + DEFINE_ENUM_MAPPING(SolveOptions::Distribution::Mode, MAP("global", SolveOptions::Distribution::mode_global), MAP("local", SolveOptions::Distribution::mode_local))),\ + "Configure nogood distribution [%D]\n" \ + " %A: [,][,][,]\n" \ + " : Distribute {all|short|conflict|loop} nogoods\n" \ + " : Use {global|local} distribution [global]\n" \ + " : Distribute only if LBD <= [4]\n" \ + " : Distribute only if size <= [-1]", \ + FUN(arg) { Distributor::Policy::Types type; SolveOptions::Distribution::Mode mode = SolveOptions::Distribution::mode_global; uint32 lbd; uint32 size; \ + if (arg.off()) { SELF.distribute.policy() = Distributor::Policy(0, 0, 0); return true; } + return (arg >> type) && (arg.peek() < 'A' || arg >> opt(mode)) && arg >> opt(lbd = 4) >> opt(size = UINT32_MAX) + && SET(SELF.distribute.types, (uint32)type) && SET(SELF.distribute.mode, (uint32)mode) && SET(SELF.distribute.lbd, lbd) && SET_OR_FILL(SELF.distribute.size, size);},\ + GET_FUN(str) { ITE(!SELF.distribute.types, str << off,\ + str << (Distributor::Policy::Types)SELF.distribute.types << (SolveOptions::Distribution::Mode)SELF.distribute.mode << SELF.distribute.lbd << SELF.distribute.size); }) +OPTION(integrate, ",@1", ARG_EXT(defaultsTo("gp")->state(Value::value_defaulted),\ + DEFINE_ENUM_MAPPING(SolveOptions::Integration::Filter, \ + MAP("all", SolveOptions::Integration::filter_no), MAP("gp", SolveOptions::Integration::filter_gp),\ + MAP("unsat", SolveOptions::Integration::filter_sat), MAP("active", SolveOptions::Integration::filter_heuristic))\ + DEFINE_ENUM_MAPPING(SolveOptions::Integration::Topology, \ + MAP("all" , SolveOptions::Integration::topo_all) , MAP("ring" , SolveOptions::Integration::topo_ring),\ + MAP("cube", SolveOptions::Integration::topo_cube), MAP("cubex", SolveOptions::Integration::topo_cubex))),\ + "Configure nogood integration [%D]\n" \ + " %A: [,][,]\n" \ + " : Add {all|unsat|gp(unsat wrt guiding path)|active} nogoods\n" \ + " : Always keep at least last integrated nogoods [1024]\n" \ + " : Accept nogoods from {all|ring|cube|cubex} peers [all]\n", FUN(arg) {\ + SolveOptions::Integration::Filter pick = SolveOptions::Integration::filter_no; uint32 n; SolveOptions::Integration::Topology topo; + return arg>>pick>>opt(n = 1024)>>opt(topo = SolveOptions::Integration::topo_all) && SET(SELF.integrate.filter, (uint32)pick) && SET_OR_FILL(SELF.integrate.grace, n) && SET(SELF.integrate.topo, (uint32)topo);},\ + GET((SolveOptions::Integration::Filter)SELF.integrate.filter, SELF.integrate.grace, (SolveOptions::Integration::Topology)SELF.integrate.topo)) +#endif +OPTION(enum_mode , ",e", ARG_EXT(defaultsTo("auto")->state(Value::value_defaulted), DEFINE_ENUM_MAPPING(SolveOptions::EnumType,\ + MAP("bt", SolveOptions::enum_bt), MAP("record", SolveOptions::enum_record), MAP("domRec", SolveOptions::enum_dom_record),\ + MAP("brave", SolveOptions::enum_brave), MAP("cautious", SolveOptions::enum_cautious), MAP("query", SolveOptions::enum_query),\ + MAP("auto", SolveOptions::enum_auto), MAP("user", SolveOptions::enum_user))),\ + "Configure enumeration algorithm [%D]\n" \ + " %A: {bt|record|brave|cautious|auto}\n" \ + " bt : Backtrack decision literals from solutions\n" \ + " record : Add nogoods for computed solutions\n" \ + " domRec : Add nogoods over true domain atoms\n" \ + " brave : Compute brave consequences (union of models)\n" \ + " cautious: Compute cautious consequences (intersection of models)\n" \ + " auto : Use bt for enumeration and record for optimization", STORE(SELF.enumMode), GET(SELF.enumMode)) +OPTION(project, "!", ARG_EXT(arg("")->implicit("auto,3"), DEFINE_ENUM_MAPPING(ProjectMode_t::Mode,\ + MAP("auto", ProjectMode_t::Implicit), MAP("show", ProjectMode_t::Output), MAP("project", ProjectMode_t::Explicit))),\ + "Enable projective solution enumeration\n" \ + " %A: {show|project|auto}[,] (Implicit: %I)\n" \ + " Project to atoms in show or project directives, or\n" \ + " select depending on the existence of a project directive\n" \ + " : Additional options for enumeration algorithm 'bt'\n" \ + " Use activity heuristic (1) when selecting backtracking literal\n" \ + " and/or progress saving (2) when retracting solution literals", \ + FUN(arg) { ProjectMode m = ProjectMode_t::Implicit; uint32 p = 0; \ + return (arg.off() || (arg.peek() < '9' && arg >> p) || ((arg >> m >> opt(p)) && (p = (p<<1)|1) != 0u)) && SET(SELF.proMode, m) && SET_LEQ(SELF.project, p, 7u);},\ + GET_IF(SELF.project, SELF.proMode, SELF.project >> 1)) +OPTION(models, ",n", ARG(arg("")), "Compute at most %A models (0 for all)\n", STORE(SELF.numModels), GET(SELF.numModels)) +OPTION(opt_mode , "", ARG_EXT(arg(""), DEFINE_ENUM_MAPPING(MinimizeMode_t::Mode,\ + MAP("opt" , MinimizeMode_t::optimize), MAP("enum" , MinimizeMode_t::enumerate),\ + MAP("optN", MinimizeMode_t::enumOpt) , MAP("ignore", MinimizeMode_t::ignore))),\ + "Configure optimization algorithm\n"\ + " %A: [,...]\n" \ + " opt : Find optimal model\n" \ + " enum : Find models with costs <= \n" \ + " optN : Find optimum, then enumerate optimal models\n"\ + " ignore: Ignore optimize statements\n" \ + " : Set initial bound for objective function(s)", \ + FUN(arg) { MinimizeMode_t::Mode m = MinimizeMode_t::optimize; SumVec B; return (arg >> m >> opt(B)) && SET(SELF.optMode, m) && (SELF.optBound.swap(B), true); }, \ + GET_FUN(str) { str << SELF.optMode; if (!SELF.optBound.empty()) str << SELF.optBound; }) +GROUP_END(SELF) +#undef CLASP_SOLVE_OPTIONS +#undef SELF +#endif + +#undef GROUP_BEGIN +#undef GROUP_END +#undef OPTION +#undef NOTIFY_SUBGROUPS +#undef ARG +#undef ARG_EXT +#undef NO_ARG diff --git a/clasp/clasp/cli/clasp_options.h b/clasp/clasp/cli/clasp_options.h new file mode 100644 index 000000000..da6edf25f --- /dev/null +++ b/clasp/clasp/cli/clasp_options.h @@ -0,0 +1,317 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_CLI_CLASP_OPTIONS_H_INCLUDED +#define CLASP_CLI_CLASP_OPTIONS_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +namespace Potassco { namespace ProgramOptions { +class OptionContext; +class OptionGroup; +class ParsedOptions; +}} +/*! + * \file + * \brief Types and functions for processing command-line options. + */ +namespace Clasp { +//! Namespace for types and functions used by the command-line interface. +namespace Cli { + +/** + * \defgroup cli Cli + * \brief Types mainly relevant to the command-line interface. + * \ingroup facade + * @{ + */ + +class ClaspCliConfig; +//! Class for iterating over a set of configurations. +class ConfigIter { +public: + const char* name() const; + const char* base() const; + const char* args() const; + bool valid()const; + bool next(); +private: + friend class ClaspCliConfig; + ConfigIter(const char* x); + const char* base_; +}; +//! Valid configuration keys. +/*! + * \see clasp_cli_configs.inl + */ +enum ConfigKey { +#define CONFIG(id,k,c,s,p) config_##k, +#define CLASP_CLI_DEFAULT_CONFIGS config_default = 0, +#define CLASP_CLI_AUX_CONFIGS config_default_max_value, +#include + config_aux_max_value, + config_many, // default portfolio + config_max_value, + config_asp_default = config_tweety, + config_sat_default = config_trendy, + config_tester_default= config_tester, +}; +/*! + * \brief Class for storing/processing command-line options. + * + * Caveats (when using incrementally, e.g. from clingo): + * - supp-models: State Transition (yes<->no) not supported. + * - supp-models=yes is irreversible for a step + * because it enables possibly destructive simplifications + * and skips SCC-checking (i.e. new SCCs are silently discarded). + * - Nogoods learnt during supp-models=no are not tagged and + * hence can't simply be removed on transition to yes. + * . + * - stats: Stats level can only be increased. + * - A stats level once activated stays activated even if + * level is subsequently decreased via option. + * . + * - save-progress, sign-fix, opt-heuristic: No unset of previously set values. + * - Once set, signs are only unset if forgetOnStep includes sign. + * . + * - no-lookback: State Transition (yes<->no) not supported. + * - noLookback=yes is a destructive meta-option that disables lookback-options by changing their value + * - noLookback=no does not re-enable those options. + * . + */ +class ClaspCliConfig : public ClaspConfig { +public: + //! Returns defaults for the given problem type. + static const char* getDefaults(ProblemType f); + //! Returns the configuration with the given key. + static ConfigIter getConfig(ConfigKey key); + //! Returns the ConfigKey of k or -1 if k is not a known configuration. + static int getConfigKey(const char* k); + + ClaspCliConfig(); + ~ClaspCliConfig(); + // Base interface + virtual void prepare(SharedContext&); + virtual void reset(); + virtual Configuration* config(const char*); + + /*! + * \name Key-based low-level interface + * + * The functions in this group do not throw exceptions but + * signal logic errors via return values < 0. + * @{ */ + + typedef uint32 KeyType; + static const KeyType KEY_INVALID; //!< Invalid key used to signal errors. + static const KeyType KEY_ROOT; //!< Root key of a configuration, i.e. "." + static const KeyType KEY_TESTER; //!< Root key for tester options, i.e. "tester." + static const KeyType KEY_SOLVER; //!< Root key for (array of) solver options, i.e. "solver." + + //! Returns true if k is a leaf, i.e. has no subkeys. + static bool isLeafKey(KeyType k); + + //! Retrieves a handle to the specified key. + /*! + * \param key A valid handle to a key. + * \param name The name of the subkey to retrieve. + * \return + * - key, if name is 0 or empty. + * - KEY_INVALID, if name is not a subkey of key. + * - A handle to the subkey. + * . + */ + KeyType getKey(KeyType key, const char* name = 0) const; + + //! Retrieves a handle to the specified element of the given array key. + /*! + * \param arr A valid handle to an array. + * \param element The index of the element to retrieve. + * \return + * - A handle to the requested element, or + * - KEY_INVALID, if arr does not reference an array or element is out of bounds. + * . + */ + KeyType getArrKey(KeyType arr, unsigned element) const; + + //! Retrieves information about the specified key. + /*! + * \param key A valid handle to a key. + * \param[out] nSubkeys The number of subkeys of this key or 0 if key is a leaf. + * \param[out] arrLen If key is an array, the length of the array (can be 0). Otherwise, -1. + * \param[out] help A description of the key. + * \param[out] nValues The number of values the key currently has (0 or 1) or -1 if it can't have values. + * \note All out parameters are optional, i.e. can be 0. + * \return The number of out values or -1 if key is invalid. + */ + int getKeyInfo(KeyType key, int* nSubkeys = 0, int* arrLen = 0, const char** help = 0, int* nValues = 0) const; + + //! Returns the name of the i'th subkey of k or 0 if no such subkey exists. + const char* getSubkey(KeyType k, uint32 i) const; + + //! Creates and returns a string representation of the value of the given key. + /*! + * \param k A valid handle to a key. + * \param[out] value The current value of the key. + * \return The length of value or < 0 if k either has no value (-1) or an error occurred while writing the value (< -1). + */ + int getValue(KeyType k, std::string& value) const; + + //! Writes a null-terminated string representation of the value of the given key into the supplied buffer. + /*! + * \param k A valid handle to a key. + * \param[out] buffer The current value of the key. + * \param bufSize The size of buffer. + * \note Although the number returned can be larger than the bufSize, the function + * never writes more than bufSize bytes into the buffer. + */ + int getValue(KeyType k, char* buffer, std::size_t bufSize) const; + + //! Sets the option identified by the given key. + /*! + * \param key A valid handle to a key. + * \param value The value to set. + * \return + * - > 0: if the value was set. + * - = 0: if value is not a valid value for the given key. + * - < 0: f key does not accept a value (-1), or some error occurred (< -1). + * . + */ + int setValue(KeyType key, const char* value); + + //@} + + /*! + * \name String-based interface + * + * The functions in this group wrap the key-based functions and + * signal logic errors by throwing exceptions. + * @{ */ + //! Returns the value of the option identified by the given key. + std::string getValue(const char* key) const; + //! Returns true if the given key has an associated value. + bool hasValue(const char* key) const; + //! Sets the option identified by the given key. + bool setValue(const char* key, const char* value); + //@} + + //! Validates this configuration. + bool validate(); + + /*! + * \name App interface + * + * Functions for connecting a configuration with the ProgramOptions library. + * @{ */ + //! Adds all available options to root. + /*! + * Once options are added, root can be used with an option source (e.g. the command-line) + * to populate this object. + */ + void addOptions(Potassco::ProgramOptions::OptionContext& root); + //! Adds options that are disabled by the options contained in parsed to parsed. + void addDisabled(Potassco::ProgramOptions::ParsedOptions& parsed); + //! Applies the options in parsed and finalizes and validates this configuration. + bool finalize(const Potassco::ProgramOptions::ParsedOptions& parsed, ProblemType type, bool applyDefaults); + //! Populates this configuration with the options given in [first, last) and finalizes it. + /*! + * \param [first, last) a range of options in argv format. + * \param t Problem type for which this configuration is created. Used to set defaults. + */ + template + bool setConfig(IT first, IT last, ProblemType t) { + RawConfig config("setConfig"); + while (first != last) { config.addArg(*first++); } + return setAppConfig(config, t); + } + //! Releases internal option objects needed for command-line style option processing. + /*! + * \note Subsequent calls to certain functions of this object (e.g. addOptions(), setConfig()) + * recreate the option objects if necessary. + */ + void releaseOptions(); + //@} +private: + static const uint8 mode_solver = 1u; + static const uint8 mode_tester = 2u; + static const uint8 mode_relaxed= 4u; + struct ParseContext; + struct OptIndex; + class ProgOption; + typedef Potassco::ProgramOptions::OptionContext OptionContext; + typedef Potassco::ProgramOptions::OptionGroup Options; + typedef SingleOwnerPtr OptionsPtr; + typedef PodVector::type ConfigVec; + typedef Potassco::ProgramOptions::ParsedOptions ParsedOpts; + struct ScopedSet { + ScopedSet(ClaspCliConfig& s, uint8 mode, uint32 sId = 0); + ~ScopedSet(); + ClaspCliConfig* operator->()const { return self; } + ClaspCliConfig* self; + }; + struct RawConfig { + std::string raw; + explicit RawConfig(const char* name); + void addArg(const char* arg); + void addArg(const std::string& arg); + ConfigIter iterator() const { return ConfigIter(raw.data()); } + }; + // Operations on active config and solver + int setActive(int o, const char* value); + int getActive(int o, std::string* value, const char** desc, const char** opt) const; + int applyActive(int o, const char* setValue, std::string* getValue, const char** getDesc, const char** name); + // App interface impl + bool setAppConfig(const RawConfig& c, ProblemType t); + int setAppOpt(int o, const char* value); + bool setAppDefaults(UserConfig* active, uint32 sId, const ParsedOpts& exclude, ProblemType t); + bool finalizeAppConfig(UserConfig* active, const ParsedOpts& exclude, ProblemType t, bool defs); + const ParsedOpts& finalizeParsed(UserConfig* active, const ParsedOpts& parsed, ParsedOpts& exclude) const; + void createOptions(); + ProgOption* createOption(int o); + bool assignDefaults(const ParsedOpts&); + // Configurations + static bool appendConfig(std::string& to, const std::string& line); + static bool loadConfig(std::string& to, const char* fileName); + ConfigIter getConfig(uint8 key, std::string& tempMem); + bool setConfig(const ConfigIter& it, bool allowAppOpt, const ParsedOpts& exclude, ParsedOpts* out); + // helpers + bool isGenerator() const { return (cliMode & mode_tester) == 0; } + const UserConfig*active()const { return isGenerator() ? this : testerConfig(); } + UserConfig* active() { return isGenerator() ? this : testerConfig(); } + bool match(const char*& path, const char* what) const; + static OptIndex index_g; + OptionsPtr opts_; + std::string config_[2]; + bool initTester_; +}; +//! Validates the given solver configuration and returns an error string if invalid. +const char* validate(const SolverParams& solver, const SolveParams& search); +//@} + +}} +#endif diff --git a/clasp/clasp/cli/clasp_output.h b/clasp/clasp/cli/clasp_output.h new file mode 100644 index 000000000..1e4986ae0 --- /dev/null +++ b/clasp/clasp/cli/clasp_output.h @@ -0,0 +1,271 @@ +// +// Copyright (c) 2009-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_CLI_OUTPUT_H_INCLUDED +#define CLASP_CLI_OUTPUT_H_INCLUDED +#include +#include +#include +#include +namespace Clasp { namespace Cli { +template +void formatEvent(const T& eventType, S& str); +template +void format(const Clasp::Event_t& ev, S& str) { + formatEvent(static_cast(ev), str); +} + +/*! + * \addtogroup cli + * @{ */ +/*! + * \brief Interface for printing status and input format dependent information, + * like models, optimization values, and summaries. + */ +class Output : public EventHandler { +public: + //! Supported levels for printing models, optimize values, and individual calls. + enum PrintLevel { + print_all = 0, //!< Print all models, optimize values, or calls. + print_best = 1, //!< Only print last model, optimize value, or call. + print_no = 2, //!< Do not print any models, optimize values, or calls. + }; + explicit Output(uint32 verb = 1); + virtual ~Output(); + //! Active verbosity level. + uint32 verbosity() const { return verbose_; } + //! Do not output any models? + bool quiet() const { return modelQ() == 2 && optQ() == 2; } + //! Print level for models. + int modelQ() const { return static_cast(quiet_[0]); } + //! Print level for optimization values. + int optQ() const { return static_cast(quiet_[1]); } + //! Print level for individual (solve) calls. + int callQ() const { return static_cast(quiet_[2]); } + + using EventHandler::setVerbosity; + void setVerbosity(uint32 verb); + void setModelQuiet(PrintLevel model); + void setOptQuiet(PrintLevel opt); + void setCallQuiet(PrintLevel call); + + //! Shall be called once on startup. + virtual void run(const char* solver, const char* version, const std::string* begInput, const std::string* endInput) = 0; + //! Shall be called once on shutdown. + virtual void shutdown(const ClaspFacade::Summary& summary); + virtual void shutdown() = 0; + //! Handles ClaspFacade events by forwarding calls to startStep() and stopStep(). + virtual void onEvent(const Event& ev); + //! Checks quiet-levels and forwards to printModel() if appropriate. + virtual bool onModel(const Solver& s, const Model& m); + //! Checks quiet-levels and forwards to printLower() if appropriate. + virtual bool onUnsat(const Solver& s, const Model& m); + //! Shall print the given model. + virtual void printModel(const OutputTable& out, const Model& m, PrintLevel x) = 0; + //! Called on unsat - may print new info. + virtual void printUnsat(const OutputTable& out, const LowerBound* lower, const Model* prevModel); + //! A solving step has started. + virtual void startStep(const ClaspFacade&); + //! A solving step has stopped. + virtual void stopStep(const ClaspFacade::Summary& summary); + //! Shall print the given summary. + virtual void printSummary(const ClaspFacade::Summary& summary, bool final) = 0; + //! Shall print the given statistics. + virtual void printStatistics(const ClaspFacade::Summary& summary, bool final) = 0; +protected: + typedef std::pair OutPair; + typedef uintp UPtr; + typedef std::pair UPair; + const Model* getModel() const { return saved_.values ? &saved_ : 0; } + void saveModel(const Model& m); + void clearModel() { saved_.reset(); } + void printWitness(const OutputTable& out, const Model& m, UPtr data); + virtual UPtr doPrint(const OutPair& out, uintp data); + UPair numCons(const OutputTable& out, const Model& m) const; + bool stats(const ClaspFacade::Summary& summary) const; +private: + Output(const Output&); + Output& operator=(const Output&); + typedef const ClaspFacade::Summary* SumPtr ; + SumPtr summary_ ; // summary of last step + ValueVec vals_ ; // saved values from most recent model + SumVec costs_ ; // saved costs from most recent model + Model saved_ ; // most recent model + uint32 verbose_ ; // verbosity level + uint8 quiet_[3]; // quiet levels for models, optimize, calls +}; + +//! Prints models and solving statistics in Json-format to stdout. +class JsonOutput : public Output, private StatsVisitor { +public: + explicit JsonOutput(uint32 verb); + ~JsonOutput(); + virtual void run(const char* solver, const char* version, const std::string* begInput, const std::string* endInput); + virtual void shutdown(const ClaspFacade::Summary& summary); + virtual void shutdown(); + virtual void printSummary(const ClaspFacade::Summary& summary, bool final); + virtual void printStatistics(const ClaspFacade::Summary& summary, bool final); +private: + virtual void startStep(const ClaspFacade&); + virtual void stopStep(const ClaspFacade::Summary& summary); + virtual bool visitThreads(Operation op); + virtual bool visitTester(Operation op); + virtual bool visitHccs(Operation op); + + virtual void visitThread(uint32, const SolverStats& stats); + virtual void visitHcc(uint32, const ProblemStats& p, const SolverStats& s); + virtual void visitLogicProgramStats(const Asp::LpStats& stats); + virtual void visitProblemStats(const ProblemStats& stats); + virtual void visitSolverStats(const SolverStats& stats); + virtual void visitExternalStats(const StatisticObject& stats); + virtual UPtr doPrint(const OutPair& out, UPtr data); + void printChildren(const StatisticObject& s); + enum ObjType { type_object, type_array }; + void pushObject(const char* k = 0, ObjType t = type_object); + char popObject(); + void printKeyValue(const char* k, const char* v) ; + void printKeyValue(const char* k, uint64 v); + void printKeyValue(const char* k, uint32 v); + void printKeyValue(const char* k, double d); + void printKeyValue(const char* k, const StatisticObject& o); + void printString(const char* s, const char* sep); + void printKey(const char* k); + void printModel(const OutputTable& out, const Model& m, PrintLevel x); + void printCosts(const SumVec& costs, const char* name = "Costs"); + void printCons(const UPair& cons); + void printCoreStats(const CoreStats&); + void printExtStats(const ExtendedStats&, bool generator); + void printJumpStats(const JumpStats&); + void startModel(); + bool hasWitness() const { return !objStack_.empty() && *objStack_.rbegin() == '['; } + uint32 indent() const { return static_cast(objStack_.size() * 2); } + const char* open_; + std::string objStack_; +}; + +//! Default clasp format printer. +/*! + * Prints all output to stdout in given format: + * - format_asp prints in clasp's default asp format + * - format_aspcomp prints in in ASP competition format + * - format_sat09 prints in SAT-competition format + * - format_pb09 in PB-competition format + * . + * \see https://www.mat.unical.it/aspcomp2013/ + * \see http://www.satcompetition.org/2009/format-solvers2009.html + * \see http://www.cril.univ-artois.fr/PB09/solver_req.html + * + */ +class TextOutput : public Output, private StatsVisitor { +public: + //! Supported text formats. + enum Format { format_asp, format_aspcomp, format_sat09, format_pb09 }; + enum ResultStr { res_unknonw = 0, res_sat = 1, res_unsat = 2, res_opt = 3, num_str }; + enum CategoryKey { cat_comment, cat_value, cat_objective, cat_result, cat_value_term, cat_atom_name, cat_atom_var, num_cat }; + + const char* result[num_str]; //!< Default result strings. + const char* format[num_cat]; //!< Format strings. + + TextOutput(uint32 verbosity, Format f, const char* catAtom = 0, char ifs = ' '); + ~TextOutput(); + + //! Prints a (comment) message containing the given solver and input. + virtual void run(const char* solver, const char* version, const std::string* begInput, const std::string* endInput); + virtual void shutdown(); + //! Prints the given model. + /*! + * Prints format[cat_value] followed by the elements of the model. Individual + * elements e are printed as format[cat_atom] and separated by the internal field separator. + */ + virtual void printModel(const OutputTable& out, const Model& m, PrintLevel x); + //! Prints the given lower bound and upper bounds that are known to be optimal. + virtual void printUnsat(const OutputTable& out, const LowerBound* lower, const Model* prevModel); + //! Called once a solving step has completed. + /*! + * Always prints "format[cat_result] result[s.result()]". + * Furthermore, if verbosity() > 0, prints a summary consisting of + * - the number of computed models m and whether the search space was exhausted + * - the number of enumerated models e if e != m + * - the state of any optimization and whether the last model was optimal + * - the state of consequence computation and whether the last model corresponded to the consequences + * - timing information + * . + */ + virtual void printSummary(const ClaspFacade::Summary& s , bool final); + virtual void printStatistics(const ClaspFacade::Summary& s, bool final); + //! Prints progress events (preprocessing/solving) if verbosity() > 1. + virtual void onEvent(const Event& ev); + //! A solving step has started. + virtual void startStep(const ClaspFacade&); + //! Prints a comment message. + void comment(uint32 v, const char* fmt, ...) const; +protected: + virtual bool visitThreads(Operation op); + virtual bool visitTester(Operation op); + + virtual void visitThread(uint32, const SolverStats& stats); + virtual void visitHcc(uint32, const ProblemStats& p, const SolverStats& s); + virtual void visitLogicProgramStats(const Asp::LpStats& stats); + virtual void visitProblemStats(const ProblemStats& stats); + virtual void visitSolverStats(const SolverStats& stats); + virtual void visitExternalStats(const StatisticObject& stats); + + virtual UPtr doPrint(const OutPair& out, UPtr data); + const char* fieldSeparator() const; + int printSep(CategoryKey c) const; + void printCosts(const SumVec&) const; + void printBounds(const SumVec& upper, const SumVec& lower) const; + void printStats(const SolverStats& stats) const; + void printJumps(const JumpStats&) const; + bool startSection(const char* n) const; + void startObject(const char* n, uint32 i) const; + void setState(uint32 state, uint32 verb, const char* st); + void printSolveProgress(const Event& ev); + void printValues(const OutputTable& out, const Model& m); + void printMeta(const OutputTable& out, const Model& m); + void printChildren(const StatisticObject& s, unsigned level = 0, const char* prefix = 0); + int printChildKey(unsigned level, const char* key, uint32 idx, const char* prefix = 0) const; +private: + void printCostsImpl(const SumVec&, char ifs, const char* ifsSuffix = "") const; + const char* getIfsSuffix(char ifs, CategoryKey cat) const; + const char* getIfsSuffix(CategoryKey cat) const; + struct SolveProgress { + int lines; + int last; + void clear() { + lines = 0; + last = -1; + } + }; + std::string fmt_; + double stTime_; // time on state enter + SolveProgress progress_;// for printing solve progress + int width_; // output width + uint32 state_; // active state + char ifs_[2]; // field separator + bool accu_; +}; +//@} + +}} +#endif diff --git a/clasp/clasp/clingo.h b/clasp/clasp/clingo.h new file mode 100644 index 000000000..19610847c --- /dev/null +++ b/clasp/clasp/clingo.h @@ -0,0 +1,267 @@ +// +// Copyright (c) 2015-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_CLINGO_H_INCLUDED +#define CLASP_CLINGO_H_INCLUDED +/*! + * \file + * \brief Types for implementing theory propagation from clingo. + */ +#include +#include +#include +namespace Clasp { + +/*! + * \defgroup clingo Clingo + * \brief Additional classes mainly used by clingo. + * \ingroup facade + * @{ */ + +//! Lock interface called by libclasp during (multi-threaded) theory propagation. +/*! + * The interface may be implemented by the application to lock + * certain global data structures. For example, in clingo, + * this interface wraps python's global interpreter lock. + */ +class ClingoPropagatorLock { +public: + virtual ~ClingoPropagatorLock(); + virtual void lock() = 0; + virtual void unlock() = 0; +}; + +//! Supported check modes for clingo propagators. +struct ClingoPropagatorCheck_t { + enum Type { + No = 0u, //!< Never call AbstractPropagator::check(). + Total = 1u, //!< Call AbstractPropagator::check() only on total assignment. + Fixpoint = 2u, //!< Call AbstractPropagator::check() on every propagation fixpoint. + Both = 3u //!< Call AbstractPropagator::check() on both fixpoints and total assignment. + }; +}; + +//! Initialization adaptor for a Potassco::AbstractPropagator. +/*! + * The class provides a function for registering watches for the propagator. + * Furthermore, it can be added to a clasp configuration so that + * a (suitably adapted) propagator is added to solvers that are attached to the configuration. + */ +class ClingoPropagatorInit : public ClaspConfig::Configurator { +public: + typedef ClingoPropagatorCheck_t::Type CheckType; + //! Creates a new adaptor. + /*! + * \param cb The (theory) propagator that should be added to solvers. + * \param lock An optional lock that should be applied during theory propagation. + * + * If lock is not null, calls to cb are wrapped in a lock->lock()/lock->unlock() pair + */ + ClingoPropagatorInit(Potassco::AbstractPropagator& cb, ClingoPropagatorLock* lock = 0, CheckType check = ClingoPropagatorCheck_t::Total); + ~ClingoPropagatorInit(); + // base class + virtual void prepare(SharedContext&); + //! Adds a ClingoPropagator adapting the propagator() to s. + virtual bool applyConfig(Solver& s); + virtual void unfreeze(SharedContext&); + + // for clingo + //! Sets the type of checks to enable during solving. + /*! + * \param checkMode A set of ClingoPropagatorCheck_t::Type values. + */ + void enableClingoPropagatorCheck(CheckType checkMode); + + void enableHistory(bool b); + + //! Adds a watch for lit to all solvers and returns encodeLit(lit). + Potassco::Lit_t addWatch(Literal lit); + //! Removes the watch for lit from all solvers. + void removeWatch(Literal lit); + //! Adds a watch for lit to the solver with the given id and returns encodeLit(lit). + Potassco::Lit_t addWatch(uint32 solverId, Literal lit); + //! Removes the watch for lit from solver with the given id. + void removeWatch(uint32 solverId, Literal lit); + //! Freezes the given literal making it exempt from Sat-preprocessing. + /*! + * \note Watched literals are automatically frozen. + */ + void freezeLit(Literal lit); + + //! Returns the propagator that was given on construction. + Potassco::AbstractPropagator* propagator() const { return prop_; } + ClingoPropagatorLock* lock() const { return lock_; } + CheckType checkMode() const { return check_; } + + uint32 init(uint32 lastStep, Potassco::AbstractSolver& s); +private: + typedef Potassco::Lit_t Lit_t; + enum Action { RemoveWatch = 0, AddWatch = 1, FreezeLit = 2 }; + struct History; + struct Change { + Change(Lit_t p, Action a); + Change(Lit_t p, Action a, uint32 sId); + bool operator<(const Change& rhs) const; + void apply(Potassco::AbstractSolver& s) const; + uint64 solverMask() const; + Lit_t lit; + int16 sId; + int16 action; + }; + typedef PodVector::type ChangeList; + ClingoPropagatorInit(const ClingoPropagatorInit&); + ClingoPropagatorInit& operator=(const ClingoPropagatorInit&); + Potassco::AbstractPropagator* prop_; + ClingoPropagatorLock* lock_; + History* history_; + ChangeList changes_; + uint32 step_; + CheckType check_; +}; + +//! Adaptor for a Potassco::AbstractPropagator. +/*! + * The class adapts a given Potassco::AbstractPropagator so that + * it is usable as a PostPropagator within libclasp. + * \note This class is meant to be a final class. + */ +class ClingoPropagator : public Clasp::PostPropagator { +public: + typedef Potassco::AbstractPropagator::ChangeList ChangeList; + typedef Clasp::PostPropagator::PropResult PPair; + + explicit ClingoPropagator(ClingoPropagatorInit* init); + + // PostPropagator + virtual uint32 priority() const; + virtual bool init(Solver& s); + virtual bool propagateFixpoint(Clasp::Solver& s, Clasp::PostPropagator* ctx); + virtual PPair propagate(Solver&, Literal, uint32&); + virtual bool isModel(Solver& s); + virtual void reason(Solver&, Literal, LitVec&); + virtual void undoLevel(Solver& s); + virtual bool simplify(Solver& s, bool reinit); + virtual void destroy(Solver* s, bool detach); +private: + typedef LitVec::size_type size_t; + typedef Potassco::Lit_t Lit_t; + class Control; + enum State { state_ctrl = 1u, state_prop = 2u, state_init = 4u }; + struct ClauseTodo { + bool empty() const { return mem.empty(); } + void clear() { mem.clear(); } + LitVec mem; + ClauseRep clause; + uint32 flags; + }; + typedef PodVector::type AspifVec; + typedef PodVector::type ClauseDB; + typedef ClingoPropagatorInit Propagator; + typedef ClingoPropagatorLock* ClingoLock; + bool addClause(Solver& s, uint32 state); + void toClause(Solver& s, const Potassco::LitSpan& clause, Potassco::Clause_t prop); + void registerUndoCheck(Solver& s); + void registerUndo(Solver& s, uint32 data); + bool inTrail(Literal p) const; + Propagator* call_; // wrapped theory propagator + AspifVec trail_; // assignment trail: watched literals that are true + AspifVec temp_; // temporary buffer used to pass changes to user + VarVec undo_; // offsets into trail marking beginnings of decision levels + ClauseDB db_; // clauses added with flag static + ClauseTodo todo_; // active clause to be added (received from theory propagator) + size_t prop_; // offset into trail: literals [0, prop_) were propagated + size_t epoch_; // number of calls into callback + uint32 level_; // highest undo level + uint32 propL_; // decision level on handling propagate() from theory propagator + int32 front_; // global assignment position for fixpoint checks + uint32 init_; // last time init() was called + Literal aux_; // max active literal +}; + +class ClingoAssignment : public Potassco::AbstractAssignment { +public: + typedef Potassco::AbstractAssignment BaseType; + typedef BaseType::Value_t Value_t; + typedef BaseType::Lit_t Lit_t; + + ClingoAssignment(const Solver& s); + + virtual uint32_t size() const; + virtual uint32_t unassigned() const; + virtual bool hasConflict() const; + virtual uint32_t level() const; + virtual uint32_t rootLevel() const; + virtual bool hasLit(Lit_t lit) const; + virtual Value_t value(Lit_t lit) const; + virtual uint32_t level(Lit_t lit) const; + virtual Lit_t decision(uint32_t) const; + virtual bool isTotal() const; + virtual uint32_t trailSize() const; + virtual Lit_t trailAt(uint32_t) const; + virtual uint32_t trailBegin(uint32_t) const; + + const Solver& solver() const { return *solver_; } +private: + const Solver* solver_; +}; + +class ClingoHeuristic : public DecisionHeuristic { +public: + class Factory : public BasicSatConfig::HeuristicCreator { + public: + /*! + * \param clingoHeuristic The heuristic that should be added to solvers. + * \param lock An optional lock that should be applied during calls to AbstractHeuristic::decide(). + */ + explicit Factory(Potassco::AbstractHeuristic& clingoHeuristic, ClingoPropagatorLock* lock = 0); + DecisionHeuristic* create(Heuristic_t::Type t, const HeuParams& p); + private: + Potassco::AbstractHeuristic* clingo_; + ClingoPropagatorLock* lock_; + }; + + explicit ClingoHeuristic(Potassco::AbstractHeuristic& clingoHeuristic, DecisionHeuristic* claspHeuristic, ClingoPropagatorLock* lock); + virtual void startInit(const Solver& s); + virtual void endInit(Solver& s); + virtual void detach(Solver& s); + virtual void setConfig(const HeuParams& p); + virtual void updateVar(const Solver& s, Var v, uint32 n); + virtual void simplify(const Solver& s, LitVec::size_type st); + virtual void undoUntil(const Solver& s, LitVec::size_type st); + virtual void newConstraint(const Solver& s, const Literal* first, LitVec::size_type size, ConstraintType t); + virtual void updateReason(const Solver& s, const LitVec& lits, Literal resolveLit); + virtual bool bump(const Solver& s, const WeightLitVec& lits, double adj); + virtual Literal doSelect(Solver& s); + virtual Literal selectRange(Solver& s, const Literal* first, const Literal* last); + + DecisionHeuristic* fallback() const; +private: + typedef SingleOwnerPtr HeuPtr; + Potassco::AbstractHeuristic* clingo_; + HeuPtr clasp_; + ClingoPropagatorLock* lock_; +}; + +///@} +} +#endif diff --git a/clasp/clasp/config.h.in b/clasp/clasp/config.h.in new file mode 100644 index 000000000..818036304 --- /dev/null +++ b/clasp/clasp/config.h.in @@ -0,0 +1,86 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +//! \file +//! \brief Active configuration. +#ifndef CLASP_CONFIG_H_INCLUDED +#define CLASP_CONFIG_H_INCLUDED +#ifdef _MSC_VER +#pragma once +#endif + +//! Library version. +#define CLASP_VERSION "@CLASP_VERSION@" +#define CLASP_VERSION_MAJOR @CLASP_VERSION_MAJOR@ +#define CLASP_VERSION_MINOR @CLASP_VERSION_MINOR@ +#define CLASP_VERSION_PATCH @CLASP_VERSION_PATCH@ +#define CLASP_LEGAL "Copyright (C) Benjamin Kaufmann" + +//! Single or multi-threaded version of clasp. +#cmakedefine01 CLASP_HAS_THREADS + +//! Use std::vector instead of pod_vector. +#define CLASP_USE_STD_VECTOR @CLASP_USE_STD_VECTOR@ + +#include +#if CLASP_HAS_THREADS +#include +#endif + +namespace Clasp { +@CLASP_DEFINE_ATOMIC@ +template struct Atomic_t { typedef @CLASP_ATOMIC_TYPE@ type; }; +template +T compare_and_swap(@CLASP_ATOMIC_TYPE@& in, T oldVal, T newVal) { + in.compare_exchange_strong(oldVal, newVal); + return oldVal; +} +typedef uint64_t uint64; +typedef uint32_t uint32; +typedef uint16_t uint16; +typedef uint8_t uint8; +typedef uintptr_t uintp; +typedef int64_t int64; +typedef int32_t int32; +typedef int16_t int16; +typedef int8_t int8; +inline void* alignedAlloc(size_t size, size_t align) { +#if defined(__CYGWIN__) + return memalign(align, size); +#elif defined(_WIN32) || defined(_WIN64) + return _aligned_malloc(size, align); +#else + void* result = 0; + return posix_memalign(&result, align, size) == 0 ? result : static_cast(0); +#endif +} +inline void alignedFree(void* p) { +#if defined(_WIN32) || defined(_WIN64) + _aligned_free(p); +#else + free(p); +#endif +} +} // namespace Clasp + +#endif diff --git a/clasp/clasp/constraint.h b/clasp/clasp/constraint.h new file mode 100644 index 000000000..527cdec66 --- /dev/null +++ b/clasp/clasp/constraint.h @@ -0,0 +1,588 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_CONSTRAINT_H_INCLUDED +#define CLASP_CONSTRAINT_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include // bits stuff +#include + +/*! + * \file + * \brief Defines the base classes for boolean constraints. + */ +namespace Clasp { + +class SharedContext; +class Solver; +class ClauseHead; +struct CCMinRecursive; + +/** + * \defgroup constraint Constraints + * \brief Boolean Constraints, post propagators, and related types. + * @{ */ + +//! Constraint types distinguished by a Solver. +struct Constraint_t { + enum Type { + Static = 0, //!< An unremovable constraint (e.g. a problem constraint). + Conflict = 1, //!< A removable constraint derived from conflict analysis. + Loop = 2, //!< A removable constraint derived from unfounded set checking. + Other = 3, //!< A removable constraint learnt by some other means. + Type__max = Other + }; + struct Set { + Set() : m(0) {} + bool inSet(Type t) const { return (m & (uint32(1)<[priority_class_simple, priority_class_general) + * - class_general: post propagators that are non-deterministic or those that are not limited to extending + * the current decision level shall have a priority of priority_class_general. They are called in FIFO order + * after \b all simple post propagators have reached a fixpoint. + * + * \note There are currently three reserved priority values, namely + * - priority_reserved_msg for message and termination handler (if any), + * - priority_reserved_ufs for the default unfounded set checker (if any), + * - and priority_reserved_look for the default lookahead operator (if any). + * . + */ +class PostPropagator : public Constraint { +public: + PostPropagator(); + virtual ~PostPropagator(); + using Constraint::propagate; // Enable overloading! + + PostPropagator* next; // main propagation lists of post propagators + //! Default priorities for post propagators. + enum Priority { + priority_class_simple = 0, //!< Starting priority of simple post propagators. + priority_reserved_msg = 0, //!< Reserved priority for message/termination handlers (if any). + priority_reserved_ufs = 10, //!< Reserved priority for the default unfounded set checker (if any). + priority_reserved_look = 1023, //!< Reserved priority for the default lookahead operator (if any). + priority_class_general = 1024, //!< Priortiy of extended post propagators. + }; + + //! Shall return a value representing the priority of this propagator. + /*! + * The priority is used to order sequences of post propagators and to + * classify post propagators w.r.t the classes: class_simple and class_general. + * \note See class description for an overview of the two priority classes. + */ + virtual uint32 priority() const = 0; + + //! Called during initialization of s. + /*! + * \note During initialization a post propagator may assign variables + * but it must not yet propagate them. + */ + virtual bool init(Solver& s); + + //! Shall enqueue and propagate new assignments implied by this propagator. + /*! + * This function shall enqueue and propagate all assignments currently implied by + * this propagator until a fixpoint is reached w.r.t this post propagator or + * a conflict is detected. + * + * \pre The assignment is fully propagated w.r.t any previous post propagator. + * \param s The solver in which this post propagator is used. + * \param ctx The post propagator from which this post propagator is called or + * 0 if no other post propagator is currently active. + * \post s.queueSize() == 0 || s.hasConflict() + * \return false if propagation led to conflict, true otherwise + * + * \note + * The function shall not call Solver::propagate() + * or any other function that would result in a recursive chain + * of propagate() calls. On the other hand, it shall call + * Solver::propagateUntil(this) after enqueuing any new assignments + * to initiate propagation up to this propagator. + * + * Typically, propagateFixpoint() should implemet a loop like this: + * \code + * for (;;) { + * if (!assign_newly_implied_literals(s)){ return false; } + * if (s.queueSize() == 0) { return true; } + * if (!s.propagateUntil(this)) { return false; } + * } + * \endcode + */ + virtual bool propagateFixpoint(Solver& s, PostPropagator* ctx) = 0; + + //! Aborts an active propagation operation. + /*! + * The function reset() is called whenever propagation on the + * current decision level is stopped before a fixpoint is reached. + * In particular, a solver calls reset() when a conflict is detected + * during propagation. + * + * \note The default implementation is a noop. + */ + virtual void reset(); + + //! Is the current total assignment a model? + /*! + * \pre The assignment is total and not conflicting. + * \return + * - true if the assignment is a model w.r.t this post propagator + * - false otherwise + * \post If the function returned true: s.numFreeVars() == 0 && !s.hasConflict(). + * If the function returned false: s.numFreeVars() > 0 || s.hasConflict(). + * \note The default implementation returns Constraint::valid(s); + */ + virtual bool isModel(Solver& s); +protected: + //! Calls reset on post propagators following this. + void cancelPropagation(); + + //! PostPropagators are not clonable by default. + Constraint* cloneAttach(Solver&) { return 0; } + // Constraint interface - noops + PropResult propagate(Solver&, Literal, uint32&); + void reason(Solver&, Literal, LitVec& ); +private: + PostPropagator(const PostPropagator&); + PostPropagator& operator=(const PostPropagator&); +}; + +//! A special post propagator used to handle messages and signals. +class MessageHandler : public PostPropagator { +public: + MessageHandler(); + virtual bool handleMessages() = 0; + virtual uint32 priority()const { return PostPropagator::priority_reserved_msg; } + virtual bool propagateFixpoint(Solver&, PostPropagator*) { return handleMessages(); } +}; + +//! An intrusive list of post propagators ordered by priority. +/*! + * Propagators in the list are owned by the list. + */ +class PropagatorList { +public: + PropagatorList(); + ~PropagatorList(); + void add(PostPropagator* p); + void remove(PostPropagator* p); + void clear(); + PostPropagator* find(uint32 prio) const; + PostPropagator*const* head() const { return &head_; } + PostPropagator** head() { return &head_; } +private: + PropagatorList(const PropagatorList&); + PropagatorList& operator=(const PropagatorList&); + PostPropagator* head_;// head of pp-list +}; +//@} + +/** +* \addtogroup constraint +* @{ */ + +//! Stores a reference to the constraint that implied a literal. +/*! + * Stores a reference to the constraint that implied a certain literal or + * null if the literal has no antecedent (i.e. is a decision literal or a top-level fact). + * + * \note + * The constraint that implied a literal can have three different representations: + * - it can be a single literal (binary clause constraint) + * - it can be two literals (ternary clause constraint) + * - it can be a pointer to a constraint (generic constraint) + * . + * + * \par Implementation: + * + * The class stores all three representations in one tagged 64-bit integer, i.e. + * from the 64-bits the 2 LSBs encode the type stored: + * - 00: Pointer to constraint + * - 01: ternary constraint (i.e. two literals stored in the remaining 62 bits). + * - 10: binary constraint (i.e. one literal stored in the highest 31 bits) + * . + */ +class Antecedent { +public: + enum Type { Generic = 0, Ternary = 1, Binary = 2}; + //! Creates a null Antecedent. + /*! + * \post: isNull() == true && type == Generic + */ + Antecedent() : data_(0) {} + + //! Creates an Antecedent from the literal p. + /*! + * \post: type() == Binary && firstLiteral() == p + */ + Antecedent(const Literal& p) { + // first lit is stored in high dword + data_ = (((uint64)p.id()) << 33) + Binary; + assert(type() == Binary && firstLiteral() == p); + } + + //! Creates an Antecedent from the literals p and q. + /*! + * \post type() == Ternary && firstLiteral() == p && secondLiteral() == q + */ + Antecedent(const Literal& p, const Literal& q) { + // first lit is stored in high dword + // second lit is stored in low dword + data_ = (((uint64)p.id()) << 33) + (((uint64)q.id()) << 2) + Ternary; + assert(type() == Ternary && firstLiteral() == p && secondLiteral() == q); + } + + //! Creates an Antecedent from the Constraint con. + /*! + * \post type() == Generic && constraint() == con + */ + Antecedent(Constraint* con) : data_((uintp)con) { + static_assert(sizeof(Constraint*) <= sizeof(uint64), "unsupported pointer size"); + assert(type() == Generic && constraint() == con); + } + + //! Returns true if this antecedent does not refer to any constraint. + bool isNull() const { return data_ == 0; } + //! Returns the antecedent's type. + Type type() const { return Type( data_ & 3 ); } + //! Returns true if the antecedent is a learnt nogood. + bool learnt() const { return data_ && (data_ & 3u) == 0 && constraint()->type() != Constraint_t::Static; } + + //! Extracts the constraint-pointer stored in this object. + /*! + * \pre type() == Generic + */ + Constraint* constraint() const { + assert(type() == Generic); + return (Constraint*)(uintp)data_; + } + + //! Extracts the first literal stored in this object. + /*! + * \pre type() != Generic + */ + Literal firstLiteral() const { + assert(type() != Generic); + return Literal::fromId(static_cast(data_ >> 33)); + } + + //! Extracts the second literal stored in this object. + /*! + * \pre type() == Ternary + */ + Literal secondLiteral() const { + assert(type() == Ternary); + return Literal::fromId( static_cast(data_>>1) >> 1 ); + } + + //! Returns the reason for p. + /*! + * \pre !isNull() + */ + void reason(Solver& s, Literal p, LitVec& lits) const { + assert(!isNull()); + Type t = type(); + if (t == Generic) { + constraint()->reason(s, p, lits); + return; + } + lits.push_back(firstLiteral()); + if (t == Ternary) { lits.push_back(secondLiteral()); } + } + template + bool minimize(S& s, Literal p, CCMinRecursive* rec) const { + assert(!isNull()); + Type t = type(); + if (t == Generic) { + return constraint()->minimize(s, p, rec); + } + return s.ccMinimize(firstLiteral(), rec) && (t != Ternary || s.ccMinimize(secondLiteral(), rec)); + } + + //! Returns true iff the antecedent refers to the constraint con. + bool operator==(const Constraint* con) const { + return static_cast(data_) == reinterpret_cast(con); + } + uint64 asUint() const { return data_; } + uint64& asUint() { return data_; } +private: + uint64 data_; +}; + +enum { LBD_MAX = 127u, ACT_MAX = (1u << 20) - 1 }; +//! Type storing a constraint's activity. +struct ConstraintScore { + typedef ConstraintScore Score; + enum { LBD_SHIFT = 20, BMP_BIT = 27, BITS_USED = 28, LBD_MASK = LBD_MAX<> LBD_SHIFT) : uint32(LBD_MAX); } + bool hasLbd() const { return (rep & LBD_MASK) != 0; } + bool bumped() const { return test_bit(rep, BMP_BIT); } + void bumpActivity() { if (activity() < uint32(ACT_MAX)) ++rep; } + void bumpLbd(uint32 x) { if (x < lbd()) { rep &= ~uint32(LBD_MASK); rep |= (x << LBD_SHIFT) | bit_mask(BMP_BIT); } } + void clearBumped() { store_clear_bit(rep, BMP_BIT); } + void reduce() { clearBumped(); if (uint32 a = activity()) { rep &= ~uint32(ACT_MAX); rep |= (a>>1); } } + void assign(Score o) { rep &= ~uint32(BITS_MASK); rep |= (o.rep & BITS_MASK); } + uint32 rep; +}; +inline ConstraintScore makeScore(uint32 act = 0, uint32 lbd = 0) { + ConstraintScore sc = {0}; sc.reset(act, lbd); + return sc; +} +//! Type storing meta information about a constraint. +class ConstraintInfo : private ConstraintScore { +public: + typedef ConstraintInfo Info; + typedef ConstraintScore Score; + ConstraintInfo(ConstraintType t = Constraint_t::Static) { + static_assert(ConstraintScore::BITS_USED <= 28, "invalid score"); + rep = uint32(t) << TYPE_SHIFT; + } + using ConstraintScore::lbd; + using ConstraintScore::activity; + ConstraintType type() const { return static_cast( (rep & uint32(TYPE_MASK)) >> TYPE_SHIFT ); } + bool tagged() const { return test_bit(rep, TAG_BIT); } + bool aux() const { return tagged() || test_bit(rep, AUX_BIT); } + bool learnt() const { return type() != Constraint_t::Static; } + const Score& score() const { return *this; } + Score& score() { return *this; } + + Info& setType(ConstraintType t) { rep &= ~uint32(TYPE_MASK); rep |= (uint32(t) << TYPE_SHIFT); return *this; } + Info& setScore(Score sc) { assign(sc); return *this; } + Info& setActivity(uint32 a) { assign(makeScore(a, lbd())); return *this; } + Info& setLbd(uint32 lbd) { assign(makeScore(activity(), lbd)); return *this; } + Info& setTagged(bool b) { if (test_bit(rep, TAG_BIT) != b) store_toggle_bit(rep, TAG_BIT); return *this; } + Info& setAux(bool b) { if (test_bit(rep, AUX_BIT) != b) store_toggle_bit(rep, AUX_BIT); return *this; } +private: + enum { TYPE_SHIFT = 28, AUX_BIT = 30, TAG_BIT = 31, TYPE_MASK = (3u << TYPE_SHIFT) }; +}; +//@} + +/** +* \defgroup shared_con Shared +* \brief %Constraint data that can safely be shared between solvers. +* \ingroup constraint +*/ + +} +#endif diff --git a/clasp/clasp/dependency_graph.h b/clasp/clasp/dependency_graph.h new file mode 100644 index 000000000..e080302ca --- /dev/null +++ b/clasp/clasp/dependency_graph.h @@ -0,0 +1,476 @@ +// +// Copyright (c) 2010-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +#ifndef CLASP_DEPENDENCY_GRAPH_H_INCLUDED +#define CLASP_DEPENDENCY_GRAPH_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +namespace Clasp { +class Solver; +class SharedContext; +struct SolverStats; +//! Event type used to signal a (partial) check in disjunctive solving. +struct SolveTestEvent : SolveEvent { + SolveTestEvent(const Solver& s, uint32 hcc, bool partial); + int result; //!< -1: before test, 0: unstable, 1: stable + uint32 hcc :31;//!< hcc under test + uint32 partial : 1;//!< partial test? + uint64 confDelta; //!< conflicts before test + uint64 choiceDelta;//!< choices before test + double time; //!< time for test + + uint64 conflicts() const; + uint64 choices() const; +}; +struct LoopReason_t { + enum Type { Explicit = 0u, Implicit = 1u, }; +}; +typedef LoopReason_t::Type LoopType; + +namespace Asp { +//! (Positive) Body-Atom-Dependency Graph. +/*! + * \ingroup shared_con + * + * Represents the PBADG of a logic program. Once initialized, the + * PBDAG is static and read-only and thus can be shared between multiple solvers. + * + * \note Initialization is *not* thread-safe, i.e. must be done only once by one thread. + */ +class PrgDepGraph { +public: + enum NonHcfMapType { + map_old = 0, + map_new = 1 + }; + explicit PrgDepGraph(NonHcfMapType m = map_old); + ~PrgDepGraph(); + typedef uint32 NodeId; + //! Type for storing a non head-cycle-free component of a disjunctive program. + class NonHcfComponent { + public: + explicit NonHcfComponent(uint32 id, const PrgDepGraph& dep, SharedContext& generator, Configuration* c, uint32 scc, const VarVec& atoms, const VarVec& bodies); + ~NonHcfComponent(); + void assumptionsFromAssignment(const Solver& generator, LitVec& assumptionsOut) const; + bool test(const Solver& generator, const LitVec& assumptions, VarVec& unfoundedOut) const; + bool simplify(const Solver& generator) const; + const SharedContext& ctx() const { return *prg_; } + void update(const SharedContext& generator); + uint32 id() const { return id_; } + uint32 scc() const { return scc_; } + private: + friend class PrgDepGraph; + NonHcfComponent(const NonHcfComponent&); + NonHcfComponent& operator=(const NonHcfComponent&); + class ComponentMap; + const PrgDepGraph* dep_; + SharedContext* prg_; + ComponentMap* comp_; + uint32 id_; + uint32 scc_; + }; + //! A class for storing statistics on checking of non head-cycle-free components. + class NonHcfStats { + public: + NonHcfStats(PrgDepGraph& g, uint32 level, bool inc); + ~NonHcfStats(); + void accept(StatsVisitor& out, bool final) const; + void startStep(uint32 statsLevel); + void endStep(); + void addTo(StatsMap& problem, StatsMap& solving, StatsMap* accu) const; + private: + friend class PrgDepGraph; + void addHcc(const NonHcfComponent&); + void removeHcc(const NonHcfComponent&); + NonHcfStats(const NonHcfStats&); + NonHcfStats& operator=(const NonHcfStats&); + struct Data; + PrgDepGraph* graph_; + Data* data_; + }; + typedef PodVector::type ComponentVec; + typedef ComponentVec::const_iterator NonHcfIter; + //! Base type for nodes. + struct Node { + Node(Literal l = Literal(0, false), uint32 sc = PrgNode::noScc) + : lit(l), scc(sc), data(0), adj_(0), sep_(0) {} + Literal lit; // literal of this node + uint32 scc : 28;// scc of this node + uint32 data : 4;// additional atom/body data + NodeId* adj_; // list of adjacent nodes + NodeId* sep_; // separates successor/predecessor nodes + }; + //! An atom node. + /*! + * The PBDAG stores a node of type AtomNode for each non-trivially connected atom. + * The predecessors of an AtomNode are the bodies that define the atom. Its successors + * are those bodies from the same SCC that contain the atom positively. + */ + struct AtomNode : public Node { + enum Property { property_in_choice = 1u, property_in_disj = 2u, property_in_ext = 4u, property_in_non_hcf = 8u }; + AtomNode() {} + void set(Property p) { data |= (uint32)p; } + void setProperties(uint32 f) { assert(f < 8); data |= f; } + //! Contained in the head of a choice rule? + bool inChoice() const { return (data & property_in_choice) != 0; } + //! Contained in the head of a non-hcf disjunctive rule? + bool inDisjunctive()const { return (data & property_in_disj) != 0; } + //! Contained in an extended body? + bool inExtended() const { return (data& property_in_ext) != 0; } + //! Contained in a non-hcf SCC? + bool inNonHcf() const { return (data & property_in_non_hcf) != 0; } + //! Bodies (i.e. predecessors): bodies from other SCCs precede those from same SCC. + const NodeId* bodies_begin() const { return adj_; } + const NodeId* bodies_end() const { return sep_; } + NodeId body(uint32 i) const { return bodies_begin()[i]; } + //! Successors from same SCC [B1,...Bn, idMax]. + /*! + * \note If extended() is true, the atom is adjacent to some extended body. + * In that case, the returned list looks like this: + * [Bn1, ..., Bnj, idMax, Bext1, pos1, ..., Bextn, posn, idMax], where + * each Bni is a normal body, each Bexti is an extended body and posi is the + * position of this atom in Bexti. + */ + const NodeId* succs() const { return sep_; } + //! Calls the given function object p once for each body containing this atom. + template + void visitSuccessors(const P& p) const { + const NodeId* s = succs(); + for (; *s != idMax; ++s) { p(*s); } + if (inExtended()) { + for (++s; *s != idMax; s += 2) { p(*s, *(s+1)); } + } + } + }; + enum { sentinel_atom = 0u }; + + //! A body node. + /*! + * The PBDAG stores a node of type BodyNode for each body that defines + * a non-trivially connected atom. + * The predecessors of a BodyNode are the body's subgoals. + * Its successors are the heads that are defined by the body. + * \note Normal bodies only store the positive subgoals from the same SCC, while + * extended rule bodies store all subgoals. In the latter case, the positive subgoals from + * the same SCC are stored as AtomNodes. All other subgoals are stored as literals. + */ + struct BodyNode : public Node { + enum Flag { flag_has_bound = 1u, flag_has_weights = 2u, flag_has_delta = 4u, flag_seen = 8u }; + explicit BodyNode(PrgBody* b, uint32 scc) : Node(b->literal(), scc) { + if (scc == PrgNode::noScc || b->type() == Body_t::Normal) { + data = 0; + } + else if (b->type() == Body_t::Count){ data = flag_has_bound; } + else if (b->type() == Body_t::Sum) { data = flag_has_bound | flag_has_weights; } + else { assert("UNKNOWN BODY TYPE!\n"); } + } + bool seen() const { return (data & flag_seen) != 0; } + void seen(bool b) { if (b) data |= flag_seen; else data &= ~uint32(flag_seen); } + + //! Heads (i.e. successors): atoms from same SCC precede those from other SCCs. + /*! + * \note Disjunctive heads are stored in flattened atom-lists, where the + * lists are terminated on both ends with the special sentinal atom 0. + * E.g. given + * x :- B. + * y :- B. + * a|b:- B. + * a|c:- B. + * would result in: [x,y,0,a,b,0,0,a,c,0] + */ + const NodeId* heads_begin() const { return adj_; } + const NodeId* heads_end() const { return sep_ - extended(); } + //! Any disjunctive heads? + bool delta() const { return (data & flag_has_delta) != 0; } + //! Predecessors from same SCC [a1,...an, idMax]. + /*! + * \note If extended() is true, the body stores all its subgoals and preds looks + * like this: [a1, [w1], ..., aj, [wj], idMax, l1, [w1], ..., lk, [wk], idMax], where + * each ai is an atom from the same SCC, each li is a literal of a subgoal from + * other SCCs and wi is an optional weight (only for weight rules). + */ + const NodeId* preds() const { assert(scc != PrgNode::noScc); return sep_; } + //! Returns idx of atomId in preds. + uint32 get_pred_idx(NodeId atomId) const { + const uint32 inc = pred_inc(); + uint32 idx = 0; + for (const NodeId* x = preds(); *x != idMax; x += inc, ++idx) { + if (*x == atomId) return idx; + } + return idMax; + } + NodeId get_pred(uint32 idx) const { return *(preds() + (idx*pred_inc())); } + //! Increment to jump from one pred to the next. + uint32 pred_inc() const { return 1 + sum(); } + //! Weight of ith subgoal. + /*! + * \pre i in [0, num_preds()) + */ + uint32 pred_weight(uint32 i, bool ext) const { + return !sum() + ? 1 + : *(preds() + (i*pred_inc()) + (1+uint32(ext))); + } + //! Number of predecessors (counting external subgoals). + uint32 num_preds() const { + if (scc == PrgNode::noScc) return 0; + uint32 p = 0; + const NodeId* x = preds(); + const uint32 inc = pred_inc(); + for (; *x != idMax; x += inc) { ++p; } + x += extended(); + for (; *x != idMax; x += inc) { ++p; } + return p; + } + //! Is the body an extended body? + bool extended()const { return (data & flag_has_bound) != 0u; } + //! Is the body a sum body? + bool sum() const { return (data & flag_has_weights) != 0u; } + //! Bound of extended body. + weight_t ext_bound() const { return sep_[-1]; } + }; + //! Adds SCCs to the graph. + /*! + * \param prg The logic program for which the dependency graph is to be created. + * \param sccAtoms Atoms of the logic program that are strongly connected. + * \param nonHcfs Sorted list of non-hcf sccs + */ + void addSccs(LogicProgram& prg, const AtomList& sccAtoms, const NonHcfSet& nonHcfs); + + //! Removes inactive non-hcfs. + void simplify(const Solver& s); + //! Number of atoms in graph. + uint32 numAtoms() const { return (uint32)atoms_.size(); } + //! Number of bodies in graph. + uint32 numBodies()const { return (uint32)bodies_.size(); } + //! Sum of atoms and bodies. + uint32 nodes() const { return numAtoms()+numBodies(); } + + //! Returns AtomNode of atom with given id. + const AtomNode& getAtom(NodeId atomId) const { + assert(atomId < atoms_.size()); + return atoms_[atomId]; + } + NodeId id(const AtomNode& n) const { return static_cast(&n - &atoms_[0]); } + //! Returns BodyNode of body with given id. + const BodyNode& getBody(NodeId bodyId) const { + assert(bodyId < bodies_.size()); + return bodies_[bodyId]; + } + //! Calls the given function object p once for each body-literal. + template + void visitBodyLiterals(const BodyNode& n, const P& p) const { + const NodeId* x = n.preds(); + const uint32 inc = n.pred_inc(); + uint32 i = 0; + for (; *x != idMax; x += inc, ++i) { p(getAtom(*x).lit, i, false); } + x += n.extended(); + for (; *x != idMax; x += inc, ++i) { p(Literal::fromRep(*x), i, true); } + } + NonHcfIter nonHcfBegin() const { return components_.begin(); } + NonHcfIter nonHcfEnd() const { return components_.end(); } + uint32 numNonHcfs() const { return (uint32)components_.size(); } + NonHcfStats* nonHcfStats() const { return stats_; } + NonHcfStats* enableNonHcfStats(uint32 level, bool incremental); +private: + typedef PodVector::type AtomVec; + typedef PodVector::type BodyVec; + PrgDepGraph(const PrgDepGraph&); + PrgDepGraph& operator=(const PrgDepGraph&); + inline bool relevantPrgAtom(const Solver& s, PrgAtom* a) const; + inline bool relevantPrgBody(const Solver& s, PrgBody* b) const; + NonHcfMapType nonHcfMapType() const { return static_cast(mapType_); } + NodeId createBody(PrgBody* b, uint32 bScc); + NodeId createAtom(Literal lit, uint32 aScc); + NodeId addBody(const LogicProgram& prg, PrgBody*); + NodeId addDisj(const LogicProgram& prg, PrgDisj*); + uint32 addHeads(const LogicProgram& prg, PrgBody*, VarVec& atoms) const; + uint32 getAtoms(const LogicProgram& prg, PrgDisj*, VarVec& atoms) const; + void addPreds(const LogicProgram& prg, PrgBody*, uint32 bScc, VarVec& preds) const; + void initBody(uint32 id, const VarVec& preds, const VarVec& atHeads); + void initAtom(uint32 id, uint32 prop, const VarVec& adj, uint32 preds); + void addNonHcf(uint32 id, SharedContext& ctx, Configuration* c, uint32 scc); + AtomVec atoms_; + BodyVec bodies_; + ComponentVec components_; + NonHcfStats* stats_; + uint32 seenComponents_ : 31; + uint32 mapType_ : 1; +}; +} // namespace Asp + +//! External dependency graph. +/*! + * \ingroup shared_con + * + * Represents external dependencies explicitly given by the user. + * For example, via aspif edge directives or the graph block in extended dimacs format. + * \note Initialization is *not* thread-safe, i.e. must be done only once by one thread. + */ +class ExtDepGraph { +public: + struct Arc { + Literal lit; + uint32 node[2]; + uint32 tail() const { return node[0]; } + uint32 head() const { return node[1]; } + static Arc create(Literal x, uint32 nodeX, uint32 nodeY) { Arc a = {x, {nodeX, nodeY}}; return a; } + }; + struct Inv { + uint32 tail() const { return rep >> 1; } + Literal lit; + uint32 rep; + }; + template + struct CmpArc { + bool operator()(const Arc& lhs, uint32 n) const { return lhs.node[x] < n; } + bool operator()(uint32 n, const Arc& rhs) const { return n < rhs.node[x]; } + bool operator()(const Arc& lhs, const Arc& rhs) const { + return lhs.node[x] < rhs.node[x] + || (lhs.node[x] == rhs.node[x] && lhs.node[1-x] < rhs.node[1-x]); + } + }; + explicit ExtDepGraph(uint32 numNodeGuess = 0); + ~ExtDepGraph(); + void addEdge(Literal lit, uint32 startNode, uint32 endNode); + void update(); + uint32 finalize(SharedContext& ctx); + bool frozen() const; + uint64 attach(Solver& s, Constraint& p, uint64 genId); + void detach(Solver* s, Constraint& p); + + const Arc& arc(uint32 id) const { return fwdArcs_[id]; } + const Arc* fwdBegin(uint32 n) const { + uint32 X = nodes_[n].fwdOff; + return validOff(X) ? &fwdArcs_[X] : 0; + } + const Arc* fwdNext(const Arc* a)const { assert(a); return a[0].node[0] == a[1].node[0] ? ++a : 0; } + const Inv* invBegin(uint32 n) const { + uint32 X = nodes_[n].invOff; + return validOff(X) ? &invArcs_[X] : 0; + } + const Inv* invNext(const Inv* a)const { assert(a); return (a->rep & 1u) == 1u ? ++a : 0; } + uint32 nodes() const { return maxNode_; } + uint32 edges() const { return comEdge_; } + bool validNode(uint32 n) const { return n < maxNode_; } +private: + ExtDepGraph(const ExtDepGraph&); + ExtDepGraph& operator=(const ExtDepGraph&); + struct Node { + uint32 fwdOff; + uint32 invOff; + }; + typedef PodVector::type ArcVec; + typedef PodVector::type InvVec; + typedef PodVector::type NodeVec; + bool validOff(uint32 n) const { + return n != UINT32_MAX; + } + ArcVec fwdArcs_; // arcs ordered by node id + InvVec invArcs_; // inverse arcs ordered by node id + NodeVec nodes_; // data for the nodes of this graph + uint32 maxNode_; // nodes have ids in the range [0, maxNode_) + uint32 comEdge_; // number of edges committed + uint32 genCnt_; // generation count (for incremental updates) +}; + +//! Acyclicity checker that operates on a ExtDepGraph. +/*! + * \ingroup propagator + * \see M. Gebser, T. Janhunen, and J. Rintanen: "SAT Modulo Graphs: Acyclicity" + */ +class AcyclicityCheck : public PostPropagator { +public: + enum Strategy { + prop_full = 0, // forward and backward check with clause generation + prop_full_imp = 1, // forward and backward check without clause generation + prop_fwd = 2, // only forward check + }; + enum { PRIO = PostPropagator::priority_reserved_ufs + 1 }; + typedef ExtDepGraph DependencyGraph; + explicit AcyclicityCheck(DependencyGraph* graph); + ~AcyclicityCheck(); + void setStrategy(Strategy p); + void setStrategy(const SolverParams& opts); + // base interface + uint32 priority() const { return uint32(PRIO); } + bool init(Solver&); + void reset(); + bool propagateFixpoint(Solver& s, PostPropagator* ctx); + bool isModel(Solver& s); + bool valid(Solver& s); + void destroy(Solver* s, bool detach); +private: + AcyclicityCheck(const AcyclicityCheck&); + AcyclicityCheck& operator=(const AcyclicityCheck&); + struct Parent { + static Parent create(Literal x, uint32 n) { Parent p = {x, n}; return p; } + Literal lit; + uint32 node; + }; + enum { config_bit = 2 }; + struct ReasonStore; + typedef DependencyGraph::Arc Arc; + typedef DependencyGraph::Inv Inv; + typedef PodQueue EdgeQueue; + typedef PodVector::type TagVec; + typedef PodVector::type ParentVec; + bool dfsForward(Solver& s, const Arc& e); + bool dfsBackward(Solver& s, const Arc& e); + void setParent(Var node, const Parent& p){ parent_[node] = p; } + void pushVisit(Var node, uint32 tv) { nStack_.push_back(node); tags_[node] = tv; } + bool visited(Var node, uint32 tv) const { return tags_[node] == tv; } + uint32 startSearch(); + void addClauseLit(Solver& s, Literal p); + void setReason(Literal p, LitVec::const_iterator first, LitVec::const_iterator end); + // ------------------------------------------------------------------------------------------- + // constraint interface + PropResult propagate(Solver&, Literal, uint32& eId) { + todo_.push(graph_->arc(eId)); + return PropResult(true, true); + } + void reason(Solver& s, Literal, LitVec&); + Strategy strategy() const { return static_cast(strat_ & 3u); } + DependencyGraph* graph_; // my graph + Solver* solver_; // my solver + ReasonStore* nogoods_;// stores at most one reason per literal + uint32 strat_; // active propagation strategy + uint32 tagCnt_; // generation counter for searches + EdgeQueue todo_; // edges that recently became enabled + TagVec tags_; // tag for each node + ParentVec parent_; // parents for each node + VarVec nStack_; // node stack for df-search + LitVec reason_; // reason for conflict/implication + uint64 genId_; // generation identifier +}; + +} +#endif + diff --git a/clasp/clasp/enumerator.h b/clasp/clasp/enumerator.h new file mode 100644 index 000000000..b30fab9c5 --- /dev/null +++ b/clasp/clasp/enumerator.h @@ -0,0 +1,329 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_ENUMERATOR_H_INCLUDED +#define CLASP_ENUMERATOR_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif +#include +#include +#include +#include + +namespace Clasp { +class Solver; +class SharedContext; +class Enumerator; +class EnumerationConstraint; + +//! Type for storing a model. +struct Model { + enum Type { Sat = 0u, Brave = 1u, Cautious = 2u, User = 4u }; + enum { cons_mask = 3u, est_pos_mask= 4u, est_neg_mask = 8u }; + static uint8 estMask(Literal p) { return est_pos_mask << ((int)p.sign()); } + bool hasVar(Var v) const { return values && v < values->size(); } + //! True if this model stores current (cautious/brave) consequences. + bool consequences() const { return (type & cons_mask) != 0; } + //! For sat models, value of v in model. Otherwise, undefined. + ValueRep value(Var v) const { assert(hasVar(v)); return (*values)[v] & 3u; } + //! True if p is true in model or part of current consequences. + bool isTrue(Literal p) const { return (value(p.var()) & trueValue(p)) != 0; } + //! True if p is part of a definite answer. + bool isDef(Literal p) const { return isTrue(p) && (def || ((type & Cautious) == 0u) || !isEst(p)); } + //! True if p is part of the current estimate of consequences. + bool isEst(Literal p) const { assert(hasVar(p.var())); return ((*values)[p.var()] & estMask(p)) != 0; } + void reset(); + uint64 num; // running number of this model + const Enumerator* ctx; // ctx in which model was found + const ValueVec* values; // variable assignment or consequences + const SumVec* costs; // associated costs (or 0) + uint32 sId :16;// id of solver that found the model + uint32 type:12;// type of model + uint32 opt : 1;// whether the model is optimal w.r.t costs (0: unknown) + uint32 def : 1;// whether the model is definite w.r.t consequences (0: unknown) + uint32 sym : 1;// whether symmetric models are possible + uint32 up : 1;// whether the model was updated on last unsat +}; + +/** + * \defgroup enumerator Solving + * \brief Enumerators and solve algorithms. + */ +//@{ + +//! Options for configuring enumeration. +struct EnumOptions { + typedef MinimizeMode OptMode; + typedef ProjectMode ProjMode; + enum EnumType { enum_auto = 0, enum_bt = 1, enum_record = 2, enum_dom_record = 3, enum_consequences = 4, enum_brave = 5, enum_cautious = 6, enum_query = 7, enum_user = 8 }; + EnumOptions() : numModels(-1), enumMode(enum_auto), optMode(MinimizeMode_t::optimize), proMode(ProjectMode_t::Implicit), project(0) {} + static Enumerator* createModelEnumerator(const EnumOptions& opts); + static Enumerator* createConsEnumerator(const EnumOptions& opts); + static Enumerator* nullEnumerator(); + static Enumerator* createEnumerator(const EnumOptions& opts); + bool consequences() const { return (enumMode & enum_consequences) != 0; } + bool models() const { return (enumMode < enum_consequences); } + bool optimize() const { return ((optMode & MinimizeMode_t::optimize) != 0); } + int64 numModels; /*!< Number of models to compute. */ + EnumType enumMode; /*!< Enumeration type to use. */ + OptMode optMode; /*!< Optimization mode to use. */ + ProjMode proMode; /*!< Projection mode to use. */ + uint32 project; /*!< Options for projection. */ + SumVec optBound; /*!< Initial bound for optimize statements. */ +}; +const char* modelType(const Model& m); + + +//! Interface for supporting enumeration of models. +/*! + * Enumerators are global w.r.t a solve algorithm. That is, + * even if the solve algorithm itself uses a parallel search, there + * shall be only one enumerator and it is the user's responsibility + * to protect the enumerator with appropriate locking. + * + * Concrete enumerators must implement a function for preparing a problem for enumeration + * and an EnumerationConstraint to be added to each solver attached to the problem. + * It is then the job of the actual solve algorithm to commit models to the enumerator + * and to integrate new information into attached solvers via appropriate calls to + * Enumerator::update(). + */ +class Enumerator { +public: + typedef EnumerationConstraint* ConPtr; + typedef EnumerationConstraint& ConRef; + typedef const SharedMinimizeData* Minimizer; + typedef EnumOptions::OptMode OptMode; + class ThreadQueue; + explicit Enumerator(); + virtual ~Enumerator(); + + void reset(); + + //! Prepares the problem for enumeration. + /*! + * The function shall be called once before search is started and before SharedContext::endInit() + * was called. It freezes enumeration-related variables and adds a suitable enumeration constraint + * to the master solver. + * + * \pre The problem is not yet frozen, i.e. SharedContext::endInit() was not yet called. + * \param problem The problem on which this enumerator should work. + * \param opt Minimization mode to apply during enumeration. + * \param limit Optional hint on max number of models to compute. + * + * \note In the incremental setting, init() must be called once for each incremental step. + */ + int init(SharedContext& problem, OptMode opt = MinimizeMode_t::optimize, int limit = 0); + + //! Prepares the given solver for enumeration under the given path. + /*! + * The function shall be called once before solving is started. It pushes the + * given path to the solver by calling Solver::pushRoot() and prepares s for + * enumeration/optimization. + * \return true if path was added. + */ + bool start(Solver& s, const LitVec& path = LitVec(), bool disjointPath = false) const; + + //! Updates the given solver with enumeration-related information. + /*! + * The function is used to integrate enumeration-related information, + * like minimization bounds or previously committed models, into the search space of s. + * It shall be called after each commit. + * + * \param s The solver to update. + * \note The function is concurrency-safe, i.e. can be called + * concurrently by different solvers. + */ + bool update(Solver& s) const; + + /*! + * \name Commit functions + * Functions for committing enumeration-related information to the enumerator. + * \note The functions in this group are *not* concurrency-safe, i.e. in a parallel search + * at most one solver shall call a commit function at any one time. + */ + //@{ + + //! Commits the model stored in the given solver. + /*! + * If the model is valid and unique, the function returns true and the + * model can be accessed via a call to Enumerator::lastModel(). + * Otherwise, the function returns false. + * In either case, Enumerator::update(s) shall be called + * in order to continue search for further models. + * + * \pre The solver's assignment is total. + */ + bool commitModel(Solver& s); + //! Expands the next symmetric model if any. + bool commitSymmetric(Solver& s); + //! Commits an unsatisfiable path stored in the given solver. + /*! + * The return value determines how search should proceed. + * If true is returned, the enumerator has relaxed an enumeration constraint + * and search may continue after a call to Enumerator::update(s). + * Otherwise, the search shall be stopped. + */ + bool commitUnsat(Solver& s); + //! Commits the given clause to this enumerator. + bool commitClause(const LitVec& clause) const; + //! Marks current enumeration phase as completed. + /*! + * If the enumerator was initialized with a minimization constraint and + * optimization mode MinimizeMode_t::enumOpt, the optimal bound is committed, + * the enumerator is prepared for enumerating optimal models, and the function + * returns false. Otherwise, the function returns true and search shall be stopped. + */ + bool commitComplete(); + //! Commits the state stored in the given solver. + /*! + * Calls commitModel() or commitUnsat() depending on the state of s. + * The function returns value_true, to signal that s stores a valid and + * unique model, value_false to signal that search shall be stopped, and + * value_free otherwise. + * \see commitModel() + * \see commitUnsat() + */ + uint8 commit(Solver& s); + //@} + + //! Removes from s the path that was passed to start() and any active (minimization) bound. + void end(Solver& s) const; + //! Returns the number of models enumerated so far. + uint64 enumerated() const { return model_.num; } + //! Returns the last model enumerated. + /*! + * \note If enumerated() is equal to 0, the returned object is in an indeterminate state. + */ + const Model& lastModel() const { return model_; } + //! Returns whether optimization is active. + bool optimize() const { return mini_ && mini_->mode() != MinimizeMode_t::enumerate && model_.opt == 0; } + //! Returns whether computed models are still tentative. + bool tentative() const { return mini_ && mini_->mode() == MinimizeMode_t::enumOpt && model_.opt == 0; } + //! Returns the active minimization constraint if any. + Minimizer minimizer() const { return mini_; } + //! Returns the type of models this enumerator computes. + virtual int modelType() const { return Model::Sat; } + enum UnsatType { + unsat_stop = 0u, /*!< First unsat stops search - commitUnsat() always return false. */ + unsat_cont = 1u, /*!< Unsat may be tentative - commitUnsat() may return true. */ + unsat_sync = 3u, /*!< Similar to unsat_cont but additionally requires synchronization among threads. */ + }; + //! Returns whether unsat may be tentative and/or requires synchronization. + virtual int unsatType() const; + //! Returns whether or not this enumerator supports full restarts once a model was found. + virtual bool supportsRestarts() const { return true; } + //! Returns whether or not this enumerator supports parallel search. + virtual bool supportsParallel() const { return true; } + //! Returns whether or not this enumerator supports splitting-based search. + virtual bool supportsSplitting(const SharedContext& problem) const; + //! Returns whether this enumerator requires exhaustive search to produce a definite answer. + virtual bool exhaustive() const { return mini_ && mini_->mode() != MinimizeMode_t::enumerate; } + //! Sets whether the search path stored in s is disjoint from all others. + void setDisjoint(Solver& s, bool b) const; + //! Sets whether symmetric should be ignored. + void setIgnoreSymmetric(bool b); + ConPtr constraint(const Solver& s) const; +protected: + //! Shall prepare the enumerator and freeze any enumeration-related variable. + /*! + * \return A prototypical enumeration constraint to be used in a solver. + */ + virtual ConPtr doInit(SharedContext& ctx, SharedMinimizeData* min, int numModels) = 0; + virtual void doReset(); + Model& model(); +private: + class SharedQueue; + Enumerator(const Enumerator&); + Enumerator& operator=(const Enumerator&); + ConRef constraintRef(const Solver& s) const; + SharedMinimizeData* mini_; + SharedQueue* queue_; + SumVec costs_; + Model model_; +}; + +//! A solver-local (i.e. thread-local) constraint to support enumeration. +/*! + * An enumeration constraint is used to extract/store enumeration-related information + * from models. + */ +class EnumerationConstraint : public Constraint { +public: + typedef EnumerationConstraint* ConPtr; + typedef MinimizeConstraint* MinPtr; + typedef Enumerator::ThreadQueue* QueuePtr; + //! Returns true if search-path is disjoint from all others. + bool disjointPath()const { return disjoint_; } + ValueRep state() const { return state_; } + ValueRep resetMode() const { return upMode_; } + //! Returns true if optimization is active. + bool optimize() const; + MinPtr minimizer() const { return mini_; } + // Methods used by enumerator + void init(Solver& s, SharedMinimizeData* min, QueuePtr q); + bool start(Solver& s, const LitVec& path, bool disjoint); + void end(Solver& s); + bool update(Solver& s); + void setDisjoint(bool x); + bool integrateBound(Solver& s); + bool integrateNogoods(Solver& s); + bool commitModel(Enumerator& ctx, Solver& s); + bool commitUnsat(Enumerator& ctx, Solver& s); + void setMinimizer(MinPtr min) { mini_ = min; } + void add(Constraint* c); + void modelHeuristic(Solver& s); +protected: + EnumerationConstraint(); + virtual ~EnumerationConstraint(); + // base interface + virtual void destroy(Solver* s, bool detach); + virtual PropResult propagate(Solver&, Literal, uint32&) { return PropResult(true, true); } + virtual void reason(Solver&, Literal, LitVec&) {} + virtual bool simplify(Solver& s, bool reinit); + virtual bool valid(Solver& s); + virtual Constraint* cloneAttach(Solver& s); + // own interface + virtual ConPtr clone() = 0; + virtual bool doUpdate(Solver& s) = 0; + virtual void doCommitModel(Enumerator&, Solver&) {} + virtual void doCommitUnsat(Enumerator&, Solver&) {} + uint32 rootLevel() const { return root_; } +private: + typedef PodVector::type ConstraintDB; + typedef SingleOwnerPtr QPtr; + MinimizeConstraint* mini_; + QPtr queue_; + ConstraintDB nogoods_; + LitVec next_; + uint32 root_; + ValueRep state_; + ValueRep upMode_; + ValueRep heuristic_; + bool disjoint_; +}; +//@} +} + +#endif diff --git a/clasp/clasp/heuristics.h b/clasp/clasp/heuristics.h new file mode 100644 index 000000000..2ea562ea4 --- /dev/null +++ b/clasp/clasp/heuristics.h @@ -0,0 +1,403 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_HEURISTICS_H_INCLUDED +#define CLASP_HEURISTICS_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file + * \brief Defines various decision heuristics to be used in clasp. + */ + +#include +#include +#include +#include +namespace Clasp { + +//! Computes a moms-like score for var v. +uint32 momsScore(const Solver& s, Var v); + +//! A variant of the BerkMin decision heuristic from the BerkMin Sat-Solver. +/*! + * \ingroup heuristic + * \see E. Goldberg, Y. Navikov: "BerkMin: a Fast and Robust Sat-Solver" + * + * \note + * This version of the BerkMin heuristic varies mostly in the following points from the original BerkMin: + * -# it considers loop-nogoods if requested (this is the default) + * -# it uses a MOMS-like heuristic as long as there are no conflicts (and therefore no activities) + * -# it uses a MOMS-like score to break ties whenever multiple variables from an unsatisfied learnt constraint have equal activities + * -# it uses a lazy decaying scheme that only touches active variables + */ +class ClaspBerkmin : public DecisionHeuristic { +public: + /*! + * \note Checks at most params.param candidates when searching for not yet + * satisfied learnt constraints. If param is 0, all candidates are checked. + */ + explicit ClaspBerkmin(const HeuParams& params = HeuParams()); + void setConfig(const HeuParams& params); + void startInit(const Solver& s); + void endInit(Solver& s); + void newConstraint(const Solver& s, const Literal* first, LitVec::size_type size, ConstraintType t); + void updateReason(const Solver& s, const LitVec& lits, Literal resolveLit); + bool bump(const Solver& s, const WeightLitVec& lits, double adj); + void undoUntil(const Solver&, LitVec::size_type); + void updateVar(const Solver& s, Var v, uint32 n); +protected: + Literal doSelect(Solver& s); +private: + Literal selectLiteral(Solver& s, Var v, bool vsids) const; + Literal selectRange(Solver& s, const Literal* first, const Literal* last); + bool initHuang() const { return order_.score[0].occ == 1; } + void initHuang(bool b) { order_.score[0].occ = b; } + bool hasActivities() const { return order_.score[0].act != 0; } + void hasActivities(bool b) { order_.score[0].act = b; } + Var getMostActiveFreeVar(const Solver& s); + Var getTopMoms(const Solver& s); + bool hasTopUnsat(Solver& s); + // Gathers heuristic information for one variable v. + struct HScore { + HScore(uint32 d = 0) : occ(0), act(0), dec(uint16(d)) {} + void incAct(uint32 gd, bool h, bool sign) { + occ += int(h) * (1 - (2*int(sign))); + decay(gd, h); + ++act; + } + void incOcc(bool sign) { occ += 1 - (2*int(sign)); } + uint32 decay(uint32 gd, bool h) { + if (uint32 x = (gd-dec)) { + // NOTE: shifts might overflow, i.e. + // activity is actually shifted by x%32. + // We deliberately ignore this "logical inaccuracy" + // and treat it as random noise ;) + act >>= x; + dec = uint16(gd); + occ /= (1<<(x*int(h))); + } + return act; + } + int32 occ; + uint16 act; + uint16 dec; + }; + typedef PodVector::type Scores; + typedef VarVec::iterator Pos; + + struct Order { + explicit Order() : decay(0), huang(false), resScore(3u) {} + struct Compare { + explicit Compare(Order* o) : self(o) {} + bool operator()(Var v1, Var v2) const { + return self->decayedScore(v1) > self->decayedScore(v2) + || (self->score[v1].act == self->score[v2].act && v1 < v2); + } + Order* self; + }; + uint32 decayedScore(Var v) { return score[v].decay(decay, huang); } + int32 occ(Var v) const { return score[v].occ; } + void inc(Literal p, bool inNant) { + if (!this->nant || inNant) { + score[p.var()].incAct(decay, huang, p.sign()); + } + } + void incOcc(Literal p) { score[p.var()].incOcc(p.sign()); } + int compare(Var v1, Var v2) { + return int(decayedScore(v1)) - int(decayedScore(v2)); + } + void resetDecay(); + Scores score; // For each var v score_[v] stores heuristic score of v + uint32 decay; // "global" decay counter. Increased every decP_ decisions + bool huang; // Use Huang's scoring scheme (see: Jinbo Huang: "A Case for Simple SAT Solvers") + bool nant; // only score vars v with varInfo(v).nant()? + uint8 resScore; + private: + Order(const Order&); + Order& operator=(const Order&); + } order_; + VarVec cache_; // Caches the most active variables + LitVec freeLits_; // Stores free variables of the last learnt conflict clause that is not sat + LitVec freeOtherLits_; // Stores free variables of the last other learnt nogood that is not sat + uint32 topConflict_; // Index into the array of learnt nogoods used when searching for conflict clauses that are not sat + uint32 topOther_; // Index into the array of learnt nogoods used when searching for other learnt nogoods that are not sat + Var front_; // First variable whose truth-value is not already known - reset on backtracking + Pos cacheFront_; // First unprocessed cache position - reset on backtracking + uint32 cacheSize_; // Cache at most cacheSize_ variables + uint32 numVsids_; // Number of consecutive vsids-based decisions + uint32 maxBerkmin_; // When searching for an open learnt constraint, check at most maxBerkmin_ candidates. + TypeSet types_; // When searching for an open learnt constraint, consider these types of nogoods. + RNG rng_; +}; + +//! Variable Move To Front decision strategies inspired by Siege. +/*! + * \ingroup heuristic + * \see Lawrence Ryan: "Efficient Algorithms for Clause Learning SAT-Solvers" + * + * \note This implementation of VMTF differs from the original implementation in three points: + * - it optionally moves to the front a selection of variables from learnt loop nogoods + * - it measures variable activity by using a BerkMin-like score scheme + * - the initial order of the var list is determined using a MOMs-like score scheme + */ +class ClaspVmtf : public DecisionHeuristic { +public: + /*! + * \note Moves at most params.param literals from constraints used during + * conflict resolution to the front. If params.param is 0, the default is + * to move up to 8 literals. + */ + explicit ClaspVmtf(const HeuParams& params = HeuParams()); + void setConfig(const HeuParams& params); + void startInit(const Solver& s); + void endInit(Solver&); + void newConstraint(const Solver& s, const Literal* first, LitVec::size_type size, ConstraintType t); + void updateReason(const Solver& s, const LitVec& lits, Literal resolveLit); + bool bump(const Solver& s, const WeightLitVec& lits, double adj); + void simplify(const Solver&, LitVec::size_type); + void undoUntil(const Solver&, LitVec::size_type); + void updateVar(const Solver& s, Var v, uint32 n); +protected: + Literal doSelect(Solver& s); +private: + Literal selectRange(Solver& s, const Literal* first, const Literal* last); + typedef std::list VarList; + typedef VarList::iterator VarPos; + struct VarInfo { + VarInfo(VarPos it) : pos_(it), activity_(0), occ_(0), decay_(0) { } + VarPos pos_; // position of var in var list + uint32 activity_; // activity of var - initially 0 + int32 occ_; // which literal of var occurred more often in learnt constraints? + uint32 decay_; // counter for lazy decaying activity + uint32& activity(uint32 globalDecay) { + if (uint32 x = (globalDecay - decay_)) { + activity_ >>= (x<<1); + decay_ = globalDecay; + } + return activity_; + } + }; + typedef PodVector::type Score; + + struct LessLevel { + LessLevel(const Solver& s, const Score& sc) : s_(s), sc_(sc) {} + bool operator()(Var v1, Var v2) const { + return s_.level(v1) < s_.level(v2) + || (s_.level(v1) == s_.level(v2) && sc_[v1].activity_ > sc_[v2].activity_); + } + bool operator()(Literal l1, Literal l2) const { + return (*this)(l1.var(), l2.var()); + } + private: + LessLevel& operator=(const LessLevel&); + const Solver& s_; + const Score& sc_; + }; + Score score_; // For each var v score_[v] stores heuristic score of v + VarList vars_; // List of possible choices, initially ordered by MOMs-like score + VarVec mtf_; // Vars to be moved to the front of vars_ + VarPos front_; // Current front-position in var list - reset on backtracking + uint32 decay_; // "Global" decay counter. Increased every 512 decisions + uint32 nMove_; // Limit on number of vars to move + TypeSet types_; // Type of nogoods to score during resolution + uint32 scType_;// Type of scoring + bool nant_; // only move vars v with varInfo(v).nant()? +}; + +//! Score type for VSIDS heuristic. +/*! + * \see ClaspVsids + */ +struct VsidsScore { + typedef VsidsScore SC; + VsidsScore(double sc = 0.0) : value(sc) {} + double get() const { return value; } + bool operator>(const SC& o) const { return value > o.value; } + void set(double f) { value = f; } + template + static double applyFactor(C&, Var, double f) { return f; } + double value; // activity +}; + +//! A variable state independent decision heuristic favoring variables that were active in recent conflicts. +/*! + * \ingroup heuristic + * \see M. W. Moskewicz, C. F. Madigan, Y. Zhao, L. Zhang, and S. Malik: + * "Chaff: Engineering an Efficient SAT Solver" + * + * \note By default, the implementation uses the exponential VSIDS scheme from MiniSAT and + * applies a MOMs-like score scheme to get an initial var order. + */ +template +class ClaspVsids_t : public DecisionHeuristic { +public: + /*! + * \note Uses params.param to init the decay value d and inc factor 1.0/d. + * If params.param is 0, d is set 0.95. Otherwise, d is set to 0.x, where x is params.param. + */ + explicit ClaspVsids_t(const HeuParams& params = HeuParams()); + virtual void setConfig(const HeuParams& params); + virtual void startInit(const Solver& s); + virtual void endInit(Solver&); + virtual void newConstraint(const Solver& s, const Literal* first, LitVec::size_type size, ConstraintType t); + virtual void updateReason(const Solver& s, const LitVec& lits, Literal resolveLit); + virtual bool bump(const Solver& s, const WeightLitVec& lits, double adj); + virtual void undoUntil(const Solver&, LitVec::size_type); + virtual void simplify(const Solver&, LitVec::size_type); + virtual void updateVar(const Solver& s, Var v, uint32 n); +protected: + virtual Literal doSelect(Solver& s); + virtual Literal selectRange(Solver& s, const Literal* first, const Literal* last); + virtual void initScores(Solver& s, bool moms); + typedef typename PodVector::type ScoreVec; + typedef PodVector::type OccVec; + void updateVarActivity(const Solver& s, Var v, double f = 1.0); + void incOcc(Literal p) { occ_[p.var()] += 1 - (int(p.sign()) << 1); } + int occ(Var v) const { return occ_[v]; } + void normalize(); + struct CmpScore { + explicit CmpScore(const ScoreVec& s) : sc_(s) {} + bool operator()(Var v1, Var v2) const { return sc_[v1] > sc_[v2]; } + private: + CmpScore& operator=(const CmpScore&); + const ScoreVec& sc_; + }; + typedef bk_lib::indexed_priority_queue VarOrder; + struct Decay : Range{ + Decay(double x = 0.0, double y = 0.95, uint32 b = 0, uint32 f = 0) : Range(x, y), bump(b), freq(f), next(f) { + this->df = 1.0 / (freq && lo > 0.0 ? lo : hi); + } + double df; // active decay factor for evsids (>= 1.0) + uint32 bump; + uint32 freq : 16; + uint32 next : 16; + }; + ScoreVec score_; // vsids score for each variable + OccVec occ_; // occurrence balance of each variable + VarOrder vars_; // priority heap of variables + Decay decay_; // (dynamic) decaying strategy + double inc_; // var bump for evsids or conflict index for acids (increased on conflict) + TypeSet types_; // set of constraints to score + uint32 scType_;// score type (one of HeuParams::Score) + bool acids_; // whether to use acids instead if evsids scoring + bool nant_; // whether bumps are restricted to vars v with varInfo(v).nant() +}; +typedef ClaspVsids_t ClaspVsids; + +//! Score type for DomainHeuristic. +/*! + * \see DomainHeuristic + */ +struct DomScore : VsidsScore { + static const uint32 domMax = (1u << 30) - 1; + typedef DomScore SC; + DomScore(double v = 0.0) : VsidsScore(v), level(0), factor(1), domP(domMax), sign(0), init(0) {} + bool operator>(const SC& o) const { return (level > o.level) || (level == o.level && value > o.value); } + bool isDom() const { return domP != domMax; } + void setDom(uint32 key) { domP = key; } + template + static double applyFactor(C& sc, Var v, double f) { + int16 df = sc[v].factor; + return df == 1 ? f : static_cast(df) * f; + } + int16 level; // priority level + int16 factor; // factor used when bumping activity + uint32 domP : 30; // index into dom-table if dom var + uint32 sign : 1; // whether v has a sign modification + uint32 init : 1; // whether value is from init modification +}; + +//! A VSIDS heuristic supporting additional domain knowledge. +/*! + * \ingroup heuristic + * + * \see M. Gebser, B. Kaufmann, R. Otero, J. Romero, T. Schaub, P. Wanko: + * "Domain-specific Heuristics in Answer Set Programming", + * http://www.cs.uni-potsdam.de/wv/pdfformat/gekaotroscwa13a.pdf + */ +class DomainHeuristic : public ClaspVsids_t + , private Constraint { +public: + typedef ClaspVsids_t BaseType; + explicit DomainHeuristic(const HeuParams& params = HeuParams()); + ~DomainHeuristic(); + void setDefaultMod(HeuParams::DomMod mod, uint32 prefSet); + virtual void setConfig(const HeuParams& params); + virtual void startInit(const Solver& s); + const DomScore& score(Var v) const { return score_[v]; } +protected: + // base interface + virtual Literal doSelect(Solver& s); + virtual void initScores(Solver& s, bool moms); + virtual void detach(Solver& s); + // Constraint interface + virtual Constraint* cloneAttach(Solver&) { return 0; } + virtual void reason(Solver&, Literal, LitVec&){} + virtual PropResult propagate(Solver&, Literal, uint32&); + virtual void undoLevel(Solver& s); +private: + struct DomAction { + static const uint32 UNDO_NIL = (1u << 31) - 1; + uint32 var:30; // dom var to be modified + uint32 mod: 2; // modification to apply + uint32 undo:31;// next action in undo list + uint32 next: 1;// next action belongs to same condition? + int16 bias; // value to apply + uint16 prio; // prio of modification + }; + struct DomPrio { + void clear() { prio[0] = prio[1] = prio[2] = prio[3] = 0; } + uint16 operator[](unsigned i) const { return prio[i]; } + uint16& operator[](unsigned i) { return prio[i]; } + uint16 prio[4]; + }; + struct Frame { + Frame(uint32 lev, uint32 h) : dl(lev), head(h) {} + uint32 dl; + uint32 head; + }; + typedef PodVector::type ActionVec; + typedef PodVector::type PrioVec; + typedef PodVector::type FrameVec; + typedef DomainTable::ValueType DomMod; + typedef PodVector >::type VarScoreVec; + + uint32 addDomAction(const DomMod& e, Solver& s, VarScoreVec& outInit, Literal& lastW); + void addDefAction(Solver& s, Literal x, int16 lev, uint32 domKey); + void pushUndo(uint32& head, uint32 actionId); + void applyAction(Solver& s, DomAction& act, uint16& oldPrio); + uint16& prio(Var v, uint32 mod) { return prios_[score_[v].domP][mod]; } + PrioVec prios_; // priorities for domain vars + ActionVec actions_; // dynamic modifications + FrameVec frames_; // dynamic undo information + uint32 domSeen_; // offset into domain table + uint32 defMax_; // max var with default modification + uint16 defMod_; // default modifier + uint16 defPref_; // default preferences +}; +} +#endif diff --git a/clasp/clasp/literal.h b/clasp/clasp/literal.h new file mode 100644 index 000000000..444ffbadc --- /dev/null +++ b/clasp/clasp/literal.h @@ -0,0 +1,247 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_LITERAL_H_INCLUDED +#define CLASP_LITERAL_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif +#include +#include +#include // std::swap +#include +/*! + * \file + * \brief Types and functions related to literals and variables. + */ +namespace Clasp { +/*! + * \addtogroup constraint + */ +//@{ + +//! A variable is an integer in the range [0..varMax). +typedef uint32 Var; + +//! varMax is not a valid variale, i.e. currently Clasp supports at most 2^30 variables. +const Var varMax = (Var(1) << 30); + +//! The variable 0 has a special meaning in the solver. +const Var sentVar = 0; + +//! Literal ids are in the range [0..litIdMax). +const uint32 litIdMax = (Var(1) << 31); + +//! Possible types of a variable. +struct Var_t { + enum Type { Atom = 1, Body = 2, Hybrid = Atom | Body}; + static bool isBody(Type t) { + return (static_cast(t) & static_cast(Body)) != 0; + } + static bool isAtom(Type t) { + return (static_cast(t) & static_cast(Atom)) != 0; + } +}; +typedef Var_t::Type VarType; + +//! A literal is a variable or its negation. +/*! + * \par Implementation: + * A literal's state is stored in a single 32-bit integer as follows: + * - 30-bits : variable id + * - 1-bit : sign, 1 if negative, 0 if positive + * - 1-bit : general purpose flag for marking a literal instance + */ +class Literal { +public: + static const uint32 sign_bit = 2u; + static const uint32 flag_bit = 1u; + + //! The default constructor creates the positive literal of the special sentinel var. + Literal() : rep_(0) { } + + //! Creates a literal of the variable var with sign s. + /*! + * \param var The literal's variable. + * \param sign true if new literal should be negative. + * \pre var < varMax + */ + Literal(Var var, bool sign) : rep_( (var<> sign_bit; } + + //! Returns the sign of the literal. + /*! + * \return true if the literal is negative. Otherwise false. + */ + bool sign() const { return (rep_ & sign_bit) != 0; } + + //! Returns var and sign encoded in a unique id. + /*! + * \note The watch-flag is ignored and thus the id of a literal can be stored in 31-bits. + */ + uint32 id() const { return rep_ >> flag_bit; } + + //! Returns the stored representation of this literal. + uint32& rep() { return rep_; } + uint32 rep() const { return rep_; } + + //! Creates a literal from an id. + static Literal fromId(uint32 id) { + assert(id < litIdMax); + return Literal(id<(-p)) : posLit(static_cast(p)); } +//! Converts the given (non-sentinel) literal to a signed integer s.th. p == toLit(toInt(p)). +inline int32 toInt(Literal p) { return p.sign() ? -static_cast(p.var()) : static_cast(p.var()); } +//! Always-true literal. +// TODO: replace with constant using constexpr ctor once we switch to C++11 +inline Literal lit_true() { return Literal(sentVar, false); } +//! Always-false literal. +// TODO: replace with constant using constexpr ctor once we switch to C++11 +inline Literal lit_false() { return Literal(sentVar, true); } +//! Returns true if p represents the special variable 0 +inline bool isSentinel(Literal p) { return p.var() == sentVar; } + +// Low-level conversion between Literals and int literals. +// We cannot use toInt() here because it is not defined for the +// sentinel literals lit_true() and lit_false(). +inline int32 encodeLit(Literal x) { return !x.sign() ? static_cast(x.var()+1) : -static_cast(x.var()+1); } +inline Var decodeVar(int32 x) { return static_cast(x >= 0 ? x : -x) - 1; } +inline Literal decodeLit(int32 x) { return Literal(decodeVar(x), x < 0); } + +inline unsigned hashId(unsigned key) { + key = ~key + (key << 15); + key ^= (key >> 11); + key += (key << 3); + key ^= (key >> 5); + key += (key << 10); + key ^= (key >> 16); + return key; +} +inline uint32 hashLit(Literal p) { return hashId(p.id()); } + +//! A signed integer type used to represent weights. +typedef int32 weight_t; +//! A signed integer type used to represent sums of weights. +typedef int64 wsum_t; +#define CLASP_WEIGHT_T_MAX ( 2147483647) +#define CLASP_WEIGHT_T_MIN (-2147483647 - 1) +#define CLASP_WEIGHT_SUM_MAX INT64_MAX +#define CLASP_WEIGHT_SUM_MIN INT64_MIN + +typedef PodVector::type VarVec; //!< A vector of variables. +typedef PodVector::type LitVec; //!< A vector of literals. +typedef PodVector::type WeightVec; //!< A vector of weights. +typedef PodVector::type SumVec; //!< A vector of sums of weights. +typedef std::pair WeightLiteral; //!< A literal associated with a weight. +typedef PodVector::type WeightLitVec; //!< A vector of weight-literals. +/////////////////////////////////////////////////////////////////////////////// +// Truth values +/////////////////////////////////////////////////////////////////////////////// +typedef uint8 ValueRep; //!< Type of the three value-literals. +const ValueRep value_true = 1; //!< Value used for variables that are true. +const ValueRep value_false = 2; //!< Value used for variables that are false. +const ValueRep value_free = 0; //!< Value used for variables that are unassigned. +typedef PodVector::type ValueVec; + +//! Returns the value that makes the literal lit true. +/*! + * \param lit The literal for which the true-value should be determined. + * \return + * - value_true iff lit is a positive literal + * - value_false iff lit is a negative literal. + * . + */ +inline ValueRep trueValue(const Literal& lit) { return 1 + lit.sign(); } + +//! Returns the value that makes the literal lit false. +/*! + * \param lit The literal for which the false-value should be determined. + * \return + * - value_false iff lit is a positive literal + * - value_true iff lit is a negative literal. + * . + */ +inline ValueRep falseValue(const Literal& lit) { return 1 + !lit.sign(); } + +//! Returns the sign that matches the value. +/*! + * \return + * - false iff v == value_true + * - true otherwise + */ +inline bool valSign(ValueRep v) { return v != value_true; } +//@} +} +#endif diff --git a/clasp/clasp/logic_program.h b/clasp/clasp/logic_program.h new file mode 100644 index 000000000..d24550ba7 --- /dev/null +++ b/clasp/clasp/logic_program.h @@ -0,0 +1,677 @@ +// +// Copyright (c) 2013-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +#ifndef CLASP_LOGIC_PROGRAM_H_INCLUDED +#define CLASP_LOGIC_PROGRAM_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif +#include +#include +#include +#include POTASSCO_EXT_INCLUDE(unordered_set) + +namespace Clasp { namespace Asp { +/*! + * \file + * \defgroup asp Asp + * \brief Classes and functions for defining logic programs. + * \ingroup problem + */ +//@{ + +//! A struct for counting program rules and directives. +struct RuleStats { + typedef uint32& Ref_t; + typedef uint32 const& CRef_t; + //! Rules and directives counted by this object. + enum Key { + Normal = Head_t::Disjunctive,//!< Normal or disjunctive rules. + Choice = Head_t::Choice, //!< Choice rules. + Minimize , //!< Distinct minimize constraints. + Acyc , //!< Edge directives. + Heuristic , //!< Heuristic directives. + Key__num + }; + //! Returns a string representation of the given key. + static const char* toStr(int k); + //! Returns the number of keys distinguished by this type. + static uint32 numKeys() { return Key__num; } + //! Updates the number of rules of the given type. + void up(Key k, int amount) { key[k] += static_cast(amount); } + //! Returns the number of rules of the given type. + Ref_t operator[](int k) { return key[k]; } + //! @copydoc operator[](int k) + CRef_t operator[](int k) const { return key[k]; } + //! Returns the sum of all rules. + uint32 sum() const; + uint32 key[Key__num]; //!< @private +}; +//! A struct for counting distinct program bodies. +struct BodyStats { + typedef uint32& Ref_t; + typedef uint32 const& CRef_t; + //! Body types distinguished by this object. + typedef Body_t Key; + //! Returns a string representation of the given key. + static const char* toStr(int k); + //! Returns the number of keys distinguished by this type. + static uint32 numKeys() { return Body_t::eMax + 1; } + //! Updates the number of bodies of the given type. + void up(Key k, int amount) { key[k] += static_cast(amount); } + //! Returns the number of bodies of the given type. + Ref_t operator[](int k) { return key[k]; } + //! @copydoc operator[](int k) + CRef_t operator[](int k) const { return key[k]; } + //! Returns the sum of all bodies. + uint32 sum() const; + uint32 key[Body_t::eMax + 1]; //!< @private +}; + +//! A type for maintaining a set of program statistics. +class LpStats { +public: + LpStats() { reset(); } + void reset(); + //! Returns the sum of all equivalences. + uint32 eqs() const { return eqs(Var_t::Atom) + eqs(Var_t::Body) + eqs(Var_t::Hybrid); } + //! Returns the number of equivalences of the given type. + uint32 eqs(VarType t) const { return eqs_[t-1]; } + //! Increments the number of equivalences of the given type. + void incEqs(VarType t) { ++eqs_[t-1]; } + //! Computes *this += o. + void accu(const LpStats& o); + RuleStats rules[2]; /**< RuleStats (initial, final). */ + BodyStats bodies[2]; /**< BodyStats (initial, final). */ + uint32 atoms; /**< Number of program atoms. */ + uint32 auxAtoms; /**< Number of aux atoms created. */ + uint32 disjunctions[2]; /**< Number of disjunctions (initial, non-hcf). */ + uint32 sccs; /**< How many strongly connected components? */ + uint32 nonHcfs; /**< How many non head-cycle free components?*/ + uint32 gammas; /**< How many non-hcf gamma rules. */ + uint32 ufsNodes; /**< How many nodes in the positive dependency graph? */ + // StatisticObject + static uint32 size(); + static const char* key(uint32 i); + StatisticObject at(const char* k) const; +private: + uint32 eqs_[3]; +}; +//! Exception type for signaling an invalid incremental program update. +class RedefinitionError : public std::logic_error { +public: + explicit RedefinitionError(unsigned atomId, const char* atomName = ""); + unsigned atom() const { return atomId_; } +private: + unsigned atomId_; +}; +using Potassco::TheoryData; +struct MapLit_t { + POTASSCO_ENUM_CONSTANTS(MapLit_t, Raw = 0, Refined = 1); +}; + +//! A class for defining a logic program. +/*! + * Use this class to specify a logic program. Once the program is completly defined, + * call endProgram() to load the logic program into a SharedContext object. + */ +class LogicProgram : public ProgramBuilder { +public: + LogicProgram(); + ~LogicProgram(); + //! Defines the possible modes for handling extended rules, i.e. choice, cardinality, and weight rules. + enum ExtendedRuleMode { + mode_native = 0, //!< Handle extended rules natively. + mode_transform = 1, //!< Transform extended rules to normal rules. + mode_transform_choice = 2, //!< Transform only choice rules to normal rules. + mode_transform_card = 3, //!< Transform only cardinality rules to normal rules. + mode_transform_weight = 4, //!< Transform cardinality- and weight rules to normal rules. + mode_transform_scc = 5, //!< Transform recursive cardinality- and weight rules to normal rules. + mode_transform_nhcf = 6, //!< Transform cardinality- and weight rules in non-hcf components to normal rules. + mode_transform_integ = 7, //!< Transform cardinality-based integrity constraints. + mode_transform_dynamic= 8 //!< Heuristically decide whether or not to transform a particular extended rule. + }; + + //! Options for the Asp-Preprocessor. + struct AspOptions { + static const uint32 MAX_EQ_ITERS = static_cast( (1u<<25)-1 ); + typedef ExtendedRuleMode TrMode; + AspOptions() { + std::memset(this, 0, sizeof(AspOptions)); + iters = 5; + } + AspOptions& iterations(uint32 it) { iters = it;return *this;} + AspOptions& depthFirst() { dfOrder = 1; return *this;} + AspOptions& backpropagate() { backprop= 1; return *this;} + AspOptions& noScc() { noSCC = 1; return *this;} + AspOptions& noEq() { iters = 0; return *this;} + AspOptions& disableGamma() { noGamma = 1; return *this;} + AspOptions& ext(ExtendedRuleMode m) { erMode = m; return *this;} + AspOptions& distinctTrue() { distTrue= 1; return *this;} + TrMode erMode; //!< How to handle extended rules? + uint32 iters : 26;//!< Number of iterations in eq-preprocessing or 0 to disable. + uint32 noSCC : 1;//!< Disable scc checking? + uint32 suppMod : 1;//!< Disable scc checking and compute supported models. + uint32 dfOrder : 1;//!< Visit nodes in eq-preprocessing in depth-first order? + uint32 backprop : 1;//!< Enable backpropagation during preprocessing? + uint32 oldMap : 1;//!< Use old and larger mapping for disjunctive programs. + uint32 noGamma : 1;//!< Disable creation of (shifted) gamma rules for non-hcf disjunctions? + uint32 distTrue : 1;//!< Add distinct true var for each step instead of one for all steps. + }; + + /*! + * \name Step control functions + */ + //@{ + + //! Starts the definition of a logic program. + LogicProgram& start(SharedContext& ctx, const AspOptions& opts = AspOptions()) { + startProgram(ctx); + setOptions(opts); + return *this; + } + //! Sets the mode for handling extended rules (default: mode_native). + void setExtendedRuleMode(ExtendedRuleMode m) { opts_.ext(m); } + //! Enable distinct true vars for incremental steps. + void enableDistinctTrue(); + //! Sets preprocessing options. + void setOptions(const AspOptions& opts); + //! Sets the configuration to be used for checker solvers in disjunctive LP solving. + void setNonHcfConfiguration(Configuration* c){ nonHcfs_.config = c; } + + //! Unfreezes a currently frozen program and starts an incremental step. + /*! + * If a program is to be defined incrementally, this function must be called + * exactly once for each step before any new rules or atoms are added. + * \note Program update only works correctly under the following assumptions: + * - Atoms introduced in step i are either: + * - solely defined in step i OR, + * - marked as frozen in step i and solely defined in step i+k OR, + * - forced to false by a compute statement in step 0. + * + * \pre The program is either frozen or at step 0. + * \post The program is no longer frozen and calling program mutating functions is valid again. + * \throws std::logic_error precondition is violated. + * \note The function is an alias for ProgramBuilder::updateProgram(). + */ + bool update() { return updateProgram(); } + + //! Finishes the definition of the logic program (or its current increment). + /*! + * Applies program mutating operations issued in the current step and transforms + * the new program into the solver's internal representation. + * + * \return false if the program is conflicting, true otherwise. + * + * \post + * - If true is returned, the program is considered to be "frozen" and calling + * program mutating functions is invalid until the next call to update(). + * - If false is returned, the state of the object is undefined and start() + * and dispose() are the only remaining valid operations. + * . + * \note The function is an alias for ProgramBuilder::endProgram(). + */ + bool end() { return endProgram(); } + + //! Visits the the simplified program by notifying out on its elements. + void accept(Potassco::AbstractProgram& out); + + //! Disposes (parts of) the internal representation of the logic program. + /*! + * \param forceFullDispose If set to true, the whole program is disposed. Otherwise, + * only the rules (of the current step) are disposed but atoms and any incremental + * control data remain. + */ + void dispose(bool forceFullDispose); + + //! Clones the program and adds it to the given ctx. + /* + * \pre The program is currently frozen. + */ + bool clone(SharedContext& ctx); + + //@} + + /*! + * \name Program mutating functions + * + * Functions in this group shall only be called if the program is currently not + * frozen. That is, only between the call to start() (resp. update() if in + * incremental setting) and end(). A std::logic_error is raised if this precondition is violated. + * + */ + //@{ + + //! Adds a new atom to the program and returns the new atom's id. + Atom_t newAtom(); + + //! Sets atomId as the last input atom of the current step. + /*! + * All (new or existing) atoms with a larger id than atomId + * are considered to be auxiliary and automatically removed before + * a new incremental step is started. + * + * \pre atomId >= startAtom() + * \post startAuxAtom() == atomId + 1 + */ + void setMaxInputAtom(uint32 atomId); + + //! Adds a new conjunctive condition to the program. + /*! + * \param cond A (possibly empty) list of atom literals. + * \return The id of the new condition, which can be later passed to + * extractCondition() or getLiteral(). + */ + Id_t newCondition(const Potassco::LitSpan& cond); + + //! Adds the given string to the problem's output table. + /*! + * \param str The string to add. + * \param cond The condition under which str should be considered part of a model. + */ + LogicProgram& addOutput(const ConstString& str, const Potassco::LitSpan& cond); + LogicProgram& addOutput(const ConstString& str, Id_t cond); + + //! Adds the given atoms to the set of projection variables. + LogicProgram& addProject(const Potassco::AtomSpan& atoms); + + //! Protects an otherwise undefined atom from preprocessing. + /*! + * If the atom is defined in this or a previous step, the operation has no effect. + * \note If atomId is not yet known, an atom with the given id is implicitly created. + * \note The second parameter defines the assumption that shall hold during solving, i.e. + * - posLit(atomId), if value is value_true, + * - negLit(atomId), if value is value_false, or + * - no assumption, if value is value_free. + * + * \see ProgramBuilder::getAssumptions(LitVec&) const; + */ + LogicProgram& freeze(Atom_t atomId, ValueRep value = value_false); + + //! Removes any protection from the given atom. + /*! + * If the atom is defined in this or a previous step, the operation has no effect. + * \note + * - The effect is logically equivalent to adding a rule atomId :- false. + * - A call to unfreeze() always overwrites a call to freeze() even if the + * latter comes after the former + * . + */ + LogicProgram& unfreeze(Atom_t atomId); + + //! Adds the given rule (or integrity constraint) to the program. + /*! + * \pre The the rule does not define an atom from a previous incremental step. + * + * Simplifies the given rule and adds it to the program if it + * is neither tautological (e.g. a :- a) nor contradictory (e.g. a :- b, not b). + * Atoms in the simplified rule that are not yet known are implicitly created. + * + * \throws RedefinitionError if the precondition is violated. + * \note If the head of the simplified rule mentions an atom from a previous step, + * that atom shall either be frozen or false. In the former case, + * unfreeze() is implicitly called. In the latter case, the rule is interpreted + * as an integrity constraint. + */ + LogicProgram& addRule(const Rule& rule); + LogicProgram& addRule(Head_t ht, const Potassco::AtomSpan& head, const Potassco::LitSpan& body); + LogicProgram& addRule(Head_t ht, const Potassco::AtomSpan& head, Potassco::Weight_t bound, const Potassco::WeightLitSpan& lits); + LogicProgram& addRule(Potassco::RuleBuilder& rb); + //! Adds the given minimize statement. + /*! + * \param prio The priority of the minimize statement. + * \param lits The literals to minimize. + * \note All minimize statements of the same priority are merged into one. + */ + LogicProgram& addMinimize(weight_t prio, const Potassco::WeightLitSpan& lits); + + //! Adds an edge to the extended (user-defined) dependency graph. + LogicProgram& addAcycEdge(uint32 n1, uint32 n2, const Potassco::LitSpan& condition) { return addAcycEdge(n1, n2, newCondition(condition)); } + LogicProgram& addAcycEdge(uint32 n1, uint32 n2, Id_t cond); + + //! Adds a conditional domain heuristic directive to the program. + LogicProgram& addDomHeuristic(Atom_t atom, DomModType t, int bias, unsigned prio, const Potassco::LitSpan& condition) { return addDomHeuristic(atom, t, bias, prio, newCondition(condition)); } + LogicProgram& addDomHeuristic(Atom_t atom, DomModType t, int bias, unsigned prio, Id_t cond); + //! Adds an unconditional domain heuristic directive to the program. + LogicProgram& addDomHeuristic(Atom_t atom, DomModType t, int bias, unsigned prio); + + //! Forces the given literals to be true during solving. + /*! + * Assumptions are retracted on the next program update. + */ + LogicProgram& addAssumption(const Potassco::LitSpan& cube); + + //! Adds or updates the given external atom. + /*! + * \see LogicProgram::freeze(Atom_t atomId, ValueRep value); + * \see LogicProgram::unfreeze(Atom_t atomId); + */ + LogicProgram& addExternal(Atom_t atomId, Potassco::Value_t value); + + //! Returns an object for adding theory data to this program. + TheoryData& theoryData(); + //@} + + /*! + * \name Query functions + * + * Functions in this group are useful to query important information + * once the program is frozen, i.e. after end() was called. + * They do not throw exceptions. + */ + //@{ + //! Returns whether the program can be represented in internal smodels format. + bool supportsSmodels() const; + //! Returns whether the program is to be defined incrementally. + bool isIncremental() const { return incData_ != 0; } + //! Returns whether the program contains any minimize statements. + bool hasMinimize() const { return !minimize_.empty(); } + //! Returns whether the program contains any theory data. + bool hasTheoryData() const { return theory_ != 0; } + //! Returns the number of atoms in the logic program. + uint32 numAtoms() const { return (uint32)atoms_.size()-1; } + //! Returns the number of bodies in the current (slice of the) logic program. + uint32 numBodies() const { return (uint32)bodies_.size(); } + //! Returns the number of disjunctive heads. + uint32 numDisjunctions() const { return (uint32)disjunctions_.size(); } + //! Returns the id of the first atom of the current step. + Atom_t startAtom() const { return input_.lo; } + //! Returns an id one past the last valid atom id in the program. + Atom_t endAtom() const { return numAtoms() + 1; } + //! Returns the id of the first atom that is not an input atom or endAtom() if no such atoms exists. + Atom_t startAuxAtom() const; + //! Returns whether a is an atom in the (simplified) program. + bool inProgram(Atom_t a) const; + //! Returns whether a is an external atom, i.e. is frozen in this step. + bool isExternal(Atom_t a) const; + //! Returns whether a occurs in the head of a rule. + bool isDefined(Atom_t a) const; + //! Returns whether a is a fact. + bool isFact(Atom_t a) const; + //! Returns the solver literal that is associated with the given atom or condition. + /*! + * \pre id is the id of a valid atom literal or was previously returned by newCondition(). + * \note Until end() is called, the function returns lit_false() for + * all atoms and conditions defined in the current step. + * \note For an atom literal x with Potassco::atom(x) == a, + * getLiteral(Potassco::id(x)) returns + * getLiteral(a), iff x == a, or + * ~getLiteral(a), iff x == -a. + * + * \note If mode is MapLit_t::Raw, the function simply returns the literal that + * was set during preprocessing. Otherwise, it also considers equivalences + * induced by domain heuristic directives and/or step-local true vars. + * + * \see enableDistinctTrue() + */ + Literal getLiteral(Id_t id, MapLit_t mode = MapLit_t::Raw) const; + //! Returns the atom literals belonging to the given condition. + /*! + * \pre cId was previously returned by newCondition() in the current step. + */ + bool extractCondition(Id_t cId, Potassco::LitVec& lits) const; + + + //! Maps the given unsat core of solver literals to original program assumptions. + /*! + * \param solverCore An unsat core found when solving under ProgramBuilder::getAssumptions(). + * \param prgLits The given unsat core expressed in terms of program literals. + * \return Whether unsatCore was successfully mapped. + */ + bool extractCore(const LitVec& unsatCore, Potassco::LitVec& prgLits) const; + + LpStats stats; //!< Statistics of the current step. + //@} + + /*! + * \name Implementation functions + * Low-level implementation functions. Use with care and only if you + * know what you are doing! + */ + //@{ + typedef VarVec::const_iterator VarIter; + typedef PrgHead*const* HeadIter; + typedef std::pair EdgeRange; + typedef std::pair HeadRange; + struct SRule { + SRule() : hash(0), pos(0), bid(varMax) {} + uint32 hash; // hash value of the body + uint32 pos; // positive size of body + uint32 bid; // id of existing body or varMax + }; + const AspOptions& options() const { return opts_; } + bool hasConflict() const { return getTrueAtom()->literal() != lit_true(); } + bool ok() const { return !hasConflict() && ProgramBuilder::ok(); } + PrgAtom* getAtom(Id_t atomId)const { return atoms_[atomId]; } + PrgHead* getHead(PrgEdge it) const { return it.isAtom() ? (PrgHead*)getAtom(it.node()) : (PrgHead*)getDisj(it.node()); } + PrgNode* getSupp(PrgEdge it) const { return it.isBody() ? (PrgNode*)getBody(it.node()) : (PrgNode*)getDisj(it.node()); } + Id_t getRootId(Id_t atom)const { return getEqNode(atoms_, atom); } + PrgAtom* getRootAtom(Id_t a) const { return getAtom(getRootId(a)); } + PrgBody* getBody(Id_t bodyId)const { return bodies_[bodyId]; } + Id_t getEqBody(Id_t b) const { return getEqNode(bodies_, b); } + PrgDisj* getDisj(Id_t disjId)const { return disjunctions_[disjId]; } + HeadIter disj_begin() const { return disjunctions_.empty() ? 0 : reinterpret_cast(&disjunctions_[0]); } + HeadIter disj_end() const { return disj_begin() + numDisjunctions(); } + HeadIter atom_begin() const { return reinterpret_cast(&atoms_[0]); } + HeadIter atom_end() const { return atom_begin() + (numAtoms()+1); } + VarIter unfreeze_begin() const { return incData_?incData_->unfreeze.begin() : propQ_.end(); } + VarIter unfreeze_end() const { return incData_?incData_->unfreeze.end() : propQ_.end(); } + bool validAtom(Id_t aId) const { return aId < (uint32)atoms_.size(); } + bool validBody(Id_t bId) const { return bId < numBodies(); } + bool validDisj(Id_t dId) const { return dId < numDisjunctions(); } + bool isFact(PrgAtom* a) const; + const char*findName(Atom_t x) const; + bool simplifyRule(const Rule& r, Potassco::RuleBuilder& out, SRule& meta); + Atom_t falseAtom(); + VarVec& getSupportedBodies(bool sorted); + uint32 update(PrgBody* b, uint32 oldHash, uint32 newHash); + bool assignValue(PrgAtom* a, ValueRep v, PrgEdge reason); + bool assignValue(PrgHead* h, ValueRep v, PrgEdge reason); + bool propagate(bool backprop); + PrgAtom* mergeEqAtoms(PrgAtom* a, Id_t rootAtom); + PrgBody* mergeEqBodies(PrgBody* b, Id_t rootBody, bool hashEq, bool atomsAssigned); + bool propagate() { return propagate(options().backprop != 0); } + void setConflict() { getTrueAtom()->setLiteral(lit_false()); } + AtomState& atomState() { return atomState_; } + void addMinimize(); + // ------------------------------------------------------------------------ + // Statistics + void incTrAux(uint32 n) { stats.auxAtoms += n; } + void incEqs(VarType t) { stats.incEqs(t); } + void upStat(RuleStats::Key k, int n = 1){ stats.rules[statsId_].up(k, n); } + void upStat(Body_t k, int n = 1) { stats.bodies[statsId_].up(k, n); } + void upStat(Head_t k, int n = 1) { stats.rules[statsId_].up(static_cast(unsigned(k)), n); } + // ------------------------------------------------------------------------ + //@} +private: + LogicProgram(const LogicProgram&); + LogicProgram& operator=(const LogicProgram&); + struct DlpTr; + struct AcycArc { Id_t cond; uint32 node[2]; }; + struct DomRule { uint32 atom : 29; uint32 type : 3; Id_t cond; int16 bias; uint16 prio; }; + struct Eq { Atom_t var; Literal lit; }; + struct TFilter { bool operator()(const Potassco::TheoryAtom& atom) const; LogicProgram* self; }; + struct Min { weight_t prio; Potassco::WLitVec lits; }; + struct CmpMin { bool operator()(const Min* m1, const Min* m2) const { return m1->prio < m2->prio; } }; + typedef Potassco::RuleBuilder RuleBuilder; + typedef std::pair ShowPair; + typedef PodVector::type ShowVec; + typedef PodVector::type DomRules; + typedef PodVector::type AcycRules; + typedef PodVector::type RuleList; + typedef PodVector::type MinList; + typedef PodVector::type SccMap; + typedef PodVector::type EqVec; + typedef POTASSCO_EXT_NS::unordered_multimap IndexMap; + typedef POTASSCO_EXT_NS::unordered_set IdSet; + typedef IndexMap::iterator IndexIter; + typedef std::pair IndexRange; + typedef Potassco::WLitVec LpWLitVec; + typedef Potassco::LitVec LpLitVec; + typedef Range AtomRange; + // ------------------------------------------------------------------------ + // virtual overrides + bool doStartProgram(); + bool doUpdateProgram(); + bool doEndProgram(); + void doGetAssumptions(LitVec& out) const; + ProgramParser* doCreateParser(); + int doType() const { return Problem_t::Asp; } + // ------------------------------------------------------------------------ + // Program definition + bool isNew(Atom_t atomId) const { return atomId >= startAtom(); } + PrgAtom* resize(Atom_t atomId); + void pushFrozen(PrgAtom* atom, ValueRep v); + void addRule(const Rule& r, const SRule& meta); + void addFact(const Potassco::AtomSpan& head); + void addIntegrity(const Rule& b, const SRule& meta); + bool handleNatively(const Rule& r) const; + bool transformNoAux(const Rule& r) const; + void freezeTheory(); + void transformExtended(); + void transformIntegrity(uint32 nAtoms, uint32 maxAux); + PrgBody* getBodyFor(const Rule& r, const SRule& m, bool addDeps = true); + PrgBody* getTrueBody(); + PrgDisj* getDisjFor(const Potassco::AtomSpan& heads, uint32 headHash); + PrgBody* assignBodyFor(const Rule& r, const SRule& m, EdgeType x, bool strongSimp); + bool equalLits(const PrgBody& b, const WeightLitSpan& lits) const; + bool simplifyNormal(Head_t ht, const Potassco::AtomSpan& head, const Potassco::LitSpan& body, RuleBuilder& out, SRule& info); + bool simplifySum(Head_t ht, const Potassco::AtomSpan& head, const Potassco::Sum_t& body, RuleBuilder& out, SRule& info); + bool pushHead(Head_t ht, const Potassco::AtomSpan& head, weight_t slack, RuleBuilder& out); + ValueRep litVal(const PrgAtom* a, bool pos) const; + uint32 findBody(uint32 hash, Body_t type, uint32 size, weight_t bound = -1, Potassco::WeightLit_t* wlits = 0); + uint32 findEqBody(const PrgBody* b, uint32 hash); + uint32 removeBody(PrgBody* b, uint32 oldHash); + Literal getEqAtomLit(Literal lit, const BodyList& supports, Preprocessor& p, const SccMap& x); + bool positiveLoopSafe(PrgBody* b, PrgBody* root) const; + void prepareExternals(); + void updateFrozenAtoms(); + void normalize(); + template + Id_t getEqNode(C& vec, Id_t id) const { + if (!vec[id]->eq()) return id; + PrgNode* n = vec[id], *r; + Id_t root = n->id(); + for (r = vec[root]; r->eq(); r = vec[root]) { + // n == r and r == r' -> n == r' + assert(root != r->id()); + n->setEq(root = r->id()); + } + return root; + } + bool checkBody(const PrgBody& rhs, Body_t type, uint32 size, weight_t bound) const { + return (rhs.relevant() || (rhs.eq() && getBody(getEqBody(rhs.id()))->relevant())) + && rhs.type() == type && rhs.size() == size && rhs.bound() == bound; + } + // ------------------------------------------------------------------------ + // Nogood creation + void prepareProgram(bool checkSccs); + void prepareOutputTable(); + void finalizeDisjunctions(Preprocessor& p, uint32 numSccs); + void prepareComponents(); + bool addConstraints(); + void addAcycConstraint(); + void addDomRules(); + void freezeAssumptions(); + // ------------------------------------------------------------------------ + void deleteAtoms(uint32 start); + PrgAtom* getTrueAtom() const { return atoms_[0]; } + RuleBuilder rule_; // temporary: active rule + AtomState atomState_; // which atoms appear in the active rule? + IndexMap bodyIndex_; // hash -> body id + IndexMap disjIndex_; // hash -> disjunction id + IndexMap domEqIndex_; // maps eq atoms modified by dom heuristic to aux vars + BodyList bodies_; // all bodies + AtomList atoms_; // all atoms + DisjList disjunctions_;// all (head) disjunctions + MinList minimize_; // list of minimize rules + RuleList extended_; // extended rules to be translated + ShowVec show_; // shown atoms/conditions + VarVec initialSupp_; // bodies that are (initially) supported + VarVec propQ_; // assigned atoms + VarVec frozen_; // list of frozen atoms + LpLitVec assume_; // set of assumptions + NonHcfSet nonHcfs_; // set of non-hcf sccs + TheoryData* theory_; // optional map of theory data + AtomRange input_; // input atoms of current step + int statsId_; // which stats to update (0 or 1) + struct Aux { + AtomList scc; // atoms that are strongly connected + DomRules dom; // list of domain heuristic directives + AcycRules acyc; // list of user-defined edges for acyclicity check + VarVec project; // atoms in projection directives + VarVec external; // atoms in external directives + IdSet skippedHeads; // heads of rules that have been removed during parsing + }* auxData_; // additional state for handling extended constructs + struct Incremental { + // first: last atom of step, second: true var + typedef std::pair StepTrue; + typedef PodVector::type TrueVec; + Incremental(); + uint32 startScc; // first valid scc number in this iteration + VarVec unfreeze; // list of atoms to unfreeze in this step + VarVec doms; // list of atom variables with domain modification + TrueVec steps; // map of steps to true var + }* incData_; // additional state for handling incrementally defined programs + AspOptions opts_; // preprocessing +}; +//! Returns the internal solver literal that is associated with the given atom literal. +/*! + * \pre The prg is frozen and atomLit is a known atom in prg. + */ +inline Literal solverLiteral(const LogicProgram& prg, Potassco::Lit_t atomLit) { + POTASSCO_REQUIRE(prg.frozen() && prg.validAtom(Potassco::atom(atomLit))); + return prg.getLiteral(Potassco::id(atomLit)); +} +//! Adapts a LogicProgram object to the Potassco::AbstractProgram interface. +class LogicProgramAdapter : public Potassco::AbstractProgram { +public: + LogicProgramAdapter(LogicProgram& prg); + void initProgram(bool inc); + void beginStep(); + void endStep(); + void rule(Potassco::Head_t ht, const Potassco::AtomSpan& head, const Potassco::LitSpan& body); + void rule(Potassco::Head_t ht, const Potassco::AtomSpan& head, Potassco::Weight_t bound, const Potassco::WeightLitSpan& body); + void minimize(Potassco::Weight_t prio, const Potassco::WeightLitSpan& lits); + void project(const Potassco::AtomSpan& atoms); + void output(const Potassco::StringSpan& str, const Potassco::LitSpan& cond); + void external(Potassco::Atom_t a, Potassco::Value_t v); + void assume(const Potassco::LitSpan& lits); + void heuristic(Potassco::Atom_t a, Potassco::Heuristic_t t, int bias, unsigned prio, const Potassco::LitSpan& cond); + void acycEdge(int s, int t, const Potassco::LitSpan& cond); + void theoryTerm(Potassco::Id_t termId, int number); + void theoryTerm(Potassco::Id_t termId, const Potassco::StringSpan& name); + void theoryTerm(Potassco::Id_t termId, int cId, const Potassco::IdSpan& args); + void theoryElement(Potassco::Id_t elementId, const Potassco::IdSpan& terms, const Potassco::LitSpan& cond); + void theoryAtom(Potassco::Id_t atomOrZero, Potassco::Id_t termId, const Potassco::IdSpan& elements); + void theoryAtom(Potassco::Id_t atomOrZero, Potassco::Id_t termId, const Potassco::IdSpan& elements, Potassco::Id_t op, Potassco::Id_t rhs); +protected: + Asp::LogicProgram* lp_; + bool inc_; +}; +//@} + +} } // end namespace Asp +#endif diff --git a/clasp/clasp/logic_program_types.h b/clasp/clasp/logic_program_types.h new file mode 100644 index 000000000..fb035cb6a --- /dev/null +++ b/clasp/clasp/logic_program_types.h @@ -0,0 +1,644 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +#ifndef CLASP_LOGIC_PROGRAM_TYPES_H_INCLUDED +#define CLASP_LOGIC_PROGRAM_TYPES_H_INCLUDED +#ifdef _MSC_VER +#pragma once +#endif +/*! + * \file + * \brief Basic types for working with a logic program. + */ +#include +#include +#include +#include +namespace Potassco { + typedef Clasp::PodVector::type LitVec; + typedef Clasp::PodVector::type WLitVec; +} +namespace Clasp { +class ClauseCreator; +using Potassco::idMax; +namespace Asp { +typedef PodVector::type AtomList; +typedef PodVector::type BodyList; +typedef PodVector::type DisjList; +const ValueRep value_weak_true = 3; /**< true but no proof */ +using Potassco::Atom_t; +using Potassco::Id_t; +/*! + * \addtogroup asp + */ +//@{ +//! A node of a program-dependency graph. +/*! + * A node represents a relevant part in a logic program. Each node + * has at least a literal and a value. + */ +class PrgNode { +public: + //! Supported node types. + enum Type { Atom = 0u, Body = 1u, Disj = 2u }; + static const uint32 noScc = (1u << 27)-1; + static const uint32 noNode = (1u << 28)-1; + static const uint32 noLit = 1; + //! Creates a new node that corresponds to a literal that is false. + explicit PrgNode(uint32 id, bool checkScc = true); + //! Is the node still relevant or removed() resp. eq()? + bool relevant() const { return eq_ == 0; } + //! Was the node removed? + bool removed() const { return eq_ != 0 && id_ == noNode; } + //! Ignore the node during scc checking? + bool ignoreScc()const { return noScc_ != 0; } + //! Returns true if this node is equivalent to some other node. + /*! + * If eq() is true, the node is no longer relevant and must not be used any further. + * The only sensible operation is to call id() in order to get the id of the node + * that is equivalent to this node. + */ + bool eq() const { return eq_ != 0 && id_ != noNode; } + bool seen() const { return seen_ != 0; } + //! Returns true if node has an associated variable in a solver. + bool hasVar() const { return litId_ != noLit; } + //! Returns the variable associated with this node or sentVar if no var is associated with this node. + Var var() const { return litId_ >> 1; } + //! Returns the literal associated with this node or a sentinel literal if no var is associated with this node. + Literal literal() const { return Literal::fromId(litId_); } + //! Returns the value currently assigned to this node. + ValueRep value() const { return val_; } + //! Returns the current id of this node. + uint32 id() const { return id_; } + //! Returns the literal that must be true in order to fulfill the truth-value of this node. + Literal trueLit() const { + return value() == value_free + ? lit_true() + : literal() ^ (value() == value_false); + } + + /*! + * \name implementation functions + * Low-level implementation functions. Use with care and only if you + * know what you are doing! + */ + //@{ + void setLiteral(Literal x) { litId_ = x.id(); } + void clearLiteral(bool clVal){ litId_ = noLit; if (clVal) val_ = value_free; } + void setValue(ValueRep v) { val_ = v; } + void setEq(uint32 eqId) { id_ = eqId; eq_ = 1; seen_ = 1; } + void setIgnoreScc(bool b) { noScc_ = (uint32)b; } + void markRemoved() { if (!eq()) setEq(noNode); } + void setSeen(bool b) { seen_ = uint32(b); } + void resetId(uint32 id, bool seen) { + id_ = id; + eq_ = 0; + seen_ = (uint32)seen; + } + bool assignValueImpl(ValueRep v, bool noWeak) { + if (v == value_weak_true && noWeak) { v = value_true; } + if (value() == value_free || v == value() || (value() == value_weak_true && v == value_true)) { + setValue(v); + return true; + } + return v == value_weak_true && value() == value_true; + } + //@} +protected: + uint32 litId_ : 31; // literal-id in solver + uint32 noScc_ : 1; // ignore during scc checks? + uint32 id_ : 28; // own id/eq-id/root-id/ufs-id + uint32 val_ : 2; // assigned value + uint32 eq_ : 1; // removed or eq to some other node? + uint32 seen_ : 1; // marked as seen? +private: + PrgNode(const PrgNode&); + PrgNode& operator=(const PrgNode&); +}; +typedef PrgNode::Type NodeType; +//! An edge of a program-dependency graph. +/*! + * Currently, clasp distinguishes four types of edges: + * - a Normal edge stipulates an implication between body and head, + * i.e. tableau-rules FTA and BFA for atoms. + * - a Choice edge only stipulates a support. + * - a Gamma edge is like a Normal edge that is only considered during + * nogood creation but ignored in the dependency graph. + * - a GammaChoice edge is like a Gamma edge but only stipulates a support. + * The head of a rule is either an atom or a disjunction. + */ +struct PrgEdge { + //! Type of edge. + enum Type { Normal = 0, Gamma = 1, Choice = 2, GammaChoice = 3 }; + static PrgEdge noEdge() { PrgEdge x; x.rep = UINT32_MAX; return x; } + + template + static PrgEdge newEdge(const NT& n, Type eType) { + // 28-bit node id, 2-bit node type, 2-bit edge type + PrgEdge x = { (n.id() << 4) | (static_cast(n.nodeType()) << 2) | eType }; + return x; + } + //! Returns the id of the adjacent node. + uint32 node() const { return rep >> 4; } + //! Returns the type of this edge. + Type type() const { return Type(rep & 3u); } + //! Returns the type of adjacent node. + NodeType nodeType() const { return NodeType((rep >> 2) & 3u); } + //! Returns true if edge has normal semantic (normal edge or gamma edge). + bool isNormal() const { return (rep & 2u) == 0; } + //! Returns true if edge has choice semantic. + bool isChoice() const { return (rep & 2u) != 0; } + //! Returns true if the edge is a gamma (aux normal) edge. + bool isGamma() const { return (rep & 1u) != 0; } + //! Returns true if the adjacent node is a body. + bool isBody() const { return nodeType() == PrgNode::Body; } + //! Returns true if the adjacant node is an atom. + bool isAtom() const { return nodeType() == PrgNode::Atom; } + //! Returns true if the adjacent node is a disjunction. + bool isDisj() const { return nodeType() == PrgNode::Disj; } + bool operator< (PrgEdge rhs) const { return rep < rhs.rep; } + bool operator==(PrgEdge rhs) const { return rep == rhs.rep; } + uint32 rep; +}; + +typedef PrgEdge::Type EdgeType; +typedef const PrgEdge* EdgeIterator; +typedef bk_lib::pod_vector EdgeVec; +inline bool isChoice(EdgeType t) { return t >= PrgEdge::Choice; } + +using Potassco::Body_t; +using Potassco::Head_t; +using Potassco::WeightLitSpan; +typedef Potassco::Rule_t Rule; +//! A class for translating extended rules to normal rules. +class RuleTransform { +public: + //! Interface that must be implemented to get the result of a transformation. + struct ProgramAdapter { + virtual Atom_t newAtom() = 0; + virtual void addRule(const Rule& r) = 0; + protected: ~ProgramAdapter() {} + }; + //! Supported transformation strategies. + enum Strategy { strategy_default, strategy_no_aux, strategy_allow_aux }; + RuleTransform(ProgramAdapter& prg); + RuleTransform(LogicProgram& prg); + ~RuleTransform(); + //! Transforms the given (extended) rule to a set of normal rules. + uint32 transform(const Rule& r, Strategy s = strategy_default); +private: + RuleTransform(const RuleTransform&); + RuleTransform& operator=(const RuleTransform&); + struct Impl; + Impl* impl_; +}; + +//! A set of flags used during rule simplification. +class AtomState { +public: + static const uint8 pos_flag = 0x1u; //!< In positive body of active rule + static const uint8 neg_flag = 0x2u; //!< In negative body of active rule + static const uint8 head_flag = 0x4u; //!< In normal head of active rule + static const uint8 choice_flag = 0x8u; //!< In choice head of active rule + static const uint8 disj_flag = 0x10u;//!< In disjunctive head of active rule + static const uint8 rule_mask = 0x1Fu;//!< In active rule + static const uint8 fact_flag = 0x20u;//!< Atom is a fact (sticky) + static const uint8 false_flag = 0x40u;//!< Atom is false (sticky) + static const uint8 simp_mask = 0x7fu;//!< In active rule or assigned + static const uint8 dom_flag = 0x80u;//!< Var of atom is a dom var (sticky) + AtomState() {} + void swap(AtomState& o) { state_.swap(o.state_); } + //! Does t.node() appear in the head of the active rule? + bool inHead(PrgEdge t) const { return isSet(t.node(), headFlag(t)); } + bool inHead(Atom_t atom) const { return isSet(atom, head_flag); } + //! Does p appear in the body of the active rule? + bool inBody(Literal p) const { return isSet(p.var(), pos_flag+p.sign()); } + bool isSet(Var v, uint8 f) const { return v < state_.size() && (state_[v] & f) != 0; } + bool isFact(Var v) const { return isSet(v, fact_flag); } + //! Mark v as a head of the active rule. + void addToHead(Atom_t v) { set(v, head_flag); } + void addToHead(PrgEdge t) { set(t.node(), headFlag(t)); } + //! Mark p as a literal contained in the active rule. + void addToBody(Literal p) { set(p.var(), pos_flag+p.sign()); } + + void set(Var v, uint8 f) { grow(v); state_[v] |= f; } + void clear(Var v, uint8 f) { if (v < state_.size()) { state_[v] &= ~f; } } + void clearRule(Var v) { clear(v, rule_mask); } + void clearHead(PrgEdge t) { clear(t.node(), headFlag(t)); } + void clearBody(Literal p) { clear(p.var(), pos_flag+p.sign()); } + void resize(uint32 sz) { state_.resize(sz); } + + template + bool allMarked(IT first, IT last, uint8 f) const { + for (; first != last; ++first) { + if (!isSet(*first, f)) return false; + } + return true; + } + bool inBody(const Literal* first, const Literal* last) const { + bool all = true; + for (; first != last && (all = inBody(*first)) == true; ++first) { ; } + return all; + } +private: + typedef PodVector::type StateVec; + void grow(Var v) { if (v >= state_.size()) { state_.resize(v+1); } } + uint8 headFlag(PrgEdge t) const { + return t.isAtom() ? (head_flag << uint8(t.isChoice())) : disj_flag; + } + StateVec state_; +}; + +//! A head node of a program-dependency graph. +/*! + * A head node is either an atom or a disjunction + * and stores its possible supports. + */ +class PrgHead : public PrgNode { +public: + enum Simplify { no_simplify = 0, force_simplify = 1 }; + typedef EdgeIterator sup_iterator; + + //! Is the head part of the (simplified) program? + bool inUpper() const { return relevant() && upper_ != 0; } + //! Is this head an atom? + bool isAtom() const { return isAtom_ != 0; } + //! Number of supports (rules) for this head. + uint32 supports() const { return supports_.size(); } + sup_iterator supps_begin()const { return supports_.begin(); } + sup_iterator supps_end() const { return supports_.end(); } + //! External atom (or defined in a later incremental step)? + bool frozen() const { return freeze_ != uint32(freeze_no); } + //! If frozen(), value to assume during solving. + ValueRep freezeValue()const { return static_cast(freeze_ - uint32(freeze_ != 0)); } + //! If frozen(), literal to assume during solving. + Literal assumption() const { return freeze_ > uint32(freeze_free) ? literal() ^ (freeze_ == freeze_false) : lit_true(); } + //! Adds r as support edge for this node. + void addSupport(PrgEdge r){ addSupport(r, force_simplify); } + void addSupport(PrgEdge r, Simplify s); + //! Removes r from the head's list of supports. + void removeSupport(PrgEdge r); + void clearSupports(); + void clearSupports(EdgeVec& to) { to.swap(supports_); clearSupports(); } + //! Removes any superfluous/irrelevant supports. + bool simplifySupports(LogicProgram& prg, bool strong, uint32* numDiffSupps = 0); + //! Assigns the value v to this head. + bool assignValue(ValueRep v) { return assignValueImpl(v, ignoreScc() && !frozen()); } + /*! + * \name implementation functions + * Low-level implementation functions. Use with care and only if you + * know what you are doing! + */ + //@{ + void setInUpper(bool b) { upper_ = (uint32)b; } + void markDirty() { dirty_ = 1; } + void assignVar(LogicProgram& prg, PrgEdge it, bool allowEq = true); + NodeType nodeType() const { return isAtom() ? PrgNode::Atom : PrgNode::Disj; } + //@} +protected: + enum FreezeState { freeze_no = 0u, freeze_free = 1u, freeze_true = 2u, freeze_false = 3u }; + //! Creates a new node that corresponds to a literal that is false. + explicit PrgHead(uint32 id, NodeType t, uint32 data = 0, bool checkScc = true); + bool backpropagate(LogicProgram& prg, ValueRep val, bool bpFull); + EdgeVec supports_; // possible supports (body or disjunction) + uint32 data_ : 27; // number of atoms in disjunction or scc of atom + uint32 upper_ : 1; // in (simplified) program? + uint32 dirty_ : 1; // is list of supports dirty? + uint32 freeze_: 2; // incremental freeze state + uint32 isAtom_: 1; // is this head an atom? +}; + +//! An atom in a logic program. +/*! + * An atom stores the list of bodies depending on it. + * Furthermore, once strongly-connected components are identified, + * atoms store their SCC-number. All trivial SCCs are represented + * with the special SCC-number PrgNode::noScc. + */ +class PrgAtom : public PrgHead { +public: + enum Dependency { dep_pos = 0, dep_neg = 1, dep_all = 2 }; + typedef LitVec::const_iterator dep_iterator; + explicit PrgAtom(uint32 id, bool checkScc = true); + NodeType nodeType() const { return PrgNode::Atom; } + //! Strongly connected component of this node. + uint32 scc() const { return data_; } + //! If eq(), stores the literal that is eq to this atom. + Literal eqGoal(bool sign) const; + //! Returns true if atom belongs to a disjunctive head. + bool inDisj() const; + /*! + * \name forward dependencies (bodies containing this atom) + */ + //@{ + dep_iterator deps_begin() const { return deps_.begin(); } + dep_iterator deps_end() const { return deps_.end(); } + bool hasDep(Dependency d) const; + void addDep(Id_t bodyId, bool pos); + void removeDep(Id_t bodyId, bool pos); + void clearDeps(Dependency d); + //@} + + /*! + * \name implementation functions + * Low-level implementation functions. Use with care and only if you + * know what you are doing! + */ + //@{ + void setEqGoal(Literal x); + bool propagateValue(LogicProgram& prg, bool backprop); + bool addConstraints(const LogicProgram& prg, ClauseCreator& c); + void setScc(uint32 scc) { data_ = scc; } + void markFrozen(ValueRep v){ freeze_ = v + freeze_free; } + void clearFrozen() { freeze_ = freeze_no; markDirty(); } + //@} +private: + LitVec deps_; // bodies depending on this atom +}; + +//! A (rule) body in a logic program. +class PrgBody : public PrgNode { +public: + typedef EdgeIterator head_iterator; + typedef const Literal* goal_iterator; + + //! Creates a new body node and (optionally) connects it to its predecessors (i.e. atoms). + /*! + * \param prg The program in which the new body is used. + * \param id The id for the new body node. + * \param rule The rule for which a body node is to be created. + * \param pos Positive body size. + * \param addDeps If true, add an edge between each atom subgoal and the new node. + */ + static PrgBody* create(LogicProgram& prg, uint32 id, const Rule& rule, uint32 pos, bool addDeps); + //! Destroys a body node created via create(). + void destroy(); + Body_t type() const { return Body_t(static_cast(type_)); } + //! Returns the number of atoms in the body. + uint32 size() const { return size_; } + bool noScc() const { return size() == 0 || goal(0).sign(); } + //! Returns the bound of this body, or size() if the body is a normal body. + weight_t bound() const { if (type() == Body_t::Normal) return (weight_t)size(); else return hasWeights() ? sumData()->bound : aggData().bound; } + //! Returns the sum of the subgoals weights, or size() if the body is not a sum with weights. + weight_t sumW() const { return static_cast(!hasWeights() ? (weight_t)size() : sumData()->sumW); } + //! Returns the idx'th subgoal as a literal. + Literal goal(uint32 idx) const { assert(idx < size()); return *(goals_begin()+idx); } + //! Returns the weight of the idx'th subgoal. + weight_t weight(uint32 idx)const { assert(idx < size()); return !hasWeights() ? 1 : sumData()->weights[idx]; } + //! Returns true if the body node is supported. + /*! + * A normal body is supported, iff all of its positive subgoals are supported. + * A count/sum body is supported if the sum of the weights of the supported positive + + * the sum of the negative weights is >= lowerBound(). + */ + bool isSupported() const { return unsupp_ <= 0; } + //! Returns true if this body defines any head. + bool hasHeads() const { return isSmallHead() ? head_ != 0 : !largeHead()->empty(); } + bool inRule() const { return hasHeads() || freeze_; } + + head_iterator heads_begin() const { return isSmallHead() ? smallHead() : largeHead()->begin(); } + head_iterator heads_end() const { return isSmallHead() ? smallHead()+head_ : largeHead()->end(); } + goal_iterator goals_begin() const { return const_cast(this)->goals_begin(); } + goal_iterator goals_end() const { return goals_begin() + size(); } + //! Adds a rule edge between this body and the given head. + /*! + * \note + * The function also adds a corresponding back edge to the head. + * \note + * Adding a head invalidates the set property for the heads of this body. + * To restore it, call simplifyHeads() + */ + void addHead(PrgHead* h, EdgeType t); + //! Simplifies the heads of this body and establishes set property. + /*! + * Removes superfluous heads and sets the body to false if for some atom a + * in the head of this body B, Ta -> FB. In that case, all heads atoms are removed, because + * a false body can't define any atom. + * If strong is true, removes head atoms that are not associated with a variable. + * \return + * setValue(value_false) if setting a head of this body to true would + * make the body false (i.e. the body is a selfblocker). Otherwise, true. + */ + bool simplifyHeads(LogicProgram& prg, bool strong); + bool mergeHeads(LogicProgram& prg, PrgBody& heads, bool strong, bool simplify = true); + void removeHead(PrgHead* h, EdgeType t); + bool hasHead(PrgHead* h, EdgeType t) const; + //! Simplifies the body, i.e. its predecessors-lists. + /*! + * - removes true/false atoms from B+/B- resp. + * - removes/merges duplicate subgoals + * - checks whether body must be false (e.g. contains false/true atoms in B+/B- resp. or contains p and ~p) + * + * \pre prg.getBody(id()) == this + * + * \param[in] prg The program containing this body. + * \param[in] strong If true, treats atoms that have no variable associated as false. + * \param[out] eqId The id of a body in prg that is equivalent to this body. + * + * \return + * - true if simplification was successful + * - false if simplification detected a conflict + */ + bool simplifyBody(LogicProgram& prg, bool strong, uint32* eqId = 0); + //! Calls simplifyBody() and/or simplifyHeads() if necessary. + bool simplify(LogicProgram& prg, bool strong, uint32* eqId = 0) { + return simplifyBody(prg, strong, eqId) && simplifyHeads(prg, strong); + } + bool toData(const LogicProgram& prg, Potassco::RuleBuilder& out) const; + //! Notifies the body node about the fact that its positive subgoal v is supported. + /*! + * \return true if the body is now also supported, false otherwise. + */ + bool propagateSupported(Var /* v */); + //! Propagates the assignment of subgoal p. + bool propagateAssigned(LogicProgram& prg, Literal p, ValueRep v); + //! Propagates the assignment of a head. + bool propagateAssigned(LogicProgram& prg, PrgHead* h, EdgeType t); + //! Propagates the value of this body. + bool propagateValue(LogicProgram& prg, bool backprop); + bool propagateValue(LogicProgram& prg); + bool addConstraints(const LogicProgram& prg, ClauseCreator& c); + void markDirty() { sBody_ = 1; } + void markHeadsDirty() { sHead_ = 1; } + void markFrozen() { freeze_= 1; } + void clearHeads(); + bool resetSupported(); + void assignVar(LogicProgram& prg); + bool assignValue(ValueRep v) { return assignValueImpl(v, noScc()); } + uint32 scc(const LogicProgram& prg) const; + bool hasWeights() const { return type() == Body_t::Sum; } + void clearRule(AtomState& rs) const { + for (head_iterator it = heads_begin(), end = heads_end(); it != end; ++it) { + rs.clearRule(it->node()); + } + for (const Literal* it = goals_begin(), *end = it + size(); it != end; ++it) { + rs.clearRule(it->var()); + } + } + NodeType nodeType() const { return PrgNode::Body; } +private: + static const uint32 maxSize = (1u<<26)-1; + typedef unsigned char byte_t; +POTASSCO_WARNING_BEGIN_RELAXED + struct SumData { + enum { LIT_OFFSET = sizeof(void*)/sizeof(uint32) }; + static SumData* create(uint32 size, weight_t bnd, weight_t ws); + void destroy(); + weight_t bound; + weight_t sumW; + weight_t weights[0]; + }; + struct Agg { + union { + SumData* sum; + weight_t bound; + }; + Literal lits[0]; + }; + struct Norm { Literal lits[0]; }; + PrgBody(uint32 id, LogicProgram& prg, const Potassco::LitSpan& lits, uint32 pos, bool addDeps); + PrgBody(uint32 id, LogicProgram& prg, const Potassco::Sum_t& sum, bool hasWeights, uint32 pos, bool addDeps); + void init(Body_t t, uint32 sz); + ~PrgBody(); + uint32 findLit(const LogicProgram& prg, Literal p) const; + bool normalize(const LogicProgram& prg, weight_t bound, weight_t sumW, weight_t reachW, uint32& hashOut); + void prepareSimplifyHeads(LogicProgram& prg, AtomState& rs); + bool simplifyHeadsImpl(LogicProgram& prg, PrgBody& target, AtomState& rs, bool strong); + bool superfluousHead(const LogicProgram& prg, const PrgHead* head, PrgEdge it, const AtomState& rs) const; + bool blockedHead(PrgEdge it, const AtomState& rs) const; + void addHead(PrgEdge h); + bool eraseHead(PrgEdge h); + bool isSmallHead() const { return head_ != 3u; } + byte_t* data() const { return const_cast(static_cast(data_)); } + PrgEdge* smallHead() const { return const_cast(headData_.sm); } + EdgeVec* largeHead() const { return headData_.ext; } + SumData* sumData() const { return aggData().sum;} + Agg& aggData() const { return *reinterpret_cast(data()); } + Literal* goals_begin() { return type() == Body_t::Normal ? reinterpret_cast(data())->lits : aggData().lits; } + Literal* goals_end() { return goals_begin() + size(); } + + uint32 size_ : 25; // |B| + uint32 head_ : 2; // simple or extended head? + uint32 type_ : 2; // body type + uint32 sBody_ : 1; // simplify body? + uint32 sHead_ : 1; // simplify head? + uint32 freeze_ : 1; // keep body even if it does not occur in a rule? + weight_t unsupp_; // <= 0 -> body is supported + union Head { + PrgEdge sm[2]; + EdgeVec* ext; + } headData_; // successors of this body + byte_t data_[0]; // empty or one of Agg|Norm +POTASSCO_WARNING_END_RELAXED +}; +//! The head of a disjunctive rule. +class PrgDisj : public PrgHead { +public: + typedef const Var* atom_iterator; + //! Constructor for disjunctions. + static PrgDisj* create(uint32 id, const Potassco::AtomSpan& head); + //! Destroys a disjunction created via create(). + void destroy(); + void detach(LogicProgram& prg); + //! Number of atoms in disjunction. + uint32 size() const { return data_; } + atom_iterator begin() const { return atoms_; } + atom_iterator end() const { return atoms_ + size(); } + //! Propagates the assignment of an atom in this disjunction. + bool propagateAssigned(LogicProgram& prg, PrgHead* at, EdgeType t); +private: + explicit PrgDisj(uint32 id, const Potassco::AtomSpan& head); + ~PrgDisj(); +POTASSCO_WARNING_BEGIN_RELAXED + Var atoms_[0]; // atoms in disjunction +POTASSCO_WARNING_END_RELAXED +}; + +inline ValueRep getMergeValue(const PrgNode* lhs, const PrgNode* rhs) { + return static_cast(std::min(static_cast(lhs->value()-1), static_cast(rhs->value()-1)) + 1); +} + +template +bool mergeValue(NT* lhs, NT* rhs){ + ValueRep mv = getMergeValue(lhs, rhs); + return (lhs->value() == mv || lhs->assignValue(mv)) + && (rhs->value() == mv || rhs->assignValue(mv)); +} + +//! A class for computing strongly connected components of the positive atom-body dependency graph. +class SccChecker { +public: + SccChecker(LogicProgram& prg, AtomList& sccAtoms, uint32 startScc); + uint32 sccs() const { return sccs_; } + void visit(PrgBody* body) { visitDfs(body, PrgNode::Body); } + void visit(PrgAtom* atom) { visitDfs(atom, PrgNode::Atom); } + void visit(PrgDisj* disj) { visitDfs(disj, PrgNode::Disj); } +private: + struct Call { + uintp node; + uint32 min; + uint32 next; + }; + typedef PodVector::type CallStack; + typedef PodVector::type NodeStack; + static uintp packNode(PrgNode* n, NodeType t) { return reinterpret_cast(n) + uintp(t); } + static PrgNode* unpackNode(uintp n) { return reinterpret_cast(n & ~uintp(3u));} + static bool isNode(uintp n, NodeType t) { return (n & 3u) == uintp(t); } + bool doVisit(PrgNode* n, bool seen = true) const { return !n->ignoreScc() && n->relevant() && n->hasVar() && (!seen || !n->seen()); } + void visitDfs(PrgNode* n, NodeType t); + bool recurse(Call& c); + bool onNode(PrgNode* n, NodeType t, Call& c, uint32 data); + void addCall(PrgNode* n, NodeType t, uint32 next, uint32 min = 0) { + Call c = {packNode(n, t), min, next}; + callStack_.push_back(c); + } + CallStack callStack_; + NodeStack nodeStack_; + LogicProgram* prg_; + AtomList* sccAtoms_; + uint32 count_; + uint32 sccs_; +}; +//! A set of ids of strongly connected components having at least one head-cycle. +struct NonHcfSet : private PodVector::type { +public: + typedef PodVector::type base_type; + typedef base_type::const_iterator const_iterator; + using base_type::begin; + using base_type::end; + using base_type::size; + NonHcfSet() : config(0) {} + void add(uint32 scc) { + iterator it = std::lower_bound(begin(), end(), scc); + if (it == end() || *it != scc) { insert(it, scc); } + } + bool find(uint32 scc) const { + const_iterator it = scc != PrgNode::noScc ? std::lower_bound(begin(), end(), scc) : end(); + return it != end() && *it == scc; + } + Configuration* config; +}; +//@} +} } +#endif diff --git a/clasp/clasp/lookahead.h b/clasp/clasp/lookahead.h new file mode 100644 index 000000000..361805ba8 --- /dev/null +++ b/clasp/clasp/lookahead.h @@ -0,0 +1,243 @@ +// +// Copyright (c) 2009-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_LOOKAHEAD_H_INCLUDED +#define CLASP_LOOKAHEAD_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif + +/*! + * \file + * \brief Defines lookahead related types. + * + * Lookahead can be used as a post propagator (e.g. failed-literal detection) and + * optionally as an heuristic. + */ + +#include +#include +namespace Clasp { +/*! + * \addtogroup propagator + */ +//@{ + +//! Type used to store lookahead-information for one variable. +struct VarScore { + VarScore() { clear(); } + void clear() { std::memset(this, 0, sizeof(VarScore)); } + //! Mark literal p as dependent. + void setSeen( Literal p ) { seen_ |= uint32(p.sign()) + 1; } + //! Is literal p dependent? + bool seen(Literal p) const { return (seen_ & (uint32(p.sign())+1)) != 0; } + //! Is this var dependent? + bool seen() const { return seen_ != 0; } + //! Mark literal p as tested during lookahead. + void setTested( Literal p ) { tested_ |= uint32(p.sign()) + 1; } + //! Was literal p tested during lookahead? + bool tested(Literal p) const { return (tested_ & (uint32(p.sign())+1)) != 0; } + //! Was some literal of this var tested? + bool tested() const { return tested_ != 0; } + //! Were both literals of this var tested? + bool testedBoth() const { return tested_ == 3; } + + //! Sets the score for literal p to value and marks p as tested. + void setScore(Literal p, LitVec::size_type value) { + if (value > (1U<<14)-1) value = (1U<<14)-1; + if (p.sign()) nVal_ = uint32(value); + else pVal_ = uint32(value); + setTested(p); + } + //! Sets the score of a dependent literal p to min(sc, current score). + void setDepScore(Literal p, uint32 sc) { + if (!seen(p) || score(p) > sc) { + if (sc > (1U<<14)-1) sc = (1U<<14)-1; + if (p.sign()) nVal_ = std::min(uint32(nVal_-(nVal_==0)), sc); + else pVal_ = std::min(uint32(pVal_-(pVal_==0)), sc); + } + } + //! Returns the score for literal p. + uint32 score(Literal p) const { return p.sign() ? nVal_ : pVal_; } + //! Returns the scores of the two literals of a variable. + /*! + * \param[out] mx The maximum score. + * \param[out] mn The minimum score. + */ + void score(uint32& mx, uint32& mn) const { + if (nVal_ > pVal_) { + mx = nVal_; + mn = pVal_; + } + else { + mx = pVal_; + mn = nVal_; + } + } + //! Returns the sign of the literal that has the higher score. + bool prefSign() const { return nVal_ > pVal_; } + + uint32 nVal() const { return nVal_; } + uint32 pVal() const { return pVal_; } +private: + uint32 pVal_ : 14; + uint32 nVal_ : 14; + uint32 seen_ : 2; + uint32 tested_: 2; +}; + +//! A small helper class used to score the result of a lookahead operation. +struct ScoreLook { + enum Mode { score_max, score_max_min }; + typedef PodVector::type VarScores; /**< A vector of variable-scores */ + ScoreLook() : best(0), mode(score_max), addDeps(true), nant(false) {} + bool validVar(Var v) const { return v < score.size(); } + void scoreLits(const Solver& s, const Literal* b, const Literal *e); + void clearDeps(); + uint32 countNant(const Solver& s, const Literal* b, const Literal *e) const; + bool greater(Var lhs, Var rhs)const; + bool greaterMax(Var x, uint32 max) const { + return score[x].nVal() > max || score[x].pVal() > max; + } + bool greaterMaxMin(Var lhs, uint32 max, uint32 min) const { + uint32 lhsMin, lhsMax; + score[lhs].score(lhsMax, lhsMin); + return lhsMin > min || (lhsMin == min && lhsMax > max); + } + VarScores score; //!< score[v] stores lookahead score of v + VarVec deps; //!< Tested vars and those that follow from them. + VarType types; //!< Var types to consider. + Var best; //!< Var with best score among those in deps. + Mode mode; //!< Score mode to apply. + bool addDeps;//!< Add/score dependent vars? + bool nant; //!< Score only atoms in NegAnte(P)? +}; + +class UnitHeuristic; + +//! Lookahead extends propagation with failed-literal detection. +/*! + * The class provides different kinds of one-step lookahead. + * Atom- and body-lookahead are uniform lookahead types, where + * either only atoms or bodies are tested. Hybrid-lookahead tests + * both types of vars but each only in a single phase. I.e. atoms + * are only tested negatively while bodies are tested positively. + */ +class Lookahead : public PostPropagator { +public: + //! Set of parameters to configure lookahead. + struct Params { + Params(VarType t = Var_t::Atom) : type(t), lim(0), topLevelImps(true), restrictNant(false) {} + Params& lookahead(VarType t){ type = t; return *this; } + Params& addImps(bool b) { topLevelImps = b; return *this; } + Params& nant(bool b) { restrictNant = b; return *this; } + Params& limit(uint32 x) { lim = x; return *this; } + VarType type; + uint32 lim; + bool topLevelImps; + bool restrictNant; + }; + static bool isType(uint32 t) { return t != 0 && t <= Var_t::Hybrid; } + /*! + * \param p Lookahead parameters to use. + */ + explicit Lookahead(const Params& p); + ~Lookahead(); + + bool init(Solver& s); + //! Clears the lookahead list. + void clear(); + //! Returns true if lookahead list is empty. + bool empty() const { return head()->next == head_id; } + //! Adds literal p to the lookahead list. + void append(Literal p, bool testBoth); + //! Executes a single-step lookahead on all vars in the loookahead list. + bool propagateFixpoint(Solver& s, PostPropagator*); + //! Returns PostPropagator::priority_reserved_look. + uint32 priority() const; + void destroy(Solver* s, bool detach); + ScoreLook score; //!< State of last lookahead operation. + //! Returns "best" literal w.r.t scoring of last lookahead or lit_true() if no such literal exists. + Literal heuristic(Solver& s); + void detach(Solver& s); + bool hasLimit() const { return limit_ != 0; } +protected: + bool propagateLevel(Solver& s); // called by propagate + void undoLevel(Solver& s); + bool test(Solver& s, Literal p); +private: + typedef uint32 NodeId; + enum { head_id = NodeId(0), undo_id = NodeId(1) }; + struct LitNode { + LitNode(Literal x) : lit(x), next(UINT32_MAX) {} + Literal lit; + NodeId next; + }; + typedef PodVector::type UndoStack; + typedef PodVector::type LookList; + typedef UnitHeuristic* HeuPtr; + void splice(NodeId n); + LitNode* node(NodeId n) { return &nodes_[n]; } + LitNode* head() { return &nodes_[head_id]; } // head of circular candidate list + LitNode* undo() { return &nodes_[undo_id]; } // head of undo list + bool checkImps(Solver& s, Literal p); + const LitNode* head() const { return &nodes_[head_id]; } + LookList nodes_; // list of literals to test + UndoStack saved_; // stack of undo lists + LitVec imps_; // additional top-level implications + NodeId last_; // last candidate in list; invariant: node(last_)->next == head_id; + NodeId pos_; // current lookahead start position + uint32 top_; // size of top-level + uint32 limit_; // stop lookahead after this number of applications +}; +//@} + +//! Heuristic that uses the results of lookahead. +/*! + * \ingroup heuristic + * The heuristic uses a Lookahead post propagator to select a literal with the highest score, + * where the score is determined by counting assignments made during + * failed-literal detection. For hybrid_lookahead, the heuristic selects the literal that + * derived the most literals. On the other hand, for uniform_lookahead the heuristic is similar to + * the smodels lookahead heuristic and selects the literal that maximizes the minimum. + * \see Patrik Simons: "Extending and Implementing the Stable Model Semantics" for a + * detailed description of the lookahead heuristic. + * + * \note The heuristic might itself apply some lookahead but only on variables that + * did not fail in a previous call to Lookahead::propagateFixpoint(). I.e. if + * priorities are correct for all post propagators in s, the lookahead operations can't fail. + * + * \note If no Lookahead post propagator exists in the solver, the heuristic selects the first free variable! + */ +class UnitHeuristic : public SelectFirst { +public: + UnitHeuristic(); + //! Decorates the heuristic given in other with temporary lookahead. + static UnitHeuristic* restricted(DecisionHeuristic* other); + void endInit(Solver& /* s */); + Literal doSelect(Solver& s); +}; + +} +#endif diff --git a/clasp/clasp/minimize_constraint.h b/clasp/clasp/minimize_constraint.h new file mode 100644 index 000000000..4d1cac857 --- /dev/null +++ b/clasp/clasp/minimize_constraint.h @@ -0,0 +1,583 @@ +// +// Copyright (c) 2010-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_MINIMIZE_CONSTRAINT_H_INCLUDED +#define CLASP_MINIMIZE_CONSTRAINT_H_INCLUDED +#ifdef _MSC_VER +#pragma once +#endif +/*! +* \file +* \brief Types and functions for implementing minimization constraints. +*/ +#include +#include +#include + +namespace Clasp { +class MinimizeConstraint; +class WeightConstraint; + +//! Supported minimization modes. +/*! + * \ingroup constraint + * Defines the possible minimization modes used during solving. + * If optimize is used, a valid candidate model is a solution that is + * strictly smaller than all previous solutions. Otherwise, + * solutions with costs no greater than a fixed bound are considered valid. + */ +struct MinimizeMode_t { + enum Mode { + ignore = 0, //!< Ignore optimize statements during solving. + optimize = 1, //!< Optimize via a decreasing bound. + enumerate = 2, //!< Enumerate models with cost less or equal to a fixed bound. + enumOpt = 3, //!< Enumerate models with cost equal to optimum. + }; +}; +typedef MinimizeMode_t::Mode MinimizeMode; + +//! A type holding data (possibly) shared between a set of minimize constraints. +/*! + * \ingroup shared_con + */ +class SharedMinimizeData { +public: + typedef SharedMinimizeData ThisType; + //! A type to represent a weight at a certain level. + /*! + * Objects of this type are used to create sparse vectors of weights. E.g. + * a weight vector (w1\@L1, w2\@L3, w3\@L5) is represented as \[\\\\], + * where each \-tuple is an object of type LevelWeight. + */ + struct LevelWeight { + LevelWeight(uint32 l, weight_t w) : level(l), next(0), weight(w) {} + uint32 level : 31; //!< The level of this weight. + uint32 next : 1; //!< Does this weight belong to a sparse vector of weights? + weight_t weight; //!< The weight at this level. + }; + //! A type for holding sparse vectors of level weights of a multi-level constraint. + typedef PodVector::type WeightVec; + typedef PodVector::type PrioVec; + explicit SharedMinimizeData(const SumVec& lhsAdjust, MinimizeMode m = MinimizeMode_t::optimize); + //! Increases the reference count of this object. + ThisType* share() { ++count_; return this; } + //! Decreases the object's reference count and destroys it if reference count drops to 0. + void release() { if (--count_ == 0) destroy(); } + //! Number of minimize statements contained in this constraint. + uint32 numRules() const{ return static_cast(adjust_.size()); } + uint32 maxLevel() const{ return numRules()-1; } + static wsum_t maxBound() { return INT64_MAX; } + //! Returns the active minimization mode. + MinimizeMode mode() const{ return mode_; } + //! Returns true if optimization is active. + bool optimize() const{ return optGen_ ? checkNext() : mode_ != MinimizeMode_t::enumerate; } + //! Returns the lower bound of level x. + wsum_t lower(uint32 x) const; + //! Returns the upper bound of level x. + wsum_t upper(uint32 x) const{ return upper()[x]; } + const wsum_t* upper() const{ return &(up_ + (gCount_ & 1u))->front(); } + //! Returns the sum of level x in the most recent model. + wsum_t sum(uint32 x) const{ return sum()[x]; } + const wsum_t* sum() const{ return (mode_ != MinimizeMode_t::enumerate) ? upper() : &up_[1][0]; } + //! Returns the adjustment for level x. + wsum_t adjust(uint32 x) const{ return adjust_[x]; } + const wsum_t* adjust() const{ return &adjust_[0]; } + //! Returns the current (ajusted and possibly tentative) optimum for level x. + wsum_t optimum(uint32 x)const; + //! Returns the highest level of the literal with the given index i. + uint32 level(uint32 i) const{ return numRules() == 1 ? 0 : weights[lits[i].second].level; } + //! Returns the most important weight of the literal with the given index i. + weight_t weight(uint32 i) const{ return numRules() == 1 ? lits[i].second : weights[lits[i].second].weight; } + uint32 generation() const{ return gCount_; } + //! Returns whether minimization should search for solutions with current or next smaller upper bound. + bool checkNext() const{ return mode() != MinimizeMode_t::enumerate && generation() != optGen_; } + /*! + * \name interface for optimization + * If not otherwise specified, the following functions shall not be called concurrently. + */ + //@{ + + //! Sets the enumeration mode and (optionally) an initial bound. + /*! + * \note If m is MinimizeMode::enumerate, the caller should always + * set a bound. Otherwise, *all* solutions are considered valid. + */ + bool setMode(MinimizeMode m, const wsum_t* bound = 0, uint32 boundSize = 0); + bool setMode(MinimizeMode m, const SumVec& bound) { return setMode(m, bound.empty() ? 0 : &bound[0], (uint32)bound.size()); } + void resetBounds(); + + //! Attaches a new minimize constraint to this data object. + /*! + * \param s Solver in which the new minimize constraint should apply. + * \param params Parameters to pass to the optimization strategy. + * \param addRef If true, the ref count of the shared object is increased. + * Otherwise, the new minimize constraint inherits the reference to the shared object. + */ + MinimizeConstraint* attach(Solver& s, const OptParams& params, bool addRef = true); + + //! Makes opt the new (tentative) optimum. + /*! + * \pre opt is a pointer to an array of size numRules() + */ + const SumVec* setOptimum(const wsum_t* opt); + //! Marks the current tentative optimum as the final optimum. + /*! + * \note Once a final optimum is set, further calls to setOptimum() + * are ignored until resetBounds() is called. + */ + void markOptimal(); + //! Sets the lower bound of level lev to low. + void setLower(uint32 lev, wsum_t low); + //! Sets the lower bound of level lev to the maximum of low and the existing value lower(lev). + /*! + * \note This function is thread-safe, i.e., can be called safely from multiple threads. + */ + wsum_t incLower(uint32 lev, wsum_t low); + //@} + + /*! + * \name Arithmetic functions on weights. + */ + //@{ + //! Computes lhs += weight(lit). + void add(wsum_t* lhs, const WeightLiteral& lit) const { if (weights.empty()) *lhs += lit.second; else add(lhs, &weights[lit.second]); } + void add(wsum_t* lhs, const LevelWeight* w) const { do { lhs[w->level] += w->weight; } while (w++->next); } + //! Computes lhs -= weight(lit). + void sub(wsum_t* lhs, const WeightLiteral& lit, uint32& aLev) const { if (weights.empty()) *lhs -= lit.second; else sub(lhs, &weights[lit.second], aLev); } + void sub(wsum_t* lhs, const LevelWeight* w, uint32& aLev) const; + //! Returns (lhs + weight(lit)) > rhs + bool imp(wsum_t* lhs, const WeightLiteral& lit, const wsum_t* rhs, uint32& lev) const { + return weights.empty() ? (*lhs+lit.second) > *rhs : imp(lhs, &weights[lit.second], rhs, lev); + } + bool imp(wsum_t* lhs, const LevelWeight* w, const wsum_t* rhs, uint32& lev) const; + //! Returns the weight of lit at level lev. + weight_t weight(const WeightLiteral& lit, uint32 lev) const { + if (numRules() == 1) { return lit.second * (lev == 0); } + const LevelWeight* w = &weights[lit.second]; + do { if (w->level == lev) return w->weight; } while (w++->next); + return 0; + } + //@} +private: + typedef Clasp::Atomic_t::type CounterType; + typedef Clasp::Atomic_t::type LowerType; + SumVec adjust_; // initial bound adjustments + SumVec up_[2]; // buffers for update via "double buffering" + LowerType* lower_; // (unadjusted) lower bound of constraint + MinimizeMode mode_; // how to compare assignments? + CounterType count_; // number of refs to this object + CounterType gCount_; // generation count - used when updating optimum + uint32 optGen_; // generation of optimal bound +public: + WeightVec weights; // sparse vectors of weights - only used for multi-level constraints + PrioVec prios; // (optional): maps levels to original priorities +POTASSCO_WARNING_BEGIN_RELAXED + WeightLiteral lits[0]; // (shared) literals - terminated with lit_true() +POTASSCO_WARNING_END_RELAXED +private: + ~SharedMinimizeData(); + void destroy() const; + SharedMinimizeData(const SharedMinimizeData&); + SharedMinimizeData& operator=(const SharedMinimizeData&); +}; +//! Helper class for creating minimize constraints. +/*! + * \ingroup constraint + */ +class MinimizeBuilder { +public: + typedef SharedMinimizeData SharedData; + MinimizeBuilder(); + + MinimizeBuilder& add(weight_t prio, const WeightLitVec& lits); + MinimizeBuilder& add(weight_t prio, WeightLiteral lit); + MinimizeBuilder& add(weight_t prio, weight_t adjust); + MinimizeBuilder& add(const SharedData& minCon); + + bool empty() const; + + //! Creates a new data object from previously added minimize literals. + /*! + * The function creates a new minimize data object from + * the previously added literals to minimize. The returned + * object can be used to attach one or more MinimizeConstraints. + * \param ctx A ctx object to be associated with the new minmize constraint. + * \return A data object representing previously added minimize statements or 0 if empty(). + * \pre !ctx.frozen() + * \post empty() + */ + SharedData* build(SharedContext& ctx); + + //! Discards any previously added minimize literals. + void clear(); +private: + struct MLit { + MLit(const WeightLiteral& wl, weight_t at) : lit(wl.first), prio(at), weight(wl.second) {} + Literal lit; + weight_t prio; + weight_t weight; + }; + struct CmpPrio { bool operator()(const MLit& lhs, const MLit& rhs) const; }; + struct CmpLit { bool operator()(const MLit& lhs, const MLit& rhs) const; }; + struct CmpWeight{ + CmpWeight(const SharedData::WeightVec* w) : weights(w) {} + bool operator()(const MLit& lhs, const MLit& rhs) const; + const SharedData::WeightVec* weights; + }; + typedef PodVector::type LitVec; + void prepareLevels(const Solver& s, SumVec& adjustOut, WeightVec& priosOut); + void mergeLevels(SumVec& adjust, SharedData::WeightVec& weightsOut); + SharedData* createShared(SharedContext& ctx, const SumVec& adjust, const CmpWeight& cmp); + LitVec lits_; +}; + +//! Base class for implementing (mulit-level) minimize statements. +/*! + * \ingroup constraint + * A solver contains at most one minimize constraint, but a minimize constraint + * may represent several minimize statements. In that case, each minimize statement + * has a unique level (starting at 0) and minimize statements with a lower level + * have higher priority. Priorities are handled like in smodels, i.e. statements + * with lower priority become relevant only if all statements with higher priority + * currently have an optimal assignment. + * + * MinimizeConstraint supports two modes of operation: if mode is set to + * optimize, solutions are considered optimal only if they are strictly smaller + * than previous solutions. Otherwise, if mode is set to enumerate a + * solution is valid if it is not greater than the initially set optimum. + * Example: + * m0: {a, b} + * m1: {c, d} + * All models: {a, c,...}, {a, d,...} {b, c,...}, {b, d,...} {a, b,...} + * Mode = optimize: {a, c, ...} (m0 = 1, m1 = 1} + * Mode = enumerate and initial opt=1,1: {a, c, ...}, {a, d,...}, {b, c,...}, {b, d,...} + * + */ +class MinimizeConstraint : public Constraint { +public: + typedef SharedMinimizeData SharedData; + typedef const SharedData* SharedDataP; + //! Returns a pointer to the shared representation of this constraint. + SharedDataP shared() const { return shared_; } + //! Attaches this object to the given solver. + virtual bool attach(Solver& s) = 0; + //! Shall activate the minimize constraint by integrating bounds stored in the shared data object. + virtual bool integrate(Solver& s) = 0; + //! Shall relax this constraint (i.e. remove any bounds). + /*! + * If reset is true, shall also remove search-path related state. + */ + virtual bool relax(Solver& s, bool reset) = 0; + //! Shall commit the model in s to the shared data object. + /*! + * The return value indicates whether the model is valid w.r.t the + * costs stored in the shared data object. + */ + virtual bool handleModel(Solver& s) = 0; + //! Shall handle the unsatisfiable path in s. + virtual bool handleUnsat(Solver& s, bool upShared, LitVec& restore) = 0; + virtual bool supportsSplitting() const { return true; } + // base interface + void destroy(Solver*, bool); + Constraint* cloneAttach(Solver&) { return 0; } +protected: + MinimizeConstraint(SharedData* s); + ~MinimizeConstraint(); + void reportLower(Solver& s, uint32 level, wsum_t low) const; + bool prepare(Solver& s, bool useTag); + SharedData* shared_; // common shared data + Literal tag_; // (optional) literal for tagging reasons +}; + +//! Minimization via branch and bound. +/*! + * \ingroup constraint + * The class supports both basic branch and bound as well as + * hierarchical branch and bound (with or without varying step sizes). + */ +class DefaultMinimize : public MinimizeConstraint { +public: + explicit DefaultMinimize(SharedData* d, const OptParams& params); + // base interface + //! Attaches the constraint to the given solver. + /*! + * \pre s.decisionLevel() == 0 + * \note If either MinimizeMode_t::enumOpt or hierarchical optimization + * is active, s.sharedContext()->tagLiteral() shall be an unassigned literal. + */ + bool attach(Solver& s); + bool integrate(Solver& s) { return integrateBound(s); } + bool relax(Solver&, bool reset) { return relaxBound(reset); } + bool handleModel(Solver& s) { commitUpperBound(s); return true; } + bool handleUnsat(Solver& s, bool up, LitVec& out); + // constraint interface + PropResult propagate(Solver& s, Literal p, uint32& data); + void undoLevel(Solver& s); + void reason(Solver& s, Literal p, LitVec& lits); + bool minimize(Solver& s, Literal p, CCMinRecursive* r); + void destroy(Solver*, bool); + // own interface + bool active() const { return *opt() != SharedData::maxBound(); } + //! Number of minimize statements contained in this constraint. + uint32 numRules()const { return size_; } + //! Tries to integrate the next tentative bound into this constraint. + /*! + * Starting from the current optimum stored in the shared data object, + * the function tries to integrate the next candidate bound into + * this constraint. + * + * \return The function returns true if integration succeeded. Otherwise + * false is returned and s.hasConflict() is true. + * + * \note If integrateBound() failed, the bound of this constraint + * is relaxed. The caller has to resolve the conflict first + * and then integrateBound() shall be called again. + * + * \note The caller has to call s.propagate() to propagate any new information + * from the new bound. + * + * \note If the tag literal (if any) is not true, the minimize constraint first assumes it. + */ + bool integrateBound(Solver& s); + + //! Sets the current local sum as the global optimum (upper bound). + /*! + * commitUpperBound() shall be called whenever the solver finds a model. + * The current local sum is recorded as new optimum in the shared data object. + * Once the local bound is committed, the function integrateBound() has to be + * called in order to continue optimization. + */ + void commitUpperBound(const Solver& s); + //! Sets the current local upper bound as the lower bound of this constraint. + /*! + * commitLowerBound() shall be called on unsat. The function stores + * the local upper bound as new lower bound in this constraint. If upShared is true, + * the lower bound is also copied to the shared data object. + * + * Once the local bound is committed, the function integrateBound() has to be + * called in order to continue optimization. + * \return false if search-space is exceeded w.r.t this constraint. + */ + bool commitLowerBound(Solver& s, bool upShared); + + //! Removes the local upper bound of this constraint and therefore disables it. + /*! + * If full is true, also removes search-path related state. + */ + bool relaxBound(bool full = false); + + bool more() const { return step_.lev != size_; } + + // FOR TESTING ONLY! + wsum_t sum(uint32 i, bool adjust) const { return sum()[i] + (adjust ? shared_->adjust(i):0); } +private: + enum PropMode { propagate_new_sum, propagate_new_opt }; + union UndoInfo; + typedef const WeightLiteral* Iter; + ~DefaultMinimize(); + // bound operations + wsum_t* opt() const { return bounds_; } + wsum_t* sum() const { return bounds_ + size_; } + wsum_t* temp()const { return bounds_ + (size_*2); } + wsum_t* end() const { return bounds_ + (size_*3); } + void assign(wsum_t* lhs, wsum_t* rhs) const; + bool greater(wsum_t* lhs, wsum_t* rhs, uint32 len, uint32& aLev) const; + // propagation & undo + uint32 lastUndoLevel(const Solver& s) const; + bool litSeen(uint32 i) const; + bool propagateImpl(Solver& s, PropMode m); + uint32 computeImplicationSet(const Solver& s, const WeightLiteral& it, uint32& undoPos); + void pushUndo(Solver& s, uint32 litIdx); + bool updateBounds(bool applyStep); + // step + wsum_t& stepLow() const { return *(end() + step_.lev); } + void stepInit(uint32 n); + wsum_t* bounds_; // [upper,sum,temp[,lower]] + Iter pos_; // position of literal to look at next + UndoInfo* undo_; // one "seen" flag for each literal + + uint32 undoTop_; // undo stack holding assigned literals + uint32 posTop_; // stack of saved "look at" positions + const uint32 size_; // number of rules + uint32 actLev_; // first level to look at when comparing bounds + struct Step { // how to reduce next tentative bound + uint32 size; // size of step + uint32 lev : 30; // level on which step is applied + uint32 type: 2; // type of step (one of OptParams::BBAlgo) + } step_; +}; + +//! Minimization via unsat cores. +/*! + * \ingroup constraint + */ +class UncoreMinimize : public MinimizeConstraint { +public: + // constraint interface + PropResult propagate(Solver& s, Literal p, uint32& data); + void reason(Solver& s, Literal p, LitVec& lits); + void destroy(Solver*, bool); + bool simplify(Solver& s, bool reinit = false); + // base interface + bool attach(Solver& s); + bool integrate(Solver& s); + bool relax(Solver&, bool reset); + bool valid(Solver& s); + bool handleModel(Solver& s); + bool handleUnsat(Solver& s, bool up, LitVec& out); + bool supportsSplitting() const { return false; } +private: + friend class SharedMinimizeData; + explicit UncoreMinimize(SharedData* d, const OptParams& params); + typedef DefaultMinimize* EnumPtr; + struct LitData { + LitData(weight_t w, bool as, uint32 c) : weight(w), coreId(c), assume((uint32)as), flag(0u) {} + weight_t weight; + uint32 coreId : 30; + uint32 assume : 1; + uint32 flag : 1; + }; + struct LitPair { + LitPair(Literal p, uint32 dataId) : lit(p), id(dataId) {} + Literal lit; + uint32 id; + }; + struct Core { + Core(WeightConstraint* c, weight_t b, weight_t w) : con(c), bound(b), weight(w) {} + uint32 size() const; + Literal at(uint32 i) const; + Literal tag() const; + WeightConstraint* con; + weight_t bound; + weight_t weight; + }; + struct WCTemp { + typedef WeightLitVec WLitVec; + typedef WeightLiteral* Ptr; + void start(weight_t b){ lits.clear(); bound = b; } + void add(Solver& s, Literal p); + bool unsat() const { return bound > 0 && static_cast(bound) > static_cast(lits.size()); } + uint32 size() const { return sizeVec(lits); } + Ptr begin() { return size() ? &lits[0] : 0; } + weight_t bound; + WLitVec lits; + }; + typedef PodVector::type LitTable; + typedef PodVector::type CoreTable; + typedef PodVector::type ConTable; + typedef PodVector::type LitSet; + class Todo { + public: + typedef LitSet::const_iterator const_iterator; + Todo() { clear(); } + const_iterator begin() const { return lits_.begin(); } + const_iterator end() const { return lits_.end(); } + uint32 size() const { return sizeVec(lits_); } + weight_t weight() const { return minW_; } + bool shrink() const { return next_ != 0u; } + void clear(bool resetShrink = true); + void add(const LitPair& x, weight_t w); + void terminate(); + bool shrinkNext(UncoreMinimize& self, ValueRep result); + void shrinkPush(UncoreMinimize& self, Solver& s); + void shrinkReset(); + private: + bool subsetNext(UncoreMinimize& self, ValueRep result); + LitSet lits_; + weight_t minW_; + // shrinking + uint32 last_; + uint32 next_; + uint32 step_; + LitSet core_; + }; + // literal and core management + bool hasCore(const LitData& x) const { return x.coreId != 0; } + bool flagged(uint32 id) const { return litData_[id-1].flag != 0u; } + void setFlag(uint32 id, bool f) { litData_[id-1].flag = uint32(f); } + LitData& getData(uint32 id) { return litData_[id-1];} + Core& getCore(const LitData& x) { return open_[x.coreId-1]; } + LitPair newAssumption(Literal p, weight_t w); + Literal newLit(Solver& s); + void releaseLits(); + bool addCore(Solver& s, const LitPair* lits, uint32 size, weight_t w, bool updateLower); + uint32 allocCore(WeightConstraint* con, weight_t bound, weight_t weight, bool open); + bool closeCore(Solver& s, LitData& x, bool sat); + bool addOll(Solver& s, const LitPair* lits, uint32 size, weight_t w); + bool addOllCon(Solver& s, const WCTemp& wc, weight_t w); + bool addK(Solver& s, uint32 K, const LitPair* lits, uint32 size, weight_t w); + enum CompType { comp_disj = 0, comp_conj = 1 }; + bool addPmr(Solver& s, const LitPair* lits, uint32 size, weight_t w); + bool addPmrCon(CompType t, Solver& s, Literal head, Literal body1, Literal body2); + bool addConstraint(Solver& s, WeightLiteral* lits, uint32 size, weight_t bound); + bool addImplication(Solver& s, Literal a, Literal b, bool concise); + // algorithm + void init(); + uint32 initRoot(Solver& s); + bool initLevel(Solver& s); + uint32 analyze(Solver& s); + bool addNext(Solver& s, bool allowInit = true); + bool pushPath(Solver& s); + bool popPath(Solver& s, uint32 dl); + bool fixLit(Solver& s, Literal p); + bool fixLevel(Solver& s); + void detach(Solver* s, bool b); + bool pushTrim(Solver& s); + void resetTrim(Solver& s); + bool push(Solver& s, Literal p, uint32 id); + wsum_t* computeSum(const Solver& s) const; + bool validLowerBound() const { + wsum_t cmp = lower_ - upper_; + return cmp < 0 || (cmp == 0 && level_ == shared_->maxLevel() && !shared_->checkNext()); + } + // data + EnumPtr enum_; // for supporting (optimal) model enumeration in parallel mode + wsum_t* sum_; // costs of active model + LitTable litData_; // data for active literals (tag lits for cores + lits from active minimize) + CoreTable open_; // open cores, i.e. relaxable and referenced by an assumption + ConTable closed_; // closed cores represented as weight constraints + LitSet assume_; // current set of assumptions + Todo todo_; // core(s) not yet represented as constraint + LitVec fix_; // set of fixed literals + LitVec conflict_; // temporary: conflicting set of assumptions + WCTemp temp_; // temporary: used for creating weight constraints + wsum_t lower_; // lower bound of active level + wsum_t upper_; // upper bound of active level + uint32 auxInit_; // number of solver aux vars on attach + uint32 auxAdd_; // number of aux vars added for cores + uint32 gen_; // active generation + uint32 level_ : 28;// active level + uint32 next_ : 1;// update because of model + uint32 disj_ : 1;// preprocessing active? + uint32 path_ : 1;// push path? + uint32 init_ : 1;// init constraint? + weight_t actW_; // active weight limit (only weighted minimization with stratification) + weight_t nextW_; // next weight limit (only weighted minimization with stratification) + uint32 eRoot_; // saved root level of solver (initial gp) + uint32 aTop_; // saved assumption level (added by us) + uint32 freeOpen_; // head of open core free list + OptParams options_; // active options +}; + +} // end namespace Clasp + +#endif diff --git a/clasp/clasp/model_enumerators.h b/clasp/clasp/model_enumerators.h new file mode 100644 index 000000000..1f6def93a --- /dev/null +++ b/clasp/clasp/model_enumerators.h @@ -0,0 +1,123 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +//! \file +//! \brief Model enumeration with minimization and projection. +#ifndef CLASP_MODEL_ENUMERATORS_H +#define CLASP_MODEL_ENUMERATORS_H + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include + +namespace Clasp { + +//! Class for model enumeration with minimization and projection. +/*! + * This class implements algorithms for enumerating models with or without optimization + * and/or projection. It supports two different algorithms (strategies). First, enumeration + * via restricted backjumping, and second, enumeration via recording of solution nogoods. + * + * The first strategy, strategy_backtrack, maintains a special backtracking level to + * suppress certain backjumps that could otherwise "re-open" search spaces already visited. + * If projection is not active, no extra nogoods are created. + * Otherwise, the number of additional solution nogoods is linear in the number of projection atoms. + * + * The second strategy, strategy_record, enumerates models by recording nogoods for found solutions. + * In general, this strategy is exponential in space (bounded by the number of solutions). + * On the other hand, if optimization is active, additional nogoods are not needed + * because the optimization constraint already serves as solution nogood. + * + * There is also a third strategy, strategy_auto, provided for convenience. This + * strategy automatically selects between strategy_backtrack and strategy_record + * based on the problem at hand. It uses strategy_record, if one of the following holds: + * - optimization is active, or + * - only one model is requested, or + * - both parallel search as well as projection are active + * . + * In all other cases, strategy_auto selects strategy_backtrack. + * + * \ingroup enumerator + */ +class ModelEnumerator : public Enumerator { +public: + //! Enumeration algorithms. + enum Strategy { + strategy_auto = 0, //!< Use strategy best suited to problem. + strategy_backtrack = 1, //!< Use backtrack-based enumeration. + strategy_record = 2 //!< Use nogood-based enumeration. + }; + //! Projective solution enumeration and options. + enum ProjectOptions { + project_enable_simple = 1, //!< Enable projective solution enumeration. + project_use_heuristic = 2, //!< Use heuristic when selecting a literal from a projection nogood. + project_save_progress = 4, //!< Enable progress saving after the first solution was found. + project_enable_full = 6, //!< Enable projective solution enumeration with heuristic and progress saving. + project_dom_lits = 8, //!< In strategy record, project only on true domain literals. + }; + /*! + * \param st Enumeration strategy to apply. + */ + explicit ModelEnumerator(Strategy st = strategy_auto); + ~ModelEnumerator(); + + //! Configure strategy. + /*! + * \param st Enumeration strategy to use. + * \param projection The set of ProjectOptions to be applied or 0 to disable projective enumeration. + * \param filter Ignore output predicates starting with filter in projective enumeration. + */ + void setStrategy(Strategy st = strategy_auto, uint32 projection = 0, char filter = '_'); + bool projectionEnabled()const { return projectOpts() != 0; } + bool domRec() const { return (projectOpts() & project_dom_lits) != 0; } + Strategy strategy() const { return static_cast(opts_.algo); } + bool project(Var v) const; +protected: + bool supportsRestarts() const { return optimize() || strategy() == strategy_record; } + bool supportsParallel() const { return !projectionEnabled() || strategy() != strategy_backtrack; } + bool supportsSplitting(const SharedContext& problem) const { + return (strategy() == strategy_backtrack || !domRec()) && Enumerator::supportsSplitting(problem); + } + ConPtr doInit(SharedContext& ctx, SharedMinimizeData* m, int numModels); +private: + class ModelFinder; + class BacktrackFinder; + class RecordFinder; + typedef PodVector::type WordVec; + void initProjection(SharedContext& ctx); + void addProject(SharedContext& ctx, Var v); + uint32 projectOpts() const { return opts_.proj; } + bool trivial() const { return trivial_; } + WordVec project_; + char filter_; + struct Options { + uint8 proj : 4; + uint8 algo : 2; + } opts_, saved_; + bool trivial_; +}; +} +#endif diff --git a/clasp/clasp/mt/mutex.h b/clasp/clasp/mt/mutex.h new file mode 100644 index 000000000..243988883 --- /dev/null +++ b/clasp/clasp/mt/mutex.h @@ -0,0 +1,50 @@ +// +// Copyright (c) 2012-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +#ifndef CLASP_UTIL_MUTEX_H_INCLUDED +#define CLASP_UTIL_MUTEX_H_INCLUDED + +#include +#include + +namespace Clasp { namespace mt { +using std::mutex; +using std::lock_guard; +using std::unique_lock; +using std::swap; +using std::defer_lock_t; +struct condition_variable : private std::condition_variable { + typedef std::condition_variable base_type; + using base_type::notify_one; + using base_type::notify_all; + using base_type::wait; + using base_type::native_handle; + + inline bool wait_for(unique_lock& lock, double timeInSecs) { + return base_type::wait_for(lock, std::chrono::duration_cast(std::chrono::duration(timeInSecs))) + == std::cv_status::no_timeout; + } +}; +}} +#endif diff --git a/clasp/clasp/mt/parallel_solve.h b/clasp/clasp/mt/parallel_solve.h new file mode 100644 index 000000000..6c4648097 --- /dev/null +++ b/clasp/clasp/mt/parallel_solve.h @@ -0,0 +1,373 @@ +// +// Copyright (c) 2010-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_PARALLEL_SOLVE_H_INCLUDED +#define CLASP_PARALLEL_SOLVE_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif +#include +#if !CLASP_HAS_THREADS +#error Thread support required for parallel solving +#endif +#include +#include +#include +#include +#include +#include + +/*! + * \file + * \brief Defines classes controlling multi-threaded parallel solving. + */ +namespace Clasp { +//! Namespace for types and functions needed for implementing multi-threaded parallel solving. +namespace mt { + +/** + * \defgroup mt Multi-threading + * \brief Parallel solving and related classes. + * \ingroup enumerator + */ +//@{ + +class ParallelHandler; +class ParallelSolve; + +//! Options for controlling parallel solving. +struct ParallelSolveOptions : BasicSolveOptions { + //! Nogood distribution options. + struct Distribution : Distributor::Policy { + enum Mode { mode_global = 0, mode_local = 1 }; + Distribution(Mode m = mode_global) : Distributor::Policy(), mode(m) {} + Distributor::Policy& policy() { return *this; } + uint32 mode; + }; + ParallelSolveOptions() {} + //! Algorithm options. + struct Algorithm { + //! Possible search strategies. + enum SearchMode { mode_split = 0, mode_compete = 1 }; + Algorithm() : threads(1), mode(mode_compete) {} + uint32 threads; + SearchMode mode; + }; + //! Nogood integration options. + struct Integration { + static const uint32 GRACE_MAX = (1u<<28)-1; + Integration() : grace(1024), filter(filter_gp), topo(topo_all) {} + enum Filter { filter_no = 0, filter_gp = 1, filter_sat = 2, filter_heuristic = 3 }; + enum Topology { topo_all = 0, topo_ring = 1, topo_cube = 2, topo_cubex = 3 }; + uint32 grace : 28; /**< Lower bound on number of shared nogoods to keep. */ + uint32 filter: 2; /**< Filter for integrating shared nogoods (one of Filter). */ + uint32 topo : 2; /**< Integration topology */ + }; + //! Global restart options. + struct GRestarts { + GRestarts():maxR(0) {} + uint32 maxR; + ScheduleStrategy sched; + }; + Integration integrate; //!< Nogood integration options to apply during search. + Distribution distribute;//!< Nogood distribution options to apply during search. + GRestarts restarts; //!< Global restart strategy to apply during search. + Algorithm algorithm; //!< Parallel algorithm to use. + //! Allocates a new solve object. + SolveAlgorithm* createSolveObject() const; + //! Returns the number of threads that can run concurrently on the current hardware. + static uint32 recommendedSolvers() { return Clasp::mt::thread::hardware_concurrency(); } + //! Returns number of maximal number of supported threads. + static uint32 supportedSolvers() { return 64; } + //! Returns the peers of the solver with the given id assuming the given topology. + static uint64 initPeerMask(uint32 sId, Integration::Topology topo, uint32 numThreads); + uint32 numSolver() const { return algorithm.threads; } + void setSolvers(uint32 i) { algorithm.threads = std::max(uint32(1), i); } + bool defaultPortfolio() const { return algorithm.mode == Algorithm::mode_compete; } +}; + +//! A parallel algorithm for multi-threaded solving with and without search-space splitting. +/*! + * The class adapts clasp's basic solve algorithm + * to a parallel solve algorithm that solves + * a problem using a given number of threads. + * It supports guiding path based solving, portfolio based solving, as well + * as a combination of these two approaches. + */ +class ParallelSolve : public SolveAlgorithm { +public: + explicit ParallelSolve(const ParallelSolveOptions& opts); + ~ParallelSolve(); + // base interface + virtual bool interrupted() const; + virtual void resetSolve(); + virtual void enableInterrupts(); + // own interface + //! Returns the number of active threads. + uint32 numThreads() const; + bool integrateUseHeuristic() const { return test_bit(intFlags_, 31); } + uint32 integrateGrace() const { return intGrace_; } + uint32 integrateFlags() const { return intFlags_; } + uint64 hasErrors() const; + //! Requests a global restart. + void requestRestart(); + bool handleMessages(Solver& s); + bool integrateModels(Solver& s, uint32& mCount); + void pushWork(LitVec* gp); + bool commitModel(Solver& s); + bool commitUnsat(Solver& s); + enum GpType { gp_none = 0, gp_split = 1, gp_fixed = 2 }; +private: + ParallelSolve(const ParallelSolve&); + ParallelSolve& operator=(const ParallelSolve&); + typedef SingleOwnerPtr PathPtr; + enum ErrorCode { LogicError = 1, RuntimeError = 2, OutOfMemory = 3, UnknownError = 4 }; + enum { masterId = 0 }; + // ------------------------------------------------------------------------------------------- + // Thread setup + struct EntryPoint; + void destroyThread(uint32 id); + void allocThread(uint32 id, Solver& s); + int joinThreads(); + // ------------------------------------------------------------------------------------------- + // Algorithm steps + void setIntegrate(uint32 grace, uint8 filter); + void setRestarts(uint32 maxR, const ScheduleStrategy& rs); + bool beginSolve(SharedContext& ctx, const LitVec& assume); + bool doSolve(SharedContext& ctx, const LitVec& assume); + void doStart(SharedContext& ctx, const LitVec& assume); + int doNext(int last); + void doStop(); + void doDetach(); + bool doInterrupt(); + void solveParallel(uint32 id); + void initQueue(); + bool requestWork(Solver& s, PathPtr& out); + void terminate(Solver& s, bool complete); + bool waitOnSync(Solver& s); + void exception(uint32 id, PathPtr& path, ErrorCode e, const char* what); + void reportProgress(const Event& ev) const; + void reportProgress(const Solver& s, const char* msg) const; + // ------------------------------------------------------------------------------------------- + typedef ParallelSolveOptions::Distribution Distribution; + struct SharedData; + // SHARED DATA + SharedData* shared_; // Shared control data + ParallelHandler** thread_; // Thread-locl control data + // READ ONLY + Distribution distribution_; // distribution options + uint32 maxRestarts_; // disable global restarts once reached + uint32 intGrace_ : 30;// grace period for clauses to integrate + uint32 intTopo_ : 2;// integration topology + uint32 intFlags_; // bitset controlling clause integration + bool modeSplit_; +}; +//! An event type for debugging messages sent between threads. +struct MessageEvent : SolveEvent { + enum Action { sent, received, completed }; + MessageEvent(const Solver& s, const char* message, Action a, double t = 0.0) + : SolveEvent(s, verbosity_high), msg(message), time(t) { op = (uint32)a; } + const char* msg; // name of message + double time; // only for action completed +}; +//! A per-solver (i.e. thread) class that implements message handling and knowledge integration. +/*! + * The class adds itself as a post propagator to the given solver. During propagation + * it checks for new messages and lemmas to integrate. + */ +class ParallelHandler : public MessageHandler { +public: + typedef ParallelSolve::GpType GpType; + + //! Creates a new parallel handler to be used in the given solve group. + /*! + * \param ctrl The object controlling the parallel solve operation. + * \param s The solver that is to be controlled by this object. + */ + explicit ParallelHandler(ParallelSolve& ctrl, Solver& s); + ~ParallelHandler(); + //! Attaches the object's solver to ctx and adds this object as a post propagator. + bool attach(SharedContext& ctx); + //! Removes this object from the list of post propagators of its solver and detaches the solver from ctx. + void detach(SharedContext& ctx, bool fastExit); + + bool setError(int e); + int error() const { return (int)error_; } + void setWinner() { win_ = 1; } + bool winner() const { return win_ != 0; } + void setThread(Clasp::mt::thread& x) { assert(!joinable()); x.swap(thread_); assert(joinable()); } + + //! True if *this has an associated thread of execution, false otherwise. + bool joinable() const { return thread_.joinable(); } + //! Waits for the thread of execution associated with *this to finish. + int join() { if (joinable()) { thread_.join(); } return error(); } + + // overridden methods + + //! Integrates new information. + bool propagateFixpoint(Solver& s, PostPropagator*); + bool handleMessages() { return ctrl_->handleMessages(solver()); } + void reset() { up_ = 1; } + bool simplify(Solver& s, bool re); + //! Checks whether new information has invalidated current model. + bool isModel(Solver& s); + + // own interface + bool isModelLocked(Solver& s); + + // TODO: make functions virtual once necessary + + //! Returns true if handler's guiding path is disjoint from all others. + bool disjointPath() const { return gp_.type == ParallelSolve::gp_split; } + //! Returns true if handler has a guiding path. + bool hasPath() const { return gp_.type != ParallelSolve::gp_none; } + void setGpType(GpType t) { gp_.type = t; } + + //! Entry point for solving the given guiding path. + /*! + * \param solve The object used for solving. + * \param type The guiding path's type. + * \param restart Request restart after restart number of conflicts. + */ + ValueRep solveGP(BasicSolve& solve, GpType type, uint64 restart); + + /*! + * \name Message handlers + * \note + * Message handlers are intended as callbacks for ParallelSolve::handleMessages(). + * They shall not change the assignment of the solver object. + */ + //@{ + + //! Algorithm is about to terminate. + /*! + * Removes this object from the solver's list of post propagators. + */ + void handleTerminateMessage(); + + //! Request for split. + /*! + * Splits off a new guiding path and adds it to the control object. + * \pre The guiding path of this object is "splittable" + */ + void handleSplitMessage(); + + //! Request for (global) restart. + /*! + * \return true if restart is valid, else false. + */ + bool handleRestartMessage(); + + Solver& solver() { return *solver_; } + //@} +private: + void add(ClauseHead* h); + void clearDB(Solver* s); + bool integrate(Solver& s); + typedef PodVector::type ClauseDB; + typedef SharedLiterals** RecBuffer; + struct GP { + uint64 restart; // don't give up before restart number of conflicts + uint32 modCount; // integration counter for synchronizing models + GpType type; // type of gp + void reset(uint64 r = UINT64_MAX, GpType t = ParallelSolve::gp_none) { + restart = r; + modCount = 0; + type = t; + } + }; + enum { RECEIVE_BUFFER_SIZE = 32 }; + Clasp::mt::thread thread_; // active thread or empty for master + GP gp_; // active guiding path + ParallelSolve* ctrl_; // message source + Solver* solver_; // associated solver + RecBuffer received_; // received clauses not yet integrated + ClauseDB integrated_; // integrated clauses + uint32 recEnd_; // where to put next received clause + uint32 intEnd_; // where to put next clause + uint32 error_:28; // error code or 0 if ok + uint32 win_ : 1; // 1 if thread was the first to terminate the search + uint32 up_ : 1; // 1 if next propagate should check for new lemmas/models + uint32 act_ : 1; // 1 if gp is active + uint32 lbd_ : 1; // 1 if integrate should compute lbds +}; +//! A class that uses a global list to exchange nogoods between threads. +class GlobalDistribution : public Distributor { +public: + explicit GlobalDistribution(const Policy& p, uint32 maxShare, uint32 topo); + ~GlobalDistribution(); + uint32 receive(const Solver& in, SharedLiterals** out, uint32 maxOut); + void publish(const Solver& source, SharedLiterals* n); +private: + void release(); + struct ClausePair { + ClausePair(uint32 sId = UINT32_MAX, SharedLiterals* x = 0) : sender(sId), lits(x) {} + uint32 sender; + SharedLiterals* lits; + }; + class Queue : public MultiQueue { + public: + typedef MultiQueue base_type; + using base_type::publish; + Queue(uint32 m) : base_type(m) {} + }; + struct ThreadInfo { + uint64 peerMask; + union { + Queue::ThreadId id; + uint64 rep; + }; + char pad[64 - (sizeof(uint64)*2)]; + }; + Queue::ThreadId& getThreadId(uint32 sId) const { return threadId_[sId].id; } + uint64 getPeerMask(uint32 sId) const { return threadId_[sId].peerMask; } + Queue* queue_; + ThreadInfo* threadId_; +}; +//! A class that uses thread-local lists to exchange nogoods between threads. +class LocalDistribution : public Distributor { +public: + explicit LocalDistribution(const Policy& p, uint32 maxShare, uint32 topo); + ~LocalDistribution(); + uint32 receive(const Solver& in, SharedLiterals** out, uint32 maxOut); + void publish(const Solver& source, SharedLiterals* n); +private: + typedef Detail::RawStack RawStack; + typedef MPSCPtrQueue::Node QNode; + enum { BLOCK_CAP = 128 }; + QNode* allocNode(uint32 tId, SharedLiterals* clause); + void freeNode(uint32 tId, QNode* n) const; + struct ThreadData { + MPSCPtrQueue received; // queue holding received clauses + uint64 peers; // set of peers from which this thread receives clauses + QNode sentinal; // sentinal node for simplifying queue impl + QNode* free; // local free list - only accessed by this thread + }** thread_; // one entry for each thread + RawStack blocks_; // allocated node blocks + uint32 numThread_; // number of threads, i.e. size of array thread_ +}; +//@} +} } +#endif + diff --git a/clasp/clasp/mt/thread.h b/clasp/clasp/mt/thread.h new file mode 100644 index 000000000..1a4b13f54 --- /dev/null +++ b/clasp/clasp/mt/thread.h @@ -0,0 +1,33 @@ +// +// Copyright (c) 2010-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +#ifndef CLASP_UTIL_THREAD_H_INCLUDED +#define CLASP_UTIL_THREAD_H_INCLUDED + +#include +namespace Clasp { namespace mt { +using std::thread; +namespace this_thread { using std::this_thread::yield; } +}} +#endif diff --git a/clasp/clasp/parser.h b/clasp/clasp/parser.h new file mode 100644 index 000000000..cd8885a0c --- /dev/null +++ b/clasp/clasp/parser.h @@ -0,0 +1,197 @@ +// +// Copyright (c) 2014-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_PARSER_H_INCLUDED +#define CLASP_PARSER_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include +#include +#include +/*! + * \file + * \brief Defines parsers for supported input formats. + */ +namespace Clasp { +///////////////////////////////////////////////////////////////////////////////////////// +// PARSING BASE +///////////////////////////////////////////////////////////////////////////////////////// +/*! + * \addtogroup problem + */ +//@{ + +//! Auto-detect type of program given in prg. +ProblemType detectProblemType(std::istream& prg); + +//! Parse additional information in symbol table/comments. +struct ParserOptions { + //! Supported parser extensions. + enum Extension { + parse_heuristic = 1u, //!< Parse heuristic info in smodels, dimacs, and pb format. + parse_acyc_edge = 2u, //!< Parse acyc info in smodels, dimacs, and pb format. + parse_minimize = 4u, //!< Parse cost function in dimacs format. + parse_project = 8u, //!< Parse project directive in dimacs and pb format. + parse_assume = 16u,//!< Parse assumption directive in dimacs and pb format. + parse_output = 32u,//!< Parse output directive in dimacs and pb format. + parse_full = 63u, + parse_maxsat = 128u//!< Parse dimacs as MaxSAT problem + }; + ParserOptions() : set(0) {} + ParserOptions& enableHeuristic() { set |= parse_heuristic; return *this; } + ParserOptions& enableAcycEdges() { set |= parse_acyc_edge; return *this; } + ParserOptions& enableMinimize() { set |= parse_minimize; return *this; } + ParserOptions& enableProject() { set |= parse_project; return *this; } + ParserOptions& enableAssume() { set |= parse_assume; return *this; } + ParserOptions& enableOutput() { set |= parse_output; return *this; } + ParserOptions& assign(uint8 f, bool b) { + if (b) { set |= f; } + else { set &= ~f; } + return *this; + } + bool isEnabled(Extension e) const { return (set & static_cast(e)) != 0u; } + bool anyOf(uint8 f) const { return (set & f) != 0u; } + uint8 set; +}; +//! Base class for parsers. +class ProgramParser { +public: + typedef Potassco::ProgramReader StrategyType; + static const Var VAR_MAX = varMax - 1; + ProgramParser(); + virtual ~ProgramParser(); + bool accept(std::istream& str, const ParserOptions& o = ParserOptions()); + bool incremental() const; + bool isOpen() const; + bool parse(); + bool more(); + void reset(); +private: + virtual StrategyType* doAccept(std::istream& str, const ParserOptions& o) = 0; + StrategyType* strat_; +}; + +//! Parser for logic programs in smodels-internal or aspif format. +class AspParser : public ProgramParser { +public: + static bool accept(char c); + explicit AspParser(Asp::LogicProgram& prg); + ~AspParser(); + enum Format { format_smodels = -1, format_aspif = 1 }; + static void write(Asp::LogicProgram& prg, std::ostream& os); + static void write(Asp::LogicProgram& prg, std::ostream& os, Format f); +protected: + virtual StrategyType* doAccept(std::istream& str, const ParserOptions& o); +private: + struct SmAdapter; + Asp::LogicProgram* lp_; + StrategyType* in_; + Potassco::AbstractProgram* out_; +}; +///////////////////////////////////////////////////////////////////////////////////////// +// SAT parsing +///////////////////////////////////////////////////////////////////////////////////////// +//! Base class for dimacs and opb parser. +class SatReader : public Potassco::ProgramReader { +public: + SatReader(); + ParserOptions options; +protected: + bool skipLines(char start); + void parseExt(const char* pre, uint32 maxVar, SharedContext& ctx); + // ::= { } + void parseProject(uint32 maxVar, SharedContext& ctx); + // ::= { } + void parseAssume(uint32 maxVar); + // ::= + void parseHeuristic(uint32 maxVar, SharedContext& ctx); + // ::= "range" + // | + void parseOutput(uint32 maxVar, SharedContext& ctx); + void parseGraph(uint32 maxVar, const char* pre, ExtDepGraph& graph); + virtual void addObjective(const WeightLitVec& vec) = 0; + virtual void addAssumption(Literal x) = 0; +private: + Literal matchLit(Var max); +}; +//! Parser for (extended) dimacs format. +class DimacsReader : public SatReader { +public: + static bool accept(char c) { return c == 'c' || c == 'p'; } + DimacsReader(SatBuilder&); +protected: + virtual bool doAttach(bool& inc); + virtual bool doParse(); + virtual void addObjective(const WeightLitVec& vec); + virtual void addAssumption(Literal x); +private: + SatBuilder* program_; + Var numVar_; + bool wcnf_; +}; +//! Parser for opb format. +class OpbReader : public SatReader { +public: + OpbReader(PBBuilder&); + static bool accept(char c) { return c == '*'; } +protected: + virtual bool doAttach(bool& inc); + virtual bool doParse(); + virtual void addObjective(const WeightLitVec& vec); + virtual void addAssumption(Literal x); + void parseOptObjective(); + void parseConstraint(); + void parseSum(); + void parseTerm(); +private: + PBBuilder* program_; + weight_t minCost_; + weight_t maxCost_; + struct Temp { + WeightLitVec lits; + LitVec term; + weight_t bound; + bool eq; + } active_; +}; +//! Parser for SAT or PB problems. +class SatParser : public ProgramParser { +public: + explicit SatParser(SatBuilder& prg); + explicit SatParser(PBBuilder& prg); + ~SatParser(); +protected: + virtual StrategyType* doAccept(std::istream& str, const ParserOptions& o); +private: + SatReader* reader_; +}; +//@} + +} +#endif diff --git a/clasp/clasp/pod_vector.h b/clasp/clasp/pod_vector.h new file mode 100644 index 000000000..19783c610 --- /dev/null +++ b/clasp/clasp/pod_vector.h @@ -0,0 +1,107 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +#ifndef CLASP_POD_VECTOR_H_INCLUDED +#define CLASP_POD_VECTOR_H_INCLUDED +#include +#include +#include +#include + +namespace Clasp { + +#if CLASP_USE_STD_VECTOR + template + struct PodVector { + typedef std::vector type; + static void destruct(type& t) {t.clear();} + }; +#else + //! Type selector for a vector type optimized for storing POD-types. + template + struct PodVector { + typedef bk_lib::pod_vector type; + static void destruct(type& t) { + for (typename type::size_type i = 0, end = t.size(); i != end; ++i) { + t[i].~Type(); + } + t.clear(); + } + }; +#endif +inline uint32 toU32(std::size_t x) { + assert(sizeof(std::size_t) <= sizeof(uint32) || x <= static_cast(UINT32_MAX)); + return static_cast(x); +} +template +inline uint32 sizeVec(const T& c) { return toU32(c.size()); } +template +inline void releaseVec(T& t) { + T().swap(t); +} + +template +inline void shrinkVecTo(T& t, typename T::size_type j) { + t.erase(t.begin()+j, t.end()); +} + +template +inline void growVecTo(T& vec, typename T::size_type j, const typename T::value_type& val = typename T::value_type()) { + if (vec.size() < j) { + if (vec.capacity() < j) { vec.reserve(j + j / 2); } + vec.resize(j, val); + } +} + +template +void moveDown(T& t, typename T::size_type from, typename T::size_type to) { + for (typename T::size_type end = t.size(); from != end;) { + t[to++] = t[from++]; + } + shrinkVecTo(t, to); +} +//! A simple vector-based fifo queue for storing POD-types. +template +struct PodQueue { + typedef typename PodVector::type vec_type; + typedef typename vec_type::size_type size_type; + PodQueue() : qFront(0) {} + bool empty() const { return qFront == vec.size(); } + size_type size() const { return vec.size() - qFront; } + const T& front() const { return vec[qFront]; } + const T& back() const { return vec.back(); } + T& front() { return vec[qFront]; } + T& back() { return vec.back(); } + void push(const T& x){ vec.push_back(x); } + void pop() { ++qFront; } + T pop_ret() { return vec[qFront++]; } + void clear() { vec.clear(); qFront = 0; } + void rewind() { qFront = 0; } + vec_type vec; // the underlying vector holding the items + size_type qFront; // front position +}; + +} + +#endif diff --git a/clasp/clasp/program_builder.h b/clasp/clasp/program_builder.h new file mode 100644 index 000000000..286065d4d --- /dev/null +++ b/clasp/clasp/program_builder.h @@ -0,0 +1,252 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +#ifndef CLASP_PROGRAM_BUILDER_H_INCLUDED +#define CLASP_PROGRAM_BUILDER_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include +#include POTASSCO_EXT_INCLUDE(unordered_map) +#include + +namespace Clasp { + +/** + * \file + * \defgroup problem Input + * \brief Classes and functions for defining input programs. + */ +//@{ + +//! Interface for defining an input program. +class ProgramBuilder { +public: + typedef SharedMinimizeData SharedMinimize; + typedef SingleOwnerPtr MinPtr; + + ProgramBuilder(); + virtual ~ProgramBuilder(); + //! Starts the definition of a program. + /*! + * This function shall be called exactly once before a new program is defined. + * It discards any previously added program. + * + * \param ctx The context object in which the program should be stored. + */ + bool startProgram(SharedContext& ctx); + //! Parses the given stream as a program of type() and adds it to this object. + bool parseProgram(std::istream& prg); + //! Unfreezes a currently frozen program. + bool updateProgram(); + //! Loads the program into the shared context passed to startProgram(). + bool endProgram(); + //! Returns any assumptions that shall hold during solving. + /*! + * \pre frozen() + */ + void getAssumptions(LitVec& out) const; + //! Returns bounds that shall hold during minimization. + void getWeakBounds(SumVec& out) const; + //! Returns the type of program that is created by this builder. + int type() const { return doType(); } + //! Returns true if the program is currently frozen. + bool frozen() const { return frozen_; } + //! Returns true if the program is not conflicting. + virtual bool ok() const; + //! Returns the stored context object. + SharedContext* ctx() const { return ctx_; } + //! Returns a parser for this type of program associated with this object. + ProgramParser& parser(); +protected: + void addMinLit(weight_t prio, WeightLiteral x); + void setFrozen(bool frozen) { frozen_ = frozen; } + void setCtx(SharedContext* x){ ctx_ = x; } + void markOutputVariables() const; +private: + typedef SingleOwnerPtr MinBuildPtr; + typedef SingleOwnerPtr ParserPtr; + ProgramBuilder(const ProgramBuilder&); + ProgramBuilder& operator=(ProgramBuilder&); + virtual bool doStartProgram() = 0; + virtual bool doUpdateProgram() = 0; + virtual bool doEndProgram() = 0; + virtual void doGetWeakBounds(SumVec& out) const; + virtual void doGetAssumptions(LitVec& out) const = 0; + virtual int doType() const = 0; + virtual ProgramParser* doCreateParser() = 0; + SharedContext* ctx_; + ParserPtr parser_; + bool frozen_; +}; + +//! A class for defining a SAT-problem in CNF. +class SatBuilder : public ProgramBuilder { +public: + explicit SatBuilder(); + // program definition + + //! Creates necessary variables and prepares the problem. + /*! + * \param numVars Number of variables to create. + * \param hardClauseWeight Weight identifying hard clauses (0 means no weight). + * Clauses added with a weight != hardClauseWeight are + * considered soft clauses (see addClause()). + * \param clauseHint A hint on how many clauses will be added. + */ + void prepareProblem(uint32 numVars, wsum_t hardClauseWeight = 0, uint32 clauseHint = 100); + //! Returns the number of variables in the problem. + Var numVars() const { return vars_; } + //! Adds the given clause to the problem. + /*! + * The SatBuilder supports the creation of (weighted) MaxSAT problems + * via the creation of "soft clauses". For this, clauses + * added to this object have an associated weight cw. If cw + * does not equal hardClauseWeight (typically 0), the clause is a + * soft clause and not satisfying it results in a penalty of cw. + * + * \pre v <= numVars(), for all variables v occuring in clause. + * \pre cw >= 0. + * \param clause The clause to add. + * \param cw The weight associated with the clause. + */ + bool addClause(LitVec& clause, wsum_t cw = 0); + //! Adds the given PB-constraint (sum(lits) >= bound) to the problem. + bool addConstraint(WeightLitVec& lits, weight_t bound); + //! Adds min as an objective function to the problem. + bool addObjective(const WeightLitVec& min); + //! Adds v to the set of projection vars. + void addProject(Var v); + //! Adds x to the set of initial assumptions. + void addAssumption(Literal x); +private: + typedef PodVector::type VarState; + bool doStartProgram(); + ProgramParser* doCreateParser(); + int doType() const { return Problem_t::Sat; } + bool doUpdateProgram() { return !frozen(); } + void doGetAssumptions(LitVec& a) const { a.insert(a.end(), assume_.begin(), assume_.end()); } + bool doEndProgram(); + bool satisfied(LitVec& clause); + bool markAssigned(); + void markLit(Literal x) { varState_[x.var()] |= 1 + x.sign(); } + VarState varState_; + LitVec softClauses_; + LitVec assume_; + wsum_t hardWeight_; + Var vars_; + uint32 pos_; +}; + +//! A class for defining a PB-problem. +class PBBuilder : public ProgramBuilder { +public: + PBBuilder(); + // program definition + //! Creates necessary variables and prepares the problem. + /*! + * \param numVars Number of problem variables to create. + * \param maxProduct Max number of products in the problem. + * \param maxSoft Max number of soft constraints in the problem. + * \param constraintHint A hint on how many clauses will be added. + */ + void prepareProblem(uint32 numVars, uint32 maxProduct, uint32 maxSoft, uint32 constraintHint = 100); + //! Returns the number of variables in the problem. + uint32 numVars() const { return auxVar_ - 1; } + //! Adds the given PB-constraint to the problem. + /*! + * A PB-constraint consists of a list of weighted Boolean literals (lhs), + * a comparison operator (either >= or =), and an integer bound (rhs). + * + * \pre v <= numVars(), for all variables v occuring in lits. + * + * \param lits The lhs of the PB-constraint. + * \param bound The rhs of the PB-constraint. + * \param eq If true, use '=' instead of '>=' as comparison operator. + * \param cw If > 0, treat constraint as soft constraint with weight cw. + */ + bool addConstraint(WeightLitVec& lits, weight_t bound, bool eq = false, weight_t cw = 0); + //! Adds the given product to the problem. + /*! + * The function creates the equality x == l1 && ... && ln, where x is a new + * literal and each li is a literal in lits. + * \pre The number of products added so far is < maxProduct that was given in prepareProblem(). + */ + Literal addProduct(LitVec& lits); + //! Adds min as an objective function to the problem. + bool addObjective(const WeightLitVec& min); + //! Adds v to the set of projection vars. + void addProject(Var v); + //! Adds x to the set of initial assumptions. + void addAssumption(Literal x); + //! Only allow solutions where the sum of violated soft constraint is less than bound. + bool setSoftBound(wsum_t bound); +private: + struct PKey { + LitVec lits; + std::size_t operator()(const PKey& k) const { return k.lits[0].rep(); } + bool operator()(const PKey& lhs, const PKey& rhs) const { return lhs.lits == rhs.lits; } + }; + typedef POTASSCO_EXT_NS::unordered_map ProductIndex; + bool doStartProgram(); + void doGetWeakBounds(SumVec& out) const; + int doType() const { return Problem_t::Pb; } + bool doUpdateProgram() { return !frozen(); } + void doGetAssumptions(LitVec& a) const { a.insert(a.end(), assume_.begin(), assume_.end()); } + ProgramParser* doCreateParser(); + bool doEndProgram(); + bool productSubsumed(LitVec& lits, PKey& prod); + void addProductConstraints(Literal eqLit, LitVec& lits); + Var getAuxVar(); + ProductIndex products_; + PKey prod_; + LitVec assume_; + uint32 auxVar_; + uint32 endVar_; + wsum_t soft_; +}; + +//! Adapts a Sat or PB builder to the Potassco::AbstractProgram interface. +class BasicProgramAdapter : public Potassco::AbstractProgram { +public: + BasicProgramAdapter(ProgramBuilder& prg); + void initProgram(bool inc); + void beginStep(); + void rule(Potassco::Head_t ht, const Potassco::AtomSpan& head, const Potassco::LitSpan& body); + void rule(Potassco::Head_t ht, const Potassco::AtomSpan& head, Potassco::Weight_t bound, const Potassco::WeightLitSpan& body); + void minimize(Potassco::Weight_t prio, const Potassco::WeightLitSpan& lits); +protected: + ProgramBuilder* prg_; + LitVec clause_; + WeightLitVec constraint_; + bool inc_; +}; + +} +#endif diff --git a/clasp/clasp/satelite.h b/clasp/clasp/satelite.h new file mode 100644 index 000000000..f15cf6f17 --- /dev/null +++ b/clasp/clasp/satelite.h @@ -0,0 +1,174 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +//! \file +//! \brief Types and functions for SAT-based preprocessing. +#ifndef CLASP_SATELITE_H_INCLUDED +#define CLASP_SATELITE_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +namespace Clasp { +//! SatElite preprocessor for clauses. +/*! + * \ingroup shared + * The preprocessor implements subsumption, self-subsumption, variable elimination, + * and (optionally) blocked clause elimination. + * \see + * - Niklas Eén, Armin Biere: "Effective Preprocessing in SAT through Variable and Clause Elimination" + * - Matti Järvisalo, Armin Biere, Marijn Heule: "Blocked Clause Elimination" + * - Parts of the SatElite preprocessor are adapted from MiniSAT 2.0 beta + * available under the MIT licence from http://minisat.se/MiniSat.html + * . + */ +class SatElite : public Clasp::SatPreprocessor { +public: + SatElite(); + ~SatElite(); + Clasp::SatPreprocessor* clone(); + //! Event type for providing information on preprocessing progress. + struct Progress : public Event_t { + enum EventOp { event_algorithm = '*', event_bce = 'B', event_var_elim = 'E', event_subsumption = 'S', }; + Progress(SatElite* p, EventOp o, uint32 i, uint32 m) : Event_t(Event::subsystem_prepare, Event::verbosity_high), self(p), cur(i), max(m) { + op = (uint32)o; + } + SatElite* self; + uint32 cur; + uint32 max; + }; +protected: + bool initPreprocess(Options& opts); + void reportProgress(Progress::EventOp, uint32 curr, uint32 max); + bool doPreprocess(); + void doExtendModel(ValueVec& m, LitVec& open); + void doCleanUp(); +private: + typedef PodVector::type TouchedList; + typedef bk_lib::left_right_sequence ClWList; + typedef ClWList::left_iterator ClIter; + typedef ClWList::right_iterator WIter; + typedef std::pair ClRange; + SatElite(const SatElite&); + const SatElite& operator=(const SatElite&); + // For each var v + struct OccurList { + OccurList() : pos(0), bce(0), dirty(0), neg(0), litMark(0) {} + ClWList refs; // left : ids of clauses containing v or ~v (var() == id, sign() == v or ~v) + // right: ids of clauses watching v or ~v (literal 0 is the watched literal) + uint32 pos:30; // number of *relevant* clauses containing v + uint32 bce:1; // in BCE queue? + uint32 dirty:1; // does clauses contain removed clauses? + uint32 neg:30; // number of *relevant* clauses containing v + uint32 litMark:2; // 00: no literal of v marked, 01: v marked, 10: ~v marked + uint32 numOcc() const { return pos + neg; } + uint32 cost() const { return pos * neg; } + ClRange clauseRange() const { return ClRange(const_cast(refs).left_begin(), const_cast(refs).left_end()); } + void clear() { + this->~OccurList(); + new (this) OccurList(); + } + void addWatch(uint32 clId) { refs.push_right(clId); } + void removeWatch(uint32 clId) { refs.erase_right(std::find(refs.right_begin(), refs.right_end(), clId)); } + void add(uint32 id, bool sign){ + pos += uint32(!sign); + neg += uint32(sign); + refs.push_left(Literal(id, sign)); + } + void remove(uint32 id, bool sign, bool updateClauseList) { + pos -= uint32(!sign); + neg -= uint32(sign); + if (updateClauseList) { + refs.erase_left(std::find(refs.left_begin(), refs.left_end(), Literal(id, sign))); + } + else { dirty = 1; } + } + // note: only one literal of v shall be marked at a time + bool marked(bool sign) const { return (litMark & (1+int(sign))) != 0; } + void mark(bool sign) { litMark = (1+int(sign)); } + void unmark() { litMark = 0; } + }; + struct LessOccCost { + explicit LessOccCost(OccurList*& occ) : occ_(occ) {} + bool operator()(Var v1, Var v2) const { return occ_[v1].cost() < occ_[v2].cost(); } + private: + const LessOccCost& operator=(LessOccCost&); + OccurList*& occ_; + }; + typedef bk_lib::indexed_priority_queue ElimHeap; + Clause* peekSubQueue() const { + assert(qFront_ < queue_.size()); + return (Clause*)clause( queue_[qFront_] ); + } + inline Clause* popSubQueue(); + inline void addToSubQueue(uint32 clauseId); + void updateHeap(Var v) { + assert(ctx_); + if (!ctx_->varInfo(v).frozen() && !ctx_->eliminated(v)) { + elimHeap_.update(v); + if (occurs_[v].bce == 0 && occurs_[0].bce != 0) { + occurs_[0].addWatch(v); + occurs_[v].bce = 1; + } + } + } + inline uint32 findUnmarkedLit(const Clause& c, uint32 x) const; + void attach(uint32 cId, bool initialClause); + void detach(uint32 cId); + void bceVeRemove(uint32 cId, bool freeId, Var v, bool blocked); + bool propagateFacts(); + bool backwardSubsume(); + Literal subsumes(const Clause& c, const Clause& other, Literal res) const; + bool strengthenClause(uint32 clauseId, Literal p); + bool subsumed(LitVec& cl); + bool eliminateVars(); + bool bce(); + bool bceVe(Var v, uint32 maxCnt); + ClRange splitOcc(Var v, bool mark); + bool trivialResolvent(const Clause& c2, Var v) const; + void markAll(const Literal* lits, uint32 size) const; + void unmarkAll(const Literal* lits, uint32 size) const; + bool addResolvent(uint32 newId, const Clause& c1, const Clause& c2); + bool cutoff(Var v) const { + return opts_->occLimit(occurs_[v].pos, occurs_[v].neg) + || (occurs_[v].cost() == 0 && ctx_->preserveModels()); + } + bool timeout() const { return time(0) > timeout_; } + enum OccSign { pos = 0, neg = 1}; + OccurList* occurs_; // occur list for each variable + ElimHeap elimHeap_; // candidates for variable elimination; ordered by increasing occurrence-cost + VarVec occT_[2]; // temporary clause lists used in eliminateVar + ClauseList resCands_; // pairs of clauses to be resolved + LitVec resolvent_; // temporary, used in addResolvent + VarVec queue_; // indices of clauses waiting for subsumption-check + uint32 qFront_; // front of queue_, i.e. [queue_.begin()+qFront_, queue.end()) is the subsumption queue + uint32 facts_; // [facts_, solver.trail.size()): new top-level facts + std::time_t timeout_; // stop once time > timeout_ +}; +} +#endif diff --git a/clasp/clasp/shared_context.h b/clasp/clasp/shared_context.h new file mode 100644 index 000000000..a52787f24 --- /dev/null +++ b/clasp/clasp/shared_context.h @@ -0,0 +1,953 @@ +// +// Copyright (c) 2010-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_SHARED_CONTEXT_H_INCLUDED +#define CLASP_SHARED_CONTEXT_H_INCLUDED +#ifdef _MSC_VER +#pragma once +#endif +#include +#include +#include +#include +#include +/*! + * \file + * \brief Contains common types shared between different solvers. + */ +namespace Clasp { +class Assignment; +class SharedLiterals; +struct SolverStats; +class StatisticObject; +typedef Asp::PrgDepGraph PrgDepGraph; +//! An immutable string with efficient copying. +/*! + * \ingroup misc + */ +class ConstString { +public: + //! Creates a string from str. + /*! + * \note If o is Ownership_t::Acquire (the default), str is copied. + * Otherwise, no copy is made and the lifetime of str shall extend + * past that of the constructed object. + */ + ConstString(const char* str = "", Ownership_t::Type o = Ownership_t::Acquire); + //! Creates a copy of str. + ConstString(const StrView& str); + ConstString(const ConstString& other); + ~ConstString(); + static ConstString fromLiteral(const char* str) { return ConstString(str, Ownership_t::Retain); } + ConstString& operator=(const ConstString& rhs); + const char* c_str() const; + operator const char* () const { return c_str(); } + void swap(ConstString& o); +private: + typedef uint64 RefType; + RefType ref_; +}; +/** +* \defgroup shared SharedContext +* \brief %SharedContext and related types. +*/ + +//! Base class for event handlers. +/*! + * \ingroup shared + */ +class EventHandler : public ModelHandler { +public: + //! Creates a handler for events with given verbosity or lower. + explicit EventHandler(Event::Verbosity verbosity = Event::verbosity_quiet); + virtual ~EventHandler(); + //! Sets the verbosity for the given event source. + /*! + * Events with higher verbosity are not dispatched to this handler. + */ + void setVerbosity(Event::Subsystem sys, Event::Verbosity verb); + //! Sets the active event source to sys if sys is not yet active. + bool setActive(Event::Subsystem sys); + Event::Subsystem active() const; + uint32 verbosity(Event::Subsystem sys) const { return (uint32(verb_) >> (uint32(sys)<(ev.system))) { onEvent(ev); } } + virtual void onEvent(const Event& /* ev */) {} + virtual bool onModel(const Solver&, const Model&) { return true; } +private: + enum { VERB_SHIFT = 2u, VERB_MAX = 15u }; + EventHandler(const EventHandler&); + EventHandler& operator=(const EventHandler&); + uint16 verb_; + uint16 sys_; +}; + +//! Event type for log or warning messages. +/*! + * \ingroup enumerator + */ +struct LogEvent : Event_t { + enum Type { Message = 'M', Warning = 'W' }; + LogEvent(Subsystem sys, Verbosity verb, Type t, const Solver* s, const char* what) : Event_t(sys, verb), solver(s), msg(what) { + op = static_cast(t); + } + bool isWarning() const { return op == static_cast(Warning); } + const Solver* solver; + const char* msg; +}; + +//! Base class for preprocessors working on clauses only. +/*! + * \ingroup shared + */ +class SatPreprocessor { +public: + //! A clause class optimized for preprocessing. + class Clause { + public: + static Clause* newClause(const Literal* lits, uint32 size); + static uint64 abstractLit(Literal p) { return uint64(1) << ((p.var()-1) & 63); } + uint32 size() const { return size_; } + const Literal& operator[](uint32 x) const { return lits_[x]; } + bool inQ() const { return inQ_ != 0; } + uint64 abstraction() const { return data_.abstr; } + Clause* next() const { return data_.next; } + bool marked() const { return marked_ != 0;} + Literal& operator[](uint32 x) { return lits_[x]; } + void setInQ(bool b) { inQ_ = (uint32)b;} + void setMarked(bool b) { marked_ = (uint32)b;} + uint64& abstraction() { return data_.abstr; } + Clause* linkRemoved(Clause* next) { data_.next = next; return this; } + void strengthen(Literal p); + void simplify(Solver& s); + void destroy(); + private: + Clause(const Literal* lits, uint32 size); + union { + uint64 abstr; // abstraction of literals + Clause* next; // next removed clause + } data_; + uint32 size_ : 30; // size of the clause + uint32 inQ_ : 1; // in todo-queue? + uint32 marked_ : 1; // a marker flag + Literal lits_[1]; // literals of the clause: [lits_[0], lits_[size_]) + }; + + SatPreprocessor(); + virtual ~SatPreprocessor(); + + //! Creates a clone of this preprocessor. + /*! + * \note The function does not clone any clauses already added to *this. + */ + virtual SatPreprocessor* clone() = 0; + + uint32 numClauses() const { return (uint32)clauses_.size(); } + //! Adds a clause to the preprocessor. + /*! + * \pre clause is not a tautology (i.e. does not contain both l and ~l) + * \pre clause is a set (i.e. does not contain l more than once) + * \return true if clause was added. False if adding the clause makes the problem UNSAT + */ + bool addClause(const LitVec& cl) { return addClause(!cl.empty() ? &cl[0] : 0, sizeVec(cl)); } + bool addClause(const Literal* clause, uint32 size); + //! Runs the preprocessor on all clauses that were previously added. + /*! + * \pre A ctx.startAddConstraint() was called and has variables for all added clauses. + */ + bool preprocess(SharedContext& ctx, SatPreParams& opts); + bool preprocess(SharedContext& ctx); + + //! Force removal of state & clauses. + void cleanUp(bool discardEliminated = false); + + //! Extends the model in m with values for any eliminated variables. + void extendModel(ValueVec& m, LitVec& open); + struct Stats { + Stats() : clRemoved(0), clAdded(0), litsRemoved(0) {} + uint32 clRemoved; + uint32 clAdded; + uint32 litsRemoved; + } stats; + typedef SatPreParams Options; +protected: + typedef PodVector::type ClauseList; + virtual bool initPreprocess(SatPreParams& opts) = 0; + virtual bool doPreprocess() = 0; + virtual void doExtendModel(ValueVec& m, LitVec& open) = 0; + virtual void doCleanUp() = 0; + Clause* clause(uint32 clId) { return clauses_[clId]; } + const Clause* clause(uint32 clId) const { return clauses_[clId]; } + void freezeSeen(); + void discardClauses(bool discardEliminated); + void setClause(uint32 clId, const LitVec& cl) { + clauses_[clId] = Clause::newClause(&cl[0], (uint32)cl.size()); + } + void destroyClause(uint32 clId){ + clauses_[clId]->destroy(); + clauses_[clId] = 0; + ++stats.clRemoved; + } + void eliminateClause(uint32 id){ + elimTop_ = clauses_[id]->linkRemoved(elimTop_); + clauses_[id] = 0; + ++stats.clRemoved; + } + SharedContext* ctx_; // current context + const Options* opts_; // active options + Clause* elimTop_; // stack of blocked/eliminated clauses +private: + SatPreprocessor(const SatPreprocessor&); + SatPreprocessor& operator=(const SatPreprocessor&); + ClauseList clauses_; // initial non-unit clauses + LitVec units_; // initial unit clauses + Range32 seen_; // vars seen in previous step +}; + +/////////////////////////////////////////////////////////////////////////////// +// Problem statistics +/////////////////////////////////////////////////////////////////////////////// +/*! + * \addtogroup shared + * @{ + */ +//! A struct for aggregating basic problem statistics. +struct ProblemStats { + ProblemStats() { reset(); } + struct { uint32 num, eliminated, frozen; } vars; + struct { uint32 other, binary, ternary; } constraints; + uint32 acycEdges; + uint32 complexity; + void reset() { std::memset(this, 0, sizeof(*this)); } + uint32 numConstraints() const { return constraints.other + constraints.binary + constraints.ternary; } + void diff(const ProblemStats& o) { + vars.num = std::max(vars.num, o.vars.num)-std::min(vars.num, o.vars.num); + vars.eliminated = std::max(vars.eliminated, o.vars.eliminated)-std::min(vars.eliminated, o.vars.eliminated); + vars.frozen = std::max(vars.frozen, o.vars.frozen)-std::min(vars.frozen, o.vars.frozen); + constraints.other = std::max(constraints.other, o.constraints.other) - std::min(constraints.other, o.constraints.other); + constraints.binary = std::max(constraints.binary, o.constraints.binary) - std::min(constraints.binary, o.constraints.binary); + constraints.ternary= std::max(constraints.ternary, o.constraints.ternary) - std::min(constraints.ternary, o.constraints.ternary); + acycEdges = std::max(acycEdges, o.acycEdges) - std::min(acycEdges, o.acycEdges); + } + void accu(const ProblemStats& o) { + vars.num += o.vars.num; + vars.eliminated += o.vars.eliminated; + vars.frozen += o.vars.frozen; + constraints.other += o.constraints.other; + constraints.binary += o.constraints.binary; + constraints.ternary += o.constraints.ternary; + acycEdges += o.acycEdges; + } + // StatisticObject + static uint32 size(); + static const char* key(uint32 i); + StatisticObject at(const char* k) const; +}; + +//! Stores static information about a variable. +struct VarInfo { + //! Possible flags. + enum Flag { + Mark_p = 0x1u, //!< Mark for positive literal. + Mark_n = 0x2u, //!< Mark for negative literal. + Input = 0x4u, //!< Is this var an input variable? + Body = 0x8u, //!< Is this var representing a body? + Eq = 0x10u, //!< Is this var representing both a body and an atom? + Nant = 0x20u, //!< Is this var in NAnt(P)? + Frozen = 0x40u, //!< Is the variable frozen? + Output = 0x80u //!< Is the variable an output variable? + }; + static uint8 flags(VarType t) { + if (t == Var_t::Body) { return VarInfo::Body; } + if (t == Var_t::Hybrid){ return VarInfo::Eq; } + return 0; + } + explicit VarInfo(uint8 r = 0) : rep(r) { } + //! Returns the type of the variable (or Var_t::Hybrid if variable was created with parameter eq=true). + VarType type() const { return has(VarInfo::Eq) ? Var_t::Hybrid : VarType(Var_t::Atom + has(VarInfo::Body)); } + //! Returns whether var is part of negative antecedents (occurs negatively or in the head of a choice rule). + bool nant() const { return has(VarInfo::Nant); } + //! Returns true if var is excluded from variable elimination. + bool frozen() const { return has(VarInfo::Frozen); } + //! Returns true if var is an input variable. + bool input() const { return has(VarInfo::Input); } + //! Returns true if var is marked as output variable. + bool output() const { return has(VarInfo::Output); } + //! Returns the preferred sign of this variable w.r.t its type. + /*! + * \return false (i.e no sign) if var originated from body, otherwise true. + */ + bool preferredSign() const { return !has(VarInfo::Body); } + + bool has(Flag f) const { return (rep & flag(f)) != 0; } + bool has(uint32 f) const { return (rep & f) != 0; } + bool hasAll(uint32 f)const { return (rep & f) == f; } + void set(Flag f) { rep |= flag(f); } + void toggle(Flag f) { rep ^= flag(f); } + static uint8 flag(Flag x) { return uint8(x); } + uint8 rep; +}; + +//! A class for efficiently storing and propagating binary and ternary clauses. +/*! + * \ingroup shared_con + */ +class ShortImplicationsGraph { +public: + ShortImplicationsGraph(); + ~ShortImplicationsGraph(); + enum ImpType { binary_imp = 2, ternary_imp = 3 }; + + //! Makes room for nodes number of nodes. + void resize(uint32 nodes); + //! Mark the instance as shared/unshared. + /*! + * A shared instance adds learnt binary/ternary clauses + * to specialized shared blocks of memory. + */ + void markShared(bool b) { shared_ = b; } + //! Adds the given constraint to the implication graph. + /*! + * \return true iff a new implication was added. + */ + bool add(ImpType t, bool learnt, const Literal* lits); + + //! Removes p and its implications. + /*! + * \pre s.isTrue(p) + */ + void removeTrue(const Solver& s, Literal p); + + //! Propagates consequences of p following from binary and ternary clauses. + /*! + * \pre s.isTrue(p) + */ + bool propagate(Solver& s, Literal p) const; + //! Propagates immediate consequences of p following from binary clauses only. + bool propagateBin(Assignment& out, Literal p, uint32 dl) const; + //! Checks whether there is a reverse arc implying p and if so returns it in out. + bool reverseArc(const Solver& s, Literal p, uint32 maxLev, Antecedent& out) const; + + uint32 numBinary() const { return bin_[0]; } + uint32 numTernary()const { return tern_[0]; } + uint32 numLearnt() const { return bin_[1] + tern_[1]; } + uint32 numEdges(Literal p) const; + uint32 size() const { return sizeVec(graph_); } + //! Applies op on all unary- and binary implications following from p. + /*! + * OP must provide two functions: + * - bool unary(Literal, Literal) + * - bool binary(Literal, Literal, Literal) + * The first argument will be p, the second (resp. third) the unary + * (resp. binary) clause implied by p. + * \note For learnt imps, at least one literal has its watch-flag set. + */ + template + bool forEach(Literal p, const OP& op) const { + const ImplicationList& x = graph_[p.id()]; + if (x.empty()) return true; + ImplicationList::const_right_iterator rEnd = x.right_end(); // prefetch + for (ImplicationList::const_left_iterator it = x.left_begin(), end = x.left_end(); it != end; ++it) { + if (!op.unary(p, *it)) { return false; } + } + for (ImplicationList::const_right_iterator it = x.right_begin(); it != rEnd; ++it) { + if (!op.binary(p, it->first, it->second)) { return false; } + } +#if CLASP_HAS_THREADS + for (Block* b = (x).learnt; b ; b = b->next) { + p.flag(); bool r = true; + for (Block::const_iterator imp = b->begin(), endOf = b->end(); imp != endOf; ) { + if (!imp->flagged()) { r = op.binary(p, imp[0], imp[1]); imp += 2; } + else { r = op.unary(p, imp[0]); imp += 1; } + if (!r) { return false; } + } + } +#endif + return true; + } +private: + ShortImplicationsGraph(const ShortImplicationsGraph&); + ShortImplicationsGraph& operator=(ShortImplicationsGraph&); + struct Propagate; + struct ReverseArc; +#if CLASP_HAS_THREADS + struct Block { + typedef Clasp::mt::atomic atomic_size; + typedef Clasp::mt::atomic atomic_ptr; + typedef const Literal* const_iterator; + typedef Literal* iterator; + enum { block_cap = (64 - (sizeof(atomic_size)+sizeof(atomic_ptr)))/sizeof(Literal) }; + explicit Block(); + const_iterator begin() const { return data; } + const_iterator end() const { return data+size(); } + iterator end() { return data+size(); } + uint32 size() const { return size_lock >> 1; } + bool tryLock(uint32& lockedSize); + void addUnlock(uint32 lockedSize, const Literal* x, uint32 xs); + atomic_ptr next; + atomic_size size_lock; + Literal data[block_cap]; + }; + typedef Block::atomic_ptr SharedBlockPtr; + typedef bk_lib::left_right_sequence, 64-sizeof(SharedBlockPtr)> ImpListBase; + struct ImplicationList : public ImpListBase { + ImplicationList() : ImpListBase() { learnt = 0; } + ImplicationList(const ImplicationList& other) : ImpListBase(other), learnt() { + learnt = static_cast(other.learnt); + } + ImplicationList& operator=(const ImplicationList& other) { + ImpListBase::operator=(other); + learnt = static_cast(other.learnt); + return *this; + } + ~ImplicationList(); + bool hasLearnt(Literal q, Literal r = lit_false()) const; + void addLearnt(Literal q, Literal r = lit_false()); + void simplifyLearnt(const Solver& s); + bool empty() const { return ImpListBase::empty() && learnt == static_cast(0); } + void move(ImplicationList& other); + void clear(bool b); + SharedBlockPtr learnt; + }; +#else + typedef bk_lib::left_right_sequence, 64> ImplicationList; +#endif + ImplicationList& getList(Literal p) { return graph_[p.id()]; } + void remove_bin(ImplicationList& w, Literal p); + void remove_tern(ImplicationList& w, Literal p); + typedef PodVector::type ImpLists; + ImpLists graph_; // one implication list for each literal + uint32 bin_[2]; // number of binary constraints (0: problem / 1: learnt) + uint32 tern_[2]; // number of ternary constraints(0: problem / 1: learnt) + bool shared_; +}; + +//! Base class for distributing learnt knowledge between solvers. +class Distributor { +public: + struct Policy { + enum Types { + no = 0, + conflict = Constraint_t::Conflict, + loop = Constraint_t::Loop, + all = conflict | loop, + implicit = all + 1 + }; + Policy(uint32 a_sz = 0, uint32 a_lbd = 0, uint32 a_type = 0) : size(a_sz), lbd(a_lbd), types(a_type) {} + uint32 size : 22; /*!< Allow distribution up to this size only. */ + uint32 lbd : 7; /*!< Allow distribution up to this lbd only. */ + uint32 types : 3; /*!< Restrict distribution to these types. */ + }; + static uint64 mask(uint32 i) { return uint64(1) << i; } + static uint32 initSet(uint32 sz) { return (uint64(1) << sz) - 1; } + static bool inSet(uint64 s, uint32 id) { return (s & mask(id)) != 0; } + explicit Distributor(const Policy& p); + virtual ~Distributor(); + bool isCandidate(uint32 size, uint32 lbd, uint32 type) const { + return size <= policy_.size && lbd <= policy_.lbd && ((type & policy_.types) != 0); + } + virtual void publish(const Solver& source, SharedLiterals* lits) = 0; + virtual uint32 receive(const Solver& in, SharedLiterals** out, uint32 maxOut) = 0; +private: + Distributor(const Distributor&); + Distributor& operator=(const Distributor&); + Policy policy_; +}; + +//! Output table that contains predicates to be output on model. +class OutputTable { +public: + typedef ConstString NameType; + typedef Range32 RangeType; + struct PredType { + NameType name; + Literal cond; + uint32 user; + }; + typedef PodVector::type FactVec; + typedef PodVector::type PredVec; + typedef FactVec::const_iterator fact_iterator; + typedef PredVec::const_iterator pred_iterator; + typedef num_iterator range_iterator; + typedef LitVec::const_iterator lit_iterator; + + OutputTable(); + ~OutputTable(); + //! Ignore predicates starting with c. + void setFilter(char c); + //! Adds a fact. + bool add(const NameType& fact); + //! Adds an output predicate, i.e. n is output if c is true. + bool add(const NameType& n, Literal c, uint32 u = 0); + //! Sets a range of output variables. + void setVarRange(const RangeType& r); + void setProjectMode(ProjectMode m); + + //! Returns whether n would be filtered out. + bool filter(const NameType& n) const; + + fact_iterator fact_begin() const { return facts_.begin(); } + fact_iterator fact_end() const { return facts_.end(); } + pred_iterator pred_begin() const { return preds_.begin(); } + pred_iterator pred_end() const { return preds_.end(); } + range_iterator vars_begin() const { return range_iterator(vars_.lo); } + range_iterator vars_end() const { return range_iterator(vars_.hi); } + RangeType vars_range() const { return vars_; } + + ProjectMode projectMode()const { return projMode_ ? static_cast(projMode_) : hasProject() ? ProjectMode_t::Explicit : ProjectMode_t::Output; } + bool hasProject() const { return !proj_.empty(); } + lit_iterator proj_begin() const { return proj_.begin(); } + lit_iterator proj_end() const { return proj_.end(); } + void addProject(Literal x); + + //! Returns the number of output elements, counting each element in a var range. + uint32 size() const; + uint32 numFacts() const { return static_cast(facts_.size()); } + uint32 numPreds() const { return static_cast(preds_.size()); } + uint32 numVars() const { return static_cast(vars_.hi - vars_.lo); } + + //! An optional callback for getting additional theory output. + class Theory { + public: + virtual ~Theory(); + //! Called once on new model m. Shall return 0 to indicate no output. + virtual const char* first(const Model& m) = 0; + //! Shall return 0 to indicate no output. + virtual const char* next() = 0; + }* theory; +private: + FactVec facts_; + PredVec preds_; + LitVec proj_; + Range32 vars_; + int projMode_; + char hide_; +}; +//! A type for storing information to be used in clasp's domain heuristic. +class DomainTable { +public: + DomainTable(); + //! A type storing a single domain modification for a variable. + class ValueType { + public: + ValueType(Var v, DomModType t, int16 bias, uint16 prio, Literal cond); + bool hasCondition() const { return cond_ != 0; } + Literal cond() const { return Literal::fromId(cond_); } + Var var() const { return var_; } + DomModType type() const; + int16 bias() const { return bias_; } + uint16 prio() const { return prio_; } + bool comp() const { return comp_ != 0; } + private: + uint32 cond_ : 31; + uint32 comp_ : 1; + uint32 var_ : 30; + uint32 type_ : 2; + int16 bias_; + uint16 prio_; + }; + typedef PodVector::type DomVec; + typedef DomVec::const_iterator iterator; + + void add(Var v, DomModType t, int16 bias, uint16 prio, Literal cond); + uint32 simplify(); + void reset(); + bool empty() const; + uint32 size() const; + iterator begin() const; + iterator end() const; + LitVec* assume; + + class DefaultAction { + public: + virtual ~DefaultAction(); + virtual void atom(Literal p, HeuParams::DomPref, uint32 strat) = 0; + }; + static void applyDefault(const SharedContext& ctx, DefaultAction& action, uint32 prefSet = 0); +private: + static bool cmp(const ValueType& lhs, const ValueType& rhs) { + return lhs.cond() < rhs.cond() || (lhs.cond() == rhs.cond() && lhs.var() < rhs.var()); + } + DomVec entries_; + uint32 seen_; // size of domain table after last simplify +}; + +//! Aggregates information to be shared between solver objects. +/*! + * Among other things, SharedContext objects store + * static information on variables, an output table, as well as the + * binary and ternary implication graph of the input problem. + * + * Furthermore, a SharedContext object always stores a distinguished + * master solver that is used to store and simplify problem constraints. + * Additional solvers can be added via SharedContext::pushSolver(). + * Once initialization is completed, any additional solvers must be attached + * to this object by calling SharedContext::attach(). + */ +class SharedContext { +public: + typedef PodVector::type SolverVec; + typedef SingleOwnerPtr SccGraph; + typedef SingleOwnerPtr ExtGraph; + typedef Configuration* ConfigPtr; + typedef SingleOwnerPtr DistrPtr; + typedef const ProblemStats& StatsCRef; + typedef DomainTable DomTab; + typedef OutputTable Output; + typedef LitVec::size_type size_type; + typedef ShortImplicationsGraph ImpGraph; + typedef const ImpGraph& ImpGraphRef; + typedef EventHandler* LogPtr; + typedef SingleOwnerPtrSatPrePtr; + typedef SharedMinimizeData* MinPtr; + enum ResizeMode { resize_reserve = 0u, resize_push = 1u, resize_pop = 2u, resize_resize = 3u}; + enum PreproMode { prepro_preserve_models = 1u, prepro_preserve_shown = 2u }; + enum ReportMode { report_default = 0u, report_conflict = 1u }; + enum SolveMode { solve_once = 0u, solve_multi = 1u }; + /*! + * \name Configuration + * \brief Functions for creating and configuring a shared context. + * @{ */ + //! Creates a new object for sharing variables and the binary and ternary implication graph. + explicit SharedContext(); + ~SharedContext(); + //! Resets this object to the state after default construction. + void reset(); + //! Enables event reporting via the given event handler. + void setEventHandler(LogPtr r, ReportMode m = report_default) { progress_ = r; share_.report = uint32(m); } + //! Sets solve mode, which can be used by other objects to query whether multi-shot solving is active. + void setSolveMode(SolveMode m); + //! Sets how to handle physical sharing of constraints. + void setShareMode(ContextParams::ShareMode m); + //! Sets whether the short implication graph should be used for storing short learnt constraints. + void setShortMode(ContextParams::ShortMode m); + //! Sets maximal number of solvers sharing this object. + void setConcurrency(uint32 numSolver, ResizeMode m = resize_reserve); + //! If b is true, sets preprocessing mode to model-preserving operations only. + void setPreserveModels(bool b = true) { setPreproMode(prepro_preserve_models, b); } + //! If b is true, excludes all shown variables from variable elimination. + void setPreserveShown(bool b = true) { setPreproMode(prepro_preserve_shown, b); } + + //! Adds an additional solver to this object and returns it. + Solver& pushSolver(); + //! Configures the statistic object of attached solvers. + /*! + * The level determines the amount of extra statistics. + * \see ExtendedStats + * \see JumpStats + */ + void enableStats(uint32 level); + //! Sets the configuration for this object and its attached solvers. + /*! + * \note If ownership is Ownership_t::Acquire, ownership of c is transferred. + */ + void setConfiguration(Configuration* c, Ownership_t::Type ownership); + SatPrePtr satPrepro; /*!< Preprocessor for simplifying the problem. */ + SccGraph sccGraph; /*!< Program dependency graph - only used for ASP-problems. */ + ExtGraph extGraph; /*!< External dependency graph - given by user. */ + + //! Returns the current configuration used in this object. + ConfigPtr configuration() const { return config_.get(); } + //! Returns the active event handler or 0 if none was set. + LogPtr eventHandler() const { return progress_; } + //! Returns whether this object seeds the RNG of new solvers. + bool seedSolvers() const { return share_.seed != 0; } + //! Returns the number of solvers that can share this object. + uint32 concurrency() const { return share_.count; } + bool preserveModels() const { return (share_.satPreM & prepro_preserve_models) != 0; } + bool preserveShown() const { return (share_.satPreM & prepro_preserve_shown) != 0; } + //! Returns whether physical sharing is enabled for constraints of type t. + bool physicalShare(ConstraintType t) const { return (share_.shareM & (1 + (t != Constraint_t::Static))) != 0; } + //! Returns whether physical sharing of problem constraints is enabled. + bool physicalShareProblem() const { return (share_.shareM & ContextParams::share_problem) != 0; } + //! Returns whether short constraints of type t can be stored in the short implication graph. + bool allowImplicit(ConstraintType t) const { return t != Constraint_t::Static ? share_.shortM != ContextParams::short_explicit : !isShared(); } + //! Returns the configured solve mode. + SolveMode solveMode() const { return static_cast(share_.solveM); } + //@} + + /*! + * \name Problem introspection + * \brief Functions for querying information about the problem. + */ + //@{ + //! Returns true unless the master has an unresolvable top-level conflict. + bool ok() const; + //! Returns whether the problem is currently frozen and therefore ready for being solved. + bool frozen() const { return share_.frozen;} + //! Returns whether more than one solver is actively working on the problem. + bool isShared() const { return frozen() && concurrency() > 1; } + //! Returns whether the problem is more than a simple CNF. + bool isExtended() const { return stats_.vars.frozen != 0; } + //! Returns whether this object has a solver associcated with the given id. + bool hasSolver(uint32 id) const { return id < solvers_.size(); } + //! Returns the master solver associated with this object. + Solver* master() const { return solver(0); } + //! Returns the solver with the given id. + Solver* solver(uint32 id) const { return solvers_[id]; } + + //! Returns the number of problem variables. + /*! + * \note The special sentinel-var 0 is not counted, i.e. numVars() returns + * the number of problem-variables. + * To iterate over all problem variables use a loop like: + * \code + * for (Var i = 1; i <= numVars(); ++i) {...} + * \endcode + */ + uint32 numVars() const { return static_cast(varInfo_.size() - 1); } + //! Returns the number of eliminated vars. + uint32 numEliminatedVars() const { return stats_.vars.eliminated; } + //! Returns true if var represents a valid variable in this problem. + /*! + * \note The range of valid variables is [1..numVars()]. The variable 0 + * is a special sentinel variable. + */ + bool validVar(Var var) const { return var < static_cast(varInfo_.size()); } + //! Returns information about the given variable. + VarInfo varInfo(Var v) const { assert(validVar(v)); return varInfo_[v]; } + //! Returns true if v is currently eliminated, i.e. no longer part of the problem. + bool eliminated(Var v) const; + bool marked(Literal p) const { return varInfo(p.var()).has(VarInfo::Mark_p + p.sign()); } + //! Returns the number of problem constraints. + uint32 numConstraints() const; + //! Returns the number of binary constraints. + uint32 numBinary() const { return btig_.numBinary(); } + //! Returns the number of ternary constraints. + uint32 numTernary() const { return btig_.numTernary(); } + //! Returns the number of unary constraints. + uint32 numUnary() const { return lastTopLevel_; } + //! Returns an estimate of the problem complexity based on the number and type of constraints. + uint32 problemComplexity() const; + //! Returns whether the problem contains minimize (i.e. weak) constraints. + bool hasMinimize() const; + StatsCRef stats() const { return stats_; } + //@} + + /*! + * \name Problem setup + * \brief Functions for specifying the problem. + * + * Problem specification is a four-stage process: + * -# Add variables to the SharedContext object. + * -# Call startAddConstraints(). + * -# Add problem constraints. + * -# Call endInit() to finish the initialization process. + * . + * \note After endInit() was called, other solvers can be attached to this object. + * \note In incremental setting, the process must be repeated for each incremental step. + * + * \note Problem specification is *not* thread-safe, i.e. during initialization no other thread shall + * access the context. + * + * \note !frozen() is a precondition for all functions in this group! + * @{ */ + //! Unfreezes a frozen program and prepares it for updates. + /*! + * The function also triggers forgetting of volatile knowledge and removes + * any auxiliary variables. + * \see requestStepVar() + * \see Solver::popAuxVar() + */ + bool unfreeze(); + + //! Adds a new variable and returns its numerical id. + /*! + * \param type Type of variable. + * \param flags Additional information associated with the new variable. + * \note Problem variables are numbered from 1 onwards! + */ + Var addVar(VarType type, uint8 flags = VarInfo::Nant | VarInfo::Input) { return addVars(1, type, flags); } + Var addVars(uint32 nVars, VarType type, uint8 flags = VarInfo::Nant | VarInfo::Input); + //! Removes the n most recently added problem variables. + /*! + * \pre The variables have either not yet been committed by a call to startAddConstraints() + * or they do not occur in any constraint. + */ + void popVars(uint32 n = 1); + //! Freezes/defreezes a variable (a frozen var is exempt from Sat-preprocessing). + void setFrozen(Var v, bool b); + //! Marks/unmarks v as input variable. + void setInput(Var v, bool b) { set(v, VarInfo::Input, b); } + //! Marks/unmarks v as output variable. + void setOutput(Var v, bool b) { set(v, VarInfo::Output, b); } + //! Marks/unmarks v as part of negative antecedents. + void setNant(Var v, bool b) { set(v, VarInfo::Nant, b); } + void setVarEq(Var v, bool b) { set(v, VarInfo::Eq, b); } + void set(Var v, VarInfo::Flag f, bool b) { if (b != varInfo(v).has(f)) varInfo_[v].toggle(f); } + void mark(Literal p) { assert(validVar(p.var())); varInfo_[p.var()].rep |= (VarInfo::Mark_p + p.sign()); } + void unmark(Literal p) { assert(validVar(p.var())); varInfo_[p.var()].rep &= ~(VarInfo::Mark_p + p.sign()); } + void unmark(Var v) { assert(validVar(v)); varInfo_[v].rep &= ~(VarInfo::Mark_p|VarInfo::Mark_n); } + //! Eliminates the variable v from the problem. + /*! + * \pre v must not occur in any constraint and frozen(v) == false and value(v) == value_free + */ + void eliminate(Var v); + + //! Prepares the master solver so that constraints can be added. + /*! + * Must be called to publish previously added variables to master solver + * and before constraints over these variables can be added. + * \return The master solver associated with this object. + */ + Solver& startAddConstraints(uint32 constraintGuess = 100); + + //! A convenience method for adding facts to the master. + bool addUnary(Literal x); + //! A convenience method for adding binary clauses. + bool addBinary(Literal x, Literal y); + //! A convenience method for adding ternary clauses. + bool addTernary(Literal x, Literal y, Literal z); + //! A convenience method for adding constraints to the master. + void add(Constraint* c); + //! Add weak constraint :~ x.first \[x.second\@p\]. + void addMinimize(WeightLiteral x, weight_t p); + //! Returns a pointer to an optimized representation of all minimize constraints in this problem. + MinPtr minimize(); + //! List of output predicates and/or variables. + Output output; + //! Set of heuristic modifications. + DomTab heuristic; + //! Requests a special variable for tagging volatile knowledge in multi-shot solving. + /*! + * The step variable is created on the next call to endInit() and removed on the next + * call to unfreeze(). + * Once the step variable S is set, learnt constraints containing ~S are + * considered to be "volatile" and removed on the next call to unfreeze(). + * For this to work correctly, S shall be a root assumption during search. + */ + void requestStepVar(); + //! Finishes initialization of the master solver. + /*! + * The function must be called once before search is started. After endInit() + * was called, previously added solvers can be attached to the + * shared context and learnt constraints may be added to solver. + * \param attachAll If true, also calls attach() for all solvers that were added to this object. + * \return If the constraints are initially conflicting, false. Otherwise, true. + * \note + * The master solver can't recover from top-level conflicts, i.e. if endInit() + * returned false, the solver is in an unusable state. + * \post frozen() + */ + bool endInit(bool attachAll = false); + //@} + + /*! + * \name (Parallel) solving + * Functions to be called during (parallel) solving. + * + * \note If not otherwise noted, the functions in this group can be safely called + * from multiple threads. + * @{ */ + //! Returns the active step literal (see requestStepVar()). + Literal stepLiteral() const { return step_; } + //! Attaches the solver with the given id to this object. + /*! + * \note It is safe to attach multiple solvers concurrently + * but the master solver shall not change during the whole operation. + * + * \pre hasSolver(id) + */ + bool attach(uint32 id) { return attach(*solver(id)); } + bool attach(Solver& s); + + //! Detaches the solver with the given id from this object. + /*! + * The function removes any tentative constraints from s. + * Shall be called once after search has stopped. + * \note The function is concurrency-safe w.r.t to different solver objects, + * i.e. in a parallel search different solvers may call detach() + * concurrently. + */ + void detach(uint32 id, bool reset = false) { return detach(*solver(id), reset); } + void detach(Solver& s, bool reset = false); + + DistrPtr distributor;/*!< Distributor object to use for distribution of learnt constraints.*/ + + uint32 winner() const { return share_.winner; } + void setWinner(uint32 sId) { share_.winner = std::min(sId, concurrency()); } + + //! Simplifies the problem constraints w.r.t the master's assignment. + void simplify(LitVec::size_type trailStart, bool shuffle); + //! Removes the constraint with the given idx from the master's db. + void removeConstraint(uint32 idx, bool detach); + //! Removes all minimize constraints from this object. + void removeMinimize(); + + //! Adds the given short implication to the short implication graph if possible. + /*! + * \return + * - > 0 if implication was added. + * - < 0 if implication can't be added because allowImplicit() is false for ct. + * - = 0 if implication is subsumed by some constraint in the short implication graph. + */ + int addImp(ImpGraph::ImpType t, const Literal* lits, ConstraintType ct); + //! Returns the number of learnt short implications. + uint32 numLearntShort() const { return btig_.numLearnt(); } + ImpGraphRef shortImplications() const { return btig_; } + void report(const Event& ev) const { if (progress_) progress_->dispatch(ev); } + bool report(const Solver& s, const Model& m) const { return !progress_ || progress_->onModel(s, m); } + void report(const char* what, const Solver* s = 0) const; + void report(Event::Subsystem sys) const; + void warn(const char* what) const; + ReportMode reportMode() const { return static_cast(share_.report); } + void initStats(Solver& s) const; + SolverStats& solverStats(uint32 sId) const; // stats of solver i + const SolverStats& accuStats(SolverStats& out) const; // accumulates all solver stats in out + MinPtr minimizeNoCreate() const; + //@} +private: + SharedContext(const SharedContext&); + SharedContext& operator=(const SharedContext&); + bool unfreezeStep(); + Literal addStepLit(); + typedef SingleOwnerPtr Config; + typedef PodVector::type VarVec; + void setPreproMode(uint32 m, bool b); + struct Minimize; + ProblemStats stats_; // problem statistics + VarVec varInfo_; // varInfo[v] stores info about variable v + ImpGraph btig_; // binary-/ternary implication graph + Config config_; // active configuration + SolverVec solvers_; // solvers associated with this context + Minimize* mini_; // pointer to set of weak constraints + LogPtr progress_; // event handler or 0 if not used + Literal step_; // literal for tagging enumeration/step constraints + uint32 lastTopLevel_; // size of master's top-level after last init + struct Share { // Additional data + uint32 count :10; // max number of objects sharing this object + uint32 winner :10; // id of solver that terminated the search + uint32 shareM : 3; // physical sharing mode + uint32 shortM : 1; // short clause mode + uint32 solveM : 1; // solve mode + uint32 frozen : 1; // is adding of problem constraints allowed? + uint32 seed : 1; // set seed of new solvers + uint32 satPreM : 2; // preprocessing mode + uint32 report : 2; // report mode + uint32 reserved: 1; + Share() : count(1), winner(0), shareM((uint32)ContextParams::share_auto), shortM(0), solveM(0), frozen(0), seed(0), satPreM(0), report(0) {} + } share_; +}; +//@} +} +#endif diff --git a/clasp/clasp/solve_algorithms.h b/clasp/clasp/solve_algorithms.h new file mode 100644 index 000000000..bfc534ced --- /dev/null +++ b/clasp/clasp/solve_algorithms.h @@ -0,0 +1,287 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_SOLVE_ALGORITHMS_H_INCLUDED +#define CLASP_SOLVE_ALGORITHMS_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif + +#include +/*! + * \file + * \brief Defines top-level functions for solving problems. + */ +namespace Clasp { + +///! \addtogroup enumerator +//@{ + +//! Type for holding global solve limits. +struct SolveLimits { + explicit SolveLimits(uint64 conf = UINT64_MAX, uint64 r = UINT64_MAX) + : conflicts(conf) + , restarts(r) { + } + bool reached() const { return conflicts == 0 || restarts == 0; } + bool enabled() const { return conflicts != UINT64_MAX || restarts != UINT32_MAX; } + uint64 conflicts; /*!< Number of conflicts. */ + uint64 restarts; /*!< Number of restarts. */ +}; + +/////////////////////////////////////////////////////////////////////////////// +// Basic solving +/////////////////////////////////////////////////////////////////////////////// +//! Basic (sequential) solving using given solving options. +class BasicSolve { +public: + //! Creates a new object for solving with the given solver using the given solving options. + /*! + * If an optional solve limit is given, solving stops once this limit is reached. + * \pre s is attached to a problem (SharedContext). + */ + BasicSolve(Solver& s, const SolveParams& p, const SolveLimits& lim = SolveLimits()); + BasicSolve(Solver& s, const SolveLimits& lim = SolveLimits()); + ~BasicSolve(); + + bool hasLimit() const { return limits_.enabled(); } + + //! Enables solving under the given assumptions. + /*! + * The use of assumptions allows for incremental solving. Literals contained + * in assumptions are assumed to be true during search but can be undone afterwards. + * + * \param assumptions A list of unit assumptions to be assumed true. + * \return false if assumptions are conflicting. + */ + bool assume(const LitVec& assumptions); + + //! Solves the path stored in the given solver using the given solving options. + /*! + * \return + * - value_true if search stopped on a model. + * - value_false if the search-space was completely examined. + * - value_free if the given solve limit was hit. + * + * \note + * The function maintains the current solving state (number of restarts, learnt limits, ...) + * between calls. + */ + ValueRep solve(); + //! Returns whether the given problem is satisfiable under the given assumptions. + /*! + * Calls assume(assumptions) followed by solve() but does not maintain any solving state. + * \param assumptions Possibly empty set of assumptions to apply before solving. + * \param init Call InitParams::randomize() before starting search? + */ + bool satisfiable(const LitVec& assumptions, bool init); + + //! Resets the internal solving state while keeping the solver and the solving options. + void reset(bool reinit = false); + //! Replaces *this with BasicSolve(s, p). + void reset(Solver& s, const SolveParams& p, const SolveLimits& lim = SolveLimits()); + Solver& solver() { return *solver_; } +private: + BasicSolve(const BasicSolve&); + BasicSolve& operator=(const BasicSolve&); + typedef const SolveParams Params; + typedef SolveLimits Limits; + struct State; + Solver* solver_; // active solver + Params* params_; // active solving options + Limits limits_; // active solving limits + State* state_; // internal solving state +}; +//! Event type for reporting basic solve events like restarts or deletion. +struct BasicSolveEvent : SolveEvent { + //! Type of operation that emitted the event. + enum EventOp { event_none = 0, event_deletion = 'D', event_exit = 'E', event_grow = 'G', event_restart = 'R' }; + BasicSolveEvent(const Solver& s, EventOp a_op, uint64 cLim, uint32 lLim) : SolveEvent(s, verbosity_max), cLimit(cLim), lLimit(lLim) { + op = a_op; + } + uint64 cLimit; //!< Next conflict limit + uint32 lLimit; //!< Next learnt limit +}; +/////////////////////////////////////////////////////////////////////////////// +// General solve +/////////////////////////////////////////////////////////////////////////////// +class Enumerator; +//! Interface for complex solve algorithms. +/*! + * \ingroup enumerator + * \relates Solver + * SolveAlgorithms implement complex algorithms like enumeration or optimization. + */ +class SolveAlgorithm { +public: + /*! + * \param limit An optional solve limit applied in solve(). + */ + explicit SolveAlgorithm(const SolveLimits& limit = SolveLimits()); + virtual ~SolveAlgorithm(); + + const Enumerator* enumerator() const { return enum_.get(); } + const SolveLimits& limits() const { return limits_; } + virtual bool interrupted()const = 0; + const Model& model() const; + const LitVec* unsatCore() const; + + void setEnumerator(Enumerator& e); + void setEnumLimit(uint64 m) { enumLimit_= m; } + void setLimits(const SolveLimits& x) { limits_ = x; } + //! If set to false, SharedContext::report() is not called for models. + /*! + * \note The default is true, i.e. models are reported via SharedContext::report(). + */ + void setReportModels(bool report) { reportM_ = report; } + + //! Runs the solve algorithm. + /*! + * \param ctx A context object containing the problem. + * \param assume A list of initial unit-assumptions. + * \param onModel Optional handler to be called on each model. + * + * \return + * - true: if the search stopped before the search-space was exceeded. + * - false: if the search-space was completely examined. + * + * \note + * The use of assumptions allows for incremental solving. Literals contained + * in assumptions are assumed to be true during search but are undone before solve returns. + * + * \note + * Conceptually, solve() behaves as follows: + * \code + * start(ctx, assume); + * while (next()) { + * if (!report(model()) || enum_limit_reached()) { stop(); } + * } + * return more(); + * \endcode + * where report() notifies all registered model handlers. + */ + bool solve(SharedContext& ctx, const LitVec& assume = LitVec(), ModelHandler* onModel = 0); + + //! Prepares the solve algorithm for enumerating models. + /*! + * \pre The algorithm is not yet active. + */ + void start(SharedContext& ctx, const LitVec& assume = LitVec(), ModelHandler* onModel = 0); + //! Searches for the next model and returns whether such a model was found. + /*! + * \pre start() was called. + */ + bool next(); + //! Stops the algorithms. + void stop(); + //! Returns whether the last search completely exhausted the search-space. + bool more(); + + //! Resets solving state and sticky messages like terminate. + /*! + * \note The function must be called between successive calls to solve(). + */ + virtual void resetSolve() = 0; + + //! Prepares the algorithm for handling (asynchronous) calls to SolveAlgorithm::interrupt(). + virtual void enableInterrupts() = 0; + + //! Tries to terminate the current solve process. + /*! + * \note If enableInterrupts() was not called, SolveAlgorithm::interrupt() may return false + * to signal that (asynchronous) termination is not supported. + */ + bool interrupt(); +protected: + SolveAlgorithm(const SolveAlgorithm&); + SolveAlgorithm& operator=(const SolveAlgorithm&); + //! The actual solve algorithm. + virtual bool doSolve(SharedContext& ctx, const LitVec& assume) = 0; + //! Shall return true if termination is supported, otherwise false. + virtual bool doInterrupt() = 0; + + virtual void doStart(SharedContext& ctx, const LitVec& assume); + virtual int doNext(int last); + virtual void doStop(); + virtual void doDetach() = 0; + + bool reportModel(Solver& s) const; + bool reportUnsat(Solver& s) const; + Enumerator& enumerator() { return *enum_; } + SharedContext& ctx() const { return *ctx_; } + const LitVec& path() const { return *path_; } + uint64 maxModels() const { return enumLimit_; } + bool moreModels(const Solver& s) const; +private: + typedef SingleOwnerPtr EnumPtr; + typedef SingleOwnerPtr PathPtr; + typedef SingleOwnerPtr CorePtr; + enum { value_stop = value_false|value_true }; + bool attach(SharedContext& ctx, ModelHandler* onModel); + void detach(); + SolveLimits limits_; + SharedContext* ctx_; + EnumPtr enum_; + ModelHandler* onModel_; + PathPtr path_; + CorePtr core_; + uint64 enumLimit_; + double time_; + int last_; + bool reportM_; +}; +//! A class that implements clasp's sequential solving algorithm. +class SequentialSolve : public SolveAlgorithm { +public: + explicit SequentialSolve(const SolveLimits& limit = SolveLimits()); + virtual bool interrupted() const; + virtual void resetSolve(); + virtual void enableInterrupts(); +protected: + virtual bool doSolve(SharedContext& ctx, const LitVec& assume); + virtual bool doInterrupt(); + virtual void doStart(SharedContext& ctx, const LitVec& assume); + virtual int doNext(int last); + virtual void doStop(); + virtual void doDetach(); +private: + typedef SingleOwnerPtr SolvePtr; + SolvePtr solve_; + volatile int term_; +}; + +//! Options for controlling solving. +struct BasicSolveOptions { + SolveLimits limit; //!< Solve limit (disabled by default). + SolveAlgorithm* createSolveObject() const { return new SequentialSolve(limit); } + static uint32 supportedSolvers() { return 1; } + static uint32 recommendedSolvers() { return 1; } + uint32 numSolver() const { return 1; } + void setSolvers(uint32) {} + bool defaultPortfolio() const { return false; } +}; +//@} + +} +#endif diff --git a/clasp/clasp/solver.h b/clasp/clasp/solver.h new file mode 100644 index 000000000..003de5be2 --- /dev/null +++ b/clasp/clasp/solver.h @@ -0,0 +1,1087 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_SOLVER_H_INCLUDED +#define CLASP_SOLVER_H_INCLUDED +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include + +namespace Clasp { + +/** + * \file + * \defgroup solver Solver + * \brief %Solver and related classes. + */ +//@{ + +//! clasp's Solver class. +/*! + * A Solver-object maintains the state and provides the functions + * necessary to implement our CDNL-algorithm. + * + * The interface supports incremental solving (search under assumptions) as well as + * solution enumeration. To make this possible the solver maintains two special + * decision levels: the root-level and the backtrack-level. + * + * The root-level is the lowest decision level to which a search + * can return. Conflicts on the root-level are non-resolvable and end a search - the + * root-level therefore acts as an artificial top-level during search. + * Incremental solving is then implemented by first adding a list of unit assumptions + * and second initializing the root-level to the current decision level. + * Once search terminates assumptions can be undone by calling clearAssumptions + * and a new a search can be started using different assumptions. + * + * For model enumeration the solver maintains a backtrack-level that restricts + * backjumping in order to prevent repeating already enumerated solutions. + * The solver will never backjump above that level and conflicts on the backtrack-level + * are resolved by backtracking, i.e. flipping the corresponding decision literal. + * + * \see "Conflict-Driven Answer Set Enumeration" for a detailed description of this approach. + * + */ +class Solver { +public: + typedef PodVector::type ConstraintDB; + typedef const ConstraintDB& DBRef; + typedef SingleOwnerPtr HeuristicPtr; +private: + friend class SharedContext; + // Creates an empty solver object with all strategies set to their default value. + Solver(SharedContext* ctx, uint32 id); + ~Solver(); + // Resets a solver object to the state it had after construction. + void reset(); + void resetConfig(); + void startInit(uint32 constraintGuess, const SolverParams& params); + void updateVars(); + bool cloneDB(const ConstraintDB& db); + bool preparePost(); + bool endInit(); + bool endStep(uint32 top, const SolverParams& params); +public: + typedef SolverStrategies::SearchStrategy SearchMode; + typedef SolverStrategies::UpdateMode UpdateMode; + typedef SolverStrategies::WatchInit WatchInitMode; + //! Returns a pointer to the shared context object of this solver. + const SharedContext* sharedContext() const { return shared_; } + //! Returns a pointer to the sat-preprocessor used by this solver. + SatPreprocessor* satPrepro() const; + //! Returns the solver's solve parameters. + const SolveParams& searchConfig() const; + SearchMode searchMode() const { return static_cast(strategy_.search); } + UpdateMode updateMode() const { return static_cast(strategy_.upMode); } + WatchInitMode watchInitMode() const { return static_cast(strategy_.initWatches); } + uint32 compressLimit() const { return strategy_.compress ? strategy_.compress : UINT32_MAX; } + bool restartOnModel()const { return strategy_.restartOnModel; } + DecisionHeuristic* heuristic() const { return heuristic_.get();} + uint32 id() const { return strategy_.id; } + VarInfo varInfo(Var v) const { return shared_->validVar(v) ? shared_->varInfo(v) : VarInfo(); } + const OutputTable& outputTable() const { return shared_->output; } + Literal tagLiteral() const { return tag_; } + bool isMaster() const { return this == sharedContext()->master(); } + /*! + * \name Setup functions + * Functions in this group are typically used before a search is started. + * @{ */ + //! Adds the problem constraint c to the solver. + /*! + * Problem constraints shall only be added to the master solver of + * a SharedContext object and only during the setup phase. + * \pre this == sharedContext()->master() && !sharedContext()->frozen(). + */ + void add(Constraint* c); + //! Adds a suitable representation of the given clause to the solver. + /*! + * Depending on the type and size of the given clause, the function + * either adds a (learnt) constraint to this solver or an implication + * to the shared implication graph. + * \note If c is a problem clause, the precondition of add(Constraint* c) applies. + */ + bool add(const ClauseRep& c, bool isNew = true); + //! Returns whether c can be stored in the shared short implication graph. + bool allowImplicit(const ClauseRep& c) const { + return c.isImp() + ? shared_->allowImplicit(c.info.type()) && !c.info.aux() && (c.prep == 1 || (!auxVar(c.lits[0].var()) && !auxVar(c.lits[1].var()) && (c.size == 2 || !auxVar(c.lits[2].var())))) + : c.size <= 1; + } + //! Returns the post propagator with the given priority or 0 if no such post propagator exists. + PostPropagator* getPost(uint32 prio) const; + //! Adds p as post propagator to this solver. + /*! + * \pre p was not added previously and is not part of any other solver. + * \note Post propagators are stored and called in priority order. + * \see PostPropagator::priority() + */ + bool addPost(PostPropagator* p); + //! Removes p from the solver's list of post propagators. + /*! + * \note The function shall not be called during propagation of any other post propagator. + */ + void removePost(PostPropagator* p); + + //! Adds path to the current root-path and adjusts the root-level accordingly. + bool pushRoot(const LitVec& path, bool pushStep = false); + bool pushRoot(Literal p); + void setEnumerationConstraint(Constraint* c); + //! Requests a special aux variable for tagging conditional knowledge. + /*! + * Once a tag variable t is set, learnt clauses containing ~t are + * tagged as "conditional". Conditional clauses are removed once t becomes + * unassigned or Solver::removeConditional() is called. Furthermore, calling + * Solver::strengthenConditional() removes ~t from conditional clauses and + * transforms them to unconditional knowledge. + * + * \note Typically, the tag variable is a root assumption and hence true during + * the whole search. + */ + Var pushTagVar(bool pushToRoot); + //@} + + /*! + * \name CDNL functions + * Top level functions that are important to the CDNL algorithm. + * @{ */ + + //! Searches for a model as long as the given limit is not reached. + /*! + * The function searches for a model as long as none of the limits given by limit + * is reached. The limits are updated during search. + * + * \param limit Imposed limit on conflicts and number of learnt constraints. + * \param randf Pick next decision variable randomly with a probability of randf. + * \return + * - value_true: if a model was found. + * - value_false: if the problem is unsatisfiable (under assumptions, if any). + * - value_free: if search was stopped because limit was reached. + * . + * + * \note search treats the root level as top-level, i.e. it will never backtrack below that level. + */ + ValueRep search(SearchLimits& limit, double randf = 0.0); + ValueRep search(uint64 maxC, uint32 maxL, bool local = false, double rp = 0.0); + + //! Moves the root-level i levels down (i.e. away from the top-level). + /*! + * The root-level is similar to the top-level in that it cannot be + * undone during search, i.e. the solver will not resolve conflicts that are on or + * above the root-level. + */ + void pushRootLevel(uint32 i = 1) { + levels_.root = std::min(decisionLevel(), levels_.root+i); + levels_.flip = std::max(levels_.flip, levels_.root); + } + + //! Moves the root-level i levels up (i.e. towards the top-level). + /*! + * The function removes all levels between the new root level and the current decision level, + * resets the current backtrack-level, and re-assigns any implied literals. + * \param i Number of root decisions to pop. + * \param[out] popped Optional storage for popped root decisions. + * \param aux Whether or not aux variables should be added to popped. + * \post decisionLevel() == rootLevel() + * \note The function first calls clearStopConflict() to remove any stop conflicts. + * \note The function *does not* propagate any asserted literals. It is + * the caller's responsibility to call propagate() after the function returned. + */ + bool popRootLevel(uint32 i = 1, LitVec* popped = 0, bool aux = true); + + //! Removes a previously set stop conflict and restores the root level. + void clearStopConflict(); + + //! Returns the current root level. + uint32 rootLevel() const { return levels_.root; } + + //! Removes any implications made between the top-level and the root-level. + /*! + * The function also resets the current backtrack-level and re-assigns learnt facts. + * \note + * Equivalent to popRootLevel(rootLevel()) followed by simplify(). + */ + bool clearAssumptions(); + + //! Adds c as a learnt constraint to the solver. + void addLearnt(Constraint* c, uint32 size, ConstraintType type) { + learnts_.push_back(c); + stats.addLearnt(size, type); + } + void addLearnt(Constraint* c, uint32 size) { addLearnt(c, size, c->type()); } + //! Tries to receive at most maxOut clauses. + /*! + * The function queries the distributor object for new clauses to be delivered to + * this solver. Clauses are stored in out. + * \return The number of clauses received. + */ + uint32 receive(SharedLiterals** out, uint32 maxOut) const; + //! Distributes the clause in lits via the distributor. + /*! + * The function first calls the distribution strategy + * to decides whether the clause is a valid candidate for distribution. + * If so and a distributor was set, it distributes the clause and returns a handle to the + * now shared literals of the clause. Otherwise, it returns 0. + * + * \param lits The literals of the clause. + * \param size The number of literals in the clause. + * \param extra Additional information about the clause. + * \note + * If the return value is not null, it is the caller's + * responsibility to release the returned handle (i.e. by calling release()). + * \note If the clause contains aux vars, it is not distributed. + */ + SharedLiterals* distribute(const Literal* lits, uint32 size, const ConstraintInfo& extra); + + + //! Returns to the maximum of rootLevel() and backtrackLevel() and increases the number of restarts. + void restart() { + undoUntil(0); + ++stats.restarts; + ccInfo_.score().bumpActivity(); + } + enum UndoMode { undo_default = 0u, undo_pop_bt_level = 1u, undo_pop_proj_level = 2u, undo_save_phases = 4u }; + //! Sets the backtracking level to dl. + /*! + * Depending on mode, the backtracking level either applies + * to normal or projective solution enumeration. + * \see "Solution Enumeration for Projected Boolean Search Problems". + */ + void setBacktrackLevel(uint32 dl, UndoMode mode = undo_pop_bt_level) { + if (uint32(mode) >= levels_.mode) { + levels_.flip = std::max(std::min(dl, decisionLevel()), rootLevel()); + levels_.mode = std::max(uint32(mode & 3u), uint32(undo_pop_bt_level)); + } + } + //! Returns the current backtracking level. + uint32 backtrackLevel() const { return levels_.flip; } + //! Returns the backjump level during an undo operation. + uint32 jumpLevel() const { return decisionLevel() - levels_.jump; } + + //! Returns whether the solver can split-off work. + bool splittable() const; + + //! Tries to split-off disjoint work from the solver's currrent guiding path and returns it in out. + /*! + * \return splittable() + */ + bool split(LitVec& out); + + //! Copies the solver's currrent guiding path to gp. + /*! + * \note The solver's guiding path consists of: + * - the decisions from levels [1, rootLevel()] + * - any literals that are implied on a level <= rootLevel() because of newly learnt + * information. This particularly includes literals that were flipped during model enumeration. + * + * \param[out] out Where to store the guiding path. + */ + void copyGuidingPath(LitVec& out); + + //! If called on top-level, removes SAT-clauses + Constraints for which Constraint::simplify returned true. + /*! + * \note If this method is called on a decision-level > 0, it is a noop and will + * simply return true. + * \return false, if a top-level conflict is detected. Otherwise, true. + */ + bool simplify(); + //! Shuffle constraints upon next simplification. + void shuffleOnNextSimplify(){ shufSimp_ = 1; } + + + //! Removes all conditional knowledge, i.e. all previously tagged learnt clauses. + /*! + * \see Solver::pushTagVar() + */ + void removeConditional(); + + //! Resolves all tagged clauses with the tag literal and thereby strengthens the learnt db. + /*! + * \see Solver::pushTagVar() + */ + void strengthenConditional(); + + //! Sets the literal p to true and schedules p for propagation. + /*! + * Setting a literal p to true means assigning the appropriate value to + * p's variable. That is: value_false if p is a negative literal and value_true + * if p is a positive literal. + * \param p The literal that should become true. + * \param a The reason for the literal to become true or 0 if no reason exists. + * + * \return + * - false if p is already false + * - otherwise true. + * + * \pre hasConflict() == false + * \pre a.isNull() == false || decisionLevel() <= rootLevel() || searchMode() == no_learning + * \post + * p.var() == trueValue(p) || p.var() == falseValue(p) && hasConflict() == true + * + * \note if setting p to true leads to a conflict, the nogood that caused the + * conflict can be requested using the conflict() function. + */ + bool force(const Literal& p, const Antecedent& a) { + assert(!hasConflict() || isTrue(p)); + if (assign_.assign(p, decisionLevel(), a)) return true; + setConflict(p, a, UINT32_MAX); + return false; + } + /*! + * \overload bool Solver::force(const Literal&, const Antecedent&) + */ + bool force(const Literal& p, const Antecedent& a, uint32 data) { + return data != UINT32_MAX + ? assign_.assign(p, decisionLevel(), a.constraint(), data) || (setConflict(p, a, data), false) + : force(p, a); + } + + //! Assigns p at dl because of r. + /*! + * \pre dl <= decisionLevel() + * \note + * If dl < ul = max(rootLevel(), backtrackLevel()), p is actually assigned + * at ul but the solver stores enough information to reassign + * p on backtracking. + */ + bool force(Literal p, uint32 dl, const Antecedent& r, uint32 d = UINT32_MAX) { + return dl == decisionLevel() ? force(p, r, d) : force(ImpliedLiteral(p, dl, r, d)); + } + //! Assigns p as a fact at decision level 0. + bool force(Literal p) { return force(p, 0, Antecedent(lit_true())); } + + //! Assumes the literal p if possible. + /*! + * If p is currently unassigned, sets p to true and starts a new decision level. + * \pre validVar(p.var()) == true + * \param p The literal to assume. + * \return !isFalse(p) + */ + bool assume(const Literal& p); + + //! Selects and assumes the next branching literal by calling the installed decision heuristic. + /*! + * \pre queueSize() == 0 + * \note The next decision literal will be selected randomly with probability f. + * \return + * - true if the assignment is not total and a literal was assumed (or forced). + * - false otherwise + * . + * \see DecisionHeuristic + */ + bool decideNextBranch(double f = 0.0); + + //! Sets a conflict that forces the solver to terminate its search. + /*! + * \pre !hasConflict() + * \post hasConflict() + * + * \note + * To prevent the solver from resolving the stop conflict, the + * function sets the root level to the current decision level. + * Call clearStopConflict() to remove the conflict and to restore + * the previous root-level. + */ + void setStopConflict(); + + /*! + * Propagates all enqueued literals. If a conflict arises during propagation + * propagate returns false and the current conflict (as a set of literals) + * is stored in the solver's conflict variable. + * \pre !hasConflict() + * \see Solver::force + * \see Solver::assume + * \note Shall not be called recursively. + */ + bool propagate(); + + /*! + * Does unit propagation and calls x->propagateFixpoint(*this) + * for all post propagators x up to but not including p. + * \note The function is meant to be called only in the context of p. + * \pre p is a post propagator of this solver, i.e. was previously added via addPost(). + * \pre Post propagators are active, i.e. the solver is fully initialized. + */ + bool propagateUntil(PostPropagator* p); + + /*! + * Calls x->propagateFixpoint(*this) for all post propagators x starting from and including p. + * \note The function is meant to be called only in the context of p. + * \pre p is a post propagator of this solver, i.e. was previously added via addPost(). + * \pre Post propagators are active, i.e. the solver is fully initialized. + * \pre Assignment is fully (unit) propagated up to p. + */ + bool propagateFrom(PostPropagator* p); + + //! Executes a one-step lookahead on p. + /*! + * Assumes p and propagates this assumption. If propagations leads to + * a conflict, false is returned. Otherwise the assumption is undone and + * the function returns true. + * \param p The literal to test. + * \param c The constraint that wants to test p (can be 0). + * \pre p is free + * \note If c is not null and testing p does not lead to a conflict, + * c->undoLevel() is called *before* p is undone. Hence, the + * range [s.levelStart(s.decisionLevel()), s.assignment().size()) + * contains p followed by all literals that were forced because of p. + * \note propagateUntil(c) is used to propagate p. + */ + bool test(Literal p, PostPropagator* c); + + //! Estimates the number of assignments following from setting p to true. + /*! + * \note For the estimate only binary clauses are considered. + */ + uint32 estimateBCP(const Literal& p, int maxRecursionDepth = 5) const; + + //! Computes the number of in-edges for each assigned literal. + /*! + * \pre !hasConflict() + * \note For a literal p assigned on level level(p), only in-edges from + * levels < level(p) are counted. + * \return The maximum number of in-edges. + */ + uint32 inDegree(WeightLitVec& out); + + struct DBInfo { uint32 size; uint32 locked; uint32 pinned; }; + //! Removes upto remMax percent of the learnt nogoods. + /*! + * \param remMax Fraction of nogoods to remove ([0.0,1.0]). + * \param rs Strategy to apply during nogood deletion. + * \return The number of locked and active/glue clauses currently exempt from deletion. + * \note + * Nogoods that are the reason for a literal to be in the assignment + * are said to be locked and won't be removed. + */ + DBInfo reduceLearnts(float remMax, const ReduceStrategy& rs = ReduceStrategy()); + + //! Resolves the active conflict using the selected strategy. + /*! + * If searchMode() is set to learning, resolveConflict implements + * First-UIP learning and backjumping. Otherwise, it simply applies + * chronological backtracking. + * \pre hasConflict() + * \return + * - true if the conflict was successfully resolved + * - false otherwise + * \note + * If decisionLevel() == rootLevel() false is returned. + */ + bool resolveConflict(); + + //! Backtracks the last decision and updates the backtrack-level if necessary. + /*! + * \return + * - true if backtracking was possible + * - false if decisionLevel() == rootLevel() + */ + bool backtrack(); + + //! Undoes all assignments up to (but not including) decision level dl. + /*! + * \post decision level == max(min(decisionLevel(), dl), max(rootLevel(), backtrackLevel())) + * \return The decision level after undoing assignments. + * \note + * undoUntil() stops at the current backtrack level unless undoMode includes the mode + * that was used when setting the backtrack level. + * \note + * If undoMode contains undo_save_phases, the functions saves the values of variables that are undone. + * Otherwise, phases are only saved if indicated by the active strategy. + */ + uint32 undoUntil(uint32 dl, uint32 undoMode); + //! Behaves like undoUntil(dl, undo_default). + uint32 undoUntil(uint32 dl) { return undoUntilImpl(dl, false); } + //! Returns whether undoUntil(decisionLevel()-1) is valid and would remove decisionLevel(). + bool isUndoLevel() const; + + //! Adds a new auxiliary variable to this solver. + /*! + * Auxiliary variables are local to one solver and are not considered + * as part of the problem. They shall be added/used only during solving, i.e. + * after problem setup is completed. + */ + Var pushAuxVar(); + //! Pops the num most recently added auxiliary variables and destroys all constraints in auxCons. + void popAuxVar(uint32 num = UINT32_MAX, ConstraintDB* auxCons = 0); + //@} + + /*! + * \name State inspection + * Functions for inspecting the state of the solver & search. + * \note validVar(v) is a precondition for all functions that take a variable as + * parameter. + * @{ */ + //! Returns the number of problem variables. + uint32 numProblemVars() const { return shared_->numVars(); } + //! Returns the number of active solver-local aux variables. + uint32 numAuxVars() const { return numVars() - numProblemVars(); } + //! Returns the number of solver variables, i.e. numProblemVars() + numAuxVars() + uint32 numVars() const { return assign_.numVars() - 1; } + //! Returns true if var represents a valid variable in this solver. + bool validVar(Var var) const { return var <= numVars(); } + //! Returns true if var is a solver-local aux var. + bool auxVar(Var var) const { return shared_->numVars() < var; } + //! Returns the number of assigned variables. + uint32 numAssignedVars() const { return assign_.assigned(); } + //! Returns the number of free variables. + /*! + * The number of free variables is the number of vars that are neither + * assigned nor eliminated. + */ + uint32 numFreeVars() const { return assign_.free()-1; } + //! Returns the value of v w.r.t the current assignment. + ValueRep value(Var v) const { assert(validVar(v)); return assign_.value(v); } + //! Returns the value of v w.r.t the top level. + ValueRep topValue(Var v) const { return level(v) == 0 ? value(v) : value_free; } + //! Returns the set of preferred values of v. + ValueSet pref(Var v) const { assert(validVar(v)); return assign_.pref(v);} + //! Returns true if p is true w.r.t the current assignment. + bool isTrue(Literal p) const { assert(validVar(p.var())); return assign_.value(p.var()) == trueValue(p); } + //! Returns true if p is false w.r.t the current assignment. + bool isFalse(Literal p) const { assert(validVar(p.var())); return assign_.value(p.var()) == falseValue(p); } + //! Returns the literal of v being true in the current assignment. + /*! + * \pre v is assigned a value in the current assignment + */ + Literal trueLit(Var v) const { assert(value(v) != value_free); return Literal(v, valSign(value(v))); } + Literal defaultLit(Var v) const; + //! Returns the decision level on which v was assigned. + /*! + * \note The returned value is only meaningful if value(v) != value_free. + */ + uint32 level(Var v) const { assert(validVar(v)); return assign_.level(v); } + //! Returns true if v is currently marked as seen. + /*! + * Note: variables assigned on level 0 are always marked. + */ + bool seen(Var v) const { assert(validVar(v)); return assign_.seen(v, 3u); } + //! Returns true if the literal p is currently marked as seen. + bool seen(Literal p) const { assert(validVar(p.var())); return assign_.seen(p.var(), uint8(1+p.sign())); } + //! Returns the current decision level. + uint32 decisionLevel() const { return (uint32)levels_.size(); } + bool validLevel(uint32 dl) const { return dl != 0 && dl <= decisionLevel(); } + //! Returns the starting position of decision level dl in the trail. + /*! + * \pre validLevel(dl) + */ + uint32 levelStart(uint32 dl) const { assert(validLevel(dl)); return levels_[dl-1].trailPos; } + //! Returns the decision literal of the decision level dl. + /*! + * \pre validLevel(dl) + */ + Literal decision(uint32 dl) const { assert(validLevel(dl)); return assign_.trail[ levels_[dl-1].trailPos ]; } + //! Returns true, if the current assignment is conflicting. + bool hasConflict() const { return !conflict_.empty(); } + bool hasStopConflict() const { return hasConflict() && conflict_[0] == lit_false(); } + //! Returns the number of (unprocessed) literals in the propagation queue. + uint32 queueSize() const { return (uint32) assign_.qSize(); } + //! Number of problem constraints in this solver. + uint32 numConstraints() const; + //! Returns the number of constraints that are currently in the solver's learnt database. + uint32 numLearntConstraints() const { return (uint32)learnts_.size(); } + //! Returns the reason for p being true. + /*! + * \pre p is true w.r.t the current assignment + */ + const Antecedent& reason(Literal p) const { assert(isTrue(p)); return assign_.reason(p.var()); } + //! Returns the additional reason data associated with p. + uint32 reasonData(Literal p) const { return assign_.data(p.var()); } + //! Returns the current (partial) assignment as a set of true literals. + /*! + * \note Although the special var 0 always has a value it is not considered to be + * part of the assignment. + */ + const LitVec& trail() const { return assign_.trail; } + const Assignment& assignment() const { return assign_; } + //! Returns the current conflict as a set of literals. + const LitVec& conflict() const { return conflict_; } + //! Returns the most recently derived conflict clause. + const LitVec& conflictClause() const { return cc_; } + //! Returns the set of eliminated literals that are unconstraint w.r.t the last model. + const LitVec& symmetric() const { return temp_; } + //! Returns the enumeration constraint set by the enumerator used. + Constraint* enumerationConstraint() const { return enum_; } + DBRef constraints() const { return constraints_; } + //! Returns the idx'th learnt constraint. + /*! + * \pre idx < numLearntConstraints() + */ + Constraint& getLearnt(uint32 idx) const { + assert(idx < numLearntConstraints()); + return *learnts_[ idx ]; + } + + mutable RNG rng; //!< Random number generator for this object. + ValueVec model; //!< Stores the last model (if any). + LowerBound lower; //!< Stores the last lower bound found (if any). + SolverStats stats; //!< Stores statistics about the solving process. + //@} + + /*! + * \name Watch management + * Functions for setting/removing watches. + * \pre validVar(v) + * @{ */ + //! Returns the number of constraints watching the literal p. + uint32 numWatches(Literal p) const; + //! Returns true if the constraint c watches the literal p. + bool hasWatch(Literal p, Constraint* c) const; + bool hasWatch(Literal p, ClauseHead* c) const; + //! Returns c's watch-structure associated with p. + /*! + * \note returns 0, if hasWatch(p, c) == false + */ + GenericWatch* getWatch(Literal p, Constraint* c) const; + //! Adds c to the watch-list of p. + /*! + * When p becomes true, c->propagate(p, data, *this) is called. + * \post hasWatch(p, c) == true + */ + void addWatch(Literal p, Constraint* c, uint32 data = 0) { + assert(validWatch(p)); + watches_[p.id()].push_right(GenericWatch(c, data)); + } + //! Adds w to the clause watch-list of p. + void addWatch(Literal p, const ClauseWatch& w) { + assert(validWatch(p)); + watches_[p.id()].push_left(w); + } + //! Removes c from p's watch-list. + /*! + * \post hasWatch(p, c) == false + */ + void removeWatch(const Literal& p, Constraint* c); + void removeWatch(const Literal& p, ClauseHead* c); + //! Adds c to the watch-list of decision-level dl. + /*! + * Constraints in the watch-list of a decision level are + * notified when that decision level is about to be backtracked. + * \pre validLevel(dl) + */ + void addUndoWatch(uint32 dl, Constraint* c) { + assert(validLevel(dl)); + if (levels_[dl-1].undo != 0) { + levels_[dl-1].undo->push_back(c); + } + else { + levels_[dl-1].undo = allocUndo(c); + } + } + //! Removes c from the watch-list of the decision level dl. + bool removeUndoWatch(uint32 dl, Constraint* c); + //@} + + /*! + * \name Misc functions + * Low-level implementation functions. Use with care and only if you + * know what you are doing! + * @{ */ + bool addPost(PostPropagator* p, bool init); + //! Updates the reason for p being true. + /*! + * \pre p is true and x is a valid reason for p + */ + bool setReason(Literal p, const Antecedent& x, uint32 data = UINT32_MAX) { + assert(isTrue(p) || shared_->eliminated(p.var())); + assign_.setReason(p.var(), x); + if (data != UINT32_MAX) { assign_.setData(p.var(), data); } + return true; + } + void setPref(Var v, ValueSet::Value which, ValueRep to) { + assert(validVar(v) && to <= value_false); + assign_.requestPrefs(); + assign_.setPref(v, which, to); + } + void resetPrefs() { assign_.resetPrefs(); } + void resetLearntActivities(); + //! Returns the reason for p being true as a set of literals. + void reason(Literal p, LitVec& out) { assert(isTrue(p)); out.clear(); return assign_.reason(p.var()).reason(*this, p, out); } + + //! Helper function for updating antecedent scores during conflict resolution. + /*! + * \param sc The current score of the active antecedent. + * \param p The literal implied by the active antecedent. + * \param lits The literals of the active antecedent. + * \return true if a score was updated. + * + * \note Depending on the active solver strategies, the function + * increases the activity and/or updates the lbd of the given antecedent. + * + * \note If SolverStrategies::bumpVarAct is active, p's activity + * is increased if the new lbd is smaller than the lbd of the + * conflict clause that is currently being derived. + */ + bool updateOnReason(ConstraintScore& sc, Literal p, const LitVec& lits) { + // update only during conflict resolution + if (&lits != &conflict_) { return false; } + sc.bumpActivity(); + uint32 up = strategy_.updateLbd; + if (up != SolverStrategies::lbd_fixed && !lits.empty()) { + uint32 lbd = sc.lbd(); + uint32 inc = uint32(up != SolverStrategies::lbd_updated_less); + uint32 nLbd = countLevels(&lits[0], &lits[0] + lits.size(), lbd - inc); + if ((nLbd + inc) < lbd) { + sc.bumpLbd(nLbd + uint32(up == SolverStrategies::lbd_update_pseudo)); + } + } + if (strategy_.bumpVarAct && isTrue(p)) { bumpAct_.push_back(WeightLiteral(p, sc.lbd())); } + return true; + } + + //! Helper function for increasing antecedent activities during conflict clause minimization. + bool updateOnMinimize(ConstraintScore& sc) { + return !strategy_.ccMinKeepAct && (sc.bumpActivity(), true); + } + + //! Helper function for antecedents to be called during conflict clause minimization. + bool ccMinimize(Literal p, CCMinRecursive* rec) const { + return seen(p.var()) || (rec && hasLevel(level(p.var())) && ccMinRecurse(*rec, p)); + } + + //! Allocates a small block (32-bytes) from the solver's small block pool. + void* allocSmall() { return smallAlloc_.allocate(); } + //! Frees a small block previously allocated from the solver's small block pool. + void freeSmall(void* m) { smallAlloc_.free(m); } + + void addLearntBytes(uint32 bytes) { memUse_ += bytes; } + void freeLearntBytes(uint64 bytes) { memUse_ -= (bytes < memUse_) ? bytes : memUse_; } + + bool restartReached(const SearchLimits& limit) const; + bool reduceReached(const SearchLimits& limit) const; + + //! simplifies cc and returns finalizeConflictClause(cc, info); + uint32 simplifyConflictClause(LitVec& cc, ConstraintInfo& info, ClauseHead* rhs); + uint32 finalizeConflictClause(LitVec& cc, ConstraintInfo& info, uint32 ccRepMode = 0); + uint32 countLevels(const Literal* first, const Literal* last, uint32 maxLevels = Clasp::LBD_MAX); + bool hasLevel(uint32 dl) const { assert(validLevel(dl)); return levels_[dl-1].marked != 0; } + bool frozenLevel(uint32 dl) const { assert(validLevel(dl)); return levels_[dl-1].freeze != 0; } + bool inputVar(Literal p) const { return varInfo(p.var()).input(); } + void markLevel(uint32 dl) { assert(validLevel(dl)); levels_[dl-1].marked = 1; } + void freezeLevel(uint32 dl) { assert(validLevel(dl)); levels_[dl-1].freeze = 1; } + void unmarkLevel(uint32 dl) { assert(validLevel(dl)); levels_[dl-1].marked = 0; } + void unfreezeLevel(uint32 dl){ assert(validLevel(dl)); levels_[dl-1].freeze = 0; } + void markSeen(Var v) { assert(validVar(v)); assign_.setSeen(v, 3u); } + void markSeen(Literal p) { assert(validVar(p.var())); assign_.setSeen(p.var(), uint8(1+p.sign())); } + void clearSeen(Var v) { assert(validVar(v)); assign_.clearSeen(v); } + void setHeuristic(DecisionHeuristic* h, Ownership_t::Type own); + void destroyDB(ConstraintDB& db); + SolverStrategies& strategies() { return strategy_; } + bool resolveToFlagged(const LitVec& conflictClause, uint8 vflag, LitVec& out, uint32& lbd) const; + void resolveToCore(LitVec& out); + void acquireProblemVar(Var var); + void acquireProblemVars() { acquireProblemVar(numProblemVars()); } + //@} +private: + struct DLevel { + explicit DLevel(uint32 pos = 0, ConstraintDB* u = 0) + : trailPos(pos) + , marked(0) + , freeze(0) + , undo(u) {} + uint32 trailPos : 30; + uint32 marked : 1; + uint32 freeze : 1; + ConstraintDB* undo; + }; + struct DecisionLevels : public PodVector::type { + DecisionLevels() : root(0), flip(0), mode(0), jump(0) {} + uint32 root; // root level + uint32 flip : 30; // backtrack level + uint32 mode : 2; // type of backtrack level + uint32 jump; // length of active undo + }; + typedef PodVector::type ReasonVec; + typedef PodVector::type Watches; + struct Dirty; + struct CmpScore { + typedef std::pair ViewPair; + CmpScore(const ConstraintDB& learnts, ReduceStrategy::Score sc, uint32 g, uint32 f = 0) : db(learnts), rs(sc), glue(g), freeze(f) {} + uint32 score(const ConstraintScore& act) const { return ReduceStrategy::asScore(rs, act); } + bool operator()(uint32 lhsId, uint32 rhsId) const { return (*this)(db[lhsId], db[rhsId]); } + bool operator()(const ViewPair& lhs, const ViewPair& rhs) const { return this->operator()(lhs.second, rhs.second); } + bool operator()(ConstraintScore lhs, ConstraintScore rhs) const { return ReduceStrategy::compare(rs, lhs, rhs) < 0;} + bool operator()(const Constraint* lhs, const Constraint* rhs) const { return this->operator()(lhs->activity(), rhs->activity()); } + bool isFrozen(const ConstraintScore& a) const { return a.bumped() && a.lbd() <= freeze; } + bool isGlue(const ConstraintScore& a) const { return a.lbd() <= glue; } + const ConstraintDB& db; + ReduceStrategy::Score rs; + uint32 glue; + uint32 freeze; + private: CmpScore& operator=(const CmpScore&); + }; + bool validWatch(Literal p) const { return p.id() < (uint32)watches_.size(); } + void freeMem(); + void resetHeuristic(Solver* detach, DecisionHeuristic* h = 0, Ownership_t::Type own = Ownership_t::Acquire); + bool simplifySAT(); + bool unitPropagate(); + bool postPropagate(PostPropagator** start, PostPropagator* stop); + void cancelPropagation(); + uint32 undoUntilImpl(uint32 dl, bool sp); + void undoLevel(bool sp); + uint32 analyzeConflict(); + bool isModel(); + bool resolveToFlagged(const LitVec& conflictClause, uint8 vf, LitVec& out, uint32& lbd); + void otfs(Antecedent& lhs, const Antecedent& rhs, Literal p, bool final); + ClauseHead* otfsRemove(ClauseHead* c, const LitVec* newC); + uint32 ccMinimize(LitVec& cc, LitVec& removed, uint32 antes, CCMinRecursive* ccMin); + void ccMinRecurseInit(CCMinRecursive& ccMin); + bool ccMinRecurse(CCMinRecursive& ccMin, Literal p) const; + bool ccRemovable(Literal p, uint32 antes, CCMinRecursive* ccMin); + Antecedent ccHasReverseArc(Literal p, uint32 maxLevel, uint32 maxNew); + void ccResolve(LitVec& cc, uint32 pos, const LitVec& reason); + void undoFree(ConstraintDB* x); + void setConflict(Literal p, const Antecedent& a, uint32 data); + bool force(const ImpliedLiteral& p); + void updateBranch(uint32 n); + uint32 incEpoch(uint32 size, uint32 inc = 1); + DBInfo reduceLinear(uint32 maxR, const CmpScore& cmp); + DBInfo reduceSort(uint32 maxR, const CmpScore& cmp); + DBInfo reduceSortInPlace(uint32 maxR, const CmpScore& cmp, bool onlyPartialSort); + Literal popVars(uint32 num, bool popLearnt, ConstraintDB* popAux); + ConstraintDB* allocUndo(Constraint* c); + SharedContext* shared_; // initialized by master thread - otherwise read-only! + SolverStrategies strategy_; // strategies used by this object + HeuristicPtr heuristic_; // active decision heuristic + CCMinRecursive* ccMin_; // additional data for supporting recursive strengthen + PostPropagator** postHead_; // head of post propagator list to propagate + ConstraintDB* undoHead_; // free list of undo DBs + Constraint* enum_; // enumeration constraint - set by enumerator + uint64 memUse_; // memory used by learnt constraints (estimate) + Dirty* lazyRem_; // set of watch lists that contain invalid constraints + SmallClauseAlloc smallAlloc_; // allocator object for small clauses + Assignment assign_; // three-valued assignment. + DecisionLevels levels_; // information (e.g. position in trail) on each decision level + ConstraintDB constraints_; // problem constraints + ConstraintDB learnts_; // learnt constraints + PropagatorList post_; // (possibly empty) list of post propagators + Watches watches_; // for each literal p: list of constraints watching p + LitVec conflict_; // conflict-literals for later analysis + LitVec cc_; // temporary: conflict clause within analyzeConflict + LitVec temp_; // temporary: redundant literals in simplifyConflictClause + WeightLitVec bumpAct_; // temporary: lits from current dl whose activity might get an extra bump + VarVec epoch_; // temporary vector for computing LBD + VarVec cflStamp_; // temporary vector for computing number of conflicts in branch + ImpliedList impliedLits_; // lits that were asserted on current dl but are logically implied earlier + ConstraintInfo ccInfo_; // temporary: information about conflict clause cc_ + Literal tag_; // aux literal for tagging learnt constraints + uint32 dbIdx_; // position of first new problem constraint in master db + uint32 lastSimp_ :30;// number of top-level assignments on last call to simplify + uint32 shufSimp_ : 1;// shuffle db on next simplify? + uint32 initPost_ : 1;// initialize new post propagators? +}; +inline bool isRevLit(const Solver& s, Literal p, uint32 maxL) { + return s.isFalse(p) && (s.seen(p) || s.level(p.var()) < maxL); +} +//! Simplifies the constraints in db and removes those that are satisfied. +template +void simplifyDB(Solver& s, C& db, bool shuffle) { + typename C::size_type j = 0; + for (typename C::size_type i = j, end = db.size(); i != end; ++i) { + Constraint* c = db[i]; + if (c->simplify(s, shuffle)){ c->destroy(&s, false); } + else { db[j++] = c; } + } + shrinkVecTo(db, j); +} +//! Destroys (and optionally detaches) all constraints in db. +void destroyDB(Solver::ConstraintDB& db, Solver* s, bool detach); +//! Returns the default decision literal of the given variable. +inline Literal Solver::defaultLit(Var v) const { + switch(strategy_.signDef) { + default: // + case SolverStrategies::sign_atom: return Literal(v, !varInfo(v).has(VarInfo::Body)); + case SolverStrategies::sign_pos : return posLit(v); + case SolverStrategies::sign_neg : return negLit(v); + case SolverStrategies::sign_rnd : return Literal(v, rng.drand() < 0.5); + } +} +//! Event type optionally emitted after a conflict. +struct NewConflictEvent : SolveEvent { + NewConflictEvent(const Solver& s, const LitVec& c, const ConstraintInfo& i) : SolveEvent(s, verbosity_quiet), learnt(&c), info(i) {} + const LitVec* learnt; //!< Learnt conflict clause. + ConstraintInfo info; //!< Additional information associated with the conflict clause. +}; +//@} + +/** + * \defgroup heuristic Decision Heuristics + * \brief Decision heuristics and related classes. + * \ingroup solver + */ +//@{ +//! Base class for decision heuristics to be used in a Solver. +/*! + * During search the decision heuristic is used whenever the DPLL-procedure must pick + * a new variable to branch on. Each concrete decision heuristic can implement a + * different algorithm for selecting the next decision variable. + */ +class DecisionHeuristic { +public: + DecisionHeuristic() {} + virtual ~DecisionHeuristic(); + /*! + * Called once after all problem variables are known to the solver. + * The default-implementation is a noop. + * \param s The solver in which this heuristic is used. + */ + virtual void startInit(const Solver& s) { (void)s; } + + /*! + * Called once after all problem constraints are known to the solver + * and the problem was simplified. + * The default-implementation is a noop. + * \param s The solver in which this heuristic is used. + */ + virtual void endInit(Solver& s) { (void)s; } + + //! Called once if s switches to a different heuristic. + virtual void detach(Solver& s) { (void)s; } + + //! Called if configuration has changed. + virtual void setConfig(const HeuParams& p) { (void)p; } + + /*! + * Called if the state of one or more variables changed. + * A state change is one of: + * - A previously eliminated variable is resurrected. + * - A new aux variable was added. + * - An aux variable was removed. + * . + * \param s Solver in which the state change occurred. + * \param v The first variable affected by the change. + * \param n The range of variables affected, i.e. [v, v+n). + * \note Use s.validVar(v) and s.auxVar(v) to determine the reason for the update. + */ + virtual void updateVar(const Solver& s, Var v, uint32 n) = 0; + + /*! + * Called on decision level 0. Variables that are assigned on this level + * may be removed from any decision heuristic. + * \note Whenever the solver returns to dl 0, simplify is only called again + * if the solver learnt new facts since the last call to simplify. + * + * \param s The solver that reached decision level 0. + * \param st The position in the trail of the first new learnt fact. + */ + virtual void simplify(const Solver& s, LitVec::size_type st) { (void)s; (void)st; } + + /*! + * Called whenever the solver backracks. + * Literals in the range [s.trail()[st], s.trail().size()) are subject to backtracking. + * The default-implementation is a noop. + * \param s The solver that is about to backtrack. + * \param st Position in the trail of the first literal that will be backtracked. + */ + virtual void undoUntil(const Solver& s, LitVec::size_type st) { (void)s; (void)st; } + + /*! + * Called whenever a new constraint is added to the solver s. + * The default-implementation is a noop. + * \param s The solver to which the constraint is added. + * \param first First literal of the new constraint. + * \param size Size of the new constraint. + * \param t Type of the new constraint. + * \note first points to an array of size size. + */ + virtual void newConstraint(const Solver& s, const Literal* first, LitVec::size_type size, ConstraintType t) { (void)s; (void)first; (void)size; (void)t; } + + /*! + * Called for each new reason-set that is traversed during conflict analysis. + * The default-implementation is a noop. + * \param s The solver in which the conflict is analyzed. + * \param lits The current reason-set under inspection. + * \param resolveLit The literal that is currently resolved. + * \note When a conflict is detected, the solver passes the conflicting literals + * in lits during the first call to updateReason. On that first call resolveLit + * is the sentinel-literal. + */ + virtual void updateReason(const Solver& s, const LitVec& lits, Literal resolveLit) { (void)s; (void)lits; (void)resolveLit; } + + //! Shall bump the activity of literals in lits by lits.second * adj. + /*! + * The default-implementation is a noop and always returns false. + * \return true if heuristic supports activities, false otherwise. + */ + virtual bool bump(const Solver& s, const WeightLitVec& lits, double adj) { (void)s; (void)lits; (void)adj; return false; } + + /*! + * Called whenever the solver must pick a new variable to branch on. + * \param s The solver that needs a new decision variable. + * \return + * - true : if the decision heuristic assumed a literal + * - false : if no decision could be made because assignment is total or there is a conflict + * . + * \post + * If true is returned, the heuristic has asserted a literal. + */ + bool select(Solver& s) { return s.numFreeVars() != 0 && s.assume(doSelect(s)); } + + //! Implements the actual selection process. + /*! + * \pre s.numFreeVars() > 0, i.e. there is at least one variable to branch on. + * \return + * - a literal that is currently free or + * - a sentinel literal. In that case, the heuristic shall have asserted a literal! + */ + virtual Literal doSelect(Solver& s) = 0; + + /*! + * Shall select one of the literals in the range [first, last). + * \param s The solver that needs a new decision variable. + * \param first Pointer to first literal in range. + * \param last Pointer to the end of the range. + * \pre [first, last) is not empty and all literals in the range are currently unassigned. + * \note The default implementation returns *first. + */ + virtual Literal selectRange(Solver& s, const Literal* first, const Literal* last) { + (void)s; (void)last; + return *first; + } + static Literal selectLiteral(Solver& s, Var v, int signScore) { + ValueSet prefs = s.pref(v); + if (signScore != 0 && !prefs.has(ValueSet::user_value | ValueSet::saved_value | ValueSet::pref_value)) { + return Literal(v, signScore < 0); + } + else if (!prefs.empty()) { + return Literal(v, prefs.sign()); + } + return s.defaultLit(v); + } +private: + DecisionHeuristic(const DecisionHeuristic&); + DecisionHeuristic& operator=(const DecisionHeuristic&); +}; +//! Selects the first free literal w.r.t to the initial variable order. +class SelectFirst : public DecisionHeuristic { +public: + void updateVar(const Solver&, Var, uint32) {} +protected: + Literal doSelect(Solver& s); +}; +//@} +} +#endif + diff --git a/clasp/clasp/solver_strategies.h b/clasp/clasp/solver_strategies.h new file mode 100644 index 000000000..c0749bf2c --- /dev/null +++ b/clasp/clasp/solver_strategies.h @@ -0,0 +1,596 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_SOLVER_STRATEGIES_H_INCLUDED +#define CLASP_SOLVER_STRATEGIES_H_INCLUDED +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include + +#if !defined(CLASP_ALIGN_BITFIELD) +# if defined(EMSCRIPTEN) +// Force alignment of bitfield to T in order to prevent +// code-generation bug in emcc +// see: https://github.com/kripken/emscripten/issues/4540 +# define CLASP_ALIGN_BITFIELD(T) T : 0; +# else +# define CLASP_ALIGN_BITFIELD(T) +# endif +#endif + +/*! + * \file + * \brief Contains strategies and options used to configure solvers and search. + */ +namespace Clasp { +//! Implements clasp's configurable schedule-strategies. +/*! + * clasp currently supports the following basic strategies: + * - geometric sequence : X = n1 * n2^k (k >= 0) + * - arithmetic sequence : X = n1 + (n2*k) (k >= 0) + * - fixed sequence : X = n1 + (0*k) (k >= 0) + * - luby's sequence : X = n1 * luby(k)(k >= 0) + * . + * Furthermore, an inner-outer scheme can be applied to the selected sequence. + * In that case, the sequence is repeated every \+j restarts, where + * \ is the initial outer-limit and j is the number of times the + * sequence was already repeated. + * + * \note For luby's seqeuence, j is not a repetition counter + * but the index where the sequence grows to the next power of two. + * + * \see Luby et al. "Optimal speedup of las vegas algorithms." + * + */ +struct ScheduleStrategy { +public: + //! Supported strategies. + enum Type { Geometric = 0, Arithmetic = 1, Luby = 2, User = 3 }; + + ScheduleStrategy(Type t = Geometric, uint32 b = 100, double g = 1.5, uint32 o = 0); + //! Creates luby's sequence with unit-length unit and optional outer limit. + static ScheduleStrategy luby(uint32 unit, uint32 limit = 0) { return ScheduleStrategy(Luby, unit, 0, limit); } + //! Creates geometric sequence base * (grow^k) with optional outer limit. + static ScheduleStrategy geom(uint32 base, double grow, uint32 limit = 0) { return ScheduleStrategy(Geometric, base, grow, limit); } + //! Creates arithmetic sequence base + (add*k) with optional outer limit. + static ScheduleStrategy arith(uint32 base, double add, uint32 limit = 0) { return ScheduleStrategy(Arithmetic, base, add, limit); } + //! Creates fixed sequence with length base. + static ScheduleStrategy fixed(uint32 base) { return ScheduleStrategy(Arithmetic, base, 0, 0); } + static ScheduleStrategy none() { return ScheduleStrategy(Geometric, 0); } + static ScheduleStrategy def() { return ScheduleStrategy(User, 0, 0.0); } + uint64 current() const; + bool disabled() const { return base == 0; } + bool defaulted()const { return base == 0 && type == User; } + void reset() { idx = 0; } + uint64 next(); + void advanceTo(uint32 idx); + uint32 base : 30; // base of sequence (n1) + uint32 type : 2; // type of basic sequence + uint32 idx; // current index into sequence + uint32 len; // length of sequence (0 if infinite) (once reached, sequence is repeated and len increased) + float grow; // update parameter n2 +}; +//! Returns the idx'th value of the luby sequence. +uint32 lubyR(uint32 idx); +//! Returns the idx'th value of the geometric sequence with the given growth factor. +double growR(uint32 idx, double g); +//! Returns the idx'th value of the arithmetic sequence with the given addend. +double addR(uint32 idx, double a); + +class DecisionHeuristic; +//! Parameter-Object for grouping solver strategies. +struct SolverStrategies { + //! Clasp's two general search strategies. + enum SearchStrategy { + use_learning = 0, //!< Analyze conflicts and learn First-1-UIP-clause. + no_learning = 1 //!< Don't analyze conflicts - chronological backtracking. + }; + //! Default sign heuristic. + enum SignHeu { + sign_atom = 0, //!< Prefer negative literal for atoms. + sign_pos = 1, //!< Prefer positive literal. + sign_neg = 2, //!< Prefer negative literal. + sign_rnd = 3, //!< Prefer random literal. + }; + //! Conflict clause minimization strategy. + enum CCMinType { + cc_local = 0, //!< Basic algorithm. + cc_recursive = 1, //!< Extended algorithm. + }; + //! Antecedents to consider during conflict clause minimization. + enum CCMinAntes { + all_antes = 0, //!< Consider all antecedents. + short_antes = 1, //!< Consider only short antecedents. + binary_antes = 2, //!< Consider only binary antecedents. + no_antes = 3, //!< Don't minimize conflict clauses. + }; + //! Simplifications for long conflict clauses. + enum CCRepMode { + cc_no_replace = 0,//!< Don't replace literals in conflict clauses. + cc_rep_decision= 1,//!< Replace conflict clause with decision sequence. + cc_rep_uip = 2,//!< Replace conflict clause with all uip clause. + cc_rep_dynamic = 3,//!< Dynamically select between cc_rep_decision and cc_rep_uip. + }; + //! Strategy for initializing watched literals in clauses. + enum WatchInit { watch_rand = 0, watch_first = 1, watch_least = 2 }; + //! Strategy for integrating new information in parallel solving. + enum UpdateMode { update_on_propagate = 0, update_on_conflict = 1 }; + enum LbdMode { lbd_fixed = 0, lbd_updated_less = 1, lbd_update_glucose = 2, lbd_update_pseudo = 3 }; + + SolverStrategies(); + void prepare(); + //----- 32 bit ------------ + uint32 compress : 16; /*!< If > 0, enable compression for learnt clauses of size > compress. */ + uint32 saveProgress : 16; /*!< Enable progress saving if > 0. */ + //----- 32 bit ------------ + uint32 heuId : 3; /*!< Type of decision heuristic. */ + uint32 reverseArcs : 2; /*!< Use "reverse-arcs" during learning if > 0. */ + uint32 otfs : 2; /*!< Enable "on-the-fly" subsumption if > 0. */ + uint32 updateLbd : 2; /*!< Update lbds of antecedents during conflict analysis (one of LbdMode). */ + uint32 ccMinAntes : 2; /*!< Antecedents to look at during conflict clause minimization. */ + uint32 ccRepMode : 2; /*!< One of CCRepMode. */ + uint32 ccMinRec : 1; /*!< If 1, use more expensive recursive nogood minimization. */ + uint32 ccMinKeepAct : 1; /*!< Do not increase nogood activities during nogood minimization? */ + uint32 initWatches : 2; /*!< Initialize watches randomly in clauses. */ + uint32 upMode : 1; /*!< One of UpdateMode. */ + uint32 bumpVarAct : 1; /*!< Bump activities of vars implied by learnt clauses with small lbd. */ + uint32 search : 1; /*!< Current search strategy. */ + uint32 restartOnModel: 1; /*!< Do a restart after each model. */ + uint32 signDef : 2; /*!< Default sign heuristic. */ + uint32 signFix : 1; /*!< Disable all sign heuristics and always use default sign. */ + uint32 reserved : 1; + uint32 hasConfig : 1; /*!< Config applied to solver? */ + uint32 id : 6; /*!< Solver id - SHALL ONLY BE SET BY Shared Context! */ +}; +//! Parameter-Object for grouping additional heuristic options. +struct HeuParams { + //! Strategy for scoring clauses not learnt by conflict analysis. + enum ScoreOther { other_auto = 0u, other_no = 1u, other_loop = 2u, other_all = 3u }; + //! Strategy for scoring during conflict analysis. + enum Score { score_auto = 0u, score_min = 1u, score_set = 2u, score_multi_set = 3u }; + //! Global preference for domain heuristic. + enum DomPref { pref_atom = 0u, pref_scc = 1u, pref_hcc = 2u, pref_disj = 4u, pref_min = 8u, pref_show = 16u }; + //! Global modification for domain heuristic. + enum DomMod { mod_none = 0u, mod_level = 1u, mod_spos = 2u, mod_true = 3u, mod_sneg = 4u, mod_false = 5u, mod_init = 6u, mod_factor = 7u }; + //! Values for dynamic decaying scheme. + struct VsidsDecay { + uint32 init: 10; /*!< Starting decay factor: 1/0.\. */ + uint32 bump: 7; /*!< Decay decrease value : \/100. */ + uint32 freq: 15; /*!< Update decay factor every \ conflicts. */ + }; + HeuParams(); + uint32 param : 16; /*!< Extra parameter for heuristic with meaning depending on type. */ + uint32 score : 2; /*!< Type of scoring during resolution. */ + uint32 other : 2; /*!< Consider other learnt nogoods in heuristic. */ + uint32 moms : 1; /*!< Use MOMS-score as top-level heuristic. */ + uint32 nant : 1; /*!< Prefer elements in NegAnte(P). */ + uint32 huang : 1; /*!< Only for Berkmin. */ + uint32 acids : 1; /*!< Only for Vsids/Dom. */ + uint32 domPref : 5; /*!< Default pref for doamin heuristic (set of DomPref). */ + uint32 domMod : 3; /*!< Default mod for domain heuristic (one of DomMod). */ + union { + uint32 extra; + VsidsDecay decay; /*!< Only for Vsids/Dom. */ + }; +}; + +struct OptParams { + //! Strategy to use for optimization. + enum Type { + type_bb = 0, //!< Branch and bound based (model-guided) optimization. + type_usc= 1, //!< Unsatisfiable-core based (core-guided) optimization. + }; + //! Algorithm for model-guided optimization. + enum BBAlgo { + bb_lin = 0u, //!< Linear branch and bound with fixed step of size 1. + bb_hier = 1u, //!< Hierarchical branch and bound with fixed step of size 1. + bb_inc = 2u, //!< Hierarchical branch and bound with increasing steps. + bb_dec = 3u, //!< Hierarchical branch and bound with decreasing steps. + }; + //! Algorithm for core-guided optimization. + enum UscAlgo { + usc_oll = 0u, //!< OLL with possibly multiple cardinality constraints per core. + usc_one = 1u, //!< ONE with one cardinality constraints per core. + usc_k = 2u, //!< K with bounded cardinality constraints of size 2 * (K+1). + usc_pmr = 3u, //!< PMRES with clauses. + }; + //! Additional tactics for core-guided optimization. + enum UscOption { + usc_disjoint = 1u, //!< Enable (disjoint) preprocessing. + usc_succinct = 2u, //!< Do not add redundant constraints. + usc_stratify = 4u, //!< Enable stratification for weighted optimization. + }; + //! Strategy for unsatisfiable-core shrinking. + enum UscTrim { + usc_trim_lin = 1u, //!< Shrinking with linear search SAT->UNSAT. + usc_trim_inv = 2u, //!< Shrinking with inverse linear search UNSAT->SAT. + usc_trim_bin = 3u, //!< Shrinking with binary search SAT->UNSAT. + usc_trim_rgs = 4u, //!< Shrinking with repeated geometric sequence until UNSAT. + usc_trim_exp = 5u, //!< Shrinking with exponential search until UNSAT. + usc_trim_min = 6u, //!< Shrinking with linear search for subset minimal core. + }; + //! Heuristic options common to all optimization strategies. + enum Heuristic { + heu_sign = 1, //!< Use optimize statements in sign heuristic. + heu_model = 2, //!< Apply model heuristic when optimizing. + }; + OptParams(Type st = type_bb); + bool supportsSplitting() const { return type != type_usc; } + bool hasOption(UscOption o) const { return (opts & uint32(o)) != 0u; } + bool hasOption(Heuristic h) const { return (heus & uint32(h)) != 0u; } + uint32 type : 1; /*!< Optimization strategy (see Type).*/ + uint32 heus : 2; /*!< Set of Heuristic values. */ + uint32 algo : 2; /*!< Optimization algorithm (see BBAlgo/UscAlgo). */ + uint32 trim : 3; /*!< Unsatisfiable-core shrinking (0=no shrinking). */ + uint32 opts : 4; /*!< Set of usc options. */ + uint32 tLim : 5; /*!< Limit core shrinking to 2^tLim conflicts (0=no limit). */ + uint32 kLim :15; /*!< Limit for algorithm K (0=dynamic limit). */ +}; + +//! Parameter-Object for configuring a solver. +struct SolverParams : SolverStrategies { + //! Supported forget options. + enum Forget { forget_heuristic = 1u, forget_signs = 2u, forget_activities = 4u, forget_learnts = 8u }; + SolverParams(); + uint32 prepare(); + inline bool forgetHeuristic() const { return (forgetSet & uint32(forget_heuristic)) != 0; } + inline bool forgetSigns() const { return (forgetSet & uint32(forget_signs)) != 0; } + inline bool forgetActivities()const { return (forgetSet & uint32(forget_activities)) != 0; } + inline bool forgetLearnts() const { return (forgetSet & uint32(forget_learnts)) != 0; } + SolverParams& setId(uint32 sId) { id = sId; return *this; } + HeuParams heuristic; /*!< Parameters for decision heuristic. */ + OptParams opt; /*!< Parameters for optimization. */ + // 64-bit + uint32 seed; /*!< Seed for the random number generator. */ + uint32 lookOps : 16; /*!< Max. number of lookahead operations (0: no limit). */ + uint32 lookType : 2; /*!< Type of lookahead operations. */ + uint32 loopRep : 2; /*!< How to represent loops? */ + uint32 acycFwd : 1; /*!< Disable backward propagation in acyclicity checker. */ + uint32 forgetSet : 4; /*!< What to forget on (incremental step). */ + uint32 reserved : 7; +}; + +typedef Range Range32; + +//! Aggregates restart-parameters to configure restarts during search. +/*! + * \see ScheduleStrategy + */ +struct RestartParams { + RestartParams(); + enum SeqUpdate { seq_continue = 0, seq_repeat = 1, seq_disable = 2 }; + uint32 prepare(bool withLookback); + void disable(); + bool dynamic() const { return dynRestart != 0; } + bool local() const { return cntLocal != 0; } + SeqUpdate update() const { return static_cast(upRestart); } + ScheduleStrategy sched; /**< Restart schedule to use. */ + float blockScale; /**< Scaling factor for blocking restarts. */ + uint32 blockWindow: 16; /**< Size of moving assignment average for blocking restarts (0: disable). */ + uint32 blockFirst : 16; /**< Enable blocking restarts after blockFirst conflicts. */ + CLASP_ALIGN_BITFIELD(uint32) + uint32 counterRestart:16;/**< Apply counter implication bump every counterRestart restarts (0: disable). */ + uint32 counterBump:16; /**< Bump factor for counter implication restarts. */ + CLASP_ALIGN_BITFIELD(uint32) + uint32 shuffle :14; /**< Shuffle program after shuffle restarts (0: disable). */ + uint32 shuffleNext:14; /**< Re-Shuffle program every shuffleNext restarts (0: disable). */ + uint32 upRestart : 2; /**< How to update restart sequence after a model was found (one of SeqUpdate). */ + uint32 cntLocal : 1; /**< Count conflicts globally or relative to current branch? */ + uint32 dynRestart : 1; /**< Dynamic restarts enabled? */ +}; + +//! Reduce strategy used during solving. +/*! + * A reduce strategy mainly consists of an algorithm and a scoring scheme + * for measuring "activity" of learnt constraints. + */ +struct ReduceStrategy { + //! Reduction algorithm to use during solving. + enum Algorithm { + reduce_linear = 0, //!< Linear algorithm from clasp-1.3.x. + reduce_stable = 1, //!< Sort constraints by score but keep order in learnt db. + reduce_sort = 2, //!< Sort learnt db by score and remove fraction with lowest score. + reduce_heap = 3 //!< Similar to reduce_sort but only partially sorts learnt db. + }; + //! Score to measure "activity" of learnt constraints. + enum Score { + score_act = 0, //!< Activity only: how often constraint is used during conflict analysis. + score_lbd = 1, //!< Use literal block distance as activity. + score_both = 2 //!< Use activity and lbd together. + }; + //! Strategy for estimating size of problem. + enum EstimateSize { + est_dynamic = 0, //!< Dynamically decide whether to use number of variables or constraints. + est_con_complexity = 1, //!< Measure size in terms of constraint complexities. + est_num_constraints = 2, //!< Measure size in terms of number constraints. + est_num_vars = 3 //!< Measure size in terms of number variable. + }; + static uint32 scoreAct(const ConstraintScore& sc) { return sc.activity(); } + static uint32 scoreLbd(const ConstraintScore& sc) { return uint32(LBD_MAX+1)-sc.lbd(); } + static uint32 scoreBoth(const ConstraintScore& sc) { return (sc.activity()+1) * scoreLbd(sc); } + static int compare(Score sc, const ConstraintScore& lhs, const ConstraintScore& rhs) { + int fs = 0; + if (sc == score_act) { fs = ((int)scoreAct(lhs)) - ((int)scoreAct(rhs)); } + else if (sc == score_lbd) { fs = ((int)scoreLbd(lhs)) - ((int)scoreLbd(rhs)); } + return fs != 0 ? fs : ((int)scoreBoth(lhs)) - ((int)scoreBoth(rhs)); + } + static uint32 asScore(Score sc, const Clasp::ConstraintScore& act) { + if (sc == score_act) { return scoreAct(act); } + if (sc == score_lbd) { return scoreLbd(act); } + /* sc == score_both*/{ return scoreBoth(act);} + } + ReduceStrategy() : protect(0), glue(0), fReduce(75), fRestart(0), score(0), algo(0), estimate(0), noGlue(0) { + static_assert(sizeof(ReduceStrategy) == sizeof(uint32), "invalid bitset"); + } + uint32 protect : 7; /*!< Protect nogoods whose lbd was reduced and is now <= freeze. */ + uint32 glue : 4; /*!< Don't remove nogoods with lbd <= glue. */ + uint32 fReduce : 7; /*!< Fraction of nogoods to remove in percent. */ + uint32 fRestart: 7; /*!< Fraction of nogoods to remove on restart. */ + uint32 score : 2; /*!< One of Score. */ + uint32 algo : 2; /*!< One of Algorithm. */ + uint32 estimate: 2; /*!< How to estimate problem size in init. */ + uint32 noGlue : 1; /*!< Do not count glue clauses in limit. */ +}; + +//! Aggregates parameters for the nogood deletion heuristic used during search. +/*! + * - S:delCond {yes,no} + * - no:del {0}[0] + * - no:del | delCond in {no} + * - deletion | delCond in {yes} + * - del-* | delCond in {yes} + * - {delCond=yes, del-grow=no, del-cfl=no} + * . + */ +struct ReduceParams { + ReduceParams() : cflSched(ScheduleStrategy::none()), growSched(ScheduleStrategy::def()) + , fInit(1.0f/3.0f) + , fMax(3.0f) + , fGrow(1.1f) + , initRange(10, UINT32_MAX) + , maxRange(UINT32_MAX) + , memMax(0) {} + void disable(); + uint32 prepare(bool withLookback); + Range32 sizeInit(const SharedContext& ctx) const; + uint32 cflInit(const SharedContext& ctx) const; + uint32 getBase(const SharedContext& ctx) const; + float fReduce() const { return strategy.fReduce / 100.0f; } + float fRestart() const { return strategy.fRestart/ 100.0f; } + static uint32 getLimit(uint32 base, double f, const Range& r); + ScheduleStrategy cflSched; /**< Conflict-based deletion schedule. */ + ScheduleStrategy growSched; /**< Growth-based deletion schedule. */ + ReduceStrategy strategy; /**< Strategy to apply during nogood deletion. */ + float fInit; /**< Initial limit. X = P*fInit clamped to initRange.*/ + float fMax; /**< Maximal limit. X = P*fMax clamped to maxRange. */ + float fGrow; /**< Growth factor for db. */ + Range32 initRange; /**< Allowed range for initial limit. */ + uint32 maxRange; /**< Allowed range for maximal limit: [initRange.lo,maxRange]*/ + uint32 memMax; /**< Memory limit in MB (0 = no limit). */ +}; + +//! Parameter-Object for grouping solve-related options. +/*! + * \ingroup enumerator + */ +struct SolveParams { + //! Creates a default-initialized object. + /*! + * The following parameters are used: + * - restart : quadratic: 100*1.5^k / no restarts after first solution + * - deletion : initial size: vars()/3, grow factor: 1.1, max factor: 3.0, do not reduce on restart + * - randomization: disabled + * - randomProp : 0.0 (disabled) + * . + */ + SolveParams(); + uint32 prepare(bool withLookback); + bool randomize(Solver& s) const; + RestartParams restart; + ReduceParams reduce; + uint32 randRuns:16; /*!< Number of initial randomized-runs. */ + uint32 randConf:16; /*!< Number of conflicts comprising one randomized-run. */ + float randProb; /*!< Use random heuristic with given probability ([0,1]) */ + struct FwdCheck { /*!< Options for (partial checks in) DLP-solving; */ + uint32 highStep : 24; /*!< Init/inc high level when reached. */ + uint32 highPct : 7; /*!< Check on low + (high - low) * highPct/100 */ + uint32 signDef : 2; /*!< Default sign heuristic for atoms in disjunctions. */ + FwdCheck() { std::memset(this, 0, sizeof(*this)); } + } fwdCheck; +}; + +class SharedContext; +class SatPreprocessor; + +//! Parameters for (optional) Sat-preprocessing. +struct SatPreParams { + enum Algo { + sat_pre_no = 0, /**< Disable sat-preprocessing. */ + sat_pre_ve = 1, /**< Run variable elimination. */ + sat_pre_ve_bce = 2, /**< Run variable- and limited blocked clause elimination. */ + sat_pre_full = 3, /**< Run variable- and full blocked clause elimination. */ + }; + SatPreParams() : type(0u), limIters(0u), limTime(0u), limFrozen(0u), limClause(4000u), limOcc(0u) {} + uint32 type : 2; /**< One of algo. */ + uint32 limIters : 11; /**< Max. number of iterations. (0=no limit)*/ + uint32 limTime : 12; /**< Max. runtime in sec, checked after each iteration. (0=no limit)*/ + uint32 limFrozen: 7; /**< Run only if percent of frozen vars < maxFrozen. (0=no limit)*/ + uint32 limClause: 16; /**< Run only if \#clauses \< (limClause*1000) (0=no limit)*/ + uint32 limOcc : 16; /**< Skip v, if \#occ(v) \>= limOcc && \#occ(~v) \>= limOcc.(0=no limit) */ + bool clauseLimit(uint32 nc) const { return limClause && nc > (limClause*1000u); } + bool occLimit(uint32 pos, uint32 neg) const { return limOcc && pos > (limOcc-1u) && neg > (limOcc-1u); } + uint32 bce() const { return type != sat_pre_no ? type - 1 : 0; } + void disableBce() { type = std::min(type, uint32(sat_pre_ve));} + static SatPreprocessor* create(const SatPreParams&); +}; + +//! Parameters for a SharedContext object. +struct ContextParams { + //! How to handle short learnt clauses. + enum ShortMode { + short_implicit = 0, /*!< Share short learnt clauses via short implication graph. */ + short_explicit = 1, /*!< Do not use short implication graph. */ + }; + //! How to handle physical sharing of (explicit) constraints. + enum ShareMode { + share_no = 0, /*!< Do not physically share constraints (use copies instead). */ + share_problem = 1, /*!< Share problem constraints but copy learnt constraints. */ + share_learnt = 2, /*!< Copy problem constraints but share learnt constraints. */ + share_all = 3, /*!< Share all constraints. */ + share_auto = 4, /*!< Use share_no or share_all depending on number of solvers. */ + }; + ContextParams() : shareMode(share_auto), stats(0), shortMode(short_implicit), seed(1), hasConfig(0), cliConfig(0), cliId(0), cliMode(0) {} + SatPreParams satPre; /*!< Preprocessing options. */ + uint8 shareMode : 3; /*!< Physical sharing mode (one of ShareMode). */ + uint8 stats : 2; /*!< See SharedContext::enableStats(). */ + uint8 shortMode : 1; /*!< One of ShortMode. */ + uint8 seed : 1; /*!< Apply new seed when adding solvers. */ + uint8 hasConfig : 1; /*!< Reserved for command-line interface. */ + uint8 cliConfig; /*!< Reserved for command-line interface. */ + uint8 cliId; /*!< Reserved for command-line interface. */ + uint8 cliMode; /*!< Reserved for command-line interface. */ +}; + +//! Interface for configuring a SharedContext object and its associated solvers. +class Configuration { +public: + typedef SolverParams SolverOpts; + typedef SolveParams SearchOpts; + typedef ContextParams CtxOpts; + virtual ~Configuration(); + //! Prepares this configuration for the usage in the given context. + virtual void prepare(SharedContext&) = 0; + //! Returns the options for the shared context. + virtual const CtxOpts& context() const = 0; + //! Returns the number of solver options in this config. + virtual uint32 numSolver() const = 0; + //! Returns the number of search options in this config. + virtual uint32 numSearch() const = 0; + //! Returns the solver options for the i'th solver to be attached to the SharedContext. + virtual const SolverOpts& solver(uint32 i) const = 0; + //! Returns the search options for the i'th solver of the SharedContext. + virtual const SearchOpts& search(uint32 i) const = 0; + //! Returns the heuristic to be used in the i'th solver. + /*! + * The function is called in Solver::startInit(). + * \note The returned object is owned by the caller. + */ + virtual DecisionHeuristic* heuristic(uint32 i) const = 0; + //! Adds post propagators to the given solver. + virtual bool addPost(Solver& s) const = 0; + //! Returns the configuration with the given name or 0 if no such config exists. + /*! + * The default implementation returns this + * if n is empty or one of "." or "/". + * Otherwise, 0 is returned. + */ + virtual Configuration* config(const char* n); +}; + +//! Base class for user-provided configurations. +class UserConfiguration : public Configuration { +public: + //! Adds a lookahead post propagator to the given solver if requested. + /*! + * The function adds a lookahead post propagator if indicated by + * the solver's SolverParams. + */ + virtual bool addPost(Solver& s) const; + //! Returns the (modifiable) solver options for the i'th solver. + virtual SolverOpts& addSolver(uint32 i) = 0; + //! Returns the (modifiable) search options for the i'th solver. + virtual SearchOpts& addSearch(uint32 i) = 0; +}; + +//! Simple factory for decision heuristics. +struct Heuristic_t { + enum Type { Default = 0, Berkmin = 1, Vsids = 2, Vmtf = 3, Domain = 4, Unit = 5, None = 6, User = 7 }; + static inline bool isLookback(uint32 type) { return type >= (uint32)Berkmin && type < (uint32)Unit; } + //! Default callback for creating decision heuristics. + static DecisionHeuristic* create(Type t, const HeuParams& p); +}; + +struct ProjectMode_t { + enum Mode { Implicit = 0u, Output = 1u, Explicit = 2u }; +}; +typedef ProjectMode_t::Mode ProjectMode; + +//! Basic configuration for one or more SAT solvers. +class BasicSatConfig : public UserConfiguration, public ContextParams { +public: + struct HeuristicCreator { + virtual ~HeuristicCreator(); + virtual DecisionHeuristic* create(Heuristic_t::Type t, const HeuParams& p) = 0; + }; + + BasicSatConfig(); + void prepare(SharedContext&); + const CtxOpts& context() const { return *this; } + uint32 numSolver() const { return sizeVec(solver_); } + uint32 numSearch() const { return sizeVec(search_); } + const SolverOpts& solver(uint32 i) const { return solver_[i % solver_.size() ]; } + const SearchOpts& search(uint32 i) const { return search_[i % search_.size() ]; } + DecisionHeuristic* heuristic(uint32 i) const; + SolverOpts& addSolver(uint32 i); + SearchOpts& addSearch(uint32 i); + + virtual void reset(); + virtual void resize(uint32 numSolver, uint32 numSearch); + void setHeuristicCreator(HeuristicCreator* hc, Ownership_t::Type = Ownership_t::Acquire); +private: + typedef PodVector::type SolverVec; + typedef PodVector::type SearchVec; + typedef SingleOwnerPtr HeuFactory; + SolverVec solver_; + SearchVec search_; + HeuFactory heu_; +}; + +//! Base class for solving related events. +template +struct SolveEvent : Event_t { + SolveEvent(const Solver& s, Event::Verbosity verb) : Event_t(Event::subsystem_solve, verb), solver(&s) {} + const Solver* solver; +}; +struct Model; +//! Base class for handling results of a solve operation. +class ModelHandler { +public: + virtual ~ModelHandler(); + virtual bool onModel(const Solver&, const Model&) = 0; + virtual bool onUnsat(const Solver&, const Model&); +}; +//! Type for storing the lower bound of a minimize statement. +struct LowerBound { + LowerBound() : level(0), bound(CLASP_WEIGHT_SUM_MIN) {} + void reset() { *this = LowerBound(); } + bool active() const { return bound != CLASP_WEIGHT_SUM_MIN; } + uint32 level; + wsum_t bound; +}; + +} +#endif diff --git a/clasp/clasp/solver_types.h b/clasp/clasp/solver_types.h new file mode 100644 index 000000000..8323d44ce --- /dev/null +++ b/clasp/clasp/solver_types.h @@ -0,0 +1,797 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_SOLVER_TYPES_H_INCLUDED +#define CLASP_SOLVER_TYPES_H_INCLUDED +#ifdef _MSC_VER +#pragma once +#endif + +#include +#include +#include +#include +#include +#include +/*! + * \file + * \brief Types and functions used by a Solver + */ +namespace Clasp { +class SharedLiterals; + +/*! + * \addtogroup solver + */ +//@{ +/////////////////////////////////////////////////////////////////////////////// +// SearchLimits +/////////////////////////////////////////////////////////////////////////////// +struct DynamicLimit; +struct BlockLimit; + +//! Parameter-Object for managing search limits. +struct SearchLimits { + typedef DynamicLimit* LimitPtr; + typedef BlockLimit* BlockPtr; + SearchLimits(); + uint64 used; + struct { + uint64 conflicts; //!< Soft limit on number of conflicts for restart. + LimitPtr dynamic; //!< Use dynamic restarts based on lbd or conflict level. + BlockPtr block; //!< Optional strategy to increase restart limit. + bool local; //!< Apply conflict limit against active branch. + } restart; //!< Restart limits. + uint64 conflicts; //!< Soft limit on number of conflicts. + uint64 memory; //!< Soft memory limit for learnt lemmas (in bytes). + uint32 learnts; //!< Limit on number of learnt lemmas. +}; + +//! Type for implementing Glucose-style dynamic restarts. +/*! + * \see G. Audemard, L. Simon. "Refining Restarts Strategies for SAT and UNSAT" + * \note In contrast to Glucose's dynamic restarts, this class also implements + * a heuristic for dynamically adjusting the margin ratio K. + */ +struct DynamicLimit { + enum Type { lbd_limit, level_limit }; + //! Creates new limit with bounded queue of the given window size. + static DynamicLimit* create(uint32 window); + //! Destroys this object and its bounded queue. + void destroy(); + //! Resets moving average and adjust limit. + void init(float k, Type type, uint32 uLimit = 16000); + //! Resets moving average, i.e. clears the bounded queue. + void resetRun(); + //! Resets moving and global average. + void reset(); + //! Adds an observation and updates the moving average. Typically called on conflict. + void update(uint32 conflictLevel, uint32 lbd); + //! Notifies this object about a restart. + /*! + * The function checks whether to adjust the active margin ratio and/or + * whether to switch from LBD based to conflict level based restarts. + * + * \param maxLBD Threshold for switching between lbd and conflict level queue. + * \param k Lower bound for margin ratio. + */ + uint32 restart(uint32 maxLBD, float k); + //! Returns the number of updates since last restart. + uint32 runLen() const { return num_; } + //! Returns the maximal size of the bounded queue. + uint32 window() const { return cap_; } + //! Returns whether it is time to restart. + bool reached() const { return runLen() >= window() && (sma(adjust.type) * adjust.rk) > global.avg(adjust.type); } + struct { + //! Returns the global lbd or conflict level average. + double avg(Type t) const { return ratio(sum[t], samples); } + uint64 sum[2]; //!< Sum of lbds/conflict levels since last call to resetGlobal(). + uint64 samples;//!< Samples since last call to resetGlobal(). + } global; //!< Global lbd/conflict level data. + struct { + //! Returns the average restart length, i.e. number of conflicts between restarts. + double avgRestart() const { return ratio(samples, restarts); } + uint32 limit; //!< Number of conflicts before an update is forced. + uint32 restarts;//!< Number of restarts since last update. + uint32 samples; //!< Number of samples since last update. + float rk; //!< BD/CFL dynamic limit factor (typically < 1.0). + Type type; //!< Dynamic limit based on lbd or confllict level. + } adjust; //!< Data for dynamically adjusting margin ratio (rk). +private: + DynamicLimit(uint32 size); + DynamicLimit(const DynamicLimit&); + DynamicLimit& operator=(const DynamicLimit&); + double sma(Type t) const { return sum_[t] / double(cap_); } + uint32 smaU(Type t) const { return static_cast(sum_[t] / cap_); } + uint64 sum_[2]; + uint32 cap_; + uint32 pos_; + uint32 num_; +POTASSCO_WARNING_BEGIN_RELAXED + uint32 buffer_[0]; +POTASSCO_WARNING_END_RELAXED +}; + +//! Type for implementing Glucose-style blocking of restarts. +/*! + * \see G. Audemard, L. Simon "Refining Restarts Strategies for SAT and UNSAT" + * \see A. Biere, A. Froehlich "Evaluating CDCL Variable Scoring Schemes" + */ +struct BlockLimit { + explicit BlockLimit(uint32 windowSize, double R = 1.4); + bool push(uint32 nAssign) { + ema = n >= span + ? exponentialMovingAverage(ema, nAssign, alpha) + : cumulativeMovingAverage(ema, nAssign, n); + return ++n >= next; + } + //! Returns the exponential moving average scaled by r. + double scaled() const { return ema * r; } + double ema; //!< Current exponential moving average. + double alpha; //!< Smoothing factor: 2/(span+1). + uint64 next; //!< Enable once n >= next. + uint64 inc; //!< Block restart for next inc conflicts. + uint64 n; //!< Number of data points seen so far. + uint32 span; //!< Minimum observation window. + float r; //!< Scale factor for ema. +}; +/////////////////////////////////////////////////////////////////////////////// +// Statistics +/////////////////////////////////////////////////////////////////////////////// +class StatisticObject; +class StatsMap; +#if !defined(DOXY) +#define DOXY(X) +#endif +#define CLASP_STAT_DEFINE(m, k, a, accu) m +#define NO_ARG +#define CLASP_DECLARE_ISTATS(T) \ + void accu(const T& o);\ + static uint32 size();\ + static const char* key(uint32 i);\ + StatisticObject at(const char* key) const + +//! A struct for holding core statistics used by a solver. +/*! + * Core statistics are always present in a solver and hence + * can be used by heuristics. + */ +struct CoreStats { +#define CLASP_CORE_STATS(STAT, LHS, RHS) \ + STAT(uint64 choices; DOXY(number of choices) , "choices" , VALUE(choices) , LHS.choices += RHS.choices )\ + STAT(uint64 conflicts; DOXY(number of conflicts) , "conflicts" , VALUE(conflicts) , LHS.conflicts += RHS.conflicts )\ + STAT(uint64 analyzed; DOXY(number of conflicts analyzed), "conflicts_analyzed", VALUE(analyzed) , LHS.analyzed += RHS.analyzed )\ + STAT(uint64 restarts; DOXY(number of restarts) , "restarts" , VALUE(restarts) , LHS.restarts += RHS.restarts )\ + STAT(uint64 lastRestart;DOXY(length of last restart), "restarts_last" , VALUE(lastRestart), LHS.lastRestart = std::max(LHS.lastRestart, RHS.lastRestart)) + + CoreStats() { reset(); } + void reset(); + uint64 backtracks() const { return conflicts-analyzed; } + uint64 backjumps() const { return analyzed; } + double avgRestart() const { return ratio(analyzed, restarts); } + CLASP_DECLARE_ISTATS(CoreStats); + CLASP_CORE_STATS(CLASP_STAT_DEFINE, NO_ARG, NO_ARG) +}; +//! A struct for holding (optional) jump statistics. +struct JumpStats { +#define CLASP_JUMP_STATS(STAT, LHS, RHS) \ + STAT(uint64 jumps; DOXY(number of backjumps) , "jumps" , VALUE(jumps) , LHS.jumps += RHS.jumps) \ + STAT(uint64 bounded; DOXY(backjumps bounded by root level) , "jumps_bounded" , VALUE(bounded) , LHS.bounded += RHS.bounded) \ + STAT(uint64 jumpSum; DOXY(levels removed by jumps) , "levels" , VALUE(jumpSum) , LHS.jumpSum += RHS.jumpSum) \ + STAT(uint64 boundSum; DOXY(levels kept because of root level) , "levels_bounded" , VALUE(boundSum) , LHS.boundSum += RHS.boundSum) \ + STAT(uint32 maxJump; DOXY(longest backjump) , "max" , VALUE(maxJump) , MAX_MEM(LHS.maxJump, RHS.maxJump)) \ + STAT(uint32 maxJumpEx;DOXY(longest unbounded backjump) , "max_executed" , VALUE(maxJumpEx), MAX_MEM(LHS.maxJumpEx,RHS.maxJumpEx)) \ + STAT(uint32 maxBound; DOXY(max levels kept because of root level), "max_bounded" , VALUE(maxBound) , MAX_MEM(LHS.maxBound, RHS.maxBound)) + + JumpStats() { reset(); } + void reset(); + void update(uint32 dl, uint32 uipLevel, uint32 bLevel) { + ++jumps; + jumpSum += dl - uipLevel; + maxJump = std::max(maxJump, dl - uipLevel); + if (uipLevel < bLevel) { + ++bounded; + boundSum += bLevel - uipLevel; + maxJumpEx = std::max(maxJumpEx, dl - bLevel); + maxBound = std::max(maxBound, bLevel - uipLevel); + } + else { maxJumpEx = maxJump; } + } + uint64 jumped() const { return jumpSum - boundSum; } + double jumpedRatio()const { return ratio(jumped(), jumpSum); } + double avgBound() const { return ratio(boundSum, bounded); } + double avgJump() const { return ratio(jumpSum, jumps); } + double avgJumpEx() const { return ratio(jumped(), jumps); } + CLASP_DECLARE_ISTATS(JumpStats); + CLASP_JUMP_STATS(CLASP_STAT_DEFINE, NO_ARG, NO_ARG) +}; + +//! A struct for holding (optional) extended statistics. +struct ExtendedStats { + typedef ConstraintType type_t; + //! An array for storing a value[t-1] for each learnt Constraint_t::Type t. + typedef uint64 Array[Constraint_t::Type__max]; +#define CLASP_EXTENDED_STATS(STAT, LHS, RHS) \ + STAT(uint64 domChoices; DOXY(number of domain choices) , "domain_choices" , VALUE(domChoices) , LHS.domChoices += RHS.domChoices) \ + STAT(uint64 models; DOXY(number of models) , "models" , VALUE(models) , LHS.models += RHS.models) \ + STAT(uint64 modelLits; DOXY(decision levels in models) , "models_level" , VALUE(modelLits) , LHS.modelLits += RHS.modelLits) \ + STAT(uint64 hccTests; DOXY(number of stability tests) , "hcc_tests" , VALUE(hccTests) , LHS.hccTests += RHS.hccTests) \ + STAT(uint64 hccPartial; DOXY(number of partial tests) , "hcc_partial" , VALUE(hccPartial) , LHS.hccPartial += RHS.hccPartial) \ + STAT(uint64 deleted; DOXY(lemmas deleted ) , "lemmas_deleted" , VALUE(deleted) , LHS.deleted += RHS.deleted) \ + STAT(uint64 distributed;DOXY(lemmas distributed ) , "distributed" , VALUE(distributed), LHS.distributed+= RHS.distributed)\ + STAT(uint64 sumDistLbd; DOXY(lbds of distributed lemmas) , "distributed_sum_lbd" , VALUE(sumDistLbd) , LHS.sumDistLbd += RHS.sumDistLbd) \ + STAT(uint64 integrated; DOXY(lemmas integrated ) , "integrated" , VALUE(integrated) , LHS.integrated += RHS.integrated) \ + STAT(Array learnts; DOXY(lemmas of each learnt type) , "lemmas" , MEM_FUN(lemmas) , NO_ARG) \ + STAT(Array lits; DOXY(lits of each learnt type) , "lits_learnt" , MEM_FUN(learntLits), NO_ARG) \ + STAT(uint32 binary; DOXY(number of binary lemmas) , "lemmas_binary" , VALUE(binary) , LHS.binary += RHS.binary) \ + STAT(uint32 ternary; DOXY(number of ternary lemmas) , "lemmas_ternary" , VALUE(ternary) , LHS.ternary += RHS.ternary) \ + STAT(double cpuTime; DOXY(cpu time used ) , "cpu_time" , VALUE(cpuTime) , LHS.cpuTime += RHS.cpuTime) \ + STAT(uint64 intImps; DOXY(implications on integrating), "integrated_imps" , VALUE(intImps) , LHS.intImps+= RHS.intImps) \ + STAT(uint64 intJumps; DOXY(backjumps on integrating) , "integrated_jumps" , VALUE(intJumps), LHS.intJumps+=RHS.intJumps) \ + STAT(uint64 gpLits; DOXY(lits in received gps) , "guiding_paths_lits" , VALUE(gpLits) , LHS.gpLits += RHS.gpLits) \ + STAT(uint32 gps; DOXY(guiding paths received) , "guiding_paths" , VALUE(gps) , LHS.gps += RHS.gps) \ + STAT(uint32 splits; DOXY(split requests handled) , "splits" , VALUE(splits) , LHS.splits += RHS.splits) \ + STAT(NO_ARG , "lemmas_conflict", VALUE(learnts[0]) , LHS.learnts[0] += RHS.learnts[0]) \ + STAT(NO_ARG , "lemmas_loop" , VALUE(learnts[1]) , LHS.learnts[1] += RHS.learnts[1]) \ + STAT(NO_ARG , "lemmas_other" , VALUE(learnts[2]) , LHS.learnts[2] += RHS.learnts[2]) \ + STAT(NO_ARG , "lits_conflict" , VALUE(lits[0]) , LHS.lits[0] += RHS.lits[0]) \ + STAT(NO_ARG , "lits_loop" , VALUE(lits[1]) , LHS.lits[1] += RHS.lits[1]) \ + STAT(NO_ARG , "lits_other" , VALUE(lits[2]) , LHS.lits[2] += RHS.lits[2]) \ + STAT(JumpStats jumps;DOXY(backjump statistics) , "jumps" , MAP(jumps) , LHS.jumps.accu(RHS.jumps)) + + ExtendedStats() { reset(); } + void reset(); + void addLearnt(uint32 size, type_t t) { + if (t == Constraint_t::Static) return; + learnts[t-1]+= 1; + lits[t-1] += size; + binary += (size == 2); + ternary += (size == 3); + } + //! Total number of lemmas learnt. + uint64 lemmas() const { return std::accumulate(learnts, learnts+Constraint_t::Type__max, uint64(0)); } + //! Total number of literals in all learnt lemmas. + uint64 learntLits() const { return std::accumulate(lits, lits+Constraint_t::Type__max, uint64(0)); } + //! Number of lemmas of learnt type t. + uint64 lemmas(type_t t)const { return learnts[t-1]; } + //! Average length of lemmas of learnt type t. + double avgLen(type_t t)const { return ratio(lits[t-1], lemmas(t)); } + //! Average decision level on which models were found. + double avgModel() const { return ratio(modelLits, models); } + //! Ratio of lemmas that were distributed to other threads. + double distRatio() const { return ratio(distributed, learnts[0] + learnts[1]); } + //! Average lbd of lemmas that were distributed to other threads. + double avgDistLbd() const { return ratio(sumDistLbd, distributed); } + double avgIntJump() const { return ratio(intJumps, intImps); } + //! Average length (i.e. number of literals) of guiding paths. + double avgGp() const { return ratio(gpLits, gps); } + //! Ratio of lemmas integrated. + double intRatio() const { return ratio(integrated, distributed); } + CLASP_DECLARE_ISTATS(ExtendedStats); + CLASP_EXTENDED_STATS(CLASP_STAT_DEFINE,NO_ARG,NO_ARG) +}; + +//! A struct for aggregating statistics maintained in a solver object. +struct SolverStats : public CoreStats { + SolverStats(); + SolverStats(const SolverStats& o); + ~SolverStats(); + bool enableExtended(); + bool enable(const SolverStats& o) { return !o.extra || enableExtended(); } + void enableLimit(uint32 size); + void reset(); + void accu(const SolverStats& o); + void accu(const SolverStats& o, bool enableRhs); + void swapStats(SolverStats& o); + void flush() const; + uint32 size() const; + const char* key(uint32 i) const; + StatisticObject at(const char* key) const; + void addTo(const char* key, StatsMap& solving, StatsMap* accu) const; + inline void addLearnt(uint32 size, ConstraintType t); + inline void addConflict(uint32 dl, uint32 uipLevel, uint32 bLevel, uint32 lbd); + inline void addDeleted(uint32 num); + inline void addDistributed(uint32 lbd, ConstraintType t); + inline void addTest(bool partial); + inline void addModel(uint32 decisionLevel); + inline void addCpuTime(double t); + inline void addSplit(uint32 num = 1); + inline void addDomChoice(uint32 num = 1); + inline void addIntegratedAsserting(uint32 receivedDL, uint32 jumpDL); + inline void addIntegrated(uint32 num = 1); + inline void removeIntegrated(uint32 num = 1); + inline void addPath(const LitVec::size_type& sz); + DynamicLimit* limit; /**< Optional dynamic limit. */ + ExtendedStats* extra; /**< Optional extended statistics. */ + SolverStats* multi; /**< Not owned: set to accu stats in multishot solving. */ +private: SolverStats& operator=(const SolverStats&); +}; +inline void SolverStats::addLearnt(uint32 size, ConstraintType t) { if (extra) { extra->addLearnt(size, t); } } +inline void SolverStats::addDeleted(uint32 num) { if (extra) { extra->deleted += num; } } +inline void SolverStats::addDistributed(uint32 lbd, ConstraintType){ if (extra) { ++extra->distributed; extra->sumDistLbd += lbd; } } +inline void SolverStats::addIntegrated(uint32 n) { if (extra) { extra->integrated += n;} } +inline void SolverStats::removeIntegrated(uint32 n) { if (extra) { extra->integrated -= n;} } +inline void SolverStats::addCpuTime(double t) { if (extra) { extra->cpuTime += t; } } +inline void SolverStats::addSplit(uint32 num) { if (extra) { extra->splits += num; } } +inline void SolverStats::addPath(const LitVec::size_type& sz) { if (extra) { ++extra->gps; extra->gpLits += sz; } } +inline void SolverStats::addTest(bool partial) { if (extra) { ++extra->hccTests; extra->hccPartial += (uint32)partial; } } +inline void SolverStats::addModel(uint32 DL) { if (extra) { ++extra->models; extra->modelLits += DL; } } +inline void SolverStats::addDomChoice(uint32 n) { if (extra) { extra->domChoices += n; } } +inline void SolverStats::addIntegratedAsserting(uint32 rDL, uint32 jDL) { + if (extra) { ++extra->intImps; extra->intJumps += (rDL - jDL); } +} +inline void SolverStats::addConflict(uint32 dl, uint32 uipLevel, uint32 bLevel, uint32 lbd) { + ++analyzed; + if (limit) { limit->update(dl, lbd); } + if (extra) { extra->jumps.update(dl, uipLevel, bLevel); } +} +#undef CLASP_STAT_DEFINE +#undef NO_ARG +#undef DOXY +#undef CLASP_DECLARE_ISTATS +/////////////////////////////////////////////////////////////////////////////// +// Clauses +/////////////////////////////////////////////////////////////////////////////// +//! Primitive representation of a clause. +struct ClauseRep { + typedef ConstraintInfo Info; + static ClauseRep create(Literal* cl, uint32 sz, const Info& i = Info()) { return ClauseRep(cl, sz, false, i);} + static ClauseRep prepared(Literal* cl, uint32 sz, const Info& i = Info()){ return ClauseRep(cl, sz, true, i); } + ClauseRep(Literal* cl = 0, uint32 sz = 0, bool p = false, const Info& i = Info()) : info(i), size(sz), prep(uint32(p)), lits(cl) {} + Info info; /*!< Additional clause info. */ + uint32 size:31; /*!< Size of array of literals. */ + uint32 prep: 1; /*!< Whether lits is already prepared. */ + Literal* lits; /*!< Pointer to array of literals (not owned!). */ + bool isImp() const { return size > 1 && size < 4; } +}; + +//! (Abstract) base class for clause types. +/*! + * ClauseHead is used to enforce a common memory-layout for all clauses. + * It contains the two watched literals and a cache literal to improve + * propagation performance. A virtual call to Constraint::propagate() + * is only needed if the other watch is not true and the cache literal + * is false. + */ +class ClauseHead : public Constraint { +public: + enum { HEAD_LITS = 3, MAX_SHORT_LEN = 5 }; + explicit ClauseHead(const InfoType& init); + // base interface + //! Propagates the head and calls updateWatch() if necessary. + PropResult propagate(Solver& s, Literal, uint32& data); + //! Type of clause. + Type type() const { return info_.type(); } + //! Returns the activity of this clause. + ScoreType activity() const { return info_.score(); } + //! True if this clause currently is the antecedent of an assignment. + bool locked(const Solver& s) const; + //! Halves the activity of this clause. + void decreaseActivity() { info_.score().reduce(); } + void resetActivity() { info_.score().reset(); } + //! Downcast from LearntConstraint. + ClauseHead* clause() { return this; } + + // clause interface + typedef std::pair BoolPair; + //! Adds watches for first two literals in head to solver. + void attach(Solver& s); + void resetScore(ScoreType sc); + //! Returns true if head is satisfied w.r.t current assignment in s. + bool satisfied(const Solver& s) const; + //! Conditional clause? + bool tagged() const { return info_.tagged(); } + //! Contains aux vars? + bool aux() const { return info_.aux(); } + bool learnt() const { return info_.learnt(); } + uint32 lbd() const { return info_.lbd(); } + //! Removes watches from s. + virtual void detach(Solver& s); + //! Returns the size of this clause. + virtual uint32 size() const = 0; + //! Returns the literals of this clause in out. + virtual void toLits(LitVec& out) const = 0; + //! Returns true if this clause is a valid "reverse antecedent" for p. + virtual bool isReverseReason(const Solver& s, Literal p, uint32 maxL, uint32 maxN) = 0; + //! Removes p from clause if possible. + /*! + * \return + * The first component of the returned pair specifies whether or not + * p was removed from the clause. + * The second component of the returned pair specifies whether + * the clause should be kept (false) or removed (true). + */ + virtual BoolPair strengthen(Solver& s, Literal p, bool allowToShort = true) = 0; +protected: + struct Local { + void init(uint32 sz); + bool isSmall() const { return (mem[0] & 1u) == 0u; } + bool contracted() const { return (mem[0] & 3u) == 3u; } + bool strengthened()const { return (mem[0] & 5u) == 5u; } + uint32 size() const { return mem[0] >> 3; } + void setSize(uint32 size) { mem[0] = (size << 3) | (mem[0] & 7u); } + void markContracted() { mem[0] |= 2u; } + void markStrengthened() { mem[0] |= 4u; } + void clearContracted() { mem[0] &= ~2u; } + void clearIdx() { mem[1] = 0; } + uint32 mem[2]; + }; + bool toImplication(Solver& s); + void clearTagged() { info_.setTagged(false); } + void setLbd(uint32 x) { info_.setLbd(x); } + //! Shall replace the watched literal at position pos with a non-false literal. + /*! + * \pre pos in [0,1] + * \pre s.isFalse(head_[pos]) && s.isFalse(head_[2]) + * \pre head_[pos^1] is the other watched literal + */ + virtual bool updateWatch(Solver& s, uint32 pos) = 0; + union { + Local local_; + SharedLiterals* shared_; + }; + InfoType info_; + Literal head_[HEAD_LITS]; // two watched literals and one cache literal +}; +//! Allocator for small (at most 32-byte) clauses. +class SmallClauseAlloc { +public: + SmallClauseAlloc(); + ~SmallClauseAlloc(); + void* allocate() { + if(freeList_ == 0) { + allocBlock(); + } + Chunk* r = freeList_; + freeList_ = r->next; + return r; + } + void free(void* mem) { + Chunk* b = reinterpret_cast(mem); + b->next = freeList_; + freeList_= b; + } +private: + SmallClauseAlloc(const SmallClauseAlloc&); + SmallClauseAlloc& operator=(const SmallClauseAlloc&); + struct Chunk { + Chunk* next; // enforce ptr alignment + unsigned char mem[32 - sizeof(Chunk*)]; + }; + struct Block { + enum { num_chunks = 1023 }; + Block* next; + unsigned char pad[32-sizeof(Block*)]; + Chunk chunk[num_chunks]; + }; + void allocBlock(); + Block* blocks_; + Chunk* freeList_; +}; +/////////////////////////////////////////////////////////////////////////////// +// Watches +/////////////////////////////////////////////////////////////////////////////// +//! Represents a clause watch in a Solver. +struct ClauseWatch { + //! Clause watch: clause head + explicit ClauseWatch(ClauseHead* a_head) : head(a_head) { } + ClauseHead* head; + struct EqHead { + explicit EqHead(ClauseHead* h) : head(h) {} + bool operator()(const ClauseWatch& w) const { return head == w.head; } + ClauseHead* head; + }; +}; + +//! Represents a generic watch in a Solver. +struct GenericWatch { + //! A constraint and some associated data. + explicit GenericWatch(Constraint* a_con, uint32 a_data = 0) : con(a_con), data(a_data) {} + //! Calls propagate on the stored constraint and passes the stored data to that constraint. + Constraint::PropResult propagate(Solver& s, Literal p) { return con->propagate(s, p, data); } + + Constraint* con; /**< The constraint watching a certain literal. */ + uint32 data; /**< Additional data associated with this watch - passed to constraint on update. */ + + struct EqConstraint { + explicit EqConstraint(Constraint* c) : con(c) {} + bool operator()(const GenericWatch& w) const { return con == w.con; } + Constraint* con; + }; +}; + +//! Watch list type. +typedef bk_lib::left_right_sequence WatchList; +inline void releaseVec(WatchList& w) { w.clear(true); } + +/////////////////////////////////////////////////////////////////////////////// +// Assignment +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +//! Type for storing reasons for variable assignments together with additional data. +/*! + * \note On 32-bit systems additional data is stored in the high-word of antecedents. + */ +struct ReasonStore32 : PodVector::type { + uint32 data(uint32 v) const { return decode((*this)[v]);} + void setData(uint32 v, uint32 data) { encode((*this)[v], data); } + static void encode(Antecedent& a, uint32 data) { + a.asUint() = (uint64(data)<<32) | static_cast(a.asUint()); + } + static uint32 decode(const Antecedent& a) { + return static_cast(a.asUint()>>32); + } + struct value_type { + value_type(const Antecedent& a, uint32 d) : ante_(a) { + if (d != UINT32_MAX) { encode(ante_, d); assert(data() == d && ante_.type() == Antecedent::Generic); } + } + const Antecedent& ante() const { return ante_; } + uint32 data() const { return ante_.type() == Antecedent::Generic ? decode(ante_) : UINT32_MAX; } + Antecedent ante_; + }; +}; + +//! Type for storing reasons for variable assignments together with additional data. +/* + * \note On 64-bit systems additional data is stored in a separate container. + */ +struct ReasonStore64 : PodVector::type { + uint32 dataSize() const { return (uint32)data_.size(); } + void dataResize(uint32 nv) { if (nv > dataSize()) data_.resize(nv, UINT32_MAX); } + uint32 data(uint32 v) const { return v < dataSize() ? data_[v] : UINT32_MAX; } + void setData(uint32 v, uint32 data) { dataResize(v+1); data_[v] = data; } + VarVec data_; + struct value_type : std::pair { + value_type(const Antecedent& a, uint32 d) : std::pair(a, d) {} + const Antecedent& ante() const { return first; } + uint32 data() const { return second; } + }; +}; + +//! A set of configurable values for a variable. +/*! + * Beside its currently assigned value, a variable + * can also have a user, saved, preferred, and default value. + * These values are used in sign selection to determine the signed literal + * of a variable to be assign first. + * During sign selection, the values form a hierarchy: + * user > saved > preferred > current sign score of heuristic > default value + */ +struct ValueSet { + ValueSet() : rep(0) {} + enum Value { user_value = 0x03u, saved_value = 0x0Cu, pref_value = 0x30u, def_value = 0xC0u }; + bool sign() const { return (right_most_bit(rep) & 0xAAu) != 0; } + bool empty() const { return rep == 0; } + bool has(Value v) const { return (rep & v) != 0; } + bool has(uint32 f)const { return (rep & f) != 0; } + ValueRep get(Value v) const { return static_cast((rep & v) / right_most_bit(static_cast(v))); } + void set(Value which, ValueRep to) { rep &= ~which; rep |= (to * right_most_bit(static_cast(which))); } + void save(ValueRep x) { rep &= ~saved_value; rep |= (x << 2); } + uint8 rep; +}; + +//! Stores assignment related information. +/*! + * For each variable v, the class stores + * - v's current value (value_free if unassigned) + * - the decision level on which v was assign (only valid if value(v) != value_free) + * - the reason why v is in the assignment (only valid if value(v) != value_free) + * - (optionally) some additional data associated with the reason + * . + * Furthermore, the class stores the sequences of assignments as a set of + * true literals in its trail-member. + */ +class Assignment { +public: + typedef PodVector::type AssignVec; + typedef PodVector::type PrefVec; + typedef bk_lib::detail::if_then_else< + sizeof(Constraint*)==sizeof(uint64) + , ReasonStore64 + , ReasonStore32>::type ReasonVec; + typedef ReasonVec::value_type ReasonWithData; + Assignment() : front(0), elims_(0), units_(0) { } + LitVec trail; // assignment sequence + uint32 front; // and "propagation queue" + bool qEmpty() const { return front == static_cast(trail.size()); } + uint32 qSize() const { return static_cast(trail.size() - front); } + Literal qPop() { return trail[front++]; } + void qReset() { front = static_cast(trail.size()); } + + //! Number of variables in the three-valued assignment. + uint32 numVars() const { return (uint32)assign_.size(); } + //! Number of assigned variables. + uint32 assigned() const { return (uint32)trail.size(); } + //! Number of free variables. + uint32 free() const { return numVars() - (assigned()+elims_); } + //! Returns the largest possible decision level. + uint32 maxLevel() const { return (1u<<28)-2; } + //! Returns v's value in the three-valued assignment. + ValueRep value(Var v) const { return ValueRep(assign_[v] & 3u); } + //! Returns the decision level on which v was assigned if value(v) != value_free. + uint32 level(Var v) const { return assign_[v] >> 4u; } + //! Returns true if v was not eliminated from the assignment. + bool valid(Var v) const { return (assign_[v] & elim_mask) != elim_mask; } + //! Returns the set of preferred values of v. + const ValueSet pref(Var v) const { return v < pref_.size() ? pref_[v] : ValueSet(); } + //! Returns the reason for v being assigned if value(v) != value_free. + const Antecedent& reason(Var v)const { return reason_[v]; } + //! Returns the reason data associated with v. + uint32 data(Var v) const { return reason_.data(v); } + + void reserve(uint32 n) { + assign_.reserve(n); + reason_.reserve(n); + } + //! Resize to nv variables. + void resize(uint32 nv) { + assign_.resize(nv); + reason_.resize(nv); + } + //! Adds a var to assignment - initially the new var is unassigned. + Var addVar() { + assign_.push_back(0); + reason_.push_back(0); + return numVars()-1; + } + //! Allocates space for storing preferred values for all variables. + void requestPrefs() { + if (pref_.size() != assign_.size()) { pref_.resize(assign_.size()); } + } + //! Eliminates v from the assignment. + void eliminate(Var v) { + assert(value(v) == value_free && "Can not eliminate assigned var!\n"); + if (valid(v)) { assign_[v] = elim_mask|value_true; ++elims_; } + } + //! Assigns p.var() on level lev to the value that makes p true and stores x as reason for the assignment. + /*! + * \return true if the assignment is consistent. False, otherwise. + * \post If true is returned, p is in trail. Otherwise, ~p is. + */ + bool assign(Literal p, uint32 lev, const Antecedent& x) { + const Var v = p.var(); + const ValueRep val = value(v); + if (val == value_free) { + assert(valid(v)); + assign_[v] = (lev<<4) + trueValue(p); + reason_[v] = x; + trail.push_back(p); + return true; + } + return val == trueValue(p); + } + bool assign(Literal p, uint32 lev, Constraint* c, uint32 data) { + const Var v = p.var(); + const ValueRep val = value(v); + if (val == value_free) { + assert(valid(v)); + assign_[v] = (lev<<4) + trueValue(p); + reason_[v] = c; + reason_.setData(v, data); + trail.push_back(p); + return true; + } + return val == trueValue(p); + } + //! Undos all assignments in the range trail[first, last). + /*! + * \param first First assignment to be undone. + * \param save If true, previous assignment of a var is saved before it is undone. + */ + void undoTrail(LitVec::size_type first, bool save) { + if (!save) { popUntil<&Assignment::clear>(trail[first]); } + else { requestPrefs(); popUntil<&Assignment::saveAndClear>(trail[first]); } + qReset(); + } + //! Undos the last assignment. + void undoLast() { clear(trail.back().var()); trail.pop_back(); } + //! Returns the last assignment as a true literal. + Literal last() const { return trail.back(); } + Literal&last() { return trail.back(); } + /*! + * \name Implementation functions + * Low-level implementation functions. Use with care and only if you + * know what you are doing! + */ + //@{ + uint32 units() const { return units_; } + bool seen(Var v, uint8 m) const { return (assign_[v] & (m<<2)) != 0; } + void setSeen(Var v, uint8 m) { assign_[v] |= (m<<2); } + void clearSeen(Var v) { assign_[v] &= ~uint32(12); } + void clearValue(Var v) { assign_[v] &= ~uint32(3); } + void setValue(Var v, ValueRep val) { + assert(value(v) == val || value(v) == value_free); + assign_[v] |= val; + } + void setReason(Var v, const Antecedent& a) { reason_[v] = a; } + void setData(Var v, uint32 data) { reason_.setData(v, data); } + void setPref(Var v, ValueSet::Value which, ValueRep to) { pref_[v].set(which, to); } + void copyAssignment(Assignment& o) const { o.assign_ = assign_; } + bool markUnits() { while (units_ != front) { setSeen(trail[units_++].var(), 3u); } return true; } + void setUnits(uint32 ts) { units_ = ts; } + void resetPrefs() { pref_.assign(pref_.size(), ValueSet()); } + void clear(Var v) { assign_[v] = 0; } + void saveAndClear(Var v) { pref_[v].save(value(v)); clear(v); } + //@} +private: + static const uint32 elim_mask = uint32(0xFFFFFFF0u); + Assignment(const Assignment&); + Assignment& operator=(const Assignment&); + template + void popUntil(Literal stop) { + Literal p; + do { + p = trail.back(); trail.pop_back(); + (this->*op)(p.var()); + } while (p != stop); + } + AssignVec assign_; // for each var: three-valued assignment + ReasonVec reason_; // for each var: reason for being assigned (+ optional data) + PrefVec pref_; // for each var: set of preferred values + uint32 elims_; // number of variables that were eliminated from the assignment + uint32 units_; // number of marked top-level assignments +}; + +//! Stores information about a literal that is implied on an earlier level than the current decision level. +struct ImpliedLiteral { + typedef Assignment::ReasonWithData AnteInfo; + ImpliedLiteral(Literal a_lit, uint32 a_level, const Antecedent& a_ante, uint32 a_data = UINT32_MAX) + : lit(a_lit) + , level(a_level) + , ante(a_ante, a_data) { + } + Literal lit; /**< The implied literal */ + uint32 level; /**< The earliest decision level on which lit is implied */ + AnteInfo ante; /**< The reason why lit is implied on decision-level level */ +}; +//! A type for storing ImpliedLiteral objects. +struct ImpliedList { + typedef PodVector::type VecType; + typedef VecType::const_iterator iterator; + ImpliedList() : level(0), front(0) {} + //! Searches for an entry

in list. Returns 0 if none is found. + ImpliedLiteral* find(Literal p) { + for (VecType::size_type i = 0, end = lits.size(); i != end; ++i) { + if (lits[i].lit == p) { return &lits[i]; } + } + return 0; + } + //! Adds a new object to the list. + void add(uint32 dl, const ImpliedLiteral& n) { + if (dl > level) { level = dl; } + lits.push_back(n); + } + //! Returns true if list contains entries that must be reassigned on current dl. + bool active(uint32 dl) const { return dl < level && front != lits.size(); } + //! Reassigns all literals that are still implied. + bool assign(Solver& s); + iterator begin() const { return lits.begin(); } + iterator end() const { return lits.end(); } + VecType lits; // current set of (out-of-order) implied literals + uint32 level; // highest dl on which lits must be reassigned + uint32 front; // current starting position in lits +}; +//@} +} +#endif diff --git a/clasp/clasp/statistics.h b/clasp/clasp/statistics.h new file mode 100644 index 000000000..970c2cec1 --- /dev/null +++ b/clasp/clasp/statistics.h @@ -0,0 +1,318 @@ +// +// Copyright (c) 2016-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +//! \file +//! \brief Types and functions for accessing statistics. +#ifndef CLASP_STATISTICS_H_INCLUDED +#define CLASP_STATISTICS_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif +#include +#include +#include +#include +namespace Clasp { + +template +double _getValue(const T* v) { return static_cast(*v); } + +//! Discriminated union representing either a single statistic value or a composite. +class StatisticObject { +public: + typedef Potassco::Statistics_t Type; + struct Hasher { std::size_t operator()(const StatisticObject& o) const { return o.hash(); } }; + //! Creates an empty (invalid) object. + StatisticObject(); + //! Creates a Value object - static_cast(*obj) shall be valid. + template + static StatisticObject value(const T* obj) { + return value(obj); + } + //! Creates a mapped Value object: f(obj) -> double + template + static StatisticObject value(const T* obj) { + return StatisticObject(obj, registerValue()); + } + //! Creates a Map object. + /*! + * The following expression shall be valid: + * obj->size(): shall return the number of keys in obj + * obj->key(i): shall return the i'th key of this object (i >= 0). + * obj->at(const char* k): shall return the StatisticObject under the given key. + * If k is invalid, shall either throw an exception or return an empty object. + */ + template + static StatisticObject map(const T* obj) { + return StatisticObject(obj, registerMap()); + } + //! Creates an Array object. + /*! + * The following expression shall be valid: + * obj->size(): shall return the size of the array. + * obj->at(i): shall return the StatisticObject under the given key i >= 0. + * If i is invalid, shall either throw an exception or return an empty object. + */ + template + static StatisticObject array(const T* obj) { + return StatisticObject(obj, registerArray()); + } + //! Returns the type of this object. + Type type() const; + //! Returns whether this object is empty. + bool empty() const; + //! Returns the number of children of this object or 0 if this is not a composite object. + uint32 size() const; + + /*! + * \name Map + * \pre type() == Map + */ + //@{ + //! Returns the i'th key of this map. + /*! + * \pre i < size() + */ + const char* key(uint32 i) const; + //! Returns the object under the given key. + /*! + * \pre k in key([0..size())) + */ + StatisticObject at(const char* k) const; + //@} + + //! Returns the object at the given index. + /*! + * \pre Type() == Array + * \pre i < size() + */ + StatisticObject operator[](uint32 i) const; + + //! Returns the value of this object. + /*! + * \pre type() == Value + */ + double value() const; + + bool operator==(const StatisticObject& rhs) const { + return this->handle_ == rhs.handle_; + } + bool operator<(const StatisticObject& rhs) const { + return this->handle_ < rhs.handle_; + } + std::size_t hash() const; + uint64 toRep() const; + const void* self() const; + std::size_t typeId()const; + static StatisticObject fromRep(uint64); +private: + struct I { + typedef const void* ObjPtr; + explicit I(Type t) : type(t) {} + Type type; + }; + struct V : I { + V(double(*v)(ObjPtr)) : I(Potassco::Statistics_t::Value), value(v) {} + double(*value)(ObjPtr); + }; + struct A : I { + A(uint32(*sz)(ObjPtr), StatisticObject(*a)(ObjPtr, uint32)) : I(Potassco::Statistics_t::Array), size(sz), at(a) {} + uint32(*size)(ObjPtr); + StatisticObject(*at)(ObjPtr, uint32); + }; + struct M : I { + M(uint32(*sz)(ObjPtr), StatisticObject(*a)(ObjPtr, const char*), const char* (*k)(ObjPtr, uint32)) : I(Potassco::Statistics_t::Map), size(sz), at(a), key(k) {} + uint32(*size)(ObjPtr); + StatisticObject(*at)(ObjPtr, const char*); + const char* (*key)(ObjPtr, uint32); + }; + static uint32 registerType(const I* vtab) { + types_s.push_back(vtab); + return static_cast(types_s.size() - 1); + } + template + static uint32 registerValue(); + template + static uint32 registerMap(); + template + static uint32 registerArray(); + StatisticObject(const void* obj, uint32 type); + + typedef PodVector::type RegVec; + const I* tid() const; + static I empty_s; + static RegVec types_s; + uint64 handle_; +}; + +template +uint32 StatisticObject::registerArray() { + static const struct Array_T : A { + Array_T() : A(&Array_T::size, &Array_T::at) {} + static uint32 size(ObjPtr obj) { return toU32(static_cast(obj)->size()); } + static StatisticObject at(ObjPtr obj, uint32 i) { return static_cast(obj)->at(i); } + } vtab_s; + static const uint32 id = registerType(&vtab_s); + return id; +} +template +uint32 StatisticObject::registerMap() { + static const struct Map_T : M { + Map_T() : M(&Map_T::size, &Map_T::at, &Map_T::key) {} + static inline const T* cast(ObjPtr obj) { return static_cast(obj); } + static uint32 size(ObjPtr obj) { return cast(obj)->size(); } + static StatisticObject at(ObjPtr obj, const char* k) { return cast(obj)->at(k); } + static const char* key(ObjPtr obj, uint32 i) { return cast(obj)->key(i); } + } vtab_s; + static const uint32 id = registerType(&vtab_s); + return id; +} + +template +uint32 StatisticObject::registerValue() { + static const struct Value_T : V { + Value_T() : V(&Value_T::value) {} + static double value(ObjPtr obj) { return f(static_cast(obj)); } + } vtab_s; + static const uint32 id = StatisticObject::registerType(&vtab_s); + return id; +} +//! A type that maps string keys to statistic objects. +class StatsMap { +public: + // StatisticObject + uint32 size() const { return sizeVec(keys_); } + const char* key(uint32 i) const { return keys_.at(i).first; } + StatisticObject at(const char* k) const; + // Own interface + const StatisticObject* find(const char* k) const; + bool add(const char* k, const StatisticObject&); + void push(const char* k, const StatisticObject&); + StatisticObject toStats() const { return StatisticObject::map(this); } +private: + typedef PodVector >::type MapType; + MapType keys_; +}; +//! An array of statistic objects. +template +class StatsVec : private PodVector::type { +public: + StatsVec() : own_(true) {} + ~StatsVec() { + if (own_) { for (iterator it = this->begin(), end = this->end(); it != end; ++it) { delete *it; } } + } + typedef typename PodVector::type base_type; + typedef typename base_type::const_iterator const_iterator; + typedef typename base_type::iterator iterator; + using base_type::size; + using base_type::operator[]; + using base_type::begin; + using base_type::end; + void growTo(uint32 newSize) { if (newSize > size()) this->resize(newSize); } + void reset() { for (iterator it = this->begin(), end = this->end(); it != end; ++it) { (*it)->reset(); } } + StatisticObject at(uint32 i) const { return get_(this->base_type::at(i), bk_lib::detail::int2type()); } + StatisticObject toStats() const { return StatisticObject::array(this); } + void acquire() { own_ = true; } + void release() { own_ = false; } +private: + static StatisticObject get_(const T* ptr, bk_lib::detail::int2type) { return StatisticObject::map(ptr); } + static StatisticObject get_(const T* ptr, bk_lib::detail::int2type) { return StatisticObject::array(ptr); } + static StatisticObject get_(const T* ptr, bk_lib::detail::int2type) { return StatisticObject::value(ptr); } + StatsVec(const StatsVec&); + StatsVec& operator=(const StatsVec&); + bool own_; +}; + +//! A class for traversing, querying, and adding statistics. +/*! + * \ingroup clingo + */ +class ClaspStatistics : public Potassco::AbstractStatistics { +public: + typedef Potassco::Statistics_t Type; + ClaspStatistics(); + ClaspStatistics(StatisticObject root); + ~ClaspStatistics(); + + StatsMap* makeRoot(); + + // Base interface + virtual Key_t root() const; + virtual Type type(Key_t key) const; + virtual size_t size(Key_t key) const; + virtual bool writable(Key_t key) const; + virtual Key_t at(Key_t arrK, size_t index) const; + virtual Key_t push(Key_t arr, Type type); + virtual const char* key(Key_t mapK, size_t i) const; + virtual Key_t get(Key_t mapK, const char* key) const; + virtual bool find(Key_t mapK, const char* element, Key_t* outKey) const; + virtual Key_t add(Key_t mapK, const char* name, Type type); + virtual double value(Key_t key) const; + virtual void set(Key_t key, double value); + + Key_t changeRoot(Key_t newRoot); + bool removeStat(const StatisticObject&, bool recurse); + bool removeStat(Key_t k, bool recurse); + + // Remove unreachable stats + void update(); + StatisticObject findObject(Key_t root, const char* path, Key_t* track = 0) const; + StatisticObject getObject(Key_t k) const; +private: + ClaspStatistics(const ClaspStatistics&); + ClaspStatistics& operator=(const ClaspStatistics&); + struct Impl; + Impl* impl_; +}; + +struct SolverStats; +struct JumpStats; +struct ExtendedStats; +struct ProblemStats; + +//! Interface for visiting statistics. +/*! + * \ingroup facade + */ +class StatsVisitor { +public: + enum Operation { Enter, Leave } ; + virtual ~StatsVisitor(); + // compound + virtual bool visitGenerator(Operation op); // default: return true + virtual bool visitThreads(Operation op); // default: return true + virtual bool visitTester(Operation op); // default: return true + virtual bool visitHccs(Operation op); // default: return true + + // leafs + virtual void visitThread(uint32, const SolverStats& stats); + virtual void visitHcc(uint32, const ProblemStats& p, const SolverStats& s); + virtual void visitLogicProgramStats(const Asp::LpStats& stats) = 0; + virtual void visitProblemStats(const ProblemStats& stats) = 0; + virtual void visitSolverStats(const SolverStats& stats) = 0; + virtual void visitExternalStats(const StatisticObject& stats) = 0; +}; + +} +#endif diff --git a/clasp/clasp/unfounded_check.h b/clasp/clasp/unfounded_check.h new file mode 100644 index 000000000..ea7a9cdb4 --- /dev/null +++ b/clasp/clasp/unfounded_check.h @@ -0,0 +1,310 @@ +// +// Copyright (c) 2010-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +#ifndef CLASP_UNFOUNDED_CHECK_H_INCLUDED +#define CLASP_UNFOUNDED_CHECK_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif +#include +#include +#include +#include +namespace Clasp { +class LoopFormula; + +//! Clasp's default unfounded set checker. +/*! + * \ingroup propagator + * Searches for unfounded atoms by checking the positive dependency graph (PDG) + * + * Basic Idea: + * - For each (non-false) atom a, let source(a) be a body B in body(a) that provides an external support for a + * - If no such B exists, a must be false + * - If source(a) becomes false and a is not false: + * - Let Q = {}; + * - add a to Q + * - For each B' s.th B' is not external to Q + * - add { a' | source(a') = B } to Q + * - Try to find new sources for all atoms a in Q + */ +class DefaultUnfoundedCheck : public PostPropagator { +public: + typedef Asp::PrgDepGraph DependencyGraph; + typedef DependencyGraph::NodeId NodeId; + typedef DependencyGraph::BodyNode BodyNode; + typedef DependencyGraph::AtomNode AtomNode; + typedef const DependencyGraph* ConstGraphPtr; + typedef DependencyGraph* GraphPtr; + //! Defines the supported reasons for explaining assignments. + enum ReasonStrategy { + common_reason = LoopReason_t::Explicit, /*!< one reason for each unfounded set but one clause for each atom */ + only_reason = LoopReason_t::Implicit, /*!< store only the reason but don't learn a nogood */ + distinct_reason, /*!< distinct reason and clause for each unfounded atom */ + shared_reason, /*!< one shared loop formula for each unfounded set */ + no_reason, /*!< do no compute reasons for unfounded sets (only valid if learning is disabled!) */ + }; + + explicit DefaultUnfoundedCheck(DependencyGraph& graph, ReasonStrategy st = common_reason); + ~DefaultUnfoundedCheck(); + + ReasonStrategy reasonStrategy() const { return strategy_; } + void setReasonStrategy(ReasonStrategy rs); + + ConstGraphPtr graph() const { return graph_; } + uint32 nodes() const { return static_cast(atoms_.size() + bodies_.size()); } + + // base interface + uint32 priority() const { return uint32(priority_reserved_ufs); } + bool init(Solver&); + void reset(); + bool propagateFixpoint(Solver& s, PostPropagator* ctx); + bool isModel(Solver& s); + bool valid(Solver& s); + bool simplify(Solver& s, bool); + void destroy(Solver* s, bool detach); +private: + DefaultUnfoundedCheck(const DefaultUnfoundedCheck&); + DefaultUnfoundedCheck& operator=(const DefaultUnfoundedCheck&); + enum UfsType { + ufs_none, + ufs_poly, + ufs_non_poly + }; + enum WatchType { + watch_source_false = 0, + watch_head_false = 1, + watch_head_true = 2, + watch_subgoal_false= 3, + }; + // data for each body + struct BodyData { + BodyData() : watches(0), picked(0) {} + uint32 watches : 31; // how many atoms watch this body as source? + uint32 picked : 1; // flag used in computeReason() + uint32 lower_or_ext; // unsourced preds or index of extended body + }; + struct BodyPtr { + BodyPtr(const BodyNode* n, uint32 i) : node(n), id(i) {} + const BodyNode* node; + uint32 id; + }; + // data for extended bodies + struct ExtData { + ExtData(weight_t bound, uint32 preds) : lower(bound), slack(-bound) { + for (uint32 i = 0; i != flagSize(preds); ++i) { flags[i] = 0; } + } + bool addToWs(uint32 idx, weight_t w) { + const uint32 fIdx = (idx / 32); + const uint32 m = (1u << (idx & 31)); + assert((flags[fIdx] & m) == 0); + flags[fIdx] |= m; + return (lower -= w) <= 0; + } + bool inWs(uint32 idx) const { + const uint32 fIdx = (idx / 32); + const uint32 m = (1u << (idx & 31)); + return (flags[fIdx] & m) != 0; + } + void removeFromWs(uint32 idx, weight_t w) { + if (inWs(idx)) { + lower += w; + flags[(idx / 32)] &= ~(uint32(1) << (idx & 31)); + } + } + static uint32 flagSize(uint32 preds) { return (preds+31)/32; } + weight_t lower; + weight_t slack; +POTASSCO_WARNING_BEGIN_RELAXED + uint32 flags[0]; +POTASSCO_WARNING_END_RELAXED + }; + // data for each atom + struct AtomData { + AtomData() : source(nill_source), todo(0), ufs(0), validS(0) {} + // returns the body that is currently watched as possible source + NodeId watch() const { return source; } + // returns true if atom has currently a source, i.e. a body that can still define it + bool hasSource() const { return validS; } + // mark source as invalid but keep the watch + void markSourceInvalid() { validS = 0; } + // restore validity of source + void resurrectSource() { validS = 1; } + // sets b as source for this atom + void setSource(NodeId b) { + source = b; + validS = 1; + } + static const uint32 nill_source = (uint32(1) << 29)-1; + uint32 source : 29; // id of body currently watched as source + uint32 todo : 1; // in todo-queue? + uint32 ufs : 1; // in ufs-queue? + uint32 validS : 1; // is source valid? + }; + // Watch-structure used to update extended bodies affected by literal assignments + struct ExtWatch { + NodeId bodyId; + uint32 data; + }; + // Minimality checker for disjunctive logic programs. + struct MinimalityCheck { + typedef SolveParams::FwdCheck FwdCheck; + explicit MinimalityCheck(const FwdCheck& fwd); + bool partialCheck(uint32 level); + void schedNext(uint32 level, bool ok); + FwdCheck fwd; + uint32 high; + uint32 low; + uint32 next; + uint32 scc; + }; + // ------------------------------------------------------------------------------------------- + // constraint interface + PropResult propagate(Solver&, Literal, uint32& data) { + uint32 index = data >> 2; + uint32 type = (data & 3u); + if (type != watch_source_false || bodies_[index].watches) { + invalidQ_.push_back(data); + } + return PropResult(true, true); + } + void reason(Solver& s, Literal, LitVec&); + // ------------------------------------------------------------------------------------------- + // initialization + BodyPtr getBody(NodeId bId) const { return BodyPtr(&graph_->getBody(bId), bId); } + void initBody(const BodyPtr& n); + void initExtBody(const BodyPtr& n); + void initSuccessors(const BodyPtr& n, weight_t lower); + void addWatch(Literal, uint32 data, WatchType type); + void addExtWatch(Literal p, const BodyPtr& n, uint32 data); + struct InitExtWatches { + void operator()(Literal p, uint32 idx, bool ext) const { + extra->slack += B->node->pred_weight(idx, ext); + self->addExtWatch(~p, *B, (idx<<1)+uint32(ext)); + if (ext && !self->solver_->isFalse(p)) { + extra->addToWs(idx, B->node->pred_weight(idx, true)); + } + } + DefaultUnfoundedCheck* self; + const BodyPtr* B; + ExtData* extra; + }; + struct RemExtWatches { + void operator()(Literal p, uint32, bool) const { s->removeWatch(~p, self); } + Constraint* self; + Solver* s; + }; + // ------------------------------------------------------------------------------------------- + // propagating source pointers + void propagateSource(); + struct AddSource { // an atom in a body has a new source, check if body is now a valid source + explicit AddSource(DefaultUnfoundedCheck* u) : self(u) {} + // normal body + void operator()(NodeId bId) const { + BodyPtr n(self->getBody(bId)); + if (--self->bodies_[bId].lower_or_ext == 0 && !self->solver_->isFalse(n.node->lit)) { self->forwardSource(n); } + } + // extended body + void operator()(NodeId bId, uint32 idx) const; + DefaultUnfoundedCheck* self; + }; + struct RemoveSource {// an atom in a body has lost its source, check if body is no longer a valid source + explicit RemoveSource(DefaultUnfoundedCheck* u, bool add = false) : self(u), addTodo(add) {} + // normal body + void operator()(NodeId bId) const { + if (++self->bodies_[bId].lower_or_ext == 1 && self->bodies_[bId].watches != 0) { + self->forwardUnsource(self->getBody(bId), addTodo); + } + } + // extended body + void operator()(NodeId bId, uint32 idx) const; + DefaultUnfoundedCheck* self; + bool addTodo; + }; + void setSource(NodeId atom, const BodyPtr& b); + void removeSource(NodeId bodyId); + void forwardSource(const BodyPtr& n); + void forwardUnsource(const BodyPtr& n, bool add); + void updateSource(AtomData& atom, const BodyPtr& n); + // ------------------------------------------------------------------------------------------- + // finding & propagating unfounded sets + void updateAssignment(Solver& s); + bool findSource(NodeId atom); + bool isValidSource(const BodyPtr&); + void addUnsourced(const BodyPtr&); + bool falsifyUfs(UfsType t); + bool assertAtom(Literal a, UfsType t); + void computeReason(UfsType t); + void addIfReason(const BodyPtr&, uint32 uScc); + bool isExternal(const BodyPtr&, weight_t& slack) const; + void addDeltaReason(const BodyPtr& body, uint32 uScc); + void addReasonLit(Literal); + void createLoopFormula(); + struct AddReasonLit { + void operator()(Literal p, NodeId id, bool ext) const { + if (self->solver_->isFalse(p) && slack >= 0) { + slack -= node->pred_weight(id, ext); + self->addReasonLit(p); + } + } + DefaultUnfoundedCheck* self; + const BodyNode* node; + mutable weight_t slack; + }; + UfsType findUfs(Solver& s, bool checkNonHcf); + UfsType findNonHcfUfs(Solver& s); + // ------------------------------------------------------------------------------------------- + bool pushTodo(NodeId at) { return (atoms_[at].todo == 0 && (todo_.push(at), atoms_[at].todo = 1) != 0); } + bool pushUfs(NodeId at) { return (atoms_[at].ufs == 0 && (ufs_.push(at), atoms_[at].ufs = 1) != 0); } + void resetTodo() { while (!todo_.empty()){ atoms_[todo_.pop_ret()].todo = 0; } todo_.clear(); } + void resetUfs() { while (!ufs_.empty()) { atoms_[ufs_.pop_ret()].ufs = 0; } ufs_.clear(); } + // ------------------------------------------------------------------------------------------- + typedef PodVector::type AtomVec; + typedef PodVector::type BodyVec; + typedef PodVector::type ExtVec; + typedef PodVector::type WatchVec; + typedef PodQueue IdQueue; + typedef SingleOwnerPtr MiniPtr; + // ------------------------------------------------------------------------------------------- + Solver* solver_; // my solver + GraphPtr graph_; // PBADG + MiniPtr mini_; // minimality checker (only for DLPs) + AtomVec atoms_; // data for each atom + BodyVec bodies_; // data for each body + IdQueue todo_; // ids of atoms that recently lost their source + IdQueue ufs_; // ids of atoms that are unfounded wrt the current assignment (limited to one scc) + VarVec invalidQ_; // ids of invalid elements to be processed + VarVec sourceQ_; // source-pointer propagation queue + ExtVec extended_; // data for each extended body + WatchVec watches_; // watches for handling choice-, cardinality- and weight rules + VarVec pickedExt_; // extended bodies visited during reason computation + LitVec loopAtoms_; // only used if strategy_ == shared_reason + LitVec activeClause_;// activeClause_[0] is the current unfounded atom + LitVec* reasons_; // only used if strategy_ == only_reason. reasons_[v] reason why v is unfounded + ConstraintInfo info_; // info on active clause + ReasonStrategy strategy_; // what kind of reasons to compute? +}; +} +#endif diff --git a/clasp/clasp/util/hash.h b/clasp/clasp/util/hash.h new file mode 100644 index 000000000..0d4e17da0 --- /dev/null +++ b/clasp/clasp/util/hash.h @@ -0,0 +1,48 @@ +// +// Copyright (c) 2016-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_HASH_H_INCLUDED +#define CLASP_HASH_H_INCLUDED +#include +#include +namespace Clasp { +//! Hasher for strings. +/*! + * \see http://research.microsoft.com/en-us/people/palarson/ + */ +struct StrHash { + std::size_t operator()(const char* str) const { + std::size_t h = 0; + for (const char* s = str; *s; ++s) { + h = h * 101 + static_cast(*s); + } + return h; + } +}; +//! Comparison function for C-strings to be used with hash map/set. +struct StrEq { + bool operator()(const char* lhs, const char* rhs) const { return std::strcmp(lhs, rhs) == 0; } +}; + +} +#endif diff --git a/clasp/clasp/util/indexed_priority_queue.h b/clasp/clasp/util/indexed_priority_queue.h new file mode 100644 index 000000000..0e1c82af8 --- /dev/null +++ b/clasp/clasp/util/indexed_priority_queue.h @@ -0,0 +1,220 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef BK_LIB_INDEXED_PRIORITY_QUEUE_H_INCLUDED +#define BK_LIB_INDEXED_PRIORITY_QUEUE_H_INCLUDED + +#ifdef _MSC_VER +#pragma warning (disable : 4267) +#pragma warning (disable : 4244) +#pragma once +#endif + +#include +#include "pod_vector.h" +namespace bk_lib { namespace detail { + +typedef std::size_t key_type; +const key_type noKey = static_cast(-1); +inline key_type heap_root() { return 0; } +inline key_type heap_left(std::size_t i) { return (i<<1)+1; } +inline key_type heap_right(std::size_t i) { return (i+1)<<1; } +inline key_type heap_parent(std::size_t i) { return (i-1)>>1; } + +} + +// Note: Uses a Max-Heap! +template < + class Cmp // sort-predicate - if Cmp(k1, k2) == true, n1 has higher priority than n2 +> +class indexed_priority_queue { +public: + typedef detail::key_type key_type; + typedef pod_vector index_container_type; + typedef std::size_t size_type; + typedef Cmp compare_type; + + explicit indexed_priority_queue( const compare_type& c = compare_type() ); + indexed_priority_queue(const indexed_priority_queue& other); + + indexed_priority_queue& operator=(const indexed_priority_queue& other) { + indices_ = other.indices_; + heap_ = other.heap_; + compare_ = other.compare_; + return *this; + } + + const compare_type& key_compare() const { + return compare_; + } + + bool empty() const { + return heap_.empty(); + } + void reserve(size_type n) { + indices_.reserve(n); + } + + void push(key_type k) { + assert( !is_in_queue(k) ); + if ((key_type)indices_.size() <= k) { + if (indices_.capacity() <= k) { indices_.reserve(((k+1)*3)>>1); } + indices_.resize(k+1, detail::noKey); + } + indices_[k] = (key_type)heap_.size(); + heap_.push_back(k); + siftup(indices_[k]); + } + + void pop() { + assert(!empty()); + key_type x = heap_[0]; + heap_[0] = heap_.back(); + indices_[heap_[0]] = 0; + indices_[x] = detail::noKey; + heap_.pop_back(); + if (heap_.size() > 1) {siftdown(0);} + } + + void clear() { + heap_.clear(); + indices_.clear(); + } + + template + void swapMem(indexed_priority_queue& o) { + clear(); + o.clear(); + heap_.swap(o.heap_); + indices_.swap(o.indices_); + } + size_type size( ) const { + return heap_.size(); + } + + key_type top() const { + assert(!empty()); + return heap_[0]; + } + + void update(key_type k) { + if (!is_in_queue(k)) { + push(k); + } + else { + siftup(indices_[k]); + siftdown(indices_[k]); + } + } + // call if priority of k has increased + void increase(key_type k) { + assert(is_in_queue(k)); + siftup(indices_[k]); + } + // call if priority of k has decreased + void decrease(key_type k) { + assert(is_in_queue(k)); + siftdown(indices_[k]); + } + + bool is_in_queue(key_type k) const { + assert(valid_key(k)); + return k < (key_type)indices_.size() && indices_[k] != detail::noKey; + } + + void remove(key_type k) { + if (is_in_queue(k)) { + key_type kInHeap = indices_[k]; + heap_[kInHeap] = heap_.back(); + indices_[heap_.back()] = kInHeap; + heap_.pop_back(); + indices_[k] = detail::noKey; + if (heap_.size() > 1 && kInHeap != (key_type)heap_.size()) { + siftup(kInHeap); + siftdown(kInHeap); + } + } + } +private: + template + friend class indexed_priority_queue; + bool valid_key(key_type k) const { + return k != detail::noKey; + } + index_container_type indices_; + index_container_type heap_; + compare_type compare_; + void siftup(key_type n) { + using namespace detail; + key_type x = heap_[n]; + key_type p = heap_parent(n); + while (n != 0 && compare_(x, heap_[p])){ + heap_[n] = heap_[p]; + indices_[heap_[n]] = n; + n = p; + p = heap_parent(n); + } + heap_[n] = x; + indices_[x] = n; + } + + void siftdown(key_type n) { + using namespace detail; + key_type x = heap_[n]; + while (heap_left(n) < (key_type)heap_.size()){ + key_type child = smaller_child(n); + if (!compare_(heap_[child], x)) { + break; + } + heap_[n] = heap_[child]; + indices_[heap_[n]] = n; + n = child; + } + heap_[n] = x; + indices_[x] = n; + } + + key_type smaller_child(size_type n) const { + using namespace detail; + return heap_right(n) < (key_type)heap_.size() && compare_(heap_[heap_right(n)], heap_[heap_left(n)]) + ? heap_right(n) + : heap_left(n); + } +}; + +template +indexed_priority_queue::indexed_priority_queue( const compare_type& c ) + : indices_() + , heap_() + , compare_(c) { +} + +template +indexed_priority_queue::indexed_priority_queue(const indexed_priority_queue& other) + : indices_(other.indices_) + , heap_(other.heap_) + , compare_(other.compare_) { +} + +} +#endif diff --git a/clasp/clasp/util/left_right_sequence.h b/clasp/clasp/util/left_right_sequence.h new file mode 100644 index 000000000..d98430430 --- /dev/null +++ b/clasp/clasp/util/left_right_sequence.h @@ -0,0 +1,363 @@ +// +// Copyright (c) 2010-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef BK_LIB_LEFT_RIGHT_SEQUENCE_INCLUDED +#define BK_LIB_LEFT_RIGHT_SEQUENCE_INCLUDED +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning (disable : 4200) +#endif +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif + +#include "type_manip.h" +#include +#include +#include +namespace bk_lib { namespace detail { + +// base class for left_right_sequence +// see below +template +class left_right_rep { +public: + typedef L left_type; + typedef R right_type; + typedef unsigned int size_type; + typedef L* left_iterator; + typedef const L* const_left_iterator; + typedef std::reverse_iterator right_iterator; + typedef std::reverse_iterator const_right_iterator; + + typedef typename bk_lib::detail::align_of::type left_align_type; + typedef typename bk_lib::detail::align_of::type right_align_type; +///@cond + typedef typename bk_lib::detail::if_then_else< + sizeof(left_type) >= sizeof(right_type), + left_type, + right_type>::type max_type; + typedef typename bk_lib::detail::if_then_else< + sizeof(left_align_type) >= sizeof(right_align_type), + left_align_type, + right_align_type>::type align_type; +///@endcond + left_right_rep() : buf_(0), cap_(0), free_(0), left_(0), right_(0) {} + + bool empty() const { return left_ == 0 && right_ == cap_; } + size_type left_size() const { return left_/sizeof(left_type); } + size_type right_size() const { return (cap_-right_)/sizeof(right_type); } + size_type size() const { return left_size() + right_size(); } + size_type left_capacity() const { return (cap_ / sizeof(left_type)); } + size_type right_capacity()const { return (cap_ / sizeof(right_type)); } + const_left_iterator left_begin() const { return const_left_iterator(reinterpret_cast(begin())); } + const_left_iterator left_end() const { return const_left_iterator(reinterpret_cast(buf_+left_)); } + left_iterator left_begin() { return left_iterator(reinterpret_cast(begin())); } + left_iterator left_end() { return left_iterator(reinterpret_cast(buf_+left_)); } + const_right_iterator right_begin()const { return const_right_iterator(reinterpret_cast(end())); } + const_right_iterator right_end() const { return const_right_iterator(reinterpret_cast(buf_+right_)); } + right_iterator right_begin() { return right_iterator(reinterpret_cast(end())); } + right_iterator right_end() { return right_iterator(reinterpret_cast(buf_+right_)); } + const left_type& left(size_type i)const { return *(left_begin()+i); } + left_type& left(size_type i) { return *(left_begin()+i); } + + void clear(bool releaseMem = false) { + if (releaseMem) { + release(); + buf_ = 0; + cap_ = 0; + free_ = 0; + } + left_ = 0; + right_= cap_; + } + void push_left(const left_type& x) { + if ((left_ + sizeof(left_type)) > right_) { + realloc(); + } + new (left())left_type(x); + left_ += sizeof(left_type); + } + + void push_right(const right_type& x) { + if ( (left_ + sizeof(right_type)) > right_ ) { + realloc(); + } + right_ -= sizeof(right_type); + new (right()) right_type(x); + } + void pop_left() { + assert(left_size() != 0); + left_ -= sizeof(left_type); + } + void pop_right() { + assert(right_size() != 0); + right_ += sizeof(right_type); + } + + void erase_left(left_iterator it) { + if (it != left_end()) { + left_iterator x = it++; + std::memmove(x, it, (left_end()-it)*sizeof(left_type)); + left_ -= sizeof(left_type); + } + } + void erase_left_unordered(left_iterator it) { + if (it != left_end()) { + left_ -= sizeof(left_type); + *it = *reinterpret_cast(left()); + } + } + void erase_right(right_iterator it) { + if (it != right_end()) { + right_type* r = (++it).base(); + right_type* b = reinterpret_cast(right()); + assert(r >= b); + std::memmove(b+1, b, (r-b)*sizeof(right_type)); + right_ += sizeof(right_type); + } + } + void erase_right_unordered(right_iterator it) { + if (it != right_end()) { + *it = *reinterpret_cast(right()); + right_+= sizeof(right_type); + } + } + void shrink_left(left_iterator it) { + left_ = static_cast((it - left_begin())*sizeof(left_type)); + } + void shrink_right(right_iterator it) { + buf_type* x = reinterpret_cast(it.base()); + right_ = static_cast(x - begin()); + } + enum { block_size = ((sizeof(max_type)+(sizeof(align_type)-1)) / sizeof(align_type)) * sizeof(align_type) }; +protected: + left_right_rep(const left_right_rep&) { } + left_right_rep& operator=(const left_right_rep&) { return *this; } + typedef unsigned char buf_type; + buf_type* begin() { return buf_; } + const buf_type* begin()const { return buf_; } + buf_type* end() { return buf_+cap_; } + const buf_type* end() const { return buf_+cap_; } + buf_type* left() { return buf_+left_; } + buf_type* right() { return buf_+right_; } + size_type capacity()const { return cap_ / block_size; } + size_type raw_size()const { return left_ + (cap_-right_); } + void release() { if (free_ != 0) { ::operator delete(buf_); } } + void realloc(); + buf_type* buf_; + size_type cap_ : 31; + size_type free_: 1; + size_type left_; + size_type right_; +}; + +template +void left_right_rep::realloc() { + size_type new_cap = ((capacity()*3)>>1) * block_size; + size_type min_cap = 4 * block_size; + if (new_cap < min_cap) new_cap = min_cap; + buf_type* temp = (buf_type*)::operator new(new_cap*sizeof(buf_type)); + // copy left + std::memcpy(temp, begin(), left_size()*sizeof(L)); + // copy right + size_type r = cap_ - right_; + std::memcpy(temp+(new_cap-r), right(), right_size() * sizeof(R)); + // swap + release(); + buf_ = temp; + cap_ = new_cap; + free_ = 1; + right_ = new_cap - r; +} + +// always store sequence in heap-allocated buffer +template +struct no_inline_buffer : public left_right_rep { + typedef typename left_right_rep::buf_type buf_type; + enum { inline_raw_cap = 0 }; + buf_type* extra() { return 0; } +}; + +// store small sequences directly inside of object +template +struct with_inline_buffer : public left_right_rep { + typedef typename left_right_rep::buf_type buf_type; + typedef typename left_right_rep::align_type align_type; + enum { inline_raw_cap = cap }; + buf_type* extra() { return rep_.mem; } + union X { + align_type align; + buf_type mem[inline_raw_cap]; + } rep_; +}; + +// select proper base class for left_right_sequence based +// on parameter i +template +struct select_base { +private: + typedef unsigned char buf_type; + typedef left_right_rep base_type; + typedef typename base_type::size_type size_type; + typedef typename base_type::align_type align_type; + typedef no_inline_buffer no_extra_type; + typedef with_inline_buffer with_extra_type; + + enum { padding = sizeof(with_extra_type) - (sizeof(no_extra_type)+sizeof(align_type)) }; + enum { size_with_pad = sizeof(no_extra_type) + padding }; + enum { store_extra = (i > size_with_pad) && (i - size_with_pad) >= base_type::block_size }; + enum { inline_raw_cap = store_extra ? ((i - size_with_pad)/base_type::block_size)*base_type::block_size : 0 }; +public: + typedef typename if_then_else< + store_extra!=0, + with_inline_buffer, + no_inline_buffer >::type type; +}; + + +} // bk_lib::detail + +//! Stores two sequences in one contiguous memory block +/*! + * The left sequence grows from left to right, while the + * right sequence grows from right to left. On overlap, the + * memory block is automatically extended. + * + * \param L value type of left sequence + * \param R value type of right sequence + * \param i max size on stack + * \pre L and R can be copied with memcpy (i.e. have trivial copy constructor and trivial destructor) + */ +template +class left_right_sequence : public bk_lib::detail::select_base::type { +public: + typedef typename bk_lib::detail::select_base::type base_type; + typedef typename base_type::left_type left_type; + typedef typename base_type::right_type right_type; + typedef typename base_type::size_type size_type; + typedef typename base_type::align_type align_type; + typedef typename base_type::max_type max_type; + typedef typename base_type::buf_type buf_type; + + typedef typename base_type::left_iterator left_iterator; + typedef typename base_type::const_left_iterator const_left_iterator; + typedef typename base_type::right_iterator right_iterator; + typedef typename base_type::const_right_iterator const_right_iterator; + + left_right_sequence() { + this->buf_ = this->extra(); + this->cap_ = base_type::inline_raw_cap; + this->free_ = 0; + this->left_ = 0; + this->right_= base_type::inline_raw_cap; + } + left_right_sequence(const left_right_sequence& other); + ~left_right_sequence() { this->release(); } + left_right_sequence& operator=(const left_right_sequence&); + + void try_shrink() { + if (this->raw_size() <= base_type::inline_raw_cap && this->buf_ != this->extra()) { + buf_type* e = this->extra(); + size_type c = base_type::inline_raw_cap; + size_type r = c - (this->right_size()*sizeof(right_type)); + std::memcpy(e, this->begin(), this->left_size() * sizeof(left_type)); + std::memcpy(e+r, this->right(), this->right_size()* sizeof(right_type)); + this->release(); + this->buf_ = e; + this->cap_ = c; + this->free_ = 0; + this->right_= r; + } + } + void move(left_right_sequence& other) { + this->clear(true); + if (other.raw_size() <= base_type::inline_raw_cap) { + copy(other); + other.clear(true); + } + else { + this->buf_ = other.buf_; + this->cap_ = other.cap_; + this->free_ = other.free_; + this->left_ = other.left_; + this->right_ = other.right_; + other.buf_ = other.extra(); + other.cap_ = base_type::inline_raw_cap; + other.free_ = 0; + other.left_ = 0; + other.right_= base_type::inline_raw_cap; + } + } +private: + void copy(const left_right_sequence&); +}; + +template +void left_right_sequence::copy(const left_right_sequence& other) { + size_type os = other.raw_size(); + if ( os <= base_type::inline_raw_cap ) { + this->buf_ = this->extra(); + this->cap_ = base_type::inline_raw_cap; + this->free_= 0; + } + else { + os = ((os + (base_type::block_size-1)) / base_type::block_size) * base_type::block_size; + this->buf_ = (buf_type*)::operator new(os*sizeof(buf_type)); + this->cap_ = os; + this->free_= 1; + } + this->left_ = other.left_; + this->right_= this->cap_ - (other.right_size()*sizeof(right_type)); + std::memcpy(this->begin(), other.begin(), other.left_size()*sizeof(left_type)); + std::memcpy(this->right(), const_cast(other).right(), other.right_size()*sizeof(right_type)); +} + +template +left_right_sequence::left_right_sequence(const left_right_sequence& other) : base_type(other) { + copy(other); +} + +template +left_right_sequence& left_right_sequence::operator=(const left_right_sequence& other) { + if (this != &other) { + this->release(); + copy(other); + } + return *this; +} + + +} // namespace bk_lib + + +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic pop +#endif + +#endif + diff --git a/clasp/clasp/util/misc_types.h b/clasp/clasp/util/misc_types.h new file mode 100644 index 000000000..a35f5a90d --- /dev/null +++ b/clasp/clasp/util/misc_types.h @@ -0,0 +1,435 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +#ifndef CLASP_UTIL_MISC_TYPES_H_INCLUDED +#define CLASP_UTIL_MISC_TYPES_H_INCLUDED + +#include +#include // std::pair +#include // std::unary_function, std::binary_function +#include +#include +/*! + * \file + * \brief Some utility types and functions. + */ +namespace Clasp { + +/*! + * \defgroup misc Miscellaneous + * \brief Miscellaneous and Internal Stuff not specific to clasp. + */ +//@{ + +template +inline T bit_mask(unsigned n) { return static_cast(1) << n; } +//! Returns whether bit n is set in x. +template +inline bool test_bit(T x, unsigned n) { return (x & bit_mask(n)) != 0; } +template +inline T clear_bit(T x, unsigned n) { return x & ~bit_mask(n); } +template +inline T set_bit(T x, unsigned n) { return x | bit_mask(n); } +template +inline T toggle_bit(T x, unsigned n) { return x ^ bit_mask(n); } +template +inline T& store_clear_bit(T& x, unsigned n) { return (x &= ~bit_mask(n)); } +template +inline T& store_set_bit(T& x, unsigned n) { return (x |= bit_mask(n)); } +template +inline T& store_toggle_bit(T& x, unsigned n) { return (x ^= bit_mask(n)); } +template +inline T right_most_bit(T x) { return x & (-x); } + +inline uint32 log2(uint32 x) { + uint32 ln = 0; + if (x & 0xFFFF0000u) { x >>= 16; ln |= 16; } + if (x & 0xFF00u ) { x >>= 8; ln |= 8; } + if (x & 0xF0u ) { x >>= 4; ln |= 4; } + if (x & 0xCu ) { x >>= 2; ln |= 2; } + if (x & 0x2u ) {/*x>>=1*/; ln |= 1; } + return ln; +} + +//! Computes n choose k. +inline uint64 choose(unsigned n, unsigned k) { + if (k == 0) return 1; + if (k > n) return 0; + if (2 * k > n) { return choose(n, n-k);} + uint64 res = n; + for (unsigned i = 2 ; i <= k; ++i) { + res *= (n + 1 - i); + res /= i; + } + return res; +} +inline double ratio(uint64 x, uint64 y) { return y ? static_cast(x) / static_cast(y) : 0; } +inline double percent(uint64 x, uint64 y) { return ratio(x, y) * 100.0; } + +//! A very simple but fast Pseudo-random number generator. +/*! + * \note This class is a replacement for the standard rand-function. It is provided + * in order to get reproducible random numbers among different compilers. + */ +class RNG { +public: + explicit RNG(uint32 seed = 1) : seed_(seed) {} + + //! Sets the starting point for random-number generation. + /*! + * The function sets the starting point for generating a series of pseudorandom integers. + * To reinitialize the generator, use 1 as the seed argument. Any other value for seed + * sets the generator to a random starting point. Calling rand() before any call to srand() + * generates the same sequence as calling srand() with seed passed as 1. + */ + void srand(uint32 seed) { seed_ = seed; } + + //! Generates a pseudorandom number + /*! + * The rand function returns a pseudorandom integer in the range 0 to 32767 + * Use the srand function to seed the pseudorandom-number generator before calling rand. + */ + uint32 rand() { + return( ((seed_ = seed_ * 214013L + 2531011L) >> 16) & 0x7fff ); + } + + //! random floating point number in the range [0, 1.0) + double drand() { + return this->rand()/static_cast(0x8000u); + } + + //! random number in the range [0, max) + unsigned irand(unsigned max) { + return static_cast(drand() * max); + } + + uint32 seed() const { return seed_; } + + uint32 operator()(unsigned max) { return irand(max); } + uint32 operator()() { return rand(); } +private: + uint32 seed_; +}; + +//! Updates the given exponential moving average with the given sample. +/*! + * Computes ema = currentEma + ((double(sample) - currentEma)*alpha); + */ +template +inline double exponentialMovingAverage(double currentEma, T sample, double alpha) { + return (static_cast(sample) * alpha) + (currentEma * (1.0 - alpha)); +} +//! Updates the given moving average with the given sample. +template +inline double cumulativeMovingAverage(double currentAvg, T sample, uint64 numSeen) { + return (static_cast(sample) + (currentAvg * numSeen)) / static_cast(numSeen + 1); +} + +//! An unary operator function that calls p->destroy(). +struct DestroyObject { + template void operator()(T* p) const { if (p) p->destroy(); } +}; +//! An unary operator function that calls delete p. +struct DeleteObject { + template void operator()(T* p) const { delete p; } +}; +//! An unary operator function that calls p->release(). +struct ReleaseObject { + template void operator()(T* p) const { if (p) p->release(); } +}; +//! An unary operator function that returns whether its argument is 0. +struct IsNull { + template bool operator()(const T& p) const { return p == 0; } +}; + +//! A predicate that checks whether a std::pair contains a certain value. +template +struct PairContains { + PairContains(const T& p) : p_(p) {} + bool operator()(const std::pair& s) const { + return s.first == p_ || s.second == p_; + } + T p_; +}; + +//! Removes from the container c the first occurrence of a value v for which p(v) returns true. +/*! + * \pre C is a container that provides back() and pop_back() + * \note Removal is implemented by replacing the element to be removed with + * the back()-element followed by a call to pop_back(). + */ +template +void remove_first_if(C& cont, const P& p) { + for (typename C::iterator it = cont.begin(), end = cont.end(); it != end; ++it) { + if (p(*it)) { + *it = cont.back(); + cont.pop_back(); + return; + } + } +} + +//! An unary operator function that simply returns its argument. +template +struct identity : std::unary_function{ + T& operator()(T& x) const { return x; } + const T& operator()(const T& x) const { return x; } +}; + + +//! An unary operator function that returns the first value of a std::pair. +template +struct select1st : std::unary_function { + typename P::first_type& operator()(P& x) const { + return x.first; + } + const typename P::first_type& operator()(const P& x) const { + return x.first; + } +}; + +//! An unary operator function that returns the second value of a std::pair. +template +struct select2nd : std::unary_function { + typename P::second_type& operator()(P& x) const { + return x.second; + } + const typename P::second_type& operator()(const P& x) const { + return x.second; + } +}; + +//! An unary operator function that returns Op1(Op2(x)). +template +struct compose_1 : public std::unary_function< + typename OP2::argument_type, + typename OP1::result_type> { + compose_1(const OP1& op1, const OP2& op2) + : op1_(op1) + , op2_(op2) {} + + typename OP1::result_type operator()(const typename OP2::argument_type& x) const { + return op1_(op2_(x)); + } +protected: + OP1 op1_; + OP2 op2_; +}; + +/*! + * A template helper function used to construct objects of type compose_1, + * where the component types are based on the data types passed as parameters. + */ +template +inline compose_1 compose1(const OP1& op1, const OP2& op2) { + return compose_1(op1, op2); +} + +//! An unary operator function that returns OP1(OP2(x), OP3(x)). +template +struct compose_2_1 : public std::unary_function< + typename OP2::argument_type, + typename OP1::result_type> { + compose_2_1(const OP1& op1, const OP2& op2, const OP3& op3) + : op1_(op1) + , op2_(op2) + , op3_(op3) {} + + typename OP1::result_type operator()(const typename OP2::argument_type& x) const { + return op1_(op2_(x), op3_(x)); + } +protected: + OP1 op1_; + OP2 op2_; + OP3 op3_; +}; + +/*! + * A template helper function used to construct objects of type compose_2_1, + * where the component types are based on the data types passed as parameters. + */ +template +inline compose_2_1 compose2(const OP1& op1, const OP2& op2, const OP3& op3) { + return compose_2_1(op1, op2, op3); +} + + +//! A binary operator function that returns OP1(OP2(x), OP3(y)). +template +struct compose_2_2 : public std::binary_function< + typename OP2::argument_type, + typename OP3::argument_type, + typename OP1::result_type> { + compose_2_2(const OP1& op1 = OP1(), const OP2& op2 = OP2(), const OP3& op3 = OP3()) + : op1_(op1) + , op2_(op2) + , op3_(op3) {} + + typename OP1::result_type operator()(const typename OP2::argument_type& x, const typename OP3::argument_type& y) const { + return op1_(op2_(x), op3_(y)); + } +protected: + OP1 op1_; + OP2 op2_; + OP3 op3_; +}; + +/*! + * A template helper function used to construct objects of type compose_2_2, + * where the component types are based on the data types passed as parameters. + */ +template +inline compose_2_2 compose22(const OP1& op1, const OP2& op2, const OP3& op3) { + return compose_2_2(op1, op2, op3); +} + +//! TODO: replace with std::is_sorted once we switch to C++11 +template +bool isSorted(ForwardIterator first, ForwardIterator last, Compare comp) { + if (first != last) { + for (ForwardIterator n = first; ++n != last; ++first) { + if (comp(*n, *first)) return false; + } + } + return true; +} + +//! Possible ownership operations. +struct Ownership_t { + enum Type { Retain = 0, Acquire = 1 }; +}; +//! A smart pointer that optionally owns its pointee. +template +class SingleOwnerPtr { +public: + SingleOwnerPtr() : ptr_(0) {} + explicit SingleOwnerPtr(T* ptr, Ownership_t::Type t = Ownership_t::Acquire) + : ptr_(uintp(ptr) | uintp(t == Ownership_t::Acquire)) { + } + ~SingleOwnerPtr() { *this = 0; } + bool is_owner() const { return test_bit(ptr_, 0); } + T* get() const { return (T*)clear_bit(ptr_, 0); } + T& operator*() const { return *get(); } + T* operator->() const { return get(); } + SingleOwnerPtr& operator=(T* ptr) { reset(ptr); return *this; } + void swap(SingleOwnerPtr& o) { std::swap(ptr_, o.ptr_); } + T* release() { store_clear_bit(ptr_, 0); return get(); } + T* acquire() { store_set_bit(ptr_, 0); return get(); } + void reset(T* x) { + if (x != get() && is_owner()) { D deleter; deleter(release()); } + ptr_ = set_bit(uintp(x),0); + } +private: + SingleOwnerPtr(const SingleOwnerPtr&); + SingleOwnerPtr& operator=(const SingleOwnerPtr&); + uintp ptr_; +}; +template +class FlaggedPtr { +public: + FlaggedPtr() : ptr_(0) {} + explicit FlaggedPtr(T* ptr, bool sf = false) : ptr_(uintp(ptr)) { if (sf) flag(); } + bool flagged() const { return test_bit(ptr_, 0); } + T* get() const { return (T*)clear_bit(ptr_, 0); } + T& operator*() const { return *get(); } + T* operator->() const { return get(); } + void swap(FlaggedPtr& o) { std::swap(ptr_, o.ptr_); } + void flag() { store_set_bit(ptr_, 0); } + void unflag() { store_clear_bit(ptr_, 0); } +private: + uintp ptr_; +}; +template +inline FlaggedPtr make_flagged(T* ptr, bool setFlag) { return FlaggedPtr(ptr, setFlag); } + +//! A (numerical) range represented by a low and a high value. +template +struct Range { + Range(T x, T y) : lo(x), hi(y) { if (x > y) { hi = x; lo = y; } } + T clamp(T val) const { + if (val < lo) return lo; + if (val > hi) return hi; + return val; + } + T lo; + T hi; +}; +template +inline bool operator==(const Range& lhs, const Range& rhs) { + return lhs.lo == rhs.lo && lhs.hi == rhs.hi; +} +//! An iterator type for iterating over a range of numerical values. +template +struct num_iterator : std::iterator { + explicit num_iterator(const T& val) : val_(val) {} + typedef typename std::iterator::value_type value_type; + typedef typename std::iterator::difference_type difference_type; + bool operator==(const num_iterator& rhs) const { return val_ == rhs.val_; } + bool operator!=(const num_iterator& rhs) const { return val_ != rhs.val_; } + bool operator<=(const num_iterator& rhs) const { return val_ <= rhs.val_; } + bool operator< (const num_iterator& rhs) const { return val_ < rhs.val_; } + bool operator>=(const num_iterator& rhs) const { return val_ >= rhs.val_; } + bool operator> (const num_iterator& rhs) const { return val_ > rhs.val_; } + value_type operator*() const { return val_; } + T const* operator->() const { return &val_; } + num_iterator& operator++() { ++val_; return *this; } + num_iterator operator++(int) { num_iterator t(*this); ++*this; return t; } + num_iterator& operator--() { --val_; return *this; } + num_iterator operator--(int) { num_iterator t(*this); --*this; return t; } + num_iterator& operator+=(difference_type n) { val_ += n; return *this; } + num_iterator& operator-=(difference_type n) { val_ -= n; return *this; } + num_iterator operator+(difference_type n) const { return num_iterator(static_cast(val_ + n)); } + num_iterator operator-(difference_type n) const { return num_iterator(static_cast(val_ - n)); } + value_type operator[](difference_type n)const { return val_ + n; } + friend num_iterator operator+(difference_type n, num_iterator it) { return num_iterator(it.val_ + n); } +private: + T val_; +}; +//@} + +//! Base class for library events. +struct Event { + //! Set of known event sources. + enum Subsystem { subsystem_facade = 0, subsystem_load = 1, subsystem_prepare = 2, subsystem_solve = 3 }; + //! Possible verbosity levels. + enum Verbosity { verbosity_quiet = 0, verbosity_low = 1, verbosity_high = 2, verbosity_max = 3 }; + explicit Event(Subsystem sys, uint32 evId, Verbosity verbosity) : system(sys), verb(verbosity), op(0), id(evId) {} + uint32 system : 2; //!< One of Event::Subsystem - subsystem that produced the event. + uint32 verb : 2; //!< One of Event::Verbosity - the verbosity level of this event. + uint32 op : 8; //!< Operation that triggered the event. + uint32 id : 16;//!< Type id of event. + static uint32 nextId(); +}; +//! CRTP-base class for events of type T that registers an id for type T. +template +struct Event_t : Event { + Event_t(Subsystem sys, Verbosity verb) : Event(sys, id_s, verb) {} + static const uint32 id_s; +}; +template const uint32 Event_t::id_s = Event::nextId(); + +template const ToType* event_cast(const EvType& ev) { return ev.id == ToType::id_s ? static_cast(&ev) : 0; } + +} +#endif diff --git a/clasp/clasp/util/multi_queue.h b/clasp/clasp/util/multi_queue.h new file mode 100644 index 000000000..384c4b803 --- /dev/null +++ b/clasp/clasp/util/multi_queue.h @@ -0,0 +1,259 @@ +// +// Copyright (c) 2010-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_MULIT_QUEUE_H_INCLUDED +#define CLASP_MULIT_QUEUE_H_INCLUDED + +#include + +namespace Clasp { namespace mt { namespace Detail { + +struct RawNode; +typedef Clasp::Atomic_t::type SafeNodePtr; +struct RawNode { + SafeNodePtr next; +}; +// Lock-free stack that is NOT ABA-safe by itself +struct RawStack { + RawStack() { top = static_cast(0); } + RawNode* tryPop() { + RawNode* n = 0, *next = 0; + do { + if ((n = top) == 0) { return 0; } + // NOTE: + // it is the caller's job to guarantee that n is safe + // and n->next is ABA-safe at this point. + next = n->next; + } while (compare_and_swap(top, n, next) != n); + return n; + } + void push(RawNode* n) { + RawNode* assumedTop; + do { + assumedTop = top; + n->next = assumedTop; + } while (compare_and_swap(top, assumedTop, n) != assumedTop); + } + SafeNodePtr top; +}; +struct DefaultDeleter { + template + void operator()(T& obj) const { + (void)obj; + obj.~T(); + } +}; +} + +//! A (base) class for distributing items between n different threads. +/*! + * Logically, the class maintains n queues, one for each + * involved thread. Threads must register themselves by + * calling addThread(). The returned handle has then + * to be used for publishing and consuming items. + */ +template +class MultiQueue { +protected: + typedef Detail::RawNode RawNode; + typedef Clasp::Atomic_t::type SafeInt; + struct Node : Detail::RawNode { + explicit Node(uint32 rc, const T& d) : data(d) { next = 0; refs = rc; } + SafeInt refs; + T data; + }; +public: + typedef Detail::RawNode* ThreadId; + //! creates a new object for at most m threads + explicit MultiQueue(uint32 m, const Deleter& d = Deleter()) : maxQ_(m), deleter_(d) { + head_.next = 0; + tail_ = &head_; + } + uint32 maxThreads() const { return maxQ_; } + void reserve(uint32 c) { + struct NodeHead : RawNode { SafeInt refs; }; + for (uint32 i = 0; i != c; ++i) { + free_.push(new (::operator new(sizeof(Node))) NodeHead()); + } + } + //! destroys the object and all unconsumed items + ~MultiQueue() { + for (Detail::RawNode* x = head_.next; x ; ) { + Node* n = toNode(x); + x = x->next; + deleter_(n->data); + ::operator delete(n); + } + for (Detail::RawNode* x; (x = free_.tryPop()) != 0; ) { + ::operator delete(toNode(x)); + } + } + //! adds a new thread to the object + /*! + * \note Shall be called at most m times + * \return A handle identifying the new thread + */ + ThreadId addThread() { + return &head_; + } + bool hasItems(ThreadId& cId) const { return cId != tail_; } + + //! tries to consume an item + /*! + * \pre cId was initially obtained via a call to addThread() + * \note tryConsume() is thread-safe w.r.t different ThreadIds + */ + bool tryConsume(ThreadId& cId, T& out) { + if (cId != tail_) { + RawNode* n = cId; + cId = cId->next; + assert(cId != 0 && "MultiQueue is corrupted!"); + release(n); + out = toNode(cId)->data; + return true; + } + return false; + } + //! pops an item from the queue associated with the given thread + /*! + * \pre hasItems(cId) == true + */ + void pop(ThreadId& cId) { + assert(hasItems(cId) && "Cannot pop from empty queue!"); + RawNode* n = cId; + cId = cId->next; + release(n); + } +protected: + //! publishes a new item + /*! + * \note the function is *not* thread-safe, i.e. + * it must not be called concurrently + */ + void unsafePublish(const T& in, const ThreadId&) { unsafePublish(in); } + void unsafePublish(const T& in) { publishRelaxed(allocate(maxQ_, in)); } + + //! concurrency-safe version of unsafePublish + void publish(const T& in, const ThreadId&) { + RawNode* newNode = allocate(maxQ_, in); + RawNode* assumedTail, *assumedNext; + do { + assumedTail = tail_; + assumedNext = assumedTail->next; + if (assumedTail != tail_) { + // tail has changed - try again + continue; + } + if (assumedNext != 0) { + // someone has added a new node but has not yet + // moved the tail - assist him and start over + compare_and_swap(tail_, assumedTail, assumedNext); + continue; + } + } while (compare_and_swap(assumedTail->next, static_cast(0), newNode) != 0); + // Now that we managed to link a new node to what we think is the current tail + // we try to update the tail. If the tail is still what we think it is, + // it is moved - otherwise some other thread already did that for us. + compare_and_swap(tail_, assumedTail, newNode); + } + + //! Non-atomically adds n to the global queue + void publishRelaxed(Node* n) { + static_cast(tail_)->next = n; + tail_ = n; + } + uint32 maxQ() const { return maxQ_; } + Node* allocate(uint32 maxR, const T& in) { + // If the queue is used correctly, the raw stack is ABA-safe at this point. + // The ref-counting in the queue makes sure that a node cannot be added back + // to the stack while another thread is still in tryPop() - that thread had + // not yet the chance to decrease the node's ref count. + if (Node* n = toNode(free_.tryPop())) { + n->next = 0; + n->refs = maxR; + new (&n->data)T(in); + return n; + } + return new (::operator new(sizeof(Node))) Node(maxR, in); + } +private: + MultiQueue(const MultiQueue&); + MultiQueue& operator=(const MultiQueue&); + Node*toNode(Detail::RawNode* x) const { return static_cast(x); } + void release(Detail::RawNode* n) { + if (n != &head_ && --toNode(n)->refs == 0) { + head_.next = static_cast(n->next); + deleter_(toNode(n)->data); + free_.push(n); + } + } + RawNode head_; + Detail::SafeNodePtr tail_; + Detail::RawStack free_; + const uint32 maxQ_; + Deleter deleter_; +}; + +//! Unbounded non-intrusive lock-free multi-producer single consumer queue. +/*! + * Based on Dmitriy Vyukov's MPSC queue: + * http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue + */ +class MPSCPtrQueue { +public: + typedef Detail::RawNode RawNode; + struct Node : RawNode { void* data; }; + typedef Clasp::Atomic_t::type SafeNodePtr; + Node* toNode(RawNode* n) const { return static_cast(n); } + MPSCPtrQueue() {} + void init(Node* sent) { + sent->next = 0; + sent->data = 0; + head_ = sent; + tail_ = sent; + } + bool empty() const { return !static_cast(tail_->next); } + void push(Node* n) { + n->next = 0; + Node* p = head_.exchange(n); + p->next = n; + } + Node* pop() { + Node* t = tail_; + Node* n = toNode(t->next); + if (!n) { return 0; } + tail_ = n; + t->data = n->data; + n->data = 0; + return t; + } +private: + MPSCPtrQueue(const MPSCPtrQueue&); + MPSCPtrQueue& operator=(const MPSCPtrQueue&); + SafeNodePtr head_; // producers + char pad_[64 - sizeof(Node*)]; + Node* tail_; // consumer +}; + +} } // end namespace Clasp::mt +#endif diff --git a/clasp/clasp/util/pod_vector.h b/clasp/clasp/util/pod_vector.h new file mode 100644 index 000000000..6e077e715 --- /dev/null +++ b/clasp/clasp/util/pod_vector.h @@ -0,0 +1,567 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef BK_LIB_POD_VECTOR_H_INCLUDED +#define BK_LIB_POD_VECTOR_H_INCLUDED +#include "type_manip.h" +#include +#include +#include +#include +#include +#include + +#if defined(__GNUC__) +#pragma GCC system_header +#endif + +namespace bk_lib { namespace detail { + template + void fill(T* first, T* last, const T& x) { + assert(first <= last); + switch ((last - first) & 7u) + { + case 0: + while (first != last) + { + new(first++) T(x); + case 7: new(first++) T(x); + case 6: new(first++) T(x); + case 5: new(first++) T(x); + case 4: new(first++) T(x); + case 3: new(first++) T(x); + case 2: new(first++) T(x); + case 1: new(first++) T(x); + assert(first <= last); + } + } + } + template + void copy(Iter first, Iter last, std::size_t s, T* out) { + switch (s & 7u) + { + case 0: + while (first != last) + { + new(out++) T(*first++); + case 7: new(out++) T(*first++); + case 6: new(out++) T(*first++); + case 5: new(out++) T(*first++); + case 4: new(out++) T(*first++); + case 3: new(out++) T(*first++); + case 2: new(out++) T(*first++); + case 1: new(out++) T(*first++); + } + } + } + template + struct Fill { + Fill(const T& val) : val_(val) {} + void operator()(T* first, std::size_t n) const { detail::fill(first, first + n, val_); } + const T& val_; + private: Fill& operator=(const Fill&); + }; + template + struct Copy { + Copy(Iter first, Iter last) : first_(first), last_(last) {} + template + void operator()(T* out, std::size_t n) const { detail::copy(first_, last_, n, out); } + Iter first_; + Iter last_; + }; + template + struct Memcpy { + Memcpy(const T* first) : first_(first) {} + void operator()(T* out, std::size_t n) const { + std::memcpy(out, first_, n*sizeof(T)); + } + const T* first_; + }; + typedef char yes_type; + typedef char (&no_type)[2]; + template + struct IterType { + static yes_type isPtr(const volatile void*); + static no_type isPtr(...); + static yes_type isLong(long long); + static no_type isLong(...); + static T& makeT(); + enum { ptr = sizeof(isPtr(makeT())) == sizeof(yes_type) }; + enum { num = sizeof(isLong(makeT())) == sizeof(yes_type) }; + enum { value = ptr ? 1 : num ? 2 : 0 }; + }; + +} // end namespace bk_lib::detail + +//! A std::vector-replacement for POD-Types. +/*! + * \pre T is a POD-Type + * \see http://www.comeaucomputing.com/techtalk/#pod for a description of POD-Types. + * \note Does not call any destructors and uses std::memcpy to copy/move elements + * \note On LP64-machines size and capacity are represented as unsigned integers (instead of e.g. std::size_t) + */ +template > +class pod_vector { +public: + // types: + typedef pod_vector this_type;//not standard + typedef Allocator allocator_type; + typedef typename Allocator::reference reference; + typedef typename Allocator::const_reference const_reference; + typedef typename Allocator::pointer iterator; + typedef typename Allocator::const_pointer const_iterator; + typedef typename Allocator::pointer pointer; + typedef typename Allocator::const_pointer const_pointer; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + typedef T value_type; + typedef typename detail::if_then_else< + sizeof(typename Allocator::size_type)<=sizeof(unsigned int), + typename Allocator::size_type, + unsigned int>::type size_type; + typedef typename detail::if_then_else< + sizeof(typename Allocator::difference_type)<=sizeof(int), + typename Allocator::difference_type, + int>::type difference_type; + // ctors + //! constructs an empty pod_vector. + /*! + * \post size() == capacity() == 0 + */ + pod_vector() : ebo_(0, allocator_type()) { } + + //! constructs an empty pod_vector that uses a copy of a for memory allocations. + /*! + * \post size() == capacity() == 0 + */ + explicit pod_vector(const allocator_type& a) : ebo_(0, a) { } + + //! constructs a pod_vector containing n copies of value. + /*! + * \post size() == n + */ + explicit pod_vector(size_type n, const T& value = T(), const allocator_type& a = allocator_type()) + : ebo_(n, a) { + detail::fill(ebo_.buf, ebo_.buf + n, value); + ebo_.size = n; + } + + //! constructs a pod_vector equal to the range [first, last). + /*! + * \post size() = distance between first and last. + */ + template + pod_vector(Iter first, Iter last, const allocator_type& a = allocator_type(), typename detail::disable_if::num>::type* = 0) + : ebo_(0, a) { + insert_range(end(), first, last, typename std::iterator_traits::iterator_category()); + } + + //! creates a copy of other + /*! + * \post size() == other.size() && capacity() == other.size() + */ + pod_vector(const pod_vector& other) : ebo_(other.size(), other.get_allocator()) { + std::memcpy(ebo_.buf, other.begin(), other.size()*sizeof(T)); + ebo_.size = other.size(); + } + + pod_vector& operator=(const pod_vector& other) { + if (this != &other) { + assign(other.begin(), other.end()); + } + return *this; + } + + //! frees all memory allocated by this pod_vector. + /*! + * \note Won't call any destructors, because PODs don't have those. + */ + ~pod_vector() { } + + /** @name inspectors + * inspector-functions + */ + //@{ + + //! returns the number of elements currently stored in this pod_vector. + size_type size() const { return ebo_.size; } + //! size of the largest possible pod_vector + size_type max_size() const { + typename allocator_type::size_type x = get_allocator().max_size(); + std::size_t y = size_type(-1)/sizeof(T); + return static_cast(std::min(std::size_t(x), y)); + } + //! returns the total number of elements this pod_vector can hold without requiring reallocation. + size_type capacity() const { return ebo_.cap; } + //! returns size() == 0 + bool empty() const { return ebo_.size == 0; } + + const_iterator begin() const { return ebo_.buf; } + const_iterator end() const { return ebo_.buf+ebo_.size;} + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + + iterator begin() { return ebo_.buf; } + iterator end() { return ebo_.buf+ebo_.size; } + reverse_iterator rbegin() { return reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + + //! returns a copy of the allocator used by this pod_vector + allocator_type get_allocator() const { return ebo_; } + + //@} + /** @name elemacc + * element access + */ + //@{ + + //! returns a reference to the element at position n + /*! + * \pre n < size() + */ + reference operator[](size_type n) { + assert(n < size()); + return ebo_.buf[n]; + } + + //! returns a reference-to-const to the element at position n + /*! + * \pre n < size() + */ + const_reference operator[](size_type n) const { + assert(n < size()); + return ebo_.buf[n]; + } + + //! same as operator[] but throws std::out_of_range if pre-condition is not met. + const_reference at(size_type n) const { + if (n < size()) return ebo_.buf[n]; + throw std::out_of_range("pod_vector::at"); + } + //! same as operator[] but throws std::out_of_range if pre-condition is not met. + reference at(size_type n) { + if (n < size()) return ebo_.buf[n]; + throw std::out_of_range("pod_vector::at"); + } + + //! equivalent to *begin() + reference front() { assert(!empty()); return *ebo_.buf; } + //! equivalent to *begin() + const_reference front() const { assert(!empty()); return *ebo_.buf; } + + //! equivalent to *--end() + reference back() { assert(!empty()); return ebo_.buf[ebo_.size-1]; } + + //! equivalent to *--end() + const_reference back() const { assert(!empty()); return ebo_.buf[ebo_.size-1]; } + + //@} + /** @name mutators + * mutator functions + */ + //@{ + + //! erases all elements in the range [begin(), end) + /*! + * \post size() == 0 + */ + void clear() { ebo_.size = 0; } + + void assign(size_type n, const T& val) { + clear(); + insert(end(), n, val); + } + + template + void assign(Iter first, Iter last) { + clear(); + insert(end(), first, last); + } + + //! erases the element pointed to by pos. + /*! + * \pre pos != end() && !empty() + * \return an iterator pointing to the element following pos (before that element was erased) + * of end() if no such element exists. + * + * \note invalidates all iterators and references referring to elements after pos. + */ + iterator erase(iterator pos) { + assert(!empty() && pos != end()); + erase(pos, pos + 1); + return pos; + } + + //! erases the elements in the range [first, last) + /*! + * \pre [first, last) must be a valid range. + */ + iterator erase(iterator first, iterator last) { + if (end() - last > 0) { + std::memmove(first, last, (end() - last) * sizeof(T)); + } + ebo_.size -= static_cast(last - first); + return first; + } + + //! adjusts the size of this pod_vector to ns. + /*! + * resize is equivalent to: + * if ns > size insert(end(), ns - size(), val) + * if ns < size erase(begin() + ns, end()) + * + * \post size() == ns + */ + void resize(size_type ns, const T& val = T()) { + if (ns > size()) { + ns <= capacity() ? detail::fill(end(), end()+(ns-size()), val) : append_realloc(ns-size(), val); + } + ebo_.size = ns; + } + + //! reallocates storage if necessary but never changes the size() of this pod_vector. + /*! + * \note if n is <= capacity() reserve is a noop. Otherwise a reallocation takes place + * and capacity() >= n after reserve returned. + * \note reallocation invalidates all references, pointers and iterators referring to + * elements in this pod_vectror. + * + * \note when reallocation occurs elements are copied from the old storage using memcpy. + */ + void reserve(size_type n) { + if (n > capacity()) { + T* temp = ebo_.allocate(n); + std::memcpy(temp, ebo_.buf, size()*sizeof(T)); + ebo_.release(); + ebo_.buf = temp; + ebo_.cap = n; + } + } + + void swap(pod_vector& other) { + std::swap(ebo_.buf, other.ebo_.buf); + std::swap(ebo_.size, other.ebo_.size); + std::swap(ebo_.cap, other.ebo_.cap); + } + + //! equivalent to insert(end(), x); + void push_back(const T& x) { + if (size() < capacity()) { + new ((ebo_.buf+ebo_.size++)) T(x); + } + else { + append_realloc(1, x); + } + } + + //! equivalent to erase(--end()); + /*! + * \pre !empty() + */ + void pop_back() { + assert(!empty()); + --ebo_.size; + } + + //! inserts a copy of val before pos. + /*! + * \pre pos is a valid iterator. + * \return an iterator pointing to the copy of val that was inserted. + * \note if size() + 1 > capacity() reallocation occurs. Otherwise iterators and + * references referring to elements before pos remain valid. + * + */ + iterator insert(iterator pos, const T& val) { + return insert(pos, (size_type)1, val); + } + + //! inserts n copies of val before pos. + /*! + * \pre pos is a valid iterator. + */ + iterator insert(iterator pos, size_type n, const T& val) { + size_type off = static_cast(pos-begin()); + insert_impl(pos, n, detail::Fill(val)); + return ebo_.buf + off; + } + + //! inserts copies of elements in the range [first, last) before pos. + /*! + * \pre first and last are not iterators into this pod_vector. + * \pre pos is a valid iterator. + * \note if first and last are pointers, memcpy is used to insert the elements + * in the range [first, last) into this container. + * + */ + template + void insert(iterator pos, Iter first, Iter last, typename detail::disable_if::num>::type* = 0) { + insert_range(pos, first, last, typename std::iterator_traits::iterator_category()); + } + + + /** @name nonstd + * Non-standard interface + */ + //@{ + + //! adjusts the size of this pod_vector to ns. + /*! + * In contrast to pod_vector::resize this function does not + * initializes new elements in case ns > size(). + * This reflects the behaviour of built-in arrays of pod-types. + * \note + * Any access to an unitialized element is illegal unless it is accessed + * in order to assign a new value. + */ + void resize_no_init(size_type ns) { + reserve(ns); + ebo_.size = ns; + } + //@} +private: + size_type grow_size(size_type n) { + size_type new_cap = size() + n; + assert(new_cap > size() && "pod_vector: max size exceeded!"); + assert(new_cap > capacity()); + if (new_cap < 4) new_cap = 1 << (new_cap+1); + size_type x = (capacity()*3)>>1; + if (new_cap < x) new_cap = x; + return new_cap; + } + void append_realloc(size_type n, const T& x) { + size_type new_cap = grow_size(n); + pointer temp = ebo_.allocate(new_cap); + std::memcpy(temp, ebo_.buf, size()*sizeof(T)); + detail::fill(temp+size(), temp+size()+n, x); + ebo_.release(); + ebo_.buf = temp; + ebo_.cap = new_cap; + ebo_.size+= n; + } + void move_right(iterator pos, size_type n) { + assert( (pos || n == 0) && (ebo_.eos() - pos) >= (int)n); + std::memmove(pos + n, pos, (end() - pos) * sizeof(T)); + } + template + void insert_range(iterator pos, It first, It last, std::random_access_iterator_tag, + typename detail::disable_if::value == 0 && detail::same_type::value == 0>::type* = 0) { + assert( (first < begin() || first >= end()) && "pod_vec::insert(): Precondition violated!"); + typename allocator_type::difference_type diff = std::distance(first, last); + assert(diff == 0 || (static_cast(size()+diff) > size() && "pod_vector: max size exceeded!")); + insert_impl(pos, static_cast(diff), detail::Memcpy(first)); + } + template + void insert_range(iterator pos, It first, It last, std::forward_iterator_tag) { + typename allocator_type::difference_type diff = std::distance(first, last); + assert(diff == 0 || (static_cast(size()+diff) > size() && "pod_vector: max size exceeded!")); + insert_impl(pos, static_cast(diff), detail::Copy(first, last)); + } + template + void insert_range(iterator pos, Iter first, Iter last, std::input_iterator_tag) { + pod_vector temp; + while (first != last) temp.push_back(*first++); + insert(pos, temp.begin(), temp.end()); + } + + // NOTE: template parameter ST should always equal size_type + // and is only needed to workaround an internal compiler error + // in gcc 3.4.3 + template + void insert_impl(iterator pos, ST n, const P& pred) { + assert(n == 0 || (size()+n) > size() ); + if (size()+n <= capacity()) { + move_right(pos, n); + pred(pos, n); + ebo_.size += n; + } + else { + size_type new_cap = grow_size(n); + pointer temp = ebo_.allocate(new_cap); + size_type prefix = static_cast(pos-begin()); + // copy prefix + std::memcpy(temp, begin(), prefix*sizeof(T)); + // insert new stuff + pred(temp+prefix, n); + // copy suffix + std::memcpy(temp+prefix+n, pos, (end()-pos)*sizeof(T)); + ebo_.release(); + ebo_.buf = temp; + ebo_.size+= n; + ebo_.cap = new_cap; + } + } + struct ebo : public Allocator { // empty-base-optimization + typedef typename this_type::size_type size_type; + typedef typename this_type::allocator_type A; + pointer buf; // pointer to array + size_type size; // current size (used elements) + size_type cap; // max size before regrow + ebo(size_type n, const Allocator& a) : Allocator(a), buf(0), size(0), cap(n) { + if (n > 0) { buf = A::allocate(n); } + } + ~ebo() { release(); } + void release() { if (buf) A::deallocate(buf, cap); } + T* eos() const { return buf + cap; } + } ebo_; +}; + +template +inline bool operator==(const pod_vector& lhs, const pod_vector& rhs) { + return lhs.size() == rhs.size() + && std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +template +inline bool operator!=(const pod_vector& lhs, const pod_vector& rhs) { + return ! (lhs == rhs); +} + +template +inline bool operator<(const pod_vector& lhs, const pod_vector& rhs) { + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); +} + +template +inline bool operator>(const pod_vector& lhs, const pod_vector& rhs) { + return rhs < lhs; +} + +template +inline bool operator<=(const pod_vector& lhs, const pod_vector& rhs) { + return !(rhs < lhs); +} + +template +inline bool operator>=(const pod_vector& lhs, const pod_vector& rhs) { + return !(lhs < rhs); +} + +template +inline void swap(pod_vector& lhs, pod_vector& rhs) { + lhs.swap(rhs); +} + +} + +#endif + diff --git a/clasp/clasp/util/timer.h b/clasp/clasp/util/timer.h new file mode 100644 index 000000000..1ed76cf0b --- /dev/null +++ b/clasp/clasp/util/timer.h @@ -0,0 +1,82 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +#ifndef CLASP_TIMER_H_INCLUDED +#define CLASP_TIMER_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif +/*! + * \file + * \brief Defines various types for getting absolute times. + */ +namespace Clasp { + +//! A type for getting the current process time. +struct ProcessTime { + static double getTime(); +}; +//! A type for getting the current thread time. +struct ThreadTime { + static double getTime(); +}; +//! A tpe for getting the current wall-clock time. +struct RealTime { + static double getTime(); +}; + +inline double diffTime(double tEnd, double tStart) { + double diff = tEnd - tStart; + return diff >= 0 ? diff : 0.0; +} + +//! A class for measuring elapsed time. +/*! + * \tparam TimeType must provide a single static function + * TimeType::getTime() returning an absolute time. + */ +template +class Timer { +public: + Timer() : start_(0), split_(0), total_(0) {} + + void start() { start_ = TimeType::getTime(); } + void stop() { split(TimeType::getTime()); } + void reset() { *this = Timer(); } + //! Same as stop(), start(); + void lap() { double t; split(t = TimeType::getTime()); start_ = t; } + //! Returns the elapsed time (in seconds) for last start-stop cycle. + double elapsed() const { return split_; } + //! Returns the total elapsed time for all start-stop cycles. + double total() const { return total_; } +private: + void split(double t) { total_ += (split_ = diffTime(t, start_)); } + double start_; + double split_; + double total_; +}; + +} +#endif diff --git a/clasp/clasp/util/type_manip.h b/clasp/clasp/util/type_manip.h new file mode 100644 index 000000000..dc2863611 --- /dev/null +++ b/clasp/clasp/util/type_manip.h @@ -0,0 +1,141 @@ +// +// Copyright (c) 2010-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef BK_LIB_TYPE_MANIP_H_INCLUDED +#define BK_LIB_TYPE_MANIP_H_INCLUDED +namespace bk_lib { namespace detail { +#if (_MSC_VER >= 1300) +#define ALIGNOF(PARAM) (__alignof(PARAM)) +#elif defined(__GNUC__) +#define ALIGNOF(PARAM) (__alignof__(PARAM)) +#else +template +struct align_helper { char x; T y; }; +#define ALIGNOF(T) (sizeof(align_helper)-sizeof(T)) +#endif + + +// if b then if_type else else_type +template +struct if_then_else; + +template +struct if_then_else { typedef if_type type; }; +template +struct if_then_else { typedef else_type type; }; + +// 1 if T == U, else 0 +template struct same_type { enum { value = 0 }; }; +template struct same_type { enum { value = 1 }; }; + +template struct disable_if { typedef bool type; }; +template <> struct disable_if { }; + +// not in list - marks end of type list +struct nil_type {}; + +// list of types - terminated by nil_type +template +struct type_list { + typedef head head_type; + typedef tail tail_type; +}; + +// generates a type lits with up to 18 elements +template < + typename T1 = nil_type, typename T2 = nil_type, typename T3 = nil_type, + typename T4 = nil_type, typename T5 = nil_type, typename T6 = nil_type, + typename T7 = nil_type, typename T8 = nil_type, typename T9 = nil_type, + typename T10 = nil_type, typename T11 = nil_type, typename T12 = nil_type, + typename T13 = nil_type, typename T14 = nil_type, typename T15 = nil_type, + typename T16 = nil_type, typename T17 = nil_type, typename T18 = nil_type +> +struct generate_type_list { + typedef typename generate_type_list::type tail_type; + typedef type_list type; +}; + +template <> +struct generate_type_list<> { typedef nil_type type; }; + +// maps an integer constant to a type +template +struct int2type { enum { value = i }; }; +typedef int2type<0> false_type; +typedef int2type<1> true_type; + +// declared but not defined +struct unknown_type; + +// finds the element in the type list TList that +// has the same alignment as X or X if no such element exists +template +struct max_align; + +// IF ALIGNOF(X) == ALIGNOF(H) then H +// ELSE max_align +template +struct max_align_aux; + +// Base case: ALIGNOF(X) == ALIGNOF(H) +template +struct max_align_aux { + typedef H type; +}; + +// Recursive case +template +struct max_align_aux { + typedef typename max_align::type type; +}; + +template +struct max_align { + typedef X type; +}; + +template +struct max_align > { +private: + enum { x_align = ALIGNOF(X) }; + enum { h_align = ALIGNOF(H) }; +public: + typedef typename max_align_aux(x_align) == static_cast(h_align), X, H, T>::type type; + enum { value = sizeof(type) }; +}; + +// computes alignment size (::value) and type (::type) of T +template +struct align_of { + typedef generate_type_list::type align_list; + typedef typename max_align::type type; + enum { value = max_align::value }; +}; + +#undef ALIGNOF + +}} +#endif + diff --git a/clasp/clasp/weight_constraint.h b/clasp/clasp/weight_constraint.h new file mode 100644 index 000000000..1cb0f37f7 --- /dev/null +++ b/clasp/clasp/weight_constraint.h @@ -0,0 +1,233 @@ +// +// Copyright (c) 2006-2017 Benjamin Kaufmann +// +// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/ +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// +#ifndef CLASP_SMODELS_CONSTRAINTS_H_INCLUDED +#define CLASP_SMODELS_CONSTRAINTS_H_INCLUDED + +#ifdef _MSC_VER +#pragma once +#endif + +#include + +namespace Clasp { + +//! Primitive representation of weight constraint literals in normal form. +struct WeightLitsRep { + //! Transforms the given literals to the normal form expected by WeightConstraint. + /*! + * The function simplifies lits and bound by removing assigned and + * merging duplicate/complementary literals. Furthermore, negative weights and + * their literals are inverted, bound is updated accordingly, and literals + * are sorted by decreasing weight. + */ + static WeightLitsRep create(Solver& s, WeightLitVec& lits, weight_t bound); + //! Propagates the constraint W == *this. + /*! + * If *this is always satisfied (bound <= 0) or unsatisfied (bound > reach), + * the function forward propagates W. Otherwise, if W is not free, it assigns + * (and removes) literals from *this that must hold. + */ + bool propagate(Solver& s, Literal W); + bool sat() const { return bound <= 0; } + bool unsat() const { return reach < bound; } + bool open() const { return bound > 0 && bound <= reach;} + bool hasWeights() const { return size && lits[0].second > 1; } + WeightLiteral* lits; /*!< Literals sorted by decreasing weight. */ + uint32 size; /*!< Number of literals in lits. */ + weight_t bound; /*!< Rhs of linear constraint. */ + weight_t reach; /*!< Sum of weights of lits. */ +}; + +//! Class implementing smodels-like cardinality- and weight constraints. +/*! + * \ingroup constraint + * This class represents a constraint of type W == w1*x1 ... wn*xn >= B, + * where W and each xi are literals and B and each wi are strictly positive integers. + * + * The class is used to represent smodels-like weight constraint, i.e. + * the body of a basic weight rule. In this case W is the literal associated with the body. + * A cardinality constraint is handled like a weight constraint where all weights are equal to 1. + * + * Given a WeightConstraint with bound \p B and set of literals \p L, + * - let \p sumTrue be the sum of the weights of all literals \p l in \p L that are currently true, + * - \p sumReach be the sum of the weights of all literals \p l in \p L that are currently not false, and + * - \p U be the set of literals \p l in \p L that are currently unassigned + * . + * the class implements the following four inference rules: + * - \b FTB: If \p sumTrue >= \p B: assign \p W to true. + * - \b BFB: If \p W is false: set false all literals \p l in \p U for which \p sumTrue + weight(\p l) >= \p B. + * - \b FFB: If \p sumReach < \p B: assign \p W to false. + * - \b BTB: If \p W is true: set true all literals \p l in \p U for which \p sumReach - weight(\p l) < \p B. + * . + */ +class WeightConstraint : public Constraint { +public: + //! Flags controlling weight constraint creation. + enum CreationFlags { + create_explicit = 1u, //!< Force creation of explicit constraint even if size/bound is small. + create_no_add = 3u, //!< Do not add constraint to solver db. + create_sat = 4u, //!< Force creation even if constraint is always satisfied. + create_no_freeze = 8u, //!< Do not freeze variables in constraint. + create_no_share =16u, //!< Do not allow sharing of literals between threads. + create_eq_bound =32u, //!< Create equality instead of greater-or-equal constraint. + create_only_btb =64u, //!< Only create FFB_BTB constraint. + create_only_bfb =128u,//!< Only create FTB_BFB constraint. + }; + //! Type used to communicate result of create(). + class CPair { + public: + CPair() { con[0] = con[1] = 0; } + bool ok() const { return con[0] != (WeightConstraint*)0x1 && con[1] != (WeightConstraint*)0x1; } + WeightConstraint* first() const { return con[0]; } + WeightConstraint* second()const { return con[1]; } + private: + friend class WeightConstraint; + WeightConstraint* con[2]; + }; + //! Creates a new weight constraint from the given weight literals. + /*! + * If the right hand side of the weight constraint is initially true/false (FTB/FFB), + * W is assigned appropriately but no constraint is created. Otherwise, + * the new weight constraint is added to s unless creationFlags contains create_no_add. + * + * \param s Solver in which the new constraint is to be used. + * \param W The literal that is associated with the constraint. + * \param lits The literals of the weight constraint. + * \param bound The lower bound of the weight constraint. + * \param creationFlags Set of CreationFlags to apply. + * \note Cardinality constraint are represented as weight constraints with all weights equal to 1. + * \note If creationFlags contains create_eq_bound, a constraint W == (lits == bound) is created that + * is represented by up to two weight constraints. + */ + static CPair create(Solver& s, Literal W, WeightLitVec& lits, weight_t bound, uint32 creationFlags = 0); + + //! Low level creation function. + /*! + * \note flag create_eq_bound is ignored by this function, that is, this function always creates + * a single >= constraint. + */ + static CPair create(Solver& s, Literal W, WeightLitsRep& rep, uint32 flags); + // constraint interface + Constraint* cloneAttach(Solver&); + bool simplify(Solver& s, bool = false); + void destroy(Solver*, bool); + PropResult propagate(Solver& s, Literal p, uint32& data); + void reason(Solver&, Literal p, LitVec& lits); + bool minimize(Solver& s, Literal p, CCMinRecursive* r); + void undoLevel(Solver& s); + uint32 estimateComplexity(const Solver& s) const; + /*! + * Logically, we distinguish two constraints: + * - FFB_BTB for handling forward false body and backward true body and + * - FTB_BFB for handling forward true body and backward false body. + * . + * Physically, we store the literals in one array: ~W=1, l0=w0,...,ln-1=wn-1. + */ + enum ActiveConstraint { + FFB_BTB = 0, //!< (\p SumW - \p B)+1 [~W=1, l0=w0,..., ln-1=wn-1] + FTB_BFB = 1, //!< \p B [ W=1,~l0=w0,...,~ln-1=wn-1] + }; + /*! + * Returns the i'th literal of constraint c, i.e. + * - li, iff c == FFB_BTB + * - ~li, iff c == FTB_BFB. + */ + Literal lit(uint32 i, ActiveConstraint c) const { return Literal::fromId( lits_->lit(i).id() ^ c ); } + //! Returns the weight of the i'th literal or 1 if constraint is a cardinality constraint. + weight_t weight(uint32 i) const { return lits_->weight(i); } + //! Returns the number of literals in this constraint (including W). + uint32 size() const { return lits_->size(); } + //! Returns false if constraint is a cardinality constraint. + bool isWeight() const { return lits_->weights(); } + // Returns the index of next literal to look at during backward propagation. + uint32 getBpIndex() const { return !isWeight() ? 1 : undo_[0].data>>1; } +private: + static WeightConstraint* doCreate(Solver& s, Literal W, WeightLitsRep& rep, uint32 flags); + bool integrateRoot(Solver& s); + struct WL { + WL(uint32 s, bool shared, bool w); + bool shareable() const { return rc != 0; } + bool unique() const { return rc == 0 || refCount() == 1; } + bool weights() const { return w != 0; } + uint32 size() const { return sz; } + Literal lit(uint32 i) const { return lits[(i<> 2; } + ActiveConstraint constraint() const { return static_cast((data&2) != 0); } + uint32 data; + }; + // Is literal idx contained as reason lit in the undo stack? + bool litSeen(uint32 idx) const { return (undo_[idx].data & 1) != 0; } + // Mark/unmark literal idx. + void toggleLitSeen(uint32 idx) { undo_[idx].data ^= 1; } + // Add watch for idx'th literal of c to the solver. + void addWatch(Solver& s, uint32 idx, ActiveConstraint c); + // Updates bound_[c] and adds an undo watch to the solver if necessary. + // Then adds the literal at position idx to the reason set (and the undo stack). + void updateConstraint(Solver& s, uint32 level, uint32 idx, ActiveConstraint c); + // Returns the starting index of the undo stack. + uint32 undoStart() const { return isWeight(); } + UndoInfo undoTop() const { assert(up_ != undoStart()); return undo_[up_-1]; } + // Returns the decision level of the last assigned literal + // or 0 if no literal was assigned yet. + uint32 highestUndoLevel(Solver&) const; + void setBpIndex(uint32 n); + WL* lits_; // literals of constraint + uint32 up_ : 27; // undo position; [undoStart(), up_) is the undo stack + uint32 ownsLit_: 1; // owns lits_? + uint32 active_ : 2; // which of the two sub-constraints is currently unit? + uint32 watched_: 2; // which constraint is watched (3 both, 2 ignore, FTB_BFB, FFB_BTB) + weight_t bound_[2]; // FFB_BTB: (sumW-bound)+1 / FTB_BFB: bound +POTASSCO_WARNING_BEGIN_RELAXED + UndoInfo undo_[0]; // undo stack + seen flag for each literal +POTASSCO_WARNING_END_RELAXED +}; +} + +#endif diff --git a/clasp/cmake/ClaspConfig.cmake.in b/clasp/cmake/ClaspConfig.cmake.in new file mode 100644 index 000000000..083c435d1 --- /dev/null +++ b/clasp/cmake/ClaspConfig.cmake.in @@ -0,0 +1,12 @@ +@PACKAGE_INIT@ + +find_package(Potassco 1.0 REQUIRED) + +if(@CLASP_BUILD_WITH_THREADS@) + find_package(Threads REQUIRED) +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/ClaspTargets.cmake") + +check_required_components(Clasp) + diff --git a/clasp/create-archive.sh b/clasp/create-archive.sh new file mode 100755 index 000000000..d2fb063b4 --- /dev/null +++ b/clasp/create-archive.sh @@ -0,0 +1,14 @@ +#! /bin/bash +VERSION=$1 +SOURCE=clasp-$VERSION.tar +git archive --prefix=clasp-$VERSION/ -o $SOURCE origin/dev +echo Running git archive submodules... +p=`pwd` && (echo .; git submodule foreach) | while read entering path; do \ + temp="${path%\'}"; \ + temp="${temp#\'}"; \ + path=$temp; \ + [ "$path" = "" ] && continue; \ + (cd $path && git archive --prefix=clasp-$VERSION/$path/ HEAD > $p/tmp.tar && tar --concatenate --file=$p/$SOURCE $p/tmp.tar && rm $p/tmp.tar); \ +done +gzip $SOURCE + diff --git a/clasp/doc/api/clasp.doxy b/clasp/doc/api/clasp.doxy new file mode 100644 index 000000000..f69f4b711 --- /dev/null +++ b/clasp/doc/api/clasp.doxy @@ -0,0 +1,2363 @@ +# Doxyfile 1.8.8 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = libclasp + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = 3.2 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = potassco-logo.png + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = NO + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = NO + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = NO + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= NO + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = "layout.xml" + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = NO + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file($line): $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = clasp.txt \ + ../../clasp \ + ../../clasp/cli \ + ../../src + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = ../../examples + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# compiled with the --with-libclang option. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = NO + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra stylesheet files is of importance (e.g. the last +# stylesheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = "layout.css" + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /

`: + + cmake -H. -B + cmake --build + +The following options can be used to configure the installation: + + CMAKE_INSTALL_PREFIX : install path prefix + LIB_POTASSCO_INSTALL_LIB: whether or not to install libpotassco + +For example, to install lpconvert and libpotassco under `/home/`: + + cmake -H. -B -DCMAKE_INSTALL_PREFIX=/home/ -DLIB_POTASSCO_INSTALL_LIB=ON + cmake --build --target install + +To use libpotassco in a cmake-based project either: + +- Place the library inside your project, e.g. using [git submodules](http://git-scm.com/docs/git-submodule). +- Call `add_subdirectory()`. + +or, if libpotassco is installed in `CMAKE_PREFIX_PATH`: +- Call `find_package(Potassco . CONFIG)`. + +Finally, call `target_link_libraries(your_target PUBLIC libpotassco)` to link to the potassco library. + +## Documentation +Source code documentation can be generated with [Doxygen][doxygen]. +Either explicitly: + + cd doc/ + doxygen + +or via the `doc_potassco` target when using cmake. + +[aspif]: https://www.cs.uni-potsdam.de/wv/publications/DBLP_conf/iclp/GebserKKOSW16x.pdf "Aspif specification" +[cmake]: https://cmake.org/ +[doxygen]: http://www.doxygen.nl/ diff --git a/clasp/libpotassco/app/CMakeLists.txt b/clasp/libpotassco/app/CMakeLists.txt new file mode 100644 index 000000000..4fef0b26c --- /dev/null +++ b/clasp/libpotassco/app/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(lpconvert lpconvert.cpp) +target_link_libraries(lpconvert libpotassco) +set_target_properties(lpconvert PROPERTIES FOLDER exe) +if (NOT CMAKE_INSTALL_BINDIR) + message(STATUS "BINDIR not set - using bin") + set(CMAKE_INSTALL_BINDIR "bin") +endif() +install(TARGETS lpconvert EXPORT lpconvert DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/clasp/libpotassco/app/lpconvert.cpp b/clasp/libpotassco/app/lpconvert.cpp new file mode 100644 index 000000000..cb6e631cb --- /dev/null +++ b/clasp/libpotassco/app/lpconvert.cpp @@ -0,0 +1,123 @@ +// +// Copyright (c) 2015-2017 Benjamin Kaufmann +// +// This file is part of Potassco. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Potassco::ProgramOptions; + +class LpConvert : public Potassco::Application { +public: + virtual const char* getName() const { return "lpconvert"; } + virtual const char* getVersion() const { return "1.0.0"; } + virtual PosOption getPositional() const { return &positional; } + virtual const char* getUsage() const { + return + "[options] []\n" + "Convert program in or standard input"; + } + virtual void initOptions(OptionContext& root); + virtual void validateOptions(const OptionContext&, const ParsedOptions&, const ParsedValues&) {} + virtual void setup() {} + virtual void run(); + virtual void printVersion() { + Potassco::Application::printVersion(); + printf("libpotassco version %s\n", LIB_POTASSCO_VERSION); + printf("Copyright (C) Benjamin Kaufmann\n"); + printf("License: The MIT License \n"); + fflush(stdout); + } +private: + static bool positional(const std::string&, std::string& optOut) { + optOut = "input"; + return true; + } + static int error(int line, const char* what) { + fprintf(stderr, "*** ERROR: In line %d: %s\n", line, what); + static_cast(Application::getInstance())->exit(EXIT_FAILURE); + return EXIT_FAILURE; + } + std::string input_; + std::string output_; + bool potassco_; + bool filter_; + bool text_; +}; + +void LpConvert::initOptions(OptionContext& root) { + OptionGroup convert("Conversion Options"); + convert.addOptions() + ("input,i,@2", storeTo(input_), "Input file") + ("potassco,p", flag(potassco_ = false), "Enable potassco extensions") + ("filter,f" , flag(filter_ = false), "Hide converted potassco predicates") + ("output,o" , storeTo(output_)->arg(""), "Write output to (default: stdout)") + ("text,t" , flag(text_ = false), "Convert to ground text format") + ; + root.add(convert); +} +void LpConvert::run() { + std::ifstream iFile; + std::ofstream oFile; + if (!input_.empty() && input_ != "-") { + iFile.open(input_.c_str()); + POTASSCO_EXPECT(iFile.is_open(), "Could not open input file!"); + } + if (!output_.empty() && output_ != "-") { + POTASSCO_EXPECT(input_ != output_, "Input and output must be different!"); + oFile.open(output_.c_str()); + POTASSCO_EXPECT(oFile.is_open(), "Could not open output file!"); + } + std::istream& in = iFile.is_open() ? iFile : std::cin; + std::ostream& os = oFile.is_open() ? oFile : std::cout; + Potassco::AspifTextOutput text(os); + POTASSCO_EXPECT(in.peek() == 'a' || std::isdigit(in.peek()), "Unrecognized input format!"); + if (in.peek() == 'a') { + Potassco::SmodelsOutput writer(os, potassco_, 0); + Potassco::SmodelsConvert smodels(writer, potassco_); + Potassco::readAspif(in, !text_ ? static_cast(smodels) : text, &error); + } + else { + Potassco::AspifOutput aspif(os); + Potassco::SmodelsInput::Options opts; + if (potassco_) { + opts.enableClaspExt().convertEdges().convertHeuristic(); + if (filter_) { opts.dropConverted(); } + } + Potassco::readSmodels(in, !text_? static_cast(aspif) : text, &error, opts); + } + iFile.close(); + oFile.close(); +} + +int main(int argc, char** argv) { + LpConvert app; + return app.main(argc, argv); +} diff --git a/clasp/libpotassco/cmake/PotasscoConfig.cmake.in b/clasp/libpotassco/cmake/PotasscoConfig.cmake.in new file mode 100644 index 000000000..70ac090c2 --- /dev/null +++ b/clasp/libpotassco/cmake/PotasscoConfig.cmake.in @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/PotasscoTargets.cmake") + +check_required_components(Potassco) diff --git a/clasp/libpotassco/doc/Doxyfile b/clasp/libpotassco/doc/Doxyfile new file mode 100644 index 000000000..0dbc42324 --- /dev/null +++ b/clasp/libpotassco/doc/Doxyfile @@ -0,0 +1,2406 @@ +# Doxyfile 1.8.11 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "libpotassco" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = potassco-logo.png + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = "." + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = libpotassco + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = layout.xml + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = NO + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = ../potassco + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl, +# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 2 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = "layout.css" + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /