# Welcome to pplpy’s documentation!

## Installation

pplpy is available from the Python Package Index. You can hence install it with:

    $ pip install pplpy

The code source can be obtained from github:

    $ git clone https://github.com/sagemath/pplpy.git

## Introduction

Cython wrapper for the Parma Polyhedra Library (PPL)

The Parma Polyhedra Library (PPL) is a library for polyhedral computations over the rationals. This interface tries to reproduce the C++ API as faithfully as possible in Python. For example, the following C++ excerpt:

    Variable x(0);
    Variable y(1);
    Constraint_System cs;
    cs.insert(x >= 0);
    cs.insert(x <= 3);
    cs.insert(y >= 0);
    cs.insert(y <= 3);
    C_Polyhedron poly_from_constraints(cs);

translates into:

    >>> from ppl import Variable, Constraint_System, C_Polyhedron
    >>> x = Variable(0)
    >>> y = Variable(1)
    >>> cs = Constraint_System()
    >>> cs.insert(x >= 0)
    >>> cs.insert(x <= 3)
    >>> cs.insert(y >= 0)
    >>> cs.insert(y <= 3)
    >>> poly_from_constraints = C_Polyhedron(cs)

The same polyhedron constructed from generators:

    >>> from ppl import Variable, Generator_System, C_Polyhedron, point
    >>> gs = Generator_System()
    >>> gs.insert(point(0*x + 0*y))
    >>> gs.insert(point(0*x + 3*y))
    >>> gs.insert(point(3*x + 0*y))
    >>> gs.insert(point(3*x + 3*y))
    >>> poly_from_generators = C_Polyhedron(gs)

Rich comparisons test equality/inequality and strict/non-strict containment:

    >>> poly_from_generators == poly_from_constraints
    True
    >>> poly_from_generators >= poly_from_constraints
    True
    >>> poly_from_generators <  poly_from_constraints
    False
    >>> poly_from_constraints.minimized_generators()
    Generator_System {point(0/1, 0/1), point(0/1, 3/1), point(3/1, 0/1), point(3/1, 3/1)}
    >>> poly_from_constraints.minimized_constraints()
    Constraint_System {-x0+3>=0, -x1+3>=0, x0>=0, x1>=0}

As we see above, the library is generally easy to use. There are a few pitfalls that are not entirely obvious without consulting the documentation, in particular:

- There are no vectors used to describe `Generator` (points, closure points, rays, lines) or `Constraint` (strict inequalities, non-strict inequalities, or equations). Coordinates are always specified via linear polynomials in `Variable`

- All coordinates of rays and lines as well as all coefficients of constraint relations are (arbitrary precision) integers. Only the generators `point()` and `closure_point()` allow one to specify an overall divisor of the otherwise integral coordinates. For example:

      >>> from ppl import Variable, point
      >>> x = Variable(0); y = Variable(1)
      >>> p = point( 2*x+3*y, 5 ); p
      point(2/5, 3/5)
      >>> p.coefficient(x)
      mpz(2)
      >>> p.coefficient(y)
      mpz(3)
      >>> p.divisor()
      mpz(5)

- PPL supports (topologically) closed polyhedra (`C_Polyhedron`) as well as not necessarily closed polyhedra (`NNC_Polyhedron`). Only the latter allows closure points (=points of the closure but not of the actual polyhedron) and strict inequalities (`>` and `<`)

The naming convention for the C++ classes is that they start with `PPL_`, for example, the original `Linear_Expression` becomes `PPL_Linear_Expression`. The Python wrapper has the same name as the original library class, that is, just `Linear_Expression`. In short:

- If you are using the Python wrapper (if in doubt: that’s you), then you use the same names as the PPL C++ class library.

- If you are writing your own Cython code, you can access the underlying C++ classes by adding the prefix `PPL_`.

Finally, PPL is fast. For example, here is the permutahedron of 5 basis vectors:

    >>> from ppl import Variable, Generator_System, point, C_Polyhedron
    >>> basis = range(0,5)
    >>> x = [ Variable(i) for i in basis ]
    >>> gs = Generator_System();
    >>> from itertools import permutations
    >>> for coeff in permutations(basis):
    ...    gs.insert(point( sum( (coeff[i]+1)*x[i] for i in basis ) ))
    >>> C_Polyhedron(gs)
    A 4-dimensional polyhedron in QQ^5 defined as the convex hull of 120 points

DIFFERENCES VS. C++

Since Python and C++ syntax are not always compatible, there are necessarily some differences. The main ones are:

- The `Linear_Expression` also accepts an iterable as input for the homogeneous coefficients.

AUTHORS:

- Volker Braun (2010): initial version (within Sage).

- Risan (2012): extension for MIP_Problem class (within Sage)

- Vincent Delecroix (2016-2020): convert Sage files into a standalone Python package, interface bit_array, interface congruence

- Vincent Klein (2017): improve doctest support and Python 3 compatibility Split the main code into several files. Remove the _mutable_immutable class.

## Using in Cython

All types from ppl are extension types and can be used with Cython. The following is a short sample of Cython code using pplpy:

    from ppl.linear_algebra cimport Variable
    from ppl.constraint cimport Constraint_System
    from ppl.polyhedron cimport C_Polyhedron

    cdef Variable x = ppl.Variable(0)
    cdef Variable y = ppl.Variable(1)
    cdef Variable z = ppl.Variable(2)
    cdef Constraint_System cs = Constraint_System()
    cs.insert(x >= 0)
    cs.insert(y >= 0)
    cs.insert(x + y + z == 1)
    cdef C_Polyhedron poly = C_Polyhedron(cs)
    print(poly.minimized_generators())

Each extension type carries an attribute `thisptr` that holds a pointer to the corresponding C++ object from ppl. Continuing the above example, one can do:

    print('dim = %lu' % poly.thisptr.space_dimension())

To avoid name collisions, the original C++ class names are prefixed with `PPL_`, for example, the original `Linear_Expression` becomes `PPL_Linear_Expression`. The Python wrapper has the same name as the original library class, that is, just `Linear_Expression`. All `ppl` declarations are done in the `.pxd` file `ppl/ppl_decl.pxd`. Only few functionalities from `ppl` are exposed in `ppl_decl.pxd`. It is also preferable to avoid mixing C++ `ppl` objects with Cython `pplpy` extension types.

To compile a Cython extension using pplpy you need to:

- add the path of your pplpy installation to `include_dirs`

- link with the `ppl` library

Here is a minimal example of a `setup.py` file for a unique Cython file called `test.pyx`:

    from distutils.core import setup
    from distutils.extension import Extension
    from Cython.Build import cythonize
    import ppl

    extensions = [
        Extension("test", ["test.pyx"], libraries=['ppl'], include_dirs=ppl.__path__, language='c++')
    ]

    setup(ext_modules=cythonize(extensions))

## Module ppl.constraint

class ppl.constraint.Constraint(arg=None)

  Bases: `object`

  Wrapper for PPL’s `Constraint` class.

  An object of the class `Constraint` is either:

  - an equality \(\sum_{i=0}^{n-1} a_i x_i + b = 0\)

  - a non-strict inequality \(\sum_{i=0}^{n-1} a_i x_i + b \geq 0\)

  - a strict inequality \(\sum_{i=0}^{n-1} a_i x_i + b > 0\)

  where \(n\) is the dimension of the space, \(a_i\) is the integer coefficient of variable \(x_i\), and \(b_i\) is the integer inhomogeneous term.

  INPUT/OUTPUT:

  You construct constraints by writing inequalities in `Linear_Expression`. Do not attempt to manually construct constraints.

  Examples:

      >>> from ppl import Variable, Linear_Expression
      >>> x = Variable(0)
      >>> y = Variable(1)
      >>> 5*x-2*y >  x+y-1
      4*x0-3*x1+1>0
      >>> 5*x-2*y >= x+y-1
      4*x0-3*x1+1>=0
      >>> 5*x-2*y == x+y-1
      4*x0-3*x1+1==0
      >>> 5*x-2*y <= x+y-1
      -4*x0+3*x1-1>=0
      >>> 5*x-2*y <  x+y-1
      -4*x0+3*x1-1>0
      >>> x > 0
      x0>0

  Special care is needed if the left hand side is a constant:

      >>> 0 == 1    # watch out!
      False
      >>> Linear_Expression(0) == 1
      mpz(-1)==0

  ascii_dump(self)

    Write an ASCII dump to stderr.

    Examples:

        >>> cmd  = 'from ppl import Linear_Expression, Variable\n'
        >>> cmd += 'x = Variable(0)\n'
        >>> cmd += 'y = Variable(1)\n'
        >>> cmd += 'e = (3*x+2*y+1 > 0)\n'
        >>> cmd += 'e.ascii_dump()\n'
        >>> import subprocess, sys
        >>> proc = subprocess.Popen([sys.executable, '-c', cmd], stderr=subprocess.PIPE)
        >>> out, err = proc.communicate()
        >>> print(str(err.decode('ascii')))
        size 4 1 3 2 -1 ...

  coefficient(self, Variable v)

    Return the coefficient of the variable `v`.

    INPUT:

    - `v` – a `Variable`.

    OUTPUT:

    An integer.

    Examples:

        >>> from ppl import Variable
        >>> x = Variable(0)
        >>> ineq = 3*x+1 > 0
        >>> ineq.coefficient(x)
        mpz(3)
        >>> y = Variable(1)
        >>> ineq = 3**50 * y + 2 > 1
        >>> str(ineq.coefficient(y))
        '717897987691852588770249'
        >>> ineq.coefficient(x)
        mpz(0)

  coefficients(self)

    Return the coefficients of the constraint.

    See also `coefficient()`.

    OUTPUT:

    A tuple of integers of length `space_dimension()`.

    Examples:

        >>> from ppl import Variable
        >>> x = Variable(0);  y = Variable(1)
        >>> ineq = ( 3*x+5*y+1 ==  2);  ineq
        3*x0+5*x1-1==0
        >>> ineq.coefficients()
        (mpz(3), mpz(5))

  inhomogeneous_term(self)

    Return the inhomogeneous term of the constraint.

    OUTPUT:

    Integer.

    Examples:

        >>> from ppl import Variable
        >>> y = Variable(1)
        >>> ineq = 10+y > 9
        >>> ineq
        x1+1>0
        >>> ineq.inhomogeneous_term()
        mpz(1)
        >>> ineq = 2**66 + y > 0
        >>> str(ineq.inhomogeneous_term())
        '73786976294838206464'

  is_equality(self)

    Test whether `self` is an equality.

    OUTPUT:

    Boolean. Returns `True` if and only if `self` is an equality constraint.

    Examples:

        >>> from ppl import Variable
        >>> x = Variable(0)
        >>> (x==0).is_equality()
        True
        >>> (x>=0).is_equality()
        False
        >>> (x>0).is_equality()
        False

  is_equivalent_to(self, Constraint c)

    Test whether `self` and `c` are equivalent.

    INPUT:

    - `c` – a `Constraint`.

    OUTPUT:

    Boolean. Returns `True` if and only if `self` and `c` are equivalent constraints.

    Note that constraints having different space dimensions are not equivalent. However, constraints having different types may nonetheless be equivalent, if they both are tautologies or inconsistent.

    Examples:

        >>> from ppl import Variable, Linear_Expression
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> (x > 0).is_equivalent_to(Linear_Expression(0) < x)
        True
        >>> (x > 0).is_equivalent_to(0*y < x)
        False
        >>> (0*x > 1).is_equivalent_to(0*x == -2)
        True

  is_inconsistent(self)

    Test whether `self` is an inconsistent constraint, that is, always false.

    An inconsistent constraint can have either one of the following forms:

    - an equality: \(\sum 0 x_i + b = 0\) with bnot=0,

    - a non-strict inequality: \(\sum 0 x_i + b \geq 0\) with b< 0, or

    - a strict inequality: \(\sum 0 x_i + b > 0\) with bleq 0.

    OUTPUT:

    Boolean. Returns `True` if and only if `self` is an inconsistent constraint.

    Examples:

        >>> from ppl import Variable
        >>> x = Variable(0)
        >>> (x==1).is_inconsistent()
        False
        >>> (0*x>=1).is_inconsistent()
        True

  is_inequality(self)

    Test whether `self` is an inequality.

    OUTPUT:

    Boolean. Returns `True` if and only if `self` is an inequality constraint, either strict or non-strict.

    Examples:

        >>> from ppl import Variable
        >>> x = Variable(0)
        >>> (x==0).is_inequality()
        False
        >>> (x>=0).is_inequality()
        True
        >>> (x>0).is_inequality()
        True

  is_nonstrict_inequality(self)

    Test whether `self` is a non-strict inequality.

    OUTPUT:

    Boolean. Returns `True` if and only if `self` is an non-strict inequality constraint.

    Examples:

        >>> from ppl import Variable
        >>> x = Variable(0)
        >>> (x==0).is_nonstrict_inequality()
        False
        >>> (x>=0).is_nonstrict_inequality()
        True
        >>> (x>0).is_nonstrict_inequality()
        False

  is_strict_inequality(self)

    Test whether `self` is a strict inequality.

    OUTPUT:

    Boolean. Returns `True` if and only if `self` is an strict inequality constraint.

    Examples:

        >>> from ppl import Variable
        >>> x = Variable(0)
        >>> (x==0).is_strict_inequality()
        False
        >>> (x>=0).is_strict_inequality()
        False
        >>> (x>0).is_strict_inequality()
        True

  is_tautological(self)

    Test whether `self` is a tautological constraint.

    A tautology can have either one of the following forms:

    - an equality: \(\sum 0 x_i + 0 = 0\),

    - a non-strict inequality: \(\sum 0 x_i + b \geq 0\) with bgeq 0, or

    - a strict inequality: \(\sum 0 x_i + b > 0\) with b> 0.

    OUTPUT:

    Boolean. Returns `True` if and only if `self` is a tautological constraint.

    Examples:

        >>> from ppl import Variable
        >>> x = Variable(0)
        >>> (x==0).is_tautological()
        False
        >>> (0*x>=0).is_tautological()
        True

  permute_space_dimensions(self, cycle)

    Permute the coordinates according to `cycle`.

        >>> from ppl import Variable
        >>> x = Variable(0); y = Variable(1); z = Variable(2)
        >>> l = 2*x - y + 3*z
        >>> ieq = l >= 5
        >>> ieq.permute_space_dimensions([2, 1, 0])
        >>> ieq
        -x0+3*x1+2*x2-5>=0

  space_dimension(self)

    Return the dimension of the vector space enclosing `self`.

    OUTPUT:

    Integer.

    Examples:

        >>> from ppl import Variable
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> (x>=0).space_dimension()
        1
        >>> (y==1).space_dimension()
        2

  type(self)

    Return the constraint type of `self`.

    OUTPUT:

    String. One of `'equality'`, `'nonstrict_inequality'`, or `'strict_inequality'`.

    Examples:

        >>> from ppl import Variable
        >>> x = Variable(0)
        >>> (x==0).type()
        'equality'
        >>> (x>=0).type()
        'nonstrict_inequality'
        >>> (x>0).type()
        'strict_inequality'

