/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include /** * A NL File reprensentation. * The purpose of this file is mainly to be a writer. * Most of the information come from 'nlwrite.pdf' * Note: * * a character '#' starts a comment until the end of line. * * A new line is '\n' */ using namespace std; namespace MiniZinc { /** *** *** *** Helpers *** *** *** **/ /** Create a string representing the name (and unique identifier) from an identifier. */ string NLFile::getVarName(const Id* id) { stringstream os; if (id->idn() != -1) { os << "X_INTRODUCED_" << id->idn() << "_"; } else if (id->v().size() != 0) { os << id->v(); } string name = os.str(); return name; } /** Create a string representing the name (and unique identifier) of a variable from a variable * declaration. */ string NLFile::getVarName(const VarDecl& vd) { return getVarName(vd.id()); } /** Create a string representing the name (and unique identifier) of a constraint from a specific * call expression. */ string NLFile::getConstraintName(const Call& c) { stringstream os; os << c.id() << "_" << static_cast(&c); // use the memory address as unique ID. string name = os.str(); return name; } /** Obtain the vector of an array, either from an identifier or an array litteral */ const ArrayLit& NLFile::getArrayLit(const Expression* e) { switch (e->eid()) { case Expression::E_ID: { return getArrayLit( e->cast()->decl()->e()); // Follow the pointer to the expression of the declaration } case Expression::E_ARRAYLIT: { const ArrayLit& al = *e->cast(); return al; } default: should_not_happen("Could not read array from expression."); } } /** Create a vector of double from a vector containing Expression being IntLit. */ vector NLFile::fromVecInt(const ArrayLit& v_int) { vector v = {}; for (unsigned int i = 0; i < v_int.size(); ++i) { double d = static_cast(v_int[i]->cast()->v().toInt()); v.push_back(d); } return v; } /** Create a vector of double from a vector containing Expression being FloatLit. */ vector NLFile::fromVecFloat(const ArrayLit& v_fp) { vector v = {}; for (unsigned int i = 0; i < v_fp.size(); ++i) { double d = v_fp[i]->cast()->v().toDouble(); v.push_back(d); } return v; } /** Create a vector of variable names from a vector containing Expression being identifier Id. */ vector NLFile::fromVecId(const ArrayLit& v_id) { vector v = {}; for (unsigned int i = 0; i < v_id.size(); ++i) { string s = getVarName(*(v_id[i]->cast()->decl())); v.push_back(s); } return v; } /** *** *** *** Phase 1: collecting data from MZN *** *** *** **/ /** Add a variable declaration in the NL File. * This function pre-analyse the declaration VarDecl, then delegate to addVarDeclInteger or * addVarDeclFloat. In flatzinc, arrays always have a rhs: the can always be replaced by their * definition (following the pointer starting at the ID) Hence, we do not reproduce arrays in the NL * file. */ void NLFile::addVarDecl(const VarDecl& vd, const TypeInst& ti, const Expression& rhs) { // Get the name string name = getVarName(vd); // Discriminate according to the type: if (ti.isEnum()) { should_not_happen("Enum type in the flatzinc"); } else if (ti.isarray()) { DEBUG_MSG(" Definition of array " << name << " is not reproduced in nl."); // Look for the annotation "output_array" for (ExpressionSetIter it = vd.ann().begin(); it != vd.ann().end(); ++it) { Call* c = (*it)->dynamicCast(); if (c != nullptr && c->id() == (constants().ann.output_array)) { NLArray array; array.name = name; array.isInteger = ti.type().bt() == Type::BT_INT; // Search the 'annotation' array const ArrayLit& aa = getArrayLit(c->arg(0)); for (int i = 0; i < aa.size(); ++i) { IntSetVal* r = aa[i]->cast()->isv(); stringstream ss; ss << r->min().toInt() << ".." << r->max().toInt(); array.dimensions.push_back(ss.str()); } // Search the 'real' array. Items can be an identifier or a litteral. const ArrayLit& ra = getArrayLit(&rhs); for (int i = 0; i < ra.size(); ++i) { NLArray::Item item; if (ra[i]->isa()) { item.variable = getVarName(ra[i]->cast()); } else if (ra[i]->isa()) { assert(array.isInteger); item.value = static_cast(ra[i]->cast()->v().toInt()); } else { assert(!array.isInteger); // Floating point item.value = ra[i]->cast()->v().toDouble(); } array.items.push_back(item); } outputArrays.push_back(array); break; } } } else { // Check if the variable needs to be reported bool toReport = vd.ann().contains(constants().ann.output_var); DEBUG_MSG(" '" << name << "' to be reported? " << toReport); // variable declaration const Type& type = ti.type(); const Expression* domain = ti.domain(); // Check the type: integer or floatin point assert(type.isvarint() || type.isvarfloat()); bool isvarint = type.isvarint(); // Call the declaration function according to the type // Check the domain and convert if not null. // Note: we directly jump to the specialized Int/Float set, going through the set literal if (isvarint) { // Integer IntSetVal* isv = nullptr; if (domain != nullptr) { isv = domain->cast()->isv(); } addVarDeclInteger(name, isv, toReport); } else { // Floating point FloatSetVal* fsv = nullptr; if (domain != nullptr) { fsv = domain->cast()->fsv(); } addVarDeclFloat(name, fsv, toReport); } } } /** Add an integer variable declaration to the NL File. */ void NLFile::addVarDeclInteger(const string& name, const IntSetVal* isv, bool toReport) { // Check that we do not have naming conflict assert(variables.find(name) == variables.end()); // Check the domain. NLBound bound; if (isv == nullptr) { bound = NLBound::makeNoBound(); } else if (isv->size() == 1) { double lb = static_cast(isv->min(0).toInt()); double ub = static_cast(isv->max(0).toInt()); bound = NLBound::makeBounded(lb, ub); } else { should_not_happen("Range: switch on mzn_opt_only_range_domains" << endl); } // Create the variable and update the NLFile NLVar v = NLVar(name, true, toReport, bound); variables[name] = v; } /** Add a floating point variable declaration to the NL File. */ void NLFile::addVarDeclFloat(const string& name, const FloatSetVal* fsv, bool toReport) { // Check that we do not have naming conflict assert(variables.find(name) == variables.end()); // Check the domain. NLBound bound; if (fsv == nullptr) { bound = NLBound::makeNoBound(); } else if (fsv->size() == 1) { double lb = fsv->min(0).toDouble(); double ub = fsv->max(0).toDouble(); bound = NLBound::makeBounded(lb, ub); } else { should_not_happen("Range: switch on mzn_opt_only_range_domains" << std::endl); } // Create the variable and update the NLFile NLVar v = NLVar(name, false, toReport, bound); variables[name] = v; } // --- --- --- Constraints analysis /** Dispatcher for constraint analysis. */ void NLFile::analyseConstraint(const Call& c) { // ID of the call auto id = c.id(); // Constants for integer builtins auto consint = constants().ids.int_; // Constants for floating point builtins auto consfp = constants().ids.float_; // Integer linear predicates if (id == consint.lin_eq) { consint_lin_eq(c); } else if (id == consint.lin_le) { consint_lin_le(c); } else if (id == consint.lin_ne) { consint_lin_ne(c); } // Integer predicates else if (id == consint.le) { consint_le(c); } else if (id == consint.eq) { consint_eq(c); } else if (id == consint.ne) { consint_ne(c); } // Integer binary operators else if (id == consint.times) { consint_times(c); } else if (id == consint.div) { consint_div(c); } else if (id == consint.mod) { consint_mod(c); } else if (id == consint.plus) { consint_plus(c); } else if (id == "int_pow") { int_pow(c); } else if (id == "int_max") { int_max(c); } else if (id == "int_min") { int_min(c); } // Integer unary operators else if (id == "int_abs") { int_abs(c); } // Floating point linear predicates else if (id == consfp.lin_eq) { consfp_lin_eq(c); } else if (id == consfp.lin_le) { consfp_lin_le(c); } else if (id == consfp.lin_lt) { consfp_lin_lt(c); } else if (id == consfp.lin_ne) { consfp_lin_ne(c); } // Floating point predicates else if (id == consfp.lt) { consfp_lt(c); } else if (id == consfp.le) { consfp_le(c); } else if (id == consfp.eq) { consfp_eq(c); } else if (id == consfp.ne) { consfp_ne(c); } // Floating point binary operators else if (id == consfp.plus) { consfp_plus(c); } else if (id == consfp.minus) { consfp_minus(c); } else if (id == consfp.div) { consfp_div(c); } else if (id == consfp.times) { consfp_times(c); } else if (id == "float_pow") { float_pow(c); } else if (id == "float_max") { float_max(c); } else if (id == "float_min") { float_min(c); } // Floating point unary operators else if (id == "float_abs") { float_abs(c); } else if (id == "float_acos") { float_acos(c); } else if (id == "float_acosh") { float_acosh(c); } else if (id == "float_asin") { float_asin(c); } else if (id == "float_asinh") { float_asinh(c); } else if (id == "float_atan") { float_atan(c); } else if (id == "float_atanh") { float_atanh(c); } else if (id == "float_cos") { float_cos(c); } else if (id == "float_cosh") { float_cosh(c); } else if (id == "float_exp") { float_exp(c); } else if (id == "float_ln") { float_ln(c); } else if (id == "float_log10") { float_log10(c); } else if (id == "float_log2") { float_log2(c); } else if (id == "float_sqrt") { float_sqrt(c); } else if (id == "float_sin") { float_sin(c); } else if (id == "float_sinh") { float_sinh(c); } else if (id == "float_tan") { float_tan(c); } else if (id == "float_tanh") { float_tanh(c); } // Other else if (id == "int2float") { int2float(c); } // Domain else if (id == consfp.in) { should_not_happen("Ignore for now: constraint 'float in ' not implemented"); } else if (id == consfp.dom) { should_not_happen("Ignore for now: constraint 'float dom ' not implemented"); } // Grey area else if (id == consint.lt) { should_not_happen("'int lt'"); } else if (id == consint.gt) { should_not_happen("'int gt'"); } else if (id == consint.ge) { should_not_happen("'int ge'"); } else if (id == consint.minus) { should_not_happen("'int minus'"); } else if (id == consfp.gt) { should_not_happen("float gt'"); } else if (id == consfp.ge) { should_not_happen("float ge'"); } // Not implemented else { should_not_happen("Builtins " << c.id() << " not implemented or not recognized."); } } // --- --- --- Helpers /** Create a token from an expression representing a variable */ NLToken NLFile::getTokenFromVarOrInt(const Expression* e) { if (e->type().isPar()) { // Constant double value = static_cast(e->cast()->v().toInt()); return NLToken::n(value); } // Variable VarDecl& vd = *(e->cast()->decl()); string n = getVarName(vd); return NLToken::v(n); } /** Create a token from an expression representing either a variable or a floating point numeric * value. */ NLToken NLFile::getTokenFromVarOrFloat(const Expression* e) { if (e->type().isPar()) { // Constant double value = e->cast()->v().toDouble(); return NLToken::n(value); } // Variable VarDecl& vd = *(e->cast()->decl()); string n = getVarName(vd); return NLToken::v(n); } /** Create a token from an expression representing either a variable. */ NLToken NLFile::getTokenFromVar(const Expression* e) { assert(!e->type().isPar()); // Variable VarDecl& vd = *(e->cast()->decl()); string n = getVarName(vd); return NLToken::v(n); } /** Update an expression graph (only by appending token) with a linear combination * of coefficients and variables. Count as "non linear" for the variables occuring here. */ void NLFile::makeSigmaMult(vector& expressionGraph, const vector& coeffs, const vector& vars) { assert(coeffs.size() == vars.size()); assert(coeffs.size() >= 2); // Create a sum of products. // WARNING: OPSUMLIST needs at least 3 operands! We need a special case for the sum of 2. if (coeffs.size() == 2) { expressionGraph.push_back(NLToken::o(NLToken::OpCode::OPPLUS)); } else { expressionGraph.push_back(NLToken::mo(NLToken::MOpCode::OPSUMLIST, coeffs.size())); } // Component of the sum. Simplify multiplication by one. for (unsigned int i = 0; i < coeffs.size(); ++i) { // Product if coeff !=1 if (coeffs[i] != 1) { expressionGraph.push_back(NLToken::o(NLToken::OpCode::OPMULT)); expressionGraph.push_back(NLToken::n(coeffs[i])); } // Set the variable as "non linear" expressionGraph.push_back(NLToken::v(vars[i])); } } // --- --- --- Linear Builders /** Create a linear constraint [coeffs] *+ [vars] = value. */ void NLFile::linconsEq(const Call& c, const vector& coeffs, const vector& vars, const NLToken& value) { // Create the Algebraic Constraint and set the data NLAlgCons cons; // Get the name of the constraint string cname = getConstraintName(c); cons.name = cname; if (value.isConstant()) { // Create the bound of the constraint NLBound bound = NLBound::makeEqual(value.numericValue); cons.range = bound; // No non linear part: leave the expression graph empty. // Linear part: set the jacobian cons.setJacobian(vars, coeffs, this); } else { // Create the bound of the constraint = 0 and change the Jacobian to incorporate a -1 on the // variable in 'value' NLBound bound = NLBound::makeEqual(0); cons.range = bound; // No non linear part: leave the expression graph empty. // Linear part: set the jacobian vector coeffs_(coeffs); coeffs_.push_back(-1); vector vars_(vars); vars_.push_back(value.str); cons.setJacobian(vars_, coeffs_, this); } // Add the constraint in our mapping constraints[cname] = cons; } /** Create a linear constraint [coeffs] *+ [vars] <= value. */ void NLFile::linconsLe(const Call& c, const vector& coeffs, const vector& vars, const NLToken& value) { // Create the Algebraic Constraint and set the data NLAlgCons cons; // Get the name of the constraint string cname = getConstraintName(c); cons.name = cname; if (value.isConstant()) { // Create the bound of the constraint NLBound bound = NLBound::makeUBBounded(value.numericValue); cons.range = bound; // No non linear part: leave the expression graph empty. // Linear part: set the jacobian cons.setJacobian(vars, coeffs, this); } else { // Create the bound of the constraint = 0 and change the Jacobian to incorporate a -1 on the // variable in 'value' NLBound bound = NLBound::makeUBBounded(0); cons.range = bound; // No non linear part: leave the expression graph empty. // Linear part: set the jacobian vector coeffs_(coeffs); coeffs_.push_back(-1); vector vars_(vars); vars_.push_back(value.str); cons.setJacobian(vars_, coeffs_, this); } // Add the constraint in our mapping constraints[cname] = cons; } /** Create a linear logical constraint [coeffs] *+ [vars] PREDICATE value. * Use a generic comparison operator. * Warnings: - Creates a logical constraint * - Only use for conmparisons that cannot be expressed with '=' xor '<='. */ void NLFile::linconsPredicate(const Call& c, NLToken::OpCode oc, const vector& coeffs, const vector& vars, const NLToken& value) { // Create the Logical Constraint and set the data NLLogicalCons cons(logicalConstraints.size()); // Get the name of the constraint string cname = getConstraintName(c); cons.name = cname; // Create the expression graph (Note: no Jacobian associated with a logical constraint). // 1) Push the comparison operator, e.g. "!= operand1 operand2" cons.expressionGraph.push_back(NLToken::o(oc)); // 2) Operand1 := sum of product makeSigmaMult(cons.expressionGraph, coeffs, vars); // 3) Operand 2 := value. cons.expressionGraph.push_back(value); // Store the constraint logicalConstraints.push_back(cons); } // --- --- --- Non Linear Builders // For predicates, uses 2 variables or literals: x := c.arg(0), y := c.arg(1) // x PREDICATE y // For operations, uses 3 variables or literals: x := c.arg(0), y := c.arg(1), and z := c.arg(2). // x OPERATOR y = z /** Create a non linear constraint x = y * Use the jacobian and the bound on constraint to translate into x - y = 0 * Simply update the bound if one is a constant. */ void NLFile::nlconsEq(const Call& c, const NLToken& x, const NLToken& y) { if (x.kind != y.kind) { if (x.isConstant()) { // Update bound on y double value = x.numericValue; NLVar& v = variables.at(y.str); v.bound.updateEq(value); } else { // Update bound on x double value = y.numericValue; NLVar& v = variables.at(x.str); v.bound.updateEq(value); } } else if (x.str != y.str) { // both must be variables anyway. assert(x.isVariable() && y.isVariable()); // Create the Algebraic Constraint and set the data NLAlgCons cons; // Get the name of the constraint string cname = getConstraintName(c); cons.name = cname; // Create the bound of the constraint: equal 0 NLBound bound = NLBound::makeEqual(0); cons.range = bound; // Create the jacobian vector coeffs = {1, -1}; vector vars = {x.str, y.str}; cons.setJacobian(vars, coeffs, this); // Store the constraint constraints[cname] = cons; } } /** Create a non linear constraint x <= y * Use the jacobian and the bound on constraint to translate into x - y <= 0 * Simply update the bound if one is a constant. */ void NLFile::nlconsLe(const Call& c, const NLToken& x, const NLToken& y) { if (x.kind != y.kind) { if (x.isConstant()) { // Update lower bound on y double value = x.numericValue; NLVar& v = variables.at(y.str); v.bound.updateLB(value); } else { // Update upper bound on x double value = y.numericValue; NLVar& v = variables.at(x.str); v.bound.updateUB(value); } } else if (x.str != y.str) { // both must be variables anyway. assert(x.isVariable() && y.isVariable()); // Create the Algebraic Constraint and set the data NLAlgCons cons; // Get the name of the constraint string cname = getConstraintName(c); cons.name = cname; // Create the bound of the constraint: <= 0 NLBound bound = NLBound::makeUBBounded(0); cons.range = bound; // Create the jacobian vector coeffs = {1, -1}; vector vars = {x.str, y.str}; cons.setJacobian(vars, coeffs, this); // Store the constraint constraints[cname] = cons; } } /** Create a non linear constraint with a predicate: x PREDICATE y * Use a generic comparison operator. * Warnings: - Creates a logical constraint * - Only use for conmparisons that cannot be expressed with '=' xor '<='. */ void NLFile::nlconsPredicate(const Call& c, NLToken::OpCode oc, const NLToken& x, const NLToken& y) { // Create the Logical Constraint and set the data NLLogicalCons cons(logicalConstraints.size()); // Get the name of the constraint string cname = getConstraintName(c); cons.name = cname; // Create the expression graph cons.expressionGraph.push_back(NLToken::o(oc)); cons.expressionGraph.push_back(x); cons.expressionGraph.push_back(y); // Store the constraint logicalConstraints.push_back(cons); } /** Create a non linear constraint with a binary operator: x OPERATOR y = z */ void NLFile::nlconsOperatorBinary(const Call& c, NLToken::OpCode oc, const NLToken& x, const NLToken& y, const NLToken& z) { // Create the Algebraic Constraint and set the data NLAlgCons cons; // Get the name of the constraint string cname = getConstraintName(c); cons.name = cname; // If z is a constant, use the bound, else use the jacobian if (z.isConstant()) { // Create the bound of the constraint with the numeric value of z NLBound bound = NLBound::makeEqual(z.numericValue); cons.range = bound; } else { // Else, use a constraint bound = 0 and use the jacobian to substract z from the result. // Create the bound of the constraint to 0 NLBound bound = NLBound::makeEqual(0); cons.range = bound; vector coeffs = {}; vector vars = {}; // If x is a variable different from y (and must be different from z), give it 0 for the linear // part if (x.isVariable() && x.str != y.str) { assert(x.str != z.str); coeffs.push_back(0); vars.push_back(x.str); } // Same as above for y. if (y.isVariable()) { assert(y.str != z.str); coeffs.push_back(0); vars.push_back(y.str); } // z is a variable whose value is substracted from the result coeffs.push_back(-1); vars.push_back(z.str); // Finish jacobian cons.setJacobian(vars, coeffs, this); } // Create the expression graph using the operand code 'oc' cons.expressionGraph.push_back(NLToken::o(oc)); cons.expressionGraph.push_back(x); cons.expressionGraph.push_back(y); // Store the constraint constraints[cname] = cons; } /** Create a non linear constraint with a binary operator: x OPERATOR y = z. * OPERATOR is now a Multiop, with a count of 2 (so the choice of the method to use depends on the * LN implementation) */ void NLFile::nlconsOperatorBinary(const Call& c, NLToken::MOpCode moc, const NLToken& x, const NLToken& y, const NLToken& z) { // Create the Algebraic Constraint and set the data NLAlgCons cons; // Get the name of the constraint string cname = getConstraintName(c); cons.name = cname; // If z is a constant, use the bound, else use the jacobian if (z.isConstant()) { // Create the bound of the constraint with the numeric value of z NLBound bound = NLBound::makeEqual(z.numericValue); cons.range = bound; } else { // Else, use a constraint bound = 0 and use the jacobian to substract vres from the result. // Create the bound of the constraint to 0 NLBound bound = NLBound::makeEqual(0); cons.range = bound; vector coeffs = {}; vector vars = {}; // If x is a variable different from y (and must be different from z), give it 0 for the linear // part if (x.isVariable() && x.str != y.str) { assert(x.str != z.str); coeffs.push_back(0); vars.push_back(x.str); } // Same as above for y. if (y.isVariable()) { assert(y.str != z.str); coeffs.push_back(0); vars.push_back(y.str); } // z is a variable whose value is substracted from the result coeffs.push_back(-1); vars.push_back(z.str); // Finish jacobian cons.setJacobian(vars, coeffs, this); } // Create the expression graph using the operand code 'oc' cons.expressionGraph.push_back(NLToken::mo(moc, 2)); cons.expressionGraph.push_back(x); cons.expressionGraph.push_back(y); // Store the constraint constraints[cname] = cons; } /** Create a non linear constraint with an unary operator: OPERATOR x = y */ void NLFile::nlconsOperatorUnary(const Call& c, NLToken::OpCode oc, const NLToken& x, const NLToken& y) { // Create the Algebraic Constraint and set the data NLAlgCons cons; // Get the name of the constraint string cname = getConstraintName(c); cons.name = cname; // If y is a constant, use the bound, else use the jacobian if (y.isConstant()) { // Create the bound of the constraint with the numeric value of y NLBound bound = NLBound::makeEqual(y.numericValue); cons.range = bound; } else { // Else, use a constraint bound = 0 and use the jacobian to substract y from the result. // Create the bound of the constraint to 0 NLBound bound = NLBound::makeEqual(0); cons.range = bound; vector coeffs = {}; vector vars = {}; // If x is a variable (must be different from y), give it '0' for the linear part if (x.isVariable()) { assert(x.str != y.str); coeffs.push_back(0); vars.push_back(x.str); } // z is a variable whose value is substracted from the result coeffs.push_back(-1); vars.push_back(y.str); // Finish jacobian cons.setJacobian(vars, coeffs, this); } // Create the expression graph using the operand code 'oc' cons.expressionGraph.push_back(NLToken::o(oc)); cons.expressionGraph.push_back(x); // Store the constraint constraints[cname] = cons; } /** Create a non linear constraint, specialized for log2 unary operator: Log2(x) = y */ void NLFile::nlconsOperatorUnaryLog2(const Call& c, const NLToken& x, const NLToken& y) { // Create the Algebraic Constraint and set the data NLAlgCons cons; // Get the name of the constraint string cname = getConstraintName(c); cons.name = cname; // If y is a constant, use the bound, else use the jacobian if (y.isConstant()) { // Create the bound of the constraint with the numeric value of y NLBound bound = NLBound::makeEqual(y.numericValue); cons.range = bound; } else { // Else, use a constraint bound = 0 and use the jacobian to substract vres from the result. // Create the bound of the constraint to 0 NLBound bound = NLBound::makeEqual(0); cons.range = bound; vector coeffs = {}; vector vars = {}; // If x is a variable (must be different from y), give it '0' for the linear part if (x.isVariable()) { assert(x.str != y.str); coeffs.push_back(0); vars.push_back(x.str); } // z is a variable whose value is substracted from the result coeffs.push_back(-1); vars.push_back(y.str); // Finish jacobian cons.setJacobian(vars, coeffs, this); } // Create the expression graph with log2(x) = ln(x)/ln(2) cons.expressionGraph.push_back(NLToken::o(NLToken::OpCode::OPDIV)); cons.expressionGraph.push_back(NLToken::o(NLToken::OpCode::OP_log)); cons.expressionGraph.push_back(x); cons.expressionGraph.push_back(NLToken::o(NLToken::OpCode::OP_log)); cons.expressionGraph.push_back(NLToken::n(2)); // Store the constraint constraints[cname] = cons; } // --- --- --- Integer Linear Constraints /** Linar constraint: [coeffs] *+ [vars] = value */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consint_lin_eq(const Call& c) { // Get the arguments arg0 (array0 = coeffs), arg1 (array = variables) and arg2 (value) vector coeffs = fromVecInt(getArrayLit(c.arg(0))); vector vars = fromVecId(getArrayLit(c.arg(1))); NLToken value = getTokenFromVarOrInt(c.arg(2)); // Create the constraint linconsEq(c, coeffs, vars, value); } /** Linar constraint: [coeffs] *+ [vars] =< value */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consint_lin_le(const Call& c) { // Get the arguments arg0 (array0 = coeffs), arg1 (array = variables) and arg2 (value) vector coeffs = fromVecInt(getArrayLit(c.arg(0))); vector vars = fromVecId(getArrayLit(c.arg(1))); NLToken value = getTokenFromVarOrInt(c.arg(2)); // Create the constraint linconsLe(c, coeffs, vars, value); } /** Linar constraint: [coeffs] *+ [vars] != value */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consint_lin_ne(const Call& c) { // Get the arguments arg0 (array0 = coeffs), arg1 (array = variables) and arg2 (value) vector coeffs = fromVecInt(getArrayLit(c.arg(0))); vector vars = fromVecId(getArrayLit(c.arg(1))); NLToken value = getTokenFromVarOrInt(c.arg(2)); // Create the constraint linconsPredicate(c, NLToken::OpCode::NE, coeffs, vars, value); } // --- --- --- Integer Non Linear Predicate Constraints /** Non linear constraint x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consint_eq(const Call& c) { nlconsEq(c, getTokenFromVarOrInt(c.arg(0)), getTokenFromVarOrInt(c.arg(1))); } /** Non linear constraint x <= y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consint_le(const Call& c) { nlconsLe(c, getTokenFromVarOrInt(c.arg(0)), getTokenFromVarOrInt(c.arg(1))); } /** Non linear constraint x != y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consint_ne(const Call& c) { nlconsPredicate(c, NLToken::OpCode::NE, getTokenFromVarOrInt(c.arg(0)), getTokenFromVarOrInt(c.arg(1))); } // --- --- --- Integer Non Linear Binary Operator Constraints /** Non linear constraint x + y = z */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consint_plus(const Call& c) { nlconsOperatorBinary(c, NLToken::OpCode::OPPLUS, getTokenFromVarOrInt(c.arg(0)), getTokenFromVarOrInt(c.arg(1)), getTokenFromVarOrInt(c.arg(2))); } /** Non linear constraint x * y = z */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consint_times(const Call& c) { nlconsOperatorBinary(c, NLToken::OpCode::OPMULT, getTokenFromVarOrInt(c.arg(0)), getTokenFromVarOrInt(c.arg(1)), getTokenFromVarOrInt(c.arg(2))); } /** Non linear constraint x / y = z */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consint_div(const Call& c) { nlconsOperatorBinary(c, NLToken::OpCode::OPDIV, getTokenFromVarOrInt(c.arg(0)), getTokenFromVarOrInt(c.arg(1)), getTokenFromVarOrInt(c.arg(2))); } /** Non linear constraint x mod y = z */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consint_mod(const Call& c) { nlconsOperatorBinary(c, NLToken::OpCode::OPREM, getTokenFromVarOrInt(c.arg(0)), getTokenFromVarOrInt(c.arg(1)), getTokenFromVarOrInt(c.arg(2))); } /** Non linear constraint x pow y = z */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::int_pow(const Call& c) { nlconsOperatorBinary(c, NLToken::OpCode::OPPOW, getTokenFromVarOrInt(c.arg(0)), getTokenFromVarOrInt(c.arg(1)), getTokenFromVarOrInt(c.arg(2))); } /** Non linear constraint max(x, y) = z */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::int_max(const Call& c) { nlconsOperatorBinary(c, NLToken::MOpCode::MAXLIST, getTokenFromVarOrInt(c.arg(0)), getTokenFromVarOrInt(c.arg(1)), getTokenFromVarOrInt(c.arg(2))); } /** Non linear constraint min(x, y) = z */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::int_min(const Call& c) { nlconsOperatorBinary(c, NLToken::MOpCode::MINLIST, getTokenFromVarOrInt(c.arg(0)), getTokenFromVarOrInt(c.arg(1)), getTokenFromVarOrInt(c.arg(2))); } // --- --- --- Integer Non Linear Unary Operator Constraints /** Non linear constraint abs x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::int_abs(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::ABS, getTokenFromVarOrInt(c.arg(0)), getTokenFromVarOrInt(c.arg(1))); } // --- --- --- Floating Point Linear Constraints /** Linar constraint: [coeffs] *+ [vars] = value */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consfp_lin_eq(const Call& c) { // Get the arguments arg0 (array0 = coeffs), arg1 (array = variables) and arg2 (value) vector coeffs = fromVecFloat(getArrayLit(c.arg(0))); vector vars = fromVecId(getArrayLit(c.arg(1))); NLToken value = getTokenFromVarOrFloat(c.arg(2)); // Create the constraint linconsEq(c, coeffs, vars, value); } /** Linar constraint: [coeffs] *+ [vars] = value */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consfp_lin_le(const Call& c) { // Get the arguments arg0 (array0 = coeffs), arg1 (array = variables) and arg2 (value) vector coeffs = fromVecFloat(getArrayLit(c.arg(0))); vector vars = fromVecId(getArrayLit(c.arg(1))); NLToken value = getTokenFromVarOrFloat(c.arg(2)); // Create the constraint linconsLe(c, coeffs, vars, value); } /** Linar constraint: [coeffs] *+ [vars] != value */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consfp_lin_ne(const Call& c) { // Get the arguments arg0 (array0 = coeffs), arg1 (array = variables) and arg2 (value) vector coeffs = fromVecFloat(getArrayLit(c.arg(0))); vector vars = fromVecId(getArrayLit(c.arg(1))); NLToken value = getTokenFromVarOrFloat(c.arg(2)); // Create the constraint linconsPredicate(c, NLToken::OpCode::NE, coeffs, vars, value); } /** Linar constraint: [coeffs] *+ [vars] < value */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consfp_lin_lt(const Call& c) { // Get the arguments arg0 (array0 = coeffs), arg1 (array = variables) and arg2 (value) vector coeffs = fromVecFloat(getArrayLit(c.arg(0))); vector vars = fromVecId(getArrayLit(c.arg(1))); NLToken value = getTokenFromVarOrFloat(c.arg(2)); // Create the constraint linconsPredicate(c, NLToken::OpCode::LT, coeffs, vars, value); } // --- --- --- Floating Point Non Linear Predicate Constraints /** Non linear constraint x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consfp_eq(const Call& c) { nlconsEq(c, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint x <= y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consfp_le(const Call& c) { nlconsLe(c, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint x != y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consfp_ne(const Call& c) { nlconsPredicate(c, NLToken::OpCode::NE, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint x < y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consfp_lt(const Call& c) { nlconsPredicate(c, NLToken::OpCode::LT, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } // --- --- --- Floating Point Non Linear Binary Operator Constraints /** Non linear constraint x + y = z */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consfp_plus(const Call& c) { nlconsOperatorBinary(c, NLToken::OpCode::OPPLUS, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1)), getTokenFromVarOrFloat(c.arg(2))); } /** Non linear constraint x - y = z */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consfp_minus(const Call& c) { nlconsOperatorBinary(c, NLToken::OpCode::OPMINUS, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1)), getTokenFromVarOrFloat(c.arg(2))); } /** Non linear constraint x * y = z */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consfp_times(const Call& c) { nlconsOperatorBinary(c, NLToken::OpCode::OPMULT, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1)), getTokenFromVarOrFloat(c.arg(2))); } /** Non linear constraint x / y = z */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consfp_div(const Call& c) { nlconsOperatorBinary(c, NLToken::OpCode::OPDIV, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1)), getTokenFromVarOrFloat(c.arg(2))); } /** Non linear constraint x mod y = z */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::consfp_mod(const Call& c) { nlconsOperatorBinary(c, NLToken::OpCode::OPREM, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1)), getTokenFromVarOrFloat(c.arg(2))); } /** Non linear constraint x pow y = z */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_pow(const Call& c) { nlconsOperatorBinary(c, NLToken::OpCode::OPPOW, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1)), getTokenFromVarOrFloat(c.arg(2))); } /** Non linear constraint max(x, y) = z */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_max(const Call& c) { nlconsOperatorBinary(c, NLToken::MOpCode::MAXLIST, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1)), getTokenFromVarOrFloat(c.arg(2))); } /** Non linear constraint min(x, y) = z */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_min(const Call& c) { nlconsOperatorBinary(c, NLToken::MOpCode::MINLIST, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1)), getTokenFromVarOrFloat(c.arg(2))); } // --- --- --- Floating Point Non Linear Unary operator Constraints /** Non linear constraint abs x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_abs(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::ABS, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint acos x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_acos(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::OP_acos, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint acosh x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_acosh(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::OP_acosh, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint asin x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_asin(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::OP_asin, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint asinh x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_asinh(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::OP_asinh, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint atan x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_atan(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::OP_atan, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint atanh x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_atanh(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::OP_atanh, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint cos x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_cos(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::OP_cos, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint cosh x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_cosh(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::OP_cosh, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint exp x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_exp(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::OP_exp, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint ln x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_ln(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::OP_log, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint log10 x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_log10(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::OP_log10, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint log2 x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_log2(const Call& c) { nlconsOperatorUnaryLog2(c, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint sqrt x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_sqrt(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::OP_sqrt, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint sin x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_sin(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::OP_sin, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint sinh x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_sinh(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::OP_sinh, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint tan x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_tan(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::OP_tan, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } /** Non linear constraint tanh x = y */ // NOLINTNEXTLINE(readability-identifier-naming) void NLFile::float_tanh(const Call& c) { nlconsOperatorUnary(c, NLToken::OpCode::OP_tanh, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); } // --- --- --- Other /** Integer x to floating point y. Constraint x = y translated into x - y = 0. * Simulate a linear constraint [1, -1]+*[x, y] = 0 */ void NLFile::int2float(const Call& c) { vector coeffs = {1, -1}; vector vars = {}; vars.push_back(getTokenFromVar(c.arg(0)).str); vars.push_back(getTokenFromVar(c.arg(1)).str); // Create the constraint linconsEq(c, coeffs, vars, NLToken::n(0)); } // --- --- --- Objective /** Add a solve goal in the NL File. In our case, we can only have one and only one solve goal. */ void NLFile::addSolve(SolveI::SolveType st, const Expression* e) { // We can only have one objective. Prevent override. assert(!objective.isDefined()); switch (st) { case SolveI::SolveType::ST_SAT: { // Satisfy: implemented by minimizing 0 (print n0 for an empty expression graph) objective.minmax = objective.SATISFY; break; } case SolveI::SolveType::ST_MIN: { // Maximize an objective represented by a variable objective.minmax = objective.MINIMIZE; string v = getTokenFromVar(e).str; // Use the gradient vector coeffs = {1}; vector vars = {v}; objective.setGradient(vars, coeffs); break; } case SolveI::SolveType::ST_MAX: { // Maximize an objective represented by a variable objective.minmax = objective.MAXIMIZE; string v = getTokenFromVar(e).str; // Use the gradient vector coeffs = {1}; vector vars = {v}; objective.setGradient(vars, coeffs); break; } } // Ensure that the obejctive is now defined. assert(objective.isDefined()); } /* *** *** *** Phase 2: processing *** *** *** */ void NLFile::phase2() { // --- --- --- Go over all constraint (algebraic AND logical) and mark non linear variables for (auto const& n_c : constraints) { for (auto const& tok : n_c.second.expressionGraph) { if (tok.isVariable()) { variables.at(tok.str).isInNLConstraint = true; } } } for (auto const& c : logicalConstraints) { for (auto const& tok : c.expressionGraph) { if (tok.isVariable()) { variables.at(tok.str).isInNLConstraint = true; } } } // --- --- --- Go over the objective and mark non linear variables for (auto const& tok : objective.expressionGraph) { if (tok.isVariable()) { variables.at(tok.str).isInNLObjective = true; } } // --- --- --- Variables ordering and indexing for (auto const& name_var : variables) { const NLVar& v = name_var.second; // Accumulate jacobian count _jacobianCount += v.jacobianCount; // First check non linear variables in BOTH objective and constraint. if (v.isInNLObjective && v.isInNLConstraint) { if (v.isInteger) { vname_nliv_both.push_back(v.name); } else { vname_nlcv_both.push_back(v.name); } } // Variables in non linear constraint ONLY else if (!v.isInNLObjective && v.isInNLConstraint) { if (v.isInteger) { vname_nliv_cons.push_back(v.name); } else { vname_nlcv_cons.push_back(v.name); } } // Variables in non linear objective ONLY else if (v.isInNLObjective && !v.isInNLConstraint) { if (v.isInteger) { vname_nliv_obj.push_back(v.name); } else { vname_nlcv_obj.push_back(v.name); } } // Variables not appearing nonlinearly else if (!v.isInNLObjective && !v.isInNLConstraint) { if (v.isInteger) { vname_liv_all.push_back(v.name); } else { vname_lcv_all.push_back(v.name); } } // Should not happen else { should_not_happen("Dispatching variables in phase2"); } } // Note: In the above, we dealt with all 'vname_*' vectors BUT 'vname_larc_all' and // 'vname_bv_all' // networks and boolean are not implemented. Nevertheless, we keep the vectors and deal // with them below to ease further implementations. vnames.reserve(variables.size()); vnames.insert(vnames.end(), vname_nlcv_both.begin(), vname_nlcv_both.end()); vnames.insert(vnames.end(), vname_nliv_both.begin(), vname_nliv_both.end()); vnames.insert(vnames.end(), vname_nlcv_cons.begin(), vname_nlcv_cons.end()); vnames.insert(vnames.end(), vname_nliv_cons.begin(), vname_nliv_cons.end()); vnames.insert(vnames.end(), vname_nlcv_obj.begin(), vname_nlcv_obj.end()); vnames.insert(vnames.end(), vname_nliv_obj.begin(), vname_nliv_obj.end()); vnames.insert(vnames.end(), vname_larc_all.begin(), vname_larc_all.end()); vnames.insert(vnames.end(), vname_lcv_all.begin(), vname_lcv_all.end()); vnames.insert(vnames.end(), vname_bv_all.begin(), vname_bv_all.end()); vnames.insert(vnames.end(), vname_liv_all.begin(), vname_liv_all.end()); // Create the mapping name->index for (int i = 0; i < vnames.size(); ++i) { variableIndexes[vnames[i]] = i; } // --- --- --- Constraint ordering, couting, and indexing for (auto const& name_cons : constraints) { const NLAlgCons& c = name_cons.second; // Sort by linearity. We do not have network constraint. if (c.isLinear()) { cnames_lin_general.push_back(c.name); } else { cnames_nl_general.push_back(c.name); } // Count the number of ranges and eqns constraints switch (c.range.tag) { case NLBound::LB_UB: { ++algConsRangeCount; } case NLBound::EQ: { ++algConsEqCount; } default: { break; } } } cnames.reserve(constraints.size()); cnames.insert(cnames.end(), cnames_nl_general.begin(), cnames_nl_general.end()); cnames.insert(cnames.end(), cnames_nl_network.begin(), cnames_nl_network.end()); cnames.insert(cnames.end(), cnames_lin_network.begin(), cnames_lin_network.end()); cnames.insert(cnames.end(), cnames_lin_general.begin(), cnames_lin_general.end()); // Create the mapping name->index for (int i = 0; i < cnames.size(); ++i) { constraintIndexes[cnames[i]] = i; } } // --- --- --- Simple tests bool NLFile::hasIntegerVars() const { return (lvbiCount() + lvciCount() + lvoiCount() + ivCount()) > 0; } bool NLFile::hasContinousVars() const { return !hasIntegerVars(); } // --- --- --- Counts /** Jacobian count. */ unsigned int NLFile::jacobianCount() const { return _jacobianCount; } /** Total number of variables. */ unsigned int NLFile::varCount() const { return variables.size(); } /** Number of variables appearing nonlinearly in constraints. */ unsigned int NLFile::lvcCount() const { // Variables in both + variables in constraint only (integer+continuous) return lvbCount() + vname_nliv_cons.size() + vname_nlcv_cons.size(); } /** Number of variables appearing nonlinearly in objectives. */ unsigned int NLFile::lvoCount() const { // Variables in both + variables in objective only (integer+continuous) return lvbCount() + vname_nliv_obj.size() + vname_nlcv_obj.size(); } /** Number of variables appearing nonlinearly in both constraints and objectives.*/ unsigned int NLFile::lvbCount() const { return vname_nlcv_both.size() + vname_nliv_both.size(); } /** Number of integer variables appearing nonlinearly in both constraints and objectives.*/ unsigned int NLFile::lvbiCount() const { return vname_nliv_both.size(); } /** Number of integer variables appearing nonlinearly in constraints **only**.*/ unsigned int NLFile::lvciCount() const { return vname_nliv_cons.size(); } /** Number of integer variables appearing nonlinearly in objectives **only**.*/ unsigned int NLFile::lvoiCount() const { return vname_nliv_obj.size(); } /** Number of linear arcs. Network nor implemented, so always 0.*/ unsigned int NLFile::wvCount() const { return vname_larc_all.size(); } /** Number of "other" integer variables.*/ unsigned int NLFile::ivCount() const { return vname_liv_all.size(); } /** Number of binary variables.*/ unsigned int NLFile::bvCount() const { return vname_bv_all.size(); } /** *** *** *** Printable *** *** *** **/ // Note: * empty line not allowed // * comment only not allowed ostream& NLFile::printToStream(ostream& os) const { // Print the header { NLHeader header; NLHeader::printToStream(os, *this); } os << endl; // Print the unique segments about the variables if (varCount() > 1) { // Print the 'k' segment Maybe to adjust with the presence of 'J' segments os << "k" << (varCount() - 1) << " # Cumulative Sum of non-zero in the jacobian matrix's (nbvar-1) columns." << endl; unsigned int acc = 0; // Note stop before the last var. Total jacobian count is in the header. for (int i = 0; i < varCount() - 1; ++i) { string name = vnames[i]; acc += variables.at(name).jacobianCount; os << acc << " # " << name << endl; } // Print the 'b' segment os << "b # Bounds on variables (" << varCount() << ")" << endl; for (const auto& n : vnames) { NLVar v = variables.at(n); v.bound.printToStream(os, n); os << endl; } } // Print the unique segments about the constraints if (!cnames.empty()) { // Create the 'r' range segment per constraint // For now, it is NOT clear if the network constraint should appear in the range constraint or // not. To be determine if later implemented. os << "r # Bounds on algebraic constraint bodies (" << cnames.size() << ")" << endl; for (const auto& n : cnames) { NLAlgCons c = constraints.at(n); c.range.printToStream(os, n); os << endl; } } // Print the Algebraic Constraints for (const auto& n : cnames) { NLAlgCons c = constraints.at(n); c.printToStream(os, *this); } // Print the Logical constraint for (const auto& lc : logicalConstraints) { lc.printToStream(os, *this); } // Print the objective objective.printToStream(os, *this); return os; } } // namespace MiniZinc