PPL  1.2
Parma_Polyhedra_Library::PIP_Problem Class Reference

A Parametric Integer (linear) Programming problem. More...

#include <ppl.hh>

Public Types

enum  Control_Parameter_Name { CUTTING_STRATEGY, PIVOT_ROW_STRATEGY }
 Possible names for PIP_Problem control parameters. More...
 
enum  Control_Parameter_Value {
  CUTTING_STRATEGY_FIRST, CUTTING_STRATEGY_DEEPEST, CUTTING_STRATEGY_ALL, PIVOT_ROW_STRATEGY_FIRST,
  PIVOT_ROW_STRATEGY_MAX_COLUMN
}
 Possible values for PIP_Problem control parameters. More...
 
typedef Constraint_Sequence::const_iterator const_iterator
 A type alias for the read-only iterator on the constraints defining the feasible region.
 

Public Member Functions

 PIP_Problem (dimension_type dim=0)
 Builds a trivial PIP problem. More...
 
template<typename In >
 PIP_Problem (dimension_type dim, In first, In last, const Variables_Set &p_vars)
 Builds a PIP problem having space dimension dim from the sequence of constraints in the range $[\mathrm{first}, \mathrm{last})$; those dimensions whose indices occur in p_vars are interpreted as parameters. More...
 
 PIP_Problem (const PIP_Problem &y)
 Ordinary copy-constructor.
 
 ~PIP_Problem ()
 Destructor.
 
PIP_Problemoperator= (const PIP_Problem &y)
 Assignment operator.
 
dimension_type space_dimension () const
 Returns the space dimension of the PIP problem.
 
const Variables_Setparameter_space_dimensions () const
 Returns a set containing all the variables' indexes representing the parameters of the PIP problem.
 
const_iterator constraints_begin () const
 Returns a read-only iterator to the first constraint defining the feasible region.
 
const_iterator constraints_end () const
 Returns a past-the-end read-only iterator to the sequence of constraints defining the feasible region.
 
void clear ()
 Resets *this to be equal to the trivial PIP problem. More...
 
void add_space_dimensions_and_embed (dimension_type m_vars, dimension_type m_params)
 Adds m_vars + m_params new space dimensions and embeds the old PIP problem in the new vector space. More...
 
void add_to_parameter_space_dimensions (const Variables_Set &p_vars)
 Sets the space dimensions whose indexes which are in set p_vars to be parameter space dimensions. More...
 
void add_constraint (const Constraint &c)
 Adds a copy of constraint c to the PIP problem. More...
 
void add_constraints (const Constraint_System &cs)
 Adds a copy of the constraints in cs to the PIP problem. More...
 
bool is_satisfiable () const
 Checks satisfiability of *this. More...
 
PIP_Problem_Status solve () const
 Optimizes the PIP problem. More...
 
PIP_Tree solution () const
 Returns a feasible solution for *this, if it exists. More...
 
PIP_Tree optimizing_solution () const
 Returns an optimizing solution for *this, if it exists. More...
 
bool OK () const
 Checks if all the invariants are satisfied.
 
void print_solution (std::ostream &s, int indent=0) const
 Prints on s the solution computed for *this. More...
 
void ascii_dump () const
 Writes to std::cerr an ASCII representation of *this.
 
void ascii_dump (std::ostream &s) const
 Writes to s an ASCII representation of *this.
 
void print () const
 Prints *this to std::cerr using operator<<.
 
bool ascii_load (std::istream &s)
 Loads from s an ASCII representation (as produced by ascii_dump(std::ostream&) const) and sets *this accordingly. Returns true if successful, false otherwise.
 
memory_size_type total_memory_in_bytes () const
 Returns the total size in bytes of the memory occupied by *this.
 
memory_size_type external_memory_in_bytes () const
 Returns the size in bytes of the memory managed by *this.
 
void m_swap (PIP_Problem &y)
 Swaps *this with y.
 
Control_Parameter_Value get_control_parameter (Control_Parameter_Name name) const
 Returns the value of control parameter name.
 
void set_control_parameter (Control_Parameter_Value value)
 Sets control parameter value.
 
