CouenneSdpCuts.cpp
Go to the documentation of this file.
1 /* $Id: CouenneSdpCuts.cpp 1259 2018-08-28 10:34:22Z pbelotti $
2  *
3  * Name: CouenneSdpCuts.cpp
4  * Author: Pietro Belotti
5  * Purpose: wrapper for Couenne to insert sdpcuts
6  *
7  * This file is licensed under the Eclipse Public License (EPL)
8  */
9 
10 #include "IpOptionsList.hpp"
11 
12 #include "CouenneJournalist.hpp"
13 #include "CouenneMatrix.hpp"
14 #include "CouennePSDcon.hpp"
15 #include "CouenneSdpCuts.hpp"
16 #include "CouenneProblem.hpp"
17 #include "CouenneExprVar.hpp"
18 #include "CouenneExprAux.hpp"
21 
22 #include "CoinTime.hpp"
23 
24 #include <set>
25 
26 using namespace Couenne;
27 
28 //#define DEBUG
29 
32  JnlstPtr jnlst,
34  problem_ (p),
35  doNotUse_ (false) {
36 
37  std::string s;
38 
39  options -> GetIntegerValue ("sdp_cuts_num_ev", numEigVec_, "couenne.");
40  options -> GetStringValue ("sdp_cuts_neg_ev", s, "couenne."); onlyNegEV_ = (s == "yes");
41  options -> GetStringValue ("sdp_cuts_sparsify", s, "couenne."); useSparsity_ = (s == "yes");
42  options -> GetStringValue ("sdp_cuts_fillmissing", s, "couenne."); fillMissingTerms_ = (s == "yes");
43 
44  CouenneExprMatrix *cauldron = new CouenneExprMatrix;
45 
46  // 1) Construct matrix with entries x_i, x_j
47 
48  for (std::vector <exprVar *>::iterator
49  i = p -> Variables (). begin ();
50  i != p -> Variables (). end (); ++i)
51 
52  if ((*i) -> Type () == AUX) {
53 
54  expression *image = (*i) -> Image ();
55 
57  if ((image -> code () == COU_EXPRMUL) &&
58  (image -> ArgList () [0] -> Type () != CONST) &&
59  (image -> ArgList () [1] -> Type () != CONST)) {
60 
61  int
62  index0 = image -> ArgList () [0] -> Index (),
63  index1 = image -> ArgList () [1] -> Index ();
64 
65  if ((index0 >= 0) &&
66  (index1 >= 0) &&
67  ((*i) -> Index () >= 0)) {
68 
69  // cauldron -> add_element (CoinMin (index0, index1),
70  // CoinMax (index0, index1), (*i));
71 
72  cauldron -> add_element (index0, index1, (*i));
73  cauldron -> add_element (index1, index0, (*i));
74  }
75  }
76 
78  if ((image -> code () == COU_EXPRPOW) &&
79  (image -> ArgList () [0] -> Type () != CONST) &&
80  (image -> ArgList () [1] -> Value () == 2.)) {
81 
82  int index0 = image -> ArgList () [0] -> Index ();
83 
84  if ( (index0 >= 0) &&
85  ((*i) -> Index () >= 0))
86 
87  cauldron -> add_element (index0, index0, (*i));
88  }
89  }
90 
91 #ifdef DEBUG
92  printf ("Cauldron so far\n");
93  cauldron -> print ();
94 #endif
95 
96  // TODO
97 
98  // 2) Block-partition it (optional), obtain matrices. Replace line
99  // below to decompose cauldron
100 
101  minors_ . push_back (cauldron);
102 
103  // 2.5) If option says so, add fictitious auxiliary variables if not there
104 
105  if (fillMissingTerms_) {
106 
107  for (std::vector <CouenneExprMatrix *>::iterator
108  i = minors_ . begin ();
109  i != minors_ . end (); ++i) {
110 
111  // First: construct (possibly sparse) index set, to check
112  // against each row (whose index set is a SUBSET, if non-proper,
113  // of varNumIndices). Do not use varIndices, as it is empty for
114  // now.
115 
116  std::set <int> varNumIndices;
117 
118  for (std::set <std::pair <int, CouenneSparseVector *>, CouenneExprMatrix::compare_pair_ind>::const_iterator
119  rowIt = (*i) -> getRows (). begin ();
120  rowIt != (*i) -> getRows (). end (); ++rowIt) {
121 
122  varNumIndices. insert (rowIt -> first);
123 
124  for (std::set <CouenneScalar *, CouenneSparseVector::compare_scalars>::const_iterator
125  elemIt = rowIt -> second -> getElements () . begin ();
126  elemIt != rowIt -> second -> getElements () . end (); ++elemIt)
127 
128  varNumIndices. insert ((*elemIt) -> getIndex ());
129  }
130 
131  // Second: check every row for elements (i,j) not in this row by
132  // parallel scanning of varNumINdices
133 
134  for (std::set <std::pair <int, CouenneSparseVector *>, CouenneExprMatrix::compare_pair_ind>::const_iterator
135  rowIt = (*i) -> getRows (). begin ();
136  rowIt != (*i) -> getRows (). end (); ++rowIt) {
137 
138  int rowInd = rowIt -> first;
139 
140  std::set <int>::iterator vniIt = varNumIndices . begin ();
141 
142  for (std::set <CouenneScalar *, CouenneSparseVector::compare_scalars>::const_iterator
143  elemIt = rowIt -> second -> getElements () . begin ();
144  elemIt != rowIt -> second -> getElements () . end (); ++elemIt) {
145 
146  int colInd = (*elemIt) -> getIndex ();
147 
148  while ((vniIt != varNumIndices . end ()) && (*vniIt < colInd)) {
149 
150  if (rowInd <= *vniIt) {
151  //printf ("missing term: %d, %d\n", rowInd, *vniIt);
152 
153  expression *image;
154 
155  if (rowInd == *vniIt) image = new exprPow (new exprClone (problem_ -> Var (rowInd)), new exprConst (2.));
156  else image = new exprMul (new exprClone (problem_ -> Var (rowInd)), new exprClone (problem_ -> Var (*vniIt)));
157 
158  exprAux *yIJ = problem_ -> addAuxiliary (image);
159 
160  // seek expression in the set
161  if (problem_ -> AuxSet () -> find (yIJ) ==
162  problem_ -> AuxSet () -> end ()) {
163 
164  // no such expression found in the set, create entry therein
165  problem_ -> Variables () . push_back (yIJ);
166  problem_ -> AuxSet () -> insert (yIJ); // insert into repetition checking structure
167  }
168 
169  (*i) -> add_element (rowInd, *vniIt, yIJ);
170  (*i) -> add_element (*vniIt, rowInd, yIJ);
171  }
172 
173  ++vniIt;
174  }
175 
176  if (vniIt == varNumIndices . end ())
177  break;
178  else ++vniIt;
179  }
180  }
181  }
182 
183  // post-rescan: update
184  //
185  // numbering_
186  // domain_
187  // commuted_
188  // optimum_
189  // integerRank_
190  // unusedOriginalsIndices_
191  //
192  // since there are new variables
193  }
194 
195  // 3) Bottom-right border each block with a row vector, a column vector,
196  // and the constant 1
197 
198  for (std::vector <CouenneExprMatrix *>::iterator
199  i = minors_ . begin ();
200  i != minors_ . end (); ++i) {
201 
202  int size = problem_ -> nVars ();
203 
204 #ifdef DEBUG
205  printf ("minor has %ld rows and %ld columns\n",
206  (*i) -> getRows () . size (),
207  (*i) -> getCols () . size ());
208 #endif
209 
210  for (std::set <std::pair <int, CouenneSparseVector *>, CouenneExprMatrix::compare_pair_ind>::const_iterator
211  j = (*i) -> getCols () . begin ();
212  j != (*i) -> getCols () . end (); ++j)
213 
214  (*i) -> varIndices () . push_back (problem_ -> Var (j -> first));
215 
216  for (std::vector <expression *>::iterator
217  j = (*i) -> varIndices () . begin ();
218  j != (*i) -> varIndices () . end (); ++j) {
219 
220  int indexVar = (*j) -> Index ();
221 #ifdef DEBUG
222  printf ("adding at [%d,%d] and viceversa\n", indexVar, size);
223 #endif
224  (*i) -> add_element (indexVar, size, *j); // note: problem_ -> Var (indexVar) == (*j)
225  (*i) -> add_element (size, indexVar, *j);
226  }
227 
228  (*i) -> add_element (size, size, new exprConst (1.));
229 
230 #ifdef DEBUG
231  (*i) -> print ();
232 #endif
233  }
234 
235  // 0) Search for X \succeq 0 constraints, if any, then add matrix to
236  // minors for each such constraint
237 
238  if (p -> ConstraintClass ("PSDcon"))
239  for (std::vector <CouenneConstraint *>::iterator
240  i = p -> ConstraintClass ("PSDcon") -> begin ();
241  i != p -> ConstraintClass ("PSDcon") -> end (); ++i) {
242 
243  CouennePSDcon *con = dynamic_cast <CouennePSDcon *> (*i);
244 
245  if (!con)
246  continue;
247 
248  minors_ . push_back (con -> getX ());
249  }
250 }
251 
252 
255 
256  for (std::vector <CouenneExprMatrix *>::iterator
257  i = minors_ . begin ();
258  i != minors_ . end (); ++i)
259 
260  delete (*i);
261 }
262 
263 
266 
267  problem_ (rhs. problem_),
268  doNotUse_ (rhs. doNotUse_),
269  numEigVec_ (rhs. numEigVec_),
270  onlyNegEV_ (rhs. onlyNegEV_),
271  useSparsity_ (rhs. useSparsity_),
272  fillMissingTerms_ (rhs. fillMissingTerms_) {
273 
274  for (std::vector <CouenneExprMatrix *>::const_iterator
275  i = rhs.minors_ . begin ();
276  i != rhs.minors_ . end (); ++i)
277 
278  minors_ . push_back (new CouenneExprMatrix (**i));
279 }
280 
281 
284 
285  problem_ = rhs. problem_;
286  doNotUse_ = rhs. doNotUse_;
287  numEigVec_ = rhs. numEigVec_;
288  onlyNegEV_ = rhs. onlyNegEV_;
289  useSparsity_ = rhs. useSparsity_;
291 
292  for (std::vector <CouenneExprMatrix *>::const_iterator
293  i = rhs.minors_ . begin ();
294  i != rhs.minors_ . end (); ++i)
295 
296  minors_ . push_back (new CouenneExprMatrix (**i));
297 
298  return *this;
299 }
300 
301 
303 CglCutGenerator *CouenneSdpCuts::clone () const
304 {return new CouenneSdpCuts (*this);}
305 
306 
309 
310  roptions -> AddLowerBoundedIntegerOption
311  ("sdp_cuts",
312  "The frequency (in terms of nodes) at which Couenne SDP cuts are generated.",
313  -99, 0,
314  "A frequency of 0 (default) means these cuts are never generated. \
315 Any positive number n instructs Couenne to generate them at every n nodes of the B&B tree. \
316 A negative number -n means that generation should be attempted at the root node, and if successful it can be repeated at every n nodes, otherwise it is stopped altogether."
317  );
318 
319  roptions -> AddLowerBoundedIntegerOption
320  ("sdp_cuts_num_ev",
321  "The number of eigenvectors of matrix X to be used to create sdp cuts.",
322  -1, -1,
323  "Set to -1 to indicate that all n eigenvectors should be used. Eigenvalues are \
324 sorted in non-decreasing order, hence selecting 1 will provide cuts on the most negative eigenvalue."
325  );
326 
327  roptions -> AddStringOption2
328  ("sdp_cuts_neg_ev",
329  "Only use negative eigenvalues to create sdp cuts.",
330  "yes",
331  "no", "use all eigenvalues regardless of their sign.",
332  "yes", "exclude all non-negative eigenvalues."
333  );
334 
336 
337  roptions -> AddStringOption2
338  ("sdp_cuts_sparsify",
339  "Make cuts sparse by greedily reducing X one column at a time before extracting eigenvectors.",
340  "no",
341  "no", "",
342  "yes", ""
343  );
344 
345  roptions -> AddStringOption2
346  ("sdp_cuts_fillmissing",
347  "Create fictitious auxiliary variables to fill non-fully dense minors. Can make a difference when Q has at least one zero term.",
348  "no",
349  "no", "Do not create auxiliaries and simply use Fourier-Motzkin to substitute a missing auxiliary y_ij with inequalities that use bounds and the definition y_ij = x_i x_j \
350 Advantage: limits the creation of auxiliaries, reformulation stays small. Default.",
351  "yes", "Create (at the beginning) auxiliaries that are linearized (through McCormick) and used within an SDP cut. This allows tighter cuts although it increases the size \
352 of the reformulation and hence of the linear relaxation."
353  );
354 
355 #if 0
356  roptions -> AddStringOption2
357  ("sdp_cuts_",
358  "",
359  "yes",
360  "no", "",
361  "yes", ""
362  );
363 #endif
364 }
CouenneProblem * problem_
pointer to problem info
Power of an expression (binary operator), with constant.
These are cuts of the form.
static void registerOptions(Ipopt::SmartPtr< Bonmin::RegisteredOptions > roptions)
Add list of options to be read from file.
bool fillMissingTerms_
If minor not fully dense, create fictitious auxiliary variables that will be used in sdp cuts only (t...
constant-type operator
virtual CglCutGenerator * clone() const
Cloning constructor.
static char * j
Definition: OSdtoa.cpp:3622
Class to represent positive semidefinite constraints //////////////////.
CouenneSdpCuts(CouenneProblem *, JnlstPtr, const Ipopt::SmartPtr< Ipopt::OptionsList >)
Constructor.
bool useSparsity_
Sparsify eigenvalues before writing inequality (default: no)
CouenneSdpCuts & operator=(const CouenneSdpCuts &)
Assignment.
fint end
Class for MINLP problems with symbolic information.
expression clone (points to another expression)
void fint fint fint fint fint fint fint fint fint fint real real real real real real real real * s
std::vector< CouenneExprMatrix * > minors_
minors on which to apply cuts
Auxiliary variable.
bool doNotUse_
after construction, true if there are enough product terms to justify application.
int numEigVec_
number of eigenvectors to be used (default: n)
Expression base class.
The in-memory representation of the variables element.
Definition: OSInstance.h:83
bool onlyNegEV_
only use negative eigenvalues (default: yes)
class for multiplications,