Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 4.0

Problem Formulation

First, we will add the following fields to the class so we can easily reference our problem data from any method. Add

}
{section} 
{column:width=300px} 
{pagetree:root=Tutorial} 
{column} 
{column} 

h1. Problem Formulation

First, we will add the following fields to the class so we can easily reference our problem data from any method.  Add
{code}
private EnumSet<Option> options;
private IloCplex cplex;
private TspInstance<V,E> tspInstance;
private ImmutableBiMap<E,IloIntVar> edgeVariables;
{code}
and initialize them, as below.
  
{code}
	public TspIpSolver(TspInstance<V,E> tspInstance, EnumSet<Option> options) throws IloException{
		this.options = options;
		this.tspInstance = tspInstance;
		this.cplex = new IloCplex();
		UndirectedGraph<V,E> graph = tspInstance.getGraph(); //for convenience, we will be using this a lot
		this.edgeVariables = Util.makeBinaryVariables(cplex, graph.getEdges());
		//the degree constraints
		//the objective		
	}
{code}
Next, we need to add the objective and the degree constraints to the {{IloCplex}} instance.  Try adding them yourself!  The following methods (as defined in [Solver Specification] and [Java Style for CPLEX]) should be useful for making the degree constraints:
From Util, the static method

* From {{Util}}, the static method {{integerSum(IloCplex cplex, BiMap<T,IloIntVar> variables, Iterable<T> set)
From IloCplex, the method
}}
* From {{IloCplex}}, the method {{addEq(IloNumExpr e, double v)
From
}}
* From {{UndirectedGraph<V,E>}}, the method {{getIncidentEdges(V vertex)
}}

{toggle-cloak
:id
=ConstraintsSolution
Solution

For the objective, we need the functions:

  • From Util, the static method sum(IloCplex cplex, BiMap<T,IloIntVar> variables, Iterable<T> set, Function<? super T,? extends Number> coefficients)
  • From UndirectedGraph<V,E>, the method getEdges()
  • From TspInstance, the method getEdgeWeights()

Wiki Markup
{composition-setup}
{composition-setup
Section
Column
width300px
Page Tree
root15DOTs60ia13:Tutorial
Column
Code Block
Code Block
Cloak
visiblefalse
idConstraintsSolution
Toggle Cloak
idObjectiveSolution
Solution

Cloak
visiblefalse
idObjectiveSolution

Solving and Extracting the Solution

Here we fill in the blank methods solve(), getEdgesInOpt(), and getOptVal(). For performance reasons, it is a good idea to cache the solution as a field of the TspIpSolver and "shut down" CPLEX. Recall the warning that calling getValue(IloIntVar var) from IloCplex repeatedly instead of a single getValues(IloIntVar[] vars) causes performance issues. Although it isn't really good "Java style," as discussed in Java Style for CPLEX, we will keep a copy of the edge variables stored in an array as a field of the class to improve performance.

To accomplish this, first add the fields
AnchoredgeVariablesAsArrayedgeVariablesAsArray Code Block
} _Solution_ 
{cloak:id=ConstraintsSolution|visible=false}
{code}
		//the degree constraints
		for(V vertex: graph.getVertices()){
			cplex.addEq(Util.integerSum(cplex, edgeVariables, 
					graph.getIncidentEdges(vertex)), 2);
		}
{code}
{cloak}
For the objective, we need the functions:
* From {{Util}}, the static method {{sum(IloCplex cplex, BiMap<T,IloIntVar> variables, Iterable<T> set, Function<? super T,? extends Number> coefficients)}}
* From {{UndirectedGraph<V,E>}}, the method {{getEdges()}}
* From {{TspInstance}}, the method {{getEdgeWeights()}}

{toggle-cloak:id=ObjectiveSolution} _Solution_ 
{cloak:id=ObjectiveSolution|visible=false}
{code}
		//the objective
		cplex.addMinimize(Util.integerSum(
				cplex, edgeVariables, graph.getEdges(),tspInstance.getEdgeWeights()));
{code}
{cloak}

h1. Solving and Extracting the Solution

Here we fill in the blank methods {{solve()}}, {{getEdgesInOpt()}}, and {{getOptVal()}}.  For performance reasons, it is a good idea to cache the solution as a field of the TspIpSolver and "shut down" CPLEX.  Recall [the warning|Java and CPLEX#Performance Issues Reading Variable Values from CPLEX] that calling {{getValue(IloIntVar var)}} from {{IloCplex}} repeatedly instead of a single {{getValues(IloIntVar[] vars)}} causes performance issues.  Although it isn't really good "Java style," as discussed in [Java Style for CPLEX], we will keep a copy of the edge variables stored in an array as a field of the class to improve performance. 

To accomplish this, first add the fields
{anchor:edgeVariablesAsArray}
{code}
	private ImmutableSet<E> edgesInOpt;
	private double optVal;
	private IloIntVar[] edgeVariablesAsArray;
{code}
Next, we will set the field {{edgeVariablesAsArray}} by adding the one-liner to the constructor:

{code
}
		...
		this.edgeVariables = Util.makeBinaryVariables(cplex, graph.getEdges());
		edgeVariablesAsArray = edgeVariables.inverse().keySet().toArray(new IloIntVar[]{});//insert this line
		//the degree constraints
		...
{code}
We will fill in the remaining fields during {{solve()}}.  But first, we add an auxiliary method to encapsulate access to {{edgeVariablesAsArray}}, reducing the chance of error down the road.  In designing this method, we must remember our previous discussion of [numerical tolerances
. Code Block
|Java and Cplex#Reading Integer Variable Values from CPLEX].
{code}
	/**
	 * assumes  edgeVarVals[i] in {0,1}, up to some tolerance.  
	 * @param edgeVarVals
	 * @return the edges where the variable takes the value one.
	 */
	private Set<E> edgesUsed(double[] edgeVarVals){
		Set<E> ans = new HashSet<E>();
		for(int e = 0; e < edgeVarVals.length; e++){
	        if(Util.doubleToBoolean(edgeVarVals[e])){
	        	ans.add(edgeVariables.inverse().get(edgeVariablesAsArray[e]));
	        }
	    }
		return ans;
	}

	public void solve() throws IloException{
		if(!cplex.solve()){
			throw new RuntimeException();
		}
		optVal = cplex.getObjValue();
		edgesInOpt = ImmutableSet.copyOf(edgesUsed(cplex.getValues(edgeVariablesAsArray)));
		cplex.end();
	}
	
	public ImmutableSet<E> getEdgesInOpt(){
		return edgesInOpt;
	}
	
	public double getOptVal(){
		return optVal;
	}
{code}
The method {{edgesUsed()}} make look a little odd right now, but its value will become apparent in the next section.


h1. Test Your Code


Now is a good time to test the code you have written thus far.  Keep in mind that we haven't implemented the _cutset_ constraints yet.  A unit test {{testDegreeConstraints()}} was made exactly for this purpose, in _tspSolver/test/solver/TspSolverTest.java_.  Run the class as a JUnit test (right click on the class in the _Project Explorer_ and select _Run As  JUnit Test_.  If you are not on Windows, again you likely will need to set a VM argument [as previously discussed|Test Your Java, Eclipse and Cplex Installations].  The test {{testDegreeConstraints()}} should now pass.


If you need to debug, the unit test is on the following graph:
Gliffy DiagramsizeLnametspDegreeConstraintsalignleftversion1


{gliffy:name=tspDegreeConstraints|align=left|size=L|version=1}

{column} 
{section}