00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "IpOptionsList.hpp"
00011
00012 #include "CouenneJournalist.hpp"
00013 #include "CouenneMatrix.hpp"
00014 #include "CouennePSDcon.hpp"
00015 #include "CouenneSdpCuts.hpp"
00016 #include "CouenneProblem.hpp"
00017 #include "CouenneExprVar.hpp"
00018 #include "CouenneExprAux.hpp"
00019 #include "operators/CouenneExprPow.hpp"
00020 #include "operators/CouenneExprMul.hpp"
00021
00022 #include "CoinTime.hpp"
00023
00024 #include <set>
00025
00026 using namespace Couenne;
00027
00028
00029
00031 CouenneSdpCuts::CouenneSdpCuts (CouenneProblem *p,
00032 JnlstPtr jnlst,
00033 const Ipopt::SmartPtr <Ipopt::OptionsList> options):
00034 problem_ (p),
00035 doNotUse_ (false) {
00036
00037 std::string s;
00038
00039 options -> GetIntegerValue ("sdp_cuts_num_ev", numEigVec_, "couenne.");
00040 options -> GetStringValue ("sdp_cuts_neg_ev", s, "couenne."); onlyNegEV_ = (s == "yes");
00041 options -> GetStringValue ("sdp_cuts_sparsify", s, "couenne."); useSparsity_ = (s == "yes");
00042 options -> GetStringValue ("sdp_cuts_fillmissing", s, "couenne."); fillMissingTerms_ = (s == "yes");
00043
00044 CouenneExprMatrix *cauldron = new CouenneExprMatrix;
00045
00046
00047
00048 for (std::vector <exprVar *>::iterator
00049 i = p -> Variables (). begin ();
00050 i != p -> Variables (). end (); ++i)
00051
00052 if ((*i) -> Type () == AUX) {
00053
00054 expression *image = (*i) -> Image ();
00055
00057 if ((image -> code () == COU_EXPRMUL) &&
00058 (image -> ArgList () [0] -> Type () != CONST) &&
00059 (image -> ArgList () [1] -> Type () != CONST)) {
00060
00061 int
00062 index0 = image -> ArgList () [0] -> Index (),
00063 index1 = image -> ArgList () [1] -> Index ();
00064
00065 if ((index0 >= 0) &&
00066 (index1 >= 0) &&
00067 ((*i) -> Index () >= 0))
00068
00069
00070
00071
00072 cauldron -> add_element (index0, index1, (*i));
00073 cauldron -> add_element (index1, index0, (*i));
00074 }
00075
00077 if ((image -> code () == COU_EXPRPOW) &&
00078 (image -> ArgList () [0] -> Type () != CONST) &&
00079 (image -> ArgList () [1] -> Value () == 2.)) {
00080
00081 int index0 = image -> ArgList () [0] -> Index ();
00082
00083 if ( (index0 >= 0) &&
00084 ((*i) -> Index () >= 0))
00085
00086 cauldron -> add_element (index0, index0, (*i));
00087 }
00088 }
00089
00090 #ifdef DEBUG
00091 printf ("Cauldron so far\n");
00092 cauldron -> print ();
00093 #endif
00094
00095
00096
00097
00098
00099
00100 minors_ . push_back (cauldron);
00101
00102
00103
00104 if (fillMissingTerms_) {
00105
00106 for (std::vector <CouenneExprMatrix *>::iterator
00107 i = minors_ . begin ();
00108 i != minors_ . end (); ++i) {
00109
00110
00111
00112
00113
00114
00115 std::set <int> varNumIndices;
00116
00117 for (std::set <std::pair <int, CouenneSparseVector *>, CouenneExprMatrix::compare_pair_ind>::const_iterator
00118 rowIt = (*i) -> getRows (). begin ();
00119 rowIt != (*i) -> getRows (). end (); ++rowIt) {
00120
00121 varNumIndices. insert (rowIt -> first);
00122
00123 for (std::set <CouenneScalar *, CouenneSparseVector::compare_scalars>::const_iterator
00124 elemIt = rowIt -> second -> getElements () . begin ();
00125 elemIt != rowIt -> second -> getElements () . end (); ++elemIt)
00126
00127 varNumIndices. insert ((*elemIt) -> getIndex ());
00128 }
00129
00130
00131
00132
00133 for (std::set <std::pair <int, CouenneSparseVector *>, CouenneExprMatrix::compare_pair_ind>::const_iterator
00134 rowIt = (*i) -> getRows (). begin ();
00135 rowIt != (*i) -> getRows (). end (); ++rowIt) {
00136
00137 int rowInd = rowIt -> first;
00138
00139 std::set <int>::iterator vniIt = varNumIndices . begin ();
00140
00141 for (std::set <CouenneScalar *, CouenneSparseVector::compare_scalars>::const_iterator
00142 elemIt = rowIt -> second -> getElements () . begin ();
00143 elemIt != rowIt -> second -> getElements () . end (); ++elemIt) {
00144
00145 int colInd = (*elemIt) -> getIndex ();
00146
00147 while ((vniIt != varNumIndices . end ()) && (*vniIt < colInd)) {
00148
00149 if (rowInd <= *vniIt) {
00150
00151
00152 expression *image;
00153
00154 if (rowInd == *vniIt) image = new exprPow (new exprClone (problem_ -> Var (rowInd)), new exprConst (2.));
00155 else image = new exprMul (new exprClone (problem_ -> Var (rowInd)), new exprClone (problem_ -> Var (*vniIt)));
00156
00157 exprAux *yIJ = problem_ -> addAuxiliary (image);
00158
00159
00160 if (problem_ -> AuxSet () -> find (yIJ) ==
00161 problem_ -> AuxSet () -> end ()) {
00162
00163
00164 problem_ -> Variables () . push_back (yIJ);
00165 problem_ -> AuxSet () -> insert (yIJ);
00166 }
00167
00168 (*i) -> add_element (rowInd, *vniIt, yIJ);
00169 (*i) -> add_element (*vniIt, rowInd, yIJ);
00170 }
00171
00172 ++vniIt;
00173 }
00174
00175 if (vniIt == varNumIndices . end ())
00176 break;
00177 else ++vniIt;
00178 }
00179 }
00180 }
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192 }
00193
00194
00195
00196
00197 for (std::vector <CouenneExprMatrix *>::iterator
00198 i = minors_ . begin ();
00199 i != minors_ . end (); ++i) {
00200
00201 int size = problem_ -> nVars ();
00202
00203 #ifdef DEBUG
00204 printf ("minor has %ld rows and %ld columns\n",
00205 (*i) -> getRows () . size (),
00206 (*i) -> getCols () . size ());
00207 #endif
00208
00209 for (std::set <std::pair <int, CouenneSparseVector *>, CouenneExprMatrix::compare_pair_ind>::const_iterator
00210 j = (*i) -> getCols () . begin ();
00211 j != (*i) -> getCols () . end (); ++j)
00212
00213 (*i) -> varIndices () . push_back (problem_ -> Var (j -> first));
00214
00215 for (std::vector <expression *>::iterator
00216 j = (*i) -> varIndices () . begin ();
00217 j != (*i) -> varIndices () . end (); ++j) {
00218
00219 int indexVar = (*j) -> Index ();
00220 #ifdef DEBUG
00221 printf ("adding at [%d,%d] and viceversa\n", indexVar, size);
00222 #endif
00223 (*i) -> add_element (indexVar, size, *j);
00224 (*i) -> add_element (size, indexVar, *j);
00225 }
00226
00227 (*i) -> add_element (size, size, new exprConst (1.));
00228
00229 #ifdef DEBUG
00230 (*i) -> print ();
00231 #endif
00232 }
00233
00234
00235
00236
00237 if (p -> ConstraintClass ("PSDcon"))
00238 for (std::vector <CouenneConstraint *>::iterator
00239 i = p -> ConstraintClass ("PSDcon") -> begin ();
00240 i != p -> ConstraintClass ("PSDcon") -> end (); ++i) {
00241
00242 CouennePSDcon *con = dynamic_cast <CouennePSDcon *> (*i);
00243
00244 if (!con)
00245 continue;
00246
00247 minors_ . push_back (con -> getX ());
00248 }
00249 }
00250
00251
00253 CouenneSdpCuts::~CouenneSdpCuts () {
00254
00255 for (std::vector <CouenneExprMatrix *>::iterator
00256 i = minors_ . begin ();
00257 i != minors_ . end (); ++i)
00258
00259 delete (*i);
00260 }
00261
00262
00264 CouenneSdpCuts::CouenneSdpCuts (const CouenneSdpCuts &rhs):
00265
00266 problem_ (rhs. problem_),
00267 doNotUse_ (rhs. doNotUse_),
00268 numEigVec_ (rhs. numEigVec_),
00269 onlyNegEV_ (rhs. onlyNegEV_),
00270 useSparsity_ (rhs. useSparsity_),
00271 fillMissingTerms_ (rhs. fillMissingTerms_) {
00272
00273 for (std::vector <CouenneExprMatrix *>::const_iterator
00274 i = rhs.minors_ . begin ();
00275 i != rhs.minors_ . end (); ++i)
00276
00277 minors_ . push_back (new CouenneExprMatrix (**i));
00278 }
00279
00280
00282 CouenneSdpCuts &CouenneSdpCuts::operator= (const CouenneSdpCuts &rhs) {
00283
00284 problem_ = rhs. problem_;
00285 doNotUse_ = rhs. doNotUse_;
00286 numEigVec_ = rhs. numEigVec_;
00287 onlyNegEV_ = rhs. onlyNegEV_;
00288 useSparsity_ = rhs. useSparsity_;
00289 fillMissingTerms_ = rhs. fillMissingTerms_;
00290
00291 for (std::vector <CouenneExprMatrix *>::const_iterator
00292 i = rhs.minors_ . begin ();
00293 i != rhs.minors_ . end (); ++i)
00294
00295 minors_ . push_back (new CouenneExprMatrix (**i));
00296
00297 return *this;
00298 }
00299
00300
00302 CglCutGenerator *CouenneSdpCuts::clone () const
00303 {return new CouenneSdpCuts (*this);}
00304
00305
00307 void CouenneSdpCuts::registerOptions (Ipopt::SmartPtr <Bonmin::RegisteredOptions> roptions) {
00308
00309 roptions -> AddLowerBoundedIntegerOption
00310 ("sdp_cuts",
00311 "The frequency (in terms of nodes) at which Couenne SDP cuts are generated.",
00312 -99, 0,
00313 "A frequency of 0 (default) means these cuts are never generated. \
00314 Any positive number n instructs Couenne to generate them at every n nodes of the B&B tree. \
00315 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."
00316 );
00317
00318 roptions -> AddLowerBoundedIntegerOption
00319 ("sdp_cuts_num_ev",
00320 "The number of eigenvectors of matrix X to be used to create sdp cuts.",
00321 -1, -1,
00322 "Set to -1 to indicate that all n eigenvectors should be used. Eigenvalues are \
00323 sorted in non-decreasing order, hence selecting 1 will provide cuts on the most negative eigenvalue."
00324 );
00325
00326 roptions -> AddStringOption2
00327 ("sdp_cuts_neg_ev",
00328 "Only use negative eigenvalues to create sdp cuts.",
00329 "yes",
00330 "no", "use all eigenvalues regardless of their sign.",
00331 "yes", "exclude all non-negative eigenvalues."
00332 );
00333
00335
00336 roptions -> AddStringOption2
00337 ("sdp_cuts_sparsify",
00338 "Make cuts sparse by greedily reducing X one column at a time before extracting eigenvectors.",
00339 "no",
00340 "no", "",
00341 "yes", ""
00342 );
00343
00344 roptions -> AddStringOption2
00345 ("sdp_cuts_fillmissing",
00346 "Create fictitious auxiliary variables to fill non-fully dense minors. Can make a difference when Q has at least one zero term.",
00347 "no",
00348 "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 \
00349 Advantage: limits the creation of auxiliaries, reformulation stays small. Default.",
00350 "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 \
00351 of the reformulation and hence of the linear relaxation."
00352 );
00353
00354 #if 0
00355 roptions -> AddStringOption2
00356 ("sdp_cuts_",
00357 "",
00358 "yes",
00359 "no", "",
00360 "yes", ""
00361 );
00362 #endif
00363 }