//**********************************************BBAlgorithm********************************************************************************* /// <summary> /// performs a Branch and Bound search of the state space of partial tours /// stops when time limit expires and uses BSSF as solution /// </summary> /// <returns>results array for GUI that contains three ints: cost of solution, time spent to find solution, number of solutions found during search (not counting initial BSSF estimate)</returns> public string[] bBSolveProblem() { string[] results = new string[3]; // TODO: Add your implementation for a branch and bound solver here. //Initalize variables. Takes O(1) space and time int numOfCitiesLeft = Cities.Length; int numOfSolutions = 0; int numOfStatesCreated = 0; int numOfStatesNotExpanded = 0; //Initalize time variable for stopping the algorithm after the default of 60 seconds. Takes O(1) space and time DateTime start = DateTime.Now; DateTime end = start.AddSeconds(time_limit / 1000); //Create initial root state and set its priority to its lower bound. Takes O(n^2) space and time as discussed above TSPState initialState = createInitialState(); numOfStatesCreated++; initialState.setPriority(calculateKey(numOfCitiesLeft - 1, initialState.getLowerBound())); //Create initial BSSF greedily double bssfBound = createGreedyBssf(); PriorityQueue queue = new PriorityQueue(Cities.Length); queue.insert(initialState); // Branch and Bound until the queue is empty, we have exceeded the time limit, or we found the optimal solution /* This loop will have a iterate 2^n times approximately with expanding and pruning for each state, then for each state it * does O(n^2) work by reducing the matrix, so over all O((n^2)*(2^n)) time and space as well as it creates a nxn * matrix for each state*/ while (!queue.isEmpty() && DateTime.Now < end && queue.getMinLB() != bssfBound) { // Grab the next state in the queue TSPState currState = queue.deleteMin(); // check if lower bound is less than the BSSF, else prune it if (currState.getLowerBound() < bssfBound) { // Branch and create the child states for (int i = 0; i < Cities.Length; i++) { // First check that we haven't exceeded the time limit if (DateTime.Now >= end) { break; } // Make sure we are only checking cities that we haven't checked already if (currState.getPath().Contains(Cities[i])) { continue; } // Create the State double[,] oldCostMatrix = currState.getCostMatrix(); double[,] newCostMatrix = new double[Cities.Length, Cities.Length]; // Copy the old array in the new one to modify the new without affecting the old for (int k = 0; k < Cities.Length; k++) { for (int l = 0; l < Cities.Length; l++) { newCostMatrix[k, l] = oldCostMatrix[k, l]; } } City lastCityinCurrState = (City)currState.getPath()[currState.getPath().Count - 1]; double oldLB = currState.getLowerBound(); setUpMatrix(ref newCostMatrix, Array.IndexOf(Cities, lastCityinCurrState), i, ref oldLB); double newLB = oldLB + reduceMatrix(ref newCostMatrix); ArrayList oldPath = currState.getPath(); ArrayList newPath = new ArrayList(); foreach (City c in oldPath) { newPath.Add(c); } newPath.Add(Cities[i]); TSPState childState = new TSPState(ref newPath, ref newLB, ref newCostMatrix); numOfStatesCreated++; // Prune States larger than the BSSF if (childState.getLowerBound() < bssfBound) { City firstCity = (City)childState.getPath()[0]; City lastCity = (City)childState.getPath()[childState.getPath().Count - 1]; double costToLoopBack = lastCity.costToGetTo(firstCity); // If we found a solution and it goes back from last city to first city if (childState.getPath().Count == Cities.Length && costToLoopBack != double.MaxValue) { childState.setLowerBound(childState.getLowerBound() + costToLoopBack); bssf = new TSPSolution(childState.getPath()); bssfBound = bssf.costOfRoute(); numOfSolutions++; numOfStatesNotExpanded++; // this state is not expanded because it is not put on the queue } else { // Set the priority for the state and add the new state to the queue numOfCitiesLeft = Cities.Length - childState.getPath().Count; childState.setPriority(calculateKey(numOfCitiesLeft, childState.getLowerBound())); queue.insert(childState); } } else { numOfStatesNotExpanded++; // States that are pruned are not expanded } } } currState = null; } numOfStatesNotExpanded += queue.getSize(); // if the code terminated before queue is empty, then those states never got expanded Console.WriteLine("Number of states generated: " + numOfStatesCreated); Console.WriteLine("Number of states not Expanded: " + numOfStatesNotExpanded); Console.WriteLine("Max Number of states put in queue: " + queue.getMaxNumOfItems()); end = DateTime.Now; TimeSpan diff = end - start; double seconds = diff.TotalSeconds; results[COST] = System.Convert.ToString(bssf.costOfRoute()); // load results into array here, replacing these dummy values results[TIME] = System.Convert.ToString(seconds); results[COUNT] = System.Convert.ToString(numOfSolutions); return(results); }
/// <summary> /// solve the problem. This is the entry point for the solver when the run button is clicked /// right now it just picks a simple solution. /// </summary> public void solveProblemBandB() { //initialize BSSF with a greedy algorithm Algorithms algorithms = new Algorithms(); bssf = new TSPSolution(algorithms.greedy(Cities)); Node.bssf = bssf.costOfRoute(); int maxQsize = 0; int totalStates = 0; int timeSeconds = Convert.ToInt32(Program.MainForm.textBoxTime.Text); //set up priority queue and stopwatch PriorityQueue PQ = new PriorityQueue(); PQ.insert(new Node(Cities)); Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); while(PQ.getSize() > 0 && stopwatch.Elapsed.TotalSeconds < timeSeconds) { //pop node off of queue and check lower bound against bssf Node node = PQ.deleteMin(); if(node.lowerBound > Node.bssf) { Node.prunes++; break; } Node include = null; Node exclude = null; double maxDif = Double.NegativeInfinity; //search for include/exclude edge that gives max difference in lower bound double[,] matrix = node.rCmatrix; for (int i = 0; i < node.matrixLength; i++) { if (node.exited[i] == -1) { for (int j = 0; j < node.matrixLength; j++) { if (matrix[i, j] == 0) { Node tempInclude = new Node(node, true, i, j); Node tempExclude = new Node(node, false, i, j); double potentialMaxDif = tempExclude.lowerBound - tempInclude.lowerBound; if (potentialMaxDif > maxDif) { maxDif = potentialMaxDif; include = tempInclude; exclude = tempExclude; } } } } } //check if found a bssf if(include.totalEdges == include.matrixLength && include.lowerBound < Node.bssf) { Node.bssfUpdates++; Node.bssf = include.lowerBound; Node.bssfNode = include; } else if(include.lowerBound < Node.bssf)//add include node to queue { PQ.insert(include); int currentQSize = PQ.getSize(); if(currentQSize > maxQsize) { maxQsize = currentQSize; } } else//prune include node { Node.prunes++; } if(exclude.lowerBound < Node.bssf)//add exclude node to queue { PQ.insert(exclude); int currentQSize = PQ.getSize(); if (currentQSize > maxQsize) { maxQsize = currentQSize; } } else//prune exclude node { Node.prunes++; } totalStates += 2;//2 states are created per while-loop iteration } stopwatch.Stop(); //if stopwatch is < 30, then we have found an optimal solution bool isOptimal = false; if(stopwatch.Elapsed.TotalSeconds < timeSeconds) { isOptimal = true; } //prune number of items left in the queue Node.prunes += PQ.getSize(); //if a bssf has been found better than the greedy solution if(Node.bssfNode != null) { Node solution = Node.bssfNode; ArrayList route = solution.getRoute(Cities); // call this the best solution so far. bssf is the route that will be drawn by the Draw method. bssf = new TSPSolution(route); } //display stats if (isOptimal) { Program.MainForm.tbCostOfTour.Text = " " + bssf.costOfRoute() + "*"; } else { Program.MainForm.tbCostOfTour.Text = " " + bssf.costOfRoute(); } Program.MainForm.tbElapsedTime.Text = " " + stopwatch.Elapsed.TotalSeconds; // do a refresh. Program.MainForm.Invalidate(); //print more stats Console.WriteLine(); Console.WriteLine("Max # of stored states: " + maxQsize); Console.WriteLine("# of BSSF updates: " + Node.bssfUpdates); Console.WriteLine("Total # of states created: " + totalStates); Console.WriteLine("Total # of states pruned: " + Node.prunes); Node.resetStaticVariables(); }
/// <summary> /// performs a Branch and Bound search of the state space of partial tours /// stops when time limit expires and uses BSSF as solution /// </summary> /// <returns>results array for GUI that contains three ints: cost of solution, time spent to find solution, number of solutions found during search (not counting initial BSSF estimate)</returns> public string[] bBSolveProblem() { string[] results = new string[3]; results[COST] = "not implemented"; // load results into array here, replacing these dummy values results[TIME] = "-1"; results[COUNT] = "-1"; int count = 0; Console.Out.WriteLine("City costs: "); for (int j = 0; j < Cities.Length; j++) { for (int h = 0; h < Cities.Length; h++) { Console.Write(Cities[j].costToGetTo(Cities[h]) + ","); } Console.WriteLine(); } Stopwatch timer = new Stopwatch(); timer.Start(); PriorityQueue queue = new PriorityQueue(); queue.make_queue(Cities.Length * Cities.Length * Cities.Length * Cities.Length); //Here I run the greedy algorithm and use that BSSF result as my starting BSSF greedySolveProblem(); Console.Out.WriteLine("Greedy BSSF: " + costOfBssf()); matrix_and_bound initial_matrix = construct_initial_matrix(); for (int j = 0; j < Cities.Length; j++) { for (int h = 0; h < Cities.Length; h++) { Console.Write(initial_matrix.Matrix[j, h] + ","); } Console.WriteLine(); } Console.WriteLine(initial_matrix.Lower_bound); //Console.WriteLine(); //This loop will do my initial population of the queue by looping through each city and getting the lower bound for (int i = 0; i < Cities.Length; i++) { for (int k = 0; k < Cities.Length; k++) { if (i != k) { //I need to get the lower bound of each reduced matrix and the bound matrix_and_bound current = matrix_reduction(initial_matrix, i, k); //Console.WriteLine(); //If the lower bound is less than current bssf add to queue for later checking, otherwise ignore if (current.Lower_bound < costOfBssf()) { //need to create new state_data object with state data set state_data data = new state_data(); //I guess depth doesn't matter to be exact so long as it's relative, so I'll keep this first one as 0 data.Depth = 0; data.Mb = current; data.add_city(Cities[i]); data.add_city(Cities[k]); data.add_city_index(i); data.add_city_index(k); data.set_priority(); Console.Out.WriteLine("bound " + data.Mb.Lower_bound); //I'm not sure this id is necessary but I'll have to see data.Id = id; queue.insert(data, id); id++; } } } } Console.Out.WriteLine("Queue length Initial " + queue.Length); //now run while queue is not empty and timer is less than 60 while (timer.Elapsed.TotalSeconds < 60 && queue.Length > 0) { //pop off of queue and repeat above matrix reduction process state_data current = queue.delete_min(); //if it's greater or equal to the current bssf I can just ignore it, this is the culling if (current.Mb.Lower_bound < costOfBssf()) { //Priority queue looks like it's working //Console.Out.WriteLine(current.Mb.Lower_bound); /* * Now I need to matrix reduce each child of the current node, * see if it's still smaller than the current bssf if it's a leaf node * I put it as the current bssf, otherwise I push it on the queue */ for (int k = 0; k < Cities.Length; k++) { if (!current.City_list.Contains(k)) { matrix_and_bound child = matrix_reduction(current.Mb, (int)current.City_list[current.City_list.Count - 1], k); //Console.Out.WriteLine("here"); //Console.Out.WriteLine("Current depth: " + ); for (int j = 0; j < Cities.Length; j++) { for (int h = 0; h < Cities.Length; h++) { Console.Write(child.Matrix[j, h] + ","); } Console.WriteLine(); } Console.WriteLine(child.Lower_bound); if (child.Lower_bound < costOfBssf()) { //need to create new state_data object with state data set state_data data = new state_data(); //I guess depth doesn't matter to be exact so long as it's relative, so I'll keep this first one as 0 data.Depth = current.Depth + 1; data.Mb = child; //The last value in the path is the current city data.Path = current.Path; data.City_list = current.City_list; data.add_city(Cities[k]); /* Console.Out.WriteLine("The Current Path and length " + data.City_list.Count); * for (int j = 0; j < data.City_list.Count; j++) * { * Console.Out.Write(data.City_list[j] + "->"); * } * Console.Out.WriteLine();*/ data.City_list = current.City_list; data.add_city_index(k); data.set_priority(); //Console.Out.WriteLine("Set Priority " + data.Priority); //I'm not sure this id is necessary but I'll have to see data.Id = id; id++; // Console.Out.WriteLine("Intermediate Lower Bound " + data.Mb.Lower_bound); if (data.City_list.Count < Cities.Length) { queue.insert(data, id); } else if (data.Mb.Lower_bound < costOfBssf())//it's a leaf node and it's less than the current BSSF { Console.Out.WriteLine("Current Lower Bound " + costOfBssf()); Console.Out.WriteLine("Final Lower Bound " + data.Mb.Lower_bound); for (int j = 0; j < data.City_list.Count; j++) { Console.Out.Write(data.City_list[j] + "->"); } bssf = new TSPSolution(data.Path); count++; } } } } } } results[COST] = costOfBssf().ToString(); results[TIME] = timer.Elapsed.ToString(); results[COUNT] = count.ToString(); return(results); }
/// <summary> /// performs a Branch and Bound search of the state space of partial tours /// stops when time limit expires and uses BSSF as solution /// </summary> /// <returns>results array for GUI that contains three ints: cost of solution, time spent to find solution, number of solutions found during search (not counting initial BSSF estimate)</returns> public string[] bBSolveProblem() { string[] results = new string[3]; int solutionCount = 0; Stopwatch timer = new Stopwatch(); PriorityQueue <State> pQueue = new PriorityQueue <State>(); int maxStoredStates = 0; int bssfUpdates = 0; int totalStatesCreated = 0; int totalStatesPruned = 0; timer.Start(); string[] intialResults = defaultSolveProblem(); double bestCostSoFar = Int32.Parse(intialResults[COST]); Console.WriteLine("BestCostSoFar: " + bestCostSoFar); State initialState = initializeState(); this.reduceState(ref initialState); pQueue.insert(initialState, (int)initialState.lowerBound); while (!pQueue.isEmpty() && timer.Elapsed.Seconds < 60) // while queue is not empty or has not passed 60 seconds { State parentState = pQueue.deleteMin(); if (parentState.lowerBound >= bestCostSoFar) // trim queue if state has worse bound than bssf { totalStatesPruned++; continue; } for (int i = 0; i < this.Cities.Length; i++) { if (!parentState.visited.Contains(i)) { // createChild totalStatesCreated++; State childState = parentState.getCopy(); this.addCityToRoute(ref childState, i); this.reduceState(ref childState); if (childState.route.Count == this.Cities.Count()) // if route is complete { TSPSolution newSolution = new TSPSolution(childState.route); double cost = newSolution.costOfRoute(); if (cost < bestCostSoFar) { bssfUpdates++; bestCostSoFar = cost; bssf.Route = childState.route; } } else // route is not complete { if (childState.lowerBound < bestCostSoFar) // don't insert in queue if state has worse bound than bssf { pQueue.insert(childState, (int)childState.lowerBound); if (pQueue.getCount() > maxStoredStates) { maxStoredStates = pQueue.getCount(); } } else { totalStatesPruned++; } } } } } timer.Stop(); results[COST] = costOfBssf().ToString(); results[TIME] = timer.Elapsed.ToString(); results[COUNT] = solutionCount.ToString(); Console.WriteLine("==================================="); Console.WriteLine("StatesCreated: " + totalStatesCreated); Console.WriteLine("StatesPruned: " + totalStatesPruned); Console.WriteLine("bssfUpdates: " + bssfUpdates); Console.WriteLine("MaxStoredStates: " + maxStoredStates); return(results); }