Beispiel #1
0
/** perform dual presolving */
static
SCIP_RETCODE performDualfix(
   SCIP*                 scip,               /**< SCIP data structure */
   int*                  nfixedvars,         /**< pointer to store number of fixed variables */
   SCIP_Bool*            unbounded,          /**< pointer to store if an unboundness was detected */
   SCIP_Bool*            cutoff              /**< pointer to store if a cutoff was detected */
   )
{
   SCIP_VAR** vars;
   int nvars;
   int v;

   /* get active problem variables */
   vars = SCIPgetVars(scip);
   nvars = SCIPgetNVars(scip);

   /* look for fixable variables
    * loop backwards, since a variable fixing can change the current and the subsequent slots in the vars array
    */
   for( v = nvars - 1; v >= 0; --v )
   {
      SCIP_VAR* var;
      SCIP_Real bound;
      SCIP_Real obj;
      SCIP_Bool infeasible;
      SCIP_Bool fixed;

      var = vars[v];
      assert(var != NULL);

      /* don't perform dual presolving operations on deleted variables */
      if( SCIPvarIsDeleted(var) )
         continue;

      /* ignore already fixed variables (use feasibility tolerance since this is used in SCIPfixVar() */
      if( SCIPisFeasEQ(scip, SCIPvarGetLbLocal(var), SCIPvarGetUbLocal(var)) )
         continue;

      obj = SCIPvarGetObj(var);

      /* if the objective coefficient of the variable is 0 and it may be rounded both
       * up and down, then fix it to the closest feasible value to 0 */
      if( SCIPisZero(scip, obj) && SCIPvarMayRoundDown(var) && SCIPvarMayRoundUp(var) )
      {
         SCIP_Real roundbound;

         bound = SCIPvarGetLbGlobal(var);
         if( SCIPisLT(scip, bound, 0.0) )
         {
            if( SCIPisLE(scip, 0.0, SCIPvarGetUbGlobal(var)) )
               bound = 0.0;
            else
            {
               /* try to take an integer value, only for polishing */
               roundbound = SCIPfloor(scip, SCIPvarGetUbGlobal(var));

               if( roundbound < bound )
                  bound = SCIPvarGetUbGlobal(var);
               else
                  bound = roundbound;
            }
         }
         else
         {
            /* try to take an integer value, only for polishing */
            roundbound = SCIPceil(scip, bound);

            if( roundbound < SCIPvarGetUbGlobal(var) )
               bound = roundbound;
         }
         SCIPdebugMessage("fixing variable <%s> with objective 0 to %g\n", SCIPvarGetName(var), bound);
      }
      else
      {
         /* if it is always possible to round variable in direction of objective value, fix it to its proper bound */
         if( SCIPvarMayRoundDown(var) && !SCIPisNegative(scip, obj) )
         {
            bound = SCIPvarGetLbGlobal(var);
            if ( SCIPisInfinity(scip, -bound) )
            {
               /* variable can be fixed to -infinity */
               if ( SCIPgetStage(scip) > SCIP_STAGE_PRESOLVING )
               {
                  /* Fixing variables to infinity is not allowed after presolving, since LP-solvers cannot handle this
                   * consistently. We thus have to ignore this (should better be handled in presolving). */
                  continue;
               }
               if ( SCIPisZero(scip, obj) && SCIPvarGetNLocksUp(var) == 1 )
               {
                  /* Variable is only contained in one constraint: we hope that the corresponding constraint handler is
                   * clever enough to set/aggregate the variable to something more useful than -infinity and do nothing
                   * here. */
                  continue;
               }
            }
            SCIPdebugMessage("fixing variable <%s> with objective %g and %d uplocks to lower bound %g\n",
               SCIPvarGetName(var), SCIPvarGetObj(var), SCIPvarGetNLocksUp(var), bound);
         }
         else if( SCIPvarMayRoundUp(var) && !SCIPisPositive(scip, obj) )
         {
            bound = SCIPvarGetUbGlobal(var);
            if ( SCIPisInfinity(scip, bound) )
            {
               /* variable can be fixed to infinity */
               if ( SCIPgetStage(scip) > SCIP_STAGE_PRESOLVING )
               {
                  /* Fixing variables to infinity is not allowed after presolving, since LP-solvers cannot handle this
                   * consistently. We thus have to ignore this (should better be handled in presolving). */
                  continue;
               }
               if ( SCIPisZero(scip, obj) && SCIPvarGetNLocksDown(var) == 1 )
               {
                  /* Variable is only contained in one constraint: we hope that the corresponding constraint handler is
                   * clever enough to set/aggregate the variable to something more useful than +infinity and do nothing
                   * here */
                  continue;
               }
            }
            SCIPdebugMessage("fixing variable <%s> with objective %g and %d downlocks to upper bound %g\n",
               SCIPvarGetName(var), SCIPvarGetObj(var), SCIPvarGetNLocksDown(var), bound);
         }
         else
            continue;
      }

      if( SCIPisInfinity(scip, REALABS(bound)) && !SCIPisZero(scip, obj) )
      {
         SCIPdebugMessage(" -> unbounded fixing\n");
         SCIPverbMessage(scip, SCIP_VERBLEVEL_NORMAL, NULL,
            "problem infeasible or unbounded: variable <%s> with objective %.15g can be made infinitely %s\n",
            SCIPvarGetName(var), SCIPvarGetObj(var), bound < 0.0 ? "small" : "large");
         *unbounded = TRUE;
         return SCIP_OKAY;
      }

      /* apply the fixing */
      SCIPdebugMessage("apply fixing of variable %s to %g\n", SCIPvarGetName(var), bound);
      SCIP_CALL( SCIPfixVar(scip, var, bound, &infeasible, &fixed) );

      if( infeasible )
      {
         SCIPdebugMessage(" -> infeasible fixing\n");
         *cutoff = TRUE;
         return SCIP_OKAY;
      }

      assert(fixed || (SCIPgetStage(scip) == SCIP_STAGE_SOLVING && SCIPisFeasEQ(scip, bound, SCIPvarGetLbLocal(var))
            && SCIPisFeasEQ(scip, bound, SCIPvarGetUbLocal(var))));
      (*nfixedvars)++;
   }

   return SCIP_OKAY;
}
/** execution method of presolver */
static
SCIP_DECL_PRESOLEXEC(presolExecDualfix)
{  /*lint --e{715}*/
   SCIP_VAR** vars;
   SCIP_Real bound;
   SCIP_Real roundbound;
   SCIP_Real obj;
   SCIP_Bool infeasible;
   SCIP_Bool fixed;
   int nvars;
   int v;

   assert(presol != NULL);
   assert(strcmp(SCIPpresolGetName(presol), PRESOL_NAME) == 0);
   assert(result != NULL);

   *result = SCIP_DIDNOTFIND;

   /* get active problem variables */
   vars = SCIPgetVars(scip);
   nvars = SCIPgetNVars(scip);

   /* look for fixable variables
    * loop backwards, since a variable fixing can change the current and the subsequent slots in the vars array
    */
   for( v = nvars - 1; v >= 0; --v )
   {
      /* don't perform dual presolving operations on deleted variables */
      if( SCIPvarIsDeleted(vars[v]) )
         continue;

      obj = SCIPvarGetObj(vars[v]);

      /* if the objective coefficient of the variable is 0 and it may be rounded both
       * up and down, then fix it to the closest feasible value to 0 */
      if( SCIPisZero(scip, obj) && SCIPvarMayRoundDown(vars[v]) && SCIPvarMayRoundUp(vars[v]) )
      {
         bound = SCIPvarGetLbGlobal(vars[v]);
         if( SCIPisLT(scip, bound, 0.0) )
         {
            if( SCIPisLE(scip, 0.0, SCIPvarGetUbGlobal(vars[v])) )
               bound = 0.0;
            else
            {
               /* try to take an integer value, only for polishing */
               roundbound = SCIPfloor(scip, SCIPvarGetUbGlobal(vars[v]));
               
               if( roundbound < bound )
                  bound = SCIPvarGetUbGlobal(vars[v]);
               else
                  bound = roundbound;
            }
         }
         else
         {
            /* try to take an integer value, only for polishing */
            roundbound = SCIPceil(scip, bound);

            if( roundbound < SCIPvarGetUbGlobal(vars[v]) )
               bound = roundbound;
         }
         SCIPdebugMessage("variable <%s> with objective 0 fixed to %g\n",
            SCIPvarGetName(vars[v]), bound);
      }
      else
      {
         /* if it is always possible to round variable in direction of objective value,
          * fix it to its proper bound
          */
         if( SCIPvarMayRoundDown(vars[v]) && !SCIPisNegative(scip, obj) )
         {
            bound = SCIPvarGetLbGlobal(vars[v]);
            if( SCIPisZero(scip, obj) && SCIPvarGetNLocksUp(vars[v]) == 1 && SCIPisInfinity(scip, -bound) )
            {
               /* variable can be set to -infinity, and it is only contained in one constraint:
                * we hope that the corresponding constraint handler is clever enough to set/aggregate the variable
                * to something more useful than -infinity and do nothing here
                */
               continue;
            }
            SCIPdebugMessage("variable <%s> with objective %g and %d uplocks fixed to lower bound %g\n",
               SCIPvarGetName(vars[v]), SCIPvarGetObj(vars[v]), SCIPvarGetNLocksUp(vars[v]), bound);
         }
         else if( SCIPvarMayRoundUp(vars[v]) && !SCIPisPositive(scip, obj) )
         {
            bound = SCIPvarGetUbGlobal(vars[v]);
            if( SCIPisZero(scip, obj) && SCIPvarGetNLocksDown(vars[v]) == 1 && SCIPisInfinity(scip, bound) )
            {
               /* variable can be set to +infinity, and it is only contained in one constraint:
                * we hope that the corresponding constraint handler is clever enough to set/aggregate the variable
                * to something more useful than +infinity and do nothing here
                */
               continue;
            }
            SCIPdebugMessage("variable <%s> with objective %g and %d downlocks fixed to upper bound %g\n",
               SCIPvarGetName(vars[v]), SCIPvarGetObj(vars[v]), SCIPvarGetNLocksDown(vars[v]), bound);
         }
         else
            continue;
      }

      /* apply the fixing */
      if( SCIPisInfinity(scip, REALABS(bound)) && !SCIPisZero(scip, obj) )
      {
         SCIPdebugMessage(" -> unbounded fixing\n");
         SCIPverbMessage(scip, SCIP_VERBLEVEL_NORMAL, NULL,
            "problem infeasible or unbounded: variable <%s> with objective %.15g can be made infinitely %s\n",
            SCIPvarGetName(vars[v]), SCIPvarGetObj(vars[v]), bound < 0.0 ? "small" : "large");
         *result = SCIP_UNBOUNDED;
         return SCIP_OKAY;
      }
      SCIP_CALL( SCIPfixVar(scip, vars[v], bound, &infeasible, &fixed) );
      if( infeasible )
      {
         SCIPdebugMessage(" -> infeasible fixing\n");
         *result = SCIP_CUTOFF;
         return SCIP_OKAY;
      }
      assert(fixed);
      (*nfixedvars)++;
      *result = SCIP_SUCCESS;
   }

   return SCIP_OKAY;
}
Beispiel #3
0
/** execution method of primal heuristic */
static
SCIP_DECL_HEUREXEC(heurExecTrivial)
{  /*lint --e{715}*/
   SCIP_VAR** vars;
   SCIP_SOL* lbsol;                     /* solution where all variables are set to their lower bounds */
   SCIP_SOL* ubsol;                     /* solution where all variables are set to their upper bounds */
   SCIP_SOL* zerosol;                   /* solution where all variables are set to zero */
   SCIP_SOL* locksol;                   /* solution where all variables are set to the bound with the fewer locks */

   SCIP_Real large;

   int nvars;
   int nbinvars;
   int i;

   SCIP_Bool success;
   SCIP_Bool zerovalid;

   *result = SCIP_DIDNOTRUN;

   if( SCIPgetNRuns(scip) > 1 )
      return SCIP_OKAY;

   *result = SCIP_DIDNOTFIND;
   success = FALSE;

   /* initialize data structure */
   SCIP_CALL( SCIPcreateSol(scip, &lbsol, heur) );
   SCIP_CALL( SCIPcreateSol(scip, &ubsol, heur) );
   SCIP_CALL( SCIPcreateSol(scip, &zerosol, heur) );
   SCIP_CALL( SCIPcreateSol(scip, &locksol, heur) );

   /* determine large value to set variables to */
   large = SCIPinfinity(scip);
   if( !SCIPisInfinity(scip, 0.1 / SCIPfeastol(scip)) )
      large = 0.1 / SCIPfeastol(scip);

   SCIP_CALL( SCIPgetVarsData(scip, &vars, &nvars, &nbinvars, NULL, NULL, NULL) );

   /* if the problem is binary, we do not have to check the zero solution, since it is equal to the lower bound
    * solution */
   zerovalid = (nvars != nbinvars);
   assert(vars != NULL || nvars == 0);

   for( i = 0; i < nvars; i++ )
   {
      SCIP_Real lb;
      SCIP_Real ub;

      assert(vars != NULL); /* this assert is needed for flexelint */

      lb = SCIPvarGetLbLocal(vars[i]);
      ub = SCIPvarGetUbLocal(vars[i]);
      
      /* if problem is obviously infeasible due to empty domain, stop */
      if( SCIPisGT(scip, lb, ub) )
         goto TERMINATE;

      /* set bounds to sufficient large value */
      if( SCIPisInfinity(scip, -lb) )
         lb = MIN(-large, ub);
      if( SCIPisInfinity(scip, ub) )
      {
         SCIP_Real tmp;

         tmp = SCIPvarGetLbLocal(vars[i]);
         ub = MAX(tmp, large);
      }

      SCIP_CALL( SCIPsetSolVal(scip, lbsol, vars[i], lb) );
      SCIP_CALL( SCIPsetSolVal(scip, ubsol, vars[i], ub) );

      /* try the zero vector, if it is in the bounds region */
      if( zerovalid )
      {
         if( SCIPisLE(scip, lb, 0.0) && SCIPisLE(scip, 0.0, ub) )
         {
            SCIP_CALL( SCIPsetSolVal(scip, zerosol, vars[i], 0.0) );
         }
         else
            zerovalid = FALSE;
      }

      /* set variables to the bound with fewer locks, if tie choose an average value */
      if( SCIPvarGetNLocksDown(vars[i]) >  SCIPvarGetNLocksUp(vars[i]) )
      {
         SCIP_CALL( SCIPsetSolVal(scip, locksol, vars[i], ub) );
      }
      else if( SCIPvarGetNLocksDown(vars[i]) <  SCIPvarGetNLocksUp(vars[i]) )
      {
         SCIP_CALL( SCIPsetSolVal(scip, locksol, vars[i], lb) );
      }
      else
      {
         SCIP_Real solval;
         solval = (lb+ub)/2.0;

         /* if a tie occurs, roughly every third integer variable will be rounded up */
         if( SCIPvarGetType(vars[i]) != SCIP_VARTYPE_CONTINUOUS )
            solval = i % 3 == 0 ? SCIPceil(scip,solval) : SCIPfloor(scip,solval);

         assert(SCIPisFeasLE(scip,SCIPvarGetLbLocal(vars[i]),solval) && SCIPisFeasLE(scip,solval,SCIPvarGetUbLocal(vars[i])));

         SCIP_CALL( SCIPsetSolVal(scip, locksol, vars[i], solval) );
      }
   }

   /* try lower bound solution */
   SCIPdebugMessage("try lower bound solution\n");
   SCIP_CALL( SCIPtrySol(scip, lbsol, FALSE, FALSE, TRUE, TRUE, &success) );

   if( success )
   {
      SCIPdebugMessage("found feasible lower bound solution:\n");
      SCIPdebug( SCIP_CALL( SCIPprintSol(scip, lbsol, NULL, FALSE) ) );

      *result = SCIP_FOUNDSOL;
   }

   /* try upper bound solution */
   SCIPdebugMessage("try upper bound solution\n");
   SCIP_CALL( SCIPtrySol(scip, ubsol, FALSE, FALSE, TRUE, TRUE, &success) );

   if( success )
   {
      SCIPdebugMessage("found feasible upper bound solution:\n");
      SCIPdebug( SCIP_CALL( SCIPprintSol(scip, ubsol, NULL, FALSE) ) );

      *result = SCIP_FOUNDSOL;
   }

   /* try zero solution */
   if( zerovalid )
   {
      SCIPdebugMessage("try zero solution\n");
      SCIP_CALL( SCIPtrySol(scip, zerosol, FALSE, FALSE, TRUE, TRUE, &success) );

      if( success )
      {
         SCIPdebugMessage("found feasible zero solution:\n");
         SCIPdebug( SCIP_CALL( SCIPprintSol(scip, zerosol, NULL, FALSE) ) );

         *result = SCIP_FOUNDSOL;
      }
   }

   /* try lock solution */
   SCIPdebugMessage("try lock solution\n");
   SCIP_CALL( SCIPtrySol(scip, locksol, FALSE, FALSE, TRUE, TRUE, &success) );

   if( success )
   {
      SCIPdebugMessage("found feasible lock solution:\n");
      SCIPdebug( SCIP_CALL( SCIPprintSol(scip, locksol, NULL, FALSE) ) );

      *result = SCIP_FOUNDSOL;
   }

TERMINATE:
   /* free solutions */
   SCIP_CALL( SCIPfreeSol(scip, &lbsol) );
   SCIP_CALL( SCIPfreeSol(scip, &ubsol) );
   SCIP_CALL( SCIPfreeSol(scip, &zerosol) );
   SCIP_CALL( SCIPfreeSol(scip, &locksol) );

   return SCIP_OKAY;
}
/** returns a fractional variable, that has most impact on rows in opposite direction, i.e. that is most crucial to
 *  fix in the other direction;
 *  if variables have equal impact, chooses the one with best objective value improvement in corresponding direction;
 *  rounding in a direction is forbidden, if this forces the objective value over the upper bound
 */
