SCIP

    Solving Constraint Integer Programs

    presol_implics.c
    Go to the documentation of this file.
    1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    2/* */
    3/* This file is part of the program and library */
    4/* SCIP --- Solving Constraint Integer Programs */
    5/* */
    6/* Copyright (c) 2002-2026 Zuse Institute Berlin (ZIB) */
    7/* */
    8/* Licensed under the Apache License, Version 2.0 (the "License"); */
    9/* you may not use this file except in compliance with the License. */
    10/* You may obtain a copy of the License at */
    11/* */
    12/* http://www.apache.org/licenses/LICENSE-2.0 */
    13/* */
    14/* Unless required by applicable law or agreed to in writing, software */
    15/* distributed under the License is distributed on an "AS IS" BASIS, */
    16/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
    17/* See the License for the specific language governing permissions and */
    18/* limitations under the License. */
    19/* */
    20/* You should have received a copy of the Apache-2.0 license */
    21/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */
    22/* */
    23/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    24
    25/**@file presol_implics.c
    26 * @ingroup DEFPLUGINS_PRESOL
    27 * @brief implics presolver
    28 * @author Tobias Achterberg
    29 */
    30
    31/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
    32
    34#include "scip/presol_implics.h"
    35#include "scip/pub_message.h"
    36#include "scip/pub_presol.h"
    37#include "scip/pub_var.h"
    38#include "scip/scip_mem.h"
    39#include "scip/scip_message.h"
    40#include "scip/scip_numerics.h"
    41#include "scip/scip_presol.h"
    42#include "scip/scip_prob.h"
    43#include "scip/scip_var.h"
    44
    45
    46#define PRESOL_NAME "implics"
    47#define PRESOL_DESC "implication graph aggregator"
    48#define PRESOL_PRIORITY -10000 /**< priority of the presolver (>= 0: before, < 0: after constraint handlers) */
    49#define PRESOL_MAXROUNDS -1 /**< maximal number of presolving rounds the presolver participates in (-1: no limit) */
    50#define PRESOL_TIMING SCIP_PRESOLTIMING_MEDIUM /* timing of the presolver (fast, medium, or exhaustive) */
    51
    52
    53/*
    54 * Callback methods of presolver
    55 */
    56
    57/** copy method for constraint handler plugins (called when SCIP copies plugins) */
    58static
    59SCIP_DECL_PRESOLCOPY(presolCopyImplics)
    60{ /*lint --e{715}*/
    61 assert(scip != NULL);
    62 assert(presol != NULL);
    63
    65
    66 /* call inclusion method of presolver */
    68
    69 return SCIP_OKAY;
    70}
    71
    72
    73/** execution method of presolver */
    74static
    75SCIP_DECL_PRESOLEXEC(presolExecImplics)
    76{ /*lint --e{715}*/
    77 SCIP_VAR** vars;
    78 SCIP_VAR** bdchgvars;
    79 SCIP_BOUNDTYPE* bdchgtypes;
    80 SCIP_Real* bdchgvals;
    81 SCIP_VAR** aggrvars;
    82 SCIP_VAR** aggraggvars;
    83 SCIP_Real* aggrcoefs;
    84 SCIP_Real* aggrconsts;
    85 int nbdchgs;
    86 int naggregations;
    87 int nbinvars;
    88 int v;
    89
    90 assert(result != NULL);
    91
    92 *result = SCIP_DIDNOTFIND;
    93
    94 /* initialize fixing and aggregation storages */
    95 bdchgvars = NULL;
    96 bdchgtypes = NULL;
    97 bdchgvals = NULL;
    98 nbdchgs = 0;
    99 aggrvars = NULL;
    100 aggraggvars = NULL;
    101 aggrcoefs = NULL;
    102 aggrconsts = NULL;
    103 naggregations = 0;
    104
    105 /* get active binary problem variables */
    106 vars = SCIPgetVars(scip);
    107 nbinvars = SCIPgetNBinVars(scip);
    108
    109 /* look for variable implications in x == 0 and x == 1 with the same implied variable:
    110 * x = 0 -> y = lb, and x = 1 -> y = lb: fix y to lb
    111 * x = 0 -> y = lb, and x = 1 -> y = ub: aggregate y == lb + (ub-lb)x
    112 * x = 0 -> y = ub, and x = 1 -> y = lb: aggregate y == ub - (ub-lb)x
    113 * x = 0 -> y = ub, and x = 1 -> y = ub: fix y to ub
    114 * the fixings and aggregations are stored in a buffer and applied afterwards, because fixing and aggregation
    115 * would modify the vars array and the implication arrays
    116 */
    117 for( v = 0; v < nbinvars; ++v )
    118 {
    119 SCIP_VAR** implvars[2];
    120 SCIP_BOUNDTYPE* impltypes[2];
    121 SCIP_Real* implbounds[2];
    122 int nimpls[2];
    123 int varfixing;
    124 int i0;
    125 int i1;
    126
    127 /* don't perform presolving operations on deleted variables */
    128 if( SCIPvarIsDeleted(vars[v]) )
    129 continue;
    130
    131 /* get implications for given variable */
    132 for( varfixing = 0; varfixing < 2; ++varfixing )
    133 {
    134 implvars[varfixing] = SCIPvarGetImplVars(vars[v], (SCIP_Bool)varfixing);
    135 impltypes[varfixing] = SCIPvarGetImplTypes(vars[v], (SCIP_Bool)varfixing);
    136 implbounds[varfixing] = SCIPvarGetImplBounds(vars[v], (SCIP_Bool)varfixing);
    137 nimpls[varfixing] = SCIPvarGetNImpls(vars[v], (SCIP_Bool)varfixing);
    138 }
    139
    140 /* scan implication arrays for equal variables */
    141 i0 = 0;
    142 i1 = 0;
    143 while( i0 < nimpls[0] && i1 < nimpls[1] )
    144 {
    145 int index0;
    146 int index1;
    147
    148 /* scan the binary or non-binary part of the implication arrays */
    149 index0 = SCIPvarGetIndex(implvars[0][i0]);
    150 index1 = SCIPvarGetIndex(implvars[1][i1]);
    151 while( index0 < index1 )
    152 {
    153 i0++;
    154 if( i0 == nimpls[0] )
    155 {
    156 index0 = -1;
    157 break;
    158 }
    159 index0 = SCIPvarGetIndex(implvars[0][i0]); /*lint !e838*/
    160 }
    161 while( index1 < index0 )
    162 {
    163 i1++;
    164 if( i1 == nimpls[1] )
    165 {
    166 index1 = -1;
    167 break;
    168 }
    169 index1 = SCIPvarGetIndex(implvars[1][i1]); /*lint !e838*/
    170 }
    171 /**@todo for all implicit binary variables y, check the cliques of x == !varfixing if y is contained */
    172
    173 if( index0 == index1 )
    174 {
    175 assert(index0 >= 0);
    176 assert(i0 < nimpls[0]);
    177 assert(i1 < nimpls[1]);
    178 assert(implvars[0][i0] == implvars[1][i1]);
    179
    180 /* multiaggregated variables cannot be aggregated or their bounds tightened */
    181 if( SCIPvarGetStatus(implvars[0][i0]) != SCIP_VARSTATUS_MULTAGGR )
    182 {
    183 if( impltypes[0][i0] == impltypes[1][i1] )
    184 {
    185 /* found implication x = 0 -> y >= b / y <= b and x = 1 -> y >= c / y <= c
    186 * => change bound y >= min(b,c) / y <= max(b,c)
    187 */
    188 SCIP_CALL( SCIPreallocBufferArray(scip, &bdchgvars, nbdchgs+1) );
    189 SCIP_CALL( SCIPreallocBufferArray(scip, &bdchgtypes, nbdchgs+1) );
    190 SCIP_CALL( SCIPreallocBufferArray(scip, &bdchgvals, nbdchgs+1) );
    191 bdchgvars[nbdchgs] = implvars[0][i0];
    192 bdchgtypes[nbdchgs] = impltypes[0][i0];
    193 if( impltypes[0][i0] == SCIP_BOUNDTYPE_LOWER )
    194 bdchgvals[nbdchgs] = MIN(implbounds[0][i0], implbounds[1][i1]);
    195 else
    196 bdchgvals[nbdchgs] = MAX(implbounds[0][i0], implbounds[1][i1]);
    197
    198 SCIPdebugMsg(scip, " -> <%s> = 0 -> <%s> %s %g, and <%s> = 1 -> <%s> %s %g: tighten <%s> %s %g\n",
    199 SCIPvarGetName(vars[v]), SCIPvarGetName(implvars[0][i0]),
    200 impltypes[0][i0] == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", implbounds[0][i0],
    201 SCIPvarGetName(vars[v]), SCIPvarGetName(implvars[1][i1]),
    202 impltypes[1][i1] == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", implbounds[1][i1],
    203 SCIPvarGetName(bdchgvars[nbdchgs]), bdchgtypes[nbdchgs] == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=",
    204 bdchgvals[nbdchgs]);
    205
    206 nbdchgs++;
    207 }
    208 else
    209 {
    210 SCIP_Real implvarlb;
    211 SCIP_Real implvarub;
    212
    213 implvarlb = SCIPvarGetLbGlobal(implvars[0][i0]);
    214 implvarub = SCIPvarGetUbGlobal(implvars[0][i0]);
    215
    216 if( impltypes[0][i0] == SCIP_BOUNDTYPE_UPPER
    217 && SCIPisEQ(scip, implbounds[0][i0], implvarlb)
    218 && SCIPisEQ(scip, implbounds[1][i1], implvarub) )
    219 {
    220 /* found implication x = 0 -> y = lb and x = 1 -> y = ub => aggregate y = lb + (ub-lb) * x */
    221 SCIP_CALL( SCIPreallocBufferArray(scip, &aggrvars, naggregations+1) );
    222 SCIP_CALL( SCIPreallocBufferArray(scip, &aggraggvars, naggregations+1) );
    223 SCIP_CALL( SCIPreallocBufferArray(scip, &aggrcoefs, naggregations+1) );
    224 SCIP_CALL( SCIPreallocBufferArray(scip, &aggrconsts, naggregations+1) );
    225 aggrvars[naggregations] = implvars[0][i0];
    226 aggraggvars[naggregations] = vars[v];
    227 aggrcoefs[naggregations] = implvarub - implvarlb;
    228 aggrconsts[naggregations] = implvarlb;
    229
    230 SCIPdebugMsg(scip, " -> <%s> = 0 -> <%s> = %g, and <%s> = 1 -> <%s> = %g: aggregate <%s> = %g %+g<%s>\n",
    231 SCIPvarGetName(vars[v]), SCIPvarGetName(implvars[0][i0]), implbounds[0][i0],
    232 SCIPvarGetName(vars[v]), SCIPvarGetName(implvars[1][i1]), implbounds[1][i1],
    233 SCIPvarGetName(aggrvars[naggregations]), aggrconsts[naggregations], aggrcoefs[naggregations],
    234 SCIPvarGetName(aggraggvars[naggregations]));
    235
    236 naggregations++;
    237 }
    238 else if( impltypes[0][i0] == SCIP_BOUNDTYPE_LOWER
    239 && SCIPisEQ(scip, implbounds[0][i0], implvarub)
    240 && SCIPisEQ(scip, implbounds[1][i1], implvarlb) )
    241 {
    242 /* found implication x = 0 -> y = ub and x = 1 -> y = lb => aggregate y = ub - (ub-lb) * x */
    243 SCIP_CALL( SCIPreallocBufferArray(scip, &aggrvars, naggregations+1) );
    244 SCIP_CALL( SCIPreallocBufferArray(scip, &aggraggvars, naggregations+1) );
    245 SCIP_CALL( SCIPreallocBufferArray(scip, &aggrcoefs, naggregations+1) );
    246 SCIP_CALL( SCIPreallocBufferArray(scip, &aggrconsts, naggregations+1) );
    247 aggrvars[naggregations] = implvars[0][i0];
    248 aggraggvars[naggregations] = vars[v];
    249 aggrcoefs[naggregations] = implvarlb - implvarub;
    250 aggrconsts[naggregations] = implvarub;
    251
    252 SCIPdebugMsg(scip, " -> <%s> = 0 -> <%s> = %g, and <%s> = 1 -> <%s> = %g: aggregate <%s> = %g %+g<%s>\n",
    253 SCIPvarGetName(vars[v]), SCIPvarGetName(implvars[0][i0]), implbounds[0][i0],
    254 SCIPvarGetName(vars[v]), SCIPvarGetName(implvars[1][i1]), implbounds[1][i1],
    255 SCIPvarGetName(aggrvars[naggregations]), aggrconsts[naggregations], aggrcoefs[naggregations],
    256 SCIPvarGetName(aggraggvars[naggregations]));
    257
    258 naggregations++;
    259 }
    260 }
    261 }
    262
    263 /* process the next implications */
    264 i0++;
    265 i1++;
    266 }
    267 }
    268 }
    269
    270 /**@todo check cliques of x == 0 and x == 1 for equal entries y == b -> fix y == !b */
    271
    272 /* perform the bound changes
    273 *
    274 * Note, that we cannot assume y to be active (see var.c: varRemoveImplicsVbs()), but it should not cause any
    275 * troubles as this case seems to be handled correctly in SCIPtightenVarLb/Ub(), unless the variable is
    276 * multiaggregated, but this has been excluded above.
    277 */
    278 for( v = 0; v < nbdchgs && *result != SCIP_CUTOFF; ++v )
    279 {
    280 SCIP_Bool infeasible;
    281 SCIP_Bool tightened;
    282
    283 assert(bdchgtypes != NULL);
    284 assert(bdchgvars != NULL);
    285 assert(bdchgvals != NULL);
    286
    287 if( bdchgtypes[v] == SCIP_BOUNDTYPE_LOWER )
    288 {
    289 SCIP_CALL( SCIPtightenVarLb(scip, bdchgvars[v], bdchgvals[v], FALSE, &infeasible, &tightened) );
    290 }
    291 else
    292 {
    293 SCIP_CALL( SCIPtightenVarUb(scip, bdchgvars[v], bdchgvals[v], FALSE, &infeasible, &tightened) );
    294 }
    295
    296 if( infeasible )
    297 {
    298 SCIPdebugMsg(scip, " -> infeasible bound change <%s> %s %g\n", SCIPvarGetName(bdchgvars[v]),
    299 bdchgtypes[v] == SCIP_BOUNDTYPE_LOWER ? ">=" : "<=", bdchgvals[v]);
    300 *result = SCIP_CUTOFF;
    301 }
    302 else if( tightened )
    303 {
    304 (*nchgbds)++;
    305 *result = SCIP_SUCCESS;
    306 }
    307 }
    308
    309 /* perform the aggregations
    310 *
    311 * Note, that we cannot assume y to be active (see var.c: varRemoveImplicsVbs()), but it should not cause any
    312 * troubles as this case seems to be handled correctly in SCIPaggregateVars(), unless the variable is
    313 * multiaggregated, but this has been excluded above.
    314 */
    315 for( v = 0; v < naggregations && *result != SCIP_CUTOFF; ++v )
    316 {
    317 SCIP_Bool infeasible;
    318 SCIP_Bool redundant;
    319 SCIP_Bool aggregated;
    320
    321 assert(aggrvars != NULL);
    322 assert(aggraggvars != NULL);
    323 assert(aggrcoefs != NULL);
    324 assert(aggrconsts != NULL);
    325
    326 /* aggregation y = const + coef * x => y - coef * x = const */
    327 SCIP_CALL( SCIPaggregateVars(scip, aggrvars[v], aggraggvars[v], 1.0, -aggrcoefs[v], aggrconsts[v],
    328 &infeasible, &redundant, &aggregated) );
    329 if( infeasible )
    330 {
    331 SCIPdebugMsg(scip, " -> infeasible aggregation <%s> = %g %+g<%s>\n",
    332 SCIPvarGetName(aggrvars[v]), aggrconsts[v], aggrcoefs[v], SCIPvarGetName(aggraggvars[v]));
    333 *result = SCIP_CUTOFF;
    334 }
    335 else if( aggregated )
    336 {
    337 (*naggrvars)++;
    338 *result = SCIP_SUCCESS;
    339 }
    340 }
    341
    342 /* free the storage buffers */
    343 SCIPfreeBufferArrayNull(scip, &aggrconsts);
    344 SCIPfreeBufferArrayNull(scip, &aggrcoefs);
    345 SCIPfreeBufferArrayNull(scip, &aggraggvars);
    346 SCIPfreeBufferArrayNull(scip, &aggrvars);
    347 SCIPfreeBufferArrayNull(scip, &bdchgvals);
    348 SCIPfreeBufferArrayNull(scip, &bdchgtypes);
    349 SCIPfreeBufferArrayNull(scip, &bdchgvars);
    350
    351 return SCIP_OKAY;
    352}
    353
    354
    355/*
    356 * presolver specific interface methods
    357 */
    358
    359/** creates the implics presolver and includes it in SCIP */
    361 SCIP* scip /**< SCIP data structure */
    362 )
    363{
    364 SCIP_PRESOL* presolptr;
    365
    366 /* include presolver */
    368
    369 assert(presolptr != NULL);
    370
    371 SCIP_CALL( SCIPsetPresolCopy(scip, presolptr, presolCopyImplics) );
    372
    373 return SCIP_OKAY;
    374}
    #define NULL
    Definition: def.h:257
    #define SCIP_Bool
    Definition: def.h:100
    #define MIN(x, y)
    Definition: def.h:233
    #define SCIP_STRINGEQ(name, reference, retcode)
    Definition: def.h:454
    #define SCIP_Real
    Definition: def.h:165
    #define FALSE
    Definition: def.h:103
    #define MAX(x, y)
    Definition: def.h:229
    #define SCIP_CALL(x)
    Definition: def.h:364
    SCIP_VAR ** SCIPgetVars(SCIP *scip)
    Definition: scip_prob.c:2201
    int SCIPgetNBinVars(SCIP *scip)
    Definition: scip_prob.c:2293
    #define SCIPdebugMsg
    Definition: scip_message.h:78
    SCIP_RETCODE SCIPincludePresolImplics(SCIP *scip)
    #define SCIPreallocBufferArray(scip, ptr, num)
    Definition: scip_mem.h:128
    #define SCIPfreeBufferArrayNull(scip, ptr)
    Definition: scip_mem.h:137
    SCIP_RETCODE SCIPsetPresolCopy(SCIP *scip, SCIP_PRESOL *presol, SCIP_DECL_PRESOLCOPY((*presolcopy)))
    Definition: scip_presol.c:148
    SCIP_RETCODE SCIPincludePresolBasic(SCIP *scip, SCIP_PRESOL **presolptr, const char *name, const char *desc, int priority, int maxrounds, SCIP_PRESOLTIMING timing, SCIP_DECL_PRESOLEXEC((*presolexec)), SCIP_PRESOLDATA *presoldata)
    Definition: scip_presol.c:113
    const char * SCIPpresolGetName(SCIP_PRESOL *presol)
    Definition: presol.c:625
    SCIP_Bool SCIPisEQ(SCIP *scip, SCIP_Real val1, SCIP_Real val2)
    SCIP_RETCODE SCIPtightenVarLb(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound, SCIP_Bool force, SCIP_Bool *infeasible, SCIP_Bool *tightened)
    Definition: scip_var.c:6401
    SCIP_Bool SCIPvarIsDeleted(SCIP_VAR *var)
    Definition: var.c:23566
    int SCIPvarGetNImpls(SCIP_VAR *var, SCIP_Bool varfixing)
    Definition: var.c:24600
    SCIP_VARSTATUS SCIPvarGetStatus(SCIP_VAR *var)
    Definition: var.c:23418
    SCIP_RETCODE SCIPaggregateVars(SCIP *scip, SCIP_VAR *varx, SCIP_VAR *vary, SCIP_Real scalarx, SCIP_Real scalary, SCIP_Real rhs, SCIP_Bool *infeasible, SCIP_Bool *redundant, SCIP_Bool *aggregated)
    Definition: scip_var.c:10550
    SCIP_RETCODE SCIPtightenVarUb(SCIP *scip, SCIP_VAR *var, SCIP_Real newbound, SCIP_Bool force, SCIP_Bool *infeasible, SCIP_Bool *tightened)
    Definition: scip_var.c:6651
    SCIP_Real SCIPvarGetUbGlobal(SCIP_VAR *var)
    Definition: var.c:24174
    SCIP_VAR ** SCIPvarGetImplVars(SCIP_VAR *var, SCIP_Bool varfixing)
    Definition: var.c:24617
    int SCIPvarGetIndex(SCIP_VAR *var)
    Definition: var.c:23684
    const char * SCIPvarGetName(SCIP_VAR *var)
    Definition: var.c:23299
    SCIP_Real * SCIPvarGetImplBounds(SCIP_VAR *var, SCIP_Bool varfixing)
    Definition: var.c:24646
    SCIP_Real SCIPvarGetLbGlobal(SCIP_VAR *var)
    Definition: var.c:24152
    SCIP_BOUNDTYPE * SCIPvarGetImplTypes(SCIP_VAR *var, SCIP_Bool varfixing)
    Definition: var.c:24632
    memory allocation routines
    static SCIP_DECL_PRESOLEXEC(presolExecImplics)
    #define PRESOL_NAME
    static SCIP_DECL_PRESOLCOPY(presolCopyImplics)
    #define PRESOL_PRIORITY
    #define PRESOL_MAXROUNDS
    #define PRESOL_TIMING
    #define PRESOL_DESC
    implication graph presolver which checks for aggregations
    public methods for message output
    public methods for presolvers
    public methods for problem variables
    public methods for memory management
    public methods for message handling
    public methods for numerical tolerances
    public methods for presolving plugins
    public methods for global and local (sub)problems
    public methods for SCIP variables
    @ SCIP_BOUNDTYPE_UPPER
    Definition: type_lp.h:58
    @ SCIP_BOUNDTYPE_LOWER
    Definition: type_lp.h:57
    enum SCIP_BoundType SCIP_BOUNDTYPE
    Definition: type_lp.h:60
    @ SCIP_CUTOFF
    Definition: type_result.h:48
    @ SCIP_DIDNOTFIND
    Definition: type_result.h:44
    @ SCIP_SUCCESS
    Definition: type_result.h:58
    @ SCIP_OKAY
    Definition: type_retcode.h:42
    @ SCIP_INVALIDCALL
    Definition: type_retcode.h:51
    enum SCIP_Retcode SCIP_RETCODE
    Definition: type_retcode.h:63
    @ SCIP_VARSTATUS_MULTAGGR
    Definition: type_var.h:56