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