SCIP-SDP  3.1.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
branch_sdpobjective.c
Go to the documentation of this file.
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of SCIPSDP - a solving framework for mixed-integer */
4 /* semidefinite programs based on SCIP. */
5 /* */
6 /* Copyright (C) 2011-2013 Discrete Optimization, TU Darmstadt */
7 /* EDOM, FAU Erlangen-Nürnberg */
8 /* 2014-2017 Discrete Optimization, TU Darmstadt */
9 /* */
10 /* */
11 /* This program is free software; you can redistribute it and/or */
12 /* modify it under the terms of the GNU Lesser General Public License */
13 /* as published by the Free Software Foundation; either version 3 */
14 /* of the License, or (at your option) any later version. */
15 /* */
16 /* This program is distributed in the hope that it will be useful, */
17 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
18 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
19 /* GNU Lesser General Public License for more details. */
20 /* */
21 /* You should have received a copy of the GNU Lesser General Public License */
22 /* along with this program; if not, write to the Free Software */
23 /* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.*/
24 /* */
25 /* */
26 /* Based on SCIP - Solving Constraint Integer Programs */
27 /* Copyright (C) 2002-2017 Zuse Institute Berlin */
28 /* SCIP is distributed under the terms of the SCIP Academic Licence, */
29 /* see file COPYING in the SCIP distribution. */
30 /* */
31 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
32 
42 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
43 
44 /*#define SCIP_DEBUG */
45 
46 #include <assert.h>
47 #include <string.h>
48 
49 #include "branch_sdpobjective.h"
50 
51 /* turn off lint warnings for whole file: */
52 /*lint --e{788,818}*/
53 
54 #define BRANCHRULE_NAME "sdpobjective"
55 #define BRANCHRULE_DESC "branch on variable with highest absolute objective of the SDP"
56 #define BRANCHRULE_PRIORITY 1500000
57 #define BRANCHRULE_MAXDEPTH -1
58 #define BRANCHRULE_MAXBOUNDDIST 1.0
59 #define DEFAULT_COUPLEDVARS FALSE
61 #define DEFAULT_SINGLECOUPLEDVARS FALSE
65 struct SCIP_BranchruleData
66 {
67  SCIP_Bool coupledvars;
69  SCIP_Bool singlecoupledvars;
72 };
73 
74 
75 /*
76  * Data structures
77  */
78 
79 /*
80  * Local methods
81  */
82 
83 /* put your local methods here, and declare them static */
84 
85 
86 /*
87  * Callback methods of branching rule
88  */
89 
91 static
92 SCIP_DECL_BRANCHCOPY(branchCopySdpobjective)
93 { /*lint --e{715}*/
94  assert(scip != NULL);
95  assert(branchrule != NULL);
96  assert(strcmp(SCIPbranchruleGetName(branchrule), BRANCHRULE_NAME) == 0);
97 
98  /* call inclusion method of branchrule */
99  SCIP_CALL( SCIPincludeBranchruleSdpobjective(scip) );
100 
101  return SCIP_OKAY;
102 }
103 
105 static
106 SCIP_DECL_BRANCHEXECEXT(branchExecextSdpobjective)
107 {/*lint --e{715}*/
108  int i;
109  int ncands;
110  SCIP_VAR** cands = NULL;
111  SCIP_Real* candssol; /* solution values of all candidates */
112  SCIP_Real* candsscore; /* scores of all candidates */
113  SCIP_Real currentfrac; /* fractionality of the current candidate */
114  SCIP_Real currentinf; /* infeasibility of the current candidate */
115  SCIP_VAR* maxobjvar = NULL; /* variable with currently highest absolute objective */
116  SCIP_Real maxobjobj; /* objective of the current candidate with highest absolute objective */
117  SCIP_Real maxobjinf; /* infeasibility of the current candidate with highest absolute objective */
118  SCIP_Real maxobjval; /* value of the current candidate with highest absolute objective */
119  SCIP_Real maxobjscore; /* score of the current candidate with highest absolute objective */
120  SCIP_BRANCHRULEDATA* branchruledata;
121 
122  assert( scip != NULL );
123  assert( branchrule != NULL );
124  assert( result != NULL );
125 
126  SCIPdebugMessage("Executing External Branching method of SDP-objective!\n");
127 
128  /* get the external candidates, as we use the score only as a tiebreaker, we aren't interested in the number of variables of different types with maximal
129  * score, so these return values are set to NULL */
130  SCIP_CALL( SCIPgetExternBranchCands(scip, &cands, &candssol, &candsscore, &ncands, NULL, NULL, NULL, NULL) );
131 
132  assert( ncands > 0 ); /* branchExecext should only be called if the list of external branching candidates is non-empty */
133 
134 #ifdef SCIP_DEBUG
135  SCIPdebugMessage("branching candidates for SDP-objective:\n");
136  for (i = 0; i < ncands; i++)
137  SCIPdebugMessage("%s, value = %f, objective = %f, score = %f\n", SCIPvarGetName(cands[i]), candssol[i], SCIPvarGetObj(cands[i]), candsscore[i]);
138 #endif
139 
140  maxobjobj = -1.0;
141  maxobjscore = 0.0;
142  maxobjval = 0.0;
143  maxobjinf = -1.0;
144 
145  /* iterate over all candidates and find the one with the highest absolute objective, use score as tiebreaker */
146  for (i = 0; i < ncands; i++)
147  {
148  /* we skip all continuous variables, since we first want to branch on integral variables */
149  if ( SCIPvarGetType(cands[i]) == SCIP_VARTYPE_CONTINUOUS )
150  {
151  SCIPdebugMessage("skipping continuous variable %s\n", SCIPvarGetName(cands[i]));
152  continue;
153  }
154 
155  currentfrac = SCIPfeasFrac(scip, candssol[i]);
156  currentinf = (currentfrac <= 0.5) ? currentfrac : 1 - currentfrac;
157 
158  /* a candidate is better than the current one if:
159  * - the absolute objective is (epsilon-)bigger than before or
160  * - the absolute objective is (epsilon-)equal and the score is (epsilon-)bigger or
161  * - the absolute objective and score are (epsilon-)equal and the integer infeasibility is (feastol-)bigger
162  * - all three above are (epsilon-/feastol)equal in the index is smaller */
163  if ( SCIPisGT(scip, REALABS(SCIPvarGetObj(cands[i])), maxobjobj) ||
164  (SCIPisEQ(scip, REALABS(SCIPvarGetObj(cands[i])), maxobjobj) && SCIPisGT(scip, candsscore[i], maxobjscore)) ||
165  (SCIPisEQ(scip, REALABS(SCIPvarGetObj(cands[i])), maxobjobj) && SCIPisEQ(scip, candsscore[i], maxobjscore) &&
166  SCIPisFeasGT(scip, currentinf, maxobjinf)) ||
167  (SCIPisEQ(scip, REALABS(SCIPvarGetObj(cands[i])), maxobjobj) && SCIPisEQ(scip, candsscore[i], maxobjscore) &&
168  SCIPisFeasEQ(scip, currentinf, maxobjinf) && (SCIPvarGetIndex(cands[i]) < SCIPvarGetIndex(maxobjvar))) )
169  {
170  maxobjvar = cands[i];
171  maxobjobj = REALABS(SCIPvarGetObj(cands[i]));
172  maxobjinf = currentinf;
173  maxobjval = candssol[i];
174  maxobjscore = candsscore[i];
175  }
176  }
177 
178  /* if all variables were continuous, we return DIDNOTRUN and let one of the SCIP branching rules decide */
179  if ( maxobjobj == -1.0 )
180  {
181  SCIPdebugMessage("Skipping SDP-objective branching rule since all branching variables are continuous\n");
182  *result = SCIP_DIDNOTFIND;
183  return SCIP_OKAY;
184  }
185 
186  assert( SCIPisFeasGE(scip, maxobjobj, 0.0) );
187  assert( maxobjvar != NULL );
188 
189  branchruledata = SCIPbranchruleGetData(branchrule);
190  assert( branchruledata != NULL );
191 
192  /* if all candidates have objective zero, we look for other variables that are coupled with the candidates and check their objective values if the
193  * coupledvars or singlecoupledvars parameter is set to true */
194  if ( SCIPisEQ(scip, maxobjobj, 0.0) && (branchruledata->coupledvars || branchruledata->singlecoupledvars) )
195  {
196  int j;
197  int c;
198  int v;
199  int cand;
200  int candpos;
201  int nvars;
202  SCIP_VAR** vars;
203  int nconss;
204  SCIP_CONS** conss;
205  int nvarsincons;
206  SCIP_VAR** varsincons;
207  SCIP_Bool** coupledvars; /* is there a constraint coupling candidate i and variable j ? */
208  SCIP_Bool** singlecoupledvars; /* is variable j coupled with candidate i AND with no other candidate */
209  int** candsincons; /* candsincons[i] gives a list of all candidates (indexed as in cands) appearing in cons i */
210  int* ncandsincons; /* ncandsincons[i] gives the length of candsincons[i] */
211  SCIP_Real currentobj;
212  SCIP_Bool success;
213  int coupledcand;
214 
215  SCIPdebugMessage("All branching candidates have objective 0.0, objective branching proceeds to check coupled variables, updated values for candidates: \n");
216 
217  nvars = SCIPgetNVars(scip);
218  vars = SCIPgetVars(scip);
219  assert( vars != NULL );
220  nconss = SCIPgetNConss(scip);
221  conss = SCIPgetConss(scip);
222  assert( conss != NULL );
223 
224  /* allocate memory to save the coupled variables and initialize the arrays */
225  SCIP_CALL( SCIPallocBufferArray(scip, &ncandsincons, nconss) );
226  for (i = 0; i < nconss; i++)
227  ncandsincons[i] = 0;
228 
229  SCIP_CALL( SCIPallocBufferArray(scip, &candsincons, nconss) );
230  for (i = 0; i < nconss; i++)
231  {
232  SCIP_CALL( SCIPallocBufferArray(scip, &(candsincons[i]), ncands) );
233  }
234 
235  SCIP_CALL( SCIPallocBufferArray(scip, &coupledvars, ncands) );
236  for (i = 0; i < ncands; i++)
237  {
238  SCIP_CALL( SCIPallocBufferArray(scip, &(coupledvars[i]), nvars) );
239  for (j = 0; j < nvars; j++)
240  coupledvars[i][j] = FALSE;
241  }
242  SCIP_CALL( SCIPallocBufferArray(scip, &varsincons, nvars) );
243 
244  /* find all variables that are coupled to a candidate */
245  for (c = 0; c < nconss; c++)
246  {
247  /* first check which candidates appear in which constraints */
248  SCIP_CALL( SCIPgetConsNVars(scip, conss[c], &nvarsincons, &success) );
249  if ( ! success )
250  {
251  SCIPdebugMessage("couldn't get variable information from constraint %s, so ignoring it for computing coupled variables\n", SCIPconsGetName(conss[c]));
252  continue; /* if we can't get the variables of this constraint, we can't include variables coupled through this constraint */
253  }
254  /* nothing to do for this constraint if there are no variables (this can happen if all vars are fixed, as the constraint is non-trivial to check) */
255  if ( nvarsincons == 0)
256  continue;
257  SCIP_CALL( SCIPgetConsVars(scip, conss[c], varsincons, nvarsincons, &success) );
258  assert( success ); /* we allocated enough memory */
259  assert( varsincons != NULL );
260  for (v = 0; v < nvarsincons; v++)
261  {
262  for (cand = 0; cand < ncands; cand++)
263  {
264  if ( varsincons[v] == cands[cand] )
265  {
266  candsincons[c][ncandsincons[c]] = cand;
267  ncandsincons[c]++;
268  }
269  }
270  }
271 
272  /* now save which variables are coupled to each candidate by adding all those that appear in this constraint to
273  * all candidates appearing in this constraint */
274  for (candpos = 0; candpos < ncandsincons[c]; candpos++)
275  {
276  for (v = 0; v < nvarsincons; v++)
277  {
278  /* the coupledvars-index corresponding to a variable is its variable index - nvars, because we work on the transformed variables which
279  * have indices nvars to 2*nvars - 1, as their indices start after those of the original variables */
280  coupledvars[candsincons[c][candpos]][SCIPvarGetIndex(varsincons[v]) - nvars] = TRUE; /*lint !e679*/
281  }
282  }
283  }
284 
285  if ( branchruledata->singlecoupledvars )
286  {
287  /* allocate memory for singlecoupledvars */
288  SCIP_CALL( SCIPallocBufferArray(scip, &singlecoupledvars, ncands) );
289  for (i = 0; i < ncands; i++)
290  {
291  SCIP_CALL( SCIPallocBufferArray(scip, &(singlecoupledvars[i]), nvars) );
292  for (j = 0; j < nvars; j++)
293  singlecoupledvars[i][j] = FALSE;
294  }
295  /* finally remove all variables, that are coupled to multiple candidates */
296  for (v = 0; v < nvars; v++)
297  {
298  /* we use coupledcand to save if we have already found a candidate this variable is coupled with (otherwise coupledcand = -1), if we find one,
299  * we set coupledcand to that index, to easily set the corresponding entry to TRUE if we don't find another candidate it is coupled with */
300  coupledcand = -1;
301  for (cand = 0; cand < ncands; cand++)
302  {
303  if ( coupledvars[cand][v] )
304  {
305  /* check if this is the first or the second found candidate for this variable */
306  if ( coupledcand == -1 )
307  {
308  /* this is the first candidate this is coupled with, so it might be the only one and we save it to
309  * potentially later set singlecoupledvars to true */
310  coupledcand = cand;
311  }
312  else
313  {
314  /* we found a second candidate, so this variable won't be taken into account for the branching rule, so we set coupledcand
315  * to -2 to not set the corresponding entry in singlecoupledvars to TRUE and continue with the next variable */
316  coupledcand = -2;
317  break;
318  }
319  }
320  }
321  if ( coupledcand > -1 )
322  {
323  /* as we found exactly one candidate this variable is coupled with, we set the corresponding singlecoupledvars-entry to TRUE */
324  singlecoupledvars[coupledcand][v] = TRUE;
325  }
326  }
327  }
328 
329  /* iterate over all variables and compute the total absolute objective of all coupled variables */
330  for (cand = 0; cand < ncands; cand++)
331  {
332  currentobj = 0.0;
333  for (v = 0; v < nvars; v++)
334  {
335  if ( branchruledata->singlecoupledvars && singlecoupledvars[cand][v] ) /*lint !e644*/
336  currentobj += REALABS(SCIPvarGetObj(vars[v]));
337  else if ( coupledvars[cand][v] )
338  currentobj += REALABS(SCIPvarGetObj(vars[v]));
339  }
340 
341  assert( SCIPisGE(scip, currentobj, 0.0) );
342 
343  currentfrac = SCIPfeasFrac(scip, candssol[i]);
344  currentinf = (currentfrac <= 0.5) ? currentfrac : 1 - currentfrac;
345 
346 #ifdef SCIP_DEBUG
347  SCIPdebugMessage("candidate %s, coupled with ", SCIPvarGetName(cands[cand]));
348  for (v = 0; v < nvars; v++)
349  {
350  if (coupledvars[cand][v])
351  SCIPdebugMessage("%s, ", SCIPvarGetName(vars[v]));
352  }
353  SCIPdebugMessage("out of those ");
354  for (v = 0; v < nvars; v++)
355  {
356  if (singlecoupledvars[cand][v])
357  SCIPdebugMessage("%s, ", SCIPvarGetName(vars[v]));
358  }
359  SCIPdebugMessage("are only coupled with this candidate, total objective = %f, score = %f\n", currentobj, candsscore[cand]);
360 
361 #endif
362 
363  /* a candidate is better than the current one if:
364  * - the absolute objective is (epsilon-)bigger than before or
365  * - the absolute objective is (epsilon-)equal and the score is (epsilon-)bigger or
366  * - the absolute objective and score are (epsilon-)equal and the integer infeasibility is (epsilon-)bigger
367  * - all three above are (epsilon-)equal in the index is smaller */
368  if ( SCIPisGT(scip, currentobj, maxobjobj) ||
369  (SCIPisEQ(scip, currentobj, maxobjobj) && SCIPisGT(scip, candsscore[i], maxobjscore)) ||
370  (SCIPisEQ(scip, currentobj, maxobjobj) && SCIPisEQ(scip, candsscore[i], maxobjscore) && SCIPisGT(scip, currentinf, maxobjinf)) ||
371  (SCIPisEQ(scip, currentobj, maxobjobj) && SCIPisEQ(scip, candsscore[i], maxobjscore) && SCIPisEQ(scip, currentinf, maxobjinf) &&
372  (SCIPvarGetIndex(cands[cand]) < SCIPvarGetIndex(maxobjvar))) )
373  {
374  maxobjvar = cands[cand];
375  maxobjobj = currentobj;
376  maxobjinf = currentinf;
377  maxobjval = candssol[cand];
378  maxobjscore = candsscore[cand];
379  }
380  }
381 
382  /* free Memory */
383  if ( branchruledata->singlecoupledvars )
384  {
385  for (i = 0; i < ncands; i++)
386  {
387  SCIPfreeBufferArray(scip, &(singlecoupledvars[i]));
388  }
389  SCIPfreeBufferArray(scip, &singlecoupledvars);
390  }
391  SCIPfreeBufferArray(scip, &varsincons);
392  for (i = 0; i < ncands; i++)
393  {
394  SCIPfreeBufferArray(scip, &(coupledvars[i]));
395  }
396  SCIPfreeBufferArray(scip, &coupledvars);
397  for (i = 0; i < nconss; i++)
398  {
399  SCIPfreeBufferArray(scip, &(candsincons[i]));
400  }
401  SCIPfreeBufferArray(scip, &candsincons);
402  SCIPfreeBufferArray(scip, &ncandsincons);
403  }
404 
405  /* if the objective values of all integer variables (and all coupled variables, if this settings was used) is zero, skip this branching rule */
406  if ( SCIPisGT(scip, maxobjobj, 0.0) )
407  {
408  /* branch */
409  SCIPdebugMessage("branching on variable %s with value %f, absolute objective %f and score %f\n", SCIPvarGetName(maxobjvar), maxobjval, maxobjobj, maxobjscore);
410  SCIP_CALL( SCIPbranchVarVal(scip, maxobjvar, maxobjval, NULL, NULL, NULL) );
411 
412  *result = SCIP_BRANCHED;
413  }
414  else
415  {
416  /* skip */
417  *result = SCIP_DIDNOTRUN;
418  }
419 
420  return SCIP_OKAY;
421 }
422 
424 static
425 SCIP_DECL_BRANCHFREE(branchFreeSdpobjective)
426 { /*lint --e{715}*/
427  SCIP_BRANCHRULEDATA* branchruledata;
428 
429  /* free branching rule data */
430  branchruledata = SCIPbranchruleGetData(branchrule);
431  SCIPfreeMemory(scip, &branchruledata);
432  SCIPbranchruleSetData(branchrule, NULL);
433 
434  return SCIP_OKAY;
435 }
436 
437 /*
438  * branching rule specific interface methods
439  */
440 
443  SCIP* scip
444  )
445 {
446  SCIP_BRANCHRULEDATA* branchruledata;
447  SCIP_BRANCHRULE* branchrule;
448 
449  /* create branching rule data */
450  SCIP_CALL( SCIPallocMemory(scip, &branchruledata) );
451 
452  branchrule = NULL;
453 
454  /* include branching rule */
455  SCIP_CALL( SCIPincludeBranchruleBasic(scip, &branchrule, BRANCHRULE_NAME, BRANCHRULE_DESC, BRANCHRULE_PRIORITY,
456  BRANCHRULE_MAXDEPTH, BRANCHRULE_MAXBOUNDDIST, branchruledata) );
457 
458  assert(branchrule != NULL);
459 
460  /* set non fundamental callbacks via setter functions */
461  SCIP_CALL( SCIPsetBranchruleCopy(scip, branchrule, branchCopySdpobjective) );
462  SCIP_CALL( SCIPsetBranchruleExecExt(scip, branchrule, branchExecextSdpobjective) );
463  SCIP_CALL( SCIPsetBranchruleFree(scip, branchrule, branchFreeSdpobjective) );
464 
465  /* add parameters for the branching rule */
466  SCIP_CALL( SCIPaddBoolParam(scip,
467  "branching/sdpobjective/coupledvars",
468  "if all branching candidates have objective zero, should we use the sum of the absolute objectives of all continuous variables coupled with the "
469  "candidate through constraints ?",
470  &branchruledata->coupledvars, TRUE, DEFAULT_COUPLEDVARS, NULL, NULL) );
471  SCIP_CALL( SCIPaddBoolParam(scip,
472  "branching/sdpobjective/singlecoupledvars",
473  "if all branching candidates have objective zero, should we use the sum of the absolute objectives of all continuous variables coupled with the "
474  "candidate through constraints in which no other candidate appears ?",
475  &branchruledata->singlecoupledvars, TRUE, DEFAULT_SINGLECOUPLEDVARS, NULL, NULL) );
476 
477  return SCIP_OKAY;
478 }
static SCIP_DECL_BRANCHFREE(branchFreeSdpobjective)
#define DEFAULT_SINGLECOUPLEDVARS
#define DEFAULT_COUPLEDVARS
highest absolute objective branching rule for SCIP-SDP
#define BRANCHRULE_DESC
#define BRANCHRULE_NAME
static SCIP_DECL_BRANCHCOPY(branchCopySdpobjective)
#define BRANCHRULE_MAXBOUNDDIST
SCIP_RETCODE SCIPincludeBranchruleSdpobjective(SCIP *scip)
#define BRANCHRULE_PRIORITY
static SCIP_DECL_BRANCHEXECEXT(branchExecextSdpobjective)
#define BRANCHRULE_MAXDEPTH