static
SCIP_RETCODE selectEssentialRounding(
   SCIP*                 scip,               /**< SCIP data structure */
   SCIP_SOL*             sol,                /**< primal solution */
   SCIP_Real             minobj,             /**< minimal objective value possible after rounding remaining fractional vars */
   SCIP_VAR**            lpcands,            /**< fractional variables in LP */
   int                   nlpcands,           /**< number of fractional variables in LP */
   SCIP_VAR**            roundvar,           /**< pointer to store the rounding variable, returns NULL if impossible */
   SCIP_Real*            oldsolval,          /**< old (fractional) solution value of rounding variable */
   SCIP_Real*            newsolval           /**< new (rounded) solution value of rounding variable */
   )
{
   SCIP_VAR* var;
   SCIP_Real solval;
   SCIP_Real roundval;
   SCIP_Real obj;
   SCIP_Real deltaobj;
   SCIP_Real bestdeltaobj;
   int maxnlocks;
   int nlocks;
   int v;

   assert(roundvar != NULL);
   assert(oldsolval != NULL);
   assert(newsolval != NULL);

   /* select rounding variable */
   maxnlocks = -1;
   bestdeltaobj = SCIPinfinity(scip);
   *roundvar = NULL;
   for( v = 0; v < nlpcands; ++v )
   {
      var = lpcands[v];
      assert(SCIPvarGetType(var) == SCIP_VARTYPE_BINARY || SCIPvarGetType(var) == SCIP_VARTYPE_INTEGER);

      solval = SCIPgetSolVal(scip, sol, var);
      if( !SCIPisFeasIntegral(scip, solval) )
      {
         obj = SCIPvarGetObj(var);

         /* rounding down */
         nlocks = SCIPvarGetNLocksUp(var);
         if( nlocks >= maxnlocks )
         {
            roundval = SCIPfeasFloor(scip, solval);
            deltaobj = obj * (roundval - solval);
            if( (nlocks > maxnlocks || deltaobj < bestdeltaobj) && minobj - obj < SCIPgetCutoffbound(scip) )
            {
               maxnlocks = nlocks;
               bestdeltaobj = deltaobj;
               *roundvar = var;
               *oldsolval = solval;
               *newsolval = roundval;
            }
         }

         /* rounding up */
         nlocks = SCIPvarGetNLocksDown(var);
         if( nlocks >= maxnlocks )
         {
            roundval = SCIPfeasCeil(scip, solval);
            deltaobj = obj * (roundval - solval);
            if( (nlocks > maxnlocks || deltaobj < bestdeltaobj) && minobj + obj < SCIPgetCutoffbound(scip) )
            {
               maxnlocks = nlocks;
               bestdeltaobj = deltaobj;
               *roundvar = var;
               *oldsolval = solval;
               *newsolval = roundval;
            }
         }
      }
   }

   return SCIP_OKAY;
}
/** returns a variable, that pushes activity of the row in the given direction with minimal negative impact on other rows;
 *  if variables have equal impact, chooses the one with best objective value improvement in corresponding direction;
 *  rounding in a direction is forbidden, if this forces the objective value over the upper bound
 */
