1 //
2 // Copyright (c) 2006-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 #include <clasp/cli/clasp_options.h>
25 #include <clasp/minimize_constraint.h>
26 #include <clasp/lookahead.h>
27 #include <clasp/unfounded_check.h>
28 #include <potassco/program_opts/program_options.h>
29 #include <potassco/program_opts/typed_value.h>
30 #include <cstring>
31 #include <cfloat>
32 #include <fstream>
33 #include <cctype>
34 #ifdef _MSC_VER
35 #pragma warning (disable : 4996)
36 #endif
37 #if (defined(__cplusplus) && __cplusplus > 199711) || (defined(_MSC_VER) && _MSC_VER >= 1900)
38 #define CLASP_NOEXCEPT_X(X) noexcept(X)
39 #else
40 #define CLASP_NOEXCEPT_X(X)
41 #endif
42 /////////////////////////////////////////////////////////////////////////////////////////
43 // Helper MACROS
44 /////////////////////////////////////////////////////////////////////////////////////////
45 #define SET(x, v) ( ((x)=(v)) == (v) )
46 #define SET_LEQ(x, v, m) ( ((v)<=(m)) && SET((x), (v)) )
47 #define SET_GEQ(x, v, m) ( ((v)>=(m)) && SET((x), (v)) )
48 #define SET_OR_FILL(x, v) ( SET((x),(v)) || ((x) = 0, (x) = ~(x),true) )
49 #define SET_OR_ZERO(x,v) ( SET((x),(v)) || SET((x),uint32(0)) )
50 #define SET_R(x, v, lo, hi) ( ((lo)<=(v)) && ((v)<=(hi)) && SET((x), (v)) )
51 /////////////////////////////////////////////////////////////////////////////////////////
52 // Primitive types/functions for string <-> T conversions
53 /////////////////////////////////////////////////////////////////////////////////////////
54 namespace bk_lib {
55 template <class T>
xconvert(const char * x,pod_vector<T> & out,const char ** errPos,int sep)56 static int xconvert(const char* x, pod_vector<T>& out, const char** errPos, int sep) {
57 if (sep == 0) { sep = Potassco::def_sep; }
58 typename pod_vector<T>::size_type sz = out.size();
59 std::size_t t = Potassco::convert_seq<T>(x, out.max_size() - sz, std::back_inserter(out), static_cast<char>(sep), errPos);
60 if (!t) { out.resize(sz); }
61 return static_cast<int>(t);
62 }
63 template <class T>
xconvert(std::string & out,const pod_vector<T> & x)64 static std::string& xconvert(std::string& out, const pod_vector<T>& x) { return Potassco::xconvert(out, x.begin(), x.end()); }
65 }
66 namespace Potassco {
67 struct KV { const char* key; int value; };
68 static const struct OffType {} off = {};
xconvert(const char * x,const OffType &,const char ** errPos,int)69 static int xconvert(const char* x, const OffType&, const char** errPos, int) {
70 bool temp = true;
71 const char* n = x;
72 if (xconvert(n, temp, &n, 0) && !temp) { x = n; }
73 if (errPos) { *errPos = x; }
74 return int(temp == false);
75 }
xconvert(std::string & out,const OffType &)76 static std::string& xconvert(std::string& out, const OffType&) { return out.append("no"); }
77
findValue(const Span<KV> & map,const char * key,const char ** next,const char * sep=",")78 static const KV* findValue(const Span<KV>& map, const char* key, const char** next, const char* sep = ",") {
79 std::size_t kLen = std::strcspn(key, sep);
80 const KV* needle = 0;
81 for (const KV* it = Potassco::begin(map), *end = Potassco::end(map); it != end; ++it) {
82 if (strncasecmp(key, it->key, kLen) == 0 && !it->key[kLen]) {
83 needle = it;
84 key += kLen;
85 break;
86 }
87 }
88 if (next) { *next = key; }
89 return needle;
90 }
findKey(const Span<KV> & map,int x)91 static const char* findKey(const Span<KV>& map, int x) {
92 for (const KV* it = Potassco::begin(map), *end = Potassco::end(map); it != end; ++it) {
93 if (it->value == x) { return it->key; }
94 }
95 return "";
96 }
97
98 struct ArgString {
ArgStringPotassco::ArgString99 ArgString(const char* x) : in(x), skip(0) { }
CLASP_NOEXCEPT_XPotassco::ArgString100 ~ArgString() CLASP_NOEXCEPT_X(false) { POTASSCO_ASSERT(!ok() || !*in || off(), "Unused argument!"); }
okPotassco::ArgString101 bool ok() const { return in != 0; }
offPotassco::ArgString102 bool off() const { return ok() && stringTo(in, Potassco::off); }
emptyPotassco::ArgString103 bool empty() const { return ok() && !*in; }
operator void*Potassco::ArgString104 operator void*()const { return (void*)in; }
peekPotassco::ArgString105 char peek() const { return ok() ? in[(*in == skip)] : 0; }
106 template <class T>
getPotassco::ArgString107 ArgString& get(T& x) {
108 if (ok()) {
109 const char* next = in + (*in == skip);
110 in = xconvert(next, x, &next, 0) != 0 ? next : 0;
111 skip = ',';
112 }
113 return *this;
114 }
115 const char* in;
116 char skip;
117 template <class T>
118 struct Opt_t {
Opt_tPotassco::ArgString::Opt_t119 Opt_t(T& x) : obj(&x) {}
120 T* obj;
121 };
122 };
123 template <class T>
opt(T & x)124 inline ArgString::Opt_t<T> opt(T& x) { return ArgString::Opt_t<T>(x); }
125 template <class T>
operator >>(ArgString & arg,T & x)126 inline ArgString& operator>>(ArgString& arg, T& x) { return arg.get(x); }
127 template <class T>
operator >>(ArgString & arg,const ArgString::Opt_t<T> & x)128 inline ArgString& operator>>(ArgString& arg, const ArgString::Opt_t<T>& x) { return !arg.empty() ? arg.get(*x.obj) : arg; }
129
130 struct StringRef {
StringRefPotassco::StringRef131 StringRef(std::string& o) : out(&o) {}
132 std::string* out;
133 };
134 template <class T>
operator <<(StringRef & str,const T & val)135 inline StringRef& operator<<(StringRef& str, const T& val) {
136 if (!str.out->empty()) { str.out->append(1, ','); }
137 xconvert(*str.out, val);
138 return str;
139 }
140
141 template <class ET>
142 struct Set {
SetPotassco::Set143 Set(unsigned v = 0) : val(v) {}
valuePotassco::Set144 unsigned value() const { return val; }
145 unsigned val;
146 };
147 // <list_of_keys>|<bitmask>
148 template <class ET>
xconvert(const char * x,Set<ET> & out,const char ** errPos,int e)149 static int xconvert(const char* x, Set<ET>& out, const char** errPos, int e) {
150 const char* it = x, *next;
151 unsigned n, len = 0u; ET v;
152 if (xconvert(it, n, &next, e)) {
153 const Potassco::Span<Potassco::KV> em = enumMap(static_cast<ET*>(0));
154 for (size_t i = 0, sum = 0; i != em.size && !len; ++i) {
155 sum |= static_cast<unsigned>(em[i].value);
156 len += (n == static_cast<unsigned>(em[i].value)) || (n && (n & sum) == n);
157 }
158 }
159 else {
160 for (next = "", n = 0u; xconvert(it + int(*next == ','), v, &next, e); it = next, ++len) {
161 n |= static_cast<unsigned>(v);
162 }
163 }
164 if (len) { out.val = n; it = next; }
165 if (errPos) { *errPos = it; }
166 return static_cast<int>(len);
167 }
168 template <class ET>
xconvert(std::string & out,const Set<ET> & x)169 static std::string& xconvert(std::string& out, const Set<ET>& x) {
170 const Potassco::Span<Potassco::KV> em = enumMap(static_cast<ET*>(0));
171 if (unsigned bitset = x.val) {
172 for (const KV* k = Potassco::begin(em), *kEnd = Potassco::end(em); k != kEnd; ++k) {
173 unsigned ev = static_cast<unsigned>(k->value);
174 if (bitset == ev || (ev && (ev & bitset) == ev)) {
175 out.append(k->key);
176 if ((bitset -= ev) == 0u) { return out; }
177 out.append(1, ',');
178 }
179 }
180 return xconvert(out, static_cast<ET>(bitset));
181 }
182 return xconvert(out, off);
183 }
184
185 }
186 namespace Clasp {
187 /////////////////////////////////////////////////////////////////////////////////////////
188 // Enum mappings for clasp types
189 /////////////////////////////////////////////////////////////////////////////////////////
190 #define MAP(x, y) {static_cast<const char*>(x), static_cast<int>(y)}
191 #define DEFINE_ENUM_MAPPING(X, ...) \
192 static Potassco::Span<Potassco::KV> enumMap(const X*) {\
193 static const Potassco::KV map[] = {__VA_ARGS__};\
194 return Potassco::toSpan(map, sizeof(map)/sizeof(map[0]));\
195 }\
196 static int xconvert(const char* x, X& out, const char** errPos, int) {\
197 if (const Potassco::KV* it = Potassco::findValue(enumMap(&out), x, errPos)) { \
198 out = static_cast<X>(it->value); \
199 return 1;\
200 }\
201 return 0;\
202 }\
203 static std::string& xconvert(std::string& out, X x) { \
204 return out.append(Potassco::findKey(enumMap(&x), static_cast<int>(x))); \
205 }
206 #define OPTION(k, e, a, d, ...) a
207 #define CLASP_CONTEXT_OPTIONS
208 #define CLASP_GLOBAL_OPTIONS
209 #define CLASP_SOLVE_OPTIONS
210 #define CLASP_ASP_OPTIONS
211 #define CLASP_SOLVER_OPTIONS
212 #define CLASP_SEARCH_OPTIONS
213 #define ARG_EXT(a, X) X
214 #define ARG(a)
215 #define NO_ARG
216 #include <clasp/cli/clasp_cli_options.inl>
217 namespace Cli {
218 DEFINE_ENUM_MAPPING(ConfigKey, \
219 MAP("auto", config_default), MAP("frumpy", config_frumpy), MAP("jumpy", config_jumpy), \
220 MAP("tweety", config_tweety) , MAP("handy" , config_handy) ,\
221 MAP("crafty", config_crafty) , MAP("trendy", config_trendy), MAP("many", config_many))
222 }
223 #undef MAP
224 #undef DEFINE_ENUM_MAPPING
225 /////////////////////////////////////////////////////////////////////////////////////////
226 // Conversion functions for complex clasp types
227 /////////////////////////////////////////////////////////////////////////////////////////
xconvert(const char * x,ScheduleStrategy & out,const char ** errPos,int e)228 static int xconvert(const char* x, ScheduleStrategy& out, const char** errPos, int e) {
229 using Potassco::xconvert;
230 if (!x) { return 0; }
231 const char* next = std::strchr(x, ',');
232 uint32 base = 0;
233 int tok = 1;
234 if (errPos) { *errPos = x; }
235 if (!next || !xconvert(next+1, base, &next, e) || base == 0) { return 0; }
236 if (strncasecmp(x, "f,", 2) == 0 || strncasecmp(x, "fixed,", 6) == 0){
237 out = ScheduleStrategy::fixed(base);
238 }
239 else if (strncasecmp(x, "l,", 2) == 0 || strncasecmp(x, "luby,", 5) == 0) {
240 uint32 lim = 0;
241 if (*next == ',' && !xconvert(next+1, lim, &next, e)) { return 0; }
242 out = ScheduleStrategy::luby(base, lim);
243 }
244 else if (strncmp(x, "+,", 2) == 0 || strncasecmp(x, "add,", 4) == 0) {
245 std::pair<uint32, uint32> arg(0, 0);
246 if (*next != ',' || !xconvert(next+1, arg, &next, e)) { return 0; }
247 out = ScheduleStrategy::arith(base, arg.first, arg.second);
248 }
249 else if (strncmp(x, "x,", 2) == 0 || strncmp(x, "*,", 2) == 0 || strncasecmp(x, "d,", 2) == 0) {
250 std::pair<double, uint32> arg(0, 0);
251 if (*next != ',' || !xconvert(next+1, arg, &next, e)) { return 0; }
252 if (strncasecmp(x, "d", 1) == 0 && arg.first > 0.0) { out = ScheduleStrategy(ScheduleStrategy::User, base, arg.first, arg.second); }
253 else if (strncasecmp(x, "d", 1) != 0 && arg.first >= 1.0){ out = ScheduleStrategy::geom(base, arg.first, arg.second); }
254 else { return 0; }
255 }
256 else { next = x; tok = 0; }
257 if (errPos) { *errPos = next; }
258 return tok;
259 }
xconvert(std::string & out,const ScheduleStrategy & sched)260 static std::string& xconvert(std::string& out, const ScheduleStrategy& sched) {
261 using Potassco::xconvert;
262 if (sched.defaulted()){ return xconvert(out, ScheduleStrategy()); }
263 if (sched.disabled()) { return out.append("0"); }
264 std::size_t t = out.size();
265 out.append("f,");
266 xconvert(out, sched.base);
267 switch (sched.type) {
268 case ScheduleStrategy::Geometric:
269 out[t] = 'x';
270 return xconvert(out.append(1, ','), std::make_pair((double)sched.grow, sched.len));
271 case ScheduleStrategy::Arithmetic:
272 if (sched.grow) { out[t] = '+'; return xconvert(out.append(1, ','), std::make_pair((uint32)sched.grow, sched.len)); }
273 else { out[t] = 'f'; return out; }
274 case ScheduleStrategy::Luby:
275 out[t] = 'l';
276 if (sched.len) { return xconvert(out.append(1, ','), sched.len); }
277 else { return out; }
278 case ScheduleStrategy::User:
279 out[t] = 'd';
280 return xconvert(out.append(1, ','), std::make_pair((double)sched.grow, sched.len));
281 default: POTASSCO_ASSERT(false, "xconvert(ScheduleStrategy): unknown type");
282 }
283 }
setOptLegacy(OptParams & out,uint32 n)284 static bool setOptLegacy(OptParams& out, uint32 n) {
285 if (n >= 20) { return false; }
286 out.type = n < 4 ? OptParams::type_bb : OptParams::type_usc;
287 out.algo = n < 4 ? n : 0;
288 out.opts = 0u;
289 out.kLim = 0u;
290 if (n > 3 && (n -= 4u) != 0u) {
291 if (test_bit(n, 0)) { out.opts |= OptParams::usc_disjoint; }
292 if (test_bit(n, 1)) { out.opts |= OptParams::usc_succinct; }
293 if (test_bit(n, 2)) { out.algo = OptParams::usc_pmr; }
294 if (test_bit(n, 3)) { out.opts |= OptParams::usc_stratify; }
295 }
296 return true;
297 }
xconvert(const char * x,OptParams & out,const char ** err,int e)298 static int xconvert(const char* x, OptParams& out, const char** err, int e) {
299 using Potassco::xconvert;
300 using Potassco::toString;
301 const char* it = x, *next;
302 unsigned n = 0u, len = 0u;
303 OptParams::Type t;
304 // clasp-3.0: <n>
305 if (xconvert(it, n, &next, e) && setOptLegacy(out, n)) {
306 it = next; ++len;
307 }
308 else if (xconvert(it, t, &next, e)) {
309 setOptLegacy(out, uint32(t)*4);
310 it = next; ++len;
311 if (*it == ',') {
312 union { OptParams::BBAlgo bb; OptParams::UscAlgo usc; } algo;
313 if (xconvert(it+1, n, &next, e) && setOptLegacy(out, n + (uint32(t)*4))) { // clasp-3.2: (bb|usc),<n>
314 it = next; ++len;
315 }
316 else if (t == OptParams::type_bb && xconvert(it+1, algo.bb, &next, e)) {
317 out.algo = algo.bb;
318 it = next; ++len;
319 }
320 else if (t == OptParams::type_usc) {
321 Potassco::Set<OptParams::UscOption> opts(0);
322 if (xconvert(it+1, algo.usc, &next, e)) {
323 out.algo = algo.usc;
324 it = next; ++len;
325 if (*it == ',' && algo.usc == OptParams::usc_k && xconvert(it + 1, n, &next)) {
326 SET_OR_FILL(out.kLim, n);
327 it = next; ++len;
328 }
329 }
330 if (*it == ',' && (xconvert(it + 1, Potassco::off, &next, e) || xconvert(it + 1, opts, &next, e))) {
331 out.opts = opts.value();
332 it = next; ++len;
333 }
334 }
335 }
336 }
337 if (err) { *err = it; }
338 return static_cast<int>(len);
339 }
xconvert(std::string & out,const OptParams & p)340 static std::string& xconvert(std::string& out, const OptParams& p) {
341 xconvert(out, static_cast<OptParams::Type>(p.type));
342 if (p.type == OptParams::type_usc) {
343 xconvert(out.append(1, ','), static_cast<OptParams::UscAlgo>(p.algo));
344 if (p.algo == OptParams::usc_k ) { Potassco::xconvert(out.append(1, ','), p.kLim); }
345 if (p.opts) { Potassco::xconvert(out.append(1, ','), Potassco::Set<OptParams::UscOption>(p.opts)); }
346 }
347 else {
348 xconvert(out.append(1, ','), static_cast<OptParams::BBAlgo>(p.algo));
349 }
350 return out;
351 }
xconvert(const char * x,SatPreParams & out,const char ** err,int e)352 static int xconvert(const char* x, SatPreParams& out, const char** err, int e) {
353 using Potassco::xconvert;
354 if (xconvert(x, Potassco::off, err, e)) {
355 out = SatPreParams();
356 return 1;
357 }
358 uint32 n, len = 0;
359 const char *next;
360 if (xconvert(x, n, &next, e) && SET(out.type, n)) {
361 x = next; ++len;
362 Potassco::KV kv[5] = {{"iter", 0}, {"occ", 0}, {"time", 0}, {"frozen", 0}, {"size", 4000}};
363 Potassco::Span<Potassco::KV> map = Potassco::toSpan(kv, 5);
364 for (uint32 id = 0; *x == ','; ++id, ++len) {
365 const char* it = x;
366 if (const Potassco::KV* val = Potassco::findValue(map, it + 1, &next, ":=")) {
367 id = static_cast<uint32>(val - kv);
368 it = next;
369 }
370 if (id > 4 || !xconvert(it + 1, kv[id].value, &next, e)) { break; }
371 x = next;
372 }
373 SET_OR_ZERO(out.limIters, unsigned(kv[0].value));
374 SET_OR_ZERO(out.limOcc, unsigned(kv[1].value));
375 SET_OR_ZERO(out.limTime, unsigned(kv[2].value));
376 SET_OR_ZERO(out.limFrozen, unsigned(kv[3].value));
377 SET_OR_ZERO(out.limClause, unsigned(kv[4].value));
378 }
379 if (err) { *err = x; }
380 return static_cast<int>(len);
381 }
xconvert(std::string & out,const SatPreParams & p)382 static std::string& xconvert(std::string& out, const SatPreParams& p) {
383 if (p.type) {
384 Potassco::xconvert(out, p.type);
385 if (uint32 n = p.limIters) { Potassco::xconvert(out.append(",iter="), n); }
386 if (uint32 n = p.limOcc) { Potassco::xconvert(out.append(",occ="), n); }
387 if (uint32 n = p.limTime) { Potassco::xconvert(out.append(",time="), n); }
388 if (uint32 n = p.limFrozen) { Potassco::xconvert(out.append(",frozen="), n); }
389 if (uint32 n = p.limClause) { Potassco::xconvert(out.append(",size="), n); }
390 return out;
391 }
392 else {
393 return xconvert(out, Potassco::off);
394 }
395 }
396 namespace Asp { using Clasp::xconvert; }
397 namespace mt { using Clasp::xconvert; }
398 namespace Cli {
399 /////////////////////////////////////////////////////////////////////////////////////////
400 // Option -> Key mapping
401 /////////////////////////////////////////////////////////////////////////////////////////
402 /// \cond
403 // Valid option keys.
404 enum OptionKey {
405 detail__before_options = -1,
406 meta_config = 0,
407 #define CLASP_CONTEXT_OPTIONS GRP(option_category_nodes_end, option_category_context_begin),
408 #define CLASP_GLOBAL_OPTIONS GRP(option_category_context_end, option_category_global_begin),
409 #define CLASP_SOLVER_OPTIONS GRP(option_category_global_end, option_category_solver_begin),
410 #define CLASP_SEARCH_OPTIONS GRP(option_category_solver_end, option_category_search_begin),
411 #define CLASP_ASP_OPTIONS GRP(option_category_search_end, option_category_asp_begin),
412 #define CLASP_SOLVE_OPTIONS GRP(option_category_asp_end, option_category_solve_begin),
413 #define OPTION(k,e,...) opt_##k,
414 #define GROUP_BEGIN(X) X
415 #define GRP(X, Y) X, Y = X, detail__before_##Y = X - 1
416 #include <clasp/cli/clasp_cli_options.inl>
417 #undef GRP
418 option_category_solve_end,
419 detail__num_options = option_category_solve_end,
420 meta_tester = detail__num_options
421 };
isOption(int k)422 static inline bool isOption(int k) { return k >= option_category_nodes_end && k < detail__num_options; }
isGlobalOption(int k)423 static inline bool isGlobalOption(int k) { return k >= option_category_global_begin && k < option_category_global_end; }
isTesterOption(int k)424 static inline bool isTesterOption(int k) { return k >= option_category_nodes_end && k < option_category_search_end && !isGlobalOption(k); }
isSolverOption(int k)425 static inline bool isSolverOption(int k) { return k >= option_category_solver_begin && k < option_category_search_end; }
426 #if CLASP_HAS_THREADS
427 #define MANY_DESC " many : Use default portfolio to configure solver(s)\n"
428 #define MANY_ARG "|many"
429 #else
430 #define MANY_DESC
431 #define MANY_ARG ""
432 #endif
433 #define KEY_INIT_DESC(desc) \
434 desc " <arg>: {auto|frumpy|jumpy|tweety|handy|crafty|trendy" MANY_ARG "|<file>}\n" \
435 " auto : Select configuration based on problem type\n" \
436 " frumpy: Use conservative defaults\n" \
437 " jumpy : Use aggressive defaults\n" \
438 " tweety: Use defaults geared towards asp problems\n" \
439 " handy : Use defaults geared towards large problems\n" \
440 " crafty: Use defaults geared towards crafted problems\n" \
441 " trendy: Use defaults geared towards industrial problems\n" \
442 MANY_DESC \
443 " <file>: Use configuration file to configure solver(s)"
444
445 struct NodeKey {
446 const char* name;
447 const char* desc;
448 int16 skBegin;
449 int16 skEnd;
numSubkeysClasp::Cli::NodeKey450 uint32 numSubkeys() const { return static_cast<uint32>( skEnd - skBegin ); }
451 };
452 enum { key_leaf = 0, key_solver = -1, key_asp = -2, key_solve = -3, key_tester = -4, key_root = -5 };
453 // nodes_g[-k]: entry for key k
454 static const NodeKey nodes_g[] = {
455 /* 0: config */ {"configuration", KEY_INIT_DESC("Initializes this configuration\n"), 0,0},
456 /* 1: */ {"solver", "Solver Options", option_category_solver_begin, option_category_search_end},
457 /* 2: */ {"asp" , "Asp Options" , option_category_asp_begin, option_category_asp_end},
458 /* 3: */ {"solve" , "Solve Options" , option_category_solve_begin, option_category_solve_end},
459 /* 4: */ {"tester", "Tester Options", key_solver, option_category_context_end},
460 /* 5: */ {"" , "Options" , key_tester, option_category_global_end}
461 };
makeKeyHandle(int16 kId,uint32 mode,uint32 sId)462 static uint32 makeKeyHandle(int16 kId, uint32 mode, uint32 sId) {
463 assert(sId <= 255 && mode <= 255);
464 return (mode << 24) | (sId << 16) | static_cast<uint16>(kId);
465 }
decodeKey(uint32 key)466 static int16 decodeKey(uint32 key) { return static_cast<int16>(static_cast<uint16>(key)); }
decodeMode(uint32 key)467 static uint8 decodeMode(uint32 key) { return static_cast<uint8>( (key >> 24) ); }
decodeSolver(uint32 key)468 static uint8 decodeSolver(uint32 key){ return static_cast<uint8>( (key >> 16) ); }
isValidId(int16 id)469 static bool isValidId(int16 id) { return id >= key_root && id < detail__num_options; }
isLeafId(int16 id)470 static bool isLeafId(int16 id) { return id >= key_leaf && id < detail__num_options; }
471 const ClaspCliConfig::KeyType ClaspCliConfig::KEY_INVALID = static_cast<ClaspCliConfig::KeyType>(-1);
472 const ClaspCliConfig::KeyType ClaspCliConfig::KEY_ROOT = makeKeyHandle(key_root, 0, 0);
473 const ClaspCliConfig::KeyType ClaspCliConfig::KEY_SOLVER = makeKeyHandle(key_solver, 0, 0);
474 const ClaspCliConfig::KeyType ClaspCliConfig::KEY_TESTER = makeKeyHandle(key_tester, ClaspCliConfig::mode_tester, 0);
475
476 struct Name2Id {
477 const char* name; int key;
operator <Clasp::Cli::Name2Id478 bool operator<(const Name2Id& rhs) const { return std::strcmp(name, rhs.name) < 0; }
479 };
480 static Name2Id options_g[detail__num_options+1] = {
481 {"configuration", meta_config},
482 #define OPTION(k, e, ...) { #k, opt_##k },
483 #define CLASP_CONTEXT_OPTIONS
484 #define CLASP_GLOBAL_OPTIONS
485 #define CLASP_SOLVER_OPTIONS
486 #define CLASP_SEARCH_OPTIONS
487 #define CLASP_ASP_OPTIONS
488 #define CLASP_SOLVE_OPTIONS
489 #include <clasp/cli/clasp_cli_options.inl>
490 {"tester" , meta_tester}
491 };
492 struct ClaspCliConfig::OptIndex {
OptIndexClasp::Cli::ClaspCliConfig::OptIndex493 OptIndex(Name2Id* first, Name2Id* last) {
494 std::sort(begin = first, end = last);
495 }
findClasp::Cli::ClaspCliConfig::OptIndex496 Name2Id* find(const char* name) const {
497 Name2Id temp = { name, 0 };
498 Name2Id* it = std::lower_bound(begin, end, temp);
499 if (it != end && std::strcmp(it->name, name) == 0) { return it; }
500 return 0;
501 }
502 Name2Id* begin;
503 Name2Id* end;
504 };
505 ClaspCliConfig::OptIndex ClaspCliConfig::index_g(options_g, options_g + detail__num_options+1);
506 /// \endcond
507 /////////////////////////////////////////////////////////////////////////////////////////
508 // Interface to ProgramOptions
509 /////////////////////////////////////////////////////////////////////////////////////////
510 // Converts option key to command-line option name.
keyToCliName(std::string & out,const char * n,const char * ext)511 static void keyToCliName(std::string& out, const char* n, const char* ext) {
512 out.clear();
513 for (const char* x; (x = std::strchr(n, '_')) != 0; n = ++x) {
514 out.append(n, x-n);
515 out.append(1, '-');
516 }
517 out.append(n).append(ext);
518 }
519 // Converts command-line option name to option key.
cliNameToKey(std::string & out,const char * n)520 static void cliNameToKey(std::string& out, const char* n) {
521 out.clear();
522 for (const char* x; (x = std::strchr(n, '-')) != 0; n = ++x) {
523 out.append(n, x-n);
524 out.append(1, '_');
525 }
526 out.append(n);
527 }
528 // Type for storing one command-line option.
529 class ClaspCliConfig::ProgOption : public Potassco::ProgramOptions::Value {
530 public:
ProgOption(ClaspCliConfig & c,int o)531 ProgOption(ClaspCliConfig& c, int o) : Potassco::ProgramOptions::Value(0), config_(&c), option_(o) {}
doParse(const std::string & opt,const std::string & value)532 bool doParse(const std::string& opt, const std::string& value) {
533 int ret = isOption(option_) ? config_->setActive(option_, value.c_str()) : config_->setAppOpt(option_, value.c_str());
534 if (ret == -1) { throw Potassco::ProgramOptions::UnknownOption(config_->isGenerator() ? "<clasp>" : "<tester>", opt); }
535 return ret > 0;
536 }
option() const537 int option() const { return option_; }
538 private:
539 ClaspCliConfig* config_;
540 int option_;
541 };
542
543 // Adapter for parsing a command string.
544 struct ClaspCliConfig::ParseContext : public Potassco::ProgramOptions::ParseContext{
545 typedef Potassco::ProgramOptions::SharedOptPtr OptPtr;
ParseContextClasp::Cli::ClaspCliConfig::ParseContext546 ParseContext(ClaspCliConfig& x, const char* c, const ParsedOpts* ex, bool allowMeta, ParsedOpts* o)
547 : self(&x), config(c), exclude(ex), out(o), meta(allowMeta) { seen[0] = seen[1] = 0; }
548 OptPtr getOption(const char* name, FindType ft);
getOptionClasp::Cli::ClaspCliConfig::ParseContext549 OptPtr getOption(int, const char* key) { throw Potassco::ProgramOptions::UnknownOption(config, key); }
550 void addValue(const OptPtr& key, const std::string& value);
551 uint64 seen[2];
552 std::string temp;
553 ClaspCliConfig* self;
554 const char* config;
555 const ParsedOpts* exclude;
556 ParsedOpts* out;
557 bool meta;
558 };
addValue(const OptPtr & key,const std::string & value)559 void ClaspCliConfig::ParseContext::addValue(const OptPtr& key, const std::string& value) {
560 using namespace Potassco::ProgramOptions;
561 if (exclude->count(key->name()) == 0) {
562 ProgOption* v = static_cast<ProgOption*>(key->value());
563 Value::State s= v->state();
564 int id = v->option();
565 uint64& xs = seen[id/64];
566 uint64 m = static_cast<uint64>(1u) << (id & 63);
567 if ((xs & m) != 0 && !v->isComposing()){ throw ValueError(config, ValueError::multiple_occurrences, key->name(), value); }
568 if (!v->parse(key->name(), value, s)) { throw ValueError(config, ValueError::invalid_value, key->name(), value); }
569 if (out) { out->add(key->name()); }
570 xs |= m;
571 }
572 }
getOption(const char * cmdName,FindType ft)573 Potassco::ProgramOptions::SharedOptPtr ClaspCliConfig::ParseContext::getOption(const char* cmdName, FindType ft) {
574 Options::option_iterator end = self->opts_->end(), it = end;
575 if (ft == OptionContext::find_alias) {
576 char a = cmdName[*cmdName == '-'];
577 for (it = self->opts_->begin(); it != end && it->get()->alias() != a; ++it) { ; }
578 }
579 else {
580 Name2Id key = { cmdName, -2 };
581 if (std::strchr(cmdName, '-') != 0) { cliNameToKey(temp, cmdName); key.name = temp.c_str(); }
582 Name2Id* pos = std::lower_bound(self->index_g.begin, self->index_g.end, key);
583 if (pos != self->index_g.end) {
584 std::size_t len = std::strlen(key.name);
585 int cmp = std::strncmp(key.name, pos->name, len);
586 bool found = cmp == 0 && !*(pos->name+len);
587 if (!found && cmp == 0 && (ft & OptionContext::find_prefix) != 0) {
588 Name2Id* next = pos + 1;
589 cmp = next != self->index_g.end ? std::strncmp(key.name, next->name, len) : -1;
590 found = cmp != 0;
591 if (!found) { throw Potassco::ProgramOptions::AmbiguousOption(config, cmdName, ""); }
592 }
593 if (found) { it = self->opts_->begin() + pos->key; }
594 }
595 assert(it == end || static_cast<const ProgOption*>(it->get()->value())->option() == pos->key);
596 }
597 if (it != end && (meta || isOption(static_cast<const ProgOption*>(it->get()->value())->option()))) {
598 return *it;
599 }
600 throw Potassco::ProgramOptions::UnknownOption(config, cmdName);
601 }
602 /////////////////////////////////////////////////////////////////////////////////////////
603 // Default Configs
604 /////////////////////////////////////////////////////////////////////////////////////////
getConfig(ConfigKey k)605 ConfigIter ClaspCliConfig::getConfig(ConfigKey k) {
606 switch(k) {
607 #define CONFIG(id, n,c,s,p) case config_##n: return ConfigIter("/[" #n "]\0/\0/" s " " c "\0");
608 #define CLASP_CLI_DEFAULT_CONFIGS
609 #define CLASP_CLI_AUX_CONFIGS
610 #include <clasp/cli/clasp_cli_configs.inl>
611 case config_many:
612 #define CONFIG(id,n,c,s,p) "/[solver." #id "]\0/\0/" c " " p "\0"
613 #define CLASP_CLI_DEFAULT_CONFIGS
614 #define CLASP_CLI_AUX_CONFIGS
615 return ConfigIter(
616 #include <clasp/cli/clasp_cli_configs.inl>
617 );
618 default: POTASSCO_REQUIRE(k == config_default, "Invalid config key '%d'", (int)k); return ConfigIter("/default\0/\0/\0");
619 }
620 }
getConfig(uint8 key,std::string & tempMem)621 ConfigIter ClaspCliConfig::getConfig(uint8 key, std::string& tempMem) {
622 POTASSCO_REQUIRE(key <= (config_max_value + 1), "Invalid key!");
623 if (key < config_max_value) { return getConfig(static_cast<ConfigKey>(key)); }
624 tempMem.clear();
625 loadConfig(tempMem, config_[key - config_max_value].c_str());
626 return ConfigIter(tempMem.data());
627 }
getConfigKey(const char * k)628 int ClaspCliConfig::getConfigKey(const char* k) {
629 ConfigKey ret;
630 return Potassco::string_cast(k, ret) ? ret : -1;
631 }
skipWs(const char * x)632 static inline const char* skipWs(const char* x) {
633 while (*x == ' ' || *x == '\t') { ++x; }
634 return x;
635 }
getIdent(const char * x,std::string & to)636 static inline const char* getIdent(const char* x, std::string& to) {
637 for (x = skipWs(x); std::strchr(" \t:()[]", *x) == 0; ++x) { to += *x; }
638 return x;
639 }
matchSep(const char * & x,char c)640 static inline bool matchSep(const char*& x, char c) {
641 if (*(x = skipWs(x)) == c) { ++x; return true; }
642 return false;
643 }
644
appendConfig(std::string & to,const std::string & line)645 bool ClaspCliConfig::appendConfig(std::string& to, const std::string& line) {
646 std::size_t sz = to.size();
647 const char* x = skipWs(line.c_str());
648 const bool p = matchSep(x, '[');
649 to.append("/[", 2);
650 // match name in optional square brackets
651 bool ok = matchSep(x = getIdent(x, to), ']') == p;
652 to.append("]\0/", 3);
653 // match optional base in parentheses followed by start of option list
654 if (ok && (!matchSep(x, '(') || matchSep((x = getIdent(x, to)), ')')) && matchSep(x, ':')) {
655 to.append("\0/", 2);
656 to.append(skipWs(x));
657 to.erase(to.find_last_not_of(" \t") + 1);
658 to.append(1, '\0');
659 return true;
660 }
661 to.resize(sz);
662 return false;
663 }
loadConfig(std::string & to,const char * name)664 bool ClaspCliConfig::loadConfig(std::string& to, const char* name) {
665 std::ifstream file(name);
666 POTASSCO_EXPECT(file, "Could not open config file '%s'", name);
667 uint32 lineNum= 0;
668 for (std::string line, cont; std::getline(file, line); ) {
669 ++lineNum;
670 line.erase(0, line.find_first_not_of(" \t"));
671 if (line.empty() || line[0] == '#') { continue; }
672 if (*line.rbegin() == '\\') { *line.rbegin() = ' '; cont += line; continue; }
673 if (!cont.empty()) { cont += line; cont.swap(line); cont.clear(); }
674 POTASSCO_EXPECT(appendConfig(to, line), "'%s@%u': Invalid configuration", name, lineNum);
675 }
676 to.append(1, '\0');
677 return true;
678 }
getDefaults(ProblemType t)679 const char* ClaspCliConfig::getDefaults(ProblemType t) {
680 if (t == Problem_t::Asp){ return "--configuration=tweety"; }
681 else { return "--configuration=trendy"; }
682 }
ConfigIter(const char * x)683 ConfigIter::ConfigIter(const char* x) : base_(x) {}
name() const684 const char* ConfigIter::name() const { return base_ + 1; }
base() const685 const char* ConfigIter::base() const { return base_ + std::strlen(base_) + 2; }
args() const686 const char* ConfigIter::args() const { const char* x = base(); return x + std::strlen(x) + 2; }
valid() const687 bool ConfigIter::valid()const { return *base_ != 0; }
next()688 bool ConfigIter::next() {
689 base_ = args();
690 base_+= std::strlen(base_) + 1;
691 return valid();
692 }
693 /////////////////////////////////////////////////////////////////////////////////////////
694 // ClaspCliConfig
695 /////////////////////////////////////////////////////////////////////////////////////////
ScopedSet(ClaspCliConfig & s,uint8 mode,uint32 sId)696 ClaspCliConfig::ScopedSet::ScopedSet(ClaspCliConfig& s, uint8 mode, uint32 sId) : self(&s) {
697 if (sId) { mode |= mode_solver; }
698 s.cliId = static_cast<uint8>(sId);
699 s.cliMode = mode;
700 }
~ScopedSet()701 ClaspCliConfig::ScopedSet::~ScopedSet() { self->cliId = self->cliMode = 0; }
RawConfig(const char * name)702 ClaspCliConfig::RawConfig::RawConfig(const char* name) {
703 raw.append(1, '/').append(name ? name : "").append("\0/\0/", 4);
704 }
addArg(const char * arg)705 void ClaspCliConfig::RawConfig::addArg(const char* arg) {
706 *raw.rbegin() = ' ';
707 raw.append(arg ? arg : "").append(1, '\0');
708 }
addArg(const std::string & arg)709 void ClaspCliConfig::RawConfig::addArg(const std::string& arg) { addArg(arg.c_str()); }
ClaspCliConfig()710 ClaspCliConfig::ClaspCliConfig() {
711 initTester_ = true;
712 static_assert(
713 (option_category_context_begin< option_category_solver_begin) &&
714 (option_category_solver_begin < option_category_search_begin) &&
715 (option_category_search_begin < option_category_asp_begin) &&
716 (option_category_asp_begin < option_category_solve_begin) &&
717 (option_category_solve_begin < option_category_solve_end), "unexpected option order");
718 }
~ClaspCliConfig()719 ClaspCliConfig::~ClaspCliConfig() {}
reset()720 void ClaspCliConfig::reset() {
721 config_[0] = config_[1] = "";
722 initTester_ = true;
723 ClaspConfig::reset();
724 }
725
prepare(SharedContext & ctx)726 void ClaspCliConfig::prepare(SharedContext& ctx) {
727 if (testerConfig()) {
728 // Force init
729 ClaspCliConfig::config("tester");
730 }
731 ClaspConfig::prepare(ctx);
732 }
config(const char * n)733 Configuration* ClaspCliConfig::config(const char* n) {
734 if (n && std::strcmp(n, "tester") == 0) {
735 if (!testerConfig() || (!testerConfig()->hasConfig && initTester_)) {
736 setAppOpt(meta_tester, "--config=auto");
737 initTester_ = false;
738 }
739 return testerConfig();
740 }
741 return ClaspConfig::config(n);
742 }
743
createOption(int o)744 ClaspCliConfig::ProgOption* ClaspCliConfig::createOption(int o) { return new ProgOption(*this, o); }
745
createOptions()746 void ClaspCliConfig::createOptions() {
747 if (opts_.get()) { return; }
748 opts_ = new Options();
749 using namespace Potassco::ProgramOptions;
750 opts_->addOptions()("configuration", createOption(meta_config)->defaultsTo("auto")->state(Value::value_defaulted), KEY_INIT_DESC("Set default configuration [%D]\n"));
751 std::string cmdName;
752 #define CLASP_CONTEXT_OPTIONS
753 #define CLASP_GLOBAL_OPTIONS
754 #define CLASP_SOLVE_OPTIONS
755 #define CLASP_ASP_OPTIONS
756 #define CLASP_SOLVER_OPTIONS
757 #define CLASP_SEARCH_OPTIONS
758 #define OPTION(k, e, a, d, ...) keyToCliName(cmdName, #k, e); opts_->addOptions()(cmdName.c_str(),static_cast<ProgOption*>( createOption(opt_##k)a ), d);
759 #define ARG(a) ->a
760 #define ARG_EXT(a, X) ARG(a)
761 #define NO_ARG
762 #include <clasp/cli/clasp_cli_options.inl>
763 opts_->addOptions()("tester", createOption(meta_tester)->arg("<options>"), "Pass (quoted) string of %A to tester");
764 }
addOptions(OptionContext & root)765 void ClaspCliConfig::addOptions(OptionContext& root) {
766 createOptions();
767 using namespace Potassco::ProgramOptions;
768 OptionGroup configOpts("Clasp.Config Options");
769 OptionGroup ctxOpts("Clasp.Context Options", Potassco::ProgramOptions::desc_level_e1);
770 OptionGroup solving("Clasp.Solving Options");
771 OptionGroup aspOpts("Clasp.ASP Options", Potassco::ProgramOptions::desc_level_e1);
772 OptionGroup search("Clasp.Search Options", Potassco::ProgramOptions::desc_level_e1);
773 OptionGroup lookback("Clasp.Lookback Options", Potassco::ProgramOptions::desc_level_e1);
774 configOpts.addOption(*opts_->begin());
775 configOpts.addOption(*(opts_->end()-1));
776 for (Options::option_iterator it = opts_->begin() + 1, end = opts_->end() - 1; it != end; ++it) {
777 int oId = static_cast<ProgOption*>(it->get()->value())->option();
778 if (isGlobalOption(oId)) { configOpts.addOption(*it);}
779 else if (oId < option_category_context_end) { ctxOpts.addOption(*it); }
780 else if (oId < opt_no_lookback) { search.addOption(*it); }
781 else if (oId < option_category_solver_end) { lookback.addOption(*it); }
782 else if (oId < opt_restarts) { search.addOption(*it); }
783 else if (oId < option_category_search_end) { lookback.addOption(*it); }
784 else if (oId < option_category_asp_end) { aspOpts.addOption(*it); }
785 else { solving.addOption(*it); }
786 }
787 root.add(configOpts).add(ctxOpts).add(aspOpts).add(solving).add(search).add(lookback);
788 root.addAlias("number", root.find("models")); // remove on next version
789 root.addAlias("opt-sat", root.find("parse-maxsat")); // remove on next version
790 }
assignDefaults(const Potassco::ProgramOptions::ParsedOptions & exclude)791 bool ClaspCliConfig::assignDefaults(const Potassco::ProgramOptions::ParsedOptions& exclude) {
792 for (Options::option_iterator it = opts_->begin(), end = opts_->end(); it != end; ++it) {
793 const Potassco::ProgramOptions::Option& o = **it;
794 POTASSCO_REQUIRE(exclude.count(o.name()) != 0 || o.assignDefault(), "Option '%s': invalid default value '%s'\n", o.name().c_str(), o.value()->defaultsTo());
795 }
796 return true;
797 }
releaseOptions()798 void ClaspCliConfig::releaseOptions() {
799 opts_ = 0;
800 }
match(const char * & path,const char * what) const801 bool ClaspCliConfig::match(const char*& path, const char* what) const {
802 std::size_t wLen = std::strlen(what);
803 if (strncmp(path, what, wLen) != 0 || (path[wLen] && path[wLen++] != '.')) {
804 return false;
805 }
806 path += wLen;
807 return true;
808 }
getKey(KeyType k,const char * path) const809 ClaspCliConfig::KeyType ClaspCliConfig::getKey(KeyType k, const char* path) const {
810 int16 id = decodeKey(k);
811 if (!isValidId(id) || !path || !*path || (*path == '.' && !*++path)) {
812 return k;
813 }
814 if (isLeafId(id)){ return KEY_INVALID; }
815 const NodeKey& x = nodes_g[-id];
816 for (int16 sk = x.skBegin; sk != x.skEnd && sk < 0; ++sk) {
817 NodeKey sub = nodes_g[-sk];
818 if (match(path, sub.name)) {
819 KeyType ret = makeKeyHandle(sk, (sk == key_tester ? mode_tester : 0) | decodeMode(k), 0);
820 if (!*path) { return ret; }
821 return getKey(ret, path);
822 }
823 }
824 uint8 mode = decodeMode(k);
825 if (id == key_solver) {
826 uint32 solverId;
827 if ((mode & mode_solver) == 0 && *path != '.' && Potassco::xconvert(path, solverId, &path, 0) == 1) {
828 return getKey(makeKeyHandle(id, mode | mode_solver, std::min(solverId, (uint32)uint8(-1))), path);
829 }
830 mode |= mode_solver;
831 }
832 const Name2Id* opt = index_g.find(path);
833 // remaining name must be a valid option in our subkey range
834 if (!opt || opt->key < x.skBegin || opt->key >= x.skEnd) {
835 return KEY_INVALID;
836 }
837 return makeKeyHandle(static_cast<int16>(opt->key), mode, decodeSolver(k));
838 }
839
getArrKey(KeyType k,unsigned i) const840 ClaspCliConfig::KeyType ClaspCliConfig::getArrKey(KeyType k, unsigned i) const {
841 int16 id = decodeKey(k);
842 if (id != key_solver || (decodeMode(k) & mode_solver) != 0 || i >= solve.supportedSolvers()) { return KEY_INVALID; }
843 return makeKeyHandle(id, decodeMode(k) | mode_solver, i);
844 }
getKeyInfo(KeyType k,int * nSubkeys,int * arrLen,const char ** help,int * nValues) const845 int ClaspCliConfig::getKeyInfo(KeyType k, int* nSubkeys, int* arrLen, const char** help, int* nValues) const {
846 int16 id = decodeKey(k);
847 int ret = 0;
848 if (!isValidId(id)){ return -1; }
849 if (isLeafId(id)){
850 if (nSubkeys && ++ret) { *nSubkeys = 0; }
851 if (arrLen && ++ret) { *arrLen = -1; }
852 if (nValues && ++ret) { *nValues = static_cast<int>( (decodeMode(k) & mode_tester) == 0 || testerConfig() != 0 ); }
853 if (help && ++ret) { getActive(id, 0, help, 0); }
854 return ret;
855 }
856 const NodeKey& x = nodes_g[-id];
857 if (nSubkeys && ++ret) { *nSubkeys = x.numSubkeys(); }
858 if (nValues && ++ret) { *nValues = -1; }
859 if (help && ++ret) { *help = x.desc; }
860 if (arrLen && ++ret) {
861 *arrLen = -1;
862 if (id == key_solver && (decodeMode(k) & mode_solver) == 0) {
863 const UserConfig* c = (decodeMode(k) & mode_tester) == 0 ? this : testerConfig();
864 *arrLen = c ? (int)c->numSolver() : 0;
865 }
866 }
867 return ret;
868 }
isLeafKey(KeyType k)869 bool ClaspCliConfig::isLeafKey(KeyType k) { return isLeafId(decodeKey(k)); }
getSubkey(KeyType k,uint32 i) const870 const char* ClaspCliConfig::getSubkey(KeyType k, uint32 i) const {
871 int16 id = decodeKey(k);
872 if (!isValidId(id) || isLeafId(id)) { return 0; }
873 const NodeKey& nk = nodes_g[-id];
874 if (i >= nk.numSubkeys()) { return 0; }
875 int sk = nk.skBegin + static_cast<int16>(i);
876 if (sk < key_leaf) { return nodes_g[-sk].name; }
877 const char* opt = 0;
878 getActive(sk, 0, 0, &opt);
879 return opt;
880 }
getValue(KeyType key,std::string & out) const881 int ClaspCliConfig::getValue(KeyType key, std::string& out) const {
882 int16 id = decodeKey(key);
883 if (!isLeafId(id)) { return -1; }
884 try {
885 int ret = ScopedSet(const_cast<ClaspCliConfig&>(*this), decodeMode(key), decodeSolver(key))->getActive(id, &out, 0, 0);
886 return ret > 0 ? static_cast<int>(out.length()) : ret;
887 }
888 catch (...) { return -2; }
889 }
getValue(KeyType key,char * buffer,std::size_t bufSize) const890 int ClaspCliConfig::getValue(KeyType key, char* buffer, std::size_t bufSize) const {
891 std::string temp;
892 int ret = getValue(key, temp);
893 if (ret <= 0) { return ret; }
894 if (buffer && bufSize) {
895 std::size_t n = temp.length() >= bufSize ? bufSize - 1 : temp.length();
896 std::memcpy(buffer, temp.c_str(), n * sizeof(char));
897 buffer[n] = 0;
898 }
899 return static_cast<int>(temp.length());
900 }
getValue(const char * path) const901 std::string ClaspCliConfig::getValue(const char* path) const {
902 std::string temp;
903 POTASSCO_REQUIRE(getValue(getKey(KEY_ROOT, path), temp) >= 0, "Invalid key: '%s'", path);
904 return temp;
905 }
hasValue(const char * path) const906 bool ClaspCliConfig::hasValue(const char* path) const {
907 int nVals;
908 return getKeyInfo(getKey(KEY_ROOT, path), 0, 0, 0, &nVals) == 1 && nVals > 0;
909 }
910
setValue(KeyType key,const char * value)911 int ClaspCliConfig::setValue(KeyType key, const char* value) {
912 int16 id = decodeKey(key);
913 if (!isLeafId(id)) { return -1; }
914 if ((decodeMode(key) & mode_tester) != 0) {
915 addTesterConfig();
916 initTester_ = false;
917 }
918 ScopedSet scope(*this, decodeMode(key), decodeSolver(key));
919 try { return setActive(id, value); }
920 catch (...) { return -2; }
921 }
922
setValue(const char * path,const char * value)923 bool ClaspCliConfig::setValue(const char* path, const char* value) {
924 int ret = setValue(getKey(KEY_ROOT, path), value);
925 POTASSCO_REQUIRE(ret >= 0, (ret == -1 ? "Invalid or incomplete key: '%s'" : "Value error in key: '%s'"), path);
926 return ret != 0;
927 }
928
applyActive(int o,const char * _val_,std::string * _val_out_,const char ** _desc_out_,const char ** _name_out_)929 int ClaspCliConfig::applyActive(int o, const char* _val_, std::string* _val_out_, const char** _desc_out_, const char** _name_out_) {
930 UserConfig* active = this->active();
931 uint32 sId = cliId;
932 SolverOpts* solver = 0;
933 SearchOpts* search = 0;
934 ContextParams* ctxOpts= active;
935 if (_name_out_) { *_name_out_ = 0; }
936 if (_val_ || _val_out_) {
937 if (!active || (active == testerConfig() && !isTesterOption(o)) || ((cliMode & mode_solver) != 0 && !isSolverOption(o))) {
938 o = (cliMode & mode_relaxed) != 0 ? detail__before_options : detail__num_options;
939 }
940 else if (isSolverOption(o)) {
941 solver = &active->addSolver(sId);
942 search = &active->addSearch(sId);
943 }
944 }
945 if (!isOption(o)) {
946 return o == detail__before_options ? int(_val_ != 0) : -1;
947 }
948 // action & helper macros used in get/set
949 using Potassco::xconvert; using Potassco::off; using Potassco::opt;
950 using Potassco::stringTo; using Potassco::toString; using Potassco::Set;
951 #define ITE(c, a, b) (!!(c) ? (a) : (b))
952 #define FUN(x) for (Potassco::ArgString x = _val_;;)
953 #define STORE(obj) { return stringTo((_val_), obj); }
954 #define STORE_LEQ(x, y) { unsigned __n; return stringTo(_val_, __n) && SET_LEQ(x, __n, y); }
955 #define STORE_FLAG(x) { bool __b; return stringTo(_val_, __b) && SET(x, static_cast<unsigned>(__b)); }
956 #define STORE_OR_FILL(x) { unsigned __n; return stringTo(_val_, __n) && SET_OR_FILL(x, __n); }
957 #define STORE_U(E, x) { E __e; return stringTo((_val_), __e) && SET(x, static_cast<unsigned>(__e));}
958 #define GET_FUN(x) Potassco::StringRef x(*_val_out_); if (!x.out);else
959 #define GET(...) *_val_out_ = toString( __VA_ARGS__ )
960 #define GET_IF(c, ...) *_val_out_ = ITE((c), toString(__VA_ARGS__), toString(off))
961 switch(static_cast<OptionKey>(o)) {
962 default: return -1;
963 #define OPTION(k, e, a, d, x, v) \
964 case opt_##k:\
965 if (_name_out_){ *_name_out_ = #k ; }\
966 if (_val_) try { x ; }catch(...) {return 0;}\
967 if (_val_out_) { v ; }\
968 if (_desc_out_){ *_desc_out_ = d; }\
969 return 1;
970 #define CLASP_CONTEXT_OPTIONS (*ctxOpts)
971 #define CLASP_GLOBAL_OPTIONS (*this)
972 #define CLASP_ASP_OPTIONS asp
973 #define CLASP_SOLVE_OPTIONS solve
974 #define CLASP_SOLVER_OPTIONS (*solver)
975 #define CLASP_SEARCH_OPTIONS (*search)
976 #define CLASP_SEARCH_RESTART_OPTIONS search->restart
977 #define CLASP_SEARCH_REDUCE_OPTIONS search->reduce
978 #include <clasp/cli/clasp_cli_options.inl>
979 }
980 #undef FUN
981 #undef STORE
982 #undef STORE_LEQ
983 #undef STORE_FLAG
984 #undef STORE_OR_FILL
985 #undef GET_IF
986 #undef GET
987 #undef ITE
988 }
989
setActive(int id,const char * setVal)990 int ClaspCliConfig::setActive(int id, const char* setVal) {
991 if (isOption(id)) { return applyActive(id, setVal ? setVal : "", 0, 0, 0); }
992 else if (id == meta_config){
993 int sz = setAppOpt(id, setVal);
994 if (sz <= 0) { return 0; }
995 std::string m;
996 UserConfig* act = active();
997 ConfigIter it = getConfig(act->cliConfig, m);
998 act->hasConfig = 0;
999 cliMode |= mode_relaxed;
1000 act->resize(1, 1);
1001 for (uint32 sId = 0; it.valid(); it.next()) {
1002 act->addSolver(sId);
1003 act->addSearch(sId);
1004 cliId = static_cast<uint8>(sId);
1005 if (!setConfig(it, false, ParsedOpts(), 0)){ return 0; }
1006 if (++sId == static_cast<uint32>(sz)) { break; }
1007 cliMode |= mode_solver;
1008 }
1009 if (sz < 65 && static_cast<uint32>(sz) > act->numSolver()) {
1010 for (uint32 sId = act->numSolver(), mod = sId, end = static_cast<uint32>(sz); sId != end; ++sId) {
1011 SolverParams& solver = act->addSolver(sId);
1012 SolveParams& search = act->addSearch(sId);
1013 (solver = act->solver(sId % mod)).setId(sId);
1014 search = act->search(sId % mod);
1015 }
1016 }
1017 return static_cast<int>((act->hasConfig = 1) == 1);
1018 }
1019 else { return -1; }
1020 }
1021
getActive(int o,std::string * val,const char ** desc,const char ** opt) const1022 int ClaspCliConfig::getActive(int o, std::string* val, const char** desc, const char** opt) const {
1023 if (isOption(o)) { return const_cast<ClaspCliConfig&>(*this).applyActive(o, 0, val, desc, opt); }
1024 else if (!active()) { return -1; }
1025 else if (o == meta_config){
1026 const NodeKey& n = nodes_g[-o];
1027 if (val) {
1028 uint8 k = (ConfigKey)active()->cliConfig;
1029 if (k < config_max_value) { xconvert(*val, static_cast<ConfigKey>(k)); }
1030 else { val->append(config_[!isGenerator()]); }
1031 }
1032 if (desc) { *desc = n.desc; }
1033 if (opt) { *opt = n.name; }
1034 return 1;
1035 }
1036 else { return -1; }
1037 }
setAppOpt(int o,const char * _val_)1038 int ClaspCliConfig::setAppOpt(int o, const char* _val_) {
1039 if (o == meta_config) {
1040 std::pair<ConfigKey, uint32> defC(config_default, INT_MAX);
1041 if (Potassco::stringTo(_val_, defC)) { active()->cliConfig = (uint8)defC.first; }
1042 else {
1043 POTASSCO_EXPECT(std::ifstream(_val_).is_open(), "Could not open config file '%s'", _val_);
1044 config_[!isGenerator()] = _val_; active()->cliConfig = config_max_value + !isGenerator();
1045 }
1046 return Range<uint32>(0, INT_MAX).clamp(defC.second);
1047 }
1048 else if (o == meta_tester && isGenerator()) {
1049 addTesterConfig();
1050 initTester_ = false;
1051 RawConfig config("<tester>");
1052 config.addArg(_val_);
1053 ParsedOpts ex;
1054 bool ret = ScopedSet(*this, mode_tester)->setConfig(config.iterator(), true, ParsedOpts(), &ex);
1055 return ret && finalizeAppConfig(testerConfig(), finalizeParsed(testerConfig(), ex, ex), Problem_t::Asp, true);
1056 }
1057 return -1; // invalid option
1058 }
setAppDefaults(UserConfig * active,uint32 sId,const ParsedOpts & cmdLine,ProblemType t)1059 bool ClaspCliConfig::setAppDefaults(UserConfig* active, uint32 sId, const ParsedOpts& cmdLine, ProblemType t) {
1060 ScopedSet temp(*this, (active == this ? 0 : mode_tester) | mode_relaxed, sId);
1061 if (sId == 0 && t != Problem_t::Asp && cmdLine.count("sat-prepro") == 0) {
1062 setActive(opt_sat_prepro, "2,iter=20,occ=25,time=120");
1063 }
1064 if (active->addSolver(sId).search == SolverParams::no_learning) {
1065 if (cmdLine.count("heuristic") == 0) { setActive(opt_heuristic, "unit"); }
1066 if (cmdLine.count("lookahead") == 0) { setActive(opt_lookahead, "atom"); }
1067 if (cmdLine.count("deletion") == 0) { setActive(opt_deletion, "no"); }
1068 if (cmdLine.count("restarts") == 0) { setActive(opt_restarts, "no"); }
1069 }
1070 return true;
1071 }
1072
setConfig(const ConfigIter & config,bool allowMeta,const ParsedOpts & exclude,ParsedOpts * out)1073 bool ClaspCliConfig::setConfig(const ConfigIter& config, bool allowMeta, const ParsedOpts& exclude, ParsedOpts* out) {
1074 createOptions();
1075 ParseContext ctx(*this, config.name(), &exclude, allowMeta, out);
1076 Potassco::ProgramOptions::parseCommandString(config.args(), ctx, Potassco::ProgramOptions::command_line_allow_flag_value);
1077 return true;
1078 }
1079
validate()1080 bool ClaspCliConfig::validate() {
1081 UserConfiguration* arr[3] = { this, testerConfig(), 0 };
1082 UserConfiguration** c = arr;
1083 const char* ctx = *c == this ? "config":"tester";
1084 const char* err = 0;
1085 do {
1086 for (uint32 i = 0; i != (*c)->numSolver(); ++i) {
1087 POTASSCO_REQUIRE((err = Clasp::Cli::validate((*c)->solver(i), (*c)->search(i))) == 0, "<%s>.%u: %s", ctx, i, err);
1088 }
1089 } while (*++c);
1090 return true;
1091 }
1092
finalize(const ParsedOpts & x,ProblemType t,bool defs)1093 bool ClaspCliConfig::finalize(const ParsedOpts& x, ProblemType t, bool defs) {
1094 ParsedOpts temp;
1095 return finalizeAppConfig(this, finalizeParsed(this, x, temp), t, defs);
1096 }
1097
addDisabled(ParsedOpts & parsed)1098 void ClaspCliConfig::addDisabled(ParsedOpts& parsed) {
1099 finalizeParsed(this, parsed, parsed);
1100 }
1101
finalizeParsed(UserConfig * active,const ParsedOpts & parsed,ParsedOpts & exclude) const1102 const ClaspCliConfig::ParsedOpts& ClaspCliConfig::finalizeParsed(UserConfig* active, const ParsedOpts& parsed, ParsedOpts& exclude) const {
1103 bool copied = &parsed == &exclude;
1104 if (active->search(0).reduce.fReduce() == 0 && parsed.count("deletion") != 0) {
1105 if (!copied) { exclude = parsed; copied = true; }
1106 exclude.add("del-cfl");
1107 exclude.add("del-max");
1108 exclude.add("del-grow");
1109 }
1110 return !copied ? parsed : exclude;
1111 }
1112
finalizeAppConfig(UserConfig * active,const ParsedOpts & parsed,ProblemType t,bool defs)1113 bool ClaspCliConfig::finalizeAppConfig(UserConfig* active, const ParsedOpts& parsed, ProblemType t, bool defs) {
1114 if (defs && !setAppDefaults(active, 0, parsed, t)) { return false; }
1115 SolverParams defSolver = active->solver(0);
1116 SolveParams defSearch = active->search(0);
1117 if (active->hasConfig) { return true; }
1118 uint8 c = active->cliConfig;
1119 if (c == config_many && solve.numSolver() == 1) { c = config_default; }
1120 if (c == config_default) {
1121 if (defSolver.search == SolverParams::no_learning) { c = config_nolearn; }
1122 else if (active == testerConfig()) { c = config_tester_default; }
1123 else if (solve.numSolver() == 1 || !solve.defaultPortfolio()) { c = t == Problem_t::Asp ? (uint8)config_asp_default : (uint8)config_sat_default; }
1124 else { c = config_many; }
1125 }
1126 std::string m;
1127 ConfigIter conf = getConfig(c, m);
1128 uint8 mode = (active == testerConfig() ? mode_tester : 0) | mode_relaxed;
1129 uint32 portSize = 0;
1130 const char* ctx = active == testerConfig() ? "tester" : "config", *err = 0;
1131 for (uint32 i = 0; i != solve.numSolver() && conf.valid(); ++i) {
1132 SolverParams& solver = (active->addSolver(i) = defSolver).setId(i);
1133 SolveParams& search = (active->addSearch(i) = defSearch);
1134 ConfigKey baseK = config_default;
1135 POTASSCO_REQUIRE(!*conf.base() || Potassco::stringTo(conf.base(), baseK), "<%s>.%s: '%s': Invalid base config!", ctx, conf.name(), conf.base());
1136 if (baseK != config_default && !ScopedSet(*this, mode|mode_solver, i)->setConfig(getConfig(baseK), false, parsed, 0)) {
1137 return false;
1138 }
1139 if (!ScopedSet(*this, mode, i)->setConfig(conf, false, parsed, 0)) {
1140 return false;
1141 }
1142 POTASSCO_REQUIRE((err = Clasp::Cli::validate(solver, search)) == 0, "<%s>.%s : %s", ctx, conf.name(), err);
1143 ++portSize;
1144 conf.next();
1145 mode |= mode_solver;
1146 }
1147 active->hasConfig = 1;
1148 return true;
1149 }
1150
setAppConfig(const RawConfig & config,ProblemType t)1151 bool ClaspCliConfig::setAppConfig(const RawConfig& config, ProblemType t) {
1152 Potassco::ProgramOptions::ParsedOptions exclude;
1153 reset();
1154 return setConfig(config.iterator(), true, exclude, &exclude) && assignDefaults(exclude) && finalize(exclude, t, true);
1155 }
1156
validate(const SolverParams & solver,const SolveParams & search)1157 const char* validate(const SolverParams& solver, const SolveParams& search) {
1158 const ReduceParams& reduce = search.reduce;
1159 if (solver.search == SolverParams::no_learning) {
1160 if (Heuristic_t::isLookback(solver.heuId)) return "Heuristic requires lookback strategy!";
1161 if (!search.restart.sched.disabled() && !search.restart.sched.defaulted()) return "'no-lookback': restart options disabled!";
1162 if (!reduce.cflSched.disabled() || (!reduce.growSched.disabled() && !reduce.growSched.defaulted()) || search.reduce.fReduce() != 0) return "'no-lookback': deletion options disabled!";
1163 }
1164 bool hasSched = !reduce.cflSched.disabled() || !reduce.growSched.disabled() || reduce.maxRange != UINT32_MAX;
1165 if (hasSched && reduce.fReduce() == 0.0f && !reduce.growSched.defaulted()) return "'no-deletion': deletion strategies disabled!";
1166 if (!hasSched && reduce.fReduce() != 0.0f && !reduce.growSched.defaulted()) return "'deletion': deletion strategy required!";
1167 return 0;
1168 }
1169 }}
1170