1 /* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include <minizinc/solvers/nl/nl_solreader.hh>
8
9 using namespace std;
10
11 namespace MiniZinc {
12
13 // *** *** *** NLSol *** *** ***
14
15 // Parse a string into a NLSol object
parseSolution(istream & in)16 NLSol NLSol::parseSolution(istream& in) {
17 string buffer;
18 string msg;
19 vector<double> vec;
20 NL_Solver_Status st;
21
22 try {
23 // Read the message
24 while (getline(in, buffer) && !buffer.empty()) {
25 msg += buffer + '\n';
26 }
27
28 if (in.bad()) {
29 return NLSol("Error reading the solver message", NL_Solver_Status::PARSE_ERROR, {});
30 }
31
32 // Check if we have 'Options', and skip them
33 if (getline(in, buffer)) {
34 if (buffer == "Options") {
35 if (getline(in, buffer)) {
36 int nb_options = stoi(buffer);
37 while (nb_options > 0) {
38 getline(in, buffer);
39 nb_options--;
40 }
41 }
42 }
43 }
44
45 if (in.bad()) {
46 return NLSol("Error reading the solver Options", NL_Solver_Status::PARSE_ERROR, {});
47 }
48
49 // Check the dual: we ignore the first, read the second. If non zero, some lines are to be
50 // skipped
51 (getline(in, buffer) && getline(in, buffer));
52 if (in.bad()) {
53 return NLSol("Error reading the number of dual", NL_Solver_Status::PARSE_ERROR, {});
54 }
55 int nb_duals = stoi(buffer);
56
57 // Check the primal: we ignore the first, read the second
58 getline(in, buffer) && getline(in, buffer);
59 if (in.bad()) {
60 return NLSol("Error reading the number of primal", NL_Solver_Status::PARSE_ERROR, {});
61 }
62 int nb_vars = stoi(buffer);
63
64 // Skip the duals
65 while (nb_duals > 0 && getline(in, buffer)) {
66 nb_duals--;
67 }
68
69 if (in.bad()) {
70 return NLSol("Error reading the dual values", NL_Solver_Status::PARSE_ERROR, {});
71 }
72
73 // Read the vars
74 while (nb_vars > 0 && getline(in, buffer)) {
75 double d = stod(buffer);
76 vec.push_back(d);
77 nb_vars--;
78 }
79
80 if (in.bad()) {
81 return NLSol("Error reading the primal values", NL_Solver_Status::PARSE_ERROR, {});
82 }
83
84 // Reading status code
85 // objno 0 EXIT
86 // ........
87 // 8 char
88 getline(in, buffer);
89 string sub = buffer.substr(8, buffer.length() - 8);
90 int resultCode = stoi(sub);
91 st = NL_Solver_Status::UNKNOWN;
92 // Not this case, this one is our own: case -2: st = NL_Solver_Status::PARSE_ERROR;
93 if (resultCode == -1) {
94 st = NL_Solver_Status::UNKNOWN;
95 } else if (resultCode >= 0 && resultCode < 100) {
96 st = NL_Solver_Status::SOLVED;
97 } else if (resultCode >= 100 && resultCode < 200) {
98 st = NL_Solver_Status::UNCERTAIN;
99 } else if (resultCode >= 200 && resultCode < 300) {
100 st = NL_Solver_Status::INFEASIBLE;
101 } else if (resultCode >= 300 && resultCode < 400) {
102 st = NL_Solver_Status::UNBOUNDED;
103 } else if (resultCode >= 400 && resultCode < 500) {
104 st = NL_Solver_Status::LIMIT;
105 } else if (resultCode >= 500 && resultCode < 600) {
106 st = NL_Solver_Status::FAILURE;
107 } else if (resultCode == 600) {
108 st = NL_Solver_Status::INTERRUPTED;
109 }
110
111 } catch (...) {
112 return NLSol("Parsing error (probably a bad number)", NL_Solver_Status::PARSE_ERROR, vec);
113 }
114
115 return NLSol(msg, st, vec);
116 }
117
118 // *** *** *** NLSolns2Out *** *** ***
119
120 /** Our "feedrawdatachunk" directly gets the solver's output, which is not the result.
121 * The result is written in the .sol file.
122 * Get the solver output and add a comment % in front of the lines
123 * We may be in a middle of a line!
124 */
feedRawDataChunk(const char * data)125 bool NLSolns2Out::feedRawDataChunk(const char* data) {
126 if (data != nullptr) {
127 std::stringstream ss(data);
128 string to;
129
130 while (getline(ss, to)) {
131 if (ss.eof()) {
132 if (_inLine) { // Must complete a line, and the line is not over yet
133 getLog() << to << endl;
134 } else { // Start an incomple line
135 getLog() << "% " << to;
136 _inLine = true;
137 }
138 } else {
139 if (_inLine) { // Must complete a line, and the line is over.
140 getLog() << to << endl;
141 _inLine = false;
142 } else { // Full line
143 getLog() << "% " << to << endl;
144 }
145 }
146 }
147 }
148 return true;
149 }
150
parseSolution(const string & filename)151 void NLSolns2Out::parseSolution(const string& filename) {
152 ifstream f(FILE_PATH(filename));
153 NLSol sol = NLSol::parseSolution(f);
154
155 switch (sol.status) {
156 case NL_Solver_Status::PARSE_ERROR: {
157 DEBUG_MSG("NL_Solver_Status: PARSE ERROR" << endl);
158 _out->feedRawDataChunk(_out->opt.errorMsgDef);
159 break;
160 }
161
162 case NL_Solver_Status::UNKNOWN: {
163 DEBUG_MSG("NL_Solver_Status: UNKNOWN" << endl);
164 _out->feedRawDataChunk(_out->opt.unknownMsgDef);
165 break;
166 }
167
168 case NL_Solver_Status::SOLVED: {
169 DEBUG_MSG("NL_Solver_Status: SOLVED" << endl);
170
171 stringstream sb;
172 // sb << std::hexfloat; // Use hexadecimal format for FP
173 sb << std::showpoint; // Always shows the decimal point, so we have e.g. '256.0' when 256 is
174 // the answer for a fp value.
175 sb.precision(numeric_limits<double>::digits10 + 2);
176
177 for (int i = 0; i < _nlFile.variables.size(); ++i) {
178 string n = _nlFile.vnames[i];
179 NLVar v = _nlFile.variables[n];
180 if (v.toReport) {
181 sb << v.name << " = ";
182 if (v.isInteger) {
183 long value = sol.values[i];
184 sb << value;
185 } else {
186 double value = sol.values[i];
187 sb << value;
188 }
189 sb << ";\n";
190 }
191 }
192
193 // Output the arrays
194 for (auto& a : _nlFile.outputArrays) {
195 sb << a.name << " = array" << a.dimensions.size() << "d( ";
196 for (const string& s : a.dimensions) {
197 sb << s << ", ";
198 }
199 sb << "[";
200 for (int j = 0; j < a.items.size(); ++j) {
201 const NLArray::Item& item = a.items.at(j);
202
203 // Case of the literals
204 if (item.variable.empty()) {
205 if (a.isInteger) {
206 sb << static_cast<long long int>(item.value);
207 } else {
208 sb << item.value;
209 }
210 } else {
211 int index = _nlFile.variableIndexes.at(item.variable);
212 if (a.isInteger) {
213 long value = sol.values[index];
214 sb << value;
215 } else {
216 double value = sol.values[index];
217 sb << value;
218 }
219 }
220
221 if (j < a.items.size() - 1) {
222 sb << ", ";
223 }
224 }
225
226 sb << "]);\n";
227 }
228
229 string s = sb.str();
230 _out->feedRawDataChunk(s.c_str());
231 _out->feedRawDataChunk(_out->opt.solutionSeparatorDef);
232 if (_nlFile.objective.isOptimisation()) {
233 _out->feedRawDataChunk("\n");
234 _out->feedRawDataChunk(_out->opt.searchCompleteMsgDef);
235 }
236
237 break;
238 }
239
240 case NL_Solver_Status::UNCERTAIN: {
241 DEBUG_MSG("NL_Solver_Status: UNCERTAIN" << endl);
242 _out->feedRawDataChunk(_out->opt.unknownMsgDef);
243 break;
244 }
245
246 case NL_Solver_Status::INFEASIBLE: {
247 DEBUG_MSG("NL_Solver_Status: INFEASIBLE" << endl);
248 _out->feedRawDataChunk(_out->opt.unsatisfiableMsgDef);
249 break;
250 }
251
252 case NL_Solver_Status::UNBOUNDED: {
253 DEBUG_MSG("NL_Solver_Status: UNBOUNDED" << endl);
254 _out->feedRawDataChunk(_out->opt.unboundedMsgDef);
255 break;
256 }
257
258 case NL_Solver_Status::LIMIT: {
259 DEBUG_MSG("NL_Solver_Status: LIMIT" << endl);
260 _out->feedRawDataChunk(_out->opt.unknownMsgDef);
261 break;
262 }
263
264 case NL_Solver_Status::INTERRUPTED: {
265 DEBUG_MSG("NL_Solver_Status: INTERRUPTED" << endl);
266 _out->feedRawDataChunk(_out->opt.unknownMsgDef);
267 break;
268 }
269
270 default:
271 should_not_happen("parseSolution: switch on status with unknown code: " << sol.status);
272 }
273
274 // "Finish" the feed
275 _out->feedRawDataChunk("\n");
276 }
277
getLog()278 ostream& NLSolns2Out::getLog() { return _verbose ? _out->getLog() : _dummyOfstream; }
279
280 } // namespace MiniZinc
281