SCIP Doxygen Documentation
 
Loading...
Searching...
No Matches
reader_nl.cpp
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the program and library */
4/* SCIP --- Solving Constraint Integer Programs */
5/* */
6/* Copyright (c) 2002-2023 Zuse Institute Berlin (ZIB) */
7/* */
8/* Licensed under the Apache License, Version 2.0 (the "License"); */
9/* you may not use this file except in compliance with the License. */
10/* You may obtain a copy of the License at */
11/* */
12/* http://www.apache.org/licenses/LICENSE-2.0 */
13/* */
14/* Unless required by applicable law or agreed to in writing, software */
15/* distributed under the License is distributed on an "AS IS" BASIS, */
16/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
17/* See the License for the specific language governing permissions and */
18/* limitations under the License. */
19/* */
20/* You should have received a copy of the Apache-2.0 license */
21/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */
22/* */
23/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24
25/**@file reader_nl.cpp
26 * @ingroup DEFPLUGINS_READER
27 * @brief AMPL .nl file reader
28 * @author Stefan Vigerske
29 *
30 * For documentation on ampl::mp, see https://ampl.github.io and https://www.zverovich.net/2014/09/19/reading-nl-files.html.
31 * For documentation on .nl files, see https://ampl.com/REFS/hooking2.pdf.
32 */
33
34/*--+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
35
36#include <string>
37#include <vector>
38#include <map>
39
40#include "scip/reader_nl.h"
41#include "scip/cons_linear.h"
42#include "scip/cons_nonlinear.h"
43#include "scip/cons_sos1.h"
44#include "scip/cons_sos2.h"
45#include "scip/expr_var.h"
46#include "scip/expr_value.h"
47#include "scip/expr_sum.h"
48#include "scip/expr_product.h"
49#include "scip/expr_pow.h"
50#include "scip/expr_log.h"
51#include "scip/expr_exp.h"
52#include "scip/expr_trig.h"
53#include "scip/expr_abs.h"
54
55// disable -Wshadow warnings for upcoming includes of AMPL/MP
56// disable -Wimplicit-fallthrough as I don't want to maintain extra comments in AMPL/MP code to suppress these
57#ifdef __GNUC__
58#pragma GCC diagnostic ignored "-Wshadow"
59#if __GNUC__ >= 7
60#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
61#endif
62#endif
63
64#include "mp/nl-reader.h"
65
66#define READER_NAME "nlreader"
67#define READER_DESC "AMPL .nl file reader"
68#define READER_EXTENSION "nl"
69
70// a variant of SCIP_CALL that throws a std::logic_error if not SCIP_OKAY
71// (using cast to long long to work around issues with old MSVC)
72#define SCIP_CALL_THROW(x) \
73 do \
74 { \
75 SCIP_RETCODE throw_retcode; \
76 if( ((throw_retcode) = (x)) != SCIP_OKAY ) \
77 throw std::logic_error("Error <" + std::to_string((long long)throw_retcode) + "> in function call"); \
78 } \
79 while( false )
80
81/*
82 * Data structures
83 */
84
85/// problem data stored in SCIP
86struct SCIP_ProbData
87{
88 char* filenamestub; /**< name of input file, without .nl extension; array is long enough to hold 5 extra chars */
89 int filenamestublen; /**< length of filenamestub string */
90
91 int amplopts[mp::MAX_AMPL_OPTIONS]; /**< AMPL options from .nl header */
92 int namplopts; /**< number of AMPL options from .nl header */
93
94 SCIP_VAR** vars; /**< variables in the order given by AMPL */
95 int nvars; /**< number of variables */
96
97 SCIP_CONS** conss; /**< constraints in the order given by AMPL */
98 int nconss; /**< number of constraints */
99
100 SCIP_Bool islp; /**< whether problem is an LP (only linear constraints, only continuous vars) */
101};
102
103/*
104 * Local methods
105 */
106
107// forward declaration
109
110/// implementation of AMPL/MPs NLHandler that constructs a SCIP problem while a .nl file is read
111class AMPLProblemHandler : public mp::NLHandler<AMPLProblemHandler, SCIP_EXPR*>
112{
113private:
114 SCIP* scip;
115 SCIP_PROBDATA* probdata;
116
117 // variable expressions corresponding to nonlinear variables
118 // created in OnHeader() and released in destructor
119 // for reuse of var-expressions in OnVariableRef()
120 std::vector<SCIP_EXPR*> varexprs;
121
122 // linear parts for nonlinear constraints
123 // first collect and then add to constraints in EndInput()
124 std::vector<std::vector<std::pair<SCIP_Real, SCIP_VAR*> > > nlconslin;
125
126 // expression that represents a nonlinear objective function
127 // used to create a corresponding constraint in EndInput(), unless NULL
128 SCIP_EXPR* objexpr;
129
130 // common expressions (defined variables from statements like "var xsqr = x^2;" in an AMPL model)
131 // they are constructed by BeginCommonExpr/EndCommonExpr below and are referenced by index in OnCommonExprRef
132 std::vector<SCIP_EXPR*> commonexprs;
133
134 // collect expressions that need to be released eventually
135 // this are all expression that are returned to the AMPL/MP code in AMPLProblemHandler::OnXyz() functions
136 // they need to be released exactly once, but after they are used in another expression or a constraint
137 // as AMPL/MP may reuse expressions (common subexpressions), we don't release an expression when it is used
138 // as a child or when constructing a constraint, but first collect them all and then release in destructor
139 // alternatively, one could encapsulate SCIP_EXPR* into a small class that handles proper reference counting
140 std::vector<SCIP_EXPR*> exprstorelease;
141
142 // SOS constraints
143 // collected while handling suffixes in SuffixHandler
144 // sosvars maps the SOS index (can be negative) to the indices of the variables in the SOS
145 // sosweights gives for each variable its weight in the SOS it appears in (if any)
146 std::map<int, std::vector<int> > sosvars;
147 std::vector<int> sosweights;
148
149 // initial solution, if any
150 SCIP_SOL* initsol;
151
152 // opened files with column/variable and row/constraint names, or NULL
153 fmt::File* colfile;
154 fmt::File* rowfile;
155
156 // get name from names strings, if possible
157 // returns whether a name has been stored
158 bool nextName(
159 const char*& namesbegin, /**< current pointer into names string, or NULL */
160 const char* namesend, /**< pointer to end of names string */
161 char* name /**< buffer to store name, should have length SCIP_MAXSTRLEN */
162 )
163 {
164 if( namesbegin == NULL )
165 return false;
166
167 // copy namesbegin into name until newline or namesend
168 // updates namesbegin
169 int nchars = 0;
170 while( namesbegin != namesend )
171 {
172 if( nchars == SCIP_MAXSTRLEN )
173 {
174 SCIPverbMessage(scip, SCIP_VERBLEVEL_FULL, NULL, "name too long when parsing names file");
175 // do no longer read names from this string (something seems awkward)
177 return false;
178 }
179 if( *namesbegin == '\n' )
180 {
181 *name = '\0';
182 ++namesbegin;
183 return true;
184 }
185 *(name++) = *(namesbegin++);
186 ++nchars;
187 }
188
189 SCIPverbMessage(scip, SCIP_VERBLEVEL_FULL, NULL, "missing newline when parsing names file");
190 return false;
191 }
192
193public:
194 /// constructor
195 ///
196 /// initializes SCIP problem and problem data
198 SCIP* scip_, ///< SCIP data structure
199 const char* filename ///< name of .nl file that is read
200 )
201 : scip(scip_),
202 probdata(NULL),
203 objexpr(NULL),
204 initsol(NULL),
205 colfile(NULL),
206 rowfile(NULL)
207 {
208 assert(scip != NULL);
209 assert(filename != NULL);
210
212
213 /* get name of input file without file extension (if any) */
214 const char* extstart = strrchr(const_cast<char*>(filename), '.');
215 if( extstart != NULL )
216 probdata->filenamestublen = extstart - filename;
217 else
218 probdata->filenamestublen = strlen(filename);
219 assert(probdata->filenamestublen > 0);
220 SCIP_CALL_THROW( SCIPallocBlockMemoryArray(scip, &probdata->filenamestub, probdata->filenamestublen + 5) );
221 memcpy(probdata->filenamestub, filename, probdata->filenamestublen);
222 probdata->filenamestub[probdata->filenamestublen] = '\0';
223
224 /* derive probname from name of input file without path and extension */
225 const char* probname = strrchr(probdata->filenamestub, '/');
226 if( probname == NULL )
227 probname = probdata->filenamestub;
228 else
229 ++probname;
230
231 // initialize empty SCIP problem
233
234 // try to open files with variable and constraint names
235 // temporarily add ".col" and ".row", respectively, to filenamestub
236 try
237 {
238 probdata->filenamestub[probdata->filenamestublen] = '.';
239 probdata->filenamestub[probdata->filenamestublen+1] = 'c';
240 probdata->filenamestub[probdata->filenamestublen+2] = 'o';
241 probdata->filenamestub[probdata->filenamestublen+3] = 'l';
242 probdata->filenamestub[probdata->filenamestublen+4] = '\0';
243 colfile = new fmt::File(probdata->filenamestub, fmt::File::RDONLY);
244
245 probdata->filenamestub[probdata->filenamestublen+1] = 'r';
246 probdata->filenamestub[probdata->filenamestublen+3] = 'w';
247 rowfile = new fmt::File(probdata->filenamestub, fmt::File::RDONLY);
248 }
249 catch( const fmt::SystemError& e )
250 {
251 // probably a file open error, probably because file not found
252 // ignore, we can make up our own names
253 }
254 probdata->filenamestub[probdata->filenamestublen] = '\0';
255 }
256
259
260 /// destructor
261 ///
262 /// only asserts that cleanup() has been called, as we cannot throw an exception or return a SCIP_RETCODE here
264 {
265 // exprs and linear constraint arrays should have been cleared up in cleanup()
266 assert(varexprs.empty());
267 assert(exprstorelease.empty());
268
269 delete colfile;
270 delete rowfile;
271 }
272
273 /// process header of .nl files
274 ///
275 /// create and add variables, allocate constraints
277 const mp::NLHeader& h ///< header data
278 )
279 {
280 char name[SCIP_MAXSTRLEN];
281 int nnlvars;
282
283 assert(probdata->vars == NULL);
284 assert(probdata->conss == NULL);
285
286 probdata->namplopts = h.num_ampl_options;
287 BMScopyMemoryArray(probdata->amplopts, h.ampl_options, h.num_ampl_options);
288
289 // read variable and constraint names from file, if available, into memory
290 // if not available, we will get varnamesbegin==NULL and consnamesbegin==NULL
291 mp::MemoryMappedFile<> mapped_colfile;
292 if( colfile != NULL )
293 mapped_colfile.map(*colfile, "colfile");
294 const char* varnamesbegin = mapped_colfile.start();
295 const char* varnamesend = mapped_colfile.start() + mapped_colfile.size();
296
297 mp::MemoryMappedFile<> mapped_rowfile;
298 if( rowfile != NULL )
299 mapped_rowfile.map(*rowfile, "rowfile");
300 const char* consnamesbegin = mapped_rowfile.start();
301 const char* consnamesend = mapped_rowfile.start() + mapped_rowfile.size();
302
303 probdata->nvars = h.num_vars;
304 SCIP_CALL_THROW( SCIPallocBlockMemoryArray(scip, &probdata->vars, probdata->nvars) );
305
306 // number of nonlinear variables
307 nnlvars = MAX(h.num_nl_vars_in_cons, h.num_nl_vars_in_objs);
308 varexprs.resize(nnlvars);
309
310 // create variables
311 // create variable expressions for nonlinear variables
312 for( int i = 0; i < h.num_vars; ++i )
313 {
314 SCIP_VARTYPE vartype;
315 // Nonlinear variables in both constraints and objective
316 if( i < h.num_nl_vars_in_both - h.num_nl_integer_vars_in_both )
317 vartype = SCIP_VARTYPE_CONTINUOUS;
318 else if( i < h.num_nl_vars_in_both )
319 vartype = SCIP_VARTYPE_INTEGER;
320 // Nonlinear variables in constraints
321 else if( i < h.num_nl_vars_in_cons - h.num_nl_integer_vars_in_cons )
322 vartype = SCIP_VARTYPE_CONTINUOUS;
323 else if( i < h.num_nl_vars_in_cons )
324 vartype = SCIP_VARTYPE_INTEGER;
325 // Nonlinear variables in objective
326 else if( i < h.num_nl_vars_in_objs - h.num_nl_integer_vars_in_objs )
327 vartype = SCIP_VARTYPE_CONTINUOUS;
328 else if( i < h.num_nl_vars_in_objs )
329 vartype = SCIP_VARTYPE_INTEGER;
330 // Linear variables
331 else if( i < h.num_vars - h.num_linear_binary_vars - h.num_linear_integer_vars )
332 vartype = SCIP_VARTYPE_CONTINUOUS;
333 else if( i < h.num_vars - h.num_linear_integer_vars )
334 vartype = SCIP_VARTYPE_BINARY;
335 else
336 vartype = SCIP_VARTYPE_INTEGER;
337
338 if( !nextName(varnamesbegin, varnamesend, name) )
339 {
340 // make up name if no names file or could not be read
341 switch( vartype )
342 {
344 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "b%d", i);
345 break;
347 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "i%d", i);
348 break;
350 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "x%d", i);
351 break;
352 // coverity[deadcode]
353 default:
354 SCIPABORT();
355 break;
356 }
357 }
358
359 SCIP_CALL_THROW( SCIPcreateVarBasic(scip, &probdata->vars[i], name,
360 vartype == SCIP_VARTYPE_BINARY ? 0.0 : -SCIPinfinity(scip),
361 vartype == SCIP_VARTYPE_BINARY ? 1.0 : SCIPinfinity(scip),
362 0.0, vartype) );
363 SCIP_CALL_THROW( SCIPaddVar(scip, probdata->vars[i]) );
364
365 if( i < nnlvars )
366 {
367 SCIP_CALL_THROW( SCIPcreateExprVar(scip, &varexprs[i], probdata->vars[i], NULL, NULL) );
368 }
369 }
370
371 // alloc some space for constraints
372 probdata->nconss = h.num_algebraic_cons;
373 SCIP_CALL_THROW( SCIPallocBlockMemoryArray(scip, &probdata->conss, probdata->nconss) );
374 nlconslin.resize(h.num_nl_cons);
375
376 // create empty nonlinear constraints
377 // use expression == 0, because nonlinear constraint don't like to be without an expression
380 for( int i = 0; i < h.num_nl_cons; ++i )
381 {
382 // make up name if no names file or could not be read
383 if( !nextName(consnamesbegin, consnamesend, name) )
384 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "nlc%d", i);
385
387 }
389
390 // create empty linear constraints
391 for( int i = h.num_nl_cons; i < h.num_algebraic_cons; ++i )
392 {
393 if( !nextName(consnamesbegin, consnamesend, name) )
394 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "lc%d", i);
396 }
397
398 if( h.num_nl_cons == 0 && h.num_integer_vars() == 0 )
399 probdata->islp = true;
400
401 // alloc space for common expressions
402 commonexprs.resize(h.num_common_exprs());
403 }
404
405 /// receive notification of a number in a nonlinear expression
407 double value ///< value
408 )
409 {
410 SCIP_EXPR* expr;
411
413
414 // remember that we have to release this expr
415 exprstorelease.push_back(expr);
416
417 return expr;
418 }
419
420 /// receive notification of a variable reference in a nonlinear expression
422 int variableIndex ///< AMPL index of variable
423 )
424 {
425 assert(variableIndex >= 0);
426 assert(variableIndex < (int)varexprs.size());
427 assert(varexprs[variableIndex] != NULL);
428
429 return varexprs[variableIndex];
430 }
431
432 /// receive notification of a unary expression
434 mp::expr::Kind kind, ///< expression operator
435 SCIP_EXPR* child ///< argument
436 )
437 {
438 SCIP_EXPR* expr;
439
440 assert(child != NULL);
441
442 switch( kind )
443 {
444 case mp::expr::MINUS:
445 {
446 SCIP_Real minusone = -1.0;
447 SCIP_CALL_THROW( SCIPcreateExprSum(scip, &expr, 1, &child, &minusone, 0.0, NULL, NULL) );
448 break;
449 }
450
451 case mp::expr::ABS:
452 SCIP_CALL_THROW( SCIPcreateExprAbs(scip, &expr, child, NULL, NULL) );
453 break;
454
455 case mp::expr::POW2:
456 SCIP_CALL_THROW( SCIPcreateExprPow(scip, &expr, child, 2.0, NULL, NULL) );
457 break;
458
459 case mp::expr::SQRT:
460 SCIP_CALL_THROW( SCIPcreateExprPow(scip, &expr, child, 0.5, NULL, NULL) );
461 break;
462
463 case mp::expr::LOG:
464 SCIP_CALL_THROW( SCIPcreateExprLog(scip, &expr, child, NULL, NULL) );
465 break;
466
467 case mp::expr::LOG10: // 1/log(10)*log(child)
468 {
470 SCIP_Real factor = 1.0/log(10.0);
474 break;
475 }
476
477 case mp::expr::EXP:
478 SCIP_CALL_THROW( SCIPcreateExprExp(scip, &expr, child, NULL, NULL) );
479 break;
480
481 case mp::expr::SIN:
482 SCIP_CALL_THROW( SCIPcreateExprSin(scip, &expr, child, NULL, NULL) );
483 break;
484
485 case mp::expr::COS:
486 SCIP_CALL_THROW( SCIPcreateExprCos(scip, &expr, child, NULL, NULL) );
487 break;
488
489 default:
490 OnUnhandled(mp::expr::str(kind));
491 return NULL;
492 }
493
494 // remember that we have to release this expr
495 exprstorelease.push_back(expr);
496
497 return expr;
498 }
499
500 /// receive notification of a binary expression
502 mp::expr::Kind kind, ///< expression operand
503 SCIP_EXPR* firstChild, ///< first argument
504 SCIP_EXPR* secondChild ///< second argument
505 )
506 {
507 SCIP_EXPR* expr;
508 SCIP_EXPR* children[2] = { firstChild, secondChild };
509
512
513 switch( kind )
514 {
515 case mp::expr::ADD:
516 SCIP_CALL_THROW( SCIPcreateExprSum(scip, &expr, 2, children, NULL, 0.0, NULL, NULL) );
517 break;
518
519 case mp::expr::SUB:
520 {
521 SCIP_Real coefs[2] = { 1.0, -1.0 };
522 SCIP_CALL_THROW( SCIPcreateExprSum(scip, &expr, 2, children, coefs, 0.0, NULL, NULL) );
523 break;
524 }
525
526 case mp::expr::MUL:
527 SCIP_CALL_THROW( SCIPcreateExprProduct(scip, &expr, 2, children, 1.0, NULL, NULL) );
528 break;
529
530 case mp::expr::DIV:
531 SCIP_CALL_THROW( SCIPcreateExprPow(scip, &children[1], secondChild, -1.0, NULL, NULL) );
532 SCIP_CALL_THROW( SCIPcreateExprProduct(scip, &expr, 2, children, 1.0, NULL, NULL) );
533 SCIP_CALL_THROW( SCIPreleaseExpr(scip, &children[1]) );
534 break;
535
536 case mp::expr::POW_CONST_BASE:
537 case mp::expr::POW_CONST_EXP:
538 case mp::expr::POW:
539 // with some .nl files, we seem to get mp::expr::POW even if base or exponent is constant,
540 // so do not rely on kind but better check expr type
542 {
544 break;
545 }
546
548 {
549 // reformulate constant^y as exp(y*log(constant)), if constant > 0.0
550 // if constant < 0, we create an expression and let cons_nonlinear figure out infeasibility somehow
552
553 SCIP_Real coef = log(SCIPgetValueExprValue(firstChild)); // log(firstChild)
554 SCIP_CALL_THROW( SCIPcreateExprSum(scip, &prod, 1, &secondChild, &coef, 0.0, NULL, NULL) ); // log(firstChild)*secondChild
555 SCIP_CALL_THROW( SCIPcreateExprExp(scip, &expr, prod, NULL, NULL) ); // expr(log(firstChild)*secondChild)
556
558 break;
559 }
560
561 {
562 // reformulate x^y as exp(y*log(x))
564
566
567 SCIP_CALL_THROW( SCIPcreateExprLog(scip, &children[0], firstChild, NULL, NULL) ); // log(firstChild)
568 SCIP_CALL_THROW( SCIPcreateExprProduct(scip, &prod, 2, children, 1.0, NULL, NULL) ); // log(firstChild)*secondChild
569 SCIP_CALL_THROW( SCIPcreateExprExp(scip, &expr, prod, NULL, NULL) ); // expr(log(firstChild)*secondChild)
570
572 SCIP_CALL_THROW( SCIPreleaseExpr(scip, &children[0]) );
573 break;
574 }
575
576 default:
577 OnUnhandled(mp::expr::str(kind));
578 return NULL;
579 }
580
581 // remember that we have to release this expr
582 exprstorelease.push_back(expr);
583
584 return expr;
585 }
586
587 /// handler to create a list of terms in a sum
588 ///
589 /// NumericArgHandler is copied around, so it keeps only a pointer (with reference counting) to actual data
591 {
592 public:
593 std::shared_ptr<std::vector<SCIP_EXPR*> > v;
594
595 /// constructor
597 int num_args ///< number of terms to expect
598 )
599 : v(new std::vector<SCIP_EXPR*>())
600 {
601 v->reserve(num_args);
602 }
603
604 /// adds term to sum
605 void AddArg(
606 SCIP_EXPR* term ///< term to add
607 )
608 {
609 v->push_back(term);
610 }
611 };
612
613 /// receive notification of the beginning of a summation
615 int num_args ///< number of terms to expect
616 )
617 {
619 return h;
620 }
621
622 /// receive notification of the end of a summation
624 NumericArgHandler handler ///< handler that handled the sum
625 )
626 {
627 SCIP_EXPR* expr;
628 SCIP_CALL_THROW( SCIPcreateExprSum(scip, &expr, (int)handler.v->size(), handler.v->data(), NULL, 0.0, NULL, NULL) );
629 // remember that we have to release this expr
630 exprstorelease.push_back(expr);
631 return expr;
632 }
633
634 /// receive notification of an objective type and the nonlinear part of an objective expression
635 void OnObj(
636 int objectiveIndex, ///< index of objective
637 mp::obj::Type type, ///< objective sense
638 SCIP_EXPR* nonlinearExpression ///< nonlinear part of objective function
639 )
640 {
641 if( objectiveIndex >= 1 )
642 OnUnhandled("multiple objective functions");
643
645
646 assert(objexpr == NULL);
647
649 {
650 // handle objective constant by adding a fixed variable for it
653
657 }
658 else
659 {
660 objexpr = nonlinearExpression;
661 }
662 }
663
664 /// receive notification of an algebraic constraint expression
666 int constraintIndex, ///< index of constraint
667 SCIP_EXPR* expr ///< nonlinear part of constraint
668 )
669 {
670 if( expr != NULL )
671 {
672 SCIP_CALL_THROW( SCIPchgExprNonlinear(scip, probdata->conss[constraintIndex], expr) );
673 }
674 }
675
676 /// handles linear part of a common expression
677 /// sets up a sum expression, if the linear part isn't empty
679 {
680 private:
681 AMPLProblemHandler& amplph;
682 SCIP_EXPR* commonexpr;
683
684 public:
685 /// constructor
687 AMPLProblemHandler& amplph_, ///< problem handler
688 int index, ///< index of common expression
689 int num_linear_terms///< number of terms to expect
690 )
691 : amplph(amplph_),
692 commonexpr(NULL)
693 {
694 if( num_linear_terms > 0 )
695 {
696 SCIP_CALL_THROW( SCIPcreateExprSum(amplph.scip, &commonexpr, 0, NULL, NULL, 0.0, NULL, NULL) );
697 amplph.commonexprs[index] = commonexpr;
698 amplph.exprstorelease.push_back(commonexpr);
699 }
700 }
701
702 /// receives notification of a term in the linear expression
704 int var_index, ///< AMPL index of variable
705 double coef ///< variable coefficient
706 )
707 {
708 assert(commonexpr != NULL);
709
710 if( coef == 0.0 )
711 return;
712
713 if( var_index < (int)amplph.varexprs.size() )
714 {
715 SCIP_CALL_THROW( SCIPappendExprSumExpr(amplph.scip, commonexpr, amplph.varexprs[var_index], coef) );
716 }
717 else
718 {
719 // the index variable is linear (not sure this can happen here)
722 SCIP_CALL_THROW( SCIPcreateExprVar(amplph.scip, &varexpr, amplph.probdata->vars[var_index], NULL, NULL) );
723 SCIP_CALL_THROW( SCIPappendExprSumExpr(amplph.scip, commonexpr, varexpr, coef) );
724 SCIP_CALL_THROW( SCIPreleaseExpr(amplph.scip, &varexpr) );
725 }
726 }
727 };
728
729 /// receive notification of the beginning of a common expression (defined variable)
731 int index, ///< index of common expression
732 int num_linear_terms ///< number of terms to expect
733 )
734 {
735 assert(index >= 0);
736 assert(index < (int)commonexprs.size());
737
738 return LinearExprHandler(*this, index, num_linear_terms);
739 }
740
741 /// receive notification of the end of a common expression
743 int index, ///< index of common expression
744 SCIP_EXPR* expr, ///< nonlinear part of common expression
745 int /* position */ ///< argument that doesn't seem to have any purpose
746 )
747 {
748 if( commonexprs[index] != NULL )
749 {
750 // add expr, if any, to linear part
751 if( expr != NULL )
752 {
753 SCIP_CALL_THROW( SCIPappendExprSumExpr(scip, commonexprs[index], expr, 1.0) );
754 }
755 }
756 else if( expr != NULL )
757 {
758 commonexprs[index] = expr;
759 }
760 }
761
762 /// receive notification of a common expression (defined variable) reference
764 int expr_index ///< index of common expression
765 )
766 {
767 assert(expr_index >= 0);
768 assert(expr_index < (int)commonexprs.size());
769 assert(commonexprs[expr_index] != NULL);
770 return commonexprs[expr_index];
771 }
772
773 /// receive notification of variable bounds
775 int variableIndex, ///< AMPL index of variable
776 double variableLB, ///< variable lower bound
777 double variableUB ///< variable upper bound
778 )
779 {
780 assert(variableIndex >= 0);
782
783 // as far as I see, ampl::mp gives -inf, +inf for no-bounds, which is always beyond SCIPinfinity()
784 // we ignore bounds outside [-scipinfinity,scipinfinity] here
785 // for binary variables, we also ignore bounds outside [0,1]
786 if( variableLB > (SCIPvarGetType(probdata->vars[variableIndex]) == SCIP_VARTYPE_BINARY ? 0.0 : -SCIPinfinity(scip)) )
787 {
789 }
790 if( variableUB < (SCIPvarGetType(probdata->vars[variableIndex]) == SCIP_VARTYPE_BINARY ? 1.0 : SCIPinfinity(scip)) )
791 {
793 }
794 }
795
796 /// receive notification of constraint sides
798 int index, ///< AMPL index of constraint
799 double lb, ///< constraint left-hand-side
800 double ub ///< constraint right-hand-side
801 )
802 {
803 assert(index >= 0);
805
806 // nonlinear constraints are first
807 if( index < (int)nlconslin.size() )
808 {
809 if( !SCIPisInfinity(scip, -lb) )
810 {
811 SCIP_CALL_THROW( SCIPchgLhsNonlinear(scip, probdata->conss[index], lb) );
812 }
813 if( !SCIPisInfinity(scip, ub) )
814 {
815 SCIP_CALL_THROW( SCIPchgRhsNonlinear(scip, probdata->conss[index], ub) );
816 }
817 }
818 else
819 {
820 if( !SCIPisInfinity(scip, -lb) )
821 {
822 SCIP_CALL_THROW( SCIPchgLhsLinear(scip, probdata->conss[index], lb) );
823 }
824 if( !SCIPisInfinity(scip, ub) )
825 {
826 SCIP_CALL_THROW( SCIPchgRhsLinear(scip, probdata->conss[index], ub) );
827 }
828 }
829 }
830
831 /// receive notification of the initial value for a variable
833 int var_index, ///< AMPL index of variable
834 double value ///< initial primal value of variable
835 )
836 {
837 if( initsol == NULL )
838 {
839 SCIP_CALL_THROW( SCIPcreateSol(scip, &initsol, NULL) );
840 }
841
842 SCIP_CALL_THROW( SCIPsetSolVal(scip, initsol, probdata->vars[var_index], value) );
843 }
844
845 /// receives notification of the initial value for a dual variable
847 int /* con_index */, ///< AMPL index of constraint
848 double /* value */ ///< initial dual value of constraint
849 )
850 {
851 // ignore initial dual value
852 }
853
854 /// receives notification of Jacobian column sizes
856 {
857 /// use ColumnSizeHandler from upper class, which does nothing
858 return ColumnSizeHandler();
859 }
860
861 /// handling of suffices for variable and constraint flags and SOS constraints
862 ///
863 /// regarding SOS in AMPL, see https://ampl.com/faqs/how-can-i-use-the-solvers-special-ordered-sets-feature/
864 /// we pass the .ref suffix as weight to the SOS constraint handlers
865 /// for a SOS2, the weights determine the order of variables in the set
866 template<typename T> class SuffixHandler
867 {
868 private:
869 AMPLProblemHandler& amplph;
870
871 // type of suffix that is handled, or IGNORE if unsupported suffix
872 enum
873 {
874 IGNORE,
875 CONSINITIAL,
876 CONSSEPARATE,
877 CONSENFORCE,
878 CONSCHECK,
879 CONSPROPAGATE,
880 CONSDYNAMIC,
881 CONSREMOVABLE,
882 VARINITIAL,
883 VARREMOVABLE,
884 VARSOSNO,
885 VARREF,
886 } suffix;
887
888 public:
889 /// constructor
891 AMPLProblemHandler& amplph_, ///< problem handler
892 fmt::StringRef name, ///< name of suffix
893 mp::suf::Kind kind ///< whether suffix applies to var, cons, etc
894 )
895 : amplph(amplph_),
896 suffix(IGNORE)
897 {
898 switch( kind )
899 {
900 case mp::suf::Kind::CON:
901 if( strncmp(name.data(), "initial", name.size()) == 0 )
902 {
903 suffix = CONSINITIAL;
904 }
905 else if( strncmp(name.data(), "separate", name.size()) == 0 )
906 {
907 suffix = CONSSEPARATE;
908 }
909 else if( strncmp(name.data(), "enforce", name.size()) == 0 )
910 {
911 suffix = CONSENFORCE;
912 }
913 else if( strncmp(name.data(), "check", name.size()) == 0 )
914 {
915 suffix = CONSCHECK;
916 }
917 else if( strncmp(name.data(), "propagate", name.size()) == 0 )
918 {
919 suffix = CONSPROPAGATE;
920 }
921 else if( strncmp(name.data(), "dynamic", name.size()) == 0 )
922 {
923 suffix = CONSDYNAMIC;
924 }
925 else if( strncmp(name.data(), "removable", name.size()) == 0 )
926 {
927 suffix = CONSREMOVABLE;
928 }
929 else
930 {
931 SCIPverbMessage(amplph.scip, SCIP_VERBLEVEL_HIGH, NULL, "Unknown constraint suffix <%.*s>. Ignoring.\n", (int)name.size(), name.data());
932 }
933 break;
934
935 case mp::suf::Kind::VAR:
936 {
937 if( strncmp(name.data(), "initial", name.size()) == 0 )
938 {
939 suffix = VARINITIAL;
940 }
941 else if( strncmp(name.data(), "removable", name.size()) == 0 )
942 {
943 suffix = VARREMOVABLE;
944 }
945 else if( strncmp(name.data(), "sosno", name.size()) == 0 )
946 {
947 // SOS membership
948 suffix = VARSOSNO;
949 }
950 else if( strncmp(name.data(), "ref", name.size()) == 0 )
951 {
952 // SOS weights
953 suffix = VARREF;
954 amplph.sosweights.resize(amplph.probdata->nvars, 0);
955 }
956 else
957 {
958 SCIPverbMessage(amplph.scip, SCIP_VERBLEVEL_HIGH, NULL, "Unknown variable suffix <%.*s>. Ignoring.\n", (int)name.size(), name.data());
959 }
960 break;
961
962 case mp::suf::Kind::OBJ:
963 SCIPverbMessage(amplph.scip, SCIP_VERBLEVEL_HIGH, NULL, "Unknown objective suffix <%.*s>. Ignoring.\n", (int)name.size(), name.data());
964 break;
965
966 case mp::suf::Kind::PROBLEM:
967 SCIPverbMessage(amplph.scip, SCIP_VERBLEVEL_HIGH, NULL, "Unknown problem suffix <%.*s>. Ignoring.\n", (int)name.size(), name.data());
968 break;
969 }
970 }
971 }
972
974 int index, ///< index of variable, constraint, etc
975 T value ///< value of suffix
976 )
977 {
978 assert(index >= 0);
979 switch( suffix )
980 {
981 case IGNORE :
982 return;
983
984 case CONSINITIAL:
985 SCIP_CALL_THROW( SCIPsetConsInitial(amplph.scip, amplph.probdata->conss[index], value == 1) );
986 break;
987
988 case CONSSEPARATE:
989 SCIP_CALL_THROW( SCIPsetConsSeparated(amplph.scip, amplph.probdata->conss[index], value == 1) );
990 break;
991
992 case CONSENFORCE:
993 SCIP_CALL_THROW( SCIPsetConsEnforced(amplph.scip, amplph.probdata->conss[index], value == 1) );
994 break;
995
996 case CONSCHECK:
997 SCIP_CALL_THROW( SCIPsetConsChecked(amplph.scip, amplph.probdata->conss[index], value == 1) );
998 break;
999
1000 case CONSPROPAGATE:
1001 SCIP_CALL_THROW( SCIPsetConsPropagated(amplph.scip, amplph.probdata->conss[index], value == 1) );
1002 break;
1003
1004 case CONSDYNAMIC:
1005 SCIP_CALL_THROW( SCIPsetConsDynamic(amplph.scip, amplph.probdata->conss[index], value == 1) );
1006 break;
1007
1008 case CONSREMOVABLE:
1009 SCIP_CALL_THROW( SCIPsetConsRemovable(amplph.scip, amplph.probdata->conss[index], value == 1) );
1010 break;
1011
1012 case VARINITIAL:
1014 SCIP_CALL_THROW( SCIPvarSetInitial(amplph.probdata->vars[index], value == 1) );
1015 break;
1016
1017 case VARREMOVABLE:
1019 SCIP_CALL_THROW( SCIPvarSetRemovable(amplph.probdata->vars[index], value == 1) );
1020 break;
1021
1022 case VARSOSNO:
1023 // remember that variable index belongs to SOS identified by value
1024 amplph.sosvars[(int)value].push_back(index);
1025 break;
1026
1027 case VARREF:
1028 // remember that variable index has weight value
1029 amplph.sosweights[index] = (int)value;
1030 break;
1031 }
1032 }
1033 };
1034
1036 /// receive notification of an integer suffix
1038 fmt::StringRef name, ///< suffix name, not null-terminated
1039 mp::suf::Kind kind, ///< suffix kind
1040 int /*num_values*/ ///< number of values to expect
1041 )
1042 {
1043 return IntSuffixHandler(*this, name, kind);
1044 }
1045
1047 /// receive notification of a double suffix
1049 fmt::StringRef name, ///< suffix name, not null-terminated
1050 mp::suf::Kind kind, ///< suffix kind
1051 int /*num_values*/ ///< number of values to expect
1052 )
1053 {
1054 return DblSuffixHandler(*this, name, kind);
1055 }
1056
1057 /// handles receiving the linear part of an objective or constraint
1058 ///
1059 /// for objective, set the objective-coefficient of the variable
1060 /// for linear constraints, add to the constraint
1061 /// for nonlinear constraints, add to nlconslin vector; adding to constraint later
1063 {
1064 private:
1065 AMPLProblemHandler& amplph;
1066 int constraintIndex;
1067
1068 public:
1069 // constructor for constraint
1071 AMPLProblemHandler& amplph_, ///< problem handler
1072 int constraintIndex_///< constraint index
1073 )
1074 : amplph(amplph_),
1075 constraintIndex(constraintIndex_)
1076 {
1079 }
1080
1081 // constructor for linear objective
1083 AMPLProblemHandler& amplph_ ///< problem handler
1084 )
1085 : amplph(amplph_),
1086 constraintIndex(-1)
1087 { }
1088
1090 int variableIndex, ///< AMPL index of variable
1091 double coefficient ///< coefficient of variable
1092 )
1093 {
1094 assert(variableIndex >= 0);
1096
1097 if( coefficient == 0.0 )
1098 return;
1099
1100 if( constraintIndex < 0 )
1101 {
1102 SCIP_CALL_THROW( SCIPchgVarObj(amplph.scip, amplph.probdata->vars[variableIndex], coefficient) );
1103 }
1104 else if( constraintIndex < (int)amplph.nlconslin.size() )
1105 {
1106 amplph.nlconslin[constraintIndex].push_back(std::pair<SCIP_Real, SCIP_VAR*>(coefficient, amplph.probdata->vars[variableIndex]));
1107 }
1108 else
1109 {
1110 SCIP_CONS* lincons = amplph.probdata->conss[constraintIndex];
1111 SCIP_CALL_THROW( SCIPaddCoefLinear(amplph.scip, lincons, amplph.probdata->vars[variableIndex], coefficient) );
1112 }
1113 }
1114 };
1115
1117
1118 /// receive notification of the linear part of an objective
1120 int objectiveIndex, ///< index of objective
1121 int /* numLinearTerms *////< number of terms to expect
1122 )
1123 {
1124 if( objectiveIndex >= 1 )
1125 OnUnhandled("multiple objective functions");
1126
1127 return LinearObjHandler(*this);
1128 }
1129
1131
1132 /// receive notification of the linear part of a constraint
1134 int constraintIndex, ///< index of constraint
1135 int /* numLinearTerms *////< number of terms to expect
1136 )
1137 {
1138 return LinearConHandler(*this, constraintIndex);
1139 }
1140
1141 /// receive notification of the end of the input
1142 ///
1143 /// - setup all nonlinear constraints and add them to SCIP
1144 /// - add linear constraints to SCIP (should be after nonlinear ones to respect order in .nl file)
1145 /// - add initial solution, if initial values were given
1147 {
1148 // turn nonlinear objective into constraint
1149 // min f(x) -> min z s.t. f(x) - z <= 0
1150 // max f(x) -> max z s.t. 0 <= f(x) - z
1151 if( objexpr != NULL )
1152 {
1153 SCIP_CONS* objcons;
1154 SCIP_VAR* objvar;
1155
1157 SCIP_CALL_THROW( SCIPaddVar(scip, objvar) );
1158
1159 SCIP_CALL_THROW( SCIPcreateConsBasicNonlinear(scip, &objcons, "objcons", objexpr,
1162 SCIP_CALL_THROW( SCIPaddLinearVarNonlinear(scip, objcons, objvar, -1.0) );
1163 SCIP_CALL_THROW( SCIPaddCons(scip, objcons) );
1164
1165 SCIP_CALL_THROW( SCIPreleaseCons(scip, &objcons) );
1166 SCIP_CALL_THROW( SCIPreleaseVar(scip, &objvar) );
1167 }
1168
1169 // add linear terms to expressions of nonlinear constraints (should be ok to do this one-by-one for now)
1170 for( size_t i = 0; i < nlconslin.size(); ++i )
1171 {
1172 for( size_t j = 0; j < nlconslin[i].size(); ++j )
1173 {
1174 SCIP_CALL_THROW( SCIPaddLinearVarNonlinear(scip, probdata->conss[i], nlconslin[i][j].second, nlconslin[i][j].first) );
1175 }
1176 }
1177
1178 // add constraints
1179 for( int i = 0; i < probdata->nconss; ++i )
1180 {
1181 SCIP_CALL_THROW( SCIPaddCons(scip, probdata->conss[i]) );
1182 }
1183
1184 // add SOS constraints
1185 std::vector<SCIP_VAR*> setvars; // variables in one SOS
1186 std::vector<SCIP_Real> setweights; // weights for one SOS
1187 if( !sosvars.empty() )
1188 {
1189 setvars.resize(probdata->nvars);
1190 probdata->islp = false;
1191 }
1192 if( !sosweights.empty() )
1193 setweights.resize(probdata->nvars);
1194 for( std::map<int, std::vector<int> >::iterator sosit(sosvars.begin()); sosit != sosvars.end(); ++sosit )
1195 {
1196 assert(sosit->first != 0);
1197 assert(!sosit->second.empty());
1198
1199 // a negative SOS identifier means SOS2
1200 bool issos2 = sosit->first < 0;
1201
1202 if( issos2 && sosweights.empty() )
1203 {
1204 // if no .ref suffix was given for a SOS2 constraint, then we consider this as an error
1205 // since the weights determine the order
1206 // for a SOS1, the weights only specify branching preference, so can treat them as optional
1207 OnUnhandled("SOS2 requires variable .ref suffix");
1208 }
1209
1210 for( size_t i = 0; i < sosit->second.size(); ++i )
1211 {
1212 int varidx = sosit->second[i];
1213 setvars[i] = probdata->vars[varidx];
1214
1215 if( issos2 && sosweights[varidx] == 0 )
1216 // 0 is the default if no ref was given for a variable; we don't allow this for SOS2
1217 OnUnhandled("Missing .ref value for SOS2 variable");
1218 if( !sosweights.empty() )
1219 setweights[i] = (SCIP_Real)sosweights[varidx];
1220 }
1221
1222 SCIP_CONS* cons;
1223 char name[20];
1224 if( !issos2 )
1225 {
1226 (void) SCIPsnprintf(name, 20, "sos1_%d", sosit->first);
1227 SCIP_CALL_THROW( SCIPcreateConsBasicSOS1(scip, &cons, name, sosit->second.size(), setvars.data(), setweights.empty() ? NULL : setweights.data()) );
1228 }
1229 else
1230 {
1231 (void) SCIPsnprintf(name, 20, "sos2_%d", -sosit->first);
1232 SCIP_CALL_THROW( SCIPcreateConsBasicSOS2(scip, &cons, name, sosit->second.size(), setvars.data(), setweights.data()) );
1233 }
1236 }
1237
1238 // add initial solution
1239 if( initsol != NULL )
1240 {
1241 SCIP_Bool stored;
1242 SCIP_CALL_THROW( SCIPaddSolFree(scip, &initsol, &stored) );
1243 }
1244
1245 // release expressions
1247 }
1248
1249 /// releases expressions and linear constraints from data
1250 ///
1251 /// should be called if there was an error while reading the .nl file
1252 /// this is not in the destructor, because we want to return SCIP_RETCODE
1254 {
1255 // release initial sol (in case EndInput() wasn't called)
1256 if( initsol != NULL )
1257 {
1258 SCIP_CALL( SCIPfreeSol(scip, &initsol) );
1259 }
1260
1261 // release created expressions (they should all be used in other expressions or constraints now)
1262 while( !exprstorelease.empty() )
1263 {
1264 SCIP_CALL( SCIPreleaseExpr(scip, &exprstorelease.back()) );
1265 exprstorelease.pop_back();
1266 }
1267
1268 // release variable expressions (they should all be used in other expressions or constraints now)
1269 while( !varexprs.empty() )
1270 {
1271 SCIP_CALL( SCIPreleaseExpr(scip, &varexprs.back()) );
1272 varexprs.pop_back();
1273 }
1274
1275 return SCIP_OKAY;
1276 }
1277};
1278
1279
1280/*
1281 * Callback methods of probdata
1282 */
1283
1284/** frees user data of original problem (called when the original problem is freed) */
1285static
1287{
1288 int i;
1289
1290 assert((*probdata)->vars != NULL || (*probdata)->nvars == 0);
1291 assert((*probdata)->conss != NULL || (*probdata)->conss == 0);
1292
1293 for( i = 0; i < (*probdata)->nconss; ++i )
1294 {
1295 SCIP_CALL( SCIPreleaseCons(scip, &(*probdata)->conss[i]) );
1296 }
1297 SCIPfreeBlockMemoryArrayNull(scip, &(*probdata)->conss, (*probdata)->nconss);
1298
1299 for( i = 0; i < (*probdata)->nvars; ++i )
1300 {
1301 SCIP_CALL( SCIPreleaseVar(scip, &(*probdata)->vars[i]) );
1302 }
1303 SCIPfreeBlockMemoryArrayNull(scip, &(*probdata)->vars, (*probdata)->nvars);
1304
1305 SCIPfreeBlockMemoryArrayNull(scip, &(*probdata)->filenamestub, (*probdata)->filenamestublen+5);
1306
1307 SCIPfreeMemory(scip, probdata);
1308
1309 return SCIP_OKAY;
1310}
1311
1312/*
1313 * Callback methods of reader
1314 */
1315
1316/** copy method for reader plugins (called when SCIP copies plugins) */
1317static
1319{ /*lint --e{715}*/
1320 assert(scip != NULL);
1321
1323
1324 return SCIP_OKAY;
1325}
1326
1327/** problem reading method of reader */
1328static
1330{ /*lint --e{715}*/
1331 assert(scip != NULL);
1332 assert(reader != NULL);
1333 assert(filename != NULL);
1334 assert(result != NULL);
1335
1336 try
1337 {
1338 // try to read the .nl file and setup SCIP problem
1339 AMPLProblemHandler handler(scip, filename);
1340 try
1341 {
1342 mp::ReadNLFile(filename, handler);
1343 }
1344 catch( const mp::UnsupportedError& e )
1345 {
1346 SCIPerrorMessage("unsupported construct in AMPL .nl file %s: %s\n", filename, e.what());
1347
1348 SCIP_CALL( handler.cleanup() );
1349
1350 return SCIP_READERROR;
1351 }
1352 catch( const mp::Error& e )
1353 {
1354 // some other error from ampl/mp, maybe invalid .nl file
1355 SCIPerrorMessage("%s\n", e.what());
1356
1357 SCIP_CALL( handler.cleanup() );
1358
1359 return SCIP_READERROR;
1360 }
1361 catch( const fmt::SystemError& e )
1362 {
1363 // probably a file open error, probably because file not found
1364 SCIPerrorMessage("%s\n", e.what());
1365
1366 SCIP_CALL( handler.cleanup() );
1367
1368 return SCIP_NOFILE;
1369 }
1370 catch( const std::bad_alloc& e )
1371 {
1372 SCIPerrorMessage("Out of memory: %s\n", e.what());
1373
1374 SCIP_CALL( handler.cleanup() );
1375
1376 return SCIP_NOMEMORY;
1377 }
1378 }
1379 catch( const std::exception& e )
1380 {
1381 SCIPerrorMessage("%s\n", e.what());
1382 return SCIP_ERROR;
1383 }
1384
1386
1387 return SCIP_OKAY;
1388}
1389
1390/*
1391 * reader specific interface methods
1392 */
1393
1394/** includes the AMPL .nl file reader in SCIP */
1396 SCIP* scip /**< SCIP data structure */
1397)
1398{
1400
1401 /* include reader */
1403 assert(reader != NULL);
1404
1405 /* set non fundamental callbacks via setter functions */
1408
1409 SCIP_CALL( SCIPincludeExternalCodeInformation(scip, "AMPL/MP 4e2d45c4", "AMPL .nl file reader library (github.com/ampl/mp)") );
1410
1411 return SCIP_OKAY;
1412}
1413
1414/** writes AMPL solution file
1415 *
1416 * problem must have been read with .nl reader
1417 */
1419 SCIP* scip /**< SCIP data structure */
1420 )
1421{
1422 SCIP_PROBDATA* probdata;
1423
1424 assert(scip != NULL);
1425
1426 probdata = SCIPgetProbData(scip);
1427 if( probdata == NULL )
1428 {
1429 SCIPerrorMessage("No AMPL nl file read. Cannot write AMPL solution.\n");
1430 return SCIP_ERROR;
1431 }
1432
1433 probdata->filenamestub[probdata->filenamestublen] = '.';
1434 probdata->filenamestub[probdata->filenamestublen+1] = 's';
1435 probdata->filenamestub[probdata->filenamestublen+2] = 'o';
1436 probdata->filenamestub[probdata->filenamestublen+3] = 'l';
1437 probdata->filenamestub[probdata->filenamestublen+4] = '\0';
1438
1439 FILE* solfile = fopen(probdata->filenamestub, "w");
1440 if( solfile == NULL )
1441 {
1442 SCIPerrorMessage("could not open file <%s> for writing\n", probdata->filenamestub);
1443 probdata->filenamestub[probdata->filenamestublen] = '\0';
1444
1445 return SCIP_WRITEERROR;
1446 }
1447 probdata->filenamestub[probdata->filenamestublen] = '\0';
1448
1449 // see ampl/mp:sol.h:WriteSolFile() (seems buggy, https://github.com/ampl/mp/issues/135) and asl/writesol.c for solution file format
1451 SCIPinfoMessage(scip, solfile, "\n\n");
1452
1453 SCIPinfoMessage(scip, solfile, "Options\n%d\n", probdata->namplopts);
1454 for( int i = 0; i < probdata->namplopts; ++i )
1455 SCIPinfoMessage(scip, solfile, "%d\n", probdata->amplopts[i]);
1456
1458 bool havedual = probdata->islp && SCIPgetStage(scip) == SCIP_STAGE_SOLVED && !SCIPhasPerformedPresolve(scip);
1459
1460 SCIPinfoMessage(scip, solfile, "%d\n%d\n", probdata->nconss, havedual ? probdata->nconss : 0);
1461 SCIPinfoMessage(scip, solfile, "%d\n%d\n", probdata->nvars, haveprimal ? probdata->nvars : 0);
1462
1464
1465 if( havedual )
1466 for( int c = 0; c < probdata->nconss; ++c )
1467 {
1469 SCIP_Real dualval;
1470
1471 /* dual solution is created by LP solver and therefore only available for linear constraints */
1472 SCIP_CALL( SCIPgetTransformedCons(scip, probdata->conss[c], &transcons) );
1474
1475 if( transcons == NULL )
1476 dualval = 0.0;
1479 else
1482
1483 SCIPinfoMessage(scip, solfile, "%.17g\n", dualval);
1484 }
1485
1486 if( haveprimal )
1487 for( int i = 0; i < probdata->nvars; ++i )
1488 SCIPinfoMessage(scip, solfile, "%.17g\n", SCIPgetSolVal(scip, SCIPgetBestSol(scip), probdata->vars[i]));
1489
1490 /* AMPL solve status codes are at http://www.ampl.com/NEW/statuses.html
1491 * number string interpretation
1492 * 0 - 99 solved optimal solution found
1493 * 100 - 199 solved? optimal solution indicated, but error likely
1494 * 200 - 299 infeasible constraints cannot be satisfied
1495 * 300 - 399 unbounded objective can be improved without limit
1496 * 400 - 499 limit stopped by a limit that you set (such as on iterations)
1497 * 500 - 599 failure stopped by an error condition in the solver routines
1498 */
1499 int solve_result_num;
1500 switch( SCIPgetStatus(scip) )
1501 {
1503 solve_result_num = 500;
1504 break;
1506 solve_result_num = 450;
1507 break;
1509 solve_result_num = 400;
1510 break;
1512 solve_result_num = 401;
1513 break;
1515 solve_result_num = 402;
1516 break;
1518 solve_result_num = 403;
1519 break;
1521 solve_result_num = 404;
1522 break;
1524 solve_result_num = 405;
1525 break;
1527 solve_result_num = 406;
1528 break;
1530 solve_result_num = 407;
1531 break;
1533 solve_result_num = 0;
1534 break;
1536 solve_result_num = 200;
1537 break;
1539 solve_result_num = 300;
1540 break;
1542 solve_result_num = 299;
1543 break;
1544 default:
1545 /* solve_result_num = 500; */
1546 SCIPerrorMessage("invalid status code <%d>\n", SCIPgetStatus(scip));
1547 (void) fclose(solfile);
1548 return SCIP_INVALIDDATA;
1549 }
1550 SCIPinfoMessage(scip, solfile, "objno 0 %d\n", solve_result_num);
1551
1552 if( fclose(solfile) != 0 )
1553 {
1554 SCIPerrorMessage("could not close solution file after writing\n");
1555 return SCIP_WRITEERROR;
1556 }
1557
1558 return SCIP_OKAY;
1559}
SCIP_VAR * h
void AddTerm(int var_index, double coef)
receives notification of a term in the linear expression
LinearExprHandler(AMPLProblemHandler &amplph_, int index, int num_linear_terms)
constructor
LinearPartHandler(AMPLProblemHandler &amplph_)
void AddTerm(int variableIndex, double coefficient)
LinearPartHandler(AMPLProblemHandler &amplph_, int constraintIndex_)
NumericArgHandler(int num_args)
constructor
void AddArg(SCIP_EXPR *term)
adds term to sum
std::shared_ptr< std::vector< SCIP_EXPR * > > v
void SetValue(int index, T value)
SuffixHandler(AMPLProblemHandler &amplph_, fmt::StringRef name, mp::suf::Kind kind)
constructor
implementation of AMPL/MPs NLHandler that constructs a SCIP problem while a .nl file is read
void EndCommonExpr(int index, SCIP_EXPR *expr, int)
receive notification of the end of a common expression
LinearPartHandler LinearObjHandler
NumericArgHandler BeginSum(int num_args)
receive notification of the beginning of a summation
void OnAlgebraicCon(int constraintIndex, SCIP_EXPR *expr)
receive notification of an algebraic constraint expression
LinearPartHandler OnLinearObjExpr(int objectiveIndex, int)
receive notification of the linear part of an objective
SCIP_EXPR * OnBinary(mp::expr::Kind kind, SCIP_EXPR *firstChild, SCIP_EXPR *secondChild)
receive notification of a binary expression
SCIP_EXPR * OnNumber(double value)
receive notification of a number in a nonlinear expression
SuffixHandler< int > IntSuffixHandler
LinearExprHandler BeginCommonExpr(int index, int num_linear_terms)
receive notification of the beginning of a common expression (defined variable)
AMPLProblemHandler(const AMPLProblemHandler &)=delete
LinearConHandler OnLinearConExpr(int constraintIndex, int)
receive notification of the linear part of a constraint
void OnInitialValue(int var_index, double value)
receive notification of the initial value for a variable
SCIP_EXPR * OnVariableRef(int variableIndex)
receive notification of a variable reference in a nonlinear expression
AMPLProblemHandler(SCIP *scip_, const char *filename)
ColumnSizeHandler OnColumnSizes()
receives notification of Jacobian column sizes
AMPLProblemHandler & operator=(const AMPLProblemHandler &)=delete
LinearPartHandler LinearConHandler
void OnVarBounds(int variableIndex, double variableLB, double variableUB)
receive notification of variable bounds
SCIP_EXPR * OnCommonExprRef(int expr_index)
receive notification of a common expression (defined variable) reference
void OnHeader(const mp::NLHeader &h)
DblSuffixHandler OnDblSuffix(fmt::StringRef name, mp::suf::Kind kind, int)
receive notification of a double suffix
void OnConBounds(int index, double lb, double ub)
receive notification of constraint sides
IntSuffixHandler OnIntSuffix(fmt::StringRef name, mp::suf::Kind kind, int)
receive notification of an integer suffix
SCIP_EXPR * OnUnary(mp::expr::Kind kind, SCIP_EXPR *child)
receive notification of a unary expression
SCIP_EXPR * EndSum(NumericArgHandler handler)
receive notification of the end of a summation
void OnInitialDualValue(int, double)
receives notification of the initial value for a dual variable
SCIP_RETCODE cleanup()
SuffixHandler< SCIP_Real > DblSuffixHandler
void OnObj(int objectiveIndex, mp::obj::Type type, SCIP_EXPR *nonlinearExpression)
receive notification of an objective type and the nonlinear part of an objective expression
Constraint handler for linear constraints in their most general form, .
constraint handler for nonlinear constraints specified by algebraic expressions
constraint handler for SOS type 1 constraints
constraint handler for SOS type 2 constraints
#define SCIP_MAXSTRLEN
Definition def.h:302
#define SCIP_INVALID
Definition def.h:206
#define SCIP_Real
Definition def.h:186
#define TRUE
Definition def.h:95
#define SCIPABORT()
Definition def.h:360
#define SCIP_CALL(x)
Definition def.h:388
absolute expression handler
exponential expression handler
logarithm expression handler
power and signed power expression handlers
product expression handler
sum expression handler
handler for sin expressions
constant value expression handler
variable expression handler
SCIP_Real SCIPgetDualsolLinear(SCIP *scip, SCIP_CONS *cons)
SCIP_RETCODE SCIPaddLinearVarNonlinear(SCIP *scip, SCIP_CONS *cons, SCIP_VAR *var, SCIP_Real coef)
SCIP_RETCODE SCIPchgRhsLinear(SCIP *scip, SCIP_CONS *cons, SCIP_Real rhs)
SCIP_RETCODE SCIPaddCoefLinear(SCIP *scip, SCIP_CONS *cons, SCIP_VAR *var, SCIP_Real val)
SCIP_RETCODE SCIPchgLhsNonlinear(SCIP *scip, SCIP_CONS *cons, SCIP_Real lhs)
SCIP_RETCODE SCIPchgRhsNonlinear(SCIP *scip, SCIP_CONS *cons, SCIP_Real rhs)
SCIP_RETCODE SCIPcreateConsBasicSOS1(SCIP *scip, SCIP_CONS **cons, const char *name, int nvars, SCIP_VAR **vars, SCIP_Real *weights)
SCIP_RETCODE SCIPcreateConsBasicLinear(SCIP *scip, SCIP_CONS **cons, const char *name, int nvars, SCIP_VAR **vars, SCIP_Real *vals, SCIP_Real lhs, SCIP_Real rhs)
SCIP_RETCODE SCIPcreateConsBasicNonlinear(SCIP *scip, SCIP_CONS **cons, const char *name, SCIP_EXPR *expr, SCIP_Real lhs, SCIP_Real rhs)
SCIP_RETCODE SCIPchgExprNonlinear(SCIP *scip, SCIP_CONS *cons, SCIP_EXPR *expr)
SCIP_RETCODE SCIPchgLhsLinear(SCIP *scip, SCIP_CONS *cons, SCIP_Real lhs)
SCIP_RETCODE SCIPcreateConsBasicSOS2(SCIP *scip, SCIP_CONS **cons, const char *name, int nvars, SCIP_VAR **vars, SCIP_Real *weights)
Definition cons_sos2.c:2465
SCIP_RETCODE SCIPcreateExprVar(SCIP *scip, SCIP_EXPR **expr, SCIP_VAR *var, SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), void *ownercreatedata)
Definition expr_var.c:390
SCIP_RETCODE SCIPcreateExprProduct(SCIP *scip, SCIP_EXPR **expr, int nchildren, SCIP_EXPR **children, SCIP_Real coefficient, SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), void *ownercreatedata)
SCIP_RETCODE SCIPcreateExprSin(SCIP *scip, SCIP_EXPR **expr, SCIP_EXPR *child, SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), void *ownercreatedata)
Definition expr_trig.c:1430
SCIP_RETCODE SCIPcreateExprCos(SCIP *scip, SCIP_EXPR **expr, SCIP_EXPR *child, SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), void *ownercreatedata)
Definition expr_trig.c:1450
SCIP_RETCODE SCIPcreateExprAbs(SCIP *scip, SCIP_EXPR **expr, SCIP_EXPR *child, SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), void *ownercreatedata)
Definition expr_abs.c:528
SCIP_RETCODE SCIPappendExprSumExpr(SCIP *scip, SCIP_EXPR *expr, SCIP_EXPR *child, SCIP_Real childcoef)
Definition expr_sum.c:1116
SCIP_RETCODE SCIPcreateExprLog(SCIP *scip, SCIP_EXPR **expr, SCIP_EXPR *child, SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), void *ownercreatedata)
Definition expr_log.c:630
SCIP_RETCODE SCIPcreateExprExp(SCIP *scip, SCIP_EXPR **expr, SCIP_EXPR *child, SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), void *ownercreatedata)
Definition expr_exp.c:510
SCIP_RETCODE SCIPcreateExprSum(SCIP *scip, SCIP_EXPR **expr, int nchildren, SCIP_EXPR **children, SCIP_Real *coefficients, SCIP_Real constant, SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), void *ownercreatedata)
Definition expr_sum.c:1079
SCIP_RETCODE SCIPcreateExprValue(SCIP *scip, SCIP_EXPR **expr, SCIP_Real value, SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), void *ownercreatedata)
Definition expr_value.c:270
SCIP_RETCODE SCIPcreateExprPow(SCIP *scip, SCIP_EXPR **expr, SCIP_EXPR *child, SCIP_Real exponent, SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), void *ownercreatedata)
Definition expr_pow.c:3174
SCIP_Bool SCIPhasPerformedPresolve(SCIP *scip)
SCIP_RETCODE SCIPprintStatus(SCIP *scip, FILE *file)
SCIP_STATUS SCIPgetStatus(SCIP *scip)
SCIP_STAGE SCIPgetStage(SCIP *scip)
SCIP_RETCODE SCIPaddVar(SCIP *scip, SCIP_VAR *var)
Definition scip_prob.c:1668
SCIP_RETCODE SCIPaddCons(SCIP *scip, SCIP_CONS *cons)
Definition scip_prob.c:2770
SCIP_PROBDATA * SCIPgetProbData(SCIP *scip)
Definition scip_prob.c:964
SCIP_RETCODE SCIPsetObjsense(SCIP *scip, SCIP_OBJSENSE objsense)
Definition scip_prob.c:1242
SCIP_OBJSENSE SCIPgetObjsense(SCIP *scip)
Definition scip_prob.c:1225
SCIP_RETCODE SCIPcreateProb(SCIP *scip, const char *name, SCIP_DECL_PROBDELORIG((*probdelorig)), SCIP_DECL_PROBTRANS((*probtrans)), SCIP_DECL_PROBDELTRANS((*probdeltrans)), SCIP_DECL_PROBINITSOL((*probinitsol)), SCIP_DECL_PROBEXITSOL((*probexitsol)), SCIP_DECL_PROBCOPY((*probcopy)), SCIP_PROBDATA *probdata)
Definition scip_prob.c:117
void SCIPinfoMessage(SCIP *scip, FILE *file, const char *formatstr,...)
void SCIPverbMessage(SCIP *scip, SCIP_VERBLEVEL msgverblevel, FILE *file, const char *formatstr,...)
const char * SCIPconshdlrGetName(SCIP_CONSHDLR *conshdlr)
Definition cons.c:4180
SCIP_CONSHDLR * SCIPconsGetHdlr(SCIP_CONS *cons)
Definition cons.c:8108
SCIP_RETCODE SCIPsetConsSeparated(SCIP *scip, SCIP_CONS *cons, SCIP_Bool separate)
Definition scip_cons.c:1242
SCIP_RETCODE SCIPsetConsDynamic(SCIP *scip, SCIP_CONS *cons, SCIP_Bool dynamic)
Definition scip_cons.c:1395
SCIP_RETCODE SCIPsetConsInitial(SCIP *scip, SCIP_CONS *cons, SCIP_Bool initial)
Definition scip_cons.c:1217
SCIP_RETCODE SCIPsetConsEnforced(SCIP *scip, SCIP_CONS *cons, SCIP_Bool enforce)
Definition scip_cons.c:1267
SCIP_RETCODE SCIPsetConsRemovable(SCIP *scip, SCIP_CONS *cons, SCIP_Bool removable)
Definition scip_cons.c:1420
SCIP_RETCODE SCIPgetTransformedCons(SCIP *scip, SCIP_CONS *cons, SCIP_CONS **transcons)
Definition scip_cons.c:1620
SCIP_RETCODE SCIPreleaseCons(SCIP *scip, SCIP_CONS **cons)
Definition scip_cons.c:1119
SCIP_RETCODE SCIPsetConsPropagated(SCIP *scip, SCIP_CONS *cons, SCIP_Bool propagate)
Definition scip_cons.c:1317
SCIP_RETCODE SCIPsetConsChecked(SCIP *scip, SCIP_CONS *cons, SCIP_Bool check)
Definition scip_cons.c:1292
SCIP_Bool SCIPisExprValue(SCIP *scip, SCIP_EXPR *expr)
Definition scip_expr.c:1432
SCIP_RETCODE SCIPreleaseExpr(SCIP *scip, SCIP_EXPR **expr)
Definition scip_expr.c:1407
SCIP_Real SCIPgetValueExprValue(SCIP_EXPR *expr)
Definition expr_value.c:294
SCIP_RETCODE SCIPincludeExternalCodeInformation(SCIP *scip, const char *name, const char *description)
#define SCIPallocClearMemory(scip, ptr)
Definition scip_mem.h:62
#define SCIPfreeMemory(scip, ptr)
Definition scip_mem.h:78
#define SCIPallocBlockMemoryArray(scip, ptr, num)
Definition scip_mem.h:93
#define SCIPfreeBlockMemoryArrayNull(scip, ptr, num)
Definition scip_mem.h:111
SCIP_RETCODE SCIPsetReaderCopy(SCIP *scip, SCIP_READER *reader,)
SCIP_RETCODE SCIPincludeReaderBasic(SCIP *scip, SCIP_READER **readerptr, const char *name, const char *desc, const char *extension, SCIP_READERDATA *readerdata)
SCIP_RETCODE SCIPsetReaderRead(SCIP *scip, SCIP_READER *reader,)
SCIP_SOL * SCIPgetBestSol(SCIP *scip)
Definition scip_sol.c:2313
SCIP_RETCODE SCIPaddSolFree(SCIP *scip, SCIP_SOL **sol, SCIP_Bool *stored)
Definition scip_sol.c:2999
SCIP_RETCODE SCIPprintSol(SCIP *scip, SCIP_SOL *sol, FILE *file, SCIP_Bool printzeros)
Definition scip_sol.c:1775
SCIP_RETCODE SCIPsetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var, SCIP_Real val)
Definition scip_sol.c:1221
SCIP_Real SCIPgetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var)
Definition scip_sol.c:1361
SCIP_Real SCIPinfinity(SCIP *scip)
SCIP_Bool SCIPisInfinity(SCIP *scip, SCIP_Real val)
SCIP_VARTYPE SCIPvarGetType(SCIP_VAR *var)
Definition var.c:17406
SCIP_RETCODE SCIPvarSetInitial(SCIP_VAR *var, SCIP_Bool initial)
Definition var.c:17328
SCIP_RETCODE SCIPreleaseVar(SCIP *scip, SCIP_VAR **var)
Definition scip_var.c:1248
SCIP_RETCODE SCIPchgVarLbGlobal(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound)
Definition scip_var.c:4943
SCIP_RETCODE SCIPvarSetRemovable(SCIP_VAR *var, SCIP_Bool removable)
Definition var.c:17344
SCIP_RETCODE SCIPchgVarUbGlobal(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound)
Definition scip_var.c:5032
SCIP_RETCODE SCIPcreateVarBasic(SCIP *scip, SCIP_VAR **var, const char *name, SCIP_Real lb, SCIP_Real ub, SCIP_Real obj, SCIP_VARTYPE vartype)
Definition scip_var.c:194
SCIP_RETCODE SCIPchgVarObj(SCIP *scip, SCIP_VAR *var, SCIP_Real newobj)
Definition scip_var.c:4513
int SCIPsnprintf(char *t, int len, const char *s,...)
Definition misc.c:10788
return SCIP_OKAY
SCIPfreeSol(scip, &heurdata->sol))
SCIPcreateSol(scip, &heurdata->sol, heur))
int c
assert(minobj< SCIPgetCutoffbound(scip))
int nvars
static SCIP_VAR ** vars
#define NULL
Definition lpi_spx1.cpp:161
#define BMScopyMemoryArray(ptr, source, num)
Definition memory.h:136
Definition pqueue.h:38
#define SCIPerrorMessage
Definition pub_message.h:64
#define SCIPdebug(x)
Definition pub_message.h:93
SCIP_RETCODE SCIPincludeReaderNl(SCIP *scip)
#define READER_DESC
Definition reader_nl.cpp:67
#define READER_EXTENSION
Definition reader_nl.cpp:68
#define SCIP_CALL_THROW(x)
Definition reader_nl.cpp:72
SCIP_RETCODE SCIPwriteSolutionNl(SCIP *scip)
#define READER_NAME
Definition reader_nl.cpp:66
AMPL .nl file reader.
#define MAX(x, y)
Definition tclique_def.h:92
@ SCIP_VERBLEVEL_HIGH
@ SCIP_VERBLEVEL_FULL
struct SCIP_ProbData SCIP_PROBDATA
Definition type_prob.h:53
@ SCIP_OBJSENSE_MAXIMIZE
Definition type_prob.h:47
@ SCIP_OBJSENSE_MINIMIZE
Definition type_prob.h:48
#define SCIP_DECL_PROBDELORIG(x)
Definition type_prob.h:64
#define SCIP_DECL_READERREAD(x)
Definition type_reader.h:87
#define SCIP_DECL_READERCOPY(x)
Definition type_reader.h:62
@ SCIP_SUCCESS
Definition type_result.h:58
@ SCIP_NOFILE
@ SCIP_READERROR
@ SCIP_INVALIDDATA
@ SCIP_WRITEERROR
@ SCIP_NOMEMORY
@ SCIP_ERROR
enum SCIP_Retcode SCIP_RETCODE
@ SCIP_STAGE_SOLVED
Definition type_set.h:54
@ SCIP_STATUS_OPTIMAL
Definition type_stat.h:61
@ SCIP_STATUS_TOTALNODELIMIT
Definition type_stat.h:45
@ SCIP_STATUS_BESTSOLLIMIT
Definition type_stat.h:57
@ SCIP_STATUS_SOLLIMIT
Definition type_stat.h:54
@ SCIP_STATUS_UNBOUNDED
Definition type_stat.h:63
@ SCIP_STATUS_UNKNOWN
Definition type_stat.h:42
@ SCIP_STATUS_GAPLIMIT
Definition type_stat.h:53
@ SCIP_STATUS_USERINTERRUPT
Definition type_stat.h:43
@ SCIP_STATUS_INFORUNBD
Definition type_stat.h:64
@ SCIP_STATUS_STALLNODELIMIT
Definition type_stat.h:48
@ SCIP_STATUS_TIMELIMIT
Definition type_stat.h:51
@ SCIP_STATUS_INFEASIBLE
Definition type_stat.h:62
@ SCIP_STATUS_NODELIMIT
Definition type_stat.h:44
@ SCIP_STATUS_MEMLIMIT
Definition type_stat.h:52
@ SCIP_VARTYPE_INTEGER
Definition type_var.h:63
@ SCIP_VARTYPE_CONTINUOUS
Definition type_var.h:71
@ SCIP_VARTYPE_BINARY
Definition type_var.h:62
enum SCIP_Vartype SCIP_VARTYPE
Definition type_var.h:73