class ppl.constraint.Constraint_System(arg=None)

  Bases: `object`

  Wrapper for PPL’s `Constraint_System` class.

  An object of the class Constraint_System is a system of constraints, i.e., a multiset of objects of the class Constraint. When inserting constraints in a system, space dimensions are automatically adjusted so that all the constraints in the system are defined on the same vector space.

  Examples:

      >>> from ppl import Constraint_System, Variable
      >>> x = Variable(0)
      >>> y = Variable(1)
      >>> cs = Constraint_System( 5*x-2*y > 0 )
      >>> cs.insert( 6*x < 3*y )
      >>> cs.insert( x >= 2*x-7*y )
      >>> cs
      Constraint_System {5*x0-2*x1>0, ...}
      >>> cs[0]
      5*x0-2*x1>0

  ascii_dump(self)

    Write an ASCII dump to stderr.

    Examples:

        >>> cmd  = 'from ppl import Constraint_System, Variable\n'
        >>> cmd += 'x = Variable(0)\n'
        >>> cmd += 'y = Variable(1)\n'
        >>> cmd += 'cs = Constraint_System( 3*x > 2*y+1 )\n'
        >>> cmd += 'cs.ascii_dump()\n'
        >>> import subprocess, sys
        >>> proc = subprocess.Popen([sys.executable, '-c', cmd], stderr=subprocess.PIPE)
        >>> out, err = proc.communicate()
        >>> print(str(err.decode('ascii')))
        topology NOT_NECESSARILY_CLOSED
        ...

  clear(self)

    Removes all constraints from the constraint system and sets its space dimension to 0.

    Examples:

        >>> from ppl import Variable, Constraint_System
        >>> x = Variable(0)
        >>> cs = Constraint_System(x>0)
        >>> cs
        Constraint_System {x0>0}
        >>> cs.clear()
        >>> cs
        Constraint_System {}

  empty(self)

    Return `True` if and only if `self` has no constraints.

    OUTPUT:

    Boolean.

    Examples:

        >>> from ppl import Variable, Constraint_System
        >>> x = Variable(0)
        >>> cs = Constraint_System()
        >>> cs.empty()
        True
        >>> cs.insert( x>0 )
        >>> cs.empty()
        False

  has_equalities(self)

    Tests whether `self` contains one or more equality constraints.

    OUTPUT:

    Boolean.

    Examples:

        >>> from ppl import Variable, Constraint_System
        >>> x = Variable(0)
        >>> cs = Constraint_System()
        >>> cs.insert( x>0 )
        >>> cs.insert( x<0 )
        >>> cs.has_equalities()
        False
        >>> cs.insert( x==0 )
        >>> cs.has_equalities()
        True

  has_strict_inequalities(self)

    Tests whether `self` contains one or more strict inequality constraints.

    OUTPUT:

    Boolean.

    Examples:

        >>> from ppl import Variable, Constraint_System
        >>> x = Variable(0)
        >>> cs = Constraint_System()
        >>> cs.insert( x>=0 )
        >>> cs.insert( x==-1 )
        >>> cs.has_strict_inequalities()
        False
        >>> cs.insert( x>0 )
        >>> cs.has_strict_inequalities()
        True

  insert(self, Constraint c)

    Insert `c` into the constraint system.

    INPUT:

    - `c` – a `Constraint`.

    Examples:

        >>> from ppl import Variable, Constraint_System
        >>> x = Variable(0)
        >>> cs = Constraint_System()
        >>> cs.insert( x>0 )
        >>> cs
        Constraint_System {x0>0}

  space_dimension(self)

    Return the dimension of the vector space enclosing `self`.

    OUTPUT:

    Integer.

    Examples:

        >>> from ppl import Variable, Constraint_System
        >>> x = Variable(0)
        >>> cs = Constraint_System( x>0 )
        >>> cs.space_dimension()
        1

class ppl.constraint.Poly_Con_Relation

  Bases: `object`

  Wrapper for PPL’s `Poly_Con_Relation` class.

  INPUT/OUTPUT:

  You must not construct `Poly_Con_Relation` objects manually. You will usually get them from `relation_with()`. You can also get pre-defined relations from the class methods `nothing()`, `is_disjoint()`, `strictly_intersects()`, `is_included()`, and `saturates()`.

  Examples:

      >>> from ppl import Poly_Con_Relation
      >>> saturates     = Poly_Con_Relation.saturates();  saturates
      saturates
      >>> is_included   = Poly_Con_Relation.is_included(); is_included
      is_included
      >>> is_included.implies(saturates)
      False
      >>> saturates.implies(is_included)
      False
      >>> rels = []
      >>> rels.append(Poly_Con_Relation.nothing())
      >>> rels.append(Poly_Con_Relation.is_disjoint())
      >>> rels.append(Poly_Con_Relation.strictly_intersects())
      >>> rels.append(Poly_Con_Relation.is_included())
      >>> rels.append(Poly_Con_Relation.saturates())
      >>> rels
      [nothing, is_disjoint, strictly_intersects, is_included, saturates]
      >>> for i, rel_i in enumerate(rels):
      ...       s = ""
      ...       for j, rel_j in enumerate(rels):
      ...           s=s+str(int(rel_i.implies(rel_j))) + ' '
      ...       print(" ".join(str(int(rel_i.implies(rel_j))) for j, rel_j in enumerate(rels)))
      1 0 0 0 0
      1 1 0 0 0
      1 0 1 0 0
      1 0 0 1 0
      1 0 0 0 1

  ascii_dump(self)

    Write an ASCII dump to stderr.

    Examples:

        >>> cmd  = 'from ppl import Poly_Con_Relation\n'
        >>> cmd += 'Poly_Con_Relation.nothing().ascii_dump()\n'
        >>> import subprocess, sys
        >>> proc = subprocess.Popen([sys.executable, '-c', cmd], stderr=subprocess.PIPE)
        >>> out, err = proc.communicate()
        >>> print(str(err.decode('ascii')))
        NOTHING

  implies(self, Poly_Con_Relation y)

    Test whether `self` implies `y`.

    INPUT:

    - `y` – a `Poly_Con_Relation`.

    OUTPUT:

    Boolean. `True` if and only if `self` implies `y`.

    Examples:

        >>> from ppl import Poly_Con_Relation
        >>> nothing = Poly_Con_Relation.nothing()
        >>> nothing.implies( nothing )
        True

  classmethod is_disjoint(cls)

    Return the assertion “The polyhedron and the set of points satisfying the constraint are disjoint”.

    OUTPUT:

    A `Poly_Con_Relation`.

    Examples:

        >>> from ppl import Poly_Con_Relation
        >>> Poly_Con_Relation.is_disjoint()
        is_disjoint

  classmethod is_included(cls)

    Return the assertion “The polyhedron is included in the set of points satisfying the constraint”.

    OUTPUT:

    A `Poly_Con_Relation`.

    Examples:

        >>> from ppl import Poly_Con_Relation
        >>> Poly_Con_Relation.is_included()
        is_included

  classmethod nothing(cls)

    Return the assertion that says nothing.

    OUTPUT:

    A `Poly_Con_Relation`.

    Examples:

        >>> from ppl import Poly_Con_Relation
        >>> Poly_Con_Relation.nothing()
        nothing

  classmethod saturates(cls)

    Return the assertion “”.

    OUTPUT:

    A `Poly_Con_Relation`.

    Examples:

        >>> from ppl import Poly_Con_Relation
        >>> Poly_Con_Relation.saturates()
        saturates

  classmethod strictly_intersects(cls)

    Return the assertion “The polyhedron intersects the set of points satisfying the constraint, but it is not included in it”.

    Returns:

      a `Poly_Con_Relation`.

    Examples:

        >>> from ppl import Poly_Con_Relation
        >>> Poly_Con_Relation.strictly_intersects()
        strictly_intersects

ppl.constraint.equation(expression)

  Construct an equation.

  INPUT:

  - `expression` – a `Linear_Expression`.

  OUTPUT:

  The equation `expression` == 0.

  Examples:

      >>> from ppl import Variable, equation
      >>> y = Variable(1)
      >>> 2*y+1 == 0
      2*x1+1==0
      >>> equation(2*y+1)
      2*x1+1==0

ppl.constraint.inequality(expression)

  Construct an inequality.

  INPUT:

  - `expression` – a `Linear_Expression`.

  OUTPUT:

  The inequality `expression` >= 0.

  Examples:

      >>> from ppl import Variable, inequality
      >>> y = Variable(1)
      >>> 2*y+1 >= 0
      2*x1+1>=0
      >>> inequality(2*y+1)
      2*x1+1>=0

ppl.constraint.strict_inequality(expression)

  Construct a strict inequality.

  INPUT:

  - `expression` – a `Linear_Expression`.

  OUTPUT:

  The inequality `expression` > 0.

  Examples:

      >>> from ppl import Variable, strict_inequality
      >>> y = Variable(1)
      >>> 2*y+1 > 0
      2*x1+1>0
      >>> strict_inequality(2*y+1)
      2*x1+1>0

## Module ppl.linear_algebra

class ppl.linear_algebra.Linear_Expression(*args)

  Bases: `object`

  Wrapper for PPL’s `PPL_Linear_Expression` class.

  INPUT:

  The constructor accepts zero, one, or two arguments.

  If there are two arguments `Linear_Expression(a,b)`, they are interpreted as

  - `a` – either a dictionary whose indices are space dimension and values are coefficients or an iterable coefficients (e.g. a list or tuple).

  - `b` – an integer. The inhomogeneous term.

  A single argument `Linear_Expression(arg)` is interpreted as

  - `arg` – something that determines a linear expression. Possibilities are:

    - a `Variable`: The linear expression given by that variable.

    - a `Linear_Expression`: The copy constructor.

    - an integer: Constructs the constant linear expression.

  No argument is the default constructor and returns the zero linear expression.

  OUTPUT:

  A `Linear_Expression`

  Examples:

      >>> from ppl import Variable, Linear_Expression

      >>> Linear_Expression({1: -3, 7: 1}, 0)
      -3*x1+x7
      >>> Linear_Expression([1, 2, 3, 4], 5)
      x0+2*x1+3*x2+4*x3+5
      >>> Linear_Expression(10)
      10
      >>> Linear_Expression()
      0
      >>> Linear_Expression({}, 2)
      2
      >>> Linear_Expression([], 2)
      2

      >>> Linear_Expression(10).inhomogeneous_term()
      mpz(10)
      >>> x = Variable(123)
      >>> expr = x+1
      >>> expr
      x123+1
      >>> expr.coefficient(x)
      mpz(1)
      >>> expr.coefficient(Variable(124))
      mpz(0)

      >>> from gmpy2 import mpz, mpq
      >>> Linear_Expression(mpz(3))
      3
      >>> Linear_Expression([mpz(5), mpz(2)], mpz(-2))
      5*x0+2*x1-2

  String, rationals and floating point types are accepted as long as they represent exact integers:

      >>> Linear_Expression(('4', 1), 2)
      4*x0+x1+2
      >>> Linear_Expression((4, 1.0, mpq('4/2')), 2.0)
      4*x0+x1+2*x2+2
      >>> Linear_Expression(('1.5',), 0)
      Traceback (most recent call last):
      ...
      ValueError: invalid digits
      >>> Linear_Expression((mpq('3/2'),), 0)
      Traceback (most recent call last):
      ...
      TypeError: ppl coefficients must be integral
      >>> Linear_Expression((1, 2.1, 1), 1)
      Traceback (most recent call last):
      ...
      TypeError: ppl coefficients must be integral
      >>> Linear_Expression(mpq('1/2'))
      Traceback (most recent call last):
      ...
      TypeError: ppl coefficients must be integral
      >>> Linear_Expression('I am a linear expression')
      Traceback (most recent call last):
      ...
      ValueError: invalid digits
      >>> Linear_Expression(('I','am','a','linear','expression'))
      Traceback (most recent call last):
      ...
      TypeError: mpz() requires numeric or string argument

  all_homogeneous_terms_are_zero(self)

    Test if `self` is a constant linear expression.

    OUTPUT:

    Boolean.

    Examples:

        >>> from ppl import Variable, Linear_Expression
        >>> Linear_Expression(10).all_homogeneous_terms_are_zero()
        True

  ascii_dump(self)

    Write an ASCII dump to stderr.

    Examples:

        >>> cmd  = 'from ppl import Linear_Expression, Variable\n'
        >>> cmd += 'x = Variable(0)\n'
        >>> cmd += 'y = Variable(1)\n'
        >>> cmd += 'e = 3*x+2*y+1\n'
        >>> cmd += 'e.ascii_dump()\n'
        >>> from subprocess import Popen, PIPE
        >>> import sys
        >>> proc = Popen([sys.executable, '-c', cmd], stdout=PIPE, stderr=PIPE)
        >>> out, err = proc.communicate()
        >>> len(out) == 0
        True
        >>> len(err) > 0
        True

  coefficient(self, v)

    Return the coefficient of the variable `v`.

    INPUT:

    - `v` – a `Variable`.

    OUTPUT:

    A gmpy2 integer.

    Examples:

        >>> from ppl import Variable
        >>> x = Variable(0)
        >>> e = 3*x+1
        >>> e.coefficient(x)
        mpz(3)
        >>> e.coefficient(Variable(13))
        mpz(0)

  coefficients(self)

    Return the coefficients of the linear expression.

    OUTPUT:

    A tuple of gmpy2 integers of length `space_dimension()`.

    Examples:

        >>> from ppl import Variable
        >>> x = Variable(0);  y = Variable(1)
        >>> e = 3*x+5*y+1
        >>> e.coefficients()
        (mpz(3), mpz(5))

  inhomogeneous_term(self)

    Return the inhomogeneous term of the linear expression.

    OUTPUT:

    Integer.

    Examples:

        >>> from ppl import Linear_Expression
        >>> Linear_Expression(10).inhomogeneous_term()
        mpz(10)

  is_equal_to(self, Linear_Expression other)

    Test equality with another linear expression.

    OUTPUT: boolean

    Examples:

        >>> from ppl import Variable
        >>> L1 = Variable(0) + 2 * Variable(3)
        >>> L2 = Variable(0) + 2 * Variable(3)
        >>> L3 = Variable(0) - Variable(2)
        >>> L1.is_equal_to(L2)
        True
        >>> L1.is_equal_to(L3)
        False

  is_zero(self)

    Test if `self` is the zero linear expression.

    OUTPUT:

    Boolean.

    Examples:

        >>> from ppl import Variable, Linear_Expression
        >>> Linear_Expression(0).is_zero()
        True
        >>> Linear_Expression(10).is_zero()
        False

  permute_space_dimensions(self, cycle)

    Permute the coordinates according to `cycle`.

    Examples:

        >>> from ppl import Variable
        >>> x = Variable(0); y = Variable(1); z = Variable(2)
        >>> l = 2*x - y + 3*z
        >>> l.permute_space_dimensions([0, 2])
        >>> l
        3*x0-x1+2*x2
        >>> l.permute_space_dimensions([1, 0, 2])
        >>> l
        -x0+2*x1+3*x2

  remove_space_dimensions(self, Variables_Set V)

    Removes the dimension specified by the set of variables `V`.

    See `Variables_Set` to construct set of variables.

    Examples:

        >>> import ppl
        >>> L = sum(i * ppl.Variable(i) for i in range(10))
        >>> L
        x1+2*x2+3*x3+4*x4+5*x5+6*x6+7*x7+8*x8+9*x9
        >>> L.remove_space_dimensions(ppl.Variables_Set(3,5))
        >>> L
        x1+2*x2+6*x3+7*x4+8*x5+9*x6

  set_coefficient(self, i, v)

    Set the `i`-th coefficient to `v`.

    INPUT:

    - `i` - variable or variable index

    - `v` - integer

    Examples:

        >>> from ppl import Variable
        >>> L = Variable(0) + 3 * Variable(1)
        >>> L.set_coefficient(1, -5)
        >>> L.set_coefficient(7, 3)
        >>> L
        x0-5*x1

  set_inhomogeneous_term(self, v)

    Set the inhomogeneous term of this linear expression.

    Examples:

        >>> from ppl import Linear_Expression
        >>> L = Linear_Expression()
        >>> L.set_inhomogeneous_term(-1313958534578713747)
        >>> L.inhomogeneous_term()
        mpz(-1313958534578713747)

  set_space_dimension(self, PPL_dimension_type n)

    Set the dimension of the ambient space to `n`

    Examples:

        >>> import ppl
        >>> L = ppl.Variable(0) + ppl.Variable(3)
        >>> L.space_dimension()
        4
        >>> L.set_space_dimension(6)
        >>> L.space_dimension()
        6

        >>> L = ppl.Variable(5) - ppl.Variable(2)
        >>> L.set_space_dimension(3)
        >>> L
        -x2

  shift_space_dimensions(self, v, PPL_dimension_type n)

    Shift by `n` the coefficients of variables starting from the coefficient of `v`.

    This increases the space dimension by `n`.

    Examples:

        >>> import ppl
        >>> L = ppl.Variable(0) + 13 * ppl.Variable(2) + 5 * ppl.Variable(7)
        >>> L
        x0+13*x2+5*x7
        >>> L.shift_space_dimensions(ppl.Variable(2), 2)
        >>> L
        x0+13*x4+5*x9
        >>> L.shift_space_dimensions(ppl.Variable(7), 3)
        >>> L
        x0+13*x4+5*x12

  space_dimension(self)

    Return the dimension of the vector space necessary for the linear expression.

    OUTPUT:

    Integer.

    Examples:

        >>> from ppl import Variable
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> (x+y+1).space_dimension()
        2
        >>> (x+y).space_dimension()
        2
        >>> (y+1).space_dimension()
        2
        >>> (x+1).space_dimension()
        1
        >>> (y+1-y).space_dimension()
        2

  swap_space_dimensions(self, v1, v2)

    Swaps the coefficients of `v1` and `v2`.

    INPUT:

    - `v1`, `v2` - variables or indices of variables

    Examples:

        >>> import ppl
        >>> L = ppl.Variable(1) - 3 * ppl.Variable(3)
        >>> L.swap_space_dimensions(ppl.Variable(1), ppl.Variable(3))
        >>> L
        -3*x1+x3

        >>> L = ppl.Variable(1) - 3 * ppl.Variable(3)
        >>> L.swap_space_dimensions(1, 3)
        >>> L
        -3*x1+x3