static
SCIP_RETCODE selectRounding(
   SCIP*                 scip,               /**< SCIP data structure */
   SCIP_SOL*             sol,                /**< primal solution */
   SCIP_Real             minobj,             /**< minimal objective value possible after rounding remaining fractional vars */
   SCIP_ROW*             row,                /**< LP row */
   int                   direction,          /**< should the activity be increased (+1) or decreased (-1)? */
   SCIP_VAR**            roundvar,           /**< pointer to store the rounding variable, returns NULL if impossible */
   SCIP_Real*            oldsolval,          /**< pointer to store old (fractional) solution value of rounding variable */
   SCIP_Real*            newsolval           /**< pointer to store new (rounded) solution value of rounding variable */
   )
{
   SCIP_COL* col;
   SCIP_VAR* var;
   SCIP_Real val;
   SCIP_COL** rowcols;
   SCIP_Real* rowvals;
   SCIP_Real solval;
   SCIP_Real roundval;
   SCIP_Real obj;
   SCIP_Real deltaobj;
   SCIP_Real bestdeltaobj;
   SCIP_VARTYPE vartype;
   int nrowcols;
   int nlocks;
   int minnlocks;
   int c;

   assert(direction == +1 || direction == -1);
   assert(roundvar != NULL);
   assert(oldsolval != NULL);
   assert(newsolval != NULL);

   /* get row entries */
   rowcols = SCIProwGetCols(row);
   rowvals = SCIProwGetVals(row);
   nrowcols = SCIProwGetNLPNonz(row);

   /* select rounding variable */
   minnlocks = INT_MAX;
   bestdeltaobj = SCIPinfinity(scip);
   *roundvar = NULL;
   for( c = 0; c < nrowcols; ++c )
   {
      col = rowcols[c];
      var = SCIPcolGetVar(col);

      vartype = SCIPvarGetType(var);
      if( vartype == SCIP_VARTYPE_BINARY || vartype == SCIP_VARTYPE_INTEGER )
      {
         solval = SCIPgetSolVal(scip, sol, var);

         if( !SCIPisFeasIntegral(scip, solval) )
         {
            val = rowvals[c];
            obj = SCIPvarGetObj(var);

            if( direction * val < 0.0 )
            {
               /* rounding down */
               nlocks = SCIPvarGetNLocksDown(var);
               if( nlocks <= minnlocks )
               {
                  roundval = SCIPfeasFloor(scip, solval);
                  deltaobj = obj * (roundval - solval);
                  if( (nlocks < minnlocks || deltaobj < bestdeltaobj) && minobj - obj < SCIPgetCutoffbound(scip) )
                  {
                     minnlocks = nlocks;
                     bestdeltaobj = deltaobj;
                     *roundvar = var;
                     *oldsolval = solval;
                     *newsolval = roundval;
                  }
               }
            }
            else
            {
               /* rounding up */
               assert(direction * val > 0.0);
               nlocks = SCIPvarGetNLocksUp(var);
               if( nlocks <= minnlocks )
               {
                  roundval = SCIPfeasCeil(scip, solval);
                  deltaobj = obj * (roundval - solval);
                  if( (nlocks < minnlocks || deltaobj < bestdeltaobj) && minobj + obj < SCIPgetCutoffbound(scip) )
                  {
                     minnlocks = nlocks;
                     bestdeltaobj = deltaobj;
                     *roundvar = var;
                     *oldsolval = solval;
                     *newsolval = roundval;
                  }
               }
            }
         }
      }
   }

   return SCIP_OKAY;
}
Beispiel #6
0
/** returns a variable, that pushes activity of the row in the given direction with minimal negative impact on other rows;
 *  if variables have equal impact, chooses the one with best objective value improvement in corresponding direction;
 *  prefer fractional integers over other variables in order to become integral during the process;
 *  shifting in a direction is forbidden, if this forces the objective value over the upper bound, or if the variable
 *  was already shifted in the opposite direction
 */
