dlinear  0.0.1
Delta-complete SMT solver for linear programming
Loading...
Searching...
No Matches
ArgParser.cpp
1
6// IWYU pragma: no_include "argparse/argparse.hpp" // Already included in the header
7#include "ArgParser.h"
8
9#include <cstdlib>
10#include <filesystem>
11#include <iostream>
12#include <stdexcept>
13
14#ifdef DLINEAR_ENABLED_QSOPTEX
15#include "dlinear/libs/libqsopt_ex.h"
16#endif
17#ifdef DLINEAR_ENABLED_SOPLEX
18#include "dlinear/libs/libsoplex.h"
19#endif
20
21#include "dlinear/util/OptionValue.hpp"
22#include "dlinear/util/exception.h"
23#include "dlinear/util/filesystem.h"
24#include "dlinear/util/logging.h"
25#include "dlinear/version.h"
26
27namespace dlinear {
28
29#define DLINEAR_PARSE_PARAM_BOOL(parser, name, ...) \
30 do { \
31 parser.add_argument(__VA_ARGS__) \
32 .help(dlinear::Config::help_##name) \
33 .default_value(dlinear::Config::default_##name) \
34 .implicit_value(!dlinear::Config::default_##name); \
35 } while (false)
36
37#define DLINEAR_PARSE_PARAM_SCAN(parser, name, scan_char, scan_type, ...) \
38 do { \
39 parser.add_argument(__VA_ARGS__) \
40 .help(dlinear::Config::help_##name) \
41 .default_value(dlinear::Config::default_##name) \
42 .nargs(1) \
43 .scan<scan_char, scan_type>(); \
44 } while (false)
45
46#define DLINEAR_PARSE_PARAM_ENUM(parser, name, cmd_name, invalid_prompt, ...) \
47 do { \
48 parser.add_argument(cmd_name) \
49 .help(dlinear::Config::help_##name) \
50 .default_value(dlinear::Config::default_##name) \
51 .action([](const std::string &value) { \
52 __VA_ARGS__ \
53 DLINEAR_INVALID_ARGUMENT_EXPECTED(cmd_name, value, invalid_prompt); \
54 }) \
55 .nargs(1); \
56 } while (false)
57
58#define DLINEAR_PARAM_TO_CONFIG(param_name, config_name, type) \
59 do { \
60 if (parser_.is_used(param_name)) config.m_##config_name().SetFromCommandLine(parser_.get<type>(param_name)); \
61 } while (false)
62
64 : parser_{DLINEAR_PROGRAM_NAME, DLINEAR_VERSION_STRING},
65 verbosity_{Config::default_verbose_dlinear},
66#ifdef DLINEAR_ENABLED_QSOPTEX
67 qsoptex_hash_{QSopt_ex_repository_status()},
68#endif
69#ifdef DLINEAR_ENABLED_SOPLEX
70 soplex_hash_{soplex::getGitHash()} // NOLINT(whitespace/braces)
71#endif
72{
73 DLINEAR_TRACE("ArgParser::ArgParser");
74 AddOptions();
75}
76
77void ArgParser::Parse(int argc, const char **argv) {
78 try {
79 parser_.parse_args(argc, argv);
80 DLINEAR_LOG_INIT_VERBOSITY((parser_.get<bool>("silent") ? 0 : verbosity_));
82 DLINEAR_TRACE("ArgParser::parse: parsed args");
83 } catch (const std::runtime_error &err) {
84 std::cerr << err.what() << "\n" << parser_ << std::endl;
85 std::exit(EXIT_FAILURE);
86 } catch (const std::invalid_argument &err) {
87 std::cerr << err.what() << "\n\n" << parser_.usage() << std::endl;
88 std::exit(EXIT_FAILURE);
89 }
90}
91
93 DLINEAR_TRACE("ArgParser::AddOptions: adding options");
94 parser_.add_description(prompt());
95 parser_.add_argument("file").help("input file").default_value("");
96 parser_.add_argument("--onnx-file").help("ONNX file name").default_value("").nargs(1);
97
98 DLINEAR_PARSE_PARAM_BOOL(parser_, csv, "--csv");
99 DLINEAR_PARSE_PARAM_BOOL(parser_, continuous_output, "--continuous-output");
100 DLINEAR_PARSE_PARAM_BOOL(parser_, complete, "-c", "--complete");
101 DLINEAR_PARSE_PARAM_BOOL(parser_, debug_parsing, "--debug-parsing");
102 DLINEAR_PARSE_PARAM_BOOL(parser_, debug_scanning, "--debug-scanning");
103 DLINEAR_PARSE_PARAM_BOOL(parser_, disable_expansion, "--disable-expansion");
104 DLINEAR_PARSE_PARAM_BOOL(parser_, enforce_check_sat, "--enforce-check-sat");
105 DLINEAR_PARSE_PARAM_BOOL(parser_, optimize, "-o", "--optimize");
106 DLINEAR_PARSE_PARAM_BOOL(parser_, produce_models, "-m", "--produce-models");
107 DLINEAR_PARSE_PARAM_BOOL(parser_, skip_check_sat, "--skip-check-sat");
108 DLINEAR_PARSE_PARAM_BOOL(parser_, silent, "-s", "--silent");
109 DLINEAR_PARSE_PARAM_BOOL(parser_, with_timings, "-t", "--timings");
110 DLINEAR_PARSE_PARAM_BOOL(parser_, read_from_stdin, "--in");
111 DLINEAR_PARSE_PARAM_BOOL(parser_, verify, "--verify");
112
113 // DLINEAR_PARSE_PARAM_SCAN(parser_, number_of_jobs, 'i', unsigned int, "-j", "--jobs");
114 DLINEAR_PARSE_PARAM_SCAN(parser_, precision, 'g', double, "-p", "--precision");
115 DLINEAR_PARSE_PARAM_SCAN(parser_, random_seed, 'i', unsigned int, "-r", "--random-seed");
116 DLINEAR_PARSE_PARAM_SCAN(parser_, simplex_sat_phase, 'i', int, "--simplex-sat-phase");
117 DLINEAR_PARSE_PARAM_SCAN(parser_, verbose_simplex, 'i', int, "--verbose-simplex");
118
119 parser_.add_argument("-V", "--verbose")
120 .help("increase verbosity level. Can be used multiple times. Maximum verbosity level is 5 and default is 2")
121 .action([this](const auto &) {
122 if (verbosity_ < 5) ++verbosity_;
123 })
124 .append()
125 .nargs(0);
126 parser_.add_argument("-q", "--quiet")
127 .help("decrease verbosity level. Can be used multiple times. Minimum verbosity level is 0 and default is 2")
128 .action([this](const auto &) {
129 if (verbosity_ > 0) --verbosity_;
130 })
131 .append()
132 .nargs(0);
133
134 DLINEAR_PARSE_PARAM_ENUM(
135 parser_, lp_mode, "--lp-mode",
136 "[ auto | pure-precision-boosting | pure-iterative-refinement | hybrid ] or [ 1 | 2 | 3 | 4 ]",
137 if (value == "auto" || value == "1") return Config::LPMode::AUTO;
138 if (value == "pure-precision-boosting" || value == "2") return Config::LPMode::PURE_PRECISION_BOOSTING;
139 if (value == "pure-iterative-refinement" || value == "3") return Config::LPMode::PURE_ITERATIVE_REFINEMENT;
140 if (value == "hybrid" || value == "4") return Config::LPMode::HYBRID;);
141 DLINEAR_PARSE_PARAM_ENUM(parser_, format, "--format", "[ auto | smt2 | mps | vnnlib ] or [ 1 | 2 | 3 | 4 ]",
142 if (value == "auto" || value == "1") return Config::Format::AUTO;
143 if (value == "smt2" || value == "2") return Config::Format::SMT2;
144 if (value == "mps" || value == "3") return Config::Format::MPS;
145 if (value == "vnnlib" || value == "4") return Config::Format::VNNLIB;);
146 DLINEAR_PARSE_PARAM_ENUM(parser_, lp_solver, "--lp-solver", "[ soplex | qsoptex ] or [ 1 | 2 ]",
147 if (value == "soplex" || value == "1") return Config::LPSolver::SOPLEX;
148 if (value == "qsoptex" || value == "2") return Config::LPSolver::QSOPTEX;);
149 DLINEAR_PARSE_PARAM_ENUM(parser_, sat_solver, "--sat-solver", "[ cadical | picosat ] or [ 1 | 2 ]",
150 if (value == "cadical" || value == "1") return Config::SatSolver::CADICAL;
151 if (value == "picosat" || value == "2") return Config::SatSolver::PICOSAT;);
152 DLINEAR_PARSE_PARAM_ENUM(parser_, sat_default_phase, "--sat-default-phase",
153 "[ false | true | jeroslow-wang | random ] or [ 1 | 2 | 3 | 4 ]",
154 if (value == "false" || value == "1") return Config::SatDefaultPhase::False;
155 if (value == "true" || value == "2") return Config::SatDefaultPhase::True;
156 if (value == "jeroslow-wang" || value == "3") return Config::SatDefaultPhase::JeroslowWang;
157 if (value == "random" || value == "4") return Config::SatDefaultPhase::RandomInitialPhase;);
158 DLINEAR_PARSE_PARAM_ENUM(
159 parser_, bound_propagation_type, "--bound-propagation-type",
160 "[ auto | eq-binomial | eq-polynomial | bound-polynomial ] or [ 1 | 2 | 3 | 4 ]",
161 if (value == "auto" || value == "1") return Config::BoundPropagationType::AUTO;
162 if (value == "eq-binomial" || value == "2") return Config::BoundPropagationType::EQ_BINOMIAL;
163 if (value == "eq-polynomial" || value == "3") return Config::BoundPropagationType::EQ_POLYNOMIAL;
164 if (value == "bound-polynomial" || value == "4") return Config::BoundPropagationType::BOUND_POLYNOMIAL;);
165 DLINEAR_PARSE_PARAM_ENUM(
166 parser_, bound_propagation_frequency, "--bound-propagation-frequency",
167 "[ auto | never | on-fixed | on-iteration | always ] or [ 1 | 2 | 3 | 4 | 5 ]",
168 if (value == "auto" || value == "1") return Config::PreprocessingRunningFrequency::AUTO;
169 if (value == "never" || value == "2") return Config::PreprocessingRunningFrequency::NEVER;
170 if (value == "on-fixed" || value == "3") return Config::PreprocessingRunningFrequency::ON_FIXED;
171 if (value == "on-iteration" || value == "4") return Config::PreprocessingRunningFrequency::ON_ITERATION;
172 if (value == "always" || value == "5") return Config::PreprocessingRunningFrequency::ALWAYS;);
173 DLINEAR_PARSE_PARAM_ENUM(
174 parser_, bound_implication_frequency, "--bound-implication-frequency",
175 "[ auto | never | always ] or [ 1 | 2 | 3 ]",
176 if (value == "auto" || value == "1") return Config::PreprocessingRunningFrequency::AUTO;
177 if (value == "never" || value == "2") return Config::PreprocessingRunningFrequency::NEVER;
178 if (value == "always" || value == "3") return Config::PreprocessingRunningFrequency::ALWAYS;);
179
180 DLINEAR_TRACE("ArgParser::ArgParser: added all arguments");
181}
182
183std::ostream &operator<<(std::ostream &os, const ArgParser &parser) { return os << parser.parser_ << std::endl; }
184
186 DLINEAR_TRACE("ArgParser::ToConfig: converting to Config");
187 Config config{};
188
189 // Add all the options to the config in alphabetical order
190 if (parser_.is_used("complete")) {
191 config.m_complete().SetFromCommandLine(parser_.get<bool>("complete"));
192 config.m_precision().SetFromCommandLine(0.0);
193 }
194 DLINEAR_PARAM_TO_CONFIG("bound-implication-frequency", bound_implication_frequency,
196 DLINEAR_PARAM_TO_CONFIG("bound-propagation-frequency", bound_propagation_frequency,
198 DLINEAR_PARAM_TO_CONFIG("csv", csv, bool);
199 DLINEAR_PARAM_TO_CONFIG("continuous-output", continuous_output, bool);
200 DLINEAR_PARAM_TO_CONFIG("debug-parsing", debug_parsing, bool);
201 DLINEAR_PARAM_TO_CONFIG("debug-scanning", debug_scanning, bool);
202 DLINEAR_PARAM_TO_CONFIG("disable-expansion", disable_expansion, bool);
203 DLINEAR_PARAM_TO_CONFIG("bound-propagation-type", bound_propagation_type, Config::BoundPropagationType);
204 DLINEAR_PARAM_TO_CONFIG("enforce-check-sat", enforce_check_sat, bool);
205 config.m_filename().SetFromCommandLine(parser_.is_used("file") ? parser_.get<std::string>("file") : "");
206 DLINEAR_PARAM_TO_CONFIG("format", format, Config::Format);
207 DLINEAR_PARAM_TO_CONFIG("lp-mode", lp_mode, Config::LPMode);
208 DLINEAR_PARAM_TO_CONFIG("lp-solver", lp_solver, Config::LPSolver);
209 // DLINEAR_PARAM_TO_CONFIG("jobs", number_of_jobs, unsigned int);
210 DLINEAR_PARAM_TO_CONFIG("onnx-file", onnx_file, std::string);
211 DLINEAR_PARAM_TO_CONFIG("optimize", optimize, bool);
212 DLINEAR_PARAM_TO_CONFIG("precision", precision, double);
213 DLINEAR_PARAM_TO_CONFIG("produce-models", produce_models, bool);
214 DLINEAR_PARAM_TO_CONFIG("random-seed", random_seed, unsigned int);
215 DLINEAR_PARAM_TO_CONFIG("in", read_from_stdin, bool);
216 DLINEAR_PARAM_TO_CONFIG("sat-default-phase", sat_default_phase, Config::SatDefaultPhase);
217 DLINEAR_PARAM_TO_CONFIG("sat-solver", sat_solver, Config::SatSolver);
218 DLINEAR_PARAM_TO_CONFIG("silent", silent, bool);
219 DLINEAR_PARAM_TO_CONFIG("simplex-sat-phase", simplex_sat_phase, int);
220 DLINEAR_PARAM_TO_CONFIG("skip-check-sat", skip_check_sat, bool);
221 config.m_verbose_dlinear().SetFromCommandLine(verbosity_);
222 DLINEAR_PARAM_TO_CONFIG("verbose-simplex", verbose_simplex, int);
223 DLINEAR_PARAM_TO_CONFIG("verify", verify, bool);
224 DLINEAR_PARAM_TO_CONFIG("timings", with_timings, bool);
225
226 DLINEAR_TRACE_FMT("ArgParser::ToConfig: {}", config);
227 return config;
228}
229
231 DLINEAR_TRACE("ArgParser::ValidateOptions: validating options");
232 if (parser_.is_used("in") && parser_.is_used("file"))
233 DLINEAR_INVALID_ARGUMENT("--in", "--in and file are mutually exclusive");
234 if (!parser_.is_used("in") && !parser_.is_used("file"))
235 DLINEAR_INVALID_ARGUMENT("file", "must be specified unless --in is used");
236 if (parser_.is_used("in") && (parser_.get<Config::Format>("format") == Config::Format::AUTO))
237 DLINEAR_INVALID_ARGUMENT("--in", "a format must be specified with --format");
238 // Check file extension if a file is provided
239 if (parser_.is_used("file")) {
240 const Config::Format format = parser_.get<Config::Format>("format");
241 const std::string extension{GetExtension(parser_.get<std::string>("file"))};
242 if (format == Config::Format::AUTO && extension != "smt2" && extension != "mps" && extension != "vnnlib") {
243 DLINEAR_INVALID_ARGUMENT("file", "file must be .smt2, .mps or .vnnlib if --format is auto");
244 }
245 if (format == Config::Format::VNNLIB || (format == Config::Format::AUTO && extension == "vnnlib")) {
246 if (!parser_.is_used("onnx-file"))
247 DLINEAR_INVALID_ARGUMENT("--onnx-file", "must be provided with --format vnnlib");
248 if (!std::filesystem::is_regular_file(parser_.get<std::string>("onnx-file")))
249 DLINEAR_INVALID_ARGUMENT("--onnx-file", "cannot find file or the file is not a regular file");
250 }
251 }
252 // Check if the file exists
253 if (!parser_.is_used("in") && !std::filesystem::is_regular_file(parser_.get<std::string>("file")))
254 DLINEAR_INVALID_ARGUMENT("file", "cannot find file or the file is not a regular file");
255 if (parser_.is_used("precision") && parser_.is_used("complete"))
256 DLINEAR_INVALID_ARGUMENT("--complete", "--complete and --precision are mutually exclusive");
257 if (parser_.get<double>("precision") < 0) DLINEAR_INVALID_ARGUMENT("--precision", "cannot be negative");
258 if (parser_.get<bool>("skip-check-sat") && parser_.get<bool>("produce-models"))
259 DLINEAR_INVALID_ARGUMENT("--produce-models", "--produce-models and --skip-check-sat are mutually exclusive");
260 if (parser_.is_used("verbose") && parser_.is_used("silent"))
261 DLINEAR_INVALID_ARGUMENT("--verbose", "verbosity is forcefully set to 0 if --silent is provided");
262 if (parser_.is_used("quiet") && parser_.is_used("silent"))
263 DLINEAR_INVALID_ARGUMENT("--quiet", "verbosity is already set to 0 if --silent is provided");
264 if (parser_.get<Config::LPSolver>("lp-solver") == Config::LPSolver::QSOPTEX)
265 if (parser_.get<Config::LPMode>("lp-mode") != Config::LPMode::AUTO &&
267 DLINEAR_INVALID_ARGUMENT("--lp-solver", "QSopt_ex only supports 'auto' and 'pure-precision-boosting' modes");
268 if (parser_.is_used("enforce-check-sat") && parser_.is_used("skip-check-sat"))
269 DLINEAR_INVALID_ARGUMENT("--enforce-check-sat", "--enforce-check-sat and --skip-check-sat are mutually exclusive");
270}
271
272std::string ArgParser::version() { return DLINEAR_VERSION_STRING; }
273
274std::string ArgParser::repository_status() { return DLINEAR_VERSION_REPOSTAT; }
275
276std::string ArgParser::prompt() const {
277#ifndef NDEBUG
278 const std::string build_type{"Debug"};
279#else
280 const std::string build_type{"Release"};
281#endif
282 std::string repo_stat = repository_status();
283 if (!repo_stat.empty()) {
284 repo_stat = " (repository: " + repo_stat + ")";
285 }
286
287 std::string vstr = fmt::format("{} (v{}): delta-complete SMT solver ({} Build) {}", DLINEAR_PROGRAM_NAME, version(),
288 build_type, repo_stat);
289 if (!qsoptex_hash_.empty()) vstr += fmt::format(" (qsopt-ex: {})", qsoptex_hash_);
290 if (!soplex_hash_.empty()) vstr += fmt::format(" (soplex: {})", soplex_hash_);
291 return vstr;
292}
293
294} // namespace dlinear
Used to parse command line arguments and produce a corresponding Config object to be used throughout ...
Definition ArgParser.h:27
const std::string soplex_hash_
The hash of the Soplex library. Used in the prompt.
Definition ArgParser.h:83
void Parse(int argc, const char **argv)
Parse the command line arguments.
Definition ArgParser.cpp:77
argparse::ArgumentParser parser_
The parser object.
Definition ArgParser.h:80
void AddOptions()
Add all the options, positional arguments and flags to the parser.
Definition ArgParser.cpp:92
int verbosity_
Verbosity level of the program.
Definition ArgParser.h:81
void ValidateOptions()
Validate the options, ensuring the correctness of the parameters and the consistency of the options.
static std::string version()
Get read-only access to the version of the program.
const std::string qsoptex_hash_
The hash of the QSoptEx library. Used in the prompt.
Definition ArgParser.h:82
ArgParser()
Construct a new argparser object.
Definition ArgParser.cpp:63
std::string prompt() const
Get read-only access to the printable console prompt of the program.
Config ToConfig() const
Convert the parser to a Config.
static std::string repository_status()
Get read-only access to the hash status of the repository.
Simple dataclass used to store the configuration of the program.
Definition Config.h:38
OptionValue< bool > & m_complete()
Get read-only access to the complete parameter of the configuration.
Definition Config.h:164
SatSolver
Underlying SAT solver used by the abstract SAT solver.
Definition Config.h:46
@ CADICAL
Cadical Solver. Default option.
@ PICOSAT
Picosat Solver.
LPMode
LP mode used by the LP solver.
Definition Config.h:65
@ HYBRID
Use both modes, if available.
@ PURE_PRECISION_BOOSTING
Use the precision boosting mode, if available.
@ AUTO
Let the LP solver choose the mode. Default option.
@ PURE_ITERATIVE_REFINEMENT
Use the iterative refinement mode, if available.
Format
Format of the input file.
Definition Config.h:58
@ VNNLIB
VNNLIB format.
@ AUTO
Automatically detect the input format based on the file extension. Default option.
BoundPropagationType
Types of bound propagation supported by the bound propagator.
Definition Config.h:72
@ EQ_POLYNOMIAL
Only propagate bound in theory formulae in the form .
@ EQ_BINOMIAL
Only propagate bounds in theory formulas in the form .
@ BOUND_POLYNOMIAL
Propagate all possible constraints.
@ AUTO
Automatically select the best configuration based on expected performance. Default option.
LPSolver
Underlying LP solver used by the theory solver.
Definition Config.h:41
@ QSOPTEX
Qsoptex Solver.
@ SOPLEX
Soplex Solver. Default option.
PreprocessingRunningFrequency
Frequency at which the preprocessors will run.
Definition Config.h:79
@ ON_ITERATION
Run this preprocess only at every iteration.
@ NEVER
Never run this preprocess, effectively disabling it.
@ ON_FIXED
Run this preprocess only once, on fixed literals, before all iterations.
@ AUTO
Automatically select the best configuration based on expected performance. Default option.
@ ALWAYS
Run this preprocess at every chance it gets. Usually combines ON_FIXED and ON_ITERATION.
SatDefaultPhase
Phase for the SAT solver.
Definition Config.h:51
@ RandomInitialPhase
Randomly assign a value to non fixed decision literals.
@ True
Assign true to non fixed decision literals.
@ False
Assign false to non fixed decision literals.
void SetFromCommandLine(const T &value)
Sets the value to value which is given by a command-line argument.
Global namespace for the dlinear library.
std::string GetExtension(const std::string &name)
Get the extension of the file.