cplex-Branch and Bound algorithm calls cplex to solve VRPTW

Keywords: Java Programming Windows

Previously, we talked about Cplex's direct solution model for VRPTW, and now we call Cplex in the branch and bound algorithm to solve VRPTW.
1. Branch and Bound Algorithms
(1) Definition:
(2) Solving process:
1) Determine a lower bound (initial solution LB) defined as infinite UB
2) Build a node to join the priority queue for the initial problem
3) Determine if the queue is empty, if it is empty jump to 7, otherwise take out and eject the first element of the queue and calculate the target value of the node P
4) If P > UB, return 3.Otherwise, determine if the current node is a legal solution (for any i, j, k, x_i J K are integers), if so, jump 5 or jump 6
5) If P < UB, record UB = P, the current node is the current best solution BS.Return 3
6) Set up two child nodes L, R.L, R is created as above, if L's target value is L.P <= UB, L is queued, and if R's target value is R.P <= UB, R is queued.Return 3
7) End, return the optimal node BS of the record.No solution if BS is empty

2. Branch and Bound Solving VRPTW
The code structure is as follows

The purpose of a Data class is to define variables, initialize variables, and read data.

package com.chb;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Scanner;

//Define parameters
class Data{
	public static final double gap= 1e-6;
	public static final double big_num = 100000;
	public static int pointnum=102;			//All point sets n (including distribution centers and customer points, starting and ending (0 and n) as distribution centers)
	public static double E;	      		//Distribution Center Time Window Start Time
	public static double L;	     		//Distribution Center Time Window End Time
	public static int carnum;    		//Number of vehicles
	public static double cap;     		//Vehicle Load
	public static int[][] point=new int[pointnum][2];;		//Coordinates x,y for all points
	public static int[] demand=new int[pointnum];			//Requirement
	public static int[] car=new int[carnum];			//Vehicle number
	public static double[] a=new double[pointnum];				//Time window start time [a[i],b[i]]
	public static double[] b=new double[pointnum];				//Time window end time [a[i],b[i]]
	public static double[] s=new double[pointnum];				//Service time at customer point
	public static int[][] arcs=new int[pointnum][pointnum];			//arcs[i][j] represents an arc from I to j
	public static double[][] dist=new double[pointnum][pointnum];		//Distance matrix, satisfies trigonometric relationships, temporary distance means cost C[i][j]=dist[i][j]
	