class ppl.linear_algebra.Variable

  Bases: `object`

  Wrapper for PPL’s `Variable` class.

  A dimension of the vector space.

  An object of the class Variable represents a dimension of the space, that is one of the Cartesian axes. Variables are used as basic blocks in order to build more complex linear expressions. Each variable is identified by a non-negative integer, representing the index of the corresponding Cartesian axis (the first axis has index 0). The space dimension of a variable is the dimension of the vector space made by all the Cartesian axes having an index less than or equal to that of the considered variable; thus, if a variable has index i, its space dimension is i+1.

  INPUT:

  - `i` – integer. The index of the axis.

  OUTPUT:

  A `Variable`

  Examples:

      >>> from ppl import Variable
      >>> x = Variable(123)
      >>> x.id()
      123
      >>> x
      x123

  Note that the “meaning” of an object of the class Variable is completely specified by the integer index provided to its constructor: be careful not to be mislead by C++ language variable names. For instance, in the following example the linear expressions `e1` and `e2` are equivalent, since the two variables `x` and `z` denote the same Cartesian axis:

      >>> x = Variable(0)
      >>> y = Variable(1)
      >>> z = Variable(0)
      >>> e1 = x + y; e1
      x0+x1
      >>> e2 = y + z; e2
      x0+x1
      >>> e1 - e2
      0

  id(self)

    Return the index of the Cartesian axis associated to the variable.

    Examples:

        >>> from ppl import Variable
        >>> x = Variable(123)
        >>> x.id()
        123

  space_dimension(self)

    Return the dimension of the vector space enclosing `self`.

    OUTPUT:

    Integer. The returned value is `self.id()+1`.

    Examples:

        >>> from ppl import Variable
        >>> x = Variable(0)
        >>> x.space_dimension()
        1

class ppl.linear_algebra.Variables_Set

  Bases: `object`

  Wrapper for PPL’s `Variables_Set` class.

  A set of variables’ indexes.

  Examples:

  Build the empty set of variable indexes:

      >>> from ppl import Variable, Variables_Set
      >>> Variables_Set()
      Variables_Set of cardinality 0

  Build the singleton set of indexes containing the index of the variable:

      >>> v123 = Variable(123)
      >>> Variables_Set(v123)
      Variables_Set of cardinality 1

  Build the set of variables’ indexes in the range from one variable to another variable:

      >>> v127 = Variable(127)
      >>> Variables_Set(v123,v127)
      Variables_Set of cardinality 5

  You can alternatively use integers:

      >>> Variables_Set(23)
      Variables_Set of cardinality 1
      >>> Variables_Set(10, 14)
      Variables_Set of cardinality 5

  ascii_dump(self)

    Write an ASCII dump to stderr.

    TODO: rewrite examples EXAMPLES:

        >>> cmd  = 'from ppl import Variable, Variables_Set\n'
        >>> cmd += 'v123 = Variable(123)\n'
        >>> cmd += 'S = Variables_Set(v123)\n'
        >>> cmd += 'S.ascii_dump()\n'
        >>> import subprocess
        >>> import sys
        >>> proc = subprocess.Popen([sys.executable, '-c', cmd], stderr=subprocess.PIPE)
        >>> out, err = proc.communicate()
        >>> print(str(err.decode('ascii')))

        variables( 1 )
        123

  insert(self, Variable v)

    Inserts the index of variable v into the set.

    EXAMPLES:

        >>> from ppl import Variable, Variables_Set
        >>> S = Variables_Set()
        >>> v123 = Variable(123)
        >>> S.insert(v123)
        >>> S.space_dimension()
        124

  space_dimension(self)

    Returns the dimension of the smallest vector space enclosing all the variables whose indexes are in the set.

    OUTPUT:

    Integer.

    EXAMPLES:

        >>> from ppl import Variable, Variables_Set
        >>> v123 = Variable(123)
        >>> S = Variables_Set(v123)
        >>> S.space_dimension()
        124

## Module ppl.generator

class ppl.generator.Generator

  Bases: `object`

  Wrapper for PPL’s `Generator` class.

  An object of the class Generator is one of the following:

  > - a line \(\ell = (a_0, \dots, a_{n-1})^T\)

  > - a ray \(r = (a_0, \dots, a_{n-1})^T\)

  > - a point \(p = (\tfrac{a_0}{d}, \dots, \tfrac{a_{n-1}}{d})^T\)

  > - a closure point \(c = (\tfrac{a_0}{d}, \dots, \tfrac{a_{n-1}}{d})^T\)

  where \(n\) is the dimension of the space and, for points and closure points, \(d\) is the divisor.

  INPUT/OUTPUT:

  Use the helper functions `line()`, `ray()`, `point()`, and `closure_point()` to construct generators. Analogous class methods are also available, see `Generator.line()`, `Generator.ray()`, `Generator.point()`, `Generator.closure_point()`. Do not attempt to construct generators manually.

  Note

  The generators are constructed from linear expressions. The inhomogeneous term is always silently discarded.

  Examples:

      >>> from ppl import Generator, Variable
      >>> x = Variable(0)
      >>> y = Variable(1)
      >>> Generator.line(5*x-2*y)
      line(5, -2)
      >>> Generator.ray(5*x-2*y)
      ray(5, -2)
      >>> Generator.point(5*x-2*y, 7)
      point(5/7, -2/7)
      >>> Generator.closure_point(5*x-2*y, 7)
      closure_point(5/7, -2/7)

  ascii_dump(self)

    Write an ASCII dump to stderr.

    Examples:

        >>> cmd  = 'from ppl import Linear_Expression, Variable, point\n'
        >>> cmd += 'x = Variable(0)\n'
        >>> cmd += 'y = Variable(1)\n'
        >>> cmd += 'p = point(3*x+2*y)\n'
        >>> cmd += 'p.ascii_dump()\n'
        >>> import subprocess
        >>> import sys
        >>> proc = subprocess.Popen([sys.executable, '-c', cmd], stderr=subprocess.PIPE)
        >>> out, err = proc.communicate()
        >>> print(str(err.decode('ascii')))
        size 3 1 3 2 ...

  classmethod closure_point(cls, expression=0, divisor=1)

    Construct a closure point.

    A closure point is a point of the topological closure of a polyhedron that is not a point of the polyhedron itself.

    INPUT:

    - `expression` – a `Linear_Expression` or something convertible to it (`Variable` or integer).

    - `divisor` – an integer.

    OUTPUT:

    A new `Generator` representing the point.

    Raises a `ValueError` if ``divisor==0`.

    Examples:

        >>> from ppl import Generator, Variable
        >>> y = Variable(1)
        >>> Generator.closure_point(2*y+7, 3)
        closure_point(0/3, 2/3)
        >>> Generator.closure_point(y+7, 3)
        closure_point(0/3, 1/3)
        >>> Generator.closure_point(7, 3)
        closure_point()
        >>> Generator.closure_point(0, 0)
        Traceback (most recent call last):
        ...
        ValueError: PPL::closure_point(e, d):
        d == 0.

  coefficient(self, Variable v)

    Return the coefficient of the variable `v`.

    INPUT:

    - `v` – a `Variable`.

    OUTPUT:

    An integer.

    Examples:

        >>> from ppl import Variable, line
        >>> x = Variable(0)
        >>> line = line(3*x+1)
        >>> line
        line(1)
        >>> line.coefficient(x)
        mpz(1)

  coefficients(self)

    Return the coefficients of the generator.

    See also `coefficient()`.

    OUTPUT:

    A tuple of integers of length `space_dimension()`.

    Examples:

        >>> from ppl import Variable, point
        >>> x = Variable(0);  y = Variable(1)
        >>> p = point(3*x+5*y+1, 2); p
        point(3/2, 5/2)
        >>> p.coefficients()
        (mpz(3), mpz(5))

  divisor(self)

    If `self` is either a point or a closure point, return its divisor.

    OUTPUT:

    An integer. If `self` is a ray or a line, raises `ValueError`.

    Examples:

        >>> from ppl import Generator, Variable
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> point = Generator.point(2*x-y+5)
        >>> point.divisor()
        mpz(1)
        >>> line = Generator.line(2*x-y+5)
        >>> line.divisor()
        Traceback (most recent call last):
        ...
        ValueError: PPL::Generator::divisor():
        *this is neither a point nor a closure point.

  is_closure_point(self)

    Test whether `self` is a closure point.

    OUTPUT:

    Boolean.

    Examples:

        >>> from ppl import Variable, point, closure_point, ray, line
        >>> x = Variable(0)
        >>> line(x).is_closure_point()
        False
        >>> ray(x).is_closure_point()
        False
        >>> point(x,2).is_closure_point()
        False
        >>> closure_point(x,2).is_closure_point()
        True

  is_equivalent_to(self, Generator g)

    Test whether `self` and `g` are equivalent.

    INPUT:

    - `g` – a `Generator`.

    OUTPUT:

    Boolean. Returns `True` if and only if `self` and `g` are equivalent generators.

    Note that generators having different space dimensions are not equivalent.

    Examples:

        >>> from ppl import Variable, point, line
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> point(2*x    , 2).is_equivalent_to( point(x) )
        True
        >>> point(2*x+0*y, 2).is_equivalent_to( point(x) )
        False
        >>> line(4*x).is_equivalent_to(line(x))
        True

  is_line(self)

    Test whether `self` is a line.

    OUTPUT:

    Boolean.

    Examples:

        >>> from ppl import Variable, point, closure_point, ray, line
        >>> x = Variable(0)
        >>> line(x).is_line()
        True
        >>> ray(x).is_line()
        False
        >>> point(x,2).is_line()
        False
        >>> closure_point(x,2).is_line()
        False

  is_line_or_ray(self)

    Test whether `self` is a line or a ray.

    OUTPUT:

    Boolean.

    Examples:

        >>> from ppl import Variable, point, closure_point, ray, line
        >>> x = Variable(0)
        >>> line(x).is_line_or_ray()
        True
        >>> ray(x).is_line_or_ray()
        True
        >>> point(x,2).is_line_or_ray()
        False
        >>> closure_point(x,2).is_line_or_ray()
        False

  is_point(self)

    Test whether `self` is a point.

    OUTPUT:

    Boolean.

    Examples:

        >>> from ppl import Variable, point, closure_point, ray, line
        >>> x = Variable(0)
        >>> line(x).is_point()
        False
        >>> ray(x).is_point()
        False
        >>> point(x,2).is_point()
        True
        >>> closure_point(x,2).is_point()
        False

  is_ray(self)

    Test whether `self` is a ray.

    OUTPUT:

    Boolean.

    Examples:

        >>> from ppl import Variable, point, closure_point, ray, line
        >>> x = Variable(0)
        >>> line(x).is_ray()
        False
        >>> ray(x).is_ray()
        True
        >>> point(x,2).is_ray()
        False
        >>> closure_point(x,2).is_ray()
        False

  classmethod line(cls, expression)

    Construct a line.

    INPUT:

    - `expression` – a `Linear_Expression` or something convertible to it (`Variable` or integer).

    OUTPUT:

    A new `Generator` representing the line.

    Raises a `ValueError` if the homogeneous part of ``expression` represents the origin of the vector space.

    Examples:

        >>> from ppl import Generator, Variable
        >>> y = Variable(1)
        >>> Generator.line(2*y)
        line(0, 1)
        >>> Generator.line(y)
        line(0, 1)
        >>> Generator.line(1)
        Traceback (most recent call last):
        ...
        ValueError: PPL::line(e):
        e == 0, but the origin cannot be a line.

  permute_space_dimensions(self, cycle)

    Permute the coordinates according to `cycle`.

        >>> from ppl import Generator, Variable
        >>> x = Variable(0); y = Variable(1); z = Variable(2)
        >>> p = Generator.point(2*x+7*y-z, 3)
        >>> p.permute_space_dimensions([0, 1])
        >>> p
        point(7/3, 2/3, -1/3)
        >>> p.permute_space_dimensions([0, 2, 1])
        >>> p
        point(2/3, -1/3, 7/3)

  classmethod point(cls, expression=0, divisor=1)

    Construct a point.

    INPUT:

    - `expression` – a `Linear_Expression` or something convertible to it (`Variable` or integer).

    - `divisor` – an integer.

    OUTPUT:

    A new `Generator` representing the point.

    Raises a `ValueError` if ``divisor==0`.

    Examples:

        >>> from ppl import Generator, Variable
        >>> y = Variable(1)
        >>> Generator.point(2*y+7, 3)
        point(0/3, 2/3)
        >>> Generator.point(y+7, 3)
        point(0/3, 1/3)
        >>> Generator.point(7, 3)
        point()
        >>> Generator.point(0, 0)
        Traceback (most recent call last):
        ...
        ValueError: PPL::point(e, d):
        d == 0.

  classmethod ray(cls, expression)

    Construct a ray.

    INPUT:

    - `expression` – a `Linear_Expression` or something convertible to it (`Variable` or integer).

    OUTPUT:

    A new `Generator` representing the ray.

    Raises a `ValueError` if the homogeneous part of ``expression` represents the origin of the vector space.

    Examples:

        >>> from ppl import Generator, Variable
        >>> y = Variable(1)
        >>> Generator.ray(2*y)
        ray(0, 1)
        >>> Generator.ray(y)
        ray(0, 1)
        >>> Generator.ray(1)
        Traceback (most recent call last):
        ...
        ValueError: PPL::ray(e):
        e == 0, but the origin cannot be a ray.

  set_space_dimension(self, size_t n)

    Set the dimension of this generator to `n`

    Examples:

        >>> import ppl
        >>> p = ppl.point()
        >>> p
        point()
        >>> p.set_space_dimension(5)
        >>> p
        point(0/1, 0/1, 0/1, 0/1, 0/1)

        >>> p = ppl.point(1 * ppl.Variable(0) + 2 * ppl.Variable(1) + 3 * ppl.Variable(2))
        >>> p.set_space_dimension(2)
        >>> p
        point(1/1, 2/1)

  space_dimension(self)

    Return the dimension of the vector space enclosing `self`.

    OUTPUT:

    Integer.

    Examples:

        >>> from ppl import Variable, point
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> point(x).space_dimension()
        1
        >>> point(y).space_dimension()
        2

  type(self)

    Return the generator type of `self`.

    OUTPUT:

    String. One of `'line'`, `'ray'`, `'point'`, or `'closure_point'`.

    Examples:

        >>> from ppl import Variable, point, closure_point, ray, line
        >>> x = Variable(0)
        >>> line(x).type()
        'line'
        >>> ray(x).type()
        'ray'
        >>> point(x,2).type()
        'point'
        >>> closure_point(x,2).type()
        'closure_point'