void set_big_parameter_dimension (dimension_type big_dim)
 Sets the dimension for the big parameter to big_dim.
 
dimension_type get_big_parameter_dimension () const
 Returns the space dimension for the big parameter. More...
 

Static Public Member Functions

static dimension_type max_space_dimension ()
 Returns the maximum space dimension a PIP_Problem can handle.
 

Related Functions

(Note that these are not member functions.)

std::ostream & operator<< (std::ostream &s, const PIP_Problem &pip)
 Output operator. More...
 
void swap (PIP_Problem &x, PIP_Problem &y)
 Swaps x with y. More...
 
void swap (PIP_Problem &x, PIP_Problem &y)
 

Detailed Description

A Parametric Integer (linear) Programming problem.

An object of this class encodes a parametric integer (linear) programming problem. The PIP problem is specified by providing:

  • the dimension of the vector space;
  • the subset of those dimensions of the vector space that are interpreted as integer parameters (the other space dimensions are interpreted as non-parameter integer variables);
  • a finite set of linear equality and (strict or non-strict) inequality constraints involving variables and/or parameters; these constraints are used to define:
    • the feasible region, if they involve one or more problem variable (and maybe some parameters);
    • the initial context, if they only involve the parameters;
  • optionally, the so-called big parameter, i.e., a problem parameter to be considered arbitrarily big.

Note that all problem variables and problem parameters are assumed to take non-negative integer values, so that there is no need to specify non-negativity constraints.

The class provides support for the (incremental) solution of the PIP problem based on variations of the revised simplex method and on Gomory cut generation techniques.

The solution for a PIP problem is the lexicographic minimum of the integer points of the feasible region, expressed in terms of the parameters. As the problem to be solved only involves non-negative variables and parameters, the problem will always be either unfeasible or optimizable.

As the feasibility and the solution value of a PIP problem depend on the values of the parameters, the solution is a binary decision tree, dividing the context parameter set into subsets. The tree nodes are of two kinds:

  • Decision nodes. These are internal tree nodes encoding one or more linear tests on the parameters; if all the tests are satisfied, then the solution is the node's true child; otherwise, the solution is the node's false child;
  • Solution nodes. These are leaf nodes in the tree, encoding the solution of the problem in the current context subset, where each variable is defined in terms of a linear expression of the parameters. Solution nodes also optionally embed a set of parameter constraints: if all these constraints are satisfied, the solution is described by the node, otherwise the problem has no solution.

It may happen that a decision node has no false child. This means that there is no solution if at least one of the corresponding constraints is not satisfied. Decision nodes having two or more linear tests on the parameters cannot have a false child. Decision nodes always have a true child.

Both kinds of tree nodes may also contain the definition of extra parameters which are artificially introduced by the solver to enforce an integral solution. Such artificial parameters are defined by the integer division of a linear expression on the parameters by an integer coefficient.

By exploiting the incremental nature of the solver, it is possible to reuse part of the computational work already done when solving variants of a given PIP_Problem: currently, incremental resolution supports the addition of space dimensions, the addition of parameters and the addition of constraints.

Example problem
An example PIP problem can be defined the following:
3*j >= -2*i+8
j <= 4*i - 4
i <= n
j <= m
where i and j are the problem variables and n and m are the problem parameters. This problem can be optimized; the resulting solution tree may be represented as follows:
if 7*n >= 10 then
  if 7*m >= 12 then
    {i = 2 ; j = 2}
  else
    Parameter P = (m) div 2
    if 2*n + 3*m >= 8 then
      {i = -m - P + 4 ; j = m}
    else
      _|_
else
  _|_
