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