1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.
on-restart-benchmarks/solvers/nl/nl_components.cpp
Jip J. Dekker f2a1c4e389 Squashed 'software/mza/' content from commit f970a59b17
git-subtree-dir: software/mza
git-subtree-split: f970a59b177c13ca3dd8aaef8cc6681d83b7e813
2021-07-11 16:34:30 +10:00

618 lines
17 KiB
C++

#include <minizinc/solvers/nl/nl_components.hh>
#include <minizinc/solvers/nl/nl_file.hh>
namespace MiniZinc {
/* *** *** *** NLBound *** *** *** */
// Constructors
NLBound::NLBound(Bound tag, double lb, double ub) : tag(tag), lb(lb), ub(ub) {}
NLBound NLBound::make_bounded(double lb, double ub) {
if (lb == ub) {
return make_equal(lb);
} else {
assert(lb < ub);
return NLBound(LB_UB, lb, ub);
}
}
NLBound NLBound::make_ub_bounded(double ub) { return NLBound(UB, 0, ub); }
NLBound NLBound::make_lb_bounded(double lb) { return NLBound(LB, lb, 0); }
NLBound NLBound::make_nobound() { return NLBound(NONE, 0, 0); }
NLBound NLBound::make_equal(double val) { return NLBound(EQ, val, val); }
/** Update the lower bound */
void NLBound::update_lb(double new_lb) {
switch (tag) {
// LB <= var <= UB. Same tag
case NLBound::LB_UB: {
assert(new_lb <= ub);
if (new_lb > lb) {
lb = new_lb;
}
break;
}
// var <= UB. Update tag
case NLBound::UB: {
assert(new_lb <= ub);
tag = LB_UB;
lb = new_lb;
break;
}
// LB <= var. Same tag
case NLBound::LB: {
if (new_lb > lb) {
lb = new_lb;
}
break;
}
// No bound. Update tag
case NLBound::NONE: {
tag = LB;
lb = new_lb;
break;
}
// LB = var = UB. Should not happen
case NLBound::EQ: {
should_not_happen(
"Updating a bound already set to \"equals\". We only allow tightening update.");
}
}
}
/** Update the upper bound */
void NLBound::update_ub(double new_ub) {
switch (tag) {
// LB <= var <= UB. Same tag
case NLBound::LB_UB: {
assert(lb <= new_ub);
if (new_ub < ub) {
ub = new_ub;
}
break;
}
// var <= UB. Same tag
case NLBound::UB: {
if (new_ub < ub) {
ub = new_ub;
}
break;
}
// LB <= var. Update tag
case NLBound::LB: {
assert(lb <= new_ub);
tag = LB_UB;
ub = new_ub;
break;
}
// No bound. Update tag
case NLBound::NONE: {
tag = UB;
ub = new_ub;
break;
}
// LB = var = UB. Should not happen
case NLBound::EQ: {
should_not_happen(
"Updating a bound already set to \"equals\". We only allow tightening update.");
}
}
}
/** Update equal bound */
void NLBound::update_eq(double new_eq) {
switch (tag) {
// LB <= var <= UB. Update tag
case NLBound::LB_UB: {
assert(lb <= new_eq && new_eq <= ub);
tag = EQ;
lb = new_eq;
ub = new_eq;
}
// var <= UB. Update tag
case NLBound::UB: {
assert(new_eq <= ub);
tag = EQ;
lb = new_eq;
ub = new_eq;
}
// LB <= var. Update tag
case NLBound::LB: {
assert(lb <= new_eq);
tag = EQ;
lb = new_eq;
ub = new_eq;
}
// No bound. Update tag
case NLBound::NONE: {
tag = EQ;
lb = new_eq;
ub = new_eq;
}
// LB = var = UB. Can happen only if we "update" with the same bound.
case NLBound::EQ: {
assert(lb == new_eq);
}
}
}
/** Printing with a name. */
ostream& NLBound::print_on(ostream& os, string vname) const {
switch (tag) {
case LB_UB: {
os << "0 " << lb << " " << ub << " # " << lb << " =< " << vname << " =< " << ub;
break;
}
case UB: {
os << "1 " << ub << " # " << vname << " =< " << ub;
break;
}
case LB: {
os << "2 " << lb << " # " << lb << " =< " << vname;
break;
}
case NONE: {
os << "3"
<< " # No constraint";
break;
}
case EQ: {
os << "4 " << lb << " # " << vname << " = " << lb;
break;
}
}
return os;
}
/** Default printing */
ostream& NLBound::print_on(ostream& os) const {
print_on(os, "body");
return os;
}
/* *** *** *** NLVar *** *** *** */
NLVar NLVar::copy_with_bound(NLBound bound) const {
NLVar v = NLVar(*this); // copy constructor
v.bound = bound;
return v;
}
/* *** *** *** NLArray *** *** *** */
/* *** *** *** NLToken *** *** *** */
const char* NLToken::get_name(OpCode oc) {
switch (oc) {
case OpCode::OPPLUS:
return "OPPLUS";
case OpCode::OPMINUS:
return "OPMINUS";
case OpCode::OPMULT:
return "OPMULT";
case OpCode::OPDIV:
return "OPDIV";
case OpCode::OPREM:
return "OPREM";
case OpCode::OPPOW:
return "OPPOW";
case OpCode::OPLESS:
return "OPLESS";
case OpCode::FLOOR:
return "FLOOR";
case OpCode::CEIL:
return "CEIL";
case OpCode::ABS:
return "ABS";
case OpCode::OPUMINUS:
return "OPUMINUS";
case OpCode::OPOR:
return "OPOR";
case OpCode::OPAND:
return "OPAND";
case OpCode::LT:
return "LT";
case OpCode::LE:
return "LE";
case OpCode::EQ:
return "EQ";
case OpCode::GE:
return "GE";
case OpCode::GT:
return "GT";
case OpCode::NE:
return "NE";
case OpCode::OPNOT:
return "OPNOT";
case OpCode::OPIFnl:
return "OPIFnl";
case OpCode::OP_tanh:
return "OP_tanh";
case OpCode::OP_tan:
return "OP_tan";
case OpCode::OP_sqrt:
return "OP_sqrt";
case OpCode::OP_sinh:
return "OP_sinh";
case OpCode::OP_sin:
return "OP_sin";
case OpCode::OP_log10:
return "OP_log10";
case OpCode::OP_log:
return "OP_log";
case OpCode::OP_exp:
return "OP_exp";
case OpCode::OP_cosh:
return "OP_cosh";
case OpCode::OP_cos:
return "OP_cos";
case OpCode::OP_atanh:
return "OP_atanh";
case OpCode::OP_atan2:
return "OP_atan2";
case OpCode::OP_atan:
return "OP_atan";
case OpCode::OP_asinh:
return "OP_asinh";
case OpCode::OP_asin:
return "OP_asin";
case OpCode::OP_acosh:
return "OP_acosh";
case OpCode::OP_acos:
return "OP_acos";
case OpCode::OPintDIV:
return "OPintDIV";
case OpCode::OPprecision:
return "OPprecision";
case OpCode::OPround:
return "OPround";
case OpCode::OPtrunc:
return "OPtrunc";
case OpCode::OPATLEAST:
return "OPATLEAST";
case OpCode::OPATMOST:
return "OPATMOST";
case OpCode::OPPLTERM:
return "OPPLTERM";
case OpCode::OPIFSYM:
return "OPIFSYM";
case OpCode::OPEXACTLY:
return "OPEXACTLY";
case OpCode::OPNOTATLEAST:
return "OPNOTATLEAST";
case OpCode::OPNOTATMOST:
return "OPNOTATMOST";
case OpCode::OPNOTEXACTLY:
return "OPNOTEXACTLY";
case OpCode::OPIMPELSE:
return "OPIMPELSE";
case OpCode::OP_IFF:
return "OP_IFF";
case OpCode::OPSOMESAME:
return "OPSOMESAME";
case OpCode::OP1POW:
return "OP1POW";
case OpCode::OP2POW:
return "OP2POW";
case OpCode::OPCPOW:
return "OPCPOW";
case OpCode::OPFUNCALL:
return "OPFUNCALL";
case OpCode::OPNUM:
return "OPNUM";
case OpCode::OPHOL:
return "OPHOL";
case OpCode::OPVARVAL:
return "OPVARVAL";
case OpCode::N_OPS:
return "N_OPS";
default:
assert(false);
return NULL;
}
};
const char* NLToken::get_name(MOpCode moc) {
switch (moc) {
case MOpCode::MINLIST:
return "MINLIST";
case MOpCode::MAXLIST:
return "MAXLIST";
case MOpCode::OPSUMLIST:
return "OPSUMLIST";
case MOpCode::OPCOUNT:
return "OPCOUNT";
case MOpCode::OPNUMBEROF:
return "OPNUMBEROF";
case MOpCode::OPNUMBEROFs:
return "OPNUMBEROFs";
case MOpCode::ANDLIST:
return "ANDLIST";
case MOpCode::ORLIST:
return "ORLIST";
case MOpCode::OPALLDIFF:
return "OPALLDIFF";
default:
assert(false);
return NULL;
}
}
NLToken NLToken::n(double value) {
NLToken tok;
tok.kind = Kind::NUMERIC;
tok.numeric_value = value;
return tok;
}
NLToken NLToken::v(string vname) {
NLToken tok;
tok.kind = Kind::VARIABLE;
tok.str = vname;
return tok;
}
NLToken NLToken::o(OpCode opc) {
NLToken tok;
tok.kind = Kind::OP;
tok.oc = opc;
return tok;
}
NLToken NLToken::mo(MOpCode mopc, int nb) {
NLToken tok;
tok.kind = Kind::MOP;
tok.moc = mopc;
tok.nb_args = nb;
return tok;
}
bool NLToken::is_variable() const { return kind == VARIABLE; }
bool NLToken::is_constant() const { return kind == NUMERIC; }
ostream& NLToken::print_on(ostream& os, const NLFile& nl_file) const {
switch (kind) {
case Kind::NUMERIC: {
os << "n" << numeric_value;
break;
}
case Kind::VARIABLE: {
os << "v" << nl_file.variable_indexes.at(str) << " # " << str;
break;
}
case Kind::STRING: {
should_not_happen("NL string token (Kind::STRING) not implemented");
}
case Kind::FUNCALL: {
should_not_happen("NL function call token (Kind::FUNCALL) not implemented");
}
case Kind::OP: {
os << "o" << oc << " # " << get_name(oc);
break;
}
case Kind::MOP: {
os << "o" << moc << " # " << get_name(moc) << " " << nb_args << endl;
os << nb_args;
break;
}
default:
should_not_happen("Unknown token kind: " << kind);
}
return os;
}
/* *** *** *** NLAlgCons *** *** *** */
/** Method to build the var_coeff vector. */
void NLAlgCons::set_jacobian(const vector<string>& vnames, const vector<double>& coeffs,
NLFile* nl_file) {
assert(vnames.size() == coeffs.size());
for (int i = 0; i < vnames.size(); ++i) {
string vn = vnames[i];
nl_file->variables.at(vn).jacobian_count++;
jacobian.push_back(pair<string, double>(vn, coeffs[i]));
}
}
/** A constraint is considered linear if the expression_graph is empty. */
bool NLAlgCons::is_linear() const { return expression_graph.empty(); }
/** Printing. */
ostream& NLAlgCons::print_on(ostream& os, const NLFile& nl_file) const {
int idx = nl_file.constraint_indexes.at(name);
// Print the 'C' segment: if no expression graph, print "n0".
os << "C" << idx << " # Non linear part of " << name << endl;
if (expression_graph.empty()) {
os << "n0 # No non linear part coded as the value '0'" << endl;
} else {
for (auto t : expression_graph) {
t.print_on(os, nl_file);
os << endl;
}
}
// Print the 'J' segment if present.
if (!jacobian.empty()) {
os << "J" << idx << " " << jacobian.size() << " # Linear part of " << name << endl;
for (auto& vn_coef : jacobian) {
os << nl_file.variable_indexes.at(vn_coef.first) << " " << vn_coef.second << " # "
<< vn_coef.first << endl;
}
}
return os;
}
/* *** *** *** NLLogicalCons *** *** *** */
/** Printing. */
ostream& NLLogicalCons::print_on(ostream& os, const NLFile& nl_file) const {
os << "L" << index << " # Logical constraint " << name << endl;
for (auto t : expression_graph) {
t.print_on(os, nl_file);
os << endl;
}
return os;
}
/* *** *** *** NLHeader *** *** *** */
/** Printing the header.
* The header is composed of then lines that we describe as we proceed.
* A '#' starts a comment until the end of the line. However, it cannot be a line on its own!*/
ostream& NLHeader::print_on(ostream& os, const NLFile& nl_file) const {
// 1st line:
// 'g': file will be in text format
// other numbers: as given in the doc (no other explanation...)
os << "g3 1 1 0" << endl;
// 2nd line:
os << nl_file.variables.size() << " " // Total number of variables
<< nl_file.constraints.size()
<< " " // Total number of algebraic constraint (including 'range' and 'eq')
<< 1 << " " // Always 1 objective
<< nl_file.nb_alg_cons_range << " " // Number of algebraic range constraints
<< nl_file.nb_alg_cons_eq << " " // Number of algebraic eq constraints
<< nl_file.logical_constraints.size() << " " // Number of logical constraints
<< "# Total nb of: variables, algebraic constraints, objectives, ranges, eqs, logical "
"constraints"
<< endl;
// 3rd line: Nonlinear and complementary information
os << nl_file.cnames_nl_general.size() << " " // Non linear constraints
<< (nl_file.objective.is_linear() ? 0 : 1) << " " // Non linear objective
<< "# Nb of nonlinear constraints, nonlinar objectives." << endl;
/* This was found in the online source of the ASL parser, but is not produce in our ampl tests.
* If needed, should be put on the same line
<< nb_complementarity_linear_conditions << " "
<< nb_complementarity_nonlinear_conditions << " "
<< nb_complementarity_items_double_inequalities << " "
<< nb_complemented_vars_non_zero_lowerbound << " "
<< "Nb of complementary: linear & nonlinear conditions, double inequalities, vars with non-0 lower
bound."
<< endl;
*/
// 4th line: Network constraints
os << nl_file.cnames_nl_network.size() << " " // Number of nonlinear network constraints
<< nl_file.cnames_lin_network.size() << " " // Number of linear network constraints
<< "# Nb of network constraints: nonlinear, linear." << endl;
// 5th line: nonlinear variables:
os << nl_file.nlvc() << " " // Nb of nonlinear vars in constraints
<< nl_file.nlvo() << " " // Nb of nonlinear vars in objectives
<< nl_file.nlvb() << " " // Nb of nonlinear vars in both
<< "# Nb of non linear vars in: constraints, objectives, both." << endl;
// 6th line:
os << nl_file.nwv() << " " // Nb of linear network vars
<< "0"
<< " " // Nb of functions. Not Implemented
<< "0 1 "
<< "# Nb of: linear network vars, functions. Floating point arithmetic mode (TEXT == 0). "
"Flag: if 1, add .sol suffixe."
<< endl;
// 7th line: discrete variables
os << nl_file.nbv() << " " // Nb of linear binary vars
<< nl_file.niv() << " " // Nb of linear integer vars
<< nl_file.nlvbi() << " " // Nb of nonlinear integer vars in both
<< nl_file.nlvci() << " " // Nb of nonlinear integer vars in constraints only
<< nl_file.nlvoi() << " " // Nb of nonlinear integer vars in objectives only
<< "# Nb of linear vars: binary, integer (non binary). "
<< "Nb of nonlinear integer vars in: both, constraints only, objectives only." << endl;
// 8th line: non zeros
os << nl_file.jacobian_count() << " " // Nb of nonzero in jacobian
<< nl_file.objective.gradient_count() << " " // Nb of nonzero in gradient
<< "# Nb of non zeros in: jacobian, objective gradients." << endl;
// 9th line: name length. Our tests always produce 0...
os << "0"
<< " " // Max constraint name length (??)
<< "0"
<< " " // Max var name length (??)
<< "# Longest name among: contraints' name, vars' name." << endl;
// 10th line: common expressions. Not enough infor for now...
os << "0"
<< " " // Nb common exprs in both
<< "0"
<< " " // Nb common exprs in constraints only
<< "0"
<< " " // Nb common exprs in objectives only
<< "0"
<< " " // Nb common exprs in single constraint only
<< "0"
<< " " // Nb common exprs in single objectives only
<< "# Nb of common expressions in: both, constraints only, objectives only, single "
"constraint, single objective.";
return os;
}
/* *** *** *** NLObjective *** *** *** */
/** Gradient count. */
int NLObjective::gradient_count() const { return gradient.size(); }
/** Set the gradient. */
void NLObjective::set_gradient(const vector<string>& vnames, const vector<double>& coeffs) {
assert(vnames.size() == coeffs.size());
for (int i = 0; i < vnames.size(); ++i) {
string vn = vnames[i];
gradient.push_back(pair<string, double>(vn, coeffs[i]));
}
}
/** A objective is considered as linear if its expression graph is non empty. */
bool NLObjective::is_defined() const { return minmax != UNDEF; }
bool NLObjective::is_linear() const { return expression_graph.empty(); }
bool NLObjective::is_optimisation() const { return minmax >= MINIMIZE; }
/** Printing. */
ostream& NLObjective::print_on(ostream& os, const NLFile& nl_file) const {
if (minmax != UNDEF) {
if (minmax == SATISFY) {
os << "O0 0 # Satisfy objectif implemented as 'minimize 0'" << endl;
os << "n0" << endl;
} else {
os << "O0 " << minmax << " # Objectif (0: minimize, 1: maximize)" << endl;
if (expression_graph.empty()) {
os << "n0 # No expression graph" << endl;
} else {
for (auto& tok : expression_graph) {
tok.print_on(os, nl_file) << endl;
}
}
// Print gradient
if (!gradient.empty()) {
os << "G0 " << gradient.size() << " # Objective Linear part" << endl;
for (auto& vn_coef : gradient) {
os << nl_file.variable_indexes.at(vn_coef.first) << " " << vn_coef.second << " # "
<< vn_coef.first << endl;
}
}
}
}
return os;
}
} // namespace MiniZinc