The solution tree starts with a decision node depending on the context constraint 7*n >= 10. If this constraint is satisfied by the values assigned to the problem parameters, then the (textually first) then branch is taken, reaching the true child of the root node (which in this case is another decision node); otherwise, the (textually last) else branch is taken, for which there is no corresponding false child.
The $\perp$ notation, also called bottom, denotes the lexicographic minimum of an empty set of solutions, here meaning the corresponding subproblem is unfeasible.
Notice that a tree node may introduce new (non-problem) parameters, as is the case for parameter P in the (textually first) else branch above. These artificial parameters are only meaningful inside the subtree where they are defined and are used to define the parametric values of the problem variables in solution nodes (e.g., the {i,j} vector in the textually third then branch).
Context restriction
The above solution is correct in an unrestricted initial context, meaning all possible values are allowed for the parameters. If we restrict the context with the following parameter inequalities:
m >= n
n >= 5
then the resulting optimizing tree will be a simple solution node:
{i = 2 ; j = 2}
Creating the PIP_Problem object
The PIP_Problem object corresponding to the above example can be created as follows:
Variable i(0);
Variable j(1);
Variable n(2);
Variable m(3);
Variables_Set params(n, m);
Constraint_System cs;
cs.insert(3*j >= -2*i+8);
cs.insert(j <= 4*i - 4);
cs.insert(j <= m);
cs.insert(i <= n);
PIP_Problem pip(cs.space_dimension(), cs.begin(), cs.end(), params);
If you want to restrict the initial context, simply add the parameter constraints the same way as for normal constraints.
cs.insert(m >= n);
cs.insert(n >= 5);
Solving the problem
Once the PIP_Problem object has been created, you can start the resolution of the problem by calling the solve() method:
PIP_Problem_Status status = pip.solve();
where the returned status indicates if the problem has been optimized or if it is unfeasible for any possible configuration of the parameter values. The resolution process is also started if an attempt is made to get its solution, as follows:
const PIP_Tree_Node* node = pip.solution();
In this case, an unfeasible problem will result in an empty solution tree, i.e., assigning a null pointer to node.
Printing the solution tree
A previously computed solution tree may be printed as follows:
pip.print_solution(std::cout);
This will produce the following output (note: variables and parameters are printed according to the default output function; see Variable::set_output_function):
if 7*C >= 10 then
  if 7*D >= 12 then
    {2 ; 2}
  else
    Parameter E = (D) div 2
    if 2*C + 3*D >= 8 then
      {-D - E + 4 ; D}
    else
      _|_
else
  _|_
Spanning the solution tree
A parameter assignment for a PIP problem binds each of the problem parameters to a non-negative integer value. After fixing a parameter assignment, the ``spanning'' of the PIP problem solution tree refers to the process whereby the solution tree is navigated, starting from the root node: the value of artificial parameters is computed according to the parameter assignment and the node's constraints are evaluated, thereby descending in either the true or the false subtree of decision nodes and eventually reaching a solution node or a bottom node. If a solution node is found, each of the problem variables is provided with a parametric expression, which can be evaluated to a fixed value using the given parameter assignment and the computed values for artificial parameters.
The coding of the spanning process can be done as follows. First, the root of the PIP solution tree is retrieved:
const PIP_Tree_Node* node = pip.solution();
If node represents an unfeasible solution (i.e., $\perp$), its value will be 0. For a non-null tree node, the virtual methods PIP_Tree_Node::as_decision() and PIP_Tree_Node::as_solution() can be used to check whether the node is a decision or a solution node:
const PIP_Solution_Node* sol = node->as_solution();
if (sol != 0) {
// The node is a solution node
...
}
else {
// The node is a decision node
const PIP_Decision_Node* dec = node->as_decision();
...
}
The true (resp., false) child node of a Decision Node may be accessed by using method PIP_Decision_Node::child_node(bool), passing true (resp., false) as the input argument.
Artificial parameters
A PIP_Tree_Node::Artificial_Parameter object represents the result of the integer division of a Linear_Expression (on the other parameters, including the previously-defined artificials) by an integer denominator (a Coefficient object). The dimensions of the artificial parameters (if any) in a tree node have consecutive indices starting from dim+1, where the value of dim is computed as follows:
  • for the tree root node, dim is the space dimension of the PIP_Problem;
  • for any other node of the tree, it is recursively obtained by adding the value of dim computed for the parent node to the number of artificial parameters defined in the parent node.
