Skip to content

Commit

Permalink
Export NL model #232
Browse files Browse the repository at this point in the history
  • Loading branch information
glebbelov committed Mar 5, 2024
1 parent e72f0c0 commit e040c80
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 32 deletions.
5 changes: 3 additions & 2 deletions include/mp/flat/converter_model.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
102 changes: 98 additions & 4 deletions include/mp/flat/problem_flattener.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ LinTerms ToLinTerms(const LinearExpr& e) {
return le;
}

/// Write algebraic expression (linear + non-linear.)
template <typename ExprTypes, typename LinearExpr, typename NumericExpr>
void WriteExpr(fmt::Writer &w, const LinearExpr &linear,
NumericExpr nonlinear);

/// Write logical expression
template <typename ExprTypes, typename LogicalExpr>
void WriteExpr(fmt::Writer &w, LogicalExpr expr);

/// Write algebraic constraint.
template <class ExprTypes, class AlgCon>
void WriteAlgCon(fmt::Writer &w, const AlgCon &con);


/// ProblemFlattener: it walks and "flattens" most expressions
/// by replacing them by a result variable and constraints.
Expand Down Expand Up @@ -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<typename ProblemType::ExprTypes>(
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<typename ProblemType::ExprTypes>(
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<typename ProblemType::ExprTypes>(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<typename ProblemType::ExprTypes>(w2, con.expr());
jw["printed"] = w2.c_str();
}
wrt.write("\n"); // EOL
GetFlatCvt().GetFileAppender().Append(wrt);
}
}

/// Convert variables
void ConvertVars() {
std::vector<double> lbs(GetModel().num_vars());
Expand Down
70 changes: 45 additions & 25 deletions src/expr-writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 <typename ExprTypes>
class ExprWriter :
public BasicExprVisitor<ExprWriter<ExprTypes>, void, ExprTypes> {
Expand All @@ -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 <typename Iter>
void WriteArgs(Iter begin, Iter end, const char *sep = ", ",
int precedence = prec::UNKNOWN);
Expand All @@ -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 <typename Expr>
void WriteFunc(Expr e) {
writer_ << str(e.kind());
Expand All @@ -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) {
Expand Down Expand Up @@ -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_ << '!';
Expand Down Expand Up @@ -280,7 +287,7 @@ void ExprWriter<ExprTypes>::VisitIf(IfExpr e) {

template <typename ExprTypes>
void ExprWriter<ExprTypes>::VisitSum(SumExpr e) {
writer_ << "/* sum */ (";
writer_ << "(";
typename SumExpr::iterator i = e.begin(), end = e.end();
if (i != end) {
Visit(*i);
Expand Down Expand Up @@ -367,12 +374,12 @@ void ExprWriter<ExprTypes>::VisitImplication(ImplicationExpr e) {
}
}

/// Write algebraic expression (linear + non-linear.)
template <typename ExprTypes, typename LinearExpr, typename NumericExpr>
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)
Expand All @@ -394,7 +401,31 @@ void WriteExpr(fmt::Writer &w, const LinearExpr &linear,
ExprWriter<ExprTypes>(w).Visit(nonlinear);
}

// Writes a problem in AMPL format.
/// Write logical expression
template <typename ExprTypes, typename LogicalExpr>
void WriteExpr(fmt::Writer &w, LogicalExpr expr) {
ExprWriter<ExprTypes>(w).Visit(expr);
}

/// Write algebraic constraint.
template <class ExprTypes, class AlgCon>
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<ExprTypes>(
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 <typename Problem>
void Write(fmt::Writer &w, const Problem &p) {
// Write variables.
Expand Down Expand Up @@ -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<typename Problem::ExprTypes>(
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<typename Problem::ExprTypes>(w, p.algebraic_con(i));
w << ";\n";
}
}
Expand Down
15 changes: 15 additions & 0 deletions src/problem.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include "mp/problem.h"
#include "mp/problem-builder.h"
#include "expr-writer.h"

namespace mp {

Expand Down Expand Up @@ -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<typename Problem::ExprTypes>
(fmt::Writer &w, const LinearExpr &linear, NumericExpr nonlinear);

template
void WriteExpr<typename Problem::ExprTypes>
(fmt::Writer &w, LogicalExpr expr);

/// Write algebraic constraint.
template
void WriteAlgCon<typename Problem::ExprTypes>
(fmt::Writer &w, const typename Problem::MutAlgebraicCon &con);

} // namespace mp
2 changes: 1 addition & 1 deletion support/cvtgraph/scripts/python/dataCvtGraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit e040c80

Please sign in to comment.