this repo has no description
at develop 89 kB view raw
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2 3/* 4 * Main authors: 5 * Gleb Belov <gleb.belov@monash.edu> 6 */ 7 8/* This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was ! distributed with this 10 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 11 12#ifdef _MSC_VER 13#define _CRT_SECURE_NO_WARNINGS 14#undef ERROR // MICROsoft. 15#undef min 16#undef max 17#endif 18 19#include <minizinc/MIPdomains.hh> 20#include <minizinc/astexception.hh> 21#include <minizinc/astiterator.hh> 22#include <minizinc/copy.hh> 23#include <minizinc/eval_par.hh> 24#include <minizinc/flatten.hh> 25#include <minizinc/flatten_internal.hh> 26#include <minizinc/hash.hh> 27 28// temporary 29#include <minizinc/prettyprinter.hh> 30//#include <ostream> 31 32#include <map> 33#include <unordered_map> 34#include <unordered_set> 35 36/// TODOs 37/// TODO Not going to work for float vars because of round-offs in the domain interval sorting... 38/// set_in etc. are ! propagated between views 39/// CLEANUP after work: ~destructor 40/// Also check initexpr of all vars? DONE 41/// In case of only_range_domains we'd need to register inequalities 42/// - so better turn that off TODO 43/// CSE for lineq coefs TODO 44 45/// TODO use integer division instead of INT_EPS 46#define INT_EPS 1e-5 // the absolute epsilon for integrality of integer vars. 47 48#define __MZN__MIPDOMAINS__PRINTMORESTATS 49#define MZN_DBG_CHECK_ITER_CUTOUT 50 51// #define __MZN__DBGOUT__MIPDOMAINS__ 52#ifdef __MZN__DBGOUT__MIPDOMAINS__ 53#define DBGOUT_MIPD(s) std::cerr << s << std::endl 54#define DBGOUT_MIPD__(s) std::cerr << s << std::flush 55#define DBGOUT_MIPD_SELF(op) op 56#else 57#define DBGOUT_MIPD(s) \ 58 do { \ 59 } while (false) 60#define DBGOUT_MIPD__(s) \ 61 do { \ 62 } while (false) 63#define DBGOUT_MIPD_SELF(op) \ 64 do { \ 65 } while (false) 66#endif 67 68namespace MiniZinc { 69 70enum EnumStatIdx__MIPD { 71 N_POSTs__all, // N all POSTs in the model 72 N_POSTs__intCmpReif, 73 N_POSTs__floatCmpReif, // in detail 74 N_POSTs__intNE, 75 N_POSTs__floatNE, 76 N_POSTs__setIn, 77 N_POSTs__domain, 78 N_POSTs__setInReif, 79 N_POSTs__eq_encode, 80 N_POSTs__intAux, 81 N_POSTs__floatAux, 82 // Kind of equality connections between involved variables 83 N_POSTs__eq2intlineq, 84 N_POSTs__eq2floatlineq, 85 N_POSTs__int2float, 86 N_POSTs__internalvarredef, 87 N_POSTs__initexpr1id, 88 N_POSTs__initexpr1linexp, 89 N_POSTs__initexprN, 90 N_POSTs__eqNlineq, 91 N_POSTs__eqNmapsize, 92 // other 93 N_POSTs__varsDirect, 94 N_POSTs__varsInvolved, 95 N_POSTs__NSubintvMin, 96 N_POSTs__NSubintvSum, 97 N_POSTs__NSubintvMax, // as N subintervals 98 N_POSTs__SubSizeMin, 99 N_POSTs__SubSizeSum, 100 N_POSTs__SubSizeMax, // subintv. size 101 N_POSTs__linCoefMin, 102 N_POSTs__linCoefMax, 103 N_POSTs__cliquesWithEqEncode, 104 N_POSTs__clEEEnforced, 105 N_POSTs__clEEFound, 106 N_POSTs__size 107}; 108extern std::vector<double> MIPD__stats; 109 110enum EnumReifType { RIT_None, RIT_Static, RIT_Reif, RIT_Halfreif }; 111enum EnumConstrType { CT_None, CT_Comparison, CT_SetIn, CT_Encode }; 112enum EnumCmpType { 113 CMPT_None = 0, 114 CMPT_LE = -4, 115 CMPT_GE = 4, 116 CMPT_EQ = 1, 117 CMPT_NE = 3, 118 CMPT_LT = -5, 119 CMPT_GT = 5, 120 CMPT_LE_0 = -6, 121 CMPT_GE_0 = 6, 122 CMPT_EQ_0 = 2, 123 CMPT_LT_0 = -7, 124 CMPT_GT_0 = 7 125}; 126enum EnumVarType { VT_None, VT_Int, VT_Float }; 127 128/// struct DomainCallType describes & characterizes a possible domain constr call 129struct DCT { 130 const char* sFuncName = 0; 131 const std::vector<Type>& aParams; 132 // unsigned iItem; // call's item number in the flat 133 EnumReifType nReifType = RIT_None; // 0/static/halfreif/reif 134 EnumConstrType nConstrType = CT_None; // 135 EnumCmpType nCmpType = CMPT_None; 136 EnumVarType nVarType = VT_None; 137 FunctionI*& pfi; 138 // double dEps = -1.0; 139 DCT(const char* fn, const std::vector<Type>& prm, EnumReifType er, EnumConstrType ec, 140 EnumCmpType ecmp, EnumVarType ev, FunctionI*& pfi__) 141 : sFuncName(fn), 142 aParams(prm), 143 nReifType(er), 144 nConstrType(ec), 145 nCmpType(ecmp), 146 nVarType(ev), 147 pfi(pfi__) {} 148}; 149 150template <class N> 151struct Interval { 152 N left = infMinus(), right = infPlus(); 153 mutable VarDecl* varFlag = 0; 154 /*constexpr*/ static N infMinus() { 155 return (std::numeric_limits<N>::has_infinity) ? -std::numeric_limits<N>::infinity() 156 : std::numeric_limits<N>::lowest(); 157 } 158 /*constexpr*/ static N infPlus() { 159 return (std::numeric_limits<N>::has_infinity) ? std::numeric_limits<N>::infinity() 160 : std::numeric_limits<N>::max(); 161 } 162 Interval(N a = infMinus(), N b = infPlus()) : left(a), right(b) {} 163 bool operator<(const Interval& intv) const { 164 if (left < intv.left) { 165 // assert( right <= intv.left ); // assume disjoint 166 return true; 167 } 168 return false; 169 } 170}; 171typedef Interval<double> IntvReal; 172 173template <class N> 174std::ostream& operator<<(std::ostream& os, const Interval<N>& ii) { 175 os << "[ " << ii.left << ", " << ii.right << " ] "; 176 return os; 177} 178 179template <class N> 180class SetOfIntervals : public std::multiset<Interval<N> > { 181public: 182 using Intv = Interval<N>; 183 typedef std::multiset<Interval<N> > Base; 184 typedef typename Base::iterator iterator; 185 SetOfIntervals() : Base() {} 186 SetOfIntervals(std::initializer_list<Interval<N> > il) : Base(il) {} 187 template <class Iter> 188 SetOfIntervals(Iter i1, Iter i2) : Base(i1, i2) {} 189 /// Number of integer values in all the intervals 190 /// Assumes the interval bounds are ints 191 int card_int() const; 192 /// Max interval length 193 N max_interval() const; 194 /// Special insert function: check if interval is ok 195 iterator insert(const Interval<N>& iv) { 196 if (iv.left > iv.right) { 197 DBGOUT_MIPD("Interval " << iv.left << ".." << iv.right 198 << " is empty, difference: " << (iv.right - iv.left) << ". Skipping"); 199 return Base::end(); 200 } 201 return Base::insert(iv); 202 } 203 template <class N1> 204 void intersect(const SetOfIntervals<N1>& s2); 205 /// Assumes open intervals to cut out from closed 206 template <class N1> 207 void cutDeltas(const SetOfIntervals<N1>& s2, N1 delta); 208 template <class N1> 209 void cutDeltas(N1 left, N1 right, N1 delta) { 210 SetOfIntervals<N1> soi; 211 soi.insert(Interval<N1>(left, right)); 212 cutDeltas(soi, delta); 213 } 214 /// Cut out an open interval from a set of closed ones (except for infinities) 215 void cutOut(const Interval<N>& intv); 216 typedef std::pair<iterator, iterator> SplitResult; 217 SplitResult split(iterator& it, N pos); 218 bool checkFiniteBounds(); 219 /// Check there are no useless interval splittings 220 bool checkDisjunctStrict(); 221 Interval<N> getBounds() const; 222 /// Split domain into the integer values 223 /// May assume integer bounds 224 void split2Bits(); 225}; // class SetOfIntervals 226typedef SetOfIntervals<double> SetOfIntvReal; 227 228template <class N> 229std::ostream& operator<<(std::ostream& os, const SetOfIntervals<N>& soi) { 230 os << "[[ "; 231 for (auto& ii : soi) { 232 os << "[ " << ii.left << ", " << ii.right; 233 if (ii.varFlag) os << " @" << ii.varFlag; 234 os << " ] "; 235 } 236 os << "]]"; 237 return os; 238} 239 240template <class Coefs, class Vars> 241class LinEq__ { 242public: 243 Coefs coefs; 244 Vars vd; 245 double rhs; 246}; 247 248template <class Coefs, class Vars> 249static std::ostream& operator<<(std::ostream& os, LinEq__<Coefs, Vars>& led) { 250 os << "( ["; 251 for (auto c : led.coefs) os << c << ' '; 252 os << " ] * [ "; 253 for (auto v : led.vd) os << v->id()->str() << ' '; 254 os << " ] ) == " << led.rhs; 255 return os; 256} 257 258typedef LinEq__<std::array<double, 2>, std::array<VarDecl*, 2> > LinEq2Vars; 259typedef LinEq__<std::vector<double>, std::vector<VarDecl*> > LinEq; 260// struct LinEq2Vars { 261// std::array<double, 2> coefs; 262// std::array<PVarDecl, 2> vd = { { 0, 0 } }; 263// double rhs; 264// }; 265// 266// struct LinEq { 267// std::vector<double> coefs; 268// std::vector<VarDecl*> vd; 269// double rhs; 270// }; 271 272std::vector<double> MIPD__stats(N_POSTs__size); 273 274template <class T> 275static std::vector<T> make_vec(T t1, T t2) { 276 T c_array[] = {t1, t2}; 277 std::vector<T> result(c_array, c_array + sizeof(c_array) / sizeof(c_array[0])); 278 return result; 279} 280template <class T> 281static std::vector<T> make_vec(T t1, T t2, T t3) { 282 T c_array[] = {t1, t2, t3}; 283 std::vector<T> result(c_array, c_array + sizeof(c_array) / sizeof(c_array[0])); 284 return result; 285} 286template <class T> 287static std::vector<T> make_vec(T t1, T t2, T t3, T t4) { 288 T c_array[] = {t1, t2, t3, t4}; 289 std::vector<T> result(c_array, c_array + sizeof(c_array) / sizeof(c_array[0])); 290 return result; 291} 292 293class MIPD { 294public: 295 MIPD(Env* env, bool fV, int nmi, double dmd) 296 : nMaxIntv2Bits(nmi), dMaxNValueDensity(dmd), __env(env) { 297 getEnv(); 298 fVerbose = fV; 299 } 300 static bool fVerbose; 301 const int nMaxIntv2Bits = 0; // Maximal interval length to enforce equality encoding 302 const double dMaxNValueDensity = 3.0; // Maximal ratio card_int() / size() of a domain 303 // to enforce ee 304 bool MIPdomains() { 305 MIPD__stats[N_POSTs__NSubintvMin] = 1e100; 306 MIPD__stats[N_POSTs__SubSizeMin] = 1e100; 307 308 if (!registerLinearConstraintDecls()) return true; 309 if (!register__POSTconstraintDecls()) // not declared => no conversions 310 return true; 311 register__POSTvariables(); 312 if (vVarDescr.empty()) return true; 313 constructVarViewCliques(); 314 if (!decomposeDomains()) return false; 315 if (fVerbose) printStats(std::cerr); 316 return true; 317 } 318 319private: 320 Env* __env = 0; 321 Env* getEnv() { 322 MZN_MIPD__assert_hard(__env); 323 return __env; 324 } 325 326 typedef VarDecl* PVarDecl; 327 328 FunctionI* int_lin_eq; 329 FunctionI* int_lin_le; 330 FunctionI* float_lin_eq; 331 FunctionI* float_lin_le; 332 FunctionI* int2float; 333 FunctionI* lin_exp_int; 334 FunctionI* lin_exp_float; 335 336 std::vector<Type> int_lin_eq_t = make_vec(Type::parint(1), Type::varint(1), Type::parint()); 337 std::vector<Type> float_lin_eq_t = 338 make_vec(Type::parfloat(1), Type::varfloat(1), Type::parfloat()); 339 std::vector<Type> t_VIVF = make_vec(Type::varint(), Type::varfloat()); 340 341 // double float_lt_EPS_coef__ = 1e-5; 342 343 bool registerLinearConstraintDecls() { 344 EnvI& env = getEnv()->envi(); 345 GCLock lock; 346 347 int_lin_eq = env.model->matchFn(env, constants().ids.int_.lin_eq, int_lin_eq_t, false); 348 DBGOUT_MIPD(" int_lin_eq = " << int_lin_eq); 349 // MZN_MIPD__assert_hard(fi); 350 // int_lin_eq = (fi && fi->e()) ? fi : NULL; 351 int_lin_le = env.model->matchFn(env, constants().ids.int_.lin_le, int_lin_eq_t, false); 352 float_lin_eq = env.model->matchFn(env, constants().ids.float_.lin_eq, float_lin_eq_t, false); 353 float_lin_le = env.model->matchFn(env, constants().ids.float_.lin_le, float_lin_eq_t, false); 354 int2float = env.model->matchFn(env, constants().ids.int2float, t_VIVF, false); 355 356 lin_exp_int = env.model->matchFn(env, constants().ids.lin_exp, int_lin_eq_t, false); 357 lin_exp_float = env.model->matchFn(env, constants().ids.lin_exp, float_lin_eq_t, false); 358 359 if (!(int_lin_eq && int_lin_le && float_lin_eq && float_lin_le)) { 360 // say something... 361 return false; 362 } 363 return true; 364 365 // std::cerr << " lin_exp_int=" << lin_exp_int << std::endl; 366 // std::cerr << " lin_exp_float=" << lin_exp_float << std::endl; 367 // For this to work, need to define a function, see mzn_only_range_domains() 368 // { 369 // GCLock lock; 370 // Call* call_EPS_for_LT = 371 // new Call(Location(),"mzn_float_lt_EPS_coef__", std::vector<Expression*>()); 372 // call_EPS_for_LT->type(Type::parfloat()); 373 // call_EPS_for_LT->decl(env.model->matchFn(getEnv()->envi(), call_EPS_for_LT)); 374 // float_lt_EPS_coef__ = eval_float(getEnv()->envi(), call_EPS_for_LT); 375 // } 376 } 377 // bool matchAndMarkFunction(); 378 // std::set<FunctionI*> funcs; 379 380 /// Possible function param sets 381 std::vector<Type> t_VII = make_vec(Type::varint(), Type::parint()); 382 std::vector<Type> t_VIVI = make_vec(Type::varint(), Type::varint()); 383 std::vector<Type> t_VIIVI = make_vec(Type::varint(), Type::parint(), Type::varint()); 384 std::vector<Type> t_VFVI = make_vec(Type::varfloat(), Type::varint()); 385 std::vector<Type> t_VFVF = make_vec(Type::varfloat(), Type::varfloat()); 386 std::vector<Type> t_VFFVI = make_vec(Type::varfloat(), Type::parfloat(), Type::varint()); 387 std::vector<Type> t_VFFVIF = 388 make_vec(Type::varfloat(), Type::parfloat(), Type::varint(), Type::parfloat()); 389 std::vector<Type> t_VFVIF = make_vec(Type::varfloat(), Type::varint(), Type::parfloat()); 390 std::vector<Type> t_VFVIVF = make_vec(Type::varfloat(), Type::varint(), Type::varfloat()); 391 std::vector<Type> t_VFVIVFF = 392 make_vec(Type::varfloat(), Type::varint(), Type::varfloat(), Type::parfloat()); 393 std::vector<Type> t_VFVFF = make_vec(Type::varfloat(), Type::varfloat(), Type::parfloat()); 394 std::vector<Type> t_VFFF = make_vec(Type::varfloat(), Type::parfloat(), Type::parfloat()); 395 // std::vector<Type> t_VFVFVIF({ Type::varfloat(), Type::varfloat(), Type::varint(), 396 // Type::parfloat() }); 397 398 std::vector<Type> t_VIAVI = make_vec(Type::varint(), Type::varint(1)); 399 std::vector<Type> t_VISI = make_vec(Type::varint(), Type::parsetint()); 400 std::vector<Type> t_VISIVI = make_vec(Type::varint(), Type::parsetint(), Type::varint()); 401 402 // std::vector<Type> t_intarray(1); 403 // t_intarray[0] = Type::parint(-1); 404 405 typedef std::unordered_map<FunctionI*, DCT*> M__POSTCallTypes; 406 M__POSTCallTypes mCallTypes; // actually declared in the input 407 std::vector<DCT> aCT; // all possible 408 409 // Fails: 410 // DomainCallType a = { NULL, t_VII, RIT_Halfreif, CT_Comparison, CMPT_EQ, VT_Float }; 411 412 /// struct VarDescr stores some info about variables involved in domain constr 413 struct VarDescr { 414 typedef unsigned char boolShort; 415 VarDescr(VarDecl* vd_, boolShort fi, double l_ = 0.0, double u_ = 0.0) 416 : lb(l_), ub(u_), vd(vd_), fInt(fi) {} 417 double lb, ub; 418 VarDecl* vd = 0; 419 int nClique = -1; // clique number 420 // std::vector<Call*> aCalls; 421 std::vector<ConstraintI*> aCalls; 422 boolShort fInt = 0; 423 ConstraintI* pEqEncoding = 0; 424 boolShort fDomainConstrProcessed = 0; 425 // boolShort fPropagatedViews=0; 426 // boolShort fPropagatedLargerEqns=0; 427 }; 428 429 std::vector<VarDescr> vVarDescr; 430 431 FunctionI *int_le_reif__POST = 0, *int_ge_reif__POST = 0, *int_eq_reif__POST = 0, 432 *int_ne__POST = 0, *float_le_reif__POST = 0, *float_ge_reif__POST = 0, 433 *aux_float_lt_zero_iff_1__POST = 0, *float_eq_reif__POST = 0, *float_ne__POST = 0, 434 *aux_float_eq_zero_if_1__POST = 0, *aux_int_le_zero_if_1__POST = 0, 435 *aux_float_le_zero_if_1__POST = 0, *aux_float_lt_zero_if_1__POST = 0, 436 *equality_encoding__POST = 0, *set_in__POST = 0, *set_in_reif__POST = 0; 437 438 bool register__POSTconstraintDecls() { 439 EnvI& env = getEnv()->envi(); 440 GCLock lock; 441 442 aCT.clear(); 443 aCT.push_back(DCT("int_le_reif__POST", t_VIIVI, RIT_Reif, CT_Comparison, CMPT_LE, VT_Int, 444 int_le_reif__POST)); 445 aCT.push_back(DCT("int_ge_reif__POST", t_VIIVI, RIT_Reif, CT_Comparison, CMPT_GE, VT_Int, 446 int_ge_reif__POST)); 447 aCT.push_back(DCT("int_eq_reif__POST", t_VIIVI, RIT_Reif, CT_Comparison, CMPT_EQ, VT_Int, 448 int_eq_reif__POST)); 449 aCT.push_back( 450 DCT("int_ne__POST", t_VII, RIT_Static, CT_Comparison, CMPT_NE, VT_Int, int_ne__POST)); 451 452 aCT.push_back(DCT("float_le_reif__POST", t_VFFVIF, RIT_Reif, CT_Comparison, CMPT_LE, VT_Float, 453 float_le_reif__POST)); 454 aCT.push_back(DCT("float_ge_reif__POST", t_VFFVIF, RIT_Reif, CT_Comparison, CMPT_GE, VT_Float, 455 float_ge_reif__POST)); 456 aCT.push_back(DCT("aux_float_lt_zero_iff_1__POST", t_VFVIF, RIT_Reif, CT_Comparison, CMPT_LT, 457 VT_Float, aux_float_lt_zero_iff_1__POST)); 458 aCT.push_back(DCT("float_eq_reif__POST", t_VFFVIF, RIT_Reif, CT_Comparison, CMPT_EQ, VT_Float, 459 float_eq_reif__POST)); 460 aCT.push_back(DCT("float_ne__POST", t_VFFF, RIT_Static, CT_Comparison, CMPT_NE, VT_Float, 461 float_ne__POST)); 462 463 aCT.push_back(DCT("aux_float_eq_zero_if_1__POST", t_VFVIVF, RIT_Halfreif, CT_Comparison, 464 CMPT_EQ_0, VT_Float, aux_float_eq_zero_if_1__POST)); 465 aCT.push_back(DCT("aux_int_le_zero_if_1__POST", t_VIVI, RIT_Halfreif, CT_Comparison, CMPT_LE_0, 466 VT_Int, aux_int_le_zero_if_1__POST)); 467 aCT.push_back(DCT("aux_float_le_zero_if_1__POST", t_VFVIVF, RIT_Halfreif, CT_Comparison, 468 CMPT_LE_0, VT_Float, aux_float_le_zero_if_1__POST)); 469 aCT.push_back(DCT("aux_float_lt_zero_if_1__POST", t_VFVIVFF, RIT_Halfreif, CT_Comparison, 470 CMPT_LT_0, VT_Float, aux_float_lt_zero_if_1__POST)); 471 472 aCT.push_back(DCT("equality_encoding__POST", t_VIAVI, RIT_Static, CT_Encode, CMPT_None, VT_Int, 473 equality_encoding__POST)); 474 aCT.push_back( 475 DCT("set_in__POST", t_VISI, RIT_Static, CT_SetIn, CMPT_None, VT_Int, set_in__POST)); 476 aCT.push_back(DCT("set_in_reif__POST", t_VISIVI, RIT_Reif, CT_SetIn, CMPT_None, VT_Int, 477 set_in_reif__POST)); 478 /// Registering all declared & compatible __POST constraints 479 /// (First, cleanup FunctionIs' payload: -- ! doing now) 480 for (int i = 0; i < aCT.size(); ++i) { 481 FunctionI* fi = env.model->matchFn(env, ASTString(aCT[i].sFuncName), aCT[i].aParams, false); 482 if (fi) { 483 mCallTypes[fi] = aCT.data() + i; 484 aCT[i].pfi = fi; 485 // fi->pPayload = (void*)this; 486 // std::cerr << " FOund declaration: " << aCT[i].sFuncName << std::endl; 487 } else { 488 aCT[i].pfi = 0; 489 DBGOUT_MIPD(" MIssing declaration: " << aCT[i].sFuncName); 490 return false; 491 } 492 } 493 return true; 494 } 495 496 /// Registering all __POST calls' domain-constrained variables 497 void register__POSTvariables() { 498 EnvI& env = getEnv()->envi(); 499 GCLock lock; 500 Model& mFlat = *getEnv()->flat(); 501 // First, cleanup VarDecls' payload which stores index in vVarDescr 502 for (VarDeclIterator ivd = mFlat.begin_vardecls(); ivd != mFlat.end_vardecls(); ++ivd) { 503 ivd->e()->payload(-1); 504 } 505 // Now add variables with non-contiguous domain 506 for (VarDeclIterator ivd = mFlat.begin_vardecls(); ivd != mFlat.end_vardecls(); ++ivd) { 507 VarDecl* vd0 = ivd->e(); 508 bool fNonCtg = 0; 509 if (vd0->type().isint()) { // currently only for int vars TODO 510 if (Expression* eDom = vd0->ti()->domain()) { 511 IntSetVal* dom = eval_intset(env, eDom); 512 fNonCtg = (dom->size() > 1); 513 } 514 } 515 if (fNonCtg) { 516 DBGOUT_MIPD(" Variable " << vd0->id()->str() << ": non-contiguous domain " 517 << (*(vd0->ti()->domain()))); 518 if (vd0->payload() == -1) { // ! yet visited 519 vd0->payload(static_cast<int>(vVarDescr.size())); 520 vVarDescr.push_back(VarDescr(vd0, vd0->type().isint())); // can use /prmTypes/ as well 521 if (vd0->e()) checkInitExpr(vd0); 522 } else { 523 DBGOUT_MIPD__(" (already touched)"); 524 } 525 ++MIPD__stats[N_POSTs__domain]; 526 ++MIPD__stats[N_POSTs__all]; 527 } 528 } 529 // Iterate thru original __POST constraints to mark constrained vars: 530 for (ConstraintIterator ic = mFlat.begin_constraints(); ic != mFlat.end_constraints(); ++ic) { 531 if (ic->removed()) continue; 532 if (Call* c = ic->e()->dyn_cast<Call>()) { 533 auto ipct = mCallTypes.find(c->decl()); 534 if (ipct != mCallTypes.end()) { 535 // No ! here because might be deleted immediately in later versions. 536 // ic->remove(); // mark removed at once 537 MZN_MIPD__assert_hard(c->n_args() > 1); 538 ++MIPD__stats[N_POSTs__all]; 539 VarDecl* vd0 = expr2VarDecl(c->arg(0)); 540 if (0 == vd0) { 541 DBGOUT_MIPD__(" Call " << *c << ": 1st arg not a VarDecl, removing if eq_encoding..."); 542 /// Only allow literals as main argument for equality_encoding 543 if (equality_encoding__POST == 544 ipct->first) // was MZN_MIPD__assert_hard before MZN 2017 545 ic->remove(); 546 continue; // ignore this call 547 } 548 DBGOUT_MIPD__(" Call " << c->id().str() << " uses variable " << vd0->id()->str()); 549 if (vd0->payload() == -1) { // ! yet visited 550 vd0->payload(static_cast<int>(vVarDescr.size())); 551 vVarDescr.push_back(VarDescr(vd0, vd0->type().isint())); // can use /prmTypes/ as well 552 // bounds/domains later for each involved var TODO 553 if (vd0->e()) checkInitExpr(vd0); 554 } else { 555 DBGOUT_MIPD__(" (already touched)"); 556 } 557 DBGOUT_MIPD(""); 558 if (equality_encoding__POST == c->decl()) { 559 MZN_MIPD__assert_hard(!vVarDescr[vd0->payload()].pEqEncoding); 560 vVarDescr[vd0->payload()].pEqEncoding = &*ic; 561 DBGOUT_MIPD(" Variable " << vd0->id()->str() << " has eq_encode."); 562 } // + if has aux_ constraints? 563 else 564 vVarDescr[vd0->payload()].aCalls.push_back(&*ic); 565 } 566 } 567 } 568 MIPD__stats[N_POSTs__varsDirect] = static_cast<double>(vVarDescr.size()); 569 } 570 571 // Should only be called on a newly added variable 572 // OR when looking thru all non-touched vars 573 /// Checks init expr of a variable 574 /// Return true IFF new connection 575 /// The bool param requires RHS to be POST-touched 576 // Guido: can! be recursive in FZN 577 bool checkInitExpr(VarDecl* vd, bool fCheckArg = false) { 578 MZN_MIPD__assert_hard(vd->e()); 579 if (!vd->type().isint() && !vd->type().isfloat()) return false; 580 if (!fCheckArg) MZN_MIPD__assert_hard(vd->payload() >= 0); 581 if (Id* id = vd->e()->dyn_cast<Id>()) { 582 // const int f1 = ( vd->payload()>=0 ); 583 // const int f2 = ( id->decl()->payload()>=0 ); 584 if (!fCheckArg || (id->decl()->payload() >= 0)) { 585 DBGOUT_MIPD__(" Checking init expr "); 586 DBGOUT_MIPD_SELF(debugprint(vd)); 587 LinEq2Vars led; 588 // FAILS: 589 // led.vd = { vd, expr2VarDecl(id->decl()->e()) }; 590 led.vd = {{vd, expr2VarDecl(vd->e())}}; 591 led.coefs = {{1.0, -1.0}}; 592 led.rhs = 0.0; 593 put2VarsConnection(led, false); 594 ++MIPD__stats[N_POSTs__initexpr1id]; 595 if (id->decl()->e()) // no initexpr for initexpr FAILS on cc-base.mzn 596 checkInitExpr(id->decl()); 597 return true; // in any case 598 } 599 } else if (Call* c = vd->e()->dyn_cast<Call>()) { 600 if (lin_exp_int == c->decl() || lin_exp_float == c->decl()) { 601 // std::cerr << " !E call " << std::flush; 602 // debugprint(c); 603 MZN_MIPD__assert_hard(c->n_args() == 3); 604 // ArrayLit* al = c->args()[1]->dyn_cast<ArrayLit>(); 605 ArrayLit* al = follow_id(c->arg(1))->cast<ArrayLit>(); 606 MZN_MIPD__assert_hard(al); 607 MZN_MIPD__assert_hard(al->size() >= 1); 608 if (al->size() == 1) { // 1-term scalar product in the rhs 609 LinEq2Vars led; 610 led.vd = {{vd, expr2VarDecl((*al)[0])}}; 611 // const int f1 = ( vd->payload()>=0 ); 612 // const int f2 = ( led.vd[1]->payload()>=0 ); 613 if (!fCheckArg || (led.vd[1]->payload() >= 0)) { 614 // Can use a!her map here: 615 // if ( sCallLinEq2.end() != sCallLinEq2.find(c) ) 616 // continue; 617 // sCallLinEq2.insert(c); // memorize this call 618 DBGOUT_MIPD__(" REG 1-LINEXP "); 619 DBGOUT_MIPD_SELF(debugprint(vd)); 620 std::array<double, 1> coef0; 621 expr2Array(c->arg(0), coef0); 622 led.coefs = {{-1.0, coef0[0]}}; 623 led.rhs = -expr2Const(c->arg(2)); // MINUS 624 put2VarsConnection(led, false); 625 ++MIPD__stats[N_POSTs__initexpr1linexp]; 626 if (led.vd[1]->e()) // no initexpr for initexpr FAILS TODO 627 checkInitExpr(led.vd[1]); 628 return true; // in any case 629 } 630 } else if (true) { // check larger views always. OK? TODO 631 // if ( vd->payload()>=0 ) { // larger views 632 // TODO should be here? 633 // std::cerr << " LE_" << al->v().size() << ' ' << std::flush; 634 DBGOUT_MIPD(" REG N-LINEXP "); 635 DBGOUT_MIPD_SELF(debugprint(vd)); 636 // Checking all but adding only touched defined vars? 637 return findOrAddDefining(vd->id(), c); 638 } 639 } 640 } 641 return false; 642 } 643 644 /// Build var cliques (i.e. of var pairs viewing each other) 645 void constructVarViewCliques() { 646 // std::cerr << " Model: " << std::endl; 647 // debugprint(getEnv()->flat()); 648 649 // TAgenda agenda(vVarDescr.size()), agendaNext; 650 // for ( int i=0; i<agenda.size(); ++i ) 651 // agenda[i] = i; 652 bool fChanges; 653 do { 654 fChanges = false; 655 propagateViews(fChanges); 656 propagateImplViews(fChanges); 657 } while (fChanges); 658 659 MIPD__stats[N_POSTs__varsInvolved] = static_cast<double>(vVarDescr.size()); 660 } 661 662 void propagateViews(bool& fChanges) { 663 // EnvI& env = getEnv()->envi(); 664 GCLock lock; 665 666 // Iterate thru original 2-variable equalities to mark views: 667 Model& mFlat = *getEnv()->flat(); 668 669 DBGOUT_MIPD(" CHECK ALL INITEXPR if they access a touched variable:"); 670 for (VarDeclIterator ivd = mFlat.begin_vardecls(); ivd != mFlat.end_vardecls(); ++ivd) { 671 if (ivd->removed()) continue; 672 if (ivd->e()->e() && ivd->e()->payload() < 0 // untouched 673 && (ivd->e()->type().isint() || ivd->e()->type().isfloat())) // scalars 674 if (checkInitExpr(ivd->e(), true)) fChanges = true; 675 } 676 677 DBGOUT_MIPD(" CHECK ALL CONSTRAINTS for 2-var equations:"); 678 for (ConstraintIterator ic = mFlat.begin_constraints(); ic != mFlat.end_constraints(); ++ic) { 679 // std::cerr << " SEE constraint: " << " "; 680 // debugprint(&*ic); 681 // debugprint(c->decl()); 682 if (ic->removed()) continue; 683 if (Call* c = ic->e()->dyn_cast<Call>()) { 684 const bool fIntLinEq = int_lin_eq == c->decl(); 685 const bool fFloatLinEq = float_lin_eq == c->decl(); 686 if (fIntLinEq || fFloatLinEq) { 687 // std::cerr << " !E call " << std::flush; 688 // debugprint(c); 689 MZN_MIPD__assert_hard(c->n_args() == 3); 690 ArrayLit* al = follow_id(c->arg(1))->cast<ArrayLit>(); 691 MZN_MIPD__assert_hard(al); 692 if (al->size() == 2) { // 2-term eqn 693 LinEq2Vars led; 694 expr2DeclArray(c->arg(1), led.vd); 695 // At least 1 touched var: 696 if (led.vd[0]->payload() >= 0 || led.vd[1]->payload() >= 0) { 697 if (sCallLinEq2.end() != sCallLinEq2.find(c)) continue; 698 sCallLinEq2.insert(c); // memorize this call 699 DBGOUT_MIPD(" REG 2-call "); 700 DBGOUT_MIPD_SELF(debugprint(c)); 701 led.rhs = expr2Const(c->arg(2)); 702 expr2Array(c->arg(0), led.coefs); 703 MZN_MIPD__assert_hard(2 == led.coefs.size()); 704 fChanges = true; 705 put2VarsConnection(led); 706 ++MIPD__stats[fIntLinEq ? N_POSTs__eq2intlineq : N_POSTs__eq2floatlineq]; 707 } 708 } else if (al->size() == 1) { 709 static int nn = 0; 710 if (++nn <= 7) { 711 std::cerr << " MIPD: LIN_EQ with 1 variable::: " << std::flush; 712 std::cerr << (*c) << std::endl; 713 } 714 } else { // larger eqns 715 // TODO should be here? 716 auto eVD = getAnnotation(c->ann(), constants().ann.defines_var); 717 if (eVD) { 718 if (sCallLinEqN.end() != sCallLinEqN.find(c)) continue; 719 sCallLinEqN.insert(c); // memorize this call 720 DBGOUT_MIPD(" REG N-call "); 721 DBGOUT_MIPD_SELF(debugprint(c)); 722 Call* pC = eVD->dyn_cast<Call>(); 723 MZN_MIPD__assert_hard(pC); 724 MZN_MIPD__assert_hard(pC->n_args()); 725 // Checking all but adding only touched defined vars? Seems too long. 726 VarDecl* vd = expr2VarDecl(pC->arg(0)); 727 if (vd->payload() >= 0) // only if touched 728 if (findOrAddDefining(pC->arg(0), c)) fChanges = true; 729 } 730 } 731 } else 732 // const bool fI2F = (int2float==c->decl()); 733 // const bool fIVR = (constants().var_redef==c->decl()); 734 // if ( fI2F || fIVR ) { 735 if (int2float == c->decl() || constants().var_redef == c->decl()) { 736 // std::cerr << " !E call " << std::flush; 737 // debugprint(c); 738 MZN_MIPD__assert_hard(c->n_args() == 2); 739 LinEq2Vars led; 740 // led.vd.resize(2); 741 led.vd[0] = expr2VarDecl(c->arg(0)); 742 led.vd[1] = expr2VarDecl(c->arg(1)); 743 // At least 1 touched var: 744 if (led.vd[0]->payload() >= 0 || led.vd[1]->payload() >= 0) { 745 if (sCallInt2Float.end() != sCallInt2Float.find(c)) continue; 746 sCallInt2Float.insert(c); // memorize this call 747 DBGOUT_MIPD(" REG call "); 748 DBGOUT_MIPD_SELF(debugprint(c)); 749 led.rhs = 0.0; 750 led.coefs = {{1.0, -1.0}}; 751 fChanges = true; 752 put2VarsConnection(led); 753 ++MIPD__stats[int2float == c->decl() ? N_POSTs__int2float : N_POSTs__internalvarredef]; 754 } 755 } 756 } 757 } 758 } 759 760 /// This vector stores the linear part of a general view 761 /// x = <linear part> + rhs 762 typedef std::vector<std::pair<VarDecl*, double> > TLinExpLin; 763 /// This struct has data describing the rest of a general view 764 struct NViewData { 765 VarDecl* pVarDefined = 0; 766 double coef0 = 1.0; 767 double rhs; 768 }; 769 typedef std::map<TLinExpLin, NViewData> NViewMap; 770 NViewMap mNViews; 771 772 /// compare to an existing defining linexp, || just add it to the map 773 /// adds only touched defined vars 774 /// return true iff new linear connection 775 // linexp: z = a^T x+b 776 // _lin_eq: a^T x == b 777 bool findOrAddDefining(Expression* exp, Call* pC) { 778 Id* pId = exp->dyn_cast<Id>(); 779 MZN_MIPD__assert_hard(pId); 780 VarDecl* vd = pId->decl(); 781 MZN_MIPD__assert_hard(vd); 782 MZN_MIPD__assert_hard(pC->n_args() == 3); 783 784 TLinExpLin rhsLin; 785 NViewData nVRest; 786 nVRest.pVarDefined = vd; 787 nVRest.rhs = expr2Const(pC->arg(2)); 788 789 std::vector<VarDecl*> vars; 790 expr2DeclArray(pC->arg(1), vars); 791 std::vector<double> coefs; 792 expr2Array(pC->arg(0), coefs); 793 MZN_MIPD__assert_hard(vars.size() == coefs.size()); 794 795 int nVD = 0; 796 for (int i = 0; i < vars.size(); ++i) { 797 // MZN_MIPD__assert_hard( 0.0!=std::fabs 798 if (vd == vars[i]) { // the defined var 799 nVRest.coef0 = -coefs[i]; 800 nVRest.rhs = -nVRest.rhs; 801 ++nVD; 802 } else 803 rhsLin.push_back(std::make_pair(vars[i], coefs[i])); 804 } 805 MZN_MIPD__assert_hard(1 >= nVD); 806 std::sort(rhsLin.begin(), rhsLin.end()); 807 808 // Divide the equation by the 1st coef 809 const double coef1 = rhsLin.begin()->second; 810 MZN_MIPD__assert_hard(0.0 != std::fabs(coef1)); 811 nVRest.coef0 /= coef1; 812 nVRest.rhs /= coef1; 813 for (auto& rhsL : rhsLin) rhsL.second /= coef1; 814 815 auto it = mNViews.find(rhsLin); 816 if (mNViews.end() != it) { 817 LinEq2Vars leq; 818 leq.vd = {{nVRest.pVarDefined, it->second.pVarDefined}}; 819 leq.coefs = {{nVRest.coef0, -it->second.coef0}}; // +, - 820 leq.rhs = nVRest.rhs - it->second.rhs; 821 put2VarsConnection(leq, false); 822 ++MIPD__stats[nVD ? N_POSTs__eqNlineq : N_POSTs__initexprN]; 823 return true; 824 } else { 825 if (vd->payload() >= 0) // only touched 826 mNViews[rhsLin] = nVRest; 827 } 828 829 return false; 830 } 831 832 void propagateImplViews(bool& fChanges) { 833 // EnvI& env = getEnv()->envi(); 834 GCLock lock; 835 836 // TODO 837 } 838 839 /// Could be better to mark the calls instead: 840 std::unordered_set<Call*> sCallLinEq2, sCallInt2Float, sCallLinEqN; 841 842 class TClique : public std::vector<LinEq2Vars> { // need more info? 843 public: 844 /// This function takes the 1st variable && relates all to it 845 /// Return false if contrad / disconnected graph 846 // bool findRelations0() { 847 // return true; 848 // } 849 }; 850 typedef std::vector<TClique> TCLiqueList; 851 TCLiqueList aCliques; 852 853 /// register a 2-variable lin eq 854 /// add it to the var clique, joining the participants' cliques if needed 855 void put2VarsConnection(LinEq2Vars& led, bool fCheckinitExpr = true) { 856 MZN_MIPD__assert_hard(led.coefs.size() == led.vd.size()); 857 MZN_MIPD__assert_hard(led.vd.size() == 2); 858 DBGOUT_MIPD__(" Register 2-var connection: " << led); 859 // register if new variables 860 // std::vector<bool> fHaveClq(led.vd.size(), false); 861 int nCliqueAvailable = -1; 862 for (auto vd : led.vd) { 863 if (vd->payload() < 0) { // ! yet visited 864 vd->payload(static_cast<int>(vVarDescr.size())); 865 vVarDescr.push_back(VarDescr(vd, vd->type().isint())); // can use /prmTypes/ as well 866 if (fCheckinitExpr && vd->e()) checkInitExpr(vd); 867 } else { 868 int nMaybeClq = vVarDescr[vd->payload()].nClique; 869 if (nMaybeClq >= 0) nCliqueAvailable = nMaybeClq; 870 // MZN_MIPD__assert_hard( nCliqueAvailable>=0 ); 871 // fHaveClq[i] = true; 872 } 873 } 874 if (nCliqueAvailable < 0) { // no clique found 875 nCliqueAvailable = static_cast<int>(aCliques.size()); 876 aCliques.resize(aCliques.size() + 1); 877 } 878 DBGOUT_MIPD(" ...adding to clique " << nCliqueAvailable << " of size " 879 << aCliques[nCliqueAvailable].size()); 880 TClique& clqNew = aCliques[nCliqueAvailable]; 881 clqNew.push_back(led); 882 for (auto vd : led.vd) { // merging cliques 883 int& nMaybeClq = vVarDescr[vd->payload()].nClique; 884 if (nMaybeClq >= 0 && nMaybeClq != nCliqueAvailable) { 885 TClique& clqOld = aCliques[nMaybeClq]; 886 MZN_MIPD__assert_hard(clqOld.size()); 887 for (auto& eq2 : clqOld) { 888 for (auto vd : eq2.vd) { // point all the variables to the new clique 889 vVarDescr[vd->payload()].nClique = nCliqueAvailable; 890 } 891 } 892 clqNew.insert(clqNew.end(), clqOld.begin(), clqOld.end()); 893 clqOld.clear(); // Can use C++11 move TODO 894 DBGOUT_MIPD(" +++ Joining cliques"); 895 } 896 nMaybeClq = nCliqueAvailable; // Could mark as 'unused' TODO 897 } 898 } 899 900 /// Finds a clique variable to which all domain constr are related 901 class TCliqueSorter { 902 MIPD& mipd; 903 const int iVarStart; // this is the first var to which all others are related 904 public: 905 // VarDecl* varRef0=0; // this is the first var to which all others are related 906 VarDecl* varRef1 = 0; // this is the 2nd main reference. 907 // it is a var with eq_encode, || 908 // an (integer if any) variable with the least rel. factor 909 bool fRef1HasEqEncode = false; 910 /// This map stores the relations y = ax+b of all the clique's vars to y 911 typedef std::unordered_map<VarDecl*, std::pair<double, double> > TMapVars; 912 TMapVars mRef0, mRef1; // to the main var 0, 1 913 914 class TMatrixVars : public std::unordered_map<VarDecl*, TMapVars> { 915 public: 916 /// Check existing connection 917 template <class IVarDecl> 918 bool checkExistingArc(IVarDecl begV, double A, double B, bool fReportRepeat = true) { 919 auto it1 = this->find(*begV); 920 if (this->end() != it1) { 921 auto it2 = it1->second.find(*(begV + 1)); 922 if (it1->second.end() != it2) { 923 MZN_MIPD__assert_hard(std::fabs(it2->second.first - A) < 924 1e-6 * std::max(std::fabs(it2->second.first), std::fabs(A))); 925 MZN_MIPD__assert_hard(std::fabs(it2->second.second - B) < 926 1e-6 * std::max(std::fabs(it2->second.second), std::fabs(B)) + 927 1e-6); 928 MZN_MIPD__assert_hard(std::fabs(A) != 0.0); 929 MZN_MIPD__assert_soft(!fVerbose || std::fabs(A) > 1e-12, 930 " Very small coef: " << (*begV)->id()->str() << " = " << A 931 << " * " << (*(begV + 1))->id()->str() 932 << " + " << B); 933 if (fReportRepeat) 934 MZN_MIPD__assert_soft(!fVerbose, "LinEqGraph: eqn between " 935 << (*begV)->id()->str() << " && " 936 << (*(begV + 1))->id()->str() 937 << " is repeated. "); 938 return true; 939 } 940 } 941 return false; 942 } 943 }; 944 945 class LinEqGraph : public TMatrixVars { 946 public: 947 static double dCoefMin, dCoefMax; 948 /// Stores the arc (x1, x2) as x1 = a*x2 + b 949 /// so that a constraint on x2, say x2<=c <-> f, 950 /// is equivalent to one for x1: x1 <=/>= a*c+b <-> f 951 //// ( the other way involves division: 952 //// so that a constraint on x1, say x1<=c <-> f, 953 //// can easily be converted into one for x2 as a*x2 <= c-b <-> f 954 //// <=> x2 (care for sign) (c-b)/a <-> f ) 955 956 template <class ICoef, class IVarDecl> 957 void addArc(ICoef begC, IVarDecl begV, double rhs) { 958 MZN_MIPD__assert_soft(!fVerbose || std::fabs(*begC) >= 1e-10, 959 " Vars " << (*begV)->id()->str() << " to " 960 << (*(begV + 1))->id()->str() << ": coef=" << (*begC)); 961 // Transform Ax+By=C into x = -B/Ay+C/A 962 const double negBA = -(*(begC + 1)) / (*begC); 963 const double CA = rhs / (*begC); 964 checkExistingArc(begV, negBA, CA); 965 (*this)[*begV][*(begV + 1)] = std::make_pair(negBA, CA); 966 const double dCoefAbs = std::fabs(negBA); 967 if (dCoefAbs < dCoefMin) dCoefMin = dCoefAbs; 968 if (dCoefAbs > dCoefMax) dCoefMax = dCoefAbs; 969 } 970 void addEdge(const LinEq2Vars& led) { 971 addArc(led.coefs.begin(), led.vd.begin(), led.rhs); 972 addArc(led.coefs.rbegin(), led.vd.rbegin(), led.rhs); 973 } 974 /// Propagate linear relations from the given variable 975 void propagate(iterator itStart, TMapVars& mWhereStore) { 976 MZN_MIPD__assert_hard(this->end() != itStart); 977 TMatrixVars mTemp; 978 mTemp[itStart->first] = itStart->second; // init with existing 979 DBGOUT_MIPD("Propagation started from " << itStart->first->id()->str() << " having " 980 << itStart->second.size() << " connections"); 981 propagate2(itStart, itStart, std::make_pair(1.0, 0.0), mTemp); 982 mWhereStore = mTemp.begin()->second; 983 MZN_MIPD__assert_hard_msg( 984 mWhereStore.size() == this->size() - 1, 985 "Variable " << (*(mTemp.begin()->first)) 986 << " should be connected to all others in the clique, but " 987 << "|edges|==" << mWhereStore.size() << ", |all nodes|==" << this->size()); 988 } 989 /// Propagate linear relations from it1 via it2 990 void propagate2(iterator itSrc, iterator itVia, std::pair<double, double> rel, 991 TMatrixVars& mWhereStore) { 992 for (auto itDst = itVia->second.begin(); itDst != itVia->second.end(); ++itDst) { 993 // Transform x1=A1x2+B1, x2=A2x3+B2 into x1=A1A2x3+A1B2+B1 994 if (itDst->first == itSrc->first) continue; 995 const double A1A2 = rel.first * itDst->second.first; 996 const double A1B2plusB1 = rel.first * itDst->second.second + rel.second; 997 bool fDive = true; 998 if (itSrc != itVia) { 999 PVarDecl vd[2] = {itSrc->first, itDst->first}; 1000 if (!mWhereStore.checkExistingArc(vd, A1A2, A1B2plusB1, false)) { 1001 mWhereStore[vd[0]][vd[1]] = std::make_pair(A1A2, A1B2plusB1); 1002 DBGOUT_MIPD(" PROPAGATING: " << vd[0]->id()->str() << " = " << A1A2 << " * " 1003 << vd[1]->id()->str() << " + " << A1B2plusB1); 1004 } else 1005 fDive = false; 1006 } 1007 if (fDive) { 1008 auto itDST = this->find(itDst->first); 1009 MZN_MIPD__assert_hard(this->end() != itDST); 1010 propagate2(itSrc, itDST, std::make_pair(A1A2, A1B2plusB1), mWhereStore); 1011 } 1012 } 1013 } 1014 }; 1015 LinEqGraph leg; 1016 1017 TCliqueSorter(MIPD* pm, int iv) : mipd(*pm), iVarStart(iv) {} 1018 void doRelate() { 1019 MZN_MIPD__assert_hard(mipd.vVarDescr[iVarStart].nClique >= 0); 1020 const TClique& clq = mipd.aCliques[mipd.vVarDescr[iVarStart].nClique]; 1021 for (auto& eq2 : clq) { 1022 leg.addEdge(eq2); 1023 } 1024 DBGOUT_MIPD(" Clique " << mipd.vVarDescr[iVarStart].nClique << ": " << leg.size() 1025 << " variables, " << clq.size() << " connections."); 1026 for (auto it1 = leg.begin(); it1 != leg.end(); ++it1) 1027 mipd.vVarDescr[it1->first->payload()].fDomainConstrProcessed = true; 1028 1029 // Propagate the 1st var's relations: 1030 leg.propagate(leg.begin(), mRef0); 1031 1032 // Find a best main variable according to: 1033 // 1. isInt 2. hasEqEncode 3. abs linFactor to ref0 1034 varRef1 = leg.begin()->first; 1035 std::array<double, 3> aCrit = { 1036 {(double)mipd.vVarDescr[varRef1->payload()].fInt, 1037 static_cast<double>(mipd.vVarDescr[varRef1->payload()].pEqEncoding != NULL), 1.0}}; 1038 for (auto it2 = mRef0.begin(); it2 != mRef0.end(); ++it2) { 1039 VarDescr& vard = mipd.vVarDescr[it2->first->payload()]; 1040 std::array<double, 3> aCrit1 = {{(double)vard.fInt, 1041 static_cast<double>(vard.pEqEncoding != NULL), 1042 std::fabs(it2->second.first)}}; 1043 if (aCrit1 > aCrit) { 1044 varRef1 = it2->first; 1045 aCrit = aCrit1; 1046 } 1047 } 1048 leg.propagate(leg.find(varRef1), mRef1); 1049 } 1050 }; // class TCliqueSorter 1051 1052 /// Build a domain decomposition for a clique 1053 /// a clique can consist of just 1 var without a clique object 1054 class DomainDecomp { 1055 public: 1056 MIPD& mipd; 1057 const int iVarStart; 1058 TCliqueSorter cls; 1059 SetOfIntvReal sDomain; 1060 1061 DomainDecomp(MIPD* pm, int iv) : mipd(*pm), iVarStart(iv), cls(pm, iv) { 1062 sDomain.insert(IntvReal()); // the decomposed domain. Init to +-inf 1063 } 1064 void doProcess() { 1065 // Choose the main variable && relate all others to it 1066 const int nClique = mipd.vVarDescr[iVarStart].nClique; 1067 if (nClique >= 0) { 1068 cls.doRelate(); 1069 } else 1070 cls.varRef1 = mipd.vVarDescr[iVarStart].vd; 1071 // Adding itself: 1072 cls.mRef1[cls.varRef1] = std::make_pair(1.0, 0.0); 1073 1074 int iVarRef1 = cls.varRef1->payload(); 1075 MZN_MIPD__assert_hard(nClique == mipd.vVarDescr[iVarRef1].nClique); 1076 cls.fRef1HasEqEncode = (mipd.vVarDescr[iVarRef1].pEqEncoding != NULL); 1077 1078 // First, construct the domain decomposition in any case 1079 // projectVariableConstr( cls.varRef1, std::make_pair(1.0, 0.0) ); 1080 // if ( nClique >= 0 ) { 1081 for (auto& iRef1 : cls.mRef1) { 1082 projectVariableConstr(iRef1.first, iRef1.second); 1083 } 1084 1085 DBGOUT_MIPD("Clique " << nClique << ": main ref var " << cls.varRef1->id()->str() 1086 << ", domain dec: " << sDomain); 1087 1088 MZN_MIPD__assert_for_feas(sDomain.size(), "Clique " << nClique << ": main ref var " 1089 << cls.varRef1->id()->str() 1090 << ", domain dec: " << sDomain); 1091 1092 MZN_MIPD__assert_hard(sDomain.checkFiniteBounds()); 1093 MZN_MIPD__assert_hard(sDomain.checkDisjunctStrict()); 1094 1095 makeRangeDomains(); 1096 1097 // Then, use equality_encoding if available 1098 if (cls.fRef1HasEqEncode) { 1099 syncWithEqEncoding(); 1100 syncOtherEqEncodings(); 1101 } else { // ! cls.fRef1HasEqEncode 1102 if (sDomain.size() >= 2) { // need to simplify stuff otherwise 1103 considerDenseEncoding(); 1104 createDomainFlags(); 1105 } 1106 } 1107 implement__POSTs(); 1108 1109 // Statistics 1110 if (sDomain.size() < MIPD__stats[N_POSTs__NSubintvMin]) 1111 MIPD__stats[N_POSTs__NSubintvMin] = static_cast<double>(sDomain.size()); 1112 MIPD__stats[N_POSTs__NSubintvSum] += sDomain.size(); 1113 if (sDomain.size() > MIPD__stats[N_POSTs__NSubintvMax]) 1114 MIPD__stats[N_POSTs__NSubintvMax] = static_cast<double>(sDomain.size()); 1115 for (auto& intv : sDomain) { 1116 const auto nSubSize = intv.right - intv.left; 1117 if (nSubSize < MIPD__stats[N_POSTs__SubSizeMin]) 1118 MIPD__stats[N_POSTs__SubSizeMin] = nSubSize; 1119 MIPD__stats[N_POSTs__SubSizeSum] += nSubSize; 1120 if (nSubSize > MIPD__stats[N_POSTs__SubSizeMax]) 1121 MIPD__stats[N_POSTs__SubSizeMax] = nSubSize; 1122 } 1123 if (cls.fRef1HasEqEncode) ++MIPD__stats[N_POSTs__cliquesWithEqEncode]; 1124 } 1125 1126 /// Project the domain-related constraints of a variable into the clique 1127 /// Deltas should be scaled but to a minimum of the target's discr 1128 /// COmparison sense changes on negated vars 1129 void projectVariableConstr(VarDecl* vd, std::pair<double, double> eq1) { 1130 DBGOUT_MIPD__(" MIPD: projecting variable "); 1131 DBGOUT_MIPD_SELF(debugprint(vd)); 1132 // Always check if domain becomes empty? TODO 1133 const double A = eq1.first; // vd = A*arg + B. conversion 1134 const double B = eq1.second; 1135 // process domain info 1136 double lb = B, ub = A + B; // projected bounds for bool 1137 if (vd->ti()->domain()) { 1138 if (vd->type().isint() || vd->type().isfloat()) { // INT VAR OR FLOAT VAR 1139 SetOfIntvReal sD1; 1140 convertIntSet(vd->ti()->domain(), sD1, cls.varRef1, A, B); 1141 sDomain.intersect(sD1); 1142 DBGOUT_MIPD(" Clique domain after proj of the init. domain " 1143 << sD1 << " of " << (vd->type().isint() ? "varint" : "varfloat") << A << " * " 1144 << vd->id()->str() << " + " << B << ": " << sDomain); 1145 auto bnds = sD1.getBounds(); 1146 lb = bnds.left; 1147 ub = bnds.right; 1148 } else { 1149 MZN_MIPD__assert_for_feas(0, "Variable " << vd->id()->str() << " of type " 1150 << vd->type().toString(mipd.__env->envi()) 1151 << " has a domain."); 1152 } 1153 // /// Deleting var domain: 1154 // vd->ti()->domain( NULL ); 1155 } else { 1156 if (NULL == vd->ti()->domain() && !vd->type().isbool()) { 1157 lb = IntvReal::infMinus(); 1158 ub = IntvReal::infPlus(); 1159 } 1160 } 1161 auto bnds = sDomain.getBounds(); // can change TODO 1162 // process calls. Can use the constr type info. 1163 auto& aCalls = mipd.vVarDescr[vd->payload()].aCalls; 1164 for (Item* pItem : aCalls) { 1165 ConstraintI* pCI = pItem->dyn_cast<ConstraintI>(); 1166 MZN_MIPD__assert_hard(pCI); 1167 Call* pCall = pCI->e()->dyn_cast<Call>(); 1168 MZN_MIPD__assert_hard(pCall); 1169 DBGOUT_MIPD__("PROPAG CALL "); 1170 DBGOUT_MIPD_SELF(debugprint(pCall)); 1171 // check the bounds for bool in reifs? TODO 1172 auto ipct = mipd.mCallTypes.find(pCall->decl()); 1173 MZN_MIPD__assert_hard(mipd.mCallTypes.end() != ipct); 1174 const DCT& dct = *ipct->second; 1175 int nCmpType_ADAPTED = dct.nCmpType; 1176 if (A < 0.0) { // negative factor 1177 if (std::abs(nCmpType_ADAPTED) >= 4) // inequality 1178 nCmpType_ADAPTED = -nCmpType_ADAPTED; 1179 } 1180 switch (dct.nConstrType) { 1181 case CT_SetIn: { 1182 SetOfIntvReal SS; 1183 convertIntSet(pCall->arg(1), SS, cls.varRef1, A, B); 1184 if (RIT_Static == dct.nReifType) { 1185 sDomain.intersect(SS); 1186 ++MIPD__stats[N_POSTs__setIn]; 1187 } else { 1188 sDomain.cutDeltas(SS, std::max(1.0, std::fabs(A))); // deltas to scale 1189 ++MIPD__stats[N_POSTs__setInReif]; 1190 } 1191 } break; 1192 case CT_Comparison: 1193 if (RIT_Reif == dct.nReifType) { 1194 const double rhs = (mipd.aux_float_lt_zero_iff_1__POST == pCall->decl()) 1195 ? B /* + A*0.0, relating to 0 */ 1196 // The 2nd argument is constant: 1197 : A * mipd.expr2Const(pCall->arg(1)) + B; 1198 const double rhsUp = rndUpIfInt(cls.varRef1, rhs); 1199 const double rhsDown = rndDownIfInt(cls.varRef1, rhs); 1200 const double rhsRnd = rndIfInt(cls.varRef1, rhs); 1201 /// Strictly, for delta we should finish domain reductions first... TODO? 1202 const double delta = computeDelta(cls.varRef1, vd, bnds, A, pCall, 3); 1203 switch (nCmpType_ADAPTED) { 1204 case CMPT_LE: 1205 sDomain.cutDeltas(IntvReal::infMinus(), rhsDown, delta); 1206 break; 1207 case CMPT_GE: 1208 sDomain.cutDeltas(rhsUp, IntvReal::infPlus(), delta); 1209 break; 1210 case CMPT_LT_0: 1211 sDomain.cutDeltas(IntvReal::infMinus(), rhsDown - delta, delta); 1212 break; 1213 case CMPT_GT_0: 1214 sDomain.cutDeltas(rhsUp + delta, IntvReal::infPlus(), delta); 1215 break; 1216 case CMPT_EQ: 1217 if (!(cls.varRef1->type().isint() && // skip if int target var 1218 std::fabs(rhs - rhsRnd) > INT_EPS)) // && fract value 1219 sDomain.cutDeltas(rhsRnd, rhsRnd, delta); 1220 break; 1221 default: 1222 MZN_MIPD__assert_hard_msg(0, " No other reified cmp type "); 1223 } 1224 ++MIPD__stats[(vd->ti()->type().isint()) ? N_POSTs__intCmpReif 1225 : N_POSTs__floatCmpReif]; 1226 } else if (RIT_Static == dct.nReifType) { 1227 // _ne, later maybe static ineq TODO 1228 MZN_MIPD__assert_hard(CMPT_NE == dct.nCmpType); 1229 const double rhs = A * mipd.expr2Const(pCall->arg(1)) + B; 1230 const double rhsRnd = rndIfInt(cls.varRef1, rhs); 1231 bool fSkipNE = (cls.varRef1->type().isint() && std::fabs(rhs - rhsRnd) > INT_EPS); 1232 if (!fSkipNE) { 1233 const double delta = computeDelta(cls.varRef1, vd, bnds, A, pCall, 2); 1234 sDomain.cutOut({rhsRnd - delta, rhsRnd + delta}); 1235 } 1236 ++MIPD__stats[(vd->ti()->type().isint()) ? N_POSTs__intNE : N_POSTs__floatNE]; 1237 } else { // aux_ relate to 0.0 1238 // But we don't modify domain splitting for them currently 1239 ++MIPD__stats[(vd->ti()->type().isint()) ? N_POSTs__intAux : N_POSTs__floatAux]; 1240 MZN_MIPD__assert_hard(RIT_Halfreif == dct.nReifType); 1241 // const double rhs = B; // + A*0 1242 // const double delta = vd->type().isint() ? 1.0 : 1e-5; // 1243 // TODO : eps 1244 } 1245 break; 1246 case CT_Encode: 1247 // See if any further constraints here? TODO 1248 ++MIPD__stats[N_POSTs__eq_encode]; 1249 break; 1250 default: 1251 MZN_MIPD__assert_hard_msg(0, "Unknown constraint type"); 1252 } 1253 } 1254 DBGOUT_MIPD(" Clique domain after proj of " << A << " * " << vd->id()->str() << " + " << B 1255 << ": " << sDomain); 1256 } 1257 1258 static double rndIfInt(VarDecl* vdTarget, double v) { 1259 return vdTarget->type().isint() ? std::round(v) : v; 1260 } 1261 static double rndIfBothInt(VarDecl* vdTarget, double v) { 1262 if (!vdTarget->type().isint()) return v; 1263 const double vRnd = std::round(v); 1264 return (fabs(v - vRnd) < INT_EPS) ? vRnd : v; 1265 } 1266 static double rndUpIfInt(VarDecl* vdTarget, double v) { 1267 return vdTarget->type().isint() ? std::ceil(v - INT_EPS) : v; 1268 } 1269 static double rndDownIfInt(VarDecl* vdTarget, double v) { 1270 return vdTarget->type().isint() ? std::floor(v + INT_EPS) : v; 1271 } 1272 1273 void makeRangeDomains() { 1274 auto bnds = sDomain.getBounds(); 1275 for (auto& iRef1 : cls.mRef1) { 1276 VarDecl* vd = iRef1.first; 1277 // projecting the bounds back: 1278 double lb0 = (bnds.left - iRef1.second.second) / iRef1.second.first; 1279 double ub0 = (bnds.right - iRef1.second.second) / iRef1.second.first; 1280 if (lb0 > ub0) { 1281 MZN_MIPD__assert_hard(iRef1.second.first < 0.0); 1282 std::swap(lb0, ub0); 1283 } 1284 if (vd->type().isint()) { 1285 lb0 = rndUpIfInt(vd, lb0); 1286 ub0 = rndDownIfInt(vd, ub0); 1287 } 1288 setVarDomain(vd, lb0, ub0); 1289 } 1290 } 1291 1292 /// tightens element bounds in the existing eq_encoding of varRef1 1293 /// necessary because if one exists, int_ne is not translated into it 1294 /// Can also back-check from there? TODO 1295 /// And further checks TODO 1296 void syncWithEqEncoding() { 1297 std::vector<Expression*> pp; 1298 auto bnds = sDomain.getBounds(); 1299 const long long iMin = mipd.expr2ExprArray( 1300 mipd.vVarDescr[cls.varRef1->payload()].pEqEncoding->e()->dyn_cast<Call>()->arg(1), pp); 1301 MZN_MIPD__assert_hard(pp.size() >= bnds.right - bnds.left + 1); 1302 MZN_MIPD__assert_hard(iMin <= bnds.left); 1303 long long vEE = iMin; 1304 DBGOUT_MIPD__( 1305 " SYNC EQ_ENCODE( " 1306 << (*cls.varRef1) << ", bitflags: " 1307 << *(mipd.vVarDescr[cls.varRef1->payload()].pEqEncoding->e()->dyn_cast<Call>()->arg(1)) 1308 << " ): SETTING 0 FLAGS FOR VALUES: "); 1309 for (auto& intv : sDomain) { 1310 for (; vEE < intv.left; ++vEE) { 1311 if (vEE >= static_cast<long long>(iMin + pp.size())) return; 1312 if (pp[vEE - iMin]->isa<Id>()) 1313 if (pp[vEE - iMin]->dyn_cast<Id>()->decl()->type().isvar()) { 1314 DBGOUT_MIPD__(vEE << ", "); 1315 setVarDomain(pp[vEE - iMin]->dyn_cast<Id>()->decl(), 0.0, 0.0); 1316 } 1317 } 1318 vEE = static_cast<long long>(intv.right + 1); 1319 } 1320 for (; vEE < static_cast<long long>(iMin + pp.size()); ++vEE) { 1321 if (pp[vEE - iMin]->isa<Id>()) 1322 if (pp[vEE - iMin]->dyn_cast<Id>()->decl()->type().isvar()) { 1323 DBGOUT_MIPD__(vEE << ", "); 1324 setVarDomain(pp[vEE - iMin]->dyn_cast<Id>()->decl(), 0.0, 0.0); 1325 } 1326 } 1327 DBGOUT_MIPD(""); 1328 } 1329 1330 /// sync varRef1's eq_encoding with those of other variables 1331 void syncOtherEqEncodings() { 1332 // TODO This could be in the var projection? No, need the final domain 1333 } 1334 1335 /// Depending on params, 1336 /// create an equality encoding for an integer variable 1337 /// TODO What if a float's domain is discrete? 1338 void considerDenseEncoding() { 1339 if (cls.varRef1->id()->type().isint()) { 1340 if (sDomain.max_interval() <= mipd.nMaxIntv2Bits || 1341 sDomain.card_int() <= mipd.dMaxNValueDensity * sDomain.size()) { 1342 sDomain.split2Bits(); 1343 ++MIPD__stats[N_POSTs__clEEEnforced]; 1344 } 1345 } 1346 } 1347 1348 /// if ! eq_encoding, creates a flag for each subinterval in the domain 1349 /// && constrains sum(flags)==1 1350 void createDomainFlags() { 1351 std::vector<Expression*> vVars(sDomain.size()); // flags for each subinterval 1352 std::vector<double> vIntvLB(sDomain.size() + 1), vIntvUB__(sDomain.size() + 1); 1353 int i = 0; 1354 double dMaxIntv = -1.0; 1355 for (auto& intv : sDomain) { 1356 intv.varFlag = addIntVar(0.0, 1.0); 1357 vVars[i] = intv.varFlag->id(); 1358 vIntvLB[i] = intv.left; 1359 vIntvUB__[i] = -intv.right; 1360 dMaxIntv = std::max(dMaxIntv, intv.right - intv.left); 1361 ++i; 1362 } 1363 // Sum of flags == 1 1364 std::vector<double> ones(sDomain.size(), 1.0); 1365 addLinConstr(ones, vVars, CMPT_EQ, 1.0); 1366 // Domain decomp 1367 vVars.push_back(cls.varRef1->id()); 1368 vIntvLB[i] = -1.0; // var1 >= sum(LBi*flagi) 1369 /// STRICT equality encoding if small intervals 1370 if (dMaxIntv > 1e-6) { // EPS = param? TODO 1371 vIntvUB__[i] = 1.0; // var1 <= sum(UBi*flagi) 1372 addLinConstr(vIntvLB, vVars, CMPT_LE, 0.0); 1373 addLinConstr(vIntvUB__, vVars, CMPT_LE, 0.0); 1374 } else { 1375 ++MIPD__stats[N_POSTs__clEEFound]; 1376 addLinConstr(vIntvLB, vVars, CMPT_EQ, 0.0); 1377 } 1378 } 1379 1380 /// deletes them as well 1381 void implement__POSTs() { 1382 auto bnds = sDomain.getBounds(); 1383 for (auto& iRef1 : cls.mRef1) { 1384 // DBGOUT_MIPD__( " MIPD: implementing constraints of variable " ); 1385 // DBGOUT_MIPD_SELF( debugprint(vd) ); 1386 VarDecl* vd = iRef1.first; 1387 auto eq1 = iRef1.second; 1388 const double A = eq1.first; // vd = A*arg + B. conversion 1389 const double B = eq1.second; 1390 // process calls. Can use the constr type info. 1391 auto& aCalls = mipd.vVarDescr[vd->payload()].aCalls; 1392 for (Item* pItem : aCalls) { 1393 ConstraintI* pCI = pItem->dyn_cast<ConstraintI>(); 1394 MZN_MIPD__assert_hard(pCI); 1395 Call* pCall = pCI->e()->dyn_cast<Call>(); 1396 MZN_MIPD__assert_hard(pCall); 1397 DBGOUT_MIPD__("IMPL CALL "); 1398 DBGOUT_MIPD_SELF(debugprint(pCall)); 1399 // check the bounds for bool in reifs? TODO 1400 auto ipct = mipd.mCallTypes.find(pCall->decl()); 1401 MZN_MIPD__assert_hard(mipd.mCallTypes.end() != ipct); 1402 const DCT& dct = *ipct->second; 1403 int nCmpType_ADAPTED = dct.nCmpType; 1404 if (A < 0.0) { // negative factor 1405 if (std::abs(nCmpType_ADAPTED) >= 4) // inequality 1406 nCmpType_ADAPTED = -nCmpType_ADAPTED; 1407 } 1408 switch (dct.nConstrType) { 1409 case CT_SetIn: 1410 if (RIT_Reif == dct.nReifType) { 1411 SetOfIntvReal SS; 1412 convertIntSet(pCall->arg(1), SS, cls.varRef1, A, B); 1413 relateReifFlag(pCall->arg(2), SS); 1414 } 1415 break; 1416 case CT_Comparison: 1417 if (RIT_Reif == dct.nReifType) { 1418 const double rhs = (mipd.aux_float_lt_zero_iff_1__POST == pCall->decl()) 1419 ? B /* + A*0.0, relating to 0 */ 1420 // The 2nd argument is constant: 1421 : A * mipd.expr2Const(pCall->arg(1)) + B; 1422 const double rhsUp = rndUpIfInt(cls.varRef1, rhs); 1423 const double rhsDown = rndDownIfInt(cls.varRef1, rhs); 1424 const double rhsRnd = rndIfBothInt( 1425 cls.varRef1, rhs); // if the ref var is int, need to round almost-int values 1426 const double delta = computeDelta(cls.varRef1, vd, bnds, A, pCall, 3); 1427 switch (nCmpType_ADAPTED) { 1428 case CMPT_LE: 1429 relateReifFlag(pCall->arg(2), {{IntvReal::infMinus(), rhsDown}}); 1430 break; 1431 case CMPT_GE: 1432 relateReifFlag(pCall->arg(2), {{rhsUp, IntvReal::infPlus()}}); 1433 break; 1434 case CMPT_LT_0: 1435 relateReifFlag(pCall->arg(1), {{IntvReal::infMinus(), rhsDown - delta}}); 1436 break; 1437 case CMPT_GT_0: 1438 relateReifFlag(pCall->arg(1), {{rhsUp + delta, IntvReal::infPlus()}}); 1439 break; 1440 case CMPT_EQ: 1441 relateReifFlag(pCall->arg(2), {{rhsRnd, rhsRnd}}); 1442 break; // ... but if the value is sign. fractional for an int var, the flag is 1443 // set=0 1444 default: 1445 break; 1446 } 1447 } else if (RIT_Static == dct.nReifType) { 1448 // !hing here for NE 1449 MZN_MIPD__assert_hard(CMPT_NE == nCmpType_ADAPTED); 1450 } else { // aux_ relate to 0.0 1451 // But we don't modify domain splitting for them currently 1452 MZN_MIPD__assert_hard(RIT_Halfreif == dct.nReifType); 1453 double rhs = B; // + A*0 1454 const double rhsUp = rndUpIfInt(cls.varRef1, rhs); 1455 const double rhsDown = rndDownIfInt(cls.varRef1, rhs); 1456 const double rhsRnd = rndIfInt(cls.varRef1, rhs); 1457 double delta = 0.0; 1458 if (mipd.aux_float_lt_zero_if_1__POST == pCall->decl()) // only float && lt 1459 delta = computeDelta(cls.varRef1, vd, bnds, A, pCall, 3); 1460 if (nCmpType_ADAPTED < 0) delta = -delta; 1461 if (cls.varRef1->type().isint() && CMPT_EQ_0 != nCmpType_ADAPTED) { 1462 if (nCmpType_ADAPTED < 0) 1463 rhs = rhsDown; 1464 else 1465 rhs = rhsUp; 1466 } else { 1467 rhs += delta; 1468 } 1469 // Now we need rhs ! to be in the inner of the domain 1470 bool fUseDD = true; 1471 if (!cls.fRef1HasEqEncode) { 1472 switch (nCmpType_ADAPTED) { 1473 case CMPT_EQ_0: { 1474 auto itLB = sDomain.lower_bound(rhsRnd); 1475 fUseDD = (itLB->left == rhsRnd && itLB->right == rhsRnd); // exactly 1476 } break; 1477 case CMPT_LT_0: 1478 case CMPT_LE_0: { 1479 auto itUB = sDomain.upper_bound(rhsUp); 1480 bool fInner = false; 1481 if (sDomain.begin() != itUB) { 1482 --itUB; 1483 if (itUB->right > rhs) fInner = true; 1484 } 1485 fUseDD = !fInner; 1486 } break; 1487 case CMPT_GT_0: 1488 case CMPT_GE_0: { 1489 auto itLB = sDomain.lower_bound(rhsDown); 1490 bool fInner = false; 1491 if (sDomain.begin() != itLB) { 1492 --itLB; 1493 if (itLB->right >= rhs) fInner = true; 1494 } 1495 fUseDD = !fInner; 1496 } break; 1497 default: 1498 MZN_MIPD__assert_hard_msg(0, "Unknown halfreif cmp type"); 1499 } 1500 } 1501 if (fUseDD) { // use sDomain 1502 if (CMPT_EQ_0 == nCmpType_ADAPTED) { 1503 relateReifFlag(pCall->arg(1), {{rhsRnd, rhsRnd}}, RIT_Halfreif); 1504 } else if (nCmpType_ADAPTED < 0) { 1505 relateReifFlag(pCall->arg(1), {{IntvReal::infMinus(), rhsDown}}, RIT_Halfreif); 1506 } else { 1507 relateReifFlag(pCall->arg(1), {{rhsUp, IntvReal::infPlus()}}, RIT_Halfreif); 1508 } 1509 } else { // use big-M 1510 DBGOUT_MIPD(" AUX BY BIG-Ms: "); 1511 const bool fLE = (CMPT_EQ_0 == nCmpType_ADAPTED || 0 > nCmpType_ADAPTED); 1512 const bool fGE = (CMPT_EQ_0 == nCmpType_ADAPTED || 0 < nCmpType_ADAPTED); 1513 // Take integer || float indicator version, depending on the constrained var: 1514 const int nIdxInd = // (VT_Int==dct.nVarType) ? 1515 // No: vd->ti()->type().isint() ? 1 : 2; 1516 cls.varRef1->ti()->type().isint() 1517 ? 1 1518 : 2; // need the type of the variable to be constr 1519 MZN_MIPD__assert_hard(static_cast<unsigned int>(nIdxInd) < pCall->n_args()); 1520 Expression* pInd = pCall->arg(nIdxInd); 1521 if (fLE && rhs < bnds.right) { 1522 if (rhs >= bnds.left) { 1523 std::vector<double> coefs = {1.0, bnds.right - rhs}; 1524 // Use the float version of indicator: 1525 std::vector<Expression*> vars = {cls.varRef1->id(), pInd}; 1526 addLinConstr(coefs, vars, CMPT_LE, bnds.right); 1527 } else 1528 setVarDomain(mipd.expr2VarDecl(pInd), 0.0, 0.0); 1529 } 1530 if (fGE && rhs > bnds.left) { 1531 if (rhs <= bnds.right) { 1532 std::vector<double> coefs = {-1.0, rhs - bnds.left}; 1533 std::vector<Expression*> vars = {cls.varRef1->id(), pInd}; 1534 addLinConstr(coefs, vars, CMPT_LE, -bnds.left); 1535 } else 1536 setVarDomain(mipd.expr2VarDecl(pInd), 0.0, 0.0); 1537 } 1538 } 1539 } 1540 break; 1541 case CT_Encode: 1542 // See if any further constraints here? TODO 1543 break; 1544 default: 1545 MZN_MIPD__assert_hard_msg(0, "Unknown constraint type"); 1546 } 1547 pItem->remove(); // removing the call 1548 } 1549 // removing the eq_encoding call 1550 if (mipd.vVarDescr[vd->payload()].pEqEncoding) 1551 mipd.vVarDescr[vd->payload()].pEqEncoding->remove(); 1552 } 1553 } 1554 1555 /// sets varFlag = || <= sum( intv.varFlag : SS ) 1556 void relateReifFlag(Expression* expFlag, const SetOfIntvReal& SS, EnumReifType nRT = RIT_Reif) { 1557 MZN_MIPD__assert_hard(RIT_Reif == nRT || RIT_Halfreif == nRT); 1558 // MZN_MIPD__assert_hard( sDomain.size()>=2 ); 1559 VarDecl* varFlag = mipd.expr2VarDecl(expFlag); 1560 std::vector<Expression*> vIntvFlags; 1561 if (cls.fRef1HasEqEncode) { // use eq_encoding 1562 MZN_MIPD__assert_hard(varFlag->type().isint()); 1563 std::vector<Expression*> pp; 1564 auto bnds = sDomain.getBounds(); 1565 const long long iMin = mipd.expr2ExprArray( 1566 mipd.vVarDescr[cls.varRef1->payload()].pEqEncoding->e()->dyn_cast<Call>()->arg(1), pp); 1567 MZN_MIPD__assert_hard(pp.size() >= bnds.right - bnds.left + 1); 1568 MZN_MIPD__assert_hard(iMin <= bnds.left); 1569 for (auto& intv : SS) { 1570 for (long long vv = (long long)std::max(double(iMin), ceil(intv.left)); 1571 vv <= (long long)std::min(double(iMin) + pp.size() - 1, floor(intv.right)); ++vv) { 1572 vIntvFlags.push_back(pp[vv - iMin]); 1573 } 1574 } 1575 } else { 1576 MZN_MIPD__assert_hard(varFlag->type().isint()); 1577 for (auto& intv : SS) { 1578 auto it1 = sDomain.lower_bound(intv.left); 1579 auto it2 = sDomain.upper_bound(intv.right); 1580 auto it11 = it1; 1581 // Check that we are looking ! into a subinterval: 1582 if (sDomain.begin() != it11) { 1583 --it11; 1584 MZN_MIPD__assert_hard(it11->right < intv.left); 1585 } 1586 auto it12 = it2; 1587 if (sDomain.begin() != it12) { 1588 --it12; 1589 MZN_MIPD__assert_hard_msg(it12->right <= intv.right, 1590 " relateReifFlag for " << intv << " in " << sDomain); 1591 } 1592 for (it12 = it1; it12 != it2; ++it12) { 1593 if (it12->varFlag) 1594 vIntvFlags.push_back(it12->varFlag->id()); 1595 else { 1596 MZN_MIPD__assert_hard(1 == sDomain.size()); 1597 vIntvFlags.push_back(IntLit::a(1)); // just a constant then 1598 } 1599 } 1600 } 1601 } 1602 if (vIntvFlags.size()) { 1603 // Could find out if reif is true -- TODO && see above for 1 subinterval 1604 std::vector<double> onesm(vIntvFlags.size(), -1.0); 1605 onesm.push_back(1.0); 1606 vIntvFlags.push_back(varFlag->id()); 1607 EnumCmpType nCmpType = (RIT_Reif == nRT) ? CMPT_EQ : CMPT_LE; 1608 addLinConstr(onesm, vIntvFlags, nCmpType, 0.0); 1609 } else { // the reif is false 1610 setVarDomain(varFlag, 0.0, 0.0); 1611 } 1612 } 1613 1614 void setVarDomain(VarDecl* vd, double lb, double ub) { 1615 // need to check if the new range is in the previous bounds... TODO 1616 if (vd->type().isfloat()) { 1617 // if ( 0.0==lb && 0.0==ub ) { 1618 BinOp* newDom = 1619 new BinOp(Location().introduce(), FloatLit::a(lb), BOT_DOTDOT, FloatLit::a(ub)); 1620 vd->ti()->domain(newDom); 1621 DBGOUT_MIPD(" NULL OUT: " << vd->id()->str()); 1622 // } 1623 } else if (vd->type().isint() || vd->type().isbool()) { 1624 SetLit* newDom = new SetLit( 1625 Location().introduce(), 1626 IntSetVal::a(static_cast<long long int>(lb), static_cast<long long int>(ub))); 1627 // TypeInst* nti = copy(mipd.getEnv()->envi(),varFlag->ti())->cast<TypeInst>(); 1628 // nti->domain(newDom); 1629 vd->ti()->domain(newDom); 1630 } else 1631 MZN_MIPD__assert_hard_msg(0, "Unknown var type "); 1632 } 1633 1634 VarDecl* addIntVar(double LB, double UB) { 1635 // GCLock lock; 1636 // Cache them? Only location can be different TODO 1637 SetLit* newDom = 1638 new SetLit(Location().introduce(), 1639 IntSetVal::a(static_cast<long long int>(LB), static_cast<long long int>(UB))); 1640 TypeInst* ti = new TypeInst(Location().introduce(), Type::varint(), newDom); 1641 VarDecl* newVar = new VarDecl(Location().introduce(), ti, mipd.getEnv()->envi().genId()); 1642 newVar->flat(newVar); 1643 mipd.getEnv()->envi().flat_addItem(new VarDeclI(Location().introduce(), newVar)); 1644 return newVar; 1645 } 1646 1647 void addLinConstr(std::vector<double>& coefs, std::vector<Expression*>& vars, 1648 EnumCmpType nCmpType, double rhs) { 1649 std::vector<Expression*> args(3); 1650 MZN_MIPD__assert_hard(vars.size() >= 2); 1651 for (auto v : vars) { 1652 MZN_MIPD__assert_hard(&v); 1653 // throw std::string("addLinConstr: &var=NULL"); 1654 MZN_MIPD__assert_hard_msg(v->isa<Id>() || v->isa<IntLit>() || v->isa<FloatLit>(), 1655 " expression at " << (&v) << " eid = " << v->eid() 1656 << " while E_INTLIT=" << Expression::E_INTLIT); 1657 // throw std::string("addLinConstr: only id's as variables allowed"); 1658 } 1659 MZN_MIPD__assert_hard(coefs.size() == vars.size()); 1660 MZN_MIPD__assert_hard(CMPT_EQ == nCmpType || CMPT_LE == nCmpType); 1661 DBGOUT_MIPD_SELF( // LinEq leq; leq.coefs=coefs; leq.vd=vars; leq.rhs=rhs; 1662 DBGOUT_MIPD__(" ADDING " << (CMPT_EQ == nCmpType ? "LIN_EQ" : "LIN_LE") << ": [ "); 1663 for (auto c 1664 : coefs) DBGOUT_MIPD__(c << ','); 1665 DBGOUT_MIPD__(" ] * [ "); for (auto v 1666 : vars) { 1667 MZN_MIPD__assert_hard(!v->isa<VarDecl>()); 1668 if (v->isa<Id>()) DBGOUT_MIPD__(v->dyn_cast<Id>()->str() << ','); 1669 // else if ( v->isa<VarDecl>() ) 1670 // MZN_MIPD__assert_hard ("addLinConstr: only id's as variables allowed"); 1671 else 1672 DBGOUT_MIPD__(mipd.expr2Const(v) << ','); 1673 } DBGOUT_MIPD(" ] " << (CMPT_EQ == nCmpType ? "== " : "<= ") << rhs);); 1674 std::vector<Expression*> nc_c; 1675 std::vector<Expression*> nx; 1676 bool fFloat = false; 1677 for (auto v : vars) { 1678 if (!v->type().isint()) { 1679 fFloat = true; 1680 break; 1681 } 1682 } 1683 auto sName = constants().ids.float_.lin_eq; // "int_lin_eq"; 1684 FunctionI* fDecl = mipd.float_lin_eq; 1685 if (fFloat) { // MZN_MIPD__assert_hard all vars of same type TODO 1686 for (int i = 0; i < vars.size(); ++i) 1687 if (fabs(coefs[i]) > 1e-8) /// Only add terms with non-0 coefs. TODO Eps=param 1688 { 1689 nc_c.push_back(FloatLit::a(coefs[i])); 1690 if (vars[i]->type().isint()) { 1691 std::vector<Expression*> i2f_args(1); 1692 i2f_args[0] = vars[i]; 1693 Call* i2f = new Call(Location().introduce(), constants().ids.int2float, i2f_args); 1694 i2f->type(Type::varfloat()); 1695 i2f->decl(mipd.getEnv()->model()->matchFn(mipd.getEnv()->envi(), i2f, false)); 1696 EE ret = flat_exp(mipd.getEnv()->envi(), Ctx(), i2f, NULL, constants().var_true); 1697 nx.push_back(ret.r()); 1698 } else { 1699 nx.push_back(vars[i]); // ->id(); once passing a general expression 1700 } 1701 } 1702 args[2] = FloatLit::a(rhs); 1703 args[2]->type(Type::parfloat(0)); 1704 args[0] = new ArrayLit(Location().introduce(), nc_c); 1705 args[0]->type(Type::parfloat(1)); 1706 args[1] = new ArrayLit(Location().introduce(), nx); 1707 args[1]->type(Type::varfloat(1)); 1708 if (CMPT_LE == nCmpType) { 1709 sName = constants().ids.float_.lin_le; // "float_lin_le"; 1710 fDecl = mipd.float_lin_le; 1711 } 1712 } else { 1713 for (int i = 0; i < vars.size(); ++i) 1714 if (fabs(coefs[i]) > 1e-8) /// Only add terms with non-0 coefs. TODO Eps=param 1715 { 1716 nc_c.push_back(IntLit::a(static_cast<long long int>(coefs[i]))); 1717 nx.push_back(vars[i]); //->id(); 1718 } 1719 args[2] = IntLit::a(static_cast<long long int>(rhs)); 1720 args[2]->type(Type::parint(0)); 1721 args[0] = new ArrayLit(Location().introduce(), nc_c); 1722 args[0]->type(Type::parint(1)); 1723 args[1] = new ArrayLit(Location().introduce(), nx); 1724 args[1]->type(Type::varint(1)); 1725 if (CMPT_LE == nCmpType) { 1726 sName = constants().ids.int_.lin_le; // "int_lin_le"; 1727 fDecl = mipd.int_lin_le; 1728 } else { 1729 sName = constants().ids.int_.lin_eq; // "int_lin_eq"; 1730 fDecl = mipd.int_lin_eq; 1731 } 1732 } 1733 if (mipd.getEnv()->envi().cse_map_end() != mipd.getEnv()->envi().cse_map_find(args[0])) { 1734 DBGOUT_MIPD__(" Found expr "); 1735 DBGOUT_MIPD_SELF(debugprint(args[0])); 1736 } 1737 auto nc = new Call(Location().introduce(), ASTString(sName), args); 1738 nc->type(Type::varbool()); 1739 nc->decl(fDecl); 1740 mipd.getEnv()->envi().flat_addItem(new ConstraintI(Location().introduce(), nc)); 1741 } 1742 1743 /// domain / reif set of one variable into that for a!her 1744 void convertIntSet(Expression* e, SetOfIntvReal& s, VarDecl* varTarget, double A, double B) { 1745 MZN_MIPD__assert_hard(A != 0.0); 1746 if (e->type().isintset()) { 1747 IntSetVal* S = eval_intset(mipd.getEnv()->envi(), e); 1748 IntSetRanges domr(S); 1749 for (; domr(); ++domr) { // * A + B 1750 IntVal mmin = domr.min(); 1751 IntVal mmax = domr.max(); 1752 if (A < 0.0) std::swap(mmin, mmax); 1753 s.insert(IntvReal( // * A + B 1754 mmin.isFinite() ? rndUpIfInt(varTarget, (mmin.toInt() * A + B)) 1755 : IntvReal::infMinus(), 1756 mmax.isFinite() ? rndDownIfInt(varTarget, (mmax.toInt() * A + B)) 1757 : IntvReal::infPlus())); 1758 } 1759 } else { 1760 assert(e->type().isfloatset()); 1761 FloatSetVal* S = eval_floatset(mipd.getEnv()->envi(), e); 1762 FloatSetRanges domr(S); 1763 for (; domr(); ++domr) { // * A + B 1764 FloatVal mmin = domr.min(); 1765 FloatVal mmax = domr.max(); 1766 if (A < 0.0) std::swap(mmin, mmax); 1767 s.insert(IntvReal( // * A + B 1768 mmin.isFinite() ? rndUpIfInt(varTarget, (mmin.toDouble() * A + B)) 1769 : IntvReal::infMinus(), 1770 mmax.isFinite() ? rndDownIfInt(varTarget, (mmax.toDouble() * A + B)) 1771 : IntvReal::infPlus())); 1772 } 1773 } 1774 } 1775 1776 /// compute the delta for float strict ineq 1777 double computeDelta(VarDecl* var, VarDecl* varOrig, IntvReal bnds, double A, Call* pCall, 1778 int nArg) { 1779 double delta = varOrig->type().isfloat() 1780 ? mipd.expr2Const(pCall->arg(nArg)) 1781 // * ( bnds.right-bnds.left ) ABANDONED 12.4.18 due to #207 1782 : std::fabs(A); // delta should be scaled as well 1783 if (var->type().isint()) // the projected-onto variable 1784 delta = std::max(1.0, delta); 1785 return delta; 1786 } 1787 }; // class DomainDecomp 1788 1789 /// Vars without explicit clique still need a decomposition. 1790 /// Have !iced all __POSTs, set_in's && eq_encode's to it BEFORE 1791 /// In each clique, relate all vars to one chosen 1792 /// Find all "smallest rel. factor" variables, integer && with eq_encode if avail 1793 /// Re-relate all vars to it 1794 /// Refer all __POSTs && dom() to it 1795 /// build domain decomposition 1796 /// Implement all domain constraints, incl. possible corresp, of eq_encode's 1797 /// 1798 /// REMARKS. 1799 /// ! impose effects of integrality scaling (e.g., int v = int k/3) 1800 /// BUT when using k's eq_encode? 1801 /// And when subdividing into intervals 1802 bool decomposeDomains() { 1803 // for (int iClq=0; iClq<aCliques.size(); ++iClq ) { 1804 // TClique& clq = aCliques[iClq]; 1805 // } 1806 bool fRetTrue = true; 1807 for (int iVar = 0; iVar < vVarDescr.size(); ++iVar) { 1808 // VarDescr& var = vVarDescr[iVar]; 1809 if (!vVarDescr[iVar].fDomainConstrProcessed) { 1810 try { 1811 GCLock lock; 1812 DomainDecomp dd(this, iVar); 1813 dd.doProcess(); 1814 vVarDescr[iVar].fDomainConstrProcessed = true; 1815 } catch (const MIPD_Infeasibility_Exception& exc) { 1816 std::cerr << " INFEASIBILITY: " << exc.msg << std::endl; 1817 fRetTrue = false; 1818 break; 1819 // } catch (const Exception& e) { 1820 // std::cerr << "ERROR: " << e.what() << ": " << e.msg() << std::endl; 1821 // fRetTrue = false; 1822 // break; 1823 // // } catch ( ... ) { // a contradiction 1824 // // return false; 1825 } 1826 } 1827 } 1828 // Clean up __POSTs: 1829 for (auto& vVar : vVarDescr) { 1830 for (auto pCallI : vVar.aCalls) pCallI->remove(); 1831 if (vVar.pEqEncoding) vVar.pEqEncoding->remove(); 1832 } 1833 return fRetTrue; 1834 } 1835 1836 VarDecl* expr2VarDecl(Expression* arg) { 1837 // The requirement to have actual variable objects 1838 // might be a limitation if more optimizations are done before... 1839 // Might need to flexibilize this TODO 1840 // MZN_MIPD__assert_hard_msg( ! arg->dyn_cast<IntLit>(), 1841 // "Expression " << *arg << " is an IntLit!" ); 1842 // MZN_MIPD__assert_hard( ! arg->dyn_cast<FloatLit>() ); 1843 // MZN_MIPD__assert_hard( ! arg->dyn_cast<BoolLit>() ); 1844 Id* id = arg->dyn_cast<Id>(); 1845 // MZN_MIPD__assert_hard(id); 1846 if (0 == id) return 0; // the call using this should be ignored? 1847 VarDecl* vd = id->decl(); 1848 MZN_MIPD__assert_hard(vd); 1849 return vd; 1850 } 1851 1852 /// Fills the vector of vardecls && returns the least index of the array 1853 template <class Array> 1854 long long expr2DeclArray(Expression* arg, Array& aVD) { 1855 ArrayLit* al = eval_array_lit(getEnv()->envi(), arg); 1856 checkOrResize(aVD, al->size()); 1857 for (unsigned int i = 0; i < al->size(); i++) aVD[i] = expr2VarDecl((*al)[i]); 1858 return al->min(0); 1859 } 1860 1861 /// Fills the vector of expressions && returns the least index of the array 1862 template <class Array> 1863 long long expr2ExprArray(Expression* arg, Array& aVD) { 1864 ArrayLit* al = eval_array_lit(getEnv()->envi(), arg); 1865 checkOrResize(aVD, al->size()); 1866 for (unsigned int i = 0; i < al->size(); i++) aVD[i] = ((*al)[i]); 1867 return al->min(0); 1868 } 1869 1870 double expr2Const(Expression* arg) { 1871 if (IntLit* il = arg->dyn_cast<IntLit>()) { 1872 return (static_cast<double>(il->v().toInt())); 1873 } else if (FloatLit* fl = arg->dyn_cast<FloatLit>()) { 1874 return (fl->v().toDouble()); 1875 } else if (BoolLit* bl = arg->dyn_cast<BoolLit>()) { 1876 return (bl->v()); 1877 } else { 1878 MZN_MIPD__assert_hard_msg(0, 1879 "unexpected expression instead of an int/float/bool literal: eid=" 1880 << arg->eid() << " while E_INTLIT=" << Expression::E_INTLIT); 1881 } 1882 return 0.0; 1883 } 1884 1885 template <class Container, class Elem = int, size_t = 0> 1886 void checkOrResize(Container& cnt, size_t sz) { 1887 cnt.resize(sz); 1888 } 1889 1890 template <class Elem, size_t N> 1891 void checkOrResize(std::array<Elem, N>& cnt, size_t sz) { 1892 MZN_MIPD__assert_hard(cnt.size() == sz); 1893 } 1894 1895 template <class Array> 1896 void expr2Array(Expression* arg, Array& vals) { 1897 ArrayLit* al = eval_array_lit(getEnv()->envi(), arg); 1898 // if ( typeid(typename Array::pointer) == typeid(typename Array::iterator) ) // fixed 1899 // array 1900 // MZN_MIPD__assert_hard( vals.size() == al->v().size() ); 1901 // else 1902 // vals.resize( al->v().size() ); 1903 checkOrResize(vals, al->size()); 1904 for (unsigned int i = 0; i < al->size(); i++) { 1905 vals[i] = expr2Const((*al)[i]); 1906 } 1907 } 1908 1909 void printStats(std::ostream& os) { 1910 // if ( aCliques.empty() ) 1911 // return; 1912 if (vVarDescr.empty()) return; 1913 int nc = 0; 1914 for (auto& cl : aCliques) 1915 if (cl.size()) ++nc; 1916 for (auto& var : vVarDescr) 1917 if (0 > var.nClique) 1918 ++nc; // 1-var cliques 1919 // os << "N cliques " << aCliques.size() << " total, " 1920 // << nc << " final" << std::endl; 1921 MZN_MIPD__assert_hard(nc); 1922 MIPD__stats[N_POSTs__eqNmapsize] = static_cast<double>(mNViews.size()); 1923 double nSubintvAve = MIPD__stats[N_POSTs__NSubintvSum] / nc; 1924 MZN_MIPD__assert_hard(MIPD__stats[N_POSTs__NSubintvSum]); 1925 double dSubSizeAve = MIPD__stats[N_POSTs__SubSizeSum] / MIPD__stats[N_POSTs__NSubintvSum]; 1926 os << MIPD__stats[N_POSTs__all] 1927 << " POSTs" 1928#ifdef __MZN__MIPDOMAINS__PRINTMORESTATS 1929 " [ "; 1930 for (int i = N_POSTs__intCmpReif; i <= N_POSTs__floatAux; ++i) os << MIPD__stats[i] << ','; 1931 os << " ], LINEQ [ "; 1932 for (int i = N_POSTs__eq2intlineq; i <= N_POSTs__eqNmapsize; ++i) os << MIPD__stats[i] << ','; 1933 os << " ]" 1934#endif 1935 ", " 1936 << MIPD__stats[N_POSTs__varsDirect] << " / " << MIPD__stats[N_POSTs__varsInvolved] 1937 << " vars, " << nc << " cliques, " << MIPD__stats[N_POSTs__NSubintvMin] << " / " 1938 << nSubintvAve << " / " << MIPD__stats[N_POSTs__NSubintvMax] << " NSubIntv m/a/m, " 1939 << MIPD__stats[N_POSTs__SubSizeMin] << " / " << dSubSizeAve << " / " 1940 << MIPD__stats[N_POSTs__SubSizeMax] << " SubIntvSize m/a/m, " 1941 << MIPD__stats[N_POSTs__cliquesWithEqEncode] << "+" << MIPD__stats[N_POSTs__clEEEnforced] 1942 << "(" << MIPD__stats[N_POSTs__clEEFound] << ")" 1943 << " clq eq_encoded "; 1944 // << std::flush 1945 if (TCliqueSorter::LinEqGraph::dCoefMax > 1.0) 1946 os << TCliqueSorter::LinEqGraph::dCoefMin << "--" << TCliqueSorter::LinEqGraph::dCoefMax 1947 << " abs coefs"; 1948 os << " ... "; 1949 } 1950 1951}; // class MIPD 1952 1953template <class N> 1954template <class N1> 1955void SetOfIntervals<N>::intersect(const SetOfIntervals<N1>& s2) { 1956 if (s2.empty()) { 1957 this->clear(); 1958 return; 1959 } 1960 this->cutOut(Interval<N>(Interval<N>::infMinus(), (N)s2.begin()->left)); 1961 for (auto is2 = s2.begin(); is2 != s2.end(); ++is2) { 1962 auto is2next = is2; 1963 ++is2next; 1964 this->cutOut( 1965 Interval<N>(is2->right, s2.end() == is2next ? Interval<N>::infPlus() : (N)is2next->left)); 1966 } 1967} 1968template <class N> 1969template <class N1> 1970void SetOfIntervals<N>::cutDeltas(const SetOfIntervals<N1>& s2, N1 delta) { 1971 if (this->empty()) return; 1972 // What if distance < delta? TODO 1973 for (auto is2 : s2) { 1974 if (is2.left > Interval<N1>::infMinus()) this->cutOut(Interval<N>(is2.left - delta, is2.left)); 1975 if (is2.right < Interval<N1>::infPlus()) 1976 this->cutOut(Interval<N>(is2.right, is2.right + delta)); 1977 } 1978} 1979template <class N> 1980void SetOfIntervals<N>::cutOut(const Interval<N>& intv) { 1981 DBGOUT_MIPD__("Cutting " << intv << " from " << (*this)); 1982 if (this->empty()) return; 1983 iterator it1 = (Interval<N>::infMinus() == intv.left) 1984 ? this->lower_bound(Interval<N>(intv.left, intv.right)) 1985 : this->upper_bound(Interval<N>(intv.left, intv.right)); 1986 auto it2Del1 = it1; // from which to delete 1987 if (this->begin() != it1) { 1988 --it1; 1989 const N it1l = it1->left; 1990 MZN_MIPD__assert_hard(it1l <= intv.left); 1991 if (it1->right > intv.left) { // split it 1992 it2Del1 = split(it1, intv.left).second; 1993 // it1->right = intv.left; READ-ONLY 1994 // this->erase(it1); 1995 // it1 = this->end(); 1996 // auto iR = this->insert( Interval<N>( it1l, intv.left ) ); 1997 // MZN_MIPD__assert_hard( iR.second ); 1998 } 1999 } 2000 DBGOUT_MIPD__("; after split 1: " << (*this)); 2001 // Processing the right end: 2002 auto it2 = this->lower_bound(Interval<N>(intv.right, intv.right + 1)); 2003 auto it2Del2 = it2; 2004 if (this->begin() != it2) { 2005 --it2; 2006 MZN_MIPD__assert_hard(it2->left < intv.right); 2007 const N it2r = it2->right; 2008 if ((Interval<N>::infPlus() == intv.right) ? (it2r > intv.right) 2009 : (it2r >= intv.right)) { // >=: split it 2010 // it2Del2 = split( it2, intv.right ).second; 2011 const bool fEEE = (it2Del1 == it2); 2012 this->erase(it2); 2013 it2 = this->end(); 2014 it2Del2 = this->insert(Interval<N>(intv.right, it2r)); 2015 if (fEEE) it2Del1 = it2Del2; 2016 } 2017 } 2018 DBGOUT_MIPD__("; after split 2: " << (*this)); 2019 DBGOUT_MIPD__("; cutting out: " << SetOfIntervals(it2Del1, it2Del2)); 2020#ifdef MZN_DBG_CHECK_ITER_CUTOUT 2021 { 2022 auto it = this->begin(); 2023 int nO = 0; 2024 do { 2025 if (it == it2Del1) { 2026 MZN_MIPD__assert_hard(!nO); 2027 ++nO; 2028 } 2029 if (it == it2Del2) { 2030 MZN_MIPD__assert_hard(1 == nO); 2031 ++nO; 2032 } 2033 if (this->end() == it) break; 2034 ++it; 2035 } while (1); 2036 MZN_MIPD__assert_hard(2 == nO); 2037 } 2038#endif 2039 this->erase(it2Del1, it2Del2); 2040 DBGOUT_MIPD(" ... gives " << (*this)); 2041} 2042template <class N> 2043typename SetOfIntervals<N>::SplitResult SetOfIntervals<N>::split(iterator& it, N pos) { 2044 MZN_MIPD__assert_hard(pos >= it->left); 2045 MZN_MIPD__assert_hard(pos <= it->right); 2046 Interval<N> intvOld = *it; 2047 this->erase(it); 2048 iterator it_01 = this->insert(Interval<N>(intvOld.left, pos)); 2049 iterator it_02 = this->insert(Interval<N>(pos, intvOld.right)); 2050 it = this->end(); 2051 return std::make_pair(it_01, it_02); 2052} 2053template <class N> 2054Interval<N> SetOfIntervals<N>::getBounds() const { 2055 if (this->empty()) return Interval<N>(Interval<N>::infPlus(), Interval<N>::infMinus()); 2056 iterator it2 = this->end(); 2057 --it2; 2058 return Interval<N>(this->begin()->left, it2->right); 2059} 2060template <class N> 2061bool SetOfIntervals<N>::checkFiniteBounds() { 2062 if (this->empty()) return false; 2063 auto bnds = getBounds(); 2064 return bnds.left > Interval<N>::infMinus() && bnds.right < Interval<N>::infPlus(); 2065} 2066template <class N> 2067bool SetOfIntervals<N>::checkDisjunctStrict() { 2068 for (auto it = this->begin(); it != this->end(); ++it) { 2069 if (it->left > it->right) return false; 2070 if (this->begin() != it) { 2071 auto it_1 = it; 2072 --it_1; 2073 if (it_1->right >= it->left) return false; 2074 } 2075 } 2076 return true; 2077} 2078/// Assumes integer interval bounds 2079template <class N> 2080int SetOfIntervals<N>::card_int() const { 2081 int nn = 0; 2082 for (auto it = this->begin(); it != this->end(); ++it) { 2083 ++nn; 2084 nn += int(round(it->right - it->left)); 2085 } 2086 return nn; 2087} 2088template <class N> 2089N SetOfIntervals<N>::max_interval() const { 2090 N ll = -1; 2091 for (auto it = this->begin(); it != this->end(); ++it) { 2092 ll = std::max(ll, it->right - it->left); 2093 } 2094 return ll; 2095} 2096/// Assumes integer interval bounds 2097template <class N> 2098void SetOfIntervals<N>::split2Bits() { 2099 Base bsNew; 2100 for (auto it = this->begin(); it != this->end(); ++it) { 2101 for (int v = static_cast<int>(round(it->left)); v <= round(it->right); ++v) 2102 bsNew.insert(Intv(v, v)); 2103 } 2104 *(Base*)this = std::move(bsNew); 2105} 2106 2107bool MIPD::fVerbose = false; 2108 2109void MIPdomains(Env& env, bool fVerbose, int nmi, double dmd) { 2110 MIPD mipd(&env, fVerbose, nmi, dmd); 2111 if (!mipd.MIPdomains()) { 2112 GCLock lock; 2113 env.envi().fail(); 2114 } 2115} 2116 2117double MIPD::TCliqueSorter::LinEqGraph::dCoefMin = +1e100; 2118double MIPD::TCliqueSorter::LinEqGraph::dCoefMax = -1e100; 2119 2120} // namespace MiniZinc