diff --git a/CMakeLists.txt b/CMakeLists.txt index 9141c254a..dfa1bfe07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -525,7 +525,8 @@ if (BUILD_EXAMPLES OR NLW2_LIB) set(NLW2_DOC_SRC ${NLW2_SRC}) # Export library target names # for a calling CMake project - set(NLW2_LIB_NAME ${NLW2_LIB_NAME} PARENT_SCOPE) + set(NLW2_INCLUDE_PATH ${NLW2_INCLUDE_PATH} PARENT_SCOPE) + set(NLW2_LIB_NAME ${NLW2_LIB_NAME} PARENT_SCOPE) set(NLW2_C_API_LIB_NAME ${NLW2_C_API_LIB_NAME} PARENT_SCOPE) # Add to folders to better organize project in IDEs add_to_folder(${MP_FOLDER_PREFIX}nlw nlw2 nlw2_c_api nl-writer-example-c nl-writer-example-cpp) diff --git a/include/mp/flat/converter_model.h b/include/mp/flat/converter_model.h index 861c61876..5803d2c12 100644 --- a/include/mp/flat/converter_model.h +++ b/include/mp/flat/converter_model.h @@ -397,6 +397,7 @@ class FlatModel /// Push variables if (var_names_storage_.size() > 0) { // Convert names to c-str if needed + var_names_.reserve(var_names_storage_.size()); for (const std::string& s : var_names_storage_) var_names_.push_back(s.c_str()); backend.AddVariables({ var_lb_subm_, var_ub_subm_, var_type_, var_names_ }); @@ -436,7 +437,9 @@ class FlatModel template void SetObjectiveTo( Backend& backend, int i, const QuadraticObjective& obj) const { - if (obj.GetQPTerms().size()) + if (obj.HasExpr()) + backend.SetNLObjective(i, obj); + else if (obj.GetQPTerms().size()) backend.SetQuadraticObjective(i, obj); else backend.SetLinearObjective(i, obj); diff --git a/include/mp/flat/model_api_base.h b/include/mp/flat/model_api_base.h index 51fc7e42c..b06b819ea 100644 --- a/include/mp/flat/model_api_base.h +++ b/include/mp/flat/model_api_base.h @@ -172,10 +172,15 @@ class BasicFlatModelAPI { /// Whether accepts NLObjective (relevant in BasicExprModelAPI) static int AcceptsNLObj() { return 0; } + /// Placeholder for SetQuadraticObjective() void SetQuadraticObjective(int , const QuadraticObjective& ) { MP_UNSUPPORTED("FlatModelAPI::SetQuadraticObjective()"); } + /// Placeholder for SetNLObjective() + void SetNLObjective(int , const NLObjective& ) { + MP_UNSUPPORTED("FlatModelAPI::SetNLObjective()"); + } /// Placeholder for AddConstraint<>() template diff --git a/nl-writer2/CMakeLists.txt b/nl-writer2/CMakeLists.txt index bd837bbd3..2f3f5a989 100644 --- a/nl-writer2/CMakeLists.txt +++ b/nl-writer2/CMakeLists.txt @@ -33,8 +33,8 @@ set(NLW2_INC_FILES ${NLW2_INCLUDE_PATH}/mp/nl-model.h ${NLW2_INCLUDE_PATH}/mp/nl-solver.h ${NLW2_INCLUDE_PATH}/mp/nl-solver.hpp - ${NLW2_INCLUDE_PATH}/mp/nl-feeder.h - ${NLW2_INCLUDE_PATH}/mp/nl-writer2.h + ${NLW2_INCLUDE_PATH}/mp/nl-feeder.h + ${NLW2_INCLUDE_PATH}/mp/nl-writer2.h ${NLW2_INCLUDE_PATH}/mp/nl-writer2.hpp ${NLW2_INCLUDE_PATH}/mp/nl-writer2-misc.h ${NLW2_INCLUDE_PATH}/mp/nl-utils.h diff --git a/nl-writer2/include/mp/nl-feeder.h b/nl-writer2/include/mp/nl-feeder.h index 94b175f2b..fc97a7176 100644 --- a/nl-writer2/include/mp/nl-feeder.h +++ b/nl-writer2/include/mp/nl-feeder.h @@ -75,14 +75,16 @@ namespace mp { `~mp::NLFeeder` can be used as a base class for other feeders, or just be an interface prototype. - **Subclassed examples:** + **Subclassed interfaces and examples:** - Simplified (MI)QP interface via `~mp::NLModel`, `~mp::NLFeeder_Easy` - C API implementation class `~mp::NLW2_NLFeeder_C_Impl` - - Other examples/tests, e.g., see `ExampleNLFeeder`. + - Smaller examples/tests, e.g., see the example folder. + - MP2NL is a meta-driver interfacing the MP library + to external NL solvers. @param: *Impl* is the final CRTP type - derived from `~mp::NLFeeder`. + derived from `~mp::NLFeeder`. Currently unused. @param: *ExprType* is a type storing expressions from methods such as `~mp::NLFeeder::FeedExpr`. If not used, diff --git a/nl-writer2/include/mp/nl-writer2.h b/nl-writer2/include/mp/nl-writer2.h index 75831f50b..cf37703ca 100644 --- a/nl-writer2/include/mp/nl-writer2.h +++ b/nl-writer2/include/mp/nl-writer2.h @@ -168,6 +168,10 @@ class NLWriter2 : template class SparseVectorWriter { public: + /// Index type + using index_type = Index; + /// Value type + using value_type = Value; /// Construct SparseVectorWriter() { } /// Not construct(const&) @@ -216,6 +220,12 @@ class NLWriter2 : template class SingleSparseVecWrtFactory { public: + /// Index type + using index_type = Index; + /// Value type + using value_type = Value; + /// Vector writer type + using writer_type = SparseVectorWriter; /// Construct. /// @param fmt: format string for printf() /// containing %d for the number of sparse elements @@ -551,7 +561,7 @@ class NLWriter2 : /// Start writing an int-valued suffix. SuffixIntWriter StartIntSuffix( const char* name, int kind, int nnz); - /// Start writing an dbl-valued suffix. + /// Start writing a dbl-valued suffix. SuffixDblWriter StartDblSuffix( const char* name, int kind, int nnz); diff --git a/nl-writer2/include/mp/sol-handler.h b/nl-writer2/include/mp/sol-handler.h index 342988b1b..020f958df 100644 --- a/nl-writer2/include/mp/sol-handler.h +++ b/nl-writer2/include/mp/sol-handler.h @@ -57,7 +57,7 @@ namespace mp { \rst SOLHandler: reads solution details on request via provided callback objects. - See the examples folder. + See the examples folder and the MP2NL driver. `~mp::SOLHandler` can be used as a base class for other handlers, or just be an interface prototype. diff --git a/nl-writer2/include/mp/unused/nl-feeder-abstract-defs.h b/nl-writer2/include/mp/unused/nl-feeder-abstract-defs.h new file mode 100644 index 000000000..fd6543d5c --- /dev/null +++ b/nl-writer2/include/mp/unused/nl-feeder-abstract-defs.h @@ -0,0 +1,252 @@ +#ifndef NLFEEDERABSTRACTDEFS_H +#define NLFEEDERABSTRACTDEFS_H + + +namespace mp { + +/// SparseVectorWriter<> interface +template +class BasicSparseVectorWriter { +public: + /// Virtual destruct + virtual ~BasicSparseVectorWriter() { } + /// Number of elements left to write + virtual int NLeft() const = 0; + /// Write entry + virtual void Write(int index, T value) = 0; +}; + +/// Typedef sparse int vec writer +using BasicSparseIntVecWriter = BasicSparseVectorWriter; + +/// Typedef sparse double vec writer +using BasicSparseDblVecWriter = BasicSparseVectorWriter; + + +/// SparseVectorWriterFactory interface. +/// An implementation can be used once to produce a SparseVectorWriter<>. +template +class BasicSparseVectorWriterFactory { +public: + /// Virtual destruct + virtual ~BasicSparseVectorWriterFactory() { } + /// Make the SparseVectorWriter. + /// @note std::size_t without include: + /// https://stackoverflow.com/questions/36594569/which-header-should-i-include-for-size-t. + virtual BasicSparseVectorWriter& + MakeVectorWriter(decltype(sizeof(int)) nnz) = 0; +}; + +/// Typedef sparse int vec writer factory +using BasicSparseIntVecWrtFactory = BasicSparseVectorWriterFactory; + +/// Typedef sparse double vec writer factory +using BasicSparseDblVecWrtFactory = BasicSparseVectorWriterFactory; + + +/// NLExprWriter interface. +/// @note Store ExprArgWriter's as local objects. +template +class BasicNLExprWriter { +public: + /// Virtual destruct + virtual ~BasicNLExprWriter() { } + + /// Write the next arg as Feeder's native expression. + /// This recursively calls Feeder::FeedExpr(). + virtual void EPut(Expr e) = 0; + + /** Write the next arg as 'variable reference'. + * 0 <= index < num_vars is a solver variable; + * index >= num_vars is a defined variable. */ + virtual void VPut(int v, const char* descr="") = 0; + + /** Write numeric constant expression. */ + virtual void NPut(double x) = 0; + + /** Write string constant expression. */ + virtual void StrPut(const char* ) = 0; + + + /// typedef ExprArgWriter + /// @todo return temporary + using ExprArgWriter = int; // BasicNLExprWriter; + + /** Write the next arg as function call expression. */ + virtual ExprArgWriter FuncPut( + int index, int nArgs, const char* descr="") = 0; + + /// Write the next arg as AMPL opcode for a unary op. + /// @return 1-arg writer. + virtual ExprArgWriter OPut1(int opcode, const char* descr="") = 0; + /// Write AMPL opcode for a binary op. + virtual ExprArgWriter OPut2(int opcode, const char* descr="") = 0; + /// Write AMPL opcode for a 3-arg op. + virtual ExprArgWriter OPut3(int opcode, const char* descr="") = 0; + + /// Write AMPL opcode for an iterated op (min, exists, sum, etc). + /// For a piecewise-linear expression, \a nArgs should be + /// 2*(N slopes) and the arguments are: + /// break points, slopes, argument variable. + virtual ExprArgWriter OPutN( + int opcode, int nArgs, const char* descr="") = 0; + + /// Shortcut: OPut1( struct Opcode ) + template ExprArgWriter OPut1(Opcode oc) + { return OPut1(oc.code, oc.name); } + /// Shortcut: OPut2( struct Opcode ) + template ExprArgWriter OPut2(Opcode oc) + { return OPut2(oc.code, oc.name); } + /// Shortcut: OPut3( struct Opcode ) + template ExprArgWriter OPut3(Opcode oc) + { return OPut3(oc.code, oc.name); } + /// Shortcut: OPutN( struct Opcode, int nArgs ) + template ExprArgWriter OPutN( + Opcode oc, int nArgs) + { return OPutN(oc.code, nArgs, oc.name); } +}; + + +/** Interface: writer of a defined variable. */ +template +class BasicNLDefVarWriter { +public: + /// Virtual destruct + virtual ~BasicNLDefVarWriter() { } + + /// Write entries c*var[v] to the linear part + /// of the defining expression. + /// All nnz entries should be written before + /// the nonlinear expression. + /// This method can be called only once. + virtual BasicSparseDblVecWriter& GetLinExprWriter() = 0; + + /// Retrieve the nonlinear expression writer. + /// Should be used after the linear expression. + virtual BasicNLExprWriter& GetExprWriter() = 0; +}; + + +/** Interface: BasicNLDefVarWriterFactory. + * Write a sequence of defined variables, + * for example, all such used in a certain constraint. */ +template +class BasicNLDefVarWriterFactory { +public: + /// Virtual destruct. + virtual ~BasicNLDefVarWriterFactory() { } + + /// Start writing a defined variable. + /// + /// @param index: defined variable index, used to + /// reference it in subsequent expression graphs. + /// Thus, the index should be >= num_vars, + /// < num_vars+num_common_exprs. + /// Providing the index explicitly, because classical NL + /// likes special order of defined variables. + /// + /// @param nnz: number of nonzeros in the linear part. + /// + /// @param descr: meta-information - what is this variable, + /// for example, "nl(t[2])". + /// Providing it here because it's not + /// included in ColNames(). + /// + /// @return A callback object writing + /// a single defined variable. + virtual BasicNLDefVarWriter& StartDefVar( + int index, int nnz, const char* descr="") = 0; +}; + + +/** Interface: write \a num_vars variable bounds + * (all except defined variables). */ +class BasicNLVarBndWriter { +public: + /// virtual destruct + virtual ~BasicNLVarBndWriter() { } + /// Write range for the next variable. + virtual void WriteLbUb(double lb, double ub) = 0; +}; + + +/** Interface: write \a num_algebraic_cons constraint bounds. */ +class BasicNLConBndWriter { +public: + /// Virtual destruct + virtual ~BasicNLConBndWriter() { } + /// Write range/complementarity for the next constraint. + virtual void WriteAlgConRange( + double L, double U, + int k, int cvar) = 0; +}; + + +/// Interface: int suffix writer +using BasicNLSuffixIntWriter = BasicSparseIntVecWriter; + +/// Interface: double-valued suffix writer +using BasicNLSuffixDblWriter = BasicSparseDblVecWriter; + +/** Interface: suffixes. */ +class BasicNLSuffixWriterFactory { +public: + /// Virtual destruct + virtual ~BasicNLSuffixWriterFactory() { } + /// Start writing an int-valued suffix. + virtual BasicNLSuffixIntWriter& StartIntSuffix( + const char* name, int kind, int nnz) = 0; + /// Start writing a dbl-valued suffix. + virtual BasicNLSuffixDblWriter& StartDblSuffix( + const char* name, int kind, int nnz) = 0; +}; + + +/** Interface: PL-SOS constraints. */ +class BasicPLSOSWriter { +public: + /// Virtual destruct. + virtual ~BasicPLSOSWriter() { } + /// .sos for variables + virtual BasicNLSuffixIntWriter& StartSOSVars(int nnz) = 0; + /// .sos for constraints + virtual BasicNLSuffixIntWriter& StartSOSCons(int nnz) = 0; + /// .sosref for variables + virtual BasicNLSuffixDblWriter& StartSOSREFVars(int nnz) = 0; +}; + + +/** Interface: write num_vars+num_rand_vars-1 + * column sizes */ +class BasicNLColSizeWriter { +public: + /// Virtual destruct + virtual ~BasicNLColSizeWriter() { } + + /// Write next col's size + virtual void Write(int s) = 0; +}; + + +/// Interface: string file writer +class BasicNLNameFileWriter { +public: + /// Virtual destruct + virtual ~BasicNLNameFileWriter() { } + + /// operator bool + virtual operator bool() const = 0; + + /// Write 1 string + virtual void Write(const char* name) = 0; + + /// Write 2 strings (name, comment) + virtual void Write(const char* name, const char* cmt) = 0; + + /// Write 1 string, 1 double + virtual void Write(const char* name, double num) = 0; +}; + +} // namespace mp + +#endif // NLFEEDERABSTRACTDEFS_H diff --git a/nl-writer2/include/mp/unused/nl-feeder-abstract.h b/nl-writer2/include/mp/unused/nl-feeder-abstract.h new file mode 100644 index 000000000..2f57d24a4 --- /dev/null +++ b/nl-writer2/include/mp/unused/nl-feeder-abstract.h @@ -0,0 +1,575 @@ +/* + mp::NLFeederAbstract. + Abstract-type interface for mp::NLFeeder. + This interface is more flexible to use in a dynamically dispatched + implementation. + + NL is a format for representing optimization problems such as linear, + quadratic, nonlinear, complementarity and constraint programming problems + in discrete or continuous variables. It is described in the technical report + "Writing .nl Files" (http://www.cs.sandia.gov/~dmgay/nlwrite.pdf). + + Usage: recommended via mp::NLSOL class. + See the MP2NL driver for example implementation. + + + Copyright (C) 2024 AMPL Optimization, Inc. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, + provided that the above copyright notice appear in all copies and that + both that the copyright notice and this permission notice and warranty + disclaimer appear in supporting documentation. + + The author and AMPL Optimization Inc. disclaim all warranties with + regard to this software, including all implied warranties of + merchantability and fitness. In no event shall the author be liable + for any special, indirect or consequential damages or any damages + whatsoever resulting from loss of use, data or profits, whether in an + action of contract, negligence or other tortious action, arising out + of or in connection with the use or performance of this software. + + Author: Gleb Belov + */ + +#ifndef NLFEEDERABSTRACT_H +#define NLFEEDERABSTRACT_H + +#include "mp/nl-feeder.h" +#include "mp/nl-feeder-abstract-defs.h" + + +namespace mp { + +/** + \rst + NLFeederAbstract: writes model details on request + via provided callback objects. + Abstract-type interface for mp::NLFeeder. + This interface is more flexible to use in a dynamically dispatched + implementation. + + Implement methods listed in the 1st part, if needed. + + Usage: recommended via mp::NLSOL class. + See the MP2NL driver for example implementation. + + + For the NL format, variables and constraints must have certain order. + + **Variable ordering:** + first continuous, then integer. + Some solvers might require more elaborate ordering, see NLHeader. + + **Constraint ordering:** + first algebraic (including complementarity), then logical. + Some solvers might require nonlinear constraints first. + + @param: *Impl* is the final CRTP type + derived from `~mp::NLFeederAbstract`. + + @param: *ExprType* is a type storing expressions from + methods such as `~FeedExpr`. If not used, + it can be any default-constructible type. + \endrst + */ +template +class NLFeederAbstract + : public NLFeeder { +public: + /** Base class. */ + using BaseClass = NLFeeder; + + /** The expression type. */ + using typename BaseClass::Expr; + + /////////////////////////////////////////////////////////////////////// + /// Methods which can/should be implemented in the derived class. + /////////////////////////////////////////////////////////////////////// + + ///////////////////// 1. NL HEADER AND OPTIONS ///////////////// + /** Provide NLHeader. + * + * This method is called first. + * + * NLHeader summarizes the model and provides some + * technical parameters, + * such as text/binary NL format. */ + NLHeader Header() { assert(0); return {}; } + + /// NL comments? + bool WantNLComments() const { return false; } + + /// The maximum number of significant digits written. + /// The default value requests full precision, which + /// might be the shortest representation that, when + /// converted to binary and properly rounded, will + /// give exactly the binary value stored in the computer. + int OutputPrecision() const { return 0; } + + /// Write bounds first? + /// The default is yes in AMPL, controlled by + /// (the value of option nl_permute) & 32 + /// (the bit is 0 for yes). + /// Changing this option is deprecated, see + /// https://netlib.org/ampl/changes. + bool WantBoundsFirst() const { return true; } + + /// Want Jacobian column sizes? + /// Required by some nonlinear solvers. + /// Options: 0 - none, 1 - cumulative, + /// 2 - non-cumulative. + /// This option controls how ColSizeWriter + /// writes the provided sizes (which should be + /// non-cumulative). + int WantColumnSizes() const { return 1; } + + + ///////////////////// 2. OBJECTIVES ///////////////////// + /** Description for objective function \a i + * (\a i in 0..num_objs-1). + * With WantNLComments()==true, this is + * written to text-format NL as a comment. */ + const char* ObjDescription(int i) { return ""; } + + /** Provide type of objective \a i. + * - 0 - minimization; + * - 1 - maximization. */ + int ObjType(int i) { return {}; } + + /** Feed gradient for objective \a i. + * Should include entries for all potentially + * nonzero elements (sparsity pattern). + * + * Implementation skeleton: + * if (obj_grad[i].size()) { + * auto& svw = svwf.MakeVectorWriter(obj_grad[i].size()); + * for (size_t j=0; j& ew) + { ew.NPut(0.0); } + + + ///////////////////// 3. DEFINED VARIABLES ///////////////////// + /** Defined variables. + * + * Classical NL writes first the defined variables + * which are used in several places (constraints and/or + * objectives). Defined variables used in a single place + * (1 constraint, or 1 objective), are written + * just before the expression tree of their usage. + * + * For most solvers, this requirement can be ignored + * and this method can return all defined variables + * in the first group (for \a i=0). + * + * The method is guaranteed to be called in the following order: + * 1. For \a i=0; + * 2. For \a i>0, increasing, before constraint \a (i-1)'s expression; + * 3. For \a i<0, decreasing, before objective \a (-i-1)'s expression. + * + * @param i: + * - For \a i=0, feed a sequence of defined variables + * used in several constraints and/or objectives. + * - For \a i>0, feed the defined variables used solely + * in constraint \a i-1. + * - For \a i<0, feed the defined variables used solely + * in objective \a -i-1. + * + * Implementation skeleton: + * // dvar_index in num_vars..num_vars+num_defvars-1. + * for (int dvar_index: dvar_indexes[i]) { + * auto dv = dvw.StartDefVar(dvar_index, lin_nnz, name_or_comment); + * /////////// Write the linear part: + * auto& linw = dv.GetLinExprWriter(); + * for (int i=0; i& ) { } + + + ///////////////////// 4. VARIABLE BOUNDS ///////////////////// + /** Bounds for variables (except defined variables). + * Use +-inf for missing lower and/or upper bounds. + * Note that variable type is given by variable ordering, + * see NLHeader. + * + * Implementation skeleton: + * for (int i = 0; i < hdr.num_vars; i++) + * vbw.WriteLbUb(lb[i], ub[i]); + */ + void FeedVarBounds(BasicNLVarBndWriter& ) { } + + + ///////////////// 5. CONSTRAINT BOUNDS & COMPLEMENTARITY /////// + /// \rst + /// Algebraic constraint bounds (for a single constraint): + /// either range (lb, ub), + /// or complementarity info (k, cvar), when k>0. + /// + /// For a complementarity constraint to hold, if cvar is at + /// its lower bound, then body >= 0; if cvar is at its upper + /// bound, then body <= 0; + /// and if cvar is strictly between its bounds, then body = 0. + /// The integer k in a complementarity constraint line indicates + /// which bounds on cvar are finite: 1 and 3 imply a finite + /// lower bound; 2 and 3 imply a finite upper bound; 0 (which + /// should not occur) would imply no finite bounds, i.e., + /// body = 0 must always hold. + /// + /// Example: + /// + /// .. code-block:: ampl + /// + /// ampl: var x; var y; var z; + /// ampl: s.t. Compl1: x+y >= 3 complements x-z <= 15; + /// ampl: s.t. Compl2: -2 <= 2*y+3*z <= 13 complements 6*z-2*x; + /// ampl: expand; + /// subject to Compl1: + /// 3 <= x + y + /// complements + /// x - z <= 15; + /// + /// subject to Compl2: + /// -2 <= 2*y + 3*z <= 13 + /// complements + /// -2*x + 6*z; + /// + /// ampl: solexpand; + /// Nonsquare complementarity system: + /// 4 complementarities including 2 equations + /// 5 variables + /// subject to Compl1.L: + /// x + y + Compl1$cvar = 0; + /// + /// subject to Compl1.R: + /// -15 + x - z <= 0 + /// complements + /// Compl1$cvar <= -3; + /// + /// subject to Compl2.L: + /// 2*y + 3*z - Compl2$cvar = 0; + /// + /// subject to Compl2.R: + /// -2*x + 6*z + /// complements + /// -2 <= Compl2$cvar <= 13; + /// + /// \endrst + + /** Bounds/complementarity for all algebraic constraints + * (\a num_algebraic_cons). + * + * Implementation skeleton: + * for (int j=0; j negInfinity) + * bnd.k = 1; + * if (vub[j] < Infinity) + * bnd.k |= 2; + * assert(bnd.k); + * bnd.cvar = j; + * } else { + * bnd.L = clb[j]; + * bnd.U = cub[j]; + * } + * cbw.WriteAlgConRange(bnd); + * } + */ + void FeedConBounds(BasicNLConBndWriter& ) { } + + + ///////////////////// 6. CONSTRAINTS ///////////////////// + /** Description of constraint \a i + * (\a i in 0..num_algebraic_cons+num_logical_cons-1). + * With WantNLComments()==true, this is + * written to text-format NL as a comment. */ + const char* ConDescription(int ) { return ""; } + + /** Feed the linear part of algebraic constraint \a i. + * For smooth solvers, should contain entries for all + * potential nonzeros (Jacobian sparsity pattern). + * + * Implementation skeleton: + * if (con_grad[i].size()) { + * auto& sv = svw.MakeVectorWriter(con_grad[i].size()); + * for (size_t j=0; j& ew) + { ew.NPut(0.0); } + + + ///////////////////// 7. EXPRESSIONS ///////////////////// + /** Feed native expression. + * This method is recursively called from NLWriter, + * when Feeder uses ExprWriter::EPut(). + * Feeder should not call this method itself. + */ + void FeedExpr(Expr e, BasicNLExprWriter& ) { } + + + ///////////////////// 8. PL-SOS CONSTRAINTS //////////// + /** + * The below feature is for AMPL's internal + * linearization of piecewise-linear functions. + * For user-definable SOS constraints, use suffixes + * .sosno/.ref. + * + * The below is a feeder interface + * for .sos/.sosref suffixes. + * The feeder can provide 3 sparse vectors: + * - .sos for variables: + * Each nonzero value defines SOS group number. + * Negative means SOS Type 2, positive - SOS Type 1. + * - .sos for constraints: + * Each nonzero value denotes a constraint used in a + * linearization of an SOS. The constraint can be deleted + * by the solver driver if using solver's SOS. + * - .sosref for variables: + * SOS weights. Variables participating in an SOS having + * zero weights are involved in linearization and can be + * deleted if the solver accepts SOS natively. + * + * Implementation: + * auto& sosv = plsos.StartSOSVars(nvsos); + * for (int i=0; insosref); + * .... + */ + void FeedPLSOS(BasicPLSOSWriter& ) { } + + + /** Function definition. */ + using typename BaseClass::FuncDef; + + /** Provide definition + * of function \a i, i=0..num_funcs-1. */ + FuncDef Function(int i) { return {}; } + + + ///////////////////// 10. RANDOM VARIABLES ///////////////////// + /// Random variables. + /// Undocumented feature. SNL2006. + /// @note Not adapting this one. + using BaseClass::FeedRandomVariables; + + + ///////////////////// 11. COLUMN SIZES ///////////////////// + + /** Jacobian column sizes (including potential nonzeros). + * Should feed LP/Jacobian column sizes + * for all but the last variable. + * + * This is called before writing Jacobian rows. + * + * Implementation skeleton: + * if (WantColumnSizes()) + * for (int i=0; i < num_vars+num_rand_vars-1; ++i) + * csw.Write(col_size[i]); + */ + void FeedColumnSizes(BasicNLColSizeWriter& ) { } + + + ///////////////////// 12. INITIAL GUESSES ///////////////////// + /** Initial primal guesses. + * + * Implementation: write all meaningfuls entries (incl. zeros.) + * if (ini_guess.size()) { + * auto ig = igw.MakeVectorWriter(ini_guess.size()); + * for (size_t i=0; i + void FeedObjGradient(int i, ObjGradWriterFactory& ); + + /// Dispatcher method + template + void FeedObjExpression(int , ObjExprWriter& ew); + + + /// Dispatcher method + template + void FeedDefinedVariables(int i, DefVarWriterFactory& ); + + + /// Dispatcher method + template + void FeedVarBounds(VarBoundsWriter& ); + + + /// Dispatcher method + template + void FeedConBounds(ConBoundsWriter& ); + + + /// Dispatcher method + template + void FeedLinearConExpr(int i, ConLinearExprWriterFactory& ); + + /// Dispatcher method + template + void FeedConExpression(int , ConExprWriter& ew); + + + /// Dispatcher method + template + void FeedExpr(Expr e, ExprWriter& ); + + + /// Dispatcher method + template + void FeedPLSOS(PLSOSWriter& ); + + + /// Dispatcher method + template + void FeedColumnSizes(ColSizeWriter& ); + + + /// Dispatcher method + template + void FeedInitialGuesses(IGWriter& ); + + /// Dispatcher method + template + void FeedInitialDualGuesses(IDGWriter& ); + + + /// Dispatcher method + template + void FeedSuffixes(SuffixWriterFactory& ); + + + /// Dispatcher method + template + void FeedRowAndObjNames(RowObjNameWriter& wrt); + + /// Dispatcher method + template + void FeedDelRowNames(DelRowNameWriter& ); + + /// Dispatcher method + template + void FeedColNames(ColNameWriter& ); + + /// Dispatcher method + template + void FeedUnusedVarNames(UnusedVarNameWriter& ); + + /// Dispatcher method + template + void FeedFixedVarNames(FixedVarNameWriter& ); + + /// Dispatcher method + template + void FeedObjAdj(ObjOffsetWriter& ); + +}; + +} // namespace mp + +#endif // NLFEEDERABSTRACT_H diff --git a/nl-writer2/include/mp/unused/nl-feeder-abstract.hpp b/nl-writer2/include/mp/unused/nl-feeder-abstract.hpp new file mode 100644 index 000000000..14700ca8a --- /dev/null +++ b/nl-writer2/include/mp/unused/nl-feeder-abstract.hpp @@ -0,0 +1,158 @@ +#ifndef NLFEEDERABSTRACT_HPP +#define NLFEEDERABSTRACT_HPP + +#include "mp/nl-feeder-abstract.h" + + +namespace mp { + +/// Wrap any reference +template +class WrapReference { +public: + /// Construct + WrapReference(Type& ref) : ref_(ref) { } + /// Get const ref + const Type& Get() const { return ref_; } + /// Get ref + Type& Get() { return ref_; } +private: + Type& ref_; +}; + + +/// Wrap given vector writer +template +class SparseVectorWriterWrp + : public BasicSparseVectorWriter, + public WrapReference { + /// Construct + SparseVectorWriterWrp(VW& vw) : WrapReference(vw) { } + /// Number of elements left to write + int NLeft() const override { return this->Get().NLeft(); } + /// Write entry + void Write(int index, typename VW::value_type value) override + { this->Get().Write(index, value); } +}; + + +/// Wrap vector writer factory +template +class SparseVectorWriterFactoryWrp + : public BasicSparseVectorWriterFactory, + public WrapReference { +public: + using T = typename VWF::value_type; + /// Construct + SparseVectorWriterFactoryWrp(VWF& vwf) : WrapReference(vwf) { } + /// Make the SparseVectorWriter. + /// @note std::size_t without include: + /// https://stackoverflow.com/questions/36594569/which-header-should-i-include-for-size-t. + BasicSparseVectorWriter& + MakeVectorWriter(decltype(sizeof(int)) nnz) override { + wrt_ = this->Get().MakeVectorWriter(nnz); + vww_ = {wrt_}; + return vww_; + } +private: + typename VWF::writer_type wrt_; + SparseVectorWriterWrp vww_; +}; + +template +template +void NLFeederAbstract:: + FeedObjGradient(int i, ObjGradWriterFactory& ) { + +} + + +/// STOPPED HERE + + +/// Dispatcher method +template +void FeedObjExpression(int , ObjExprWriter& ew); + + +/// Dispatcher method +template +void FeedDefinedVariables(int i, DefVarWriterFactory& ); + + +/// Dispatcher method +template +void FeedVarBounds(VarBoundsWriter& ); + + +/// Dispatcher method +template +void FeedConBounds(ConBoundsWriter& ); + + +/// Dispatcher method +template +void FeedLinearConExpr(int i, ConLinearExprWriterFactory& ); + +/// Dispatcher method +template +void FeedConExpression(int , ConExprWriter& ew); + + +/// Dispatcher method +template +void FeedExpr(Expr e, ExprWriter& ); + + +/// Dispatcher method +template +void FeedPLSOS(PLSOSWriter& ); + + +/// Dispatcher method +template +void FeedColumnSizes(ColSizeWriter& ); + + +/// Dispatcher method +template +void FeedInitialGuesses(IGWriter& ); + +/// Dispatcher method +template +void FeedInitialDualGuesses(IDGWriter& ); + + +/// Dispatcher method +template +void FeedSuffixes(SuffixWriterFactory& ); + + +/// Dispatcher method +template +void FeedRowAndObjNames(RowObjNameWriter& wrt); + +/// Dispatcher method +template +void FeedDelRowNames(DelRowNameWriter& ); + +/// Dispatcher method +template +void FeedColNames(ColNameWriter& ); + +/// Dispatcher method +template +void FeedUnusedVarNames(UnusedVarNameWriter& ); + +/// Dispatcher method +template +void FeedFixedVarNames(FixedVarNameWriter& ); + +/// Dispatcher method +template +void FeedObjAdj(ObjOffsetWriter& ); + + +} // namespace mp + +#endif // NLFEEDERABSTRACT_HPP diff --git a/solvers/mp2nl/mp2nlcommon.h b/solvers/mp2nl/mp2nlcommon.h index 8704c4c7e..fb4eb6042 100644 --- a/solvers/mp2nl/mp2nlcommon.h +++ b/solvers/mp2nl/mp2nlcommon.h @@ -19,10 +19,12 @@ struct MP2NLCommonInfo { }; -/// Common API for Scip classes +/// Common API for MP2NL classes class MP2NLCommon : public Backend2ModelAPIConnector { public: + static constexpr double Infinity() { return INFINITY; } + static constexpr double MinusInfinity() { return -INFINITY; } protected: void OpenSolver(); diff --git a/solvers/mp2nl/mp2nlmodelapi.cc b/solvers/mp2nl/mp2nlmodelapi.cc index 403976539..568b83b5e 100644 --- a/solvers/mp2nl/mp2nlmodelapi.cc +++ b/solvers/mp2nl/mp2nlmodelapi.cc @@ -1,23 +1,44 @@ #include "mp2nlmodelapi.h" +#include "mp/nl-solver.hpp" +#include "mp/nl-opcodes.h" namespace mp { void MP2NLModelAPI::InitProblemModificationPhase(const FlatModelInfo* flat_model_info) { + alg_con_info_.reserve( + flat_model_info->GetNumberOfConstraintsOfGroup(CG_Algebraic)); + log_con_info_.reserve( + flat_model_info->GetNumberOfConstraintsOfGroup(CG_Logical)); + sos_info_.reserve( + flat_model_info->GetNumberOfConstraintsOfGroup(CG_SOS)); } -void MP2NLModelAPI::AddVariables(const VarArrayDef& v) { +void MP2NLModelAPI::AddVariables(const VarArrayDef& vad) { + var_lbs_ = {vad.plb(), (size_t)vad.size()}; + var_ubs_ = {vad.pub(), (size_t)vad.size()}; + var_types_ = {vad.ptype(), (size_t)vad.size()}; + var_names_ = {vad.pnames(), (size_t)vad.size()}; + mark_data_.col_sizes_orig_.resize(var_lbs_.size()); } + void MP2NLModelAPI::SetLinearObjective( int iobj, const LinearObjective& lo ) { + assert(iobj == (int)obj_info_.size()); + obj_info_.push_back(MakeItemInfo(lo, StaticItemTypeID::ID_LinearObjective)); } - void MP2NLModelAPI::SetQuadraticObjective(int iobj, const QuadraticObjective& qo) { - /// @todo + assert(iobj == (int)obj_info_.size()); + /// @todo ? throw std::runtime_error("Quadratic objective not supported"); } +void MP2NLModelAPI::SetNLObjective( int iobj, const NLObjective& nlo ) { + assert(iobj == (int)obj_info_.size()); + obj_info_.push_back(MakeItemInfo(nlo, StaticItemTypeID::ID_NLObjective)); +} + MP2NL_Expr MP2NLModelAPI::GetVarExpression(int i) { return {i+1}; // ? @@ -27,32 +48,17 @@ MP2NL_Expr MP2NLModelAPI::GetZeroExpression() { return {}; } -void MP2NLModelAPI::AddConstraint(const LinConRange& lc) { -} - -void MP2NLModelAPI::AddConstraint(const LinConLE& lc) { -} - -void MP2NLModelAPI::AddConstraint(const LinConEQ& lc) { -} - -void MP2NLModelAPI::AddConstraint(const LinConGE& lc) { -} - - -MP2NL_Expr MP2NLModelAPI::AddExpression(const AbsExpression &abse) { - return {}; -} - +void MP2NLModelAPI::AddConstraint(const LinConRange& lc) +{ alg_con_info_.push_back(MakeItemInfo(lc, StaticItemTypeID::ID_LinConRange)); } -void MP2NLModelAPI::AddConstraint(const IndicatorConstraintLinLE &ic) { -} +void MP2NLModelAPI::AddConstraint(const LinConLE& lc) +{ alg_con_info_.push_back(MakeItemInfo(lc, StaticItemTypeID::ID_LinConLE)); } -void MP2NLModelAPI::AddConstraint(const IndicatorConstraintLinEQ &ic) { -} +void MP2NLModelAPI::AddConstraint(const LinConEQ& lc) +{ alg_con_info_.push_back(MakeItemInfo(lc, StaticItemTypeID::ID_LinConEQ)); } -void MP2NLModelAPI::AddConstraint(const IndicatorConstraintLinGE &ic) { -} +void MP2NLModelAPI::AddConstraint(const LinConGE& lc) +{ alg_con_info_.push_back(MakeItemInfo(lc, StaticItemTypeID::ID_LinConGE)); } /// To access information from an NLConstraint, @@ -61,25 +67,45 @@ void MP2NLModelAPI::AddConstraint(const IndicatorConstraintLinGE &ic) { /// GetExpression(nlc), GetLower(nlc), GetUpper(nlc). /// /// Implementation follows partly reader_nl.cc from SCIP. -void MP2NLModelAPI::AddConstraint( const NLConstraint& nlc ) { -} +void MP2NLModelAPI::AddConstraint( const NLConstraint& nlc ) +{ alg_con_info_.push_back(MakeItemInfo(nlc, StaticItemTypeID::ID_NLConstraint)); } -void MP2NLModelAPI::AddConstraint( const NLAssignEQ& nlae ) { -} -void MP2NLModelAPI::AddConstraint( const NLAssignLE& nlae ) { -} -void MP2NLModelAPI::AddConstraint( const NLAssignGE& nlae ) { -} +void MP2NLModelAPI::AddConstraint( const NLAssignEQ& nlae ) +{ alg_con_info_.push_back(MakeItemInfo(nlae, StaticItemTypeID::ID_NLAssignEQ)); } +void MP2NLModelAPI::AddConstraint( const NLAssignLE& nlae ) +{ alg_con_info_.push_back(MakeItemInfo(nlae, StaticItemTypeID::ID_NLAssignLE)); } +void MP2NLModelAPI::AddConstraint( const NLAssignGE& nlae ) +{ alg_con_info_.push_back(MakeItemInfo(nlae, StaticItemTypeID::ID_NLAssignGE)); } -void MP2NLModelAPI::AddConstraint( const NLLogical& nll ) { -} +void MP2NLModelAPI::AddConstraint(const NLComplementarity& cc) +{ alg_con_info_.push_back(MakeItemInfo(cc, StaticItemTypeID::ID_NLComplementarity)); } + + +void MP2NLModelAPI::AddConstraint( const NLLogical& nll ) +{ log_con_info_.push_back(MakeItemInfo(nll, StaticItemTypeID::ID_NLLogical)); } + +void MP2NLModelAPI::AddConstraint( const NLEquivalence& nll ) +{ log_con_info_.push_back(MakeItemInfo(nll, StaticItemTypeID::ID_NLEquivalence)); } +void MP2NLModelAPI::AddConstraint( const NLImpl& nll ) +{ log_con_info_.push_back(MakeItemInfo(nll, StaticItemTypeID::ID_NLImpl)); } +void MP2NLModelAPI::AddConstraint( const NLRimpl& nll ) +{ log_con_info_.push_back(MakeItemInfo(nll, StaticItemTypeID::ID_NLRimpl)); } + + +void MP2NLModelAPI::AddConstraint(const IndicatorConstraintLinLE &ic) +{ log_con_info_.push_back(MakeItemInfo(ic, StaticItemTypeID::ID_IndicatorConstraintLinLE)); } +void MP2NLModelAPI::AddConstraint(const IndicatorConstraintLinEQ &ic) +{ log_con_info_.push_back(MakeItemInfo(ic, StaticItemTypeID::ID_IndicatorConstraintLinEQ)); } +void MP2NLModelAPI::AddConstraint(const IndicatorConstraintLinGE &ic) +{ log_con_info_.push_back(MakeItemInfo(ic, StaticItemTypeID::ID_IndicatorConstraintLinGE)); } + + + +void MP2NLModelAPI::AddConstraint(const SOS1Constraint& sos) +{ sos_info_.push_back(MakeItemInfo(sos, StaticItemTypeID::ID_SOS1Constraint)); } +void MP2NLModelAPI::AddConstraint(const SOS2Constraint& sos) +{ sos_info_.push_back(MakeItemInfo(sos, StaticItemTypeID::ID_SOS2Constraint)); } -void MP2NLModelAPI::AddConstraint( const NLEquivalence& nll ) { -} -void MP2NLModelAPI::AddConstraint( const NLImpl& nll ) { -} -void MP2NLModelAPI::AddConstraint( const NLRimpl& nll ) { -} MP2NL_Expr MP2NLModelAPI::AddExpression(const LinExpression &le) { return {}; @@ -90,10 +116,8 @@ MP2NL_Expr MP2NLModelAPI::AddExpression(const QuadExpression &qe) { } -void MP2NLModelAPI::AddConstraint(const SOS1Constraint& sos) { -} - -void MP2NLModelAPI::AddConstraint(const SOS2Constraint& sos) { +MP2NL_Expr MP2NLModelAPI::AddExpression(const AbsExpression &abse) { + return {}; } MP2NL_Expr MP2NLModelAPI::AddExpression(const AndExpression &ee) { @@ -128,4 +152,197 @@ void MP2NLModelAPI::FinishProblemModificationPhase() { } + + +NLHeader MP2NLModelAPI::Header() { + PrepareModel(); + return DoMakeHeader(); +} + +void MP2NLModelAPI::PrepareModel() { + MarkVars(); + SortVars(); + MarkItems(); +} + +void MP2NLModelAPI::MarkVars() { + mark_data_.var_prior_.resize(var_lbs_.size()); + for (auto i=var_lbs_.size(); i--; ) { + mark_data_.var_prior_[i].second = i; + if (var::Type::INTEGER == var_types_[i]) { + if (!var_lbs_[i] && 1.0==var_ubs_[i]) { + mark_data_.var_prior_[i].first = 1; + ++mark_data_.n_var_lin_bin_; + } else { + mark_data_.var_prior_[i].first = 2; + ++mark_data_.n_var_lin_int_; + } + } + } +} + +void MP2NLModelAPI::SortVars() { + std::sort(mark_data_.var_prior_.begin(), mark_data_.var_prior_.end()); + mark_data_.var_order_12_.resize(var_lbs_.size()); + mark_data_.var_order_21_.resize(var_lbs_.size()); + for (auto i=var_lbs_.size(); i--; ) { + mark_data_.var_order_12_[i] = mark_data_.var_prior_[i].second; + mark_data_.var_order_21_[mark_data_.var_prior_[i].second] = i; + } +} + +void MP2NLModelAPI::MarkItems() { + ItemMarkingData prm; + for (auto& info: alg_con_info_) + info.GetDispatcher().MarkItem(info.GetPItem(), prm); +} + +void MP2NLModelAPI::Add2ColSizes(const std::vector& vars) { + for (auto v: vars) + ++mark_data_.col_sizes_orig_[v]; +} + + +NLHeader MP2NLModelAPI::DoMakeHeader() { + mp::NLHeader hdr; + + hdr.num_vars = var_lbs_.size(); + hdr.num_algebraic_cons = alg_con_info_.size(); + hdr.num_objs = obj_info_.size(); + hdr.num_ranges = mark_data_.n_ranges_; + hdr.num_eqns = mark_data_.n_eqns_; + hdr.num_logical_cons = 0; + + /** Total number of nonlinear constraints. */ + hdr.num_nl_cons = 0; + hdr.num_nl_objs = 0; + hdr.num_compl_conds = 0; + hdr.num_nl_compl_conds = 0; + hdr.num_compl_dbl_ineqs = 0; + hdr.num_compl_vars_with_nz_lb = 0; + + /** Number of nonlinear network constraints. */ + hdr.num_nl_net_cons = 0; + hdr.num_linear_net_cons = 0; + + /** + Number of nonlinear variables in constraints including nonlinear + variables in both constraints and objectives. + */ + hdr.num_nl_vars_in_cons = 0; + + /** + Number of nonlinear variables in objectives including nonlinear + variables in both constraints and objectives. + */ + hdr.num_nl_vars_in_objs = 0; + + /** Number of nonlinear variables in both constraints and objectives. */ + hdr.num_nl_vars_in_both = 0; + + // Miscellaneous + // ------------- + + /** Number of linear network variables (arcs). */ + hdr.num_linear_net_vars = 0; + + /** Number of functions. */ + hdr.num_funcs = 0; + + // Information about discrete variables + // ------------------------------------ + + /** Number of linear binary variables. */ + hdr.num_linear_binary_vars = mark_data_.n_var_lin_bin_; + + /** Number of linear non-binary integer variables. */ + hdr.num_linear_integer_vars = mark_data_.n_var_lin_int_; + + /** + Number of integer nonlinear variables in both constraints and objectives. + */ + hdr.num_nl_integer_vars_in_both = 0; + + /** Number of integer nonlinear variables just in constraints. */ + hdr.num_nl_integer_vars_in_cons = 0; + + /** Number of integer nonlinear variables just in objectives. */ + hdr.num_nl_integer_vars_in_objs = 0; + + // Information about nonzeros + // -------------------------- + + /** Number of nonzeros in constraints' Jacobian. */ + hdr.num_con_nonzeros = 0; + + /** Number of nonzeros in all objective gradients. */ + hdr.num_obj_nonzeros = 0; + + // Information about names + // ----------------------- + + /** Length of longest con/obj name if names are available. */ + hdr.max_con_name_len = 0; // no need to set + + /** Length of longest variable name if names are available. */ + hdr.max_var_name_len = 0; // no need to set + + // Information about common expressions + // ------------------------------------ + + /** + Number of common expressions that appear both in constraints + and objectives. + */ + hdr.num_common_exprs_in_both = 0; + + /** + Number of common expressions that appear in multiple constraints + and don't appear in objectives. + */ + hdr.num_common_exprs_in_cons = 0; + + /** + Number of common expressions that appear in multiple objectives + and don't appear in constraints. + */ + hdr.num_common_exprs_in_objs = 0; + + /** + Number of common expressions that only appear in a single constraint + and don't appear in objectives. + */ + hdr.num_common_exprs_in_single_cons = 0; + + /** + Number of common expressions that only appear in a single objective + and don't appear in constraints. + */ + hdr.num_common_exprs_in_single_objs = 0; + + hdr.prob_name = "mp2nl_model"; + + return hdr; +} + + +void CreateNLS() { + Env env; + MP2NLModelAPI nlf(env); + SOLHandler esolh; + mp::NLUtils utils; + + mp::NLSolver nlsol(&utils); + + std::string solver = "minos"; + std::string sopts = ""; + + if (!nlsol.Solve(nlf, esolh, solver, sopts)) { + printf("%s\n", nlsol.GetErrorMessage()); + } else { + } +} + + + } // namespace mp diff --git a/solvers/mp2nl/mp2nlmodelapi.h b/solvers/mp2nl/mp2nlmodelapi.h index 96529720d..830d46c3b 100644 --- a/solvers/mp2nl/mp2nlmodelapi.h +++ b/solvers/mp2nl/mp2nlmodelapi.h @@ -5,17 +5,21 @@ #include "mp2nlcommon.h" #include "mp/flat/nl_expr/model_api_base.h" +#include "mp/nl-feeder.h" + + namespace mp { -/// Expression ID for MP2NLModelAPI +/// Expression ID for MP2NLModelAPI. +/// Can be +/// 1. "Empty expression" (0.0), +/// 2. variable expression (represent a variable), +/// 3. normal expression node. class MP2NL_Expr { public: /// Construct MP2NL_Expr(int e=0) : id_(e) { } - /// Assign - MP2NL_Expr& operator=(const MP2NL_Expr& ) = default; - /// Get the expr ID int GetID() const { return id_; } @@ -27,13 +31,17 @@ class MP2NL_Expr { /// MP2NLModelAPI. /// MP2NL is to be used as a meta-driver, /// performing reformulations for the final NL solver. +/// +/// MP2NLModelAPI translates the reformulated model for the NL Writer. class MP2NLModelAPI : public MP2NLCommon, public EnvKeeper, - public BasicExprModelAPI + public BasicExprModelAPI, + public NLFeeder { public: using BaseModelAPI = BasicExprModelAPI; using Expr = MP2NL_Expr; + using BaseNLFeeder = NLFeeder; /// Construct MP2NLModelAPI(Env& e) : EnvKeeper(e) { } @@ -52,12 +60,16 @@ class MP2NLModelAPI void FinishProblemModificationPhase(); /// Implement the following functions using the solver's API - void AddVariables(const VarArrayDef& ); + void AddVariables(const VarArrayDef& vad); void SetLinearObjective( int iobj, const LinearObjective& lo ); /// Whether accepting quadratic objectives: /// 0 - no, 1 - convex, 2 - nonconvex static int AcceptsQuadObj() { return 2; } void SetQuadraticObjective(int iobj, const QuadraticObjective& qo); + /// Whether accepts NLObjective. + static int AcceptsNLObj() { return 1; } + void SetNLObjective(int iobj, const NLObjective& nlo); + //////////////////////////// GENERAL CONSTRAINTS //////////////////////////// /// Handle flat constraints: inherit basic API @@ -67,6 +79,10 @@ class MP2NLModelAPI /// Hanlde expression trees: inherit basic API USE_BASE_EXPRESSION_HANDLERS(BaseModelAPI) + + ///////////// !!! First go with linear models. ////////////////// + + /// ACCEPT_EXPRESSION_INTERFACE(): /// 'NotAccepted' or /// 'AcceptedButNotRecommended' would resort to flat constraints uniformly @@ -86,11 +102,7 @@ class MP2NLModelAPI /// NLConstraint, NLAssignLE, NLAssignEQ, NLAssignGE, /// NLComplementarity, /// NLLogical, NLEquivalence, NLImpl, NLRimpl, and NLObjective. - ACCEPT_EXPRESSION_INTERFACE(Recommended); - - /// Whether accepts NLObjective. - /// No, as of SCIP 9.1. - static int AcceptsNLObj() { return 1; } + ACCEPT_EXPRESSION_INTERFACE(AcceptedButNotRecommended); /// Once expressions are supported, need the following /// helper methods. @@ -119,18 +131,18 @@ class MP2NLModelAPI /// The linear range constraint, if fully supported with basis info etc. /// NL format supports this. - ACCEPT_CONSTRAINT(LinConRange, Recommended, CG_Linear) + ACCEPT_CONSTRAINT(LinConRange, Recommended, CG_Algebraic) void AddConstraint(const LinConRange& lc); /// LinCon(LE/EQ/GE) should have 'Recommended' for all non-expression backends /// and have an implementation, /// or a conversion rule is needed in a derived FlatConverter /// Even for expression backends, they can be implemented for efficiency. - ACCEPT_CONSTRAINT(LinConLE, Recommended, CG_Linear) + ACCEPT_CONSTRAINT(LinConLE, Recommended, CG_Algebraic) void AddConstraint(const LinConLE& lc); - ACCEPT_CONSTRAINT(LinConEQ, Recommended, CG_Linear) + ACCEPT_CONSTRAINT(LinConEQ, Recommended, CG_Algebraic) void AddConstraint(const LinConEQ& lc); - ACCEPT_CONSTRAINT(LinConGE, Recommended, CG_Linear) + ACCEPT_CONSTRAINT(LinConGE, Recommended, CG_Algebraic) void AddConstraint(const LinConGE& lc); /// Ask if the solver accepts non-convex quadratic constraints @@ -149,6 +161,11 @@ class MP2NLModelAPI ACCEPT_CONSTRAINT(QuadConEQ, NotAccepted, CG_Quadratic) ACCEPT_CONSTRAINT(QuadConGE, NotAccepted, CG_Quadratic) + + /// Cones + ACCEPT_CONSTRAINT(QuadraticConeConstraint, NotAccepted, CG_Conic) + + /// If NLConstraint is accepted, with expression trees, /// then top-level nonlinear algebraic constraints /// are submitted to the solver via NLConstraint. @@ -167,7 +184,7 @@ class MP2NLModelAPI /// use the following accessors (don't use methods of NLConstraint itself): /// - GetLinSize(nlc), GetLinCoef(nlc, i), GetLinVar(nlc, i), /// GetExpression(nlc), GetLower(nlc), GetUpper(nlc). - ACCEPT_CONSTRAINT(NLConstraint, Recommended, CG_Nonlinear) + ACCEPT_CONSTRAINT(NLConstraint, Recommended, CG_Algebraic) void AddConstraint(const NLConstraint& nlc); /// NLAssignEQ: algebraic expression expicifier. @@ -179,7 +196,7 @@ class MP2NLModelAPI /// NL algebraic constraint. /// /// @note Accessors: GetExpression(nle), GetVariable(nle). - ACCEPT_CONSTRAINT(NLAssignEQ, Recommended, CG_Nonlinear) + ACCEPT_CONSTRAINT(NLAssignEQ, Recommended, CG_Algebraic) void AddConstraint(const NLAssignEQ& nle); /// NLAssignLE: algebraic expression expicifier in positive context. /// Meaning: var <= expr. @@ -188,7 +205,7 @@ class MP2NLModelAPI /// @note Can be implemented as NLAssignEQ, /// but this may lose convexity. /// @note Accessors: GetExpression(nle), GetVariable(nle). - ACCEPT_CONSTRAINT(NLAssignLE, Recommended, CG_Nonlinear) + ACCEPT_CONSTRAINT(NLAssignLE, Recommended, CG_Algebraic) void AddConstraint(const NLAssignLE& nle); /// NLAssignGE: algebraic expression expicifier in negative context. /// Meaning: var >= expr. @@ -197,18 +214,20 @@ class MP2NLModelAPI /// @note Can be implemented as NLAssignEQ, /// but this may lose convexity. /// @note Accessors: GetExpression(nle), GetVariable(nle). - ACCEPT_CONSTRAINT(NLAssignGE, Recommended, CG_Nonlinear) + ACCEPT_CONSTRAINT(NLAssignGE, Recommended, CG_Algebraic) void AddConstraint(const NLAssignGE& nle); + /// @todo + ACCEPT_CONSTRAINT(NLComplementarity, NotAccepted, CG_Algebraic) + void AddConstraint(const NLComplementarity& cc); + /// NL logical constraint: expression = true. /// /// @note Should be 'Recommended' /// whenever logical expressions are accepted. /// @note Use GetExpression(nll) to access the expression. - /// @note Constraint group: CG_Nonlinear for SCIP, - /// because using SCIPcreateConsBasicNonlinear(). - ACCEPT_CONSTRAINT(NLLogical, Recommended, CG_Nonlinear) + ACCEPT_CONSTRAINT(NLLogical, Recommended, CG_Logical) void AddConstraint(const NLLogical& nll); /// NL equivalence: expression <==> var. @@ -217,7 +236,7 @@ class MP2NLModelAPI /// @note Should be 'Recommended' /// whenever logical expressions are accepted. /// @note Accessors: GetExpression(nle), GetVariable(nle). - ACCEPT_CONSTRAINT(NLEquivalence, Recommended, CG_Nonlinear) + ACCEPT_CONSTRAINT(NLEquivalence, Recommended, CG_Logical) void AddConstraint(const NLEquivalence& nle); /// NL implication: var==1 ==> expression. /// This is an expression explicifier in positive context. @@ -225,7 +244,7 @@ class MP2NLModelAPI /// @note Should be 'Recommended' /// whenever logical expressions are accepted. /// @note Accessors: GetExpression(nle), GetVariable(nle). - ACCEPT_CONSTRAINT(NLImpl, Recommended, CG_Nonlinear) + ACCEPT_CONSTRAINT(NLImpl, Recommended, CG_Logical) void AddConstraint(const NLImpl& nle); /// NL reverse implication: expression ==> var==1. /// This is an expression explicifier in negative context. @@ -233,14 +252,45 @@ class MP2NLModelAPI /// @note Should be 'Recommended' /// whenever logical expressions are accepted. /// @note Accessors: GetExpression(nle), GetVariable(nle). - ACCEPT_CONSTRAINT(NLRimpl, Recommended, CG_Nonlinear) + ACCEPT_CONSTRAINT(NLRimpl, Recommended, CG_Logical) void AddConstraint(const NLRimpl& nle); - /// Moreover, once algebraic expressions are accepted - /// via NLConstraint, subexpressions might be submitted via - /// LinExpression and QuadExpression. + + /// Linear indicator constraints can be used as + /// auxiliary constraints for logical conditions. + /// If not handled, the compared expressions need + /// deducible finite bounds for a big-M redefinition. + /// + /// @note Need them in NL format output? Currently seems yes, + /// several reformulations produce it. + /// As long as the target solver accepts. + /// @todo But if ever there appears a solver that only accepts + /// Cond(Lin/Quad)..., we should produce them instead. + /// Mosek??? + ACCEPT_CONSTRAINT(IndicatorConstraintLinLE, NotAccepted, CG_Logical) + void AddConstraint(const IndicatorConstraintLinLE& mc); + ACCEPT_CONSTRAINT(IndicatorConstraintLinEQ, NotAccepted, CG_Logical) + void AddConstraint(const IndicatorConstraintLinEQ& mc); + ACCEPT_CONSTRAINT(IndicatorConstraintLinGE, NotAccepted, CG_Logical) + void AddConstraint(const IndicatorConstraintLinGE& mc); + + + /// SOS constraints can be used by AMPL for redefinition of + /// piecewise-linear expressions. + /// @note Set ``option pl_linearize 0;`` in AMPL if the solver + /// supports PL natively. + ACCEPT_CONSTRAINT(SOS1Constraint, NotAccepted, CG_SOS) + void AddConstraint(const SOS1Constraint& cc); + ACCEPT_CONSTRAINT(SOS2Constraint, NotAccepted, CG_SOS) + void AddConstraint(const SOS2Constraint& cc); + + /// @brief LinExpression. + /// Once algebraic expressions are accepted + /// via NLConstraint, subexpressions might be submitted via + /// LinExpression and QuadExpression. + /// /// @note Use accessors, not methods; /// - GetLinSize(le), GetLinCoef(le, i), GetLinTerm(le, i); /// GetConstTerm(le). @@ -289,37 +339,6 @@ class MP2NLModelAPI // ACCEPT_CONSTRAINT(OrConstraint, Recommended, CG_General) // void AddConstraint(const OrConstraint& dc); - /// Linear indicator constraints can be used as - /// auxiliary constraints for logical conditions. - /// If not handled, the compared expressions need - /// deducible finite bounds for a big-M redefinition. - /// - /// @note Need them in NL format output? Currently seems yes, - /// several reformulations produce it. - /// As long as the target solver accepts. - /// @todo But if ever there appears a solver that only accepts - /// Cond(Lin/Quad)..., we should produce them instead. - /// Mosek??? - ACCEPT_CONSTRAINT(IndicatorConstraintLinLE, Recommended, CG_General) - void AddConstraint(const IndicatorConstraintLinLE& mc); - ACCEPT_CONSTRAINT(IndicatorConstraintLinEQ, Recommended, CG_General) - void AddConstraint(const IndicatorConstraintLinEQ& mc); - ACCEPT_CONSTRAINT(IndicatorConstraintLinGE, Recommended, CG_General) - void AddConstraint(const IndicatorConstraintLinGE& mc); - - /// Cones - ACCEPT_CONSTRAINT(QuadraticConeConstraint, NotAccepted, CG_Conic) - - /// SOS constraints can be used by AMPL for redefinition of - /// piecewise-linear expressions. - /// @note Set ``option pl_linearize 0;`` in AMPL if the solver - /// supports PL natively. - ACCEPT_CONSTRAINT(SOS1Constraint, Recommended, CG_SOS) - void AddConstraint(const SOS1Constraint& cc); - ACCEPT_CONSTRAINT(SOS2Constraint, Recommended, CG_SOS) - void AddConstraint(const SOS2Constraint& cc); - - /// SCIP nonlinear general constraints and expressions. ACCEPT_EXPRESSION(ExpExpression, Recommended) Expr AddExpression(const ExpExpression& ); @@ -336,6 +355,802 @@ class MP2NLModelAPI // TODO Div; PowVarVar; // CondLin... - not really, reader_nl.cpp only handles bool args + + +public: + /////////////////////////////////////////////////////////////////// + ///////////////// Implement NLFeeder interface //////////////////// + /////////////////////////////////////////////////////////////////// + + ///////////////////// 1. NL HEADER AND OPTIONS ///////////////// + /** Provide NLHeader. + * + * This method is called first. + * + * NLHeader summarizes the model and provides some + * technical parameters, + * such as text/binary NL format. */ + NLHeader Header(); + + /// NL comments? + bool WantNLComments() const { return false; } + + /// The maximum number of significant digits written. + /// The default value requests full precision, which + /// might be the shortest representation that, when + /// converted to binary and properly rounded, will + /// give exactly the binary value stored in the computer. + int OutputPrecision() const { return 0; } + + /// Write bounds first? + /// The default is yes in AMPL, controlled by + /// (the value of option nl_permute) & 32 + /// (the bit is 0 for yes). + /// Changing this option is deprecated, see + /// https://netlib.org/ampl/changes. + bool WantBoundsFirst() const { return true; } + + /// Want Jacobian column sizes? + /// Required by some nonlinear solvers. + /// Options: 0 - none, 1 - cumulative, + /// 2 - non-cumulative. + /// This option controls how ColSizeWriter + /// writes the provided sizes (which should be + /// non-cumulative). + int WantColumnSizes() const { return 1; } + + + ///////////////////// 2. OBJECTIVES ///////////////////// + /** Description for objective function \a i + * (\a i in 0..num_objs-1). + * With WantNLComments()==true, this is + * written to text-format NL as a comment. */ + const char* ObjDescription(int i) { return ""; } + + /** Provide type of objective \a i. + * - 0 - minimization; + * - 1 - maximization. */ + int ObjType(int i) { + switch (obj_info_[i].GetStaticTypeID()) { + case StaticItemTypeID::ID_LinearObjective: + case StaticItemTypeID::ID_NLObjective: + return obj::MIN==((LinearObjective*)(obj_info_[i].GetPItem()))->obj_sense() + ? 0 : 1; + default: + MP_RAISE("Unknown objective type"); + } + } + + /** Feed gradient for objective \a i. + * Should include entries for all potentially + * nonzero elements (sparsity pattern). + * + * Implementation skeleton: + * if (obj_grad[i].size()) { + * auto svw = svwf.MakeVectorWriter(obj_grad[i].size()); + * for (size_t j=0; j + void FeedObjGradient(int i, ObjGradWriterFactory& svwf) { + switch (obj_info_[i].GetStaticTypeID()) { + case StaticItemTypeID::ID_LinearObjective: + case StaticItemTypeID::ID_NLObjective: { + const auto& obj = *((LinearObjective*)(obj_info_[i].GetPItem())); + if (obj.num_terms()) { + auto svw = svwf.MakeVectorWriter(obj.num_terms()); + for (int j=0; j + void FeedObjExpression(int , ObjExprWriter& ew) + { ew.NPut(0.0); } + + + ///////////////////// 3. DEFINED VARIABLES ///////////////////// + /** Defined variables. + * + * Classical NL writes first the defined variables + * which are used in several places (constraints and/or + * objectives). Defined variables used in a single place + * (1 constraint, or 1 objective), are written + * just before the expression tree of their usage. + * + * For most solvers, this requirement can be ignored + * and this method can return all defined variables + * in the first group (for \a i=0). + * + * The method is guaranteed to be called in the following order: + * 1. For \a i=0; + * 2. For \a i>0, increasing, before constraint \a (i-1)'s expression; + * 3. For \a i<0, decreasing, before objective \a (-i-1)'s expression. + * + * @param i: + * - For \a i=0, feed a sequence of defined variables + * used in several constraints and/or objectives. + * - For \a i>0, feed the defined variables used solely + * in constraint \a i-1. + * - For \a i<0, feed the defined variables used solely + * in objective \a -i-1. + * + * Implementation skeleton: + * // dvar_index in num_vars..num_vars+num_defvars-1. + * for (int dvar_index: dvar_indexes[i]) { + * auto dv = dvw.StartDefVar(dvar_index, lin_nnz, name_or_comment); + * /////////// Write the linear part: + * auto linw = dv.GetLinExprWriter(); + * for (int i=0; i + void FeedDefinedVariables(int i, DefVarWriterFactory& ) { } + + + ///////////////////// 4. VARIABLE BOUNDS ///////////////////// + /** Bounds for variables (except defined variables). + * Use +-inf for missing lower and/or upper bounds. + * Note that variable type is given by variable ordering, + * see NLHeader. + * + * Implementation skeleton: + * for (int i = 0; i < hdr.num_vars; i++) + * vbw.WriteLbUb(lb[i], ub[i]); + */ + template + void FeedVarBounds(VarBoundsWriter& vbw) { + for (size_t i = 0; i < var_lbs_.size(); i++) { + auto i_old = GetOldVarIndex(i); + vbw.WriteLbUb(var_lbs_[i_old], var_ubs_[i_old]); + } + } + + + ///////////////// 5. CONSTRAINT BOUNDS & COMPLEMENTARITY /////// + /// \rst + /// Algebraic constraint bounds (for a single constraint): + /// either range (lb, ub), + /// or complementarity info (k, cvar), when k>0. + /// + /// For a complementarity constraint to hold, if cvar is at + /// its lower bound, then body >= 0; if cvar is at its upper + /// bound, then body <= 0; + /// and if cvar is strictly between its bounds, then body = 0. + /// The integer k in a complementarity constraint line indicates + /// which bounds on cvar are finite: 1 and 3 imply a finite + /// lower bound; 2 and 3 imply a finite upper bound; 0 (which + /// should not occur) would imply no finite bounds, i.e., + /// body = 0 must always hold. + /// + /// Example: + /// + /// .. code-block:: ampl + /// + /// ampl: var x; var y; var z; + /// ampl: s.t. Compl1: x+y >= 3 complements x-z <= 15; + /// ampl: s.t. Compl2: -2 <= 2*y+3*z <= 13 complements 6*z-2*x; + /// ampl: expand; + /// subject to Compl1: + /// 3 <= x + y + /// complements + /// x - z <= 15; + /// + /// subject to Compl2: + /// -2 <= 2*y + 3*z <= 13 + /// complements + /// -2*x + 6*z; + /// + /// ampl: solexpand; + /// Nonsquare complementarity system: + /// 4 complementarities including 2 equations + /// 5 variables + /// subject to Compl1.L: + /// x + y + Compl1$cvar = 0; + /// + /// subject to Compl1.R: + /// -15 + x - z <= 0 + /// complements + /// Compl1$cvar <= -3; + /// + /// subject to Compl2.L: + /// 2*y + 3*z - Compl2$cvar = 0; + /// + /// subject to Compl2.R: + /// -2*x + 6*z + /// complements + /// -2 <= Compl2$cvar <= 13; + /// + /// \endrst + using BaseNLFeeder::AlgConRange; + + /** Bounds/complementarity for all algebraic constraints + * (\a num_algebraic_cons). + * + * Implementation skeleton: + * for (int j=0; j negInfinity) + * bnd.k = 1; + * if (vub[j] < Infinity) + * bnd.k |= 2; + * assert(bnd.k); + * bnd.cvar = j; + * } else { + * bnd.L = clb[j]; + * bnd.U = cub[j]; + * } + * cbw.WriteAlgConRange(bnd); + * } + */ + template + void FeedConBounds(ConBoundsWriter& cbw) { + for (size_t i=0; i + void FeedLinearConExpr(int i, ConLinearExprWriterFactory& svwf) { + switch (alg_con_info_[i].GetStaticTypeID()) { + case StaticItemTypeID::ID_LinConRange: { + FeedLinearConExpr( *((LinConRange*)(obj_info_[i].GetPItem())), svwf); + } break; + case StaticItemTypeID::ID_LinConLE: { + FeedLinearConExpr( *((LinConLE*)(obj_info_[i].GetPItem())), svwf); + } break; + case StaticItemTypeID::ID_LinConEQ: { + FeedLinearConExpr( *((LinConEQ*)(obj_info_[i].GetPItem())), svwf); + } break; + case StaticItemTypeID::ID_LinConGE: { + FeedLinearConExpr( *((LinConGE*)(obj_info_[i].GetPItem())), svwf); + } break; + default: + MP_RAISE("Unknown algebraic constraint type"); + } + } + + template + void FeedLinearConExpr( + const AlgebraicConstraint& algcon, + ConLinearExprWriterFactory& svwf) { + if (algcon.size()) { + auto svw = svwf.MakeVectorWriter(algcon.size()); + for (int j=0; j + void FeedConExpression(int , ConExprWriter& ew) + { ew.NPut(0.0); } + + + ///////////////////// 7. EXPRESSIONS ///////////////////// + /** Feed native expression. + * This method is recursively called from NLWriter, + * when Feeder uses ExprWriter::EPut(). + * Feeder should not call this method itself. + * + * Details of ExprWriter: see NLWriter2. + */ + template + void FeedExpr(Expr e, ExprWriter& ) { } + + + ///////////////////// 8. PL-SOS CONSTRAINTS //////////// + /** + * The below feature is for AMPL's internal + * linearization of piecewise-linear functions. + * For user-definable SOS constraints, use suffixes + * .sosno/.ref. + * + * The below is a feeder interface + * for .sos/.sosref suffixes. + * The feeder can provide 3 sparse vectors: + * - .sos for variables: + * Each nonzero value defines SOS group number. + * Negative means SOS Type 2, positive - SOS Type 1. + * - .sos for constraints: + * Each nonzero value denotes a constraint used in a + * linearization of an SOS. The constraint can be deleted + * by the solver driver if using solver's SOS. + * - .sosref for variables: + * SOS weights. Variables participating in an SOS having + * zero weights are involved in linearization and can be + * deleted if the solver accepts SOS natively. + * + * Implementation: + * auto sosv = plsos.StartSOSVars(nvsos); + * for (int i=0; insosref); + * .... + */ + template + void FeedPLSOS(PLSOSWriter& ) { } + + + ///////////////////// 9. FUNCTIONS ///////////////////// + /** Function definition. */ + using BaseNLFeeder::FuncDef; + + /** Provide definition + * of function \a i, i=0..num_funcs-1. */ + FuncDef Function(int i) { return {}; } + + + ///////////////////// 10. RANDOM VARIABLES ///////////////////// + /// Random variables. + /// Undocumented feature. SNL2006. + /// Example: + /// var z >= 0; + /// let z.stage := 1; + /// var x{0..1, 0..1} random := Uniform(0,2); + /// for {i in 0..1, j in 0..1} {let x[i,j].stage := 1;}; + /// display z.stage, x.stage; + /// c: z * sum{i in 0..1, j in 0..1} x[i,j] <= 3 + Sample(Uniform(0,2)); + /// + /// Feed random variables. + /// Indexes: num_vars+num_common_exprs + /// .. num_vars+num_common_exprs+num_rand_vars-1. + /// + /// Implementation skeleton: + /// for(j = num_vars+num_common_exprs; + /// j < num_vars+num_common_exprs+num_rand_vars; j++) { + /// auto ew = rvw.StartRandVar(j, rand_var_comment(j)); + /// ew.EPut(rand_var_root_expr(j)); + /// } + template + void FeedRandomVariables(RandVarWriterFactory& ) { } + + + ///////////////////// 11. COLUMN SIZES ///////////////////// + + /** Jacobian column sizes (including potential nonzeros). + * Should feed LP/Jacobian column sizes + * for all but the last variable. + * + * This is called before writing Jacobian rows. + * + * Implementation skeleton: + * if (WantColumnSizes()) + * for (int i=0; i < num_vars+num_rand_vars-1; ++i) + * csw.Write(col_size[i]); + */ + template + void FeedColumnSizes(ColSizeWriter& csw) { + if (WantColumnSizes()) + for (int i=0; i < var_lbs_.size()-1; ++i) // use old ordering + csw.Write(mark_data_.col_sizes_orig_[ GetOldVarIndex( i ) ]); + } + + + ///////////////////// 12. INITIAL GUESSES ///////////////////// + /** Initial primal guesses. + * + * Implementation: write all meaningfuls entries (incl. zeros.) + * if (ini_guess.size()) { + * auto ig = igw.MakeVectorWriter(ini_guess.size()); + * for (size_t i=0; i + void FeedInitialGuesses(IGWriter& ) { } + + /** Initial dual guesses. */ + template + void FeedInitialDualGuesses(IDGWriter& ) { } + + + ///////////////////// 13. SUFFIXES ///////////////////// + /** Feed suffixes. + * + * For constraints, assume ordering: + * first algebraic, then logical. + * + * Implementation: write all non-0 entries (0 is the default.) + * while (....) { + * auto sw = swf.StartIntSuffix( // or ...DblSuffix + * suf_name, kind, n_nonzeros); + * for (int i=0; i + void FeedSuffixes(SuffixWriterFactory& ) { } + + + //////////////////// 14. ROW/COLUMN NAMES ETC ///////////////////// + /** FeedRowAndObjNames: + * Provide constraint, then objective names. + * Name information is optional. + * + * Implementation: + * if ((output_desired) && wrt) + * for (i: ....) + * wrt << name[i].c_str(); + */ + template + void FeedRowAndObjNames(RowObjNameWriter& wrt) { } + + /** Provide deleted row names.*/ + template + void FeedDelRowNames(DelRowNameWriter& ) { } + + /** Provide variable names. */ + template + void FeedColNames(ColNameWriter& ) { } + + /** Provide unused variable names. */ + template + void FeedUnusedVarNames(UnusedVarNameWriter& ) { } + + /** Provide {fixed variable, extra info} pairs. + * This includes defined eliminated variables. + * + * Implementation: + * if ((output_desired) && wrt) + * for (....) + * wrt << typename Writer::StrStrValue + * { name[i].c_str(), comment[i].c_str() }; + */ + template + void FeedFixedVarNames(FixedVarNameWriter& ) { } + + /** Provide {obj name, constant term} pairs. + * + * Implementation: + * if (wrt) + * for (....) + * wrt << typename Writer::StrDblValue + * { name[i].c_str(), (double)obj_offset[i] }; + */ + template + void FeedObjAdj(ObjOffsetWriter& ) { } + + +protected: + /// For writing NL + void PrepareModel(); + + /// Mark variables. + /// Now just count binary, integer. + void MarkVars(); + + /// Sort variables + void SortVars(); + + /// Mark and count constraint and variable kinds + /// (non/linear/integer, range/eqns.) + void MarkItems(); + + NLHeader DoMakeHeader(); + + /// Parameters passed when marking variables in an expression tree + struct ItemMarkingData { + int n_var_lin_bin_ {0}; + int n_var_lin_int_ {0}; + + int n_ranges_ {0}; + int n_eqns_ {0}; + + std::size_t n_obj_nz_ {0}; + std::size_t n_con_nz_ {0}; + + std::vector< std::pair< int, int > > var_prior_; // new index -> var weight, orig. index + std::vector var_order_12_; // new index -> old index + std::vector var_order_21_; // old index -> new index + + std::vector col_sizes_orig_; // column sizes for original sorting + }; + + + /// Get new var index for an old var index + int GetNewVarIndex(int i) const { return mark_data_.var_order_21_[i]; } + + /// Get old var index for a new var index + int GetOldVarIndex(int i) const { return mark_data_.var_order_12_[i]; } + + + /// Parameters passed when submitting a con/obj to the "solver" + class NLWParams {}; + + + /// Placeholder for item marker + template + void MarkItem(const Item& , ItemMarkingData& ) { + MP_UNSUPPORTED(std::string("MP2NLModelAPI::MarkVars() not supported for ") + + typeid(Item).name()); + } + + /// Mark LinConEQ + void MarkItem(const LinearObjective& lo, ItemMarkingData& prm) { + mark_data_.n_obj_nz_ += lo.vars().size(); // count nnz + } + + /// Mark LinConRange + void MarkItem(const LinConRange& lcr, ItemMarkingData& prm) { + mark_data_.n_con_nz_ += lcr.size(); + Add2ColSizes(lcr.vars()); + if (lcr.lb() > MinusInfinity() + && lcr.ub() < Infinity() + && lcr.lb() < lcr.ub()) + ++prm.n_ranges_; + } + + /// Mark LinConEQ + void MarkItem(const LinConLE& lcr, ItemMarkingData& prm) { + mark_data_.n_con_nz_ += lcr.size(); + Add2ColSizes(lcr.vars()); + } + + /// Mark LinConEQ + void MarkItem(const LinConEQ& lcr, ItemMarkingData& prm) { + mark_data_.n_con_nz_ += lcr.size(); + Add2ColSizes(lcr.vars()); + ++prm.n_eqns_; + } + + /// Mark LinConEQ + void MarkItem(const LinConGE& lcr, ItemMarkingData& prm) { + mark_data_.n_con_nz_ += lcr.size(); + Add2ColSizes(lcr.vars()); + } + + /// Add to col sizes + void Add2ColSizes(const std::vector& vars); + + /// Placeholder for bounds writer + template + void WriteBounds(const Item& , const NLWParams&) { + MP_UNSUPPORTED(std::string("MP2NLModelAPI::WriteBounds() not supported for ") + + typeid(Item).name()); + } + + + /// Dispatch con/obj operations: abstract base + class BasicItemDispatcher { + public: + virtual ~BasicItemDispatcher() { } + /// Construct + BasicItemDispatcher(MP2NLModelAPI& mapi) : mapi_(mapi) { } + const MP2NLModelAPI& GetMAPI() const { return mapi_; } + MP2NLModelAPI& GetMAPI() { return mapi_; } + + virtual void MarkItem(void* pitem, ItemMarkingData& vmp) = 0; + virtual void WriteBounds(void* pitem, const NLWParams& nlwp) = 0; + + private: + MP2NLModelAPI& mapi_; + }; + + + /// Dispatch con/obj operations: specialization + template + class ItemDispatcher : public BasicItemDispatcher { + public: + /// Construct + ItemDispatcher(MP2NLModelAPI& mapi) + : BasicItemDispatcher(mapi) { } + /// Var marking + void MarkItem(void* pitem, ItemMarkingData& vmp) override + { GetMAPI().MarkItem(*(const Item*)pitem, vmp); } + /// Write bounds + void WriteBounds(void* pitem, const NLWParams& nlwp) override + { GetMAPI().WriteBounds(*(const Item*)pitem, nlwp); } + }; + + + /// Placeholder for the dispatcher getter + template + ItemDispatcher& GetItemDispatcher() { throw 0; } + +/// Macro to define an item dispatcher +#define CREATE_ITEM_DISPATCHER(ItemType) \ + ItemDispatcher item_dispatcher_ ## ItemType ## _ { *this }; \ + template <> \ + ItemDispatcher& GetItemDispatcher() \ + { return item_dispatcher_ ## ItemType ## _; } + + + /// Item dispatchers + /// for static item types in the NL format + CREATE_ITEM_DISPATCHER(LinearObjective) + CREATE_ITEM_DISPATCHER(NLObjective) + + CREATE_ITEM_DISPATCHER(LinConRange) + CREATE_ITEM_DISPATCHER(LinConLE) + CREATE_ITEM_DISPATCHER(LinConEQ) + CREATE_ITEM_DISPATCHER(LinConGE) + CREATE_ITEM_DISPATCHER(NLConstraint) + CREATE_ITEM_DISPATCHER(NLAssignLE) + CREATE_ITEM_DISPATCHER(NLAssignEQ) + CREATE_ITEM_DISPATCHER(NLAssignGE) + CREATE_ITEM_DISPATCHER(NLLogical) + CREATE_ITEM_DISPATCHER(NLEquivalence) + CREATE_ITEM_DISPATCHER(NLImpl) + CREATE_ITEM_DISPATCHER(NLRimpl) + CREATE_ITEM_DISPATCHER(IndicatorConstraintLinLE) + CREATE_ITEM_DISPATCHER(IndicatorConstraintLinEQ) + CREATE_ITEM_DISPATCHER(IndicatorConstraintLinGE) + CREATE_ITEM_DISPATCHER(SOS1Constraint) + CREATE_ITEM_DISPATCHER(SOS2Constraint) + CREATE_ITEM_DISPATCHER(NLComplementarity) + + + /// We need item type ID for manual dispatch + enum class StaticItemTypeID { + ID_None, + ID_LinearObjective, + ID_NLObjective, + + ID_LinConRange, + ID_LinConLE, + ID_LinConEQ, + ID_LinConGE, + ID_NLConstraint, + ID_NLAssignLE, + ID_NLAssignEQ, + ID_NLAssignGE, + ID_NLLogical, + ID_NLEquivalence, + ID_NLImpl, + ID_NLRimpl, + ID_IndicatorConstraintLinLE, + ID_IndicatorConstraintLinEQ, + ID_IndicatorConstraintLinGE, + ID_SOS1Constraint, + ID_SOS2Constraint, + ID_NLComplementarity + }; + + + /// Expression type IDs for manual dispatch. + /// These are not NL opcodes: + /// correspond to our expressions. + enum class ExpressionTypeID { + ID_None, + + ID_Lin, + ID_Quad, + + ID_Abs, + ID_And, + ID_Or, + ID_Exp, + ID_Log, + ID_Pow, + ID_Sin, + ID_Cos + }; + + + /// Constraint/objective/expression info. + /// We rely on the pointers staying valid. + class ItemInfo { + public: + /// Construct + ItemInfo (BasicItemDispatcher& disp, + void* pitem, StaticItemTypeID iid, ExpressionTypeID eid) + : disp_(disp), pitem_(pitem), itemID_(iid), exprID_(eid) { } + /// Get dispatcher + BasicItemDispatcher& GetDispatcher() const { return disp_; } + /// Get &item + void* GetPItem() const { return pitem_; } + /// Get static item type ID, if any + StaticItemTypeID GetStaticTypeID() const { return itemID_; } + /// Get expression type ID, if any + ExpressionTypeID GetExprTypeID() const { return exprID_; } + + private: + BasicItemDispatcher& disp_; + void* pitem_; + StaticItemTypeID itemID_ {StaticItemTypeID::ID_None}; + ExpressionTypeID exprID_ {ExpressionTypeID::ID_None}; + }; + + /// Fill static item info + template + ItemInfo MakeItemInfo(const Item& i, StaticItemTypeID sid) { + return { GetItemDispatcher(), (void*)&i, sid, ExpressionTypeID::ID_None }; + } + + /// Fill expression item info + template + ItemInfo MakeItemInfo(const Item& i, ExpressionTypeID eid) { + return { GetItemDispatcher(), (void*)&i, StaticItemTypeID::ID_None, eid }; + } + + +private: + /// References to the model data. + /// @note we rely on them staying valid. + + ArrayRef var_lbs_; + ArrayRef var_ubs_; + ArrayRef var_types_; + ArrayRef var_names_; // can be empty + + + /// @todo still need permutations of NL constraints? + std::vector obj_info_, alg_con_info_, log_con_info_, sos_info_; + + + ItemMarkingData mark_data_; + }; } // namespace mp