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