class ppl.generator.Generator_System

  Bases: `object`

  Wrapper for PPL’s `Generator_System` class.

  An object of the class Generator_System is a system of generators, i.e., a multiset of objects of the class Generator (lines, rays, points and closure points). When inserting generators in a system, space dimensions are automatically adjusted so that all the generators in the system are defined on the same vector space. A system of generators which is meant to define a non-empty polyhedron must include at least one point: the reason is that lines, rays and closure points need a supporting point (lines and rays only specify directions while closure points only specify points in the topological closure of the NNC polyhedron).

  Examples:

      >>> from ppl import Generator_System, Variable, line, ray, point, closure_point
      >>> x = Variable(0)
      >>> y = Variable(1)
      >>> gs = Generator_System(line(5*x-2*y))
      >>> gs.insert(ray(6*x-3*y))
      >>> gs.insert(point(2*x-7*y, 5))
      >>> gs.insert(closure_point(9*x-1*y, 2))
      >>> gs
      Generator_System {line(5, -2), ray(2, -1), point(2/5, -7/5), closure_point(9/2, -1/2)}

  ascii_dump(self)

    Write an ASCII dump to stderr.

    Examples:

        >>> cmd  = 'from ppl import Generator_System, point, Variable\n'
        >>> cmd += 'x = Variable(0)\n'
        >>> cmd += 'y = Variable(1)\n'
        >>> cmd += 'gs = Generator_System( point(3*x+2*y+1) )\n'
        >>> cmd += 'gs.ascii_dump()\n'
        >>> import subprocess, sys
        >>> proc = subprocess.Popen([sys.executable, '-c', cmd], stderr=subprocess.PIPE)
        >>> out, err = proc.communicate()
        >>> print(str(err.decode('ascii')))
        topology NECESSARILY_CLOSED
        ...

  clear(self)

    Removes all generators from the generator system and sets its space dimension to 0.

    Examples:

        >>> from ppl import Variable, Generator_System, point
        >>> x = Variable(0)
        >>> gs = Generator_System( point(3*x) ); gs
        Generator_System {point(3/1)}
        >>> gs.clear()
        >>> gs
        Generator_System {}

  empty(self)

    Return `True` if and only if `self` has no generators.

    OUTPUT:

    Boolean.

    Examples:

        >>> from ppl import Variable, Generator_System, point
        >>> x = Variable(0)
        >>> gs = Generator_System()
        >>> gs.empty()
        True
        >>> gs.insert( point(-3*x) )
        >>> gs.empty()
        False

  insert(self, Generator g)

    Insert `g` into the generator system.

    The number of space dimensions of `self` is increased, if needed.

    INPUT:

    - `g` – a `Generator`.

    Examples:

        >>> from ppl import Variable, Generator_System, point
        >>> x = Variable(0)
        >>> gs = Generator_System( point(3*x) )
        >>> gs.insert( point(-3*x) )
        >>> gs
        Generator_System {point(3/1), point(-3/1)}

  set_space_dimension(self, size_t n)

    Set the dimension of the vector space enclosing `self`

    Examples:

        >>> import ppl
        >>> gs = ppl.Generator_System()
        >>> gs.insert(ppl.point(ppl.Variable(0) + ppl.Variable(12)))
        >>> gs.insert(ppl.point(ppl.Variable(1) + 2*ppl.Variable(3)))
        >>> gs.space_dimension()
        13
        >>> gs.set_space_dimension(25)
        >>> gs.space_dimension()
        25
        >>> gs.set_space_dimension(3)
        >>> gs.space_dimension()
        3

  space_dimension(self)

    Return the dimension of the vector space enclosing `self`.

    OUTPUT:

    Integer.

    Examples:

        >>> from ppl import Variable, Generator_System, point
        >>> x = Variable(0)
        >>> gs = Generator_System( point(3*x) )
        >>> gs.space_dimension()
        1

class ppl.generator.Poly_Gen_Relation

  Bases: `object`

  Wrapper for PPL’s `Poly_Con_Relation` class.

  INPUT/OUTPUT:

  You must not construct `Poly_Gen_Relation` objects manually. You will usually get them from `relation_with()`. You can also get pre-defined relations from the class methods `nothing()` and `subsumes()`.

  Examples:

      >>> from ppl import Poly_Gen_Relation
      >>> nothing = Poly_Gen_Relation.nothing(); nothing
      nothing
      >>> subsumes = Poly_Gen_Relation.subsumes(); subsumes
      subsumes
      >>> nothing.implies( subsumes )
      False
      >>> subsumes.implies( nothing )
      True

  ascii_dump(self)

    Write an ASCII dump to stderr.

    Examples:

        >>> cmd  = 'from ppl import Poly_Gen_Relation\n'
        >>> cmd += 'Poly_Gen_Relation.nothing().ascii_dump()\n'
        >>> from subprocess import Popen, PIPE
        >>> import sys
        >>> proc = Popen([sys.executable, '-c', cmd], stderr=PIPE)
        >>> out, err = proc.communicate()
        >>> print(str(err.decode('ascii')))
        NOTHING
        >>> proc.returncode
        0

  implies(self, Poly_Gen_Relation y)

    Test whether `self` implies `y`.

    INPUT:

    - `y` – a `Poly_Gen_Relation`.

    OUTPUT:

    Boolean. `True` if and only if `self` implies `y`.

    Examples:

        >>> from ppl import Poly_Gen_Relation
        >>> nothing = Poly_Gen_Relation.nothing()
        >>> nothing.implies( nothing )
        True

  classmethod nothing(cls)

    Return the assertion that says nothing.

    OUTPUT:

    A `Poly_Gen_Relation`.

    Examples:

        >>> from ppl import Poly_Gen_Relation
        >>> Poly_Gen_Relation.nothing()
        nothing

  classmethod subsumes(cls)

    Return the assertion “Adding the generator would not change the polyhedron”.

    OUTPUT:

    A `Poly_Gen_Relation`.

    Examples:

        >>> from ppl import Poly_Gen_Relation
        >>> Poly_Gen_Relation.subsumes()
        subsumes

## Module ppl.polyhedron

class ppl.polyhedron.C_Polyhedron(*args)

  Bases: `Polyhedron`

  Wrapper for PPL’s `C_Polyhedron` class.

  An object of the class `C_Polyhedron` represents a topologically closed convex polyhedron in the vector space. See `NNC_Polyhedron` for more general (not necessarily closed) polyhedra.

  When building a closed polyhedron starting from a system of constraints, an exception is thrown if the system contains a strict inequality constraint. Similarly, an exception is thrown when building a closed polyhedron starting from a system of generators containing a closure point.

  INPUT:

  - `arg` – the defining data of the polyhedron. Any one of the following is accepted:

    - A non-negative integer. Depending on `degenerate_element`, either the space-filling or the empty polytope in the given dimension `arg` is constructed.

    - A `Constraint_System`.

    - A `Generator_System`.

    - A single `Constraint`.

    - A single `Generator`.

    - A `C_Polyhedron`.

  - `degenerate_element` – string, either `'universe'` or `'empty'`. Only used if `arg` is an integer.

  OUTPUT:

  A `C_Polyhedron`.

  Examples:

      >>> from ppl import Constraint_System, Generator_System, Variable, C_Polyhedron, point, ray
      >>> x = Variable(0)
      >>> y = Variable(1)
      >>> C_Polyhedron( 5*x-2*y >=  x+y-1 )
      A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, 1 ray, 1 line
      >>> cs = Constraint_System()
      >>> cs.insert( x >= 0 )
      >>> cs.insert( y >= 0 )
      >>> C_Polyhedron(cs)
      A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, 2 rays
      >>> C_Polyhedron( point(x+y) )
      A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point
      >>> gs = Generator_System()
      >>> gs.insert( point(-x-y) )
      >>> gs.insert( ray(x) )
      >>> C_Polyhedron(gs)
      A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, 1 ray

  The empty and universe polyhedra are constructed like this:

      >>> C_Polyhedron(3, 'empty')
      The empty polyhedron in QQ^3
      >>> C_Polyhedron(3, 'empty').constraints()
      Constraint_System {-1==0}
      >>> C_Polyhedron(3, 'universe')
      The space-filling polyhedron in QQ^3
      >>> C_Polyhedron(3, 'universe').constraints()
      Constraint_System {}

  Note that, by convention, the generator system of a polyhedron is either empty or contains at least one point. In particular, if you define a polyhedron via a non-empty `Generator_System` it must contain a point (at any position). If you start with a single generator, this generator must be a point:

      >>> C_Polyhedron( ray(x) )
      Traceback (most recent call last):
      ...
      ValueError: PPL::C_Polyhedron::C_Polyhedron(gs):
      *this is an empty polyhedron and
      the non-empty generator system gs contains no points.

class ppl.polyhedron.NNC_Polyhedron(*args)

  Bases: `Polyhedron`

  Wrapper for PPL’s `NNC_Polyhedron` class.

  An object of the class `NNC_Polyhedron` represents a not necessarily closed (NNC) convex polyhedron in the vector space.

  Note: Since NNC polyhedra are a generalization of closed polyhedra, any object of the class `C_Polyhedron` can be (explicitly) converted into an object of the class `NNC_Polyhedron`. The reason for defining two different classes is that objects of the class `C_Polyhedron` are characterized by a more efficient implementation, requiring less time and memory resources.

  INPUT:

  - `arg` – the defining data of the polyhedron. Any one of the following is accepted:

    - An non-negative integer. Depending on `degenerate_element`, either the space-filling or the empty polytope in the given dimension `arg` is constructed.

    - A `Constraint_System`.

    - A `Generator_System`.

    - A single `Constraint`.

    - A single `Generator`.

    - A `NNC_Polyhedron`.

    - A `C_Polyhedron`.

  - `degenerate_element` – string, either `'universe'` or `'empty'`. Only used if `arg` is an integer.

  OUTPUT:

  A `C_Polyhedron`.

  Examples:

      >>> from ppl import Constraint, Constraint_System, Generator, Generator_System, Variable, NNC_Polyhedron, point, ray, closure_point
      >>> x = Variable(0)
      >>> y = Variable(1)
      >>> NNC_Polyhedron( 5*x-2*y >  x+y-1 )
      A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, 1 closure_point, 1 ray, 1 line
      >>> cs = Constraint_System()
      >>> cs.insert( x > 0 )
      >>> cs.insert( y > 0 )
      >>> NNC_Polyhedron(cs)
      A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, 1 closure_point, 2 rays
      >>> NNC_Polyhedron( point(x+y) )
      A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point
      >>> gs = Generator_System()
      >>> gs.insert( point(-y) )
      >>> gs.insert( closure_point(-x-y) )
      >>> gs.insert( ray(x) )
      >>> p = NNC_Polyhedron(gs); p
      A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, 1 closure_point, 1 ray
      >>> p.minimized_constraints()
      Constraint_System {x1+1==0, x0+1>0}

  Note that, by convention, every polyhedron must contain a point:

      >>> NNC_Polyhedron( closure_point(x+y) )
      Traceback (most recent call last):
      ...
      ValueError: PPL::NNC_Polyhedron::NNC_Polyhedron(gs):
      *this is an empty polyhedron and
      the non-empty generator system gs contains no points.

