All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
prop_vbounds.c
Go to the documentation of this file.
22 * This propagator uses global bound information provided by SCIP to deduce global and local bound changes.
25 * - cliques (set of binary variables, each with a corresponding value, of which at most one variable can get the value)
26 * - variable lower/upper bounds (bounds of arbitrary variables that depend linearly on the value of another variable)
28 * The propagator does not look at a variable in whole, but at one point in time only handles one specific bound (lower
29 * or upper) of a variable and deduces changes for lower or upper bounds of other variables. The concept is as follows:
33 * Implications and cliques are stored in a way such that given a variable and its new value, we can access all bound
34 * changes that can be deduced from setting the variable to that value. However, for variable bounds, this currently
35 * does not hold, they are only stored in the other direction, i.e. for a bound of a given variable, we have a list
36 * of all other bounds of variables that directly influence the bound of the given variable and a linear function
38 * For the propagation, we need the other direction, thus we store it in the propagator data when the branch-and-bound
43 * We compute a topological order of the bounds of variables. This is needed to define an order in which we will
44 * regard bounds of variables in the propagation process in order to avoid unneccessarily regarding the same variable
45 * bound multiple times because it was changed in the meantime when propagating another bound of a variable.
46 * Therefore, we implictly regard a directed graph, in which each node corresponds to a bound of a variable and there
47 * exists a directed edge from one node to another, if the bound corresponding to the former node influences the
48 * bound corresponding to the latter node. This is done by iteratively running a DFS until all nodes were visited.
49 * Note that there might be cycles in the graph, which are randomly broken, so the order is only almost topological.
53 * For each bound of a variable, which can trigger bound changes of other variables, the propagator catches all
54 * events informing about a global change of the bound or a local toghtening of the bound. The event handler
55 * then adds the bound of the variable to a priority queue, with the key in the priority queue corresponding
60 * As long as there are bounds contained in the priority queue, the propagator pops one bound from the queue, which
61 * is the one most at the beginning of the topological sort, so it should not be influenced by propagating other
62 * bounds currently contained in the queue. Starting at this bound, all implication, clique, and variable bound
63 * information is used to deduce tigther bounds for other variables and change the bounds, if a tighter one is found.
64 * These bound changes trigger an event that will lead to adding the corresponding bound to the priority queue,
65 * if it is not contained, yet. The process is iterated until the priority queue contains no more bounds.
68 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
85 #define PROP_DELAY FALSE /**< should propagation method be delayed, if other propagators found reductions? */
104 #define DEFAULT_USEBDWIDENING TRUE /**< should bound widening be used to initialize conflict analysis? */
117 * The propagator works on indices representing a bound of a variable. This index will be called bound index in the
118 * following. For a given active variable with problem index i (note that active variables have problem indices
119 * between 0 and nactivevariable - 1), the bound index of its lower bound is 2*i, the bound index of its upper
120 * bound is 2*i + 1. The other way around, a given bound index i corresponds to the variable with problem index
122 * The following macros can be used to convert bound index into variable problem index and boundtype and vice versa.
143 SCIP_VAR** vars; /**< array containing all variable which are considered within the propagator */
154 SCIP_Real** vboundcoefs; /**< array storing for each bound index the coefficients in the variable
157 SCIP_Real** vboundconstants; /**< array storing for each bound index the constants in the variable
162 int nbounds; /**< number of bounds of variables regarded (two times number of active variables) */
163 SCIP_PQUEUE* propqueue; /**< priority queue to handle the bounds of variables that were changed and have to be propagated */
164 SCIP_Bool* inqueue; /**< boolean array to store whether a bound of a variable is already contained in propqueue */
261 assert(SCIPhashmapExists(propdata->varhashmap, var) == ((size_t)SCIPhashmapGetImage(propdata->varhashmap, var) > 0));
273 assert(SCIPhashmapExists(propdata->varhashmap, var) == ((size_t)SCIPhashmapGetImage(propdata->varhashmap, var) > 0));
337 if( propdata->nvbounds[idx] == 0 && SCIPvarGetNImpls(var, lower) == 0 && SCIPvarGetNCliques(var, lower) == 0 )
349 SCIP_CALL( SCIPcatchVarEvent(scip, var, eventtype, eventhdlr, (SCIP_EVENTDATA*) (size_t) v, NULL) );
398 SCIP_CALL( SCIPdropVarEvent(scip, var, eventtype, eventhdlr, (SCIP_EVENTDATA*) (size_t) v, -1) );
427 SCIP_CALL( SCIPreallocMemoryArray(scip, &propdata->vboundboundedidx[startidx], propdata->vboundsize[startidx]) ); /*lint !e866*/
428 SCIP_CALL( SCIPreallocMemoryArray(scip, &propdata->vboundcoefs[startidx], propdata->vboundsize[startidx]) ); /*lint !e866*/
429 SCIP_CALL( SCIPreallocMemoryArray(scip, &propdata->vboundconstants[startidx], propdata->vboundsize[startidx]) ); /*lint !e866*/
437 SCIP_CALL( SCIPreallocMemoryArray(scip, &propdata->vboundboundedidx[startidx], propdata->vboundsize[startidx]) ); /*lint !e866*/
438 SCIP_CALL( SCIPreallocMemoryArray(scip, &propdata->vboundcoefs[startidx], propdata->vboundsize[startidx]) ); /*lint !e866*/
439 SCIP_CALL( SCIPreallocMemoryArray(scip, &propdata->vboundconstants[startidx], propdata->vboundsize[startidx]) ); /*lint !e866*/
452 /** comparison method for two indices in the topoorder array, preferring higher indices because the order is reverse
464 /** performs depth-first-search in the implicitly given directed graph from the given start index */
506 /* we run until no more bounds indices are on the stack, i.e. all changed bounds were propagated */
524 /* stacknextedge is negative, if the last visited edge from the current node belongs to a clique;
573 /* we stopped because we found an unhandled node and not because we reached the end of the list */
619 /* it might happen that implications point to inactive variables (normally, those are removed when a
620 * variable becomes inactive, but in some cases, it cannot be done), we have to ignore these variables
625 /* implication is just a shortcut, so we dont regard it now, because will later go the long way, anyway;
631 idx = (impltypes[i] == SCIP_BOUNDTYPE_LOWER ? varGetLbIndex(propdata, implvars[i]) : varGetUbIndex(propdata, implvars[i]));
638 /* we stopped because we found an unhandled node and not because we reached the end of the list */
686 /* we stopped because we found an unhandled node and not because we reached the end of the list */
710 SCIPdebugMessage("topoorder[%d] = %s(%s)\n", *ndfsnodes, getBoundString(lower), SCIPvarGetName(startvar));
749 /* while there are unvisited nodes, run dfs starting from one of these nodes; the dfs orders are stored in the
750 * topoorder array, later dfs calls are just appended after the stacks of previous dfs calls, which gives us a
757 SCIP_CALL( dfs(scip, propdata, i, propdata->inqueue, dfsstack, stacknextedge, propdata->topoorder, &nsortednodes) );
812 /* we need to copy the variable since this array is the basis of the propagator and the corresponding variable array
816 SCIP_CALL( SCIPhashmapCreate(&propdata->varhashmap, SCIPblkmem(scip), SCIPcalcHashtableSize(5 * nvars)) );
820 SCIP_CALL( SCIPhashmapInsert(propdata->varhashmap, propdata->vars[v], (void*)(size_t)(v + 1)) );
895 /* if the coefficient is positive, the type of bound is the same for the bounded and the bounding variable */
903 * However, it might happen that vbvar was integer when the variable bound was added, but was converted
904 * to a binary variable later during presolving when its upper bound was changed to 1. In this case,
937 SCIPdebugMessage("varbound <%s> %s %g * <%s> + %g not added to propagator data due to reverse implication\n",
973 SCIP_BDCHGIDX* bdchgidx /**< the index of the bound change, representing the point of time where the change took place, or NULL for the current local bounds */
999 /** relaxes bound of give variable as long as the given inference bound still leads to a cutoff and add that bound
1007 SCIP_BDCHGIDX* bdchgidx, /**< the index of the bound change, representing the point of time where the change took place, or NULL for the current local bounds */
1024 /** compute the relaxed bound which is sufficient to propagate the inference lower bound of given variable */
1041 /* check the computed relaxed lower/upper bound is a proper reason for the inference bound which has to be explained */
1048 /** analyzes an infeasibility which was reached by updating the lower bound of the inference variable above its upper
1061 SCIP_Bool canwide /**< can bound widening be used (for vbounds) or not (for inplications or cliques) */
1067 assert(SCIPisEQ(scip, SCIPvarGetUbLocal(infervar), SCIPvarGetUbAtIndex(infervar, NULL, FALSE)));
1068 assert(SCIPisEQ(scip, SCIPvarGetUbAtIndex(infervar, NULL, TRUE), SCIPvarGetUbAtIndex(infervar, NULL, FALSE)));
1081 SCIPdebugMessage("try to create conflict using bound widening order: inference variable, variable bound variable\n");
1083 /* initialize conflict analysis, and add all variables of infeasible constraint to conflict candidate queue */
1107 /* compute the relaxed bound which is sufficient to propagate the inference lower bound of given variable */
1118 /* initialize conflict analysis, and add all variables of infeasible constraint to conflict candidate queue */
1134 /** compute the relaxed bound which is sufficient to propagate the inference upper bound of given variable */
1151 /* check the computed relaxed lower/upper bound is a proper reason for the inference bound which has to be explained */
1157 /** analyzes an infeasibility which was reached by updating the upper bound of the inference variable below its lower
1170 SCIP_Bool canwide /**< can bound widening be used (for vbounds) or not (for inplications or cliques) */
1176 assert(SCIPisEQ(scip, SCIPvarGetLbLocal(infervar), SCIPvarGetLbAtIndex(infervar, NULL, FALSE)));
1177 assert(SCIPisEQ(scip, SCIPvarGetLbAtIndex(infervar, NULL, TRUE), SCIPvarGetLbAtIndex(infervar, NULL, FALSE)));
1190 SCIPdebugMessage("try to create conflict using bound widening order: inference variable, variable bound variable\n");
1192 /* initialize conflict analysis, and add all variables of infeasible constraint to conflict candidate queue */
1216 /* compute the relaxed bound which is sufficient to propagate the inference upper bound of given variable */
1227 /* initialize conflict analysis, and add all variables of infeasible constraint to conflict candidate queue */
1260 SCIP_Bool canwide, /**< can bound widening be used (for vbounds) or not (for inplications or cliques) */
1292 inferinfo = getInferInfo(boundtype == SCIP_BOUNDTYPE_LOWER ? varGetLbIndex(propdata, vbdvar) : varGetUbIndex(propdata, vbdvar), boundtype);
1294 SCIP_CALL( SCIPinferVarLbProp(scip, var, newlb, prop, inferInfoToInt(inferinfo), force, &infeasible, &tightened) );
1299 /* the infeasible results comes from the fact that the new lower bound lies above the current upper bound */
1303 SCIPdebugMessage("tightening%s lower bound of variable <%s> to %g due the %s bound of variable <%s> led to infeasibility\n",
1304 (global ? " global" : ""), SCIPvarGetName(var), newlb, getBoundtypeString(boundtype), SCIPvarGetName(vbdvar));
1314 SCIP_CALL( analyzeConflictLowerbound(scip, propdata, var, newlb, vbdvar, boundtype, coef, constant, canwide) );
1320 SCIPdebugMessage("tightened%s lower bound of variable <%s> to %g due the %s bound of variable <%s>\n",
1321 (global ? " global" : ""), SCIPvarGetName(var), newlb, getBoundtypeString(boundtype), SCIPvarGetName(vbdvar));
1344 SCIP_Bool canwide, /**< can bound widening be used (for vbounds) or not (for inplications or cliques) */
1376 inferinfo = getInferInfo(boundtype == SCIP_BOUNDTYPE_LOWER ? varGetLbIndex(propdata, vbdvar) : varGetUbIndex(propdata, vbdvar), boundtype);
1378 SCIP_CALL( SCIPinferVarUbProp(scip, var, newub, prop, inferInfoToInt(inferinfo), force, &infeasible, &tightened) );
1383 /* the infeasible results comes from the fact that the new upper bound lies below the current lower bound */
1387 SCIPdebugMessage("tightening%s upper bound of variable <%s> to %g due the %s bound of variable <%s> led to infeasibility\n",
1388 (global ? " global" : ""), SCIPvarGetName(var), newub, getBoundtypeString(boundtype), SCIPvarGetName(vbdvar));
1398 SCIP_CALL( analyzeConflictUpperbound(scip, propdata, var, newub, vbdvar, boundtype, coef, constant, canwide) );
1404 SCIPdebugMessage("tightened%s upper bound of variable <%s> to %g due the %s bound of variable <%s>\n",
1405 (global ? " global" : ""), SCIPvarGetName(var), newub, getBoundtypeString(boundtype), SCIPvarGetName(vbdvar));
1442 /* we do not run the propagator in presolving, because we want to avoid doing the expensive creation of the graph twice */
1485 /* return if no bound changes are in the priority queue (no changed bounds to handle since last propagation) */
1494 SCIPdebugMessage("varbound propagator: %d elements in the propagation queue\n", SCIPpqueueNElems(propdata->propqueue));
1496 /* get variable bound of highest priority from priority queue and try to deduce bound changes for other variables;
1523 /* we only propagate binary variables if the lower bound changed to 1.0 or the upper bound changed to 0.0 */
1532 /* if the lower bound of the startvar was changed, it was fixed to 1.0, otherwise it was fixed to 0.0;
1558 /* it might happen that implications point to inactive variables (normally, those are removed when a
1559 * variable becomes inactive, but in some cases, it cannot be done), we have to ignore these variables
1586 /* if the lower bound of the startvar was changed, it was fixed to 1.0, otherwise it was fixed to 0.0;
1657 SCIP_CALL( tightenVarLb(scip, prop, propdata, boundedvar, newbound, global, startvar, starttype, force,
1662 SCIP_CALL( tightenVarUb(scip, prop, propdata, boundedvar, newbound, global, startvar, starttype, force,
1717 /** solving process deinitialization method of propagator (called before branch and bound process data is freed) */
1822 inferidx = boundtype == SCIP_BOUNDTYPE_LOWER ? varGetLbIndex(propdata, infervar) : varGetUbIndex(propdata, infervar);
1836 /* compute the relaxed bound which is sufficient to propagate the inference bound of given variable */
1877 SCIPdebugMessage("eventexec (type=%u): try to add sort index %d: %s(%s) to priority queue\n", SCIPeventGetType(event),
1881 if( SCIPeventGetType(event) == SCIP_EVENTTYPE_GUBCHANGED && SCIPvarIsBinary(SCIPeventGetVar(event))
1885 if( SCIPeventGetType(event) == SCIP_EVENTTYPE_GLBCHANGED && SCIPvarIsBinary(SCIPeventGetVar(event))
1890 assert(SCIPvarGetType(propdata->vars[getVarIndex(propdata->topoorder[idx])]) != SCIP_VARTYPE_BINARY
1926 SCIP_CALL( SCIPincludePropBasic(scip, &prop, PROP_NAME, PROP_DESC, PROP_PRIORITY, PROP_FREQ, PROP_DELAY, PROP_TIMING,
1937 SCIP_CALL( SCIPincludeEventhdlrBasic(scip, &propdata->eventhdlr, EVENTHDLR_NAME, EVENTHDLR_DESC,
1941 "propagating/"PROP_NAME"/usebdwidening", "should bound widening be used to initialize conflict analysis?",
|