	//Truncate Decimal 3.26434-->3.2
	public double double_truncate(double v){
		int iv = (int) v;
		if(iv+1 - v <= gap)
			return iv+1;
		double dv = (v - iv) * 10;
		int idv = (int) dv;
		double rv = iv + idv / 10.0;
		return rv;
	}	
	public Data() {
		super();
	}
	//Function function: Read data from txt file and initialize parameters
	public void read_data(String path,Data data) throws Exception{
		String line = null;
		String[] substr = null;
		Scanner cin = new Scanner(new BufferedReader(new FileReader(path)));  //read file
		//Read 1-4 lines
		for(int i =0; i < 4;i++){
			line = cin.nextLine();  
		}
		//Read 5 lines
		line = cin.nextLine();
		line.trim(); 
		substr = line.split(("\\s+")); 
		carnum = Integer.parseInt(substr[1]); 
		cap = Integer.parseInt(substr[2]);
		//Read 6-9 lines
		for(int i =0; i < 4;i++){
			line = cin.nextLine();
		}
		//Read pointnum-1 row data
		for (int i = 0; i < pointnum - 1; i++) {
			line = cin.nextLine();
			line.trim();
			substr = line.split("\\s+");
			point[i][0] = Integer.parseInt(substr[2]);
			point[i][1] = Integer.parseInt(substr[3]);
			demand[i] = Integer.parseInt(substr[4]);
			a[i] = Integer.parseInt(substr[5]);
			b[i] = Integer.parseInt(substr[6]);
			s[i] = Integer.parseInt(substr[7]);
		}
		cin.close();//Close Stream
		//Initialize endpoint parameters
		point[pointnum-1] = point[0];
		demand[pointnum-1] = 0;
		a[pointnum-1] = a[0];
		b[pointnum-1] = b[0];
		E = a[0];
		L = b[0];
		s[pointnum-1] = 0;		
		double min1 = 1e15;
		double min2 = 1e15;
		//Distance Matrix Initialization
		for (int i = 0; i < pointnum; i++) {
			for (int j = 0; j < pointnum; j++) {
				if (i == j) {
					dist[i][j] = 0;
					continue;
				}
				dist[i][j] = 
					Math.sqrt(Math.pow(point[i][0]-point[j][0], 2)+Math.pow(point[i][1]-point[j][1], 2));
				dist[i][j]=double_truncate(dist[i][j]);
			}
		}
		dist[0][pointnum-1] = 0;
		dist[pointnum-1][0] = 0;
		//Distance Matrix Satisfies Triangular Relation
		for (int  k = 0; k < pointnum; k++) {
			for (int i = 0; i < pointnum; i++) {
				for (int j = 0; j < pointnum; j++) {
					if (dist[i][j] > dist[i][k] + dist[k][j]) {
						dist[i][j] = dist[i][k] + dist[k][j];
					}
				}
			}
		}
		//Initialize to Full Graph
		for (int i = 0; i < pointnum; i++) {
			for (int j = 0; j < pointnum; j++) {
				if (i != j) {
					arcs[i][j] = 1;
				}
				else {
					arcs[i][j] = 0;
				}
			}
		}
		//Remove edges that do not meet time window and capacity constraints
		for (int i = 0; i < pointnum; i++) {
			for (int j = 0; j < pointnum; j++) {
				if (i == j) {
					continue;
				}
				if (a[i]+s[i]+dist[i][j]>b[j] ||
						demand[i]+demand[j]>cap) {
					arcs[i][j] = 0;
				}
				if (a[0]+s[i]+dist[0][i]+dist[i][pointnum-1]>
				b[pointnum-1]) {
					System.out.println("the calculating example is false");
					
				}
			}
		}
		for (int i = 1; i < pointnum-1; i++) {
			if (b[i] - dist[0][i] < min1) {
				min1 = b[i] - dist[0][i];
			}
			if (a[i] + s[i] + dist[i][pointnum-1] < min2) {
				min2 = a[i] + s[i] + dist[i][pointnum-1];
			}
		}
		if (E > min1 || L < min2) {
			System.out.println("Duration false!");
			System.exit(0);//Terminate program
		}
		//Initialize parameters for 0, n+1 points of the distribution center
		arcs[pointnum-1][0] = 0;
		arcs[0][pointnum-1] = 1;
		for (int i = 1; i < pointnum-1; i++) {
			arcs[pointnum-1][i] = 0;
		}
		for (int i = 1; i < pointnum-1; i++) {
			arcs[i][0] = 0;
		}
	}
}

The primary purpose of the Node class is to record branch nodes

package com.chb;

import java.util.ArrayList;

public class Node implements Comparable{
	Data data;
	int d;
	double node_cost;				//Target value object
	double[][][]lp_x;//Record lp solution
	int[][][] node_x_map;//When node_xij=1, node_x_mapijk=1 means it must be accessed, and node_x_mapijk=0 means it cannot be accessed
	int[][] node_x;//0 means the arc is accessible, 1 means it must be accessed, and -1 means it cannot be accessed
	ArrayList<ArrayList<Integer>> node_routes;		//Define Vehicle Route Chain List
	ArrayList<ArrayList<Double>> node_servetimes;	//Define a time-consuming list
	
	public Node(Data data) {
		super();
		this.data = data;
		node_cost = data.big_num;
		lp_x = new double [data.pointnum][data.pointnum][data.carnum];
		node_x_map = new int[data.pointnum][data.pointnum][data.carnum];
		node_x = new int[data.pointnum][data.pointnum];
		node_routes = new ArrayList<ArrayList<Integer>>();
		node_servetimes = new ArrayList<ArrayList<Double>>();
	}
	//Duplicate node
	@SuppressWarnings("unchecked")
	public Node note_copy() {
		Node new_node = new Node(data);
		new_node.d = d;
		new_node.node_cost = node_cost;
		for (int i = 0; i < lp_x.length; i++) {
			for (int j = 0; j < lp_x[i].length; j++) {
				new_node.lp_x[i][j] = lp_x[i][j].clone();
			}
		}
		for (int i = 0; i < node_x.length; i++) {
			new_node.node_x[i] = node_x[i].clone();
		}
		for (int i = 0; i < node_x_map.length; i++) {
			for (int j = 0; j < node_x_map[i].length; j++) {
				new_node.node_x_map[i][j] = node_x_map[i][j].clone();
			}
		}
		for (int i = 0; i < node_routes.size(); i++) {
			new_node.node_routes.add((ArrayList<Integer>) node_routes.get(i).clone());
		}
		for (int i = 0; i < node_servetimes.size(); i++) {
			new_node.node_servetimes.add((ArrayList<Double>) node_servetimes.get(i).clone());
		}
		return new_node;
	}
	
