SCIP Doxygen Documentation
Loading...
Searching...
No Matches
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
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 */
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]);
301 }
302 else if( tightened )
303 {
304 (*nchgbds)++;
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]));
334 }
335 else if( aggregated )
336 {
337 (*naggrvars)++;
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
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_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)
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
return SCIP_OKAY
assert(minobj< SCIPgetCutoffbound(scip))
static SCIP_VAR ** vars
memory allocation routines
#define PRESOL_NAME
#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
#define SCIP_DECL_PRESOLCOPY(x)
Definition type_presol.h:60
struct SCIP_Presol SCIP_PRESOL
Definition type_presol.h:50
#define SCIP_DECL_PRESOLEXEC(x)
@ SCIP_CUTOFF
Definition type_result.h:48
@ SCIP_DIDNOTFIND
Definition type_result.h:44
@ SCIP_SUCCESS
Definition type_result.h:58
@ SCIP_INVALIDCALL
enum SCIP_Retcode SCIP_RETCODE
struct Scip SCIP
Definition type_scip.h:39
struct SCIP_Var SCIP_VAR
Definition type_var.h:166
@ SCIP_VARSTATUS_MULTAGGR
Definition type_var.h:56