this repo has no description
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2 3/* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7#include <minizinc/hash.hh> 8#include <minizinc/solvers/nl/nl_file.hh> 9 10/** 11 * A NL File reprensentation. 12 * The purpose of this file is mainly to be a writer. 13 * Most of the information come from 'nlwrite.pdf' 14 * Note: 15 * * a character '#' starts a comment until the end of line. 16 * * A new line is '\n' 17 */ 18 19using namespace std; 20 21namespace MiniZinc { 22 23/** *** *** *** Helpers *** *** *** **/ 24 25/** Create a string representing the name (and unique identifier) from an identifier. */ 26string NLFile::getVarName(const Id* id) { 27 stringstream os; 28 if (id->idn() != -1) { 29 os << "X_INTRODUCED_" << id->idn() << "_"; 30 } else if (id->v().size() != 0) { 31 os << id->v(); 32 } 33 string name = os.str(); 34 return name; 35} 36 37/** Create a string representing the name (and unique identifier) of a variable from a variable 38 * declaration. */ 39string NLFile::getVarName(const VarDecl& vd) { return getVarName(vd.id()); } 40 41/** Create a string representing the name (and unique identifier) of a constraint from a specific 42 * call expression. */ 43string NLFile::getConstraintName(const Call& c) { 44 stringstream os; 45 os << c.id() << "_" << static_cast<const void*>(&c); // use the memory address as unique ID. 46 string name = os.str(); 47 return name; 48} 49 50/** Obtain the vector of an array, either from an identifier or an array litteral */ 51const ArrayLit& NLFile::getArrayLit(const Expression* e) { 52 switch (e->eid()) { 53 case Expression::E_ID: { 54 return getArrayLit( 55 e->cast<Id>()->decl()->e()); // Follow the pointer to the expression of the declaration 56 } 57 58 case Expression::E_ARRAYLIT: { 59 const ArrayLit& al = *e->cast<ArrayLit>(); 60 return al; 61 } 62 63 default: 64 should_not_happen("Could not read array from expression."); 65 } 66} 67 68/** Create a vector of double from a vector containing Expression being IntLit. */ 69vector<double> NLFile::fromVecInt(const ArrayLit& v_int) { 70 vector<double> v = {}; 71 for (unsigned int i = 0; i < v_int.size(); ++i) { 72 double d = static_cast<double>(v_int[i]->cast<IntLit>()->v().toInt()); 73 v.push_back(d); 74 } 75 return v; 76} 77 78/** Create a vector of double from a vector containing Expression being FloatLit. */ 79vector<double> NLFile::fromVecFloat(const ArrayLit& v_fp) { 80 vector<double> v = {}; 81 for (unsigned int i = 0; i < v_fp.size(); ++i) { 82 double d = v_fp[i]->cast<FloatLit>()->v().toDouble(); 83 v.push_back(d); 84 } 85 return v; 86} 87 88/** Create a vector of variable names from a vector containing Expression being identifier Id. */ 89vector<string> NLFile::fromVecId(const ArrayLit& v_id) { 90 vector<string> v = {}; 91 for (unsigned int i = 0; i < v_id.size(); ++i) { 92 string s = getVarName(*(v_id[i]->cast<Id>()->decl())); 93 v.push_back(s); 94 } 95 return v; 96} 97 98/** *** *** *** Phase 1: collecting data from MZN *** *** *** **/ 99 100/** Add a variable declaration in the NL File. 101 * This function pre-analyse the declaration VarDecl, then delegate to addVarDeclInteger or 102 * addVarDeclFloat. In flatzinc, arrays always have a rhs: the can always be replaced by their 103 * definition (following the pointer starting at the ID) Hence, we do not reproduce arrays in the NL 104 * file. 105 */ 106void NLFile::addVarDecl(const VarDecl& vd, const TypeInst& ti, const Expression& rhs) { 107 // Get the name 108 string name = getVarName(vd); 109 // Discriminate according to the type: 110 if (ti.isEnum()) { 111 should_not_happen("Enum type in the flatzinc"); 112 } else if (ti.isarray()) { 113 DEBUG_MSG(" Definition of array " << name << " is not reproduced in nl."); 114 115 // Look for the annotation "output_array" 116 for (ExpressionSetIter it = vd.ann().begin(); it != vd.ann().end(); ++it) { 117 Call* c = (*it)->dynamicCast<Call>(); 118 if (c != nullptr && c->id() == (constants().ann.output_array)) { 119 NLArray array; 120 array.name = name; 121 array.isInteger = ti.type().bt() == Type::BT_INT; 122 123 // Search the 'annotation' array 124 const ArrayLit& aa = getArrayLit(c->arg(0)); 125 for (int i = 0; i < aa.size(); ++i) { 126 IntSetVal* r = aa[i]->cast<SetLit>()->isv(); 127 stringstream ss; 128 ss << r->min().toInt() << ".." << r->max().toInt(); 129 array.dimensions.push_back(ss.str()); 130 } 131 132 // Search the 'real' array. Items can be an identifier or a litteral. 133 const ArrayLit& ra = getArrayLit(&rhs); 134 for (int i = 0; i < ra.size(); ++i) { 135 NLArray::Item item; 136 137 if (ra[i]->isa<Id>()) { 138 item.variable = getVarName(ra[i]->cast<Id>()); 139 } else if (ra[i]->isa<IntLit>()) { 140 assert(array.isInteger); 141 item.value = static_cast<double>(ra[i]->cast<IntLit>()->v().toInt()); 142 } else { 143 assert(!array.isInteger); // Floating point 144 item.value = ra[i]->cast<FloatLit>()->v().toDouble(); 145 } 146 147 array.items.push_back(item); 148 } 149 150 outputArrays.push_back(array); 151 152 break; 153 } 154 } 155 } else { 156 // Check if the variable needs to be reported 157 bool toReport = vd.ann().contains(constants().ann.output_var); 158 DEBUG_MSG(" '" << name << "' to be reported? " << toReport); 159 160 // variable declaration 161 const Type& type = ti.type(); 162 const Expression* domain = ti.domain(); 163 164 // Check the type: integer or floatin point 165 assert(type.isvarint() || type.isvarfloat()); 166 bool isvarint = type.isvarint(); 167 168 // Call the declaration function according to the type 169 // Check the domain and convert if not null. 170 // Note: we directly jump to the specialized Int/Float set, going through the set literal 171 if (isvarint) { 172 // Integer 173 IntSetVal* isv = nullptr; 174 if (domain != nullptr) { 175 isv = domain->cast<SetLit>()->isv(); 176 } 177 addVarDeclInteger(name, isv, toReport); 178 } else { 179 // Floating point 180 FloatSetVal* fsv = nullptr; 181 if (domain != nullptr) { 182 fsv = domain->cast<SetLit>()->fsv(); 183 } 184 addVarDeclFloat(name, fsv, toReport); 185 } 186 } 187} 188 189/** Add an integer variable declaration to the NL File. */ 190void NLFile::addVarDeclInteger(const string& name, const IntSetVal* isv, bool toReport) { 191 // Check that we do not have naming conflict 192 assert(variables.find(name) == variables.end()); 193 194 // Check the domain. 195 NLBound bound; 196 if (isv == nullptr) { 197 bound = NLBound::makeNoBound(); 198 } else if (isv->size() == 1) { 199 double lb = static_cast<double>(isv->min(0).toInt()); 200 double ub = static_cast<double>(isv->max(0).toInt()); 201 bound = NLBound::makeBounded(lb, ub); 202 } else { 203 should_not_happen("Range: switch on mzn_opt_only_range_domains" << endl); 204 } 205 // Create the variable and update the NLFile 206 NLVar v = NLVar(name, true, toReport, bound); 207 variables[name] = v; 208} 209 210/** Add a floating point variable declaration to the NL File. */ 211void NLFile::addVarDeclFloat(const string& name, const FloatSetVal* fsv, bool toReport) { 212 // Check that we do not have naming conflict 213 assert(variables.find(name) == variables.end()); 214 // Check the domain. 215 NLBound bound; 216 if (fsv == nullptr) { 217 bound = NLBound::makeNoBound(); 218 } else if (fsv->size() == 1) { 219 double lb = fsv->min(0).toDouble(); 220 double ub = fsv->max(0).toDouble(); 221 bound = NLBound::makeBounded(lb, ub); 222 } else { 223 should_not_happen("Range: switch on mzn_opt_only_range_domains" << std::endl); 224 } 225 // Create the variable and update the NLFile 226 NLVar v = NLVar(name, false, toReport, bound); 227 variables[name] = v; 228} 229 230// --- --- --- Constraints analysis 231 232/** Dispatcher for constraint analysis. */ 233void NLFile::analyseConstraint(const Call& c) { 234 // ID of the call 235 auto id = c.id(); 236 // Constants for integer builtins 237 auto consint = constants().ids.int_; 238 // Constants for floating point builtins 239 auto consfp = constants().ids.float_; 240 241 // Integer linear predicates 242 if (id == consint.lin_eq) { 243 consint_lin_eq(c); 244 } else if (id == consint.lin_le) { 245 consint_lin_le(c); 246 } else if (id == consint.lin_ne) { 247 consint_lin_ne(c); 248 } 249 250 // Integer predicates 251 else if (id == consint.le) { 252 consint_le(c); 253 } else if (id == consint.eq) { 254 consint_eq(c); 255 } else if (id == consint.ne) { 256 consint_ne(c); 257 } 258 259 // Integer binary operators 260 else if (id == consint.times) { 261 consint_times(c); 262 } else if (id == consint.div) { 263 consint_div(c); 264 } else if (id == consint.mod) { 265 consint_mod(c); 266 } else if (id == consint.plus) { 267 consint_plus(c); 268 } else if (id == "int_pow") { 269 int_pow(c); 270 } else if (id == "int_max") { 271 int_max(c); 272 } else if (id == "int_min") { 273 int_min(c); 274 } 275 276 // Integer unary operators 277 else if (id == "int_abs") { 278 int_abs(c); 279 } 280 281 // Floating point linear predicates 282 else if (id == consfp.lin_eq) { 283 consfp_lin_eq(c); 284 } else if (id == consfp.lin_le) { 285 consfp_lin_le(c); 286 } else if (id == consfp.lin_lt) { 287 consfp_lin_lt(c); 288 } else if (id == consfp.lin_ne) { 289 consfp_lin_ne(c); 290 } 291 292 // Floating point predicates 293 else if (id == consfp.lt) { 294 consfp_lt(c); 295 } else if (id == consfp.le) { 296 consfp_le(c); 297 } else if (id == consfp.eq) { 298 consfp_eq(c); 299 } else if (id == consfp.ne) { 300 consfp_ne(c); 301 } 302 303 // Floating point binary operators 304 else if (id == consfp.plus) { 305 consfp_plus(c); 306 } else if (id == consfp.minus) { 307 consfp_minus(c); 308 } else if (id == consfp.div) { 309 consfp_div(c); 310 } else if (id == consfp.times) { 311 consfp_times(c); 312 } else if (id == "float_pow") { 313 float_pow(c); 314 } else if (id == "float_max") { 315 float_max(c); 316 } else if (id == "float_min") { 317 float_min(c); 318 } 319 320 // Floating point unary operators 321 else if (id == "float_abs") { 322 float_abs(c); 323 } else if (id == "float_acos") { 324 float_acos(c); 325 } else if (id == "float_acosh") { 326 float_acosh(c); 327 } else if (id == "float_asin") { 328 float_asin(c); 329 } else if (id == "float_asinh") { 330 float_asinh(c); 331 } else if (id == "float_atan") { 332 float_atan(c); 333 } else if (id == "float_atanh") { 334 float_atanh(c); 335 } else if (id == "float_cos") { 336 float_cos(c); 337 } else if (id == "float_cosh") { 338 float_cosh(c); 339 } else if (id == "float_exp") { 340 float_exp(c); 341 } else if (id == "float_ln") { 342 float_ln(c); 343 } else if (id == "float_log10") { 344 float_log10(c); 345 } else if (id == "float_log2") { 346 float_log2(c); 347 } else if (id == "float_sqrt") { 348 float_sqrt(c); 349 } else if (id == "float_sin") { 350 float_sin(c); 351 } else if (id == "float_sinh") { 352 float_sinh(c); 353 } else if (id == "float_tan") { 354 float_tan(c); 355 } else if (id == "float_tanh") { 356 float_tanh(c); 357 } 358 359 // Other 360 else if (id == "int2float") { 361 int2float(c); 362 } 363 364 // Domain 365 else if (id == consfp.in) { 366 should_not_happen("Ignore for now: constraint 'float in ' not implemented"); 367 } else if (id == consfp.dom) { 368 should_not_happen("Ignore for now: constraint 'float dom ' not implemented"); 369 } 370 371 // Grey area 372 else if (id == consint.lt) { 373 should_not_happen("'int lt'"); 374 } else if (id == consint.gt) { 375 should_not_happen("'int gt'"); 376 } else if (id == consint.ge) { 377 should_not_happen("'int ge'"); 378 } else if (id == consint.minus) { 379 should_not_happen("'int minus'"); 380 } else if (id == consfp.gt) { 381 should_not_happen("float gt'"); 382 } else if (id == consfp.ge) { 383 should_not_happen("float ge'"); 384 } 385 386 // Not implemented 387 else { 388 should_not_happen("Builtins " << c.id() << " not implemented or not recognized."); 389 } 390} 391 392// --- --- --- Helpers 393 394/** Create a token from an expression representing a variable */ 395NLToken NLFile::getTokenFromVarOrInt(const Expression* e) { 396 if (e->type().isPar()) { 397 // Constant 398 double value = static_cast<double>(e->cast<IntLit>()->v().toInt()); 399 return NLToken::n(value); 400 } // Variable 401 VarDecl& vd = *(e->cast<Id>()->decl()); 402 string n = getVarName(vd); 403 return NLToken::v(n); 404} 405 406/** Create a token from an expression representing either a variable or a floating point numeric 407 * value. */ 408NLToken NLFile::getTokenFromVarOrFloat(const Expression* e) { 409 if (e->type().isPar()) { 410 // Constant 411 double value = e->cast<FloatLit>()->v().toDouble(); 412 return NLToken::n(value); 413 } // Variable 414 VarDecl& vd = *(e->cast<Id>()->decl()); 415 string n = getVarName(vd); 416 return NLToken::v(n); 417} 418 419/** Create a token from an expression representing either a variable. */ 420NLToken NLFile::getTokenFromVar(const Expression* e) { 421 assert(!e->type().isPar()); 422 // Variable 423 VarDecl& vd = *(e->cast<Id>()->decl()); 424 string n = getVarName(vd); 425 return NLToken::v(n); 426} 427 428/** Update an expression graph (only by appending token) with a linear combination 429 * of coefficients and variables. Count as "non linear" for the variables occuring here. 430 */ 431void NLFile::makeSigmaMult(vector<NLToken>& expressionGraph, const vector<double>& coeffs, 432 const vector<string>& vars) { 433 assert(coeffs.size() == vars.size()); 434 assert(coeffs.size() >= 2); 435 436 // Create a sum of products. 437 // WARNING: OPSUMLIST needs at least 3 operands! We need a special case for the sum of 2. 438 if (coeffs.size() == 2) { 439 expressionGraph.push_back(NLToken::o(NLToken::OpCode::OPPLUS)); 440 } else { 441 expressionGraph.push_back(NLToken::mo(NLToken::MOpCode::OPSUMLIST, coeffs.size())); 442 } 443 444 // Component of the sum. Simplify multiplication by one. 445 for (unsigned int i = 0; i < coeffs.size(); ++i) { 446 // Product if coeff !=1 447 if (coeffs[i] != 1) { 448 expressionGraph.push_back(NLToken::o(NLToken::OpCode::OPMULT)); 449 expressionGraph.push_back(NLToken::n(coeffs[i])); 450 } 451 // Set the variable as "non linear" 452 expressionGraph.push_back(NLToken::v(vars[i])); 453 } 454} 455 456// --- --- --- Linear Builders 457 458/** Create a linear constraint [coeffs] *+ [vars] = value. */ 459void NLFile::linconsEq(const Call& c, const vector<double>& coeffs, const vector<string>& vars, 460 const NLToken& value) { 461 // Create the Algebraic Constraint and set the data 462 NLAlgCons cons; 463 464 // Get the name of the constraint 465 string cname = getConstraintName(c); 466 cons.name = cname; 467 468 if (value.isConstant()) { 469 // Create the bound of the constraint 470 NLBound bound = NLBound::makeEqual(value.numericValue); 471 cons.range = bound; 472 // No non linear part: leave the expression graph empty. 473 // Linear part: set the jacobian 474 cons.setJacobian(vars, coeffs, this); 475 } else { 476 // Create the bound of the constraint = 0 and change the Jacobian to incorporate a -1 on the 477 // variable in 'value' 478 NLBound bound = NLBound::makeEqual(0); 479 cons.range = bound; 480 // No non linear part: leave the expression graph empty. 481 // Linear part: set the jacobian 482 vector<double> coeffs_(coeffs); 483 coeffs_.push_back(-1); 484 vector<string> vars_(vars); 485 vars_.push_back(value.str); 486 cons.setJacobian(vars_, coeffs_, this); 487 } 488 489 // Add the constraint in our mapping 490 constraints[cname] = cons; 491} 492 493/** Create a linear constraint [coeffs] *+ [vars] <= value. */ 494void NLFile::linconsLe(const Call& c, const vector<double>& coeffs, const vector<string>& vars, 495 const NLToken& value) { 496 // Create the Algebraic Constraint and set the data 497 NLAlgCons cons; 498 499 // Get the name of the constraint 500 string cname = getConstraintName(c); 501 cons.name = cname; 502 503 if (value.isConstant()) { 504 // Create the bound of the constraint 505 NLBound bound = NLBound::makeUBBounded(value.numericValue); 506 cons.range = bound; 507 // No non linear part: leave the expression graph empty. 508 // Linear part: set the jacobian 509 cons.setJacobian(vars, coeffs, this); 510 } else { 511 // Create the bound of the constraint = 0 and change the Jacobian to incorporate a -1 on the 512 // variable in 'value' 513 NLBound bound = NLBound::makeUBBounded(0); 514 cons.range = bound; 515 // No non linear part: leave the expression graph empty. 516 // Linear part: set the jacobian 517 vector<double> coeffs_(coeffs); 518 coeffs_.push_back(-1); 519 vector<string> vars_(vars); 520 vars_.push_back(value.str); 521 cons.setJacobian(vars_, coeffs_, this); 522 } 523 524 // Add the constraint in our mapping 525 constraints[cname] = cons; 526} 527 528/** Create a linear logical constraint [coeffs] *+ [vars] PREDICATE value. 529 * Use a generic comparison operator. 530 * Warnings: - Creates a logical constraint 531 * - Only use for conmparisons that cannot be expressed with '=' xor '<='. 532 */ 533void NLFile::linconsPredicate(const Call& c, NLToken::OpCode oc, const vector<double>& coeffs, 534 const vector<string>& vars, const NLToken& value) { 535 // Create the Logical Constraint and set the data 536 NLLogicalCons cons(logicalConstraints.size()); 537 538 // Get the name of the constraint 539 string cname = getConstraintName(c); 540 cons.name = cname; 541 542 // Create the expression graph (Note: no Jacobian associated with a logical constraint). 543 // 1) Push the comparison operator, e.g. "!= operand1 operand2" 544 cons.expressionGraph.push_back(NLToken::o(oc)); 545 546 // 2) Operand1 := sum of product 547 makeSigmaMult(cons.expressionGraph, coeffs, vars); 548 549 // 3) Operand 2 := value. 550 cons.expressionGraph.push_back(value); 551 552 // Store the constraint 553 logicalConstraints.push_back(cons); 554} 555 556// --- --- --- Non Linear Builders 557// For predicates, uses 2 variables or literals: x := c.arg(0), y := c.arg(1) 558// x PREDICATE y 559 560// For operations, uses 3 variables or literals: x := c.arg(0), y := c.arg(1), and z := c.arg(2). 561// x OPERATOR y = z 562 563/** Create a non linear constraint x = y 564 * Use the jacobian and the bound on constraint to translate into x - y = 0 565 * Simply update the bound if one is a constant. 566 */ 567void NLFile::nlconsEq(const Call& c, const NLToken& x, const NLToken& y) { 568 if (x.kind != y.kind) { 569 if (x.isConstant()) { 570 // Update bound on y 571 double value = x.numericValue; 572 NLVar& v = variables.at(y.str); 573 v.bound.updateEq(value); 574 } else { 575 // Update bound on x 576 double value = y.numericValue; 577 NLVar& v = variables.at(x.str); 578 v.bound.updateEq(value); 579 } 580 } else if (x.str != y.str) { // both must be variables anyway. 581 assert(x.isVariable() && y.isVariable()); 582 // Create the Algebraic Constraint and set the data 583 NLAlgCons cons; 584 585 // Get the name of the constraint 586 string cname = getConstraintName(c); 587 cons.name = cname; 588 589 // Create the bound of the constraint: equal 0 590 NLBound bound = NLBound::makeEqual(0); 591 cons.range = bound; 592 593 // Create the jacobian 594 vector<double> coeffs = {1, -1}; 595 vector<string> vars = {x.str, y.str}; 596 cons.setJacobian(vars, coeffs, this); 597 598 // Store the constraint 599 constraints[cname] = cons; 600 } 601} 602 603/** Create a non linear constraint x <= y 604 * Use the jacobian and the bound on constraint to translate into x - y <= 0 605 * Simply update the bound if one is a constant. 606 */ 607void NLFile::nlconsLe(const Call& c, const NLToken& x, const NLToken& y) { 608 if (x.kind != y.kind) { 609 if (x.isConstant()) { 610 // Update lower bound on y 611 double value = x.numericValue; 612 NLVar& v = variables.at(y.str); 613 v.bound.updateLB(value); 614 } else { 615 // Update upper bound on x 616 double value = y.numericValue; 617 NLVar& v = variables.at(x.str); 618 v.bound.updateUB(value); 619 } 620 } else if (x.str != y.str) { // both must be variables anyway. 621 assert(x.isVariable() && y.isVariable()); 622 623 // Create the Algebraic Constraint and set the data 624 NLAlgCons cons; 625 626 // Get the name of the constraint 627 string cname = getConstraintName(c); 628 cons.name = cname; 629 630 // Create the bound of the constraint: <= 0 631 NLBound bound = NLBound::makeUBBounded(0); 632 cons.range = bound; 633 634 // Create the jacobian 635 vector<double> coeffs = {1, -1}; 636 vector<string> vars = {x.str, y.str}; 637 cons.setJacobian(vars, coeffs, this); 638 639 // Store the constraint 640 constraints[cname] = cons; 641 } 642} 643 644/** Create a non linear constraint with a predicate: x PREDICATE y 645 * Use a generic comparison operator. 646 * Warnings: - Creates a logical constraint 647 * - Only use for conmparisons that cannot be expressed with '=' xor '<='. 648 */ 649void NLFile::nlconsPredicate(const Call& c, NLToken::OpCode oc, const NLToken& x, 650 const NLToken& y) { 651 // Create the Logical Constraint and set the data 652 NLLogicalCons cons(logicalConstraints.size()); 653 654 // Get the name of the constraint 655 string cname = getConstraintName(c); 656 cons.name = cname; 657 658 // Create the expression graph 659 cons.expressionGraph.push_back(NLToken::o(oc)); 660 cons.expressionGraph.push_back(x); 661 cons.expressionGraph.push_back(y); 662 663 // Store the constraint 664 logicalConstraints.push_back(cons); 665} 666 667/** Create a non linear constraint with a binary operator: x OPERATOR y = z */ 668void NLFile::nlconsOperatorBinary(const Call& c, NLToken::OpCode oc, const NLToken& x, 669 const NLToken& y, const NLToken& z) { 670 // Create the Algebraic Constraint and set the data 671 NLAlgCons cons; 672 673 // Get the name of the constraint 674 string cname = getConstraintName(c); 675 cons.name = cname; 676 677 // If z is a constant, use the bound, else use the jacobian 678 if (z.isConstant()) { 679 // Create the bound of the constraint with the numeric value of z 680 NLBound bound = NLBound::makeEqual(z.numericValue); 681 cons.range = bound; 682 } else { 683 // Else, use a constraint bound = 0 and use the jacobian to substract z from the result. 684 // Create the bound of the constraint to 0 685 NLBound bound = NLBound::makeEqual(0); 686 cons.range = bound; 687 688 vector<double> coeffs = {}; 689 vector<string> vars = {}; 690 691 // If x is a variable different from y (and must be different from z), give it 0 for the linear 692 // part 693 if (x.isVariable() && x.str != y.str) { 694 assert(x.str != z.str); 695 coeffs.push_back(0); 696 vars.push_back(x.str); 697 } 698 // Same as above for y. 699 if (y.isVariable()) { 700 assert(y.str != z.str); 701 coeffs.push_back(0); 702 vars.push_back(y.str); 703 } 704 // z is a variable whose value is substracted from the result 705 coeffs.push_back(-1); 706 vars.push_back(z.str); 707 708 // Finish jacobian 709 cons.setJacobian(vars, coeffs, this); 710 } 711 712 // Create the expression graph using the operand code 'oc' 713 cons.expressionGraph.push_back(NLToken::o(oc)); 714 cons.expressionGraph.push_back(x); 715 cons.expressionGraph.push_back(y); 716 717 // Store the constraint 718 constraints[cname] = cons; 719} 720 721/** Create a non linear constraint with a binary operator: x OPERATOR y = z. 722 * OPERATOR is now a Multiop, with a count of 2 (so the choice of the method to use depends on the 723 * LN implementation) */ 724void NLFile::nlconsOperatorBinary(const Call& c, NLToken::MOpCode moc, const NLToken& x, 725 const NLToken& y, const NLToken& z) { 726 // Create the Algebraic Constraint and set the data 727 NLAlgCons cons; 728 729 // Get the name of the constraint 730 string cname = getConstraintName(c); 731 cons.name = cname; 732 733 // If z is a constant, use the bound, else use the jacobian 734 if (z.isConstant()) { 735 // Create the bound of the constraint with the numeric value of z 736 NLBound bound = NLBound::makeEqual(z.numericValue); 737 cons.range = bound; 738 } else { 739 // Else, use a constraint bound = 0 and use the jacobian to substract vres from the result. 740 // Create the bound of the constraint to 0 741 NLBound bound = NLBound::makeEqual(0); 742 cons.range = bound; 743 744 vector<double> coeffs = {}; 745 vector<string> vars = {}; 746 747 // If x is a variable different from y (and must be different from z), give it 0 for the linear 748 // part 749 if (x.isVariable() && x.str != y.str) { 750 assert(x.str != z.str); 751 coeffs.push_back(0); 752 vars.push_back(x.str); 753 } 754 // Same as above for y. 755 if (y.isVariable()) { 756 assert(y.str != z.str); 757 coeffs.push_back(0); 758 vars.push_back(y.str); 759 } 760 // z is a variable whose value is substracted from the result 761 coeffs.push_back(-1); 762 vars.push_back(z.str); 763 764 // Finish jacobian 765 cons.setJacobian(vars, coeffs, this); 766 } 767 768 // Create the expression graph using the operand code 'oc' 769 cons.expressionGraph.push_back(NLToken::mo(moc, 2)); 770 cons.expressionGraph.push_back(x); 771 cons.expressionGraph.push_back(y); 772 773 // Store the constraint 774 constraints[cname] = cons; 775} 776 777/** Create a non linear constraint with an unary operator: OPERATOR x = y */ 778void NLFile::nlconsOperatorUnary(const Call& c, NLToken::OpCode oc, const NLToken& x, 779 const NLToken& y) { 780 // Create the Algebraic Constraint and set the data 781 NLAlgCons cons; 782 783 // Get the name of the constraint 784 string cname = getConstraintName(c); 785 cons.name = cname; 786 787 // If y is a constant, use the bound, else use the jacobian 788 if (y.isConstant()) { 789 // Create the bound of the constraint with the numeric value of y 790 NLBound bound = NLBound::makeEqual(y.numericValue); 791 cons.range = bound; 792 } else { 793 // Else, use a constraint bound = 0 and use the jacobian to substract y from the result. 794 // Create the bound of the constraint to 0 795 NLBound bound = NLBound::makeEqual(0); 796 cons.range = bound; 797 798 vector<double> coeffs = {}; 799 vector<string> vars = {}; 800 801 // If x is a variable (must be different from y), give it '0' for the linear part 802 if (x.isVariable()) { 803 assert(x.str != y.str); 804 coeffs.push_back(0); 805 vars.push_back(x.str); 806 } 807 808 // z is a variable whose value is substracted from the result 809 coeffs.push_back(-1); 810 vars.push_back(y.str); 811 812 // Finish jacobian 813 cons.setJacobian(vars, coeffs, this); 814 } 815 816 // Create the expression graph using the operand code 'oc' 817 cons.expressionGraph.push_back(NLToken::o(oc)); 818 cons.expressionGraph.push_back(x); 819 820 // Store the constraint 821 constraints[cname] = cons; 822} 823 824/** Create a non linear constraint, specialized for log2 unary operator: Log2(x) = y */ 825void NLFile::nlconsOperatorUnaryLog2(const Call& c, const NLToken& x, const NLToken& y) { 826 // Create the Algebraic Constraint and set the data 827 NLAlgCons cons; 828 829 // Get the name of the constraint 830 string cname = getConstraintName(c); 831 cons.name = cname; 832 833 // If y is a constant, use the bound, else use the jacobian 834 if (y.isConstant()) { 835 // Create the bound of the constraint with the numeric value of y 836 NLBound bound = NLBound::makeEqual(y.numericValue); 837 cons.range = bound; 838 } else { 839 // Else, use a constraint bound = 0 and use the jacobian to substract vres from the result. 840 // Create the bound of the constraint to 0 841 NLBound bound = NLBound::makeEqual(0); 842 cons.range = bound; 843 844 vector<double> coeffs = {}; 845 vector<string> vars = {}; 846 847 // If x is a variable (must be different from y), give it '0' for the linear part 848 if (x.isVariable()) { 849 assert(x.str != y.str); 850 coeffs.push_back(0); 851 vars.push_back(x.str); 852 } 853 // z is a variable whose value is substracted from the result 854 coeffs.push_back(-1); 855 vars.push_back(y.str); 856 857 // Finish jacobian 858 cons.setJacobian(vars, coeffs, this); 859 } 860 861 // Create the expression graph with log2(x) = ln(x)/ln(2) 862 cons.expressionGraph.push_back(NLToken::o(NLToken::OpCode::OPDIV)); 863 cons.expressionGraph.push_back(NLToken::o(NLToken::OpCode::OP_log)); 864 cons.expressionGraph.push_back(x); 865 cons.expressionGraph.push_back(NLToken::o(NLToken::OpCode::OP_log)); 866 cons.expressionGraph.push_back(NLToken::n(2)); 867 868 // Store the constraint 869 constraints[cname] = cons; 870} 871 872// --- --- --- Integer Linear Constraints 873 874/** Linar constraint: [coeffs] *+ [vars] = value */ 875// NOLINTNEXTLINE(readability-identifier-naming) 876void NLFile::consint_lin_eq(const Call& c) { 877 // Get the arguments arg0 (array0 = coeffs), arg1 (array = variables) and arg2 (value) 878 vector<double> coeffs = fromVecInt(getArrayLit(c.arg(0))); 879 vector<string> vars = fromVecId(getArrayLit(c.arg(1))); 880 NLToken value = getTokenFromVarOrInt(c.arg(2)); 881 // Create the constraint 882 linconsEq(c, coeffs, vars, value); 883} 884 885/** Linar constraint: [coeffs] *+ [vars] =< value */ 886// NOLINTNEXTLINE(readability-identifier-naming) 887void NLFile::consint_lin_le(const Call& c) { 888 // Get the arguments arg0 (array0 = coeffs), arg1 (array = variables) and arg2 (value) 889 vector<double> coeffs = fromVecInt(getArrayLit(c.arg(0))); 890 vector<string> vars = fromVecId(getArrayLit(c.arg(1))); 891 NLToken value = getTokenFromVarOrInt(c.arg(2)); 892 // Create the constraint 893 linconsLe(c, coeffs, vars, value); 894} 895 896/** Linar constraint: [coeffs] *+ [vars] != value */ 897// NOLINTNEXTLINE(readability-identifier-naming) 898void NLFile::consint_lin_ne(const Call& c) { 899 // Get the arguments arg0 (array0 = coeffs), arg1 (array = variables) and arg2 (value) 900 vector<double> coeffs = fromVecInt(getArrayLit(c.arg(0))); 901 vector<string> vars = fromVecId(getArrayLit(c.arg(1))); 902 NLToken value = getTokenFromVarOrInt(c.arg(2)); 903 // Create the constraint 904 linconsPredicate(c, NLToken::OpCode::NE, coeffs, vars, value); 905} 906 907// --- --- --- Integer Non Linear Predicate Constraints 908 909/** Non linear constraint x = y */ 910// NOLINTNEXTLINE(readability-identifier-naming) 911void NLFile::consint_eq(const Call& c) { 912 nlconsEq(c, getTokenFromVarOrInt(c.arg(0)), getTokenFromVarOrInt(c.arg(1))); 913} 914 915/** Non linear constraint x <= y */ 916// NOLINTNEXTLINE(readability-identifier-naming) 917void NLFile::consint_le(const Call& c) { 918 nlconsLe(c, getTokenFromVarOrInt(c.arg(0)), getTokenFromVarOrInt(c.arg(1))); 919} 920 921/** Non linear constraint x != y */ 922// NOLINTNEXTLINE(readability-identifier-naming) 923void NLFile::consint_ne(const Call& c) { 924 nlconsPredicate(c, NLToken::OpCode::NE, getTokenFromVarOrInt(c.arg(0)), 925 getTokenFromVarOrInt(c.arg(1))); 926} 927 928// --- --- --- Integer Non Linear Binary Operator Constraints 929 930/** Non linear constraint x + y = z */ 931// NOLINTNEXTLINE(readability-identifier-naming) 932void NLFile::consint_plus(const Call& c) { 933 nlconsOperatorBinary(c, NLToken::OpCode::OPPLUS, getTokenFromVarOrInt(c.arg(0)), 934 getTokenFromVarOrInt(c.arg(1)), getTokenFromVarOrInt(c.arg(2))); 935} 936 937/** Non linear constraint x * y = z */ 938// NOLINTNEXTLINE(readability-identifier-naming) 939void NLFile::consint_times(const Call& c) { 940 nlconsOperatorBinary(c, NLToken::OpCode::OPMULT, getTokenFromVarOrInt(c.arg(0)), 941 getTokenFromVarOrInt(c.arg(1)), getTokenFromVarOrInt(c.arg(2))); 942} 943 944/** Non linear constraint x / y = z */ 945// NOLINTNEXTLINE(readability-identifier-naming) 946void NLFile::consint_div(const Call& c) { 947 nlconsOperatorBinary(c, NLToken::OpCode::OPDIV, getTokenFromVarOrInt(c.arg(0)), 948 getTokenFromVarOrInt(c.arg(1)), getTokenFromVarOrInt(c.arg(2))); 949} 950 951/** Non linear constraint x mod y = z */ 952// NOLINTNEXTLINE(readability-identifier-naming) 953void NLFile::consint_mod(const Call& c) { 954 nlconsOperatorBinary(c, NLToken::OpCode::OPREM, getTokenFromVarOrInt(c.arg(0)), 955 getTokenFromVarOrInt(c.arg(1)), getTokenFromVarOrInt(c.arg(2))); 956} 957 958/** Non linear constraint x pow y = z */ 959// NOLINTNEXTLINE(readability-identifier-naming) 960void NLFile::int_pow(const Call& c) { 961 nlconsOperatorBinary(c, NLToken::OpCode::OPPOW, getTokenFromVarOrInt(c.arg(0)), 962 getTokenFromVarOrInt(c.arg(1)), getTokenFromVarOrInt(c.arg(2))); 963} 964 965/** Non linear constraint max(x, y) = z */ 966// NOLINTNEXTLINE(readability-identifier-naming) 967void NLFile::int_max(const Call& c) { 968 nlconsOperatorBinary(c, NLToken::MOpCode::MAXLIST, getTokenFromVarOrInt(c.arg(0)), 969 getTokenFromVarOrInt(c.arg(1)), getTokenFromVarOrInt(c.arg(2))); 970} 971 972/** Non linear constraint min(x, y) = z */ 973// NOLINTNEXTLINE(readability-identifier-naming) 974void NLFile::int_min(const Call& c) { 975 nlconsOperatorBinary(c, NLToken::MOpCode::MINLIST, getTokenFromVarOrInt(c.arg(0)), 976 getTokenFromVarOrInt(c.arg(1)), getTokenFromVarOrInt(c.arg(2))); 977} 978 979// --- --- --- Integer Non Linear Unary Operator Constraints 980 981/** Non linear constraint abs x = y */ 982// NOLINTNEXTLINE(readability-identifier-naming) 983void NLFile::int_abs(const Call& c) { 984 nlconsOperatorUnary(c, NLToken::OpCode::ABS, getTokenFromVarOrInt(c.arg(0)), 985 getTokenFromVarOrInt(c.arg(1))); 986} 987 988// --- --- --- Floating Point Linear Constraints 989 990/** Linar constraint: [coeffs] *+ [vars] = value */ 991// NOLINTNEXTLINE(readability-identifier-naming) 992void NLFile::consfp_lin_eq(const Call& c) { 993 // Get the arguments arg0 (array0 = coeffs), arg1 (array = variables) and arg2 (value) 994 vector<double> coeffs = fromVecFloat(getArrayLit(c.arg(0))); 995 vector<string> vars = fromVecId(getArrayLit(c.arg(1))); 996 NLToken value = getTokenFromVarOrFloat(c.arg(2)); 997 // Create the constraint 998 linconsEq(c, coeffs, vars, value); 999} 1000 1001/** Linar constraint: [coeffs] *+ [vars] = value */ 1002// NOLINTNEXTLINE(readability-identifier-naming) 1003void NLFile::consfp_lin_le(const Call& c) { 1004 // Get the arguments arg0 (array0 = coeffs), arg1 (array = variables) and arg2 (value) 1005 vector<double> coeffs = fromVecFloat(getArrayLit(c.arg(0))); 1006 vector<string> vars = fromVecId(getArrayLit(c.arg(1))); 1007 NLToken value = getTokenFromVarOrFloat(c.arg(2)); 1008 // Create the constraint 1009 linconsLe(c, coeffs, vars, value); 1010} 1011 1012/** Linar constraint: [coeffs] *+ [vars] != value */ 1013// NOLINTNEXTLINE(readability-identifier-naming) 1014void NLFile::consfp_lin_ne(const Call& c) { 1015 // Get the arguments arg0 (array0 = coeffs), arg1 (array = variables) and arg2 (value) 1016 vector<double> coeffs = fromVecFloat(getArrayLit(c.arg(0))); 1017 vector<string> vars = fromVecId(getArrayLit(c.arg(1))); 1018 NLToken value = getTokenFromVarOrFloat(c.arg(2)); 1019 // Create the constraint 1020 linconsPredicate(c, NLToken::OpCode::NE, coeffs, vars, value); 1021} 1022 1023/** Linar constraint: [coeffs] *+ [vars] < value */ 1024// NOLINTNEXTLINE(readability-identifier-naming) 1025void NLFile::consfp_lin_lt(const Call& c) { 1026 // Get the arguments arg0 (array0 = coeffs), arg1 (array = variables) and arg2 (value) 1027 vector<double> coeffs = fromVecFloat(getArrayLit(c.arg(0))); 1028 vector<string> vars = fromVecId(getArrayLit(c.arg(1))); 1029 NLToken value = getTokenFromVarOrFloat(c.arg(2)); 1030 // Create the constraint 1031 linconsPredicate(c, NLToken::OpCode::LT, coeffs, vars, value); 1032} 1033 1034// --- --- --- Floating Point Non Linear Predicate Constraints 1035 1036/** Non linear constraint x = y */ 1037// NOLINTNEXTLINE(readability-identifier-naming) 1038void NLFile::consfp_eq(const Call& c) { 1039 nlconsEq(c, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); 1040} 1041 1042/** Non linear constraint x <= y */ 1043// NOLINTNEXTLINE(readability-identifier-naming) 1044void NLFile::consfp_le(const Call& c) { 1045 nlconsLe(c, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); 1046} 1047 1048/** Non linear constraint x != y */ 1049// NOLINTNEXTLINE(readability-identifier-naming) 1050void NLFile::consfp_ne(const Call& c) { 1051 nlconsPredicate(c, NLToken::OpCode::NE, getTokenFromVarOrFloat(c.arg(0)), 1052 getTokenFromVarOrFloat(c.arg(1))); 1053} 1054 1055/** Non linear constraint x < y */ 1056// NOLINTNEXTLINE(readability-identifier-naming) 1057void NLFile::consfp_lt(const Call& c) { 1058 nlconsPredicate(c, NLToken::OpCode::LT, getTokenFromVarOrFloat(c.arg(0)), 1059 getTokenFromVarOrFloat(c.arg(1))); 1060} 1061 1062// --- --- --- Floating Point Non Linear Binary Operator Constraints 1063 1064/** Non linear constraint x + y = z */ 1065// NOLINTNEXTLINE(readability-identifier-naming) 1066void NLFile::consfp_plus(const Call& c) { 1067 nlconsOperatorBinary(c, NLToken::OpCode::OPPLUS, getTokenFromVarOrFloat(c.arg(0)), 1068 getTokenFromVarOrFloat(c.arg(1)), getTokenFromVarOrFloat(c.arg(2))); 1069} 1070 1071/** Non linear constraint x - y = z */ 1072// NOLINTNEXTLINE(readability-identifier-naming) 1073void NLFile::consfp_minus(const Call& c) { 1074 nlconsOperatorBinary(c, NLToken::OpCode::OPMINUS, getTokenFromVarOrFloat(c.arg(0)), 1075 getTokenFromVarOrFloat(c.arg(1)), getTokenFromVarOrFloat(c.arg(2))); 1076} 1077 1078/** Non linear constraint x * y = z */ 1079// NOLINTNEXTLINE(readability-identifier-naming) 1080void NLFile::consfp_times(const Call& c) { 1081 nlconsOperatorBinary(c, NLToken::OpCode::OPMULT, getTokenFromVarOrFloat(c.arg(0)), 1082 getTokenFromVarOrFloat(c.arg(1)), getTokenFromVarOrFloat(c.arg(2))); 1083} 1084 1085/** Non linear constraint x / y = z */ 1086// NOLINTNEXTLINE(readability-identifier-naming) 1087void NLFile::consfp_div(const Call& c) { 1088 nlconsOperatorBinary(c, NLToken::OpCode::OPDIV, getTokenFromVarOrFloat(c.arg(0)), 1089 getTokenFromVarOrFloat(c.arg(1)), getTokenFromVarOrFloat(c.arg(2))); 1090} 1091 1092/** Non linear constraint x mod y = z */ 1093// NOLINTNEXTLINE(readability-identifier-naming) 1094void NLFile::consfp_mod(const Call& c) { 1095 nlconsOperatorBinary(c, NLToken::OpCode::OPREM, getTokenFromVarOrFloat(c.arg(0)), 1096 getTokenFromVarOrFloat(c.arg(1)), getTokenFromVarOrFloat(c.arg(2))); 1097} 1098 1099/** Non linear constraint x pow y = z */ 1100// NOLINTNEXTLINE(readability-identifier-naming) 1101void NLFile::float_pow(const Call& c) { 1102 nlconsOperatorBinary(c, NLToken::OpCode::OPPOW, getTokenFromVarOrFloat(c.arg(0)), 1103 getTokenFromVarOrFloat(c.arg(1)), getTokenFromVarOrFloat(c.arg(2))); 1104} 1105 1106/** Non linear constraint max(x, y) = z */ 1107// NOLINTNEXTLINE(readability-identifier-naming) 1108void NLFile::float_max(const Call& c) { 1109 nlconsOperatorBinary(c, NLToken::MOpCode::MAXLIST, getTokenFromVarOrFloat(c.arg(0)), 1110 getTokenFromVarOrFloat(c.arg(1)), getTokenFromVarOrFloat(c.arg(2))); 1111} 1112 1113/** Non linear constraint min(x, y) = z */ 1114// NOLINTNEXTLINE(readability-identifier-naming) 1115void NLFile::float_min(const Call& c) { 1116 nlconsOperatorBinary(c, NLToken::MOpCode::MINLIST, getTokenFromVarOrFloat(c.arg(0)), 1117 getTokenFromVarOrFloat(c.arg(1)), getTokenFromVarOrFloat(c.arg(2))); 1118} 1119 1120// --- --- --- Floating Point Non Linear Unary operator Constraints 1121 1122/** Non linear constraint abs x = y */ 1123// NOLINTNEXTLINE(readability-identifier-naming) 1124void NLFile::float_abs(const Call& c) { 1125 nlconsOperatorUnary(c, NLToken::OpCode::ABS, getTokenFromVarOrFloat(c.arg(0)), 1126 getTokenFromVarOrFloat(c.arg(1))); 1127} 1128 1129/** Non linear constraint acos x = y */ 1130// NOLINTNEXTLINE(readability-identifier-naming) 1131void NLFile::float_acos(const Call& c) { 1132 nlconsOperatorUnary(c, NLToken::OpCode::OP_acos, getTokenFromVarOrFloat(c.arg(0)), 1133 getTokenFromVarOrFloat(c.arg(1))); 1134} 1135 1136/** Non linear constraint acosh x = y */ 1137// NOLINTNEXTLINE(readability-identifier-naming) 1138void NLFile::float_acosh(const Call& c) { 1139 nlconsOperatorUnary(c, NLToken::OpCode::OP_acosh, getTokenFromVarOrFloat(c.arg(0)), 1140 getTokenFromVarOrFloat(c.arg(1))); 1141} 1142 1143/** Non linear constraint asin x = y */ 1144// NOLINTNEXTLINE(readability-identifier-naming) 1145void NLFile::float_asin(const Call& c) { 1146 nlconsOperatorUnary(c, NLToken::OpCode::OP_asin, getTokenFromVarOrFloat(c.arg(0)), 1147 getTokenFromVarOrFloat(c.arg(1))); 1148} 1149 1150/** Non linear constraint asinh x = y */ 1151// NOLINTNEXTLINE(readability-identifier-naming) 1152void NLFile::float_asinh(const Call& c) { 1153 nlconsOperatorUnary(c, NLToken::OpCode::OP_asinh, getTokenFromVarOrFloat(c.arg(0)), 1154 getTokenFromVarOrFloat(c.arg(1))); 1155} 1156 1157/** Non linear constraint atan x = y */ 1158// NOLINTNEXTLINE(readability-identifier-naming) 1159void NLFile::float_atan(const Call& c) { 1160 nlconsOperatorUnary(c, NLToken::OpCode::OP_atan, getTokenFromVarOrFloat(c.arg(0)), 1161 getTokenFromVarOrFloat(c.arg(1))); 1162} 1163 1164/** Non linear constraint atanh x = y */ 1165// NOLINTNEXTLINE(readability-identifier-naming) 1166void NLFile::float_atanh(const Call& c) { 1167 nlconsOperatorUnary(c, NLToken::OpCode::OP_atanh, getTokenFromVarOrFloat(c.arg(0)), 1168 getTokenFromVarOrFloat(c.arg(1))); 1169} 1170 1171/** Non linear constraint cos x = y */ 1172// NOLINTNEXTLINE(readability-identifier-naming) 1173void NLFile::float_cos(const Call& c) { 1174 nlconsOperatorUnary(c, NLToken::OpCode::OP_cos, getTokenFromVarOrFloat(c.arg(0)), 1175 getTokenFromVarOrFloat(c.arg(1))); 1176} 1177 1178/** Non linear constraint cosh x = y */ 1179// NOLINTNEXTLINE(readability-identifier-naming) 1180void NLFile::float_cosh(const Call& c) { 1181 nlconsOperatorUnary(c, NLToken::OpCode::OP_cosh, getTokenFromVarOrFloat(c.arg(0)), 1182 getTokenFromVarOrFloat(c.arg(1))); 1183} 1184 1185/** Non linear constraint exp x = y */ 1186// NOLINTNEXTLINE(readability-identifier-naming) 1187void NLFile::float_exp(const Call& c) { 1188 nlconsOperatorUnary(c, NLToken::OpCode::OP_exp, getTokenFromVarOrFloat(c.arg(0)), 1189 getTokenFromVarOrFloat(c.arg(1))); 1190} 1191 1192/** Non linear constraint ln x = y */ 1193// NOLINTNEXTLINE(readability-identifier-naming) 1194void NLFile::float_ln(const Call& c) { 1195 nlconsOperatorUnary(c, NLToken::OpCode::OP_log, getTokenFromVarOrFloat(c.arg(0)), 1196 getTokenFromVarOrFloat(c.arg(1))); 1197} 1198 1199/** Non linear constraint log10 x = y */ 1200// NOLINTNEXTLINE(readability-identifier-naming) 1201void NLFile::float_log10(const Call& c) { 1202 nlconsOperatorUnary(c, NLToken::OpCode::OP_log10, getTokenFromVarOrFloat(c.arg(0)), 1203 getTokenFromVarOrFloat(c.arg(1))); 1204} 1205 1206/** Non linear constraint log2 x = y */ 1207// NOLINTNEXTLINE(readability-identifier-naming) 1208void NLFile::float_log2(const Call& c) { 1209 nlconsOperatorUnaryLog2(c, getTokenFromVarOrFloat(c.arg(0)), getTokenFromVarOrFloat(c.arg(1))); 1210} 1211 1212/** Non linear constraint sqrt x = y */ 1213// NOLINTNEXTLINE(readability-identifier-naming) 1214void NLFile::float_sqrt(const Call& c) { 1215 nlconsOperatorUnary(c, NLToken::OpCode::OP_sqrt, getTokenFromVarOrFloat(c.arg(0)), 1216 getTokenFromVarOrFloat(c.arg(1))); 1217} 1218 1219/** Non linear constraint sin x = y */ 1220// NOLINTNEXTLINE(readability-identifier-naming) 1221void NLFile::float_sin(const Call& c) { 1222 nlconsOperatorUnary(c, NLToken::OpCode::OP_sin, getTokenFromVarOrFloat(c.arg(0)), 1223 getTokenFromVarOrFloat(c.arg(1))); 1224} 1225 1226/** Non linear constraint sinh x = y */ 1227// NOLINTNEXTLINE(readability-identifier-naming) 1228void NLFile::float_sinh(const Call& c) { 1229 nlconsOperatorUnary(c, NLToken::OpCode::OP_sinh, getTokenFromVarOrFloat(c.arg(0)), 1230 getTokenFromVarOrFloat(c.arg(1))); 1231} 1232 1233/** Non linear constraint tan x = y */ 1234// NOLINTNEXTLINE(readability-identifier-naming) 1235void NLFile::float_tan(const Call& c) { 1236 nlconsOperatorUnary(c, NLToken::OpCode::OP_tan, getTokenFromVarOrFloat(c.arg(0)), 1237 getTokenFromVarOrFloat(c.arg(1))); 1238} 1239 1240/** Non linear constraint tanh x = y */ 1241// NOLINTNEXTLINE(readability-identifier-naming) 1242void NLFile::float_tanh(const Call& c) { 1243 nlconsOperatorUnary(c, NLToken::OpCode::OP_tanh, getTokenFromVarOrFloat(c.arg(0)), 1244 getTokenFromVarOrFloat(c.arg(1))); 1245} 1246 1247// --- --- --- Other 1248 1249/** Integer x to floating point y. Constraint x = y translated into x - y = 0. 1250 * Simulate a linear constraint [1, -1]+*[x, y] = 0 1251 */ 1252void NLFile::int2float(const Call& c) { 1253 vector<double> coeffs = {1, -1}; 1254 vector<string> vars = {}; 1255 vars.push_back(getTokenFromVar(c.arg(0)).str); 1256 vars.push_back(getTokenFromVar(c.arg(1)).str); 1257 // Create the constraint 1258 linconsEq(c, coeffs, vars, NLToken::n(0)); 1259} 1260 1261// --- --- --- Objective 1262 1263/** Add a solve goal in the NL File. In our case, we can only have one and only one solve goal. */ 1264void NLFile::addSolve(SolveI::SolveType st, const Expression* e) { 1265 // We can only have one objective. Prevent override. 1266 assert(!objective.isDefined()); 1267 1268 switch (st) { 1269 case SolveI::SolveType::ST_SAT: { 1270 // Satisfy: implemented by minimizing 0 (print n0 for an empty expression graph) 1271 objective.minmax = objective.SATISFY; 1272 break; 1273 } 1274 case SolveI::SolveType::ST_MIN: { 1275 // Maximize an objective represented by a variable 1276 objective.minmax = objective.MINIMIZE; 1277 string v = getTokenFromVar(e).str; 1278 // Use the gradient 1279 vector<double> coeffs = {1}; 1280 vector<string> vars = {v}; 1281 objective.setGradient(vars, coeffs); 1282 break; 1283 } 1284 case SolveI::SolveType::ST_MAX: { 1285 // Maximize an objective represented by a variable 1286 objective.minmax = objective.MAXIMIZE; 1287 string v = getTokenFromVar(e).str; 1288 // Use the gradient 1289 vector<double> coeffs = {1}; 1290 vector<string> vars = {v}; 1291 objective.setGradient(vars, coeffs); 1292 break; 1293 } 1294 } 1295 1296 // Ensure that the obejctive is now defined. 1297 assert(objective.isDefined()); 1298} 1299 1300/* *** *** *** Phase 2: processing *** *** *** */ 1301 1302void NLFile::phase2() { 1303 // --- --- --- Go over all constraint (algebraic AND logical) and mark non linear variables 1304 for (auto const& n_c : constraints) { 1305 for (auto const& tok : n_c.second.expressionGraph) { 1306 if (tok.isVariable()) { 1307 variables.at(tok.str).isInNLConstraint = true; 1308 } 1309 } 1310 } 1311 1312 for (auto const& c : logicalConstraints) { 1313 for (auto const& tok : c.expressionGraph) { 1314 if (tok.isVariable()) { 1315 variables.at(tok.str).isInNLConstraint = true; 1316 } 1317 } 1318 } 1319 1320 // --- --- --- Go over the objective and mark non linear variables 1321 for (auto const& tok : objective.expressionGraph) { 1322 if (tok.isVariable()) { 1323 variables.at(tok.str).isInNLObjective = true; 1324 } 1325 } 1326 1327 // --- --- --- Variables ordering and indexing 1328 for (auto const& name_var : variables) { 1329 const NLVar& v = name_var.second; 1330 1331 // Accumulate jacobian count 1332 _jacobianCount += v.jacobianCount; 1333 1334 // First check non linear variables in BOTH objective and constraint. 1335 if (v.isInNLObjective && v.isInNLConstraint) { 1336 if (v.isInteger) { 1337 vname_nliv_both.push_back(v.name); 1338 } else { 1339 vname_nlcv_both.push_back(v.name); 1340 } 1341 } 1342 // Variables in non linear constraint ONLY 1343 else if (!v.isInNLObjective && v.isInNLConstraint) { 1344 if (v.isInteger) { 1345 vname_nliv_cons.push_back(v.name); 1346 } else { 1347 vname_nlcv_cons.push_back(v.name); 1348 } 1349 } 1350 // Variables in non linear objective ONLY 1351 else if (v.isInNLObjective && !v.isInNLConstraint) { 1352 if (v.isInteger) { 1353 vname_nliv_obj.push_back(v.name); 1354 } else { 1355 vname_nlcv_obj.push_back(v.name); 1356 } 1357 } 1358 // Variables not appearing nonlinearly 1359 else if (!v.isInNLObjective && !v.isInNLConstraint) { 1360 if (v.isInteger) { 1361 vname_liv_all.push_back(v.name); 1362 } else { 1363 vname_lcv_all.push_back(v.name); 1364 } 1365 } 1366 // Should not happen 1367 else { 1368 should_not_happen("Dispatching variables in phase2"); 1369 } 1370 } 1371 1372 // Note: In the above, we dealt with all 'vname_*' vectors BUT 'vname_larc_all' and 1373 // 'vname_bv_all' 1374 // networks and boolean are not implemented. Nevertheless, we keep the vectors and deal 1375 // with them below to ease further implementations. 1376 1377 vnames.reserve(variables.size()); 1378 1379 vnames.insert(vnames.end(), vname_nlcv_both.begin(), vname_nlcv_both.end()); 1380 vnames.insert(vnames.end(), vname_nliv_both.begin(), vname_nliv_both.end()); 1381 vnames.insert(vnames.end(), vname_nlcv_cons.begin(), vname_nlcv_cons.end()); 1382 vnames.insert(vnames.end(), vname_nliv_cons.begin(), vname_nliv_cons.end()); 1383 vnames.insert(vnames.end(), vname_nlcv_obj.begin(), vname_nlcv_obj.end()); 1384 vnames.insert(vnames.end(), vname_nliv_obj.begin(), vname_nliv_obj.end()); 1385 vnames.insert(vnames.end(), vname_larc_all.begin(), vname_larc_all.end()); 1386 vnames.insert(vnames.end(), vname_lcv_all.begin(), vname_lcv_all.end()); 1387 vnames.insert(vnames.end(), vname_bv_all.begin(), vname_bv_all.end()); 1388 vnames.insert(vnames.end(), vname_liv_all.begin(), vname_liv_all.end()); 1389 1390 // Create the mapping name->index 1391 for (int i = 0; i < vnames.size(); ++i) { 1392 variableIndexes[vnames[i]] = i; 1393 } 1394 1395 // --- --- --- Constraint ordering, couting, and indexing 1396 for (auto const& name_cons : constraints) { 1397 const NLAlgCons& c = name_cons.second; 1398 1399 // Sort by linearity. We do not have network constraint. 1400 if (c.isLinear()) { 1401 cnames_lin_general.push_back(c.name); 1402 } else { 1403 cnames_nl_general.push_back(c.name); 1404 } 1405 1406 // Count the number of ranges and eqns constraints 1407 switch (c.range.tag) { 1408 case NLBound::LB_UB: { 1409 ++algConsRangeCount; 1410 } 1411 case NLBound::EQ: { 1412 ++algConsEqCount; 1413 } 1414 default: { 1415 break; 1416 } 1417 } 1418 } 1419 1420 cnames.reserve(constraints.size()); 1421 cnames.insert(cnames.end(), cnames_nl_general.begin(), cnames_nl_general.end()); 1422 cnames.insert(cnames.end(), cnames_nl_network.begin(), cnames_nl_network.end()); 1423 cnames.insert(cnames.end(), cnames_lin_network.begin(), cnames_lin_network.end()); 1424 cnames.insert(cnames.end(), cnames_lin_general.begin(), cnames_lin_general.end()); 1425 1426 // Create the mapping name->index 1427 for (int i = 0; i < cnames.size(); ++i) { 1428 constraintIndexes[cnames[i]] = i; 1429 } 1430} 1431 1432// --- --- --- Simple tests 1433 1434bool NLFile::hasIntegerVars() const { 1435 return (lvbiCount() + lvciCount() + lvoiCount() + ivCount()) > 0; 1436} 1437 1438bool NLFile::hasContinousVars() const { return !hasIntegerVars(); } 1439 1440// --- --- --- Counts 1441 1442/** Jacobian count. */ 1443unsigned int NLFile::jacobianCount() const { return _jacobianCount; } 1444 1445/** Total number of variables. */ 1446unsigned int NLFile::varCount() const { return variables.size(); } 1447 1448/** Number of variables appearing nonlinearly in constraints. */ 1449unsigned int NLFile::lvcCount() const { 1450 // Variables in both + variables in constraint only (integer+continuous) 1451 return lvbCount() + vname_nliv_cons.size() + vname_nlcv_cons.size(); 1452} 1453 1454/** Number of variables appearing nonlinearly in objectives. */ 1455unsigned int NLFile::lvoCount() const { 1456 // Variables in both + variables in objective only (integer+continuous) 1457 return lvbCount() + vname_nliv_obj.size() + vname_nlcv_obj.size(); 1458} 1459 1460/** Number of variables appearing nonlinearly in both constraints and objectives.*/ 1461unsigned int NLFile::lvbCount() const { return vname_nlcv_both.size() + vname_nliv_both.size(); } 1462 1463/** Number of integer variables appearing nonlinearly in both constraints and objectives.*/ 1464unsigned int NLFile::lvbiCount() const { return vname_nliv_both.size(); } 1465 1466/** Number of integer variables appearing nonlinearly in constraints **only**.*/ 1467unsigned int NLFile::lvciCount() const { return vname_nliv_cons.size(); } 1468 1469/** Number of integer variables appearing nonlinearly in objectives **only**.*/ 1470unsigned int NLFile::lvoiCount() const { return vname_nliv_obj.size(); } 1471 1472/** Number of linear arcs. Network nor implemented, so always 0.*/ 1473unsigned int NLFile::wvCount() const { return vname_larc_all.size(); } 1474 1475/** Number of "other" integer variables.*/ 1476unsigned int NLFile::ivCount() const { return vname_liv_all.size(); } 1477 1478/** Number of binary variables.*/ 1479unsigned int NLFile::bvCount() const { return vname_bv_all.size(); } 1480 1481/** *** *** *** Printable *** *** *** **/ 1482// Note: * empty line not allowed 1483// * comment only not allowed 1484ostream& NLFile::printToStream(ostream& os) const { 1485 // Print the header 1486 { 1487 NLHeader header; 1488 NLHeader::printToStream(os, *this); 1489 } 1490 os << endl; 1491 1492 // Print the unique segments about the variables 1493 if (varCount() > 1) { 1494 // Print the 'k' segment Maybe to adjust with the presence of 'J' segments 1495 os << "k" << (varCount() - 1) 1496 << " # Cumulative Sum of non-zero in the jacobian matrix's (nbvar-1) columns." << endl; 1497 unsigned int acc = 0; 1498 // Note stop before the last var. Total jacobian count is in the header. 1499 for (int i = 0; i < varCount() - 1; ++i) { 1500 string name = vnames[i]; 1501 acc += variables.at(name).jacobianCount; 1502 os << acc << " # " << name << endl; 1503 } 1504 1505 // Print the 'b' segment 1506 os << "b # Bounds on variables (" << varCount() << ")" << endl; 1507 for (const auto& n : vnames) { 1508 NLVar v = variables.at(n); 1509 v.bound.printToStream(os, n); 1510 os << endl; 1511 } 1512 } 1513 1514 // Print the unique segments about the constraints 1515 if (!cnames.empty()) { 1516 // Create the 'r' range segment per constraint 1517 // For now, it is NOT clear if the network constraint should appear in the range constraint or 1518 // not. To be determine if later implemented. 1519 os << "r # Bounds on algebraic constraint bodies (" << cnames.size() << ")" << endl; 1520 for (const auto& n : cnames) { 1521 NLAlgCons c = constraints.at(n); 1522 c.range.printToStream(os, n); 1523 os << endl; 1524 } 1525 } 1526 1527 // Print the Algebraic Constraints 1528 for (const auto& n : cnames) { 1529 NLAlgCons c = constraints.at(n); 1530 c.printToStream(os, *this); 1531 } 1532 1533 // Print the Logical constraint 1534 for (const auto& lc : logicalConstraints) { 1535 lc.printToStream(os, *this); 1536 } 1537 1538 // Print the objective 1539 objective.printToStream(os, *this); 1540 return os; 1541} 1542 1543} // namespace MiniZinc