static
SCIP_RETCODE selectShifting(
    SCIP*                 scip,               /**< SCIP data structure */
    SCIP_SOL*             sol,                /**< primal solution */
    SCIP_ROW*             row,                /**< LP row */
    SCIP_Real             rowactivity,        /**< activity of LP row */
    int                   direction,          /**< should the activity be increased (+1) or decreased (-1)? */
    SCIP_Real*            nincreases,         /**< array with weighted number of increasings per variables */
    SCIP_Real*            ndecreases,         /**< array with weighted number of decreasings per variables */
    SCIP_Real             increaseweight,     /**< current weight of increase/decrease updates */
    SCIP_VAR**            shiftvar,           /**< pointer to store the shifting variable, returns NULL if impossible */
    SCIP_Real*            oldsolval,          /**< pointer to store old solution value of shifting variable */
    SCIP_Real*            newsolval           /**< pointer to store new (shifted) solution value of shifting variable */
)
{
    SCIP_COL** rowcols;
    SCIP_Real* rowvals;
    int nrowcols;
    SCIP_Real activitydelta;
    SCIP_Real bestshiftscore;
    SCIP_Real bestdeltaobj;
    int c;

    assert(direction == +1 || direction == -1);
    assert(nincreases != NULL);
    assert(ndecreases != NULL);
    assert(shiftvar != NULL);
    assert(oldsolval != NULL);
    assert(newsolval != NULL);

    /* get row entries */
    rowcols = SCIProwGetCols(row);
    rowvals = SCIProwGetVals(row);
    nrowcols = SCIProwGetNLPNonz(row);

    /* calculate how much the activity must be shifted in order to become feasible */
    activitydelta = (direction == +1 ? SCIProwGetLhs(row) - rowactivity : SCIProwGetRhs(row) - rowactivity);
    assert((direction == +1 && SCIPisPositive(scip, activitydelta))
           || (direction == -1 && SCIPisNegative(scip, activitydelta)));

    /* select shifting variable */
    bestshiftscore = SCIP_REAL_MAX;
    bestdeltaobj = SCIPinfinity(scip);
    *shiftvar = NULL;
    *newsolval = 0.0;
    *oldsolval = 0.0;
    for( c = 0; c < nrowcols; ++c )
    {
        SCIP_COL* col;
        SCIP_VAR* var;
        SCIP_Real val;
        SCIP_Real solval;
        SCIP_Real shiftval;
        SCIP_Real shiftscore;
        SCIP_Bool isinteger;
        SCIP_Bool isfrac;
        SCIP_Bool increase;

        col = rowcols[c];
        var = SCIPcolGetVar(col);
        val = rowvals[c];
        assert(!SCIPisZero(scip, val));
        solval = SCIPgetSolVal(scip, sol, var);

        isinteger = (SCIPvarGetType(var) == SCIP_VARTYPE_BINARY || SCIPvarGetType(var) == SCIP_VARTYPE_INTEGER);
        isfrac = isinteger && !SCIPisFeasIntegral(scip, solval);
        increase = (direction * val > 0.0);

        /* calculate the score of the shifting (prefer smaller values) */
        if( isfrac )
            shiftscore = increase ? -1.0 / (SCIPvarGetNLocksUp(var) + 1.0) :
                         -1.0 / (SCIPvarGetNLocksDown(var) + 1.0);
        else
        {
            int probindex;
            probindex = SCIPvarGetProbindex(var);

            if( increase )
                shiftscore = ndecreases[probindex]/increaseweight;
            else
                shiftscore = nincreases[probindex]/increaseweight;
            if( isinteger )
                shiftscore += 1.0;
        }

        if( shiftscore <= bestshiftscore )
        {
            SCIP_Real deltaobj;

            if( !increase )
            {
                /* shifting down */
                assert(direction * val < 0.0);
                if( isfrac )
                    shiftval = SCIPfeasFloor(scip, solval);
                else
                {
                    SCIP_Real lb;

                    assert(activitydelta/val < 0.0);
                    shiftval = solval + activitydelta/val;
                    assert(shiftval <= solval); /* may be equal due to numerical digit erasement in the subtraction */
                    if( SCIPvarIsIntegral(var) )
                        shiftval = SCIPfeasFloor(scip, shiftval);
                    lb = SCIPvarGetLbGlobal(var);
                    shiftval = MAX(shiftval, lb);
                }
            }
            else
            {
                /* shifting up */
                assert(direction * val > 0.0);
                if( isfrac )
                    shiftval = SCIPfeasCeil(scip, solval);
                else
                {
                    SCIP_Real ub;

                    assert(activitydelta/val > 0.0);
                    shiftval = solval + activitydelta/val;
                    assert(shiftval >= solval); /* may be equal due to numerical digit erasement in the subtraction */
                    if( SCIPvarIsIntegral(var) )
                        shiftval = SCIPfeasCeil(scip, shiftval);
                    ub = SCIPvarGetUbGlobal(var);
                    shiftval = MIN(shiftval, ub);
                }
            }

            if( SCIPisEQ(scip, shiftval, solval) )
                continue;

            deltaobj = SCIPvarGetObj(var) * (shiftval - solval);
            if( shiftscore < bestshiftscore || deltaobj < bestdeltaobj )
            {
                bestshiftscore = shiftscore;
                bestdeltaobj = deltaobj;
                *shiftvar = var;
                *oldsolval = solval;
                *newsolval = shiftval;
            }
        }
    }

    return SCIP_OKAY;
}