BCP_lp_main_loop.cpp
Go to the documentation of this file.
1 // Copyright (C) 2000, International Business Machines
2 // Corporation and others. All Rights Reserved.
3 #include <cstdio>
4 #include "CoinTime.hpp"
5 #include "BCP_message.hpp"
6 #include "BCP_error.hpp"
7 #include "BCP_lp_node.hpp"
8 #include "BCP_lp.hpp"
9 #include "BCP_lp_user.hpp"
10 #include "BCP_lp_functions.hpp"
11 #include "BCP_lp_result.hpp"
12 #include "BCP_warmstart.hpp"
13 #include "BCP_solution.hpp"
14 
16 {
17  BCP_lp_result& lpres = *p.lp_result;
18  // argument flag for a number of functions. of course, here we invoke those
19  // functions from the main loop, but this flag must be tru if the functions
20  // are invoked from repricing. hence the flag is set here to false.
21  const bool from_repricing = false;
22 
23  /*-----------------------------------------------------------------------*
24  * The main loop -- continue solving relaxations until no new cuts
25  * are found
26  *-----------------------------------------------------------------------*/
27  bool varset_changed = true;
28  bool cutset_changed = true;
29  double time0;
30 
31  double nodeStart = CoinCpuTime();
32 
35  "LP: **** Processing NODE %i on LEVEL %i (from TM) ****\n",
36  p.node->index, p.node->level);
37  // let the user do whatever she wants before the new node starts
39 
40  while (true){
41  ++p.node->iteration_count;
42 
44 
47  "LP: *** Starting iteration %i ***\n",
49 
50  // Solve the lp relaxation and get the results
51  time0 = CoinCpuTime();
52  BCP_lp_check_ub(p);
53  // Whether primal/dual feasibility was affected at the end of an
54  // iteration. See doc of BCP_lp_user::modify_lp_parameters
55  const int changeType = (varset_changed ? 2:0) + (cutset_changed ? 1:0);
56 
57  p.user->modify_lp_parameters(p.lp_solver, changeType, false);
58 #if 0
59  char fname[1000];
60  sprintf(fname, "matrix-%i.%i.%i",
62  p.lp_solver->writeMps(fname, "mps");
63 #endif
64  p.lp_solver->resolve();
65  lpres.get_results(*p.lp_solver);
66  const int tc = lpres.termcode();
67  p.stat.time_lp_solving += CoinCpuTime() - time0;
68 
69  if (varset_changed) {
70  p.node->lb_at_cutgen.clear();
72  p.node->cuts.size(), lpres.objval());
73  }
74 
75  // Display the matrix solution value
77  p.user->print(true, "LP: Matrix size: %i vars x %i cuts\n",
78  static_cast<int>(p.node->vars.size()),
79  static_cast<int>(p.node->cuts.size()));
80  p.user->print(true, "LP: Solution value: %.4f / %i , %i \n",
81  lpres.objval(), tc, lpres.iternum());
82  }
83 
84  // Display the relaxed solution if needed
86  p.user->display_lp_solution(lpres, p.node->vars, p.node->cuts,
87  false);
88  }
89 
90  // Test feasibility (note that it might be infeasible, but the user
91  // might want to generate a heur feas sol anyway)
92  time0 = CoinCpuTime();
93  p.node->quality = lpres.objval();
94 
95  BCP_lp_process_result(p, lpres);
96 
97  BCP_lp_test_feasibility(p, lpres);
98  p.stat.time_feas_testing += CoinCpuTime() - time0;
99 
100  // Update the lower bound
101  const double tlb = BCP_lp_compute_lower_bound(p, lpres);
102  if (tlb > p.node->true_lower_bound)
103  p.node->true_lower_bound = tlb;
104 
105  if (p.over_ub(p.node->true_lower_bound)) {
107 LP: Terminating and fathoming due to proven high cost.\n",
109  return;
110  }
111 
112  // If we get here then we either
113  // - do not generate columns AND the lp value is below the ub
114  // - generate columns
115 
116  if (tc & BCP_ProvenPrimalInf) {
118  "LP: Primal feasibility lost.\n");
119  if (BCP_lp_fathom(p, from_repricing)) {
120  return;
121  }
122  varset_changed = true;
123  continue;
124  }
125 
127  // *FIXME* : for now just throw an exception, but *THINK*
128  p.user->print(true, "LP: ############ Unexpected termcode: %i\n",
129  lpres. termcode());
130  throw BCP_fatal_error("Unexpected termcode in BCP_lp_main_loop!\n");
131  }
132 
133  // We came here, therefore termcode must have been optimal and the
134  // cost cannot be too high. OR, the LP solver has abandoned things and
135  // we want to branch through.
136 
137  if (! (tc & BCP_Abandoned)) {
138  // So termcode must have been optimal and the cost cannot be too
139  // high.
140 
141  // So far we haven't generated any new variables.
142  varset_changed = false;
143 
144  if (BCP_lp_fix_vars(p) ||
145  (p.lp_solver->canDoSimplexInterface() &&
146  !p.lp_solver->basisIsAvailable())) {
147  // during variable fixing primal feasibility is lost (must be due
148  // to logical fixing by the user) OR we can do simplex, but for
149  // some reason the basis is lost (generally when the LP solver
150  // discards the basis if bounds are changed). Go back and resolve,
151  // but keep the same iteration number
152  --p.node->iteration_count;
153  continue;
154  }
155 
156  p.no_more_cuts_cnt = 0;
157  p.no_more_vars_cnt = 0;
159  // If the message passing environment is really parallel (i.e.,
160  // while the CG/CP are working we can do something else) then:
161  // send the current solution to CG, and also to CP if either
162  // - lb_at_cutgen was reset, i.e., we are at the beginning of a
163  // chain, or columns were generated. (This way we'll check the
164  // pool at the top of the root, too, which is unnecessary, but
165  // it doesn't hurt and no big time is lost.)
166  // - or this is the cut_pool_check_freq-th iteration.
167  if (p.node->cg != -1 || p.node->cp != -1) {
168  const BCP_message_tag msgtag = BCP_lp_pack_for_cg(p);
169  if (p.node->cg != -1) {
170  ++p.no_more_cuts_cnt;
171  p.msg_env->send(p.node->cg, msgtag, p.msg_buf);
172  }
173  if (p.node->cp != -1) {
174  if (! (p.node->iteration_count %
176  || varset_changed) {
177  ++p.no_more_cuts_cnt;
178  p.msg_env->send(p.node->cp, msgtag, p.msg_buf);
179  }
180  }
181  }
182  // Similarly, send stuff to the VG/VP
183  if (p.node->vg != -1 || p.node->vp != -1) {
184  const BCP_message_tag msgtag = BCP_lp_pack_for_vg(p);
185  if (p.node->vg != -1) {
186  ++p.no_more_vars_cnt;
187  p.msg_env->send(p.node->vg, msgtag, p.msg_buf);
188  }
189  if (p.node->vp != -1) {
190  if (! (p.node->iteration_count %
192  || cutset_changed) {
193  ++p.no_more_vars_cnt;
194  p.msg_env->send(p.node->cp, msgtag, p.msg_buf);
195  }
196  }
197  }
198  }
199 
201 
202  // Generate and receive the cuts
203  const int cuts_to_add_cnt =
204  BCP_lp_generate_cuts(p, varset_changed, from_repricing);
205  // Generate and receive the vars
206  const int vars_to_add_cnt =
207  BCP_lp_generate_vars(p, cutset_changed, from_repricing);
208 
209  time0 = CoinCpuTime();
210  BCP_solution* sol =
212  p.node->vars, p.node->cuts);
213  p.stat.time_heuristics += CoinCpuTime() - time0;
214  // If the sol is a generic sol then look through the vars in it, and
215  // if any of them has 0 bcpindex then assign an index to it.
216  BCP_solution_generic* gsol = dynamic_cast<BCP_solution_generic*>(sol);
217  if (gsol) {
218  const int size = gsol->_vars.size();
219  for (int i = 0; i < size; ++i) {
220  if (gsol->_vars[i]->bcpind() == 0 &&
221  gsol->_vars[i]->obj_type() == BCP_AlgoObj)
222  gsol->_vars[i]->set_bcpind(-BCP_lp_next_var_index(p));
223  }
224  }
225 
226  if (sol != NULL) {
228  delete sol;
229  if (p.over_ub(p.node->true_lower_bound)) {
231 LP: Terminating and fathoming due to proven high cost (good heur soln!).\n",
233  return;
234  }
235  }
236 
237  const bool verb_cut = p.param(BCP_lp_par::LpVerb_GeneratedCutCount);
238  const bool verb_var = p.param(BCP_lp_par::LpVerb_GeneratedVarCount);
239  // Report how many have been generated
240  if (verb_cut && ! verb_var) {
241  p.user->print(true, "LP: In iteration %i BCP generated",
242  p.node->iteration_count);
243  p.user->print(true, " %i cuts before calling branch()\n",
244  cuts_to_add_cnt);
245  } else if (! verb_cut && verb_var) {
246  p.user->print(true, "LP: In iteration %i BCP generated",
247  p.node->iteration_count);
248  p.user->print(true, " %i vars before calling branch()\n",
249  vars_to_add_cnt);
250  } else if (verb_cut && verb_var) {
251  p.user->print(true, "LP: In iteration %i BCP generated",
252  p.node->iteration_count);
253  p.user->print(true," %i cuts , %i vars before calling branch()\n",
254  cuts_to_add_cnt, vars_to_add_cnt);
255  }
256 
257  if (cuts_to_add_cnt == 0 && vars_to_add_cnt == 0 &&
259  // Display solution if nothing is generated
260  p.user->display_lp_solution(lpres,
261  p.node->vars, p.node->cuts, true);
262  }
263  }
264 
265  // Try to branch
266  switch (BCP_lp_branch(p)){
269  "BCP_lp: Time spent in this node: %15.4f seconds\n",
270  CoinCpuTime() - nodeStart);
271  // Note that BCP_lp_branch() has already sent the node description
272  // to the TM, info is printed, node is cleaned up, so just return
273  return;
274 
277  "BCP_lp: Time spent in this node: %15.4f seconds\n",
278  CoinCpuTime() - nodeStart);
279  nodeStart = CoinCpuTime();
282  "LP: **** Processing NODE %i on LEVEL %i (dived) ****\n",
283  p.node->index, p.node->level);
284  // let the user do whatever she wants before the new node starts
286  // here we don't have to delete cols and rows, it's done as part of
287  // the cleanup during branching.
288  varset_changed = true;
289  cutset_changed = true;
290 
291  break;
292 
294  // got to add things from the local pools
295  const int added_cuts = BCP_lp_add_from_local_cut_pool(p);
296  const int added_vars = BCP_lp_add_from_local_var_pool(p);
297  const bool added_cut = p.param(BCP_lp_par::LpVerb_AddedCutCount);
298  const bool added_var = p.param(BCP_lp_par::LpVerb_AddedVarCount);
299  p.user->print(added_cut && ! added_var,
300  "LP: In iteration %i BCP added %i cuts.\n",
301  p.node->iteration_count, added_cuts);
302  p.user->print(! added_cut && added_var,
303  "LP: In iteration %i BCP added %i vars.\n",
304  p.node->iteration_count, added_vars);
305  p.user->print(added_cut && added_var,
306  "LP: In iteration %i BCP added %i cuts, %i vars.\n",
307  p.node->iteration_count, added_cuts, added_vars);
308  varset_changed = (added_vars > 0);
309  cutset_changed = (added_cuts > 0);
310  // the args are: (p, col_indices, row_indices, force_delete).
311  // Here we don't have col/row_indices to compress, we are not from
312  // fathom and we don't want to force deletion.
313  BCP_lp_delete_cols_and_rows(p, 0, 0, 0, false, false);
314  break;
315  }
316  }
317 }
BCP_message_tag
This enumerative constant describes the message tags different processes of BCP understand.
virtual void send(const int target, const BCP_message_tag tag)=0
Send an empty message (message tag only) to the process given by the frist argument.
BCP_lp_node * node
Description he current search tree node.
Definition: BCP_lp.hpp:168
void print(const bool ifprint, const char *format,...) const
A method to print a message with the process id.
Definition: BCP_lp_user.cpp:38
No branching happend, continue to work on the same node.
Print the number of cuts generated during this iteration (since the LP was resolved last time)...
BCP_vec< BCP_var * > _vars
Vector of variables that are at nonzero level in the solution.
void clear()
Delete every entry.
char param(BCP_lp_par::chr_params key) const
Definition: BCP_lp.hpp:275
int BCP_lp_next_var_index(BCP_lp_prob &p)
Print the number of variables generated during this iteration.
int BCP_lp_generate_vars(BCP_lp_prob &p, bool first_in_loop, const bool from_repricing)
bool BCP_lp_fathom(BCP_lp_prob &p, const bool from_repricing)
BCP_var_set vars
double quality
void BCP_lp_process_result(BCP_lp_prob &p, const BCP_lp_result &lpres)
Definition: BCP_lp_misc.cpp:14
BCP_lp_statistics stat
Definition: BCP_lp.hpp:213
BCP_branching_result BCP_lp_branch(BCP_lp_prob &p)
void BCP_lp_test_feasibility(BCP_lp_prob &p, const BCP_lp_result &lpres)
Definition: BCP_lp_misc.cpp:35
Print the size of the problem matrix and the LP solution value after resolving the LP...
Indicates whether message passing is serial (all processes are on the same processor) or not...
Turn on the user hook &quot;display_lp_solution&quot;.
NO OLD DOC.
Definition: BCP_lp.hpp:102
Print the &quot;Starting iteration x&quot; line.
int BCP_lp_add_from_local_var_pool(BCP_lp_prob &p)
void BCP_lp_perform_fathom(BCP_lp_prob &p, const char *msg, BCP_message_tag msgtag)
double BCP_lp_compute_lower_bound(BCP_lp_prob &p, const BCP_lp_result &lpres)
Definition: BCP_lp_misc.cpp:50
Print the number of variables added from the local variable pool in the curent iteration.
BCP_cut_set cuts
int no_more_cuts_cnt
Definition: BCP_lp.hpp:226
The Variable Pool is queried for columns that improve the formulation after the first LP realxation i...
int iternum() const
BCP_lp_user * user
A class that holds the methods about how to pack things.
Definition: BCP_lp.hpp:131
void BCP_lp_adjust_row_effectiveness(BCP_lp_prob &p)
The node is fathomed, the LP process should wait for a new node.
BCP_message_tag BCP_lp_pack_for_vg(BCP_lp_prob &p)
Definition: BCP_lp_misc.cpp:91
OsiSolverInterface * lp_solver
A class that holds the methods about how to pack things.
Definition: BCP_lp.hpp:137
BCP_lp_result * lp_result
Definition: BCP_lp.hpp:185
double objval() const
virtual BCP_solution * generate_heuristic_solution(const BCP_lp_result &lpres, const BCP_vec< BCP_var * > &vars, const BCP_vec< BCP_cut * > &cuts)
Try to generate a heuristic solution (or return one generated during cut/variable generation...
int BCP_lp_generate_cuts(BCP_lp_prob &p, bool first_in_loop, const bool from_repricing)
BCP_vec< double > lb_at_cutgen
bool BCP_lp_fix_vars(BCP_lp_prob &p)
Branching happened, one of the children is kept and that&#39;s what the LP process will continue to work ...
For each tree node print out how much time was spent on it.
void insert(iterator position, const void *first, const size_t num)
Insert num entries starting from memory location first into the vector from position pos...
The lower bound corresponding to the node is above the upper bound.
bool over_ub(double lb) const
Definition: BCP_lp.hpp:310
int BCP_lp_add_from_local_cut_pool(BCP_lp_prob &p)
void BCP_lp_purge_slack_pool(BCP_lp_prob &p)
Definition: BCP_lp_misc.cpp:25
Print information related to fathoming.
double time_lp_solving
Definition: BCP_lp.hpp:67
Currently there isn&#39;t any error handling in BCP.
Definition: BCP_error.hpp:20
virtual void display_lp_solution(const BCP_lp_result &lp_result, const BCP_vec< BCP_var * > &vars, const BCP_vec< BCP_cut * > &cuts, const bool final_lp_solution)
Display the result of most recent LP optimization.
void BCP_lp_prepare_for_new_node(BCP_lp_prob &p)
void BCP_lp_check_ub(BCP_lp_prob &p)
size_t size() const
Return the current number of entries.
Definition: BCP_vector.hpp:116
Algorithmic object.
Definition: BCP_enum.hpp:53
iterator end()
Return an iterator to the end of the object.
Definition: BCP_vector.hpp:104
double true_lower_bound
BCP_message_tag BCP_lp_pack_for_cg(BCP_lp_prob &p)
Definition: BCP_lp_misc.cpp:75
Turn on the user hook &quot;display_lp_solution&quot; for the last LP relaxation solved at a search tree node...
int no_more_vars_cnt
Definition: BCP_lp.hpp:230
void get_results(OsiSolverInterface &lp_solver)
Get the result from the LP solver.
double time_feas_testing
Definition: BCP_lp.hpp:59
int termcode() const
void BCP_lp_main_loop(BCP_lp_prob &p)
The Cut Pool is queried for violated valid inequalities after the first LP relaxation is solved and t...
BCP_message_environment * msg_env
A class that holds the methods about how to pack things.
Definition: BCP_lp.hpp:139
Print the &quot;Processing NODE x on LEVEL y&quot; line.
This class holds the results after solving an LP relaxation.
double time_heuristics
Definition: BCP_lp.hpp:65
This class holds a MIP feasible primal solution.
void send_feasible_solution(const BCP_solution *sol)
Definition: BCP_lp_user.cpp:77
BCP_buffer msg_buf
Definition: BCP_lp.hpp:239
virtual void modify_lp_parameters(OsiSolverInterface *lp, const int changeType, bool in_strong_branching)
Modify parameters of the LP solver before optimization.
void BCP_lp_delete_cols_and_rows(BCP_lp_prob &p, BCP_lp_branching_object *can, const int added_colnum, const int added_rownum, const bool from_fathom, const bool force_delete)
Print the number of cuts added from the local cut pool in the current iteration.
This is the abstract base class for a solution to a Mixed Integer Programming problem.