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