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