{composition-setup} {composition-setup} {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 {{integerSum(IloCplex cplex, BiMap<T,IloIntVar> variables, Iterable<T> set)}} * From {{IloCplex}}, the method {{addEq(IloNumExpr e, double v)}} * From {{UndirectedGraph<V,E>}}, the method {{getIncidentEdges(V vertex)}} {toggle-cloak:id=ConstraintsSolution} _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|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:name=tspDegreeConstraints|align=left|size=L|version=1} {column} {section} |