	public int compareTo(Object o){
		Node node = (Node) o;
		if(node_cost < node.node_cost)
			return -1;
		else if(node_cost == node.node_cost)
			return 0;
		else
			return 1;
	}
}

The BaB_Vrptw class is the main function of the program, where
The function init() is used to determine the minimum number of vehicles with a legal solution by building a relaxed cplex model and calculating the number of vehicles used. If aa vehicles are not used, reduce aa available vehicles; otherwise, reduce one until there is no feasible solution.Of course, the fewest vehicles are available in the end, and the relaxed model is one that removes the x_ijk integer constraint from the previous model
branch and bound process: By relaxing the mathematical model of VRPTW into a linear programming problem, a lower bound of the VRPTW problem can be solved. The principle of branching is that for a selected x_ijk with 0<x_ijk<1, then the x_ijk can be divided into two branches, the left branch cannot walk the arc ij, the right branch must walk the arc ij and mustVehicle k must pass by.That is, the left branch has x_ijt = 0 for any t.On the right is x_ijk = 1, and the arc to branch is found by the find_arc() function

package com.chb;
import java.util.ArrayList;
import java.util.PriorityQueue;

import ilog.concert.IloException;
import ilog.concert.IloNumExpr;
import ilog.concert.IloNumVar;
import ilog.concert.IloNumVarType;
import ilog.cplex.IloCplex;

//Class functions: building models and solving
public class BaB_Vrptw {
	Data data;					//Object defining class Data
	Node node1;
	Node node2;
	int deep;//depth
	public PriorityQueue<Node> queue;//Branch Queue
	Node best_note;//Current best branch
	double cur_best;//Best solution
	int []record_arc;//Record nodes that need branching
	double x_gap;//Very small number
	IloCplex model;				//Object defining cplex internal class		
	public IloNumVar[][][] x;	//x[i][j][k] means that arcs[i][j] are accessed by vehicle K
	public IloNumVar[][] w;		//Time Matrix for Vehicle Access to All Points
	double cost;				//Target value object
	double[][][] x_map;//cplex parameter x
	ArrayList<ArrayList<Integer>> routes;		//Define Vehicle Route Chain List
	ArrayList<ArrayList<Double>> servetimes;	//Define a time-consuming list
	
