1 /* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2 
3 /*
4  *  Main authors:
5  *     Guido Tack <guido.tack@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 not distributed with this
10  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
11 
12 #include <minizinc/eval_par.hh>
13 #include <minizinc/flatten_internal.hh>
14 #include <minizinc/optimize_constraints.hh>
15 
16 namespace MiniZinc {
17 
reg(const MiniZinc::ASTString & call,optimizer opt)18 void OptimizeRegistry::reg(const MiniZinc::ASTString& call, optimizer opt) {
19   _m.insert(std::make_pair(call, opt));
20 }
21 
process(EnvI & env,MiniZinc::Item * i,MiniZinc::Call * c,Expression * & rewrite)22 OptimizeRegistry::ConstraintStatus OptimizeRegistry::process(EnvI& env, MiniZinc::Item* i,
23                                                              MiniZinc::Call* c,
24                                                              Expression*& rewrite) {
25   auto it = _m.find(c->id());
26   if (it != _m.end()) {
27     return it->second(env, i, c, rewrite);
28   }
29   return CS_NONE;
30 }
31 
registry()32 OptimizeRegistry& OptimizeRegistry::registry() {
33   static OptimizeRegistry reg;
34   return reg;
35 }
36 
37 namespace Optimizers {
38 
o_linear(EnvI & env,Item * ii,Call * c,Expression * & rewrite)39 OptimizeRegistry::ConstraintStatus o_linear(EnvI& env, Item* ii, Call* c, Expression*& rewrite) {
40   ArrayLit* al_c = eval_array_lit(env, c->arg(0));
41   std::vector<IntVal> coeffs(al_c->size());
42   for (unsigned int i = 0; i < al_c->size(); i++) {
43     coeffs[i] = eval_int(env, (*al_c)[i]);
44   }
45   ArrayLit* al_x = eval_array_lit(env, c->arg(1));
46   std::vector<KeepAlive> x(al_x->size());
47   for (unsigned int i = 0; i < al_x->size(); i++) {
48     x[i] = (*al_x)[i];
49   }
50   IntVal d = 0;
51   simplify_lin<IntLit>(coeffs, x, d);
52   if (coeffs.empty()) {
53     bool failed;
54     if (c->id() == constants().ids.int_.lin_le) {
55       failed = (d > eval_int(env, c->arg(2)));
56     } else if (c->id() == constants().ids.int_.lin_eq) {
57       failed = (d != eval_int(env, c->arg(2)));
58     } else {
59       failed = (d == eval_int(env, c->arg(2)));
60     }
61     if (failed) {
62       return OptimizeRegistry::CS_FAILED;
63     }
64     return OptimizeRegistry::CS_ENTAILED;
65   }
66   if (coeffs.size() == 1 && (ii->isa<ConstraintI>() || ii->cast<VarDeclI>()->e()->ti()->domain() ==
67                                                            constants().literalTrue)) {
68     VarDecl* vd = x[0]()->cast<Id>()->decl();
69     IntSetVal* domain =
70         vd->ti()->domain() != nullptr ? eval_intset(env, vd->ti()->domain()) : nullptr;
71     if (c->id() == constants().ids.int_.lin_eq) {
72       IntVal rd = eval_int(env, c->arg(2)) - d;
73       if (rd % coeffs[0] == 0) {
74         IntVal nd = rd / coeffs[0];
75         if ((domain != nullptr) && !domain->contains(nd)) {
76           return OptimizeRegistry::CS_FAILED;
77         }
78         std::vector<Expression*> args(2);
79         args[0] = x[0]();
80         args[1] = IntLit::a(nd);
81         Call* nc = new Call(Location(), constants().ids.int_.eq, args);
82         nc->type(Type::varbool());
83         rewrite = nc;
84         return OptimizeRegistry::CS_REWRITE;
85       }
86       return OptimizeRegistry::CS_FAILED;
87     }
88     if (c->id() == constants().ids.int_.lin_le) {
89       IntVal ac = std::abs(coeffs[0]);
90       IntVal rd = eval_int(env, c->arg(2)) - d;
91       IntVal ad = std::abs(rd);
92       IntVal nd;
93       if (ad % ac == 0) {
94         nd = rd / coeffs[0];
95       } else {
96         double nd_d = static_cast<double>(ad.toInt()) / static_cast<double>(ac.toInt());
97         if (coeffs[0] >= 0 && rd >= 0) {
98           nd = static_cast<long long int>(std::floor(nd_d));
99         } else if (rd >= 0) {
100           nd = -static_cast<long long int>(std::floor(nd_d));
101         } else if (coeffs[0] >= 0) {
102           nd = -static_cast<long long int>(std::ceil(nd_d));
103         } else {
104           nd = static_cast<long long int>(std::ceil(nd_d));
105         }
106       }
107       bool swapSign = coeffs[0] < 0;
108       if (domain != nullptr) {
109         if (swapSign) {
110           if (domain->max() < nd) {
111             return OptimizeRegistry::CS_FAILED;
112           }
113           if (domain->min() >= nd) {
114             return OptimizeRegistry::CS_ENTAILED;
115           }
116         } else {
117           if (domain->min() > nd) {
118             return OptimizeRegistry::CS_FAILED;
119           }
120           if (domain->max() <= nd) {
121             return OptimizeRegistry::CS_ENTAILED;
122           }
123         }
124         std::vector<Expression*> args(2);
125         args[0] = x[0]();
126         args[1] = IntLit::a(nd);
127         if (swapSign) {
128           std::swap(args[0], args[1]);
129         }
130         Call* nc = new Call(Location(), constants().ids.int_.le, args);
131         nc->type(Type::varbool());
132         rewrite = nc;
133         return OptimizeRegistry::CS_REWRITE;
134       }
135     }
136   } else if (c->id() == constants().ids.int_.lin_eq && coeffs.size() == 2 &&
137              ((coeffs[0] == 1 && coeffs[1] == -1) || (coeffs[1] == 1 && coeffs[0] == -1)) &&
138              eval_int(env, c->arg(2)) - d == 0) {
139     std::vector<Expression*> args(2);
140     args[0] = x[0]();
141     args[1] = x[1]();
142     Call* nc = new Call(Location(), constants().ids.int_.eq, args);
143     rewrite = nc;
144     return OptimizeRegistry::CS_REWRITE;
145   }
146   if (coeffs.size() < al_c->size()) {
147     std::vector<Expression*> coeffs_e(coeffs.size());
148     std::vector<Expression*> x_e(coeffs.size());
149     for (unsigned int i = 0; i < coeffs.size(); i++) {
150       coeffs_e[i] = IntLit::a(coeffs[i]);
151       x_e[i] = x[i]();
152     }
153     auto* al_c_new = new ArrayLit(al_c->loc(), coeffs_e);
154     al_c_new->type(Type::parint(1));
155     auto* al_x_new = new ArrayLit(al_x->loc(), x_e);
156     al_x_new->type(al_x->type());
157 
158     std::vector<Expression*> args(3);
159     args[0] = al_c_new;
160     args[1] = al_x_new;
161     args[2] = IntLit::a(eval_int(env, c->arg(2)) - d);
162     Call* nc = new Call(Location(), c->id(), args);
163     nc->type(Type::varbool());
164     for (ExpressionSetIter it = c->ann().begin(); it != c->ann().end(); ++it) {
165       nc->addAnnotation(*it);
166     }
167 
168     rewrite = nc;
169     return OptimizeRegistry::CS_REWRITE;
170   }
171   return OptimizeRegistry::CS_OK;
172 }
173 
o_lin_exp(EnvI & env,Item * i,Call * c,Expression * & rewrite)174 OptimizeRegistry::ConstraintStatus o_lin_exp(EnvI& env, Item* i, Call* c, Expression*& rewrite) {
175   if (c->type().isint()) {
176     ArrayLit* al_c = eval_array_lit(env, c->arg(0));
177     std::vector<IntVal> coeffs(al_c->size());
178     for (unsigned int j = 0; j < al_c->size(); j++) {
179       coeffs[j] = eval_int(env, (*al_c)[j]);
180     }
181     ArrayLit* al_x = eval_array_lit(env, c->arg(1));
182     std::vector<KeepAlive> x(al_x->size());
183     for (unsigned int j = 0; j < al_x->size(); j++) {
184       x[j] = (*al_x)[j];
185     }
186     IntVal d = eval_int(env, c->arg(2));
187     simplify_lin<IntLit>(coeffs, x, d);
188     if (coeffs.empty()) {
189       rewrite = IntLit::a(d);
190       return OptimizeRegistry::CS_REWRITE;
191     }
192     if (coeffs.size() < al_c->size()) {
193       if (coeffs.size() == 1 && coeffs[0] == 1 && d == 0) {
194         rewrite = x[0]();
195         return OptimizeRegistry::CS_REWRITE;
196       }
197 
198       std::vector<Expression*> coeffs_e(coeffs.size());
199       std::vector<Expression*> x_e(coeffs.size());
200       for (unsigned int j = 0; j < coeffs.size(); j++) {
201         coeffs_e[j] = IntLit::a(coeffs[j]);
202         x_e[j] = x[j]();
203       }
204       auto* al_c_new = new ArrayLit(al_c->loc(), coeffs_e);
205       al_c_new->type(Type::parint(1));
206       auto* al_x_new = new ArrayLit(al_x->loc(), x_e);
207       al_x_new->type(al_x->type());
208 
209       std::vector<Expression*> args(3);
210       args[0] = al_c_new;
211       args[1] = al_x_new;
212       args[2] = IntLit::a(d);
213       Call* nc = new Call(Location(), c->id(), args);
214       nc->type(c->type());
215       for (ExpressionSetIter it = c->ann().begin(); it != c->ann().end(); ++it) {
216         nc->addAnnotation(*it);
217       }
218       rewrite = nc;
219       return OptimizeRegistry::CS_REWRITE;
220     }
221   }
222   return OptimizeRegistry::CS_OK;
223 }
224 
o_element(EnvI & env,Item * i,Call * c,Expression * & rewrite)225 OptimizeRegistry::ConstraintStatus o_element(EnvI& env, Item* i, Call* c, Expression*& rewrite) {
226   if (c->arg(0)->isa<IntLit>()) {
227     IntVal idx = eval_int(env, c->arg(0));
228     ArrayLit* al = eval_array_lit(env, c->arg(1));
229     if (idx < 1 || idx > al->size()) {
230       return OptimizeRegistry::CS_FAILED;
231     }
232     Expression* result = (*al)[static_cast<int>(idx.toInt()) - 1];
233     std::vector<Expression*> args(2);
234     args[0] = result;
235     args[1] = c->arg(2);
236     Call* eq = new Call(Location(), constants().ids.int_.eq, args);
237     rewrite = eq;
238     return OptimizeRegistry::CS_REWRITE;
239   }
240   return OptimizeRegistry::CS_OK;
241 }
242 
o_clause(EnvI & env,Item * i,Call * c,Expression * & rewrite)243 OptimizeRegistry::ConstraintStatus o_clause(EnvI& env, Item* i, Call* c, Expression*& rewrite) {
244   std::vector<VarDecl*> pos;
245   std::vector<VarDecl*> neg;
246   ArrayLit* al_pos = eval_array_lit(env, c->arg(0));
247   for (unsigned int j = 0; j < al_pos->size(); j++) {
248     if (Id* ident = (*al_pos)[j]->dynamicCast<Id>()) {
249       if (ident->decl()->ti()->domain() == nullptr) {
250         pos.push_back(ident->decl());
251       }
252     }
253   }
254   ArrayLit* al_neg = eval_array_lit(env, c->arg(1));
255   for (unsigned int j = 0; j < al_neg->size(); j++) {
256     if (Id* ident = (*al_neg)[j]->dynamicCast<Id>()) {
257       if (ident->decl()->ti()->domain() == nullptr) {
258         neg.push_back(ident->decl());
259       }
260     }
261   }
262   bool subsumed = false;
263   if (!pos.empty() && !neg.empty()) {
264     std::sort(pos.begin(), pos.end());
265     std::sort(neg.begin(), neg.end());
266     unsigned int ix = 0;
267     unsigned int iy = 0;
268     for (;;) {
269       if (pos[ix] == neg[iy]) {
270         subsumed = true;
271         break;
272       }
273       if (pos[ix] < neg[iy]) {
274         ix++;
275       } else {
276         iy++;
277       }
278       if (ix == pos.size() || iy == neg.size()) {
279         break;
280       }
281     }
282   }
283   if (subsumed) {
284     return OptimizeRegistry::CS_ENTAILED;
285   }
286   return OptimizeRegistry::CS_OK;
287 }
288 
o_not(EnvI & env,Item * i,Call * c,Expression * & rewrite)289 OptimizeRegistry::ConstraintStatus o_not(EnvI& env, Item* i, Call* c, Expression*& rewrite) {
290   if (c->argCount() == 2) {
291     Expression* e0 = c->arg(0);
292     Expression* e1 = c->arg(1);
293     if (e0->type().isPar() && e1->type().isPar()) {
294       return eval_bool(env, e0) == eval_bool(env, e1) ? OptimizeRegistry::CS_FAILED
295                                                       : OptimizeRegistry::CS_ENTAILED;
296     }
297     if (e1->type().isPar()) {
298       std::swap(e0, e1);
299     }
300     if (e0->type().isPar()) {
301       Call* eq = new Call(Location(), constants().ids.bool_eq,
302                           {e1, constants().boollit(!eval_bool(env, e0))});
303       rewrite = eq;
304       return OptimizeRegistry::CS_REWRITE;
305     }
306   }
307   return OptimizeRegistry::CS_OK;
308 }
309 
o_div(EnvI & env,Item * i,Call * c,Expression * & rewrite)310 OptimizeRegistry::ConstraintStatus o_div(EnvI& env, Item* i, Call* c, Expression*& rewrite) {
311   if (c->arg(1)->type().isPar()) {
312     IntVal c1v = eval_int(env, c->arg(1));
313     if (c->arg(0)->type().isPar() && c->argCount() == 3 && c->arg(2)->type().isPar()) {
314       IntVal c0v = eval_int(env, c->arg(0));
315       IntVal c2v = eval_int(env, c->arg(2));
316       return (c0v / c1v == c2v) ? OptimizeRegistry::CS_ENTAILED : OptimizeRegistry::CS_FAILED;
317     }
318   }
319   return OptimizeRegistry::CS_OK;
320 }
321 
o_times(EnvI & env,Item * i,Call * c,Expression * & rewrite)322 OptimizeRegistry::ConstraintStatus o_times(EnvI& env, Item* i, Call* c, Expression*& rewrite) {
323   Expression* result = nullptr;
324   Expression* arg0 = c->arg(0);
325   Expression* arg1 = c->arg(1);
326   if (arg0->type().isPar() && arg1->type().isPar()) {
327     IntVal c0v = eval_int(env, arg0);
328     IntVal c1v = eval_int(env, arg1);
329     result = IntLit::a(c0v * c1v);
330   } else if (arg0->type().isPar()) {
331     IntVal c0v = eval_int(env, arg0);
332     if (c0v == 0) {
333       result = IntLit::a(0);
334     } else if (c0v == 1) {
335       result = arg1;
336     }
337   } else if (arg1->type().isPar()) {
338     IntVal c1v = eval_int(env, arg1);
339     if (c1v == 0) {
340       result = IntLit::a(0);
341     }
342     if (c1v == 1) {
343       result = arg0;
344     }
345   }
346 
347   if (result != nullptr) {
348     if (c->argCount() == 2) {
349       // this is the functional version of times
350       rewrite = result;
351       return OptimizeRegistry::CS_REWRITE;
352     }  // this is the relational version of times
353     assert(c->argCount() == 3);
354     rewrite = new Call(Location().introduce(), constants().ids.int_.eq, {c->arg(2), result});
355     return OptimizeRegistry::CS_REWRITE;
356   }
357   return OptimizeRegistry::CS_OK;
358 }
359 
o_set_in(EnvI & env,Item * i,Call * c,Expression * & rewrite)360 OptimizeRegistry::ConstraintStatus o_set_in(EnvI& env, Item* i, Call* c, Expression*& rewrite) {
361   if (c->arg(1)->type().isPar()) {
362     if (c->arg(0)->type().isPar()) {
363       IntSetVal* isv = eval_intset(env, c->arg(1));
364       return isv->contains(eval_int(env, c->arg(0))) ? OptimizeRegistry::CS_ENTAILED
365                                                      : OptimizeRegistry::CS_FAILED;
366     }
367     if (Id* ident = c->arg(0)->dynamicCast<Id>()) {
368       VarDecl* vd = ident->decl();
369       IntSetVal* isv = eval_intset(env, c->arg(1));
370       if (vd->ti()->domain() != nullptr) {
371         IntSetVal* dom = eval_intset(env, vd->ti()->domain());
372         {
373           IntSetRanges isv_r(isv);
374           IntSetRanges dom_r(dom);
375           if (Ranges::subset(dom_r, isv_r)) {
376             return OptimizeRegistry::CS_ENTAILED;
377           }
378         }
379         {
380           IntSetRanges isv_r(isv);
381           IntSetRanges dom_r(dom);
382           if (Ranges::disjoint(dom_r, isv_r)) {
383             return OptimizeRegistry::CS_FAILED;
384           }
385         }
386       } else if (isv->min() == isv->max()) {
387         std::vector<Expression*> args(2);
388         args[0] = vd->id();
389         args[1] = IntLit::a(isv->min());
390         Call* eq = new Call(Location(), constants().ids.int_.eq, args);
391         rewrite = eq;
392         return OptimizeRegistry::CS_REWRITE;
393       }
394     }
395   }
396   return OptimizeRegistry::CS_OK;
397 }
398 
o_int_ne(EnvI & env,Item * i,Call * c,Expression * & rewrite)399 OptimizeRegistry::ConstraintStatus o_int_ne(EnvI& env, Item* i, Call* c, Expression*& rewrite) {
400   Expression* e0 = c->arg(0);
401   Expression* e1 = c->arg(1);
402   if (e0->type().isPar() && e1->type().isPar()) {
403     return eval_int(env, e0) != eval_int(env, e1) ? OptimizeRegistry::CS_ENTAILED
404                                                   : OptimizeRegistry::CS_FAILED;
405   }
406   if (e1->isa<Id>()) {
407     std::swap(e0, e1);
408   }
409   if (Id* ident = e0->dynamicCast<Id>()) {
410     if (e1->type().isPar()) {
411       if (ident->decl()->ti()->domain() != nullptr) {
412         IntVal e1v = eval_int(env, e1);
413         IntSetVal* isv = eval_intset(env, ident->decl()->ti()->domain());
414         if (!isv->contains(e1v)) {
415           return OptimizeRegistry::CS_ENTAILED;
416         }
417         if (e1v == isv->min() && e1v == isv->max()) {
418           return OptimizeRegistry::CS_FAILED;
419         }
420       }
421     }
422   }
423 
424   return OptimizeRegistry::CS_OK;
425 }
426 
o_int_le(EnvI & env,Item * i,Call * c,Expression * & rewrite)427 OptimizeRegistry::ConstraintStatus o_int_le(EnvI& env, Item* i, Call* c, Expression*& rewrite) {
428   Expression* e0 = c->arg(0);
429   Expression* e1 = c->arg(1);
430   if (e0->type().isPar() && e1->type().isPar()) {
431     return eval_int(env, e0) <= eval_int(env, e1) ? OptimizeRegistry::CS_ENTAILED
432                                                   : OptimizeRegistry::CS_FAILED;
433   }
434   bool swapped = false;
435   if (e1->isa<Id>()) {
436     std::swap(e0, e1);
437     swapped = true;
438   }
439   if (Id* ident = e0->dynamicCast<Id>()) {
440     if (e1->type().isPar()) {
441       if (ident->decl()->ti()->domain() != nullptr) {
442         IntVal e1v = eval_int(env, e1);
443         IntSetVal* isv = eval_intset(env, ident->decl()->ti()->domain());
444         if (!swapped) {
445           if (isv->max() <= e1v) {
446             return OptimizeRegistry::CS_ENTAILED;
447           }
448           if (isv->min() > e1v) {
449             return OptimizeRegistry::CS_FAILED;
450           }
451         } else {
452           if (e1v <= isv->min()) {
453             return OptimizeRegistry::CS_ENTAILED;
454           }
455           if (e1v > isv->max()) {
456             return OptimizeRegistry::CS_FAILED;
457           }
458         }
459       }
460     }
461   }
462 
463   return OptimizeRegistry::CS_OK;
464 }
465 
466 class Register {
467 private:
468   Model* _keepAliveModel;
469 
470 public:
Register()471   Register() {
472     GCLock lock;
473     _keepAliveModel = new Model;
474     ASTString id_element("array_int_element");
475     ASTString id_var_element("array_var_int_element");
476     std::vector<Expression*> e;
477     e.push_back(new StringLit(Location(), id_element));
478     e.push_back(new StringLit(Location(), id_var_element));
479     _keepAliveModel->addItem(new ConstraintI(Location(), new ArrayLit(Location(), e)));
480     OptimizeRegistry::registry().reg(constants().ids.int_.lin_eq, o_linear);
481     OptimizeRegistry::registry().reg(constants().ids.int_.lin_le, o_linear);
482     OptimizeRegistry::registry().reg(constants().ids.int_.lin_ne, o_linear);
483     OptimizeRegistry::registry().reg(constants().ids.int_.div, o_div);
484     OptimizeRegistry::registry().reg(constants().ids.int_.times, o_times);
485     OptimizeRegistry::registry().reg(id_element, o_element);
486     OptimizeRegistry::registry().reg(constants().ids.lin_exp, o_lin_exp);
487     OptimizeRegistry::registry().reg(id_var_element, o_element);
488     OptimizeRegistry::registry().reg(constants().ids.clause, o_clause);
489     OptimizeRegistry::registry().reg(constants().ids.bool_clause, o_clause);
490     OptimizeRegistry::registry().reg(constants().ids.bool_not, o_not);
491     OptimizeRegistry::registry().reg(constants().ids.set_in, o_set_in);
492     OptimizeRegistry::registry().reg(constants().ids.int_.ne, o_int_ne);
493     OptimizeRegistry::registry().reg(constants().ids.int_.le, o_int_le);
494   }
~Register()495   ~Register() { delete _keepAliveModel; }
496 } _r;
497 
498 }  // namespace Optimizers
499 
500 }  // namespace MiniZinc
501