From e040c80bef57d61191f1e93b7afe5758deb7e68e Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Tue, 5 Mar 2024 16:40:39 +1100 Subject: [PATCH] Export NL model #232 --- include/mp/flat/converter_model.h | 5 +- include/mp/flat/problem_flattener.h | 102 +++++++++++++++++- src/expr-writer.h | 70 +++++++----- src/problem.cc | 15 +++ .../cvtgraph/scripts/python/dataCvtGraph.py | 2 +- 5 files changed, 162 insertions(+), 32 deletions(-) diff --git a/include/mp/flat/converter_model.h b/include/mp/flat/converter_model.h index 4c08eadca..d67bce9af 100644 --- a/include/mp/flat/converter_model.h +++ b/include/mp/flat/converter_model.h @@ -74,10 +74,11 @@ class FlatModel fmt::MemoryWriter wrt; { MiniJSONWriter jw(wrt); - jw["VAR_index"] = i+i_start; + int i_actual = i+i_start; + jw["VAR_index"] = i_actual; jw["bounds"] << lbs[i] << ubs[i]; jw["type"] = (int)types[i]; - jw["is_from_nl"] = (int)is_var_original(i); + jw["is_from_nl"] = (int)is_var_original(i_actual); } wrt.write("\n"); // with EOL GetFileAppender().Append(wrt); diff --git a/include/mp/flat/problem_flattener.h b/include/mp/flat/problem_flattener.h index dfad110a7..db09cc34d 100644 --- a/include/mp/flat/problem_flattener.h +++ b/include/mp/flat/problem_flattener.h @@ -28,6 +28,19 @@ LinTerms ToLinTerms(const LinearExpr& e) { return le; } +/// Write algebraic expression (linear + non-linear.) +template +void WriteExpr(fmt::Writer &w, const LinearExpr &linear, + NumericExpr nonlinear); + +/// Write logical expression +template +void WriteExpr(fmt::Writer &w, LogicalExpr expr); + +/// Write algebraic constraint. +template +void WriteAlgCon(fmt::Writer &w, const AlgCon &con); + /// ProblemFlattener: it walks and "flattens" most expressions /// by replacing them by a result variable and constraints. @@ -134,31 +147,112 @@ class ProblemFlattener : ////////////////////////// Common exprs int num_common_exprs = GetModel().num_common_exprs(); - for (int i = 0; i < num_common_exprs; ++i) + for (int i = 0; i < num_common_exprs; ++i) { + MPD( ExportCommonExpr(i) ); MP_DISPATCH( Convert( GetModel().common_expr(i) ) ); + } ////////////////////////// Objectives ifFltCon_ = 0; if (int num_objs = GetModel().num_objs()) - for (int i = 0; i < num_objs; ++i) + for (int i = 0; i < num_objs; ++i) { + MPD( ExportObj(i) ); MP_DISPATCH( Convert( GetModel().obj(i) ) ); + } ////////////////////////// Algebraic constraints ifFltCon_ = 1; if (int n_cons = GetModel().num_algebraic_cons()) - for (int i = 0; i < n_cons; ++i) + for (int i = 0; i < n_cons; ++i) { + MPD( ExportAlgCon(i) ); MP_DISPATCH( ConvertAlgCon( i ) ); + } ////////////////////////// Logical constraints ifFltCon_ = 1; if (int n_lcons = GetModel().num_logical_cons()) - for (int i = 0; i < n_lcons; ++i) + for (int i = 0; i < n_lcons; ++i) { + MPD( ExportLogCon(i) ); MP_DISPATCH( ConvertLogicalCon( i ) ); + } /// Signal we are not flattening anything ifFltCon_ = -1; } + /// Export common expression \a i. + void ExportCommonExpr(int i) { + if (GetFlatCvt().GetFileAppender().IsOpen()) { + fmt::MemoryWriter wrt; + { + MiniJSONWriter jw(wrt); + jw["NL_COMMON_EXPR_index"] = i; + auto ce = GetModel().common_expr(i); + fmt::MemoryWriter w2; + WriteExpr( + w2, ce.linear_expr(), ce.nonlinear_expr()); + jw["printed"] = w2.c_str(); + } + wrt.write("\n"); // EOL + GetFlatCvt().GetFileAppender().Append(wrt); + } + } + + /// Export objective \a i. + void ExportObj(int i) { + if (GetFlatCvt().GetFileAppender().IsOpen()) { + fmt::MemoryWriter wrt; + { + MiniJSONWriter jw(wrt); + jw["NL_OBJECTIVE_index"] = i; + auto obj = GetModel().obj(i); + jw["sense"] = (int)obj.type(); + fmt::MemoryWriter w2; + WriteExpr( + w2, obj.linear_expr(), obj.nonlinear_expr()); + jw["printed"] = w2.c_str(); + } + wrt.write("\n"); // EOL + GetFlatCvt().GetFileAppender().Append(wrt); + } + } + + /// Export algebraic constraint \a i. + void ExportAlgCon(int i) { + if (GetFlatCvt().GetFileAppender().IsOpen()) { + fmt::MemoryWriter wrt; + { + MiniJSONWriter jw(wrt); + auto con = GetModel().algebraic_con(i); + jw["NL_CON_TYPE"] = (con.nonlinear_expr() ? "nonlin" : "lin"); + jw["index"] = i; + fmt::MemoryWriter w2; + WriteAlgCon(w2, con); + jw["printed"] = w2.c_str(); + } + wrt.write("\n"); // EOL + GetFlatCvt().GetFileAppender().Append(wrt); + } + } + + /// Export logical constraint \a i. + void ExportLogCon(int i) { + if (GetFlatCvt().GetFileAppender().IsOpen()) { + fmt::MemoryWriter wrt; + { + MiniJSONWriter jw(wrt); + auto con = GetModel().logical_con(i); + jw["NL_CON_TYPE"] = "logical"; + jw["index"] = GetModel().num_algebraic_cons() + i; + fmt::MemoryWriter w2; + WriteExpr(w2, con.expr()); + jw["printed"] = w2.c_str(); + } + wrt.write("\n"); // EOL + GetFlatCvt().GetFileAppender().Append(wrt); + } + } + /// Convert variables void ConvertVars() { std::vector lbs(GetModel().num_vars()); diff --git a/src/expr-writer.h b/src/expr-writer.h index 695a138a8..f29b9b084 100644 --- a/src/expr-writer.h +++ b/src/expr-writer.h @@ -51,8 +51,8 @@ enum Precedence { } namespace expr { -// Returns operator precedence for the specified expression kind assuming the -// notation used by ExprWriter. +/// Returns operator precedence for the specified expression kind assuming the +/// notation used by ExprWriter. prec::Precedence precedence(expr::Kind kind); class PrecInfo { @@ -72,10 +72,10 @@ inline bool IsZero(NumericExpr expr) { return n && n.value() == 0; } -// An expression visitor that writes AMPL expressions in a textual form -// to fmt::Writer. It takes into account precedence and associativity -// of operators avoiding unnecessary parentheses except for potentially -// confusing cases such as "!x = y" which is written as "!(x = y) instead. +/// An expression visitor that writes AMPL expressions in a textual form +/// to fmt::Writer. It takes into account precedence and associativity +/// of operators avoiding unnecessary parentheses except for potentially +/// confusing cases such as "!x = y" which is written as "!(x = y) instead. template class ExprWriter : public BasicExprVisitor, void, ExprTypes> { @@ -89,7 +89,7 @@ class ExprWriter : static int precedence(Expr e) { return expr::precedence(e.kind()); } - // Writes an argument list surrounded by parentheses. + /// Writes an argument list surrounded by parentheses. template void WriteArgs(Iter begin, Iter end, const char *sep = ", ", int precedence = prec::UNKNOWN); @@ -100,7 +100,7 @@ class ExprWriter : WriteArgs(e.begin(), e.end(), sep, precedence); } - // Writes a function or an expression that has a function syntax. + /// Writes a function or an expression that has a function syntax. template void WriteFunc(Expr e) { writer_ << str(e.kind()); @@ -124,19 +124,25 @@ class ExprWriter : }; public: + /// Construct explicit ExprWriter(fmt::Writer &w) : writer_(w), precedence_(prec::UNKNOWN) {} + /// Visit numeric expr void Visit(NumericExpr e, int precedence = -1) { Parenthesizer p(*this, e, precedence); Base::Visit(e); } + /// Visit logical expr void Visit(LogicalExpr e, int precedence = -1) { Parenthesizer p(*this, e, precedence); Base::Visit(e); } + void VisitCommonExpr(CommonExpr e) + { writer_ << "ce" << (e.index() + 1); } + void VisitNumericConstant(NumericConstant c) { writer_ << c.value(); } void VisitUnary(UnaryExpr e) { @@ -164,7 +170,8 @@ class ExprWriter : void VisitNumberOf(NumberOfExpr e); void VisitPLTerm(PLTerm e); void VisitCall(CallExpr e); - void VisitVariable(Variable v) { writer_ << 'x' << (v.index() + 1); } + void VisitVariable(Variable v) + { writer_ << 'x' << (v.index() + 1); } void VisitNot(NotExpr e) { writer_ << '!'; @@ -280,7 +287,7 @@ void ExprWriter::VisitIf(IfExpr e) { template void ExprWriter::VisitSum(SumExpr e) { - writer_ << "/* sum */ ("; + writer_ << "("; typename SumExpr::iterator i = e.begin(), end = e.end(); if (i != end) { Visit(*i); @@ -367,12 +374,12 @@ void ExprWriter::VisitImplication(ImplicationExpr e) { } } +/// Write algebraic expression (linear + non-linear.) template void WriteExpr(fmt::Writer &w, const LinearExpr &linear, NumericExpr nonlinear) { bool have_terms = false; - typedef typename LinearExpr::iterator Iterator; - for (Iterator i = linear.begin(), e = linear.end(); i != e; ++i) { + for (auto i = linear.begin(), e = linear.end(); i != e; ++i) { double coef = i->coef(); if (coef != 0) { if (have_terms) @@ -394,7 +401,31 @@ void WriteExpr(fmt::Writer &w, const LinearExpr &linear, ExprWriter(w).Visit(nonlinear); } -// Writes a problem in AMPL format. +/// Write logical expression +template +void WriteExpr(fmt::Writer &w, LogicalExpr expr) { + ExprWriter(w).Visit(expr); +} + +/// Write algebraic constraint. +template +void WriteAlgCon(fmt::Writer &w, + const AlgCon &con) { + double inf = INFINITY; + double lb = con.lb(), ub = con.ub(); + if (lb != ub && lb != -inf && ub != inf) + w << lb << " <= "; + WriteExpr( + w, con.linear_expr(), con.nonlinear_expr()); + if (lb == ub) + w << " = " << lb; + else if (ub != inf) + w << " <= " << ub; + else if (lb != -inf) + w << " >= " << lb; +} + +/// Writes a problem in AMPL format. template void Write(fmt::Writer &w, const Problem &p) { // Write variables. @@ -427,18 +458,7 @@ void Write(fmt::Writer &w, const Problem &p) { // Write algebraic constraints. for (int i = 0, n = p.num_algebraic_cons(); i < n; ++i) { w << "s.t. c" << (i + 1) << ": "; - typename Problem::AlgebraicCon con = p.algebraic_con(i); - double lb = con.lb(), ub = con.ub(); - if (lb != ub && lb != -inf && ub != inf) - w << lb << " <= "; - WriteExpr( - w, con.linear_expr(), con.nonlinear_expr()); - if (lb == ub) - w << " = " << lb; - else if (ub != inf) - w << " <= " << ub; - else if (lb != -inf) - w << " >= " << lb; + WriteAlgCon(w, p.algebraic_con(i)); w << ";\n"; } } diff --git a/src/problem.cc b/src/problem.cc index 39bc3ca2e..dc7b5e894 100644 --- a/src/problem.cc +++ b/src/problem.cc @@ -24,6 +24,7 @@ #include "mp/problem.h" #include "mp/problem-builder.h" +#include "expr-writer.h" namespace mp { @@ -118,4 +119,18 @@ template void ReadNLFile(fmt::CStringRef filename, Problem &p, int flags); template void ReadNLString(NLStringRef str, Problem &p, fmt::CStringRef name, int flags); + +template +void WriteExpr +(fmt::Writer &w, const LinearExpr &linear, NumericExpr nonlinear); + +template +void WriteExpr +(fmt::Writer &w, LogicalExpr expr); + +/// Write algebraic constraint. +template +void WriteAlgCon +(fmt::Writer &w, const typename Problem::MutAlgebraicCon &con); + } // namespace mp diff --git a/support/cvtgraph/scripts/python/dataCvtGraph.py b/support/cvtgraph/scripts/python/dataCvtGraph.py index 348cb7122..c87c7ae2a 100644 --- a/support/cvtgraph/scripts/python/dataCvtGraph.py +++ b/support/cvtgraph/scripts/python/dataCvtGraph.py @@ -64,7 +64,7 @@ def _compileLinkTerminalNodeRange(self, data: dict): return range # Add graph arcs. - # Currenlty this also saves nodes (node types), + # Currently this also saves nodes (node types), # as we don't expect node records from the file. # @param src_nodes: source node ranges # @param dest_nodes: destination node ranges