1//
2// Copyright (c) 2013-2017 Benjamin Kaufmann
3//
4// This file is part of Clasp. See http://www.cs.uni-potsdam.de/clasp/
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to
8// deal in the Software without restriction, including without limitation the
9// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10// sell copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22// IN THE SOFTWARE.
23//
24/*!
25 * \file
26 * \brief Supermacros for describing clasp's options.
27 * \code
28 * OPTION(key, "ext", ARG(...), "help", set, get)
29 * \endcode
30 * An option consists of:
31 *  - a key  (valid and unique c identifier in 'snake_case')
32 *  - a key extension ("[!][,<alias>][,@<level>]") as understood by ProgramOptions::OptionInitHelper
33 *  - an arg description (ARG macro) as understood by ProgramOptions::Value
34 *  - a description (string)
35 *  - a set action to be executed when a value (string) for the option is found in a source
36 *  - a get action to be executed when the current value for an option is requested
37 *  .
38 *
39 * \note In the implementation of ClaspCliConfig, each key is mapped to an enumeration constant and
40 * the stringified version of key (i.e. \#key) is used to identify options.
41 * Furthermore, the key is also used for generating command-line option names.
42 * As a convention, compound keys using 'snake_case' to separate words
43 * are mapped to dash-separated command-line option names.
44 * E.g. an \<option_like_this\> is mapped to the command-line option "option-like-this".
45 *
46 * \note ClaspCliConfig assumes a certain option order. In particular, context options shall
47 * precede all solver/search options, which in turn shall precede global asp/solving options.
48 *
49 * \note The following set actions may be used:
50 *  - STORE(obj): converts the string value to the type of obj and stores the result in obj.
51 *  - STORE_U(E, n): converts the string value to type E and stores it as unsigned int in n.
52 *  - STORE_LEQ(n, max): converts the string value to an unsinged int and stores the result in n if it is <= max.
53 *  - STORE_FLAG(n): converts the string value to a bool and stores the result in n as either 0 or 1.
54 *  - STORE_OR_FILL(n): converts the string value to an unsinged int t and sets n to std::min(t, maxValue(n)).
55 *  - FUN(arg): anonymous function of type bool (ArgStream& arg), where arg provides the following interface:
56 *    - arg.ok()     : returns whether arg is still valid
57 *    - arg.empty()  : returns whether arg is empty (i.e. all tokens were extracted)
58 *    - arg.off()    : returns whether arg contains a single token representing a valid off value
59 *    - arg >> x     : extracts and converts the current token and stores it in x. Sets failbit if conversion fails.
60 *    - arg >> opt(x): extracts an optional argument, shorthand for (!arg.empty() ? arg>>obj : arg)
61 *    .
62 *
63 * \note The following get actions may be used:
64 *  - GET_FUN(str)  : anonymous function of type void (OutputStream& str)
65 *  - GET(obj...)   : shorthand for GET_FUN(str) { (str << obj)...; }
66 *  - GET_IF(C, obj): shorthand for GET_FUN(str) { ITE(C, str << obj, str << off); }
67 *  .
68 *
69 * \note The following primitives may be used in the set/get arguments:
70 *  - off                  : singleton object representing a valid off value ("no", "off", "false", "0")
71 *  - ITE(C, x, y)         : if-then-else expression, i.e. behaves like (C ? x : y).
72 *  - SET(x, y)            : shorthand for (x=y) == y.
73 *  - SET_LEQ(x, v, m)     : shorthand for (x <= m && SET(x, v)).
74 *  - SET_GEQ(x, v, m)     : shorthand for (x >= m && SET(x, v)).
75 *  - SET_OR_FILL(x, v)    : behaves like SET(x, min(v, maxValue(x)))
76 *  - SET_OR_ZERO(x,v)     : behaves like ITE(v <= maxValue(x), SET(x, v), SET(x, 0)).
77 *  .
78 */
79#if !defined(OPTION) || defined(SELF) || !defined(CLASP_HAS_THREADS)
80#error Invalid include context
81#endif
82
83#if !defined(GROUP_BEGIN)
84#define GROUP_BEGIN(X)
85#endif
86
87#if !defined(GROUP_END)
88#define GROUP_END(X)
89#endif
90
91//! Options for configuring a SharedContext object stored in a Clasp::ContextParams object.
92#if defined(CLASP_CONTEXT_OPTIONS)
93#define SELF CLASP_CONTEXT_OPTIONS
94GROUP_BEGIN(SELF)
95OPTION(share, "!,@1", ARG_EXT(defaultsTo("auto")->state(Value::value_defaulted), DEFINE_ENUM_MAPPING(ContextParams::ShareMode, \
96       MAP("no"  , ContextParams::share_no)  , MAP("all", ContextParams::share_all),\
97       MAP("auto", ContextParams::share_auto), MAP("problem", ContextParams::share_problem),\
98       MAP("learnt", ContextParams::share_learnt))),\
99       "Configure physical sharing of constraints [%D]\n"\
100       "      %A: {auto|problem|learnt|all}", FUN(arg) {ContextParams::ShareMode x; return arg>>x && SET(SELF.shareMode, (uint32)x);}, GET((ContextParams::ShareMode)SELF.shareMode))
101OPTION(learn_explicit, ",@2" , ARG(flag()), "Do not use Short Implication Graph for learning", STORE_FLAG(SELF.shortMode), GET(SELF.shortMode))
102OPTION(sat_prepro    , "!,@1", ARG(arg("<arg>")->implicit("2")),                     \
103       "Run SatELite-like preprocessing (Implicit: %I)\n"                            \
104       "      %A: <level>[,<limit>...]\n"                                            \
105       "        <level> : Set preprocessing level to <level  {1..3}>\n"              \
106       "          1: Variable elimination with subsumption (VE)\n"                   \
107       "          2: VE with limited blocked clause elimination (BCE)\n"             \
108       "          3: Full BCE followed by VE\n"                                      \
109       "        <limit> : [<key {iter|occ|time|frozen|clause}>=]<n> (0=no limit)\n"  \
110       "          iter  : Set iteration limit to <n>           [0]\n"                \
111       "          occ   : Set variable occurrence limit to <n> [0]\n"                \
112       "          time  : Set time limit to <n> seconds        [0]\n"                \
113       "          frozen: Set frozen variables limit to <n>%%   [0]\n"               \
114       "          size  : Set size limit to <n>*1000 clauses   [4000]", STORE(SELF.satPre), GET(SELF.satPre))
115GROUP_END(SELF)
116#undef CLASP_CONTEXT_OPTIONS
117#undef SELF
118#endif
119
120//! Global options only valid in facade.
121#if defined(CLASP_GLOBAL_OPTIONS)
122#define SELF CLASP_GLOBAL_OPTIONS
123GROUP_BEGIN(SELF)
124OPTION(stats, ",s", ARG(implicit("1")->arg("<n>[,<t>]")), "Enable {1=basic|2=full} statistics (<t> for tester)",\
125    FUN(arg) { uint32 s = 0; uint32 t = 0;\
126      return (arg.off() || (arg >> s >> opt(t) && s > 0))
127        && SET(SELF.stats, s) && ((!SELF.testerConfig() && t == 0) || SET(SELF.addTesterConfig()->stats, t));
128    },\
129    GET_FUN(str) { ITE(!SELF.testerConfig() || !SELF.testerConfig()->stats, str << SELF.stats, str << SELF.stats << SELF.testerConfig()->stats); })
130OPTION(parse_ext, "!", ARG(flag()), "Enable extensions in non-aspif input",\
131    FUN(arg) { bool b = false; return (arg.off() || arg >> b) && (SELF.parse.assign(ParserOptions::parse_full, b), true); }, \
132    GET((SELF.parse.anyOf(ParserOptions::parse_full))))
133OPTION(parse_maxsat, "!", ARG(flag()), "Treat dimacs input as MaxSAT problem", \
134    FUN(arg) { bool b = false; return (arg.off() || arg >> b) && (SELF.parse.assign(ParserOptions::parse_maxsat, b), true); }, \
135    GET(static_cast<bool>(SELF.parse.isEnabled(ParserOptions::parse_maxsat))))
136GROUP_END(SELF)
137#undef CLASP_GLOBAL_OPTIONS
138#undef SELF
139#endif
140
141//! Solver options (see SolverParams).
142#if defined(CLASP_SOLVER_OPTIONS)
143#define SELF CLASP_SOLVER_OPTIONS
144GROUP_BEGIN(SELF)
145OPTION(opt_strategy , ""  , ARG_EXT(arg("<arg>"),\
146       DEFINE_ENUM_MAPPING(OptParams::Type, MAP("bb", OptParams::type_bb), MAP("usc", OptParams::type_usc))\
147       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))\
148       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))\
149       DEFINE_ENUM_MAPPING(OptParams::UscOption, MAP("disjoint", OptParams::usc_disjoint), MAP("succinct", OptParams::usc_succinct), MAP("stratify", OptParams::usc_stratify))),\
150       "Configure optimization strategy\n" \
151       "      %A: {bb|usc}[,<tactics>]\n" \
152       "        bb : Model-guided optimization with <tactics {lin|hier|inc|dec}> [lin]\n" \
153       "          lin : Basic lexicographical descent\n"                                  \
154       "          hier: Hierarchical (highest priority criteria first) descent \n"        \
155       "          inc : Hierarchical descent with exponentially increasing steps\n"       \
156       "          dec : Hierarchical descent with exponentially decreasing steps\n"       \
157       "        usc: Core-guided optimization with <tactics>: <relax>[,<opts>]\n"         \
158       "          <relax>: Relaxation algorithm {oll|one|k|pmres}                [oll]\n" \
159       "            oll    : Use strategy from unclasp\n"                                 \
160       "            one    : Add one cardinality constraint per core\n"                   \
161       "            k[,<n>]: Add cardinality constraints of bounded size ([0]=dynamic)\n" \
162       "            pmres  : Add clauses of size 3\n"                                     \
163       "          <opts> : Tactics <list {disjoint|succinct|stratify}>|<mask {0..7}>\n"   \
164       "            disjoint: Disjoint-core preprocessing                    (1)\n"       \
165       "            succinct: No redundant (symmetry) constraints            (2)\n"       \
166       "            stratify: Stratification heuristic for handling weights  (4)",        \
167       STORE(SELF.opt), GET(SELF.opt))
168OPTION(opt_usc_shrink, "", ARG_EXT(arg("<arg>"), DEFINE_ENUM_MAPPING(OptParams::UscTrim, \
169       MAP("lin", OptParams::usc_trim_lin), MAP("rgs", OptParams::usc_trim_rgs), MAP("min", OptParams::usc_trim_min),\
170       MAP("exp", OptParams::usc_trim_exp), MAP("inv", OptParams::usc_trim_inv), MAP("bin", OptParams::usc_trim_bin))),\
171       "Enable core-shrinking in core-guided optimization\n"        \
172       "      %A: <algo>[,<limit> (0=no limit)]\n"                  \
173       "        <algo> : Use algorithm {lin|inv|bin|rgs|exp|min}\n" \
174       "          lin  : Forward linear search unsat\n"             \
175       "          inv  : Inverse linear search not unsat\n"         \
176       "          bin  : Binary search\n"                           \
177       "          rgs  : Repeated geometric sequence until unsat\n" \
178       "          exp  : Exponential search until unsat\n"          \
179       "          min  : Linear search for subset minimal core\n"   \
180       "        <limit>: Limit solve calls to 2^<n> conflicts [10]",\
181      FUN(arg) {\
182        OptParams::UscTrim t = (OptParams::UscTrim)0; uint32 n = 0; \
183        return (arg.off() || arg >> t >> opt(n=10)) && SET(SELF.opt.trim, uint32(t)) && SET(SELF.opt.tLim, uint32(n)); },\
184      GET_IF(SELF.opt.trim, (OptParams::UscTrim)SELF.opt.trim, SELF.opt.tLim))
185OPTION(opt_heuristic, "", ARG_EXT(arg("<list>"), DEFINE_ENUM_MAPPING(OptParams::Heuristic, \
186       MAP("sign", OptParams::heu_sign), MAP("model", OptParams::heu_model))),\
187       "Use opt. in <list {sign|model}> heuristics",\
188       FUN(arg) { Set<OptParams::Heuristic> h; return (arg.off() || arg >> h) && SET(SELF.opt.heus, h.value());},\
189       GET(Set<OptParams::Heuristic>(SELF.opt.heus)))
190OPTION(restart_on_model, "!", ARG(flag()), "Restart after each model\n", STORE_FLAG(SELF.restartOnModel), GET(SELF.restartOnModel))
191OPTION(lookahead    , "!", ARG_EXT(implicit("atom"), DEFINE_ENUM_MAPPING(VarType, \
192       MAP("atom", Var_t::Atom), MAP("body", Var_t::Body), MAP("hybrid", Var_t::Hybrid))),\
193       "Configure failed-literal detection (fld)\n" \
194       "      %A: <type>[,<limit>] / Implicit: %I\n" \
195       "        <type> : Run fld via {atom|body|hybrid} lookahead\n" \
196       "        <limit>: Disable fld after <limit> applications ([0]=no limit)\n" \
197       "      --lookahead=atom is default if --no-lookback is used\n", FUN(arg) { \
198       VarType type = Var_t::Atom; uint32 limit = (SELF.lookOps = 0u);\
199       return ITE(arg.off(), SET(SELF.lookType, 0u), arg>>type>>opt(limit) && SET(SELF.lookType, (uint32)type)) && SET_OR_ZERO(SELF.lookOps, limit);},\
200       GET_IF(SELF.lookType, (VarType)SELF.lookType, SELF.lookOps))
201OPTION(heuristic, "", ARG_EXT(arg("<heu>"), DEFINE_ENUM_MAPPING(Heuristic_t::Type, \
202       MAP("berkmin", Heuristic_t::Berkmin), MAP("vmtf"  , Heuristic_t::Vmtf), \
203       MAP("vsids"  , Heuristic_t::Vsids)  , MAP("domain", Heuristic_t::Domain), \
204       MAP("unit"   , Heuristic_t::Unit)   , MAP("auto", Heuristic_t::Default), MAP("none"  , Heuristic_t::None))), \
205       "Configure decision heuristic\n"  \
206       "      %A: {Berkmin|Vmtf|Vsids|Domain|Unit|None}[,<n>]\n" \
207       "        Berkmin: Use BerkMin-like heuristic (Check last <n> nogoods [0]=all)\n" \
208       "        Vmtf   : Use Siege-like heuristic (Move <n> literals to the front [8])\n" \
209       "        Vsids  : Use Chaff-like heuristic (Use 1.0/0.<n> as decay factor  [95])\n"\
210       "        Domain : Use domain knowledge in Vsids-like heuristic\n"\
211       "        Unit   : Use Smodels-like heuristic (Default if --no-lookback)\n" \
212       "        None   : Select the first free variable", FUN(arg) { Heuristic_t::Type h = Heuristic_t::Berkmin; uint32 n = 0u; \
213       return arg>>h>>opt(n) && SET(SELF.heuId, (uint32)h) && (Heuristic_t::isLookback(h) || !n) && SET_OR_FILL(SELF.heuristic.param, n);},\
214       GET((Heuristic_t::Type)SELF.heuId, SELF.heuristic.param))
215OPTION(init_moms  , "!,@2", ARG(flag())    , "Initialize heuristic with MOMS-score", STORE_FLAG(SELF.heuristic.moms), GET(SELF.heuristic.moms))
216OPTION(score_res  , ",@2" , ARG_EXT(arg("<score>"), DEFINE_ENUM_MAPPING(HeuParams::Score, \
217       MAP("auto", HeuParams::score_auto), MAP("min", HeuParams::score_min), MAP("set", HeuParams::score_set), MAP("multiset", HeuParams::score_multi_set))),\
218       "Resolution score {auto|min|set|multiset}", STORE_U(HeuParams::Score, SELF.heuristic.score), GET(static_cast<HeuParams::Score>(SELF.heuristic.score)))
219OPTION(score_other, ",@2" , ARG_EXT(arg("<arg>"), DEFINE_ENUM_MAPPING(HeuParams::ScoreOther, \
220       MAP("auto", HeuParams::other_auto), MAP("no", HeuParams::other_no), MAP("loop", HeuParams::other_loop), MAP("all", HeuParams::other_all))),\
221       "Score other learnt nogoods: {auto|no|loop|all}", STORE_U(HeuParams::ScoreOther, SELF.heuristic.other), GET(static_cast<HeuParams::ScoreOther>(SELF.heuristic.other)))
222OPTION(sign_def   , ",@1" , ARG_EXT(arg("<sign>"),\
223       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))),\
224       "Default sign: {asp|pos|neg|rnd}", STORE_U(SolverStrategies::SignHeu, SELF.signDef), GET((SolverStrategies::SignHeu)SELF.signDef))
225OPTION(sign_fix   , "!,@2", ARG(flag())    , "Disable sign heuristics and use default signs only", STORE_FLAG(SELF.signFix), GET(SELF.signFix))
226OPTION(berk_huang , "!,@2", ARG(flag())    , "Enable Huang-scoring in Berkmin", STORE_FLAG(SELF.heuristic.huang), GET(SELF.heuristic.huang))
227OPTION(vsids_acids, "!,@2", ARG(flag())    , "Enable acids-scheme in Vsids/Domain", STORE_FLAG(SELF.heuristic.acids), GET(SELF.heuristic.acids))
228OPTION(vsids_progress, ",@2", NO_ARG, "Enable dynamic decaying scheme in Vsids/Domain\n"\
229       "      %A: <n>[,<i {1..100}>][,<c>]|(0=disable)\n"\
230       "        <n> : Set initial decay factor to 1.0/0.<n>\n"\
231       "        <i> : Set decay update to <i>/100.0      [1]\n"\
232       "        <c> : Decrease decay every <c> conflicts [5000]", \
233       FUN(arg) { uint32 n = 80; uint32 i = 1; uint32 c = 5000; \
234       return ITE(arg.off(), SET(SELF.heuristic.decay.init, 0), (arg >> n >> opt(i) >> opt(c))\
235         && SET(SELF.heuristic.decay.init, n) && SET_LEQ(SELF.heuristic.decay.bump, i, 100) && SET(SELF.heuristic.decay.freq, c));}, \
236       GET_IF(SELF.heuristic.decay.init, SELF.heuristic.decay.init, SELF.heuristic.decay.bump, SELF.heuristic.decay.freq))
237OPTION(nant       , "!,@2", ARG(flag())    , "Prefer negative antecedents of P in heuristic", STORE_FLAG(SELF.heuristic.nant), GET(SELF.heuristic.nant))
238OPTION(dom_mod    , ",@1" , ARG_EXT(arg("<arg>"), DEFINE_ENUM_MAPPING(HeuParams::DomMod, \
239       MAP("level", HeuParams::mod_level), MAP("pos", HeuParams::mod_spos), MAP("true", HeuParams::mod_true),\
240       MAP("neg", HeuParams::mod_sneg), MAP("false", HeuParams::mod_false), MAP("init", HeuParams::mod_init), MAP("factor", HeuParams::mod_factor))\
241       DEFINE_ENUM_MAPPING(HeuParams::DomPref, MAP("all", HeuParams::pref_atom), MAP("scc", HeuParams::pref_scc), MAP("hcc", HeuParams::pref_hcc),\
242       MAP("disj", HeuParams::pref_disj), MAP("opt", HeuParams::pref_min), MAP("show", HeuParams::pref_show))),\
243       "Default modification for domain heuristic\n"\
244       "      %A: (no|<mod>[,<pick>])\n"\
245       "        <mod>  : Modifier {level|pos|true|neg|false|init|factor}\n"\
246       "        <pick> : Apply <mod> to (all | <list {scc|hcc|disj|opt|show}>) atoms", \
247       FUN(arg) { HeuParams::DomMod modK; unsigned modN = 0; Set<HeuParams::DomPref> k; bool ok = true;\
248         if (!arg.off()) { ok = ITE(arg.peek() >= 'A', arg >> modK && SET(modN, uint32(modK)), arg >> modN && modN > 0u && modN < 8u); }\
249         return ok && (arg.off() || arg >> opt(k)) && SET(SELF.heuristic.domMod, modN) && SET(SELF.heuristic.domPref, k.value());},\
250       GET_FUN(str) { Set<HeuParams::DomMod> mod(SELF.heuristic.domMod); Set<HeuParams::DomPref> pick(SELF.heuristic.domPref); \
251         ITE(mod.value() && pick.value(), str << mod << pick, str << mod); })
252OPTION(save_progress, "", ARG(implicit("1")->arg("<n>")), "Use RSat-like progress saving on backjumps > %A", STORE_OR_FILL(SELF.saveProgress), GET(SELF.saveProgress))
253OPTION(init_watches , ",@2", ARG_EXT(arg("<arg>"), DEFINE_ENUM_MAPPING(SolverStrategies::WatchInit, \
254       MAP("rnd", SolverStrategies::watch_rand), MAP("first", SolverStrategies::watch_first), MAP("least", SolverStrategies::watch_least))),\
255       "Watched literal initialization: {rnd|first|least}", STORE_U(SolverStrategies::WatchInit, SELF.initWatches), GET(static_cast<SolverStrategies::WatchInit>(SELF.initWatches)))
256OPTION(update_mode   , ",@2", ARG_EXT(arg("<mode>"), DEFINE_ENUM_MAPPING(SolverStrategies::UpdateMode, \
257       MAP("propagate", SolverStrategies::update_on_propagate), MAP("conflict", SolverStrategies::update_on_conflict))),\
258       "Process messages on {propagate|conflict}", STORE_U(SolverStrategies::UpdateMode, SELF.upMode), GET(static_cast<SolverStrategies::UpdateMode>(SELF.upMode)))
259OPTION(acyc_prop, ",@2", ARG(implicit("1")->arg("{0..1}")), "Use backward inference in acyc propagation", \
260       FUN(arg) { uint32 x; return arg>>x && SET_LEQ(SELF.acycFwd, (1u-x), 1u); }, GET(1u-SELF.acycFwd))
261OPTION(seed          , ""   , ARG(arg("<n>")),"Set random number generator's seed to %A", STORE(SELF.seed), GET(SELF.seed))
262OPTION(no_lookback   , ""   , ARG(flag()), "Disable all lookback strategies\n", STORE_FLAG(SELF.search),GET(static_cast<bool>(SELF.search == SolverStrategies::no_learning)))
263OPTION(forget_on_step, ""   , ARG_EXT(arg("<opts>"), DEFINE_ENUM_MAPPING(SolverParams::Forget, \
264       MAP("varScores", SolverParams::forget_heuristic), MAP("signs", SolverParams::forget_signs), MAP("lemmaScores", SolverParams::forget_activities), MAP("lemmas", SolverParams::forget_learnts))),\
265       "Configure forgetting on (incremental) step\n"\
266       "      %A: <list {varScores|signs|lemmaScores|lemmas}>|<mask {0..15}>\n",\
267       FUN(arg) { Set<SolverParams::Forget> s; return (arg.off() || arg >> s) && SET(SELF.forgetSet, s.value()); },\
268       GET(Set<SolverParams::Forget>(SELF.forgetSet)))
269OPTION(strengthen    , "!"  , ARG_EXT(arg("<X>"),\
270       DEFINE_ENUM_MAPPING(SolverStrategies::CCMinType, MAP("local", SolverStrategies::cc_local), MAP("recursive", SolverStrategies::cc_recursive))\
271       DEFINE_ENUM_MAPPING(SolverStrategies::CCMinAntes, MAP("all", SolverStrategies::all_antes), MAP("short", SolverStrategies::short_antes), MAP("binary", SolverStrategies::binary_antes))), \
272       "Use MiniSAT-like conflict nogood strengthening\n"                      \
273       "      %A: <mode>[,<type>][,<bump {yes|no}>]\n"                         \
274       "        <mode>: Use {local|recursive} self-subsumption check\n"        \
275       "        <type>: Follow {all|short|binary} antecedents [all]\n"         \
276       "        <bump>: Bump activities of antecedents        [yes]", FUN(arg) {\
277       SolverStrategies::CCMinType m = SolverStrategies::cc_local; SolverStrategies::CCMinAntes t = SolverStrategies::no_antes; bool b = true; \
278       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)); }, \
279       GET_IF(SELF.ccMinAntes != SolverStrategies::no_antes, (SolverStrategies::CCMinType)SELF.ccMinRec, (SolverStrategies::CCMinAntes)SELF.ccMinAntes, ITE(SELF.ccMinKeepAct, "no", "yes")))
280OPTION(otfs        , ""   , ARG(implicit("1")->arg("{0..2}")), "Enable {1=partial|2=full} on-the-fly subsumption", STORE_LEQ(SELF.otfs, 2u), GET(SELF.otfs))
281OPTION(update_lbd  , "!,@2" , ARG_EXT(arg("<arg>"), DEFINE_ENUM_MAPPING(SolverStrategies::LbdMode, \
282       MAP("less", SolverStrategies::lbd_updated_less), MAP("glucose", SolverStrategies::lbd_update_glucose), MAP("pseudo", SolverStrategies::lbd_update_pseudo))),\
283       "Configure LBD updates during conflict resolution\n"                 \
284       "      %A: <mode {less|glucose|pseudo}>[,<n {0..127}>]\n"            \
285       "        less   : update to X = new LBD   iff X   < previous LBD\n"  \
286       "        glucose: update to X = new LBD   iff X+1 < previous LBD\n"  \
287       "        pseudo : update to X = new LBD+1 iff X   < previous LBD\n"  \
288       "           <n> : Protect updated nogoods on next reduce if X <= <n>",\
289       FUN(arg) { SolverStrategies::LbdMode n = SolverStrategies::lbd_fixed; uint32 m = 0; \
290         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));},\
291       GET_IF(SELF.updateLbd, (SolverStrategies::LbdMode)SELF.updateLbd, search->reduce.strategy.protect))
292OPTION(update_act  , ",@2", ARG(flag()), "Enable LBD-based activity bumping", STORE_FLAG(SELF.bumpVarAct), GET(SELF.bumpVarAct))
293OPTION(reverse_arcs, ""   , ARG(implicit("1")->arg("{0..3}")), "Enable ManySAT-like inverse-arc learning", STORE_LEQ(SELF.reverseArcs, 3u), GET(SELF.reverseArcs))
294OPTION(contraction , "!,@2", ARG_EXT(arg("<arg>"),\
295       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))),\
296       "Configure handling of long learnt nogoods\n"
297       "      %A: <n>[,<rep>]\n"\
298       "        <n>  : Contract nogoods if size > <n> (0=disable)\n"\
299       "        <rep>: Nogood replacement {no|decisionSeq|allUIP|dynamic} [no]\n", FUN(arg) { uint32 n = 0; SolverStrategies::CCRepMode r = SolverStrategies::cc_no_replace;\
300       return (arg.off() || (arg>>n>>opt(r) && n != 0u)) && SET_OR_FILL(SELF.compress, n) && SET(SELF.ccRepMode, uint32(r));},\
301       GET_IF(SELF.compress, SELF.compress, (SolverStrategies::CCRepMode)SELF.ccRepMode))
302OPTION(loops, "" , ARG_EXT(arg("<type>"), DEFINE_ENUM_MAPPING(DefaultUnfoundedCheck::ReasonStrategy,\
303       MAP("common"  , DefaultUnfoundedCheck::common_reason)  , MAP("shared", DefaultUnfoundedCheck::shared_reason), \
304       MAP("distinct", DefaultUnfoundedCheck::distinct_reason), MAP("no", DefaultUnfoundedCheck::only_reason))),\
305       "Configure learning of loop nogoods\n" \
306       "      %A: {common|distinct|shared|no}\n" \
307       "        common  : Create loop nogoods for atoms in an unfounded set\n" \
308       "        distinct: Create distinct loop nogood for each atom in an unfounded set\n" \
309       "        shared  : Create loop formula for a whole unfounded set\n" \
310       "        no      : Do not learn loop formulas\n", FUN(arg) {DefaultUnfoundedCheck::ReasonStrategy x; return arg>>x && SET(SELF.loopRep, (uint32)x);},\
311       GET(static_cast<DefaultUnfoundedCheck::ReasonStrategy>(SELF.loopRep)))
312GROUP_END(SELF)
313#undef CLASP_SOLVER_OPTIONS
314#undef SELF
315#endif
316
317//! Search-related options (see SolveParams).
318#if defined(CLASP_SEARCH_OPTIONS)
319#define SELF CLASP_SEARCH_OPTIONS
320GROUP_BEGIN(SELF)
321OPTION(partial_check, "", ARG(implicit("50")), "Configure partial stability tests\n" \
322       "      %A: <p>[,<h>] / Implicit: %I\n" \
323       "        <p>: Partial check skip percentage\n"    \
324       "        <h>: Init/update value for high bound ([0]=umax)", FUN(arg) {\
325       uint32 p = 0; uint32 h = 0; \
326       return (arg.off() || (arg>>p>>opt(h) && p)) && SET_LEQ(SELF.fwdCheck.highPct, p, 100u) && SET_OR_ZERO(SELF.fwdCheck.highStep, h);},\
327       GET_IF(SELF.fwdCheck.highPct, SELF.fwdCheck.highPct, SELF.fwdCheck.highStep))
328OPTION(sign_def_disj, ",@2", ARG(arg("<sign>")), "Default sign for atoms in disjunctions", STORE_U(SolverStrategies::SignHeu, SELF.fwdCheck.signDef), GET((SolverStrategies::SignHeu)SELF.fwdCheck.signDef))
329OPTION(rand_freq, "!", ARG(arg("<p>")), "Make random decisions with probability %A", FUN(arg) {\
330       double f = 0.0; \
331       return (arg.off() || arg>>f) && SET_R(SELF.randProb, (float)f, 0.0f, 1.0f);}, GET(SELF.randProb))
332OPTION(rand_prob, "", ARG(arg("<n>[,<m>]")), "Do <n> random searches with [<m>=100] conflicts",\
333       FUN(arg) { uint32 n1 = 0; uint32 n2 = 100;\
334       return (arg.off() || (arg>>n1>>opt(n2) && n1)) && SET_OR_FILL(SELF.randRuns, n1) && SET_OR_FILL(SELF.randConf, n2);},\
335       GET_IF(SELF.randRuns, SELF.randRuns,SELF.randConf))
336#undef SELF
337//! Options for configuring the restart strategy of a solver.
338#define SELF CLASP_SEARCH_RESTART_OPTIONS
339#if defined(NOTIFY_SUBGROUPS)
340GROUP_BEGIN(SELF)
341#endif
342OPTION(restarts, "!,r", ARG(arg("<sched>")), "Configure restart policy\n" \
343       "      %A: <type {D|F|L|x|+}>,<n {1..umax}>[,<args>][,<lim>]\n"                    \
344       "        F,<n>    : Run fixed sequence of <n> conflicts\n"                         \
345       "        L,<n>    : Run Luby et al.'s sequence with unit length <n>\n"             \
346       "        x,<n>,<f>: Run geometric seq. of <n>*(<f>^i) conflicts  (<f> >= 1.0)\n"   \
347       "        +,<n>,<m>: Run arithmetic seq. of <n>+(<m>*i) conflicts (<m {0..umax}>)\n"\
348       "        ...,<lim>: Repeat seq. every <lim>+j restarts           (<type> != F)\n"  \
349       "        D,<n>,<f>: Restart based on moving LBD average over last <n> conflicts\n" \
350       "                   Mavg(<n>,LBD)*<f> > avg(LBD)\n"                                \
351       "                   use conflict level average if <lim> > 0 and avg(LBD) > <lim>\n"\
352       "      no|0       : Disable restarts", FUN(arg) { return ITE(arg.off(), (SELF.disable(),true), \
353       arg>>SELF.sched && SET(SELF.dynRestart, uint32(SELF.sched.type == ScheduleStrategy::User)));}, GET(SELF.sched))
354OPTION(reset_restarts  , ",@2", ARG_EXT(arg("<arg>"), DEFINE_ENUM_MAPPING(RestartParams::SeqUpdate, \
355       MAP("no",RestartParams::seq_continue), MAP("repeat", RestartParams::seq_repeat), MAP("disable", RestartParams::seq_disable))),\
356       "Update restart seq. on model {no|repeat|disable}",\
357       STORE_U(RestartParams::SeqUpdate, SELF.upRestart), GET(static_cast<RestartParams::SeqUpdate>(SELF.upRestart)))
358OPTION(local_restarts  , "!"  , ARG(flag()), "Use Ryvchin et al.'s local restarts", STORE_FLAG(SELF.cntLocal), GET(SELF.cntLocal))
359OPTION(counter_restarts, ""   , ARG(arg("<arg>")), "Use counter implication restarts\n" \
360       "      %A: (<rate>[,<bump>] | {0|no})\n" \
361       "      <rate>: Interval in number of restarts\n" \
362       "      <bump>: Bump factor applied to indegrees", \
363       FUN(arg) { uint32 n = 0; uint32 m = SELF.counterBump; \
364       return (arg.off() || (arg >> n >> opt(m) && n > 0)) && SET_OR_FILL(SELF.counterRestart, n) && SET_OR_FILL(SELF.counterBump, m); },\
365       GET_IF(SELF.counterRestart, SELF.counterRestart, SELF.counterBump))
366OPTION(block_restarts  , ""   , ARG(arg("<arg>")), "Use glucose-style blocking restarts\n" \
367       "      %A: <n>[,<R {1.0..5.0}>][,<c>]\n" \
368       "        <n>: Window size for moving average (0=disable blocking)\n" \
369       "        <R>: Block restart if assignment > average * <R>  [1.4]\n"             \
370       "        <c>: Disable blocking for the first <c> conflicts [10000]\n", FUN(arg) { \
371       uint32 n = 0; double R = 0.0; uint32 x = 0;\
372       return (arg.off() || (arg>>n>>opt(R = 1.4)>>opt(x = 10000) && n && R >= 1.0 && R <= 5.0))\
373         && SET(SELF.blockWindow, n) && SET(SELF.blockScale, (float)R) && SET_OR_FILL(SELF.blockFirst, x);},\
374       GET_IF(SELF.blockWindow, SELF.blockWindow, SELF.blockScale, SELF.blockFirst))
375OPTION(shuffle         , "!"  , ARG(arg("<n1>,<n2>")), "Shuffle problem after <n1>+(<n2>*i) restarts\n", FUN(arg) { uint32 n1 = 0; uint32 n2 = 0;\
376       return (arg.off() || (arg>>n1>>opt(n2) && n1)) && SET_OR_FILL(SELF.shuffle, n1) && SET_OR_FILL(SELF.shuffleNext, n2);},\
377       GET_IF(SELF.shuffle, SELF.shuffle, SELF.shuffleNext))
378#if defined(NOTIFY_SUBGROUPS)
379GROUP_END(SELF)
380#endif
381#undef SELF
382#undef CLASP_SEARCH_RESTART_OPTIONS
383//! Options for configuring the deletion strategy of a solver.
384#define SELF CLASP_SEARCH_REDUCE_OPTIONS
385#if defined(NOTIFY_SUBGROUPS)
386GROUP_BEGIN(SELF)
387#endif
388OPTION(deletion    , "!,d", ARG_EXT(defaultsTo("basic,75,activity")->state(Value::value_defaulted), DEFINE_ENUM_MAPPING(ReduceStrategy::Algorithm,\
389       MAP("basic", ReduceStrategy::reduce_linear), MAP("sort", ReduceStrategy::reduce_stable),\
390       MAP("ipSort", ReduceStrategy::reduce_sort) , MAP("ipHeap", ReduceStrategy::reduce_heap))\
391       DEFINE_ENUM_MAPPING(ReduceStrategy::Score, MAP("activity",ReduceStrategy::score_act), MAP("lbd", ReduceStrategy::score_lbd), MAP("mixed", ReduceStrategy::score_both))),\
392       "Configure deletion algorithm [%D]\n"                                    \
393       "      %A: <algo>[,<n {1..100}>][,<sc>]\n"                               \
394       "        <algo>: Use {basic|sort|ipSort|ipHeap} algorithm\n"             \
395       "        <n>   : Delete at most <n>%% of nogoods on reduction    [75]\n" \
396       "        <sc>  : Use {activity|lbd|mixed} nogood scores    [activity]\n" \
397       "      no      : Disable nogood deletion", FUN(arg){\
398       ReduceStrategy::Algorithm algo = ReduceStrategy::reduce_linear; uint32 n; ReduceStrategy::Score sc;\
399       return ITE(arg.off(), (SELF.disable(), true), arg>>algo>>opt(n = 75)>>opt(sc = ReduceStrategy::score_act)\
400         && SET(SELF.strategy.algo, (uint32)algo) && SET_R(SELF.strategy.fReduce, n, 1, 100) && SET(SELF.strategy.score, (uint32)sc));},\
401       GET_IF(SELF.strategy.fReduce, (ReduceStrategy::Algorithm)SELF.strategy.algo, SELF.strategy.fReduce,(ReduceStrategy::Score)SELF.strategy.score))
402OPTION(del_grow    , "!", NO_ARG, "Configure size-based deletion policy\n" \
403       "      %A: <f>[,<g>][,<sched>] (<f> >= 1.0)\n"          \
404       "        <f>     : Keep at most T = X*(<f>^i) learnt nogoods with X being the\n"\
405       "                  initial limit and i the number of times <sched> fired\n"     \
406       "        <g>     : Stop growth once T > P*<g> (0=no limit)      [3.0]\n"        \
407       "        <sched> : Set grow schedule (<type {F|L|x|+}>) [grow on restart]", FUN(arg){ double f; double g; ScheduleStrategy sc = ScheduleStrategy::def();\
408       return ITE(arg.off(), (SELF.growSched = ScheduleStrategy::none(), SELF.fGrow = 0.0f, true),\
409         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)\
410           && (sc.defaulted() || sc.type != ScheduleStrategy::User) && (SELF.growSched=sc, true));},\
411       GET_FUN(str) { if (SELF.fGrow == 0.0f) str<<off; else { str<<SELF.fGrow<<SELF.fMax; if (!SELF.growSched.disabled()) str<<SELF.growSched;}})
412OPTION(del_cfl     , "!", ARG(arg("<sched>")), "Configure conflict-based deletion policy\n" \
413       "      %A:   <type {F|L|x|+}>,<args>... (see restarts)", FUN(arg){\
414       return ITE(arg.off(), (SELF.cflSched=ScheduleStrategy::none()).disabled(), arg>>SELF.cflSched && SELF.cflSched.type != ScheduleStrategy::User);}, GET(SELF.cflSched))
415OPTION(del_init  , ""  , ARG(defaultsTo("3.0")->state(Value::value_defaulted)), "Configure initial deletion limit\n"\
416       "      %A: <f>[,<n>,<o>] (<f> > 0)\n" \
417       "        <f>    : Set initial limit to P=estimated problem size/<f> [%D]\n" \
418       "        <n>,<o>: Clamp initial limit to the range [<n>,<n>+<o>]" , FUN(arg) { double f; uint32 lo = 10; uint32 hi = UINT32_MAX;\
419       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));},\
420       GET_IF(SELF.fInit, 1.0/SELF.fInit, SELF.initRange.lo, SELF.initRange.hi - SELF.initRange.lo))
421OPTION(del_estimate, "", ARG(arg("0..3")->implicit("1")), "Use estimated problem complexity in limits", STORE_LEQ(SELF.strategy.estimate, 3u), GET(SELF.strategy.estimate))
422OPTION(del_max     , "!", ARG(arg("<n>,<X>")), "Keep at most <n> learnt nogoods taking up to <X> MB", FUN(arg) { uint32 n = UINT32_MAX; uint32 mb = 0; \
423       return (arg.off() || arg>>n>>opt(mb)) && SET_GEQ(SELF.maxRange, n, 1u) && SET(SELF.memMax, mb);}, GET(SELF.maxRange, SELF.memMax))
424OPTION(del_glue    , "", NO_ARG, "Configure glue clause handling\n" \
425       "      %A: <n {0..15}>[,<m {0|1}>]\n"                                    \
426       "        <n>: Do not delete nogoods with LBD <= <n>\n"                    \
427       "        <m>: Count (0) or ignore (1) glue clauses in size limit [0]", FUN(arg) {uint32 lbd; uint32 m = 0; \
428       return arg>>lbd>>opt(m) && SET(SELF.strategy.glue, lbd) && SET(SELF.strategy.noGlue, m);}, GET(SELF.strategy.glue, SELF.strategy.noGlue))
429OPTION(del_on_restart, "", ARG(arg("<n>")), "Delete %A%% of learnt nogoods on each restart", STORE_LEQ(SELF.strategy.fRestart, 100u), GET(SELF.strategy.fRestart))
430#if defined(NOTIFY_SUBGROUPS)
431GROUP_END(SELF)
432#endif
433#undef SELF
434#undef CLASP_SEARCH_REDUCE_OPTIONS
435GROUP_END(CLASP_SEARCH_OPTIONS)
436#undef CLASP_SEARCH_OPTIONS
437#endif
438
439//! ASP-front-end options stored in an Clasp::Asp::LogicProgram::AspOptions object.
440#if defined(CLASP_ASP_OPTIONS)
441#define SELF CLASP_ASP_OPTIONS
442GROUP_BEGIN(SELF)
443OPTION(trans_ext  , "!", ARG_EXT(arg("<mode>"), DEFINE_ENUM_MAPPING(Asp::LogicProgram::ExtendedRuleMode,\
444       MAP("no"    , Asp::LogicProgram::mode_native)          , MAP("all" , Asp::LogicProgram::mode_transform),\
445       MAP("choice", Asp::LogicProgram::mode_transform_choice), MAP("card", Asp::LogicProgram::mode_transform_card),\
446       MAP("weight", Asp::LogicProgram::mode_transform_weight), MAP("scc" , Asp::LogicProgram::mode_transform_scc),\
447       MAP("integ" , Asp::LogicProgram::mode_transform_integ) , MAP("dynamic", Asp::LogicProgram::mode_transform_dynamic))),\
448       "Configure handling of extended rules\n"\
449       "      %A: {all|choice|card|weight|integ|dynamic}\n"\
450       "        all    : Transform all extended rules to basic rules\n"\
451       "        choice : Transform choice rules, but keep cardinality and weight rules\n"\
452       "        card   : Transform cardinality rules, but keep choice and weight rules\n"\
453       "        weight : Transform cardinality and weight rules, but keep choice rules\n"\
454       "        scc    : Transform \"recursive\" cardinality and weight rules\n"\
455       "        integ  : Transform cardinality integrity constraints\n"\
456       "        dynamic: Transform \"simple\" extended rules, but keep more complex ones", STORE(SELF.erMode), GET((Asp::LogicProgram::ExtendedRuleMode)SELF.erMode))
457OPTION(eq, "", ARG(arg("<n>")), "Configure equivalence preprocessing\n" \
458       "      Run for at most %A iterations (-1=run to fixpoint)", STORE_OR_FILL(SELF.iters), GET(SELF.iters))
459OPTION(backprop    ,"!,@1", ARG(flag()), "Use backpropagation in ASP-preprocessing", STORE_FLAG(SELF.backprop), GET(SELF.backprop))
460OPTION(supp_models , ",@1", ARG(flag()), "Compute supported models", STORE_FLAG(SELF.suppMod), GET(SELF.suppMod))
461OPTION(no_ufs_check, ",@1", ARG(flag()), "Disable unfounded set check", STORE_FLAG(SELF.noSCC), GET(SELF.noSCC))
462OPTION(no_gamma    , ",@1", ARG(flag()), "Do not add gamma rules for non-hcf disjunctions", STORE_FLAG(SELF.noGamma), GET(SELF.noGamma))
463OPTION(eq_dfs      , ",@2", ARG(flag()), "Enable df-order in eq-preprocessing", STORE_FLAG(SELF.dfOrder), GET(SELF.dfOrder))
464OPTION(dlp_old_map , ",@3", ARG(flag()), "Enable old mapping for disjunctive LPs", STORE_FLAG(SELF.oldMap), GET(SELF.oldMap))
465GROUP_END(SELF)
466#undef CLASP_ASP_OPTIONS
467#undef SELF
468#endif
469
470//! Options for the solving algorithm (see Clasp::SolveOptions)
471#if defined(CLASP_SOLVE_OPTIONS)
472#define SELF CLASP_SOLVE_OPTIONS
473GROUP_BEGIN(SELF)
474OPTION(solve_limit , ",@1", ARG(arg("<n>[,<m>]")), "Stop search after <n> conflicts or <m> restarts\n", FUN(arg) {\
475       uint32 n = UINT32_MAX; uint32 m = UINT32_MAX;\
476       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);},\
477       GET((uint32)Range<uint64>(0u,UINT32_MAX).clamp(SELF.limit.conflicts),(uint32)Range<uint64>(0u,UINT32_MAX).clamp(SELF.limit.restarts)))
478#if CLASP_HAS_THREADS
479OPTION(parallel_mode, ",t", ARG_EXT(arg("<arg>"), DEFINE_ENUM_MAPPING(SolveOptions::Algorithm::SearchMode,\
480       MAP("compete", SolveOptions::Algorithm::mode_compete), MAP("split", SolveOptions::Algorithm::mode_split))),\
481       "Run parallel search with given number of threads\n" \
482       "      %A: <n {1..64}>[,<mode {compete|split}>]\n"   \
483       "        <n>   : Number of threads to use in search\n"\
484       "        <mode>: Run competition or splitting based search [compete]\n", FUN(arg){\
485       uint32 n; SolveOptions::Algorithm::SearchMode mode;\
486       return arg>>n>>opt(mode = SolveOptions::Algorithm::mode_compete) && SET_R(SELF.algorithm.threads, n, 1u, 64u) && SET(SELF.algorithm.mode, mode);},\
487       GET(SELF.algorithm.threads, (SolveOptions::Algorithm::SearchMode)SELF.algorithm.mode))
488OPTION(global_restarts, ",@1", ARG(arg("<X>")), "Configure global restart policy\n" \
489       "      %A: <n>[,<sched>]\n"                                        \
490       "        <n> : Maximal number of global restarts (0=disable)\n"    \
491       "     <sched>: Restart schedule [x,100,1.5] (<type {F|L|x|+}>)\n", FUN(arg) {\
492       return ITE(arg.off(), (SELF.restarts = SolveOptions::GRestarts(), true), arg>>SELF.restarts.maxR>>opt(SELF.restarts.sched = ScheduleStrategy())\
493         && SELF.restarts.maxR && SELF.restarts.sched.type != ScheduleStrategy::User);},\
494       GET_IF(SELF.restarts.maxR, SELF.restarts.maxR, SELF.restarts.sched))
495OPTION(distribute, "!,@1", ARG_EXT(defaultsTo("conflict,global,4"), \
496       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))\
497       DEFINE_ENUM_MAPPING(SolveOptions::Distribution::Mode, MAP("global", SolveOptions::Distribution::mode_global), MAP("local", SolveOptions::Distribution::mode_local))),\
498       "Configure nogood distribution [%D]\n" \
499       "      %A: <type>[,<mode>][,<lbd {0..127}>][,<size>]\n"            \
500       "        <type> : Distribute {all|short|conflict|loop} nogoods\n"  \
501       "        <mode> : Use {global|local} distribution   [global]\n"    \
502       "        <lbd>  : Distribute only if LBD  <= <lbd>  [4]\n"         \
503       "        <size> : Distribute only if size <= <size> [-1]",         \
504       FUN(arg) { Distributor::Policy::Types type; SolveOptions::Distribution::Mode mode = SolveOptions::Distribution::mode_global; uint32 lbd; uint32 size; \
505         if (arg.off()) { SELF.distribute.policy() = Distributor::Policy(0, 0, 0); return true; }
506         return (arg >> type) && (arg.peek() < 'A' || arg >> opt(mode)) && arg >> opt(lbd = 4) >> opt(size = UINT32_MAX)
507           && SET(SELF.distribute.types, (uint32)type) && SET(SELF.distribute.mode, (uint32)mode) && SET(SELF.distribute.lbd, lbd) && SET_OR_FILL(SELF.distribute.size, size);},\
508       GET_FUN(str) { ITE(!SELF.distribute.types, str << off,\
509         str << (Distributor::Policy::Types)SELF.distribute.types << (SolveOptions::Distribution::Mode)SELF.distribute.mode << SELF.distribute.lbd << SELF.distribute.size); })
510OPTION(integrate, ",@1", ARG_EXT(defaultsTo("gp")->state(Value::value_defaulted),\
511       DEFINE_ENUM_MAPPING(SolveOptions::Integration::Filter, \
512       MAP("all", SolveOptions::Integration::filter_no), MAP("gp", SolveOptions::Integration::filter_gp),\
513       MAP("unsat", SolveOptions::Integration::filter_sat), MAP("active", SolveOptions::Integration::filter_heuristic))\
514       DEFINE_ENUM_MAPPING(SolveOptions::Integration::Topology, \
515       MAP("all" , SolveOptions::Integration::topo_all) , MAP("ring" , SolveOptions::Integration::topo_ring),\
516       MAP("cube", SolveOptions::Integration::topo_cube), MAP("cubex", SolveOptions::Integration::topo_cubex))),\
517       "Configure nogood integration [%D]\n" \
518       "      %A: <pick>[,<n>][,<topo>]\n"                                           \
519       "        <pick>: Add {all|unsat|gp(unsat wrt guiding path)|active} nogoods\n" \
520       "        <n>   : Always keep at least last <n> integrated nogoods   [1024]\n" \
521       "        <topo>: Accept nogoods from {all|ring|cube|cubex} peers    [all]\n", FUN(arg) {\
522       SolveOptions::Integration::Filter pick = SolveOptions::Integration::filter_no; uint32 n; SolveOptions::Integration::Topology topo;
523       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);},\
524       GET((SolveOptions::Integration::Filter)SELF.integrate.filter, SELF.integrate.grace, (SolveOptions::Integration::Topology)SELF.integrate.topo))
525#endif
526OPTION(enum_mode   , ",e", ARG_EXT(defaultsTo("auto")->state(Value::value_defaulted), DEFINE_ENUM_MAPPING(SolveOptions::EnumType,\
527       MAP("bt", SolveOptions::enum_bt), MAP("record", SolveOptions::enum_record), MAP("domRec", SolveOptions::enum_dom_record),\
528       MAP("brave", SolveOptions::enum_brave), MAP("cautious", SolveOptions::enum_cautious), MAP("query", SolveOptions::enum_query),\
529       MAP("auto", SolveOptions::enum_auto), MAP("user", SolveOptions::enum_user))),\
530       "Configure enumeration algorithm [%D]\n" \
531       "      %A: {bt|record|brave|cautious|auto}\n" \
532       "        bt      : Backtrack decision literals from solutions\n" \
533       "        record  : Add nogoods for computed solutions\n" \
534       "        domRec  : Add nogoods over true domain atoms\n" \
535       "        brave   : Compute brave consequences (union of models)\n" \
536       "        cautious: Compute cautious consequences (intersection of models)\n" \
537       "        auto    : Use bt for enumeration and record for optimization", STORE(SELF.enumMode), GET(SELF.enumMode))
538OPTION(project, "!", ARG_EXT(arg("<arg>")->implicit("auto,3"), DEFINE_ENUM_MAPPING(ProjectMode_t::Mode,\
539       MAP("auto", ProjectMode_t::Implicit), MAP("show", ProjectMode_t::Output), MAP("project", ProjectMode_t::Explicit))),\
540       "Enable projective solution enumeration\n"                            \
541       "      %A: {show|project|auto}[,<bt {0..3}>] (Implicit: %I)\n"        \
542       "        Project to atoms in show or project directives, or\n"        \
543       "        select depending on the existence of a project directive\n"  \
544       "      <bt> : Additional options for enumeration algorithm 'bt'\n"    \
545       "        Use activity heuristic (1) when selecting backtracking literal\n" \
546       "        and/or progress saving (2) when retracting solution literals", \
547       FUN(arg) { ProjectMode m = ProjectMode_t::Implicit; uint32 p = 0; \
548         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);},\
549       GET_IF(SELF.project, SELF.proMode, SELF.project >> 1))
550OPTION(models, ",n", ARG(arg("<n>")), "Compute at most %A models (0 for all)\n", STORE(SELF.numModels), GET(SELF.numModels))
551OPTION(opt_mode   , "", ARG_EXT(arg("<arg>"), DEFINE_ENUM_MAPPING(MinimizeMode_t::Mode,\
552       MAP("opt" , MinimizeMode_t::optimize), MAP("enum"  , MinimizeMode_t::enumerate),\
553       MAP("optN", MinimizeMode_t::enumOpt) , MAP("ignore", MinimizeMode_t::ignore))),\
554       "Configure optimization algorithm\n"\
555       "      %A: <mode {opt|enum|optN|ignore}>[,<bound>...]\n"       \
556       "        opt   : Find optimal model\n"                         \
557       "        enum  : Find models with costs <= <bound>\n"          \
558       "        optN  : Find optimum, then enumerate optimal models\n"\
559       "        ignore: Ignore optimize statements\n"                 \
560       "      <bound> : Set initial bound for objective function(s)", \
561       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); }, \
562       GET_FUN(str) { str << SELF.optMode; if (!SELF.optBound.empty()) str << SELF.optBound; })
563GROUP_END(SELF)
564#undef CLASP_SOLVE_OPTIONS
565#undef SELF
566#endif
567
568#undef GROUP_BEGIN
569#undef GROUP_END
570#undef OPTION
571#undef NOTIFY_SUBGROUPS
572#undef ARG
573#undef ARG_EXT
574#undef NO_ARG
575