1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the program and library */
4 /* SCIP --- Solving Constraint Integer Programs */
5 /* */
6 /* Copyright (C) 2002-2021 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* SCIP is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with SCIP; see the file COPYING. If not visit scipopt.org. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15
16 /**@file presol_dualagg.c
17 * @ingroup DEFPLUGINS_PRESOL
18 * @brief aggregate variables by dual arguments
19 * @author Dieter Weninger
20 *
21 * This presolver looks for variables which could not be handled by
22 * duality fixing because of one up-/downlock.
23 * If the constraint which delivers the up-/downlock has
24 * a specific structure, we can aggregate the corresponding variable.
25 *
26 * In more detail (for a minimization problem and the case of only one uplock):
27 *
28 * Given a variable \f$x_i\f$ with \f$c_i \leq 0\f$ and only one up lock (originating from a constraint c),
29 * we are looking for a binary variable \f$x_j\f$ such that:
30 * 1. if \f$x_j = 0\f$, constraint c can only be fulfilled for \f$x_i = lb_i\f$, and
31 * 2. if \f$x_j = 1\f$, constraint c becomes redundant and \f$x_i\f$ can be dual-fixed to its upper bound \f$ub_i\f$
32 * (or vice versa). Then we can perform the following aggregation: \f$x_i = lb_i + x_j (ub_i - lb_i)\f$.
33 *
34 * Similar arguments apply for the case of only one down lock and \f$c_i \geq 0\f$.
35 */
36
37 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
38
39 #include "blockmemshell/memory.h"
40 #include "scip/presol_dualagg.h"
41 #include "scip/pub_matrix.h"
42 #include "scip/pub_message.h"
43 #include "scip/pub_var.h"
44 #include "scip/scip_general.h"
45 #include "scip/scip_mem.h"
46 #include "scip/scip_message.h"
47 #include "scip/scip_nlp.h"
48 #include "scip/scip_numerics.h"
49 #include "scip/scip_presol.h"
50 #include "scip/scip_pricer.h"
51 #include "scip/scip_prob.h"
52 #include "scip/scip_probing.h"
53 #include "scip/scip_var.h"
54
55 #define PRESOL_NAME "dualagg"
56 #define PRESOL_DESC "aggregate variables by dual arguments"
57 #define PRESOL_PRIORITY -12000 /**< priority of the presolver (>= 0: before, < 0: after constraint handlers) */
58 #define PRESOL_MAXROUNDS 0 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */
59 #define PRESOL_TIMING SCIP_PRESOLTIMING_EXHAUSTIVE /* timing of the presolver (fast, medium, or exhaustive) */
60
61 /** type of aggregation */
62 enum AggrType
63 {
64 BIN0UBOUND = -1, /**< x_j = u_j + (l_j-u_j)x_i with x_i binary and x_j aggregation variable */
65 NOAGG = 0, /**< do not aggregate */
66 BIN0LBOUND = 1 /**< x_j = l_j + (u_j-l_j)x_i with x_i binary and x_j aggregation variable */
67 };
68 typedef enum AggrType AGGRTYPE;
69
70 /*
71 * Local methods
72 */
73
74 /** find row which leads to the uplock of the given variable */
75 static
getUplockRowIdx(SCIP_MATRIX * matrix,int aggvaridx,int * rowidx,SCIP_Real * coef)76 void getUplockRowIdx(
77 SCIP_MATRIX* matrix, /**< constraint matrix */
78 int aggvaridx, /**< index of variable which should be aggregated */
79 int* rowidx, /**< pointer to store row index of uplock */
80 SCIP_Real* coef /**< pointer to store coefficient of variable */
81 )
82 {
83 int* colpnt;
84 int* colend;
85 SCIP_Real* valpnt;
86
87 assert(rowidx != NULL);
88 assert(coef != NULL);
89 assert(SCIPmatrixGetColNUplocks(matrix, aggvaridx) == 1);
90
91 /* get nonzero entries of the variable in the matrix */
92 colpnt = SCIPmatrixGetColIdxPtr(matrix, aggvaridx);
93 colend = colpnt + SCIPmatrixGetColNNonzs(matrix, aggvaridx);
94 valpnt = SCIPmatrixGetColValPtr(matrix, aggvaridx);
95
96 /* iterate over all non-zero coefficients of the column */
97 *rowidx = -1;
98 for(; (colpnt < colend); colpnt++, valpnt++)
99 {
100 /* currently we support only >= relation */
101 if( !SCIPmatrixIsRowRhsInfinity(matrix, *colpnt) )
102 break;
103
104 /* coef < 0 for >= relation: this row provides an uplock for the variable */
105 if( *valpnt < 0.0 )
106 {
107 *rowidx = *colpnt;
108 *coef = *valpnt;
109 break;
110 }
111 }
112 #ifndef NDEBUG
113 /* in debug mode, we check that the lock number is correct */
114 assert(colpnt < colend);
115 for(colpnt++, valpnt++; (colpnt < colend); colpnt++, valpnt++)
116 {
117 assert(*valpnt > 0.0);
118 }
119 #endif
120 }
121
122 /** find row which leads to the downlock of the given variable */
123 static
getDownlockRowIdx(SCIP_MATRIX * matrix,int aggvaridx,int * rowidx,SCIP_Real * coef)124 void getDownlockRowIdx(
125 SCIP_MATRIX* matrix, /**< constraint matrix */
126 int aggvaridx, /**< index of variable which should be aggregated */
127 int* rowidx, /**< pointer to store row index of downlock */
128 SCIP_Real* coef /**< pointer to store coefficient of variable */
129 )
130 {
131 int* colpnt;
132 int* colend;
133 SCIP_Real* valpnt;
134
135 assert(rowidx != NULL);
136 assert(coef != NULL);
137 assert(SCIPmatrixGetColNDownlocks(matrix, aggvaridx) == 1);
138
139 /* get nonzero entries of the variable in the matrix */
140 colpnt = SCIPmatrixGetColIdxPtr(matrix, aggvaridx);
141 colend = colpnt + SCIPmatrixGetColNNonzs(matrix, aggvaridx);
142 valpnt = SCIPmatrixGetColValPtr(matrix, aggvaridx);
143
144 /* iterate over all non-zero coefficients of the column */
145 *rowidx = -1;
146 for(; (colpnt < colend); colpnt++, valpnt++)
147 {
148 /* currently we support only >= relation */
149 if( !SCIPmatrixIsRowRhsInfinity(matrix, *colpnt) )
150 break;
151
152 /* coef > 0 for >= relation: this row provides a downlock for the variable */
153 if( *valpnt > 0.0 )
154 {
155 *rowidx = *colpnt;
156 *coef = *valpnt;
157 break;
158 }
159 }
160 #ifndef NDEBUG
161 /* in debug mode, we check that the lock number is correct */
162 assert(colpnt < colend);
163 for(colpnt++, valpnt++; (colpnt < colend); colpnt++, valpnt++)
164 {
165 assert(*valpnt < 0.0);
166 }
167 #endif
168 }
169
170 /** find fitting binary variable aggregation for uplock case */
171 static
getBinVarIdxInUplockRow(SCIP * scip,SCIP_MATRIX * matrix,int aggvaridx,int * binvaridx,AGGRTYPE * aggtype)172 void getBinVarIdxInUplockRow(
173 SCIP* scip, /**< SCIP main data structure */
174 SCIP_MATRIX* matrix, /**< constraint matrix */
175 int aggvaridx, /**< index of variable which should be aggregated */
176 int* binvaridx, /**< pointer to store index of binary variable */
177 AGGRTYPE* aggtype /**< pointer to store type of aggregation */
178 )
179 {
180 int rowidx;
181 SCIP_Real coef;
182 int* rowpnt;
183 int* rowend;
184 SCIP_Real* valpnt;
185 SCIP_Real minact;
186 SCIP_Real maxact;
187 SCIP_Real lhs;
188 SCIP_Real lb;
189
190 assert(binvaridx != NULL);
191 assert(aggtype != NULL);
192
193 *binvaridx = -1;
194 *aggtype = NOAGG;
195
196 getUplockRowIdx(matrix, aggvaridx, &rowidx, &coef);
197
198 if( rowidx < 0 )
199 return;
200
201 assert(coef < 0);
202 minact = SCIPmatrixGetRowMinActivity(matrix, rowidx);
203 maxact = SCIPmatrixGetRowMaxActivity(matrix, rowidx);
204
205 if( SCIPisInfinity(scip, -minact) || SCIPisInfinity(scip, maxact) )
206 return;
207
208 lhs = SCIPmatrixGetRowLhs(matrix, rowidx);
209 lb = SCIPmatrixGetColLb(matrix, aggvaridx);
210
211 /* search for appropriate binary variables */
212 rowpnt = SCIPmatrixGetRowIdxPtr(matrix, rowidx);
213 rowend = rowpnt + SCIPmatrixGetRowNNonzs(matrix, rowidx);
214 valpnt = SCIPmatrixGetRowValPtr(matrix, rowidx);
215 for( ; (rowpnt < rowend); rowpnt++, valpnt++ )
216 {
217 SCIP_VAR* var;
218
219 if( *rowpnt == aggvaridx )
220 continue;
221
222 var = SCIPmatrixGetVar(matrix, *rowpnt);
223
224 /* avoid cases where the binary variable has lb=ub=1 or lb=ub=0 */
225 if( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY &&
226 SCIPmatrixGetColLb(matrix, *rowpnt) < 0.5 &&
227 SCIPmatrixGetColUb(matrix, *rowpnt) > 0.5 )
228 {
229 SCIP_Real bincoef;
230 bincoef = *valpnt;
231
232 if( bincoef < 0 )
233 {
234 /* binvar = 0 implies that the constraint is redundant */
235 if( SCIPisGE(scip, minact-bincoef, lhs) )
236 {
237 /* binvar = 1 implies that aggvar = lb */
238 SCIP_Real bnd;
239 bnd = (lhs - maxact + coef*lb - bincoef) / coef;
240 if( SCIPisGE(scip, lb, bnd) )
241 {
242 *binvaridx = *rowpnt;
243 *aggtype = BIN0UBOUND;
244 break;
245 }
246 }
247 }
248
249 if( bincoef > 0 )
250 {
251 /* binvar = 1 implies that the constraint is redundant */
252 if( SCIPisGE(scip, minact+bincoef, lhs) )
253 {
254 /* binvar = 0 implies that aggvar = lb */
255 SCIP_Real bnd;
256 bnd = (lhs - maxact + coef*lb + bincoef) / coef;
257 if( SCIPisGE(scip, lb, bnd) )
258 {
259 *binvaridx = *rowpnt;
260 *aggtype = BIN0LBOUND;
261 }
262 }
263 }
264 }
265 }
266 }
267
268 /** find fitting binary variable aggregation for downlock case */
269 static
getBinVarIdxInDownlockRow(SCIP * scip,SCIP_MATRIX * matrix,int aggvaridx,int * binvaridx,AGGRTYPE * aggtype)270 void getBinVarIdxInDownlockRow(
271 SCIP* scip, /**< SCIP main data structure */
272 SCIP_MATRIX* matrix, /**< constraint matrix */
273 int aggvaridx, /**< index of variable which should be aggregated */
274 int* binvaridx, /**< pointer to store index of binary variable */
275 AGGRTYPE* aggtype /**< pointer to store type of aggregation */
276 )
277 {
278 int rowidx;
279 SCIP_Real coef;
280 int* rowpnt;
281 int* rowend;
282 SCIP_Real* valpnt;
283 SCIP_Real minact;
284 SCIP_Real maxact;
285 SCIP_Real lhs;
286 SCIP_Real ub;
287
288 assert(binvaridx != NULL);
289 assert(aggtype != NULL);
290
291 *binvaridx = -1;
292 *aggtype = NOAGG;
293
294 getDownlockRowIdx(matrix, aggvaridx, &rowidx, &coef);
295
296 if( rowidx < 0 )
297 return;
298
299 assert(coef > 0);
300 minact = SCIPmatrixGetRowMinActivity(matrix, rowidx);
301 maxact = SCIPmatrixGetRowMaxActivity(matrix, rowidx);
302
303 if( SCIPisInfinity(scip, -minact) || SCIPisInfinity(scip, maxact) )
304 return;
305
306 lhs = SCIPmatrixGetRowLhs(matrix, rowidx);
307 ub = SCIPmatrixGetColUb(matrix, aggvaridx);
308
309 /* search for appropriate binary variables */
310 rowpnt = SCIPmatrixGetRowIdxPtr(matrix, rowidx);
311 rowend = rowpnt + SCIPmatrixGetRowNNonzs(matrix, rowidx);
312 valpnt = SCIPmatrixGetRowValPtr(matrix, rowidx);
313 for( ; (rowpnt < rowend); rowpnt++, valpnt++ )
314 {
315 SCIP_VAR* var;
316
317 if( *rowpnt == aggvaridx )
318 continue;
319
320 var = SCIPmatrixGetVar(matrix, *rowpnt);
321
322 /* avoid cases where the binary variable has lb=ub=1 or lb=ub=0 */
323 if( SCIPvarGetType(var) == SCIP_VARTYPE_BINARY &&
324 SCIPmatrixGetColLb(matrix, *rowpnt) < 0.5 &&
325 SCIPmatrixGetColUb(matrix, *rowpnt) > 0.5 )
326 {
327 SCIP_Real bincoef;
328
329 bincoef = *valpnt;
330
331 if( bincoef < 0 )
332 {
333 /* binvar = 0 implies that the constraint is redundant */
334 if( SCIPisGE(scip, minact-bincoef, lhs) )
335 {
336 /* binvar = 1 implies that aggvar = ub */
337 SCIP_Real bnd;
338 bnd = (lhs - maxact + coef*ub - bincoef) / coef;
339 if( SCIPisGE(scip, bnd, ub) )
340 {
341 *binvaridx = *rowpnt;
342 *aggtype = BIN0LBOUND;
343 break;
344 }
345 }
346 }
347
348 if( bincoef > 0 )
349 {
350 /* binvar = 1 implies that the constraint is redundant */
351 if( SCIPisGE(scip, minact+bincoef, lhs) )
352 {
353 /* binvar = 0 implies that aggvar = ub */
354 SCIP_Real bnd;
355 bnd = (lhs - maxact + coef*ub + bincoef) / coef;
356 if( SCIPisGE(scip, bnd, ub) )
357 {
358 *binvaridx = *rowpnt;
359 *aggtype = BIN0UBOUND;
360 break;
361 }
362 }
363 }
364 }
365 }
366 }
367
368 /** find variable aggregations for uplock case */
369 static
findUplockAggregations(SCIP * scip,SCIP_MATRIX * matrix,int * nvaragg,AGGRTYPE * aggtypes,SCIP_VAR ** binvars)370 SCIP_RETCODE findUplockAggregations(
371 SCIP* scip, /**< SCIP main data structure */
372 SCIP_MATRIX* matrix, /**< constraint matrix */
373 int* nvaragg, /**< number of redundant variables */
374 AGGRTYPE* aggtypes, /**< type of aggregations (in same order as variables in matrix) */
375 SCIP_VAR** binvars /**< pointers to the binary variables (in same order as variables in matrix) */
376 )
377 {
378 int nvars;
379 int i;
380
381 assert(scip != NULL);
382 assert(matrix != NULL);
383 assert(nvaragg != NULL);
384 assert(aggtypes != NULL);
385 assert(binvars != NULL);
386
387 nvars = SCIPmatrixGetNColumns(matrix);
388
389 for( i = 0; i < nvars; i++ )
390 {
391 /* column has only one uplock which keeps it from being fixed by duality fixing */
392 if( SCIPmatrixGetColNUplocks(matrix, i) == 1 &&
393 SCIPisLE(scip, SCIPvarGetObj(SCIPmatrixGetVar(matrix, i)), 0.0) )
394 {
395 SCIP_Real lb;
396 SCIP_Real ub;
397
398 lb = SCIPmatrixGetColLb(matrix, i);
399 ub = SCIPmatrixGetColUb(matrix, i);
400 assert(lb == SCIPvarGetLbGlobal(SCIPmatrixGetVar(matrix, i))); /*lint !e777*/
401 assert(ub == SCIPvarGetUbGlobal(SCIPmatrixGetVar(matrix, i))); /*lint !e777*/
402
403 /* the variable needs to have finite bounds to allow an agregation */
404 if( !SCIPisInfinity(scip, -lb) && !SCIPisInfinity(scip, ub) )
405 {
406 int binvaridx;
407 AGGRTYPE aggtype;
408
409 getBinVarIdxInUplockRow(scip, matrix, i, &binvaridx, &aggtype);
410
411 if( binvaridx >= 0 )
412 {
413 aggtypes[i] = aggtype;
414 binvars[i] = SCIPmatrixGetVar(matrix, binvaridx);
415 (*nvaragg)++;
416 }
417 }
418 }
419 }
420
421 return SCIP_OKAY;
422 }
423
424 /** find variable aggregations for downlock case */
425 static
findDownlockAggregations(SCIP * scip,SCIP_MATRIX * matrix,int * nvaragg,AGGRTYPE * aggtypes,SCIP_VAR ** binvars)426 SCIP_RETCODE findDownlockAggregations(
427 SCIP* scip, /**< SCIP main data structure */
428 SCIP_MATRIX* matrix, /**< constraint matrix */
429 int* nvaragg, /**< number of redundant variables */
430 AGGRTYPE* aggtypes, /**< type of aggregations (in same order as variables in matrix) */
431 SCIP_VAR** binvars /**< pointers to the binary variables (in same order as variables in matrix) */
432 )
433 {
434 int nvars;
435 int i;
436
437 assert(scip != NULL);
438 assert(matrix != NULL);
439 assert(nvaragg != NULL);
440 assert(aggtypes != NULL);
441 assert(binvars != NULL);
442
443 nvars = SCIPmatrixGetNColumns(matrix);
444
445 for( i = 0; i < nvars; i++ )
446 {
447 /* column has only one downlock which keeps it from being fixed by duality fixing;
448 * only handle variable if it was not yet aggregated due to a single uplock
449 */
450 if( SCIPmatrixGetColNDownlocks(matrix, i) == 1 &&
451 SCIPisGE(scip, SCIPvarGetObj(SCIPmatrixGetVar(matrix, i)), 0.0) &&
452 aggtypes[i] == NOAGG )
453 {
454 SCIP_Real lb;
455 SCIP_Real ub;
456
457 lb = SCIPmatrixGetColLb(matrix, i);
458 ub = SCIPmatrixGetColUb(matrix, i);
459 assert(lb == SCIPvarGetLbGlobal(SCIPmatrixGetVar(matrix, i))); /*lint !e777*/
460 assert(ub == SCIPvarGetUbGlobal(SCIPmatrixGetVar(matrix, i))); /*lint !e777*/
461
462 /* the variable needs to have finite bounds to allow an agregation */
463 if( !SCIPisInfinity(scip, -lb) && !SCIPisInfinity(scip, ub) )
464 {
465 int binvaridx;
466 AGGRTYPE aggtype;
467 getBinVarIdxInDownlockRow(scip, matrix, i, &binvaridx, &aggtype);
468
469 if( binvaridx >= 0 )
470 {
471 aggtypes[i] = aggtype;
472 binvars[i] = SCIPmatrixGetVar(matrix, binvaridx);
473 (*nvaragg)++;
474 }
475 }
476 }
477 }
478
479 return SCIP_OKAY;
480 }
481
482 /*
483 * Callback methods of presolver
484 */
485
486
487 /** execution method of presolver */
488 static
SCIP_DECL_PRESOLEXEC(presolExecDualagg)489 SCIP_DECL_PRESOLEXEC(presolExecDualagg)
490 { /*lint --e{715}*/
491 SCIP_MATRIX* matrix;
492 SCIP_Bool initialized;
493 SCIP_Bool complete;
494 SCIP_Bool infeasible;
495
496 assert(result != NULL);
497 *result = SCIP_DIDNOTRUN;
498
499 if( (SCIPgetStage(scip) != SCIP_STAGE_PRESOLVING) || SCIPinProbing(scip) || SCIPisNLPEnabled(scip) )
500 return SCIP_OKAY;
501
502 if( SCIPisStopped(scip) || SCIPgetNActivePricers(scip) > 0 )
503 return SCIP_OKAY;
504
505 if( SCIPgetNBinVars(scip) == 0 )
506 return SCIP_OKAY;
507
508 if( !SCIPallowStrongDualReds(scip) )
509 return SCIP_OKAY;
510
511 *result = SCIP_DIDNOTFIND;
512
513 matrix = NULL;
514
515 SCIP_CALL( SCIPmatrixCreate(scip, &matrix, TRUE, &initialized, &complete, &infeasible,
516 naddconss, ndelconss, nchgcoefs, nchgbds, nfixedvars) );
517
518 /* if infeasibility was detected during matrix creation, return here */
519 if( infeasible )
520 {
521 if( initialized )
522 SCIPmatrixFree(scip, &matrix);
523
524 *result = SCIP_CUTOFF;
525 return SCIP_OKAY;
526 }
527
528 /* we only work on pure MIPs currently */
529 if( initialized && complete )
530 {
531 AGGRTYPE* aggtypes;
532 SCIP_VAR** binvars;
533 int nvaragg;
534 int ncols;
535
536 ncols = SCIPmatrixGetNColumns(matrix);
537 nvaragg = 0;
538
539 SCIP_CALL( SCIPallocBufferArray(scip, &aggtypes, ncols) );
540 BMSclearMemoryArray(aggtypes, ncols);
541
542 SCIP_CALL( SCIPallocBufferArray(scip, &binvars, ncols) );
543 SCIPdebug( BMSclearMemoryArray(binvars, ncols) );
544
545 /* search for aggregations */
546 SCIP_CALL( findUplockAggregations(scip, matrix, &nvaragg, aggtypes, binvars) );
547 SCIP_CALL( findDownlockAggregations(scip, matrix, &nvaragg, aggtypes, binvars) );
548
549 /* apply aggregations, if we found any */
550 if( nvaragg > 0 )
551 {
552 int v;
553
554 for( v = 0; v < ncols; v++ )
555 {
556 if( aggtypes[v] != NOAGG )
557 {
558 SCIP_Bool redundant;
559 SCIP_Bool aggregated;
560 SCIP_Real ub;
561 SCIP_Real lb;
562
563 ub = SCIPmatrixGetColUb(matrix, v);
564 lb = SCIPmatrixGetColLb(matrix, v);
565
566 /* aggregate variable */
567 assert(binvars[v] != NULL);
568 if( aggtypes[v] == BIN0UBOUND )
569 {
570 SCIP_CALL( SCIPaggregateVars(scip, SCIPmatrixGetVar(matrix, v), binvars[v], 1.0, ub-lb,
571 ub, &infeasible, &redundant, &aggregated) );
572 }
573 else
574 {
575 assert(aggtypes[v] == BIN0LBOUND);
576 SCIP_CALL( SCIPaggregateVars(scip, SCIPmatrixGetVar(matrix, v), binvars[v], 1.0, lb-ub,
577 lb, &infeasible, &redundant, &aggregated) );
578 }
579
580 /* infeasible aggregation */
581 if( infeasible )
582 {
583 SCIPdebugMsg(scip, " -> infeasible aggregation\n");
584 *result = SCIP_CUTOFF;
585 return SCIP_OKAY;
586 }
587
588 if( aggregated )
589 (*naggrvars)++;
590 }
591 }
592
593 /* set result pointer */
594 if( (*naggrvars) > 0 )
595 *result = SCIP_SUCCESS;
596 }
597
598 SCIPfreeBufferArray(scip, &binvars);
599 SCIPfreeBufferArray(scip, &aggtypes);
600 }
601
602 SCIPmatrixFree(scip, &matrix);
603
604 return SCIP_OKAY;
605 }
606
607 /*
608 * presolver specific interface methods
609 */
610
611 /** creates the dualagg presolver and includes it in SCIP */
SCIPincludePresolDualagg(SCIP * scip)612 SCIP_RETCODE SCIPincludePresolDualagg(
613 SCIP* scip /**< SCIP data structure */
614 )
615 {
616 SCIP_PRESOL* presol;
617
618 /* include presolver */
619 SCIP_CALL( SCIPincludePresolBasic(scip, &presol, PRESOL_NAME, PRESOL_DESC, PRESOL_PRIORITY, PRESOL_MAXROUNDS,
620 PRESOL_TIMING, presolExecDualagg, NULL) );
621
622 return SCIP_OKAY;
623 }
624