diff --git a/CHANGELOG b/CHANGELOG index 80b973877d..e6c37b911a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -163,6 +163,7 @@ Fixed bugs - fixed bug in calculation of "fractionality score" for spatial branching candidates in cons_nonlinear - fixed bug with installing symmetry dialog in sub-SCIPs - add copy callbacks for default plugins presolver dualagg, presolver redvub, branchrule lookahead, branchrule cloud, heuristic dualval, heuristic repair, propagator nlobbt, separator gauge, and separator convexproj missing in SCIPsetCopyPlugins() +- respect maximal round setting of separation by running remaining delayed separators in last round Unit tests ---------- diff --git a/doc/scipfooter.html b/doc/scipfooter.html index a53c1a9735..0518db8c1e 100644 --- a/doc/scipfooter.html +++ b/doc/scipfooter.html @@ -1,4 +1,4 @@ - + @@ -21,7 +19,7 @@ © 2002-2024 by Zuse Institute Berlin (ZIB), Imprint - Generated on $date for $projectname by doxygen ($doxygenversion) + Generated on $date for $projectname by doxygen ($doxygenversion) diff --git a/doc/scipheader.html b/doc/scipheader.html index 01a9cba88f..aa5a94ce3b 100644 --- a/doc/scipheader.html +++ b/doc/scipheader.html @@ -1,14 +1,19 @@ - - + + - + $projectname: $title $title + + + + + $treeview @@ -18,37 +23,49 @@ $extrastylesheet + + +
+ + +
- + - - + - + + + + + + +
-
$projectname -  $projectnumber +
+
$projectname $projectnumber
$projectbrief
+
$projectbrief
$searchbox
$searchbox
diff --git a/doc/xternal.c b/doc/xternal.c index e96bbdf738..4174673088 100644 --- a/doc/xternal.c +++ b/doc/xternal.c @@ -2626,7 +2626,8 @@ * * \par SEPA_DELAY: the default for whether the separation method should be delayed, if other separators or constraint handlers found cuts. * If the separator's separation method is marked to be delayed, it is only executed after no other separator - * or constraint handler found a cut during the price-and-cut loop. + * or constraint handler found a cut during the price-and-cut loop, and in the last separation or stalling round, + * either in the end, or in an additional round if only the maximal subsequent round is exceeded. * If the separation method of the separator is very expensive, you may want to mark it to be delayed until all cheap * separation methods have been executed. * @@ -7856,33 +7857,33 @@ * @section SRCORGA_CORE SCIP core components * * - Each core component has an implementation with an internal API and a public API. - * - The internal implementation should be in a file .c,h and should not be included in the public API. - * - Internal API functions usually do not take a SCIP* parameter, but a pointer to the component as first argument and pointers to internal structures like SCIP_SET* or SCIP_STAT*, where necessary. - * - The name of internal API functions follows the style `SCIP...`, e.g., SCIPvarCreate() or SCIPvarAddLocks(). - * - pub_.h declares the functions of the public API that do not need a SCIP pointer. + * - The internal implementation should be in a file `.c,h` and should not be included in the public API. + * - Internal API functions usually do not take a `SCIP*` parameter, but a pointer to the component as first argument and pointers to internal structures like `SCIP_SET*` or `SCIP_STAT*`, where necessary. + * - The name of internal API functions follows the style `SCIP...`, e.g., SCIPvarCreateOriginal() or SCIPvarAddLocks(). + * - `pub_.h` declares the functions of the public API that do not need a SCIP pointer. * Often, these are getter-functions. * For example, \ref pub_var.h contains public variable API functions. - * - Functions in pub_.h follow the same naming style as those in .h and are used by the implementation of the internal API as well. - * - scip_.h declares the functions of the public API that need a SCIP instance (SCIP*), e.g., \ref scip_var.h for public variable manipulation functions. - * Functions declared in scip_.h are often thin wrappers that call the internal API functions from .h. - * These functions should follow the naming style `SCIP...`, e.g., SCIPcreateVar() or SCIPaddVarLocks. + * - Functions in `pub_.h` follow the same naming style as those in `.h` and are used by the implementation of the internal API as well. + * - `scip_.h` declares the functions of the public API that need a SCIP instance (`SCIP*`), e.g., \ref scip_var.h for public variable manipulation functions. + * Functions declared in `scip_.h` are often thin wrappers that call the internal API functions from `.h`. + * These functions should follow the naming style `SCIP...`, e.g., SCIPcreateVarOriginal() or SCIPaddVarLocks(). * - To ensure functions of the public API being reachable in shared libraries, their declaration needs to contain the SCIP_EXPORT attribute. - * - Public types (typedef's, enumerations) are defined in file type_.h. - * Type names follow the style SCIP_.... For every struct, we have a typedef that shortens the name - * (so one could for instance use SCIP_PARAM instead of struct SCIP_Param). + * - Public types (typedef's, enumerations) are defined in file `type_.h`. + * Type names follow the style `SCIP_...`. For every struct, we have a typedef that shortens the name + * (so one could for instance use `SCIP_PARAM` instead of `struct SCIP_Param`). * The convention is to have the mixed-casing for the struct name, and then all-capital for the typedef's type. Similar for enums. - * - Structs that need to be accessed by several source files are defined in struct_.h. - * struct_.h is usually included only by .c and maybe scip_.c. + * - Structs that need to be accessed by several source files are defined in `struct_.h`. + * `struct_.h` is usually included only by `.c` and maybe `scip_.c`. * Exceptions are due to manual inlining of functions via macros when compiling for optimized mode. * - All types, structs, and functions are documented with Doxygen-style comments. * The documentation of the implementation of a function must repeat the documentation of the function declaration exactly (for doxygen to treat them as identical). * * @section SRCORGA_PLUGINS Plugins - * - Each plugin is defined in files _.c,h, e.g., + * - Each plugin is defined in files `_.c,h`, e.g., * \ref cons_knapsack.c implements the Knapsack constraint handler plugin and * \ref cons_knapsack.h declares its public API functions. - * - Public types that belong to a plugin are declared in its header, _.h. - * - API functions of plugins are named as SCIP..., e.g., SCIPincludeConshdlrAnd(), SCIPcreateConsAnd(), or SCIPgetNVarsAnd(). + * - Public types that belong to a plugin are declared in its header, `_.h`. + * - API functions of plugins are named as `SCIP...`, e.g., SCIPincludeConshdlrAnd(), SCIPcreateConsAnd(), or SCIPgetNVarsAnd(). * - Plugins access only the public API. * - Plugins that need to be included by default should be registered in src/scip/scipdefplugins.c. */ diff --git a/src/scip/def.h b/src/scip/def.h index d371a53d4f..4b2c72c22b 100644 --- a/src/scip/def.h +++ b/src/scip/def.h @@ -104,11 +104,11 @@ */ #ifndef SCIP_EXPORT #if defined(_WIN32) -#define SCIP_EXPORT __declspec(dllexport) +#define SCIP_EXPORT __declspec(dllexport) /**< mark symbol to be exported in DLL */ #elif defined(__GNUC__) && __GNUC__ >= 4 -#define SCIP_EXPORT __attribute__((__visibility__("default"))) +#define SCIP_EXPORT __attribute__((__visibility__("default"))) /**< mark symbol to be visible in shared library */ #else -#define SCIP_EXPORT +#define SCIP_EXPORT /**< no symbol export attribute known for current compiler */ #endif #endif diff --git a/src/scip/scip_expr.c b/src/scip/scip_expr.c index 25664cbfb2..b7d5a1aac4 100644 --- a/src/scip/scip_expr.c +++ b/src/scip/scip_expr.c @@ -1366,12 +1366,12 @@ SCIP_RETCODE SCIPcopyExpr( * a `Term` is a product of `Factors` and an `Expression` is a sum of `Terms`. * * The actual definition: - *
+ * ```
  * Expression -> ["+" | "-"] Term { [ ("+" | "-" | "number *") Term | ("number" ) ] }
  * Term       -> Factor { ("*" | "/" ) Factor }
  * Factor     -> Base [ "^" "number" | "^(" "number" ")" ]
  * Base       -> "number" | "" | "(" Expression ")" | Op "(" OpExpression ")
- * 
+ * ``` * where `[a|b]` means `a` or `b` or none, `(a|b)` means `a` or `b`, `{a}` means 0 or more `a`. * * Note that `Op` and `OpExpression` are undefined. diff --git a/src/scip/scip_expr.h b/src/scip/scip_expr.h index 5bf845be6a..139839928d 100644 --- a/src/scip/scip_expr.h +++ b/src/scip/scip_expr.h @@ -277,12 +277,12 @@ SCIP_RETCODE SCIPcopyExpr( * a `Term` is a product of `Factors` and an `Expression` is a sum of `Terms`. * * The actual definition: - *
+ * ```
  * Expression -> ["+" | "-"] Term { [ ("+" | "-" | "number *") Term | ("number" ) ] }
  * Term       -> Factor { ("*" | "/" ) Factor }
  * Factor     -> Base [ "^" "number" | "^(" "number" ")" ]
  * Base       -> "number" | "" | "(" Expression ")" | Op "(" OpExpression ")
- * 
+ * ``` * where `[a|b]` means `a` or `b` or none, `(a|b)` means `a` or `b`, `{a}` means 0 or more `a`. * * Note that `Op` and `OpExpression` are undefined. diff --git a/src/scip/solve.c b/src/scip/solve.c index 13a211ae52..a30f878efa 100644 --- a/src/scip/solve.c +++ b/src/scip/solve.c @@ -2507,8 +2507,9 @@ SCIP_RETCODE priceAndCutLoop( SCIP_Bool root; SCIP_Bool allowlocal; int maxseparounds; + int maxsepapartialrounds; int nsepastallrounds; - int maxnsepastallrounds; + int maxsepastallrounds; int stallnfracs; int actdepth; int npricedcolvars; @@ -2542,17 +2543,22 @@ SCIP_RETCODE priceAndCutLoop( allowlocal = SCIPsetIsLE(set, bounddist, set->sepa_maxlocalbounddist); separate = (set->sepa_maxruns == -1 || stat->nruns < set->sepa_maxruns); - /* get maximal number of separation rounds */ + /* determine maximal number of separation rounds */ maxseparounds = (root ? set->sepa_maxroundsroot : set->sepa_maxrounds); if( maxseparounds == -1 ) maxseparounds = INT_MAX; if( stat->nruns > 1 && root && set->sepa_maxroundsrootsubrun >= 0 ) maxseparounds = MIN(maxseparounds, set->sepa_maxroundsrootsubrun); + + /* determine maximal number of partial rounds excluding delayed round */ + maxsepapartialrounds = INT_MAX; if( !fullseparation && set->sepa_maxaddrounds >= 0 ) - maxseparounds = MIN(maxseparounds, stat->nseparounds + set->sepa_maxaddrounds); - maxnsepastallrounds = root ? set->sepa_maxstallroundsroot : set->sepa_maxstallrounds; - if( maxnsepastallrounds == -1 ) - maxnsepastallrounds = INT_MAX; + maxsepapartialrounds = stat->nseparounds + set->sepa_maxaddrounds; + + /* determine maximal number of stalling rounds */ + maxsepastallrounds = root ? set->sepa_maxstallroundsroot : set->sepa_maxstallrounds; + if( maxsepastallrounds == -1 ) + maxsepastallrounds = INT_MAX; /* solve initial LP of price-and-cut loop */ SCIPsetDebugMsg(set, "node: solve LP with price and cut\n"); @@ -2576,7 +2582,7 @@ SCIP_RETCODE priceAndCutLoop( stalllpobjval = SCIP_REAL_MIN; stallnfracs = INT_MAX; lp->installing = FALSE; - while( !(*cutoff) && !(*unbounded) && !(*lperror) && (mustprice || mustsepa || delayedsepa) ) + while( !(*cutoff) && !(*unbounded) && !(*lperror) && ( mustprice || mustsepa ) ) { SCIPsetDebugMsg(set, "-------- node solving loop --------\n"); assert(lp->flushed); @@ -2714,33 +2720,17 @@ SCIP_RETCODE priceAndCutLoop( assert(lp->flushed || *cutoff || *unbounded); assert(lp->solved || *lperror || *cutoff || *unbounded); - /* check, if we exceeded the separation round limit */ - mustsepa = mustsepa + /* if we are infeasible, unbounded, exceeded a separation round, the objective, or a global performance limit, + * we don't need to separate cuts + * (the global limits are only checked at the root node in order to not query system time too often) + */ + mustsepa = mustsepa && separate && !(*cutoff) && !(*unbounded) && stat->nseparounds < maxseparounds - && nsepastallrounds < maxnsepastallrounds - && !(*cutoff); - - /* if separators were delayed, we want to apply a final separation round with the delayed separators */ - delayedsepa = delayedsepa && !mustsepa && !(*cutoff); /* if regular separation applies, we ignore delayed separators */ - mustsepa = mustsepa || delayedsepa; - - if( mustsepa ) - { - /* if the LP is infeasible, unbounded, exceeded the objective limit or a global performance limit was reached, - * we don't need to separate cuts - * (the global limits are only checked at the root node in order to not query system time too often) - */ - if( !separate || (*cutoff) || (*unbounded) - || (SCIPlpGetSolstat(lp) != SCIP_LPSOLSTAT_OPTIMAL && SCIPlpGetSolstat(lp) != SCIP_LPSOLSTAT_UNBOUNDEDRAY) - || SCIPsetIsGE(set, SCIPnodeGetLowerbound(focusnode), primal->cutoffbound) - || (root && SCIPsolveIsStopped(set, stat, FALSE)) ) - { - mustsepa = FALSE; - delayedsepa = FALSE; - } - else - assert(!(*lperror)); - } + && ( delayedsepa || stat->nseparounds < maxsepapartialrounds ) + && nsepastallrounds < maxsepastallrounds + && ( SCIPlpGetSolstat(lp) == SCIP_LPSOLSTAT_OPTIMAL || SCIPlpGetSolstat(lp) == SCIP_LPSOLSTAT_UNBOUNDEDRAY ) + && SCIPsetIsLT(set, SCIPnodeGetLowerbound(focusnode), primal->cutoffbound) + && ( !root || !SCIPsolveIsStopped(set, stat, FALSE) ); /* separation (needs not to be done completely, because we just want to increase the lower bound) */ if( mustsepa ) @@ -2759,6 +2749,7 @@ SCIP_RETCODE priceAndCutLoop( oldninitconssadded = stat->ninitconssadded; mustsepa = FALSE; + delayedsepa = delayedsepa && stat->nseparounds >= maxsepapartialrounds; enoughcuts = SCIPsetIsZero(set, SCIPsetGetSepaMaxcutsGenFactor(set, root) * SCIPsetGetSepaMaxcuts(set, root)); /* global cut pool separation */ @@ -2789,10 +2780,10 @@ SCIP_RETCODE priceAndCutLoop( &delayedsepa, &enoughcuts, cutoff, lperror, &mustsepa, &mustprice) ); assert(BMSgetNUsedBufferMemory(mem->buffer) == 0); - /* if we are close to the stall round limit, also call the delayed separators */ - if( !(*cutoff) && !(*lperror) && !enoughcuts && lp->solved - && (SCIPlpGetSolstat(lp) == SCIP_LPSOLSTAT_OPTIMAL || SCIPlpGetSolstat(lp) == SCIP_LPSOLSTAT_UNBOUNDEDRAY) - && nsepastallrounds >= maxnsepastallrounds-1 && delayedsepa ) + /* if we are in the last separation or stall round, also call the delayed separators */ + if( !(*cutoff) && !(*lperror) && lp->solved && !enoughcuts && delayedsepa + && ( stat->nseparounds + 1 >= maxseparounds || nsepastallrounds + 1 >= maxsepastallrounds ) + && ( SCIPlpGetSolstat(lp) == SCIP_LPSOLSTAT_OPTIMAL || SCIPlpGetSolstat(lp) == SCIP_LPSOLSTAT_UNBOUNDEDRAY ) ) { SCIP_CALL( separationRoundLP(blkmem, set, messagehdlr, stat, eventqueue, eventfilter, transprob, primal, tree, lp, sepastore, actdepth, bounddist, allowlocal, delayedsepa, @@ -2980,32 +2971,33 @@ SCIP_RETCODE priceAndCutLoop( } else { - nsepastallrounds++; + ++nsepastallrounds; } stalllpsolstat = SCIPlpGetSolstat(lp); - /* tell LP that we are (close to) stalling */ - if( nsepastallrounds >= maxnsepastallrounds-2 ) + /* tell LP that we are stalling */ + if( nsepastallrounds + 1 >= maxsepastallrounds ) lp->installing = TRUE; - SCIPsetDebugMsg(set, " -> nsepastallrounds=%d/%d\n", nsepastallrounds, maxnsepastallrounds); + + SCIPsetDebugMsg(set, " -> nsepastallrounds=%d/%d\n", nsepastallrounds, maxsepastallrounds); } } } } assert(*cutoff || *lperror || (lp->flushed && lp->solved)); /* cutoff: LP may be unsolved due to bound changes */ - SCIPsetDebugMsg(set, "separation round %d/%d finished (%d/%d stall rounds): mustprice=%u, mustsepa=%u, delayedsepa=%u, propagateagain=%u\n", - stat->nseparounds, maxseparounds, nsepastallrounds, maxnsepastallrounds, mustprice, mustsepa, delayedsepa, *propagateagain); - /* increase separation round counter */ - stat->nseparounds++; + ++stat->nseparounds; + + SCIPsetDebugMsg(set, "separation round %d/%d finished (%d/%d stall rounds): mustprice=%u, mustsepa=%u, delayedsepa=%u, propagateagain=%u\n", + stat->nseparounds, maxseparounds, nsepastallrounds, maxsepastallrounds, mustprice, mustsepa, delayedsepa, *propagateagain); } } - if( root && nsepastallrounds >= maxnsepastallrounds ) + if( root && nsepastallrounds >= maxsepastallrounds ) { SCIPmessagePrintVerbInfo(messagehdlr, set->disp_verblevel, SCIP_VERBLEVEL_FULL, - "Truncate separation round because of stalling (%d stall rounds).\n", maxnsepastallrounds); + "Truncate separation round because of stalling (%d stall rounds).\n", maxsepastallrounds); } if( !*lperror )