	public BaB_Vrptw(Data data) {
		this.data = data;
		x_gap = data.gap;
		routes = new ArrayList<>();		//Define Vehicle Route Chain List
		servetimes = new ArrayList<>();	//Define a time-consuming list
		//Initialize vehicle routing and time-consuming chain table with length k of vehicles
		for (int k = 0; k < data.carnum; k++) {
			ArrayList<Integer> r = new ArrayList<>();	
			ArrayList<Double> t = new ArrayList<>();	
			routes.add(r);							
			servetimes.add(t);							
		}
		x_map = new double[data.pointnum][data.pointnum][data.carnum];
	}
	public void clear_lp() {
		data=null;
		routes.clear();
		servetimes.clear();
		x_map=null;
	}
	//Helper lp Solve to node
	@SuppressWarnings("unchecked")
	public void copy_lp_to_node(BaB_Vrptw lp, Node node) {
		node.node_routes.clear();
		node.node_servetimes.clear();
		node.node_cost = lp.cost;
		for (int i = 0; i < lp.x_map.length; i++) {
			for (int j = 0; j < lp.x_map[i].length; j++) {
				node.lp_x[i][j] = lp.x_map[i][j].clone();
			}
		}
		for (int i = 0; i < lp.routes.size(); i++) {
			node.node_routes.add((ArrayList<Integer>) lp.routes.get(i).clone());
		}
		for (int i = 0; i < lp.servetimes.size(); i++) {
			node.node_servetimes.add((ArrayList<Double>) lp.servetimes.get(i).clone());
		}
	}
	//Function function: Establish the cplex model of VRPTW according to the mathematical model of VRPTW
	//Modeling
	private void build_model() throws IloException {
		//model
		model = new IloCplex();
		model.setOut(null);
//		model.setParam(IloCplex.DoubleParam.EpOpt, 1e-9);
//		model.setParam(IloCplex.DoubleParam.EpGap, 1e-9);
		//variables
		x = new IloNumVar[data.pointnum][data.pointnum][data.carnum];
		w = new IloNumVar[data.pointnum][data.carnum];				//Time of Vehicle Access Point
		//Define the data type and range of cplex variables x and w
		for (int i = 0; i < data.pointnum; i++) {
			for (int k = 0; k < data.carnum; k++) {
		w[i][k] = model.numVar(0, 1e15, IloNumVarType.Float, "w" + i + "," + k);
			}
			for (int j = 0; j < data.pointnum; j++) {
				if (data.arcs[i][j]==0) {
					x[i][j] = null;
				}
				else{
					//Xijk, Formula (10) -(11)
					for (int k = 0; k < data.carnum; k++) {
	x[i][j][k] = model.numVar(0, 1, IloNumVarType.Float, "x" + i + "," + j + "," + k);
					}
				}
			}
		}
		//Join objective function
		//Formula (1)
		IloNumExpr obj = model.numExpr();
		for(int i = 0; i < data.pointnum; i++){
			for(int j = 0; j < data.pointnum; j++){
				if (data.arcs[i][j]==0) {
					continue;
				}
				for(int k = 0; k < data.carnum; k++){
					obj = model.sum(obj, model.prod(data.dist[i][j], x[i][j][k]));
				}
			}
		}
		model.addMinimize(obj);
		//Join Constraint
		//Formula (2)
		for(int i= 1; i < data.pointnum-1;i++){
			IloNumExpr expr1 = model.numExpr();
			for (int k = 0; k < data.carnum; k++) {
				for (int j = 1; j < data.pointnum; j++) {
					if (data.arcs[i][j]==1) {
						expr1 = model.sum(expr1, x[i][j][k]);
					}
				}
			}
			model.addEq(expr1, 1);
		}
		//Formula (3)
		for (int k = 0; k < data.carnum; k++) {
			IloNumExpr expr2 = model.numExpr();
			for (int j = 1; j < data.pointnum; j++) {
				if (data.arcs[0][j]==1) {
					expr2 = model.sum(expr2, x[0][j][k]);
				}
			}
			model.addEq(expr2, 1);
		}
		//Formula (4)
		for (int k = 0; k < data.carnum; k++) {
			for (int j = 1; j < data.pointnum-1; j++) {
				IloNumExpr expr3 = model.numExpr();
				IloNumExpr subExpr1 = model.numExpr();
				IloNumExpr subExpr2 = model.numExpr();
				for (int i = 0; i < data.pointnum; i++) {
					if (data.arcs[i][j]==1) {
						subExpr1 = model.sum(subExpr1,x[i][j][k]);
					}
					if (data.arcs[j][i]==1) {
						subExpr2 = model.sum(subExpr2,x[j][i][k]);
					}
				}
				expr3 = model.sum(subExpr1,model.prod(-1, subExpr2));
				model.addEq(expr3, 0);
			}
		}
		//Formula (5)
		for (int k = 0; k < data.carnum; k++) {
			IloNumExpr expr4 = model.numExpr();
			for (int i = 0; i < data.pointnum-1; i++) {
				if (data.arcs[i][data.pointnum-1]==1) {
					expr4 = model.sum(expr4,x[i][data.pointnum-1][k]);
				}
			}
			model.addEq(expr4, 1);
		}
		//Formula (6)
		double M = 1e5;
		for (int k = 0; k < data.carnum; k++) {
			for (int i = 0; i < data.pointnum; i++) {
				for (int j = 0; j < data.pointnum; j++) {
					if (data.arcs[i][j] == 1) {
						IloNumExpr expr5 = model.numExpr();
						IloNumExpr expr6 = model.numExpr();
						expr5 = model.sum(w[i][k], data.s[i]+data.dist[i][j]);
						expr5 = model.sum(expr5,model.prod(-1, w[j][k]));
						expr6 = model.prod(M,model.sum(1,model.prod(-1, x[i][j][k])));
						model.addLe(expr5, expr6);
					}
				}
			}
		}
		//Formula (7)
		for (int k = 0; k < data.carnum; k++) {
			for (int i = 1; i < data.pointnum-1; i++) {
				IloNumExpr expr7 = model.numExpr();
				for (int j = 0; j < data.pointnum; j++) {
					if (data.arcs[i][j] == 1) {
						expr7 = model.sum(expr7,x[i][j][k]);
					}
				}
				model.addLe(model.prod(data.a[i], expr7), w[i][k]);
				model.addLe(w[i][k], model.prod(data.b[i], expr7));
			}
		}
		//Formula (8)
		for (int k = 0; k < data.carnum; k++) {
			model.addLe(data.E, w[0][k]);
			model.addLe(data.E, w[data.pointnum-1][k]);
			model.addLe(w[0][k], data.L);
			model.addLe(w[data.pointnum-1][k], data.L);
		}
		//Formula (9)
		for (int k = 0; k < data.carnum; k++) {
			IloNumExpr expr8 = model.numExpr();
			for (int i = 1; i < data.pointnum-1; i++) {
				IloNumExpr expr9 = model.numExpr();
				for (int j = 0; j < data.pointnum; j++) {
					if (data.arcs[i][j] == 1) {
						expr9=model.sum(expr9,x[i][j][k]);
					}
				}
				expr8 = model.sum(expr8,model.prod(data.demand[i],expr9));
			}
			model.addLe(expr8, data.cap);
		}
	}
	//Functional functions: Solve the model, generate vehicle routes, and get target values
	//Get cplex solution
	public void get_value() throws IloException {
		routes.clear();
		servetimes.clear();
		cost = 0;
//		//Initialize vehicle routing and time-consuming chains, length of which is number of vehicles k
		for (int k = 0; k < data.carnum; k++) {
			ArrayList<Integer> r = new ArrayList<>();	
			ArrayList<Double> t = new ArrayList<>();	
			routes.add(r);							
			servetimes.add(t);							
		}
		for (int i = 0; i < data.pointnum; i++) {
			for (int j = 0; j < data.pointnum; j++) {
				for (int k = 0; k < data.carnum; k++) {
					x_map[i][j][k] = 0.0;
				}
				if (data.arcs[i][j]>0.5) {
					for (int k = 0; k < data.carnum; k++) {
						x_map[i][j][k]=model.getValue(x[i][j][k]);
					}
				}
			}
		}
		//Model solvable to generate vehicle routes
		for(int k = 0; k < data.carnum; k++){
			boolean terminate = true;
			int i = 0;
			routes.get(k).add(0);		
			servetimes.get(k).add(0.0);
			while(terminate){
				for (int j = 0; j < data.pointnum; j++) {
					if (doubleCompare(x_map[i][j][k], 0)==1) {
						routes.get(k).add(j);
						servetimes.get(k).add(model.getValue(w[j][k]));
						i = j;
						break;
					}
				}
				if (i == data.pointnum-1) {
					terminate = false;
				}
			}
		}
		cost = model.getObjValue();
	}
	//branch and bound process
	public void branch_and_bound(BaB_Vrptw lp) throws IloException {
		cur_best = 3000;//Set Up Last
		deep=0;
		record_arc = new int[3];
		node1 = new Node(data);
		best_note = null;
		queue = new PriorityQueue<Node>();
		//Initial solution (illegal solution)
		for (int i = 0; i < lp.routes.size(); i++) {
			ArrayList<Integer> r = lp.routes.get(i);
			System.out.println();
			for (int j = 0; j < r.size(); j++) {
				System.out.print(r.get(j)+" ");
			}
		}
		lp.copy_lp_to_node(lp, node1);
//		node1.node_cost = lp.cost;
//		node1.lp_x = lp.x_map.clone();
//		node1.node_routes =lp.routes;
//		node1.node_servetimes = lp.servetimes;
		node2 = node1.note_copy();
		deep=0;
		node1.d=deep;
		queue.add(node1);
		//branch and bound process
		while (!queue.isEmpty()) {
			Node node = queue.poll();
			//A branch of optimal solution is larger than the current best feasible solution, delete
			if (doubleCompare(node.node_cost, cur_best)>0) {
				continue;
			}else {
				record_arc = lp.find_arc(node.lp_x);
				//The legal solution of a branch, the solution of 0,1 combination, the best solution for the current branch
				if (record_arc[0]==-1) {
					//Better than the current best solution, update the current solution
					if (doubleCompare(node.node_cost, cur_best)==-1) {
						lp.cur_best = node.node_cost;
						System.out.println(node.d+"  cur_best:"+cur_best);
						lp.best_note = node;
					}
					continue;
				}else {//Can branch
					node1 = lp.branch_left_arc(lp, node, record_arc);//Left Branch
					node2 = lp.branch_right_arc(lp, node, record_arc);//Right Branch
					if (node1!=null && doubleCompare(node1.node_cost, cur_best)<=0) {
						queue.add(node1);
					}
					if (node2!=null && doubleCompare(node2.node_cost, cur_best)<=0) {
						queue.add(node2);
					}
				}
			}
		}
	}
	//Branch Settings
	public void set_bound(Node node) throws IloException {
		for (int i = 0; i < data.pointnum; i++) {
			for (int j = 0; j < data.pointnum; j++) {
				if (data.arcs[i][j]>0.5) {
					if (node.node_x[i][j]==0) {
						for (int k = 0; k < data.carnum; k++) {
							x[i][j][k].setLB(0.0);
							x[i][j][k].setUB(1.0);
						}
					}else if (node.node_x[i][j]==-1) {
						for (int k = 0; k < data.carnum; k++) {
							x[i][j][k].setLB(0.0);
							x[i][j][k].setUB(0.0);
						}
					}else {
						for (int k = 0; k < data.carnum; k++) {
							if (node.node_x_map[i][j][k]==1) {
								x[i][j][k].setLB(1.0);
								x[i][j][k].setUB(1.0);
							}else {
								x[i][j][k].setLB(0.0);
								x[i][j][k].setUB(0.0);
							}
						}
					}
				}
			}
		}
	}
	public void set_bound1(Node node) throws IloException {
		for (int i = 0; i < data.pointnum; i++) {
			for (int j = 0; j < data.pointnum; j++) {
				if (data.arcs[i][j]>0.5) {
					for (int k = 0; k < data.carnum; k++) {
						if (node.node_x_map[i][j][k]==0) {
							x[i][j][k].setLB(0.0);
							x[i][j][k].setUB(1.0);
						}else if (node.node_x_map[i][j][k]==-1) {
							x[i][j][k].setLB(0.0);
							x[i][j][k].setUB(0.0);
						}else {
							x[i][j][k].setLB(1.0);
							x[i][j][k].setUB(1.0);
						}
					}
				}
			}
		}
	}
	//Set Left Branch
	public Node branch_left_arc(BaB_Vrptw lp,Node father_node,int[] record) throws IloException {
		if (record[0] == -1) {
			return null;
		}
		Node new_node = new Node(data);
		new_node = father_node.note_copy();
		new_node.node_x[record[0]][record[1]] = -1;
		for (int k = 0; k < data.carnum; k++) {
			new_node.node_x_map[record[0]][record[1]][k]=0;
		}
//		new_node.node_x_map[record[0]][record[1]][record[2]]=-1;
		//Set Left Branch
		lp.set_bound(new_node);
		
		if (lp.model.solve()) {
			lp.get_value();
			deep++;
			new_node.d=deep;
			lp.copy_lp_to_node(lp, new_node);
			System.out.println(new_node.d+" "+lp.cost);
		}else {
			new_node.node_cost = data.big_num;
		}
		return new_node;
}
	//Set Right Branch
	public Node branch_right_arc(BaB_Vrptw lp,Node father_node,int[] record) throws IloException {
		if (record[0] == -1) {
			return null;
		}
		Node new_node = new Node(data);
		new_node = father_node.note_copy();
		new_node.node_x[record[0]][record[1]] = 1;
//		new_node.node_x_map[record[0]][record[1]][record[2]]=1;
		for (int k = 0; k < data.carnum; k++) {
			if (k==record[2]) {
				new_node.node_x_map[record[0]][record[1]][k]=1;
			}else {
				new_node.node_x_map[record[0]][record[1]][k]=0;
			}
		}
		//Set Right Branch
		lp.set_bound(new_node);
		if (lp.model.solve()) {
			lp.get_value();
			deep++;
			new_node.d=deep;
			System.out.println(new_node.d+" right: "+lp.cost);
			lp.copy_lp_to_node(lp, new_node);
		}else {
			new_node.node_cost = data.big_num;
		}
		return new_node;
	}
	//Find pivot locations that need branching
	public int[] find_arc1(double[][][] x) {
		int record[] = new int[3];//Record branch vertices
		double cur_dif = 0;
		double min_dif = Double.MAX_VALUE;
		//Find the arc closest to 0.5
		for (int i = 1; i <data.pointnum-1; i++) {
			for (int j = 1; j < data.pointnum-1; j++) {
				if (data.arcs[i][j]>0.5) {
					for (int k = 0; k <data.carnum; k++) {
						//If the arc value is 0 or 1, continue
						if (is_one_zero(x[i][j][k])) {
							continue;
						}
						cur_dif = get_dif(x[i][j][k]);
						if (doubleCompare(cur_dif, min_dif)==-1) {
							record[0] = i;
							record[1] = j;
							record[2] = k;
							min_dif = cur_dif;
						}
					}
				}
			}
		}
		//depot
		if (doubleCompare(min_dif, Double.MAX_VALUE)==0) {
			for (int i = 1; i < data.pointnum-1; i++) {
				if (data.arcs[0][i]>0.5) {
					for (int k = 0; k < data.carnum; k++) {
						if (is_fractional(x[0][i][k])) {
							cur_dif = get_dif(x[0][i][k]);
							if (doubleCompare(cur_dif, min_dif)==-1) {
								record[0] = 0;
								record[1] = i;
								record[2] = k;
								min_dif = cur_dif;
							}
						}
					}
				}
				if (data.arcs[i][data.pointnum-1]>0.5) {
					for (int k = 0; k < data.carnum; k++) {
						if (is_fractional(x[i][data.pointnum-1][k])) {
							cur_dif = get_dif(x[i][data.pointnum-1][k]);
							if (doubleCompare(cur_dif, min_dif)==-1) {
								record[0] = i;
								record[1] = data.pointnum-1;
								record[2] = k;
								min_dif = cur_dif;
							}
						}
					}
				}
			}
		}
		if (doubleCompare(min_dif, data.big_num)==1) {
			record[0] = -1;
			record[1] = -1;
			record[2] = -1;
		}
		return record;
	}
//	Find the arc to branch
	public int[] find_arc(double[][][] x) {
		int record[] = new int[3];//Record branch vertices
		for (int i = 0; i <data.pointnum; i++) {
			for (int j = 0; j < data.pointnum; j++) {
				if (data.arcs[i][j]>0.5) {
					for (int k = 0; k <data.carnum; k++) {
						//If the arc value is 0 or 1, continue
						if (is_one_zero(x[i][j][k])) {
							continue;
						}
//						cur_dif = get_dif(x[i][j][k]);
						record[0] = i;
						record[1] = j;
						record[2] = k;
						return record;
					}
				}
			}
		}
		record[0] = -1;
		record[1] = -1;
		record[2] = -1;
		return record;
	}
	//Compare the size of two double s
	public int doubleCompare(double a, double b){
		if(a - b > x_gap)
			return 1;
		if(b - a > x_gap)
			return -1;		
		return 0;
	} 
	//Decimal number between 0 and 1
	public boolean is_fractional(double v){
		if( v > (int) v + x_gap && v < (int) v + 1 - x_gap)
			return true;
		else
			return false;
	}
	//Determine whether 0 or 1
	public boolean is_one_zero(double temp) {
		if (doubleCompare(temp, 0)==0 || doubleCompare(temp, 1)==0) {
			return true;
		}else {
			return false;
		}
	}
	//Get a distance of 0.5
	public double get_dif(double temp) {
		double v = (int)temp+0.5;
		if (v>temp) {
			return v-temp;
		} else {
			return temp-v;
		}
	}
	public BaB_Vrptw init(BaB_Vrptw lp) throws IloException {
		lp.build_model();
		if (lp.model.solve()) {
			lp.get_value();
			int aa=0;
			for (int i = 0; i < lp.routes.size(); i++) {
				if (lp.routes.get(i).size()==2) {
					aa++;
				}
			}
			System.out.println(aa);
			if (aa==0) {
				data.carnum -=1;
				lp.model.clearModel();
				lp = new BaB_Vrptw(data);
				return init(lp);
			}else {
				data.carnum -=aa;
				lp.model.clearModel();
				lp = new BaB_Vrptw(data);
				return init(lp);
			}
		}else {
			data.carnum +=1;
			System.out.println("vehicle number: "+data.carnum);
			lp.model.clearModel();
			lp = new BaB_Vrptw(data);
			lp.build_model();
			if (lp.model.solve()) {
				lp.get_value();
				return lp;
			}else {
				System.out.println("error init");
				return null;
			}
		}
	}
	public static void main(String[] args) throws Exception {
		Data data = new Data();
		//Manually modify the vetexnum parameter before reading in different files. The parameter value is equal to the number of all points, including the distribution center
		String path = "data/c102.txt";//Example address
		data.read_data(path,data);
		System.out.println("input succesfully");
		System.out.println("cplex procedure###########################");
		BaB_Vrptw lp = new BaB_Vrptw(data);
		double cplex_time1 = System.nanoTime();
		//Delete unused vehicles to reduce solution space
		lp=lp.init(lp);
		System.out.println(":   "+lp.data.carnum);
		lp.branch_and_bound(lp);
		Check check = new Check(lp);
		check.fesible();
		double cplex_time2 = System.nanoTime();
		double cplex_time = (cplex_time2 - cplex_time1) / 1e9;//Solution time, units s
		System.out.println("cplex_time " + cplex_time + " bestcost " + lp.cur_best);
		for (int i = 0; i < lp.best_note.node_routes.size(); i++) {
			ArrayList<Integer> r = lp.best_note.node_routes.get(i);
			System.out.println();
			for (int j = 0; j < r.size(); j++) {
				System.out.print(r.get(j)+" ");
			}
		}
	}
}

