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 1 commit
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 @@ -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
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 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 @@ -36,7 +36,7 @@ public class PruneNodeEvent<T extends ModelInterface, U extends AbstractColumn<T
/** Bound on this node **/
public final double nodeBound;
/** Best integer solution discovered so far **/
public final int bestIntegerSolution;
public final double bestIntegerSolution;
Copy link
Collaborator

@jkinable jkinable Nov 28, 2016

Choose a reason for hiding this comment

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

this variable should probably be renamed, as it is no longer an integer solution. I would change it to:
objectiveIncumbentSolution (that would probably make it more consistent as well)

Copy link
Author

Choose a reason for hiding this comment

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

I agree, the term integer solution is somewhat vague anyhow as the integrality could refer to both the objective value as well as the integrality of the solution itself. I will change this.


/**
* Creates a new PruneNodeEvent
Expand All @@ -46,7 +46,7 @@ public class PruneNodeEvent<T extends ModelInterface, U extends AbstractColumn<T
* @param nodeBound Bound on the node
* @param bestIntegerSolution Best integer solution discovered thus far
Copy link
Collaborator

Choose a reason for hiding this comment

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

description needs update

*/
public PruneNodeEvent(Object source, BAPNode<T, U> node, double nodeBound, int bestIntegerSolution)
public PruneNodeEvent(Object source, BAPNode<T, U> node, double nodeBound, double bestIntegerSolution)
{
super(source);
this.node = node;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class StartEvent
* Best available integer solution at the start of the Branch-and-Price or Column generation
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

* procedure
**/
public final int objectiveIncumbentSolution;
public final double objectiveIncumbentSolution;

/**
* Creates a new StartEvent
Expand All @@ -43,7 +43,7 @@ public class StartEvent
* @param objectiveIncumbentSolution Best available integer 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 @@ -77,7 +77,7 @@ public class ColGen<T extends ModelInterface, U extends AbstractColumn<T, V>,
* is a maximization problem, the Colgen procedure is terminated if
* {@code floor(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.

desr needs update

**/
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

Expand Down Expand Up @@ -114,7 +114,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 +161,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 +188,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 +519,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 @@ -55,7 +55,7 @@ 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;
protected double bestIntegerSolution;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Best to rename this to: objectiveIncumbentSolution
Also, please update descr


/**
* Creates a debugger for Column Generation instances
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,8 @@ 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 if an integer solution has an integer objective. Default = true */
Copy link
Collaborator

Choose a reason for hiding this comment

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

I suggest to be a bit more verbose here:
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.

public final boolean INTEGER_OBJECTIVE;

/*
* Cut handling
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,14 @@ public void testBAPFrameworkThroughTSP()
if (inputStream == null)
Assert.fail("Cannot find problem instance!");
TSP tsp = new TSP(inputStream);
int solution = this.solveTSPInstance(tsp);
double solution = this.solveTSPInstance(tsp);
System.out.println("Solution for : " + instance + " is: " + solution);
Assert.assertEquals(solution, instances.get(instance).intValue());
Assert.assertEquals(solution, instances.get(instance).intValue(), 0.000001);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use Config.PRECISION

inputStream.close();
}
}

private int solveTSPInstance(TSP tsp)
private double solveTSPInstance(TSP tsp)
{
if (tsp.N % 2 == 1)
throw new RuntimeException(
Expand Down Expand Up @@ -146,7 +146,7 @@ private int solveTSPInstance(TSP tsp)
bap.runBranchAndPrice(System.currentTimeMillis() + 8000000L);

// Get the solution
int solution = -1;
double solution = -1;
if (bap.hasSolution()) {
assert (bap.isOptimal());
solution = bap.getObjective();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public final class Matching
**/
public final int[] succ;
/** Weighted cost of the matching **/
public final int cost;
public final double cost;

/**
* Creates a new column (matching)
Expand All @@ -51,7 +51,7 @@ public final class Matching
*/
public Matching(
String creator, boolean isVolatile, PricingProblemByColor associatedPricingProblem,
Set<DefaultWeightedEdge> edges, int[] succ, int cost)
Set<DefaultWeightedEdge> edges, int[] succ, double cost)
{
super(associatedPricingProblem, isVolatile, creator);
this.edges = edges;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public final class IndependentSet
/** Vertices in the independent set **/
public final Set<Integer> vertices;
/** Cost of this column in the objective of the Master Problem **/
public final int cost;
public final double cost;

/**
* Constructs a new column
Expand All @@ -43,7 +43,7 @@ public final class IndependentSet
*/
public IndependentSet(
ChromaticNumberPricingProblem associatedPricingProblem, boolean isVolatile,
String creator, Set<Integer> vertices, int cost)
String creator, Set<Integer> vertices, double cost)
{
super(associatedPricingProblem, isVolatile, creator);
this.vertices = vertices;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public final class Matching
**/
public final int[] succ;
/** Weighted cost of the matching **/
public final int cost;
public final double cost;

/**
* Creates a new column (matching)
Expand All @@ -51,7 +51,7 @@ public final class Matching
*/
public Matching(
String creator, boolean isVolatile, PricingProblemByColor associatedPricingProblem,
Set<DefaultWeightedEdge> edges, int[] succ, int cost)
Set<DefaultWeightedEdge> edges, int[] succ, double cost)
{
super(associatedPricingProblem, isVolatile, creator);
this.edges = edges;
Expand Down