Skip to content

Commit

Permalink
Merge pull request #17 from blubin/feat-advanced-variables-of-interest
Browse files Browse the repository at this point in the history
feat(pool): enabling more complex sets of variables of interest, without breaking the simple functionality
  • Loading branch information
blubin committed Mar 4, 2019
2 parents 42635ce + 1f913c7 commit 3a66760
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 29 deletions.
36 changes: 29 additions & 7 deletions src/main/java/edu/harvard/econcs/jopt/solver/IMIP.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,9 @@
package edu.harvard.econcs.jopt.solver;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import edu.harvard.econcs.jopt.solver.mip.Constraint;
import edu.harvard.econcs.jopt.solver.mip.LinearTerm;
Expand Down Expand Up @@ -97,14 +96,37 @@ public interface IMIP extends Serializable {
* in combination with SOLUTION_POOL_MODE = 3 or SOLUTION_POOL_MODE = 4. They form the set
* of variables that distinguish different solutions in the context of the MIP. For example, in an auction,
* these variables could be the allocation variables.
* @param variables The variables of interest. Currently, only boolean variables are supported.
*
* @param variables The variables of interest. For SOLUTION_POOL_MODE = 3, only boolean variables are supported.
*/
void setVariablesOfInterest(Collection<Variable> variables);
default void setVariablesOfInterest(Collection<Variable> variables) {
setAdvancedVariablesOfInterest(variables
.stream()
.map(v -> Stream.of(v).collect(Collectors.toSet()))
.collect(Collectors.toSet())
);
}

/**
* Internally, the variables of interest are stored as a collection of collections, even when the user
* sets them as a single collection. A user may, however, set the variables of interest directly in this advanced
* structure. This gives, without breaking the API of a simple collection of variables, the user the possibility
* to define sets of variables that all have to be equal in their sum to be considered a duplicate.
*
* @param variableSets The sets of variables of interest. For SOLUTION_POOL_MODE = 3, only boolean variables are
* supported.
*/
void setAdvancedVariablesOfInterest(Collection<Collection<Variable>> variableSets);

/**
* @return the variables of interest if defined, else null
*/
Collection<Variable> getVariablesOfInterest();
default Collection<Variable> getVariablesOfInterest() {
if (getAdvancedVariablesOfInterest() == null) return null;
return getAdvancedVariablesOfInterest().stream().flatMap(Collection::stream).collect(Collectors.toSet());
}

Collection<Collection<Variable>> getAdvancedVariablesOfInterest();

// Proposed Variable Values:
////////////////////////////
Expand Down
42 changes: 36 additions & 6 deletions src/main/java/edu/harvard/econcs/jopt/solver/ISolution.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@

import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Comparing based on Objective Value
Expand Down Expand Up @@ -64,17 +66,45 @@ default int compareTo(ISolution o) {
return Double.compare(getObjectiveValue(), o.getObjectiveValue());
}

default boolean isDuplicate(ISolution o, Collection<Variable> variablesOfInterest) {
/**
* To take advantage of the advanced structure of variables of interest, this method checks if for all collections
* collections of variables, the sum of these variables are equal.
*
* @see IMIP#setAdvancedVariablesOfInterest(Collection)
*
* @param o The other solution
* @param variableSetsOfInterest A collection of collections of variables of interest
* @return true, if the sum of the values in each collection of variables of interest is equal; else false
*/
default boolean isDuplicateAdvanced(ISolution o, Collection<Collection<Variable>> variableSetsOfInterest) {
if (variableSetsOfInterest == null) return false;
double e = 1e-8;
for (Variable var : variablesOfInterest) {
double thisValue = this.getValue(var);
double otherValue = o.getValue(var);
if (thisValue < otherValue - e
|| thisValue > otherValue + e) {
for (Collection<Variable> variableCollection : variableSetsOfInterest) {
double thisSum = variableCollection.stream().mapToDouble(this::getValue).sum();
double otherSum = variableCollection.stream().mapToDouble(o::getValue).sum();
if (thisSum < otherSum - e
|| thisSum > otherSum + e) {
return false;
}
}
return true;
}

/**
* Given a collection of variables of interest, this method checks if this solution is a duplicate to another
* solution
*
* @param o The other solution
* @param variablesOfInterest A single collection of variables of interest
* @return true, if this solution has the same value for all variables of interest; else false
*/
default boolean isDuplicate(ISolution o, Collection<Variable> variablesOfInterest) {
if (variablesOfInterest == null) return false;
return isDuplicateAdvanced(o,
variablesOfInterest
.stream()
.map(v -> Stream.of(v).collect(Collectors.toSet()))
.collect(Collectors.toSet()));
}

}
8 changes: 5 additions & 3 deletions src/main/java/edu/harvard/econcs/jopt/solver/mip/MIP.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public class MIP implements IMIP, Serializable, Cloneable {
private Collection<QuadraticTerm> quadraticObjectiveTerms = null;
private boolean isMax;
private Map<SolveParam, Object> solveParams = new HashMap<>();
private Collection<Variable> variablesOfInterest = null;
private Collection<Collection<Variable>> variablesOfInterest = null;

public MIP() {
resetDefaultSolveParams();
Expand Down Expand Up @@ -119,11 +119,13 @@ public void remove(Variable var) {
}
}

public Collection<Variable> getVariablesOfInterest() {
@Override
public Collection<Collection<Variable>> getAdvancedVariablesOfInterest() {
return variablesOfInterest;
}

public void setVariablesOfInterest(Collection<Variable> variablesOfInterest) {
@Override
public void setAdvancedVariablesOfInterest(Collection<Collection<Variable>> variablesOfInterest) {
this.variablesOfInterest = variablesOfInterest;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,10 +297,6 @@ private IMIPResult solveMip(IMIP mip, IloCplex cplex, Map<String, IloNumVar> var
cplex.setParam(DoubleParam.TimeLimit, 1e75);

double solutionPoolMultiplier = mip.getDoubleSolveParam(SolveParam.SOLUTION_POOL_MODE_4_MULTIPLIER, 2d);
boolean complexProblem = false;
if (mip.getVariablesOfInterest() != null && mip.getVariablesOfInterest().size() < mip.getNumVars()) {
complexProblem = true;
}
int finalSolutionPoolCapacity = mip.getIntSolveParam(SolveParam.SOLUTION_POOL_CAPACITY);
cplex.setParam(IntParam.SolnPoolCapacity, 2100000000);
cplex.setParam(IntParam.SolnPoolIntensity, 4);
Expand All @@ -325,11 +321,9 @@ private IMIPResult solveMip(IMIP mip, IloCplex cplex, Map<String, IloNumVar> var

logger.debug("Start of round {}.", count + 1);
printPool(cplex);
if (complexProblem) {
clearDuplicates(mip, vars, cplex);
logger.debug("After clearing duplicates in round {}.", count + 1);
printPool(cplex);
}
clearDuplicates(mip, vars, cplex);
logger.debug("After clearing duplicates in round {}.", count + 1);
printPool(cplex);
truncatePool(mip, cplex);
logger.debug("After truncating pool in round {}.", count + 1);
printPool(cplex);
Expand Down Expand Up @@ -382,9 +376,7 @@ private IMIPResult solveMip(IMIP mip, IloCplex cplex, Map<String, IloNumVar> var
}
count++;
}
if (complexProblem) {
clearDuplicates(mip, vars, cplex);
}
clearDuplicates(mip, vars, cplex);
truncatePool(mip, cplex);
logger.debug("Pool filled. Made {} refinement(s).", count);

Expand Down Expand Up @@ -517,6 +509,7 @@ private void truncatePool(IMIP mip, IloCplex cplex) {
}

private void clearDuplicates(IMIP mip, Map<String, IloNumVar> vars, IloCplex cplex) {
if (mip.getAdvancedVariablesOfInterest() == null) return;
List<Integer> duplicateSolutions = new ArrayList<>();
try {
for (int i = 0; i < cplex.getSolnPoolNsolns() - 1; ++i) {
Expand All @@ -525,7 +518,7 @@ private void clearDuplicates(IMIP mip, Map<String, IloNumVar> vars, IloCplex cpl
for (int j = i + 1; j < cplex.getSolnPoolNsolns(); ++j) {
if (!duplicateSolutions.contains(j)) {
PoolSolution jSol = extractSolution(cplex, vars, j);
if (iSol.isDuplicate(jSol, mip.getVariablesOfInterest())) {
if (iSol.isDuplicateAdvanced(jSol, mip.getAdvancedVariablesOfInterest())) {
duplicateSolutions.add(j);
}
}
Expand Down
68 changes: 68 additions & 0 deletions src/test/java/edu/harvard/econcs/jopt/CplexTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import org.junit.rules.Timeout;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.junit.Assert.*;

Expand Down Expand Up @@ -107,6 +109,72 @@ public void testDuplicateCheckFalse() {
assertFalse(first.isDuplicate(second, variablesOfInterest));
}

@Test
public void testDuplicateAdvancedCheckTrue() {
Map<String, Double> poolValuesFirst = new HashMap<>();
poolValuesFirst.put("A1", 1.0);
poolValuesFirst.put("A2", 0.0);
poolValuesFirst.put("B", 2.0);
poolValuesFirst.put("C", 3.0);

ISolution first = new PoolSolution(10.0, poolValuesFirst);

Map<String, Double> poolValuesSecond = new HashMap<>();
poolValuesSecond.put("A1", 0.0);
poolValuesSecond.put("A2", 1.0);
poolValuesSecond.put("B", 2.0);
poolValuesSecond.put("C", 3.0);

ISolution second = new PoolSolution(10.0, poolValuesSecond);

Collection<Collection<Variable>> variableSetsOfInterest = new HashSet<>();
Collection<Variable> setA = Stream
.of(new Variable("A1", VarType.INT, 0, MIP.MAX_VALUE),
new Variable("A2", VarType.INT, 0, MIP.MAX_VALUE))
.collect(Collectors.toSet());
variableSetsOfInterest.add(setA);
variableSetsOfInterest.add(
Stream.of(new Variable("B", VarType.INT, 0, MIP.MAX_VALUE)).collect(Collectors.toSet())
);
variableSetsOfInterest.add(
Stream.of(new Variable("C", VarType.INT, 0, MIP.MAX_VALUE)).collect(Collectors.toSet())
);
assertTrue(first.isDuplicateAdvanced(second, variableSetsOfInterest));
}

@Test
public void testDuplicateAdvancedCheckFalse() {
Map<String, Double> poolValuesFirst = new HashMap<>();
poolValuesFirst.put("A1", 1.0);
poolValuesFirst.put("A2", 2.0);
poolValuesFirst.put("B", 2.0);
poolValuesFirst.put("C", 3.0);

ISolution first = new PoolSolution(10.0, poolValuesFirst);

Map<String, Double> poolValuesSecond = new HashMap<>();
poolValuesSecond.put("A1", 1.0);
poolValuesSecond.put("A2", 2.0);
poolValuesSecond.put("B", 2.0);
poolValuesSecond.put("C", 3.0);

ISolution second = new PoolSolution(10.0, poolValuesSecond);

Collection<Collection<Variable>> variableSetsOfInterest = new HashSet<>();
Collection<Variable> setA = Stream
.of(new Variable("A1", VarType.INT, 0, MIP.MAX_VALUE),
new Variable("A2", VarType.INT, 0, MIP.MAX_VALUE))
.collect(Collectors.toSet());
variableSetsOfInterest.add(setA);
variableSetsOfInterest.add(
Stream.of(new Variable("B", VarType.INT, 0, MIP.MAX_VALUE)).collect(Collectors.toSet())
);
variableSetsOfInterest.add(
Stream.of(new Variable("C", VarType.INT, 0, MIP.MAX_VALUE)).collect(Collectors.toSet())
);
assertTrue(first.isDuplicateAdvanced(second, variableSetsOfInterest));
}

@Test
public void testSolutionPoolMode3() {
testSolutionPoolMode3(2);
Expand Down

0 comments on commit 3a66760

Please sign in to comment.