class ppl.polyhedron.Polyhedron

  Bases: `object`

  Wrapper for PPL’s `Polyhedron` class.

  An object of the class Polyhedron represents a convex polyhedron in the vector space.

  A polyhedron can be specified as either a finite system of constraints or a finite system of generators (see Section Representations of Convex Polyhedra) and it is always possible to obtain either representation. That is, if we know the system of constraints, we can obtain from this the system of generators that define the same polyhedron and vice versa. These systems can contain redundant members: in this case we say that they are not in the minimal form.

  INPUT/OUTPUT:

  This is an abstract base for `C_Polyhedron` and `NNC_Polyhedron`. You cannot instantiate this class.

  BHRZ03_widening_assign(self, Polyhedron y, unsigned int tp=0)

    Assigns to `self`` the result of computing the BHRZ03-widening between `self` and `y`.

    INPUT:

    - `y` – a `Polyhedron` that must be contained in `self`

    - `tp` – an optional unsigned variable with the number of available tokens (to be used when applying the widening with tokens delay technique).

    OUTPUT:

    This method assigns to `self` the result of computing the BHRZ03-widening between `self` and `y`. And returns the new value of `tp`.

    Raises a `ValueError` if `self` and `y` are topology-incompatible.

    Examples:

        >>> from ppl import NNC_Polyhedron, Variable
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> ph1 = NNC_Polyhedron(2)
        >>> ph1.add_constraint( y >= 0 )
        >>> ph1.add_constraint( x + y > 0 )
        >>> ph1.add_constraint( x - y < 1 )
        >>> ph2 = NNC_Polyhedron(2)
        >>> ph2.add_constraint( y >= 0 )
        >>> ph2.add_constraint( x > 0 )
        >>> ph2.add_constraint( x < 1 )
        >>> tp = ph1.BHRZ03_widening_assign( ph2 )
        >>> known_result = NNC_Polyhedron(2)
        >>> known_result.add_constraint(y >= 0)
        >>> known_result == ph1
        True

        >>> from ppl import C_Polyhedron, Generator_System, point, ray
        >>> gs1 = Generator_System()
        >>> gs1.insert(point())
        >>> gs1.insert(point( x + 2 * y ))
        >>> gs1.insert(ray( x ))
        >>> gs1.insert(ray( 2 * x + y ))
        >>> ph1 = C_Polyhedron(gs1)
        >>> gs2 = Generator_System()
        >>> gs2.insert(point())
        >>> gs2.insert(point( x + 2 * y ))
        >>> gs2.insert(ray( x ))
        >>> gs2.insert(ray( x + y ))
        >>> ph2 = C_Polyhedron( gs2 )
        >>> tp = ph2.BHRZ03_widening_assign( ph1 )
        >>> known_result = C_Polyhedron(2)
        >>> known_result.add_constraint( y >= 0 )
        >>> known_result.add_constraint( 2 * x - y >= 0 )
        >>> ph2 == known_result
        True

    if this method is going to be called too many times, the `tp` parameter allows one to reduce the computation cost by computing the widening only when `tp` is equal to 0, otherwise the this method will decrement the value of `tp` and return it.

        >>> from ppl import closure_point
        >>> gs1 = Generator_System()
        >>> gs1.insert(point( 2 * x ))
        >>> gs1.insert(closure_point( x + y ))
        >>> gs1.insert(closure_point( 3 * x + y ))
        >>> ph1 = NNC_Polyhedron(gs1)
        >>> gs2 = Generator_System()
        >>> gs2.insert(point( 2 * x ))
        >>> gs2.insert(closure_point( y ))
        >>> gs2.insert(closure_point( 4 * x + y ))
        >>> ph2 = NNC_Polyhedron(gs2)
        >>> ph2_copy = NNC_Polyhedron(ph2)
        >>> known_result = NNC_Polyhedron(2)
        >>> known_result.add_constraint(y >= 0)
        >>> known_result.add_constraint(y < 1)
        >>> tp = ph2.BHRZ03_widening_assign(ph1, 1)
        >>> tp == 0
        True
        >>> ph2 == ph2_copy
        True
        >>> tp = ph2.BHRZ03_widening_assign(ph1, 0)
        >>> tp == 0
        True
        >>> ph2 == known_result
        True

  H79_widening_assign(self, Polyhedron y, unsigned int tp=0)

    Assigns to `self`` the result of computing the H79-widening between `self` and `y`.

    INPUT:

    - `y` – a `Polyhedron` that must be contained in `self`

    - `tp` – an optional unsigned variable with the number of available tokens (to be used when applying the widening with tokens delay technique).

    OUTPUT:

    This method assigns to `self` the result of computing the BHRZ03-widening between `self` and `y`. And returns the new value of `tp`.

    Raises a `ValueError` if `self` and `y` are topology-incompatible.

    Examples: >>> from ppl import Variable, C_Polyhedron >>> x = Variable(0) >>> y = Variable(1) >>> ph1 = C_Polyhedron(2) >>> ph1.add_constraint( x >= 2 ) >>> ph1.add_constraint( y >= 0 ) >>> ph2 = C_Polyhedron(2) >>> ph2.add_constraint( x >= 0 ) >>> ph2.add_constraint( y >= 0 ) >>> ph2.add_constraint( x-y >= 2 ) >>> tp = ph1.H79_widening_assign(ph2) >>> known_result = C_Polyhedron(2) >>> known_result.add_constraint(y >= 0) >>> known_result == ph1 True

    `self` and `y` must be dimension- and topology-compatible, or an exception is raised:

        >>> z = Variable(2)
        >>> ph1.H79_widening_assign( C_Polyhedron(z>=0) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::H79_widening_assign(y):
        this->space_dimension() == 2, y.space_dimension() == 3.

    if this method is going to be called too many times, the `tp` parameter allows one to reduce the computation cost by computing the widening only when `tp` is equal to 0, otherwise the this method will decrement the value of `tp` and return it.

        >>> from ppl import point, ray, Generator_System
        >>> gs1 = Generator_System()
        >>> gs1.insert(point())
        >>> gs1.insert(ray( x + y ))
        >>> gs1.insert(ray( x ))
        >>> ph1 = C_Polyhedron(gs1)
        >>> gs2 = Generator_System()
        >>> gs2.insert(point())
        >>> gs2.insert(ray( x ))
        >>> gs2.insert(ray( x + 2 * y ))
        >>> ph2 = C_Polyhedron(gs2)
        >>> ph2_copy = C_Polyhedron(ph2)
        >>> known_result = C_Polyhedron(2)
        >>> known_result.add_constraint(y >= 0)
        >>> tp = ph2.H79_widening_assign(ph1, 1)
        >>> tp == 0
        True
        >>> ph2 == ph2_copy
        True
        >>> tp = ph2.H79_widening_assign(ph1, 0)
        >>> tp == 0
        True
        >>> ph2 == known_result
        True

  add_constraint(self, Constraint c)

    Add a constraint to the polyhedron.

    Adds a copy of constraint `c` to the system of constraints of `self`, without minimizing the result.

    See also `add_constraints()`.

    INPUT:

    - `c` – the `Constraint` that will be added to the system of constraints of `self`.

    OUTPUT:

    This method modifies the polyhedron `self` and does not return anything.

    Raises a `ValueError` if `self` and the constraint `c` are topology-incompatible or dimension-incompatible.

    Examples:

        >>> from ppl import Variable, C_Polyhedron
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> p = C_Polyhedron( y>=0 )
        >>> p.add_constraint( x>=0 )

    > We just added a 1-d constraint to a 2-d polyhedron, this is fine. The other way is not:

        >>> p = C_Polyhedron( x>=0 )
        >>> p.add_constraint( y>=0 )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::add_constraint(c):
        this->space_dimension() == 1, c.space_dimension() == 2.

    > The constraint must also be topology-compatible, that is, `C_Polyhedron` only allows non-strict inequalities:

        >>> p = C_Polyhedron( x>=0 )
        >>> p.add_constraint( x< 1 )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::add_constraint(c):
        c is a strict inequality.

  add_constraints(self, Constraint_System cs)

    Add constraints to the polyhedron.

    Adds a copy of constraints in `cs` to the system of constraints of `self`, without minimizing the result.

    See also `add_constraint()`.

    INPUT:

    - `cs` – the `Constraint_System` that will be added to the system of constraints of `self`.

    OUTPUT:

    This method modifies the polyhedron `self` and does not return anything.

    Raises a `ValueError` if `self` and the constraints in `cs` are topology-incompatible or dimension-incompatible.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, Constraint_System
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> cs = Constraint_System()
        >>> cs.insert(x>=0)
        >>> cs.insert(y>=0)
        >>> p = C_Polyhedron( y<=1 )
        >>> p.add_constraints(cs)

    We just added a 1-d constraint to a 2-d polyhedron, this is fine. The other way is not:

        >>> p = C_Polyhedron( x<=1 )
        >>> p.add_constraints(cs)
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::add_recycled_constraints(cs):
        this->space_dimension() == 1, cs.space_dimension() == 2.

    The constraints must also be topology-compatible, that is, `C_Polyhedron` only allows non-strict inequalities:

        >>> p = C_Polyhedron( x>=0 )
        >>> p.add_constraints( Constraint_System(x<0) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::add_recycled_constraints(cs):
        cs contains strict inequalities.

  add_generator(self, Generator g)

    Add a generator to the polyhedron.

    Adds a copy of constraint `c` to the system of generators of `self`, without minimizing the result.

    INPUT:

    - `g` – the `Generator` that will be added to the system of Generators of `self`.

    OUTPUT:

    This method modifies the polyhedron `self` and does not return anything.

    Raises a `ValueError` if `self` and the generator `g` are topology-incompatible or dimension-incompatible, or if `self` is an empty polyhedron and `g` is not a point.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, point, closure_point, ray
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> p = C_Polyhedron(1, 'empty')
        >>> p.add_generator( point(0*x) )

    > We just added a 1-d generator to a 2-d polyhedron, this is fine. The other way is not:

        >>> p = C_Polyhedron(1, 'empty')
        >>> p.add_generator(  point(0*y) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::add_generator(g):
        this->space_dimension() == 1, g.space_dimension() == 2.

    > The constraint must also be topology-compatible, that is, `C_Polyhedron` does not allow `closure_point()` generators:

        >>> p = C_Polyhedron( point(0*x+0*y) )
        >>> p.add_generator( closure_point(0*x) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::add_generator(g):
        g is a closure point.

    Finally, ever non-empty polyhedron must have at least one point generator:

        >>> p = C_Polyhedron(3, 'empty')
        >>> p.add_generator( ray(x) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::add_generator(g):
        *this is an empty polyhedron and g is not a point.

  add_generators(self, Generator_System gs)

    Add generators to the polyhedron.

    Adds a copy of the generators in `gs` to the system of generators of `self`, without minimizing the result.

    See also `add_generator()`.

    INPUT:

    - `gs` – the `Generator_System` that will be added to the system of constraints of `self`.

    OUTPUT:

    This method modifies the polyhedron `self` and does not return anything.

    Raises a `ValueError` if `self` and one of the generators in `gs` are topology-incompatible or dimension-incompatible, or if `self` is an empty polyhedron and `gs` does not contain a point.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, Generator_System, point, ray, closure_point
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> gs = Generator_System()
        >>> gs.insert(point(0*x+0*y))
        >>> gs.insert(point(1*x+1*y))
        >>> p = C_Polyhedron(2, 'empty')
        >>> p.add_generators(gs)

    We just added a 1-d constraint to a 2-d polyhedron, this is fine. The other way is not:

        >>> p = C_Polyhedron(1, 'empty')
        >>> p.add_generators(gs)
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::add_recycled_generators(gs):
        this->space_dimension() == 1, gs.space_dimension() == 2.

    The constraints must also be topology-compatible, that is, `C_Polyhedron` does not allow `closure_point()` generators:

        >>> p = C_Polyhedron( point(0*x+0*y) )
        >>> p.add_generators( Generator_System(closure_point(x) ))
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::add_recycled_generators(gs):
        gs contains closure points.

  add_space_dimensions_and_embed(self, m)

    Add `m` new space dimensions and embed `self` in the new vector space.

    The new space dimensions will be those having the highest indexes in the new polyhedron, which is characterized by a system of constraints in which the variables running through the new dimensions are not constrained. For instance, when starting from the polyhedron P and adding a third space dimension, the result will be the polyhedron

    \[\Big\{ (x,y,z)^T \in \mathbb{R}^3 \Big| (x,y)^T \in P \Big\}\]

    INPUT:

    - `m` – integer.

    OUTPUT:

    This method assigns the embedded polyhedron to `self` and does not return anything.

    Raises a `ValueError` if adding `m` new space dimensions would cause the vector space to exceed dimension `self.max_space_dimension()`.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, point
        >>> x = Variable(0)
        >>> p = C_Polyhedron( point(3*x) )
        >>> p.add_space_dimensions_and_embed(1)
        >>> p.minimized_generators()
        Generator_System {line(0, 1), point(3/1, 0/1)}
        >>> p.add_space_dimensions_and_embed( p.max_space_dimension() )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::add_space_dimensions_and_embed(m):
        adding m new space dimensions exceeds the maximum allowed space dimension.

  add_space_dimensions_and_project(self, m)

    Add `m` new space dimensions and embed `self` in the new vector space.

    The new space dimensions will be those having the highest indexes in the new polyhedron, which is characterized by a system of constraints in which the variables running through the new dimensions are all constrained to be equal to 0. For instance, when starting from the polyhedron P and adding a third space dimension, the result will be the polyhedron

    \[\Big\{ (x,y,0)^T \in \mathbb{R}^3 \Big| (x,y)^T \in P \Big\}\]

    INPUT:

    - `m` – integer.

    OUTPUT:

    This method assigns the projected polyhedron to `self` and does not return anything.

    Raises a `ValueError` if adding `m` new space dimensions would cause the vector space to exceed dimension `self.max_space_dimension()`.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, point
        >>> x = Variable(0)
        >>> p = C_Polyhedron( point(3*x) )
        >>> p.add_space_dimensions_and_project(1)
        >>> p.minimized_generators()
        Generator_System {point(3/1, 0/1)}
        >>> p.add_space_dimensions_and_project( p.max_space_dimension() )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::add_space_dimensions_and_project(m):
        adding m new space dimensions exceeds the maximum allowed space dimension.

  affine_dimension(self)

    Return the affine dimension of `self`.

    OUTPUT:

    An integer. Returns 0 if `self` is empty. Otherwise, returns the affine dimension of `self`.

    Examples:

        >>> from ppl import Variable, C_Polyhedron
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> p = C_Polyhedron( 5*x-2*y ==  x+y-1 )
        >>> p.affine_dimension()
        1

  affine_image(self, Variable v, Linear_Expression le)

    Set this polyhedron to the image of the map v -> le

    INPUT:

    - `v` – a variable

    - `le` – a linear expression

    Examples:

        >>> import ppl
        >>> x = ppl.Variable(0)
        >>> y = ppl.Variable(1)
        >>> gs0 = ppl.Generator_System()
        >>> gs0.insert(ppl.point())
        >>> gs0.insert(ppl.point(x))
        >>> gs0.insert(ppl.point(y))
        >>> gs0.insert(ppl.point(x+y))
        >>> p0 = ppl.C_Polyhedron(gs0)
        >>> gs1 = ppl.Generator_System()
        >>> gs1.insert(ppl.point())
        >>> gs1.insert(ppl.point(x))
        >>> gs1.insert(ppl.point(x+y))
        >>> gs1.insert(ppl.point(2*x+y))
        >>> p1 = ppl.C_Polyhedron(gs1)

        >>> p0 == p1
        False
        >>> p0.affine_image(x, x+y)
        >>> p0 == p1
        True

  affine_preimage(self, Variable v, Linear_Expression le)

    Set this polyhedron to the preimage of the map v -> le

    INPUT:

    - `v` – a variable

    - `le` – a linear expression

    Examples:

        >>> import ppl
        >>> x = ppl.Variable(0)
        >>> y = ppl.Variable(1)
        >>> gs0 = ppl.Generator_System()
        >>> gs0.insert(ppl.point())
        >>> gs0.insert(ppl.point(x))
        >>> gs0.insert(ppl.point(y))
        >>> gs0.insert(ppl.point(x+y))
        >>> p0 = ppl.C_Polyhedron(gs0)
        >>> gs1 = ppl.Generator_System()
        >>> gs1.insert(ppl.point())
        >>> gs1.insert(ppl.point(x))
        >>> gs1.insert(ppl.point(x+y))
        >>> gs1.insert(ppl.point(2*x+y))
        >>> p1 = ppl.C_Polyhedron(gs1)

        >>> p0 == p1
        False
        >>> p1.affine_preimage(x, x+y)
        >>> p0 == p1
        True

  ascii_dump(self)

    Write an ASCII dump to stderr.

    Examples:

        >>> cmd  = 'from ppl import C_Polyhedron, Variable\n'
        >>> cmd += 'x = Variable(0)\n'
        >>> cmd += 'y = Variable(1)\n'
        >>> cmd += 'p = C_Polyhedron(3*x+2*y==1)\n'
        >>> cmd += 'p.minimized_generators()\n'
        >>> cmd += 'p.ascii_dump()\n'
        >>> from subprocess import Popen, PIPE
        >>> import sys
        >>> proc = Popen([sys.executable, '-c', cmd], stdout=PIPE, stderr=PIPE)
        >>> out, err = proc.communicate()
        >>> len(out)
        0
        >>> print(str(err.decode('ascii')))
        space_dim 2
        ...
        con_sys (up-to-date)
        topology NECESSARILY_CLOSED
        ...

        sat_c
        0 x 0

        sat_g
        2 x 2
        0 0
        0 1

  bounded_BHRZ03_extrapolation_assign(self, Polyhedron y, Constraint_System cs, unsigned int tp=0)

    Assigns to `self`` the result of computing the bounded extrapolation between `self` and `y` using the BHRZ03-widening operator.

    INPUT:

    - `y` – a `Polyhedron` that must be contained in `self`

    - `cs` – a `Constraint_System` used to improve the widened polyhedron

    - `tp` – an optional unsigned variable with the number of available tokens (to be used when applying the widening with tokens delay technique).

    OUTPUT:

    This method assigns to `self` the result of computing the bounded extrapolation between `self` and `y` using the BHRZ03-widening operator. And returns the new value of `tp`.

    Raises a `ValueError` if `self` and `y` are topology-incompatible or dimesion-incompatible.

    Examples:

        >>> from ppl import Variable, Constraint_System, C_Polyhedron
        >>> x = Variable(0)
        >>> ph1 = C_Polyhedron(1)
        >>> ph1.add_constraint( 1 <= x )
        >>> ph1.add_constraint( x <= 2 )
        >>> ph2 = C_Polyhedron(1)
        >>> ph2.add_constraint( 0 <= x )
        >>> ph2.add_constraint( x <= 3 )
        >>> cs = Constraint_System()
        >>> tp = ph2.bounded_BHRZ03_extrapolation_assign(ph1, cs)
        >>> known_result = C_Polyhedron(1)
        >>> known_result.add_constraint(0 <= x)
        >>> known_result == ph2
        True

  bounded_H79_extrapolation_assign(self, Polyhedron y, Constraint_System cs, unsigned int tp=0)

    Assigns to `self`` the result of computing the bounded extrapolation between `self` and `y` using the H79-widening operator.

    INPUT:

    - `y` – a `Polyhedron` that must be contained in `self`

    - `cs` – a `Constraint_System` used to improve the widened polyhedron

    - `tp` – an optional unsigned variable with the number of available tokens (to be used when applying the widening with tokens delay technique).

    OUTPUT:

    This method assigns to `self` the result of computing the bounded extrapolation between `self` and `y` using the H79-widening operator. And returns the new value of `tp`.

    Raises a `ValueError` if `self` and `y` are topology-incompatible or dimesion-incompatible.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, Constraint_System
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> ph1 = C_Polyhedron(2)
        >>> ph1.add_constraint( x-3 >= 0 )
        >>> ph1.add_constraint( x-3 <= 1 )
        >>> ph1.add_constraint( y >= 0 )
        >>> ph1.add_constraint( y <= 1 )
        >>> ph2 = C_Polyhedron(2)
        >>> ph2.add_constraint( 2*x-5 >= 0 )
        >>> ph2.add_constraint( x-3 <= 1 )
        >>> ph2.add_constraint( 2*y+3 >= 0 )
        >>> ph2.add_constraint( 2*y-5 <= 0 )
        >>> cs = Constraint_System()
        >>> cs.insert( x >= y )
        >>> tp = ph2.bounded_H79_extrapolation_assign(ph1, cs)
        >>> known_result = C_Polyhedron(2)
        >>> known_result.add_constraint( x >= 2 )
        >>> known_result.add_constraint( x <= 4 )
        >>> known_result.add_constraint( y >= -2 )
        >>> known_result.add_constraint( x >= y )
        >>> known_result == ph2
        True

  bounds_from_above(self, Linear_Expression expr)

    Test whether the `expr` is bounded from above.

    INPUT:

    - `expr` – a `Linear_Expression`

    OUTPUT:

    Boolean. Returns `True` if and only if `expr` is bounded from above in `self`.

    Raises a `ValueError` if `expr` and `this` are dimension-incompatible.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, Linear_Expression
        >>> x = Variable(0);  y = Variable(1)
        >>> p = C_Polyhedron(y<=0)
        >>> p.bounds_from_above(x+1)
        False
        >>> p.bounds_from_above(Linear_Expression(y))
        True
        >>> p = C_Polyhedron(x<=0)
        >>> p.bounds_from_above(y+1)
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::bounds_from_above(e):
        this->space_dimension() == 1, e.space_dimension() == 2.

  bounds_from_below(self, Linear_Expression expr)

    Test whether the `expr` is bounded from above.

    INPUT:

    - `expr` – a `Linear_Expression`

    OUTPUT:

    Boolean. Returns `True` if and only if `expr` is bounded from above in `self`.

    Raises a `ValueError` if `expr` and `this` are dimension-incompatible.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, Linear_Expression
        >>> x = Variable(0);  y = Variable(1)
        >>> p = C_Polyhedron(y>=0)
        >>> p.bounds_from_below(x+1)
        False
        >>> p.bounds_from_below(Linear_Expression(y))
        True
        >>> p = C_Polyhedron(x<=0)
        >>> p.bounds_from_below(y+1)
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::bounds_from_below(e):
        this->space_dimension() == 1, e.space_dimension() == 2.

  concatenate_assign(self, Polyhedron y)

    Assign to `self` the concatenation of `self` and `y`.

    This functions returns the Cartesian product of `self` and `y`.

    Viewing a polyhedron as a set of tuples (its points), it is sometimes useful to consider the set of tuples obtained by concatenating an ordered pair of polyhedra. Formally, the concatenation of the polyhedra P and Q (taken in this order) is the polyhedron such that

    \[R = \Big\{ (x_0,\dots,x_{n-1},y_0,\dots,y_{m-1})^T \in \mathbb{R}^{n+m} \Big| (x_0,\dots,x_{n-1})^T \in P ,~ (y_0,\dots,y_{m-1})^T \in Q \Big\}\]

    Another way of seeing it is as follows: first embed polyhedron P into a vector space of dimension n+m and then add a suitably renamed-apart version of the constraints defining Q.

    INPUT:

    - `m` – integer.

    OUTPUT:

    This method assigns the concatenated polyhedron to `self` and does not return anything.

    Raises a `ValueError` if `self` and `y` are topology-incompatible or if adding `y.space_dimension()` new space dimensions would cause the vector space to exceed dimension `self.max_space_dimension()`.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, NNC_Polyhedron, point
        >>> x = Variable(0)
        >>> p1 = C_Polyhedron( point(1*x) )
        >>> p2 = C_Polyhedron( point(2*x) )
        >>> p1.concatenate_assign(p2)
        >>> p1.minimized_generators()
        Generator_System {point(1/1, 2/1)}

    The polyhedra must be topology-compatible and not exceed the maximum space dimension:

        >>> p1.concatenate_assign( NNC_Polyhedron(1, 'universe') )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::concatenate_assign(y):
        y is a NNC_Polyhedron.

        >>> p1.concatenate_assign( C_Polyhedron(p1.max_space_dimension(), 'empty') )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::concatenate_assign(y):
        concatenation exceeds the maximum allowed space dimension.

  constrains(self, Variable var)

    Test whether `var` is constrained in `self`.

    INPUT:

    - `var` – a `Variable`.

    OUTPUT:

    Boolean. Returns `True` if and only if `var` is constrained in `self`.

    Raises a `ValueError` if `var` is not a space dimension of `self`.

    Examples:

        >>> from ppl import Variable, C_Polyhedron
        >>> x = Variable(0)
        >>> p = C_Polyhedron(1, 'universe')
        >>> p.constrains(x)
        False
        >>> p = C_Polyhedron(x>=0)
        >>> p.constrains(x)
        True
        >>> y = Variable(1)
        >>> p.constrains(y)
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::constrains(v):
        this->space_dimension() == 1, v.space_dimension() == 2.

  constraints(self)

    Returns the system of constraints.

    See also `minimized_constraints()`.

    OUTPUT:

    A `Constraint_System`.

    Examples:

        >>> from ppl import Variable, C_Polyhedron
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> p = C_Polyhedron(y >= 0)
        >>> p.add_constraint(x >= 0)
        >>> p.add_constraint(x+y >= 0)
        >>> p.constraints()
        Constraint_System {x1>=0, x0>=0, x0+x1>=0}
        >>> p.minimized_constraints()
        Constraint_System {x1>=0, x0>=0}

  contains(self, Polyhedron y)

    Test whether `self` contains `y`.

    INPUT:

    - `y` – a `Polyhedron`.

    OUTPUT:

    Boolean. Returns `True` if and only if `self` contains `y`.

    Raises a `ValueError` if `self` and `y` are topology-incompatible or dimension-incompatible.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, NNC_Polyhedron
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> p0 = C_Polyhedron( x>=0 )
        >>> p1 = C_Polyhedron( x>=1 )
        >>> p0.contains(p1)
        True
        >>> p1.contains(p0)
        False

    Errors are raised if the dimension or topology is not compatible:

        >>> p0.contains(C_Polyhedron(y>=0))
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::contains(y):
        this->space_dimension() == 1, y.space_dimension() == 2.
        >>> p0.contains(NNC_Polyhedron(x>0))
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::contains(y):
        y is a NNC_Polyhedron.

  contains_integer_point(self)

    Test whether `self` contains an integer point.

    OUTPUT:

    Boolean. Returns `True` if and only if `self` contains an integer point.

    Examples:

        >>> from ppl import Variable, NNC_Polyhedron
        >>> x = Variable(0)
        >>> p = NNC_Polyhedron(x>0)
        >>> p.add_constraint(x<1)
        >>> p.contains_integer_point()
        False
        >>> p.topological_closure_assign()
        >>> p.contains_integer_point()
        True

  difference_assign(y)

    Polyhedron.poly_difference_assign(self, Polyhedron y)

    Assign to `self` the poly-difference of `self` and `y`.

    For any pair of NNC polyhedra P_1 and P_2 the convex polyhedral difference (or poly-difference) of P_1 and P_2 is defined as the smallest convex polyhedron containing the set-theoretic difference P_1setminus P_2 of P_1 and P_2.

    In general, even if P_1 and P_2 are topologically closed polyhedra, their poly-difference may be a convex polyhedron that is not topologically closed. For this reason, when computing the poly-difference of two `C_Polyhedron`, the library will enforce the topological closure of the result.

    INPUT:

    - `y` – a `Polyhedron`

    OUTPUT:

    This method assigns the poly-difference to `self` and does not return anything.

    Raises a `ValueError` if `self` and and `y` are topology-incompatible or dimension-incompatible.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, point, NNC_Polyhedron
        >>> x = Variable(0)
        >>> p = NNC_Polyhedron( point(0*x) )
        >>> p.add_generator( point(1*x) )
        >>> p.poly_difference_assign(NNC_Polyhedron( point(0*x) ))
        >>> p.minimized_constraints()
        Constraint_System {-x0+1>=0, x0>0}

    The poly-difference of `C_polyhedron` is really its closure:

        >>> p = C_Polyhedron( point(0*x) )
        >>> p.add_generator( point(1*x) )
        >>> p.poly_difference_assign(C_Polyhedron( point(0*x) ))
        >>> p.minimized_constraints()
        Constraint_System {x0>=0, -x0+1>=0}

    `self` and `y` must be dimension- and topology-compatible, or an exception is raised:

        >>> y = Variable(1)
        >>> p.poly_difference_assign( C_Polyhedron(y>=0) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::poly_difference_assign(y):
        this->space_dimension() == 1, y.space_dimension() == 2.
        >>> p.poly_difference_assign( NNC_Polyhedron(x+y<1) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::poly_difference_assign(y):
        y is a NNC_Polyhedron.

  drop_some_non_integer_points(self)

    Possibly tighten `self` by dropping some points with non-integer coordinates.

    The modified polyhedron satisfies:

    - it is (not necessarily strictly) contained in the original polyhedron.

    - integral vertices (generating points with integer coordinates) of the original polyhedron are not removed.

    Note

    The modified polyhedron is not necessarily a lattice polyhedron; Some vertices will, in general, still be rational. Lattice points interior to the polyhedron may be lost in the process.

    Examples:

        >>> from ppl import Variable, NNC_Polyhedron, Constraint_System
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> cs = Constraint_System()
        >>> cs.insert( x>=0 )
        >>> cs.insert( y>=0 )
        >>> cs.insert( 3*x+2*y<5 )
        >>> p = NNC_Polyhedron(cs)
        >>> p.minimized_generators()
        Generator_System {point(0/1, 0/1), closure_point(0/2, 5/2), closure_point(5/3, 0/3)}
        >>> p.drop_some_non_integer_points()
        >>> p.minimized_generators()
        Generator_System {point(0/1, 0/1), point(0/1, 2/1), point(4/3, 0/3)}

  generators(self)

    Returns the system of generators.

    See also `minimized_generators()`.

    OUTPUT:

    A `Generator_System`.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, point
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> p = C_Polyhedron(3,'empty')
        >>> p.add_generator(point(-x-y))
        >>> p.add_generator(point(0))
        >>> p.add_generator(point(+x+y))
        >>> p.generators()
        Generator_System {point(-1/1, -1/1, 0/1), point(0/1, 0/1, 0/1), point(1/1, 1/1, 0/1)}
        >>> p.minimized_generators()
        Generator_System {point(-1/1, -1/1, 0/1), point(1/1, 1/1, 0/1)}

  hash_code(self)

    Return a hash code

    Tests:

        >>> from ppl import Constraint_System, Variable, C_Polyhedron
        >>> x = Variable(0)
        >>> p = C_Polyhedron( 5*x >= 3 )
        >>> p.hash_code()
        1

        >>> y = Variable(1)
        >>> cs = Constraint_System()
        >>> cs.insert( x >= 0 )
        >>> cs.insert( y >= 0 )
        >>> p = C_Polyhedron(cs)
        >>> p.hash_code()
        2

  intersection_assign(self, Polyhedron y)

    Assign to `self` the intersection of `self` and `y`.

    INPUT:

    - `y` – a `Polyhedron`

    OUTPUT:

    This method assigns the intersection to `self` and does not return anything.

    Raises a `ValueError` if `self` and and `y` are topology-incompatible or dimension-incompatible.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, NNC_Polyhedron
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> p = C_Polyhedron( 1*x+0*y >= 0 )
        >>> p.intersection_assign( C_Polyhedron(y>=0) )
        >>> p.constraints()
        Constraint_System {x0>=0, x1>=0}
        >>> z = Variable(2)
        >>> p.intersection_assign( C_Polyhedron(z>=0) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::intersection_assign(y):
        this->space_dimension() == 2, y.space_dimension() == 3.
        >>> p.intersection_assign( NNC_Polyhedron(x+y<1) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::intersection_assign(y):
        y is a NNC_Polyhedron.

  is_bounded(self)

    Test whether `self` is bounded.

    OUTPUT:

    Boolean. Returns `True` if and only if `self` is a bounded polyhedron.

    Examples:

        >>> from ppl import Variable, NNC_Polyhedron, point, closure_point, ray
        >>> x = Variable(0)
        >>> p = NNC_Polyhedron( point(0*x) )
        >>> p.add_generator( closure_point(1*x) )
        >>> p.is_bounded()
        True
        >>> p.add_generator( ray(1*x) )
        >>> p.is_bounded()
        False

  is_discrete(self)

    Test whether `self` is discrete.

    OUTPUT:

    Boolean. Returns `True` if and only if `self` is discrete.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, point
        >>> x = Variable(0);  y = Variable(1)
        >>> p = C_Polyhedron( point(1*x+2*y) )
        >>> p.is_discrete()
        True
        >>> p.add_generator( point(x) )
        >>> p.is_discrete()
        False

  is_disjoint_from(self, Polyhedron y)

    Tests whether `self` and `y` are disjoint.

    INPUT:

    - `y` – a `Polyhedron`.

    OUTPUT:

    Boolean. Returns `True` if and only if `self` and `y` are disjoint.

    Rayises a `ValueError` if `self` and `y` are topology-incompatible or dimension-incompatible.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, NNC_Polyhedron
        >>> x = Variable(0);  y = Variable(1)
        >>> C_Polyhedron(x<=0).is_disjoint_from( C_Polyhedron(x>=1) )
        True

    This is not allowed:

        >>> x = Variable(0);  y = Variable(1)
        >>> poly_1d = C_Polyhedron(x<=0)
        >>> poly_2d = C_Polyhedron(x+0*y>=1)
        >>> poly_1d.is_disjoint_from(poly_2d)
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::intersection_assign(y):
        this->space_dimension() == 1, y.space_dimension() == 2.

    Nor is this:

        >>> x = Variable(0);  y = Variable(1)
        >>> c_poly   =   C_Polyhedron( x<=0 )
        >>> nnc_poly = NNC_Polyhedron( x >0 )
        >>> c_poly.is_disjoint_from(nnc_poly)
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::intersection_assign(y):
        y is a NNC_Polyhedron.

        >>> NNC_Polyhedron(c_poly).is_disjoint_from(nnc_poly)
        True

  is_empty(self)

    Test if `self` is an empty polyhedron.

    OUTPUT:

    Boolean.

    Examples:

        >>> from ppl import C_Polyhedron
        >>> C_Polyhedron(3, 'empty').is_empty()
        True
        >>> C_Polyhedron(3, 'universe').is_empty()
        False

  is_topologically_closed(self)

    Tests if `self` is topologically closed.

    OUTPUT:

    Returns `True` if and only if `self` is a topologically closed subset of the ambient vector space.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, NNC_Polyhedron
        >>> x = Variable(0);  y = Variable(1)
        >>> C_Polyhedron(3, 'universe').is_topologically_closed()
        True
        >>> C_Polyhedron( x>=1 ).is_topologically_closed()
        True
        >>> NNC_Polyhedron( x>1 ).is_topologically_closed()
        False

  is_universe(self)

    Test if `self` is a universe (space-filling) polyhedron.

    OUTPUT:

    Boolean.

    Examples:

        >>> from ppl import C_Polyhedron
        >>> C_Polyhedron(3, 'empty').is_universe()
        False
        >>> C_Polyhedron(3, 'universe').is_universe()
        True

  limited_BHRZ03_extrapolation_assign(self, Polyhedron y, Constraint_System cs, unsigned int tp=0)

    Assigns to `self`` the result of computing the limited extrapolation between `self` and `y` using the BHRZ03-widening operator.

    INPUT:

    - `y` – a `Polyhedron` that must be contained in `self`

    - `cs` – a `Constraint_System` used to improve the widened polyhedron

    - `tp` – an optional unsigned variable with the number of available tokens (to be used when applying the widening with tokens delay technique).

    OUTPUT:

    This method assigns to `self` the result of computing the limited extrapolation between `self` and `y` using the BHRZ03-widening operator. And returns the new value of `tp`.

    Raises a `ValueError` if `self` and `y` are topology-incompatible or dimesion-incompatible.

    Examples: >>> from ppl import C_Polyhedron, Generator_System, Variable, point, Constraint_System >>> x = Variable(0) >>> y = Variable(1) >>> gs1 = Generator_System() >>> gs1.insert(point()) >>> gs1.insert(point( x + y )) >>> gs1.insert(point( x )) >>> ph1 = C_Polyhedron( gs1 ) >>> gs2 = Generator_System() >>> gs2.insert(point()) >>> gs2.insert(point( 2 * x )) >>> gs2.insert(point( 2 * x + 2 * y )) >>> ph2 = C_Polyhedron( gs2 ) >>> cs = Constraint_System() >>> cs.insert( x <= 5 ) >>> cs.insert( y <= 4 ) >>> tp = ph2.limited_BHRZ03_extrapolation_assign(ph1, cs) >>> known_result = C_Polyhedron(2) >>> known_result.add_constraint(y >= 0) >>> known_result.add_constraint(x - y >= 0) >>> known_result.add_constraint(y <= 4) >>> known_result.add_constraint(x <= 5) >>> known_result == ph2 True

  limited_H79_extrapolation_assign(self, Polyhedron y, Constraint_System cs, unsigned int tp=0)

    Assigns to `self`` the result of computing the limited extrapolation between `self` and `y` using the H79-widening operator.

    INPUT:

    - `y` – a `Polyhedron` that must be contained in `self`

    - `cs` – a `Constraint_System` used to improve the widened polyhedron

    - `tp` – an optional unsigned variable with the number of available tokens (to be used when applying the widening with tokens delay technique).

    OUTPUT:

    This method assigns to `self` the result of computing the limited extrapolation between `self` and `y` using the H79-widening operator. And returns the new value of `tp`.

    Raises a `ValueError` if `self` and `y` are topology-incompatible or dimesion-incompatible.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, Constraint_System, point
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> ph1 = C_Polyhedron(2)
        >>> ph1.add_constraint( x >= 0 )
        >>> ph1.add_constraint( x <= 1 )
        >>> ph1.add_constraint( y >= 0 )
        >>> ph1.add_constraint( x - y >= 0 )
        >>> ph2 = C_Polyhedron(2)
        >>> ph2.add_constraint( x >= 0 )
        >>> ph2.add_constraint( x <= 2 )
        >>> ph2.add_constraint( y >= 0 )
        >>> ph2.add_constraint( x - y >= 0 )
        >>> cs = Constraint_System()
        >>> cs.insert( x >= 0 )
        >>> cs.insert( y >= 0 )
        >>> cs.insert( x <= 5 )
        >>> cs.insert( y <= 5 )
        >>> tp = ph2.limited_H79_extrapolation_assign(ph1, cs)
        >>> known_result = C_Polyhedron(2)
        >>> known_result.add_constraint( x - y >= 0)
        >>> known_result.add_constraint(y >= 0)
        >>> known_result.add_constraint(x <= 5)
        >>> known_result == ph2
        True

        >>> ph1 = C_Polyhedron(2)
        >>> ph1.add_constraint( x >= 0 )
        >>> ph1.add_constraint( x <= 1 )
        >>> ph1.add_constraint( y == 0 )
        >>> ph2 = C_Polyhedron(2)
        >>> ph2.add_constraint( x <= 2 )
        >>> ph2.add_constraint( y >= 0 )
        >>> ph2.add_constraint( y <= x )
        >>> cs = Constraint_System()
        >>> cs.insert( x <= 5 )
        >>> cs.insert( y <= -1 )
        >>> known_result = C_Polyhedron(ph2)
        >>> known_result.add_generator(point(5*x))
        >>> known_result.add_generator(point(5*x + 5*y))
        >>> tp = ph2.limited_H79_extrapolation_assign(ph1, cs)
        >>> known_result == ph2
        True

  max_space_dimension(self)

    Return the maximum space dimension all kinds of Polyhedron can handle.

    OUTPUT:

    Integer.

    Examples:

        >>> from ppl import C_Polyhedron
        >>> C_Polyhedron(1, 'empty').max_space_dimension() > 2**20
        True

  maximize(self, Linear_Expression expr)

    Maximize `expr`.

    INPUT:

    - `expr` – a `Linear_Expression`.

    OUTPUT:

    A dictionary with the following keyword:value pair:

    - `'bounded'`: Boolean. Whether the linear expression `expr` is bounded from above on `self`.

    If `expr` is bounded from above, the following additional keyword:value pairs are set to provide information about the supremum:

    - `'sup_n'`: Integer. The numerator of the supremum value.

    - `'sup_d'`: Non-zero integer. The denominator of the supremum value.

    - `'maximum'`: Boolean. `True` if and only if the supremum is also the maximum value.

    - `'generator'`: a `Generator`. A point or closure point where expr reaches its supremum value.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, NNC_Polyhedron, Constraint_System
        >>> x = Variable(0);  y = Variable(1)
        >>> cs = Constraint_System()
        >>> cs.insert(x >= 0)
        >>> cs.insert(y >= 0)
        >>> cs.insert(3*x+5*y <= 10)
        >>> p = C_Polyhedron(cs)
        >>> pm = p.maximize(x+y)
        >>> for key in sorted(pm):
        ...     print("{} {}".format(key, pm[key]))
        bounded True
        generator point(10/3, 0/3)
        maximum True
        sup_d 3
        sup_n 10

    Unbounded case:

        >>> cs = Constraint_System()
        >>> cs.insert(x > 0)
        >>> p = NNC_Polyhedron(cs)
        >>> p.maximize(+x)
        {'bounded': False}
        >>> pm = p.maximize(-x)
        >>> for key in sorted(pm):
        ...     print("{} {}".format(key, pm[key]))
        bounded True
        generator closure_point(0/1)
        maximum False
        sup_d 1
        sup_n 0

  minimize(self, Linear_Expression expr)

    Minimize `expr`.

    INPUT:

    - `expr` – a `Linear_Expression`.

    OUTPUT:

    A dictionary with the following keyword:value pair:

    - `'bounded'`: Boolean. Whether the linear expression `expr` is bounded from below on `self`.

    If `expr` is bounded from below, the following additional keyword:value pairs are set to provide information about the infimum:

    - `'inf_n'`: Integer. The numerator of the infimum value.

    - `'inf_d'`: Non-zero integer. The denominator of the infimum value.

    - `'minimum'`: Boolean. `True` if and only if the infimum is also the minimum value.

    - `'generator'`: a `Generator`. A point or closure point where expr reaches its infimum value.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, NNC_Polyhedron, Constraint_System
        >>> x = Variable(0);  y = Variable(1)
        >>> cs = Constraint_System()
        >>> cs.insert( x>=0 )
        >>> cs.insert( y>=0 )
        >>> cs.insert( 3*x+5*y<=10 )
        >>> p = C_Polyhedron(cs)
        >>> pm = p.minimize( x+y )
        >>> for key in sorted(pm):
        ...     print("{} {}".format(key, pm[key]))
        bounded True
        generator point(0/1, 0/1)
        inf_d 1
        inf_n 0
        minimum True

    Unbounded case:

        >>> cs = Constraint_System()
        >>> cs.insert(x > 0)
        >>> p = NNC_Polyhedron(cs)
        >>> pm = p.minimize(+x)
        >>> for key in sorted(pm):
        ...    print("{} {}".format(key, pm[key]))
        bounded True
        generator closure_point(0/1)
        inf_d 1
        inf_n 0
        minimum False
        >>> p.minimize( -x )
        {'bounded': False}

  minimized_constraints(self)

    Returns the minimized system of constraints.

    See also `constraints()`.

    OUTPUT:

    A `Constraint_System`.

    Examples:

        >>> from ppl import Variable, C_Polyhedron
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> p = C_Polyhedron(y >= 0)
        >>> p.add_constraint(x >= 0)
        >>> p.add_constraint(x+y >= 0)
        >>> p.constraints()
        Constraint_System {x1>=0, x0>=0, x0+x1>=0}
        >>> p.minimized_constraints()
        Constraint_System {x1>=0, x0>=0}

  minimized_generators(self)

    Returns the minimized system of generators.

    See also `generators()`.

    OUTPUT:

    A `Generator_System`.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, point
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> p = C_Polyhedron(3,'empty')
        >>> p.add_generator(point(-x-y))
        >>> p.add_generator(point(0))
        >>> p.add_generator(point(+x+y))
        >>> p.generators()
        Generator_System {point(-1/1, -1/1, 0/1), point(0/1, 0/1, 0/1), point(1/1, 1/1, 0/1)}
        >>> p.minimized_generators()
        Generator_System {point(-1/1, -1/1, 0/1), point(1/1, 1/1, 0/1)}

  poly_difference_assign(self, Polyhedron y)

    Assign to `self` the poly-difference of `self` and `y`.

    For any pair of NNC polyhedra P_1 and P_2 the convex polyhedral difference (or poly-difference) of P_1 and P_2 is defined as the smallest convex polyhedron containing the set-theoretic difference P_1setminus P_2 of P_1 and P_2.

    In general, even if P_1 and P_2 are topologically closed polyhedra, their poly-difference may be a convex polyhedron that is not topologically closed. For this reason, when computing the poly-difference of two `C_Polyhedron`, the library will enforce the topological closure of the result.

    INPUT:

    - `y` – a `Polyhedron`

    OUTPUT:

    This method assigns the poly-difference to `self` and does not return anything.

    Raises a `ValueError` if `self` and and `y` are topology-incompatible or dimension-incompatible.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, point, NNC_Polyhedron
        >>> x = Variable(0)
        >>> p = NNC_Polyhedron( point(0*x) )
        >>> p.add_generator( point(1*x) )
        >>> p.poly_difference_assign(NNC_Polyhedron( point(0*x) ))
        >>> p.minimized_constraints()
        Constraint_System {-x0+1>=0, x0>0}

    The poly-difference of `C_polyhedron` is really its closure:

        >>> p = C_Polyhedron( point(0*x) )
        >>> p.add_generator( point(1*x) )
        >>> p.poly_difference_assign(C_Polyhedron( point(0*x) ))
        >>> p.minimized_constraints()
        Constraint_System {x0>=0, -x0+1>=0}

    `self` and `y` must be dimension- and topology-compatible, or an exception is raised:

        >>> y = Variable(1)
        >>> p.poly_difference_assign( C_Polyhedron(y>=0) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::poly_difference_assign(y):
        this->space_dimension() == 1, y.space_dimension() == 2.
        >>> p.poly_difference_assign( NNC_Polyhedron(x+y<1) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::poly_difference_assign(y):
        y is a NNC_Polyhedron.

  poly_hull_assign(self, Polyhedron y)

    Assign to `self` the poly-hull of `self` and `y`.

    For any pair of NNC polyhedra P_1 and P_2, the convex polyhedral hull (or poly-hull) of is the smallest NNC polyhedron that includes both P_1 and P_2. The poly-hull of any pair of closed polyhedra in is also closed.

    INPUT:

    - `y` – a `Polyhedron`

    OUTPUT:

    This method assigns the poly-hull to `self` and does not return anything.

    Raises a `ValueError` if `self` and and `y` are topology-incompatible or dimension-incompatible.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, point, NNC_Polyhedron
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> p = C_Polyhedron( point(1*x+0*y) )
        >>> p.poly_hull_assign(C_Polyhedron( point(0*x+1*y) ))
        >>> p.generators()
        Generator_System {point(0/1, 1/1), point(1/1, 0/1)}

    `self` and `y` must be dimension- and topology-compatible, or an exception is raised:

        >>> z = Variable(2)
        >>> p.poly_hull_assign( C_Polyhedron(z>=0) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::poly_hull_assign(y):
        this->space_dimension() == 2, y.space_dimension() == 3.
        >>> p.poly_hull_assign( NNC_Polyhedron(x+y<1) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::poly_hull_assign(y):
        y is a NNC_Polyhedron.

  relation_with(self, arg)

    Return the relations holding between the polyhedron `self` and the generator or constraint `arg`.

    INPUT:

    - `arg` – a `Generator` or a `Constraint`.

    OUTPUT:

    A `Poly_Gen_Relation` or a `Poly_Con_Relation` according to the type of the input.

    Raises `ValueError` if `self` and the generator/constraint `arg` are dimension-incompatible.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, point
        >>> x = Variable(0);  y = Variable(1)
        >>> p = C_Polyhedron(2, 'empty')
        >>> p.add_generator( point(1*x+0*y) )
        >>> p.add_generator( point(0*x+1*y) )
        >>> p.minimized_constraints()
        Constraint_System {x0+x1-1==0, -x1+1>=0, x1>=0}
        >>> p.relation_with( point(1*x+1*y) )
        nothing
        >>> p.relation_with( point(1*x+1*y, 2) )
        subsumes
        >>> p.relation_with( x+y==-1 )
        is_disjoint
        >>> p.relation_with( x==y )
        strictly_intersects
        >>> p.relation_with( x+y<=1 )
        is_included, saturates
        >>> p.relation_with( x+y<1 )
        is_disjoint, saturates

    In a Python program you will usually use `relation_with()` together with `implies()` or `implies()`, for example:

        >>> from ppl import Poly_Con_Relation
        >>> p.relation_with( x+y<1 ).implies(Poly_Con_Relation.saturates())
        True

    You can only get relations with dimension-compatible generators or constraints:

        >>> z = Variable(2)
        >>> p.relation_with( point(x+y+z) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::relation_with(g):
        this->space_dimension() == 2, g.space_dimension() == 3.
        >>> p.relation_with( z>0 )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::relation_with(c):
        this->space_dimension() == 2, c.space_dimension() == 3.

  remove_higher_space_dimensions(self, new_dimension)

    Remove the higher dimensions of the vector space so that the resulting space will have dimension `new_dimension`.

    OUTPUT:

    This method modifies `self` and does not return anything.

    Raises a `ValueError` if `new_dimensions` is greater than the space dimension of `self`.

    Examples:

        >>> from ppl import C_Polyhedron, Variable
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> p = C_Polyhedron(3*x+0*y==2)
        >>> p.remove_higher_space_dimensions(1)
        >>> p.minimized_constraints()
        Constraint_System {3*x0-2==0}
        >>> p.remove_higher_space_dimensions(2)
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::remove_higher_space_dimensions(nd):
        this->space_dimension() == 1, required space dimension == 2.

  space_dimension(self)

    Return the dimension of the vector space enclosing `self`.

    OUTPUT:

    Integer.

    Examples:

        >>> from ppl import Variable, C_Polyhedron
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> p = C_Polyhedron( 5*x-2*y >=  x+y-1 )
        >>> p.space_dimension()
        2

  strictly_contains(self, Polyhedron y)

    Test whether `self` strictly contains `y`.

    INPUT:

    - `y` – a `Polyhedron`.

    OUTPUT:

    Boolean. Returns `True` if and only if `self` contains `y` and `self` does not equal `y`.

    Raises a `ValueError` if `self` and `y` are topology-incompatible or dimension-incompatible.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, NNC_Polyhedron
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> p0 = C_Polyhedron( x>=0 )
        >>> p1 = C_Polyhedron( x>=1 )
        >>> p0.strictly_contains(p1)
        True
        >>> p1.strictly_contains(p0)
        False

    Errors are raised if the dimension or topology is not compatible:

        >>> p0.strictly_contains(C_Polyhedron(y>=0))
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::contains(y):
        this->space_dimension() == 1, y.space_dimension() == 2.
        >>> p0.strictly_contains(NNC_Polyhedron(x>0))
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::contains(y):
        y is a NNC_Polyhedron.

  topological_closure_assign(self)

    Assign to `self` its topological closure.

    Examples:

        >>> from ppl import Variable, NNC_Polyhedron
        >>> x = Variable(0)
        >>> p = NNC_Polyhedron(x>0)
        >>> p.is_topologically_closed()
        False
        >>> p.topological_closure_assign()
        >>> p.is_topologically_closed()
        True
        >>> p.minimized_constraints()
        Constraint_System {x0>=0}

  unconstrain(self, Variable var)

    Compute the cylindrification of `self` with respect to space dimension `var`.

    INPUT:

    - `var` – a `Variable`. The space dimension that will be unconstrained. Exceptions:

    OUTPUT:

    This method assigns the cylindrification to `self` and does not return anything.

    Raises a `ValueError` if `var` is not a space dimension of `self`.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, point
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> p = C_Polyhedron( point(x+y) ); p
        A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point
        >>> p.unconstrain(x); p
        A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, 1 line
        >>> z = Variable(2)
        >>> p.unconstrain(z)
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::unconstrain(var):
        this->space_dimension() == 2, required space dimension == 3.

  upper_bound_assign(y)

    Polyhedron.poly_hull_assign(self, Polyhedron y)

    Assign to `self` the poly-hull of `self` and `y`.

    For any pair of NNC polyhedra P_1 and P_2, the convex polyhedral hull (or poly-hull) of is the smallest NNC polyhedron that includes both P_1 and P_2. The poly-hull of any pair of closed polyhedra in is also closed.

    INPUT:

    - `y` – a `Polyhedron`

    OUTPUT:

    This method assigns the poly-hull to `self` and does not return anything.

    Raises a `ValueError` if `self` and and `y` are topology-incompatible or dimension-incompatible.

    Examples:

        >>> from ppl import Variable, C_Polyhedron, point, NNC_Polyhedron
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> p = C_Polyhedron( point(1*x+0*y) )
        >>> p.poly_hull_assign(C_Polyhedron( point(0*x+1*y) ))
        >>> p.generators()
        Generator_System {point(0/1, 1/1), point(1/1, 0/1)}

    `self` and `y` must be dimension- and topology-compatible, or an exception is raised:

        >>> z = Variable(2)
        >>> p.poly_hull_assign( C_Polyhedron(z>=0) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::poly_hull_assign(y):
        this->space_dimension() == 2, y.space_dimension() == 3.
        >>> p.poly_hull_assign( NNC_Polyhedron(x+y<1) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::poly_hull_assign(y):
        y is a NNC_Polyhedron.

  widening_assign(y, tp=0)

    Polyhedron.H79_widening_assign(self, Polyhedron y, unsigned int tp=0)

    Assigns to `self`` the result of computing the H79-widening between `self` and `y`.

    INPUT:

    - `y` – a `Polyhedron` that must be contained in `self`

    - `tp` – an optional unsigned variable with the number of available tokens (to be used when applying the widening with tokens delay technique).

    OUTPUT:

    This method assigns to `self` the result of computing the BHRZ03-widening between `self` and `y`. And returns the new value of `tp`.

    Raises a `ValueError` if `self` and `y` are topology-incompatible.

    Examples: >>> from ppl import Variable, C_Polyhedron >>> x = Variable(0) >>> y = Variable(1) >>> ph1 = C_Polyhedron(2) >>> ph1.add_constraint( x >= 2 ) >>> ph1.add_constraint( y >= 0 ) >>> ph2 = C_Polyhedron(2) >>> ph2.add_constraint( x >= 0 ) >>> ph2.add_constraint( y >= 0 ) >>> ph2.add_constraint( x-y >= 2 ) >>> tp = ph1.H79_widening_assign(ph2) >>> known_result = C_Polyhedron(2) >>> known_result.add_constraint(y >= 0) >>> known_result == ph1 True

    `self` and `y` must be dimension- and topology-compatible, or an exception is raised:

        >>> z = Variable(2)
        >>> ph1.H79_widening_assign( C_Polyhedron(z>=0) )
        Traceback (most recent call last):
        ...
        ValueError: PPL::C_Polyhedron::H79_widening_assign(y):
        this->space_dimension() == 2, y.space_dimension() == 3.

    if this method is going to be called too many times, the `tp` parameter allows one to reduce the computation cost by computing the widening only when `tp` is equal to 0, otherwise the this method will decrement the value of `tp` and return it.

        >>> from ppl import point, ray, Generator_System
        >>> gs1 = Generator_System()
        >>> gs1.insert(point())
        >>> gs1.insert(ray( x + y ))
        >>> gs1.insert(ray( x ))
        >>> ph1 = C_Polyhedron(gs1)
        >>> gs2 = Generator_System()
        >>> gs2.insert(point())
        >>> gs2.insert(ray( x ))
        >>> gs2.insert(ray( x + 2 * y ))
        >>> ph2 = C_Polyhedron(gs2)
        >>> ph2_copy = C_Polyhedron(ph2)
        >>> known_result = C_Polyhedron(2)
        >>> known_result.add_constraint(y >= 0)
        >>> tp = ph2.H79_widening_assign(ph1, 1)
        >>> tp == 0
        True
        >>> ph2 == ph2_copy
        True
        >>> tp = ph2.H79_widening_assign(ph1, 0)
        >>> tp == 0
        True
        >>> ph2 == known_result
        True

## Module ppl.mip_problem

class ppl.mip_problem.MIP_Problem

  Bases: `object`

  wrapper for PPL’s MIP_Problem class

  An object of the class MIP_Problem represents a Mixed Integer (Linear) Program problem.

  INPUT:

  - `dim` – integer

  - `args` – an array of the defining data of the MIP_Problem. For each element, any one of the following is accepted:

    - A `Constraint_System`.

    - A `Linear_Expression`.

  OUTPUT:

  A `MIP_Problem`.

  Examples:

      >>> from ppl import Variable, Constraint_System, MIP_Problem
      >>> x = Variable(0)
      >>> y = Variable(1)
      >>> cs = Constraint_System()
      >>> cs.insert(x >= 0)
      >>> cs.insert(y >= 0)
      >>> cs.insert(3 * x + 5 * y <= 10)
      >>> m = MIP_Problem(2, cs, x + y)
      >>> m
      MIP Problem (maximization, 2 variables, 3 constraints)
      >>> m.optimal_value()
      mpq(10,3)
      >>> float(_)
      3.333333333333333
      >>> m.optimizing_point()
      point(10/3, 0/3)

  add_constraint(self, Constraint c)

    Adds a copy of constraint c to the MIP problem.

    Examples:

        >>> from ppl import Variable, MIP_Problem
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> m = MIP_Problem()
        >>> m.add_space_dimensions_and_embed(2)
        >>> m.add_constraint(x >= 0)
        >>> m.add_constraint(y >= 0)
        >>> m.add_constraint(3 * x + 5 * y <= 10)
        >>> m.set_objective_function(x + y)
        >>> m.optimal_value()
        mpq(10,3)

    Tests:

        >>> z = Variable(2)
        >>> m.add_constraint(z >= -3)
        Traceback (most recent call last):
        ...
        ValueError: PPL::MIP_Problem::add_constraint(c):
        c.space_dimension() == 3 exceeds this->space_dimension == 2.

  add_constraints(self, Constraint_System cs)

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

    Examples:

        >>> from ppl import Variable, Constraint_System, MIP_Problem
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> cs = Constraint_System()
        >>> cs.insert(x >= 0)
        >>> cs.insert(y >= 0)
        >>> cs.insert(3 * x + 5 * y <= 10)
        >>> m = MIP_Problem(2)
        >>> m.set_objective_function(x + y)
        >>> m.add_constraints(cs)
        >>> m.optimal_value()
        mpq(10,3)

    Tests:

        >>> p = Variable(9)
        >>> cs.insert(p >= -3)
        >>> m.add_constraints(cs)
        Traceback (most recent call last):
        ...
        ValueError: PPL::MIP_Problem::add_constraints(cs):
        cs.space_dimension() == 10 exceeds this->space_dimension() == 2.

  add_space_dimensions_and_embed(self, PPL_dimension_type m)

    Adds m new space dimensions and embeds the old MIP problem in the new vector space.

    Examples:

        >>> from ppl import Variable, Constraint_System, MIP_Problem
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> cs = Constraint_System()
        >>> cs.insert( x >= 0)
        >>> cs.insert( y >= 0 )
        >>> cs.insert( 3 * x + 5 * y <= 10 )
        >>> m = MIP_Problem(2, cs, x + y)
        >>> m.add_space_dimensions_and_embed(5)
        >>> m.space_dimension()
        7

  add_to_integer_space_dimensions(self, Variables_Set i_vars)

    Sets the variables whose indexes are in set i_vars to be integer space dimensions.

    Examples:

        >>> from ppl import Variable, Variables_Set, Constraint_System, MIP_Problem
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> cs = Constraint_System()
        >>> cs.insert( x >= 0)
        >>> cs.insert( y >= 0 )
        >>> cs.insert( 3 * x + 5 * y <= 10 )
        >>> m = MIP_Problem(2)
        >>> m.set_objective_function(x + y)
        >>> m.add_constraints(cs)
        >>> i_vars = Variables_Set(x, y)
        >>> m.add_to_integer_space_dimensions(i_vars)
        >>> m.optimal_value()
        mpq(3,1)

  clear(self)

    Reset the MIP_Problem to be equal to the trivial MIP_Problem.

    Examples:

        >>> from ppl import Variable, Constraint_System, MIP_Problem
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> cs = Constraint_System()
        >>> cs.insert( x >= 0)
        >>> cs.insert( y >= 0 )
        >>> cs.insert( 3 * x + 5 * y <= 10 )
        >>> m = MIP_Problem(2, cs, x + y)
        >>> m.objective_function()
        x0+x1
        >>> m.clear()
        >>> m.objective_function()
        0

  constraints(self)

    Return the constraints of this MIP

    The output is an instance of `Constraint_System`.

    Examples:

        >>> from ppl import Variable, MIP_Problem
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> M = MIP_Problem(2)
        >>> M.add_constraint(x + y <= 5)
        >>> M.add_constraint(3*x - 18*y >= -2)
        >>> M.constraints()
        Constraint_System {-x0-x1+5>=0, 3*x0-18*x1+2>=0}

    Note that modifying the output of this method will not modify the underlying MIP problem object:

        >>> cs = M.constraints()
        >>> cs.insert(x <= 3)
        >>> cs
        Constraint_System {-x0-x1+5>=0, 3*x0-18*x1+2>=0, -x0+3>=0}
        >>> M.constraints()
        Constraint_System {-x0-x1+5>=0, 3*x0-18*x1+2>=0}

  evaluate_objective_function(self, Generator evaluating_point)

    Return the result of evaluating the objective function on evaluating_point. ValueError thrown if self and evaluating_point are dimension-incompatible or if the generator evaluating_point is not a point.

    Examples:

        >>> from ppl import Variable, MIP_Problem, Generator
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> m = MIP_Problem()
        >>> m.add_space_dimensions_and_embed(2)
        >>> m.add_constraint(x >= 0)
        >>> m.add_constraint(y >= 0)
        >>> m.add_constraint(3 * x + 5 * y <= 10)
        >>> m.set_objective_function(x + y)
        >>> g = Generator.point(5 * x - 2 * y, 7)
        >>> m.evaluate_objective_function(g)
        mpq(3,7)
        >>> z = Variable(2)
        >>> g = Generator.point(5 * x - 2 * z, 7)
        >>> m.evaluate_objective_function(g)
        Traceback (most recent call last):
        ...
        ValueError: PPL::MIP_Problem::evaluate_objective_function(p, n, d):
        *this and p are dimension incompatible.

  is_satisfiable(self)

    Check if the MIP_Problem is satisfiable

    Examples:

        >>> from ppl import Variable, MIP_Problem
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> m = MIP_Problem()
        >>> m.add_space_dimensions_and_embed(2)
        >>> m.add_constraint(x >= 0)
        >>> m.add_constraint(y >= 0)
        >>> m.add_constraint(3 * x + 5 * y <= 10)
        >>> m.is_satisfiable()
        True

  objective_function(self)

    Return the optimal value of the MIP_Problem.

    Examples:

        >>> from ppl import Variable, Constraint_System, MIP_Problem
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> cs = Constraint_System()
        >>> cs.insert( x >= 0)
        >>> cs.insert( y >= 0 )
        >>> cs.insert( 3 * x + 5 * y <= 10 )
        >>> m = MIP_Problem(2, cs, x + y)
        >>> m.objective_function()
        x0+x1

  optimal_value(self)

    Return the optimal value of the MIP_Problem. ValueError thrown if self does not have an optimizing point, i.e., if the MIP problem is unbounded or not satisfiable.

    Examples:

        >>> from ppl import Variable, Constraint_System, MIP_Problem
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> cs = Constraint_System()
        >>> cs.insert( x >= 0 )
        >>> cs.insert( y >= 0 )
        >>> cs.insert( 3 * x + 5 * y <= 10 )
        >>> m = MIP_Problem(2, cs, x + y)
        >>> m.optimal_value()
        mpq(10,3)
        >>> cs = Constraint_System()
        >>> cs.insert( x >= 0 )
        >>> m = MIP_Problem(1, cs, x + x )
        >>> m.optimal_value()
        Traceback (most recent call last):
        ...
        ValueError: PPL::MIP_Problem::optimizing_point():
        *this ... have an optimizing point.

  optimization_mode(self)

    Return the optimization mode used in the MIP_Problem.

    It will return “maximization” if the MIP_Problem was set to MAXIMIZATION mode, and “minimization” otherwise.

    Examples:

        >>> from ppl import MIP_Problem
        >>> m = MIP_Problem()
        >>> m.optimization_mode()
        'maximization'

  optimizing_point(self)

    Returns an optimal point for the MIP_Problem, if it exists.

    Examples:

        >>> from ppl import Variable, MIP_Problem
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> m = MIP_Problem()
        >>> m.add_space_dimensions_and_embed(2)
        >>> m.add_constraint(x >= 0)
        >>> m.add_constraint(y >= 0)
        >>> m.add_constraint(3 * x + 5 * y <= 10)
        >>> m.set_objective_function(x + y)
        >>> m.optimizing_point()
        point(10/3, 0/3)

  set_objective_function(self, obj)

    Sets the objective function to obj.

    Examples:

        >>> from ppl import Variable, MIP_Problem
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> m = MIP_Problem()
        >>> m.add_space_dimensions_and_embed(2)
        >>> m.add_constraint(x >= 0)
        >>> m.add_constraint(y >= 0)
        >>> m.add_constraint(3 * x + 5 * y <= 10)
        >>> m.set_objective_function(x + y)
        >>> m.optimal_value()
        mpq(10,3)

    Tests:

        >>> z = Variable(2)
        >>> m.set_objective_function(x + y + z)
        Traceback (most recent call last):
        ...
        ValueError: PPL::MIP_Problem::set_objective_function(obj):
        obj.space_dimension() == 3 exceeds this->space_dimension == 2.

        >>> M = MIP_Problem(1)
        >>> M.set_objective_function(Variable(0))

  set_optimization_mode(self, mode)

    Sets the optimization mode to mode.

    Examples:

        >>> from ppl import MIP_Problem
        >>> m = MIP_Problem()
        >>> m.optimization_mode()
        'maximization'
        >>> m.set_optimization_mode('minimization')
        >>> m.optimization_mode()
        'minimization'

    Tests:

        >>> m.set_optimization_mode('max')
        Traceback (most recent call last):
        ...
        ValueError: Unknown value: mode=max.

  solve(self)

    Optimizes the MIP_Problem

    Examples:

        >>> from ppl import Variable, MIP_Problem
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> m = MIP_Problem()
        >>> m.add_space_dimensions_and_embed(2)
        >>> m.add_constraint(x >= 0)
        >>> m.add_constraint(y >= 0)
        >>> m.add_constraint(3 * x + 5 * y <= 10)
        >>> m.set_objective_function(x + y)
        >>> m.solve()
        {'status': 'optimized'}

  space_dimension(self)

    Return the space dimension of the MIP_Problem.

    Examples:

        >>> from ppl import Variable, Constraint_System, MIP_Problem
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> cs = Constraint_System()
        >>> cs.insert( x >= 0)
        >>> cs.insert( y >= 0 )
        >>> cs.insert( 3 * x + 5 * y <= 10 )
        >>> m = MIP_Problem(2, cs, x + y)
        >>> m.space_dimension()
        2

## Module ppl.congruence

class ppl.congruence.Congruence(arg=None, mod=None)

  Bases: `object`

  Wrapper for PPL’s `Congruence` class.

      >>> import ppl
      >>> x = ppl.Variable(0)
      >>> y = ppl.Variable(1)
      >>> ppl.Congruence(x + 2*y - 1, 7)
      x0+2*x1-1==0 (mod 7)
      >>> ppl.Congruence(x + y == 2, 5)
      x0+x1-2==0 (mod 5)

  coefficient(self, v)

    Return the coefficient `v` of this congruence.

    Examples:

        >>> import ppl
        >>> x = ppl.Variable(0)
        >>> y = ppl.Variable(1)
        >>> c = (2 * x + y == 1) % 12
        >>> c.coefficient(0)
        mpz(2)
        >>> c.coefficient(x)
        mpz(2)

    Note that contrarily to `Linear_Expression` the congruences raise an error when trying to access a coefficient beyond the dimension

        >>> c.coefficient(ppl.Variable(13))
        Traceback (most recent call last):
        ...
        ValueError: PPL::Congruence::coefficient(v):
        this->space_dimension() == 2, v.space_dimension() == 14.

  coefficients(self)

    Return the coefficients of this congruence as a tuple.

    Examples:

        >>> import ppl
        >>> x = ppl.Variable(0)
        >>> y = ppl.Variable(1)
        >>> t = ppl.Variable(3)
        >>> c = ppl.Congruence(x + 2*y + t - 1, 7)
        >>> c.coefficients()
        (mpz(1), mpz(2), mpz(0), mpz(1))

  inhomogeneous_term(self)

    Return the inhomogeneous term of this congruence.

    Examples:

        >>> import ppl
        >>> x = ppl.Variable(0)
        >>> y = ppl.Variable(1)
        >>> c = ppl.Congruence(x + 2*y - 3, 7)
        >>> c.inhomogeneous_term()
        mpz(-3)

  is_equal(self, Congruence other)

    Return whether `self` is equal to `other`.

    Examples:

        >>> import ppl
        >>> x = ppl.Variable(0)
        >>> Congruence(x + 1, 3).is_equal(Congruence(x + 1, 3))
        True
        >>> Congruence(x, 3).is_equal(Congruence(x - 2, 3))
        False
        >>> Congruence(x, 3).is_equal(Congruence(x , 2))
        False

  modulus(self)

    Return the modulus of this congruence.

    Examples:

        >>> import ppl
        >>> x = ppl.Variable(0)
        >>> y = ppl.Variable(1)
        >>> c = ppl.Congruence(x + 2*y - 3, 7)
        >>> c.modulus()
        mpz(7)

  space_dimension(self)

class ppl.congruence.Congruence_System(arg=None)

  Bases: `object`

  Wrapper for PPL’s `Congruence_System` class.

      >>> from ppl import Variable, Congruence, Congruence_System
      >>> x = Variable(0)
      >>> y = Variable(1)
      >>> z = Variable(2)
      >>> c1 = Congruence(2*x + 3*y - 5*z == 0, 12)
      >>> c2 = Congruence(4*x + y == 5, 18)
      >>> C = Congruence_System()
      >>> C.insert(c1)
      >>> C.insert(c2)
      >>> C
      Congruence_System {2*x0+3*x1-5*x2==0 (mod 12), 4*x0+x1+13==0 (mod 18)}

  ascii_dump(self)

    Write an ASCII dump of this congruence to stderr.

    Examples:

        >>> cmd  = 'from ppl import Variable, Congruence_System\n'
        >>> cmd += 'x = Variable(0)\n'
        >>> cmd += 'y = Variable(1)\n'
        >>> cmd += 'cs = Congruence_System( (3*x == 2*y+1) % 7 )\n'
        >>> cmd += 'cs.ascii_dump()\n'
        >>> import subprocess, sys
        >>> proc = subprocess.Popen([sys.executable, '-c', cmd], stderr=subprocess.PIPE)
        >>> out, err = proc.communicate()
        >>> print(str(err.decode('ascii')))
        1 x 2 SPARSE
        size 3 6 3 -2 m 7

  clear(self)

    Remove all congruences from this congruence system.

        >>> from ppl import Variable, Congruence_System
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> cs = Congruence_System([(3*x+2*y+1 == 3) % 15, (x+2*y + 7 == 0) % 22])
        >>> len(cs)
        2
        >>> cs.clear()
        >>> len(cs)
        0

  insert(self, Congruence c)

    Insert the congruence `c` into the congruence system.

    Examples:

        >>> from ppl import Variable, Congruence_System
        >>> x = Variable(0)
        >>> y = Variable(1)
        >>> cs = Congruence_System()
        >>> cs.insert((3 * x + 2 == 0) % 13)
        >>> cs
        Congruence_System {3*x0+2==0 (mod 13)}

ppl.congruence.congruence(le, m)