Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove assumption of integer objective, fixes #23 #24

Open
wants to merge 3 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public abstract class AbstractBranchAndPrice<T extends ModelInterface,
protected final OptimizationSense optimizationSenseMaster;

/** Stores the objective of the best (integer) solution **/
protected int objectiveIncumbentSolution;
protected double objectiveIncumbentSolution;
/**
* List containing the columns corresponding to the best integer solution (empty list when no
* feasible solution has been found)
Expand Down Expand Up @@ -289,19 +289,19 @@ public void runBranchAndPrice(long timeLimit)

// If solution is integral, check whether it is better than the current best solution
if (this.isIntegerNode(bapNode)) {
int integerObjective = MathProgrammingUtil.doubleToInt(bapNode.objective);
notifier.fireNodeIsIntegerEvent(bapNode, bapNode.bound, integerObjective);
double objective = bapNode.objective;
notifier.fireNodeIsIntegerEvent(bapNode, bapNode.bound, objective);
if (optimizationSenseMaster == OptimizationSense.MINIMIZE
&& integerObjective < this.upperBoundOnObjective)
&& objective < this.upperBoundOnObjective)
{
this.objectiveIncumbentSolution = integerObjective;
this.upperBoundOnObjective = integerObjective;
this.objectiveIncumbentSolution = objective;
this.upperBoundOnObjective = objective;
this.incumbentSolution = bapNode.solution;
} else if (optimizationSenseMaster == OptimizationSense.MAXIMIZE
&& integerObjective > this.lowerBoundOnObjective)
&& objective > this.lowerBoundOnObjective)
{
this.objectiveIncumbentSolution = integerObjective;
this.lowerBoundOnObjective = integerObjective;
this.objectiveIncumbentSolution = objective;
this.lowerBoundOnObjective = objective;
this.incumbentSolution = bapNode.solution;
}
} else { // We need to branch
Expand Down Expand Up @@ -401,7 +401,7 @@ protected int getUniqueNodeID()
*
* @return the objective of the best integer solution found during the Branch-and-Price search
*/
public int getObjective()
public double getObjective()
{
return this.objectiveIncumbentSolution;
}
Expand Down Expand Up @@ -596,10 +596,17 @@ protected List<U> generateArtificialSolution()
*/
protected boolean nodeCanBePruned(BAPNode<T, U> node)
{
return (optimizationSenseMaster == OptimizationSense.MINIMIZE
&& Math.ceil(node.bound - config.PRECISION) >= upperBoundOnObjective
|| optimizationSenseMaster == OptimizationSense.MAXIMIZE
&& Math.floor(node.bound + config.PRECISION) <= lowerBoundOnObjective);
if (config.INTEGER_OBJECTIVE) {
return (optimizationSenseMaster == OptimizationSense.MINIMIZE
&& Math.ceil(node.bound - config.PRECISION) >= upperBoundOnObjective
|| optimizationSenseMaster == OptimizationSense.MAXIMIZE
&& Math.floor(node.bound + config.PRECISION) <= lowerBoundOnObjective);
} else {
return optimizationSenseMaster == OptimizationSense.MINIMIZE
&& node.bound >= upperBoundOnObjective
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this bit is tricky. It doesn't take the precision into account. I need to read up on this topic to decide what is the best comparison.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was also not entirely sure what to do here either. However, allowing to deviate by the precision would mean that this would be allowed recursively if the nodes are pruned. Hence, the final precision could turn out significantly worse than the given precision. That was my reason for not taking it into account here.

|| optimizationSenseMaster == OptimizationSense.MAXIMIZE
&& node.bound <= lowerBoundOnObjective;
}
}

