|
PPL
0.12.1
|
00001 /* MIP_Problem class implementation: non-inline functions. 00002 Copyright (C) 2001-2010 Roberto Bagnara <bagnara@cs.unipr.it> 00003 Copyright (C) 2010-2012 BUGSENG srl (http://bugseng.com) 00004 00005 This file is part of the Parma Polyhedra Library (PPL). 00006 00007 The PPL is free software; you can redistribute it and/or modify it 00008 under the terms of the GNU General Public License as published by the 00009 Free Software Foundation; either version 3 of the License, or (at your 00010 option) any later version. 00011 00012 The PPL is distributed in the hope that it will be useful, but WITHOUT 00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 00015 for more details. 00016 00017 You should have received a copy of the GNU General Public License 00018 along with this program; if not, write to the Free Software Foundation, 00019 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA. 00020 00021 For the most up-to-date information see the Parma Polyhedra Library 00022 site: http://bugseng.com/products/ppl/ . */ 00023 00024 #include "ppl-config.h" 00025 #include "MIP_Problem.defs.hh" 00026 #include "globals.defs.hh" 00027 #include "Checked_Number.defs.hh" 00028 #include "Linear_Expression.defs.hh" 00029 #include "Constraint.defs.hh" 00030 #include "Constraint_System.defs.hh" 00031 #include "Constraint_System.inlines.hh" 00032 #include "Generator.defs.hh" 00033 #include "Scalar_Products.defs.hh" 00034 #include <stdexcept> 00035 #include <deque> 00036 #include <vector> 00037 #include <algorithm> 00038 #include <cmath> 00039 00040 // TODO: Remove this when the sparse working cost has been tested enough. 00041 #if PPL_USE_SPARSE_MATRIX 00042 00043 // These are needed for the linear_combine() method that takes a Dense_Row and 00044 // a Sparse_Row. 00045 #include "Dense_Row.defs.hh" 00046 #include "Sparse_Row.defs.hh" 00047 00048 #endif // PPL_USE_SPARSE_MATRIX 00049 00050 #ifndef PPL_NOISY_SIMPLEX 00051 #define PPL_NOISY_SIMPLEX 0 00052 #endif 00053 00054 #ifndef PPL_SIMPLEX_USE_MIP_HEURISTIC 00055 #define PPL_SIMPLEX_USE_MIP_HEURISTIC 1 00056 #endif 00057 00058 #if PPL_NOISY_SIMPLEX 00059 #include <iostream> 00060 #endif 00061 00062 00063 namespace PPL = Parma_Polyhedra_Library; 00064 00065 #if PPL_NOISY_SIMPLEX 00066 namespace { 00067 00068 unsigned long num_iterations = 0; 00069 00070 unsigned mip_recursion_level = 0; 00071 00072 } // namespace 00073 #endif // PPL_NOISY_SIMPLEX 00074 00075 PPL::MIP_Problem::MIP_Problem(const dimension_type dim) 00076 : external_space_dim(dim), 00077 internal_space_dim(0), 00078 tableau(), 00079 working_cost(0, Row_Flags()), 00080 mapping(), 00081 base(), 00082 status(PARTIALLY_SATISFIABLE), 00083 pricing(PRICING_STEEPEST_EDGE_FLOAT), 00084 initialized(false), 00085 input_cs(), 00086 inherited_constraints(0), 00087 first_pending_constraint(0), 00088 input_obj_function(), 00089 opt_mode(MAXIMIZATION), 00090 last_generator(point()), 00091 i_variables() { 00092 // Check for space dimension overflow. 00093 if (dim > max_space_dimension()) 00094 throw std::length_error("PPL::MIP_Problem::MIP_Problem(dim, cs, obj, " 00095 "mode):\n" 00096 "dim exceeds the maximum allowed " 00097 "space dimension."); 00098 PPL_ASSERT(OK()); 00099 } 00100 00101 PPL::MIP_Problem::MIP_Problem(const dimension_type dim, 00102 const Constraint_System& cs, 00103 const Linear_Expression& obj, 00104 const Optimization_Mode mode) 00105 : external_space_dim(dim), 00106 internal_space_dim(0), 00107 tableau(), 00108 working_cost(0, Row_Flags()), 00109 mapping(), 00110 base(), 00111 status(PARTIALLY_SATISFIABLE), 00112 pricing(PRICING_STEEPEST_EDGE_FLOAT), 00113 initialized(false), 00114 input_cs(), 00115 inherited_constraints(0), 00116 first_pending_constraint(0), 00117 input_obj_function(obj), 00118 opt_mode(mode), 00119 last_generator(point()), 00120 i_variables() { 00121 // Check for space dimension overflow. 00122 if (dim > max_space_dimension()) 00123 throw std::length_error("PPL::MIP_Problem::MIP_Problem(dim, cs, obj, " 00124 "mode):\n" 00125 "dim exceeds the maximum allowed" 00126 "space dimension."); 00127 // Check the objective function. 00128 if (obj.space_dimension() > dim) { 00129 std::ostringstream s; 00130 s << "PPL::MIP_Problem::MIP_Problem(dim, cs, obj," 00131 << " mode):\n" 00132 << "obj.space_dimension() == "<< obj.space_dimension() 00133 << " exceeds dim == "<< dim << "."; 00134 throw std::invalid_argument(s.str()); 00135 } 00136 // Check the constraint system. 00137 if (cs.space_dimension() > dim) { 00138 std::ostringstream s; 00139 s << "PPL::MIP_Problem::MIP_Problem(dim, cs, obj, mode):\n" 00140 << "cs.space_dimension == " << cs.space_dimension() 00141 << " exceeds dim == " << dim << "."; 00142 throw std::invalid_argument(s.str()); 00143 } 00144 if (cs.has_strict_inequalities()) 00145 throw std::invalid_argument("PPL::MIP_Problem::" 00146 "MIP_Problem(d, cs, obj, m):\n" 00147 "cs contains strict inequalities."); 00148 // Actually copy the constraints. 00149 for (Constraint_System::const_iterator 00150 i = cs.begin(), i_end = cs.end(); i != i_end; ++i) 00151 add_constraint_helper(*i); 00152 00153 PPL_ASSERT(OK()); 00154 } 00155 00156 void 00157 PPL::MIP_Problem::add_constraint(const Constraint& c) { 00158 if (space_dimension() < c.space_dimension()) { 00159 std::ostringstream s; 00160 s << "PPL::MIP_Problem::add_constraint(c):\n" 00161 << "c.space_dimension() == "<< c.space_dimension() << " exceeds " 00162 "this->space_dimension == " << space_dimension() << "."; 00163 throw std::invalid_argument(s.str()); 00164 } 00165 if (c.is_strict_inequality()) 00166 throw std::invalid_argument("PPL::MIP_Problem::add_constraint(c):\n" 00167 "c is a strict inequality."); 00168 add_constraint_helper(c); 00169 if (status != UNSATISFIABLE) 00170 status = PARTIALLY_SATISFIABLE; 00171 PPL_ASSERT(OK()); 00172 } 00173 00174 void 00175 PPL::MIP_Problem::add_constraints(const Constraint_System& cs) { 00176 if (space_dimension() < cs.space_dimension()) { 00177 std::ostringstream s; 00178 s << "PPL::MIP_Problem::add_constraints(cs):\n" 00179 << "cs.space_dimension() == " << cs.space_dimension() 00180 << " exceeds this->space_dimension() == " << this->space_dimension() 00181 << "."; 00182 throw std::invalid_argument(s.str()); 00183 } 00184 if (cs.has_strict_inequalities()) 00185 throw std::invalid_argument("PPL::MIP_Problem::add_constraints(cs):\n" 00186 "cs contains strict inequalities."); 00187 for (Constraint_System::const_iterator 00188 i = cs.begin(), i_end = cs.end(); i != i_end; ++i) 00189 add_constraint_helper(*i); 00190 if (status != UNSATISFIABLE) 00191 status = PARTIALLY_SATISFIABLE; 00192 PPL_ASSERT(OK()); 00193 } 00194 00195 void 00196 PPL::MIP_Problem::set_objective_function(const Linear_Expression& obj) { 00197 if (space_dimension() < obj.space_dimension()) { 00198 std::ostringstream s; 00199 s << "PPL::MIP_Problem::set_objective_function(obj):\n" 00200 << "obj.space_dimension() == " << obj.space_dimension() 00201 << " exceeds this->space_dimension == " << space_dimension() 00202 << "."; 00203 throw std::invalid_argument(s.str()); 00204 } 00205 input_obj_function = obj; 00206 if (status == UNBOUNDED || status == OPTIMIZED) 00207 status = SATISFIABLE; 00208 PPL_ASSERT(OK()); 00209 } 00210 00211 const PPL::Generator& 00212 PPL::MIP_Problem::feasible_point() const { 00213 if (is_satisfiable()) 00214 return last_generator; 00215 else 00216 throw std::domain_error("PPL::MIP_Problem::feasible_point():\n" 00217 "*this is not satisfiable."); 00218 } 00219 00220 const PPL::Generator& 00221 PPL::MIP_Problem::optimizing_point() const { 00222 if (solve() == OPTIMIZED_MIP_PROBLEM) 00223 return last_generator; 00224 else 00225 throw std::domain_error("PPL::MIP_Problem::optimizing_point():\n" 00226 "*this does not have an optimizing point."); 00227 } 00228 00229 bool 00230 PPL::MIP_Problem::is_satisfiable() const { 00231 // Check `status' to filter out trivial cases. 00232 switch (status) { 00233 case UNSATISFIABLE: 00234 PPL_ASSERT(OK()); 00235 return false; 00236 case SATISFIABLE: 00237 // Intentionally fall through 00238 case UNBOUNDED: 00239 // Intentionally fall through. 00240 case OPTIMIZED: 00241 PPL_ASSERT(OK()); 00242 return true; 00243 case PARTIALLY_SATISFIABLE: 00244 { 00245 MIP_Problem& x = const_cast<MIP_Problem&>(*this); 00246 // LP case. 00247 if (x.i_variables.empty()) 00248 return x.is_lp_satisfiable(); 00249 00250 // MIP case. 00251 { 00252 // Temporarily relax the MIP into an LP problem. 00253 RAII_Temporary_Real_Relaxation relaxed(x); 00254 Generator p = point(); 00255 relaxed.lp.is_lp_satisfiable(); 00256 #if PPL_NOISY_SIMPLEX 00257 mip_recursion_level = 0; 00258 #endif // PPL_NOISY_SIMPLEX 00259 if (is_mip_satisfiable(relaxed.lp, relaxed.i_vars, p)) { 00260 x.last_generator = p; 00261 x.status = SATISFIABLE; 00262 } 00263 else 00264 x.status = UNSATISFIABLE; 00265 } // `relaxed' destroyed here: relaxation automatically reset. 00266 return (x.status == SATISFIABLE); 00267 } 00268 } 00269 // We should not be here! 00270 PPL_UNREACHABLE; 00271 return false; 00272 } 00273 00274 PPL::MIP_Problem_Status 00275 PPL::MIP_Problem::solve() const{ 00276 switch (status) { 00277 case UNSATISFIABLE: 00278 PPL_ASSERT(OK()); 00279 return UNFEASIBLE_MIP_PROBLEM; 00280 case UNBOUNDED: 00281 PPL_ASSERT(OK()); 00282 return UNBOUNDED_MIP_PROBLEM; 00283 case OPTIMIZED: 00284 PPL_ASSERT(OK()); 00285 return OPTIMIZED_MIP_PROBLEM; 00286 case SATISFIABLE: 00287 // Intentionally fall through 00288 case PARTIALLY_SATISFIABLE: 00289 { 00290 MIP_Problem& x = const_cast<MIP_Problem&>(*this); 00291 if (x.i_variables.empty()) { 00292 // LP case. 00293 if (x.is_lp_satisfiable()) { 00294 x.second_phase(); 00295 if (x.status == UNBOUNDED) 00296 return UNBOUNDED_MIP_PROBLEM; 00297 else { 00298 PPL_ASSERT(x.status == OPTIMIZED); 00299 return OPTIMIZED_MIP_PROBLEM; 00300 } 00301 } 00302 return UNFEASIBLE_MIP_PROBLEM; 00303 } 00304 00305 // MIP case. 00306 MIP_Problem_Status return_value; 00307 Generator g = point(); 00308 { 00309 // Temporarily relax the MIP into an LP problem. 00310 RAII_Temporary_Real_Relaxation relaxed(x); 00311 if (relaxed.lp.is_lp_satisfiable()) 00312 relaxed.lp.second_phase(); 00313 else { 00314 x.status = UNSATISFIABLE; 00315 // NOTE: `relaxed' destroyed: relaxation automatically reset. 00316 return UNFEASIBLE_MIP_PROBLEM; 00317 } 00318 PPL_DIRTY_TEMP(mpq_class, incumbent_solution); 00319 bool have_incumbent_solution = false; 00320 00321 MIP_Problem lp_copy(relaxed.lp, Inherit_Constraints()); 00322 PPL_ASSERT(lp_copy.integer_space_dimensions().empty()); 00323 return_value = solve_mip(have_incumbent_solution, 00324 incumbent_solution, g, 00325 lp_copy, relaxed.i_vars); 00326 } // `relaxed' destroyed here: relaxation automatically reset. 00327 00328 switch (return_value) { 00329 case UNFEASIBLE_MIP_PROBLEM: 00330 x.status = UNSATISFIABLE; 00331 break; 00332 case UNBOUNDED_MIP_PROBLEM: 00333 x.status = UNBOUNDED; 00334 // A feasible point has been set in `solve_mip()', so that 00335 // a call to `feasible_point' will be successful. 00336 x.last_generator = g; 00337 break; 00338 case OPTIMIZED_MIP_PROBLEM: 00339 x.status = OPTIMIZED; 00340 // Set the internal generator. 00341 x.last_generator = g; 00342 break; 00343 } 00344 PPL_ASSERT(OK()); 00345 return return_value; 00346 } 00347 } 00348 // We should not be here! 00349 PPL_UNREACHABLE; 00350 return UNFEASIBLE_MIP_PROBLEM; 00351 } 00352 00353 void 00354 PPL::MIP_Problem::add_space_dimensions_and_embed(const dimension_type m) { 00355 // The space dimension of the resulting MIP problem should not 00356 // overflow the maximum allowed space dimension. 00357 if (m > max_space_dimension() - space_dimension()) 00358 throw std::length_error("PPL::MIP_Problem::" 00359 "add_space_dimensions_and_embed(m):\n" 00360 "adding m new space dimensions exceeds " 00361 "the maximum allowed space dimension."); 00362 external_space_dim += m; 00363 if (status != UNSATISFIABLE) 00364 status = PARTIALLY_SATISFIABLE; 00365 PPL_ASSERT(OK()); 00366 } 00367 00368 void 00369 PPL::MIP_Problem 00370 ::add_to_integer_space_dimensions(const Variables_Set& i_vars) { 00371 if (i_vars.space_dimension() > external_space_dim) 00372 throw std::invalid_argument("PPL::MIP_Problem::" 00373 "add_to_integer_space_dimension(i_vars):\n" 00374 "*this and i_vars are dimension" 00375 "incompatible."); 00376 const dimension_type original_size = i_variables.size(); 00377 i_variables.insert(i_vars.begin(), i_vars.end()); 00378 // If a new integral variable was inserted, set the internal status to 00379 // PARTIALLY_SATISFIABLE. 00380 if (i_variables.size() != original_size && status != UNSATISFIABLE) 00381 status = PARTIALLY_SATISFIABLE; 00382 } 00383 00384 bool 00385 PPL::MIP_Problem::is_in_base(const dimension_type var_index, 00386 dimension_type& row_index) const { 00387 for (row_index = base.size(); row_index-- > 0; ) 00388 if (base[row_index] == var_index) 00389 return true; 00390 return false; 00391 } 00392 00393 PPL::dimension_type 00394 PPL::MIP_Problem::merge_split_variable(dimension_type var_index) { 00395 // Initialize the return value to a dummy index. 00396 dimension_type unfeasible_tableau_row = not_a_dimension(); 00397 00398 const dimension_type removing_column = mapping[1+var_index].second; 00399 00400 // Check if the negative part of the split variable is in base: 00401 // if so, the corresponding row of the tableau becomes non-feasible. 00402 { 00403 dimension_type base_index; 00404 if (is_in_base(removing_column, base_index)) { 00405 // Set the return value. 00406 unfeasible_tableau_row = base_index; 00407 // Reset base[base_index] to zero to remember non-feasibility. 00408 base[base_index] = 0; 00409 // Since the negative part of the variable is in base, 00410 // the positive part can not be in base too. 00411 PPL_ASSERT(!is_in_base(mapping[1+var_index].first, base_index)); 00412 } 00413 } 00414 00415 tableau.remove_column(removing_column); 00416 00417 // var_index is no longer split. 00418 mapping[1+var_index].second = 0; 00419 00420 // Adjust data structures, `shifting' the proper columns to the left by 1. 00421 const dimension_type base_size = base.size(); 00422 for (dimension_type i = base_size; i-- > 0; ) { 00423 if (base[i] > removing_column) 00424 --base[i]; 00425 } 00426 const dimension_type mapping_size = mapping.size(); 00427 for (dimension_type i = mapping_size; i-- > 0; ) { 00428 if (mapping[i].first > removing_column) 00429 --mapping[i].first; 00430 if (mapping[i].second > removing_column) 00431 --mapping[i].second; 00432 } 00433 00434 return unfeasible_tableau_row; 00435 } 00436 00437 bool 00438 PPL::MIP_Problem::is_satisfied(const Constraint& c, const Generator& g) { 00439 // Scalar_Products::sign() requires the second argument to be at least 00440 // as large as the first one. 00441 int sp_sign 00442 = (g.space_dimension() <= c.space_dimension()) 00443 ? Scalar_Products::sign(g, c) 00444 : Scalar_Products::sign(c, g); 00445 return c.is_inequality() ? (sp_sign >= 0) : (sp_sign == 0); 00446 } 00447 00448 bool 00449 PPL::MIP_Problem::is_saturated(const Constraint& c, const Generator& g) { 00450 // Scalar_Products::sign() requires the space dimension of the second 00451 // argument to be at least as large as the one of the first one. 00452 int sp_sign 00453 = (g.space_dimension() <= c.space_dimension()) 00454 ? Scalar_Products::sign(g, c) 00455 : Scalar_Products::sign(c, g); 00456 return sp_sign == 0; 00457 } 00458 00459 bool 00460 PPL::MIP_Problem 00461 ::parse_constraints(dimension_type& additional_tableau_rows, 00462 dimension_type& additional_slack_variables, 00463 std::deque<bool>& is_tableau_constraint, 00464 std::deque<bool>& is_satisfied_inequality, 00465 std::deque<bool>& is_nonnegative_variable, 00466 std::deque<bool>& is_remergeable_variable) const { 00467 // Initially all containers are empty. 00468 PPL_ASSERT(is_tableau_constraint.empty() 00469 && is_satisfied_inequality.empty() 00470 && is_nonnegative_variable.empty() 00471 && is_remergeable_variable.empty()); 00472 00473 const dimension_type cs_space_dim = external_space_dim; 00474 const dimension_type cs_num_rows = input_cs.size(); 00475 const dimension_type cs_num_pending 00476 = cs_num_rows - first_pending_constraint; 00477 00478 // Counters determining the change in dimensions of the tableau: 00479 // initialized here, they will be updated while examining `input_cs'. 00480 additional_tableau_rows = cs_num_pending; 00481 additional_slack_variables = 0; 00482 00483 // Resize containers appropriately. 00484 00485 // On exit, `is_tableau_constraint[i]' will be true if and only if 00486 // `input_cs[first_pending_constraint + i]' is neither a tautology 00487 // (e.g., 1 >= 0) nor a non-negativity constraint (e.g., X >= 0). 00488 is_tableau_constraint.insert(is_tableau_constraint.end(), 00489 cs_num_pending, true); 00490 00491 // On exit, `is_satisfied_inequality[i]' will be true if and only if 00492 // `input_cs[first_pending_constraint + i]' is an inequality and it is 00493 // satisfied by `last_generator'. 00494 is_satisfied_inequality.insert(is_satisfied_inequality.end(), 00495 cs_num_pending, false); 00496 00497 // On exit, `is_nonnegative_variable[j]' will be true if and only if 00498 // Variable(j) is bound to be nonnegative in `input_cs'. 00499 is_nonnegative_variable.insert(is_nonnegative_variable.end(), 00500 cs_space_dim, false); 00501 00502 // On exit, `is_remergeable_variable[j]' will be true if and only if 00503 // Variable(j) was initially split and is now remergeable. 00504 is_remergeable_variable.insert(is_remergeable_variable.end(), 00505 internal_space_dim, false); 00506 00507 // Check for variables that are already known to be nonnegative 00508 // due to non-pending constraints. 00509 const dimension_type mapping_size = mapping.size(); 00510 if (mapping_size > 0) { 00511 // Note: mapping[0] is associated to the cost function. 00512 for (dimension_type i = std::min(mapping_size - 1, cs_space_dim); 00513 i-- > 0; ) 00514 if (mapping[i + 1].second == 0) 00515 is_nonnegative_variable[i] = true; 00516 } 00517 00518 // Process each pending constraint in `input_cs' and 00519 // - detect variables that are constrained to be nonnegative; 00520 // - detect (non-negativity or tautology) pending constraints that 00521 // will not be part of the tableau; 00522 // - count the number of new slack variables. 00523 for (dimension_type i = cs_num_rows; i-- > first_pending_constraint; ) { 00524 const Constraint& cs_i = *(input_cs[i]); 00525 bool found_a_nonzero_coeff = false; 00526 bool found_many_nonzero_coeffs = false; 00527 dimension_type nonzero_coeff_column_index = 0; 00528 for (dimension_type sd = cs_i.space_dimension(); sd-- > 0; ) { 00529 if (cs_i.coefficient(Variable(sd)) != 0) { 00530 if (found_a_nonzero_coeff) { 00531 found_many_nonzero_coeffs = true; 00532 if (cs_i.is_inequality()) 00533 ++additional_slack_variables; 00534 break; 00535 } 00536 else { 00537 nonzero_coeff_column_index = sd + 1; 00538 found_a_nonzero_coeff = true; 00539 } 00540 } 00541 } 00542 // If more than one coefficient is nonzero, 00543 // continue with next constraint. 00544 if (found_many_nonzero_coeffs) { 00545 // CHECKME: Is it true that in the first phase we can apply 00546 // `is_satisfied()' with the generator `point()'? If so, the following 00547 // code works even if we do not have a feasible point. 00548 // Check for satisfiability of the inequality. This can be done if we 00549 // have a feasible point of *this. 00550 if (cs_i.is_inequality() && is_satisfied(cs_i, last_generator)) 00551 is_satisfied_inequality[i - first_pending_constraint] = true; 00552 continue; 00553 } 00554 00555 if (!found_a_nonzero_coeff) { 00556 // All coefficients are 0. 00557 // The constraint is either trivially true or trivially false. 00558 if (cs_i.is_inequality()) { 00559 if (cs_i.inhomogeneous_term() < 0) 00560 // A constraint such as -1 >= 0 is trivially false. 00561 return false; 00562 } 00563 else 00564 // The constraint is an equality. 00565 if (cs_i.inhomogeneous_term() != 0) 00566 // A constraint such as 1 == 0 is trivially false. 00567 return false; 00568 // Here the constraint is trivially true. 00569 is_tableau_constraint[i - first_pending_constraint] = false; 00570 --additional_tableau_rows; 00571 continue; 00572 } 00573 else { 00574 // Here we have only one nonzero coefficient. 00575 /* 00576 00577 We have the following methods: 00578 A) Do split the variable and do add the constraint 00579 in the tableau. 00580 B) Do not split the variable and do add the constraint 00581 in the tableau. 00582 C) Do not split the variable and do not add the constraint 00583 in the tableau. 00584 00585 Let the constraint be (a*v + b relsym 0). 00586 These are the 12 possible combinations we can have: 00587 a | b | relsym | method 00588 ---------------------------------- 00589 1) >0 | >0 | >= | A 00590 2) >0 | >0 | == | A 00591 3) <0 | <0 | >= | A 00592 4) >0 | =0 | == | B 00593 5) >0 | <0 | == | B 00594 Note: <0 | >0 | == | impossible by strong normalization 00595 Note: <0 | =0 | == | impossible by strong normalization 00596 Note: <0 | <0 | == | impossible by strong normalization 00597 6) >0 | <0 | >= | B 00598 7) >0 | =0 | >= | C 00599 8) <0 | >0 | >= | A 00600 9) <0 | =0 | >= | A 00601 00602 The next lines will apply the correct method to each case. 00603 */ 00604 00605 // The variable index is not equal to the column index. 00606 const dimension_type nonzero_var_index = nonzero_coeff_column_index - 1; 00607 00608 const int sgn_a = sgn(cs_i.coefficient(Variable(nonzero_var_index))); 00609 const int sgn_b = sgn(cs_i.inhomogeneous_term()); 00610 00611 // Cases 1-3: apply method A. 00612 if (sgn_a == sgn_b) { 00613 if (cs_i.is_inequality()) 00614 ++additional_slack_variables; 00615 } 00616 // Cases 4-5: apply method B. 00617 else if (cs_i.is_equality()) 00618 is_nonnegative_variable[nonzero_var_index] = true; 00619 // Case 6: apply method B. 00620 else if (sgn_b < 0) { 00621 is_nonnegative_variable[nonzero_var_index] = true; 00622 ++additional_slack_variables; 00623 } 00624 // Case 7: apply method C. 00625 else if (sgn_a > 0) { 00626 if (!is_nonnegative_variable[nonzero_var_index]) { 00627 is_nonnegative_variable[nonzero_var_index] = true; 00628 if (nonzero_coeff_column_index < mapping_size) { 00629 // Remember to merge back the positive and negative parts. 00630 PPL_ASSERT(nonzero_var_index < internal_space_dim); 00631 is_remergeable_variable[nonzero_var_index] = true; 00632 } 00633 } 00634 is_tableau_constraint[i - first_pending_constraint] = false; 00635 --additional_tableau_rows; 00636 } 00637 // Cases 8-9: apply method A. 00638 else { 00639 PPL_ASSERT(cs_i.is_inequality()); 00640 ++additional_slack_variables; 00641 } 00642 } 00643 } 00644 return true; 00645 } 00646 00647 bool 00648 PPL::MIP_Problem::process_pending_constraints() { 00649 // Check the pending constraints to adjust the data structures. 00650 // If `false' is returned, they are trivially unfeasible. 00651 dimension_type additional_tableau_rows = 0; 00652 dimension_type additional_slack_vars = 0; 00653 std::deque<bool> is_tableau_constraint; 00654 std::deque<bool> is_satisfied_inequality; 00655 std::deque<bool> is_nonnegative_variable; 00656 std::deque<bool> is_remergeable_variable; 00657 if (!parse_constraints(additional_tableau_rows, 00658 additional_slack_vars, 00659 is_tableau_constraint, 00660 is_satisfied_inequality, 00661 is_nonnegative_variable, 00662 is_remergeable_variable)) { 00663 status = UNSATISFIABLE; 00664 return false; 00665 } 00666 00667 // Merge back any variable that was previously split into a positive 00668 // and a negative part and is now known to be nonnegative. 00669 std::vector<dimension_type> unfeasible_tableau_rows; 00670 for (dimension_type i = internal_space_dim; i-- > 0; ) { 00671 if (!is_remergeable_variable[i]) 00672 continue; 00673 // TODO: merging all rows in a single shot may be more efficient 00674 // as it would require a single call to permute_columns(). 00675 const dimension_type unfeasible_row = merge_split_variable(i); 00676 if (unfeasible_row != not_a_dimension()) 00677 unfeasible_tableau_rows.push_back(unfeasible_row); 00678 } 00679 00680 const dimension_type old_tableau_num_rows = tableau.num_rows(); 00681 const dimension_type old_tableau_num_cols = tableau.num_columns(); 00682 const dimension_type first_free_tableau_index = old_tableau_num_cols - 1; 00683 00684 // Update mapping for the new problem variables (if any). 00685 dimension_type additional_problem_vars = 0; 00686 if (external_space_dim > internal_space_dim) { 00687 const dimension_type space_diff = external_space_dim - internal_space_dim; 00688 for (dimension_type i = 0, j = 0; i < space_diff; ++i) { 00689 // Let `mapping' associate the variable index with the corresponding 00690 // tableau column: split the variable into positive and negative 00691 // parts if it is not known to be nonnegative. 00692 const dimension_type positive = first_free_tableau_index + j; 00693 if (is_nonnegative_variable[internal_space_dim + i]) { 00694 // Do not split. 00695 mapping.push_back(std::make_pair(positive, 0)); 00696 ++j; 00697 ++additional_problem_vars; 00698 } 00699 else { 00700 // Split: negative index is positive + 1. 00701 mapping.push_back(std::make_pair(positive, positive + 1)); 00702 j += 2; 00703 additional_problem_vars += 2; 00704 } 00705 } 00706 } 00707 00708 // Resize the tableau: first add additional rows ... 00709 if (additional_tableau_rows > 0) 00710 tableau.add_zero_rows(additional_tableau_rows, Row_Flags()); 00711 00712 // ... then add additional columns. 00713 // We need columns for additional (split) problem variables, additional 00714 // slack variables and additional artificials. 00715 // The number of artificials to be added is computed as: 00716 // * number of pending constraints entering the tableau 00717 // minus 00718 // * pending constraints that are inequalities and are already satisfied 00719 // by `last_generator' 00720 // plus 00721 // * number of non-pending constraints that are no longer satisfied 00722 // due to re-merging of split variables. 00723 00724 const dimension_type num_satisfied_inequalities 00725 = static_cast<dimension_type>(std::count(is_satisfied_inequality.begin(), 00726 is_satisfied_inequality.end(), 00727 true)); 00728 const dimension_type unfeasible_tableau_rows_size 00729 = unfeasible_tableau_rows.size(); 00730 00731 PPL_ASSERT(additional_tableau_rows >= num_satisfied_inequalities); 00732 const dimension_type additional_artificial_vars 00733 = (additional_tableau_rows - num_satisfied_inequalities) 00734 + unfeasible_tableau_rows_size; 00735 00736 const dimension_type additional_tableau_columns 00737 = additional_problem_vars 00738 + additional_slack_vars 00739 + additional_artificial_vars; 00740 00741 if (additional_tableau_columns > 0) 00742 tableau.add_zero_columns(additional_tableau_columns); 00743 00744 // Dimensions of the tableau after resizing. 00745 const dimension_type tableau_num_rows = tableau.num_rows(); 00746 const dimension_type tableau_num_cols = tableau.num_columns(); 00747 00748 // The following vector will be useful know if a constraint is feasible 00749 // and does not require an additional artificial variable. 00750 std::deque<bool> worked_out_row (tableau_num_rows, false); 00751 00752 // Sync the `base' vector size to the new tableau: fill with zeros 00753 // to encode that these rows are not OK and must be adjusted. 00754 base.insert(base.end(), additional_tableau_rows, 0); 00755 const dimension_type base_size = base.size(); 00756 00757 // These indexes will be used to insert slack and artificial variables 00758 // in the appropriate position. 00759 dimension_type slack_index 00760 = tableau_num_cols - additional_artificial_vars - 1; 00761 dimension_type artificial_index = slack_index; 00762 00763 // The first column index of the tableau that contains an 00764 // artificial variable. Encode with 0 the fact the there are not 00765 // artificial variables. 00766 const dimension_type begin_artificials 00767 = (additional_artificial_vars > 0) 00768 ? artificial_index 00769 : 0; 00770 00771 // Proceed with the insertion of the constraints. 00772 for (dimension_type k = tableau_num_rows, 00773 i = input_cs.size() - first_pending_constraint; i-- > 0; ) { 00774 if (!is_tableau_constraint[i]) 00775 continue; 00776 // Copy the original constraint in the tableau. 00777 Row& tableau_k = tableau[--k]; 00778 Row::iterator itr = tableau_k.end(); 00779 00780 const Constraint& c = *(input_cs[i + first_pending_constraint]); 00781 for (dimension_type sd = c.space_dimension(); sd-- > 0; ) { 00782 Coefficient_traits::const_reference coeff_sd 00783 = c.coefficient(Variable(sd)); 00784 if (coeff_sd != 0) { 00785 itr = tableau_k.insert(itr, mapping[sd+1].first, coeff_sd); 00786 // Split if needed. 00787 if (mapping[sd+1].second != 0) { 00788 itr = tableau_k.insert(itr, mapping[sd+1].second); 00789 neg_assign(*itr, coeff_sd); 00790 } 00791 } 00792 } 00793 Coefficient_traits::const_reference inhomo 00794 = c.inhomogeneous_term(); 00795 if (inhomo != 0) { 00796 tableau_k.insert(itr, mapping[0].first, inhomo); 00797 // Split if needed. 00798 if (mapping[0].second != 0) { 00799 itr = tableau_k.insert(itr, mapping[0].second); 00800 neg_assign(*itr, inhomo); 00801 } 00802 } 00803 00804 // Add the slack variable, if needed. 00805 if (c.is_inequality()) { 00806 neg_assign(tableau_k[--slack_index], Coefficient_one()); 00807 // If the constraint is already satisfied, we will not use artificial 00808 // variables to compute a feasible base: this to speed up 00809 // the algorithm. 00810 if (is_satisfied_inequality[i]) { 00811 base[k] = slack_index; 00812 worked_out_row[k] = true; 00813 } 00814 } 00815 for (dimension_type j = base_size; j-- > 0; ) 00816 if (k != j && base[j] != 0 && tableau_k.get(base[j]) != 0) 00817 linear_combine(tableau_k, tableau[j], base[j]); 00818 } 00819 00820 // Let all inhomogeneous terms in the tableau be nonpositive, 00821 // so as to simplify the insertion of artificial variables 00822 // (the coefficient of each artificial variable will be 1). 00823 for (dimension_type i = tableau_num_rows; i-- > 0 ; ) { 00824 Row& tableau_i = tableau[i]; 00825 if (tableau_i.get(0) > 0) { 00826 for (Row::iterator 00827 j = tableau_i.begin(), j_end = tableau_i.end(); j != j_end; ++j) 00828 neg_assign(*j); 00829 } 00830 } 00831 00832 // Reset the working cost function to have the right size. 00833 working_cost = working_cost_type(tableau_num_cols, Row_Flags()); 00834 00835 // Set up artificial variables: these will have coefficient 1 in the 00836 // constraint, will enter the base and will have coefficient -1 in 00837 // the cost function. 00838 00839 // This is used as a hint for insertions in working_cost. 00840 working_cost_type::iterator cost_itr = working_cost.end(); 00841 00842 // First go through non-pending constraints that became unfeasible 00843 // due to re-merging of split variables. 00844 for (dimension_type i = 0; i < unfeasible_tableau_rows_size; ++i) { 00845 tableau[unfeasible_tableau_rows[i]].insert(artificial_index, 00846 Coefficient_one()); 00847 cost_itr = working_cost.insert(cost_itr, artificial_index); 00848 *cost_itr = -1; 00849 base[unfeasible_tableau_rows[i]] = artificial_index; 00850 ++artificial_index; 00851 } 00852 // Then go through newly added tableau rows, disregarding inequalities 00853 // that are already satisfied by `last_generator' (this information 00854 // is encoded in `worked_out_row'). 00855 for (dimension_type i = old_tableau_num_rows; i < tableau_num_rows; ++i) { 00856 if (worked_out_row[i]) 00857 continue; 00858 tableau[i].insert(artificial_index, Coefficient_one()); 00859 cost_itr = working_cost.insert(cost_itr, artificial_index); 00860 *cost_itr = -1; 00861 base[i] = artificial_index; 00862 ++artificial_index; 00863 } 00864 // One past the last tableau column index containing an artificial variable. 00865 const dimension_type end_artificials = artificial_index; 00866 00867 // Set the extra-coefficient of the cost functions to record its sign. 00868 // This is done to keep track of the possible sign's inversion. 00869 const dimension_type last_obj_index = working_cost.size() - 1; 00870 working_cost.insert(cost_itr, last_obj_index, Coefficient_one()); 00871 00872 // Express the problem in terms of the variables in base. 00873 { 00874 working_cost_type::const_iterator itr = working_cost.end(); 00875 for (dimension_type i = tableau_num_rows; i-- > 0; ) { 00876 itr = working_cost.lower_bound(itr, base[i]); 00877 if (itr != working_cost.end() && itr.index() == base[i] && *itr != 0) { 00878 linear_combine(working_cost, tableau[i], base[i]); 00879 // itr has been invalidated by the call to linear_combine(). 00880 itr = working_cost.end(); 00881 } 00882 } 00883 } 00884 00885 // Deal with zero dimensional problems. 00886 if (space_dimension() == 0) { 00887 status = OPTIMIZED; 00888 last_generator = point(); 00889 return true; 00890 } 00891 // Deal with trivial cases. 00892 // If there is no constraint in the tableau, then the feasible region 00893 // is only delimited by non-negativity constraints. Therefore, 00894 // the problem is unbounded as soon as the cost function has 00895 // a variable with a positive coefficient. 00896 if (tableau_num_rows == 0) { 00897 const dimension_type input_obj_function_size 00898 = input_obj_function.space_dimension(); 00899 for (dimension_type i = input_obj_function_size; i-- > 0; ) 00900 // If a the value of a variable in the objective function is 00901 // different from zero, the final status is unbounded. 00902 // In the first part the variable is constrained to be greater or equal 00903 // than zero. 00904 if ((((input_obj_function.coefficient(Variable(i)) > 0 00905 && opt_mode == MAXIMIZATION) 00906 || (input_obj_function.coefficient(Variable(i)) < 0 00907 && opt_mode == MINIMIZATION)) && mapping[i].second == 0) 00908 // In the following case the variable is unconstrained. 00909 || (input_obj_function.coefficient(Variable(i)) != 0 00910 && mapping[i].second != 0)) { 00911 // Ensure the right space dimension is obtained. 00912 last_generator = point(0 * Variable(space_dimension() - 1)); 00913 status = UNBOUNDED; 00914 return true; 00915 } 00916 00917 // The problem is neither trivially unfeasible nor trivially unbounded. 00918 // The tableau was successful computed and the caller has to figure 00919 // out which case applies. 00920 status = OPTIMIZED; 00921 // Ensure the right space dimension is obtained. 00922 last_generator = point(0 * Variable(space_dimension() - 1)); 00923 PPL_ASSERT(OK()); 00924 return true; 00925 } 00926 00927 // Now we are ready to solve the first phase. 00928 bool first_phase_successful 00929 = (get_control_parameter(PRICING) == PRICING_STEEPEST_EDGE_FLOAT) 00930 ? compute_simplex_using_steepest_edge_float() 00931 : compute_simplex_using_exact_pricing(); 00932 00933 #if PPL_NOISY_SIMPLEX 00934 std::cout << "MIP_Problem::process_pending_constraints(): " 00935 << "1st phase ended at iteration " << num_iterations 00936 << "." << std::endl; 00937 #endif // PPL_NOISY_SIMPLEX 00938 00939 if (!first_phase_successful || working_cost.get(0) != 0) { 00940 // The feasible region is empty. 00941 status = UNSATISFIABLE; 00942 return false; 00943 } 00944 00945 // Prepare *this for a possible second phase. 00946 if (begin_artificials != 0) 00947 erase_artificials(begin_artificials, end_artificials); 00948 compute_generator(); 00949 status = SATISFIABLE; 00950 PPL_ASSERT(OK()); 00951 return true; 00952 } 00953 00954 namespace { 00955 00956 // NOTE: the following two `assign' helper functions are needed to 00957 // handle the assignment of a Coefficient to a double in method 00958 // MIP_Problem::steepest_edge_float_entering_index(). 00959 // We cannot use assign_r(double, Coefficient, Rounding_Dir) as it would 00960 // lead to a compilation error on those platforms (e.g., ARM) where 00961 // controlled floating point rounding is not available (even if the 00962 // rounding mode would be set to ROUND_IGNORE). 00963 00964 inline void 00965 assign(double& d, const mpz_class& c) { 00966 d = c.get_d(); 00967 } 00968 00969 template <typename T, typename Policy> 00970 inline void 00971 assign(double& d, 00972 const Parma_Polyhedra_Library::Checked_Number<T, Policy>& c) { 00973 d = raw_value(c); 00974 } 00975 00976 } // namespace 00977 00978 PPL::dimension_type 00979 PPL::MIP_Problem::steepest_edge_float_entering_index() const { 00980 const dimension_type tableau_num_rows = tableau.num_rows(); 00981 const dimension_type tableau_num_columns = tableau.num_columns(); 00982 PPL_ASSERT(tableau_num_rows == base.size()); 00983 double current_value = 0.0; 00984 // Due to our integer implementation, the `1' term in the denominator 00985 // of the original formula has to be replaced by `squared_lcm_basis'. 00986 double float_tableau_value; 00987 double float_tableau_denom; 00988 dimension_type entering_index = 0; 00989 const int cost_sign = sgn(working_cost.get(working_cost.size() - 1)); 00990 00991 // These two implementation work for both sparse and dense matrices. 00992 // However, when using sparse matrices the first one is fast and the second 00993 // one is slow, and when using dense matrices the first one is slow and 00994 // the second one is fast. 00995 #if PPL_USE_SPARSE_MATRIX 00996 00997 const dimension_type tableau_num_columns_minus_1 = tableau_num_columns - 1; 00998 // This is static to improve performance. 00999 // A vector of <column_index, challenger_denom> pairs, ordered by 01000 // column_index. 01001 static std::vector<std::pair<dimension_type, double> > columns; 01002 columns.clear(); 01003 // (working_cost.size() - 2) is an upper bound only. 01004 columns.reserve(working_cost.size() - 2); 01005 { 01006 working_cost_type::const_iterator i = working_cost.lower_bound(1); 01007 // Note that find() is used instead of lower_bound(). 01008 working_cost_type::const_iterator i_end 01009 = working_cost.find(tableau_num_columns_minus_1); 01010 for ( ; i != i_end; ++i) 01011 if (sgn(*i) == cost_sign) 01012 columns.push_back(std::make_pair(i.index(), 1.0)); 01013 } 01014 for (dimension_type i = tableau_num_rows; i-- > 0; ) { 01015 const Row& tableau_i = tableau[i]; 01016 assign(float_tableau_denom, tableau_i.get(base[i])); 01017 Row::const_iterator j = tableau_i.begin(); 01018 Row::const_iterator j_end = tableau_i.end(); 01019 std::vector<std::pair<dimension_type, double> >::iterator k 01020 = columns.begin(); 01021 std::vector<std::pair<dimension_type, double> >::iterator k_end 01022 = columns.end(); 01023 while (j != j_end && k != k_end) { 01024 const dimension_type column = j.index(); 01025 while (k != k_end && column > k->first) 01026 ++k; 01027 if (k == k_end) 01028 break; 01029 if (k->first > column) { 01030 j = tableau_i.lower_bound(j, k->first); 01031 } 01032 else { 01033 PPL_ASSERT(k->first == column); 01034 PPL_ASSERT(tableau_i.get(base[i]) != 0); 01035 WEIGHT_BEGIN(); 01036 assign(float_tableau_value, *j); 01037 float_tableau_value /= float_tableau_denom; 01038 float_tableau_value *= float_tableau_value; 01039 k->second += float_tableau_value; 01040 WEIGHT_ADD(22); 01041 ++j; 01042 ++k; 01043 } 01044 } 01045 } 01046 // The candidates are processed backwards to get the same result in both 01047 // this implementation and the dense implementation below. 01048 for (std::vector<std::pair<dimension_type, double> >::const_reverse_iterator 01049 i = columns.rbegin(), i_end = columns.rend(); i != i_end; ++i) { 01050 double challenger_value = sqrt(i->second); 01051 if (entering_index == 0 || challenger_value > current_value) { 01052 current_value = challenger_value; 01053 entering_index = i->first; 01054 } 01055 } 01056 01057 #else // !PPL_USE_SPARSE_MATRIX 01058 01059 double challenger_numer = 0.0; 01060 double challenger_denom = 0.0; 01061 for (dimension_type j = tableau_num_columns - 1; j-- > 1; ) { 01062 Coefficient_traits::const_reference cost_j = working_cost.get(j); 01063 if (sgn(cost_j) == cost_sign) { 01064 WEIGHT_BEGIN(); 01065 // We cannot compute the (exact) square root of abs(\Delta x_j). 01066 // The workaround is to compute the square of `cost[j]'. 01067 assign(challenger_numer, cost_j); 01068 challenger_numer = std::abs(challenger_numer); 01069 // Due to our integer implementation, the `1' term in the denominator 01070 // of the original formula has to be replaced by `squared_lcm_basis'. 01071 challenger_denom = 1.0; 01072 for (dimension_type i = tableau_num_rows; i-- > 0; ) { 01073 const Row& tableau_i = tableau[i]; 01074 Coefficient_traits::const_reference tableau_ij = tableau_i[j]; 01075 if (tableau_ij != 0) { 01076 PPL_ASSERT(tableau_i[base[i]] != 0); 01077 assign(float_tableau_value, tableau_ij); 01078 assign(float_tableau_denom, tableau_i[base[i]]); 01079 float_tableau_value /= float_tableau_denom; 01080 challenger_denom += float_tableau_value * float_tableau_value; 01081 } 01082 } 01083 double challenger_value = sqrt(challenger_denom); 01084 // Initialize `current_value' during the first iteration. 01085 // Otherwise update if the challenger wins. 01086 if (entering_index == 0 || challenger_value > current_value) { 01087 current_value = challenger_value; 01088 entering_index = j; 01089 } 01090 WEIGHT_ADD_MUL(10, tableau_num_rows); 01091 } 01092 } 01093 01094 #endif // !PPL_USE_SPARSE_MATRIX 01095 01096 return entering_index; 01097 } 01098 01099 PPL::dimension_type 01100 PPL::MIP_Problem::steepest_edge_exact_entering_index() const { 01101 using std::swap; 01102 const dimension_type tableau_num_rows = tableau.num_rows(); 01103 PPL_ASSERT(tableau_num_rows == base.size()); 01104 // The square of the lcm of all the coefficients of variables in base. 01105 PPL_DIRTY_TEMP_COEFFICIENT(squared_lcm_basis); 01106 // The normalization factor for each coefficient in the tableau. 01107 std::vector<Coefficient> norm_factor(tableau_num_rows); 01108 { 01109 WEIGHT_BEGIN(); 01110 // Compute the lcm of all the coefficients of variables in base. 01111 PPL_DIRTY_TEMP_COEFFICIENT(lcm_basis); 01112 lcm_basis = 1; 01113 for (dimension_type i = tableau_num_rows; i-- > 0; ) 01114 lcm_assign(lcm_basis, lcm_basis, tableau[i].get(base[i])); 01115 // Compute normalization factors. 01116 for (dimension_type i = tableau_num_rows; i-- > 0; ) 01117 exact_div_assign(norm_factor[i], lcm_basis, tableau[i].get(base[i])); 01118 // Compute the square of `lcm_basis', exploiting the fact that 01119 // `lcm_basis' will no longer be needed. 01120 lcm_basis *= lcm_basis; 01121 swap(squared_lcm_basis, lcm_basis); 01122 WEIGHT_ADD_MUL(444, tableau_num_rows); 01123 } 01124 01125 PPL_DIRTY_TEMP_COEFFICIENT(challenger_numer); 01126 PPL_DIRTY_TEMP_COEFFICIENT(scalar_value); 01127 PPL_DIRTY_TEMP_COEFFICIENT(challenger_value); 01128 PPL_DIRTY_TEMP_COEFFICIENT(current_value); 01129 01130 PPL_DIRTY_TEMP_COEFFICIENT(current_numer); 01131 PPL_DIRTY_TEMP_COEFFICIENT(current_denom); 01132 dimension_type entering_index = 0; 01133 const int cost_sign = sgn(working_cost.get(working_cost.size() - 1)); 01134 01135 // These two implementation work for both sparse and dense matrices. 01136 // However, when using sparse matrices the first one is fast and the second 01137 // one is slow, and when using dense matrices the first one is slow and 01138 // the second one is fast. 01139 #if PPL_USE_SPARSE_MATRIX 01140 01141 const dimension_type tableau_num_columns = tableau.num_columns(); 01142 const dimension_type tableau_num_columns_minus_1 = tableau_num_columns - 1; 01143 // This is static to improve performance. 01144 // A pair (i, x) means that sgn(working_cost[i]) == cost_sign and x 01145 // is the denominator of the challenger, for the column i. 01146 static std::vector<std::pair<dimension_type, Coefficient> > columns; 01147 columns.clear(); 01148 // tableau_num_columns - 2 is only an upper bound on the required elements. 01149 // This helps to reduce the number of calls to new [] and delete [] and 01150 // the construction/destruction of Coefficient objects. 01151 columns.reserve(tableau_num_columns - 2); 01152 { 01153 working_cost_type::const_iterator i = working_cost.lower_bound(1); 01154 // Note that find() is used instead of lower_bound. 01155 working_cost_type::const_iterator i_end 01156 = working_cost.find(tableau_num_columns_minus_1); 01157 for ( ; i != i_end; ++i) 01158 if (sgn(*i) == cost_sign) 01159 columns.push_back(std::pair<dimension_type, Coefficient> 01160 (i.index(), squared_lcm_basis)); 01161 } 01162 for (dimension_type i = tableau_num_rows; i-- > 0; ) { 01163 const Row& tableau_i = tableau[i]; 01164 Row::const_iterator j = tableau_i.begin(); 01165 Row::const_iterator j_end = tableau_i.end(); 01166 std::vector<std::pair<dimension_type, Coefficient> >::iterator 01167 k = columns.begin(); 01168 std::vector<std::pair<dimension_type, Coefficient> >::iterator 01169 k_end = columns.end(); 01170 while (j != j_end) { 01171 while (k != k_end && j.index() > k->first) 01172 ++k; 01173 if (k == k_end) 01174 break; 01175 PPL_ASSERT(j.index() <= k->first); 01176 if (j.index() < k->first) 01177 j = tableau_i.lower_bound(j, k->first); 01178 else { 01179 Coefficient_traits::const_reference tableau_ij = *j; 01180 WEIGHT_BEGIN(); 01181 #if PPL_USE_SPARSE_MATRIX 01182 scalar_value = tableau_ij * norm_factor[i]; 01183 add_mul_assign(k->second, scalar_value, scalar_value); 01184 #else 01185 // The test against 0 gives rise to a consistent speed up in the dense 01186 // implementation: see 01187 // http://www.cs.unipr.it/pipermail/ppl-devel/2009-February/ 01188 // 014000.html 01189 if (tableau_ij != 0) { 01190 scalar_value = tableau_ij * norm_factor[i]; 01191 add_mul_assign(k->second, scalar_value, scalar_value); 01192 } 01193 #endif 01194 WEIGHT_ADD_MUL(47, tableau_num_rows); 01195 ++k; 01196 ++j; 01197 } 01198 } 01199 } 01200 working_cost_type::const_iterator itr = working_cost.end(); 01201 for (std::vector<std::pair<dimension_type, Coefficient> >::reverse_iterator 01202 k = columns.rbegin(), k_end = columns.rend(); k != k_end; ++k) { 01203 itr = working_cost.lower_bound(itr, k->first); 01204 if (itr != working_cost.end() && itr.index() == k->first) { 01205 // We cannot compute the (exact) square root of abs(\Delta x_j). 01206 // The workaround is to compute the square of `cost[j]'. 01207 challenger_numer = (*itr) * (*itr); 01208 // Initialization during the first loop. 01209 if (entering_index == 0) { 01210 swap(current_numer, challenger_numer); 01211 swap(current_denom, k->second); 01212 entering_index = k->first; 01213 continue; 01214 } 01215 challenger_value = challenger_numer * current_denom; 01216 current_value = current_numer * k->second; 01217 // Update the values, if the challenger wins. 01218 if (challenger_value > current_value) { 01219 swap(current_numer, challenger_numer); 01220 swap(current_denom, k->second); 01221 entering_index = k->first; 01222 } 01223 } else { 01224 PPL_ASSERT(working_cost.get(k->first) == 0); 01225 // Initialization during the first loop. 01226 if (entering_index == 0) { 01227 current_numer = 0; 01228 swap(current_denom, k->second); 01229 entering_index = k->first; 01230 continue; 01231 } 01232 // Update the values, if the challenger wins. 01233 if (0 > sgn(current_numer) * sgn(k->second)) { 01234 current_numer = 0; 01235 swap(current_denom, k->second); 01236 entering_index = k->first; 01237 } 01238 } 01239 } 01240 01241 #else // !PPL_USE_SPARSE_MATRIX 01242 01243 PPL_DIRTY_TEMP_COEFFICIENT(challenger_denom); 01244 for (dimension_type j = tableau.num_columns() - 1; j-- > 1; ) { 01245 Coefficient_traits::const_reference cost_j = working_cost[j]; 01246 if (sgn(cost_j) == cost_sign) { 01247 WEIGHT_BEGIN(); 01248 // We cannot compute the (exact) square root of abs(\Delta x_j). 01249 // The workaround is to compute the square of `cost[j]'. 01250 challenger_numer = cost_j * cost_j; 01251 // Due to our integer implementation, the `1' term in the denominator 01252 // of the original formula has to be replaced by `squared_lcm_basis'. 01253 challenger_denom = squared_lcm_basis; 01254 for (dimension_type i = tableau_num_rows; i-- > 0; ) { 01255 Coefficient_traits::const_reference tableau_ij = tableau[i][j]; 01256 // The test against 0 gives rise to a consistent speed up: see 01257 // http://www.cs.unipr.it/pipermail/ppl-devel/2009-February/ 01258 // 014000.html 01259 if (tableau_ij != 0) { 01260 scalar_value = tableau_ij * norm_factor[i]; 01261 add_mul_assign(challenger_denom, scalar_value, scalar_value); 01262 } 01263 } 01264 // Initialization during the first loop. 01265 if (entering_index == 0) { 01266 swap(current_numer, challenger_numer); 01267 swap(current_denom, challenger_denom); 01268 entering_index = j; 01269 continue; 01270 } 01271 challenger_value = challenger_numer * current_denom; 01272 current_value = current_numer * challenger_denom; 01273 // Update the values, if the challenger wins. 01274 if (challenger_value > current_value) { 01275 swap(current_numer, challenger_numer); 01276 swap(current_denom, challenger_denom); 01277 entering_index = j; 01278 } 01279 WEIGHT_ADD_MUL(47, tableau_num_rows); 01280 } 01281 } 01282 01283 #endif // !PPL_USE_SPARSE_MATRIX 01284 01285 return entering_index; 01286 } 01287 01288 01289 // See page 47 of [PapadimitriouS98]. 01290 PPL::dimension_type 01291 PPL::MIP_Problem::textbook_entering_index() const { 01292 // The variable entering the base is the first one whose coefficient 01293 // in the cost function has the same sign the cost function itself. 01294 // If no such variable exists, then we met the optimality condition 01295 // (and return 0 to the caller). 01296 01297 // Get the "sign" of the cost function. 01298 const dimension_type cost_sign_index = working_cost.size() - 1; 01299 const int cost_sign = sgn(working_cost.get(cost_sign_index)); 01300 PPL_ASSERT(cost_sign != 0); 01301 01302 working_cost_type::const_iterator i = working_cost.lower_bound(1); 01303 // Note that find() is used instead of lower_bound() because they are 01304 // equivalent when searching the last element in the row. 01305 working_cost_type::const_iterator i_end 01306 = working_cost.find(cost_sign_index); 01307 for ( ; i != i_end; ++i) 01308 if (sgn(*i) == cost_sign) 01309 return i.index(); 01310 // No variable has to enter the base: 01311 // the cost function was optimized. 01312 return 0; 01313 } 01314 01315 void 01316 PPL::MIP_Problem::linear_combine(Row& x, const Row& y, 01317 const dimension_type k) { 01318 PPL_ASSERT(x.size() == y.size()); 01319 WEIGHT_BEGIN(); 01320 const dimension_type x_size = x.size(); 01321 Coefficient_traits::const_reference x_k = x.get(k); 01322 Coefficient_traits::const_reference y_k = y.get(k); 01323 PPL_ASSERT(y_k != 0 && x_k != 0); 01324 // Let g be the GCD between `x[k]' and `y[k]'. 01325 // For each i the following computes 01326 // x[i] = x[i]*y[k]/g - y[i]*x[k]/g. 01327 PPL_DIRTY_TEMP_COEFFICIENT(normalized_x_k); 01328 PPL_DIRTY_TEMP_COEFFICIENT(normalized_y_k); 01329 normalize2(x_k, y_k, normalized_x_k, normalized_y_k); 01330 01331 neg_assign(normalized_y_k); 01332 x.linear_combine(y, normalized_y_k, normalized_x_k); 01333 01334 PPL_ASSERT(x.get(k) == 0); 01335 01336 #if PPL_USE_SPARSE_MATRIX 01337 PPL_ASSERT(x.find(k) == x.end()); 01338 #endif 01339 01340 x.normalize(); 01341 WEIGHT_ADD_MUL(31, x_size); 01342 } 01343 01344 // TODO: Remove this when the sparse working cost has been tested enough. 01345 #if PPL_USE_SPARSE_MATRIX 01346 01347 void 01348 PPL::MIP_Problem::linear_combine(Dense_Row& x, 01349 const Sparse_Row& y, 01350 const dimension_type k) { 01351 PPL_ASSERT(x.size() == y.size()); 01352 WEIGHT_BEGIN(); 01353 const dimension_type x_size = x.size(); 01354 Coefficient_traits::const_reference x_k = x.get(k); 01355 Coefficient_traits::const_reference y_k = y.get(k); 01356 PPL_ASSERT(y_k != 0 && x_k != 0); 01357 // Let g be the GCD between `x[k]' and `y[k]'. 01358 // For each i the following computes 01359 // x[i] = x[i]*y[k]/g - y[i]*x[k]/g. 01360 PPL_DIRTY_TEMP_COEFFICIENT(normalized_x_k); 01361 PPL_DIRTY_TEMP_COEFFICIENT(normalized_y_k); 01362 normalize2(x_k, y_k, normalized_x_k, normalized_y_k); 01363 Sparse_Row::const_iterator j = y.begin(); 01364 Sparse_Row::const_iterator j_end = y.end(); 01365 dimension_type i; 01366 for (i = 0; j != j_end; ++i) { 01367 PPL_ASSERT(i < x_size); 01368 PPL_ASSERT(j.index() >= i); 01369 if (j.index() == i) { 01370 if (i != k) { 01371 Coefficient& x_i = x[i]; 01372 x_i *= normalized_y_k; 01373 Coefficient_traits::const_reference y_i = *j; 01374 sub_mul_assign(x_i, y_i, normalized_x_k); 01375 } 01376 ++j; 01377 } else { 01378 if (i != k) 01379 x[i] *= normalized_y_k; 01380 } 01381 } 01382 PPL_ASSERT(j == j_end); 01383 for ( ; i < x_size; ++i) 01384 if (i != k) 01385 x[i] *= normalized_y_k; 01386 01387 x[k] = 0; 01388 x.normalize(); 01389 WEIGHT_ADD_MUL(83, x_size); 01390 } 01391 01392 #endif // defined(PPL_USE_SPARSE_MATRIX) 01393 01394 // See pages 42-43 of [PapadimitriouS98]. 01395 void 01396 PPL::MIP_Problem::pivot(const dimension_type entering_var_index, 01397 const dimension_type exiting_base_index) { 01398 const Row& tableau_out = tableau[exiting_base_index]; 01399 // Linearly combine the constraints. 01400 for (dimension_type i = tableau.num_rows(); i-- > 0; ) { 01401 Row& tableau_i = tableau[i]; 01402 if (i != exiting_base_index && tableau_i.get(entering_var_index) != 0) 01403 linear_combine(tableau_i, tableau_out, entering_var_index); 01404 } 01405 // Linearly combine the cost function. 01406 if (working_cost.get(entering_var_index) != 0) 01407 linear_combine(working_cost, tableau_out, entering_var_index); 01408 // Adjust the base. 01409 base[exiting_base_index] = entering_var_index; 01410 } 01411 01412 // See pages 47 and 50 of [PapadimitriouS98]. 01413 PPL::dimension_type 01414 PPL::MIP_Problem 01415 ::get_exiting_base_index(const dimension_type entering_var_index) const { 01416 // The variable exiting the base should be associated to a tableau 01417 // constraint such that the ratio 01418 // tableau[i][entering_var_index] / tableau[i][base[i]] 01419 // is strictly positive and minimal. 01420 01421 // Find the first tableau constraint `c' having a positive value for 01422 // tableau[i][entering_var_index] / tableau[i][base[i]] 01423 const dimension_type tableau_num_rows = tableau.num_rows(); 01424 dimension_type exiting_base_index = tableau_num_rows; 01425 for (dimension_type i = 0; i < tableau_num_rows; ++i) { 01426 const Row& t_i = tableau[i]; 01427 const int num_sign = sgn(t_i.get(entering_var_index)); 01428 if (num_sign != 0 && num_sign == sgn(t_i.get(base[i]))) { 01429 exiting_base_index = i; 01430 break; 01431 } 01432 } 01433 // Check for unboundedness. 01434 if (exiting_base_index == tableau_num_rows) 01435 return tableau_num_rows; 01436 01437 // Reaching this point means that a variable will definitely exit the base. 01438 PPL_DIRTY_TEMP_COEFFICIENT(lcm); 01439 PPL_DIRTY_TEMP_COEFFICIENT(current_min); 01440 PPL_DIRTY_TEMP_COEFFICIENT(challenger); 01441 Coefficient t_e0 = tableau[exiting_base_index].get(0); 01442 Coefficient t_ee = tableau[exiting_base_index].get(entering_var_index); 01443 for (dimension_type i = exiting_base_index + 1; i < tableau_num_rows; ++i) { 01444 const Row& t_i = tableau[i]; 01445 Coefficient_traits::const_reference t_ie = t_i.get(entering_var_index); 01446 const int t_ie_sign = sgn(t_ie); 01447 if (t_ie_sign != 0 && t_ie_sign == sgn(t_i.get(base[i]))) { 01448 WEIGHT_BEGIN(); 01449 Coefficient_traits::const_reference t_i0 = t_i.get(0); 01450 lcm_assign(lcm, t_ee, t_ie); 01451 exact_div_assign(current_min, lcm, t_ee); 01452 current_min *= t_e0; 01453 abs_assign(current_min); 01454 exact_div_assign(challenger, lcm, t_ie); 01455 challenger *= t_i0; 01456 abs_assign(challenger); 01457 current_min -= challenger; 01458 const int sign = sgn(current_min); 01459 if (sign > 0 01460 || (sign == 0 && base[i] < base[exiting_base_index])) { 01461 exiting_base_index = i; 01462 t_e0 = t_i0; 01463 t_ee = t_ie; 01464 } 01465 WEIGHT_ADD(642); 01466 } 01467 } 01468 return exiting_base_index; 01469 } 01470 01471 // See page 49 of [PapadimitriouS98]. 01472 bool 01473 PPL::MIP_Problem::compute_simplex_using_steepest_edge_float() { 01474 // We may need to temporarily switch to the textbook pricing. 01475 const unsigned long allowed_non_increasing_loops = 200; 01476 unsigned long non_increased_times = 0; 01477 bool textbook_pricing = false; 01478 01479 PPL_DIRTY_TEMP_COEFFICIENT(cost_sgn_coeff); 01480 PPL_DIRTY_TEMP_COEFFICIENT(current_numer); 01481 PPL_DIRTY_TEMP_COEFFICIENT(current_denom); 01482 PPL_DIRTY_TEMP_COEFFICIENT(challenger); 01483 PPL_DIRTY_TEMP_COEFFICIENT(current); 01484 01485 cost_sgn_coeff = working_cost.get(working_cost.size() - 1); 01486 current_numer = working_cost.get(0); 01487 if (cost_sgn_coeff < 0) 01488 neg_assign(current_numer); 01489 abs_assign(current_denom, cost_sgn_coeff); 01490 PPL_ASSERT(tableau.num_columns() == working_cost.size()); 01491 const dimension_type tableau_num_rows = tableau.num_rows(); 01492 01493 while (true) { 01494 // Choose the index of the variable entering the base, if any. 01495 const dimension_type entering_var_index 01496 = textbook_pricing 01497 ? textbook_entering_index() 01498 : steepest_edge_float_entering_index(); 01499 01500 // If no entering index was computed, the problem is solved. 01501 if (entering_var_index == 0) 01502 return true; 01503 01504 // Choose the index of the row exiting the base. 01505 const dimension_type exiting_base_index 01506 = get_exiting_base_index(entering_var_index); 01507 // If no exiting index was computed, the problem is unbounded. 01508 if (exiting_base_index == tableau_num_rows) 01509 return false; 01510 01511 // Check if the client has requested abandoning all expensive 01512 // computations. If so, the exception specified by the client 01513 // is thrown now. 01514 maybe_abandon(); 01515 01516 // We have not reached the optimality or unbounded condition: 01517 // compute the new base and the corresponding vertex of the 01518 // feasible region. 01519 pivot(entering_var_index, exiting_base_index); 01520 01521 WEIGHT_BEGIN(); 01522 // Now begins the objective function's value check to choose between 01523 // the `textbook' and the float `steepest-edge' technique. 01524 cost_sgn_coeff = working_cost.get(working_cost.size() - 1); 01525 01526 challenger = working_cost.get(0); 01527 if (cost_sgn_coeff < 0) 01528 neg_assign(challenger); 01529 challenger *= current_denom; 01530 abs_assign(current, cost_sgn_coeff); 01531 current *= current_numer; 01532 #if PPL_NOISY_SIMPLEX 01533 ++num_iterations; 01534 if (num_iterations % 200 == 0) 01535 std::cout << "Primal simplex: iteration " << num_iterations 01536 << "." << std::endl; 01537 #endif // PPL_NOISY_SIMPLEX 01538 // If the following condition fails, probably there's a bug. 01539 PPL_ASSERT(challenger >= current); 01540 // If the value of the objective function does not improve, 01541 // keep track of that. 01542 if (challenger == current) { 01543 ++non_increased_times; 01544 // In the following case we will proceed using the `textbook' 01545 // technique, until the objective function is not improved. 01546 if (non_increased_times > allowed_non_increasing_loops) 01547 textbook_pricing = true; 01548 } 01549 // The objective function has an improvement: 01550 // reset `non_increased_times' and `textbook_pricing'. 01551 else { 01552 non_increased_times = 0; 01553 textbook_pricing = false; 01554 } 01555 current_numer = working_cost.get(0); 01556 if (cost_sgn_coeff < 0) 01557 neg_assign(current_numer); 01558 abs_assign(current_denom, cost_sgn_coeff); 01559 WEIGHT_ADD(433); 01560 } 01561 } 01562 01563 bool 01564 PPL::MIP_Problem::compute_simplex_using_exact_pricing() { 01565 PPL_ASSERT(tableau.num_columns() == working_cost.size()); 01566 PPL_ASSERT(get_control_parameter(PRICING) == PRICING_STEEPEST_EDGE_EXACT 01567 || get_control_parameter(PRICING) == PRICING_TEXTBOOK); 01568 01569 const dimension_type tableau_num_rows = tableau.num_rows(); 01570 const bool textbook_pricing 01571 = (PRICING_TEXTBOOK == get_control_parameter(PRICING)); 01572 01573 while (true) { 01574 // Choose the index of the variable entering the base, if any. 01575 const dimension_type entering_var_index 01576 = textbook_pricing 01577 ? textbook_entering_index() 01578 : steepest_edge_exact_entering_index(); 01579 // If no entering index was computed, the problem is solved. 01580 if (entering_var_index == 0) 01581 return true; 01582 01583 // Choose the index of the row exiting the base. 01584 const dimension_type exiting_base_index 01585 = get_exiting_base_index(entering_var_index); 01586 // If no exiting index was computed, the problem is unbounded. 01587 if (exiting_base_index == tableau_num_rows) 01588 return false; 01589 01590 // Check if the client has requested abandoning all expensive 01591 // computations. If so, the exception specified by the client 01592 // is thrown now. 01593 maybe_abandon(); 01594 01595 // We have not reached the optimality or unbounded condition: 01596 // compute the new base and the corresponding vertex of the 01597 // feasible region. 01598 pivot(entering_var_index, exiting_base_index); 01599 #if PPL_NOISY_SIMPLEX 01600 ++num_iterations; 01601 if (num_iterations % 200 == 0) 01602 std::cout << "Primal simplex: iteration " << num_iterations 01603 << "." << std::endl; 01604 #endif // PPL_NOISY_SIMPLEX 01605 } 01606 } 01607 01608 01609 // See pages 55-56 of [PapadimitriouS98]. 01610 void 01611 PPL::MIP_Problem::erase_artificials(const dimension_type begin_artificials, 01612 const dimension_type end_artificials) { 01613 PPL_ASSERT(0 < begin_artificials && begin_artificials < end_artificials); 01614 01615 const dimension_type old_last_column = tableau.num_columns() - 1; 01616 dimension_type tableau_n_rows = tableau.num_rows(); 01617 // Step 1: try to remove from the base all the remaining slack variables. 01618 for (dimension_type i = 0; i < tableau_n_rows; ++i) 01619 if (begin_artificials <= base[i] && base[i] < end_artificials) { 01620 // Search for a non-zero element to enter the base. 01621 Row& tableau_i = tableau[i]; 01622 bool redundant = true; 01623 Row::const_iterator j = tableau_i.begin(); 01624 Row::const_iterator j_end = tableau_i.end(); 01625 // Skip the first element 01626 if (j != j_end && j.index() == 0) 01627 ++j; 01628 for ( ; (j != j_end) && (j.index() < begin_artificials); ++j) 01629 if (*j != 0) { 01630 pivot(j.index(), i); 01631 redundant = false; 01632 break; 01633 } 01634 if (redundant) { 01635 // No original variable entered the base: 01636 // the constraint is redundant and should be deleted. 01637 --tableau_n_rows; 01638 if (i < tableau_n_rows) { 01639 // Replace the redundant row with the last one, 01640 // taking care of adjusting the iteration index. 01641 tableau_i.m_swap(tableau[tableau_n_rows]); 01642 base[i] = base[tableau_n_rows]; 01643 --i; 01644 } 01645 tableau.remove_trailing_rows(1); 01646 base.pop_back(); 01647 } 01648 } 01649 01650 // Step 2: Adjust data structures so as to enter phase 2 of the simplex. 01651 01652 // Resize the tableau. 01653 const dimension_type num_artificials = end_artificials - begin_artificials; 01654 tableau.remove_trailing_columns(num_artificials); 01655 01656 // Zero the last column of the tableau. 01657 const dimension_type new_last_column = tableau.num_columns() - 1; 01658 for (dimension_type i = tableau_n_rows; i-- > 0; ) 01659 tableau[i].reset(new_last_column); 01660 01661 // ... then properly set the element in the (new) last column, 01662 // encoding the kind of optimization; ... 01663 { 01664 // This block is equivalent to 01665 // 01666 // <CODE> 01667 // working_cost[new_last_column] = working_cost.get(old_last_column); 01668 // </CODE> 01669 // 01670 // but it avoids storing zeroes. 01671 Coefficient_traits::const_reference old_cost 01672 = working_cost.get(old_last_column); 01673 if (old_cost == 0) 01674 working_cost.reset(new_last_column); 01675 else 01676 working_cost.insert(new_last_column, old_cost); 01677 } 01678 01679 // ... and finally remove redundant columns. 01680 const dimension_type working_cost_new_size 01681 = working_cost.size() - num_artificials; 01682 working_cost.shrink(working_cost_new_size); 01683 } 01684 01685 // See page 55 of [PapadimitriouS98]. 01686 void 01687 PPL::MIP_Problem::compute_generator() const { 01688 // Early exit for 0-dimensional problems. 01689 if (external_space_dim == 0) { 01690 MIP_Problem& x = const_cast<MIP_Problem&>(*this); 01691 x.last_generator = point(); 01692 return; 01693 } 01694 01695 // We will store in numer[] and in denom[] the numerators and 01696 // the denominators of every variable of the original problem. 01697 std::vector<Coefficient> numer(external_space_dim); 01698 std::vector<Coefficient> denom(external_space_dim); 01699 dimension_type row = 0; 01700 01701 PPL_DIRTY_TEMP_COEFFICIENT(lcm); 01702 // Speculatively allocate temporaries out of loop. 01703 PPL_DIRTY_TEMP_COEFFICIENT(split_numer); 01704 PPL_DIRTY_TEMP_COEFFICIENT(split_denom); 01705 01706 // We start to compute numer[] and denom[]. 01707 for (dimension_type i = external_space_dim; i-- > 0; ) { 01708 Coefficient& numer_i = numer[i]; 01709 Coefficient& denom_i = denom[i]; 01710 // Get the value of the variable from the tableau 01711 // (if it is not a basic variable, the value is 0). 01712 const dimension_type original_var = mapping[i+1].first; 01713 if (is_in_base(original_var, row)) { 01714 const Row& t_row = tableau[row]; 01715 Coefficient_traits::const_reference t_row_original_var 01716 = t_row.get(original_var); 01717 if (t_row_original_var > 0) { 01718 neg_assign(numer_i, t_row.get(0)); 01719 denom_i = t_row_original_var; 01720 } 01721 else { 01722 numer_i = t_row.get(0); 01723 neg_assign(denom_i, t_row_original_var); 01724 } 01725 } 01726 else { 01727 numer_i = 0; 01728 denom_i = 1; 01729 } 01730 // Check whether the variable was split. 01731 const dimension_type split_var = mapping[i+1].second; 01732 if (split_var != 0) { 01733 // The variable was split: get the value for the negative component, 01734 // having index mapping[i+1].second . 01735 // Like before, we he have to check if the variable is in base. 01736 if (is_in_base(split_var, row)) { 01737 const Row& t_row = tableau[row]; 01738 Coefficient_traits::const_reference t_row_split_var 01739 = t_row.get(split_var); 01740 if (t_row_split_var > 0) { 01741 neg_assign(split_numer, t_row.get(0)); 01742 split_denom = t_row_split_var; 01743 } 01744 else { 01745 split_numer = t_row.get(0); 01746 neg_assign(split_denom, t_row_split_var); 01747 } 01748 // We compute the lcm to compute subsequently the difference 01749 // between the 2 variables. 01750 lcm_assign(lcm, denom_i, split_denom); 01751 exact_div_assign(denom_i, lcm, denom_i); 01752 exact_div_assign(split_denom, lcm, split_denom); 01753 numer_i *= denom_i; 01754 sub_mul_assign(numer_i, split_numer, split_denom); 01755 if (numer_i == 0) 01756 denom_i = 1; 01757 else 01758 denom_i = lcm; 01759 } 01760 // Note: if the negative component was not in base, then 01761 // it has value zero and there is nothing left to do. 01762 } 01763 } 01764 01765 // Compute the lcm of all denominators. 01766 PPL_ASSERT(external_space_dim > 0); 01767 lcm = denom[0]; 01768 for (dimension_type i = 1; i < external_space_dim; ++i) 01769 lcm_assign(lcm, lcm, denom[i]); 01770 // Use the denominators to store the numerators' multipliers 01771 // and then compute the normalized numerators. 01772 for (dimension_type i = external_space_dim; i-- > 0; ) { 01773 exact_div_assign(denom[i], lcm, denom[i]); 01774 numer[i] *= denom[i]; 01775 } 01776 01777 // Finally, build the generator. 01778 Linear_Expression expr; 01779 for (dimension_type i = external_space_dim; i-- > 0; ) 01780 add_mul_assign(expr, numer[i], Variable(i)); 01781 01782 MIP_Problem& x = const_cast<MIP_Problem&>(*this); 01783 x.last_generator = point(expr, lcm); 01784 } 01785 01786 void 01787 PPL::MIP_Problem::second_phase() { 01788 // Second_phase requires that *this is satisfiable. 01789 PPL_ASSERT(status == SATISFIABLE 01790 || status == UNBOUNDED 01791 || status == OPTIMIZED); 01792 // In the following cases the problem is already solved. 01793 if (status == UNBOUNDED || status == OPTIMIZED) 01794 return; 01795 01796 // Build the objective function for the second phase. 01797 const dimension_type input_obj_function_sd 01798 = input_obj_function.space_dimension(); 01799 Row new_cost(input_obj_function_sd + 1, Row_Flags()); 01800 { 01801 // This will be used as a hint. 01802 Row::iterator itr = new_cost.end(); 01803 for (dimension_type i = input_obj_function_sd; i-- > 0; ) { 01804 Coefficient_traits::const_reference c 01805 = input_obj_function.coefficient(Variable(i)); 01806 if (c != 0) 01807 itr = new_cost.insert(itr, i + 1, c); 01808 } 01809 if (input_obj_function.inhomogeneous_term() != 0) 01810 itr = new_cost.insert(itr, 0, input_obj_function.inhomogeneous_term()); 01811 } 01812 01813 // Negate the cost function if we are minimizing. 01814 if (opt_mode == MINIMIZATION) 01815 for (Row::iterator 01816 i = new_cost.begin(), i_end = new_cost.end(); i != i_end; ++i) 01817 neg_assign(*i); 01818 01819 const dimension_type cost_zero_size = working_cost.size(); 01820 01821 // Substitute properly the cost function in the `costs' matrix. 01822 { 01823 working_cost_type tmp_cost 01824 = working_cost_type(cost_zero_size, cost_zero_size, new_cost.flags()); 01825 tmp_cost.m_swap(working_cost); 01826 } 01827 01828 { 01829 working_cost_type::iterator itr 01830 = working_cost.insert(cost_zero_size - 1, Coefficient_one()); 01831 01832 // Split the variables in the cost function. 01833 for (Row::const_iterator 01834 i = new_cost.lower_bound(1), i_end = new_cost.end(); 01835 i != i_end; ++i) { 01836 const dimension_type index = i.index(); 01837 const dimension_type original_var = mapping[index].first; 01838 const dimension_type split_var = mapping[index].second; 01839 itr = working_cost.insert(itr, original_var, *i); 01840 if (mapping[index].second != 0) { 01841 itr = working_cost.insert(itr, split_var); 01842 neg_assign(*itr, *i); 01843 } 01844 } 01845 } 01846 01847 // Here the first phase problem succeeded with optimum value zero. 01848 // Express the old cost function in terms of the computed base. 01849 { 01850 working_cost_type::iterator itr = working_cost.end(); 01851 for (dimension_type i = tableau.num_rows(); i-- > 0; ) { 01852 const dimension_type base_i = base[i]; 01853 itr = working_cost.lower_bound(itr, base_i); 01854 if (itr != working_cost.end() && itr.index() == base_i && *itr != 0) { 01855 linear_combine(working_cost, tableau[i], base_i); 01856 itr = working_cost.end(); 01857 } 01858 } 01859 } 01860 01861 // Solve the second phase problem. 01862 bool second_phase_successful 01863 = (get_control_parameter(PRICING) == PRICING_STEEPEST_EDGE_FLOAT) 01864 ? compute_simplex_using_steepest_edge_float() 01865 : compute_simplex_using_exact_pricing(); 01866 compute_generator(); 01867 #if PPL_NOISY_SIMPLEX 01868 std::cout << "MIP_Problem::second_phase(): 2nd phase ended at iteration " 01869 << num_iterations 01870 << "." << std::endl; 01871 #endif // PPL_NOISY_SIMPLEX 01872 status = second_phase_successful ? OPTIMIZED : UNBOUNDED; 01873 PPL_ASSERT(OK()); 01874 } 01875 01876 void 01877 PPL::MIP_Problem 01878 ::evaluate_objective_function(const Generator& evaluating_point, 01879 Coefficient& numer, 01880 Coefficient& denom) const { 01881 const dimension_type ep_space_dim = evaluating_point.space_dimension(); 01882 if (space_dimension() < ep_space_dim) 01883 throw std::invalid_argument("PPL::MIP_Problem::" 01884 "evaluate_objective_function(p, n, d):\n" 01885 "*this and p are dimension incompatible."); 01886 if (!evaluating_point.is_point()) 01887 throw std::invalid_argument("PPL::MIP_Problem::" 01888 "evaluate_objective_function(p, n, d):\n" 01889 "p is not a point."); 01890 01891 // Compute the smallest space dimension between `input_obj_function' 01892 // and `evaluating_point'. 01893 const dimension_type working_space_dim 01894 = std::min(ep_space_dim, input_obj_function.space_dimension()); 01895 // Compute the optimal value of the cost function. 01896 Coefficient_traits::const_reference divisor = evaluating_point.divisor(); 01897 numer = input_obj_function.inhomogeneous_term() * divisor; 01898 for (dimension_type i = working_space_dim; i-- > 0; ) 01899 numer += evaluating_point.coefficient(Variable(i)) 01900 * input_obj_function.coefficient(Variable(i)); 01901 // Numerator and denominator should be coprime. 01902 normalize2(numer, divisor, numer, denom); 01903 } 01904 01905 bool 01906 PPL::MIP_Problem::is_lp_satisfiable() const { 01907 #if PPL_NOISY_SIMPLEX 01908 num_iterations = 0; 01909 #endif // PPL_NOISY_SIMPLEX 01910 switch (status) { 01911 case UNSATISFIABLE: 01912 return false; 01913 case SATISFIABLE: 01914 // Intentionally fall through. 01915 case UNBOUNDED: 01916 // Intentionally fall through. 01917 case OPTIMIZED: 01918 // Intentionally fall through. 01919 return true; 01920 case PARTIALLY_SATISFIABLE: 01921 { 01922 MIP_Problem& x = const_cast<MIP_Problem&>(*this); 01923 // This code tries to handle the case that happens if the tableau is 01924 // empty, so it must be initialized. 01925 if (tableau.num_columns() == 0) { 01926 // Add two columns, the first that handles the inhomogeneous term and 01927 // the second that represent the `sign'. 01928 x.tableau.add_zero_columns(2); 01929 // Sync `mapping' for the inhomogeneous term. 01930 x.mapping.push_back(std::make_pair(0, 0)); 01931 // The internal data structures are ready, so prepare for more 01932 // assertion to be checked. 01933 x.initialized = true; 01934 } 01935 01936 // Apply incrementality to the pending constraint system. 01937 x.process_pending_constraints(); 01938 // Update `first_pending_constraint': no more pending. 01939 x.first_pending_constraint = input_cs.size(); 01940 // Update also `internal_space_dim'. 01941 x.internal_space_dim = x.external_space_dim; 01942 PPL_ASSERT(OK()); 01943 return status != UNSATISFIABLE; 01944 } 01945 } 01946 // We should not be here! 01947 PPL_UNREACHABLE; 01948 return false; 01949 } 01950 01951 PPL::MIP_Problem_Status 01952 PPL::MIP_Problem::solve_mip(bool& have_incumbent_solution, 01953 mpq_class& incumbent_solution_value, 01954 Generator& incumbent_solution_point, 01955 MIP_Problem& mip, 01956 const Variables_Set& i_vars) { 01957 // Solve the problem as a non MIP one, it must be done internally. 01958 PPL::MIP_Problem_Status mip_status; 01959 if (mip.is_lp_satisfiable()) { 01960 mip.second_phase(); 01961 mip_status = (mip.status == OPTIMIZED) ? OPTIMIZED_MIP_PROBLEM 01962 : UNBOUNDED_MIP_PROBLEM; 01963 } 01964 else 01965 return UNFEASIBLE_MIP_PROBLEM; 01966 01967 PPL_DIRTY_TEMP(mpq_class, tmp_rational); 01968 01969 Generator p = point(); 01970 PPL_DIRTY_TEMP_COEFFICIENT(tmp_coeff1); 01971 PPL_DIRTY_TEMP_COEFFICIENT(tmp_coeff2); 01972 01973 if (mip_status == UNBOUNDED_MIP_PROBLEM) 01974 p = mip.last_generator; 01975 else { 01976 PPL_ASSERT(mip_status == OPTIMIZED_MIP_PROBLEM); 01977 // Do not call optimizing_point(). 01978 p = mip.last_generator; 01979 mip.evaluate_objective_function(p, tmp_coeff1, tmp_coeff2); 01980 assign_r(tmp_rational.get_num(), tmp_coeff1, ROUND_NOT_NEEDED); 01981 assign_r(tmp_rational.get_den(), tmp_coeff2, ROUND_NOT_NEEDED); 01982 PPL_ASSERT(is_canonical(tmp_rational)); 01983 if (have_incumbent_solution 01984 && ((mip.optimization_mode() == MAXIMIZATION 01985 && tmp_rational <= incumbent_solution_value) 01986 || (mip.optimization_mode() == MINIMIZATION 01987 && tmp_rational >= incumbent_solution_value))) 01988 // Abandon this path. 01989 return mip_status; 01990 } 01991 01992 bool found_satisfiable_generator = true; 01993 PPL_DIRTY_TEMP_COEFFICIENT(gcd); 01994 Coefficient_traits::const_reference p_divisor = p.divisor(); 01995 dimension_type non_int_dim = mip.space_dimension(); 01996 for (Variables_Set::const_iterator v_begin = i_vars.begin(), 01997 v_end = i_vars.end(); v_begin != v_end; ++v_begin) { 01998 gcd_assign(gcd, p.coefficient(Variable(*v_begin)), p_divisor); 01999 if (gcd != p_divisor) { 02000 non_int_dim = *v_begin; 02001 found_satisfiable_generator = false; 02002 break; 02003 } 02004 } 02005 if (found_satisfiable_generator) { 02006 // All the coordinates of `point' are satisfiable. 02007 if (mip_status == UNBOUNDED_MIP_PROBLEM) { 02008 // This is a point that belongs to the MIP_Problem. 02009 // In this way we are sure that we will return every time 02010 // a feasible point if requested by the user. 02011 incumbent_solution_point = p; 02012 return mip_status; 02013 } 02014 if (!have_incumbent_solution 02015 || (mip.optimization_mode() == MAXIMIZATION 02016 && tmp_rational > incumbent_solution_value) 02017 || tmp_rational < incumbent_solution_value) { 02018 incumbent_solution_value = tmp_rational; 02019 incumbent_solution_point = p; 02020 have_incumbent_solution = true; 02021 #if PPL_NOISY_SIMPLEX 02022 PPL_DIRTY_TEMP_COEFFICIENT(numer); 02023 PPL_DIRTY_TEMP_COEFFICIENT(denom); 02024 mip.evaluate_objective_function(p, numer, denom); 02025 std::cout << "MIP_Problem::solve_mip(): " 02026 << "new value found: " << numer << "/" << denom 02027 << "." << std::endl; 02028 #endif // PPL_NOISY_SIMPLEX 02029 } 02030 return mip_status; 02031 } 02032 02033 PPL_ASSERT(non_int_dim < mip.space_dimension()); 02034 02035 assign_r(tmp_rational.get_num(), p.coefficient(Variable(non_int_dim)), 02036 ROUND_NOT_NEEDED); 02037 assign_r(tmp_rational.get_den(), p_divisor, ROUND_NOT_NEEDED); 02038 tmp_rational.canonicalize(); 02039 assign_r(tmp_coeff1, tmp_rational, ROUND_DOWN); 02040 assign_r(tmp_coeff2, tmp_rational, ROUND_UP); 02041 { 02042 MIP_Problem mip_aux(mip, Inherit_Constraints()); 02043 mip_aux.add_constraint(Variable(non_int_dim) <= tmp_coeff1); 02044 #if PPL_NOISY_SIMPLEX 02045 using namespace IO_Operators; 02046 std::cout << "MIP_Problem::solve_mip(): " 02047 << "descending with: " 02048 << (Variable(non_int_dim) <= tmp_coeff1) 02049 << "." << std::endl; 02050 #endif // PPL_NOISY_SIMPLEX 02051 solve_mip(have_incumbent_solution, incumbent_solution_value, 02052 incumbent_solution_point, mip_aux, i_vars); 02053 } 02054 // TODO: change this when we will be able to remove constraints. 02055 mip.add_constraint(Variable(non_int_dim) >= tmp_coeff2); 02056 #if PPL_NOISY_SIMPLEX 02057 using namespace IO_Operators; 02058 std::cout << "MIP_Problem::solve_mip(): " 02059 << "descending with: " 02060 << (Variable(non_int_dim) >= tmp_coeff2) 02061 << "." << std::endl; 02062 #endif // PPL_NOISY_SIMPLEX 02063 solve_mip(have_incumbent_solution, incumbent_solution_value, 02064 incumbent_solution_point, mip, i_vars); 02065 return have_incumbent_solution ? mip_status : UNFEASIBLE_MIP_PROBLEM; 02066 } 02067 02068 bool 02069 PPL::MIP_Problem::choose_branching_variable(const MIP_Problem& mip, 02070 const Variables_Set& i_vars, 02071 dimension_type& branching_index) { 02072 // Insert here the variables that do not satisfy the integrality 02073 // condition. 02074 const std::vector<Constraint*>& input_cs = mip.input_cs; 02075 const Generator& last_generator = mip.last_generator; 02076 Coefficient_traits::const_reference last_generator_divisor 02077 = last_generator.divisor(); 02078 Variables_Set candidate_variables; 02079 02080 PPL_DIRTY_TEMP_COEFFICIENT(gcd); 02081 for (Variables_Set::const_iterator v_it = i_vars.begin(), 02082 v_end = i_vars.end(); v_it != v_end; ++v_it) { 02083 gcd_assign(gcd, 02084 last_generator.coefficient(Variable(*v_it)), 02085 last_generator_divisor); 02086 if (gcd != last_generator_divisor) 02087 candidate_variables.insert(*v_it); 02088 } 02089 // If this set is empty, we have finished. 02090 if (candidate_variables.empty()) 02091 return true; 02092 02093 // Check how many `active constraints' we have and track them. 02094 const dimension_type input_cs_num_rows = input_cs.size(); 02095 std::deque<bool> satisfiable_constraints (input_cs_num_rows, false); 02096 for (dimension_type i = input_cs_num_rows; i-- > 0; ) 02097 // An equality is an `active constraint' by definition. 02098 // If we have an inequality, check if it is an `active constraint'. 02099 if (input_cs[i]->is_equality() 02100 || is_saturated(*(input_cs[i]), last_generator)) 02101 satisfiable_constraints[i] = true; 02102 02103 dimension_type current_num_appearances = 0; 02104 dimension_type winning_num_appearances = 0; 02105 02106 // For every candidate variable, check how many times this appear in the 02107 // active constraints. 02108 for (Variables_Set::const_iterator v_it = candidate_variables.begin(), 02109 v_end = candidate_variables.end(); v_it != v_end; ++v_it) { 02110 current_num_appearances = 0; 02111 for (dimension_type i = input_cs_num_rows; i-- > 0; ) 02112 if (satisfiable_constraints[i] 02113 && *v_it < input_cs[i]->space_dimension() 02114 && input_cs[i]->coefficient(Variable(*v_it)) != 0) 02115 ++current_num_appearances; 02116 if (current_num_appearances >= winning_num_appearances) { 02117 winning_num_appearances = current_num_appearances; 02118 branching_index = *v_it; 02119 } 02120 } 02121 return false; 02122 } 02123 02124 bool 02125 PPL::MIP_Problem::is_mip_satisfiable(MIP_Problem& mip, 02126 const Variables_Set& i_vars, 02127 Generator& p) { 02128 #if PPL_NOISY_SIMPLEX 02129 ++mip_recursion_level; 02130 std::cout << "MIP_Problem::is_mip_satisfiable(): " 02131 << "entering recursion level " << mip_recursion_level 02132 << "." << std::endl; 02133 #endif // PPL_NOISY_SIMPLEX 02134 PPL_ASSERT(mip.integer_space_dimensions().empty()); 02135 02136 // Solve the MIP problem. 02137 if (!mip.is_lp_satisfiable()) { 02138 #if PPL_NOISY_SIMPLEX 02139 std::cout << "MIP_Problem::is_mip_satisfiable(): " 02140 << "exiting from recursion level " << mip_recursion_level 02141 << "." << std::endl; 02142 --mip_recursion_level; 02143 #endif // PPL_NOISY_SIMPLEX 02144 return false; 02145 } 02146 02147 PPL_DIRTY_TEMP(mpq_class, tmp_rational); 02148 PPL_DIRTY_TEMP_COEFFICIENT(tmp_coeff1); 02149 PPL_DIRTY_TEMP_COEFFICIENT(tmp_coeff2); 02150 bool found_satisfiable_generator = true; 02151 dimension_type non_int_dim; 02152 p = mip.last_generator; 02153 Coefficient_traits::const_reference p_divisor = p.divisor(); 02154 02155 #if PPL_SIMPLEX_USE_MIP_HEURISTIC 02156 found_satisfiable_generator 02157 = choose_branching_variable(mip, i_vars, non_int_dim); 02158 #else 02159 PPL_DIRTY_TEMP_COEFFICIENT(gcd); 02160 for (Variables_Set::const_iterator v_begin = i_vars.begin(), 02161 v_end = i_vars.end(); v_begin != v_end; ++v_begin) { 02162 gcd_assign(gcd, p.coefficient(Variable(*v_begin)), p_divisor); 02163 if (gcd != p_divisor) { 02164 non_int_dim = *v_begin; 02165 found_satisfiable_generator = false; 02166 break; 02167 } 02168 } 02169 #endif 02170 02171 if (found_satisfiable_generator) 02172 return true; 02173 02174 PPL_ASSERT(non_int_dim < mip.space_dimension()); 02175 02176 assign_r(tmp_rational.get_num(), p.coefficient(Variable(non_int_dim)), 02177 ROUND_NOT_NEEDED); 02178 assign_r(tmp_rational.get_den(), p_divisor, ROUND_NOT_NEEDED); 02179 tmp_rational.canonicalize(); 02180 assign_r(tmp_coeff1, tmp_rational, ROUND_DOWN); 02181 assign_r(tmp_coeff2, tmp_rational, ROUND_UP); 02182 { 02183 MIP_Problem mip_aux(mip, Inherit_Constraints()); 02184 mip_aux.add_constraint(Variable(non_int_dim) <= tmp_coeff1); 02185 #if PPL_NOISY_SIMPLEX 02186 using namespace IO_Operators; 02187 std::cout << "MIP_Problem::is_mip_satisfiable(): " 02188 << "descending with: " 02189 << (Variable(non_int_dim) <= tmp_coeff1) 02190 << "." << std::endl; 02191 #endif // PPL_NOISY_SIMPLEX 02192 if (is_mip_satisfiable(mip_aux, i_vars, p)) { 02193 #if PPL_NOISY_SIMPLEX 02194 std::cout << "MIP_Problem::is_mip_satisfiable(): " 02195 << "exiting from recursion level " << mip_recursion_level 02196 << "." << std::endl; 02197 --mip_recursion_level; 02198 #endif // PPL_NOISY_SIMPLEX 02199 return true; 02200 } 02201 } 02202 mip.add_constraint(Variable(non_int_dim) >= tmp_coeff2); 02203 #if PPL_NOISY_SIMPLEX 02204 using namespace IO_Operators; 02205 std::cout << "MIP_Problem::is_mip_satisfiable(): " 02206 << "descending with: " 02207 << (Variable(non_int_dim) >= tmp_coeff2) 02208 << "." << std::endl; 02209 #endif // PPL_NOISY_SIMPLEX 02210 bool satisfiable = is_mip_satisfiable(mip, i_vars, p); 02211 #if PPL_NOISY_SIMPLEX 02212 std::cout << "MIP_Problem::is_mip_satisfiable(): " 02213 << "exiting from recursion level " << mip_recursion_level 02214 << "." << std::endl; 02215 --mip_recursion_level; 02216 #endif // PPL_NOISY_SIMPLEX 02217 return satisfiable; 02218 } 02219 02220 bool 02221 PPL::MIP_Problem::OK() const { 02222 #ifndef NDEBUG 02223 using std::endl; 02224 using std::cerr; 02225 #endif 02226 const dimension_type input_cs_num_rows = input_cs.size(); 02227 02228 // Check that every member used is OK. 02229 02230 if (inherited_constraints > input_cs_num_rows) { 02231 #ifndef NDEBUG 02232 cerr << "The MIP_Problem claims to have inherited from its ancestors " 02233 << "more constraints than are recorded in this->input_cs." 02234 << endl; 02235 ascii_dump(cerr); 02236 #endif 02237 return false; 02238 } 02239 02240 if (first_pending_constraint > input_cs_num_rows) { 02241 #ifndef NDEBUG 02242 cerr << "The MIP_Problem claims to have pending constraints " 02243 << "that are not recorded in this->input_cs." 02244 << endl; 02245 ascii_dump(cerr); 02246 #endif 02247 return false; 02248 } 02249 02250 for (dimension_type i = input_cs_num_rows; i-- > 0; ) 02251 if (!input_cs[i]->OK()) 02252 return false; 02253 02254 if (!tableau.OK() || !input_obj_function.OK() || !last_generator.OK()) 02255 return false; 02256 02257 // Constraint system should contain no strict inequalities. 02258 for (dimension_type i = input_cs_num_rows; i-- > 0; ) 02259 if (input_cs[i]->is_strict_inequality()) { 02260 #ifndef NDEBUG 02261 cerr << "The feasible region of the MIP_Problem is defined by " 02262 << "a constraint system containing strict inequalities." 02263 << endl; 02264 ascii_dump(cerr); 02265 #endif 02266 return false; 02267 } 02268 02269 // Constraint system and objective function should be dimension compatible. 02270 if (external_space_dim < input_obj_function.space_dimension()) { 02271 #ifndef NDEBUG 02272 cerr << "The MIP_Problem and the objective function have " 02273 << "incompatible space dimensions (" 02274 << external_space_dim << " < " 02275 << input_obj_function.space_dimension() << ")." 02276 << endl; 02277 ascii_dump(cerr); 02278 #endif 02279 return false; 02280 } 02281 02282 if (status != UNSATISFIABLE && initialized) { 02283 // Here `last_generator' has to be meaningful. 02284 // Check for dimension compatibility and actual feasibility. 02285 if (external_space_dim != last_generator.space_dimension()) { 02286 #ifndef NDEBUG 02287 cerr << "The MIP_Problem and the cached feasible point have " 02288 << "incompatible space dimensions (" 02289 << external_space_dim << " != " 02290 << last_generator.space_dimension() << ")." 02291 << endl; 02292 ascii_dump(cerr); 02293 #endif 02294 return false; 02295 } 02296 02297 for (dimension_type i = 0; i < first_pending_constraint; ++i) 02298 if (!is_satisfied(*(input_cs[i]), last_generator)) { 02299 #ifndef NDEBUG 02300 cerr << "The cached feasible point does not belong to " 02301 << "the feasible region of the MIP_Problem." 02302 << endl; 02303 ascii_dump(cerr); 02304 #endif 02305 return false; 02306 } 02307 02308 // Check that every integer declared variable is really integer. 02309 // in the solution found. 02310 if (!i_variables.empty()) { 02311 PPL_DIRTY_TEMP_COEFFICIENT(gcd); 02312 for (Variables_Set::const_iterator v_it = i_variables.begin(), 02313 v_end = i_variables.end(); v_it != v_end; ++v_it) { 02314 gcd_assign(gcd, last_generator.coefficient(Variable(*v_it)), 02315 last_generator.divisor()); 02316 if (gcd != last_generator.divisor()) 02317 return false; 02318 } 02319 } 02320 02321 const dimension_type tableau_num_rows = tableau.num_rows(); 02322 const dimension_type tableau_num_columns = tableau.num_columns(); 02323 02324 // The number of rows in the tableau and base should be equal. 02325 if (tableau_num_rows != base.size()) { 02326 #ifndef NDEBUG 02327 cerr << "tableau and base have incompatible sizes" << endl; 02328 ascii_dump(cerr); 02329 #endif 02330 return false; 02331 } 02332 // The size of `mapping' should be equal to the space dimension 02333 // of `input_cs' plus one. 02334 if (mapping.size() != external_space_dim + 1) { 02335 #ifndef NDEBUG 02336 cerr << "`input_cs' and `mapping' have incompatible sizes" << endl; 02337 ascii_dump(cerr); 02338 #endif 02339 return false; 02340 } 02341 02342 // The number of columns in the tableau and working_cost should be equal. 02343 if (tableau_num_columns != working_cost.size()) { 02344 #ifndef NDEBUG 02345 cerr << "tableau and working_cost have incompatible sizes" << endl; 02346 ascii_dump(cerr); 02347 #endif 02348 return false; 02349 } 02350 02351 // The vector base should contain indices of tableau's columns. 02352 for (dimension_type i = base.size(); i-- > 0; ) { 02353 if (base[i] > tableau_num_columns) { 02354 #ifndef NDEBUG 02355 cerr << "base contains an invalid column index" << endl; 02356 ascii_dump(cerr); 02357 #endif 02358 return false; 02359 } 02360 } 02361 { 02362 // Needed to sort accesses to tableau_j, improving performance. 02363 typedef std::vector<std::pair<dimension_type, dimension_type> > 02364 pair_vector_t; 02365 pair_vector_t vars_in_base; 02366 for (dimension_type i = base.size(); i-- > 0; ) 02367 vars_in_base.push_back(std::make_pair(base[i], i)); 02368 02369 std::sort(vars_in_base.begin(), vars_in_base.end()); 02370 02371 for (dimension_type j = tableau_num_rows; j-- > 0; ) { 02372 const Row& tableau_j = tableau[j]; 02373 pair_vector_t::iterator i = vars_in_base.begin(); 02374 pair_vector_t::iterator i_end = vars_in_base.end(); 02375 Row::const_iterator itr = tableau_j.begin(); 02376 Row::const_iterator itr_end = tableau_j.end(); 02377 for ( ; i != i_end && itr != itr_end; ++i) { 02378 // tableau[i][base[j]], with i different from j, must be zero. 02379 if (itr.index() < i->first) 02380 itr = tableau_j.lower_bound(itr, itr.index()); 02381 if (i->second != j && itr.index() == i->first && *itr != 0) { 02382 #ifndef NDEBUG 02383 cerr << "tableau[i][base[j]], with i different from j, must be " 02384 << "zero" << endl; 02385 ascii_dump(cerr); 02386 #endif 02387 return false; 02388 } 02389 } 02390 } 02391 } 02392 // tableau[i][base[i]] must not be a zero. 02393 for (dimension_type i = base.size(); i-- > 0; ) { 02394 if (tableau[i].get(base[i]) == 0) { 02395 #ifndef NDEBUG 02396 cerr << "tableau[i][base[i]] must not be a zero" << endl; 02397 ascii_dump(cerr); 02398 #endif 02399 return false; 02400 } 02401 } 02402 02403 // The last column of the tableau must contain only zeroes. 02404 for (dimension_type i = tableau_num_rows; i-- > 0; ) 02405 if (tableau[i].get(tableau_num_columns - 1) != 0) { 02406 #ifndef NDEBUG 02407 cerr << "the last column of the tableau must contain only" 02408 "zeroes"<< endl; 02409 ascii_dump(cerr); 02410 #endif 02411 return false; 02412 } 02413 } 02414 02415 // All checks passed. 02416 return true; 02417 } 02418 02419 void 02420 PPL::MIP_Problem::ascii_dump(std::ostream& s) const { 02421 using namespace IO_Operators; 02422 s << "\nexternal_space_dim: " << external_space_dim << " \n"; 02423 s << "\ninternal_space_dim: " << internal_space_dim << " \n"; 02424 02425 const dimension_type input_cs_size = input_cs.size(); 02426 02427 s << "\ninput_cs( " << input_cs_size << " )\n"; 02428 for (dimension_type i = 0; i < input_cs_size; ++i) 02429 input_cs[i]->ascii_dump(s); 02430 02431 s << "\ninherited_constraints: " << inherited_constraints 02432 << std::endl; 02433 02434 s << "\nfirst_pending_constraint: " << first_pending_constraint 02435 << std::endl; 02436 02437 s << "\ninput_obj_function\n"; 02438 input_obj_function.ascii_dump(s); 02439 s << "\nopt_mode " 02440 << ((opt_mode == MAXIMIZATION) ? "MAXIMIZATION" : "MINIMIZATION") << "\n"; 02441 02442 s << "\ninitialized: " << (initialized ? "YES" : "NO") << "\n"; 02443 s << "\npricing: "; 02444 switch (pricing) { 02445 case PRICING_STEEPEST_EDGE_FLOAT: 02446 s << "PRICING_STEEPEST_EDGE_FLOAT"; 02447 break; 02448 case PRICING_STEEPEST_EDGE_EXACT: 02449 s << "PRICING_STEEPEST_EDGE_EXACT"; 02450 break; 02451 case PRICING_TEXTBOOK: 02452 s << "PRICING_TEXTBOOK"; 02453 break; 02454 } 02455 s << "\n"; 02456 02457 s << "\nstatus: "; 02458 switch (status) { 02459 case UNSATISFIABLE: 02460 s << "UNSATISFIABLE"; 02461 break; 02462 case SATISFIABLE: 02463 s << "SATISFIABLE"; 02464 break; 02465 case UNBOUNDED: 02466 s << "UNBOUNDED"; 02467 break; 02468 case OPTIMIZED: 02469 s << "OPTIMIZED"; 02470 break; 02471 case PARTIALLY_SATISFIABLE: 02472 s << "PARTIALLY_SATISFIABLE"; 02473 break; 02474 } 02475 s << "\n"; 02476 02477 s << "\ntableau\n"; 02478 tableau.ascii_dump(s); 02479 s << "\nworking_cost( " << working_cost.size()<< " )\n"; 02480 working_cost.ascii_dump(s); 02481 02482 const dimension_type base_size = base.size(); 02483 s << "\nbase( " << base_size << " )\n"; 02484 for (dimension_type i = 0; i != base_size; ++i) 02485 s << base[i] << ' '; 02486 02487 s << "\nlast_generator\n"; 02488 last_generator.ascii_dump(s); 02489 02490 const dimension_type mapping_size = mapping.size(); 02491 s << "\nmapping( " << mapping_size << " )\n"; 02492 for (dimension_type i = 1; i < mapping_size; ++i) 02493 s << "\n"<< i << " -> " << mapping[i].first << " -> " << mapping[i].second 02494 << ' '; 02495 02496 s << "\n\ninteger_variables"; 02497 i_variables.ascii_dump(s); 02498 } 02499 02500 PPL_OUTPUT_DEFINITIONS(MIP_Problem) 02501 02502 bool 02503 PPL::MIP_Problem::ascii_load(std::istream& s) { 02504 std::string str; 02505 if (!(s >> str) || str != "external_space_dim:") 02506 return false; 02507 02508 if (!(s >> external_space_dim)) 02509 return false; 02510 02511 if (!(s >> str) || str != "internal_space_dim:") 02512 return false; 02513 02514 if (!(s >> internal_space_dim)) 02515 return false; 02516 02517 if (!(s >> str) || str != "input_cs(") 02518 return false; 02519 02520 dimension_type input_cs_size; 02521 02522 if (!(s >> input_cs_size)) 02523 return false; 02524 02525 if (!(s >> str) || str != ")") 02526 return false; 02527 02528 Constraint c(Constraint::zero_dim_positivity()); 02529 input_cs.reserve(input_cs_size); 02530 for (dimension_type i = 0; i < input_cs_size; ++i) { 02531 if (!c.ascii_load(s)) 02532 return false; 02533 add_constraint_helper(c); 02534 } 02535 02536 if (!(s >> str) || str != "inherited_constraints:") 02537 return false; 02538 02539 if (!(s >> inherited_constraints)) 02540 return false; 02541 // NOTE: we loaded the number of inherited constraints, but we nonetheless 02542 // reset to zero the corresponding data member, since we do not support 02543 // constraint inheritance via ascii_load. 02544 inherited_constraints = 0; 02545 02546 if (!(s >> str) || str != "first_pending_constraint:") 02547 return false; 02548 02549 if (!(s >> first_pending_constraint)) 02550 return false; 02551 02552 if (!(s >> str) || str != "input_obj_function") 02553 return false; 02554 02555 if (!input_obj_function.ascii_load(s)) 02556 return false; 02557 02558 if (!(s >> str) || str != "opt_mode") 02559 return false; 02560 02561 if (!(s >> str)) 02562 return false; 02563 02564 if (str == "MAXIMIZATION") 02565 set_optimization_mode(MAXIMIZATION); 02566 else { 02567 if (str != "MINIMIZATION") 02568 return false; 02569 set_optimization_mode(MINIMIZATION); 02570 } 02571 02572 if (!(s >> str) || str != "initialized:") 02573 return false; 02574 if (!(s >> str)) 02575 return false; 02576 if (str == "YES") 02577 initialized = true; 02578 else if (str == "NO") 02579 initialized = false; 02580 else 02581 return false; 02582 02583 if (!(s >> str) || str != "pricing:") 02584 return false; 02585 if (!(s >> str)) 02586 return false; 02587 if (str == "PRICING_STEEPEST_EDGE_FLOAT") 02588 pricing = PRICING_STEEPEST_EDGE_FLOAT; 02589 else if (str == "PRICING_STEEPEST_EDGE_EXACT") 02590 pricing = PRICING_STEEPEST_EDGE_EXACT; 02591 else if (str == "PRICING_TEXTBOOK") 02592 pricing = PRICING_TEXTBOOK; 02593 else 02594 return false; 02595 02596 if (!(s >> str) || str != "status:") 02597 return false; 02598 02599 if (!(s >> str)) 02600 return false; 02601 02602 if (str == "UNSATISFIABLE") 02603 status = UNSATISFIABLE; 02604 else if (str == "SATISFIABLE") 02605 status = SATISFIABLE; 02606 else if (str == "UNBOUNDED") 02607 status = UNBOUNDED; 02608 else if (str == "OPTIMIZED") 02609 status = OPTIMIZED; 02610 else if (str == "PARTIALLY_SATISFIABLE") 02611 status = PARTIALLY_SATISFIABLE; 02612 else 02613 return false; 02614 02615 if (!(s >> str) || str != "tableau") 02616 return false; 02617 02618 if (!tableau.ascii_load(s)) 02619 return false; 02620 02621 if (!(s >> str) || str != "working_cost(") 02622 return false; 02623 02624 dimension_type working_cost_dim; 02625 02626 if (!(s >> working_cost_dim)) 02627 return false; 02628 02629 if (!(s >> str) || str != ")") 02630 return false; 02631 02632 if (!working_cost.ascii_load(s)) 02633 return false; 02634 02635 if (!(s >> str) || str != "base(") 02636 return false; 02637 02638 dimension_type base_size; 02639 if (!(s >> base_size)) 02640 return false; 02641 02642 if (!(s >> str) || str != ")") 02643 return false; 02644 02645 for (dimension_type i = 0; i != base_size; ++i) { 02646 dimension_type base_value; 02647 if (!(s >> base_value)) 02648 return false; 02649 base.push_back(base_value); 02650 } 02651 02652 if (!(s >> str) || str != "last_generator") 02653 return false; 02654 02655 if (!last_generator.ascii_load(s)) 02656 return false; 02657 02658 if (!(s >> str) || str != "mapping(") 02659 return false; 02660 02661 dimension_type mapping_size; 02662 if (!(s >> mapping_size)) 02663 return false; 02664 02665 if (!(s >> str) || str != ")") 02666 return false; 02667 02668 // The first `mapping' index is never used, so we initialize 02669 // it pushing back a dummy value. 02670 if (tableau.num_columns() != 0) 02671 mapping.push_back(std::make_pair(0, 0)); 02672 02673 for (dimension_type i = 1; i < mapping_size; ++i) { 02674 dimension_type index; 02675 if (!(s >> index)) 02676 return false; 02677 02678 if (!(s >> str) || str != "->") 02679 return false; 02680 02681 dimension_type first_value; 02682 if (!(s >> first_value)) 02683 return false; 02684 02685 if (!(s >> str) || str != "->") 02686 return false; 02687 02688 dimension_type second_value; 02689 if (!(s >> second_value)) 02690 return false; 02691 02692 mapping.push_back(std::make_pair(first_value, second_value)); 02693 } 02694 02695 if (!(s >> str) || str != "integer_variables") 02696 return false; 02697 02698 if (!i_variables.ascii_load(s)) 02699 return false; 02700 02701 PPL_ASSERT(OK()); 02702 return true; 02703 } 02704 02706 std::ostream& 02707 PPL::IO_Operators::operator<<(std::ostream& s, const MIP_Problem& mip) { 02708 s << "Constraints:"; 02709 for (MIP_Problem::const_iterator i = mip.constraints_begin(), 02710 i_end = mip.constraints_end(); i != i_end; ++i) 02711 s << "\n" << *i; 02712 s << "\nObjective function: " 02713 << mip.objective_function() 02714 << "\nOptimization mode: " 02715 << ((mip.optimization_mode() == MAXIMIZATION) 02716 ? "MAXIMIZATION" 02717 : "MINIMIZATION"); 02718 s << "\nInteger variables: " << mip.integer_space_dimensions(); 02719 return s; 02720 }