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