1 /* $Id$
2  *
3  * Name:    exprGroup.cpp
4  * Author:  Pietro Belotti
5  * Purpose: implementation of some methods for exprGroup
6  *
7  * (C) Carnegie-Mellon University, 2006-10.
8  * This file is licensed under the Eclipse Public License (EPL)
9  */
10 
11 #include "CouenneExprConst.hpp"
12 #include "CouenneExprVar.hpp"
13 #include "CouenneExprGroup.hpp"
14 #include "CouenneExprClone.hpp"
15 #include "CouenneExprMul.hpp"
16 #include "CouenneDepGraph.hpp"
17 #include "CouenneProblem.hpp"
18 
19 #include <cassert>
20 
21 using namespace Couenne;
22 
23 namespace Couenne {
24 class Domain;
25 }
26 
27 // eliminates elements with zero coefficients
cleanZeros(std::vector<std::pair<exprVar *,CouNumber>> & lcoeff)28 void cleanZeros (std::vector <std::pair <exprVar *, CouNumber> > &lcoeff) {
29 
30   std::vector <std::pair <exprVar *, CouNumber> >::iterator i = lcoeff.begin ();
31 
32   int    ind  = 0;
33   size_t size = lcoeff.size ();
34 
35   while (size-- > 0) {
36     if (i -> second ==  0.) { // or equivalently, i -> second == -0.
37       lcoeff.erase (i);
38       i = lcoeff.begin () + ind;
39     } else {
40       ++i;
41       ++ind;
42     }
43   }
44 }
45 
46 
47 
48 /// Generalized (static) constructor: check parameters and return a
49 /// constant, a single variable, or a real exprGroup
genExprGroup(CouNumber c0,std::vector<std::pair<exprVar *,CouNumber>> & lcoeff,expression ** al,int n)50 expression *exprGroup::genExprGroup (CouNumber c0,
51 				     std::vector <std::pair <exprVar *, CouNumber> > &lcoeff,
52 				     expression **al,
53 				     int n) {
54   size_t nl = lcoeff.size ();
55   expression *ret = NULL;
56 
57   cleanZeros (lcoeff);
58 
59   // a constant
60   if ((n==0) && (nl==0))
61     ret = new exprConst (c0); // a constant auxiliary? FIX!
62 
63   else if ((n==0) && (fabs (c0) < COUENNE_EPS) && (nl==1)) { // a linear monomial, cx
64 
65     if (fabs (lcoeff[0]. second - 1) < COUENNE_EPS)
66       ret    = new exprClone (lcoeff[0]. first);
67     else ret = new exprMul (new exprConst (lcoeff[0]. second), new exprClone (lcoeff[0]. first));
68 
69   } else ret = new exprGroup (c0, lcoeff, al, n);
70 
71   return ret;
72 }
73 
74 
75 /// Constructor
exprGroup(CouNumber c0,std::vector<std::pair<exprVar *,CouNumber>> & lcoeff,expression ** al,int n)76 exprGroup::exprGroup (CouNumber c0,
77 		      std::vector <std::pair <exprVar *, CouNumber> > &lcoeff,
78 		      expression **al,
79 		      int n):
80   exprSum  (al, n),
81   lcoeff_  (lcoeff),
82   c0_      (c0) {
83 
84   cleanZeros (lcoeff_);
85 }
86 
87 
88 /// copy constructor
exprGroup(const exprGroup & src,Domain * d)89 exprGroup::exprGroup  (const exprGroup &src, Domain *d):
90   exprSum   (src.clonearglist (d), src.nargs_),
91   c0_       (src.c0_) {
92 
93   for (lincoeff::iterator i = src.lcoeff_.begin (); i != src.lcoeff_.end (); ++i)
94 
95     lcoeff_ . push_back (std::pair <exprVar *, CouNumber>
96 			 //(dynamic_cast <exprVar *> (i -> first -> clone (d)), i -> second));
97 			 (new exprVar (i -> first -> Index (), d), i -> second));
98 }
99 
100 
101 /// Destructor -- check if there are exprBounds and delete them
~exprGroup()102 exprGroup::~exprGroup () {
103 
104   for (lincoeff::iterator i = lcoeff_.begin (); i != lcoeff_.end (); ++i) {
105     enum expr_type code = i -> first -> code ();
106     if ((code == COU_EXPRLBOUND) ||
107 	(code == COU_EXPRUBOUND))
108       delete i -> first;
109   }
110 }
111 
112 
113 /// I/O
print(std::ostream & out,bool descend) const114 void exprGroup::print (std::ostream &out, bool descend) const {
115 
116   //if (code () == COU_EXPRGROUP)
117   if (lcoeff_.size () > 0)
118     out << '(';
119 
120   bool nzNL = nargs_ && ((nargs_ > 1) ||
121 			 ((*arglist_) -> Type () != CONST) ||
122 			 (fabs ((*arglist_) -> Value ()) > COUENNE_EPS));
123 
124   if (nzNL)
125     exprSum::print (out, descend);
126 
127   if      (c0_ >   0.) {if (nzNL) out << '+'; out << c0_;}
128   else if (c0_ < - 0.)                        out << c0_;
129 
130   for (size_t n = lcoeff_.size (), i=0; n--; i++) {
131 
132     CouNumber coeff = lcoeff_ [i]. second;
133 
134     if      (coeff >   0.) { if (i || (c0_ != 0.) || nzNL) out << '+'; if (coeff !=  1.) out <<  coeff << "*";}
135     else if (coeff < - 0.) {                               out << '-'; if (coeff != -1.) out << -coeff << "*";}
136 
137     lcoeff_ [i]. first -> print (out, descend);
138     if (!((i + 1) % MAX_ARG_LINE) && n)
139       out << std::endl;
140   }
141 
142   //if (code () == COU_EXPRGROUP)
143   if (lcoeff_.size () > 0)
144     out << ')';
145 }
146 
147 
148 /// differentiation
differentiate(int index)149 expression *exprGroup::differentiate (int index) {
150 
151   expression **arglist = new expression * [nargs_ + 1];
152 
153   CouNumber totlin=0;
154 
155   for (lincoeff::iterator el = lcoeff_.begin (); el != lcoeff_.end (); ++el)
156     if (el -> first -> Index () == index)
157       totlin += el -> second;
158 
159   int nargs = 0;
160 
161   if (fabs (totlin) > COUENNE_EPS)
162     arglist [nargs++] = new exprConst (totlin);
163 
164   for (int i = 0; i < nargs_; i++)
165     if (arglist_ [i] -> dependsOn (&index, 1))
166       arglist [nargs++] = arglist_ [i] -> differentiate (index);
167 
168   if ((nargs == 0) ||
169       ((nargs == 1) && (fabs (totlin) > COUENNE_EPS))) {
170     delete [] arglist;
171     return new exprConst (totlin);
172   }
173   else return new exprSum (arglist, nargs);
174 }
175 
176 
177 /// get a measure of "how linear" the expression is:
Linearity()178 int exprGroup::Linearity () {
179 
180   int
181     nllin = exprSum::Linearity (),    // linearity of nonlinear part
182     llin  = (lcoeff_.size () == 0) ?  //              linear part
183     ((fabs (c0_) < COUENNE_EPS) ? ZERO : CONSTANT) :
184     LINEAR;
185 
186   return (llin > nllin) ? llin : nllin;
187 }
188 
189 
190 /// compare affine terms
compare(exprGroup & e)191 int exprGroup::compare (exprGroup &e) {
192 
193   // !!! why?
194 
195   //int sum = exprSum::compare (e);
196 
197   //if (sum != 0)
198   //return sum;
199 
200   if (c0_ < e.c0_ - COUENNE_EPS) return -1;
201   if (c0_ > e.c0_ + COUENNE_EPS) return  1;
202 
203   if (lcoeff_.size () < e.lcoeff_.size ()) return -1;
204   if (lcoeff_.size () > e.lcoeff_.size ()) return  1;
205 
206   for (lincoeff::iterator
207 	 el1 =   lcoeff_.begin (),
208 	 el2 = e.lcoeff_.begin ();
209        el1 != lcoeff_.end ();
210        ++el1, ++el2) {
211 
212     int
213       ind1 = el1 -> first -> Index (),
214       ind2 = el2 -> first -> Index ();
215 
216     CouNumber
217       coe1 = el1 -> second,
218       coe2 = el2 -> second;
219 
220     if (ind1 < ind2) return -1;
221     if (ind1 > ind2) return  1;
222 
223     if (coe1 < coe2 - COUENNE_EPS) return -1;
224     if (coe1 > coe2 + COUENNE_EPS) return  1;
225   }
226 
227   return 0;
228 }
229 
230 
231 /// used in rank-based branching variable choice
232 
rank()233 int exprGroup::rank () {
234 
235   int maxrank = exprOp::rank ();
236 
237   if (maxrank < 0)
238     maxrank = 0;
239 
240   for (lincoeff::iterator el = lcoeff_.begin (); el != lcoeff_.end (); ++el) {
241 
242     int r = el -> first -> rank ();
243     if (r > maxrank)
244       maxrank = r;
245   }
246 
247   return maxrank;
248 }
249 
250 
251 /// update dependence set with index of this variable
fillDepSet(std::set<DepNode *,compNode> * dep,DepGraph * g)252 void exprGroup::fillDepSet (std::set <DepNode *, compNode> *dep, DepGraph *g) {
253 
254   exprOp::fillDepSet (dep, g);
255 
256   for (lincoeff::iterator el = lcoeff_.begin (); el != lcoeff_.end (); ++el)
257     dep -> insert (g -> lookup (el -> first -> Index ()));
258 }
259 
260 
261 /// fill in the set with all indices of variables appearing in the
262 /// expression
DepList(std::set<int> & deplist,enum dig_type type)263 int exprGroup::DepList (std::set <int> &deplist,
264 			enum dig_type type) {
265 
266   int deps = exprOp::DepList (deplist, type);
267 
268   for (lincoeff::iterator el = lcoeff_.begin (); el != lcoeff_.end (); ++el) {
269 
270     /*printf ("before [");
271     el -> first -> print ();
272     printf ("]: {");
273     for (std::set <int>::iterator i=deplist.begin (); i != deplist.end(); ++i)
274     printf ("%d ", *i);*/
275 
276     deps += el -> first -> DepList (deplist, type);
277 
278     /*printf ("}, after: {");
279     for (std::set <int>::iterator i=deplist.begin (); i != deplist.end(); ++i)
280       printf ("%d ", *i);
281       printf ("}\n");*/
282   }
283 
284   return deps;
285 }
286 
287 
288 /// is this linear term integer?
isInteger()289 bool exprGroup::isInteger () {
290 
291   if (!(::isInteger (c0_)) ||
292       !(exprOp::isInteger ()))
293     return false;
294 
295   for (lincoeff::iterator el = lcoeff_.begin (); el != lcoeff_.end (); ++el) {
296 
297     CouNumber coe = el -> second;
298 
299     bool
300       intCoe = ::isInteger (coe),
301       intVar = el -> first -> isInteger ();
302 
303     if (intCoe && intVar)
304       continue;
305 
306     CouNumber
307       lb = el -> first -> lb (),
308       ub = el -> first -> ub ();
309 
310     // check var fixed and product is integer
311     if ((fabs (lb - ub) < COUENNE_EPS) &&
312 	(::isInteger (lb * coe) ||
313 	 (intCoe && ::isInteger (lb))))
314       continue;
315 
316     return false;
317   }
318 
319   return true;
320 }
321 
322 
323 /// replace variable x with new (aux) w
replace(exprVar * x,exprVar * w)324 void exprGroup::replace (exprVar *x, exprVar *w) {
325 
326   exprOp::replace (x, w);
327 
328   int
329     xind = x -> Index (),
330     wind = w -> Index ();
331 
332   // find occurrences of x and w in vector of variabls
333 
334   lincoeff::iterator x_occur = lcoeff_.begin ();
335 
336   // Do not assume index vector is sorted in ascending order
337   // w.r.t. (*iterator) -> first () -> Index()
338   while ((x_occur != lcoeff_.end ()) &&
339 	 (x_occur -> first -> Index () != xind))
340     ++x_occur;
341 
342   if (x_occur == lcoeff_ .end ()) // not found, bail out
343     return;
344 
345   if (xind == wind)
346     x_occur -> first = w;
347   else { // replacing two variables, not the features of one
348 
349     lincoeff::iterator w_occur = lcoeff_.begin ();
350 
351     // Do not assume index vector is sorted in ascending order
352     // w.r.t. (*iterator) -> first () -> Index()
353     while ((w_occur != lcoeff_.end ()) &&
354 	   (w_occur -> first -> Index () != wind))
355       ++w_occur;
356 
357     if (w_occur == lcoeff_ . end ()) // not found w, simply substitute
358       x_occur -> first = w;
359     else {
360 		if ((w_occur -> second += x_occur -> second) == 0.) { // add coefficients
361 	lcoeff_.erase (w_occur);                // if they cancel out, delete w as well
362 
363 	// under Microsoft, x_occur may have been invalidated by removing w_occur from lcoeff_, so we search for it again
364 	for( x_occur = lcoeff_.begin (); x_occur -> first -> Index () != xind; ++x_occur )
365 		assert(x_occur != lcoeff_ .end ()); // it was found before, so it should be still found
366 	}
367       lcoeff_.erase   (x_occur);                // delete entry of x
368     }
369   }
370 }
371 
372 
373 /// return l-2 norm of gradient at given point. Not needed for now, as
374 /// we only use it with nonlinear operators
gradientNorm(const double * x)375 CouNumber exprGroup::gradientNorm (const double *x) {
376 
377   CouNumber retval = 0;
378 
379   for (lincoeff::iterator el = lcoeff_.begin (); el != lcoeff_.end (); ++el)
380     retval += el -> second * el -> second;
381 
382   return sqrt (retval);
383 }
384 
385 
386 /// Redirect variables to proper variable vector
realign(const CouenneProblem * p)387 void exprGroup::realign (const CouenneProblem *p) {
388 
389   for (lincoeff::iterator el = lcoeff_.begin (); el != lcoeff_.end (); ++el) {
390 
391     exprVar *var = el -> first;
392 
393     if (((var -> Type () == VAR) ||
394 	 (var -> Type () == AUX)) &&
395 	(var -> Original () != p -> Var (var -> Index ()))) {
396 
397       expression *trash = var;
398       el -> first = p -> Var (var -> Index ());
399       delete trash;
400     }
401   }
402 }
403 
404 
405 /// simplification
simplify()406 expression *exprGroup::simplify () {
407   exprOp::simplify ();
408   //if (lcoeff_. size () <= 0) // this is just a constant
409   //return new exprConst (c0_);
410   //else
411   return NULL;
412 }
413 
414