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