git-subtree-dir: prototype git-subtree-split: 91f7db00d45e7f991b5587ee07f09977ae311ee7
501 lines
15 KiB
C++
501 lines
15 KiB
C++
#ifndef __MINIZINC_NL_COMPONENTS_HH__
|
|
#define __MINIZINC_NL_COMPONENTS_HH__
|
|
|
|
//#include <assert.h>
|
|
|
|
#include <exception>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
using namespace std;
|
|
|
|
/* A NL File is composed of a header and several segments.
|
|
Adding items in the nl file is done through adding segment (or adding item in a segment).
|
|
As for the header, segment are printable.
|
|
Segment are identified by a prefix, which should be one of (taken from table 13 in 'writing nl
|
|
files'): F imported function description S suffix values V defined variable definition (must
|
|
precede V,C,L,O segments where used) (yes, I know, "V must preceed V"...) C algebraic constraint
|
|
body L logical constraint expression O objective function d dual initial guess x primal
|
|
initial guess r bounds on algebraic constraint bodies (“ranges”), can only appears once b bounds
|
|
on variable, can only appears once k Jacobian column counts (must precede all J segments) J
|
|
Jacobian sparsity, linear terms G Gradient sparsity, linear terms
|
|
*/
|
|
|
|
namespace MiniZinc {
|
|
|
|
// --- --- --- Tooling
|
|
|
|
/** Exception when translating
|
|
* Code mostly taken from https://www.softwariness.com/articles/assertions-in-cpp/
|
|
*/
|
|
class NLException : public exception {
|
|
public:
|
|
// --- --- --- Fields
|
|
const char* expression;
|
|
const char* file;
|
|
int line;
|
|
string message;
|
|
string report;
|
|
|
|
/** Exception constructor. Use with the macro assert/should_not_happen.
|
|
* If not, WARNING: stream must be a std::ostringstream&
|
|
* We only use a ostreamé so we can use the standard "<<" operator, which is returning a ostream&
|
|
*/
|
|
NLException(const char* expression, const char* file, int line, ostream& stream)
|
|
: expression(expression), file(file), line(line) {
|
|
message = static_cast<std::ostringstream&>(stream).str();
|
|
std::ostringstream outputStream;
|
|
|
|
if (expression == NULL) {
|
|
outputStream << "Something should not have happen in file '" << file << "' line " << line
|
|
<< ". Message:" << endl;
|
|
if (!message.empty()) {
|
|
outputStream << message << endl;
|
|
} else {
|
|
outputStream << "No message provided..." << endl;
|
|
}
|
|
} else {
|
|
string expressionString = expression;
|
|
if (expressionString == "false" || expressionString == "0" || expressionString == "FALSE") {
|
|
outputStream << "Unreachable code assertion";
|
|
} else {
|
|
outputStream << "Assertion '" << expression << "'";
|
|
}
|
|
outputStream << " failed in file '" << file << "' line " << line << endl;
|
|
}
|
|
outputStream << "Note: the NL component is still in development!" << endl;
|
|
report = outputStream.str();
|
|
}
|
|
|
|
/** Exception interface */
|
|
virtual const char* what() const noexcept { return report.c_str(); }
|
|
|
|
~NLException() noexcept = default;
|
|
};
|
|
|
|
#ifdef assert
|
|
#undef assert
|
|
#endif
|
|
|
|
/** Should not happen macro */
|
|
#define should_not_happen(MESSAGE) \
|
|
do { \
|
|
ostringstream oss; \
|
|
oss << MESSAGE; \
|
|
throw NLException(NULL, __FILE__, __LINE__, oss); \
|
|
} while (false)
|
|
|
|
/* CMake febug build flag: double negation... because... ? */
|
|
#ifndef NDEBUG
|
|
#define DEBUG_MSG(STR) \
|
|
do { \
|
|
std::cerr << "%[NL DEBUG] " << STR << endl; \
|
|
} while (false)
|
|
#define assert(EXPRESSION) \
|
|
do { \
|
|
if (!(EXPRESSION)) { \
|
|
ostringstream oss; \
|
|
throw NLException(#EXPRESSION, __FILE__, __LINE__, oss); \
|
|
} \
|
|
} while (false)
|
|
#else
|
|
#define DEBUG_MSG(STR) \
|
|
do { \
|
|
} while (false)
|
|
#define assert(EXPRESSION) \
|
|
do { \
|
|
} while (false)
|
|
#endif
|
|
|
|
// --- --- --- Components
|
|
|
|
// Declaration
|
|
class NLFile;
|
|
|
|
/** A Bound.
|
|
* A bound can represent various constraint on a variable or a constraint.
|
|
* Because it apply to both variables and constraints, we keep it general enough.
|
|
* Note that the targeted variable or constraint is implicitely represented by its position in the
|
|
final NL File.
|
|
* As a result, this information does not appear here.
|
|
* Bounds are used in the 'b' and 'r' segments.
|
|
|
|
# Text # Starting the segment # Variable # Tag in enum NLS_Bounditem::Bound
|
|
0 1.1 3.4 # 1.1 =< V =< 3.4 First variable LB_UB
|
|
1 2.5 # V =< 2.5 Second variable UB
|
|
2 7 # 7 =< V etc... LB
|
|
3 # no constraint NONE
|
|
4 9.4 # V = 9.4 EQ
|
|
|
|
* Notes: - bound values are stored as 'double', even for integer variables.
|
|
* - we do not make that class Printable as it is better "printed" with a name for the
|
|
targeted variable/constraint
|
|
*/
|
|
class NLBound {
|
|
public:
|
|
/** Bound kind. Declaration matches specification above. */
|
|
enum Bound { LB_UB = 0, UB = 1, LB = 2, NONE = 3, EQ = 4 };
|
|
|
|
/** *** *** *** Fields *** *** *** **/
|
|
Bound tag = NONE;
|
|
double lb = 0;
|
|
double ub = 0;
|
|
|
|
/** *** *** *** Constructors & helpers *** *** *** **/
|
|
NLBound() = default;
|
|
NLBound(Bound tag, double lb, double ub);
|
|
|
|
static NLBound make_bounded(double lb, double ub);
|
|
static NLBound make_ub_bounded(double ub);
|
|
static NLBound make_lb_bounded(double lb);
|
|
static NLBound make_nobound();
|
|
static NLBound make_equal(double val);
|
|
|
|
/** *** *** *** Update the lower or upper bound *** *** *** **/
|
|
// Note: this method are "additive only": we cannot use them to remove a a bound.
|
|
void update_lb(double new_lb);
|
|
void update_ub(double new_ub);
|
|
void update_eq(double new_eq);
|
|
|
|
/** *** *** *** Printing Methods *** *** *** **/
|
|
|
|
/** Print the bound with a comment containing the name of the variable/constraint. */
|
|
ostream& print_on(ostream& o, string vname) const;
|
|
|
|
/** Printing with 'body' as the name of the variable/constraint. */
|
|
ostream& print_on(ostream& o) const;
|
|
};
|
|
|
|
/** A Declared variable.
|
|
* A variable is identified by its name, which is supposed to be unique in the MZN representation.
|
|
* In an NL file, variables are identified by their index. However, those index are dependent on
|
|
* the variable ordering, which can only be known once all variables are known. Hence, the
|
|
* computation of the index can only be achieved at a later stage. A variable is always associated
|
|
* to a bound, even if none are specified (See LNBound above)/
|
|
*/
|
|
class NLVar {
|
|
public:
|
|
/** Variable name. */
|
|
string name;
|
|
|
|
/** Is the variable an integer variable? Else is a floating point variable. */
|
|
bool is_integer = false;
|
|
|
|
/** Is this variable flagged to be reported? */
|
|
bool to_report = false;
|
|
|
|
/** Is the variable appearing in a nonlinear constraint (including logical constraint, L segment).
|
|
*/
|
|
bool is_in_nl_constraint = false;
|
|
|
|
/** Is the variable appearing non linearly in the objective? */
|
|
bool is_in_nl_objective = false;
|
|
|
|
/** Number of occurrences in Jacobian. */
|
|
long jacobian_count = 0;
|
|
|
|
/** The bound over this variable.
|
|
* Used when producing the unique 'b' segment of the NL file.
|
|
*/
|
|
NLBound bound;
|
|
|
|
/* *** *** *** Constructors *** *** *** */
|
|
NLVar() = default;
|
|
|
|
/** Constructor with declare time information */
|
|
NLVar(const string& name, bool is_integer, bool to_report, NLBound bound)
|
|
: name(name), is_integer(is_integer), to_report(to_report), bound(bound) {}
|
|
|
|
/** Copy constructor, with update on bound */
|
|
NLVar copy_with_bound(NLBound bound) const;
|
|
};
|
|
|
|
/** A NLArray:
|
|
* We do not use "real" array.
|
|
* This type only serves when sending the result back to minizinc
|
|
*/
|
|
class NLArray {
|
|
public:
|
|
/** Array item; if the string is empty, use the value. */
|
|
class Item {
|
|
public:
|
|
string variable;
|
|
double value;
|
|
};
|
|
|
|
/** Array name */
|
|
string name;
|
|
|
|
/** Dimensions part, e.g. array2d( '0..4', '0..5' [ .... ]) */
|
|
vector<string> dimensions;
|
|
|
|
/** Related variables */
|
|
vector<Item> items;
|
|
|
|
/** Is this an array or integers or floats? */
|
|
bool is_integer = false;
|
|
};
|
|
|
|
/** A token from an 'expression graph'.
|
|
* An expression graph is express in Polish Prefix Notation: operator followed by operand.
|
|
* A token represent an operator or an operand.
|
|
* See the definition of the various enum.
|
|
*/
|
|
class NLToken {
|
|
public:
|
|
/** Kind of token. */
|
|
enum Kind {
|
|
NUMERIC, // "n42.42" a numeric constant, double
|
|
VARIABLE, // "v4" reference to a decision variable 0<= i < nb_vars (see header) or a
|
|
// defined variable for i>=nb_vars
|
|
STRING, // "h11:some string" Probably unused in our case.
|
|
FUNCALL, // "f0 3" Call a defined function (index 0, 3 args). Probably unused in
|
|
// our case.
|
|
OP, // "o5" An operation defined by its operation code
|
|
MOP // "o7\n3" Operator with multiple operand. The number of operands (3) is on
|
|
// the next line ("\n")?
|
|
};
|
|
|
|
/** Opcode for operator with a fixed number of operands. */
|
|
enum OpCode {
|
|
OPPLUS = 0,
|
|
OPMINUS = 1,
|
|
OPMULT = 2,
|
|
OPDIV = 3,
|
|
OPREM = 4,
|
|
OPPOW = 5,
|
|
OPLESS = 6,
|
|
FLOOR = 13,
|
|
CEIL = 14,
|
|
ABS = 15,
|
|
OPUMINUS = 16,
|
|
OPOR = 20,
|
|
OPAND = 21,
|
|
LT = 22,
|
|
LE = 23,
|
|
EQ = 24,
|
|
GE = 28,
|
|
GT = 29,
|
|
NE = 30,
|
|
OPNOT = 34,
|
|
OPIFnl = 35,
|
|
OP_tanh = 37,
|
|
OP_tan = 38,
|
|
OP_sqrt = 39,
|
|
OP_sinh = 40,
|
|
OP_sin = 41,
|
|
OP_log10 = 42,
|
|
OP_log = 43,
|
|
OP_exp = 44,
|
|
OP_cosh = 45,
|
|
OP_cos = 46,
|
|
OP_atanh = 47,
|
|
OP_atan2 = 48,
|
|
OP_atan = 49,
|
|
OP_asinh = 50,
|
|
OP_asin = 51,
|
|
OP_acosh = 52,
|
|
OP_acos = 53,
|
|
OPintDIV = 55,
|
|
OPprecision = 56,
|
|
OPround = 57,
|
|
OPtrunc = 58,
|
|
OPATLEAST = 62,
|
|
OPATMOST = 63,
|
|
OPPLTERM = 64,
|
|
OPIFSYM = 65,
|
|
OPEXACTLY = 66,
|
|
OPNOTATLEAST = 67,
|
|
OPNOTATMOST = 68,
|
|
OPNOTEXACTLY = 69,
|
|
OPIMPELSE = 72,
|
|
OP_IFF = 73,
|
|
OPSOMESAME = 75,
|
|
OP1POW = 76,
|
|
OP2POW = 77,
|
|
OPCPOW = 78,
|
|
OPFUNCALL = 79,
|
|
OPNUM = 80,
|
|
OPHOL = 81,
|
|
OPVARVAL = 82,
|
|
N_OPS = 83,
|
|
};
|
|
|
|
/** Opcodes for operand taking multiple arguments. */
|
|
enum MOpCode {
|
|
MINLIST = 11,
|
|
MAXLIST = 12,
|
|
OPSUMLIST = 54,
|
|
OPCOUNT = 59,
|
|
OPNUMBEROF = 60,
|
|
OPNUMBEROFs = 61,
|
|
ANDLIST = 70,
|
|
ORLIST = 71,
|
|
OPALLDIFF = 74,
|
|
};
|
|
|
|
/** Obtain the name of an operator from its opcode. */
|
|
static const char* get_name(OpCode oc);
|
|
|
|
/** Obtain the name of an operator (with multiple operands) from its opcode. */
|
|
static const char* get_name(MOpCode moc);
|
|
|
|
/* *** *** *** Fields *** *** *** */
|
|
|
|
Kind kind;
|
|
double numeric_value; // if kind==NUMERIC
|
|
int nb_args; // if kind==FUNCALL or kind==MOP
|
|
string str; // if kind==STRING or kind=VARIABLE (variable name) or kind=FUNCALL (function name)
|
|
OpCode oc; // if kind==OP
|
|
MOpCode moc; // if kind==MOP
|
|
|
|
/* *** *** *** Constructor and helpers *** *** *** */
|
|
|
|
NLToken() = default;
|
|
|
|
static NLToken n(double value);
|
|
|
|
static NLToken v(string vname);
|
|
|
|
static NLToken o(OpCode opc);
|
|
|
|
static NLToken mo(MOpCode mopc, int nb);
|
|
|
|
/* *** *** *** Query *** *** *** */
|
|
bool is_variable() const;
|
|
|
|
bool is_constant() const;
|
|
|
|
/* *** *** *** Printable *** *** *** */
|
|
|
|
ostream& print_on(ostream& o, const NLFile& nl_file) const;
|
|
};
|
|
|
|
/** A algebraic constraint.
|
|
* Contains both a linear and a non linear part.
|
|
* We do not handle network constraints.
|
|
*/
|
|
class NLAlgCons {
|
|
public:
|
|
/** Constraint name, also acts as identifier. */
|
|
string name;
|
|
|
|
/** Bound on the algebraic constraint.
|
|
* Used when producing the unique r of the NL file.
|
|
*/
|
|
NLBound range;
|
|
|
|
/** Expression graph, used for the non linear part.
|
|
* Used to produce a new, standalone, C segment.
|
|
* If the expression graph is empty (linear constraint), produce the expression graph 'n0'
|
|
*/
|
|
vector<NLToken> expression_graph = {};
|
|
|
|
/** Jacobian, used for the linear part. Identify a variable by its name and associate a
|
|
* coefficent. Used to produce a new, standalone, J segment.
|
|
*/
|
|
vector<pair<string, double>> jacobian = {};
|
|
|
|
/** Method to build the var_coeff vector.
|
|
* The NLFile is used to access the variables through their name in order to increase their
|
|
* jacobian count.
|
|
*/
|
|
void set_jacobian(const vector<string>& vnames, const vector<double>& coeffs, NLFile* nl_file);
|
|
|
|
/* *** *** *** Helpers *** *** *** */
|
|
|
|
/** A constraint is considered linear if the expression_graph is empty. */
|
|
bool is_linear() const;
|
|
|
|
/* *** *** *** Printable *** *** *** */
|
|
|
|
ostream& print_on(ostream& o, const NLFile& nl_file) const;
|
|
};
|
|
|
|
/** A logical constraint.
|
|
* Contains only a non linear part.
|
|
* We do not handle network constraints.
|
|
* Logical constraint stands on their own and do not need any identifier.
|
|
* However, for consistency sake, we still keep their name.
|
|
*/
|
|
class NLLogicalCons {
|
|
public:
|
|
/** Constraint name, also acts as identifier. */
|
|
string name;
|
|
|
|
/** Index */
|
|
int index = -1;
|
|
|
|
/** Expression graph, used for the non linear part.
|
|
* Used to produce a new, standalone, L segment.
|
|
* If the expression graph is empty (linear constraint), produce the expression graph 'n0'
|
|
*/
|
|
vector<NLToken> expression_graph = {};
|
|
|
|
/* *** *** *** Constructor *** *** *** */
|
|
NLLogicalCons(int idx) : index(idx) {}
|
|
|
|
/* *** *** *** Printable *** *** *** */
|
|
|
|
ostream& print_on(ostream& o, const NLFile& nl_file) const;
|
|
};
|
|
|
|
/** The header. */
|
|
class NLHeader {
|
|
public:
|
|
/* *** *** *** Printable *** *** *** */
|
|
|
|
ostream& print_on(ostream& o, const NLFile& nl_file) const;
|
|
};
|
|
|
|
/** An Objective
|
|
* In an NL file, we can have several of those.
|
|
* However, in flatzinc, only one is allowed, so we only have one.
|
|
* Note that in NL, we do not have a "satisfy" objective, only a minimize or maximize one.
|
|
* We translate the "satisfy" with "minimize n0".
|
|
*/
|
|
class NLObjective {
|
|
public:
|
|
enum MinMax {
|
|
UNDEF = -2,
|
|
SATISFY = -1,
|
|
MINIMIZE = 0,
|
|
MAXIMIZE = 1,
|
|
};
|
|
|
|
/* *** *** *** Fields *** *** *** */
|
|
MinMax minmax = UNDEF;
|
|
vector<NLToken> expression_graph = {}; // If empty, produce a 'n0' when printing
|
|
|
|
/* *** *** *** Gradient *** *** *** */
|
|
|
|
/** Gradient, used for the linear part. Identify a variable by its name and associate a
|
|
* coefficent. Used to produce a new, standalone, G segment.
|
|
*/
|
|
vector<pair<string, double>> gradient = {};
|
|
|
|
/** Method to build the var_coeff vector. */
|
|
void set_gradient(const vector<string>& vnames, const vector<double>& coeffs);
|
|
|
|
int gradient_count() const;
|
|
|
|
/* *** *** *** Helpers *** *** *** */
|
|
bool is_defined() const;
|
|
|
|
bool is_linear() const;
|
|
|
|
bool is_optimisation() const;
|
|
|
|
/* *** *** *** Constructor *** *** *** */
|
|
NLObjective() = default;
|
|
|
|
/* *** *** *** Printable *** *** *** */
|
|
ostream& print_on(ostream& o, const NLFile& nl_file) const;
|
|
};
|
|
|
|
} // namespace MiniZinc
|
|
|
|
#endif
|