/**
Expand Down Expand Up @@ -797,7 +804,7 @@ public void fireNodeIsFractionalEvent(BAPNode<T, U> node, double nodeBound, doub
* @param nodeBound Bound on the node
* @param nodeValue Objective value of the node
*/
public void fireNodeIsIntegerEvent(BAPNode<T, U> node, double nodeBound, int nodeValue)
public void fireNodeIsIntegerEvent(BAPNode<T, U> node, double nodeBound, double nodeValue)
{
NodeIsIntegerEvent<T,U> nodeIsIntegerEvent = null;
for (BAPListener<T,U> listener : listeners) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class FinishMasterEvent
* Cutoff value: Column Generation is terminated when the bound on the Master Objective is worse
* than the cutoff value
**/
public final int cutoffValue;
public final double cutoffValue;
/** Best available bound on the master objective **/
public final double boundOnMasterObjective;

Expand All @@ -49,7 +49,7 @@ public class FinishMasterEvent
* @param boundOnMasterObjective best available bound on master problem
*/
public FinishMasterEvent(
Object source, int columnGenerationIteration, double objective, int cutoffValue,
Object source, int columnGenerationIteration, double objective, double cutoffValue,
double boundOnMasterObjective)
{
super(source);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class FinishPricingEvent<T extends ModelInterface, U extends AbstractColu
* Cutoff value: Column Generation is terminated when the bound on the Master Objective is worse
* than the cutoff value
**/
public final int cutoffValue;
public final double cutoffValue;
/** Best available bound on the master objective **/
public final double boundOnMasterObjective;

Expand All @@ -60,7 +60,7 @@ public class FinishPricingEvent<T extends ModelInterface, U extends AbstractColu
*/
public FinishPricingEvent(
Object source, int columnGenerationIteration, List<U> columns, double objective,
int cutoffValue, double boundOnMasterObjective)
double cutoffValue, double boundOnMasterObjective)
{
super(source);
this.columnGenerationIteration = columnGenerationIteration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class NodeIsIntegerEvent<T extends ModelInterface, U extends AbstractColu
/** Bound on this node **/
public final double nodeBound;
/** Objective value of this node **/
public final int nodeValue;
public final double nodeValue;

/**
* Creates a new NodeIsIntegerEvent
Expand All @@ -47,7 +47,7 @@ public class NodeIsIntegerEvent<T extends ModelInterface, U extends AbstractColu
* @param nodeValue Objective value of the node. nodeBound and nodeValue are equal when the node
* is solved to optimality
*/
public NodeIsIntegerEvent(Object source, BAPNode<T,U> node, double nodeBound, int nodeValue)
public NodeIsIntegerEvent(Object source, BAPNode<T,U> node, double nodeBound, double nodeValue)
{
super(source);
this.node = node;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class ProcessingNextNodeEvent<T extends ModelInterface, U extends Abstrac
/** Number of nodes currently waiting in the queue **/
public final int nodesInQueue;
/** Best integer solution obtained thus far **/
public final int objectiveIncumbentSolution;
public final double objectiveIncumbentSolution;

/**
* Creates a new ProcessingNextNodeEvent
Expand All @@ -47,7 +47,7 @@ public class ProcessingNextNodeEvent<T extends ModelInterface, U extends Abstrac
* @param objectiveIncumbentSolution Best integer solution found thus far
*/
public ProcessingNextNodeEvent(
Object source, BAPNode<T,U> node, int nodesInQueue, int objectiveIncumbentSolution)
Object source, BAPNode<T,U> node, int nodesInQueue, double objectiveIncumbentSolution)
{
super(source);
this.node = node;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,22 @@ public class PruneNodeEvent<T extends ModelInterface, U extends AbstractColumn<T
public final BAPNode<T, U> node;
/** Bound on this node **/
public final double nodeBound;
/** Best integer solution discovered so far **/
public final int bestIntegerSolution;
/** Objective value of best incumbent solution discovered so far **/
public final double objectiveIncumbentSolution;

/**
* Creates a new PruneNodeEvent
*
* @param source Generator of the event
* @param node ID of the node being pruned
* @param nodeBound Bound on the node
* @param bestIntegerSolution Best integer solution discovered thus far
* @param objectiveIncumbentSolution Objective value of best incumbent solution discovered thus far
*/
public PruneNodeEvent(Object source, BAPNode<T, U> node, double nodeBound, int bestIntegerSolution)
public PruneNodeEvent(Object source, BAPNode<T, U> node, double nodeBound, double objectiveIncumbentSolution)
{
super(source);
this.node = node;
this.nodeBound = nodeBound;
this.bestIntegerSolution = bestIntegerSolution;
this.objectiveIncumbentSolution = objectiveIncumbentSolution;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,20 @@ public class StartEvent
public final String instanceName;

/**
* Best available integer solution at the start of the Branch-and-Price or Column generation
* Objective value of best available incumbent solution at the start of the Branch-and-Price or Column generation
* procedure
**/
public final int objectiveIncumbentSolution;
public final double objectiveIncumbentSolution;

/**
* Creates a new StartEvent
*
* @param source Generator of the event
* @param instanceName Name of the instance being solved
* @param objectiveIncumbentSolution Best available integer solution at the start of the
* @param objectiveIncumbentSolution Objective value of best available incumbent solution at the start of the
* Branch-and-Price or Column generation procedure
*/
public StartEvent(Object source, String instanceName, int objectiveIncumbentSolution)
public StartEvent(Object source, String instanceName, double objectiveIncumbentSolution)
{
super(source);
this.instanceName = instanceName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,18 @@ public class ColGen<T extends ModelInterface, U extends AbstractColumn<T, V>,
/**
* The ColGen procedure is terminated if the bound on the best attainable solution to the master
* problem is worse than the cutoffValue. If the master is a minimization problem, the Colgen
* procedure is terminated if {@code ceil(boundOnMasterObjective) >= cutoffValue}. If the master
* is a maximization problem, the Colgen procedure is terminated if
* {@code floor(boundOnMasterObjective) <= cutoffValue}.
* procedure is terminated if {@code ceil(boundOnMasterObjective) >= cutoffValue} in case
* {@link Configuration#INTEGER_OBJECTIVE} is set to {@code true} and if
* {@code boundOnMasterObjective) >= cutoffValue} otherwise. If the master is a maximization problem, the Colgen
* procedure is terminated if {@code floor(boundOnMasterObjective) <= cutoffValue} when the objective value
* is guaranteed to be integer and is terminated if {@code boundOnMasterObjective <= cutoffValue} otherwise.
**/
protected int cutoffValue;
protected double cutoffValue;
/**
* Bound on the best attainable objective value from the master problem. Assuming that the
* master is a minimization problem, the Colgen procedure is terminated if
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

descr needs update

* {@code ceil(boundOnMasterObjective) >= cutoffValue}.
* {@code ceil(boundOnMasterObjective) >= cutoffValue} if {@link Configuration#INTEGER_OBJECTIVE} is set to
* {@code true} and is terminated if {@code boundOnMasterObjective >= cutoffValue} otherwise.
**/
protected double boundOnMasterObjective = 0;
/** Total number of column generation iterations. **/
Expand Down Expand Up @@ -114,7 +117,7 @@ public class ColGen<T extends ModelInterface, U extends AbstractColumn<T, V>,
public ColGen(
T dataModel, AbstractMaster<T, U, V, ? extends MasterData<T, U, V, ?>> master, List<V> pricingProblems,
List<Class<? extends AbstractPricingProblemSolver<T, U, V>>> solvers, List<U> initSolution,
int cutoffValue, double boundOnMasterObjective)
double cutoffValue, double boundOnMasterObjective)
{
this.dataModel = dataModel;
this.master = master;
Expand Down Expand Up @@ -161,7 +164,7 @@ public ColGen(
public ColGen(
T dataModel, AbstractMaster<T, U, V, ? extends MasterData<T, U, V, ?>> master, V pricingProblem,
List<Class<? extends AbstractPricingProblemSolver<T, U, V>>> solvers, List<U> initSolution,
int cutoffValue, double boundOnMasterObjective)
double cutoffValue, double boundOnMasterObjective)
{
this(
dataModel, master, Collections.singletonList(pricingProblem), solvers, initSolution,
Expand All @@ -188,7 +191,7 @@ public ColGen(
public ColGen(
T dataModel, AbstractMaster<T, U, V, ? extends MasterData<T, U, V, ?>> master, List<V> pricingProblems,
List<Class<? extends AbstractPricingProblemSolver<T, U, V>>> solvers,
PricingProblemManager<T, U, V> pricingProblemManager, List<U> initSolution, int cutoffValue,
PricingProblemManager<T, U, V> pricingProblemManager, List<U> initSolution, double cutoffValue,
double boundOnMasterObjective)
{
this.dataModel = dataModel;
Expand Down Expand Up @@ -519,10 +522,17 @@ public List<AbstractInequality> getCuts()
*/
protected boolean boundOnMasterExceedsCutoffValue()
{
if (optimizationSenseMaster == OptimizationSense.MINIMIZE)
return Math.ceil(boundOnMasterObjective - config.PRECISION) >= cutoffValue;
else
return Math.floor(boundOnMasterObjective + config.PRECISION) <= cutoffValue;
if (config.INTEGER_OBJECTIVE) {
if (optimizationSenseMaster == OptimizationSense.MINIMIZE)
return Math.ceil(boundOnMasterObjective - config.PRECISION) >= cutoffValue;
else
return Math.floor(boundOnMasterObjective + config.PRECISION) <= cutoffValue;
} else {
if (optimizationSenseMaster == OptimizationSense.MINIMIZE)
return boundOnMasterObjective >= cutoffValue;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check: how to handle precision correctly.

else
return boundOnMasterObjective <= cutoffValue;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class SimpleBAPLogger<T extends ModelInterface, U extends AbstractColumn<
/** Parent node ID, -1 if root node **/
protected int parentNodeID;
/** Best integer solution **/
protected int objectiveIncumbentSolution;
protected double objectiveIncumbentSolution;
/** Bound on the BAP node **/
protected double nodeBound;
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public class SimpleCGLogger<T extends ModelInterface, U extends AbstractColumn<T
/** Objective of master problem at the end of iteration it **/
protected double objective;
/** Cutoff value **/
protected int cutoffValue;
protected double cutoffValue;
/** Bound on the objective at the end of iteration it **/
protected double boundOnMasterObjective;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ public class SimpleDebugger<T extends ModelInterface, U extends AbstractColumn<T

/** Name of the instance being solved **/
protected String instanceName;
/** Best integer solution obtained thus far **/
protected int bestIntegerSolution;
/** Objective value of best incumbent solution obtained thus far **/
protected double objectiveIncumbentSolution;

/**
* Creates a debugger for Column Generation instances
Expand Down Expand Up @@ -120,7 +120,7 @@ public SimpleDebugger(
public void startBAP(StartEvent startEvent)
{
instanceName = startEvent.instanceName;
bestIntegerSolution = startEvent.objectiveIncumbentSolution;
objectiveIncumbentSolution = startEvent.objectiveIncumbentSolution;
logger.debug(
"BAP solving {} - Initial solution: {}", instanceName,
startEvent.objectiveIncumbentSolution);
Expand All @@ -138,7 +138,7 @@ public void pruneNode(PruneNodeEvent<T,U> pruneNodeEvent)
logger.debug(
"Pruning node {}. Bound: {}, best integer solution: {}",
new Object[] { pruneNodeEvent.node.nodeID, pruneNodeEvent.nodeBound,
pruneNodeEvent.bestIntegerSolution });
pruneNodeEvent.objectiveIncumbentSolution});
}

@Override
Expand All @@ -150,11 +150,11 @@ public void nodeIsInfeasible(NodeIsInfeasibleEvent<T,U> nodeIsInfeasibleEvent)
@Override
public void nodeIsInteger(NodeIsIntegerEvent<T,U> nodeIsIntegerEvent)
{
this.bestIntegerSolution = Math.min(this.bestIntegerSolution, nodeIsIntegerEvent.nodeValue);
this.objectiveIncumbentSolution = Math.min(this.objectiveIncumbentSolution, nodeIsIntegerEvent.nodeValue);
logger.debug(
"Node {} is integer. Objective: {} (best integer solution: {})",
new Object[] { nodeIsIntegerEvent.node.nodeID, nodeIsIntegerEvent.nodeValue,
bestIntegerSolution });
objectiveIncumbentSolution});
}

@Override
Expand Down Expand Up @@ -185,7 +185,7 @@ public void startCG(StartEvent startEvent)
{
if (colGen != null) {
instanceName = startEvent.instanceName;
bestIntegerSolution = startEvent.objectiveIncumbentSolution;
objectiveIncumbentSolution = startEvent.objectiveIncumbentSolution;
logger.debug(
"CG solving {} - Initial upper bound: {}", instanceName,
startEvent.objectiveIncumbentSolution);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ protected Configuration()
CUTSENABLED = true;
EXPORT_MODEL = false;
EXPORT_MASTER_DIR = "./output/masterLP/";
INTEGER_OBJECTIVE = true;

// Cut handling
QUICK_RETURN_AFTER_CUTS_FOUND = true;
Expand Down Expand Up @@ -70,6 +71,8 @@ protected Configuration(Properties properties)
? Boolean.valueOf(properties.getProperty("EXPORT_MODEL")) : false);
EXPORT_MASTER_DIR = (properties.containsKey("EXPORT_MODEL_DIR")
? properties.getProperty("EXPORT_MODEL_DIR") : "./output/masterLP/");
INTEGER_OBJECTIVE = (properties.containsKey("INTEGER_OBJECTIVE")
? Boolean.valueOf(properties.getProperty("INTEGER_OBJECTIVE")): true);

// Cut handling
QUICK_RETURN_AFTER_CUTS_FOUND = (properties.containsKey("QUICK_RETURN_AFTER_CUTS_FOUND")
Expand Down Expand Up @@ -122,6 +125,11 @@ public static void readFromFile(Properties properties)
public final boolean EXPORT_MODEL;
/** Define export directory for master models. Default: ./output/masterLP/ **/
public final String EXPORT_MASTER_DIR;
/** Defines whether the objective value of any feasible solution is an integer value. More precisely, this value
* should be set to true if all coefficients in the objective function are integer values, and all variables in
* the objective function are integer variables. This parameter influences the rounding and pruning behavior in
* a Branch-and-Price application. Default: true */
public final boolean INTEGER_OBJECTIVE;

/*
* Cut handling
Expand Down
Loading