Since the numbering of dimensions for artificial parameters follows the rule above, the addition of new problem variables and/or new problem parameters to an already solved PIP_Problem object (as done when incrementally solving a problem) will result in the systematic renumbering of all the existing artificial parameters.
Node constraints
All kind of tree nodes can contain context constraints. Decision nodes always contain at least one of them. The node's local constraint system can be obtained using method PIP_Tree_Node::constraints. These constraints only involve parameters, including both the problem parameters and the artificial parameters that have been defined in nodes occurring on the path from the root node to the current node. The meaning of these constraints is as follows:
  • On a decision node, if all tests in the constraints are true, then the solution is the true child; otherwise it is the false child.
  • On a solution node, if the (possibly empty) system of constraints evaluates to true for a given parameter assignment, then the solution is described by the node; otherwise the solution is $\perp$ (i.e., the problem is unfeasible for that parameter assignment).
Getting the optimal values for the variables
After spanning the solution tree using the given parameter assignment, if a solution node has been reached, then it is possible to retrieve the parametric expression for each of the problem variables using method PIP_Solution_Node::parametric_values. The retrieved expression will be defined in terms of all the parameters (problem parameters and artificial parameters defined along the path).
Solving maximization problems
You can solve a lexicographic maximization problem by reformulating its constraints using variable substitution. Proceed the following steps:
  • Create a big parameter (see PIP_Problem::set_big_parameter_dimension), which we will call $M$.
  • Reformulate each of the maximization problem constraints by substituting each $x_i$ variable with an expression of the form $M-x'_i$, where the $x'_i$ variables are positive variables to be minimized.
  • Solve the lexicographic minimum for the $x'$ variable vector.
  • In the solution expressions, the values of the $x'$ variables will be expressed in the form: $x'_i = M-x_i$. To get back the value of the expression of each $x_i$ variable, just apply the formula: $x_i = M-x'_i$.
Note that if the resulting expression of one of the $x'_i$ variables is not in the $x'_i = M-x_i$ form, this means that the sign-unrestricted problem is unbounded.
You can choose to maximize only a subset of the variables while minimizing the other variables. In that case, just apply the variable substitution method on the variables you want to be maximized. The variable optimization priority will still be in lexicographic order.
Example: consider you want to find the lexicographic maximum of the $(x,y)$ vector, under the constraints:

