1 /* MIP_Problem class implementation: non-inline functions.
2 Copyright (C) 2001-2010 Roberto Bagnara <bagnara@cs.unipr.it>
3 Copyright (C) 2010-2016 BUGSENG srl (http://bugseng.com)
4
5 This file is part of the Parma Polyhedra Library (PPL).
6
7 The PPL is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11
12 The PPL is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software Foundation,
19 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA.
20
21 For the most up-to-date information see the Parma Polyhedra Library
22 site: http://bugseng.com/products/ppl/ . */
23
24 #include "ppl-config.h"
25 #include "MIP_Problem_defs.hh"
26 #include "globals_defs.hh"
27 #include "Checked_Number_defs.hh"
28 #include "Linear_Expression_defs.hh"
29 #include "Constraint_defs.hh"
30 #include "Constraint_System_defs.hh"
31 #include "Constraint_System_inlines.hh"
32 #include "Generator_defs.hh"
33 #include "Scalar_Products_defs.hh"
34 #include "Scalar_Products_inlines.hh"
35 #include "math_utilities_defs.hh"
36
37 // TODO: Remove this when the sparse working cost has been tested enough.
38 #if PPL_USE_SPARSE_MATRIX
39
40 // These are needed for the linear_combine() method that takes a Dense_Row and
41 // a Sparse_Row.
42 #include "Dense_Row_defs.hh"
43 #include "Sparse_Row_defs.hh"
44
45 #endif // PPL_USE_SPARSE_MATRIX
46
47 #ifndef PPL_NOISY_SIMPLEX
48 #define PPL_NOISY_SIMPLEX 0
49 #endif
50
51 #ifndef PPL_SIMPLEX_USE_MIP_HEURISTIC
52 #define PPL_SIMPLEX_USE_MIP_HEURISTIC 1
53 #endif
54
55 #include <stdexcept>
56 #include <deque>
57 #include <vector>
58 #include <algorithm>
59 #include <cmath>
60
61 #if PPL_NOISY_SIMPLEX
62 #include <iostream>
63 #endif
64
65
66 namespace PPL = Parma_Polyhedra_Library;
67
68 #if PPL_NOISY_SIMPLEX
69 namespace {
70
71 unsigned long num_iterations = 0;
72
73 unsigned mip_recursion_level = 0;
74
75 } // namespace
76 #endif // PPL_NOISY_SIMPLEX
77
MIP_Problem(const dimension_type dim)78 PPL::MIP_Problem::MIP_Problem(const dimension_type dim)
79 : external_space_dim(dim),
80 internal_space_dim(0),
81 tableau(),
82 working_cost(0),
83 mapping(),
84 base(),
85 status(PARTIALLY_SATISFIABLE),
86 pricing(PRICING_STEEPEST_EDGE_FLOAT),
87 initialized(false),
88 input_cs(),
89 inherited_constraints(0),
90 first_pending_constraint(0),
91 input_obj_function(),
92 opt_mode(MAXIMIZATION),
93 last_generator(point()),
94 i_variables() {
95 // Check for space dimension overflow.
96 if (dim > max_space_dimension()) {
97 throw std::length_error("PPL::MIP_Problem::MIP_Problem(dim, cs, obj, "
98 "mode):\n"
99 "dim exceeds the maximum allowed "
100 "space dimension.");
101 }
102 PPL_ASSERT(OK());
103 }
104
MIP_Problem(const dimension_type dim,const Constraint_System & cs,const Linear_Expression & obj,const Optimization_Mode mode)105 PPL::MIP_Problem::MIP_Problem(const dimension_type dim,
106 const Constraint_System& cs,
107 const Linear_Expression& obj,
108 const Optimization_Mode mode)
109 : external_space_dim(dim),
110 internal_space_dim(0),
111 tableau(),
112 working_cost(0),
113 mapping(),
114 base(),
115 status(PARTIALLY_SATISFIABLE),
116 pricing(PRICING_STEEPEST_EDGE_FLOAT),
117 initialized(false),
118 input_cs(),
119 inherited_constraints(0),
120 first_pending_constraint(0),
121 input_obj_function(obj),
122 opt_mode(mode),
123 last_generator(point()),
124 i_variables() {
125 // Check for space dimension overflow.
126 if (dim > max_space_dimension()) {
127 throw std::length_error("PPL::MIP_Problem::MIP_Problem(dim, cs, obj, "
128 "mode):\n"
129 "dim exceeds the maximum allowed"
130 "space dimension.");
131 }
132 // Check the objective function.
133 if (obj.space_dimension() > dim) {
134 std::ostringstream s;
135 s << "PPL::MIP_Problem::MIP_Problem(dim, cs, obj,"
136 << " mode):\n"
137 << "obj.space_dimension() == "<< obj.space_dimension()
138 << " exceeds dim == "<< dim << ".";
139 throw std::invalid_argument(s.str());
140 }
141 // Check the constraint system.
142 if (cs.space_dimension() > dim) {
143 std::ostringstream s;
144 s << "PPL::MIP_Problem::MIP_Problem(dim, cs, obj, mode):\n"
145 << "cs.space_dimension == " << cs.space_dimension()
146 << " exceeds dim == " << dim << ".";
147 throw std::invalid_argument(s.str());
148 }
149 if (cs.has_strict_inequalities()) {
150 throw std::invalid_argument("PPL::MIP_Problem::"
151 "MIP_Problem(d, cs, obj, m):\n"
152 "cs contains strict inequalities.");
153 }
154 // Actually copy the constraints.
155 for (Constraint_System::const_iterator
156 i = cs.begin(), i_end = cs.end(); i != i_end; ++i) {
157 add_constraint_helper(*i);
158 }
159
160 PPL_ASSERT(OK());
161 }
162
163 void
add_constraint(const Constraint & c)164 PPL::MIP_Problem::add_constraint(const Constraint& c) {
165 if (space_dimension() < c.space_dimension()) {
166 std::ostringstream s;
167 s << "PPL::MIP_Problem::add_constraint(c):\n"
168 << "c.space_dimension() == "<< c.space_dimension() << " exceeds "
169 "this->space_dimension == " << space_dimension() << ".";
170 throw std::invalid_argument(s.str());
171 }
172 if (c.is_strict_inequality()) {
173 throw std::invalid_argument("PPL::MIP_Problem::add_constraint(c):\n"
174 "c is a strict inequality.");
175 }
176 add_constraint_helper(c);
177 if (status != UNSATISFIABLE) {
178 status = PARTIALLY_SATISFIABLE;
179 }
180 PPL_ASSERT(OK());
181 }
182
183 void
add_constraints(const Constraint_System & cs)184 PPL::MIP_Problem::add_constraints(const Constraint_System& cs) {
185 if (space_dimension() < cs.space_dimension()) {
186 std::ostringstream s;
187 s << "PPL::MIP_Problem::add_constraints(cs):\n"
188 << "cs.space_dimension() == " << cs.space_dimension()
189 << " exceeds this->space_dimension() == " << this->space_dimension()
190 << ".";
191 throw std::invalid_argument(s.str());
192 }
193 if (cs.has_strict_inequalities()) {
194 throw std::invalid_argument("PPL::MIP_Problem::add_constraints(cs):\n"
195 "cs contains strict inequalities.");
196 }
197 for (Constraint_System::const_iterator
198 i = cs.begin(), i_end = cs.end(); i != i_end; ++i) {
199 add_constraint_helper(*i);
200 }
201 if (status != UNSATISFIABLE) {
202 status = PARTIALLY_SATISFIABLE;
203 }
204 PPL_ASSERT(OK());
205 }
206
207 void
set_objective_function(const Linear_Expression & obj)208 PPL::MIP_Problem::set_objective_function(const Linear_Expression& obj) {
209 if (space_dimension() < obj.space_dimension()) {
210 std::ostringstream s;
211 s << "PPL::MIP_Problem::set_objective_function(obj):\n"
212 << "obj.space_dimension() == " << obj.space_dimension()
213 << " exceeds this->space_dimension == " << space_dimension()
214 << ".";
215 throw std::invalid_argument(s.str());
216 }
217 input_obj_function = obj;
218 if (status == UNBOUNDED || status == OPTIMIZED) {
219 status = SATISFIABLE;
220 }
221 PPL_ASSERT(OK());
222 }
223
224 const PPL::Generator&
feasible_point() const225 PPL::MIP_Problem::feasible_point() const {
226 if (is_satisfiable()) {
227 return last_generator;
228 }
229 else {
230 throw std::domain_error("PPL::MIP_Problem::feasible_point():\n"
231 "*this is not satisfiable.");
232 }
233 }
234
235 const PPL::Generator&
optimizing_point() const236 PPL::MIP_Problem::optimizing_point() const {
237 if (solve() == OPTIMIZED_MIP_PROBLEM) {
238 return last_generator;
239 }
240 else {
241 throw std::domain_error("PPL::MIP_Problem::optimizing_point():\n"
242 "*this does not have an optimizing point.");
243 }
244 }
245
246 bool
is_satisfiable() const247 PPL::MIP_Problem::is_satisfiable() const {
248 // Check `status' to filter out trivial cases.
249 switch (status) {
250 case UNSATISFIABLE:
251 PPL_ASSERT(OK());
252 return false;
253 case SATISFIABLE:
254 // Intentionally fall through
255 case UNBOUNDED:
256 // Intentionally fall through.
257 case OPTIMIZED:
258 PPL_ASSERT(OK());
259 return true;
260 case PARTIALLY_SATISFIABLE:
261 {
262 MIP_Problem& x = const_cast<MIP_Problem&>(*this);
263 // LP case.
264 if (x.i_variables.empty()) {
265 return x.is_lp_satisfiable();
266 }
267 // MIP case.
268 {
269 // Temporarily relax the MIP into an LP problem.
270 RAII_Temporary_Real_Relaxation relaxed(x);
271 Generator p = point();
272 relaxed.lp.is_lp_satisfiable();
273 #if PPL_NOISY_SIMPLEX
274 mip_recursion_level = 0;
275 #endif // PPL_NOISY_SIMPLEX
276 if (is_mip_satisfiable(relaxed.lp, relaxed.i_vars, p)) {
277 x.last_generator = p;
278 x.status = SATISFIABLE;
279 }
280 else {
281 x.status = UNSATISFIABLE;
282 }
283 } // `relaxed' destroyed here: relaxation automatically reset.
284 return (x.status == SATISFIABLE);
285 }
286 }
287 // We should not be here!
288 PPL_UNREACHABLE;
289 return false;
290 }
291
292 PPL::MIP_Problem_Status
solve() const293 PPL::MIP_Problem::solve() const{
294 switch (status) {
295 case UNSATISFIABLE:
296 PPL_ASSERT(OK());
297 return UNFEASIBLE_MIP_PROBLEM;
298 case UNBOUNDED:
299 PPL_ASSERT(OK());
300 return UNBOUNDED_MIP_PROBLEM;
301 case OPTIMIZED:
302 PPL_ASSERT(OK());
303 return OPTIMIZED_MIP_PROBLEM;
304 case SATISFIABLE:
305 // Intentionally fall through
306 case PARTIALLY_SATISFIABLE:
307 {
308 MIP_Problem& x = const_cast<MIP_Problem&>(*this);
309 if (x.i_variables.empty()) {
310 // LP case.
311 if (x.is_lp_satisfiable()) {
312 x.second_phase();
313 if (x.status == UNBOUNDED) {
314 return UNBOUNDED_MIP_PROBLEM;
315 }
316 else {
317 PPL_ASSERT(x.status == OPTIMIZED);
318 return OPTIMIZED_MIP_PROBLEM;
319 }
320 }
321 return UNFEASIBLE_MIP_PROBLEM;
322 }
323
324 // MIP case.
325 MIP_Problem_Status return_value;
326 Generator g = point();
327 {
328 // Temporarily relax the MIP into an LP problem.
329 RAII_Temporary_Real_Relaxation relaxed(x);
330 if (relaxed.lp.is_lp_satisfiable()) {
331 relaxed.lp.second_phase();
332 }
333 else {
334 x.status = UNSATISFIABLE;
335 // NOTE: `relaxed' destroyed: relaxation automatically reset.
336 return UNFEASIBLE_MIP_PROBLEM;
337 }
338 PPL_DIRTY_TEMP(mpq_class, incumbent_solution);
339 bool have_incumbent_solution = false;
340
341 MIP_Problem lp_copy(relaxed.lp, Inherit_Constraints());
342 PPL_ASSERT(lp_copy.integer_space_dimensions().empty());
343 return_value = solve_mip(have_incumbent_solution,
344 incumbent_solution, g,
345 lp_copy, relaxed.i_vars);
346 } // `relaxed' destroyed here: relaxation automatically reset.
347
348 switch (return_value) {
349 case UNFEASIBLE_MIP_PROBLEM:
350 x.status = UNSATISFIABLE;
351 break;
352 case UNBOUNDED_MIP_PROBLEM:
353 x.status = UNBOUNDED;
354 // A feasible point has been set in `solve_mip()', so that
355 // a call to `feasible_point' will be successful.
356 x.last_generator = g;
357 break;
358 case OPTIMIZED_MIP_PROBLEM:
359 x.status = OPTIMIZED;
360 // Set the internal generator.
361 x.last_generator = g;
362 break;
363 }
364 PPL_ASSERT(OK());
365 return return_value;
366 }
367 }
368 // We should not be here!
369 PPL_UNREACHABLE;
370 return UNFEASIBLE_MIP_PROBLEM;
371 }
372
373 void
add_space_dimensions_and_embed(const dimension_type m)374 PPL::MIP_Problem::add_space_dimensions_and_embed(const dimension_type m) {
375 // The space dimension of the resulting MIP problem should not
376 // overflow the maximum allowed space dimension.
377 if (m > max_space_dimension() - space_dimension()) {
378 throw std::length_error("PPL::MIP_Problem::"
379 "add_space_dimensions_and_embed(m):\n"
380 "adding m new space dimensions exceeds "
381 "the maximum allowed space dimension.");
382 }
383 external_space_dim += m;
384 if (status != UNSATISFIABLE) {
385 status = PARTIALLY_SATISFIABLE;
386 }
387 PPL_ASSERT(OK());
388 }
389
390 void
391 PPL::MIP_Problem
add_to_integer_space_dimensions(const Variables_Set & i_vars)392 ::add_to_integer_space_dimensions(const Variables_Set& i_vars) {
393 if (i_vars.space_dimension() > external_space_dim) {
394 throw std::invalid_argument("PPL::MIP_Problem::"
395 "add_to_integer_space_dimension(i_vars):\n"
396 "*this and i_vars are dimension"
397 "incompatible.");
398 }
399 const dimension_type original_size = i_variables.size();
400 i_variables.insert(i_vars.begin(), i_vars.end());
401 // If a new integral variable was inserted, set the internal status to
402 // PARTIALLY_SATISFIABLE.
403 if (i_variables.size() != original_size && status != UNSATISFIABLE) {
404 status = PARTIALLY_SATISFIABLE;
405 }
406 }
407
408 bool
is_in_base(const dimension_type var_index,dimension_type & row_index) const409 PPL::MIP_Problem::is_in_base(const dimension_type var_index,
410 dimension_type& row_index) const {
411 for (row_index = base.size(); row_index-- > 0; ) {
412 if (base[row_index] == var_index) {
413 return true;
414 }
415 }
416 return false;
417 }
418
419 PPL::dimension_type
merge_split_variable(dimension_type var_index)420 PPL::MIP_Problem::merge_split_variable(dimension_type var_index) {
421 // Initialize the return value to a dummy index.
422 dimension_type unfeasible_tableau_row = not_a_dimension();
423
424 const dimension_type removing_column = mapping[1+var_index].second;
425
426 // Check if the negative part of the split variable is in base:
427 // if so, the corresponding row of the tableau becomes non-feasible.
428 {
429 dimension_type base_index;
430 if (is_in_base(removing_column, base_index)) {
431 // Set the return value.
432 unfeasible_tableau_row = base_index;
433 // Reset base[base_index] to zero to remember non-feasibility.
434 base[base_index] = 0;
435 // Since the negative part of the variable is in base,
436 // the positive part can not be in base too.
437 PPL_ASSERT(!is_in_base(mapping[1+var_index].first, base_index));
438 }
439 }
440
441 tableau.remove_column(removing_column);
442
443 // var_index is no longer split.
444 mapping[1+var_index].second = 0;
445
446 // Adjust data structures, `shifting' the proper columns to the left by 1.
447 const dimension_type base_size = base.size();
448 for (dimension_type i = base_size; i-- > 0; ) {
449 if (base[i] > removing_column) {
450 --base[i];
451 }
452 }
453 const dimension_type mapping_size = mapping.size();
454 for (dimension_type i = mapping_size; i-- > 0; ) {
455 if (mapping[i].first > removing_column) {
456 --mapping[i].first;
457 }
458 if (mapping[i].second > removing_column) {
459 --mapping[i].second;
460 }
461 }
462
463 return unfeasible_tableau_row;
464 }
465
466 bool
is_satisfied(const Constraint & c,const Generator & g)467 PPL::MIP_Problem::is_satisfied(const Constraint& c, const Generator& g) {
468 // Scalar_Products::sign() requires the second argument to be at least
469 // as large as the first one.
470 const int sp_sign
471 = (g.space_dimension() <= c.space_dimension())
472 ? Scalar_Products::sign(g, c)
473 : Scalar_Products::sign(c, g);
474 return c.is_inequality() ? (sp_sign >= 0) : (sp_sign == 0);
475 }
476
477 bool
is_saturated(const Constraint & c,const Generator & g)478 PPL::MIP_Problem::is_saturated(const Constraint& c, const Generator& g) {
479 // Scalar_Products::sign() requires the space dimension of the second
480 // argument to be at least as large as the one of the first one.
481 const int sp_sign
482 = (g.space_dimension() <= c.space_dimension())
483 ? Scalar_Products::sign(g, c)
484 : Scalar_Products::sign(c, g);
485 return sp_sign == 0;
486 }
487
488 bool
489 PPL::MIP_Problem
parse_constraints(dimension_type & additional_tableau_rows,dimension_type & additional_slack_variables,std::deque<bool> & is_tableau_constraint,std::deque<bool> & is_satisfied_inequality,std::deque<bool> & is_nonnegative_variable,std::deque<bool> & is_remergeable_variable) const490 ::parse_constraints(dimension_type& additional_tableau_rows,
491 dimension_type& additional_slack_variables,
492 std::deque<bool>& is_tableau_constraint,
493 std::deque<bool>& is_satisfied_inequality,
494 std::deque<bool>& is_nonnegative_variable,
495 std::deque<bool>& is_remergeable_variable) const {
496 // Initially all containers are empty.
497 PPL_ASSERT(is_tableau_constraint.empty()
498 && is_satisfied_inequality.empty()
499 && is_nonnegative_variable.empty()
500 && is_remergeable_variable.empty());
501
502 const dimension_type cs_space_dim = external_space_dim;
503 const dimension_type cs_num_rows = input_cs.size();
504 const dimension_type cs_num_pending
505 = cs_num_rows - first_pending_constraint;
506
507 // Counters determining the change in dimensions of the tableau:
508 // initialized here, they will be updated while examining `input_cs'.
509 additional_tableau_rows = cs_num_pending;
510 additional_slack_variables = 0;
511
512 // Resize containers appropriately.
513
514 // On exit, `is_tableau_constraint[i]' will be true if and only if
515 // `input_cs[first_pending_constraint + i]' is neither a tautology
516 // (e.g., 1 >= 0) nor a non-negativity constraint (e.g., X >= 0).
517 is_tableau_constraint.insert(is_tableau_constraint.end(),
518 cs_num_pending, true);
519
520 // On exit, `is_satisfied_inequality[i]' will be true if and only if
521 // `input_cs[first_pending_constraint + i]' is an inequality and it is
522 // satisfied by `last_generator'.
523 is_satisfied_inequality.insert(is_satisfied_inequality.end(),
524 cs_num_pending, false);
525
526 // On exit, `is_nonnegative_variable[j]' will be true if and only if
527 // Variable(j) is bound to be nonnegative in `input_cs'.
528 is_nonnegative_variable.insert(is_nonnegative_variable.end(),
529 cs_space_dim, false);
530
531 // On exit, `is_remergeable_variable[j]' will be true if and only if
532 // Variable(j) was initially split and is now remergeable.
533 is_remergeable_variable.insert(is_remergeable_variable.end(),
534 internal_space_dim, false);
535
536 // Check for variables that are already known to be nonnegative
537 // due to non-pending constraints.
538 const dimension_type mapping_size = mapping.size();
539 if (mapping_size > 0) {
540 // Note: mapping[0] is associated to the cost function.
541 for (dimension_type i = std::min(mapping_size - 1, cs_space_dim);
542 i-- > 0; ) {
543 if (mapping[i + 1].second == 0) {
544 is_nonnegative_variable[i] = true;
545 }
546 }
547 }
548
549 // Process each pending constraint in `input_cs' and
550 // - detect variables that are constrained to be nonnegative;
551 // - detect (non-negativity or tautology) pending constraints that
552 // will not be part of the tableau;
553 // - count the number of new slack variables.
554 for (dimension_type i = cs_num_rows; i-- > first_pending_constraint; ) {
555 const Constraint& cs_i = *(input_cs[i]);
556 const dimension_type cs_i_end = cs_i.space_dimension() + 1;
557
558 const dimension_type nonzero_coeff_column_index
559 = cs_i.expression().first_nonzero(1, cs_i_end);
560 const bool found_a_nonzero_coeff = (nonzero_coeff_column_index != cs_i_end);
561 const bool found_many_nonzero_coeffs
562 = (found_a_nonzero_coeff
563 && !cs_i.expression().all_zeroes(nonzero_coeff_column_index + 1,
564 cs_i_end));
565
566 // If more than one coefficient is nonzero,
567 // continue with next constraint.
568 if (found_many_nonzero_coeffs) {
569 if (cs_i.is_inequality()) {
570 ++additional_slack_variables;
571 // CHECKME: Is it true that in the first phase we can apply
572 // `is_satisfied()' with the generator `point()'? If so, the following
573 // code works even if we do not have a feasible point.
574 // Check for satisfiability of the inequality. This can be done if we
575 // have a feasible point of *this.
576 }
577 if (cs_i.is_inequality() && is_satisfied(cs_i, last_generator)) {
578 is_satisfied_inequality[i - first_pending_constraint] = true;
579 }
580 continue;
581 }
582
583 if (!found_a_nonzero_coeff) {
584 // All coefficients are 0.
585 // The constraint is either trivially true or trivially false.
586 if (cs_i.is_inequality()) {
587 if (cs_i.inhomogeneous_term() < 0) {
588 // A constraint such as -1 >= 0 is trivially false.
589 return false;
590 }
591 }
592 else {
593 // The constraint is an equality.
594 if (cs_i.inhomogeneous_term() != 0) {
595 // A constraint such as 1 == 0 is trivially false.
596 return false;
597 }
598 }
599 // Here the constraint is trivially true.
600 is_tableau_constraint[i - first_pending_constraint] = false;
601 --additional_tableau_rows;
602 continue;
603 }
604 else {
605 // Here we have only one nonzero coefficient.
606 /*
607
608 We have the following methods:
609 A) Do split the variable and do add the constraint
610 in the tableau.
611 B) Do not split the variable and do add the constraint
612 in the tableau.
613 C) Do not split the variable and do not add the constraint
614 in the tableau.
615
616 Let the constraint be (a*v + b relsym 0).
617 These are the 12 possible combinations we can have:
618 a | b | relsym | method
619 ----------------------------------
620 1) >0 | >0 | >= | A
621 2) >0 | >0 | == | A
622 3) <0 | <0 | >= | A
623 4) >0 | =0 | == | B
624 5) >0 | <0 | == | B
625 Note: <0 | >0 | == | impossible by strong normalization
626 Note: <0 | =0 | == | impossible by strong normalization
627 Note: <0 | <0 | == | impossible by strong normalization
628 6) >0 | <0 | >= | B
629 7) >0 | =0 | >= | C
630 8) <0 | >0 | >= | A
631 9) <0 | =0 | >= | A
632
633 The next lines will apply the correct method to each case.
634 */
635
636 // The variable index is not equal to the column index.
637 const dimension_type nonzero_var_index = nonzero_coeff_column_index - 1;
638
639 const int sgn_a = sgn(cs_i.coefficient(Variable(nonzero_var_index)));
640 const int sgn_b = sgn(cs_i.inhomogeneous_term());
641
642 // Cases 1-3: apply method A.
643 if (sgn_a == sgn_b) {
644 if (cs_i.is_inequality()) {
645 ++additional_slack_variables;
646 }
647 }
648 // Cases 4-5: apply method B.
649 else if (cs_i.is_equality()) {
650 is_nonnegative_variable[nonzero_var_index] = true;
651 // Case 6: apply method B.
652 }
653 else if (sgn_b < 0) {
654 is_nonnegative_variable[nonzero_var_index] = true;
655 ++additional_slack_variables;
656 }
657 // Case 7: apply method C.
658 else if (sgn_a > 0) {
659 if (!is_nonnegative_variable[nonzero_var_index]) {
660 is_nonnegative_variable[nonzero_var_index] = true;
661 if (nonzero_coeff_column_index < mapping_size) {
662 // Remember to merge back the positive and negative parts.
663 PPL_ASSERT(nonzero_var_index < internal_space_dim);
664 is_remergeable_variable[nonzero_var_index] = true;
665 }
666 }
667 is_tableau_constraint[i - first_pending_constraint] = false;
668 --additional_tableau_rows;
669 }
670 // Cases 8-9: apply method A.
671 else {
672 PPL_ASSERT(cs_i.is_inequality());
673 ++additional_slack_variables;
674 }
675 }
676 }
677 return true;
678 }
679
680 void
process_pending_constraints()681 PPL::MIP_Problem::process_pending_constraints() {
682 // Check the pending constraints to adjust the data structures.
683 // If `false' is returned, they are trivially unfeasible.
684 dimension_type additional_tableau_rows = 0;
685 dimension_type additional_slack_vars = 0;
686 std::deque<bool> is_tableau_constraint;
687 std::deque<bool> is_satisfied_inequality;
688 std::deque<bool> is_nonnegative_variable;
689 std::deque<bool> is_remergeable_variable;
690 if (!parse_constraints(additional_tableau_rows,
691 additional_slack_vars,
692 is_tableau_constraint,
693 is_satisfied_inequality,
694 is_nonnegative_variable,
695 is_remergeable_variable)) {
696 status = UNSATISFIABLE;
697 return;
698 }
699
700 // Merge back any variable that was previously split into a positive
701 // and a negative part and is now known to be nonnegative.
702 std::vector<dimension_type> unfeasible_tableau_rows;
703 for (dimension_type i = internal_space_dim; i-- > 0; ) {
704 if (!is_remergeable_variable[i]) {
705 continue;
706 }
707 // TODO: merging all rows in a single shot may be more efficient
708 // as it would require a single call to permute_columns().
709 const dimension_type unfeasible_row = merge_split_variable(i);
710 if (unfeasible_row != not_a_dimension()) {
711 unfeasible_tableau_rows.push_back(unfeasible_row);
712 }
713 }
714
715 const dimension_type old_tableau_num_rows = tableau.num_rows();
716 const dimension_type old_tableau_num_cols = tableau.num_columns();
717 const dimension_type first_free_tableau_index = old_tableau_num_cols - 1;
718
719 // Update mapping for the new problem variables (if any).
720 dimension_type additional_problem_vars = 0;
721 if (external_space_dim > internal_space_dim) {
722 const dimension_type space_diff = external_space_dim - internal_space_dim;
723 for (dimension_type i = 0, j = 0; i < space_diff; ++i) {
724 // Let `mapping' associate the variable index with the corresponding
725 // tableau column: split the variable into positive and negative
726 // parts if it is not known to be nonnegative.
727 const dimension_type positive = first_free_tableau_index + j;
728 if (is_nonnegative_variable[internal_space_dim + i]) {
729 // Do not split.
730 mapping.push_back(std::make_pair(positive, 0));
731 ++j;
732 ++additional_problem_vars;
733 }
734 else {
735 // Split: negative index is positive + 1.
736 mapping.push_back(std::make_pair(positive, positive + 1));
737 j += 2;
738 additional_problem_vars += 2;
739 }
740 }
741 }
742
743 // Resize the tableau: first add additional rows ...
744 if (additional_tableau_rows > 0) {
745 tableau.add_zero_rows(additional_tableau_rows);
746 }
747
748 // ... then add additional columns.
749 // We need columns for additional (split) problem variables, additional
750 // slack variables and additional artificials.
751 // The number of artificials to be added is computed as:
752 // * number of pending constraints entering the tableau
753 // minus
754 // * pending constraints that are inequalities and are already satisfied
755 // by `last_generator'
756 // plus
757 // * number of non-pending constraints that are no longer satisfied
758 // due to re-merging of split variables.
759
760 const dimension_type num_satisfied_inequalities
761 = static_cast<dimension_type>(std::count(is_satisfied_inequality.begin(),
762 is_satisfied_inequality.end(),
763 true));
764 const dimension_type unfeasible_tableau_rows_size
765 = unfeasible_tableau_rows.size();
766
767 PPL_ASSERT(additional_tableau_rows >= num_satisfied_inequalities);
768 const dimension_type additional_artificial_vars
769 = (additional_tableau_rows - num_satisfied_inequalities)
770 + unfeasible_tableau_rows_size;
771
772 const dimension_type additional_tableau_columns
773 = additional_problem_vars
774 + additional_slack_vars
775 + additional_artificial_vars;
776
777 if (additional_tableau_columns > 0) {
778 tableau.add_zero_columns(additional_tableau_columns);
779 }
780
781 // Dimensions of the tableau after resizing.
782 const dimension_type tableau_num_rows = tableau.num_rows();
783 const dimension_type tableau_num_cols = tableau.num_columns();
784
785 // The following vector will be useful know if a constraint is feasible
786 // and does not require an additional artificial variable.
787 std::deque<bool> worked_out_row(tableau_num_rows, false);
788
789 // Sync the `base' vector size to the new tableau: fill with zeros
790 // to encode that these rows are not OK and must be adjusted.
791 base.insert(base.end(), additional_tableau_rows, 0);
792 const dimension_type base_size = base.size();
793
794 // These indexes will be used to insert slack and artificial variables
795 // in the appropriate position.
796 dimension_type slack_index
797 = tableau_num_cols - additional_artificial_vars - 1;
798 dimension_type artificial_index = slack_index;
799
800 // The first column index of the tableau that contains an
801 // artificial variable. Encode with 0 the fact the there are not
802 // artificial variables.
803 const dimension_type begin_artificials
804 = (additional_artificial_vars > 0)
805 ? artificial_index
806 : 0;
807
808 // Proceed with the insertion of the constraints.
809 for (dimension_type k = tableau_num_rows,
810 i = input_cs.size() - first_pending_constraint; i-- > 0; ) {
811 if (!is_tableau_constraint[i]) {
812 continue;
813 }
814 // Copy the original constraint in the tableau.
815 Row& tableau_k = tableau[--k];
816 Row::iterator itr = tableau_k.end();
817
818 const Constraint& c = *(input_cs[i + first_pending_constraint]);
819 const Constraint::expr_type c_e = c.expression();
820 for (Constraint::expr_type::const_iterator j = c_e.begin(),
821 j_end = c_e.end(); j != j_end; ++j) {
822 Coefficient_traits::const_reference coeff_sd = *j;
823 const std::pair<dimension_type, dimension_type> mapped
824 = mapping[j.variable().space_dimension()];
825 itr = tableau_k.insert(itr, mapped.first, coeff_sd);
826 // Split if needed.
827 if (mapped.second != 0) {
828 itr = tableau_k.insert(itr, mapped.second);
829 neg_assign(*itr, coeff_sd);
830 }
831 }
832 Coefficient_traits::const_reference inhomo = c.inhomogeneous_term();
833 if (inhomo != 0) {
834 tableau_k.insert(itr, mapping[0].first, inhomo);
835 // Split if needed.
836 if (mapping[0].second != 0) {
837 itr = tableau_k.insert(itr, mapping[0].second);
838 neg_assign(*itr, inhomo);
839 }
840 }
841
842 // Add the slack variable, if needed.
843 if (c.is_inequality()) {
844 neg_assign(tableau_k[--slack_index], Coefficient_one());
845 // If the constraint is already satisfied, we will not use artificial
846 // variables to compute a feasible base: this to speed up
847 // the algorithm.
848 if (is_satisfied_inequality[i]) {
849 base[k] = slack_index;
850 worked_out_row[k] = true;
851 }
852 }
853 for (dimension_type j = base_size; j-- > 0; ) {
854 if (k != j && base[j] != 0 && tableau_k.get(base[j]) != 0) {
855 linear_combine(tableau_k, tableau[j], base[j]);
856 }
857 }
858 }
859
860 // Let all inhomogeneous terms in the tableau be nonpositive,
861 // so as to simplify the insertion of artificial variables
862 // (the coefficient of each artificial variable will be 1).
863 for (dimension_type i = tableau_num_rows; i-- > 0 ; ) {
864 Row& tableau_i = tableau[i];
865 if (tableau_i.get(0) > 0) {
866 for (Row::iterator
867 j = tableau_i.begin(), j_end = tableau_i.end();
868 j != j_end; ++j) {
869 neg_assign(*j);
870 }
871 }
872 }
873
874 // Reset the working cost function to have the right size.
875 working_cost = working_cost_type(tableau_num_cols);
876
877 // Set up artificial variables: these will have coefficient 1 in the
878 // constraint, will enter the base and will have coefficient -1 in
879 // the cost function.
880
881 // This is used as a hint for insertions in working_cost.
882 working_cost_type::iterator cost_itr = working_cost.end();
883
884 // First go through non-pending constraints that became unfeasible
885 // due to re-merging of split variables.
886 for (dimension_type i = 0; i < unfeasible_tableau_rows_size; ++i) {
887 tableau[unfeasible_tableau_rows[i]].insert(artificial_index,
888 Coefficient_one());
889 cost_itr = working_cost.insert(cost_itr, artificial_index);
890 *cost_itr = -1;
891 base[unfeasible_tableau_rows[i]] = artificial_index;
892 ++artificial_index;
893 }
894 // Then go through newly added tableau rows, disregarding inequalities
895 // that are already satisfied by `last_generator' (this information
896 // is encoded in `worked_out_row').
897 for (dimension_type i = old_tableau_num_rows; i < tableau_num_rows; ++i) {
898 if (worked_out_row[i]) {
899 continue;
900 }
901 tableau[i].insert(artificial_index, Coefficient_one());
902 cost_itr = working_cost.insert(cost_itr, artificial_index);
903 *cost_itr = -1;
904 base[i] = artificial_index;
905 ++artificial_index;
906 }
907 // One past the last tableau column index containing an artificial variable.
908 const dimension_type end_artificials = artificial_index;
909
910 // Set the extra-coefficient of the cost functions to record its sign.
911 // This is done to keep track of the possible sign's inversion.
912 const dimension_type last_obj_index = working_cost.size() - 1;
913 working_cost.insert(cost_itr, last_obj_index, Coefficient_one());
914
915 // Express the problem in terms of the variables in base.
916 {
917 working_cost_type::const_iterator itr = working_cost.end();
918 for (dimension_type i = tableau_num_rows; i-- > 0; ) {
919 itr = working_cost.lower_bound(itr, base[i]);
920 if (itr != working_cost.end() && itr.index() == base[i] && *itr != 0) {
921 linear_combine(working_cost, tableau[i], base[i]);
922 // itr has been invalidated by the call to linear_combine().
923 itr = working_cost.end();
924 }
925 }
926 }
927
928 // Deal with zero dimensional problems.
929 if (space_dimension() == 0) {
930 status = OPTIMIZED;
931 last_generator = point();
932 return;
933 }
934 // Deal with trivial cases.
935 // If there is no constraint in the tableau, then the feasible region
936 // is only delimited by non-negativity constraints. Therefore,
937 // the problem is unbounded as soon as the cost function has
938 // a variable with a positive coefficient.
939 if (tableau_num_rows == 0) {
940 if (is_unbounded_obj_function(input_obj_function, mapping, opt_mode)) {
941 // Ensure the right space dimension is obtained.
942 last_generator = point();
943 last_generator.set_space_dimension(space_dimension());
944 status = UNBOUNDED;
945 return;
946 }
947
948 // The problem is neither trivially unfeasible nor trivially unbounded.
949 // The tableau was successful computed and the caller has to figure
950 // out which case applies.
951 status = OPTIMIZED;
952 // Ensure the right space dimension is obtained.
953 last_generator = point();
954 last_generator.set_space_dimension(space_dimension());
955 return;
956 }
957
958 // Now we are ready to solve the first phase.
959 const bool first_phase_successful
960 = (get_control_parameter(PRICING) == PRICING_STEEPEST_EDGE_FLOAT)
961 ? compute_simplex_using_steepest_edge_float()
962 : compute_simplex_using_exact_pricing();
963
964 #if PPL_NOISY_SIMPLEX
965 std::cout << "MIP_Problem::process_pending_constraints(): "
966 << "1st phase ended at iteration " << num_iterations
967 << "." << std::endl;
968 #endif // PPL_NOISY_SIMPLEX
969
970 if (!first_phase_successful || working_cost.get(0) != 0) {
971 // The feasible region is empty.
972 status = UNSATISFIABLE;
973 return;
974 }
975
976 // Prepare *this for a possible second phase.
977 if (begin_artificials != 0) {
978 erase_artificials(begin_artificials, end_artificials);
979 }
980 compute_generator();
981 status = SATISFIABLE;
982 }
983
984 namespace {
985
986 // NOTE: the following two `assign' helper functions are needed to
987 // handle the assignment of a Coefficient to a double in method
988 // MIP_Problem::steepest_edge_float_entering_index().
989 // We cannot use assign_r(double, Coefficient, Rounding_Dir) as it would
990 // lead to a compilation error on those platforms (e.g., ARM) where
991 // controlled floating point rounding is not available (even if the
992 // rounding mode would be set to ROUND_IGNORE).
993
994 inline void
assign(double & d,const mpz_class & c)995 assign(double& d, const mpz_class& c) {
996 d = c.get_d();
997 }
998
999 template <typename T, typename Policy>
1000 inline void
assign(double & d,const Parma_Polyhedra_Library::Checked_Number<T,Policy> & c)1001 assign(double& d,
1002 const Parma_Polyhedra_Library::Checked_Number<T, Policy>& c) {
1003 d = raw_value(c);
1004 }
1005
1006 } // namespace
1007
1008 PPL::dimension_type
steepest_edge_float_entering_index() const1009 PPL::MIP_Problem::steepest_edge_float_entering_index() const {
1010 const dimension_type tableau_num_rows = tableau.num_rows();
1011 const dimension_type tableau_num_columns = tableau.num_columns();
1012 PPL_ASSERT(tableau_num_rows == base.size());
1013 double current_value = 0.0;
1014 // Due to our integer implementation, the `1' term in the denominator
1015 // of the original formula has to be replaced by `squared_lcm_basis'.
1016 double float_tableau_value;
1017 double float_tableau_denom;
1018 dimension_type entering_index = 0;
1019 const int cost_sign = sgn(working_cost.get(working_cost.size() - 1));
1020
1021 // These two implementation work for both sparse and dense matrices.
1022 // However, when using sparse matrices the first one is fast and the second
1023 // one is slow, and when using dense matrices the first one is slow and
1024 // the second one is fast.
1025 #if PPL_USE_SPARSE_MATRIX
1026
1027 const dimension_type tableau_num_columns_minus_1 = tableau_num_columns - 1;
1028 // This is static to improve performance.
1029 // A vector of <column_index, challenger_denom> pairs, ordered by
1030 // column_index.
1031 static std::vector<std::pair<dimension_type, double> > columns;
1032 columns.clear();
1033 // (working_cost.size() - 2) is an upper bound only.
1034 columns.reserve(working_cost.size() - 2);
1035 {
1036 working_cost_type::const_iterator i = working_cost.lower_bound(1);
1037 // Note that find() is used instead of lower_bound().
1038 working_cost_type::const_iterator i_end
1039 = working_cost.find(tableau_num_columns_minus_1);
1040 for ( ; i != i_end; ++i) {
1041 if (sgn(*i) == cost_sign) {
1042 columns.push_back(std::make_pair(i.index(), 1.0));
1043 }
1044 }
1045 }
1046 for (dimension_type i = tableau_num_rows; i-- > 0; ) {
1047 const Row& tableau_i = tableau[i];
1048 assign(float_tableau_denom, tableau_i.get(base[i]));
1049 Row::const_iterator j = tableau_i.begin();
1050 Row::const_iterator j_end = tableau_i.end();
1051 std::vector<std::pair<dimension_type, double> >::iterator k
1052 = columns.begin();
1053 std::vector<std::pair<dimension_type, double> >::iterator k_end
1054 = columns.end();
1055 while (j != j_end && k != k_end) {
1056 const dimension_type column = j.index();
1057 while (k != k_end && column > k->first) {
1058 ++k;
1059 }
1060 if (k == k_end) {
1061 break;
1062 }
1063 if (k->first > column) {
1064 j = tableau_i.lower_bound(j, k->first);
1065 }
1066 else {
1067 PPL_ASSERT(k->first == column);
1068 PPL_ASSERT(tableau_i.get(base[i]) != 0);
1069 WEIGHT_BEGIN();
1070 assign(float_tableau_value, *j);
1071 float_tableau_value /= float_tableau_denom;
1072 float_tableau_value *= float_tableau_value;
1073 k->second += float_tableau_value;
1074 WEIGHT_ADD(22);
1075 ++j;
1076 ++k;
1077 }
1078 }
1079 }
1080 // The candidates are processed backwards to get the same result in both
1081 // this implementation and the dense implementation below.
1082 for (std::vector<std::pair<dimension_type, double> >::const_reverse_iterator
1083 i = columns.rbegin(), i_end = columns.rend(); i != i_end; ++i) {
1084 const double challenger_value = sqrt(i->second);
1085 if (entering_index == 0 || challenger_value > current_value) {
1086 current_value = challenger_value;
1087 entering_index = i->first;
1088 }
1089 }
1090
1091 #else // !PPL_USE_SPARSE_MATRIX
1092
1093 double challenger_numer = 0.0;
1094 double challenger_denom = 0.0;
1095 for (dimension_type j = tableau_num_columns - 1; j-- > 1; ) {
1096 Coefficient_traits::const_reference cost_j = working_cost.get(j);
1097 if (sgn(cost_j) == cost_sign) {
1098 WEIGHT_BEGIN();
1099 // We cannot compute the (exact) square root of abs(\Delta x_j).
1100 // The workaround is to compute the square of `cost[j]'.
1101 assign(challenger_numer, cost_j);
1102 challenger_numer = std::abs(challenger_numer);
1103 // Due to our integer implementation, the `1' term in the denominator
1104 // of the original formula has to be replaced by `squared_lcm_basis'.
1105 challenger_denom = 1.0;
1106 for (dimension_type i = tableau_num_rows; i-- > 0; ) {
1107 const Row& tableau_i = tableau[i];
1108 Coefficient_traits::const_reference tableau_ij = tableau_i[j];
1109 if (tableau_ij != 0) {
1110 PPL_ASSERT(tableau_i[base[i]] != 0);
1111 assign(float_tableau_value, tableau_ij);
1112 assign(float_tableau_denom, tableau_i[base[i]]);
1113 float_tableau_value /= float_tableau_denom;
1114 challenger_denom += float_tableau_value * float_tableau_value;
1115 }
1116 }
1117 double challenger_value = sqrt(challenger_denom);
1118 // Initialize `current_value' during the first iteration.
1119 // Otherwise update if the challenger wins.
1120 if (entering_index == 0 || challenger_value > current_value) {
1121 current_value = challenger_value;
1122 entering_index = j;
1123 }
1124 WEIGHT_ADD_MUL(10, tableau_num_rows);
1125 }
1126 }
1127
1128 #endif // !PPL_USE_SPARSE_MATRIX
1129
1130 return entering_index;
1131 }
1132
1133 PPL::dimension_type
steepest_edge_exact_entering_index() const1134 PPL::MIP_Problem::steepest_edge_exact_entering_index() const {
1135 using std::swap;
1136 const dimension_type tableau_num_rows = tableau.num_rows();
1137 PPL_ASSERT(tableau_num_rows == base.size());
1138 // The square of the lcm of all the coefficients of variables in base.
1139 PPL_DIRTY_TEMP_COEFFICIENT(squared_lcm_basis);
1140 // The normalization factor for each coefficient in the tableau.
1141 std::vector<Coefficient> norm_factor(tableau_num_rows);
1142 {
1143 WEIGHT_BEGIN();
1144 // Compute the lcm of all the coefficients of variables in base.
1145 PPL_DIRTY_TEMP_COEFFICIENT(lcm_basis);
1146 lcm_basis = 1;
1147 for (dimension_type i = tableau_num_rows; i-- > 0; ) {
1148 lcm_assign(lcm_basis, lcm_basis, tableau[i].get(base[i]));
1149 }
1150 // Compute normalization factors.
1151 for (dimension_type i = tableau_num_rows; i-- > 0; ) {
1152 exact_div_assign(norm_factor[i], lcm_basis, tableau[i].get(base[i]));
1153 }
1154 // Compute the square of `lcm_basis', exploiting the fact that
1155 // `lcm_basis' will no longer be needed.
1156 lcm_basis *= lcm_basis;
1157 swap(squared_lcm_basis, lcm_basis);
1158 WEIGHT_ADD_MUL(444, tableau_num_rows);
1159 }
1160
1161 PPL_DIRTY_TEMP_COEFFICIENT(challenger_numer);
1162 PPL_DIRTY_TEMP_COEFFICIENT(scalar_value);
1163 PPL_DIRTY_TEMP_COEFFICIENT(challenger_value);
1164 PPL_DIRTY_TEMP_COEFFICIENT(current_value);
1165
1166 PPL_DIRTY_TEMP_COEFFICIENT(current_numer);
1167 PPL_DIRTY_TEMP_COEFFICIENT(current_denom);
1168 dimension_type entering_index = 0;
1169 const int cost_sign = sgn(working_cost.get(working_cost.size() - 1));
1170
1171 // These two implementation work for both sparse and dense matrices.
1172 // However, when using sparse matrices the first one is fast and the second
1173 // one is slow, and when using dense matrices the first one is slow and
1174 // the second one is fast.
1175 #if PPL_USE_SPARSE_MATRIX
1176
1177 const dimension_type tableau_num_columns = tableau.num_columns();
1178 const dimension_type tableau_num_columns_minus_1 = tableau_num_columns - 1;
1179 // This is static to improve performance.
1180 // A pair (i, x) means that sgn(working_cost[i]) == cost_sign and x
1181 // is the denominator of the challenger, for the column i.
1182 static std::vector<std::pair<dimension_type, Coefficient> > columns;
1183 columns.clear();
1184 // tableau_num_columns - 2 is only an upper bound on the required elements.
1185 // This helps to reduce the number of calls to new [] and delete [] and
1186 // the construction/destruction of Coefficient objects.
1187 columns.reserve(tableau_num_columns - 2);
1188 {
1189 working_cost_type::const_iterator i = working_cost.lower_bound(1);
1190 // Note that find() is used instead of lower_bound.
1191 working_cost_type::const_iterator i_end
1192 = working_cost.find(tableau_num_columns_minus_1);
1193 for ( ; i != i_end; ++i) {
1194 if (sgn(*i) == cost_sign) {
1195 columns.push_back(std::pair<dimension_type, Coefficient>
1196 (i.index(), squared_lcm_basis));
1197 }
1198 }
1199 }
1200 for (dimension_type i = tableau_num_rows; i-- > 0; ) {
1201 const Row& tableau_i = tableau[i];
1202 Row::const_iterator j = tableau_i.begin();
1203 Row::const_iterator j_end = tableau_i.end();
1204 std::vector<std::pair<dimension_type, Coefficient> >::iterator
1205 k = columns.begin();
1206 std::vector<std::pair<dimension_type, Coefficient> >::iterator
1207 k_end = columns.end();
1208 while (j != j_end) {
1209 while (k != k_end && j.index() > k->first) {
1210 ++k;
1211 }
1212 if (k == k_end) {
1213 break;
1214 }
1215 PPL_ASSERT(j.index() <= k->first);
1216 if (j.index() < k->first) {
1217 j = tableau_i.lower_bound(j, k->first);
1218 }
1219 else {
1220 Coefficient_traits::const_reference tableau_ij = *j;
1221 WEIGHT_BEGIN();
1222 #if PPL_USE_SPARSE_MATRIX
1223 scalar_value = tableau_ij * norm_factor[i];
1224 add_mul_assign(k->second, scalar_value, scalar_value);
1225 #else
1226 // The test against 0 gives rise to a consistent speed up in the dense
1227 // implementation: see
1228 // http://www.cs.unipr.it/pipermail/ppl-devel/2009-February/
1229 // 014000.html
1230 if (tableau_ij != 0) {
1231 scalar_value = tableau_ij * norm_factor[i];
1232 add_mul_assign(k->second, scalar_value, scalar_value);
1233 }
1234 #endif
1235 WEIGHT_ADD_MUL(47, tableau_num_rows);
1236 ++k;
1237 ++j;
1238 }
1239 }
1240 }
1241 working_cost_type::const_iterator itr = working_cost.end();
1242 for (std::vector<std::pair<dimension_type, Coefficient> >::reverse_iterator
1243 k = columns.rbegin(), k_end = columns.rend(); k != k_end; ++k) {
1244 itr = working_cost.lower_bound(itr, k->first);
1245 if (itr != working_cost.end() && itr.index() == k->first) {
1246 // We cannot compute the (exact) square root of abs(\Delta x_j).
1247 // The workaround is to compute the square of `cost[j]'.
1248 challenger_numer = (*itr) * (*itr);
1249 // Initialization during the first loop.
1250 if (entering_index == 0) {
1251 swap(current_numer, challenger_numer);
1252 swap(current_denom, k->second);
1253 entering_index = k->first;
1254 continue;
1255 }
1256 challenger_value = challenger_numer * current_denom;
1257 current_value = current_numer * k->second;
1258 // Update the values, if the challenger wins.
1259 if (challenger_value > current_value) {
1260 swap(current_numer, challenger_numer);
1261 swap(current_denom, k->second);
1262 entering_index = k->first;
1263 }
1264 }
1265 else {
1266 PPL_ASSERT(working_cost.get(k->first) == 0);
1267 // Initialization during the first loop.
1268 if (entering_index == 0) {
1269 current_numer = 0;
1270 swap(current_denom, k->second);
1271 entering_index = k->first;
1272 continue;
1273 }
1274 // Update the values, if the challenger wins.
1275 if (0 > sgn(current_numer) * sgn(k->second)) {
1276 current_numer = 0;
1277 swap(current_denom, k->second);
1278 entering_index = k->first;
1279 }
1280 }
1281 }
1282
1283 #else // !PPL_USE_SPARSE_MATRIX
1284
1285 PPL_DIRTY_TEMP_COEFFICIENT(challenger_denom);
1286 for (dimension_type j = tableau.num_columns() - 1; j-- > 1; ) {
1287 Coefficient_traits::const_reference cost_j = working_cost[j];
1288 if (sgn(cost_j) == cost_sign) {
1289 WEIGHT_BEGIN();
1290 // We cannot compute the (exact) square root of abs(\Delta x_j).
1291 // The workaround is to compute the square of `cost[j]'.
1292 challenger_numer = cost_j * cost_j;
1293 // Due to our integer implementation, the `1' term in the denominator
1294 // of the original formula has to be replaced by `squared_lcm_basis'.
1295 challenger_denom = squared_lcm_basis;
1296 for (dimension_type i = tableau_num_rows; i-- > 0; ) {
1297 Coefficient_traits::const_reference tableau_ij = tableau[i][j];
1298 // The test against 0 gives rise to a consistent speed up: see
1299 // http://www.cs.unipr.it/pipermail/ppl-devel/2009-February/
1300 // 014000.html
1301 if (tableau_ij != 0) {
1302 scalar_value = tableau_ij * norm_factor[i];
1303 add_mul_assign(challenger_denom, scalar_value, scalar_value);
1304 }
1305 }
1306 // Initialization during the first loop.
1307 if (entering_index == 0) {
1308 swap(current_numer, challenger_numer);
1309 swap(current_denom, challenger_denom);
1310 entering_index = j;
1311 continue;
1312 }
1313 challenger_value = challenger_numer * current_denom;
1314 current_value = current_numer * challenger_denom;
1315 // Update the values, if the challenger wins.
1316 if (challenger_value > current_value) {
1317 swap(current_numer, challenger_numer);
1318 swap(current_denom, challenger_denom);
1319 entering_index = j;
1320 }
1321 WEIGHT_ADD_MUL(47, tableau_num_rows);
1322 }
1323 }
1324
1325 #endif // !PPL_USE_SPARSE_MATRIX
1326
1327 return entering_index;
1328 }
1329
1330
1331 // See page 47 of [PapadimitriouS98].
1332 PPL::dimension_type
textbook_entering_index() const1333 PPL::MIP_Problem::textbook_entering_index() const {
1334 // The variable entering the base is the first one whose coefficient
1335 // in the cost function has the same sign the cost function itself.
1336 // If no such variable exists, then we met the optimality condition
1337 // (and return 0 to the caller).
1338
1339 // Get the "sign" of the cost function.
1340 const dimension_type cost_sign_index = working_cost.size() - 1;
1341 const int cost_sign = sgn(working_cost.get(cost_sign_index));
1342 PPL_ASSERT(cost_sign != 0);
1343
1344 working_cost_type::const_iterator i = working_cost.lower_bound(1);
1345 // Note that find() is used instead of lower_bound() because they are
1346 // equivalent when searching the last element in the row.
1347 working_cost_type::const_iterator i_end
1348 = working_cost.find(cost_sign_index);
1349 for ( ; i != i_end; ++i) {
1350 if (sgn(*i) == cost_sign) {
1351 return i.index();
1352 }
1353 }
1354 // No variable has to enter the base:
1355 // the cost function was optimized.
1356 return 0;
1357 }
1358
1359 void
linear_combine(Row & x,const Row & y,const dimension_type k)1360 PPL::MIP_Problem::linear_combine(Row& x, const Row& y,
1361 const dimension_type k) {
1362 PPL_ASSERT(x.size() == y.size());
1363 WEIGHT_BEGIN();
1364 const dimension_type x_size = x.size();
1365 Coefficient_traits::const_reference x_k = x.get(k);
1366 Coefficient_traits::const_reference y_k = y.get(k);
1367 PPL_ASSERT(y_k != 0 && x_k != 0);
1368 // Let g be the GCD between `x[k]' and `y[k]'.
1369 // For each i the following computes
1370 // x[i] = x[i]*y[k]/g - y[i]*x[k]/g.
1371 PPL_DIRTY_TEMP_COEFFICIENT(normalized_x_k);
1372 PPL_DIRTY_TEMP_COEFFICIENT(normalized_y_k);
1373 normalize2(x_k, y_k, normalized_x_k, normalized_y_k);
1374
1375 neg_assign(normalized_y_k);
1376 x.linear_combine(y, normalized_y_k, normalized_x_k);
1377
1378 PPL_ASSERT(x.get(k) == 0);
1379
1380 #if PPL_USE_SPARSE_MATRIX
1381 PPL_ASSERT(x.find(k) == x.end());
1382 #endif
1383
1384 x.normalize();
1385 WEIGHT_ADD_MUL(31, x_size);
1386 }
1387
1388 // TODO: Remove this when the sparse working cost has been tested enough.
1389 #if PPL_USE_SPARSE_MATRIX
1390
1391 void
linear_combine(Dense_Row & x,const Sparse_Row & y,const dimension_type k)1392 PPL::MIP_Problem::linear_combine(Dense_Row& x,
1393 const Sparse_Row& y,
1394 const dimension_type k) {
1395 PPL_ASSERT(x.size() == y.size());
1396 WEIGHT_BEGIN();
1397 const dimension_type x_size = x.size();
1398 Coefficient_traits::const_reference x_k = x.get(k);
1399 Coefficient_traits::const_reference y_k = y.get(k);
1400 PPL_ASSERT(y_k != 0 && x_k != 0);
1401 // Let g be the GCD between `x[k]' and `y[k]'.
1402 // For each i the following computes
1403 // x[i] = x[i]*y[k]/g - y[i]*x[k]/g.
1404 PPL_DIRTY_TEMP_COEFFICIENT(normalized_x_k);
1405 PPL_DIRTY_TEMP_COEFFICIENT(normalized_y_k);
1406 normalize2(x_k, y_k, normalized_x_k, normalized_y_k);
1407
1408 neg_assign(normalized_y_k);
1409 Parma_Polyhedra_Library::linear_combine(x, y, normalized_y_k, normalized_x_k);
1410
1411 PPL_ASSERT(x[k] == 0);
1412
1413 x.normalize();
1414 WEIGHT_ADD_MUL(83, x_size);
1415 }
1416
1417 #endif // defined(PPL_USE_SPARSE_MATRIX)
1418
1419 bool
is_unbounded_obj_function(const Linear_Expression & x,const std::vector<std::pair<dimension_type,dimension_type>> & mapping,Optimization_Mode optimization_mode)1420 PPL::MIP_Problem::is_unbounded_obj_function(
1421 const Linear_Expression& x,
1422 const std::vector<std::pair<dimension_type, dimension_type> >& mapping,
1423 Optimization_Mode optimization_mode) {
1424
1425 for (Linear_Expression::const_iterator i = x.begin(),
1426 i_end = x.end(); i != i_end; ++i) {
1427 // If a the value of a variable in the objective function is
1428 // different from zero, the final status is unbounded.
1429 // In the first part the variable is constrained to be greater or equal
1430 // than zero.
1431 if (mapping[i.variable().space_dimension()].second != 0) {
1432 return true;
1433 }
1434 if (optimization_mode == MAXIMIZATION) {
1435 if (*i > 0) {
1436 return true;
1437 }
1438 }
1439 else {
1440 PPL_ASSERT(optimization_mode == MINIMIZATION);
1441 if (*i < 0) {
1442 return true;
1443 }
1444 }
1445 }
1446 return false;
1447 }
1448
1449 // See pages 42-43 of [PapadimitriouS98].
1450 void
pivot(const dimension_type entering_var_index,const dimension_type exiting_base_index)1451 PPL::MIP_Problem::pivot(const dimension_type entering_var_index,
1452 const dimension_type exiting_base_index) {
1453 const Row& tableau_out = tableau[exiting_base_index];
1454 // Linearly combine the constraints.
1455 for (dimension_type i = tableau.num_rows(); i-- > 0; ) {
1456 Row& tableau_i = tableau[i];
1457 if (i != exiting_base_index && tableau_i.get(entering_var_index) != 0) {
1458 linear_combine(tableau_i, tableau_out, entering_var_index);
1459 }
1460 }
1461 // Linearly combine the cost function.
1462 if (working_cost.get(entering_var_index) != 0) {
1463 linear_combine(working_cost, tableau_out, entering_var_index);
1464 }
1465 // Adjust the base.
1466 base[exiting_base_index] = entering_var_index;
1467 }
1468
1469 // See pages 47 and 50 of [PapadimitriouS98].
1470 PPL::dimension_type
1471 PPL::MIP_Problem
get_exiting_base_index(const dimension_type entering_var_index) const1472 ::get_exiting_base_index(const dimension_type entering_var_index) const {
1473 // The variable exiting the base should be associated to a tableau
1474 // constraint such that the ratio
1475 // tableau[i][entering_var_index] / tableau[i][base[i]]
1476 // is strictly positive and minimal.
1477
1478 // Find the first tableau constraint `c' having a positive value for
1479 // tableau[i][entering_var_index] / tableau[i][base[i]]
1480 const dimension_type tableau_num_rows = tableau.num_rows();
1481 dimension_type exiting_base_index = tableau_num_rows;
1482 for (dimension_type i = 0; i < tableau_num_rows; ++i) {
1483 const Row& t_i = tableau[i];
1484 const int num_sign = sgn(t_i.get(entering_var_index));
1485 if (num_sign != 0 && num_sign == sgn(t_i.get(base[i]))) {
1486 exiting_base_index = i;
1487 break;
1488 }
1489 }
1490 // Check for unboundedness.
1491 if (exiting_base_index == tableau_num_rows) {
1492 return tableau_num_rows;
1493 }
1494
1495 // Reaching this point means that a variable will definitely exit the base.
1496 PPL_DIRTY_TEMP_COEFFICIENT(lcm);
1497 PPL_DIRTY_TEMP_COEFFICIENT(current_min);
1498 PPL_DIRTY_TEMP_COEFFICIENT(challenger);
1499 Coefficient t_e0 = tableau[exiting_base_index].get(0);
1500 Coefficient t_ee = tableau[exiting_base_index].get(entering_var_index);
1501 for (dimension_type i = exiting_base_index + 1; i < tableau_num_rows; ++i) {
1502 const Row& t_i = tableau[i];
1503 Coefficient_traits::const_reference t_ie = t_i.get(entering_var_index);
1504 const int t_ie_sign = sgn(t_ie);
1505 if (t_ie_sign != 0 && t_ie_sign == sgn(t_i.get(base[i]))) {
1506 WEIGHT_BEGIN();
1507 Coefficient_traits::const_reference t_i0 = t_i.get(0);
1508 lcm_assign(lcm, t_ee, t_ie);
1509 exact_div_assign(current_min, lcm, t_ee);
1510 current_min *= t_e0;
1511 abs_assign(current_min);
1512 exact_div_assign(challenger, lcm, t_ie);
1513 challenger *= t_i0;
1514 abs_assign(challenger);
1515 current_min -= challenger;
1516 const int sign = sgn(current_min);
1517 if (sign > 0
1518 || (sign == 0 && base[i] < base[exiting_base_index])) {
1519 exiting_base_index = i;
1520 t_e0 = t_i0;
1521 t_ee = t_ie;
1522 }
1523 WEIGHT_ADD(642);
1524 }
1525 }
1526 return exiting_base_index;
1527 }
1528
1529 // See page 49 of [PapadimitriouS98].
1530 bool
compute_simplex_using_steepest_edge_float()1531 PPL::MIP_Problem::compute_simplex_using_steepest_edge_float() {
1532 // We may need to temporarily switch to the textbook pricing.
1533 const unsigned long allowed_non_increasing_loops = 200;
1534 unsigned long non_increased_times = 0;
1535 bool textbook_pricing = false;
1536
1537 PPL_DIRTY_TEMP_COEFFICIENT(cost_sgn_coeff);
1538 PPL_DIRTY_TEMP_COEFFICIENT(current_numer);
1539 PPL_DIRTY_TEMP_COEFFICIENT(current_denom);
1540 PPL_DIRTY_TEMP_COEFFICIENT(challenger);
1541 PPL_DIRTY_TEMP_COEFFICIENT(current);
1542
1543 cost_sgn_coeff = working_cost.get(working_cost.size() - 1);
1544 current_numer = working_cost.get(0);
1545 if (cost_sgn_coeff < 0) {
1546 neg_assign(current_numer);
1547 }
1548 abs_assign(current_denom, cost_sgn_coeff);
1549 PPL_ASSERT(tableau.num_columns() == working_cost.size());
1550 const dimension_type tableau_num_rows = tableau.num_rows();
1551
1552 while (true) {
1553 // Choose the index of the variable entering the base, if any.
1554 const dimension_type entering_var_index
1555 = textbook_pricing
1556 ? textbook_entering_index()
1557 : steepest_edge_float_entering_index();
1558
1559 // If no entering index was computed, the problem is solved.
1560 if (entering_var_index == 0) {
1561 return true;
1562 }
1563
1564 // Choose the index of the row exiting the base.
1565 const dimension_type exiting_base_index
1566 = get_exiting_base_index(entering_var_index);
1567 // If no exiting index was computed, the problem is unbounded.
1568 if (exiting_base_index == tableau_num_rows) {
1569 return false;
1570 }
1571
1572 // Check if the client has requested abandoning all expensive
1573 // computations. If so, the exception specified by the client
1574 // is thrown now.
1575 maybe_abandon();
1576
1577 // We have not reached the optimality or unbounded condition:
1578 // compute the new base and the corresponding vertex of the
1579 // feasible region.
1580 pivot(entering_var_index, exiting_base_index);
1581
1582 WEIGHT_BEGIN();
1583 // Now begins the objective function's value check to choose between
1584 // the `textbook' and the float `steepest-edge' technique.
1585 cost_sgn_coeff = working_cost.get(working_cost.size() - 1);
1586
1587 challenger = working_cost.get(0);
1588 if (cost_sgn_coeff < 0) {
1589 neg_assign(challenger);
1590 }
1591 challenger *= current_denom;
1592 abs_assign(current, cost_sgn_coeff);
1593 current *= current_numer;
1594 #if PPL_NOISY_SIMPLEX
1595 ++num_iterations;
1596 if (num_iterations % 200 == 0) {
1597 std::cout << "Primal simplex: iteration " << num_iterations
1598 << "." << std::endl;
1599 }
1600 #endif // PPL_NOISY_SIMPLEX
1601 // If the following condition fails, probably there's a bug.
1602 PPL_ASSERT(challenger >= current);
1603 // If the value of the objective function does not improve,
1604 // keep track of that.
1605 if (challenger == current) {
1606 ++non_increased_times;
1607 // In the following case we will proceed using the `textbook'
1608 // technique, until the objective function is not improved.
1609 if (non_increased_times > allowed_non_increasing_loops) {
1610 textbook_pricing = true;
1611 }
1612 }
1613 // The objective function has an improvement:
1614 // reset `non_increased_times' and `textbook_pricing'.
1615 else {
1616 non_increased_times = 0;
1617 textbook_pricing = false;
1618 }
1619 current_numer = working_cost.get(0);
1620 if (cost_sgn_coeff < 0) {
1621 neg_assign(current_numer);
1622 }
1623 abs_assign(current_denom, cost_sgn_coeff);
1624 WEIGHT_ADD(433);
1625 }
1626 }
1627
1628 bool
compute_simplex_using_exact_pricing()1629 PPL::MIP_Problem::compute_simplex_using_exact_pricing() {
1630 PPL_ASSERT(tableau.num_columns() == working_cost.size());
1631 PPL_ASSERT(get_control_parameter(PRICING) == PRICING_STEEPEST_EDGE_EXACT
1632 || get_control_parameter(PRICING) == PRICING_TEXTBOOK);
1633
1634 const dimension_type tableau_num_rows = tableau.num_rows();
1635 const bool textbook_pricing
1636 = (PRICING_TEXTBOOK == get_control_parameter(PRICING));
1637
1638 while (true) {
1639 // Choose the index of the variable entering the base, if any.
1640 const dimension_type entering_var_index
1641 = textbook_pricing
1642 ? textbook_entering_index()
1643 : steepest_edge_exact_entering_index();
1644 // If no entering index was computed, the problem is solved.
1645 if (entering_var_index == 0) {
1646 return true;
1647 }
1648
1649 // Choose the index of the row exiting the base.
1650 const dimension_type exiting_base_index
1651 = get_exiting_base_index(entering_var_index);
1652 // If no exiting index was computed, the problem is unbounded.
1653 if (exiting_base_index == tableau_num_rows) {
1654 return false;
1655 }
1656
1657 // Check if the client has requested abandoning all expensive
1658 // computations. If so, the exception specified by the client
1659 // is thrown now.
1660 maybe_abandon();
1661
1662 // We have not reached the optimality or unbounded condition:
1663 // compute the new base and the corresponding vertex of the
1664 // feasible region.
1665 pivot(entering_var_index, exiting_base_index);
1666 #if PPL_NOISY_SIMPLEX
1667 ++num_iterations;
1668 if (num_iterations % 200 == 0) {
1669 std::cout << "Primal simplex: iteration " << num_iterations
1670 << "." << std::endl;
1671 }
1672 #endif // PPL_NOISY_SIMPLEX
1673 }
1674 }
1675
1676
1677 // See pages 55-56 of [PapadimitriouS98].
1678 void
erase_artificials(const dimension_type begin_artificials,const dimension_type end_artificials)1679 PPL::MIP_Problem::erase_artificials(const dimension_type begin_artificials,
1680 const dimension_type end_artificials) {
1681 PPL_ASSERT(0 < begin_artificials && begin_artificials < end_artificials);
1682
1683 const dimension_type old_last_column = tableau.num_columns() - 1;
1684 dimension_type tableau_n_rows = tableau.num_rows();
1685 // Step 1: try to remove from the base all the remaining slack variables.
1686 for (dimension_type i = 0; i < tableau_n_rows; ++i) {
1687 if (begin_artificials <= base[i] && base[i] < end_artificials) {
1688 // Search for a non-zero element to enter the base.
1689 Row& tableau_i = tableau[i];
1690 bool redundant = true;
1691 Row::const_iterator j = tableau_i.begin();
1692 Row::const_iterator j_end = tableau_i.end();
1693 // Skip the first element
1694 if (j != j_end && j.index() == 0) {
1695 ++j;
1696 }
1697 for ( ; (j != j_end) && (j.index() < begin_artificials); ++j) {
1698 if (*j != 0) {
1699 pivot(j.index(), i);
1700 redundant = false;
1701 break;
1702 }
1703 }
1704 if (redundant) {
1705 // No original variable entered the base:
1706 // the constraint is redundant and should be deleted.
1707 --tableau_n_rows;
1708 if (i < tableau_n_rows) {
1709 // Replace the redundant row with the last one,
1710 // taking care of adjusting the iteration index.
1711 tableau_i.m_swap(tableau[tableau_n_rows]);
1712 base[i] = base[tableau_n_rows];
1713 --i;
1714 }
1715 tableau.remove_trailing_rows(1);
1716 base.pop_back();
1717 }
1718 }
1719 }
1720 // Step 2: Adjust data structures so as to enter phase 2 of the simplex.
1721
1722 // Resize the tableau.
1723 const dimension_type num_artificials = end_artificials - begin_artificials;
1724 tableau.remove_trailing_columns(num_artificials);
1725
1726 // Zero the last column of the tableau.
1727 const dimension_type new_last_column = tableau.num_columns() - 1;
1728 for (dimension_type i = tableau_n_rows; i-- > 0; ) {
1729 tableau[i].reset(new_last_column);
1730 }
1731
1732 // ... then properly set the element in the (new) last column,
1733 // encoding the kind of optimization; ...
1734 {
1735 // This block is equivalent to
1736 //
1737 // <CODE>
1738 // working_cost[new_last_column] = working_cost.get(old_last_column);
1739 // </CODE>
1740 //
1741 // but it avoids storing zeroes.
1742 Coefficient_traits::const_reference old_cost
1743 = working_cost.get(old_last_column);
1744 if (old_cost == 0) {
1745 working_cost.reset(new_last_column);
1746 }
1747 else {
1748 working_cost.insert(new_last_column, old_cost);
1749 }
1750 }
1751
1752 // ... and finally remove redundant columns.
1753 const dimension_type working_cost_new_size
1754 = working_cost.size() - num_artificials;
1755 working_cost.shrink(working_cost_new_size);
1756 }
1757
1758 // See page 55 of [PapadimitriouS98].
1759 void
compute_generator() const1760 PPL::MIP_Problem::compute_generator() const {
1761 // Early exit for 0-dimensional problems.
1762 if (external_space_dim == 0) {
1763 MIP_Problem& x = const_cast<MIP_Problem&>(*this);
1764 x.last_generator = point();
1765 return;
1766 }
1767
1768 // We will store in numer[] and in denom[] the numerators and
1769 // the denominators of every variable of the original problem.
1770 std::vector<Coefficient> numer(external_space_dim);
1771 std::vector<Coefficient> denom(external_space_dim);
1772 dimension_type row = 0;
1773
1774 PPL_DIRTY_TEMP_COEFFICIENT(lcm);
1775 // Speculatively allocate temporaries out of loop.
1776 PPL_DIRTY_TEMP_COEFFICIENT(split_numer);
1777 PPL_DIRTY_TEMP_COEFFICIENT(split_denom);
1778
1779 // We start to compute numer[] and denom[].
1780 for (dimension_type i = external_space_dim; i-- > 0; ) {
1781 Coefficient& numer_i = numer[i];
1782 Coefficient& denom_i = denom[i];
1783 // Get the value of the variable from the tableau
1784 // (if it is not a basic variable, the value is 0).
1785 const dimension_type original_var = mapping[i+1].first;
1786 if (is_in_base(original_var, row)) {
1787 const Row& t_row = tableau[row];
1788 Coefficient_traits::const_reference t_row_original_var
1789 = t_row.get(original_var);
1790 if (t_row_original_var > 0) {
1791 neg_assign(numer_i, t_row.get(0));
1792 denom_i = t_row_original_var;
1793 }
1794 else {
1795 numer_i = t_row.get(0);
1796 neg_assign(denom_i, t_row_original_var);
1797 }
1798 }
1799 else {
1800 numer_i = 0;
1801 denom_i = 1;
1802 }
1803 // Check whether the variable was split.
1804 const dimension_type split_var = mapping[i+1].second;
1805 if (split_var != 0) {
1806 // The variable was split: get the value for the negative component,
1807 // having index mapping[i+1].second .
1808 // Like before, we he have to check if the variable is in base.
1809 if (is_in_base(split_var, row)) {
1810 const Row& t_row = tableau[row];
1811 Coefficient_traits::const_reference t_row_split_var
1812 = t_row.get(split_var);
1813 if (t_row_split_var > 0) {
1814 neg_assign(split_numer, t_row.get(0));
1815 split_denom = t_row_split_var;
1816 }
1817 else {
1818 split_numer = t_row.get(0);
1819 neg_assign(split_denom, t_row_split_var);
1820 }
1821 // We compute the lcm to compute subsequently the difference
1822 // between the 2 variables.
1823 lcm_assign(lcm, denom_i, split_denom);
1824 exact_div_assign(denom_i, lcm, denom_i);
1825 exact_div_assign(split_denom, lcm, split_denom);
1826 numer_i *= denom_i;
1827 sub_mul_assign(numer_i, split_numer, split_denom);
1828 if (numer_i == 0) {
1829 denom_i = 1;
1830 }
1831 else {
1832 denom_i = lcm;
1833 }
1834 }
1835 // Note: if the negative component was not in base, then
1836 // it has value zero and there is nothing left to do.
1837 }
1838 }
1839
1840 // Compute the lcm of all denominators.
1841 PPL_ASSERT(external_space_dim > 0);
1842 lcm = denom[0];
1843 for (dimension_type i = 1; i < external_space_dim; ++i) {
1844 lcm_assign(lcm, lcm, denom[i]);
1845 }
1846 // Use the denominators to store the numerators' multipliers
1847 // and then compute the normalized numerators.
1848 for (dimension_type i = external_space_dim; i-- > 0; ) {
1849 exact_div_assign(denom[i], lcm, denom[i]);
1850 numer[i] *= denom[i];
1851 }
1852
1853 // Finally, build the generator.
1854 Linear_Expression expr;
1855 for (dimension_type i = external_space_dim; i-- > 0; ) {
1856 add_mul_assign(expr, numer[i], Variable(i));
1857 }
1858
1859 MIP_Problem& x = const_cast<MIP_Problem&>(*this);
1860 x.last_generator = point(expr, lcm);
1861 }
1862
1863 void
second_phase()1864 PPL::MIP_Problem::second_phase() {
1865 // Second_phase requires that *this is satisfiable.
1866 PPL_ASSERT(status == SATISFIABLE
1867 || status == UNBOUNDED
1868 || status == OPTIMIZED);
1869 // In the following cases the problem is already solved.
1870 if (status == UNBOUNDED || status == OPTIMIZED) {
1871 return;
1872 }
1873 // Build the objective function for the second phase.
1874 Row new_cost;
1875 input_obj_function.get_row(new_cost);
1876
1877 // Negate the cost function if we are minimizing.
1878 if (opt_mode == MINIMIZATION) {
1879 for (Row::iterator i = new_cost.begin(),
1880 i_end = new_cost.end(); i != i_end; ++i) {
1881 neg_assign(*i);
1882 }
1883 }
1884
1885 const dimension_type cost_zero_size = working_cost.size();
1886
1887 // Substitute properly the cost function in the `costs' matrix.
1888 {
1889 working_cost_type tmp_cost(cost_zero_size, cost_zero_size);
1890 swap(tmp_cost, working_cost);
1891 }
1892
1893 {
1894 working_cost_type::iterator itr
1895 = working_cost.insert(cost_zero_size - 1, Coefficient_one());
1896
1897 // Split the variables in the cost function.
1898 for (Row::const_iterator i = new_cost.lower_bound(1),
1899 i_end = new_cost.end(); i != i_end; ++i) {
1900 const dimension_type index = i.index();
1901 const dimension_type original_var = mapping[index].first;
1902 const dimension_type split_var = mapping[index].second;
1903 itr = working_cost.insert(itr, original_var, *i);
1904 if (mapping[index].second != 0) {
1905 itr = working_cost.insert(itr, split_var);
1906 neg_assign(*itr, *i);
1907 }
1908 }
1909 }
1910
1911 // Here the first phase problem succeeded with optimum value zero.
1912 // Express the old cost function in terms of the computed base.
1913 {
1914 working_cost_type::iterator itr = working_cost.end();
1915 for (dimension_type i = tableau.num_rows(); i-- > 0; ) {
1916 const dimension_type base_i = base[i];
1917 itr = working_cost.lower_bound(itr, base_i);
1918 if (itr != working_cost.end() && itr.index() == base_i && *itr != 0) {
1919 linear_combine(working_cost, tableau[i], base_i);
1920 itr = working_cost.end();
1921 }
1922 }
1923 }
1924
1925 // Solve the second phase problem.
1926 const bool second_phase_successful
1927 = (get_control_parameter(PRICING) == PRICING_STEEPEST_EDGE_FLOAT)
1928 ? compute_simplex_using_steepest_edge_float()
1929 : compute_simplex_using_exact_pricing();
1930 compute_generator();
1931 #if PPL_NOISY_SIMPLEX
1932 std::cout << "MIP_Problem::second_phase(): 2nd phase ended at iteration "
1933 << num_iterations
1934 << "." << std::endl;
1935 #endif // PPL_NOISY_SIMPLEX
1936 status = second_phase_successful ? OPTIMIZED : UNBOUNDED;
1937 PPL_ASSERT(OK());
1938 }
1939
1940 void
1941 PPL::MIP_Problem
evaluate_objective_function(const Generator & evaluating_point,Coefficient & numer,Coefficient & denom) const1942 ::evaluate_objective_function(const Generator& evaluating_point,
1943 Coefficient& numer,
1944 Coefficient& denom) const {
1945 const dimension_type ep_space_dim = evaluating_point.space_dimension();
1946 if (space_dimension() < ep_space_dim) {
1947 throw std::invalid_argument("PPL::MIP_Problem::"
1948 "evaluate_objective_function(p, n, d):\n"
1949 "*this and p are dimension incompatible.");
1950 }
1951 if (!evaluating_point.is_point()) {
1952 throw std::invalid_argument("PPL::MIP_Problem::"
1953 "evaluate_objective_function(p, n, d):\n"
1954 "p is not a point.");
1955 }
1956 // Compute the smallest space dimension between `input_obj_function'
1957 // and `evaluating_point'.
1958 const dimension_type working_space_dim
1959 = std::min(ep_space_dim, input_obj_function.space_dimension());
1960 input_obj_function.scalar_product_assign(numer,
1961 evaluating_point.expr,
1962 0, working_space_dim + 1);
1963
1964 // Numerator and denominator should be coprime.
1965 normalize2(numer, evaluating_point.divisor(), numer, denom);
1966 }
1967
1968 bool
is_lp_satisfiable() const1969 PPL::MIP_Problem::is_lp_satisfiable() const {
1970 #if PPL_NOISY_SIMPLEX
1971 num_iterations = 0;
1972 #endif // PPL_NOISY_SIMPLEX
1973 switch (status) {
1974 case UNSATISFIABLE:
1975 return false;
1976 case SATISFIABLE:
1977 // Intentionally fall through.
1978 case UNBOUNDED:
1979 // Intentionally fall through.
1980 case OPTIMIZED:
1981 // Intentionally fall through.
1982 return true;
1983 case PARTIALLY_SATISFIABLE:
1984 {
1985 MIP_Problem& x = const_cast<MIP_Problem&>(*this);
1986 // This code tries to handle the case that happens if the tableau is
1987 // empty, so it must be initialized.
1988 if (tableau.num_columns() == 0) {
1989 // Add two columns, the first that handles the inhomogeneous term and
1990 // the second that represent the `sign'.
1991 x.tableau.add_zero_columns(2);
1992 // Sync `mapping' for the inhomogeneous term.
1993 x.mapping.push_back(std::make_pair(0, 0));
1994 // The internal data structures are ready, so prepare for more
1995 // assertion to be checked.
1996 x.initialized = true;
1997 }
1998
1999 // Apply incrementality to the pending constraint system.
2000 x.process_pending_constraints();
2001 // Update `first_pending_constraint': no more pending.
2002 x.first_pending_constraint = input_cs.size();
2003 // Update also `internal_space_dim'.
2004 x.internal_space_dim = x.external_space_dim;
2005 PPL_ASSERT(OK());
2006 return status != UNSATISFIABLE;
2007 }
2008 }
2009 // We should not be here!
2010 PPL_UNREACHABLE;
2011 return false;
2012 }
2013
2014 PPL::MIP_Problem_Status
solve_mip(bool & have_incumbent_solution,mpq_class & incumbent_solution_value,Generator & incumbent_solution_point,MIP_Problem & mip,const Variables_Set & i_vars)2015 PPL::MIP_Problem::solve_mip(bool& have_incumbent_solution,
2016 mpq_class& incumbent_solution_value,
2017 Generator& incumbent_solution_point,
2018 MIP_Problem& mip,
2019 const Variables_Set& i_vars) {
2020 // Solve the problem as a non MIP one, it must be done internally.
2021 PPL::MIP_Problem_Status mip_status;
2022 if (mip.is_lp_satisfiable()) {
2023 mip.second_phase();
2024 mip_status = (mip.status == OPTIMIZED) ? OPTIMIZED_MIP_PROBLEM
2025 : UNBOUNDED_MIP_PROBLEM;
2026 }
2027 else {
2028 return UNFEASIBLE_MIP_PROBLEM;
2029 }
2030 PPL_DIRTY_TEMP(mpq_class, tmp_rational);
2031
2032 Generator p = point();
2033 PPL_DIRTY_TEMP_COEFFICIENT(tmp_coeff1);
2034 PPL_DIRTY_TEMP_COEFFICIENT(tmp_coeff2);
2035
2036 if (mip_status == UNBOUNDED_MIP_PROBLEM) {
2037 p = mip.last_generator;
2038 }
2039 else {
2040 PPL_ASSERT(mip_status == OPTIMIZED_MIP_PROBLEM);
2041 // Do not call optimizing_point().
2042 p = mip.last_generator;
2043 mip.evaluate_objective_function(p, tmp_coeff1, tmp_coeff2);
2044 assign_r(tmp_rational.get_num(), tmp_coeff1, ROUND_NOT_NEEDED);
2045 assign_r(tmp_rational.get_den(), tmp_coeff2, ROUND_NOT_NEEDED);
2046 PPL_ASSERT(is_canonical(tmp_rational));
2047 if (have_incumbent_solution
2048 && ((mip.optimization_mode() == MAXIMIZATION
2049 && tmp_rational <= incumbent_solution_value)
2050 || (mip.optimization_mode() == MINIMIZATION
2051 && tmp_rational >= incumbent_solution_value))) {
2052 // Abandon this path.
2053 return mip_status;
2054 }
2055 }
2056
2057 bool found_satisfiable_generator = true;
2058 PPL_DIRTY_TEMP_COEFFICIENT(gcd);
2059 Coefficient_traits::const_reference p_divisor = p.divisor();
2060 dimension_type non_int_dim = mip.space_dimension();
2061 // TODO: This can be optimized more, exploiting the (possible)
2062 // sparseness of p, if the size of i_vars is expected to be greater than
2063 // the number of nonzeroes in p in most cases.
2064 for (Variables_Set::const_iterator v_begin = i_vars.begin(),
2065 v_end = i_vars.end(); v_begin != v_end; ++v_begin) {
2066 gcd_assign(gcd, p.coefficient(Variable(*v_begin)), p_divisor);
2067 if (gcd != p_divisor) {
2068 non_int_dim = *v_begin;
2069 found_satisfiable_generator = false;
2070 break;
2071 }
2072 }
2073 if (found_satisfiable_generator) {
2074 // All the coordinates of `point' are satisfiable.
2075 if (mip_status == UNBOUNDED_MIP_PROBLEM) {
2076 // This is a point that belongs to the MIP_Problem.
2077 // In this way we are sure that we will return every time
2078 // a feasible point if requested by the user.
2079 incumbent_solution_point = p;
2080 return mip_status;
2081 }
2082 if (!have_incumbent_solution
2083 || (mip.optimization_mode() == MAXIMIZATION
2084 && tmp_rational > incumbent_solution_value)
2085 || tmp_rational < incumbent_solution_value) {
2086 incumbent_solution_value = tmp_rational;
2087 incumbent_solution_point = p;
2088 have_incumbent_solution = true;
2089 #if PPL_NOISY_SIMPLEX
2090 PPL_DIRTY_TEMP_COEFFICIENT(numer);
2091 PPL_DIRTY_TEMP_COEFFICIENT(denom);
2092 mip.evaluate_objective_function(p, numer, denom);
2093 std::cout << "MIP_Problem::solve_mip(): "
2094 << "new value found: " << numer << "/" << denom
2095 << "." << std::endl;
2096 #endif // PPL_NOISY_SIMPLEX
2097 }
2098 return mip_status;
2099 }
2100
2101 PPL_ASSERT(non_int_dim < mip.space_dimension());
2102
2103 assign_r(tmp_rational.get_num(), p.coefficient(Variable(non_int_dim)),
2104 ROUND_NOT_NEEDED);
2105 assign_r(tmp_rational.get_den(), p_divisor, ROUND_NOT_NEEDED);
2106 tmp_rational.canonicalize();
2107 assign_r(tmp_coeff1, tmp_rational, ROUND_DOWN);
2108 assign_r(tmp_coeff2, tmp_rational, ROUND_UP);
2109 {
2110 MIP_Problem mip_aux(mip, Inherit_Constraints());
2111 mip_aux.add_constraint(Variable(non_int_dim) <= tmp_coeff1);
2112 #if PPL_NOISY_SIMPLEX
2113 using namespace IO_Operators;
2114 std::cout << "MIP_Problem::solve_mip(): "
2115 << "descending with: "
2116 << (Variable(non_int_dim) <= tmp_coeff1)
2117 << "." << std::endl;
2118 #endif // PPL_NOISY_SIMPLEX
2119 solve_mip(have_incumbent_solution, incumbent_solution_value,
2120 incumbent_solution_point, mip_aux, i_vars);
2121 }
2122 // TODO: change this when we will be able to remove constraints.
2123 mip.add_constraint(Variable(non_int_dim) >= tmp_coeff2);
2124 #if PPL_NOISY_SIMPLEX
2125 using namespace IO_Operators;
2126 std::cout << "MIP_Problem::solve_mip(): "
2127 << "descending with: "
2128 << (Variable(non_int_dim) >= tmp_coeff2)
2129 << "." << std::endl;
2130 #endif // PPL_NOISY_SIMPLEX
2131 solve_mip(have_incumbent_solution, incumbent_solution_value,
2132 incumbent_solution_point, mip, i_vars);
2133 return have_incumbent_solution ? mip_status : UNFEASIBLE_MIP_PROBLEM;
2134 }
2135
2136 bool
choose_branching_variable(const MIP_Problem & mip,const Variables_Set & i_vars,dimension_type & branching_index)2137 PPL::MIP_Problem::choose_branching_variable(const MIP_Problem& mip,
2138 const Variables_Set& i_vars,
2139 dimension_type& branching_index) {
2140 // Insert here the variables that do not satisfy the integrality
2141 // condition.
2142 const std::vector<Constraint*>& input_cs = mip.input_cs;
2143 const Generator& last_generator = mip.last_generator;
2144 Coefficient_traits::const_reference last_generator_divisor
2145 = last_generator.divisor();
2146 Variables_Set candidate_variables;
2147
2148 // TODO: This can be optimized more, exploiting the (possible)
2149 // sparseness of last_generator, if the size of i_vars is expected to be
2150 // greater than the number of nonzeroes in last_generator in most cases.
2151 PPL_DIRTY_TEMP_COEFFICIENT(gcd);
2152 for (Variables_Set::const_iterator v_it = i_vars.begin(),
2153 v_end = i_vars.end(); v_it != v_end; ++v_it) {
2154 gcd_assign(gcd,
2155 last_generator.coefficient(Variable(*v_it)),
2156 last_generator_divisor);
2157 if (gcd != last_generator_divisor) {
2158 candidate_variables.insert(*v_it);
2159 }
2160 }
2161 // If this set is empty, we have finished.
2162 if (candidate_variables.empty()) {
2163 return true;
2164 }
2165
2166 // Check how many `active constraints' we have and track them.
2167 const dimension_type input_cs_num_rows = input_cs.size();
2168 std::deque<bool> satisfiable_constraints(input_cs_num_rows, false);
2169 for (dimension_type i = input_cs_num_rows; i-- > 0; ) {
2170 // An equality is an `active constraint' by definition.
2171 // If we have an inequality, check if it is an `active constraint'.
2172 if (input_cs[i]->is_equality()
2173 || is_saturated(*(input_cs[i]), last_generator)) {
2174 satisfiable_constraints[i] = true;
2175 }
2176 }
2177
2178 dimension_type winning_num_appearances = 0;
2179
2180 std::vector<dimension_type>
2181 num_appearances(candidate_variables.space_dimension(), 0);
2182
2183 // For every candidate variable, check how many times this appear in the
2184 // active constraints.
2185 for (dimension_type i = input_cs_num_rows; i-- > 0; ) {
2186 if (!satisfiable_constraints[i]) {
2187 continue;
2188 }
2189 // TODO: This can be optimized more, exploiting the (possible)
2190 // sparseness of input_cs, if the size of candidate_variables is expected
2191 // to be greater than the number of nonzeroes of most rows.
2192 for (Variables_Set::const_iterator v_it = candidate_variables.begin(),
2193 v_end = candidate_variables.end(); v_it != v_end; ++v_it) {
2194 if (*v_it >= input_cs[i]->space_dimension()) {
2195 break;
2196 }
2197 if (input_cs[i]->coefficient(Variable(*v_it)) != 0) {
2198 ++num_appearances[*v_it];
2199 }
2200 }
2201 }
2202 for (Variables_Set::const_iterator v_it = candidate_variables.begin(),
2203 v_end = candidate_variables.end(); v_it != v_end; ++v_it) {
2204 const dimension_type n = num_appearances[*v_it];
2205 if (n >= winning_num_appearances) {
2206 winning_num_appearances = n;
2207 branching_index = *v_it;
2208 }
2209 }
2210 return false;
2211 }
2212
2213 bool
is_mip_satisfiable(MIP_Problem & mip,const Variables_Set & i_vars,Generator & p)2214 PPL::MIP_Problem::is_mip_satisfiable(MIP_Problem& mip,
2215 const Variables_Set& i_vars,
2216 Generator& p) {
2217 #if PPL_NOISY_SIMPLEX
2218 ++mip_recursion_level;
2219 std::cout << "MIP_Problem::is_mip_satisfiable(): "
2220 << "entering recursion level " << mip_recursion_level
2221 << "." << std::endl;
2222 #endif // PPL_NOISY_SIMPLEX
2223 PPL_ASSERT(mip.integer_space_dimensions().empty());
2224
2225 // Solve the MIP problem.
2226 if (!mip.is_lp_satisfiable()) {
2227 #if PPL_NOISY_SIMPLEX
2228 std::cout << "MIP_Problem::is_mip_satisfiable(): "
2229 << "exiting from recursion level " << mip_recursion_level
2230 << "." << std::endl;
2231 --mip_recursion_level;
2232 #endif // PPL_NOISY_SIMPLEX
2233 return false;
2234 }
2235
2236 PPL_DIRTY_TEMP(mpq_class, tmp_rational);
2237 PPL_DIRTY_TEMP_COEFFICIENT(tmp_coeff1);
2238 PPL_DIRTY_TEMP_COEFFICIENT(tmp_coeff2);
2239 bool found_satisfiable_generator = true;
2240 dimension_type non_int_dim;
2241 p = mip.last_generator;
2242 Coefficient_traits::const_reference p_divisor = p.divisor();
2243
2244 #if PPL_SIMPLEX_USE_MIP_HEURISTIC
2245 found_satisfiable_generator
2246 = choose_branching_variable(mip, i_vars, non_int_dim);
2247 #else
2248 PPL_DIRTY_TEMP_COEFFICIENT(gcd);
2249 // TODO: This can be optimized more, exploiting the (possible)
2250 // sparseness of p, if the size of i_vars is expected to be greater than
2251 // the number of nonzeroes in p in most cases.
2252 for (Variables_Set::const_iterator v_begin = i_vars.begin(),
2253 v_end = i_vars.end(); v_begin != v_end; ++v_begin) {
2254 gcd_assign(gcd, p.coefficient(Variable(*v_begin)), p_divisor);
2255 if (gcd != p_divisor) {
2256 non_int_dim = *v_begin;
2257 found_satisfiable_generator = false;
2258 break;
2259 }
2260 }
2261 #endif
2262
2263 if (found_satisfiable_generator) {
2264 return true;
2265 }
2266
2267 PPL_ASSERT(non_int_dim < mip.space_dimension());
2268
2269 assign_r(tmp_rational.get_num(), p.coefficient(Variable(non_int_dim)),
2270 ROUND_NOT_NEEDED);
2271 assign_r(tmp_rational.get_den(), p_divisor, ROUND_NOT_NEEDED);
2272 tmp_rational.canonicalize();
2273 assign_r(tmp_coeff1, tmp_rational, ROUND_DOWN);
2274 assign_r(tmp_coeff2, tmp_rational, ROUND_UP);
2275 {
2276 MIP_Problem mip_aux(mip, Inherit_Constraints());
2277 mip_aux.add_constraint(Variable(non_int_dim) <= tmp_coeff1);
2278 #if PPL_NOISY_SIMPLEX
2279 using namespace IO_Operators;
2280 std::cout << "MIP_Problem::is_mip_satisfiable(): "
2281 << "descending with: "
2282 << (Variable(non_int_dim) <= tmp_coeff1)
2283 << "." << std::endl;
2284 #endif // PPL_NOISY_SIMPLEX
2285 if (is_mip_satisfiable(mip_aux, i_vars, p)) {
2286 #if PPL_NOISY_SIMPLEX
2287 std::cout << "MIP_Problem::is_mip_satisfiable(): "
2288 << "exiting from recursion level " << mip_recursion_level
2289 << "." << std::endl;
2290 --mip_recursion_level;
2291 #endif // PPL_NOISY_SIMPLEX
2292 return true;
2293 }
2294 }
2295 mip.add_constraint(Variable(non_int_dim) >= tmp_coeff2);
2296 #if PPL_NOISY_SIMPLEX
2297 using namespace IO_Operators;
2298 std::cout << "MIP_Problem::is_mip_satisfiable(): "
2299 << "descending with: "
2300 << (Variable(non_int_dim) >= tmp_coeff2)
2301 << "." << std::endl;
2302 #endif // PPL_NOISY_SIMPLEX
2303 const bool satisfiable = is_mip_satisfiable(mip, i_vars, p);
2304 #if PPL_NOISY_SIMPLEX
2305 std::cout << "MIP_Problem::is_mip_satisfiable(): "
2306 << "exiting from recursion level " << mip_recursion_level
2307 << "." << std::endl;
2308 --mip_recursion_level;
2309 #endif // PPL_NOISY_SIMPLEX
2310 return satisfiable;
2311 }
2312
2313 bool
OK() const2314 PPL::MIP_Problem::OK() const {
2315 #ifndef NDEBUG
2316 using std::endl;
2317 using std::cerr;
2318 #endif
2319 const dimension_type input_cs_num_rows = input_cs.size();
2320
2321 // Check that every member used is OK.
2322
2323 if (inherited_constraints > input_cs_num_rows) {
2324 #ifndef NDEBUG
2325 cerr << "The MIP_Problem claims to have inherited from its ancestors "
2326 << "more constraints than are recorded in this->input_cs."
2327 << endl;
2328 ascii_dump(cerr);
2329 #endif
2330 return false;
2331 }
2332
2333 if (first_pending_constraint > input_cs_num_rows) {
2334 #ifndef NDEBUG
2335 cerr << "The MIP_Problem claims to have pending constraints "
2336 << "that are not recorded in this->input_cs."
2337 << endl;
2338 ascii_dump(cerr);
2339 #endif
2340 return false;
2341 }
2342
2343 if (!tableau.OK() || !last_generator.OK()) {
2344 return false;
2345 }
2346
2347 // Constraint system should contain no strict inequalities.
2348 for (dimension_type i = input_cs_num_rows; i-- > 0; ) {
2349 if (input_cs[i]->is_strict_inequality()) {
2350 #ifndef NDEBUG
2351 cerr << "The feasible region of the MIP_Problem is defined by "
2352 << "a constraint system containing strict inequalities."
2353 << endl;
2354 ascii_dump(cerr);
2355 #endif
2356 return false;
2357 }
2358
2359 }
2360
2361 if (external_space_dim < internal_space_dim) {
2362 #ifndef NDEBUG
2363 cerr << "The MIP_Problem claims to have an internal space dimension "
2364 << "greater than its external space dimension."
2365 << endl;
2366 ascii_dump(cerr);
2367 #endif
2368 return false;
2369 }
2370
2371 if (external_space_dim > internal_space_dim
2372 && status != UNSATISFIABLE
2373 && status != PARTIALLY_SATISFIABLE) {
2374 #ifndef NDEBUG
2375 cerr << "The MIP_Problem claims to have a pending space dimension "
2376 << "addition, but the status is incompatible."
2377 << endl;
2378 ascii_dump(cerr);
2379 #endif
2380 return false;
2381 }
2382
2383 // Constraint system and objective function should be dimension compatible.
2384 if (external_space_dim < input_obj_function.space_dimension()) {
2385 #ifndef NDEBUG
2386 cerr << "The MIP_Problem and the objective function have "
2387 << "incompatible space dimensions ("
2388 << external_space_dim << " < "
2389 << input_obj_function.space_dimension() << ")."
2390 << endl;
2391 ascii_dump(cerr);
2392 #endif
2393 return false;
2394 }
2395
2396 if (status != UNSATISFIABLE && initialized) {
2397 // Here `last_generator' has to be meaningful.
2398 // Check for dimension compatibility and actual feasibility.
2399 if (internal_space_dim != last_generator.space_dimension()) {
2400 #ifndef NDEBUG
2401 cerr << "The MIP_Problem and the cached feasible point have "
2402 << "incompatible space dimensions ("
2403 << internal_space_dim << " != "
2404 << last_generator.space_dimension() << ")."
2405 << endl;
2406 ascii_dump(cerr);
2407 #endif
2408 return false;
2409 }
2410
2411 for (dimension_type i = 0; i < first_pending_constraint; ++i) {
2412 if (!is_satisfied(*(input_cs[i]), last_generator)) {
2413 #ifndef NDEBUG
2414 cerr << "The cached feasible point does not belong to "
2415 << "the feasible region of the MIP_Problem."
2416 << endl;
2417 ascii_dump(cerr);
2418 #endif
2419 return false;
2420 }
2421 }
2422
2423 // Check that every integer declared variable is really integer.
2424 // in the solution found.
2425 if (!i_variables.empty()) {
2426 PPL_DIRTY_TEMP_COEFFICIENT(gcd);
2427 // TODO: This can be optimized more, exploiting the (possible)
2428 // sparseness of last_generator, if the size of i_variables is expected
2429 // to be greater than the number of nonzeroes in last_generator in most
2430 // cases.
2431 for (Variables_Set::const_iterator v_it = i_variables.begin(),
2432 v_end = i_variables.end(); v_it != v_end; ++v_it) {
2433 gcd_assign(gcd, last_generator.coefficient(Variable(*v_it)),
2434 last_generator.divisor());
2435 if (gcd != last_generator.divisor()) {
2436 return false;
2437 }
2438 }
2439 }
2440
2441 const dimension_type tableau_num_rows = tableau.num_rows();
2442 const dimension_type tableau_num_columns = tableau.num_columns();
2443
2444 // The number of rows in the tableau and base should be equal.
2445 if (tableau_num_rows != base.size()) {
2446 #ifndef NDEBUG
2447 cerr << "tableau and base have incompatible sizes" << endl;
2448 ascii_dump(cerr);
2449 #endif
2450 return false;
2451 }
2452 // The size of `mapping' should be equal to the internal
2453 // space dimension plus one.
2454 if (mapping.size() != internal_space_dim + 1) {
2455 #ifndef NDEBUG
2456 cerr << "The internal space dimension and `mapping' "
2457 << "have incompatible sizes" << endl;
2458 ascii_dump(cerr);
2459 #endif
2460 return false;
2461 }
2462
2463 // The number of columns in the tableau and working_cost should be equal.
2464 if (tableau_num_columns != working_cost.size()) {
2465 #ifndef NDEBUG
2466 cerr << "tableau and working_cost have incompatible sizes" << endl;
2467 ascii_dump(cerr);
2468 #endif
2469 return false;
2470 }
2471
2472 // The vector base should contain indices of tableau's columns.
2473 for (dimension_type i = base.size(); i-- > 0; ) {
2474 if (base[i] > tableau_num_columns) {
2475 #ifndef NDEBUG
2476 cerr << "base contains an invalid column index" << endl;
2477 ascii_dump(cerr);
2478 #endif
2479 return false;
2480 }
2481 }
2482
2483 {
2484 // Needed to sort accesses to tableau_j, improving performance.
2485 typedef std::vector<std::pair<dimension_type, dimension_type> >
2486 pair_vector_t;
2487 pair_vector_t vars_in_base;
2488 for (dimension_type i = base.size(); i-- > 0; ) {
2489 vars_in_base.push_back(std::make_pair(base[i], i));
2490 }
2491
2492 std::sort(vars_in_base.begin(), vars_in_base.end());
2493
2494 for (dimension_type j = tableau_num_rows; j-- > 0; ) {
2495 const Row& tableau_j = tableau[j];
2496 pair_vector_t::iterator i = vars_in_base.begin();
2497 pair_vector_t::iterator i_end = vars_in_base.end();
2498 Row::const_iterator itr = tableau_j.begin();
2499 Row::const_iterator itr_end = tableau_j.end();
2500 for ( ; i != i_end && itr != itr_end; ++i) {
2501 // tableau[i][base[j]], with i different from j, must be zero.
2502 if (itr.index() < i->first) {
2503 itr = tableau_j.lower_bound(itr, itr.index());
2504 }
2505 if (i->second != j && itr.index() == i->first && *itr != 0) {
2506 #ifndef NDEBUG
2507 cerr << "tableau[i][base[j]], with i different from j, must be "
2508 << "zero" << endl;
2509 ascii_dump(cerr);
2510 #endif
2511 return false;
2512 }
2513 }
2514 }
2515 }
2516 // tableau[i][base[i]] must not be a zero.
2517 for (dimension_type i = base.size(); i-- > 0; ) {
2518 if (tableau[i].get(base[i]) == 0) {
2519 #ifndef NDEBUG
2520 cerr << "tableau[i][base[i]] must not be a zero" << endl;
2521 ascii_dump(cerr);
2522 #endif
2523 return false;
2524 }
2525 }
2526
2527 // The last column of the tableau must contain only zeroes.
2528 for (dimension_type i = tableau_num_rows; i-- > 0; ) {
2529 if (tableau[i].get(tableau_num_columns - 1) != 0) {
2530 #ifndef NDEBUG
2531 cerr << "the last column of the tableau must contain only"
2532 "zeroes"<< endl;
2533 ascii_dump(cerr);
2534 #endif
2535 return false;
2536 }
2537 }
2538 }
2539
2540 // All checks passed.
2541 return true;
2542 }
2543
2544 void
ascii_dump(std::ostream & s) const2545 PPL::MIP_Problem::ascii_dump(std::ostream& s) const {
2546 using namespace IO_Operators;
2547 s << "\nexternal_space_dim: " << external_space_dim << " \n";
2548 s << "\ninternal_space_dim: " << internal_space_dim << " \n";
2549
2550 const dimension_type input_cs_size = input_cs.size();
2551
2552 s << "\ninput_cs( " << input_cs_size << " )\n";
2553 for (dimension_type i = 0; i < input_cs_size; ++i) {
2554 input_cs[i]->ascii_dump(s);
2555 }
2556
2557 s << "\ninherited_constraints: " << inherited_constraints
2558 << std::endl;
2559
2560 s << "\nfirst_pending_constraint: " << first_pending_constraint
2561 << std::endl;
2562
2563 s << "\ninput_obj_function\n";
2564 input_obj_function.ascii_dump(s);
2565 s << "\nopt_mode "
2566 << ((opt_mode == MAXIMIZATION) ? "MAXIMIZATION" : "MINIMIZATION") << "\n";
2567
2568 s << "\ninitialized: " << (initialized ? "YES" : "NO") << "\n";
2569 s << "\npricing: ";
2570 switch (pricing) {
2571 case PRICING_STEEPEST_EDGE_FLOAT:
2572 s << "PRICING_STEEPEST_EDGE_FLOAT";
2573 break;
2574 case PRICING_STEEPEST_EDGE_EXACT:
2575 s << "PRICING_STEEPEST_EDGE_EXACT";
2576 break;
2577 case PRICING_TEXTBOOK:
2578 s << "PRICING_TEXTBOOK";
2579 break;
2580 }
2581 s << "\n";
2582
2583 s << "\nstatus: ";
2584 switch (status) {
2585 case UNSATISFIABLE:
2586 s << "UNSATISFIABLE";
2587 break;
2588 case SATISFIABLE:
2589 s << "SATISFIABLE";
2590 break;
2591 case UNBOUNDED:
2592 s << "UNBOUNDED";
2593 break;
2594 case OPTIMIZED:
2595 s << "OPTIMIZED";
2596 break;
2597 case PARTIALLY_SATISFIABLE:
2598 s << "PARTIALLY_SATISFIABLE";
2599 break;
2600 }
2601 s << "\n";
2602
2603 s << "\ntableau\n";
2604 tableau.ascii_dump(s);
2605 s << "\nworking_cost( " << working_cost.size()<< " )\n";
2606 working_cost.ascii_dump(s);
2607
2608 const dimension_type base_size = base.size();
2609 s << "\nbase( " << base_size << " )\n";
2610 for (dimension_type i = 0; i != base_size; ++i) {
2611 s << base[i] << ' ';
2612 }
2613
2614 s << "\nlast_generator\n";
2615 last_generator.ascii_dump(s);
2616
2617 const dimension_type mapping_size = mapping.size();
2618 s << "\nmapping( " << mapping_size << " )\n";
2619 for (dimension_type i = 1; i < mapping_size; ++i) {
2620 s << "\n"<< i << " -> " << mapping[i].first << " -> " << mapping[i].second
2621 << ' ';
2622 }
2623
2624 s << "\n\ninteger_variables";
2625 i_variables.ascii_dump(s);
2626 }
2627
PPL_OUTPUT_DEFINITIONS(MIP_Problem)2628 PPL_OUTPUT_DEFINITIONS(MIP_Problem)
2629
2630 bool
2631 PPL::MIP_Problem::ascii_load(std::istream& s) {
2632 std::string str;
2633 if (!(s >> str) || str != "external_space_dim:") {
2634 return false;
2635 }
2636
2637 if (!(s >> external_space_dim)) {
2638 return false;
2639 }
2640
2641 if (!(s >> str) || str != "internal_space_dim:") {
2642 return false;
2643 }
2644
2645 if (!(s >> internal_space_dim)) {
2646 return false;
2647 }
2648
2649 if (!(s >> str) || str != "input_cs(") {
2650 return false;
2651 }
2652
2653 dimension_type input_cs_size;
2654
2655 if (!(s >> input_cs_size)) {
2656 return false;
2657 }
2658
2659 if (!(s >> str) || str != ")") {
2660 return false;
2661 }
2662
2663 Constraint c(Constraint::zero_dim_positivity());
2664 input_cs.reserve(input_cs_size);
2665 for (dimension_type i = 0; i < input_cs_size; ++i) {
2666 if (!c.ascii_load(s)) {
2667 return false;
2668 }
2669 add_constraint_helper(c);
2670 }
2671
2672 if (!(s >> str) || str != "inherited_constraints:") {
2673 return false;
2674 }
2675
2676 if (!(s >> inherited_constraints)) {
2677 return false;
2678 }
2679 // NOTE: we loaded the number of inherited constraints, but we nonetheless
2680 // reset to zero the corresponding data member, since we do not support
2681 // constraint inheritance via ascii_load.
2682 inherited_constraints = 0;
2683
2684 if (!(s >> str) || str != "first_pending_constraint:") {
2685 return false;
2686 }
2687 if (!(s >> first_pending_constraint)) {
2688 return false;
2689 }
2690 if (!(s >> str) || str != "input_obj_function") {
2691 return false;
2692 }
2693 if (!input_obj_function.ascii_load(s)) {
2694 return false;
2695 }
2696 if (!(s >> str) || str != "opt_mode") {
2697 return false;
2698 }
2699 if (!(s >> str)) {
2700 return false;
2701 }
2702 if (str == "MAXIMIZATION") {
2703 set_optimization_mode(MAXIMIZATION);
2704 }
2705 else {
2706 if (str != "MINIMIZATION") {
2707 return false;
2708 }
2709 set_optimization_mode(MINIMIZATION);
2710 }
2711
2712 if (!(s >> str) || str != "initialized:") {
2713 return false;
2714 }
2715 if (!(s >> str)) {
2716 return false;
2717 }
2718 if (str == "YES") {
2719 initialized = true;
2720 }
2721 else if (str == "NO") {
2722 initialized = false;
2723 }
2724 else {
2725 return false;
2726 }
2727
2728 if (!(s >> str) || str != "pricing:") {
2729 return false;
2730 }
2731 if (!(s >> str)) {
2732 return false;
2733 }
2734 if (str == "PRICING_STEEPEST_EDGE_FLOAT") {
2735 pricing = PRICING_STEEPEST_EDGE_FLOAT;
2736 }
2737 else if (str == "PRICING_STEEPEST_EDGE_EXACT") {
2738 pricing = PRICING_STEEPEST_EDGE_EXACT;
2739 }
2740 else if (str == "PRICING_TEXTBOOK") {
2741 pricing = PRICING_TEXTBOOK;
2742 }
2743 else {
2744 return false;
2745 }
2746
2747 if (!(s >> str) || str != "status:") {
2748 return false;
2749 }
2750
2751 if (!(s >> str)) {
2752 return false;
2753 }
2754
2755 if (str == "UNSATISFIABLE") {
2756 status = UNSATISFIABLE;
2757 }
2758 else if (str == "SATISFIABLE") {
2759 status = SATISFIABLE;
2760 }
2761 else if (str == "UNBOUNDED") {
2762 status = UNBOUNDED;
2763 }
2764 else if (str == "OPTIMIZED") {
2765 status = OPTIMIZED;
2766 }
2767 else if (str == "PARTIALLY_SATISFIABLE") {
2768 status = PARTIALLY_SATISFIABLE;
2769 }
2770 else {
2771 return false;
2772 }
2773
2774 if (!(s >> str) || str != "tableau") {
2775 return false;
2776 }
2777
2778 if (!tableau.ascii_load(s)) {
2779 return false;
2780 }
2781
2782 if (!(s >> str) || str != "working_cost(") {
2783 return false;
2784 }
2785
2786 dimension_type working_cost_dim;
2787
2788 if (!(s >> working_cost_dim)) {
2789 return false;
2790 }
2791
2792 if (!(s >> str) || str != ")") {
2793 return false;
2794 }
2795
2796 if (!working_cost.ascii_load(s)) {
2797 return false;
2798 }
2799
2800 if (!(s >> str) || str != "base(") {
2801 return false;
2802 }
2803
2804 dimension_type base_size;
2805 if (!(s >> base_size)) {
2806 return false;
2807 }
2808
2809 if (!(s >> str) || str != ")") {
2810 return false;
2811 }
2812
2813 for (dimension_type i = 0; i != base_size; ++i) {
2814 dimension_type base_value;
2815 if (!(s >> base_value)) {
2816 return false;
2817 }
2818 base.push_back(base_value);
2819 }
2820
2821 if (!(s >> str) || str != "last_generator") {
2822 return false;
2823 }
2824
2825 if (!last_generator.ascii_load(s)) {
2826 return false;
2827 }
2828
2829 if (!(s >> str) || str != "mapping(") {
2830 return false;
2831 }
2832
2833 dimension_type mapping_size;
2834 if (!(s >> mapping_size)) {
2835 return false;
2836 }
2837 if (!(s >> str) || str != ")") {
2838 return false;
2839 }
2840
2841 // The first `mapping' index is never used, so we initialize
2842 // it pushing back a dummy value.
2843 if (tableau.num_columns() != 0) {
2844 mapping.push_back(std::make_pair(0, 0));
2845 }
2846
2847 for (dimension_type i = 1; i < mapping_size; ++i) {
2848 dimension_type index;
2849 if (!(s >> index)) {
2850 return false;
2851 }
2852
2853 if (!(s >> str) || str != "->") {
2854 return false;
2855 }
2856
2857 dimension_type first_value;
2858 if (!(s >> first_value)) {
2859 return false;
2860 }
2861
2862 if (!(s >> str) || str != "->") {
2863 return false;
2864 }
2865
2866 dimension_type second_value;
2867 if (!(s >> second_value)) {
2868 return false;
2869 }
2870
2871 mapping.push_back(std::make_pair(first_value, second_value));
2872 }
2873
2874 if (!(s >> str) || str != "integer_variables") {
2875 return false;
2876 }
2877
2878 if (!i_variables.ascii_load(s)) {
2879 return false;
2880 }
2881
2882 PPL_ASSERT(OK());
2883 return true;
2884 }
2885
2886 /*! \relates Parma_Polyhedra_Library::MIP_Problem */
2887 std::ostream&
operator <<(std::ostream & s,const MIP_Problem & mip)2888 PPL::IO_Operators::operator<<(std::ostream& s, const MIP_Problem& mip) {
2889 s << "Constraints:";
2890 for (MIP_Problem::const_iterator i = mip.constraints_begin(),
2891 i_end = mip.constraints_end(); i != i_end; ++i) {
2892 s << "\n" << *i;
2893 }
2894 s << "\nObjective function: "
2895 << mip.objective_function()
2896 << "\nOptimization mode: "
2897 << ((mip.optimization_mode() == MAXIMIZATION)
2898 ? "MAXIMIZATION"
2899 : "MINIMIZATION");
2900 s << "\nInteger variables: " << mip.integer_space_dimensions();
2901 return s;
2902 }
2903