Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members

BCP_tm_main.cpp

00001 // Copyright (C) 2000, International Business Machines
00002 // Corporation and others.  All Rights Reserved.
00003 #include <cstdio>
00004 #include <cerrno>
00005 #include <cmath>
00006 #include <queue>
00007 
00008 #include "CoinTime.hpp"
00009 
00010 #include "BCP_os.hpp"
00011 
00012 #include "BCP_USER.hpp"
00013 #include "BCP_string.hpp"
00014 #include "BCP_vector.hpp"
00015 #include "BCP_buffer.hpp"
00016 #include "BCP_message.hpp"
00017 #include "BCP_var.hpp"
00018 #include "BCP_cut.hpp"
00019 #include "BCP_node_change.hpp"
00020 #include "BCP_tm.hpp"
00021 #include "BCP_tm_functions.hpp"
00022 #include "BCP_main_fun.hpp"
00023 
00024 #include "BCP_tm_user.hpp"
00025 #include "BCP_lp_user.hpp"
00026 
00027 #include "BCP_message_single.hpp"
00028 
00029 //#############################################################################
00030 
00031 int main(int argc, char* argv[])
00032 {
00033    USER_initialize* user_init = BCP_user_init();
00034 
00035    BCP_message_environment* msg_env = user_init->msgenv_init();
00036    {
00037      BCP_single_environment* single_env =
00038        dynamic_cast<BCP_single_environment*>(msg_env);
00039      if (single_env) {
00040        // this way when register_process takes over the execution the single
00041        // environment will have access to the command line arguments and can
00042        // parse it.
00043        single_env->set_arguments(argc, argv);
00044      }
00045    }
00046 
00047    BCP_proc_id* my_id = msg_env->register_process();
00048    if (my_id == 0) {
00049       // Using BCP_single_environment will return '0'.
00050       delete msg_env;
00051       throw BCP_fatal_error("Exiting.\n");
00052    }
00053    BCP_proc_id* parent = msg_env->parent_process();
00054 
00055    // The master process takes one command-line argument which is the name of
00056    // the parameter file. The slaves do not take any arguments.
00057    if (! parent) {
00058      BCP_tm_main(msg_env, user_init, my_id, argc, argv);
00059    } else {
00060      if (argc != 1) {
00061        throw BCP_fatal_error("The slaves do not take any argument!\n");
00062      }
00063      BCP_buffer msg_buf;
00064      msg_env->receive(parent, BCP_Msg_AnyMessage, msg_buf, -1);
00065      if (msg_buf.msgtag() != BCP_Msg_ProcessType) {
00066        throw BCP_fatal_error("The first message is not ProcessType!!!\n");
00067      }
00068      // got a new identity, act on it
00069      BCP_process_t ptype;
00070      msg_buf.unpack(ptype);
00071      switch (ptype) {
00072      case BCP_ProcessType_LP:
00073        BCP_lp_main(msg_env, user_init, my_id, parent);
00074        break;
00075      case BCP_ProcessType_CP:
00076        // BCP_cp_main(msg_env, user_init, my_id, parent);
00077        break;
00078      case BCP_ProcessType_VP:
00079        // BCP_vp_main(msg_env, user_init, my_id, parent);
00080        break;
00081      case BCP_ProcessType_CG:
00082        BCP_cg_main(msg_env, user_init, my_id, parent);
00083        break;
00084      case BCP_ProcessType_VG:
00085        BCP_vg_main(msg_env, user_init, my_id, parent);
00086        break;
00087      case BCP_ProcessType_Any:
00088        throw BCP_fatal_error("New process identity is BCP_ProcessType_Any!\n");
00089      case BCP_ProcessType_TM:
00090        throw BCP_fatal_error("New process identity is BCP_ProcessType_TM!\n");
00091      }
00092    }
00093    delete parent;
00094    delete my_id;
00095    delete msg_env;
00096    delete user_init;
00097 
00098    return 0;
00099 }
00100 
00101 //#############################################################################
00102 
00103 void
00104 BCP_tm_main(BCP_message_environment* msg_env,
00105             USER_initialize* user_init,
00106             BCP_proc_id* my_id,
00107             const int argnum, const char* const * arglist)
00108 {
00109    // Start to create the universe... (we don't have a user universe yet).
00110    BCP_tm_prob p;
00111 
00112    // this also reads in the parameters from a file
00113    BCP_tm_parse_command_line(p, argnum, arglist);
00114    
00115    BCP_buffer msg_buf;
00116    p.msg_env = msg_env;
00117    p.start_time = CoinCpuTime();
00118 
00119    FILE* logfile = 0;
00120 
00121    const BCP_string& log = p.par.entry(BCP_tm_par::LogFileName);
00122    if (! (p.par.entry(BCP_tm_par::LogFileName) == "")) {
00123       int len = log.length();
00124       char *logname = new char[len + 300];
00125       memcpy(logname, log.c_str(), len);
00126       memcpy(logname + len, "-tm-", 4);
00127       len += 4;
00128       gethostname(logname + len, 255);
00129       len = strlen(logname);
00130       logname[len++] = '-';
00131       sprintf(logname + len, "%i", static_cast<int>(getpid()));
00132       logfile = freopen(logname, "a", stdout);
00133       if (logfile == 0) {
00134          fprintf(stderr, "Error while redirecting stdout: %i\n", errno);
00135          abort();
00136       }
00137       setvbuf(logfile, NULL, _IOLBF, 0); // make it line buffered
00138       delete[] logname;
00139    } else {
00140       setvbuf(stdout, NULL, _IOLBF, 0); // make it line buffered
00141    }
00142 
00143    // BCP_tm_user_init() returns a BCP_tm_user* and that will be part of p.
00144    // Also, it should take care of every I/O, heuristic startup, etc. the user
00145    // wants to do. Wreak havoc in p if (s)he wants.
00146    // BUT: once this function returns, the processes designated to be part of
00147    // BCP must be idle and waiting for a message. See the
00148    // BCP_slave_process_stub() function below. 
00149    p.user = user_init->tm_init(p, argnum, arglist);
00150    p.user->setTmProblemPointer(&p);
00151 
00152    if (!p.param(BCP_tm_par::DoBranchAndCut)){
00153       printf("\n**********************************************\n");
00154       printf("* Heuristics Finished!!!!!!!                 *\n");
00155       printf("* Now displaying stats and best solution.... *\n");
00156       printf("**********************************************\n\n");
00157       // *FIXME-NOW*
00158       // Print out timing and solution
00159 
00160       // delete parent; -- not needed: TM has no parent, it's a 0 pointer
00161       delete my_id;   my_id = 0;
00162       delete msg_env;   msg_env = 0;
00163       exit(0);
00164    }
00165 
00166    // Fire up the LP/CG/CP/VG/VP processes
00167    // Actually, this is firing up enough copies of self.
00168    BCP_tm_start_processes(p);
00169 
00170    // Notify the LP/CG/CP/VG/VP processes about their identity. Also, send out
00171    // their parameters.
00172    BCP_tm_notify_processes(p);
00173 
00174    // Initialize the number of leaves assigned to CP's and VP's as 0
00175    if (p.param(BCP_tm_par::CpProcessNum) > 0) {
00176       for (int i = p.slaves.cp->size() - 1; i >= 0; --i)
00177          p.leaves_per_cp.
00178             push_back( std::make_pair(p.slaves.cp->procs()[i]->clone(), 0) );
00179    }
00180    if (p.param(BCP_tm_par::VpProcessNum) > 0) {
00181       for (int i = p.slaves.vp->size() - 1; i >= 0; --i)
00182          p.leaves_per_vp.
00183             push_back( std::make_pair(p.slaves.vp->procs()[i]->clone(), 0) );
00184    }
00185 
00186    // Set the core (variables & cuts)
00187    p.core = BCP_tm_create_core(p);
00188    p.core_as_change = new BCP_problem_core_change;
00189    *p.core_as_change = *p.core;
00190    // Send the core description to every process
00191    BCP_tm_distribute_core(p);
00192 
00193    BCP_tm_distribute_user_info(p);
00194 
00195    // Initialize the root of the search tree (can't invoke directly
00196    // p.user->create_root(), b/c the root might contain extra vars/cuts and
00197    // it's better if we take care of inserting them into the appropriate data
00198    // structures.
00199    BCP_tm_node* root = BCP_tm_create_root(p);
00200 
00201    p.next_phase_nodes.push_back(root);
00202    p.search_tree.insert(root);
00203 
00204    BCP_sanity_checks(p);
00205 
00206    //--------------------------------------------------------------------------
00207    // The main loop
00208    //--------------------------------------------------------------------------
00209    bool something_died = false;
00210    for ( p.phase = 0; true ; ++p.phase) {
00211       // insert the nodes in next_phase_nodes into candidates, print out some
00212       // statistics about the previous phase (if there was one) and do some
00213       // other stuff, too.
00214       BCP_tm_tasks_before_new_phase(p);
00215       // do one phase (return true/false depending on success)
00216       something_died = BCP_tm_do_one_phase(p);
00217       // If nothing is left for the next phase or if something has died then
00218       // quit the infinite loop.
00219       if (p.next_phase_nodes.size() == 0 || something_died)
00220          break;
00221    }
00222 
00223    //--------------------------------------------------------------------------
00224    // Everything is done
00225    //--------------------------------------------------------------------------
00226    // first let the processes know that they're not needed in BCP any more,
00227    // they can start idling (this will be used when we'll loop around in TM).
00228    // The processes will respond by sending statistics.
00229    BCP_tm_idle_processes(p);
00230 
00231    BCP_tm_wrapup(&p, 0, 0, 0, true);
00232 
00233    // Finally stop all the processes.
00234    BCP_tm_stop_processes(p);
00235 
00236    if (logfile)
00237       fclose(logfile);
00238 }
00239 
00240 //#############################################################################
00241 
00242 bool BCP_tm_do_one_phase(BCP_tm_prob& p)
00243 {
00244    BCP_buffer& buf = p.msg_buf;
00245    // While there are nodes waiting to be processed (or being processed) we
00246    // don't go to the next phase
00247    while (!p.candidates.empty() > 0 || p.slaves.lp->busy_num() > 0){
00248       // Fill up as many free LP processes as we can
00249       if (BCP_tm_start_new_nodes(p) == BCP_NodeStart_Error)
00250          // Error indicates that something has died
00251          return true;
00252       // Process incoming messages. If there are no active nodes left then
00253       // timeout is set to 0, so we just check the queue, but not wait.
00254       buf.clear();
00255       p.msg_env->receive(BCP_AnyProcess, BCP_Msg_AnyMessage, buf,
00256                          p.slaves.lp->busy_num() == 0 ?
00257                          0 : p.param(BCP_tm_par::TmTimeout));
00258       p.process_message();
00259    }
00260    return false;
00261 }
00262 
00263 //#############################################################################
00264 
00265 BCP_problem_core* BCP_tm_create_core(BCP_tm_prob& p)
00266 {
00267    BCP_vec<BCP_var_core*> bvars;
00268    BCP_vec<BCP_cut_core*> bcuts;
00269    BCP_lp_relax* matrix = 0;
00270 
00271    p.user->initialize_core(bvars, bcuts, matrix);
00272 
00273    const int bvarnum = bvars.size();
00274    if (bvarnum > 0) {
00275       BCP_IndexType i = p.next_var_index_set_start;
00276       for (i = 0; i < bvarnum; ++i) {
00277          BCP_var_core* var = bvars[i];
00278          // make certain that the bounds of integer vars is integer...
00279          if (var->var_type() != BCP_ContinuousVar) {
00280             var->set_lb(floor(var->lb()+1e-8));
00281             var->set_ub(ceil(var->ub()-1e-8));
00282          }
00283          var->set_bcpind(i);
00284          p.vars[i] = new BCP_var_core(*var);
00285       }
00286       p.next_var_index_set_start = i;
00287    }
00288 
00289    const int bcutnum = bcuts.size();
00290    if (bcutnum > 0) {
00291       BCP_IndexType i = p.next_cut_index_set_start;
00292       for (i = 0; i < bcutnum; ++i) {
00293          BCP_cut_core* cut = bcuts[i];
00294          cut->set_bcpind(i);
00295          p.cuts[i] = new BCP_cut_core(*cut);
00296       }
00297       p.next_cut_index_set_start = i;
00298    }
00299 
00300    if (!matrix)
00301       matrix = new BCP_lp_relax;
00302 
00303    return new BCP_problem_core(bvars, bcuts, matrix);
00304 }
00305    
00306 //#############################################################################
00307 
00308 static inline BCP_cut*
00309 BCP_tm_unpack_root_cut(BCP_tm_prob& tm)
00310 {
00311   BCP_cut* cut;
00312   BCP_buffer& buf = tm.msg_buf;
00313   BCP_object_t obj_t;
00314   int index;
00315   double lb, ub;
00316   BCP_obj_status stat;
00317   buf.unpack(obj_t).unpack(stat).unpack(lb).unpack(ub);
00318   switch (obj_t) {
00319   case BCP_CoreObj:
00320     cut = new BCP_cut_core(lb, ub);
00321     break;
00322   case BCP_IndexedObj:
00323     buf.unpack(index);
00324     cut = new BCP_cut_indexed(index, lb, ub);
00325     break;
00326   case BCP_AlgoObj:
00327     cut = tm.user->unpack_cut_algo(buf);
00328     cut->change_bounds(lb, ub);
00329     break;
00330   default:
00331     throw BCP_fatal_error("BCP_tm_unpack_root_cut: unexpected obj_t.\n");
00332   }
00333   cut->set_bcpind(0);
00334   cut->set_status(stat);
00335 
00336   return cut;
00337 }
00338 
00339 //#############################################################################
00340 
00341 BCP_tm_node* BCP_tm_create_root(BCP_tm_prob& p)
00342 {
00343    BCP_vec<BCP_var*> added_vars;
00344    BCP_vec<BCP_cut*> added_cuts;
00345    BCP_user_data* user_data = 0;
00346    BCP_pricing_status pricing_status = BCP_PriceNothing;
00347 
00348    // If the root cuts are saved then read them in
00349    const BCP_string& cutfile = p.param(BCP_tm_par::ReadRootCutsFrom);
00350    if (cutfile.length() > 0) {
00351       BCP_buffer& buf = p.msg_buf;
00352       buf.clear();
00353       FILE* f = fopen(cutfile.c_str(), "r");
00354       size_t size;
00355       if (fread(&size, 1, sizeof(size), f) != sizeof(size))
00356          throw BCP_fatal_error("ReadRootCutsFrom read error.\n");
00357       char * data = new char[size];
00358       if (fread(data, 1, size, f) != size)
00359          throw BCP_fatal_error("ReadRootCutsFrom read error.\n");
00360       fclose(f);
00361       buf.set_content(data, size, 0, BCP_Msg_NoMessage);
00362       delete[] data;
00363       
00364       int num;
00365       buf.unpack(num);
00366       added_cuts.reserve(added_cuts.size() + num);
00367       for (int i = 0; i < num; ++i) {
00368          added_cuts.unchecked_push_back(BCP_tm_unpack_root_cut(p));
00369       }
00370    }
00371 
00372    p.user->create_root(added_vars, added_cuts, user_data, pricing_status);
00373 
00374    BCP_node_change* root_changes = new BCP_node_change;
00375    root_changes->core_change._storage = BCP_Storage_WrtCore;
00376    root_changes->indexed_pricing.set_status(pricing_status);
00377 
00378    if (added_vars.size() > 0) {
00379       BCP_vec<BCP_var*>::iterator vi = added_vars.begin();
00380       const BCP_vec<BCP_var*>::iterator lastvi = added_vars.end();
00381       root_changes->var_change._change.reserve(added_vars.size());
00382       BCP_IndexType i = p.next_var_index_set_start;
00383       while (vi != lastvi) {
00384          BCP_var* var = *vi;
00385          p.vars[i] = var;
00386          var->set_bcpind(i++);
00387          // make certain that the bounds of integer vars is integer...
00388          if (var->var_type() != BCP_ContinuousVar) {
00389             var->set_lb(floor(var->lb()+1e-8));
00390             var->set_ub(ceil(var->ub()-1e-8));
00391          }
00392          root_changes->var_change._change.
00393             unchecked_push_back(BCP_obj_change(var->lb(), var->ub(),
00394                                                var->status()));
00395          ++vi;
00396       }
00397       p.next_var_index_set_start = i;
00398       root_changes->var_change._new_vars.swap(added_vars);
00399    }
00400 
00401    if (added_cuts.size() > 0) {
00402       BCP_vec<BCP_cut*>::iterator ci = added_cuts.begin();
00403       const BCP_vec<BCP_cut*>::iterator lastci = added_cuts.end();
00404       root_changes->cut_change._change.reserve(added_cuts.size());
00405       BCP_IndexType i = p.next_cut_index_set_start;
00406       while (ci != lastci) {
00407          BCP_cut* cut = *ci;
00408          p.cuts[i] = cut;
00409          cut->set_bcpind(i++);
00410          root_changes->cut_change._change.
00411             unchecked_push_back(BCP_obj_change(cut->lb(), cut->ub(),
00412                                                cut->status()));
00413          ++ci;
00414       }
00415       p.next_cut_index_set_start = i;
00416       root_changes->cut_change._new_cuts.swap(added_cuts);
00417    }
00418 
00419    BCP_tm_node* root = new BCP_tm_node(0, root_changes);
00420 
00421    root->_user_data = user_data;
00422 
00423    return root;
00424 }
00425 
00426 //#############################################################################
00427 
00428 void BCP_tm_tasks_before_new_phase(BCP_tm_prob& p)
00429 {
00430    if (p.param(BCP_tm_par::TmVerb_NewPhaseStart)) {
00431       printf("############################################################\n");
00432       printf("TM: Starting phase %i\n", p.phase);
00433       printf("############################################################\n");
00434    }
00435 
00436    BCP_tm_node* root = 0;
00437 
00438    if (p.phase > 0) {
00439       // Notify the LPs about the start of the new phase and get back the
00440       // timing data for the previous phase
00441       BCP_tm_notify_about_new_phase(p);
00442 
00443       if (p.phase == 1 &&
00444           p.search_tree.size() > 1 &&
00445           p.param(BCP_tm_par::PriceInRootBeforePhase2)) {
00446          // note that pricing the root won't mess up ANYTHING in the storage
00447          // of the root, except that it'll send back the new indexed_pricing
00448          // field correctly put together, so just unpacking it in
00449          // BCP_tm_prob::process_message will create a correct new root
00450          // description.
00451          p.flags.root_pricing_unpacked = false;
00452          p.current_phase_colgen = BCP_GenerateColumns;
00453          root = p.search_tree.root();
00454          root->_desc->indexed_pricing.
00455            set_status(BCP_PriceAfterLastIndexedToPrice);
00456 #ifdef BCP_DEBUG
00457          if (root->lp != 0 || root->cg != 0 || root->vg != 0)
00458             throw BCP_fatal_error("\
00459 TM: At least on of lp/cg/vg of the root is non-0 (before repricing).\n");
00460 #endif
00461          // cp and vp are left what they were
00462          if (! BCP_tm_assign_processes(p, root)) {
00463             // *LATER* : oops, something has died. do sthing
00464          }
00465          // send out the root to be priced
00466          BCP_tm_send_node(p, root, BCP_Msg_RootToPrice);
00467       }
00468 
00469       // print statistics about the previous phase
00470       BCP_tm_wrapup(&p, 0, 0, 0, false); // false refers to not being final
00471 
00472       // trim the tree if needed and possible
00473       if (p.param(BCP_tm_par::TrimTreeBeforeNewPhase) && p.has_ub())
00474          BCP_tm_trim_tree_wrapper(p, true /* called between phases */);
00475    }
00476 
00477    // while the LP (maybe) working on repricing, build up candidates.
00478    // initialize the candidate list comparison function.
00479    p.current_phase_colgen = BCP_DoNotGenerateColumns_Fathom;
00480    p.user->init_new_phase(p.phase, p.current_phase_colgen);
00481 
00482    for (int i = p.next_phase_nodes.size() - 1; i >= 0; --i) {
00483      p.candidates.insert(p.next_phase_nodes[i]);
00484    }
00485 
00486    p.next_phase_nodes.clear();
00487 
00488    if (root && !p.flags.root_pricing_unpacked) {
00489       // if root is not 0 then we have sent out the root for pricing and
00490       // now we have to receive the results unless it has been already
00491       // unpacked (this happens in a single process environment).
00492       p.msg_env->receive(root->lp, BCP_Msg_PricedRoot, p.msg_buf, -1);
00493       BCP_tm_unpack_priced_root(p, p.msg_buf);
00494    }
00495 
00496    // Reset column generation if PriceInRootBeforePhase2
00497    if (p.phase == 0 && p.param(BCP_tm_par::PriceInRootBeforePhase2)) {
00498       if (p.current_phase_colgen != BCP_DoNotGenerateColumns_Send) {
00499          printf("#########################################################\n");
00500          printf("TM: *WARNING*  *WARNING*  *WARNING*  *WARNING*  *WARNING*\n");
00501          printf("    PriceInRootBeforePhase2 is set yet the column \n");
00502          printf("    generation strategy is set to ");
00503          switch (p.current_phase_colgen) {
00504           case BCP_DoNotGenerateColumns_Fathom:
00505             printf("BCP_DoNotGenerateColumns_Fathom"); break;
00506           case BCP_GenerateColumns:
00507             printf("BCP_GenerateColumns"); break;
00508           case BCP_DoNotGenerateColumns_Send:
00509             break;
00510          }
00511          printf(" in the first phase!\n");
00512          printf("   The strategy is reset to BCP_DoNotGenerateColumns_Send\n");
00513          printf("#########################################################\n");
00514       }
00515    }
00516 }
00517 
00518 //#############################################################################
00519 
00520 

Generated on Wed Dec 3 14:32:31 2003 for BCP by doxygen 1.3.5