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