Scippy

SCIP

Solving Constraint Integer Programs

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-2025 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 and writer
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 * TODO:
34 * - writing of logical constraints (and, or, xor)
35 * - writing of SOS constraints (into suffixes)
36 */
37
38/*--+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
39
40#include <string>
41#include <sstream>
42#include <vector>
43#include <map>
44#include <cstdlib>
45#ifdef _WIN32
46#include <windows.h> // to be able to do the includes below
47#include <io.h> // for _mktemp_s
48#include <direct.h> // for _mkdir
49#include <fileapi.h> // for GetTempPath
50#ifdef max
51#undef max // undo definition of max in windows.h
52#endif
53#ifdef IGNORE
54#undef IGNORE // undo definition of IGNORE in windows.h
55#endif
56#else
57#include <unistd.h> // for mkdtemp on macOS
58#endif
59
60#include "scip/reader_nl.h"
61#include "scip/cons_linear.h"
62#include "scip/cons_setppc.h"
63#include "scip/cons_logicor.h"
64#include "scip/cons_knapsack.h"
65#include "scip/cons_varbound.h"
66#include "scip/cons_nonlinear.h"
67#include "scip/cons_sos1.h"
68#include "scip/cons_sos2.h"
69#include "scip/cons_and.h"
70#include "scip/cons_or.h"
71#include "scip/cons_xor.h"
72#include "scip/expr_var.h"
73#include "scip/expr_value.h"
74#include "scip/expr_sum.h"
75#include "scip/expr_product.h"
76#include "scip/expr_pow.h"
77#include "scip/expr_log.h"
78#include "scip/expr_exp.h"
79#include "scip/expr_trig.h"
80#include "scip/expr_abs.h"
81
82// disable -Wshadow warnings for upcoming includes of AMPL/MP
83// disable -Wimplicit-fallthrough as I don't want to maintain extra comments in AMPL/MP code to suppress these
84#ifdef __GNUC__
85#pragma GCC diagnostic ignored "-Wshadow"
86#if __GNUC__ >= 7
87#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
88#endif
89#endif
90
91#include "mp/nl-reader.h"
92#include "mp/nl-writer2.hpp"
93#include "mp/nl-opcodes.h"
94
95#define READER_NAME "nlreader"
96#define READER_DESC "AMPL .nl file reader"
97#define READER_EXTENSION "nl"
98
99// a variant of SCIP_CALL that throws a std::logic_error if not SCIP_OKAY
100// (using cast to long long to work around issues with old MSVC)
101#define SCIP_CALL_THROW(x) \
102 do \
103 { \
104 SCIP_RETCODE throw_retcode; \
105 if( ((throw_retcode) = (x)) != SCIP_OKAY ) \
106 throw std::logic_error("Error <" + std::to_string((long long)throw_retcode) + "> in function call at reader_nl.cpp:" + std::to_string(__LINE__)); \
107 } \
108 while( false )
109
110/*
111 * Data structures
112 */
113
114/// problem data stored in SCIP
115struct SCIP_ProbNlData
116{
117 char* filenamestub; /**< name of input file, without .nl extension; array is long enough to hold 5 extra chars */
118 int filenamestublen; /**< length of filenamestub string */
119
120 int amplopts[mp::MAX_AMPL_OPTIONS]; /**< AMPL options from .nl header */
121 int namplopts; /**< number of AMPL options from .nl header */
122
123 SCIP_VAR** vars; /**< variables in the order given by AMPL */
124 int nvars; /**< number of variables */
125
126 SCIP_CONS** conss; /**< constraints in the order given by AMPL */
127 int nconss; /**< number of constraints */
128
129 SCIP_Bool islp; /**< whether problem is an LP (only linear constraints, only continuous vars) */
130};
131typedef struct SCIP_ProbNlData SCIP_PROBNLDATA;
132
133/*
134 * Local methods
135 */
136
137// forward declaration
138static SCIP_DECL_PROBDELORIG(probdataDelOrigNl);
139
140/// implementation of AMPL/MPs NLHandler that constructs a SCIP problem while a .nl file is read
141class AMPLProblemHandler : public mp::NLHandler<AMPLProblemHandler, SCIP_EXPR*>
142{
143private:
144 SCIP* scip;
145 SCIP_PROBNLDATA* probdata;
146
147 // variable expressions corresponding to nonlinear variables
148 // created in OnHeader() and released in destructor
149 // for reuse of var-expressions in OnVariableRef()
150 std::vector<SCIP_EXPR*> varexprs;
151
152 // linear parts for nonlinear constraints
153 // first collect and then add to constraints in EndInput()
154 std::vector<std::vector<std::pair<SCIP_Real, SCIP_VAR*> > > nlconslin;
155
156 // expression that represents a nonlinear objective function
157 // used to create a corresponding constraint in EndInput(), unless NULL
158 SCIP_EXPR* objexpr;
159
160 // common expressions (defined variables from statements like "var xsqr = x^2;" in an AMPL model)
161 // they are constructed by BeginCommonExpr/EndCommonExpr below and are referenced by index in OnCommonExprRef
162 std::vector<SCIP_EXPR*> commonexprs;
163
164 // collect expressions that need to be released eventually
165 // this are all expression that are returned to the AMPL/MP code in AMPLProblemHandler::OnXyz() functions
166 // they need to be released exactly once, but after they are used in another expression or a constraint
167 // as AMPL/MP may reuse expressions (common subexpressions), we don't release an expression when it is used
168 // as a child or when constructing a constraint, but first collect them all and then release in destructor
169 // alternatively, one could encapsulate SCIP_EXPR* into a small class that handles proper reference counting
170 std::vector<SCIP_EXPR*> exprstorelease;
171
172 // count on variables or constraints added for logical expressions
173 int logiccount;
174
175 // SOS constraints
176 // collected while handling suffixes in SuffixHandler
177 // sosvars maps the SOS index (can be negative) to the indices of the variables in the SOS
178 // sosweights gives for each variable its weight in the SOS it appears in (if any)
179 std::map<int, std::vector<int> > sosvars;
180 std::vector<int> sosweights;
181
182 // initial solution, if any
183 SCIP_SOL* initsol;
184
185 // opened files with column/variable and row/constraint names, or NULL
186 fmt::File* colfile;
187 fmt::File* rowfile;
188
189 // get name from names strings, if possible
190 // returns whether a name has been stored
191 bool nextName(
192 const char*& namesbegin, /**< current pointer into names string, or NULL */
193 const char* namesend, /**< pointer to end of names string */
194 char* name /**< buffer to store name, should have length SCIP_MAXSTRLEN */
195 )
196 {
197 if( namesbegin == NULL )
198 return false;
199
200 // copy namesbegin into name until newline or namesend
201 // updates namesbegin
202 int nchars = 0;
203 while( namesbegin != namesend )
204 {
205 if( nchars == SCIP_MAXSTRLEN )
206 {
207 SCIPverbMessage(scip, SCIP_VERBLEVEL_FULL, NULL, "name too long when parsing names file");
208 // do no longer read names from this string (something seems awkward)
209 namesbegin = NULL;
210 return false;
211 }
212 if( *namesbegin == '\n' )
213 {
214 *name = '\0';
215 ++namesbegin;
216 return true;
217 }
218 *(name++) = *(namesbegin++);
219 ++nchars;
220 }
221
222 SCIPverbMessage(scip, SCIP_VERBLEVEL_FULL, NULL, "missing newline when parsing names file");
223 return false;
224 }
225
226 /// returns variable or value for given expression
227 ///
228 /// if expression is variable, ensure that it is a binary variable and set var
229 /// if expression is value, then set val to whether value is nonzero and set var to NULL
230 /// otherwise throw UnsupportedError exception
231 void LogicalExprToVarVal(
232 LogicalExpr expr,
233 SCIP_VAR*& var,
234 SCIP_Bool& val
235 )
236 {
237 assert(expr != NULL);
238
239 if( SCIPisExprVar(scip, expr) )
240 {
241 var = SCIPgetVarExprVar(expr);
243 {
244 SCIP_Bool infeas;
245 SCIP_Bool tightened;
247 assert(!infeas);
248 SCIP_CALL_THROW( SCIPtightenVarLbGlobal(scip, var, 0.0, TRUE, &infeas, &tightened) );
249 assert(!infeas);
250 SCIP_CALL_THROW( SCIPtightenVarUbGlobal(scip, var, 1.0, TRUE, &infeas, &tightened) );
251 assert(!infeas);
252 }
253 val = FALSE; // for scan-build
254
255 return;
256 }
257
258 if( SCIPisExprValue(scip, expr) )
259 {
260 var = NULL;
261 val = SCIPgetValueExprValue(expr) != 0.0;
262 return;
263 }
264
265 OnUnhandled("logical expression must be binary or constant");
266 }
267
268public:
269 /// constructor
270 ///
271 /// initializes SCIP problem and problem data
273 SCIP* scip_, ///< SCIP data structure
274 const char* filename ///< name of .nl file that is read
275 )
276 : scip(scip_),
277 probdata(NULL),
278 objexpr(NULL),
279 logiccount(0),
280 initsol(NULL),
281 colfile(NULL),
282 rowfile(NULL)
283 {
284 assert(scip != NULL);
285 assert(filename != NULL);
286
288
289 /* get name of input file without file extension (if any) */
290 const char* extstart = strrchr(const_cast<char*>(filename), '.');
291 if( extstart != NULL )
292 probdata->filenamestublen = extstart - filename;
293 else
294 probdata->filenamestublen = strlen(filename);
295 assert(probdata->filenamestublen > 0);
296 SCIP_CALL_THROW( SCIPallocBlockMemoryArray(scip, &probdata->filenamestub, probdata->filenamestublen + 5) );
297 memcpy(probdata->filenamestub, filename, probdata->filenamestublen);
298 probdata->filenamestub[probdata->filenamestublen] = '\0';
299
300 /* derive probname from name of input file without path and extension */
301 const char* probname = strrchr(probdata->filenamestub, '/');
302 if( probname == NULL )
303 probname = probdata->filenamestub;
304 else
305 ++probname;
306
307 // initialize empty SCIP problem
308 SCIP_CALL_THROW( SCIPcreateProb(scip, probname, probdataDelOrigNl, NULL, NULL, NULL, NULL, NULL, (SCIP_PROBDATA*)probdata) );
309
310 // try to open files with variable and constraint names
311 // temporarily add ".col" and ".row", respectively, to filenamestub
312 try
313 {
314 probdata->filenamestub[probdata->filenamestublen] = '.';
315 probdata->filenamestub[probdata->filenamestublen+1] = 'c';
316 probdata->filenamestub[probdata->filenamestublen+2] = 'o';
317 probdata->filenamestub[probdata->filenamestublen+3] = 'l';
318 probdata->filenamestub[probdata->filenamestublen+4] = '\0';
319 colfile = new fmt::File(probdata->filenamestub, fmt::File::RDONLY);
320
321 probdata->filenamestub[probdata->filenamestublen+1] = 'r';
322 probdata->filenamestub[probdata->filenamestublen+3] = 'w';
323 rowfile = new fmt::File(probdata->filenamestub, fmt::File::RDONLY);
324 }
325 catch( const fmt::SystemError& e )
326 {
327 // probably a file open error, probably because file not found
328 // ignore, we can make up our own names
329 }
330 probdata->filenamestub[probdata->filenamestublen] = '\0';
331 }
332
335
336 /// destructor
337 ///
338 /// only asserts that cleanup() has been called, as we cannot throw an exception or return a SCIP_RETCODE here
340 {
341 // exprs and linear constraint arrays should have been cleared up in cleanup()
342 assert(varexprs.empty());
343 assert(exprstorelease.empty());
344
345 delete colfile;
346 delete rowfile;
347 }
348
349 /// process header of .nl files
350 ///
351 /// create and add variables, allocate constraints
353 const mp::NLHeader& h ///< header data
354 )
355 {
356 char name[SCIP_MAXSTRLEN];
357 int nnlvars;
358
359 assert(probdata->vars == NULL);
360 assert(probdata->conss == NULL);
361
362 probdata->namplopts = h.num_ampl_options;
363 BMScopyMemoryArray(probdata->amplopts, h.ampl_options, h.num_ampl_options);
364
365 // read variable and constraint names from file, if available, into memory
366 // if not available, we will get varnamesbegin==NULL and consnamesbegin==NULL
367 mp::MemoryMappedFile<> mapped_colfile;
368 if( colfile != NULL )
369 mapped_colfile.map(*colfile, "colfile");
370 const char* varnamesbegin = mapped_colfile.start();
371 const char* varnamesend = mapped_colfile.start() + mapped_colfile.size();
372
373 mp::MemoryMappedFile<> mapped_rowfile;
374 if( rowfile != NULL )
375 mapped_rowfile.map(*rowfile, "rowfile");
376 const char* consnamesbegin = mapped_rowfile.start();
377 const char* consnamesend = mapped_rowfile.start() + mapped_rowfile.size();
378
379 probdata->nvars = h.num_vars;
380 SCIP_CALL_THROW( SCIPallocBlockMemoryArray(scip, &probdata->vars, probdata->nvars) );
381
382 // number of nonlinear variables
383 nnlvars = MAX(h.num_nl_vars_in_cons, h.num_nl_vars_in_objs);
384 varexprs.resize(nnlvars);
385
386 // create variables
387 // create variable expressions for nonlinear variables
388 for( int i = 0; i < h.num_vars; ++i )
389 {
390 SCIP_VARTYPE vartype;
391 // Nonlinear variables in both constraints and objective
392 if( i < h.num_nl_vars_in_both - h.num_nl_integer_vars_in_both )
393 vartype = SCIP_VARTYPE_CONTINUOUS;
394 else if( i < h.num_nl_vars_in_both )
395 vartype = SCIP_VARTYPE_INTEGER;
396 // Nonlinear variables in constraints
397 else if( i < h.num_nl_vars_in_cons - h.num_nl_integer_vars_in_cons )
398 vartype = SCIP_VARTYPE_CONTINUOUS;
399 else if( i < h.num_nl_vars_in_cons )
400 vartype = SCIP_VARTYPE_INTEGER;
401 // Nonlinear variables in objective
402 else if( i < h.num_nl_vars_in_objs - h.num_nl_integer_vars_in_objs )
403 vartype = SCIP_VARTYPE_CONTINUOUS;
404 else if( i < h.num_nl_vars_in_objs )
405 vartype = SCIP_VARTYPE_INTEGER;
406 // Linear variables
407 else if( i < h.num_vars - h.num_linear_binary_vars - h.num_linear_integer_vars )
408 vartype = SCIP_VARTYPE_CONTINUOUS;
409 else if( i < h.num_vars - h.num_linear_integer_vars )
410 vartype = SCIP_VARTYPE_BINARY;
411 else
412 vartype = SCIP_VARTYPE_INTEGER;
413
414 if( !nextName(varnamesbegin, varnamesend, name) )
415 {
416 // make up name if no names file or could not be read
417 switch( vartype )
418 {
420 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "b%d", i);
421 break;
423 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "i%d", i);
424 break;
426 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "x%d", i);
427 break;
428 // coverity[deadcode]
429 default:
430 SCIPABORT();
431 break;
432 }
433 }
434
435 SCIP_CALL_THROW( SCIPcreateVarBasic(scip, &probdata->vars[i], name,
436 vartype == SCIP_VARTYPE_BINARY ? 0.0 : -SCIPinfinity(scip),
437 vartype == SCIP_VARTYPE_BINARY ? 1.0 : SCIPinfinity(scip),
438 0.0, vartype) );
439 SCIP_CALL_THROW( SCIPaddVar(scip, probdata->vars[i]) );
440
441 if( i < nnlvars )
442 {
443 SCIP_CALL_THROW( SCIPcreateExprVar(scip, &varexprs[i], probdata->vars[i], NULL, NULL) );
444 }
445 }
446
447 // alloc some space for algebraic constraints
448 probdata->nconss = h.num_algebraic_cons;
449 SCIP_CALL_THROW( SCIPallocBlockMemoryArray(scip, &probdata->conss, probdata->nconss) );
450 nlconslin.resize(h.num_nl_cons);
451
452 // create empty nonlinear constraints
453 // use expression == 0, because nonlinear constraint don't like to be without an expression
454 SCIP_EXPR* dummyexpr;
455 SCIP_CALL_THROW( SCIPcreateExprValue(scip, &dummyexpr, 0.0, NULL, NULL) );
456 for( int i = 0; i < h.num_nl_cons; ++i )
457 {
458 // make up name if no names file or could not be read
459 if( !nextName(consnamesbegin, consnamesend, name) )
460 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "nlc%d", i);
461
462 SCIP_CALL_THROW( SCIPcreateConsBasicNonlinear(scip, &probdata->conss[i], name, dummyexpr, -SCIPinfinity(scip), SCIPinfinity(scip)) );
463 }
464 SCIP_CALL_THROW( SCIPreleaseExpr(scip, &dummyexpr) );
465
466 // create empty linear constraints
467 for( int i = h.num_nl_cons; i < h.num_algebraic_cons; ++i )
468 {
469 if( !nextName(consnamesbegin, consnamesend, name) )
470 (void) SCIPsnprintf(name, SCIP_MAXSTRLEN, "lc%d", i);
472 }
473
474 if( h.num_nl_cons == 0 && h.num_logical_cons == 0 && h.num_integer_vars() == 0 )
475 probdata->islp = true;
476
477 // alloc space for common expressions
478 commonexprs.resize(h.num_common_exprs());
479 }
480
481 /// receive notification of a number in a nonlinear expression
483 double value ///< value
484 )
485 {
486 SCIP_EXPR* expr;
487
489
490 // remember that we have to release this expr
491 exprstorelease.push_back(expr);
492
493 return expr;
494 }
495
496 /// receive notification of a variable reference in a nonlinear expression
498 int variableIndex ///< AMPL index of variable
499 )
500 {
501 assert(variableIndex >= 0);
502 assert(variableIndex < (int)varexprs.size());
503 assert(varexprs[variableIndex] != NULL);
504
505 return varexprs[variableIndex];
506 }
507
508 /// receive notification of a unary expression
510 mp::expr::Kind kind, ///< expression operator
511 SCIP_EXPR* child ///< argument
512 )
513 {
514 SCIP_EXPR* expr;
515
516 assert(child != NULL);
517
518 switch( kind )
519 {
520 case mp::expr::MINUS:
521 {
522 SCIP_Real minusone = -1.0;
523 SCIP_CALL_THROW( SCIPcreateExprSum(scip, &expr, 1, &child, &minusone, 0.0, NULL, NULL) );
524 break;
525 }
526
527 case mp::expr::ABS:
528 SCIP_CALL_THROW( SCIPcreateExprAbs(scip, &expr, child, NULL, NULL) );
529 break;
530
531 case mp::expr::POW2:
532 SCIP_CALL_THROW( SCIPcreateExprPow(scip, &expr, child, 2.0, NULL, NULL) );
533 break;
534
535 case mp::expr::SQRT:
536 SCIP_CALL_THROW( SCIPcreateExprPow(scip, &expr, child, 0.5, NULL, NULL) );
537 break;
538
539 case mp::expr::LOG:
540 SCIP_CALL_THROW( SCIPcreateExprLog(scip, &expr, child, NULL, NULL) );
541 break;
542
543 case mp::expr::LOG10: // 1/log(10)*log(child)
544 {
545 SCIP_EXPR* logexpr;
546 SCIP_Real factor = 1.0/log(10.0);
547 SCIP_CALL_THROW( SCIPcreateExprLog(scip, &logexpr, child, NULL, NULL) );
548 SCIP_CALL_THROW( SCIPcreateExprSum(scip, &expr, 1, &logexpr, &factor, 0.0, NULL, NULL) );
550 break;
551 }
552
553 case mp::expr::EXP:
554 SCIP_CALL_THROW( SCIPcreateExprExp(scip, &expr, child, NULL, NULL) );
555 break;
556
557 case mp::expr::SIN:
558 SCIP_CALL_THROW( SCIPcreateExprSin(scip, &expr, child, NULL, NULL) );
559 break;
560
561 case mp::expr::COS:
562 SCIP_CALL_THROW( SCIPcreateExprCos(scip, &expr, child, NULL, NULL) );
563 break;
564
565 default:
566 OnUnhandled(mp::expr::str(kind));
567 return NULL;
568 }
569
570 // remember that we have to release this expr
571 exprstorelease.push_back(expr);
572
573 return expr;
574 }
575
576 /// receive notification of a binary expression
578 mp::expr::Kind kind, ///< expression operand
579 SCIP_EXPR* firstChild, ///< first argument
580 SCIP_EXPR* secondChild ///< second argument
581 )
582 {
583 SCIP_EXPR* expr;
584 SCIP_EXPR* children[2] = { firstChild, secondChild };
585
586 assert(firstChild != NULL);
587 assert(secondChild != NULL);
588
589 switch( kind )
590 {
591 case mp::expr::ADD:
592 SCIP_CALL_THROW( SCIPcreateExprSum(scip, &expr, 2, children, NULL, 0.0, NULL, NULL) );
593 break;
594
595 case mp::expr::SUB:
596 {
597 SCIP_Real coefs[2] = { 1.0, -1.0 };
598 SCIP_CALL_THROW( SCIPcreateExprSum(scip, &expr, 2, children, coefs, 0.0, NULL, NULL) );
599 break;
600 }
601
602 case mp::expr::MUL:
603 SCIP_CALL_THROW( SCIPcreateExprProduct(scip, &expr, 2, children, 1.0, NULL, NULL) );
604 break;
605
606 case mp::expr::DIV:
607 SCIP_CALL_THROW( SCIPcreateExprPow(scip, &children[1], secondChild, -1.0, NULL, NULL) );
608 SCIP_CALL_THROW( SCIPcreateExprProduct(scip, &expr, 2, children, 1.0, NULL, NULL) );
609 SCIP_CALL_THROW( SCIPreleaseExpr(scip, &children[1]) );
610 break;
611
612 case mp::expr::POW_CONST_BASE:
613 case mp::expr::POW_CONST_EXP:
614 case mp::expr::POW:
615 // with some .nl files, we seem to get mp::expr::POW even if base or exponent is constant,
616 // so do not rely on kind but better check expr type
617 if( SCIPisExprValue(scip, secondChild) )
618 {
619 SCIP_CALL_THROW( SCIPcreateExprPow(scip, &expr, firstChild, SCIPgetValueExprValue(secondChild), NULL, NULL) );
620 break;
621 }
622
623 if( SCIPisExprValue(scip, firstChild) && SCIPgetValueExprValue(firstChild) > 0.0 )
624 {
625 // reformulate constant^y as exp(y*log(constant)), if constant > 0.0
626 // if constant < 0, we create an expression and let cons_nonlinear figure out infeasibility somehow
627 SCIP_EXPR* prod;
628
629 SCIP_Real coef = log(SCIPgetValueExprValue(firstChild)); // log(firstChild)
630 SCIP_CALL_THROW( SCIPcreateExprSum(scip, &prod, 1, &secondChild, &coef, 0.0, NULL, NULL) ); // log(firstChild)*secondChild
631 SCIP_CALL_THROW( SCIPcreateExprExp(scip, &expr, prod, NULL, NULL) ); // expr(log(firstChild)*secondChild)
632
634 break;
635 }
636
637 {
638 // reformulate x^y as exp(y*log(x))
639 SCIP_EXPR* prod;
640
641 assert(SCIPisExprValue(scip, secondChild));
642
643 SCIP_CALL_THROW( SCIPcreateExprLog(scip, &children[0], firstChild, NULL, NULL) ); // log(firstChild)
644 SCIP_CALL_THROW( SCIPcreateExprProduct(scip, &prod, 2, children, 1.0, NULL, NULL) ); // log(firstChild)*secondChild
645 SCIP_CALL_THROW( SCIPcreateExprExp(scip, &expr, prod, NULL, NULL) ); // expr(log(firstChild)*secondChild)
646
648 SCIP_CALL_THROW( SCIPreleaseExpr(scip, &children[0]) );
649 break;
650 }
651
652 default:
653 OnUnhandled(mp::expr::str(kind));
654 return NULL;
655 }
656
657 // remember that we have to release this expr
658 exprstorelease.push_back(expr);
659
660 return expr;
661 }
662
663 /// handler to create a list of terms in a sum
664 ///
665 /// NumericArgHandler is copied around, so it keeps only a pointer (with reference counting) to actual data
667 {
668 public:
669 std::shared_ptr<std::vector<SCIP_EXPR*> > v;
670
671 /// constructor
673 int num_args ///< number of terms to expect
674 )
675 : v(new std::vector<SCIP_EXPR*>())
676 {
677 v->reserve(num_args);
678 }
679
680 /// adds term to sum
681 void AddArg(
682 SCIP_EXPR* term ///< term to add
683 )
684 {
685 v->push_back(term);
686 }
687 };
688
689 /// receive notification of the beginning of a summation
691 int num_args ///< number of terms to expect
692 )
693 {
694 NumericArgHandler h(num_args);
695 return h;
696 }
697
698 /// receive notification of the end of a summation
700 NumericArgHandler handler ///< handler that handled the sum
701 )
702 {
703 SCIP_EXPR* expr;
704 SCIP_CALL_THROW( SCIPcreateExprSum(scip, &expr, (int)handler.v->size(), handler.v->data(), NULL, 0.0, NULL, NULL) );
705 // remember that we have to release this expr
706 exprstorelease.push_back(expr);
707 return expr;
708 }
709
710 /// receive notification of an objective type and the nonlinear part of an objective expression
711 void OnObj(
712 int objectiveIndex, ///< index of objective
713 mp::obj::Type type, ///< objective sense
714 SCIP_EXPR* nonlinearExpression ///< nonlinear part of objective function
715 )
716 {
717 if( objectiveIndex >= 1 )
718 OnUnhandled("multiple objective functions");
719
721
722 assert(objexpr == NULL);
723
724 if( nonlinearExpression != NULL && SCIPisExprValue(scip, nonlinearExpression) )
725 {
726 // handle objective constant by adding a fixed variable for it
727 SCIP_VAR* objconstvar;
728 SCIP_Real objconst = SCIPgetValueExprValue(nonlinearExpression);
729
730 SCIP_CALL_THROW( SCIPcreateVarBasic(scip, &objconstvar, "objconstant", objconst, objconst, 1.0, SCIP_VARTYPE_CONTINUOUS) );
731 SCIP_CALL_THROW( SCIPaddVar(scip, objconstvar) );
732 SCIP_CALL_THROW( SCIPreleaseVar(scip, &objconstvar) );
733 }
734 else
735 {
736 objexpr = nonlinearExpression;
737 }
738 }
739
740 /// receive notification of an algebraic constraint expression
742 int constraintIndex, ///< index of constraint
743 SCIP_EXPR* expr ///< nonlinear part of constraint
744 )
745 {
746 if( expr != NULL )
747 {
748 SCIP_CALL_THROW( SCIPchgExprNonlinear(scip, probdata->conss[constraintIndex], expr) );
749 }
750 }
751
752 /// receives notification of a logical constraint expression
754 int index,
755 LogicalExpr expr
756 )
757 {
758 if( expr != NULL )
759 {
760 SCIP_CONS* cons;
761 SCIP_CALL_THROW( SCIPcreateConsBasicNonlinear(scip, &cons, "logiccons", expr, 1.0, 1.0) );
764 }
765 }
766
767 /// handles linear part of a common expression
768 /// sets up a sum expression, if the linear part isn't empty
770 {
771 private:
772 AMPLProblemHandler& amplph;
773 SCIP_EXPR* commonexpr;
774
775 public:
776 /// constructor
778 AMPLProblemHandler& amplph_, ///< problem handler
779 int index, ///< index of common expression
780 int num_linear_terms///< number of terms to expect
781 )
782 : amplph(amplph_),
783 commonexpr(NULL)
784 {
785 if( num_linear_terms > 0 )
786 {
787 SCIP_CALL_THROW( SCIPcreateExprSum(amplph.scip, &commonexpr, 0, NULL, NULL, 0.0, NULL, NULL) );
788 amplph.commonexprs[index] = commonexpr;
789 amplph.exprstorelease.push_back(commonexpr);
790 }
791 }
792
793 /// receives notification of a term in the linear expression
795 int var_index, ///< AMPL index of variable
796 double coef ///< variable coefficient
797 )
798 {
799 assert(commonexpr != NULL);
800
801 if( coef == 0.0 )
802 return;
803
804 if( var_index < (int)amplph.varexprs.size() )
805 {
806 SCIP_CALL_THROW( SCIPappendExprSumExpr(amplph.scip, commonexpr, amplph.varexprs[var_index], coef) );
807 }
808 else
809 {
810 // the index variable is linear (not sure this can happen here)
811 assert(var_index < amplph.probdata->nvars);
812 SCIP_EXPR* varexpr;
813 SCIP_CALL_THROW( SCIPcreateExprVar(amplph.scip, &varexpr, amplph.probdata->vars[var_index], NULL, NULL) );
814 SCIP_CALL_THROW( SCIPappendExprSumExpr(amplph.scip, commonexpr, varexpr, coef) );
815 SCIP_CALL_THROW( SCIPreleaseExpr(amplph.scip, &varexpr) );
816 }
817 }
818 };
819
820 /// receive notification of the beginning of a common expression (defined variable)
822 int index, ///< index of common expression
823 int num_linear_terms ///< number of terms to expect
824 )
825 {
826 assert(index >= 0);
827 assert(index < (int)commonexprs.size());
828
829 return LinearExprHandler(*this, index, num_linear_terms);
830 }
831
832 /// receive notification of the end of a common expression
834 int index, ///< index of common expression
835 SCIP_EXPR* expr, ///< nonlinear part of common expression
836 int /* position */ ///< argument that doesn't seem to have any purpose
837 )
838 {
839 if( commonexprs[index] != NULL )
840 {
841 // add expr, if any, to linear part
842 if( expr != NULL )
843 {
844 SCIP_CALL_THROW( SCIPappendExprSumExpr(scip, commonexprs[index], expr, 1.0) );
845 }
846 }
847 else if( expr != NULL )
848 {
849 commonexprs[index] = expr;
850 }
851 }
852
853 /// receive notification of a common expression (defined variable) reference
855 int expr_index ///< index of common expression
856 )
857 {
858 assert(expr_index >= 0);
859 assert(expr_index < (int)commonexprs.size());
860 assert(commonexprs[expr_index] != NULL);
861 return commonexprs[expr_index];
862 }
863
864 /// receive notification of variable bounds
866 int variableIndex, ///< AMPL index of variable
867 double variableLB, ///< variable lower bound
868 double variableUB ///< variable upper bound
869 )
870 {
871 assert(variableIndex >= 0);
872 assert(variableIndex < probdata->nvars);
873
874 // as far as I see, ampl::mp gives -inf, +inf for no-bounds, which is always beyond SCIPinfinity()
875 // we ignore bounds outside [-scipinfinity,scipinfinity] here
876 // for binary variables, we also ignore bounds outside [0,1]
877 SCIP_Bool binary = (SCIPvarGetType(probdata->vars[variableIndex]) == SCIP_VARTYPE_BINARY);
878 if( variableLB > (binary ? 0.0 : -SCIPinfinity(scip)) )
879 {
880 SCIP_CALL_THROW( SCIPchgVarLbGlobal(scip, probdata->vars[variableIndex], variableLB) );
881 }
882 if( variableUB < (binary ? 1.0 : SCIPinfinity(scip)) )
883 {
884 SCIP_CALL_THROW( SCIPchgVarUbGlobal(scip, probdata->vars[variableIndex], variableUB) );
885 }
886 }
887
888 /// receive notification of constraint sides
890 int index, ///< AMPL index of constraint
891 double lb, ///< constraint left-hand-side
892 double ub ///< constraint right-hand-side
893 )
894 {
895 assert(index >= 0);
896 assert(index < probdata->nconss);
897
898 // nonlinear constraints are first
899 if( index < (int)nlconslin.size() )
900 {
901 if( !SCIPisInfinity(scip, -lb) )
902 {
903 SCIP_CALL_THROW( SCIPchgLhsNonlinear(scip, probdata->conss[index], lb) );
904 }
905 if( !SCIPisInfinity(scip, ub) )
906 {
907 SCIP_CALL_THROW( SCIPchgRhsNonlinear(scip, probdata->conss[index], ub) );
908 }
909 }
910 else
911 {
912 /* there are asserts in cons_linear.c:chgLhs/chgRhs to forbid changing a side
913 * from one infinity to another; to workaround this, we change the side to 0.0 first
914 */
915 if( !SCIPisInfinity(scip, -lb) )
916 {
917 if( SCIPisInfinity(scip, lb) )
918 {
919 SCIP_CALL_THROW( SCIPchgLhsLinear(scip, probdata->conss[index], 0.0) );
920 }
921 SCIP_CALL_THROW( SCIPchgLhsLinear(scip, probdata->conss[index], lb) );
922 }
923 if( !SCIPisInfinity(scip, ub) )
924 {
925 if( SCIPisInfinity(scip, -ub) )
926 {
927 SCIP_CALL_THROW( SCIPchgRhsLinear(scip, probdata->conss[index], 0.0) );
928 }
929 SCIP_CALL_THROW( SCIPchgRhsLinear(scip, probdata->conss[index], ub) );
930 }
931 }
932 }
933
934 /// receive notification of the initial value for a variable
936 int var_index, ///< AMPL index of variable
937 double value ///< initial primal value of variable
938 )
939 {
940 if( initsol == NULL )
941 {
942 SCIP_CALL_THROW( SCIPcreateSol(scip, &initsol, NULL) );
943 }
944
945 SCIP_CALL_THROW( SCIPsetSolVal(scip, initsol, probdata->vars[var_index], value) );
946 }
947
948 /// receives notification of the initial value for a dual variable
950 int /* con_index */, ///< AMPL index of constraint
951 double /* value */ ///< initial dual value of constraint
952 )
953 {
954 // ignore initial dual value
955 }
956
957 /// receives notification of Jacobian column sizes
958 ColumnSizeHandler OnColumnSizes()
959 {
960 /// use ColumnSizeHandler from upper class, which does nothing
961 return ColumnSizeHandler();
962 }
963
964 /// handling of suffices for variable and constraint flags and SOS constraints
965 ///
966 /// regarding SOS in AMPL, see https://ampl.com/faqs/how-can-i-use-the-solvers-special-ordered-sets-feature/
967 /// we pass the .ref suffix as weight to the SOS constraint handlers
968 /// for a SOS2, the weights determine the order of variables in the set
969 template<typename T> class SuffixHandler
970 {
971 private:
972 AMPLProblemHandler& amplph;
973
974 // type of suffix that is handled, or IGNORE if unsupported suffix
975 enum
976 {
977 IGNORE,
978 CONSINITIAL,
979 CONSSEPARATE,
980 CONSENFORCE,
981 CONSCHECK,
982 CONSPROPAGATE,
983 CONSDYNAMIC,
984 CONSREMOVABLE,
985 VARINITIAL,
986 VARREMOVABLE,
987 VARSOSNO,
988 VARREF,
989 } suffix;
990
991 public:
992 /// constructor
994 AMPLProblemHandler& amplph_, ///< problem handler
995 fmt::StringRef name, ///< name of suffix
996 mp::suf::Kind kind ///< whether suffix applies to var, cons, etc
997 )
998 : amplph(amplph_),
999 suffix(IGNORE)
1000 {
1001 switch( kind )
1002 {
1003 case mp::suf::Kind::CON:
1004 if( strncmp(name.data(), "initial", name.size()) == 0 )
1005 {
1006 suffix = CONSINITIAL;
1007 }
1008 else if( strncmp(name.data(), "separate", name.size()) == 0 )
1009 {
1010 suffix = CONSSEPARATE;
1011 }
1012 else if( strncmp(name.data(), "enforce", name.size()) == 0 )
1013 {
1014 suffix = CONSENFORCE;
1015 }
1016 else if( strncmp(name.data(), "check", name.size()) == 0 )
1017 {
1018 suffix = CONSCHECK;
1019 }
1020 else if( strncmp(name.data(), "propagate", name.size()) == 0 )
1021 {
1022 suffix = CONSPROPAGATE;
1023 }
1024 else if( strncmp(name.data(), "dynamic", name.size()) == 0 )
1025 {
1026 suffix = CONSDYNAMIC;
1027 }
1028 else if( strncmp(name.data(), "removable", name.size()) == 0 )
1029 {
1030 suffix = CONSREMOVABLE;
1031 }
1032 else
1033 {
1034 SCIPverbMessage(amplph.scip, SCIP_VERBLEVEL_HIGH, NULL, "Unknown constraint suffix <%.*s>. Ignoring.\n", (int)name.size(), name.data());
1035 }
1036 break;
1037
1038 case mp::suf::Kind::CON_BIT:
1039 SCIPverbMessage(amplph.scip, SCIP_VERBLEVEL_HIGH, NULL, "Unknown constraint bit suffix <%.*s>. Ignoring.\n", (int)name.size(), name.data());
1040 break;
1041
1042 case mp::suf::Kind::VAR:
1043 {
1044 if( strncmp(name.data(), "initial", name.size()) == 0 )
1045 {
1046 suffix = VARINITIAL;
1047 }
1048 else if( strncmp(name.data(), "removable", name.size()) == 0 )
1049 {
1050 suffix = VARREMOVABLE;
1051 }
1052 else if( strncmp(name.data(), "sosno", name.size()) == 0 )
1053 {
1054 // SOS membership
1055 suffix = VARSOSNO;
1056 }
1057 else if( strncmp(name.data(), "ref", name.size()) == 0 )
1058 {
1059 // SOS weights
1060 suffix = VARREF;
1061 amplph.sosweights.resize(amplph.probdata->nvars, 0);
1062 }
1063 else
1064 {
1065 SCIPverbMessage(amplph.scip, SCIP_VERBLEVEL_HIGH, NULL, "Unknown variable suffix <%.*s>. Ignoring.\n", (int)name.size(), name.data());
1066 }
1067 break;
1068
1069 case mp::suf::Kind::VAR_BIT:
1070 SCIPverbMessage(amplph.scip, SCIP_VERBLEVEL_HIGH, NULL, "Unknown variable bit suffix <%.*s>. Ignoring.\n", (int)name.size(), name.data());
1071 break;
1072
1073 case mp::suf::Kind::OBJ:
1074 SCIPverbMessage(amplph.scip, SCIP_VERBLEVEL_HIGH, NULL, "Unknown objective suffix <%.*s>. Ignoring.\n", (int)name.size(), name.data());
1075 break;
1076
1077 case mp::suf::Kind::OBJ_BIT:
1078 SCIPverbMessage(amplph.scip, SCIP_VERBLEVEL_HIGH, NULL, "Unknown objective bit suffix <%.*s>. Ignoring.\n", (int)name.size(), name.data());
1079 break;
1080
1082 SCIPverbMessage(amplph.scip, SCIP_VERBLEVEL_HIGH, NULL, "Unknown problem suffix <%.*s>. Ignoring.\n", (int)name.size(), name.data());
1083 break;
1084
1085 case mp::suf::Kind::PROB_BIT:
1086 SCIPverbMessage(amplph.scip, SCIP_VERBLEVEL_HIGH, NULL, "Unknown problem bit suffix <%.*s>. Ignoring.\n", (int)name.size(), name.data());
1087 break;
1088 }
1089 }
1090 }
1091
1093 int index, ///< index of variable, constraint, etc
1094 T value ///< value of suffix
1095 )
1096 {
1097 assert(index >= 0);
1098 switch( suffix )
1099 {
1100 case IGNORE :
1101 return;
1102
1103 case CONSINITIAL:
1104 SCIP_CALL_THROW( SCIPsetConsInitial(amplph.scip, amplph.probdata->conss[index], value == 1) );
1105 break;
1106
1107 case CONSSEPARATE:
1108 SCIP_CALL_THROW( SCIPsetConsSeparated(amplph.scip, amplph.probdata->conss[index], value == 1) );
1109 break;
1110
1111 case CONSENFORCE:
1112 SCIP_CALL_THROW( SCIPsetConsEnforced(amplph.scip, amplph.probdata->conss[index], value == 1) );
1113 break;
1114
1115 case CONSCHECK:
1116 SCIP_CALL_THROW( SCIPsetConsChecked(amplph.scip, amplph.probdata->conss[index], value == 1) );
1117 break;
1118
1119 case CONSPROPAGATE:
1120 SCIP_CALL_THROW( SCIPsetConsPropagated(amplph.scip, amplph.probdata->conss[index], value == 1) );
1121 break;
1122
1123 case CONSDYNAMIC:
1124 SCIP_CALL_THROW( SCIPsetConsDynamic(amplph.scip, amplph.probdata->conss[index], value == 1) );
1125 break;
1126
1127 case CONSREMOVABLE:
1128 SCIP_CALL_THROW( SCIPsetConsRemovable(amplph.scip, amplph.probdata->conss[index], value == 1) );
1129 break;
1130
1131 case VARINITIAL:
1132 assert(index < amplph.probdata->nvars);
1133 SCIP_CALL_THROW( SCIPvarSetInitial(amplph.probdata->vars[index], value == 1) );
1134 break;
1135
1136 case VARREMOVABLE:
1137 assert(index < amplph.probdata->nvars);
1138 SCIP_CALL_THROW( SCIPvarSetRemovable(amplph.probdata->vars[index], value == 1) );
1139 break;
1140
1141 case VARSOSNO:
1142 // remember that variable index belongs to SOS identified by value
1143 amplph.sosvars[(int)value].push_back(index);
1144 break;
1145
1146 case VARREF:
1147 // remember that variable index has weight value
1148 amplph.sosweights[index] = (int)value;
1149 break;
1150 }
1151 }
1152 };
1153
1155 /// receive notification of an integer suffix
1157 fmt::StringRef name, ///< suffix name, not null-terminated
1158 mp::suf::Kind kind, ///< suffix kind
1159 int /*num_values*/ ///< number of values to expect
1160 )
1161 {
1162 return IntSuffixHandler(*this, name, kind);
1163 }
1164
1166 /// receive notification of a double suffix
1168 fmt::StringRef name, ///< suffix name, not null-terminated
1169 mp::suf::Kind kind, ///< suffix kind
1170 int /*num_values*/ ///< number of values to expect
1171 )
1172 {
1173 return DblSuffixHandler(*this, name, kind);
1174 }
1175
1176 /// handles receiving the linear part of an objective or constraint
1177 ///
1178 /// for objective, set the objective-coefficient of the variable
1179 /// for linear constraints, add to the constraint
1180 /// for nonlinear constraints, add to nlconslin vector; adding to constraint later
1182 {
1183 private:
1184 AMPLProblemHandler& amplph;
1185 int constraintIndex;
1186
1187 public:
1188 // constructor for constraint
1190 AMPLProblemHandler& amplph_, ///< problem handler
1191 int constraintIndex_///< constraint index
1192 )
1193 : amplph(amplph_),
1194 constraintIndex(constraintIndex_)
1195 {
1196 assert(constraintIndex_ >= 0);
1197 assert(constraintIndex_ < amplph.probdata->nconss);
1198 }
1199
1200 // constructor for linear objective
1202 AMPLProblemHandler& amplph_ ///< problem handler
1203 )
1204 : amplph(amplph_),
1205 constraintIndex(-1)
1206 { }
1207
1209 int variableIndex, ///< AMPL index of variable
1210 double coefficient ///< coefficient of variable
1211 )
1212 {
1213 assert(variableIndex >= 0);
1214 assert(variableIndex < amplph.probdata->nvars);
1215
1216 if( coefficient == 0.0 )
1217 return;
1218
1219 if( constraintIndex < 0 )
1220 {
1221 SCIP_CALL_THROW( SCIPchgVarObj(amplph.scip, amplph.probdata->vars[variableIndex], coefficient) );
1222 }
1223 else if( constraintIndex < (int)amplph.nlconslin.size() )
1224 {
1225 amplph.nlconslin[constraintIndex].push_back(std::pair<SCIP_Real, SCIP_VAR*>(coefficient, amplph.probdata->vars[variableIndex]));
1226 }
1227 else
1228 {
1229 SCIP_CONS* lincons = amplph.probdata->conss[constraintIndex];
1230 SCIP_CALL_THROW( SCIPaddCoefLinear(amplph.scip, lincons, amplph.probdata->vars[variableIndex], coefficient) );
1231 }
1232 }
1233 };
1234
1236
1237 /// receive notification of the linear part of an objective
1239 int objectiveIndex, ///< index of objective
1240 int /* numLinearTerms *////< number of terms to expect
1241 )
1242 {
1243 if( objectiveIndex >= 1 )
1244 OnUnhandled("multiple objective functions");
1245
1246 return LinearObjHandler(*this);
1247 }
1248
1250
1251 /// receive notification of the linear part of a constraint
1253 int constraintIndex, ///< index of constraint
1254 int /* numLinearTerms *////< number of terms to expect
1255 )
1256 {
1257 return LinearConHandler(*this, constraintIndex);
1258 }
1259
1260 /// receives notification of a `Boolean value <mp::expr::BOOL>`
1261 LogicalExpr OnBool(
1262 bool value
1263 )
1264 {
1265 SCIP_EXPR* expr;
1266
1267 SCIP_CALL_THROW( SCIPcreateExprValue(scip, &expr, value ? 1.0 : 0.0, NULL, NULL) );
1268
1269 // remember that we have to release this expr
1270 exprstorelease.push_back(expr);
1271
1272 return expr;
1273 }
1274
1275 /// receives notification of a `logical not <mp::expr::NOT>`
1276 LogicalExpr OnNot(
1277 LogicalExpr arg
1278 )
1279 {
1280 SCIP_EXPR* expr;
1281 SCIP_VAR* var;
1282 SCIP_Bool val;
1283
1284 LogicalExprToVarVal(arg, var, val);
1285 if( var != NULL )
1286 {
1287 SCIP_CALL_THROW( SCIPgetNegatedVar(scip, var, &var) );
1289 }
1290 else
1291 {
1292 SCIP_CALL_THROW( SCIPcreateExprValue(scip, &expr, val ? 1.0 : 0.0, NULL, NULL) );
1293 }
1294
1295 // remember that we have to release this expr
1296 exprstorelease.push_back(expr);
1297
1298 return expr;
1299 }
1300
1301 /// receives notification of a `binary logical expression <mp::expr::FIRST_BINARY_LOGICAL>`
1302 LogicalExpr OnBinaryLogical(
1303 mp::expr::Kind kind,
1304 LogicalExpr lhs,
1305 LogicalExpr rhs
1306 )
1307 {
1308 SCIP_VAR* lhsvar = NULL;
1309 SCIP_VAR* rhsvar = NULL;
1310 SCIP_Bool lhsval;
1311 SCIP_Bool rhsval;
1312 SCIP_EXPR* expr;
1313
1314 assert(lhs != NULL);
1315 assert(rhs != NULL);
1316
1317 LogicalExprToVarVal(lhs, lhsvar, lhsval);
1318 LogicalExprToVarVal(rhs, rhsvar, rhsval);
1319
1320 switch( kind )
1321 {
1322 case mp::expr::OR:
1323 {
1324 if( lhsvar == NULL && rhsvar == NULL )
1325 {
1326 SCIP_CALL_THROW( SCIPcreateExprValue(scip, &expr, lhsval != 0.0 || rhsval != 0.0 ? 1.0 : 0.0, NULL, NULL) );
1327 exprstorelease.push_back(expr);
1328 break;
1329 }
1330
1331 if( (lhsvar == NULL && lhsval != 0.0) || (rhsvar == NULL && rhsval != 0.0) )
1332 {
1333 /* nonzero or rhs == 1, lhs or nonzero == 1 */
1335 exprstorelease.push_back(expr);
1336 break;
1337 }
1338
1339 if( lhsvar == NULL )
1340 {
1341 /* zero or rhs == rhs */
1342 assert(lhsval == 0.0);
1343 expr = rhs;
1344 break;
1345 }
1346
1347 if( rhsvar == NULL )
1348 {
1349 /* lhs or zero == lhs */
1350 assert(rhsval == 0.0);
1351 expr = lhs;
1352 break;
1353 }
1354
1355 /* create new resvar and constraint resvar = lhsvar or rhsvar */
1356 SCIP_VAR* vars[2];
1357 SCIP_VAR* resvar;
1358 SCIP_CONS* cons;
1359
1360 std::string name = std::string("_logic") + std::to_string((long long)logiccount++);
1361 SCIP_CALL_THROW( SCIPcreateVarBasic(scip, &resvar, name.c_str(), 0.0, 1.0, 0.0, SCIP_VARTYPE_BINARY) );
1362 SCIP_CALL_THROW( SCIPaddVar(scip, resvar) );
1363 SCIP_CALL_THROW( SCIPcreateExprVar(scip, &expr, resvar, NULL, NULL) );
1364 exprstorelease.push_back(expr);
1365
1366 vars[0] = lhsvar;
1367 vars[1] = rhsvar;
1368 name += "def";
1369 SCIP_CALL_THROW( SCIPcreateConsBasicOr(scip, &cons, name.c_str(), resvar, 2, vars) );
1371
1372 SCIP_CALL_THROW( SCIPreleaseVar(scip, &resvar) );
1374
1375 break;
1376 }
1377
1378 case mp::expr::AND:
1379 {
1380 if( lhsvar == NULL && rhsvar == NULL )
1381 {
1382 SCIP_CALL_THROW( SCIPcreateExprValue(scip, &expr, lhsval != 0.0 && rhsval != 0.0 ? 1.0 : 0.0, NULL, NULL) );
1383 exprstorelease.push_back(expr);
1384 break;
1385 }
1386
1387 if( (lhsvar == NULL && lhsval == 0.0) || (rhsvar == NULL && rhsval == 0.0) )
1388 {
1389 /* zero and rhs == 0, lhs and zero == 0 */
1391 exprstorelease.push_back(expr);
1392 break;
1393 }
1394
1395 if( lhsvar == NULL )
1396 {
1397 /* nonzero and rhs == rhs */
1398 assert(lhsval != 0.0);
1399 expr = rhs;
1400 break;
1401 }
1402
1403 if( rhsvar == NULL )
1404 {
1405 /* lhs and nonzero == lhs */
1406 assert(rhsval != 0.0);
1407 expr = lhs;
1408 break;
1409 }
1410
1411 /* create new resvar and constraint resvar = lhsvar and rhsvar */
1412 SCIP_VAR* vars[2];
1413 SCIP_VAR* resvar;
1414 SCIP_CONS* cons;
1415
1416 std::string name = std::string("_logic") + std::to_string((long long)logiccount++);
1417 SCIP_CALL_THROW( SCIPcreateVarBasic(scip, &resvar, name.c_str(), 0.0, 1.0, 0.0, SCIP_VARTYPE_BINARY) );
1418 SCIP_CALL_THROW( SCIPaddVar(scip, resvar) );
1419 SCIP_CALL_THROW( SCIPcreateExprVar(scip, &expr, resvar, NULL, NULL) );
1420 exprstorelease.push_back(expr);
1421
1422 vars[0] = lhsvar;
1423 vars[1] = rhsvar;
1424 name += "def";
1425 SCIP_CALL_THROW( SCIPcreateConsBasicAnd(scip, &cons, name.c_str(), resvar, 2, vars) );
1427
1428 SCIP_CALL_THROW( SCIPreleaseVar(scip, &resvar) );
1430
1431 break;
1432 }
1433
1434 case mp::expr::IFF:
1435 {
1436 // the IFF operator returns 1 if both operands are nonzero or both are zero and returns zero otherwise
1437 // so this is lhs == rhs
1438 if( lhsvar == NULL && rhsvar == NULL )
1439 {
1440 SCIP_CALL_THROW( SCIPcreateExprValue(scip, &expr, lhsval == rhsval ? 1.0 : 0.0, NULL, NULL) );
1441 exprstorelease.push_back(expr);
1442 break;
1443 }
1444
1445 if( lhsvar == NULL )
1446 {
1447 std::swap(lhs, rhs);
1448 std::swap(lhsval, rhsval);
1449 std::swap(lhsvar, rhsvar);
1450 }
1451 assert(lhsvar != NULL);
1452
1453 if( rhsvar == NULL )
1454 {
1455 // expression is lhsvar == true
1456 // so we return lhsvar or ~lhsvar
1457 if( rhsval == TRUE )
1458 {
1459 expr = lhs;
1460 }
1461 else
1462 {
1463 SCIP_CALL_THROW( SCIPgetNegatedVar(scip, lhsvar, &lhsvar) );
1464 SCIP_CALL_THROW( SCIPcreateExprVar(scip, &expr, lhsvar, NULL, NULL) );
1465 exprstorelease.push_back(expr);
1466 }
1467 break;
1468 }
1469
1470 // expressions is lhsvar == rhsvar
1471 // we create a new variable auxvar and add a constraint xor(auxvar, lhsvar, rhsvar, TRUE)
1472 // to ensure auxvar = (lhsvar == rhsvar)
1473 SCIP_VAR* vars[3];
1474 SCIP_CONS* cons;
1475 std::string name = std::string("_logic") + std::to_string((long long)logiccount++);
1476 SCIP_CALL_THROW( SCIPcreateVarBasic(scip, &vars[0], name.c_str(), 0.0, 1.0, 0.0, SCIP_VARTYPE_BINARY) );
1477 SCIP_CALL_THROW( SCIPaddVar(scip, vars[0]) );
1478 SCIP_CALL_THROW( SCIPcreateExprVar(scip, &expr, vars[0], NULL, NULL) );
1479 exprstorelease.push_back(expr);
1480
1481 vars[1] = lhsvar;
1482 vars[2] = rhsvar;
1483 name += "def";
1484 SCIP_CALL_THROW( SCIPcreateConsBasicXor(scip, &cons, name.c_str(), TRUE, 3, vars) );
1486
1487 SCIP_CALL_THROW( SCIPreleaseVar(scip, &vars[0]) );
1489
1490 break;
1491 }
1492
1493 default:
1494 OnUnhandled(mp::expr::str(kind));
1495 return NULL;
1496 }
1497
1498 return expr;
1499 }
1500
1501 /// receives notification of a `relational expression <mp::expr::FIRST_RELATIONAL>`
1502 /// we only handle equality or inequality between binary variables and boolean values here
1503 LogicalExpr OnRelational(
1504 mp::expr::Kind kind,
1505 NumericExpr lhs,
1506 NumericExpr rhs
1507 )
1508 {
1509 SCIP_VAR* lhsvar = NULL;
1510 SCIP_VAR* rhsvar = NULL;
1511 SCIP_Bool lhsval;
1512 SCIP_Bool rhsval;
1513 SCIP_EXPR* expr;
1514
1515 assert(lhs != NULL);
1516 assert(rhs != NULL);
1517
1518 LogicalExprToVarVal(lhs, lhsvar, lhsval);
1519 LogicalExprToVarVal(rhs, rhsvar, rhsval);
1520
1521 switch( kind )
1522 {
1523 case mp::expr::EQ:
1524 case mp::expr::NE:
1525 {
1526 bool isne = (kind == mp::expr::NE);
1527 if( lhsvar == NULL && rhsvar == NULL )
1528 {
1529 SCIP_CALL_THROW( SCIPcreateExprValue(scip, &expr, lhsval == rhsval ? (isne ? 0.0 : 1.0) : (isne ? 1.0 : 0.0), NULL, NULL) );
1530 exprstorelease.push_back(expr);
1531 break;
1532 }
1533
1534 if( lhsvar == NULL )
1535 {
1536 std::swap(lhs, rhs);
1537 std::swap(lhsval, rhsval);
1538 std::swap(lhsvar, rhsvar);
1539 }
1540 assert(lhsvar != NULL);
1541
1542 if( rhsvar == NULL )
1543 {
1544 // expression is lhsvar == true or lhsvar == false if EQ
1545 // so we return lhsvar or ~lhsvar, opposite if NE
1546 if( rhsval == (isne ? FALSE : TRUE) )
1547 {
1548 expr = lhs;
1549 }
1550 else
1551 {
1552 SCIP_CALL_THROW( SCIPgetNegatedVar(scip, lhsvar, &lhsvar) );
1553 SCIP_CALL_THROW( SCIPcreateExprVar(scip, &expr, lhsvar, NULL, NULL) );
1554 exprstorelease.push_back(expr);
1555 }
1556 break;
1557 }
1558
1559 // expressions is lhsvar == rhsvar or lhsvar != rhsvar
1560 // we create a new variable auxvar and add a constraint xor(auxvar, lhsvar, rhsvar, isne ? FALSE : TRUE)
1561 // to ensure auxvar = (lhsvar == rhsvar) or auxvar = (lhsvar != rhsvar)
1562
1563 SCIP_VAR* vars[3];
1564 SCIP_CONS* cons;
1565 std::string name = std::string("_logic") + std::to_string((long long)logiccount++);
1566 SCIP_CALL_THROW( SCIPcreateVarBasic(scip, &vars[0], name.c_str(), 0.0, 1.0, 0.0, SCIP_VARTYPE_BINARY) );
1567 SCIP_CALL_THROW( SCIPaddVar(scip, vars[0]) );
1568 SCIP_CALL_THROW( SCIPcreateExprVar(scip, &expr, vars[0], NULL, NULL) );
1569 exprstorelease.push_back(expr);
1570
1571 vars[1] = lhsvar;
1572 vars[2] = rhsvar;
1573 name += "def";
1574 SCIP_CALL_THROW( SCIPcreateConsBasicXor(scip, &cons, name.c_str(), isne ? FALSE : TRUE, 3, vars) );
1576
1577 SCIP_CALL_THROW( SCIPreleaseVar(scip, &vars[0]) );
1579
1580 break;
1581 }
1582
1583 default:
1584 OnUnhandled(mp::expr::str(kind));
1585 return NULL;
1586 }
1587
1588 return expr;
1589 }
1590
1591 /// receive notification of the end of the input
1592 ///
1593 /// - setup all nonlinear constraints and add them to SCIP
1594 /// - add linear constraints to SCIP (should be after nonlinear ones to respect order in .nl file)
1595 /// - add initial solution, if initial values were given
1597 {
1598 // turn nonlinear objective into constraint
1599 // min f(x) -> min z s.t. f(x) - z <= 0
1600 // max f(x) -> max z s.t. 0 <= f(x) - z
1601 if( objexpr != NULL )
1602 {
1603 SCIP_CONS* objcons;
1604 SCIP_VAR* objvar;
1605
1607 SCIP_CALL_THROW( SCIPaddVar(scip, objvar) );
1608
1609 SCIP_CALL_THROW( SCIPcreateConsBasicNonlinear(scip, &objcons, "objcons", objexpr,
1612 SCIP_CALL_THROW( SCIPaddLinearVarNonlinear(scip, objcons, objvar, -1.0) );
1613 SCIP_CALL_THROW( SCIPaddCons(scip, objcons) );
1614
1615 if( initsol != NULL )
1616 {
1617 /* compute value for objvar in initial solution from other variable values */
1618 SCIP_CALL_THROW( SCIPevalExpr(scip, objexpr, initsol, 0) );
1619 if( SCIPexprGetEvalValue(objexpr) != SCIP_INVALID )
1620 {
1621 SCIPsetSolVal(scip, initsol, objvar, SCIPexprGetEvalValue(objexpr));
1622 }
1623 else
1624 {
1625 SCIPwarningMessage(scip, "Objective function could not be evaluated in initial point. Domain error.");
1626 }
1627 }
1628
1629 SCIP_CALL_THROW( SCIPreleaseCons(scip, &objcons) );
1630 SCIP_CALL_THROW( SCIPreleaseVar(scip, &objvar) );
1631 }
1632
1633 // add linear terms to expressions of nonlinear constraints (should be ok to do this one-by-one for now)
1634 for( size_t i = 0; i < nlconslin.size(); ++i )
1635 {
1636 for( size_t j = 0; j < nlconslin[i].size(); ++j )
1637 {
1638 SCIP_CALL_THROW( SCIPaddLinearVarNonlinear(scip, probdata->conss[i], nlconslin[i][j].second, nlconslin[i][j].first) );
1639 }
1640 }
1641
1642 // add constraints
1643 for( int i = 0; i < probdata->nconss; ++i )
1644 {
1645 SCIP_CALL_THROW( SCIPaddCons(scip, probdata->conss[i]) );
1646 }
1647
1648 // add SOS constraints
1649 std::vector<SCIP_VAR*> setvars; // variables in one SOS
1650 std::vector<SCIP_Real> setweights; // weights for one SOS
1651 if( !sosvars.empty() )
1652 {
1653 setvars.resize(probdata->nvars);
1654 probdata->islp = false;
1655 }
1656 if( !sosweights.empty() )
1657 setweights.resize(probdata->nvars);
1658 for( std::map<int, std::vector<int> >::iterator sosit(sosvars.begin()); sosit != sosvars.end(); ++sosit )
1659 {
1660 assert(sosit->first != 0);
1661 assert(!sosit->second.empty());
1662
1663 // a negative SOS identifier means SOS2
1664 bool issos2 = sosit->first < 0;
1665
1666 if( issos2 && sosweights.empty() )
1667 {
1668 // if no .ref suffix was given for a SOS2 constraint, then we consider this as an error
1669 // since the weights determine the order
1670 // for a SOS1, the weights only specify branching preference, so can treat them as optional
1671 OnUnhandled("SOS2 requires variable .ref suffix");
1672 }
1673
1674 for( size_t i = 0; i < sosit->second.size(); ++i )
1675 {
1676 int varidx = sosit->second[i];
1677 setvars[i] = probdata->vars[varidx]; /* cppcheck-suppress unreadVariable */
1678
1679 if( issos2 && sosweights[varidx] == 0 )
1680 // 0 is the default if no ref was given for a variable; we don't allow this for SOS2
1681 OnUnhandled("Missing .ref value for SOS2 variable");
1682 if( !sosweights.empty() )
1683 setweights[i] = (SCIP_Real)sosweights[varidx];
1684 }
1685
1686 SCIP_CONS* cons;
1687 char name[20];
1688 if( !issos2 )
1689 {
1690 (void) SCIPsnprintf(name, 20, "sos1_%d", sosit->first);
1691 SCIP_CALL_THROW( SCIPcreateConsBasicSOS1(scip, &cons, name, sosit->second.size(), setvars.data(), setweights.empty() ? NULL : setweights.data()) );
1692 }
1693 else
1694 {
1695 (void) SCIPsnprintf(name, 20, "sos2_%d", -sosit->first);
1696 SCIP_CALL_THROW( SCIPcreateConsBasicSOS2(scip, &cons, name, sosit->second.size(), setvars.data(), setweights.data()) );
1697 }
1700 }
1701
1702 // add initial solution
1703 if( initsol != NULL )
1704 {
1705 SCIP_Bool stored;
1706 SCIP_CALL_THROW( SCIPaddSolFree(scip, &initsol, &stored) );
1707 }
1708
1709 // release expressions
1711 }
1712
1713 /// releases expressions and linear constraints from data
1714 ///
1715 /// should be called if there was an error while reading the .nl file
1716 /// this is not in the destructor, because we want to return SCIP_RETCODE
1718 {
1719 // release initial sol (in case EndInput() wasn't called)
1720 if( initsol != NULL )
1721 {
1722 SCIP_CALL( SCIPfreeSol(scip, &initsol) );
1723 }
1724
1725 // release created expressions (they should all be used in other expressions or constraints now)
1726 while( !exprstorelease.empty() )
1727 {
1728 SCIP_CALL( SCIPreleaseExpr(scip, &exprstorelease.back()) );
1729 exprstorelease.pop_back();
1730 }
1731
1732 // release variable expressions (they should all be used in other expressions or constraints now)
1733 while( !varexprs.empty() )
1734 {
1735 SCIP_CALL( SCIPreleaseExpr(scip, &varexprs.back()) );
1736 varexprs.pop_back();
1737 }
1738
1739 return SCIP_OKAY;
1740 }
1741};
1742
1743class SCIPNLFeeder : public mp::NLFeeder<SCIPNLFeeder, SCIP_EXPR*>
1744{
1745private:
1746 SCIP* scip; ///< SCIP data structure (problem to write)
1747 const char* probname; ///< problem name
1748 SCIP_OBJSENSE objsense; ///< objective sense
1749 SCIP_Real objscale; ///< objective scale
1750 SCIP_Real objoffset; ///< objective offset
1751 SCIP_VAR** activevars; ///< active variables
1752 int nactivevars; ///< number of active variables
1753 SCIP_VAR** fixedvars; ///< fixed variables
1754 int nfixedvars; ///< number of fixed variables
1755 SCIP_CONS** allconss; ///< all constraints given to writer
1756 int nallconss; ///< number of all constraints
1757
1758 bool nlcomments; ///< whether to write nl files with comments
1759 SCIP_Bool genericnames; ///< are generic names used
1760
1761 SCIP_CONSHDLR* conshdlr_nonlinear; ///< nonlinear constraints handler
1762 SCIP_CONSHDLR* conshdlr_linear; ///< linear constraints handler
1763 SCIP_CONSHDLR* conshdlr_setppc; ///< setppc constraints handler
1764 SCIP_CONSHDLR* conshdlr_logicor; ///< logicor constraints handler
1765 SCIP_CONSHDLR* conshdlr_knapsack; ///< knapsack constraints handler
1766 SCIP_CONSHDLR* conshdlr_varbound; ///< varbound constraints handlers
1767
1768 mp::NLHeader nlheader; ///< NL header with various counts
1769 SCIP_VAR** vars; ///< variables in AMPL order
1770 int nvars; ///< number of variables (= nactivevars)
1771 SCIP_HASHMAP* var2idx; ///< map variable to AMPL index
1772 SCIP_CONS** algconss; ///< algebraic constraints that will be written, permuted in AMPL order
1773 SCIP_Real* algconsslhs; ///< left hand side of algebraic constraints
1774 SCIP_Real* algconssrhs; ///< right hand side of algebraic constraints
1775 int nalgconss; ///< number of algebraic constraint we will actually write
1776 SCIP_VAR** aggconss; ///< fixed variable for which aggregation constraints need to be written
1777 int naggconss; ///< number of fixed variables for which aggregation constraints are written
1778
1779 /** variable types by which variables need to be ordered for .nl
1780 * (names are taken from pyomo nl writer, with those for nonlinear objective removed)
1781 */
1782 typedef enum
1783 {
1784 ConNonlinearVars = 0, /* only in cons */
1785 ConNonlinearVarsInt = 1, /* only in cons */
1786 LinearVars = 2,
1787 LinearVarsBool = 3,
1788 LinearVarsInt = 4
1789 } NlVarType;
1790
1791 /** checks variable types and other properties for nlheader;
1792 * sets up variables permutation
1793 */
1794 void analyseVariables()
1795 {
1796 NlVarType* vartype = NULL;
1797 SCIP_HASHMAP* var2expr = NULL;
1798
1799 int nlvars_cons = 0;
1800 int binvars_lin = 0;
1801 int intvars_lin = 0;
1802 int discrvars_nlcons = 0;
1803
1804 nlheader.max_var_name_len = 0;
1805
1806 /* number of nonzeros in objective gradient */
1807 nlheader.num_obj_nonzeros = 0;
1808
1809 if( conshdlr_nonlinear != NULL )
1810 var2expr = SCIPgetVarExprHashmapNonlinear(conshdlr_nonlinear);
1811
1812 SCIP_CALL_THROW( SCIPallocBufferArray(scip, &vartype, nactivevars + nfixedvars) );
1813
1814 /* collect statistics on variables; determine variable types */
1815 for( int i = 0; i < nactivevars + nfixedvars; ++i )
1816 {
1817 SCIP_VAR* var = (i < nactivevars ? activevars[i] : fixedvars[i-nactivevars]);
1818 SCIP_Bool isdiscrete;
1819 SCIP_Bool isnonlinear = FALSE;
1820
1821 if( SCIPvarGetObj(var) != 0.0 )
1822 ++nlheader.num_obj_nonzeros;
1823
1824 isdiscrete = SCIPvarGetType(var) <= SCIP_VARTYPE_INTEGER;
1825
1826 /* we think of a variable as nonlinear if cons_nonlinear has a SCIP_EXPR* for this variable
1827 * this is usually an overestimation, since also variables that appear only linearly in nonlinear constraints
1828 * are regarded as nonlinear this way
1829 * we also consider variables as nonlinear when only its negation appears in a nonlinear constraint,
1830 * since we will write out the negation of var as 1-var into the nl file
1831 */
1832 if( var2expr != NULL )
1833 {
1834 isnonlinear = SCIPhashmapExists(var2expr, (void*)var);
1835 if( !isnonlinear && SCIPvarGetNegatedVar(var) != NULL )
1836 isnonlinear = SCIPhashmapExists(var2expr, (void*)SCIPvarGetNegatedVar(var));
1837 }
1838
1839 /* this is how Pyomo counts vars (nlvars_* = nlvb,c,o) when writing NL
1840 * https://github.com/Pyomo/pyomo/blob/main/pyomo/repn/plugins/ampl/ampl_.py#L1202
1841 * this, together with the ominous line below, seems to correspond to what AMPL writes
1842 */
1843 if( isnonlinear )
1844 {
1845 /* nonlinear (in constraints only, as this is SCIP) */
1846 ++nlvars_cons;
1847 if( isdiscrete )
1848 {
1849 ++discrvars_nlcons;
1850 vartype[i] = ConNonlinearVarsInt;
1851 }
1852 else
1853 vartype[i] = ConNonlinearVars;
1854 }
1855 else
1856 {
1857 /* linear */
1858 if( isdiscrete )
1859 {
1860 /* for compatibility with AMPL generated nl files, count integer with 0/1 bounds as binary, too */
1862 {
1863 ++binvars_lin;
1864 vartype[i] = LinearVarsBool;
1865 }
1866 else
1867 {
1868 ++intvars_lin;
1869 vartype[i] = LinearVarsInt;
1870 }
1871 }
1872 else
1873 vartype[i] = LinearVars;
1874 }
1875
1876 if( !genericnames )
1877 {
1878 int namelen = (int)strlen(SCIPvarGetName(var));
1879 if( namelen > nlheader.max_var_name_len )
1880 nlheader.max_var_name_len = namelen;
1881 }
1882 }
1883
1884 /* setup var permutation */
1885 assert(vars == NULL);
1886 SCIP_CALL_THROW( SCIPallocBlockMemoryArray(scip, &vars, nactivevars + nfixedvars) );
1887 SCIP_CALL_THROW( SCIPhashmapCreate(&var2idx, SCIPblkmem(scip), nactivevars + nfixedvars) );
1888 nvars = 0;
1889 for( int vtype = ConNonlinearVars; vtype <= LinearVarsInt; ++vtype )
1890 for( int i = 0; i < nactivevars + nfixedvars; ++i )
1891 if( vartype[i] == (NlVarType)vtype )
1892 {
1893 vars[nvars] = (i < nactivevars ? activevars[i] : fixedvars[i-nactivevars]);
1894 SCIP_CALL_THROW( SCIPhashmapInsertInt(var2idx, (void*)vars[nvars], nvars) );
1895 ++nvars;
1896 }
1897 assert(nvars == nactivevars + nfixedvars);
1898
1899 SCIPfreeBufferArray(scip, &vartype);
1900
1901 nlheader.num_vars = nvars;
1902
1903 /* number of nonlinear variables
1904 * setting num_nl_vars_in_objs = nlvars_cons looks odd, but makes the generated nl files
1905 * consistent with what AMPL or Pyomo writes
1906 */
1907 nlheader.num_nl_vars_in_cons = nlvars_cons;
1908 nlheader.num_nl_vars_in_objs = nlvars_cons;
1909 nlheader.num_nl_vars_in_both = 0;
1910
1911 /* number of linear network variables */
1912 nlheader.num_linear_net_vars = 0;
1913
1914 /* number of linear binary and integer variables */
1915 nlheader.num_linear_binary_vars = binvars_lin;
1916 nlheader.num_linear_integer_vars = intvars_lin;
1917
1918 /* number of integer nonlinear variables */
1919 nlheader.num_nl_integer_vars_in_both = 0;
1920 nlheader.num_nl_integer_vars_in_cons = discrvars_nlcons;
1921 nlheader.num_nl_integer_vars_in_objs = 0;
1922 }
1923
1924 /** checks constraint types and other properties for nlheader;
1925 * sets up constraints permutation
1926 */
1927 void analyzeConstraints()
1928 {
1929 /* collect algebraic constraints and their side: for AMPL, nonlinear comes before linear */
1930 SCIP_CALL_THROW( SCIPallocBlockMemoryArray(scip, &algconss, nallconss) );
1931 SCIP_CALL_THROW( SCIPallocBlockMemoryArray(scip, &algconsslhs, nallconss) );
1932 SCIP_CALL_THROW( SCIPallocBlockMemoryArray(scip, &algconssrhs, nallconss) );
1933
1934 nalgconss = 0;
1935 if( nlheader.num_nl_vars_in_cons > 0 )
1936 {
1937 for( int i = 0; i < nallconss; ++i )
1938 {
1939 SCIP_CONS* cons = allconss[i];
1940 if( SCIPconsGetHdlr(cons) == conshdlr_nonlinear )
1941 {
1942 algconss[nalgconss] = cons;
1943 algconsslhs[nalgconss] = SCIPgetLhsNonlinear(cons);
1944 algconssrhs[nalgconss] = SCIPgetRhsNonlinear(cons);
1945 ++nalgconss;
1946 }
1947 }
1948 }
1949 /* total number of nonlinear constraints */
1950 nlheader.num_nl_cons = nalgconss;
1951
1952 /* pick constraints we recognize as linear
1953 * count ranged and equality constraints
1954 * check constraint name lengths (if not skipped due to being generic)
1955 * count number of variables in constraints
1956 */
1957 nlheader.num_ranges = 0; /* number of ranged constraints */
1958 nlheader.num_eqns = 0; /* number of equality constraints */
1959 nlheader.max_con_name_len = 0; /* maximal length of constraints' names */
1960 nlheader.num_con_nonzeros = 0; /* number of nonzeros in constraints' Jacobian */
1961 for( int i = 0; i < nallconss; ++i )
1962 {
1963 SCIP_CONS* cons = allconss[i];
1964 SCIP_CONSHDLR* conshdlr = SCIPconsGetHdlr(cons);
1965 SCIP_Real lhs;
1966 SCIP_Real rhs;
1967
1968 if( conshdlr == conshdlr_nonlinear )
1969 {
1970 lhs = SCIPgetLhsNonlinear(cons);
1971 rhs = SCIPgetRhsNonlinear(cons);
1972 }
1973 else
1974 {
1975 /* negated variables may not show up in fixedvars
1976 * so we instead replace the negation when providing the coefficients of the linear constraint
1977 * this means additional constants to subtract from lhs/rhs
1978 */
1979 if( conshdlr == conshdlr_linear )
1980 {
1981 int nconsvars = SCIPgetNVarsLinear(scip, cons);
1982 SCIP_VAR** consvars = SCIPgetVarsLinear(scip, cons);
1983 SCIP_Real* conscoefs = SCIPgetValsLinear(scip, cons);
1984 SCIP_Real negconstant = 0.0;
1985 for( int v = 0; v < nconsvars; ++v )
1986 if( SCIPvarIsNegated(consvars[v]) )
1987 negconstant += conscoefs[v];
1988
1989 lhs = SCIPgetLhsLinear(scip, cons);
1990 if( !SCIPisInfinity(scip, -lhs) )
1991 lhs -= negconstant;
1992
1993 rhs = SCIPgetRhsLinear(scip, cons);
1994 if( !SCIPisInfinity(scip, rhs) )
1995 rhs -= negconstant;
1996 }
1997 else if( conshdlr == conshdlr_setppc )
1998 {
1999 int nconsvars = SCIPgetNVarsSetppc(scip, cons);
2000 SCIP_VAR** consvars = SCIPgetVarsSetppc(scip, cons);
2001 SCIP_Real negconstant = 0.0;
2002 for( int v = 0; v < nconsvars; ++v )
2003 if( SCIPvarIsNegated(consvars[v]) )
2004 negconstant += 1.0;
2005
2006 switch( SCIPgetTypeSetppc(scip, cons) )
2007 {
2009 lhs = 1.0 - negconstant;
2010 rhs = 1.0 - negconstant;
2011 break;
2013 lhs = 1.0 - negconstant;
2014 rhs = SCIPinfinity(scip);
2015 break;
2017 lhs = -SCIPinfinity(scip);
2018 rhs = 1.0 - negconstant;
2019 break;
2020 default:
2021 throw mp::UnsupportedError("Unexpected SETPPC type");
2022 }
2023 }
2024 else if( conshdlr == conshdlr_logicor )
2025 {
2026 int nconsvars = SCIPgetNVarsLogicor(scip, cons);
2027 SCIP_VAR** consvars = SCIPgetVarsLogicor(scip, cons);
2028 SCIP_Real negconstant = 0.0;
2029 for( int v = 0; v < nconsvars; ++v )
2030 if( SCIPvarIsNegated(consvars[v]) )
2031 negconstant += 1.0;
2032
2033 lhs = 1.0 - negconstant;
2034 rhs = SCIPinfinity(scip);
2035 }
2036 else if( conshdlr == conshdlr_knapsack )
2037 {
2038 int nconsvars = SCIPgetNVarsKnapsack(scip, cons);
2039 SCIP_VAR** consvars = SCIPgetVarsKnapsack(scip, cons);
2040 SCIP_Longint* weights = SCIPgetWeightsKnapsack(scip, cons);
2041 SCIP_Longint negweights = 0.0;
2042 for( int v = 0; v < nconsvars; ++v )
2043 if( SCIPvarIsNegated(consvars[v]) )
2044 negweights += weights[v];
2045
2046 lhs = -SCIPinfinity(scip);
2047 rhs = (SCIP_Real)(SCIPgetCapacityKnapsack(scip, cons) - negweights);
2048 }
2049 else if( conshdlr == conshdlr_varbound )
2050 {
2051 /* lhs <= var + vbdcoef*vbdvar <= rhs */
2052 SCIP_Real negconstant = 0.0;
2054 negconstant = 1.0;
2056 negconstant += SCIPgetVbdcoefVarbound(scip, cons);
2057
2058 lhs = SCIPgetLhsVarbound(scip, cons);
2059 if( !SCIPisInfinity(scip, -lhs) )
2060 lhs -= negconstant;
2061
2062 rhs = SCIPgetRhsVarbound(scip, cons);
2063 if( !SCIPisInfinity(scip, rhs) )
2064 rhs -= negconstant;
2065 }
2066 else
2067 {
2068 SCIPwarningMessage(scip, "constraint <%s> of type <%s> cannot be printed in requested format\n", SCIPconsGetName(cons), SCIPconshdlrGetName(conshdlr));
2069 continue;
2070 }
2071 algconss[nalgconss] = cons;
2072 algconsslhs[nalgconss] = lhs;
2073 algconssrhs[nalgconss] = rhs;
2074 ++nalgconss;
2075 }
2076
2077 if( !SCIPisInfinity(scip, -lhs) && !SCIPisInfinity(scip, rhs) )
2078 {
2079 if( SCIPisEQ(scip, lhs, rhs) )
2080 ++nlheader.num_eqns;
2081 else
2082 ++nlheader.num_ranges;
2083 }
2084
2085 if( !genericnames )
2086 {
2087 int namelen = (int)strlen(SCIPconsGetName(allconss[i]));
2088 if( namelen > nlheader.max_con_name_len )
2089 nlheader.max_con_name_len = namelen;
2090 }
2091
2092 SCIP_Bool success;
2093 int nvarsincons;
2094 SCIP_CALL_THROW( SCIPgetConsNVars(scip, cons, &nvarsincons, &success) );
2095 if( !success )
2096 {
2097 /* this should never happen */
2098 SCIPwarningMessage(scip, "could not get number of variable from constraint handler <%s>; nonzero count in nl file will be wrong\n", SCIPconshdlrGetName(conshdlr));
2099 }
2100 else
2101 {
2102 nlheader.num_con_nonzeros += nvarsincons;
2103 }
2104 }
2105 assert(nalgconss <= nallconss);
2106
2107 /* now add counts for aggregation constraints (definition of fixedvars that are aggregated, multiaggregated, or negated) */
2108 SCIP_CALL_THROW( SCIPallocBlockMemoryArray(scip, &aggconss, nfixedvars) );
2109 naggconss = 0;
2110 for( int i = 0; i < nfixedvars; ++i )
2111 {
2112 SCIP_VAR* var = fixedvars[i];
2113
2114 switch( SCIPvarGetStatus(var) )
2115 {
2117 continue;
2118
2121 nlheader.num_con_nonzeros += 2;
2122 break;
2123
2125 nlheader.num_con_nonzeros += SCIPvarGetMultaggrNVars(var) + 1;
2126 break;
2127
2128 default:
2129 SCIPerrorMessage("unexpected variable status %d of fixed variable <%s>\n", SCIPvarGetStatus(var), SCIPvarGetName(var));
2131 }
2132
2133 if( !genericnames )
2134 {
2135 // AMPL constraint will be named aggr_<varname>
2136 int namelen = (int)strlen(SCIPvarGetName(var)) + 5;
2137 if( namelen > nlheader.max_con_name_len )
2138 nlheader.max_con_name_len = namelen;
2139 }
2140
2141 aggconss[naggconss] = var;
2142 ++naggconss;
2143
2144 ++nlheader.num_eqns;
2145 }
2146
2147 nlheader.num_algebraic_cons = nalgconss + naggconss;
2148 nlheader.num_logical_cons = 0;
2149
2150 /* no complementarity conditions */
2151 nlheader.num_compl_conds = 0;
2152 nlheader.num_nl_compl_conds = 0;
2153 nlheader.num_compl_dbl_ineqs = 0;
2154 nlheader.num_compl_vars_with_nz_lb = 0;
2155
2156 /** no network constraints */
2157 nlheader.num_nl_net_cons = 0;
2158 nlheader.num_linear_net_cons = 0;
2159 }
2160
2161 /* gets AMPL index of variable (using var2idx) */
2162 int getVarAMPLIndex(
2163 SCIP_VAR* var
2164 )
2165 {
2166 int varidx = SCIPhashmapGetImageInt(var2idx, (void*)var);
2167 assert(varidx >= 0);
2168 assert(varidx != INT_MAX);
2169 assert(varidx < nvars);
2170 assert(vars[varidx] == var);
2171 return varidx;
2172 }
2173
2174public:
2175 /// Constructor
2177 SCIP* scip_, ///< SCIP data structure
2178 const char* probname_, ///< problem name
2179 SCIP_OBJSENSE objsense_, ///< objective sense
2180 SCIP_Real objscale_, ///< objective scale
2181 SCIP_Real objoffset_, ///< objective offset
2182 SCIP_VAR** vars_, ///< active variables
2183 int nvars_, ///< number of active variables
2184 SCIP_VAR** fixedvars_, ///< fixed variables
2185 int nfixedvars_, ///< number of fixed variables
2186 SCIP_CONS** conss_, ///< constraints
2187 int nconss_, ///< number of constraints
2188 SCIP_Bool nlbinary_, ///< whether to write binary or text nl
2189 SCIP_Bool nlcomments_, ///< whether to include comments into nl
2190 SCIP_Bool genericnames_ ///< are generic names used
2191 )
2192 : scip(scip_),
2193 probname(probname_),
2194 objsense(objsense_),
2195 objscale(objscale_),
2196 objoffset(objoffset_),
2197 activevars(vars_),
2198 nactivevars(nvars_),
2199 fixedvars(fixedvars_),
2200 nfixedvars(nfixedvars_),
2201 allconss(conss_),
2202 nallconss(nconss_),
2203 nlcomments(nlcomments_),
2204 genericnames(genericnames_),
2205 vars(NULL),
2206 nvars(0),
2207 var2idx(NULL),
2208 algconss(NULL),
2209 algconsslhs(NULL),
2210 algconssrhs(NULL),
2211 nalgconss(0),
2212 aggconss(NULL),
2213 naggconss(0)
2214 {
2215 nlheader.format = nlbinary_ ? mp::NLHeader::BINARY : mp::NLHeader::TEXT;
2216
2217 conshdlr_nonlinear = SCIPfindConshdlr(scip, "nonlinear");
2218 conshdlr_linear = SCIPfindConshdlr(scip, "linear");
2219 conshdlr_setppc = SCIPfindConshdlr(scip, "setppc");
2220 conshdlr_logicor = SCIPfindConshdlr(scip, "logicor");
2221 conshdlr_knapsack = SCIPfindConshdlr(scip, "knapsack");
2222 conshdlr_varbound = SCIPfindConshdlr(scip, "varbound");
2223 }
2224
2226 {
2227 SCIPfreeBlockMemoryArrayNull(scip, &aggconss, nfixedvars);
2228 SCIPfreeBlockMemoryArrayNull(scip, &algconssrhs, nallconss);
2229 SCIPfreeBlockMemoryArrayNull(scip, &algconsslhs, nallconss);
2230 SCIPfreeBlockMemoryArrayNull(scip, &algconss, nallconss);
2231 SCIPfreeBlockMemoryArrayNull(scip, &vars, nactivevars + nfixedvars);
2232 if( var2idx != NULL )
2233 SCIPhashmapFree(&var2idx);
2234 }
2235
2236 /** Provide NLHeader.
2237 *
2238 * This method is called first.
2239 *
2240 * NLHeader summarizes the model and provides some technical parameters,
2241 * such as text/binary NL format.
2242 */
2243 mp::NLHeader Header()
2244 {
2245 analyseVariables();
2246 analyzeConstraints();
2247
2248 nlheader.prob_name = probname;
2249
2250 /* number of objectives
2251 * if objective is all zero in SCIP, then just don't write any objective to nl
2252 */
2253 if( nlheader.num_obj_nonzeros == 0 && objoffset == 0.0 )
2254 nlheader.num_objs = 0;
2255 else
2256 nlheader.num_objs = 1;
2257 nlheader.num_nl_objs = 0;
2258
2259 /* number of functions */
2260 nlheader.num_funcs = 0;
2261
2262 /* it would have been nice to handle fixed variables as common expressions,
2263 * but as common expression are handled like nonlinear expressions,
2264 * this would turn any linear constraint with fixed variables into common expressions
2265 */
2266 nlheader.num_common_exprs_in_both = 0;
2267 nlheader.num_common_exprs_in_cons = 0;
2268 nlheader.num_common_exprs_in_objs = 0;
2269 nlheader.num_common_exprs_in_single_cons = 0;
2270 nlheader.num_common_exprs_in_single_objs = 0;
2271
2272 return nlheader;
2273 }
2274
2275 /// NL comments?
2276 bool WantNLComments() const
2277 {
2278 return nlcomments;
2279 }
2280
2281 /// currently we do not want to write size of each column in Jacobian
2282 /// (i.e., number of constraints each variable appears in)
2284 {
2285 return 0;
2286 }
2287
2289 int
2290 ) const
2291 {
2292 return objsense == SCIP_OBJSENSE_MAXIMIZE ? 1 : 0;
2293 }
2294
2295 template <class ObjGradWriter>
2297 int i,
2298 ObjGradWriter& gw
2299 )
2300 {
2301 assert(i == 0);
2302
2303 if( nlheader.num_obj_nonzeros == 0 )
2304 return;
2305
2306 auto gvw = gw.MakeVectorWriter(nlheader.num_obj_nonzeros);
2307 for( int v = 0; v < nvars; ++v )
2308 {
2309 SCIP_Real coef = SCIPvarGetObj(vars[v]);
2310 if( coef != 0.0 )
2311 gvw.Write(v, objscale * coef);
2312 }
2313 }
2314
2315 template <class ObjExprWriter>
2317 int i,
2318 ObjExprWriter& ew
2319 )
2320 {
2321 assert(i == 0);
2322 ew.NPut(objscale * objoffset);
2323 }
2324
2325 template <class VarBoundsWriter>
2327 VarBoundsWriter& vbw
2328 ) const
2329 {
2330 for( int v = 0; v < nvars; ++v )
2331 {
2332 SCIP_Real lb = SCIPvarGetLbGlobal(vars[v]);
2333 SCIP_Real ub = SCIPvarGetUbGlobal(vars[v]);
2334
2335 if( SCIPisInfinity(scip, -lb) )
2336 lb = -INFINITY;
2337
2338 if( SCIPisInfinity(scip, ub) )
2339 ub = INFINITY;
2340
2341 vbw.WriteLbUb(lb, ub);
2342 }
2343 }
2344
2345 template <class ConBoundsWriter>
2347 ConBoundsWriter& cbw
2348 )
2349 {
2350 for( int c = 0; c < nalgconss; ++c )
2351 {
2352 AlgConRange bnd;
2353 bnd.L = SCIPisInfinity(scip, -algconsslhs[c]) ? -INFINITY : algconsslhs[c];
2354 bnd.U = SCIPisInfinity(scip, algconssrhs[c]) ? INFINITY : algconssrhs[c];
2355 cbw.WriteAlgConRange(bnd);
2356 }
2357
2358 for( int v = 0; v < naggconss; ++v )
2359 {
2360 SCIP_VAR* var = aggconss[v];
2361 AlgConRange bnd;
2362
2363 switch( SCIPvarGetStatus(var) )
2364 {
2366 bnd.L = SCIPvarGetAggrConstant(var);
2367 break;
2368
2370 bnd.L = SCIPvarGetNegationConstant(var);
2371 break;
2372
2374 bnd.L = SCIPvarGetMultaggrConstant(var);
2375 break;
2376
2377 default:
2378 SCIPerrorMessage("unexpected variable status %d of aggregated variable <%s>\n", SCIPvarGetStatus(var), SCIPvarGetName(var));
2380 }
2381
2382 bnd.U = bnd.L;
2383 cbw.WriteAlgConRange(bnd);
2384 }
2385 }
2386
2387 /* this is for the comments in .nl files if comments enabled */
2388 const char* ConDescription(
2389 int i
2390 )
2391 {
2392 if( i < nalgconss )
2393 return SCIPconsGetName(algconss[i]);
2394
2395 assert(i < nalgconss + naggconss);
2396 return SCIPvarGetName(aggconss[i-nalgconss]);
2397 }
2398
2399 template <class ConLinearExprWriter>
2401 int i,
2402 ConLinearExprWriter& clw
2403 )
2404 {
2405 if( i < nlheader.num_nl_cons )
2406 return;
2407
2408 if( i < nalgconss )
2409 {
2410 SCIP_CONS* cons = algconss[i];
2411 SCIP_CONSHDLR* conshdlr = SCIPconsGetHdlr(cons);
2412
2413 if( conshdlr == conshdlr_linear )
2414 {
2415 SCIP_Real* conscoefs = SCIPgetValsLinear(scip, cons);
2416 SCIP_VAR** consvars = SCIPgetVarsLinear(scip, cons);
2417 int nconsvars = SCIPgetNVarsLinear(scip, cons);
2418
2419 /* if we write 0 coefficients, then this gives an error when reading
2420 * (nl-reader.h: NLReader<Reader, Handler>::ReadLinearExpr(): ReadUInt(1, ...)
2421 * says that the expected number of coefs is at least 1)
2422 */
2423 if( nconsvars == 0 )
2424 return;
2425
2426 auto vw = clw.MakeVectorWriter(nconsvars);
2427 for( int v = 0; v < nconsvars; ++v )
2428 if( SCIPvarIsNegated(consvars[v]) )
2429 vw.Write(getVarAMPLIndex(SCIPvarGetNegationVar(consvars[v])), -conscoefs[v]);
2430 else
2431 vw.Write(getVarAMPLIndex(consvars[v]), conscoefs[v]);
2432
2433 return;
2434 }
2435
2436 if( conshdlr == conshdlr_setppc )
2437 {
2438 SCIP_VAR** consvars = SCIPgetVarsSetppc(scip, cons);
2439 int nconsvars = SCIPgetNVarsSetppc(scip, cons);
2440
2441 if( nconsvars == 0 )
2442 return;
2443
2444 auto vw = clw.MakeVectorWriter(nconsvars);
2445 for( int v = 0; v < nconsvars; ++v )
2446 if( SCIPvarIsNegated(consvars[v]) )
2447 vw.Write(getVarAMPLIndex(SCIPvarGetNegationVar(consvars[v])), -1.0);
2448 else
2449 vw.Write(getVarAMPLIndex(consvars[v]), 1.0);
2450
2451 return;
2452 }
2453
2454 if( conshdlr == conshdlr_logicor )
2455 {
2456 SCIP_VAR** consvars = SCIPgetVarsLogicor(scip, cons);
2457 int nconsvars = SCIPgetNVarsLogicor(scip, cons);
2458
2459 if( nconsvars == 0 )
2460 return;
2461
2462 auto vw = clw.MakeVectorWriter(nconsvars);
2463 for( int v = 0; v < nconsvars; ++v )
2464 if( SCIPvarIsNegated(consvars[v]) )
2465 vw.Write(getVarAMPLIndex(SCIPvarGetNegationVar(consvars[v])), -1.0);
2466 else
2467 vw.Write(getVarAMPLIndex(consvars[v]), 1.0);
2468
2469 return;
2470 }
2471
2472 if( conshdlr == conshdlr_knapsack )
2473 {
2474 SCIP_Longint* weights = SCIPgetWeightsKnapsack(scip, cons);
2475 SCIP_VAR** consvars = SCIPgetVarsKnapsack(scip, cons);
2476 int nconsvars = SCIPgetNVarsKnapsack(scip, cons);
2477
2478 if( nconsvars == 0 )
2479 return;
2480
2481 auto vw = clw.MakeVectorWriter(nconsvars);
2482 for( int v = 0; v < nconsvars; ++v )
2483 if( SCIPvarIsNegated(consvars[v]) )
2484 vw.Write(getVarAMPLIndex(SCIPvarGetNegationVar(consvars[v])), -(SCIP_Real)weights[v]);
2485 else
2486 vw.Write(getVarAMPLIndex(consvars[v]), (SCIP_Real)weights[v]);
2487
2488 return;
2489 }
2490
2491 assert(conshdlr == conshdlr_varbound);
2492
2493 auto vw = clw.MakeVectorWriter(2);
2495 vw.Write(getVarAMPLIndex(SCIPvarGetNegationVar(SCIPgetVarVarbound(scip, cons))), -1.0);
2496 else
2497 vw.Write(getVarAMPLIndex(SCIPgetVarVarbound(scip, cons)), 1.0);
2498
2500 vw.Write(getVarAMPLIndex(SCIPvarGetNegationVar(SCIPgetVbdvarVarbound(scip, cons))), -SCIPgetVbdcoefVarbound(scip, cons));
2501 else
2502 vw.Write(getVarAMPLIndex(SCIPgetVbdvarVarbound(scip, cons)), SCIPgetVbdcoefVarbound(scip, cons));
2503
2504 return;
2505 }
2506
2507 assert(i < nalgconss + naggconss);
2508 SCIP_VAR* var = aggconss[i-nalgconss];
2509
2510 switch( SCIPvarGetStatus(var) )
2511 {
2513 {
2514 /* var - aggrscalar*aggrvar = aggrconstant */
2515 auto vw = clw.MakeVectorWriter(2);
2516 vw.Write(getVarAMPLIndex(var), 1.0);
2517 vw.Write(getVarAMPLIndex(SCIPvarGetAggrVar(var)), -SCIPvarGetAggrScalar(var));
2518 break;
2519 }
2520
2522 {
2523 /* var + negationvar = negationconstant */
2524 auto vw = clw.MakeVectorWriter(2);
2525 vw.Write(getVarAMPLIndex(var), 1.0);
2526 vw.Write(getVarAMPLIndex(SCIPvarGetNegationVar(var)), 1.0);
2527 break;
2528 }
2529
2531 {
2532 /* var - sum_i aggrscalar_i aggrvar_i = aggrconstant */
2533 auto vw = clw.MakeVectorWriter(SCIPvarGetMultaggrNVars(var) + 1);
2534 vw.Write(getVarAMPLIndex(var), 1.0);
2535 for( int v = 0; v < SCIPvarGetMultaggrNVars(var); ++v )
2536 vw.Write(getVarAMPLIndex(SCIPvarGetMultaggrVars(var)[v]), -SCIPvarGetMultaggrScalars(var)[v]);
2537 break;
2538 }
2539
2540 default:
2541 {
2542 SCIPerrorMessage("unexpected variable status %d of aggregated variable <%s>\n", SCIPvarGetStatus(var), SCIPvarGetName(var));
2544 }
2545 }
2546 }
2547
2548 template <class ConExprWriter>
2550 int i,
2551 ConExprWriter& ew
2552 )
2553 {
2554 if( i >= nlheader.num_nl_cons )
2555 {
2556 ew.NPut(0.0);
2557 return;
2558 }
2559
2560 // will store an error message if some expr couldn't be handled
2561 std::stringstream unhandledexprmsg;
2562
2563 SCIP_EXPR* rootexpr = SCIPgetExprNonlinear(algconss[i]);
2564
2565 SCIP_EXPRITER* it;
2567
2570
2571 for( SCIP_EXPR* expr = SCIPexpriterGetCurrent(it); !SCIPexpriterIsEnd(it); expr = SCIPexpriterGetNext(it) )
2572 {
2573 switch( SCIPexpriterGetStageDFS(it) )
2574 {
2576 {
2577 // retrieve the ConExprWriter of parent expr
2578 ConExprWriter* parentew;
2579 if( expr == rootexpr )
2580 parentew = &ew;
2581 else
2582 parentew = (ConExprWriter*)SCIPexpriterGetExprUserData(it, SCIPexpriterGetParentDFS(it)).ptrval;
2583 assert(parentew != NULL);
2584
2585 ConExprWriter* newew = NULL;
2586
2587 if( SCIPisExprVar(scip, expr) )
2588 {
2589 SCIP_VAR* var = SCIPgetVarExprVar(expr); /* cppcheck-suppress dangerousTypeCast */
2590 if( SCIPvarIsNegated(var) )
2591 {
2592 ConExprWriter ew2(parentew->OPut2(mp::nl::SUB));
2593 ew2.NPut(SCIPvarGetNegationConstant(var));
2594 ew2.VPut(getVarAMPLIndex(SCIPvarGetNegatedVar(var)), SCIPvarGetName(SCIPvarGetNegatedVar(var)));
2595 }
2596 else
2597 {
2598 parentew->VPut(getVarAMPLIndex(var), SCIPvarGetName(var));
2599 }
2600 }
2601 else if( SCIPisExprValue(scip, expr) )
2602 {
2603 parentew->NPut(SCIPgetValueExprValue(expr));
2604 }
2605 else if( SCIPisExprSum(scip, expr) )
2606 {
2607 int nargs = SCIPexprGetNChildren(expr);
2608 assert(nargs > 0);
2609
2610 if( SCIPgetConstantExprSum(expr) != 0.0 )
2611 {
2612 if( nargs == 0 )
2613 {
2614 parentew->NPut(SCIPgetConstantExprSum(expr));
2615 SCIP_EXPRITER_USERDATA userdata;
2616 userdata.ptrval = NULL;
2617 SCIPexpriterSetCurrentUserData(it, userdata);
2618 break;
2619 }
2620
2621 ++nargs;
2622 }
2623
2624 // we will need to store two ConExprWriter's for sum or add
2625 // one for the sum, and one for multiplication (coef*expr) of the currently considered child
2626 // the one for the sum will go second, so in the child, we don't need to check for case of sum
2627 // there is no default constructor for ConExprWriter, so we only alloc mem and then use replacement-new
2629
2630 if( nargs == 1 )
2631 {
2632 assert(SCIPgetConstantExprSum(expr) == 0.0); // handled above
2633 // skip the SUM and attach only a MUL, which will be done in VISITINGCHILD
2634 // so we put the parentew to which the MUL should be attached into newew[1]
2635 memcpy((void*)(newew+1), (void*)parentew, sizeof(ConExprWriter));
2636 }
2637 else if( nargs == 2 )
2638 {
2639 new (newew+1) ConExprWriter(parentew->OPut2(mp::nl::ADD));
2640 }
2641 else
2642 {
2643 new (newew+1) ConExprWriter(parentew->OPutN(mp::nl::SUM, nargs));
2644 }
2645
2646 if( SCIPgetConstantExprSum(expr) != 0.0 )
2647 newew[1].NPut(SCIPgetConstantExprSum(expr));
2648 }
2649 else if( SCIPisExprProduct(scip, expr) )
2650 {
2651 int nargs = SCIPexprGetNChildren(expr);
2652 assert(nargs > 0);
2653
2654 // in VISITEDCHILD we will take care of turning a product of more than 2 factors
2655 // into a recursion of multiplications
2656 newew = new ConExprWriter(parentew->OPut2(mp::nl::MUL));
2657
2658 // nargs should be >= 2, but theoretically could be 1
2659 // we will then write this as 1*arg for simplicity
2660 if( nargs == 1 )
2661 newew->NPut(1.0);
2662 }
2663 else if( SCIPisExprPower(scip, expr) )
2664 {
2665 if( SCIPgetExponentExprPow(expr) == 2.0 )
2666 newew = new ConExprWriter(parentew->OPut1(mp::nl::POW2));
2667 else if( SCIPgetExponentExprPow(expr) == 0.5 )
2668 newew = new ConExprWriter(parentew->OPut1(mp::nl::SQRT));
2669 else
2670 newew = new ConExprWriter(parentew->OPut2(mp::nl::POW_CONST_EXP));
2671 }
2672 else if( SCIPisExprLog(scip, expr) )
2673 {
2674 newew = new ConExprWriter(parentew->OPut1(mp::nl::LOG));
2675 }
2676 else if( SCIPisExprExp(scip, expr) )
2677 {
2678 newew = new ConExprWriter(parentew->OPut1(mp::nl::EXP));
2679 }
2680 else if( SCIPisExprAbs(scip, expr) )
2681 {
2682 newew = new ConExprWriter(parentew->OPut1(mp::nl::ABS));
2683 }
2684 else if( SCIPisExprSin(scip, expr) )
2685 {
2686 newew = new ConExprWriter(parentew->OPut1(mp::nl::SIN));
2687 }
2688 else if( SCIPisExprCos(scip, expr) )
2689 {
2690 newew = new ConExprWriter(parentew->OPut1(mp::nl::COS));
2691 }
2692 else
2693 {
2694 // entropy, signpower, or unrecognized handler
2695 unhandledexprmsg << "Cannot represent <" << SCIPexprhdlrGetName(SCIPexprGetHdlr(expr)) << "> expression in constraint <" << SCIPconsGetName(algconss[i]) << "> in .nl" << std::endl;
2696
2697 // this is to make the assert in the destructor of parentew pass, which asserts that all arguments were written
2698 parentew->NPut(0.0);
2699
2700 // skip children and move on to LEAVEEXPR directly, thus skipping this subexpression
2701 // (we still set userdata.ptrval = NULL next, so LEAVEEXPR will do delete NULL (which is well defined))
2703 }
2704
2705 SCIP_EXPRITER_USERDATA userdata;
2706 userdata.ptrval = newew;
2707 SCIPexpriterSetCurrentUserData(it, userdata);
2708
2709 break;
2710 }
2711
2713 {
2714 if( SCIPisExprSum(scip, expr) )
2715 {
2716 int childidx = SCIPexpriterGetChildIdxDFS(it);
2717 SCIP_Real coef = SCIPgetCoefsExprSum(expr)[childidx];
2718
2719 ConExprWriter* ews = (ConExprWriter*)SCIPexpriterGetCurrentUserData(it).ptrval;
2720
2721 if( coef != 1.0 )
2722 {
2723 // if coef, then create MUL and store ExprWriter in ews[0]
2724 new (ews) ConExprWriter(ews[1].OPut2(mp::nl::MUL));
2725 ews[0].NPut(coef);
2726 }
2727 else
2728 {
2729 // if trivial coef, then only move ews[1] (ExprWriter for SUM/ADD) to ews[0] (implementation forbids copy)
2730 // cannot use move-assignment, because it asserts that destination and source have same nlw_, but my destination is not initialized
2731 //ews[0] = std::move(ews[1]);
2732 memcpy((void*)ews, (void*)(ews+1), sizeof(ConExprWriter));
2733 }
2734 }
2735 break;
2736 }
2737
2739 {
2740 if( SCIPisExprSum(scip, expr) )
2741 {
2742 int childidx = SCIPexpriterGetChildIdxDFS(it);
2743
2744 ConExprWriter* ews = (ConExprWriter*)SCIPexpriterGetCurrentUserData(it).ptrval;
2745
2746 if( SCIPgetCoefsExprSum(expr)[childidx] != 1.0 )
2747 {
2748 // destructor for ExprWriter that was stored for MUL
2749 ews->~ConExprWriter();
2750 }
2751 else
2752 {
2753 // move ExprWrite for SUM back into 2nd position
2754 //ews[1] = std::move(ews[0]);
2755 memcpy((void*)(ews+1), (void*)ews, sizeof(ConExprWriter));
2756 }
2757 }
2758 else if( SCIPisExprProduct(scip, expr) )
2759 {
2760 ConExprWriter* ew2 = (ConExprWriter*)SCIPexpriterGetCurrentUserData(it).ptrval;
2761
2762 int childidx = SCIPexpriterGetChildIdxDFS(it);
2763 int nchildren = SCIPexprGetNChildren(expr);
2764 if( childidx < nchildren-2 )
2765 {
2766 // if there is more than one more factor coming (so we are in product with > 2 factors)
2767 // then add another MUL to the current ew2 and make the new ConExprWriter the current ew2
2768 ConExprWriter* newew = new ConExprWriter(ew2->OPut2(mp::nl::MUL));
2769 delete ew2;
2770
2771 SCIP_EXPRITER_USERDATA userdata;
2772 userdata.ptrval = newew;
2773 SCIPexpriterSetCurrentUserData(it, userdata);
2774 }
2775 }
2776
2777 break;
2778 }
2779
2781 {
2782 ConExprWriter* ews = (ConExprWriter*)SCIPexpriterGetCurrentUserData(it).ptrval;
2783 if( SCIPisExprSum(scip, expr) )
2784 {
2785 if( SCIPgetConstantExprSum(expr) == 0.0 && SCIPexprGetNChildren(expr) == 1 )
2786 {
2787 // continuation of nargs==1 in ENTEREXPR: copy the modified newew[1] into parentew
2788 ConExprWriter* parentew;
2789 if( expr == rootexpr )
2790 parentew = &ew;
2791 else
2792 parentew = (ConExprWriter*)SCIPexpriterGetExprUserData(it, SCIPexpriterGetParentDFS(it)).ptrval;
2793 assert(parentew != NULL);
2794
2795 memcpy((void*)parentew, (void*)(ews+1), sizeof(ConExprWriter));
2796 }
2797 else
2798 {
2799 // destructor for ExprWriter for SUM/ADD
2800 ews[1].~ConExprWriter();
2801 }
2803 }
2804 else
2805 {
2806 // write exponent of power (if not 0.5 or 2)
2807 if( SCIPisExprPower(scip, expr) && SCIPgetExponentExprPow(expr) != 0.5 && SCIPgetExponentExprPow(expr) != 2.0 )
2808 ews->NPut(SCIPgetExponentExprPow(expr));
2809
2810 delete ews;
2811 }
2812 break;
2813 }
2814 }
2815 }
2816
2817 SCIPfreeExpriter(&it);
2818
2819 if( unhandledexprmsg.tellp() > 0 )
2820 throw mp::UnsupportedError(unhandledexprmsg.str());
2821 }
2822
2823 template <class RowObjNameWriter>
2825 RowObjNameWriter& wrt
2826 ) const
2827 {
2828 if( !wrt || genericnames )
2829 return;
2830
2831 for( int c = 0; c < nalgconss; ++c )
2832 wrt << SCIPconsGetName(algconss[c]);
2833
2834 for( int v = 0; v < naggconss; ++v )
2835 {
2836 std::string aggname("aggr_");
2837 aggname += SCIPvarGetName(aggconss[v]);
2838 wrt << aggname.c_str();
2839 }
2840
2841 wrt << "obj";
2842 }
2843
2844 template <class ColNameWriter>
2846 ColNameWriter& wrt
2847 ) const
2848 {
2849 if( !wrt || genericnames )
2850 return;
2851
2852 for( int v = 0; v < nvars; ++v )
2853 wrt << SCIPvarGetName(vars[v]);
2854 }
2855};
2856
2857/*
2858 * Callback methods of probdata
2859 */
2860
2861/** frees user data of original problem (called when the original problem is freed) */
2862static
2863SCIP_DECL_PROBDELORIG(probdataDelOrigNl)
2864{
2865 SCIP_PROBNLDATA* probnldata = (SCIP_PROBNLDATA*)*probdata;
2866 int i;
2867
2868 assert(probnldata != NULL);
2869 assert(probnldata->vars != NULL || probnldata->nvars == 0);
2870 assert(probnldata->conss != NULL || probnldata->conss == 0);
2871
2872 for( i = 0; i < probnldata->nconss; ++i )
2873 {
2874 SCIP_CALL( SCIPreleaseCons(scip, &probnldata->conss[i]) );
2875 }
2876 SCIPfreeBlockMemoryArrayNull(scip, &probnldata->conss, probnldata->nconss);
2877
2878 for( i = 0; i < probnldata->nvars; ++i )
2879 {
2880 SCIP_CALL( SCIPreleaseVar(scip, &probnldata->vars[i]) );
2881 }
2882 SCIPfreeBlockMemoryArrayNull(scip, &probnldata->vars, probnldata->nvars);
2883
2884 SCIPfreeBlockMemoryArrayNull(scip, &probnldata->filenamestub, probnldata->filenamestublen+5);
2885
2886 SCIPfreeMemory(scip, (SCIP_PROBNLDATA**)probdata);
2887
2888 return SCIP_OKAY;
2889}
2890
2891/*
2892 * Callback methods of reader
2893 */
2894
2895/** copy method for reader plugins (called when SCIP copies plugins) */
2896static
2898{ /*lint --e{715}*/
2899 assert(scip != NULL);
2900
2902
2903 return SCIP_OKAY;
2904}
2905
2906/** problem reading method of reader */
2907static
2909{ /*lint --e{715}*/
2910 assert(scip != NULL);
2911 assert(reader != NULL);
2912 assert(filename != NULL);
2913 assert(result != NULL);
2914
2915 *result = SCIP_DIDNOTRUN;
2916
2917 try
2918 {
2919 // try to read the .nl file and setup SCIP problem
2920 AMPLProblemHandler handler(scip, filename);
2921 try
2922 {
2923 mp::ReadNLFile(filename, handler);
2924 }
2925 catch( const mp::UnsupportedError& e )
2926 {
2927 SCIPerrorMessage("unsupported construct in AMPL .nl file %s: %s\n", filename, e.what());
2928
2929 SCIP_CALL( handler.cleanup() );
2930
2931 return SCIP_READERROR;
2932 }
2933 catch( const mp::Error& e )
2934 {
2935 // some other error from ampl/mp, maybe invalid .nl file
2936 SCIPerrorMessage("%s\n", e.what());
2937
2938 SCIP_CALL( handler.cleanup() );
2939
2940 return SCIP_READERROR;
2941 }
2942 catch( const fmt::SystemError& e )
2943 {
2944 // probably a file open error, probably because file not found
2945 SCIPerrorMessage("%s\n", e.what());
2946
2947 SCIP_CALL( handler.cleanup() );
2948
2949 return SCIP_NOFILE;
2950 }
2951 catch( const std::bad_alloc& e )
2952 {
2953 SCIPerrorMessage("Out of memory: %s\n", e.what());
2954
2955 SCIP_CALL( handler.cleanup() );
2956
2957 return SCIP_NOMEMORY;
2958 }
2959 }
2960 catch( const std::exception& e )
2961 {
2962 SCIPerrorMessage("%s\n", e.what());
2963 return SCIP_ERROR;
2964 }
2965
2966 *result = SCIP_SUCCESS;
2967
2968 return SCIP_OKAY;
2969}
2970
2971#ifdef _WIN32
2972#define PATHSEP "\\"
2973#else
2974#define PATHSEP "/"
2975#endif
2976
2977/** problem writing method of reader */
2978static
2980{ /*lint --e{715}*/
2981 mp::WriteNLResult writerresult;
2982 SCIP_Bool binary;
2983 SCIP_Bool comments;
2984 char* tempdir = NULL;
2985 char* tempnamestub = NULL;
2986 char* tempname = NULL;
2987 FILE* tempfile = NULL;
2988 int templen;
2990
2991 *result = SCIP_DIDNOTRUN;
2992
2993 SCIP_CALL( SCIPgetBoolParam(scip, "reading/" READER_NAME "/binary", &binary) );
2994 SCIP_CALL( SCIPgetBoolParam(scip, "reading/" READER_NAME "/comments", &comments) );
2995
2996 SCIPNLFeeder nlf(scip,
2997 name, objsense, objscale, objoffset,
2998 vars, nvars, fixedvars, nfixedvars,
2999 conss, nconss,
3000 binary, comments, genericnames);
3001
3002 try
3003 {
3004 /* we need to give the NLWriter a filename, but can only rely on the FILE* from SCIP
3005 * so we let the NLWriter write to a temporary file and then copy its content to file;
3006 * if we also have a filename, then we do the same for the row/col files
3007 */
3008 mp::NLUtils nlutils;
3009 char buf[1024];
3010 int n;
3011
3012 /* construct a temporary directory in /tmp/scipnlwrite-XXXXXX */
3013#ifdef _WIN32
3014 TCHAR systemtmp[MAX_PATH + 1];
3015 DWORD gettemprc = GetTempPath2A(MAX_PATH + 1, systemtmp);
3016 if( gettemprc == 0 || gettemprc > MAX_PATH + 1 )
3017 {
3018 SCIPerrorMessage("Cannot get name of directory for temporary files: error %d\n", errno);
3020 goto TERMINATE;
3021 }
3022#else
3023 const char* systemtmp = getenv("TMPDIR");
3024 if( systemtmp == NULL )
3025 systemtmp = "/tmp";
3026#endif
3027 templen = strlen(systemtmp) + 30;
3028 SCIP_CALL( SCIPallocBufferArray(scip, &tempdir, templen) );
3029 (void) SCIPsnprintf(tempdir, templen, "%s" PATHSEP "scipnlwrite-XXXXXX", systemtmp);
3030
3031#ifdef _WIN32
3032 if( _mktemp_s(tempdir, templen) )
3033 {
3034 SCIPerrorMessage("Cannot generate name for temporary directory from template <%s>: error %d\n", tempdir, errno);
3036 goto TERMINATE;
3037 }
3038 if( _mkdir(tempdir) )
3039 {
3040 SCIPerrorMessage("Cannot create temporary directory with name <%s>: error %d\n", tempdir, errno);
3042 goto TERMINATE;
3043 }
3044#else
3045 if( mkdtemp(tempdir) == NULL )
3046 {
3047 SCIPerrorMessage("Cannot generate temporary directory from template <%s>: error %d\n", tempdir, errno);
3049 goto TERMINATE;
3050 }
3051#endif
3052
3053 /* stub for temporary file: /tmp/scipnlwrite-XXXXXX/prob */
3054 SCIP_CALL( SCIPallocBufferArray(scip, &tempnamestub, templen) );
3055 (void) SCIPsnprintf(tempnamestub, templen, "%s" PATHSEP "prob", tempdir);
3056
3057 SCIPdebugMsg(scip, "Temporary file stub for NL writing: %s\n", tempnamestub);
3058
3059 writerresult = mp::WriteNLFile(tempnamestub, nlf, nlutils);
3060
3061 /* name of nl file that was (possibly) written: tempnamestub + .nl */
3062 SCIP_CALL( SCIPallocBufferArray(scip, &tempname, templen) );
3063 (void) SCIPsnprintf(tempname, templen, "%s.nl", tempnamestub);
3064
3065 switch( writerresult.first )
3066 {
3067 case NLW2_WriteNL_OK:
3068 break;
3069 case NLW2_WriteNL_CantOpen:
3070 SCIPerrorMessage("%s\n", writerresult.second.c_str());
3072 goto TERMINATE;
3073 case NLW2_WriteNL_Failed:
3074 SCIPerrorMessage("%s\n", writerresult.second.c_str());
3075 rc = SCIP_WRITEERROR;
3076 goto TERMINATE;
3077 case NLW2_WriteNL_Unset:
3078 default:
3079 SCIPerrorMessage("%s\n", writerresult.second.c_str());
3080 rc = SCIP_ERROR;
3081 goto TERMINATE;
3082 }
3083
3084 /* copy temporary file into file */
3085 tempfile = fopen(tempname, "rb");
3086 if( tempfile == NULL )
3087 {
3088 SCIPerrorMessage("Cannot open temporary file <%s> for reading: error %d\n", tempname, errno);
3089 return SCIP_NOFILE;
3090 }
3091
3092 while( (n=fread(buf, 1, sizeof(buf), tempfile)) != 0 )
3093 fwrite(buf, 1, n, file != NULL ? file : stdout);
3094
3095 fclose(tempfile);
3096
3097 /* move col/row files */
3098 if( !genericnames && filename != NULL )
3099 {
3100 char* filename2 = NULL;
3101 FILE* file2;
3102 int filenamelen;
3103
3104 /* before overwriting tempname, remove .nl file */
3105 remove(tempname);
3106
3107 /* make filename2 same as filename, but with .nl removed, if present */
3108 filenamelen = strlen(filename);
3109 SCIP_CALL( SCIPallocBufferArray(scip, &filename2, filenamelen + 5) );
3110 memcpy(filename2, filename, filenamelen+1);
3111 if( SCIPstrcasecmp(filename + (filenamelen-3), ".nl") == 0 )
3112 {
3113 filename2[filenamelen-3] = '\0';
3114 filenamelen -= 3;
3115 }
3116
3117 /* copy row file from temporary to current location */
3118 SCIPsnprintf(tempname, templen, "%s.row", tempnamestub);
3119 strcpy(filename2 + filenamelen, ".row");
3120
3121 tempfile = fopen(tempname, "rb");
3122 if( tempfile == NULL )
3123 {
3124 SCIPerrorMessage("Cannot open temporary file <%s> for reading: error %d\n", tempname, errno);
3125 return SCIP_NOFILE;
3126 }
3127 file2 = fopen(filename2, "wb");
3128 if( file2 == NULL )
3129 {
3130 SCIPerrorMessage("Cannot open file <%s> for writing: error %d\n", filename2, errno);
3131 return SCIP_FILECREATEERROR;
3132 }
3133
3134 while( (n=fread(buf, 1, sizeof(buf), tempfile)) != 0 )
3135 fwrite(buf, 1, n, file2);
3136
3137 fclose(file2);
3138 fclose(tempfile);
3139 remove(tempname); /* remove .row file */
3140
3141 /* copy col file from temporary to current location */
3142 SCIPsnprintf(tempname, templen, "%s.col", tempnamestub);
3143 strcpy(filename2 + filenamelen, ".col");
3144
3145 tempfile = fopen(tempname, "rb");
3146 if( tempfile == NULL )
3147 {
3148 SCIPerrorMessage("Cannot open temporary file <%s> for reading: error %d\n", tempname, errno);
3149 return SCIP_NOFILE;
3150 }
3151 file2 = fopen(filename2, "wb");
3152 if( file2 == NULL )
3153 {
3154 SCIPerrorMessage("Cannot open file <%s> for writing: error %d\n", filename2, errno);
3155 return SCIP_FILECREATEERROR;
3156 }
3157
3158 while( (n=fread(buf, 1, sizeof(buf), tempfile)) != 0 )
3159 fwrite(buf, 1, n, file2);
3160
3161 fclose(file2);
3162 fclose(tempfile);
3163 /* .col file will be removed further down */
3164
3165 SCIPfreeBufferArray(scip, &filename2);
3166 }
3167
3168 *result = SCIP_SUCCESS;
3169
3170 TERMINATE: ;
3171 }
3172 catch( const mp::UnsupportedError& e )
3173 {
3174 SCIPerrorMessage("constraint not writable as AMPL .nl: %s\n", e.what());
3175 rc = SCIP_WRITEERROR;
3176 }
3177 catch( const mp::Error& e )
3178 {
3179 // some other error from ampl/mp
3180 SCIPerrorMessage("%s\n", e.what());
3181 rc = SCIP_WRITEERROR;
3182 }
3183 catch( const fmt::SystemError& e )
3184 {
3185 // probably a file open error
3186 SCIPerrorMessage("%s\n", e.what());
3188 }
3189 catch( const std::bad_alloc& e )
3190 {
3191 SCIPerrorMessage("Out of memory: %s\n", e.what());
3192 rc = SCIP_NOMEMORY;
3193 }
3194 catch( const std::exception& e )
3195 {
3196 SCIPerrorMessage("%s\n", e.what());
3197 rc = SCIP_ERROR;
3198 }
3199
3200 /* remove file with tempname, or fail trying */
3201 if( tempname != NULL )
3202 {
3203 remove(tempname);
3204 SCIPfreeBufferArray(scip, &tempname);
3205 }
3206
3207 SCIPfreeBufferArrayNull(scip, &tempnamestub);
3208
3209 if( tempdir != NULL )
3210 {
3211 remove(tempdir);
3212 SCIPfreeBufferArray(scip, &tempdir);
3213 }
3214
3215 return rc;
3216}
3217
3218/*
3219 * reader specific interface methods
3220 */
3221
3222/** includes the AMPL .nl file reader in SCIP */
3224 SCIP* scip /**< SCIP data structure */
3225 )
3226{
3227 SCIP_READER* reader = NULL;
3228
3229 /* include reader */
3231 assert(reader != NULL);
3232
3233 /* set non fundamental callbacks via setter functions */
3234 SCIP_CALL( SCIPsetReaderCopy(scip, reader, readerCopyNl) );
3235 SCIP_CALL( SCIPsetReaderRead(scip, reader, readerReadNl) );
3236 SCIP_CALL( SCIPsetReaderWrite(scip, reader, readerWriteNl) );
3237
3238 /* add nl reader parameters for writing routines */
3240 "reading/" READER_NAME "/binary", "should nl files be written in binary format",
3241 NULL, FALSE, FALSE, NULL, NULL) );
3243 "reading/" READER_NAME "/comments", "should comments be written to nl files",
3244 NULL, FALSE, FALSE, NULL, NULL) );
3245
3246 SCIP_CALL( SCIPincludeExternalCodeInformation(scip, "AMPL/MP 4.0.3", "AMPL .nl file reader library (github.com/ampl/mp)") );
3247
3248 return SCIP_OKAY;
3249}
3250
3251/** writes AMPL solution file
3252 *
3253 * problem must have been read with .nl reader
3254 */
3256 SCIP* scip /**< SCIP data structure */
3257 )
3258{
3259 SCIP_PROBNLDATA* probdata;
3260
3261 assert(scip != NULL);
3262
3263 probdata = (SCIP_PROBNLDATA*)SCIPgetProbData(scip);
3264 if( probdata == NULL )
3265 {
3266 SCIPerrorMessage("No AMPL nl file read. Cannot write AMPL solution.\n");
3267 return SCIP_ERROR;
3268 }
3269
3270 probdata->filenamestub[probdata->filenamestublen] = '.';
3271 probdata->filenamestub[probdata->filenamestublen+1] = 's';
3272 probdata->filenamestub[probdata->filenamestublen+2] = 'o';
3273 probdata->filenamestub[probdata->filenamestublen+3] = 'l';
3274 probdata->filenamestub[probdata->filenamestublen+4] = '\0';
3275
3276 FILE* solfile = fopen(probdata->filenamestub, "w");
3277 if( solfile == NULL )
3278 {
3279 SCIPerrorMessage("could not open file <%s> for writing\n", probdata->filenamestub);
3280 probdata->filenamestub[probdata->filenamestublen] = '\0';
3281
3282 return SCIP_WRITEERROR;
3283 }
3284 probdata->filenamestub[probdata->filenamestublen] = '\0';
3285
3286 // see ampl/mp:sol.h:WriteSolFile() (seems buggy, https://github.com/ampl/mp/issues/135) and asl/writesol.c for solution file format
3287 SCIP_CALL( SCIPprintStatus(scip, solfile) );
3288 SCIPinfoMessage(scip, solfile, "\n\n");
3289
3290 SCIPinfoMessage(scip, solfile, "Options\n%d\n", probdata->namplopts);
3291 for( int i = 0; i < probdata->namplopts; ++i )
3292 SCIPinfoMessage(scip, solfile, "%d\n", probdata->amplopts[i]);
3293
3294 bool haveprimal = SCIPgetBestSol(scip) != NULL;
3295 bool havedual = probdata->islp && SCIPgetStage(scip) == SCIP_STAGE_SOLVED && !SCIPhasPerformedPresolve(scip);
3296
3297 SCIPinfoMessage(scip, solfile, "%d\n%d\n", probdata->nconss, havedual ? probdata->nconss : 0);
3298 SCIPinfoMessage(scip, solfile, "%d\n%d\n", probdata->nvars, haveprimal ? probdata->nvars : 0);
3299
3301
3302 if( havedual )
3303 for( int c = 0; c < probdata->nconss; ++c )
3304 {
3305 SCIP_CONS* transcons;
3306 SCIP_Real dualval;
3307
3308 /* dual solution is created by LP solver and therefore only available for linear constraints */
3309 SCIP_CALL( SCIPgetTransformedCons(scip, probdata->conss[c], &transcons) );
3310 assert(transcons == NULL || strcmp(SCIPconshdlrGetName(SCIPconsGetHdlr(transcons)), "linear") == 0);
3311
3312 if( transcons == NULL )
3313 dualval = 0.0;
3315 dualval = SCIPgetDualsolLinear(scip, transcons);
3316 else
3317 dualval = -SCIPgetDualsolLinear(scip, transcons);
3318 assert(dualval != SCIP_INVALID);
3319
3320 SCIPinfoMessage(scip, solfile, "%.17g\n", dualval);
3321 }
3322
3323 if( haveprimal )
3324 for( int i = 0; i < probdata->nvars; ++i )
3325 SCIPinfoMessage(scip, solfile, "%.17g\n", SCIPgetSolVal(scip, SCIPgetBestSol(scip), probdata->vars[i]));
3326
3327 /* AMPL solve status codes are at https://mp.ampl.com/details.html#_CPPv4N2mp3sol6StatusE
3328 * (mp::sol::Status enum in amplmp/include/mp/common.h)
3329 */
3330 int solve_result_num = mp::sol::FAILURE;
3331 switch( SCIPgetStatus(scip) )
3332 {
3334 break;
3337 if( haveprimal )
3338 solve_result_num = mp::sol::LIMIT_FEAS_INTERRUPT;
3339 else
3340 solve_result_num = mp::sol::LIMIT_NO_FEAS_INTERRUPT;
3341 break;
3345 if( haveprimal )
3346 solve_result_num = mp::sol::LIMIT_FEAS_NODES;
3347 else
3348 solve_result_num = mp::sol::LIMIT_NO_FEAS_NODES;
3349 break;
3351 if( haveprimal )
3352 solve_result_num = mp::sol::LIMIT_FEAS_TIME;
3353 else
3354 solve_result_num = mp::sol::LIMIT_NO_FEAS_TIME;
3355 break;
3357 if( haveprimal )
3358 solve_result_num = mp::sol::LIMIT_FEAS_SOFTMEM;
3359 else
3360 solve_result_num = mp::sol::LIMIT_NO_FEAS_SOFTMEM;
3361 break;
3363 /* there is no enum value for gaplimit, so use "work limit" */
3364 if( haveprimal )
3365 solve_result_num = mp::sol::LIMIT_FEAS_WORK;
3366 else
3367 solve_result_num = mp::sol::LIMIT_NO_FEAS_WORK;
3368 break;
3370 solve_result_num = mp::sol::LIMIT_FEAS_BESTOBJ;
3371 break;
3373 if( haveprimal )
3374 solve_result_num = mp::sol::LIMIT_FEAS_BESTBND;
3375 else
3376 solve_result_num = mp::sol::LIMIT_NO_FEAS_BESTBND;
3377 break;
3379 if( haveprimal )
3380 solve_result_num = mp::sol::LIMIT_FEAS_NUMSOLS;
3381 else /* reach solution limit without solution? */
3382 solve_result_num = mp::sol::LIMIT_NO_FEAS;
3383 break;
3386 /* rare SCIP specific limits that don't map to an AMPL status */
3387 if( haveprimal )
3388 solve_result_num = mp::sol::LIMIT_FEAS;
3389 else
3390 solve_result_num = mp::sol::LIMIT_NO_FEAS;
3391 break;
3393 solve_result_num = mp::sol::SOLVED;
3394 break;
3396 solve_result_num = mp::sol::INFEASIBLE;
3397 break;
3399 if( haveprimal )
3400 solve_result_num = mp::sol::UNBOUNDED_FEAS;
3401 else
3402 solve_result_num = mp::sol::UNBOUNDED_NO_FEAS;
3403 break;
3405 solve_result_num = mp::sol::LIMIT_INF_UNB;
3406 break;
3407 }
3408 SCIPinfoMessage(scip, solfile, "objno 0 %d\n", solve_result_num);
3409
3410 if( fclose(solfile) != 0 )
3411 {
3412 SCIPerrorMessage("could not close solution file after writing\n");
3413 return SCIP_WRITEERROR;
3414 }
3415
3416 return SCIP_OKAY;
3417}
SCIP_VAR * h
Definition: circlepacking.c:68
void AddTerm(int var_index, double coef)
receives notification of a term in the linear expression
Definition: reader_nl.cpp:794
LinearExprHandler(AMPLProblemHandler &amplph_, int index, int num_linear_terms)
constructor
Definition: reader_nl.cpp:777
LinearPartHandler(AMPLProblemHandler &amplph_)
Definition: reader_nl.cpp:1201
void AddTerm(int variableIndex, double coefficient)
Definition: reader_nl.cpp:1208
LinearPartHandler(AMPLProblemHandler &amplph_, int constraintIndex_)
Definition: reader_nl.cpp:1189
NumericArgHandler(int num_args)
constructor
Definition: reader_nl.cpp:672
void AddArg(SCIP_EXPR *term)
adds term to sum
Definition: reader_nl.cpp:681
std::shared_ptr< std::vector< SCIP_EXPR * > > v
Definition: reader_nl.cpp:669
void SetValue(int index, T value)
Definition: reader_nl.cpp:1092
SuffixHandler(AMPLProblemHandler &amplph_, fmt::StringRef name, mp::suf::Kind kind)
constructor
Definition: reader_nl.cpp:993
implementation of AMPL/MPs NLHandler that constructs a SCIP problem while a .nl file is read
Definition: reader_nl.cpp:142
void EndCommonExpr(int index, SCIP_EXPR *expr, int)
receive notification of the end of a common expression
Definition: reader_nl.cpp:833
LinearPartHandler LinearObjHandler
Definition: reader_nl.cpp:1235
NumericArgHandler BeginSum(int num_args)
receive notification of the beginning of a summation
Definition: reader_nl.cpp:690
void OnAlgebraicCon(int constraintIndex, SCIP_EXPR *expr)
receive notification of an algebraic constraint expression
Definition: reader_nl.cpp:741
LinearPartHandler OnLinearObjExpr(int objectiveIndex, int)
receive notification of the linear part of an objective
Definition: reader_nl.cpp:1238
LogicalExpr OnBinaryLogical(mp::expr::Kind kind, LogicalExpr lhs, LogicalExpr rhs)
receives notification of a binary logical expression <mp::expr::FIRST_BINARY_LOGICAL>
Definition: reader_nl.cpp:1302
LogicalExpr OnNot(LogicalExpr arg)
receives notification of a logical not <mp::expr::NOT>
Definition: reader_nl.cpp:1276
SCIP_EXPR * OnBinary(mp::expr::Kind kind, SCIP_EXPR *firstChild, SCIP_EXPR *secondChild)
receive notification of a binary expression
Definition: reader_nl.cpp:577
SCIP_EXPR * OnNumber(double value)
receive notification of a number in a nonlinear expression
Definition: reader_nl.cpp:482
LogicalExpr OnRelational(mp::expr::Kind kind, NumericExpr lhs, NumericExpr rhs)
Definition: reader_nl.cpp:1503
SuffixHandler< int > IntSuffixHandler
Definition: reader_nl.cpp:1154
LinearExprHandler BeginCommonExpr(int index, int num_linear_terms)
receive notification of the beginning of a common expression (defined variable)
Definition: reader_nl.cpp:821
AMPLProblemHandler(const AMPLProblemHandler &)=delete
LinearConHandler OnLinearConExpr(int constraintIndex, int)
receive notification of the linear part of a constraint
Definition: reader_nl.cpp:1252
void OnInitialValue(int var_index, double value)
receive notification of the initial value for a variable
Definition: reader_nl.cpp:935
SCIP_EXPR * OnVariableRef(int variableIndex)
receive notification of a variable reference in a nonlinear expression
Definition: reader_nl.cpp:497
AMPLProblemHandler(SCIP *scip_, const char *filename)
Definition: reader_nl.cpp:272
ColumnSizeHandler OnColumnSizes()
receives notification of Jacobian column sizes
Definition: reader_nl.cpp:958
AMPLProblemHandler & operator=(const AMPLProblemHandler &)=delete
LinearPartHandler LinearConHandler
Definition: reader_nl.cpp:1249
void OnVarBounds(int variableIndex, double variableLB, double variableUB)
receive notification of variable bounds
Definition: reader_nl.cpp:865
SCIP_EXPR * OnCommonExprRef(int expr_index)
receive notification of a common expression (defined variable) reference
Definition: reader_nl.cpp:854
~AMPLProblemHandler() override
Definition: reader_nl.cpp:339
void OnHeader(const mp::NLHeader &h)
Definition: reader_nl.cpp:352
void OnLogicalCon(int index, LogicalExpr expr)
receives notification of a logical constraint expression
Definition: reader_nl.cpp:753
DblSuffixHandler OnDblSuffix(fmt::StringRef name, mp::suf::Kind kind, int)
receive notification of a double suffix
Definition: reader_nl.cpp:1167
void OnConBounds(int index, double lb, double ub)
receive notification of constraint sides
Definition: reader_nl.cpp:889
IntSuffixHandler OnIntSuffix(fmt::StringRef name, mp::suf::Kind kind, int)
receive notification of an integer suffix
Definition: reader_nl.cpp:1156
LogicalExpr OnBool(bool value)
receives notification of a Boolean value <mp::expr::BOOL>
Definition: reader_nl.cpp:1261
SCIP_EXPR * OnUnary(mp::expr::Kind kind, SCIP_EXPR *child)
receive notification of a unary expression
Definition: reader_nl.cpp:509
SCIP_EXPR * EndSum(NumericArgHandler handler)
receive notification of the end of a summation
Definition: reader_nl.cpp:699
void OnInitialDualValue(int, double)
receives notification of the initial value for a dual variable
Definition: reader_nl.cpp:949
SCIP_RETCODE cleanup()
Definition: reader_nl.cpp:1717
SuffixHandler< SCIP_Real > DblSuffixHandler
Definition: reader_nl.cpp:1165
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
Definition: reader_nl.cpp:711
void FeedConExpression(int i, ConExprWriter &ew)
Definition: reader_nl.cpp:2549
SCIPNLFeeder(SCIP *scip_, const char *probname_, SCIP_OBJSENSE objsense_, SCIP_Real objscale_, SCIP_Real objoffset_, SCIP_VAR **vars_, int nvars_, SCIP_VAR **fixedvars_, int nfixedvars_, SCIP_CONS **conss_, int nconss_, SCIP_Bool nlbinary_, SCIP_Bool nlcomments_, SCIP_Bool genericnames_)
Constructor.
Definition: reader_nl.cpp:2176
void FeedObjGradient(int i, ObjGradWriter &gw)
Definition: reader_nl.cpp:2296
void FeedVarBounds(VarBoundsWriter &vbw) const
Definition: reader_nl.cpp:2326
void FeedObjExpression(int i, ObjExprWriter &ew)
Definition: reader_nl.cpp:2316
bool WantNLComments() const
NL comments?
Definition: reader_nl.cpp:2276
void FeedRowAndObjNames(RowObjNameWriter &wrt) const
Definition: reader_nl.cpp:2824
int WantColumnSizes() const
Definition: reader_nl.cpp:2283
void FeedLinearConExpr(int i, ConLinearExprWriter &clw)
Definition: reader_nl.cpp:2400
void FeedConBounds(ConBoundsWriter &cbw)
Definition: reader_nl.cpp:2346
const char * ConDescription(int i)
Definition: reader_nl.cpp:2388
void FeedColNames(ColNameWriter &wrt) const
Definition: reader_nl.cpp:2845
mp::NLHeader Header()
Definition: reader_nl.cpp:2243
int ObjType(int) const
Definition: reader_nl.cpp:2288
Constraint handler for AND constraints, .
struct Problem PROBLEM
Constraint handler for knapsack constraints of the form , x binary and .
Constraint handler for linear constraints in their most general form, .
Constraint handler for logicor constraints (equivalent to set covering, but algorithms are suited fo...
constraint handler for nonlinear constraints specified by algebraic expressions
Constraint handler for "or" constraints, .
Constraint handler for the set partitioning / packing / covering constraints .
constraint handler for SOS type 1 constraints
constraint handler for SOS type 2 constraints
Constraint handler for variable bound constraints .
unsigned short Type
Definition: cons_xor.c:131
Constraint handler for XOR constraints, .
#define NULL
Definition: def.h:248
#define SCIP_MAXSTRLEN
Definition: def.h:269
#define SCIP_Longint
Definition: def.h:141
#define SCIP_INVALID
Definition: def.h:178
#define SCIP_Bool
Definition: def.h:91
#define SCIP_Real
Definition: def.h:156
#define ABS(x)
Definition: def.h:216
#define TRUE
Definition: def.h:93
#define FALSE
Definition: def.h:94
#define MAX(x, y)
Definition: def.h:220
#define SCIPABORT()
Definition: def.h:327
#define SCIP_CALL(x)
Definition: def.h:355
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)
int SCIPgetNVarsKnapsack(SCIP *scip, SCIP_CONS *cons)
SCIP_Real SCIPgetVbdcoefVarbound(SCIP *scip, SCIP_CONS *cons)
int SCIPgetNVarsLogicor(SCIP *scip, SCIP_CONS *cons)
SCIP_Real SCIPgetRhsLinear(SCIP *scip, SCIP_CONS *cons)
SCIP_VAR ** SCIPgetVarsLinear(SCIP *scip, SCIP_CONS *cons)
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 SCIPcreateConsBasicXor(SCIP *scip, SCIP_CONS **cons, const char *name, SCIP_Bool rhs, int nvars, SCIP_VAR **vars)
Definition: cons_xor.c:6084
SCIP_Real SCIPgetLhsLinear(SCIP *scip, SCIP_CONS *cons)
SCIP_RETCODE SCIPchgLhsNonlinear(SCIP *scip, SCIP_CONS *cons, SCIP_Real lhs)
SCIP_HASHMAP * SCIPgetVarExprHashmapNonlinear(SCIP_CONSHDLR *conshdlr)
int SCIPgetNVarsLinear(SCIP *scip, SCIP_CONS *cons)
SCIP_RETCODE SCIPcreateConsBasicOr(SCIP *scip, SCIP_CONS **cons, const char *name, SCIP_VAR *resvar, int nvars, SCIP_VAR **vars)
Definition: cons_or.c:2293
SCIP_RETCODE SCIPchgRhsNonlinear(SCIP *scip, SCIP_CONS *cons, SCIP_Real rhs)
SCIP_Real * SCIPgetValsLinear(SCIP *scip, SCIP_CONS *cons)
SCIP_RETCODE SCIPcreateConsBasicSOS1(SCIP *scip, SCIP_CONS **cons, const char *name, int nvars, SCIP_VAR **vars, SCIP_Real *weights)
Definition: cons_sos1.c:10709
SCIP_VAR * SCIPgetVbdvarVarbound(SCIP *scip, SCIP_CONS *cons)
int SCIPgetNVarsSetppc(SCIP *scip, SCIP_CONS *cons)
Definition: cons_setppc.c:9596
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_VAR ** SCIPgetVarsSetppc(SCIP *scip, SCIP_CONS *cons)
Definition: cons_setppc.c:9619
SCIP_EXPR * SCIPgetExprNonlinear(SCIP_CONS *cons)
SCIP_Real SCIPgetRhsNonlinear(SCIP_CONS *cons)
SCIP_VAR * SCIPgetVarVarbound(SCIP *scip, SCIP_CONS *cons)
SCIP_RETCODE SCIPcreateConsBasicNonlinear(SCIP *scip, SCIP_CONS **cons, const char *name, SCIP_EXPR *expr, SCIP_Real lhs, SCIP_Real rhs)
SCIP_Longint * SCIPgetWeightsKnapsack(SCIP *scip, SCIP_CONS *cons)
SCIP_Longint SCIPgetCapacityKnapsack(SCIP *scip, SCIP_CONS *cons)
SCIP_Real SCIPgetLhsVarbound(SCIP *scip, SCIP_CONS *cons)
SCIP_SETPPCTYPE SCIPgetTypeSetppc(SCIP *scip, SCIP_CONS *cons)
Definition: cons_setppc.c:9642
SCIP_VAR ** SCIPgetVarsLogicor(SCIP *scip, SCIP_CONS *cons)
SCIP_Real SCIPgetRhsVarbound(SCIP *scip, SCIP_CONS *cons)
SCIP_VAR ** SCIPgetVarsKnapsack(SCIP *scip, SCIP_CONS *cons)
SCIP_RETCODE SCIPcreateConsBasicAnd(SCIP *scip, SCIP_CONS **cons, const char *name, SCIP_VAR *resvar, int nvars, SCIP_VAR **vars)
Definition: cons_and.c:5180
SCIP_RETCODE SCIPchgExprNonlinear(SCIP *scip, SCIP_CONS *cons, SCIP_EXPR *expr)
SCIP_RETCODE SCIPchgLhsLinear(SCIP *scip, SCIP_CONS *cons, SCIP_Real lhs)
SCIP_Real SCIPgetLhsNonlinear(SCIP_CONS *cons)
SCIP_RETCODE SCIPcreateConsBasicSOS2(SCIP *scip, SCIP_CONS **cons, const char *name, int nvars, SCIP_VAR **vars, SCIP_Real *weights)
Definition: cons_sos2.c:2671
@ SCIP_SETPPCTYPE_PARTITIONING
Definition: cons_setppc.h:87
@ SCIP_SETPPCTYPE_COVERING
Definition: cons_setppc.h:89
@ SCIP_SETPPCTYPE_PACKING
Definition: cons_setppc.h:88
SCIP_RETCODE SCIPcreateExprVar(SCIP *scip, SCIP_EXPR **expr, SCIP_VAR *var, SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), void *ownercreatedata)
Definition: expr_var.c:398
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_Bool SCIPisExprAbs(SCIP *scip, SCIP_EXPR *expr)
Definition: expr_abs.c:546
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_Bool SCIPisExprLog(SCIP *scip, SCIP_EXPR *expr)
Definition: expr_log.c:648
SCIP_RETCODE SCIPappendExprSumExpr(SCIP *scip, SCIP_EXPR *expr, SCIP_EXPR *child, SCIP_Real childcoef)
Definition: expr_sum.c:1154
SCIP_Bool SCIPisExprExp(SCIP *scip, SCIP_EXPR *expr)
Definition: expr_exp.c:528
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_Bool SCIPisExprCos(SCIP *scip, SCIP_EXPR *expr)
Definition: expr_trig.c:1480
SCIP_Bool SCIPisExprSin(SCIP *scip, SCIP_EXPR *expr)
Definition: expr_trig.c:1469
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:1117
SCIP_RETCODE SCIPcreateExprValue(SCIP *scip, SCIP_EXPR **expr, SCIP_Real value, SCIP_DECL_EXPR_OWNERCREATE((*ownercreate)), void *ownercreatedata)
Definition: expr_value.c:274
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:3185
SCIP_Bool SCIPhasPerformedPresolve(SCIP *scip)
Definition: scip_general.c:730
SCIP_RETCODE SCIPprintStatus(SCIP *scip, FILE *file)
Definition: scip_general.c:631
SCIP_STATUS SCIPgetStatus(SCIP *scip)
Definition: scip_general.c:562
SCIP_STAGE SCIPgetStage(SCIP *scip)
Definition: scip_general.c:444
SCIP_RETCODE SCIPaddVar(SCIP *scip, SCIP_VAR *var)
Definition: scip_prob.c:1907
SCIP_RETCODE SCIPaddCons(SCIP *scip, SCIP_CONS *cons)
Definition: scip_prob.c:3274
SCIP_PROBDATA * SCIPgetProbData(SCIP *scip)
Definition: scip_prob.c:1139
SCIP_RETCODE SCIPsetObjsense(SCIP *scip, SCIP_OBJSENSE objsense)
Definition: scip_prob.c:1417
SCIP_OBJSENSE SCIPgetObjsense(SCIP *scip)
Definition: scip_prob.c:1400
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:119
void SCIPhashmapFree(SCIP_HASHMAP **hashmap)
Definition: misc.c:3095
int SCIPhashmapGetImageInt(SCIP_HASHMAP *hashmap, void *origin)
Definition: misc.c:3304
SCIP_RETCODE SCIPhashmapCreate(SCIP_HASHMAP **hashmap, BMS_BLKMEM *blkmem, int mapsize)
Definition: misc.c:3061
SCIP_Bool SCIPhashmapExists(SCIP_HASHMAP *hashmap, void *origin)
Definition: misc.c:3466
SCIP_RETCODE SCIPhashmapInsertInt(SCIP_HASHMAP *hashmap, void *origin, int image)
Definition: misc.c:3179
void SCIPinfoMessage(SCIP *scip, FILE *file, const char *formatstr,...)
Definition: scip_message.c:208
void SCIPverbMessage(SCIP *scip, SCIP_VERBLEVEL msgverblevel, FILE *file, const char *formatstr,...)
Definition: scip_message.c:225
#define SCIPdebugMsg
Definition: scip_message.h:78
void SCIPwarningMessage(SCIP *scip, const char *formatstr,...)
Definition: scip_message.c:120
SCIP_RETCODE SCIPgetBoolParam(SCIP *scip, const char *name, SCIP_Bool *value)
Definition: scip_param.c:250
SCIP_RETCODE SCIPaddBoolParam(SCIP *scip, const char *name, const char *desc, SCIP_Bool *valueptr, SCIP_Bool isadvanced, SCIP_Bool defaultvalue, SCIP_DECL_PARAMCHGD((*paramchgd)), SCIP_PARAMDATA *paramdata)
Definition: scip_param.c:57
const char * SCIPconshdlrGetName(SCIP_CONSHDLR *conshdlr)
Definition: cons.c:4316
SCIP_CONSHDLR * SCIPfindConshdlr(SCIP *scip, const char *name)
Definition: scip_cons.c:940
SCIP_RETCODE SCIPgetConsNVars(SCIP *scip, SCIP_CONS *cons, int *nvars, SCIP_Bool *success)
Definition: scip_cons.c:2621
SCIP_CONSHDLR * SCIPconsGetHdlr(SCIP_CONS *cons)
Definition: cons.c:8409
SCIP_RETCODE SCIPsetConsSeparated(SCIP *scip, SCIP_CONS *cons, SCIP_Bool separate)
Definition: scip_cons.c:1296
SCIP_RETCODE SCIPsetConsDynamic(SCIP *scip, SCIP_CONS *cons, SCIP_Bool dynamic)
Definition: scip_cons.c:1449
SCIP_RETCODE SCIPsetConsInitial(SCIP *scip, SCIP_CONS *cons, SCIP_Bool initial)
Definition: scip_cons.c:1271
SCIP_RETCODE SCIPsetConsEnforced(SCIP *scip, SCIP_CONS *cons, SCIP_Bool enforce)
Definition: scip_cons.c:1321
const char * SCIPconsGetName(SCIP_CONS *cons)
Definition: cons.c:8389
SCIP_RETCODE SCIPsetConsRemovable(SCIP *scip, SCIP_CONS *cons, SCIP_Bool removable)
Definition: scip_cons.c:1474
SCIP_RETCODE SCIPgetTransformedCons(SCIP *scip, SCIP_CONS *cons, SCIP_CONS **transcons)
Definition: scip_cons.c:1674
SCIP_RETCODE SCIPreleaseCons(SCIP *scip, SCIP_CONS **cons)
Definition: scip_cons.c:1173
SCIP_RETCODE SCIPsetConsPropagated(SCIP *scip, SCIP_CONS *cons, SCIP_Bool propagate)
Definition: scip_cons.c:1371
SCIP_RETCODE SCIPsetConsChecked(SCIP *scip, SCIP_CONS *cons, SCIP_Bool check)
Definition: scip_cons.c:1346
const char * SCIPexprhdlrGetName(SCIP_EXPRHDLR *exprhdlr)
Definition: expr.c:545
SCIP_RETCODE SCIPevalExpr(SCIP *scip, SCIP_EXPR *expr, SCIP_SOL *sol, SCIP_Longint soltag)
Definition: scip_expr.c:1661
int SCIPexprGetNChildren(SCIP_EXPR *expr)
Definition: expr.c:3872
SCIP_Real SCIPgetExponentExprPow(SCIP_EXPR *expr)
Definition: expr_pow.c:3448
SCIP_Bool SCIPisExprProduct(SCIP *scip, SCIP_EXPR *expr)
Definition: scip_expr.c:1490
SCIP_Bool SCIPexpriterIsEnd(SCIP_EXPRITER *iterator)
Definition: expriter.c:969
SCIP_EXPR * SCIPexpriterSkipDFS(SCIP_EXPRITER *iterator)
Definition: expriter.c:930
SCIP_Bool SCIPisExprSum(SCIP *scip, SCIP_EXPR *expr)
Definition: scip_expr.c:1479
SCIP_Real * SCIPgetCoefsExprSum(SCIP_EXPR *expr)
Definition: expr_sum.c:1554
SCIP_EXPRITER_USERDATA SCIPexpriterGetCurrentUserData(SCIP_EXPRITER *iterator)
Definition: expriter.c:756
SCIP_Bool SCIPisExprValue(SCIP *scip, SCIP_EXPR *expr)
Definition: scip_expr.c:1468
SCIP_RETCODE SCIPreleaseExpr(SCIP *scip, SCIP_EXPR **expr)
Definition: scip_expr.c:1443
SCIP_EXPR * SCIPexpriterGetCurrent(SCIP_EXPRITER *iterator)
Definition: expriter.c:683
void SCIPexpriterSetStagesDFS(SCIP_EXPRITER *iterator, SCIP_EXPRITER_STAGE stopstages)
Definition: expriter.c:664
SCIP_Bool SCIPisExprVar(SCIP *scip, SCIP_EXPR *expr)
Definition: scip_expr.c:1457
SCIP_RETCODE SCIPcreateExpriter(SCIP *scip, SCIP_EXPRITER **iterator)
Definition: scip_expr.c:2362
SCIP_EXPR * SCIPexpriterGetParentDFS(SCIP_EXPRITER *iterator)
Definition: expriter.c:740
SCIP_Real SCIPgetValueExprValue(SCIP_EXPR *expr)
Definition: expr_value.c:298
void SCIPexpriterSetCurrentUserData(SCIP_EXPRITER *iterator, SCIP_EXPRITER_USERDATA userdata)
Definition: expriter.c:806
SCIP_Bool SCIPisExprPower(SCIP *scip, SCIP_EXPR *expr)
Definition: scip_expr.c:1501
SCIP_Real SCIPexprGetEvalValue(SCIP_EXPR *expr)
Definition: expr.c:3946
SCIP_EXPR * SCIPexpriterGetNext(SCIP_EXPRITER *iterator)
Definition: expriter.c:858
SCIP_Real SCIPgetConstantExprSum(SCIP_EXPR *expr)
Definition: expr_sum.c:1569
SCIP_VAR * SCIPgetVarExprVar(SCIP_EXPR *expr)
Definition: expr_var.c:424
int SCIPexpriterGetChildIdxDFS(SCIP_EXPRITER *iterator)
Definition: expriter.c:707
void SCIPfreeExpriter(SCIP_EXPRITER **iterator)
Definition: scip_expr.c:2376
SCIP_EXPRITER_STAGE SCIPexpriterGetStageDFS(SCIP_EXPRITER *iterator)
Definition: expriter.c:696
SCIP_RETCODE SCIPexpriterInit(SCIP_EXPRITER *iterator, SCIP_EXPR *expr, SCIP_EXPRITER_TYPE type, SCIP_Bool allowrevisit)
Definition: expriter.c:501
SCIP_EXPRITER_USERDATA SCIPexpriterGetExprUserData(SCIP_EXPRITER *iterator, SCIP_EXPR *expr)
Definition: expriter.c:790
SCIP_EXPRHDLR * SCIPexprGetHdlr(SCIP_EXPR *expr)
Definition: expr.c:3895
SCIP_RETCODE SCIPincludeExternalCodeInformation(SCIP *scip, const char *name, const char *description)
Definition: scip_general.c:769
#define SCIPallocClearMemory(scip, ptr)
Definition: scip_mem.h:62
BMS_BLKMEM * SCIPblkmem(SCIP *scip)
Definition: scip_mem.c:57
#define SCIPallocClearBufferArray(scip, ptr, num)
Definition: scip_mem.h:126
#define SCIPallocBufferArray(scip, ptr, num)
Definition: scip_mem.h:124
#define SCIPfreeBufferArray(scip, ptr)
Definition: scip_mem.h:136
#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
#define SCIPfreeBufferArrayNull(scip, ptr)
Definition: scip_mem.h:137
SCIP_RETCODE SCIPincludeReaderBasic(SCIP *scip, SCIP_READER **readerptr, const char *name, const char *desc, const char *extension, SCIP_READERDATA *readerdata)
Definition: scip_reader.c:109
SCIP_RETCODE SCIPsetReaderCopy(SCIP *scip, SCIP_READER *reader, SCIP_DECL_READERCOPY((*readercopy)))
Definition: scip_reader.c:147
SCIP_RETCODE SCIPsetReaderRead(SCIP *scip, SCIP_READER *reader, SCIP_DECL_READERREAD((*readerread)))
Definition: scip_reader.c:195
SCIP_RETCODE SCIPsetReaderWrite(SCIP *scip, SCIP_READER *reader, SCIP_DECL_READERWRITE((*readerwrite)))
Definition: scip_reader.c:219
SCIP_SOL * SCIPgetBestSol(SCIP *scip)
Definition: scip_sol.c:2981
SCIP_RETCODE SCIPcreateSol(SCIP *scip, SCIP_SOL **sol, SCIP_HEUR *heur)
Definition: scip_sol.c:516
SCIP_RETCODE SCIPfreeSol(SCIP *scip, SCIP_SOL **sol)
Definition: scip_sol.c:1252
SCIP_RETCODE SCIPaddSolFree(SCIP *scip, SCIP_SOL **sol, SCIP_Bool *stored)
Definition: scip_sol.c:3909
SCIP_RETCODE SCIPprintSol(SCIP *scip, SCIP_SOL *sol, FILE *file, SCIP_Bool printzeros)
Definition: scip_sol.c:2349
SCIP_RETCODE SCIPsetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var, SCIP_Real val)
Definition: scip_sol.c:1571
SCIP_Real SCIPgetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var)
Definition: scip_sol.c:1765
SCIP_Real SCIPinfinity(SCIP *scip)
SCIP_Bool SCIPisLE(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Bool SCIPisInfinity(SCIP *scip, SCIP_Real val)
SCIP_Bool SCIPisNegative(SCIP *scip, SCIP_Real val)
SCIP_Bool SCIPisEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
SCIP_Real SCIPvarGetNegationConstant(SCIP_VAR *var)
Definition: var.c:23889
SCIP_Real SCIPvarGetMultaggrConstant(SCIP_VAR *var)
Definition: var.c:23843
SCIP_VAR * SCIPvarGetNegatedVar(SCIP_VAR *var)
Definition: var.c:23868
SCIP_RETCODE SCIPtightenVarUbGlobal(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound, SCIP_Bool force, SCIP_Bool *infeasible, SCIP_Bool *tightened)
Definition: scip_var.c:8257
SCIP_VARSTATUS SCIPvarGetStatus(SCIP_VAR *var)
Definition: var.c:23386
SCIP_Real SCIPvarGetAggrConstant(SCIP_VAR *var)
Definition: var.c:23771
SCIP_Real SCIPvarGetObj(SCIP_VAR *var)
Definition: var.c:23900
SCIP_Real SCIPvarGetAggrScalar(SCIP_VAR *var)
Definition: var.c:23748
SCIP_VARTYPE SCIPvarGetType(SCIP_VAR *var)
Definition: var.c:23453
SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
Definition: var.c:24142
SCIP_RETCODE SCIPvarSetInitial(SCIP_VAR *var, SCIP_Bool initial)
Definition: var.c:23354
const char * SCIPvarGetName(SCIP_VAR *var)
Definition: var.c:23267
SCIP_RETCODE SCIPreleaseVar(SCIP *scip, SCIP_VAR **var)
Definition: scip_var.c:1887
SCIP_RETCODE SCIPchgVarLbGlobal(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound)
Definition: scip_var.c:6141
SCIP_RETCODE SCIPchgVarType(SCIP *scip, SCIP_VAR *var, SCIP_VARTYPE vartype, SCIP_Bool *infeasible)
Definition: scip_var.c:10113
SCIP_RETCODE SCIPgetNegatedVar(SCIP *scip, SCIP_VAR *var, SCIP_VAR **negvar)
Definition: scip_var.c:2166
SCIP_VAR ** SCIPvarGetMultaggrVars(SCIP_VAR *var)
Definition: var.c:23806
int SCIPvarGetMultaggrNVars(SCIP_VAR *var)
Definition: var.c:23794
SCIP_RETCODE SCIPvarSetRemovable(SCIP_VAR *var, SCIP_Bool removable)
Definition: var.c:23370
SCIP_Bool SCIPvarIsNegated(SCIP_VAR *var)
Definition: var.c:23443
SCIP_RETCODE SCIPchgVarUbGlobal(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound)
Definition: scip_var.c:6230
SCIP_VAR * SCIPvarGetNegationVar(SCIP_VAR *var)
Definition: var.c:23878
SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
Definition: var.c:24120
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:184
SCIP_RETCODE SCIPchgVarObj(SCIP *scip, SCIP_VAR *var, SCIP_Real newobj)
Definition: scip_var.c:5372
SCIP_Real * SCIPvarGetMultaggrScalars(SCIP_VAR *var)
Definition: var.c:23818
SCIP_RETCODE SCIPtightenVarLbGlobal(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound, SCIP_Bool force, SCIP_Bool *infeasible, SCIP_Bool *tightened)
Definition: scip_var.c:8026
SCIP_VAR * SCIPvarGetAggrVar(SCIP_VAR *var)
Definition: var.c:23736
int SCIPstrcasecmp(const char *s1, const char *s2)
Definition: misc.c:10863
int SCIPsnprintf(char *t, int len, const char *s,...)
Definition: misc.c:10827
#define BMScopyMemoryArray(ptr, source, num)
Definition: memory.h:134
Definition: pqueue.h:38
#define SCIPerrorMessage
Definition: pub_message.h:64
#define SCIPdebug(x)
Definition: pub_message.h:93
SCIP_RETCODE SCIPincludeReaderNl(SCIP *scip)
Definition: reader_nl.cpp:3223
struct SCIP_ProbNlData SCIP_PROBNLDATA
Definition: reader_nl.cpp:131
static SCIP_DECL_PROBDELORIG(probdataDelOrigNl)
Definition: reader_nl.cpp:2863
#define READER_DESC
Definition: reader_nl.cpp:96
static SCIP_DECL_READERWRITE(readerWriteNl)
Definition: reader_nl.cpp:2979
#define READER_EXTENSION
Definition: reader_nl.cpp:97
#define SCIP_CALL_THROW(x)
Definition: reader_nl.cpp:101
SCIP_RETCODE SCIPwriteSolutionNl(SCIP *scip)
Definition: reader_nl.cpp:3255
static SCIP_DECL_READERCOPY(readerCopyNl)
Definition: reader_nl.cpp:2897
#define READER_NAME
Definition: reader_nl.cpp:95
static SCIP_DECL_READERREAD(readerReadNl)
Definition: reader_nl.cpp:2908
#define PATHSEP
Definition: reader_nl.cpp:2974
AMPL .nl file reader and writer.
#define SCIP_EXPRITER_VISITINGCHILD
Definition: type_expr.h:695
@ SCIP_EXPRITER_DFS
Definition: type_expr.h:718
#define SCIP_EXPRITER_VISITEDCHILD
Definition: type_expr.h:696
#define SCIP_EXPRITER_LEAVEEXPR
Definition: type_expr.h:697
#define SCIP_EXPRITER_ALLSTAGES
Definition: type_expr.h:698
#define SCIP_EXPRITER_ENTEREXPR
Definition: type_expr.h:694
@ SCIP_VERBLEVEL_HIGH
Definition: type_message.h:61
@ SCIP_VERBLEVEL_FULL
Definition: type_message.h:62
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
enum SCIP_Objsense SCIP_OBJSENSE
Definition: type_prob.h:50
@ SCIP_DIDNOTRUN
Definition: type_result.h:42
@ SCIP_SUCCESS
Definition: type_result.h:58
@ SCIP_FILECREATEERROR
Definition: type_retcode.h:48
@ SCIP_NOFILE
Definition: type_retcode.h:47
@ SCIP_READERROR
Definition: type_retcode.h:45
@ SCIP_WRITEERROR
Definition: type_retcode.h:46
@ SCIP_NOMEMORY
Definition: type_retcode.h:44
@ SCIP_OKAY
Definition: type_retcode.h:42
@ SCIP_ERROR
Definition: type_retcode.h:43
enum SCIP_Retcode SCIP_RETCODE
Definition: type_retcode.h:63
@ SCIP_STAGE_SOLVED
Definition: type_set.h:54
@ SCIP_STATUS_OPTIMAL
Definition: type_stat.h:43
@ SCIP_STATUS_TOTALNODELIMIT
Definition: type_stat.h:50
@ SCIP_STATUS_BESTSOLLIMIT
Definition: type_stat.h:60
@ SCIP_STATUS_SOLLIMIT
Definition: type_stat.h:59
@ SCIP_STATUS_UNBOUNDED
Definition: type_stat.h:45
@ SCIP_STATUS_UNKNOWN
Definition: type_stat.h:42
@ SCIP_STATUS_PRIMALLIMIT
Definition: type_stat.h:57
@ SCIP_STATUS_GAPLIMIT
Definition: type_stat.h:56
@ SCIP_STATUS_USERINTERRUPT
Definition: type_stat.h:47
@ SCIP_STATUS_TERMINATE
Definition: type_stat.h:48
@ SCIP_STATUS_INFORUNBD
Definition: type_stat.h:46
@ SCIP_STATUS_STALLNODELIMIT
Definition: type_stat.h:52
@ SCIP_STATUS_TIMELIMIT
Definition: type_stat.h:54
@ SCIP_STATUS_INFEASIBLE
Definition: type_stat.h:44
@ SCIP_STATUS_NODELIMIT
Definition: type_stat.h:49
@ SCIP_STATUS_DUALLIMIT
Definition: type_stat.h:58
@ SCIP_STATUS_MEMLIMIT
Definition: type_stat.h:55
@ SCIP_STATUS_RESTARTLIMIT
Definition: type_stat.h:62
@ SCIP_VARTYPE_INTEGER
Definition: type_var.h:65
@ SCIP_VARTYPE_CONTINUOUS
Definition: type_var.h:71
@ SCIP_VARTYPE_BINARY
Definition: type_var.h:64
@ SCIP_VARSTATUS_FIXED
Definition: type_var.h:54
@ SCIP_VARSTATUS_MULTAGGR
Definition: type_var.h:56
@ SCIP_VARSTATUS_NEGATED
Definition: type_var.h:57
@ SCIP_VARSTATUS_AGGREGATED
Definition: type_var.h:55
enum SCIP_Vartype SCIP_VARTYPE
Definition: type_var.h:73