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