The purpose of the Check class is to check the feasibility of the solution, including whether it satisfies the number of vehicles constraint, capacity constraint, time window constraint, and so on.

package com.chb;

import java.util.ArrayList;

import ilog.concert.IloException;

//Class Functions: Feasibility Judgment of Solutions (can be skipped directly)
class Check{
	double epsilon = 0.0001;
	Data data = new Data();
	ArrayList<ArrayList<Integer>> routes = new ArrayList<>();
	ArrayList<ArrayList<Double>> servetimes = new ArrayList<>();
	public Check(BaB_Vrptw lp) {
		super();
		this.data = lp.data;
		this.routes = lp.routes;
		this.servetimes = lp.servetimes;
	}
	//Function function: Compare the size of two numbers
	public int double_compare(double v1,double v2) {
		if (v1 < v2 - epsilon) {
			return -1;
		}
		if (v1 > v2 + epsilon) {
			return 1;
		}
		return 0;
	}
	//Functional Functions: Feasibility of Solutions
	public void fesible() throws IloException {
		//Feasibility Judgment of Vehicle Quantity
		if (routes.size() > data.carnum) {
			System.out.println("error: vecnum!!!");
			System.exit(0);
		}
		//Feasibility Judgment of Vehicle Load
		for (int k = 0; k < routes.size(); k++) {
			ArrayList<Integer> route = routes.get(k);
			double capasity = 0;
			for (int i = 0; i < route.size(); i++) {
				capasity += data.demand[route.get(i)];
			}
			if (capasity > data.cap) {
				System.out.println("error: cap!!!");
				System.exit(0);
			}
		}
		//Feasibility Judgment of Time Window and Vehicle Capacity
		for (int k = 0; k < routes.size(); k++) {
			ArrayList<Integer> route = routes.get(k);
			ArrayList<Double> servertime = servetimes.get(k);
			double capasity = 0;
			for (int i = 0; i < route.size()-1; i++) {
				int origin = route.get(i);
				int destination = route.get(i+1);
				double si = servertime.get(i);
				double sj = servertime.get(i+1);
				if (si < data.a[origin] && si >  data.b[origin]) {
					System.out.println("error: servertime!");
					System.exit(0);
				}
		if (double_compare(si + data.dist[origin][destination],data.b[destination]) > 0) {
					System.out.println(origin + ": [" + data.a[origin] 
							+ ","+data.b[origin]+"]"+ " "+ si);
					System.out.println(destination + ": [" +
							data.a[destination] + ","+data.b[destination]+"]"+ " "+ sj);
					System.out.println(data.dist[origin][destination]);
					System.out.println(destination + ":" );
					System.out.println("error: forward servertime!");
					System.exit(0);
				}
			if (double_compare(sj - data.dist[origin][destination],data.a[origin]) < 0) {
					System.out.println(origin + ": [" + data.a[origin]
							+ ","+data.b[origin]+"]"+ " "+ si);
					System.out.println(destination + ": [" + data.a[destination] 
							+ ","+data.b[destination]+"]"+ " "+ sj);
					System.out.println(data.dist[origin][destination]);
					System.out.println(destination + ":" );
					System.out.println("error: backward servertime!");
					System.exit(0);
				}
			}
			if (capasity > data.cap) {
				System.out.println("error: cap!!!");
				System.exit(0);
			}
		}
	}
}

Run result:

Note: Text is extracted and reproduced from
https://mp.weixin.qq.com/s?__biz=MzU0NzgyMjgwNg==&mid=2247484626&idx=1&sn=fa7ab4ea6e02084f4e8ed52c6c203eda&chksm=fb49c96bcc3e407db8c7bb9433dfb1fa787518328922bf061a4421c46557089b3429c9aeb391&mpshare=1&scene=1&srcid=0819ACkwrk90AoXHmuTfOewW&sharer_sharetime=1566189897645&sharer_shareid=054592193644de509623829748e83807&key=39e8dcac62579260153e141217c0c82d2c6e85d9ab908f37737e7a53942622438640915d71ffff9fcb2a13fe7ce9bddfcdfbd31c41de4e97aa05544e971a3b9da9c1048280222095be4e5c215e240a6b&ascene=1&uin=MjYzMDA1MzAyMQ%3D%3D&devicetype=Windows+10&version=62060834&lang=zh_CN&pass_ticket=XkzfvjtynhPBmAjeYlUVZf95yJsB8gJ7VNbzHQQ9yaInrn1wKsdFjIxND%2FyV40nF

Posted by n_wattam on Mon, 19 Aug 2019 19:03:13 -0700