1 /* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2 
3 /*
4  *  Main authors:
5  *     Guido Tack <guido.tack@monash.edu>
6  */
7 
8 /* This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
11 
12 #pragma once
13 
14 #include <minizinc/exception.hh>
15 
16 #include <string>
17 #include <unordered_map>
18 #include <utility>
19 #include <vector>
20 
21 namespace MiniZinc {
22 class SolverConfigs;
23 
24 /**
25  * \brief Configuration data for individual MiniZinc solvers
26  */
27 class SolverConfig {
28 public:
29   /// Extra command line flags supported by solver
30   struct ExtraFlag {
31     enum FlagType { T_BOOL, T_INT, T_FLOAT, T_STRING };
32     std::string flag;
33     std::string description;
34     FlagType flagType;
35     std::vector<std::string> range;
36     std::string defaultValue;
ExtraFlagMiniZinc::SolverConfig::ExtraFlag37     ExtraFlag(std::string f, std::string d, FlagType t = T_BOOL, std::vector<std::string> r = {},
38               std::string v = "false")
39         : flag(std::move(f)),
40           description(std::move(d)),
41           flagType(t),
42           range(std::move(r)),
43           defaultValue(std::move(v)) {}
44 
45     bool validate(const std::string& v) const;
46   };
47 
48 protected:
49   /// The configuration file for this solver (or empty string for built-in solvers)
50   std::string _configFile;
51   /// The unique identifier for the solver
52   std::string _id;
53   /// Name of the solver (used for output)
54   std::string _name;
55   /// The path to the executable
56   std::string _executable;
57   /// The path to the executable, after resolving
58   std::string _executableResolved;
59   /// The path to the solver's MiniZinc library
60   std::string _mznlib;
61   /// The path to the solver's MiniZinc library, after resolving
62   std::string _mznlibResolved;
63   /// Version string
64   std::string _version;
65   /// MiniZinc library version
66   int _mznlibVersion = 1;
67   /// Short description
68   std::string _description;
69   /// Contact email
70   std::string _contact;
71   /// URL for more information
72   std::string _website;
73   /// Whether solver supports MiniZinc input
74   bool _supportsMzn = false;
75   /// Whether solver supports FlatZinc input
76   bool _supportsFzn = true;
77   /// Whether solver supports NL input
78   bool _supportsNL = false;
79   /// Whether solver requires solutions2out processing
80   bool _needsSolns2Out = true;
81   /// Whether solver is a GUI application
82   bool _isGUIApplication = false;
83   /// Whether solver needs path to minizinc executable (passed as --minizinc-exe)
84   bool _needsMznExecutable = false;
85   /// Whether solver needs path to MiniZinc standard library (passed as --stdlib-dir)
86   bool _needsStdlibDir = false;
87   /// Whether solver needs path to symbol table (paths file) (passed as --paths)
88   bool _needsPathsFile = false;
89   /// Supported standard command line flags
90   std::vector<std::string> _stdFlags;
91   /// Supported extra command line flags (flag and description)
92   std::vector<ExtraFlag> _extraFlags;
93   /// Required command line flags
94   std::vector<std::string> _requiredFlags;
95   /// Default command line flags (imported from global or user configuration)
96   std::vector<std::string> _defaultFlags;
97   /// Tags
98   std::vector<std::string> _tags;
99 
100 public:
101   /// Load solver configuration from \a filename
102   static SolverConfig load(const std::string& filename);
103   /// Default constructor
SolverConfig()104   SolverConfig() {}
105   /// Constructor
SolverConfig(std::string id,std::string version)106   SolverConfig(std::string id, std::string version)
107       : _id(std::move(id)), _version(std::move(version)) {}
108   /// Return identifier
id() const109   std::string id() const { return _id; }
110   /// Return version string
version() const111   std::string version() const { return _version; }
112 
113   /// Return configuration file name
configFile() const114   std::string configFile() const { return _configFile; }
115   /// Set configuration file name
configFile(const std::string & s)116   void configFile(const std::string& s) { _configFile = s; }
117 
118   /// Return name
name() const119   std::string name() const { return _name; }
120   // Set name
name(const std::string & s)121   void name(const std::string& s) { _name = s; }
122 
123   /// Return executable path
executable() const124   std::string executable() const { return _executable; }
125   /// Set executable path
executable(const std::string & s)126   void executable(const std::string& s) { _executable = s; }
127 
128   /// Return resolved executable path
executableResolved() const129   std::string executableResolved() const { return _executableResolved; }
130 
131   /// Return MiniZinc library path
mznlib() const132   std::string mznlib() const { return _mznlib; }
133   /// Set MiniZinc library path
mznlib(const std::string & s)134   void mznlib(const std::string& s) { _mznlib = s; }
135 
136   /// Return resolved MiniZinc library path
mznlibResolved() const137   std::string mznlibResolved() const { return _mznlibResolved; }
138 
139   /// Return required MiniZinc library version
mznlibVersion() const140   int mznlibVersion() const { return _mznlibVersion; }
141   /// Set required MiniZinc library version
mznlibVersion(int i)142   void mznlibVersion(int i) { _mznlibVersion = i; }
143 
144   /// Whether solver supports MiniZinc input
supportsMzn() const145   bool supportsMzn() const { return _supportsMzn; }
146   /// Set whether solver supports MiniZinc input
supportsMzn(bool b)147   void supportsMzn(bool b) { _supportsMzn = b; }
148 
149   /// Whether solver supports FlatZinc input
supportsFzn() const150   bool supportsFzn() const { return _supportsFzn; }
151   /// Set whether solver supports FlatZinc input
supportsFzn(bool b)152   void supportsFzn(bool b) { _supportsFzn = b; }
153 
154   /// Whether solver supports NL input
supportsNL() const155   bool supportsNL() const { return _supportsNL; }
156   /// Set whether solver supports NL input
supportsNL(bool b)157   void supportsNL(bool b) { _supportsNL = b; }
158 
159   /// Whether solver requires solutions2out processing
needsSolns2Out() const160   bool needsSolns2Out() const { return _needsSolns2Out; }
161   /// Set whether solver requires solutions2out processing
needsSolns2Out(bool b)162   void needsSolns2Out(bool b) { _needsSolns2Out = b; }
163 
164   /// Whether solver is a GUI application
isGUIApplication() const165   bool isGUIApplication() const { return _isGUIApplication; }
166   /// Set whether solver is a GUI application
isGUIApplication(bool b)167   void isGUIApplication(bool b) { _isGUIApplication = b; }
168 
169   /// Whether solver needs path to minizinc executable (passed as --minizinc-exe)
needsMznExecutable() const170   bool needsMznExecutable() const { return _needsMznExecutable; }
171   /// Set whether solver needs path to minizinc executable
needsMznExecutable(bool b)172   void needsMznExecutable(bool b) { _needsMznExecutable = b; }
173 
174   /// Whether solver needs path to MiniZinc standard library (passed as --stdlib-dir)
needsStdlibDir() const175   bool needsStdlibDir() const { return _needsStdlibDir; }
176   /// Set whether solver needs path to MiniZinc standard library
needsStdlibDir(bool b)177   void needsStdlibDir(bool b) { _needsStdlibDir = b; }
178 
179   /// Whether solver needs path to symbol table (paths file) (passed as --paths)
needsPathsFile() const180   bool needsPathsFile() const { return _needsPathsFile; }
181   /// Set whether solver needs path to symbol table (paths file)
needsPathsFile(bool b)182   void needsPathsFile(bool b) { _needsPathsFile = b; }
183 
184   /// Return short description
description() const185   std::string description() const { return _description; }
186   /// Set short description
description(const std::string & s)187   void description(const std::string& s) { _description = s; }
188 
189   /// Return contact email
contact() const190   std::string contact() const { return _contact; }
191   /// Set contact email
contact(const std::string & s)192   void contact(const std::string& s) { _contact = s; }
193 
194   /// Return web site URL
website() const195   std::string website() const { return _website; }
196   /// Set web site URL
website(const std::string & s)197   void website(const std::string& s) { _website = s; }
198 
199   /// Return supported standard command line flags
stdFlags() const200   const std::vector<std::string>& stdFlags() const { return _stdFlags; }
201   /// Set supported standard command line flags
stdFlags(const std::vector<std::string> & f)202   void stdFlags(const std::vector<std::string>& f) { _stdFlags = f; }
203 
204   /// Return supported extra command line flags
extraFlags() const205   const std::vector<ExtraFlag>& extraFlags() const { return _extraFlags; }
206   /// Set supported extra command line flags
extraFlags(const std::vector<ExtraFlag> & f)207   void extraFlags(const std::vector<ExtraFlag>& f) { _extraFlags = f; }
208 
209   /// Return supported required command line flags
requiredFlags() const210   const std::vector<std::string>& requiredFlags() const { return _requiredFlags; }
211   /// Set supported required command line flags
requiredFlags(const std::vector<std::string> & f)212   void requiredFlags(const std::vector<std::string>& f) { _requiredFlags = f; }
213 
214   /// Return default command line flags
defaultFlags() const215   const std::vector<std::string>& defaultFlags() const { return _defaultFlags; }
216   /// Set default command line flags
defaultFlags(const std::vector<std::string> & f)217   void defaultFlags(const std::vector<std::string>& f) { _defaultFlags = f; }
218 
219   /// Return tags
tags() const220   const std::vector<std::string>& tags() const { return _tags; }
221   /// Set tags
tags(const std::vector<std::string> & t)222   void tags(const std::vector<std::string>& t) { _tags = t; }
223 
224   /// Output as JSON
225   std::string toJSON(const SolverConfigs& configs) const;
226 
227   /// Test equality
operator ==(const SolverConfig & sc) const228   bool operator==(const SolverConfig& sc) const {
229     return _id == sc.id() && _version == sc.version();
230   }
231 };
232 
233 /// A container for solver configurations
234 class SolverConfigs {
235 protected:
236   /// The solvers
237   std::vector<SolverConfig> _solvers;
238   typedef std::unordered_map<std::string, std::vector<int> > TagMap;
239   /// Mapping tags to vectors of solvers (indexed into _solvers)
240   TagMap _tags;
241   /// The default solver
242   std::string _defaultSolver;
243   /// The MiniZinc library directory
244   std::string _mznlibDir;
245   /// The solver configurations path
246   std::vector<std::string> _solverPath;
247   typedef std::unordered_map<std::string, std::string> DefaultMap;
248   /// Mapping from tag to default solver for that tag
249   DefaultMap _tagDefault;
250   typedef std::unordered_map<std::string, std::vector<std::string> > SolverDefaultMap;
251   /// Mapping from solver id to default options for that solver
252   SolverDefaultMap _solverDefaultOptions;
253   /// Add new solver configuration \a sc
254   void addConfig(const SolverConfig& sc);
255 
256 public:
257   /** \brief Constructor loading configurations from \a solverpath
258    *
259    * Configuration files must be called config.msc and the path
260    * uses platform specific separators (: on Unix-like systems, ; on Windows).
261    */
262   SolverConfigs(std::ostream& log);
263 
264   /// Populate the solver configurations
265   void populate(std::ostream& log);
266 
267   /// Return configuration for solver \a s
268   /// The string can be a comma separated list of tags, in which case a
269   /// solver that matches all tags will be returned. The tag can also be
270   /// a solver id. A concrete version can be requested using @<version>.
271   /// Examples:
272   ///   config("gecode@6.1.0") would request a gecode solver of version 6.1.0
273   ///   config("mip,internal") would request a MIP solver that uses the internal API
274   ///   config("org.minizinc.mip.coin-bc@2.9/1.16 would request a specific version of OSICBC
275   const SolverConfig& config(const std::string& s);
276   /// Return list of all solver ids
277   std::vector<std::string> solvers() const;
278   /// Return search path for solver configs
279   std::vector<std::string> solverConfigsPath() const;
280   /// Return JSON list of all solver configurations
281   std::string solverConfigsJSON() const;
282   /// Add a built-in solver
283   static void registerBuiltinSolver(const SolverConfig& sc);
284 
285   /// Default solver
defaultSolver() const286   const std::string& defaultSolver() const { return _defaultSolver; }
287   /// Default solver for tag \a t
defaultSolver(const std::string & t) const288   const std::string& defaultSolver(const std::string& t) const {
289     static std::string noDefault;
290     auto it = _tagDefault.find(t);
291     return it == _tagDefault.end() ? noDefault : it->second;
292   }
293   /// MiniZinc library directory
mznlibDir() const294   const std::string& mznlibDir() const { return _mznlibDir; }
295   /// Default options for the solver with the given ID
296   std::vector<std::string> defaultOptions(const std::string& id);
297 };
298 
299 /// An exception thrown when encountering an error in a solver configuration
300 class ConfigException : public Exception {
301 public:
302   /// Construct with message \a msg
ConfigException(const std::string & msg)303   ConfigException(const std::string& msg) : Exception(msg) {}
304   /// Destructor
~ConfigException()305   ~ConfigException() throw() override {}
306   /// Return description
what() const307   const char* what() const throw() override { return "MiniZinc: configuration error"; }
308 };
309 }  // namespace MiniZinc
310