int random_integer(float prob_distrib[]) /* Random integer generation function. */ { int i; float u; /* Generate a U(0,1) random variate. */ u = lcgrand(1); /* Return a random integer in accordance with the (cumulative) distribution function prob_distrib. */ for (i = 1; u >= prob_distrib[i]; ++i) ; return i; }
//=========================================================================== //= Function to generate exponentially distributed random variables = //= - Input: Mean value of distribution = //= - Output: Returns with exponentially distributed random variable = //=========================================================================== double expon(double x) { double z; // Uniform random number (0 < z < 1) double exp_value; // Computed exponential value to be returned // Pull a uniform random number (0 < z < 1) do { z = lcgrand(stream_id); } while ((z == 0) || (z == 1)); // Compute exponential random variable using inversion method exp_value = -x * log(z); return(exp_value); }
float uniform(float a, float b) /* Uniform variate generation function. */ { /* Return a U(a,b) random variate. */ return a + lcgrand(1) * (b - a); }
float expon(float mean) /* Exponential variate generation function. */ { /* Return an exponential random variate with mean "mean". */ return -mean * log(lcgrand(1)); }
int main(int argc, char **argv) { if(argc==1 || (argc==2 && strcmp(argv[1],"-h")==0) || (argc==2 && strcmp(argv[1],"--help")==0) || (argc==2 && strcmp(argv[1],"--h")==0) ) { usage(argv[0]); exit(-1); } time_t global_start=clock(); int i,j,new_node, num_pushes=0, seed=0, nsplits=0, root=-1,subtree_root=-1,new_ms,max_bits=-1, num_spawns=0; // Set default p to 1/3 double p=0.3333; list<int>::iterator ii; char *DIMACS_file=NULL; bool has_graph=false,verbose=false,do_push=true,do_two_sep=true, do_init_push=false; Graph *G=NULL; time_t start,stop; //for printing out the graphviz representations of each move on the bd bool gviz_all = false; bool gviz_el = false; //edge labels on bool gviz_th = true; //thicknesses off char gvizfile[20]; int step = 0; // time_t begin=clock(); // Controller object has some methods which are used independently from // graph objects. goblinController *CT; CT = new goblinController(); //Turn off printing of dots. CT->traceLevel = 0; // Parse arguments for(i=0;i<argc;i++) { if(strcmp(argv[i],"-v")==0) verbose=true; if(strcmp(argv[i],"-f")==0) { DIMACS_file=argv[i+1]; // Read in the file G=new Graph(DIMACS_file, true); G->write_graphviz_file("orig.gviz"); char metis_file[100]; sprintf(metis_file,"%s.metis",argv[i+1]); G->write_METIS_file(metis_file); sprintf(metis_file,"%s.hmetis",argv[i+1]); G->write_HMETIS_file(metis_file); has_graph=true; if(verbose) { print_message(0,"Read in graph from file: %s\n",DIMACS_file); cout << *G; } } if(strcmp(argv[i],"-p")==0) p=atof(argv[i+1]); if(strcmp(argv[i],"-nopush")==0) do_push=false; if(strcmp(argv[i],"-no2sep")==0) do_two_sep=false; if(strcmp(argv[i],"-seed")==0) seed=atoi(argv[i+1]); if(strcmp(argv[i],"-root")==0) root=atoi(argv[i+1]); if(strcmp(argv[i],"-subtree")==0) subtree_root=atoi(argv[i+1]); if(strcmp(argv[i],"-exhaust")==0) max_bits=atoi(argv[i+1]); } // Make sure we have a graph if(!has_graph) fatal_error("Did not load graph\n"); if(!G->check_connected()) fatal_error("Graph is not connected\n"); if(!G->check_two_connected()) fatal_error("Graph is not 2 connected!\n"); // "seed" the rng for(i=0;i<seed;i++) lcgrand(0); // Create the tree BDTree btree(G); // Initialize to the star configuration btree.create_star(); if(gviz_all) { sprintf(gvizfile, "bd.%d.gviz", step); btree.write_graphviz_file(false,gvizfile,gviz_el, gviz_th); step++; } // Find candidate pushes if(do_init_push) { num_pushes=btree.push(0); print_message(0,"Found %d initial pushes\n",num_pushes); } if(gviz_all && num_pushes > 0) { sprintf(gvizfile, "bd.%d.gviz", step); btree.write_graphviz_file(false,gvizfile,gviz_el, gviz_th); step++; } if(do_two_sep) { // Look for 2-separations list<int> X1, Y1; bool ts = false; int bv = 0; bool found; while(!ts) { //we need to try to separate a vertex with at least 4 edges adjacent to it found = false; while(found == false && bv < btree.num_nodes) { if(btree.nodes[bv].edges.size() >3) found = true; else bv++; } if(!found) { print_message(0, "Did not find a(nother) node of btree to split.\n"); break; } if(verbose) print_message(0,"Running two separation function at vertex %d\n", bv); X1.clear(); Y1.clear(); start = clock(); ts= btree.two_separation(bv, &X1, &Y1, CT); //ts = btree.bf_two_separation(bv, &X1, &Y1); stop = clock(); print_message(0, "Checked for two separation in %f seconds.\n", ((double)(stop-start))/CLOCKS_PER_SEC); if(ts) { print_message(0, "Found a valid 2 separation!\n"); print_message(0, "Splitting node %d\n", bv); // Split the node new_node = btree.split_node(bv,&X1,EDGE_SPLIT,&new_ms); print_message(0,"After split - new edge has middle set of size %d\n", new_ms); if(gviz_all) { sprintf(gvizfile, "bd.%d.gviz", step); btree.write_graphviz_file(false,gvizfile,gviz_el, gviz_th); step++; } if(do_push) { //Push the vertex you split num_pushes=btree.push(bv); print_message(0, "Found %d pushes at %d \n",num_pushes, bv); if(gviz_all && num_pushes > 0) { sprintf(gvizfile, "bd.%d.gviz", step); btree.write_graphviz_file(false,gvizfile,gviz_el, gviz_th); step++; } //Push the new vertex created num_pushes=btree.push(new_node); print_message(0, "Found %d pushes at %d \n",num_pushes, new_node); if(gviz_all && num_pushes > 0) { sprintf(gvizfile, "bd.%d.gviz", step); btree.write_graphviz_file(false,gvizfile,gviz_el, gviz_th); step++; } } //Add one to our count, then reset ts and bv so we restart search for 2-seps. nsplits++; ts = false; bv = 0; } else { print_message(0, "No 2 separation at vertex %d!\n", bv); bv++; } } print_message(0, "Finished with two-separations. Split %d nodes.\n", nsplits); } // Now run eigenvector splitting until BD is valid int split_node=0,new_node_1, new_node_2; nsplits=0; list<int> A, B, CA, CB, partition, partition2; vector<int> candidates(btree.num_nodes); while(!btree.is_valid) { // Find a BDTreeNode with at least 4 neighbors // fill candidates with possibilities // This is not smart since we really should just update // the candidates as we split and add nodes... // but it's probably in the noise anyway j=0; split_node = -1; for(i=0;i<btree.num_nodes;i++) { if(btree.nodes[i].edges.size()>=4) { candidates[j]=i; j++; } } print_message(1,"generating random int in 0...%d\n",j-1); split_node=rand_int(0,j-1); split_node=candidates[split_node]; print_message(1,"split_node is %d (degree=%d)\n",split_node,btree.nodes[split_node].edges.size()); A.clear(); B.clear(); CA.clear(); CB.clear(); nsplits++; // Run eigenvector heuristic - if fill_extra=true then we will // we are adding "obvious" edges to A and B within this function! start=clock(); if(btree.num_interior_nodes==1) btree.eigenvector_split(split_node,&A, &B, p, false); else btree.eigenvector_leaf_split(split_node,&A, &B, p, false); stop=clock(); print_message(0,"Computed eigenvector with p=%f in %f seconds.\n",p, ((double)(stop-start))/CLOCKS_PER_SEC); print_message(0,"Eigenvector A:\n"); print(0,A); print_message(0,"Eigenvector B:\n"); print(0,B); // Should do this only if A and B require it if(A.size()+B.size() < btree.nodes[split_node].edges.size()) { // Run the max flow to get an actual splitting of the edges start=clock(); btree.split_maxflow_partition(split_node,&A,&B,&CA,&CB, CT); stop = clock(); print_message(0,"Computed partition given eigenvector results in %f seconds.\n", ((double)(stop-start))/CLOCKS_PER_SEC); // Create the edge set partition.clear(); for(ii=A.begin();ii!=A.end();++ii) partition.push_back(*ii); for(ii=CA.begin();ii!=CA.end();++ii) partition.push_back(*ii); partition.sort(); } else { // Just use A partition.clear(); for(ii=A.begin();ii!=A.end();++ii) partition.push_back(*ii); } // Check the size of the exhaust BEFORE splitting the node int exhaust_bits=btree.nodes[split_node].edges.size() - A.size() - B.size(); print_message(1,"Exhaust size is %d bits\n",exhaust_bits); int exhaust_ms_size=-1; time_t exh_start=0, exh_stop=0; list<int> C; // Check to see if we can/want to exhaust if( exhaust_bits <= max_bits) { print_message(0,"Checking exhaust\n"); // Check this exhaustively exh_start=clock(); C.clear(); exhaust_ms_size=btree.best_partition(split_node,&A, &B, &C); exh_stop=clock(); } print_message(1,"Splitting %d with edge partition of size %d\n",split_node,partition.size()); print(1,partition); if(btree.num_interior_nodes==1) { // initial star - use split node new_node_2=-1; new_node_1 = btree.split_node(split_node,&partition,EDGE_SPLIT,&new_ms); btree.write_graphviz_file(true,"init.gviz",true,true); } else { // Later on in the process - use spawn node int ms1,ms2; btree.spawn_nodes(split_node,&partition,EDGE_SPLIT,&ms1,&ms2, &new_node_1, &new_node_2); print_message(0,"After spawn - new middle sets of size %d,%d (max ms is %d)\n",ms1,ms2,btree.max_middle_set_size); print_message(0,"new_nodes: %d,%d\n",new_node_1, new_node_2); //char spawn_file[100]; //sprintf(spawn_file,"spawn_%d.gviz",num_spawns); //btree.write_graphviz_file(true,spawn_file,true,true); num_spawns++; } // Check for validity here!!! if(btree.is_valid) break; // CSG - this fails because we had a spawn of a leaf that was an old node - // so new node1 and 2 are getting set incorrectly in spawn_node if(new_node_1!=-1) { // Try to push the newly created nodes if(btree.nodes[new_node_1].edges.size()>=4 && do_push) { num_pushes=btree.push(new_node_1); print_message(0,"\n\tFound %d pushes for newly introduced node %d\n",num_pushes,new_node_1); if(gviz_all && num_pushes > 0) { sprintf(gvizfile, "bd.%d.gviz", step); btree.write_graphviz_file(false,gvizfile,gviz_el, gviz_th); step++; } } // Check for validity here!!! if(btree.is_valid) break; } if(new_node_2!=-1) { if(btree.nodes[new_node_2].edges.size()>=4 && do_push) { num_pushes=btree.push(new_node_2); print_message(0,"\n\tFound %d pushes for newly introduced node %d\n",num_pushes,new_node_2); if(gviz_all && num_pushes > 0) { sprintf(gvizfile, "bd.%d.gviz", step); btree.write_graphviz_file(false,gvizfile,gviz_el, gviz_th); step++; } } } // Check for validity here!!! if(btree.is_valid) break; } if(root!=-1) { // This seems to be working when all graphviz flags are off, but something doesn't seem right // if last param is set to 2, probably because middle set is empty and we get 0 pen width??! // BDS - fixed this by making minimum penwidth 1 (i.e. pw = |mid set| unless |mid set| = 0, in which // case, pw = 1. btree.write_graphviz_file(false,"before_root.gviz",false,true); //cout<<btree; btree.root(root); btree.write_graphviz_file(false,"after_root.gviz",false,true); //cout<<btree; } if(verbose) cout<<btree; #if 0 // This section was just for generating a specific plot when running // c:\Users\tcg\PROJECTS\SGD\gaudi\code\trunk\branch_decomposition\Release>BranchDecomposition.exe -p . // 33 -no2sep -root 10 -subtree 330 -f ..\data\ch130.tsp.del.100.dimacs // Check to see if a subtree is desired if(subtree_root!=-1) { int roots[24]={400,328,267,292,263, 403,438,257,251,302, 276,452,294,405,364, 379,349,369,330,443, 338,420,291,425}; char *colors[6]={"red","blue","green","orange","purple","yellow"}; list<int> subtree; for(i=0;i<24;i++) { subtree.clear(); btree.find_subtree(roots[i], &subtree); print_message(1,"Subtree rooted at %d:\n",roots[i]); for(ii=subtree.begin();ii!=subtree.end();++ii) { printf("%d [label=\"\",style=filled,fillcolor=%s,color=%s];\n",*ii,colors[i%6],colors[i%6]); } printf("\n\n"); } } #endif print_message(0,"%d splits performed\n",nsplits); int max_ms=0; for(i=0;i<btree.num_edges;i++) if((int)btree.edges[i].middle_set.size()>max_ms) max_ms=btree.edges[i].middle_set.size(); print_message(0,"max middle set is %d\n",max_ms); vector<int> hist(max_ms+1,0); for(i=0;i<btree.num_edges;i++) hist[btree.edges[i].middle_set.size()]++; print(0,hist); if(gviz_all && num_pushes > 0) { sprintf(gvizfile, "bd.%d.gviz", step); btree.write_graphviz_file(false,gvizfile,gviz_el, gviz_th); step++; } time_t global_stop=clock(); printf("%s %3.3f %3.3f %d %d\n",DIMACS_file,p,(double)(global_stop-global_start)/CLOCKS_PER_SEC,nsplits,max_ms); fflush(stdout); //write a file with thick/thin lines. //btree.write_graphviz_file(false,"final.gviz",false, true); //write a file with thick/thin lines and edge labels //btree.write_graphviz_file(false,"final.gviz",true, true); delete G; delete CT; return 1; }
int main(int argc, char **argv) { #if USE_VRPH // A few things required by VRPH int i; double best_sol=VRP_INFINITY; int best_sol_buff[500]; #endif vrp_problem *vrp; sym_environment *env = sym_open_environment(); version(); sym_parse_command_line(env, argc, argv); sym_get_user_data(env, (void**)&vrp); #if USE_VRPH // Get the size of the problem in the input file int n=VRPGetDimension(vrp->par.infile); // Declare a VRP object of size n VRP V(n); // Declare a ClarkeWright object of size n ClarkeWright CW(n); // Populate the VRP object with the input file V.read_TSPLIB_file(vrp->par.infile); // Now create NUM_VRPH_SOLUTIONS solutions using VRPH and set the // upper bound to the best solution discovered for(i=0;i<NUM_VRPH_SOLUTIONS;i++) { // Create default routes - each customer on its own route V.create_default_routes(); // Create a new random feasible solution with Clarke Wright CW.Construct(&V, .5+ lcgrand(1),false); // Improve it with the RTR heuristic V.RTR_solve(ONE_POINT_MOVE+TWO_POINT_MOVE+TWO_OPT+THREE_OPT, 30,5,1,.01,25,VRPH_LI_PERTURB,VRPH_BEST_ACCEPT,false); if(V.get_total_route_length()-V.get_total_service_time()<best_sol) { best_sol=V.get_total_route_length()-V.get_total_service_time(); V.export_canonical_solution_buff(best_sol_buff); } // Reset VRPH's internal data structures V.reset(); } // Import the best solution and display it - if SYMPHONY claims an infeasibility // because the VRPH solution is optimal, we wouldn't see it otherwise! printf("VRPH set SYMPHONY upper bound to %f based on solution:\n",best_sol); V.import_solution_buff(best_sol_buff); V.summary(); // Set the upper bound using VRPH solution by accessing SYMPHONY's // internal data structures env->has_ub=1; env->ub=best_sol ; #if 0 // Note that this might be incorrect if the VRPH solution is not optimal // So the # of trucks still needs to be passed in on the command line! vrp->numroutes=V.get_total_number_of_routes(); #endif #endif // Now just let SYMPHONY do its thing. If an infeasibility is encountered, // then this certifies that the solution found by VRPH is indeed optimal // Note that the par.test will not work as we have processed only a single // file. Thus, we changed the following line // if (vrp->par.test){ if (0 && vrp->par.test){ vrp_test(env, argc, argv); } else { sym_load_problem(env); sym_find_initial_bounds(env); sym_set_str_param(env, "lp_executable_name", "vrp_lp_cg"); sym_set_str_param(env, "cp_executable_name", "vrp_cp"); sym_set_int_param(env, "generate_cgl_cuts", FALSE); sym_solve(env); } sym_close_environment(env); return(0); }
double VRP::RTR_solve(int heuristics, int intensity, int max_stuck, int max_perturbs, double dev, int nlist_size, int perturb_type, int accept_type, bool verbose) { /// /// Uses the given parameters to generate a /// VRP solution via record-to-record travel. /// Assumes that data has already been imported into V and that we have /// some existing solution. /// Returns the objective function value of the best solution found /// // Make sure accept_type is either VRPH_BEST_ACCEPT or VRPH_FIRST_ACCEPT - matters only // for the downhill phase as we use VRPH_LI_ACCEPT in the diversification phase if(accept_type!=VRPH_BEST_ACCEPT && accept_type!=VRPH_FIRST_ACCEPT) report_error("%s: accept_type must be VRPH_BEST_ACCEPT or VRPH_FIRST_ACCEPT\n"); int ctr, n, j, i, R, random, fixed, neighbor_list, objective, tabu; random=fixed=neighbor_list=0; if(heuristics & VRPH_RANDOMIZED) random=VRPH_RANDOMIZED; if(heuristics & VRPH_FIXED_EDGES) fixed=VRPH_FIXED_EDGES; if(heuristics & VRPH_USE_NEIGHBOR_LIST) neighbor_list=VRPH_USE_NEIGHBOR_LIST; objective=VRPH_SAVINGS_ONLY; // default strategy if(heuristics & VRPH_MINIMIZE_NUM_ROUTES) objective=VRPH_MINIMIZE_NUM_ROUTES; if(heuristics & VRPH_TABU) { tabu=VRPH_TABU; // We will use a primitive Tabu Search in the uphill phase // Clear the tabu list this->tabu_list->empty(); } else tabu=0; n=num_nodes; // Define the heuristics we will use OnePointMove OPM; TwoPointMove TPM; TwoOpt TO; OrOpt OR; ThreeOpt ThreeO; CrossExchange CE; ThreePointMove ThreePM; double start_val; int *perm; perm=new int[this->num_nodes]; j=VRPH_ABS(this->next_array[VRPH_DEPOT]); for(i=0;i<this->num_nodes;i++) { perm[i]=j; if(!routed[j]) report_error("%s: Unrouted node in solution!!\n"); j=VRPH_ABS(this->next_array[j]); } if(j!=VRPH_DEPOT) report_error("%s: VRPH_DEPOT is not last node in solution!!\n"); int rules; // Set the neighbor list size used in the improvement search neighbor_list_size=VRPH_MIN(nlist_size, this->num_nodes); // Set the deviation deviation=dev; int num_perturbs=0; record=this->total_route_length; this->best_total_route_length=this->total_route_length; this->export_solution_buff(this->current_sol_buff); this->export_solution_buff(this->best_sol_buff); normalize_route_numbers(); ctr=0; uphill: // Start an uphill phase using the following "rules": double beginning_best=this->best_total_route_length; rules=VRPH_LI_ACCEPT+VRPH_RECORD_TO_RECORD+objective+random+fixed+neighbor_list+tabu; if(verbose) printf("Uphill starting at %5.2f\n",this->total_route_length); for(int k=1;k<intensity;k++) { start_val=total_route_length; if(heuristics & ONE_POINT_MOVE) { if(random) random_permutation(perm, this->num_nodes); for(i=1;i<=n;i++) { #if FIXED_DEBUG if(fixed && !check_fixed_edges("Before 1PM\n")) fprintf(stderr,"Error before OPM search(%d)\n",perm[i-1]); #endif OPM.search(this,perm[i-1],rules); #if FIXED_DEBUG if(fixed && !check_fixed_edges("After 1PM\n")) { fprintf(stderr,"Error after OPM search(%d)\n",perm[i-1]); this->show_route(this->route_num[perm[i-1]]); } #endif } } if(heuristics & TWO_POINT_MOVE) { if(random) random_permutation(perm, this->num_nodes); for(i=1;i<=n;i++) TPM.search(this,perm[i-1],rules + VRPH_INTER_ROUTE_ONLY); //check_fixed_edges("After 2PM\n"); } if(heuristics & THREE_POINT_MOVE) { if(random) random_permutation(perm, this->num_nodes); for(i=1;i<=n;i++) ThreePM.search(this,perm[i-1],rules + VRPH_INTER_ROUTE_ONLY); //check_fixed_edges("After 3PM\n"); } if(heuristics & TWO_OPT) { if(random) random_permutation(perm, this->num_nodes); for(i=1;i<=n;i++) TO.search(this,perm[i-1],rules); //check_fixed_edges("After TO\n"); } if(heuristics & OR_OPT) { if(random) random_permutation(perm, this->num_nodes); for(i=1;i<=n;i++) OR.search(this,perm[i-1],4,rules); for(i=1;i<=n;i++) OR.search(this,perm[i-1],3,rules); for(i=1;i<=n;i++) OR.search(this,perm[i-1],2,rules); //check_fixed_edges("After OR\n"); } if(heuristics & THREE_OPT) { normalize_route_numbers(); R=total_number_of_routes; for(i=1; i<=R; i++) ThreeO.route_search(this,i,rules-neighbor_list); //check_fixed_edges("After 3O\n"); } if(heuristics & CROSS_EXCHANGE) { normalize_route_numbers(); this->find_neighboring_routes(); R=total_number_of_routes; for(i=1; i<=R-1; i++) { for(j=0;j<1;j++) CE.route_search(this,i, route[i].neighboring_routes[j],rules-neighbor_list); } //check_fixed_edges("After CE\n"); } } if(total_route_length<record) record = total_route_length; if(verbose) { printf("Uphill complete\t(%d,%5.2f,%5.2f)\n",count_num_routes(),total_route_length, record); printf("# of recorded routes: %d[%d]\n",total_number_of_routes,count_num_routes()); } if(this->best_total_route_length<beginning_best-VRPH_EPSILON) { if(verbose) printf("New best found in uphill!\n"); // We found a new best solution during the uphill phase that might // now be "forgotten"!! I have seen this happen where it is never recovered // again, so we just import it and start the downhill phase with this solution... //this->import_solution_buff(this->best_sol_buff); } downhill: // Now enter a downhill phase double orig_val=total_route_length; if(verbose) printf("Downhill starting at %f (best=%f)\n",orig_val,this->best_total_route_length); if((heuristics & ONE_POINT_MOVE)|| (heuristics & KITCHEN_SINK) ) { rules=VRPH_DOWNHILL+objective+random+fixed+neighbor_list+accept_type; for(;;) { // One Point Move start_val=total_route_length; if(random) random_permutation(perm, this->num_nodes); for(i=1;i<=n;i++) OPM.search(this,perm[i-1],rules ); if(VRPH_ABS(total_route_length-start_val)<VRPH_EPSILON) break; } } if((heuristics & TWO_POINT_MOVE) || (heuristics & KITCHEN_SINK) ) { rules=VRPH_DOWNHILL+VRPH_INTER_ROUTE_ONLY+objective+random+fixed+neighbor_list+accept_type; for(;;) { // Two Point Move start_val=total_route_length; if(random) random_permutation(perm, this->num_nodes); for(i=1;i<=n;i++) TPM.search(this,perm[i-1],rules); if(VRPH_ABS(total_route_length-start_val)<VRPH_EPSILON) break; } } if((heuristics & TWO_OPT)|| (heuristics & KITCHEN_SINK) ) { // Do inter-route first a la Li rules=VRPH_DOWNHILL+VRPH_INTER_ROUTE_ONLY+objective+random+fixed+neighbor_list+accept_type; for(;;) { start_val=total_route_length; if(random) random_permutation(perm, this->num_nodes); for(i=1;i<=n;i++) TO.search(this,perm[i-1],rules); if(VRPH_ABS(total_route_length-start_val)<VRPH_EPSILON) break; } // Now do both intra and inter rules=VRPH_DOWNHILL+objective+random+fixed+neighbor_list+accept_type; for(;;) { start_val=total_route_length; if(random) random_permutation(perm, this->num_nodes); for(i=1;i<=n;i++) TO.search(this,perm[i-1],rules); if(VRPH_ABS(total_route_length-start_val)<VRPH_EPSILON) break; } } if((heuristics & THREE_POINT_MOVE) || (heuristics & KITCHEN_SINK) ) { rules=VRPH_DOWNHILL+VRPH_INTER_ROUTE_ONLY+objective+random+fixed+accept_type+neighbor_list; for(;;) { // Three Point Move start_val=total_route_length; if(random) random_permutation(perm, this->num_nodes); for(i=1;i<=n;i++) ThreePM.search(this,perm[i-1],rules); if(VRPH_ABS(total_route_length-start_val)<VRPH_EPSILON) break; } } if((heuristics & OR_OPT) || (heuristics & KITCHEN_SINK)) { rules=VRPH_DOWNHILL+ objective +random +fixed + accept_type + neighbor_list; for(;;) { // OrOpt start_val=total_route_length; if(random) random_permutation(perm, this->num_nodes); for(i=1;i<=n;i++) OR.search(this,perm[i-1],4,rules); for(i=1;i<=n;i++) OR.search(this,perm[i-1],3,rules); for(i=1;i<=n;i++) OR.search(this,perm[i-1],2,rules); if(VRPH_ABS(total_route_length-start_val)<VRPH_EPSILON) break; } } if((heuristics & THREE_OPT) || (heuristics & KITCHEN_SINK) ) { normalize_route_numbers(); R= total_number_of_routes; rules=VRPH_DOWNHILL+objective+VRPH_INTRA_ROUTE_ONLY+ random +fixed + accept_type; for(;;) { // 3OPT start_val=total_route_length; for(i=1;i<=R;i++) ThreeO.route_search(this,i,rules); if(VRPH_ABS(total_route_length-start_val)<VRPH_EPSILON) break; } } if( (heuristics & CROSS_EXCHANGE) ) { normalize_route_numbers(); this->find_neighboring_routes(); R=total_number_of_routes; rules=VRPH_DOWNHILL+objective+VRPH_INTRA_ROUTE_ONLY+ random +fixed + accept_type; for(i=1; i<=R-1; i++) { for(j=0;j<=1;j++) CE.route_search(this,i, route[i].neighboring_routes[j], rules); } } // Repeat the downhill phase until we find no more improvements if(total_route_length<orig_val-VRPH_EPSILON) goto downhill; if(verbose) printf("Downhill complete: %5.2f[downhill started at %f] (%5.2f)\n",total_route_length,orig_val, this->best_total_route_length); if(total_route_length < record-VRPH_EPSILON) { // New record - reset ctr ctr=1; record=total_route_length; } else ctr++; if(ctr<max_stuck) goto uphill; if(ctr==max_stuck) { if(num_perturbs<max_perturbs) { if(verbose) printf("perturbing\n"); if(perturb_type==VRPH_LI_PERTURB) perturb(); else osman_perturb(VRPH_MAX(20,num_nodes/10),.5+lcgrand(20)); // Reset record this->record=this->total_route_length; if(tabu) this->tabu_list->empty(); ctr=1; num_perturbs++; goto uphill; } } if(verbose) { if(has_service_times==false) printf("BEST OBJ: %f\n",best_total_route_length); else printf("BEST OBJ: %f\n",best_total_route_length-total_service_time); } delete [] perm; // Import the best solution found this->import_solution_buff(best_sol_buff); if(has_service_times==false) return best_total_route_length; else return best_total_route_length-total_service_time; }
int main(int argc, char **argv) { VRPH_version(); int i, j, k, n, status, num_attempts, *sol_buff, *IP_sol_buff; char in_file[200]; double lambda, best_heur_sol=VRP_INFINITY; bool first_sol=false, bootstrap=false;; VRPSolution *fresh_solution; OsiSolverInterface *si; const double *x; int last_num_cols=0, route_id=0; time_t start, stop; int *orderings[MAX_ROUTES]; for(i=0;i<MAX_ROUTES;i++) orderings[i]=NULL; // Set timing counters to 0 heur_time=mip_time=0; // Check arguments if(argc<5) { fprintf(stderr,"Usage: %s -f input_file -n num_runs [-v,-b,-c max_columns -d cols_to_delete]\n", argv[0]); fprintf(stderr,"\t Will solve the problem num_solutions times and add the routes\n"); fprintf(stderr,"\t to a set partitioning problem.\n"); fprintf(stderr,"\t Other options:\n"); fprintf(stderr,"\t -v runs in verbose mode\n"); fprintf(stderr,"\t -b will use bootstrapping where we send the set partitioning\n" "\t solution back to the metaheuristic solver\n"); fprintf(stderr,"\t -c max_columns will allow this many active columns/variables in the IP.\n"); fprintf(stderr,"\t Default value is max_columns=500\n"); fprintf(stderr,"\t -d num_cols_to_delete will delete this many columns once we have too many\n"); fprintf(stderr,"\t in the IP. Default value is num_cols_to_delete=100\n"); exit(-1); } // Set defaults verbose=false; max_columns=500; num_cols_to_delete=100; // Parse command line for(i=0;i<argc;i++) { if(strcmp(argv[i],"-f")==0) strcpy(in_file,argv[i+1]); if(strcmp(argv[i],"-n")==0) num_attempts=atoi(argv[i+1]); if(strcmp(argv[i],"-v")==0) verbose=true; if(strcmp(argv[i],"-b")==0) bootstrap=true; if(strcmp(argv[i],"-c")==0) max_columns=atoi(argv[i+1]); if(strcmp(argv[i],"-d")==0) num_cols_to_delete=atoi(argv[i+1]); } // This is the # of non-VRPH_DEPOT nodes n=VRPGetDimension(in_file); // This will be used to import/export solutions fresh_solution = new VRPSolution(n); // Create buffers for importing solutions sol_buff= new int[n+2]; IP_sol_buff = new int[n+2]; // Declare an OSI interface si=new OsiGlpkSolverInterface; si->setIntParam(OsiNameDiscipline,2); for(i=0;i<n;i++) { si->addRow(0,NULL,NULL,1,1); } // Declare a VRP of the right size and import the file VRP V(n); ClarkeWright CW(n); VRPRoute route(n); V.read_TSPLIB_file(in_file); // Set up a "route warehouse" to store the routes to be added to the IP V.route_wh=new VRPRouteWarehouse(HASH_TABLE_SIZE); // Set up a minimization problem si->setObjSense(1); // Set to error only output si->setHintParam(OsiDoReducePrint,true, OsiHintDo); // Unfortunately GLPK still prints out something regarding the conflict graph for(i=0;i<num_attempts;i++) { if(i==0 || !bootstrap) { lambda=.5+1.5*lcgrand(0); // Start with a clean VRP object V.reset(); CW.Construct(&V, lambda, false); if(verbose) printf("CW solution %d[%5.3f]: %f\n",i,lambda,V.get_total_route_length()-V.get_total_service_time()); } else // Use the solution from the IP V.import_solution_buff(IP_sol_buff); // Run VRPH's RTR algorithm to improve the solution start=clock(); V.RTR_solve(ONE_POINT_MOVE | TWO_POINT_MOVE | TWO_OPT | VRPH_USE_NEIGHBOR_LIST, 30, 5, 2, .01, 30, VRPH_LI_PERTURB, VRPH_FIRST_ACCEPT,false); stop=clock(); heur_time += (stop-start); if(verbose) printf("RTR Metaheuristic found solution %5.3f\n",V.get_total_route_length()-V.get_total_service_time()); // The RTR algorithm keeps a "warehouse" of the best solutions discovered during // the algorithm's search // Now go through the solutions in the solution warehouse and add the new routes // discovered to the IP for(j=0;j<V.solution_wh->num_sols;j++) { // Import solution j from the warehouse V.import_solution_buff(V.solution_wh->sols[j].sol); if(V.get_total_route_length()-V.get_total_service_time() < best_heur_sol) best_heur_sol = V.get_total_route_length()-V.get_total_service_time() ; // Now add the routes from this solution to the IP for(k=1;k<=V.get_total_number_of_routes();k++) { // Clean up the route by running INTRA_ROUTE optimizations only // using the route_search method of the different local search // heuristics, accepting improving moves only (VRPH_DOWNHILL) OnePointMove OPM; TwoOpt TO; ThreeOpt ThO; while(OPM.route_search(&V,k,k,VRPH_DOWNHILL|VRPH_INTRA_ROUTE_ONLY )){} while(TO.route_search(&V,k,k,VRPH_DOWNHILL|VRPH_INTRA_ROUTE_ONLY )){}; while(ThO.route_search(&V,k,VRPH_DOWNHILL|VRPH_INTRA_ROUTE_ONLY )){}; // Copy route k from the solution to the VRPRoute R V.update_route(k,&route); route.create_name(); // Add it to the "route warehouse" - this uses a hash table to keep track // of duplicate columns status=V.route_wh->add_route(&route); if(status!=DUPLICATE_ROUTE) { // This route is not currently in the WH and so it cannot be in the // set partitioning problem //OSI_add_route(si,&V,&route); OSI_add_route(si,&V,&route,route_id,orderings); route_id++; } } // Set the row RHS's if we need to if(first_sol) { first_sol=false; for(int rownum=0;rownum<n;rownum++) si->setRowBounds(rownum,1,1); // Note that changing this to >= would be a set covering problem // where each customer can be visited by more than one route } } // Now erase all the solutions from the WH V.solution_wh->liquidate(); if(verbose) { printf("Attempt %02d: Solving IP with %d columns\n",i,si->getNumCols()); printf("%d routes in the WH\n",V.route_wh->num_unique_routes); } // Solve the current set partitioning problem using the MIP solver start=clock(); si->branchAndBound(); stop=clock(); mip_time += (stop-start); double opt=si->getObjValue(); x=si->getColSolution(); last_num_cols=si->getNumCols(); if(verbose) printf("Optimal solution (%d columns) is %f\n",last_num_cols,opt); // Now recover the solution from the IP solution OSI_recover_solution(si, orderings, IP_sol_buff); if(verbose) printf("IP solution has obj. function value: %5.2f\n" "Best heuristic obj. function value: %5.2f\n", si->getObjValue(),best_heur_sol); } if(verbose) printf( "\nResults\n" "--------\n" "After %d runs\n" "IP solution has obj. function value: %5.2f\n" "Best heuristic obj. function value: %5.2f\n", num_attempts,si->getObjValue(),best_heur_sol); // print to stderr since GLPK prints "conflict graph" to stdout (a known bug...) // best_heur_sol best_mip_sol heur_time mip_time fprintf(stderr,"%5.3f %5.3f %5.3f %5.3f\n", best_heur_sol, si->getObjValue(), (double)(heur_time)/CLOCKS_PER_SEC, (double)(mip_time)/CLOCKS_PER_SEC); delete V.route_wh; delete fresh_solution; delete [] sol_buff; delete [] IP_sol_buff; delete si; for(i=0;i<MAX_ROUTES;i++) if(orderings[i]) delete [] orderings[i]; return 0; }
void OSI_add_route(OsiSolverInterface *si, VRP *V, VRPRoute *r, int id, int **orderings) { /// /// Adds a column/route to the current set partitioning problem. The column is assigned /// the name of id, which is converted to a string. /// if(id>MAX_ROUTES) { fprintf(stderr,"Too many routes added! Increase value of MAX_ROUTES=%d\n",MAX_ROUTES); exit(-1); } int i, j, k, nvars, ncols; int *cols_to_delete; int *col_indices; std::string col_name; const double *U; time_t start, stop; // Create a new route object of the right size VRPRoute removed_route(V->get_num_nodes()); // Create a CoinPackedVector for the new column CoinPackedVector col; for(i=0;i<r->num_customers;i++) col.insert(r->ordering[i]-1, 1);// 0-based // Check to see if we have exceeded max_columns // Since we have to fix variable values instead of deleting them in GLPK, // we have to calculate this U=si->getColUpper(); ncols=si->getNumCols(); nvars=0; for(i=0 ; i < ncols ; i++) { if(U[i]==1) nvars++; } if(nvars>=max_columns) { // Solve the problem and then delete num_cols_to_delete variables not in the current // optimal solution start=clock(); si->branchAndBound(); stop=clock(); mip_time += (stop-start); const double *y=si->getColSolution(); // Select a set of num_cols_to_delete columns to delete int nn=si->getNumCols(); cols_to_delete = new int[nn]; col_indices= new int[num_cols_to_delete]; memset(cols_to_delete,0,nn*sizeof(int)); memset(col_indices,0,num_cols_to_delete*sizeof(int)); U=si->getColUpper(); if(verbose) printf("Finding %d columns to delete \n",num_cols_to_delete); k=0; while(k<num_cols_to_delete) { while(true) { j=(int)floor(lcgrand(10)*nn); // Make sure the variable/column is not in the optimal solution // Since OSI/GLPK implementation of deleteCols doesn't seem right, // Need to make sure that this column isn't already fixed! if(y[j]<0.01 && cols_to_delete[j]==0 && U[j]==1) { cols_to_delete[j]=1; col_indices[k]=j; k++; break; } } } // Delete the columns - this does not seem to work in GLPK - just fix the variable to 0 // si->deleteCols(num_cols_to_delete,col_indices); for(k=0;k<num_cols_to_delete;k++) { col_name=si->getColName(col_indices[k],VRP_INFINITY); // Remove the route from VRPH's "Route Warehouse" int route_id=atoi(col_name.c_str()); OSI_recover_route(route_id,orderings, &removed_route); V->route_wh->remove_route(removed_route.hash_val, removed_route.hash_val2); // Fix this variable/column to 0 si->setColBounds(col_indices[k],0,0); } if(verbose) printf("Deleted/Fixed %d columns/variables\n",num_cols_to_delete); delete [] cols_to_delete; delete [] col_indices; } // Add the named column to the IP sprintf(r->name,"%d",id); std::string new_name(r->name); si->addCol(col,0,1,r->length-r->total_service_time,new_name); ncols=si->getNumCols(); // Set as integer (binary) si->setInteger(ncols-1); // 0-based // Copy the ordering of the route to the orderings[] array // Add a -1 to denote the end orderings[id]=new int[r->num_customers+1]; memcpy(orderings[id],r->ordering,r->num_customers*sizeof(int)); orderings[id][r->num_customers]=-1; return; }