\[\left\{\begin{array}{l} y \geq 2x - 4\\ y \leq -x + p \end{array}\right.\]

where $p$ is a parameter.
After variable substitution, the constraints become:

\[\left\{\begin{array}{l} M - y \geq 2M - 2x - 4\\ M - y \leq -M + x + p \end{array}\right.\]

The code for creating the corresponding problem object is the following:
Variable x(0);
Variable y(1);
Variable p(2);
Variable M(3);
Variables_Set params(p, M);
Constraint_System cs;
cs.insert(M - y >= 2*M - 2*x - 4);
cs.insert(M - y <= -M + x + p);
PIP_Problem pip(cs.space_dimension(), cs.begin(), cs.end(), params);
pip.set_big_parameter_dimension(3); // M is the big parameter
Solving the problem provides the following solution:
Parameter E = (C + 1) div 3
{D - E - 1 ; -C + D + E + 1}
Under the notations above, the solution is:

\[ \left\{\begin{array}{l} x' = M - \left\lfloor\frac{p+1}{3}\right\rfloor - 1 \\ y' = M - p + \left\lfloor\frac{p+1}{3}\right\rfloor + 1 \end{array}\right. \]

Performing substitution again provides us with the values of the original variables:

\[ \left\{\begin{array}{l} x = \left\lfloor\frac{p+1}{3}\right\rfloor + 1 \\ y = p - \left\lfloor\frac{p+1}{3}\right\rfloor - 1 \end{array}\right. \]

Allowing variables to be arbitrarily signed
You can deal with arbitrarily signed variables by reformulating the constraints using variable substitution. Proceed the following steps:
  • Create a big parameter (see PIP_Problem::set_big_parameter_dimension), which we will call $M$.
  • Reformulate each of the maximization problem constraints by substituting each $x_i$ variable with an expression of the form $x'_i-M$, where the $x'_i$ variables are positive.
  • Solve the lexicographic minimum for the $x'$ variable vector.
  • The solution expression can be read in the form:
  • In the solution expressions, the values of the $x'$ variables will be expressed in the form: $x'_i = x_i+M$. To get back the value of the expression of each signed $x_i$ variable, just apply the formula: $x_i = x'_i-M$.
Note that if the resulting expression of one of the $x'_i$ variables is not in the $x'_i = x_i+M$ form, this means that the sign-unrestricted problem is unbounded.
You can choose to define only a subset of the variables to be sign-unrestricted. In that case, just apply the variable substitution method on the variables you want to be sign-unrestricted.
Example: consider you want to find the lexicographic minimum of the $(x,y)$ vector, where the $x$ and $y$ variables are sign-unrestricted, under the constraints:

\[\left\{\begin{array}{l} y \geq -2x - 4\\ 2y \leq x + 2p \end{array}\right.\]

where $p$ is a parameter.
After variable substitution, the constraints become:

\[\left\{\begin{array}{l} y' - M \geq -2x' + 2M - 4\\ 2y' - 2M \leq x' - M + 2p \end{array}\right.\]

The code for creating the corresponding problem object is the following:
Variable x(0);
Variable y(1);
Variable p(2);
Variable M(3);
Variables_Set params(p, M);
Constraint_System cs;
cs.insert(y - M >= -2*x + 2*M - 4);
cs.insert(2*y - 2*M <= x - M + 2*p);
PIP_Problem pip(cs.space_dimension(), cs.begin(), cs.end(), params);
pip.set_big_parameter_dimension(3); // M is the big parameter
Solving the problem provides the following solution:
Parameter E = (2*C + 3) div 5
{D - E - 1 ; D + 2*E - 2}
Under the notations above, the solution is:

\[ \left\{\begin{array}{l} x' = M - \left\lfloor\frac{2p+3}{5}\right\rfloor - 1 \\ y' = M + 2\left\lfloor\frac{2p+3}{5}\right\rfloor - 2 \end{array}\right. \]

Performing substitution again provides us with the values of the original variables:

\[ \left\{\begin{array}{l} x = -\left\lfloor\frac{2p+3}{5}\right\rfloor - 1 \\ y = 2\left\lfloor\frac{2p+3}{5}\right\rfloor - 2 \end{array}\right. \]

Allowing parameters to be arbitrarily signed
You can consider a parameter $p$ arbitrarily signed by replacing $p$ with $p^+-p^-$, where both $p^+$ and $p^-$ are positive parameters. To represent a set of arbitrarily signed parameters, replace each parameter $p_i$ with $p^+_i-p^-$, where $-p^-$ is the minimum negative value of all parameters.
Minimizing a linear cost function
Lexicographic solving can be used to find the parametric minimum of a linear cost function.
Suppose the variables are named $x_1, x_2, \dots, x_n$, and the parameters $p_1, p_2, \dots, p_m$. You can minimize a linear cost function $f(x_2, \dots, x_n, p_1, \dots, p_m)$ by simply adding the constraint $x_1 \geq f(x_2, \dots, x_n, p_1, \dots, p_m)$ to the constraint system. As lexicographic minimization ensures $x_1$ is minimized in priority, and because $x_1$ is forced by a constraint to be superior or equal to the cost function, optimal solutions of the problem necessarily ensure that the solution value of $x_1$ is the optimal value of the cost function.

Constructor & Destructor Documentation

Parma_Polyhedra_Library::PIP_Problem::PIP_Problem ( dimension_type  dim = 0)
explicit

Builds a trivial PIP problem.

A trivial PIP problem requires to compute the lexicographic minimum on a vector space under no constraints and with no parameters: due to the implicit non-negativity constraints, the origin of the vector space is an optimal solution.

Parameters
dimThe dimension of the vector space enclosing *this (optional argument with default value $0$).
Exceptions
std::length_errorThrown if dim exceeds max_space_dimension().
template<typename In >
Parma_Polyhedra_Library::PIP_Problem::PIP_Problem ( dimension_type  dim,
In  first,
In  last,
const Variables_Set p_vars 
)

Builds a PIP problem having space dimension dim from the sequence of constraints in the range $[\mathrm{first}, \mathrm{last})$; those dimensions whose indices occur in p_vars are interpreted as parameters.

Parameters
dimThe dimension of the vector space (variables and parameters) enclosing *this.
firstAn input iterator to the start of the sequence of constraints.
lastA past-the-end input iterator to the sequence of constraints.
p_varsThe set of variables' indexes that are interpreted as parameters.
Exceptions
std::length_errorThrown if dim exceeds max_space_dimension().
std::invalid_argumentThrown if the space dimension of a constraint in the sequence (resp., the parameter variables) is strictly greater than dim.

Member Function Documentation

void Parma_Polyhedra_Library::PIP_Problem::clear ( )

Resets *this to be equal to the trivial PIP problem.

The space dimension is reset to $0$.

void Parma_Polyhedra_Library::PIP_Problem::add_space_dimensions_and_embed ( dimension_type  m_vars,
dimension_type  m_params 
)

Adds m_vars + m_params new space dimensions and embeds the old PIP problem in the new vector space.

Parameters
m_varsThe number of space dimensions to add that are interpreted as PIP problem variables (i.e., non parameters). These are added before adding the m_params parameters.
m_paramsThe number of space dimensions to add that are interpreted as PIP problem parameters. These are added after having added the m_vars problem variables.
Exceptions
std::length_errorThrown if adding m_vars + m_params new space dimensions would cause the vector space to exceed dimension max_space_dimension().

The new space dimensions will be those having the highest indexes in the new PIP problem; they are initially unconstrained.

void Parma_Polyhedra_Library::PIP_Problem::add_to_parameter_space_dimensions ( const Variables_Set p_vars)

Sets the space dimensions whose indexes which are in set p_vars to be parameter space dimensions.

Exceptions
std::invalid_argumentThrown if some index in p_vars does not correspond to a space dimension in *this.
void Parma_Polyhedra_Library::PIP_Problem::add_constraint ( const Constraint c)

Adds a copy of constraint c to the PIP problem.

Exceptions
std::invalid_argumentThrown if the space dimension of c is strictly greater than the space dimension of *this.
void Parma_Polyhedra_Library::PIP_Problem::add_constraints ( const Constraint_System cs)

Adds a copy of the constraints in cs to the PIP problem.

Exceptions
std::invalid_argumentThrown if the space dimension of constraint system cs is strictly greater than the space dimension of *this.
bool Parma_Polyhedra_Library::PIP_Problem::is_satisfiable ( ) const

Checks satisfiability of *this.

Returns
true if and only if the PIP problem is satisfiable.
PIP_Problem_Status Parma_Polyhedra_Library::PIP_Problem::solve ( ) const

Optimizes the PIP problem.

Returns
A PIP_Problem_Status flag indicating the outcome of the optimization attempt (unfeasible or optimized problem).
PIP_Tree Parma_Polyhedra_Library::PIP_Problem::solution ( ) const

Returns a feasible solution for *this, if it exists.

A null pointer is returned for an unfeasible PIP problem.

PIP_Tree Parma_Polyhedra_Library::PIP_Problem::optimizing_solution ( ) const

Returns an optimizing solution for *this, if it exists.

A null pointer is returned for an unfeasible PIP problem.

void Parma_Polyhedra_Library::PIP_Problem::print_solution ( std::ostream &  s,
int  indent = 0 
) const

Prints on s the solution computed for *this.

Parameters
sThe output stream.
indentAn indentation parameter (default value 0).
Exceptions
std::logic_errorThrown if trying to print the solution when the PIP problem still has to be solved.
dimension_type Parma_Polyhedra_Library::PIP_Problem::get_big_parameter_dimension ( ) const
inline

Returns the space dimension for the big parameter.

If a big parameter was not set, returns not_a_dimension().

Friends And Related Function Documentation

std::ostream & operator<< ( std::ostream &  s,
const PIP_Problem pip 
)
related

Output operator.

void swap ( PIP_Problem x,
PIP_Problem y 
)
related

Swaps x with y.

void swap ( PIP_Problem x,
PIP_Problem y 
)
related

The documentation for this class was generated from the following file: