Example #1
0
    def equals(self, other, allowed_error = EPSILON):
        """Test approximate equality of two foliations.

        For the two foliations to be considered equal, the permutation
        of intervals has to be the same (but possibly with different
        labels). Also the vectors made from the length and twists
        parameters must differ by less than ``allowed_error``.

        INPUT:

        - ``other`` -- another ``Foliation`` object

        - ``allowed_error`` -- a positive number, the distance between
        the two length-twist vectors must be smaller than this in
        order for equality.
        
        EXAMPLES::

        sage: f = Foliation('a a b b c c','moebius',[1,2,3])
        sage: g = Foliation('1 1 2 2 3 3','moebius',[2.0000001,4,6])
        sage: f.equals(g,0.0001)
        True

        """
        return self.is_bottom_side_moebius() == other.is_bottom_side_moebius()\
                and self._gen_perm == other._gen_perm and \
                abs(vector(flatten(self._divpoints)) -
                vector(flatten(other._divpoints))) <= allowed_error
Example #2
0
    def arithmetic_independence_poset(self):
        """
        Compute the poset of (arithmetic) independent sets of the associated central toric arrangement.
        This is defined in [Len17b, Definition 5], [Mar18, Definitions 2.1 and 2.2], [DD18, Section 7].
        Notice that it is not the same as the independence poset of the underlying matroid.
        """
        # TODO: implement for Q != 0
        if self._Q.ncols() > 0:
            raise NotImplementedError

        A = self._A.transpose()
        data = {}

        # compute Smith normal forms of all submatrices
        for S_labeled in self.independent_sets():
            S = tuple(sorted(self._groundset_to_index[e] for e in S_labeled))
            D, U, V = A[S, :].smith_form()  # D == U*A[S,:]*V
            diagonal = [D[i, i] if i < D.ncols() else 0 for i in range(len(S))]
            data[tuple(S)] = (diagonal, U)

        # generate all elements of the poset
        elements = {
            S: list(
                vector(ZZ, x) for x in itertools.product(
                    *(range(max(data[tuple(S)][0][i], 1))
                      for i in range(len(S)))))
            for S in data.keys()
        }

        for l in elements.values():
            for v in l:
                v.set_immutable()

        all_elements = list((tuple(self._E[i] for i in S), x)
                            for (S, l) in elements.items() for x in l)
        cover_relations = []

        for (S, l) in elements.items():
            diagonal_S, U_S = data[S]
            S_labeled = tuple(self._E[i] for i in S)

            for s in S:
                i = S.index(s)  # index where the element s appears in S
                T = tuple(t for t in S if t != s)
                T_labeled = tuple(self._E[i] for i in T)

                diagonal_T, U_T = data[T]

                for x in l:
                    y = U_S**(-1) * x
                    z = U_T * vector(ZZ, y[:i].list() + y[i + 1:].list())
                    w = vector(ZZ,
                               (a % diagonal_T[j] if diagonal_T[j] > 0 else 0
                                for j, a in enumerate(z)))
                    w.set_immutable()

                    cover_relations.append(((T_labeled, w), (S_labeled, x)))

        return Poset(data=(all_elements, cover_relations),
                     cover_relations=True)
Example #3
0
 def cs_range( f, subset = None):
     """
     For a symmetric semi-positive integral matrix $f$,
     return a list of all integral $n$-vectors $v$ such that
     $x^tfx - (v*x)^2 >= 0$ for all $x$.
     """
     n = f.dimensions()[0]
     b = vector( s.isqrt() for s in f.diagonal())
     zv = vector([0]*n)
     box = [b - vector(t) for t in mrange( (2*b + vector([1]*n)).list()) if b - vector(t) > zv]
     if subset:
         box = [v for v in box if v in subset]
     rge = [v for v in box if min( (f - matrix(v).transpose()*matrix(v)).eigenvalues()) >=0]
     return rge
Example #4
0
def diameter_support_function(A, b):
    r"""Compute the diameter of a polytope using the H-representation.

    The diameter is computed in the supremum norm.

    INPUT:

    Polyhedron in H-representation, `Ax \leq b`.

    OUTPUT:

    - ``diam`` -- scalar, diameter of the polyhedron in the supremum norm

    - ``u`` -- vector with components `u_j = \max x_j` for `x` in `P`

    - ``l`` -- vector with components `l_j = \min x_j` for `x` in `P`

    EXAMPLES::

        sage: from polyhedron_tools.misc import diameter_support_function, polyhedron_to_Hrep
        sage: [A, b] = polyhedron_to_Hrep(7*polytopes.hypercube(5))
        sage: diameter_support_function(A, b)
        (14.0, (7.0, 7.0, 7.0, 7.0, 7.0), (-7.0, -7.0, -7.0, -7.0, -7.0))
    """
    from polyhedron_tools.misc import support_function

    # ambient dimension
    n = A.ncols()

    # number of constraints
    m = A.rows()

    In = identity_matrix(n)

    # lower : min x_j
    l = []
    for j in range(n):
        l += [-support_function([A, b], -In.column(j))]
    l = vector(l)

    # upper : max x_j
    u = []
    for j in range(n):
        u += [support_function([A, b], In.column(j))]
    u = vector(u)

    diam = max(u - l)

    return diam, u, l
Example #5
0
    def vertices(self):
        r"""
        Return the vertices as a tuple of `\ZZ`-vectors.

        OUTPUT:

        A tuple of `\ZZ`-vectors. Each entry is the coordinate vector
        of an integral points of the lattice polytope.

        EXAMPLES::

            sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
            sage: p = LatticePolytope_PPL((-9,-6,-1,-1),(0,0,0,1),(0,0,1,0),(0,1,0,0),(1,0,0,0))
            sage: p.vertices()
            ((-9, -6, -1, -1), (0, 0, 0, 1), (0, 0, 1, 0), (0, 1, 0, 0), (1, 0, 0, 0))
            sage: p.minimized_generators()
            Generator_System {point(-9/1, -6/1, -1/1, -1/1), point(0/1, 0/1, 0/1, 1/1),
            point(0/1, 0/1, 1/1, 0/1), point(0/1, 1/1, 0/1, 0/1), point(1/1, 0/1, 0/1, 0/1)}
        """
        d = self.space_dimension()
        v = vector(ZZ, d)
        points = []
        for g in self.minimized_generators():
            for i in range(0,d):
                v[i] = g.coefficient(Variable(i))
            v_copy = copy.copy(v)
            v_copy.set_immutable()
            points.append(v_copy)
        return tuple(points)
Example #6
0
def opposite_polyhedron(P, base_ring=None):
    r"""Generate the polyhedron whose vertices are oppositve to a given one.

    The opposite polyhedron `-P` is defined as the polyhedron such that `x \in -P` if and only if `-x \in P`. 

    INPUT:

    * ``P`` - an object of class Polyhedron.

    * ``base_ring`` - (default: that of P). The ``base_ring`` passed to construct `-P`.

    OUTPUT:

    A polyhedron whose vertices are opposite to those of `P`.

    EXAMPLES::

        sage: from polyhedron_tools.misc import BoxInfty, opposite_polyhedron
        sage: P = BoxInfty([1,1], 0.5)
        sage: minusP = opposite_polyhedron(P)
        sage: P.plot(aspect_ratio=1) + minusP.plot()    # not tested (plot)

    TO-DO:

    The possibility to receive P in matrix form `(A, b)`.
    """
    if base_ring is None:
        base_ring = P.base_ring()

    return Polyhedron(vertices=[-1 * vector(v) for v in P.vertices_list()],
                      base_ring=base_ring)
Example #7
0
    def evaluate(self, point):
        """
        Evaluate the linear expression.

        INPUT:

        - ``point`` -- list/tuple/iterable of coordinates; the
          coordinates of a point

        OUTPUT:

        The linear expression `Ax + b` evaluated at the point `x`.

        EXAMPLES::

            sage: from sage.geometry.linear_expression import LinearExpressionModule
            sage: L.<x,y> = LinearExpressionModule(QQ)
            sage: ex = 2*x + 3* y + 4
            sage: ex.evaluate([1,1])
            9
            sage: ex([1,1])    # syntactic sugar
            9
            sage: ex([pi, e])
            2*pi + 3*e + 4
        """
        try:
            point = self.parent().ambient_module()(point)
        except TypeError:
            from sage.matrix.constructor import vector
            point = vector(point)
        return self._coeffs * point + self._const
Example #8
0
    def evaluate(self, point):
        """
        Evaluate the linear expression.

        INPUT:

        - ``point`` -- list/tuple/iterable of coordinates; the
          coordinates of a point

        OUTPUT:

        The linear expression `Ax + b` evaluated at the point `x`.

        EXAMPLES::
        
            sage: from sage.geometry.linear_expression import LinearExpressionModule
            sage: L.<x,y> = LinearExpressionModule(QQ)
            sage: ex = 2*x + 3* y + 4
            sage: ex.evaluate([1,1])
            9
            sage: ex([1,1])    # syntactic sugar
            9
            sage: ex([pi, e])
            2*pi + 3*e + 4
        """
        try:
            point = self.parent().ambient_module()(point)
        except TypeError:
            from sage.matrix.constructor import vector
            point = vector(point)
        return self._coeffs * point + self._const
Example #9
0
def support_function_ellipsoid(Q, d):
    r"""Compute support function of an ellipsoid.

    The ellipsoid is defined as `\max_{x in Q} \langle x, d \rangle`, where 
    `d` is a given vector. The ellipsoid is defined as the set 
    `\{ x \in \mathbb{R}^n : x^T Q x \leq 1\}`.

    INPUT:

    * ``Q`` - a square matrix

    * ``d`` - a vector (or list) where the support function is evaluated

    OUTPUT:

    The value of the support function at `d`.
    """
    if (Q.is_singular()):
        raise ValueError(
            "The coefficient matrix of the ellipsoid is not invertible.")

    if (type(d) == list):
        d = vector(d)

    return sqrt(d.inner_product((~Q) * d))
Example #10
0
def extreme_point (A, b, d) :
    r""" Extreme point.
    
    INPUT:

    * ``A``  - a matrix of M of size k x n (R)

    * ``b`` - a vector of Rk

    * ``d`` - a vector of Rn (direction)
    """
    from sage.numerical.mip import MixedIntegerLinearProgram
    
    p = MixedIntegerLinearProgram (maximization = True)
    x = p.new_variable ()
    p.add_constraint (A*x <= b)
    f = 0
    for i in range (0, d.length ()) :
        f = f + d[i]*x[i]
    p.set_objective (f)
    p.solve ()
    l = []
    for i in range (0, d.length ()) :
        l = l + [p.get_values (x[i])]
    return vector (l)
Example #11
0
def normal_vector (v) :
    r"""Normal vector.

    INPUT:

    * v - a 2d vector
    """
    return vector ([v[1], -v[0]])
Example #12
0
def chebyshev_center(P=None, A=None, b=None):
    r"""Compute the Chebyshev center of a polytope.

    The Chebyshev center of a polytope is the center of the largest hypersphere 
    enclosed by the polytope.

    INPUT:

    * ``A, b`` - Matrix and vector representing the polyhedron, as `Ax \leq b`.

    * ``P`` - Polyhedron object.

    OUTPUT:

    * The Chebyshev center, as a vector; the base ring of ``P`` preserved.

    """
    from sage.numerical.mip import MixedIntegerLinearProgram

    # parse input
    got_P, got_Ab = False, False
    if (P is not None):
        if (A is None) and (b is None):
            got_P = True
        elif (A is not None) and (b is None):
            b, A = A, P
            P = []
            got_Ab = True
    else:
        got_Ab = True if (A is not None and b is not None) else False

    if got_Ab or got_P:
        if got_P:
            [A, b] = polyhedron_to_Hrep(P)

        base_ring = A.base_ring()
        p = MixedIntegerLinearProgram(maximization=True)
        x = p.new_variable()
        r = p.new_variable()
        n = A.nrows()
        m = A.ncols()
        if not n == b.length():
            return []
        for i in range(0, n):
            v = A.row(i)
            norm = sqrt(v.dot_product(v))
            p.add_constraint(
                sum([v[j] * x[j] for j in range(0, m)]) + norm * r[0] <= b[i])
        f = sum([0 * x[i] for i in range(0, m)]) + 1 * r[0]
        p.set_objective(f)
        p.solve()
        cheby_center = [base_ring(p.get_values(x[i])) for i in range(0, m)]
        return vector(cheby_center)
    else:
        raise ValueError(
            'The input should be either as a Polyhedron, P, or as matrices, [A, b].'
        )
Example #13
0
def vertex_connections(P):
    r"""Return the vertices that are connected to each edge.
    
    INPUT: 
    
    "P" - a polygon (Polyhedron object in a two-dimensional ambient space). 
    
    OUTPUT: 
    
    A collection of lists ``li`` of integers, where each ``li`` corresponds to a pair of vertices. 
    The vertices are indexed with respect to ``P.vertices_list()``.
    
    NOTES: 
    
    - This has been tested in ``QQ`` and in ``RDF`` base rings. 
    - For ``RDF`` we have a ``tol`` parameter to decide if a vertex 
    satisfies a constraint. 
    """
    # tolerance for numerical error (for use in RDF only)
    tol = 1e-6

    got_QQ = True if P.base_ring() == QQ else False

    constraints_matrix = []

    # loop in the constraints
    for i, constraint in enumerate(P.inequalities_list()):

        # independent term and coefficient vector
        bi = constraint[0]
        ai = vector([constraint[1], constraint[2]])

        constraint_i_vertices = []

        for j, vj in enumerate(P.vertices_list()):

            if (got_QQ and (ai * vector(vj) + bi == 0)) or (
                    not got_QQ and abs(ai * vector(vj) + bi < tol)):
                constraint_i_vertices.append(j)
        constraints_matrix.append(constraint_i_vertices)

    return constraints_matrix
Example #14
0
    def path_to_vector_teich(self, path, edge_index_to_elevation):
        edge_coeffs = [0] * len(self._index_to_edge)
        elevation = 1
        for oe in path:
            index = self._edge_to_index[oe.edge]
            if oe.direction == -1:
                elevation /= edge_index_to_elevation[index]
            edge_coeffs[index] += elevation
            if oe.direction == 1:
                elevation *= edge_index_to_elevation[index]

        return (vector(edge_coeffs), elevation)
Example #15
0
def _asphericity_iteration(xk, l, m, p, A, a, B, b, verbose=0, solver='GLPK'):
    # find better way to share all those parameters, maybe a class

    #alphak = asphericity(xk)
    R_xk = max(A.row(i) * vector(xk) + a[i] for i in range(m))
    r_xk = min(B.row(j) * vector(xk) + b[j] for j in range(l))
    alphak = R_xk / r_xk

    a_LP = MixedIntegerLinearProgram(maximization=False, solver=solver)
    x = a_LP.new_variable(integer=False, nonnegative=False)
    z = a_LP.new_variable(integer=False, nonnegative=False)

    for i in range(m):
        for j in range(l):
            aux = sum(
                (A.row(i)[k] - alphak * B.row(j)[k]) * x[k] for k in range(p))
            a_LP.add_constraint(aux + a[i] - alphak * b[j] <= z[0])

    #solve LP
    a_LP.set_objective(z[0])

    if (verbose):
        print '**** Solve LP ****'
        a_LP.show()

    opt_val = a_LP.solve()

    x_opt = a_LP.get_values(x)
    z_opt = a_LP.get_values(z)

    if (verbose):
        print 'Objective Value:', opt_val
        for i, v in x_opt.iteritems():
            print 'x_%s = %f' % (i, v)

        for i, v in z_opt.iteritems():
            print 'z = %f' % (v)
        print '\n'

    return [opt_val, x_opt, z_opt]
Example #16
0
def circumradius(P, x):
    r"""Compute the radius of the box of smallest volume (= ball in the sup-norm)
    which contains P, with center at x.
    """

    if not (P.contains(x)):
        return NotImplementedError("The polytope should contain x.")

    (A, a, B, b, C, c, D, d) = dual_representation(P)

    m = len(c)

    return max(A.row(i) * vector(x) + a[i] for i in range(m))
Example #17
0
def inradius(P, x):
    r"""Compute the radius of the box of biggest volume (= ball in the sup-norm)
    which is contained P, with center at x.
    """

    if not (P.contains(x)):
        return NotImplementedError("The polytope should contain x.")

    (A, a, B, b, C, c, D, d) = dual_representation(P)

    l = D.nrows()

    return min(B.row(j) * vector(x) + b[j] for j in range(l))
Example #18
0
def diameter_vertex_enumeration(P, p=oo):
    r"""Compute the diameter of a polytope using the V-representation.

    EXAMPLES:

    By default, the supremum norm is used::
    
        sage: from polyhedron_tools.misc import diameter_vertex_enumeration
        sage: diameter_vertex_enumeration(7*polytopes.hypercube(5))
        14

    A custom `p`-norm can be specified as well::

        sage: diameter_vertex_enumeration(7*polytopes.hypercube(2), p=2)
        14*sqrt(2)
    """
    diam = 0
    vlist = P.vertices_list()
    num_vertices = len(vlist)
    for i in range(num_vertices):
        for j in range(i + 1, num_vertices):
            dist_vi_vj = (vector(vlist[i]) - vector(vlist[j])).norm(p=p)
            diam = max(diam, dist_vi_vj)
    return diam
Example #19
0
    def point(self):
        """
        Return the point closest to the origin.
        
        OUTPUT:

        A vector of the ambient vector space. The closest point to the
        origin in the `L^2`-norm.

        In finite characteristic a random point will be returned if
        the norm of the hyperplane normal vector is zero.
        
        EXAMPLES::


            sage: H.<x,y,z> = HyperplaneArrangements(QQ)
            sage: h = x + 2*y + 3*z - 4
            sage: h.point()
            (2/7, 4/7, 6/7)
            sage: h.point() in h
            True
        
            sage: H.<x,y,z> = HyperplaneArrangements(GF(3))
            sage: h = 2*x + y + z + 1
            sage: h.point()
            (1, 0, 0)
            sage: h.point().base_ring()
            Finite Field of size 3

            sage: H.<x,y,z> = HyperplaneArrangements(GF(3))
            sage: h = x + y + z + 1
            sage: h.point()
            (2, 0, 0)
        """
        P = self.parent()
        AA = P.ambient_module()
        R = P.base_ring()
        norm2 = sum(x ** 2 for x in self.A())
        if norm2 == 0:
            from sage.matrix.constructor import matrix, vector

            solution = matrix(R, self.A()).solve_right(vector(R, [-self.b()]))
        else:
            solution = [-x * self.b() / norm2 for x in self.A()]
        return AA(solution)
Example #20
0
    def point(self):
        """
        Return the point closest to the origin.
        
        OUTPUT:

        A vector of the ambient vector space. The closest point to the
        origin in the `L^2`-norm.

        In finite characteristic a random point will be returned if
        the norm of the hyperplane normal vector is zero.
        
        EXAMPLES::


            sage: H.<x,y,z> = HyperplaneArrangements(QQ)
            sage: h = x + 2*y + 3*z - 4
            sage: h.point()
            (2/7, 4/7, 6/7)
            sage: h.point() in h
            True
        
            sage: H.<x,y,z> = HyperplaneArrangements(GF(3))
            sage: h = 2*x + y + z + 1
            sage: h.point()
            (1, 0, 0)
            sage: h.point().base_ring()
            Finite Field of size 3

            sage: H.<x,y,z> = HyperplaneArrangements(GF(3))
            sage: h = x + y + z + 1
            sage: h.point()
            (2, 0, 0)
        """
        P = self.parent()
        AA = P.ambient_module()
        R = P.base_ring()
        norm2 = sum(x**2 for x in self.A())
        if norm2 == 0:
            from sage.matrix.constructor import matrix, vector
            solution = matrix(R, self.A()).solve_right(vector(R, [-self.b()]))
        else:
            solution = [-x * self.b() / norm2 for x in self.A()]
        return AA(solution)
def vanishing_space(p, Fqm=None, Fq=None):
    """
    Compute the vanishing space of the Ore polynomial p.
    """
    if not Fqm:
        Fqm = p.base_ring()
    if not Fq:
        Fq = Fqm.prime_subfield()
    m = Fqm.degree() // Fq.degree()
    d = p.degree()
    extension, to_big_field, from_big_field = Fqm.vector_space(Fq,
                                                               None,
                                                               map=True)
    basis = extension.basis_matrix()
    ev = from_matrix_representation(basis, Fqm)
    ev = vector(p.multi_point_evaluation(ev))
    ev = to_matrix_representation(ev, Fq)
    ev = ev.right_kernel()
    return ev
def matrice_systeme(systeme, variables):
    """
    Renvoie une matrice par block représentant un programme linéaire sous forme standard.

    INPUT::

    - ``systeme`` -- Un programme linéaire sous forme standard
    - ``variables`` -- La liste des variables du système

    EXAMPLES::

        sage: x = x1,x2,x3 = var('x1,x2,x3')
        sage: Chvatal13 = [[2*x1 + 3*x2 +   x3 <=  5,
        ....:               4*x1 +   x2 + 2*x3 <= 11,
        ....:               3*x1 + 4*x2 + 2*x3 <=  8],
        ....:               5*x1 + 4*x2 + 3*x3]

        sage: m = matrice_systeme(Chvatal13, x); m
        [ z|s1 s2 s3|x1 x2 x3| 0]
        [--+--------+--------+--]
        [ 1| 0  0  0|-5 -4 -3| 0]
        [--+--------+--------+--]
        [ 0| 1  0  0| 2  3  1| 5]
        [ 0| 0  1  0| 4  1  2|11]
        [ 0| 0  0  1| 3  4  2| 8]
    """

    def liste_coeffs(expression):
        return [expression.coeff(v) for v in variables]

    inequations = systeme[0]
    m = matrix([liste_coeffs(ineq.lhs()) for ineq in inequations])
    rhs = vector(ineq.rhs() for ineq in inequations).column()
    slack = SR.var(",".join("s%s" % i for i in range(1, len(inequations) + 1)))
    z = SR.var("z")
    return block_matrix(
        [
            [z, matrix([slack]), matrix([variables]), ZZ(0)],
            [ZZ(1), ZZ(0), -matrix([liste_coeffs(systeme[1])]), ZZ(0)],
            [ZZ(0), ZZ(1), m, rhs],
        ]
    )
Example #23
0
    def base_rays(self, fiber, points):
        """
        Return the primitive lattice vectors that generate the
        direction given by the base projection of points.

        INPUT:

        - ``fiber`` -- a sub-lattice polytope defining the
          :meth:`base_projection`.

        - ``points`` -- the points to project to the base.

        OUTPUT:

        A tuple of primitive `\ZZ`-vectors.

        EXAMPLES::

            sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
            sage: poly = LatticePolytope_PPL((-9,-6,-1,-1),(0,0,0,1),(0,0,1,0),(0,1,0,0),(1,0,0,0))
            sage: fiber = poly.fibration_generator(2).next()
            sage: poly.base_rays(fiber, poly.integral_points_not_interior_to_facets())
            ((-1, -1), (0, 1), (1, 0))

            sage: p = LatticePolytope_PPL((1,0),(1,2),(-1,0))
            sage: f = LatticePolytope_PPL((1,0),(-1,0))
            sage: p.base_rays(f, p.integral_points())
            ((1),)
        """
        quo = self.base_projection(fiber)
        vertices = []
        for p in points:
            v = vector(ZZ,quo(p))
            if v.is_zero():
                continue
            d =  GCD_list(v.list())
            if d>1:
                for i in range(0,v.degree()):
                    v[i] /= d
            v.set_immutable()
            vertices.append(v)
        return tuple(uniq(vertices))
Example #24
0
    def decomposition(self):
        """
        Find the decomposition of the matroid as a direct sum of indecomposable matroids.
        Return a partition of the groundset.
        Uses the algorithm of [PP19, Section 7].
        """
        B = self.basis()
        new_groundset = list(B) + list(self.groundset().difference(B))

        # construct matrix with permuted columns
        columns = [vector(self._A[:,self._groundset_to_index[e]]) for e in new_groundset]
        A = matrix(ZZ, columns).transpose().echelon_form()

        uf = DisjointSet(self.groundset())

        for i in xrange(A.nrows()):
            for j in xrange(i+1, A.ncols()):
                if A[i,j] != 0:
                    uf.union(new_groundset[i], new_groundset[j])

        return SetPartition(uf)
Example #25
0
    def _vectors_in_shell( F, bound, max = 1000):
        """
        For an (integral) positive symmetric matrix $F$
        return a list of vectors such that $x^tFx <= bound$.
        The method returns only nonzero vectors
        and for each pair $v$ and $-v$
        only one.

        NOTE
            A wrapper for the pari method qfminim().
        """
        # assert bound >=1, 'Pari bug: bound should be >= 2'
        if bound < 1: return []
        p = F._pari_()
        po = p.qfminim( bound, max, flag = 0).sage()
        if max == po[0]:
            raise ArithmeticError( 'Increase max')
        ves = [vector([0]*F.nrows())]
        for x in po[2].columns():
            ves += [x,-x]
        # return po[2].columns()
        return ves
Example #26
0
def intersection_point (a1, p1, a2, p2) :
    r"""Intersection point.

    INPUTS:

    * ``a1``, ``p1`` - a line defined as a1 x = a1 p1

    * ``a2``, ``p2`` - a line defined as a2 x = a2 p2

    NOTES:
    
    * we assume that a1 and a2 has to be 2d vectors.

    * if a1 and a2 are colinears, p2 is returned by convention.
    """

    M = matrix ([a1, a2])
    if (M.determinant () == 0) :
        return p2
    else :
        b = vector ([a1.dot_product(p1), a2.dot_product(p2)])
        return M.solve_right (b)
def matrice_systeme(systeme, variables):
    """
    Renvoie une matrice par block représentant un programme linéaire sous forme standard.

    INPUT::

    - ``systeme`` -- Un programme linéaire sous forme standard
    - ``variables`` -- La liste des variables du système

    EXAMPLES::

        sage: x = x1,x2,x3 = var('x1,x2,x3')
        sage: Chvatal13 = [[2*x1 + 3*x2 +   x3 <=  5,
        ....:               4*x1 +   x2 + 2*x3 <= 11,
        ....:               3*x1 + 4*x2 + 2*x3 <=  8],
        ....:               5*x1 + 4*x2 + 3*x3]

        sage: m = matrice_systeme(Chvatal13, x); m
        [ z|s1 s2 s3|x1 x2 x3| 0]
        [--+--------+--------+--]
        [ 1| 0  0  0|-5 -4 -3| 0]
        [--+--------+--------+--]
        [ 0| 1  0  0| 2  3  1| 5]
        [ 0| 0  1  0| 4  1  2|11]
        [ 0| 0  0  1| 3  4  2| 8]
    """
    def liste_coeffs(expression):
        return [expression.coeff(v) for v in variables]

    inequations = systeme[0]
    m = matrix([liste_coeffs(ineq.lhs()) for ineq in inequations])
    rhs = vector(ineq.rhs() for ineq in inequations).column()
    slack = SR.var(','.join("s%s" % i for i in range(1, len(inequations) + 1)))
    z = SR.var('z')
    return block_matrix(
        [[z, matrix([slack]), matrix([variables]),
          ZZ(0)], [ZZ(1),
                   ZZ(0), -matrix([liste_coeffs(systeme[1])]),
                   ZZ(0)], [ZZ(0), ZZ(1), m, rhs]])
    def __contains__(self, q):
        r"""
        Test whether the point ``q`` is in the affine space.

        INPUT:

        - ``q`` -- point as a list/tuple/iterable

        OUTPUT:

        A boolean.

        EXAMPLES::

            sage: from sage.geometry.hyperplane_arrangement.affine_subspace import AffineSubspace
            sage: a = AffineSubspace([1,0,0], matrix([[1,0,0]]).right_kernel())
            sage: (1,1,0) in a
            True
            sage: (0,0,0) in a
            False
        """
        q = vector(self._base_ring, q)
        return self._point - q in self._linear_part
Example #29
0
    def __contains__(self, q):
        r"""
        Test whether the point ``q`` is in the affine space.

        INPUT:

        - ``q`` -- point as a list/tuple/iterable

        OUTPUT:

        A boolean.

        EXAMPLES::

            sage: from sage.geometry.hyperplane_arrangement.affine_subspace import AffineSubspace
            sage: a = AffineSubspace([1,0,0], matrix([[1,0,0]]).right_kernel())
            sage: (1,1,0) in a
            True
            sage: (0,0,0) in a
            False
        """
        q = vector(self._base_ring, q)
        return self._point - q in self._linear_part
Example #30
0
    def cohomology(self, degree=None, weight=None, dim=False):
        r"""
        Return the bundle cohomology groups.

        INPUT:

        - ``degree`` -- ``None`` (default) or an integer. The degree of
          the cohomology group.

        - ``weight`` -- ``None`` (default) or a tuple of integers or a
          `M`-lattice point. A point in the dual lattice of the fan
          defining a torus character. The weight of the cohomology
          group.

        - ``dim`` -- Boolean (default: ``False``). Whether to return
          vector spaces or only their dimension.

        OUTPUT:

        The cohomology group of given cohomological ``degree`` and
        torus ``weight``.

        * If no ``weight`` is specified, the unweighted group (sum
          over all weights) is returned.

        * If no ``degree`` is specified, a dictionary whose keys are
          integers and whose values are the cohomology groups is
          returned. If, in addition, ``dim=True``, then an integral
          vector of the dimensions is returned.

        EXAMPLES::

            sage: V = toric_varieties.P2().sheaves.tangent_bundle()
            sage: V.cohomology(degree=0, weight=(0,0))
            Vector space of dimension 2 over Rational Field
            sage: V.cohomology(weight=(0,0), dim=True)
            (2, 0, 0)
            sage: for i,j in cartesian_product((list(range(-2,3)), list(range(-2,3)))):
            ....:       HH = V.cohomology(weight=(i,j), dim=True)
            ....:       if HH.is_zero(): continue
            ....:       print('H^*i(P^2, TP^2)_M({}, {}) = {}'.format(i,j,HH))
            H^*i(P^2, TP^2)_M(-1, 0) = (1, 0, 0)
            H^*i(P^2, TP^2)_M(-1, 1) = (1, 0, 0)
            H^*i(P^2, TP^2)_M(0, -1) = (1, 0, 0)
            H^*i(P^2, TP^2)_M(0, 0) = (2, 0, 0)
            H^*i(P^2, TP^2)_M(0, 1) = (1, 0, 0)
            H^*i(P^2, TP^2)_M(1, -1) = (1, 0, 0)
            H^*i(P^2, TP^2)_M(1, 0) = (1, 0, 0)
        """
        from sage.modules.all import FreeModule
        if weight is None:
            raise NotImplementedError('sum over weights is not implemented')
        else:
            weight = self.variety().fan().dual_lattice()(weight)
            weight.set_immutable()
        if degree is not None:
            return self.cohomology(weight=weight, dim=dim)[degree]
        C = self.cohomology_complex(weight)
        space_dim = self._variety.dimension()
        C_homology = C.homology()
        HH = dict()
        for d in range(space_dim + 1):
            try:
                HH[d] = C_homology[d]
            except KeyError:
                HH[d] = FreeModule(self.base_ring(), 0)
        if dim:
            HH = vector(ZZ, [HH[i].rank() for i in range(space_dim + 1)])
        return HH
Example #31
0
def polyhedron_from_Hrep(A, b, base_ring=QQ):
    r"""Builds a polytope given the H-representation, in the form `Ax \leq b`

    INPUT:

    * ``A`` - matrix of size m x n, in RDF or QQ ring. Accepts generic Sage matrix, and also a Numpy arrays with a matrix shape.

    * ``b`` - vector of size m, in RDF or QQ ring. Accepts generic Sage matrix, and also a Numpy array.

    * ``base_ring`` - (default: ``QQ``). Specifies the ring (base_ring) for the Polyhedron constructor. Valid choices are:

        * ``QQ`` - rational. Uses ``'ppl'`` (Parma Polyhedra Library) backend

        * ``RDF`` - Real double field. Uses ``'cdd'`` backend.

    OUTPUT:

    * "P" - a Polyhedron object

    TO-DO:

    * accept numpy arrays. notice that we often handle numpy arrays (for instance if we load some data from matlab using the function
    ``scipy.io.loadmat(..)``, then the data will be loaded as a dictionary of numpy arrays)

    EXAMPLES::

        sage: A = matrix(RDF, [[-1.0, 0.0,  0.0,  0.0,  0.0,  0.0],
        ....: [ 1.0,  0.0,  0.0,  0.0,  0.0,  0.0],
        ....: [ 0.0,  1.0,  0.0,  0.0,  0.0,  0.0],
        ....: [ 0.0, -1.0,  0.0,  0.0,  0.0,  0.0],
        ....: [ 0.0,  0.0, -1.0,  0.0,  0.0,  0.0],
        ....: [ 0.0,  0.0,  1.0,  0.0,  0.0,  0.0],
        ....: [ 0.0,  0.0,  0.0, -1.0,  0.0,  0.0],
        ....: [ 0.0,  0.0,  0.0,  1.0,  0.0,  0.0],
        ....: [ 0.0,  0.0,  0.0,  0.0,  1.0,  0.0],
        ....: [ 0.0,  0.0,  0.0,  0.0, -1.0,  0.0],
        ....: [ 0.0,  0.0,  0.0,  0.0,  0.0,  1.0],
        ....: [ 0.0,  0.0,  0.0,  0.0,  0.0, -1.0]])
        sage: b = vector(RDF, [0.0, 10.0, 0.0, 0.0, 0.2, 0.2, 0.1, 0.1, 0.0, 0.0, 0.0, 0.0])
        sage: from polyhedron_tools.misc import polyhedron_from_Hrep
        sage: P = polyhedron_from_Hrep(A, b, base_ring=QQ); P
        A 3-dimensional polyhedron in QQ^6 defined as the convex hull of 8 vertices

    NOTES:

    - This function is useful especially when the input matrices `A`, `b` are ill-defined 
    (constraints that differ by tiny amounts making the input data to be degenerate or almost degenerate), 
    causing problems to Polyhedron(...). 
    
    - In this case it is recommended to use ``base_ring = QQ``. Each element of `A` and `b` will be converted to rational, and this will be sent to Polyhedron.
     Note that Polyhedron automatically removes redundant constraints.
    """

    if (base_ring == RDF):

        if 'numpy.ndarray' in str(type(A)):
            # assuming that b is also a numpy array
            m = A.shape[0]
            n = A.shape[1]

            b_RDF = vector(RDF, m, [RDF(bi) for bi in b])

            A_RDF = matrix(RDF, m, n)
            for i in range(m):
                A_RDF.set_row(i, [RDF(A[i][j]) for j in range(n)])

            A = copy(A_RDF)
            b = copy(b_RDF)

        ambient_dim = A.ncols()

        # transform to real, if needed
        if A.base_ring() != RDF:
            A.change_ring(RDF)

        if b.base_ring() != RDF:
            b.change_ring(RDF)

        ieqs_list = []
        for i in range(A.nrows()):
            ieqs_list.append(
                list(-A.row(i))
            )  #change in sign, necessary since Polyhedron receives Ax+b>=0
            ieqs_list[i].insert(0, b[i])

        P = Polyhedron(ieqs=ieqs_list,
                       base_ring=RDF,
                       ambient_dim=A.ncols(),
                       backend='cdd')

    elif (base_ring == QQ):

        if 'numpy.ndarray' in str(type(A)):
            # assuming that b is also a numpy array
            m = A.shape[0]
            n = A.shape[1]

            b_QQ = vector(QQ, m, [QQ(b[i]) for i in range(m)])
            A_QQ = matrix(QQ, m, n)

            for i in range(m):
                A_QQ.set_row(i, [QQ(A[i][j]) for j in range(n)])

            A = copy(A_QQ)
            b = copy(b_QQ)

        ambient_dim = A.ncols()

        # transform to rational, if needed
        if A.base_ring() != QQ:
            #for i in range(A.nrows()):
            #    A.set_row(i,[QQ(A.row(i)[j]) for j in range(ambient_dim)]);
            A.change_ring(QQ)

        if b.base_ring() != QQ:
            #b = vector(QQ, [QQ(bi) for bi in b]);
            b.change_ring(QQ)

        ieqs_list = []
        for i in range(A.nrows()):
            ieqs_list.append(
                list(-A.row(i))
            )  #change in sign, necessary since Polyhedron receives Ax+b>=0
            ieqs_list[i].insert(0, b[i])

        P = Polyhedron(ieqs=ieqs_list,
                       base_ring=QQ,
                       ambient_dim=A.ncols(),
                       backend='ppl')

    else:
        raise ValueError('Base ring not supported. Try with RDF or QQ.')

    return P
Example #32
0
    def poset_of_layers(self):
        """
        Compute the poset of layers of the associated toric arrangement, using Lenz's algorithm [Len17a].
        """
        # TODO: implement for Q != 0
        if self._Q.ncols() > 0:
            raise NotImplementedError


        A = self._A.transpose()
        E = range(A.nrows())

        data = {}

        # compute Smith normal forms of all submatrices
        for S in powerset(E):
            D, U, V = A[S,:].smith_form()   # D == U*A[S,:]*V
            diagonal = [D[i,i] if i < D.ncols() else 0 for i in xrange(len(S))]
            data[tuple(S)] = (diagonal, U)

        # generate al possible elements of the poset of layers
        elements = {tuple(S): list(vector(ZZ, x) for x in itertools.product(*(range(max(data[tuple(S)][0][i], 1)) for i in xrange(len(S))))) for S in powerset(E)}

        for l in elements.itervalues():
            for v in l:
                v.set_immutable()

        possible_layers = list((S, x) for (S, l) in elements.iteritems() for x in l)
        uf = DisjointSet(possible_layers)

        cover_relations = []

        for (S, l) in elements.iteritems():
            diagonal_S, U_S = data[S]
            rk_S = A[S,:].rank()

            for s in S:
                i = S.index(s)  # index where the element s appears in S
                T = tuple(t for t in S if t != s)

                diagonal_T, U_T = data[T]
                rk_T = A[T,:].rank()

                for x in l:
                    h = (S, x)

                    y = U_S**(-1) * x
                    z = U_T * vector(ZZ, y[:i].list() + y[i+1:].list())
                    w = vector(ZZ, (a % diagonal_T[j] if diagonal_T[j] > 0 else 0 for j, a in enumerate(z)))
                    w.set_immutable()

                    ph = (T, w)

                    if rk_S == rk_T:
                        uf.union(h, ph)

                    else:
                        cover_relations.append((ph, h))

        # find representatives for layers (we keep the representative (S,x) with maximal S)
        root_to_representative_dict = {}
        for root, subset in uf.root_to_elements_dict().iteritems():
            S, x = max(subset, key=lambda (S, x): len(S))
            S_labeled = tuple(self._E[i] for i in S)
            root_to_representative_dict[root] = (S_labeled, x)

        # get layers and cover relations
        layers = root_to_representative_dict.values()
        cover_relations = set(
            (root_to_representative_dict[uf.find(a)], root_to_representative_dict[uf.find(b)])
            for (a,b) in cover_relations)

        return Poset(data=(layers, cover_relations), cover_relations=True)
Example #33
0
    def integral_points(self, threshold=10000):
        r"""
        Return the integral points in the polyhedron.

        Uses either the naive algorithm (iterate over a rectangular
        bounding box) or triangulation + Smith form.

        INPUT:

        - ``threshold`` -- integer (default: 10000); use the naïve
          algorithm as long as the bounding box is smaller than this

        OUTPUT:

        The list of integral points in the polyhedron. If the
        polyhedron is not compact, a ``ValueError`` is raised.

        EXAMPLES::

            sage: Polyhedron(vertices=[(-1,-1), (1,0), (1,1), (0,1)],      # optional - pynormaliz
            ....:            backend='normaliz').integral_points()
            ((-1, -1), (0, 0), (0, 1), (1, 0), (1, 1))

            sage: simplex = Polyhedron([(1,2,3), (2,3,7), (-2,-3,-11)],    # optional - pynormaliz
            ....:                      backend='normaliz')
            sage: simplex.integral_points()                                # optional - pynormaliz
            ((-2, -3, -11), (0, 0, -2), (1, 2, 3), (2, 3, 7))

        The polyhedron need not be full-dimensional::

            sage: simplex = Polyhedron([(1,2,3,5), (2,3,7,5), (-2,-3,-11,5)],   # optional - pynormaliz
            ....:                      backend='normaliz')
            sage: simplex.integral_points()                                # optional - pynormaliz
            ((-2, -3, -11, 5), (0, 0, -2, 5), (1, 2, 3, 5), (2, 3, 7, 5))

            sage: point = Polyhedron([(2,3,7)],                            # optional - pynormaliz
            ....:                    backend='normaliz')
            sage: point.integral_points()                                  # optional - pynormaliz
            ((2, 3, 7),)

            sage: empty = Polyhedron(backend='normaliz')                   # optional - pynormaliz
            sage: empty.integral_points()                                  # optional - pynormaliz
            ()

        Here is a simplex where the naive algorithm of running over
        all points in a rectangular bounding box no longer works fast
        enough::

            sage: v = [(1,0,7,-1), (-2,-2,4,-3), (-1,-1,-1,4), (2,9,0,-5), (-2,-1,5,1)]
            sage: simplex = Polyhedron(v, backend='normaliz'); simplex     # optional - pynormaliz
            A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices
            sage: len(simplex.integral_points())                           # optional - pynormaliz
            49

        A rather thin polytope for which the bounding box method would
        be a very bad idea (note this is a rational (non-lattice)
        polytope, so the other backends use the bounding box method)::

            sage: P = Polyhedron(vertices=((0, 0), (178933,37121))) + 1/1000*polytopes.hypercube(2)
            sage: P = Polyhedron(vertices=P.vertices_list(),               # optional - pynormaliz
            ....:                backend='normaliz')
            sage: len(P.integral_points())                                 # optional - pynormaliz
            434

        Finally, the 3-d reflexive polytope number 4078::

            sage: v = [(1,0,0), (0,1,0), (0,0,1), (0,0,-1), (0,-2,1),
            ....:      (-1,2,-1), (-1,2,-2), (-1,1,-2), (-1,-1,2), (-1,-3,2)]
            sage: P = Polyhedron(v, backend='normaliz')                    # optional - pynormaliz
            sage: pts1 = P.integral_points()                               # optional - pynormaliz
            sage: all(P.contains(p) for p in pts1)                         # optional - pynormaliz
            True
            sage: pts2 = LatticePolytope(v).points()          # PALP
            sage: for p in pts1: p.set_immutable()                         # optional - pynormaliz
            sage: set(pts1) == set(pts2)                                   # optional - pynormaliz
            True

            sage: timeit('Polyhedron(v, backend='normaliz').integral_points()')   # not tested - random
            625 loops, best of 3: 1.41 ms per loop
            sage: timeit('LatticePolytope(v).points()')       # not tested - random
            25 loops, best of 3: 17.2 ms per loop

        TESTS:

        Test some trivial cases (see :trac:`17937`):

        Empty polyhedron in 1 dimension::

            sage: P = Polyhedron(ambient_dim=1, backend='normaliz')        # optional - pynormaliz
            sage: P.integral_points()                                      # optional - pynormaliz
            ()

        Empty polyhedron in 0 dimensions::

            sage: P = Polyhedron(ambient_dim=0, backend='normaliz')        # optional - pynormaliz
            sage: P.integral_points()                                      # optional - pynormaliz
            ()

        Single point in 1 dimension::

            sage: P = Polyhedron([[3]], backend='normaliz')                # optional - pynormaliz
            sage: P.integral_points()                                      # optional - pynormaliz
            ((3),)

        Single non-integral point in 1 dimension::

            sage: P = Polyhedron([[1/2]], backend='normaliz')              # optional - pynormaliz
            sage: P.integral_points()                                      # optional - pynormaliz
            ()

        Single point in 0 dimensions::

            sage: P = Polyhedron([[]], backend='normaliz')                 # optional - pynormaliz
            sage: P.integral_points()                                      # optional - pynormaliz
            ((),)

        A polytope with no integral points (:trac:`22938`)::

            sage: ieqs = [[1, 2, -1, 0], [0, -1, 2, -1], [0, 0, -1, 2],
            ....:         [0, -1, 0, 0], [0, 0, -1, 0],  [0, 0, 0, -1],
            ....:         [-1, -1, -1, -1], [1, 1, 0, 0], [1, 0, 1, 0],
            ....:         [1, 0, 0, 1]]
            sage: P = Polyhedron(ieqs=ieqs, backend='normaliz')            # optional - pynormaliz
            sage: P.bounding_box()                                         # optional - pynormaliz
            ((-3/4, -1/2, -1/4), (-1/2, -1/4, 0))
            sage: P.bounding_box(integral_hull=True)                       # optional - pynormaliz
            (None, None)
            sage: P.integral_points()                                      # optional - pynormaliz
            ()
        """
        import PyNormaliz
        if not self.is_compact():
            raise ValueError(
                'can only enumerate points in a compact polyhedron')
        # Trivial cases: polyhedron with 0 or 1 vertices
        if self.n_vertices() == 0:
            return ()
        if self.n_vertices() == 1:
            v = self.vertices_list()[0]
            try:
                return (vector(ZZ, v), )
            except TypeError:  # vertex not integral
                return ()
        # for small bounding boxes, it is faster to naively iterate over the points of the box
        if threshold > 1:
            box_min, box_max = self.bounding_box(integral_hull=True)
            if box_min is None:
                return ()
            box_points = prod(
                max_coord - min_coord + 1
                for min_coord, max_coord in zip(box_min, box_max))
            if box_points < threshold:
                from sage.geometry.integral_points import rectangular_box_points
                return rectangular_box_points(list(box_min), list(box_max),
                                              self)
        # Compute with normaliz
        points = []
        cone = self._normaliz_cone
        assert cone
        for g in PyNormaliz.NmzResult(cone, "ModuleGenerators"):
            assert g[-1] == 1
            points.append(vector(ZZ, g[:-1]))
        return tuple(points)
Example #34
0
    def cohomology(self, degree=None, weight=None, dim=False):
        r"""
        Return the bundle cohomology groups.

        INPUT:

        - ``degree`` -- ``None`` (default) or an integer. The degree of
          the cohomology group.

        - ``weight`` -- ``None`` (default) or a tuple of integers or a
          `M`-lattice point. A point in the dual lattice of the fan
          defining a torus character. The weight of the cohomology
          group.

        - ``dim`` -- Boolean (default: ``False``). Whether to return
          vector spaces or only their dimension.

        OUTPUT:

        The cohomology group of given cohomological ``degree`` and
        torus ``weight``.

        * If no ``weight`` is specified, the unweighted group (sum
          over all weights) is returned.

        * If no ``degree`` is specified, a dictionary whose keys are
          integers and whose values are the cohomology groups is
          returned. If, in addition, ``dim=True``, then an integral
          vector of the dimensions is returned.

        EXAMPLES::

            sage: V = toric_varieties.P2().sheaves.tangent_bundle()
            sage: V.cohomology(degree=0, weight=(0,0))
            Vector space of dimension 2 over Rational Field
            sage: V.cohomology(weight=(0,0), dim=True)
            (2, 0, 0)
            sage: for i,j in cartesian_product((range(-2,3), range(-2,3))):
            ....:       HH = V.cohomology(weight=(i,j), dim=True)
            ....:       if HH.is_zero(): continue
            ....:       print('H^*i(P^2, TP^2)_M({}, {}) = {}'.format(i,j,HH))
            H^*i(P^2, TP^2)_M(-1, 0) = (1, 0, 0)
            H^*i(P^2, TP^2)_M(-1, 1) = (1, 0, 0)
            H^*i(P^2, TP^2)_M(0, -1) = (1, 0, 0)
            H^*i(P^2, TP^2)_M(0, 0) = (2, 0, 0)
            H^*i(P^2, TP^2)_M(0, 1) = (1, 0, 0)
            H^*i(P^2, TP^2)_M(1, -1) = (1, 0, 0)
            H^*i(P^2, TP^2)_M(1, 0) = (1, 0, 0)
        """
        from sage.modules.all import FreeModule
        if weight is None:
            raise NotImplementedError('sum over weights is not implemented')
        else:
            weight = self.variety().fan().dual_lattice()(weight)
            weight.set_immutable()
        if degree is not None:
            return self.cohomology(weight=weight, dim=dim)[degree]
        C = self.cohomology_complex(weight)
        space_dim = self._variety.dimension()
        C_homology = C.homology()
        HH = dict()
        for d in range(0, space_dim+1):
            try:
                HH[d] = C_homology[d]
            except KeyError:
                HH[d] = FreeModule(self.base_ring(), 0)
        if dim:
            HH = vector(ZZ, [HH[i].rank() for i in range(0, space_dim+1) ])
        return HH
def make_positive(vec):
    """
    Returns a parallel vector to a given vector with all positive
    coordinates if possible.

    INPUT:

    - ``vec`` - a vector or list or tuple of numbers

    OUTPUT:

    - vector - a vector with positive coordinates if this is possible,
      otherwise -1

    EXAMPLES:

    If all coordinates of the input vector are already positive, the
    same vector is returned::

        sage: from sage.dynamics.foliations.foliation import make_positive
        sage: v = vector([1, 3, 5])
        sage: make_positive(v) == v
        True

    If all are negative, its negative is returned::

        sage: make_positive((-1, -5))
        (1, 5)

    Even if the coordinates are complex, but have very small imaginary
    part as a result of an approximate eigenvector calculation for 
    example, the coordinates are treated as real::

        sage: make_positive((40.24 - 5.64e-16*I, 1.2 + 4.3e-14*I))
        (40.2400000000000, 1.20000000000000)
        sage: make_positive((-40.24 - 5.64e-16*I, -1.2 + 4.3e-14*I))
        (40.2400000000000, 1.20000000000000)

    If there is a complex coordinate which is not negligible, -1 is
    returned::

        sage: make_positive((-40.24 - 5.64e-6*I, -1.2 + 4.3e-14*I))
        -1

    If one coordinate is zero, or very close to zero, -1 is returned::

        sage: make_positive((1, 0, 2))
        -1
        sage: make_positive((-40.24e-15*I - 5.64e-16*I, -1.2))
        -1

    If there are both negative and positive coordinates, -1 is 
    returned::

        sage: make_positive((-3, 4, 5))
        -1
        
    """
    if any(abs(x) < EPSILON for x in vec) or \
        any(abs(x.imag()) > EPSILON for x in vec):
            return -1
    newvec = vector([x.real() for x in vec])
    if vec[0].real() < 0:
        newvec = -newvec
    if any(x < 0 for x in newvec):
        return -1
    return newvec
Example #36
0
def lotov_algo (A, b, v1, v2, err, rel=1) :
    r""" Approximate two-dimensional projection of a convex polytope by 
    Lotov's algorithm.

    INPUT:

    * ``A`` - a matrix of M of size k x n (R)

    * ``b`` - a vector of Rk

    * ``v1``, ``v2`` - two vectors of R^n

    * ``err`` ( > 0) - the maximal error

    * ``rel`` - true if the error is relative (err in ]0, 1]), and 
    false if absolute (err > 0)

"""
    # init
    err = float (err)
    Mnd2d = matrix ([v1, v2])
    M2dnd = Mnd2d.transpose ()

    # 1) first approximation
    dn2d = vector ([0, 1])
    dw2d = vector ([-1, 0])
    ds2d = vector ([0, -1])
    de2d = vector ([1, 0])
    dnnd = M2dnd * dn2d
    dwnd = M2dnd * dw2d
    dsnd = M2dnd * ds2d
    dend = M2dnd * de2d

    # border points computation
    #print dnnd[0], ' ', dnnd[1], ' ', dnnd[2]
    n = Mnd2d * extreme_point (A, b, dnnd)
    w = Mnd2d * extreme_point (A, b, dwnd)
    s = Mnd2d * extreme_point (A, b, dsnd)
    e = Mnd2d * extreme_point (A, b, dend)
    # calculus of the bounding-box and epsilon if relative err
    if rel :
        xMin = w[0]
        xMax = e[0]
        yMin = s[1]
        yMax = n[1]
        err = max ([xMax - xMin, yMax - yMin]) * err
    # first underapproximation
    fi = [[n, w], [w, s], [s, e], [e, n]]
    # first overapproximation
    oi = [intersection_point (dn2d, n, dw2d, w), intersection_point (dw2d, w, ds2d, s), intersection_point (ds2d, s, de2d, e), intersection_point (de2d, e, dn2d, n)]
    # distances calculus
    di = []
    iMax = 0
    k = 4
    for i in range (0, k) :
        di = di + [point_line_distance (fi[i], oi[i])]
        if (di[i] > di[iMax]) :
            iMax = i

    # 2) successive approximations
    while (di[iMax] > err) :
        #print 'k', k
        # refinement
        f = fi[iMax]
        p1 = f[0]
        p2 = f[1]
        d2d = normal_vector (p2 - p1)
        dnd = M2dnd * d2d
        pnd = extreme_point (A, b, dnd)
        p = Mnd2d * pnd
        q = intersection_point (normal_vector (p1 - oi[previous_i (iMax, k)]), p1, d2d, p)
        r = intersection_point (normal_vector (oi[next_i (iMax, k)] - p2), p2, d2d, p)
        # lists update
        fi = [fi[i] for i in range (0, iMax)] + [[p1, p], [p, p2]] + [fi[i] for i in range (iMax + 1, k)]
        oi = [oi[i] for i in range (0, iMax)] + [q, r] + [oi[i] for i in range (iMax + 1, k)]
        dq = point_line_distance ([p1, p], q)
        dr = point_line_distance ([p2, p], r)
        di = [di[i] for i in range (0, iMax)] + [dq, dr] + [di[i] for i in range (iMax + 1, k)]
        k = k + 1
        # next iMax calculus
        iMax = 0
        for i in range (1, k) :
            if (di[i] > di[iMax]) :
                iMax = i

    # result packaging and return
    ui = []
    for i in range (0, len (fi)) :
        ui = ui + [fi[i][0]]
    return [oi, ui]
Example #37
0
def asphericity_polytope(P, tol=1e-1, MaxIter=1e2, verbose=0, solver='GLPK'):
    r""" Algorithm for computing asphericity

    INPUT:

    ``x0`` - point in the interior of P

    ``tol`` - stop condition

    OUTPUT:

    ``psi_opt`` -  `\min_{x \in P} psi(x) = R(x)/r(x)`, where `R(x)` and `r(x)` are the circumradius
                and the inradius at `x` respectively

    ``alphakList`` - sequence of `\psi(x)` which converges to `\psi_{\text{opt}}`

    ``xkList`` - sequence of interior points which converge to the minimum
    """
    # check if the polytope is not full-dimensional -- in this case the notion
    # of asphericity still makes sense but there is yet no "affine function" (or
    # "relative interior") method from the Polyhedron class that we can use
    # see :trac:16045
    if not P.is_full_dimensional():
        raise NotImplementedError('The polytope is not full-dimensional')

    # initial data
    p = P.ambient_dim()

    [DP, d] = polyhedron_to_Hrep(P)  # in the form Dy <= d
    D = -DP  # transform to <Dj,y> + dj >= 0
    l = D.nrows()
    mP = opposite_polyhedron(P)

    # unit ball, infinity norm
    Bn = BoxInfty(center=zero_vector(RDF, p), radius=1)
    [C, c] = polyhedron_to_Hrep(Bn)
    C = -C  # transform to <Cj,y> + cj >= 0
    m = len(c)

    # auxiliary matrices A, a, B, b
    A = matrix(RDF, C.nrows(), C.ncols())
    a = []
    for i in range(m):
        A.set_row(i, -C.row(i) / c[i])
        a.append(support_function(mP, A.row(i), solver=solver))

    B = matrix(RDF, D.nrows(), D.ncols())
    b = []
    for j in range(l):
        [nDj, ymax] = support_function(Bn,
                                       D.row(j),
                                       return_xopt=True,
                                       solver=solver)
        B.set_row(j, D.row(j) / nDj)
        b.append(d[j] / nDj)

    # generate an initial point
    x0 = chebyshev_center(DP, d)

    # compute asphericity at x0
    R = max(A.row(i) * vector(x0) + a[i] for i in range(m))
    r = min(B.row(j) * vector(x0) + b[j] for j in range(l))
    alpha0 = R / r

    xkList = [vector(x0)]
    alphakList = [alpha0]

    xk0 = x0
    psi_k0 = alpha0
    alphak0 = alpha0
    convergeFlag = 0
    iterCount = 0
    while (iterCount < MaxIter) and (not convergeFlag):
        [psi_k, xkDict, zkDict] = _asphericity_iteration(xk0,
                                                         l,
                                                         m,
                                                         p,
                                                         A,
                                                         a,
                                                         B,
                                                         b,
                                                         verbose=verbose,
                                                         solver=solver)
        xk = vector(xkDict)
        xkList.append(xk)
        zk = vector(zkDict)
        alphakList.append(zk[0])
        xk0 = xk
        if (abs(psi_k - psi_k0) < tol):
            convergeFlag = 1
        psi_k0 = psi_k
        iterCount += 1

    x_asph = xkList.pop()
    R_asph = max(A.row(i) * vector(x_asph) + a[i] for i in range(m))
    r_asph = min(B.row(j) * vector(x_asph) + b[j] for j in range(l))
    asph = R_asph / r_asph

    return [asph, x_asph]
Example #38
0
    def lattice_automorphism_group(self, points=None, point_labels=None):
        """
        The integral subgroup of the restricted automorphism group.

        INPUT:

        - ``points`` -- A tuple of coordinate vectors or ``None``
          (default). If specified, the points must form complete
          orbits under the lattice automorphism group. If ``None`` all
          vertices are used.

        - ``point_labels`` -- A tuple of labels for the ``points`` or
          ``None`` (default). These will be used as labels for the do
          permutation group. If ``None`` the ``points`` will be used
          themselves.

        OUTPUT:

        The integral subgroup of the restricted automorphism group
        acting on the given ``points``, or all vertices if not
        specified.

        EXAMPLES::

            sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
            sage: Z3square = LatticePolytope_PPL((0,0), (1,2), (2,1), (3,3))
            sage: Z3square.lattice_automorphism_group()
            Permutation Group with generators [(), ((1,2),(2,1)),
            ((0,0),(3,3)), ((0,0),(3,3))((1,2),(2,1))]

            sage: G1 = Z3square.lattice_automorphism_group(point_labels=(1,2,3,4));  G1
            Permutation Group with generators [(), (2,3), (1,4), (1,4)(2,3)]
            sage: G1.cardinality()
            4

            sage: G2 = Z3square.restricted_automorphism_group(vertex_labels=(1,2,3,4)); G2
            Permutation Group with generators [(2,3), (1,2)(3,4), (1,4)]
            sage: G2.cardinality()
            8

            sage: points = Z3square.integral_points();  points
            ((0, 0), (1, 1), (1, 2), (2, 1), (2, 2), (3, 3))
            sage: Z3square.lattice_automorphism_group(points, point_labels=(1,2,3,4,5,6))
            Permutation Group with generators [(), (3,4), (1,6)(2,5), (1,6)(2,5)(3,4)]
        """
        if not self.is_full_dimensional():
            return self.affine_lattice_polytope().lattice_automorphism_group()

        if points is None:
            points = self.vertices()
        if point_labels is None:
            point_labels = tuple(points)
        points = [ vector(ZZ, [1]+v.list()) for v in points ]
        map(lambda x:x.set_immutable(), points)

        vertices = [ vector(ZZ, [1]+v.list()) for v in self.vertices() ]
        pivots = matrix(ZZ, vertices).pivot_rows()
        basis = matrix(ZZ, [ vertices[i] for i in pivots ])
        Mat_ZZ = basis.parent()
        basis_inverse = basis.inverse()

        from sage.groups.perm_gps.permgroup import PermutationGroup
        from sage.groups.perm_gps.permgroup_element import PermutationGroupElement
        lattice_gens = []
        G = self.restricted_automorphism_group(
            vertex_labels=tuple(range(len(vertices))))
        for g in G:
            image = matrix(ZZ, [ vertices[g(i)] for i in pivots ])
            m = basis_inverse*image
            if m not in Mat_ZZ:
                continue
            perm_list = [ point_labels[points.index(p*m)]
                          for p in points ]
            lattice_gens.append(perm_list)
        return PermutationGroup(lattice_gens, domain=point_labels)
Example #39
0
    def integral_points(self, threshold=10000):
        r"""
        Return the integral points in the polyhedron.

        Uses either the naive algorithm (iterate over a rectangular
        bounding box) or triangulation + Smith form.

        INPUT:

        - ``threshold`` -- integer (default: 10000); use the naïve
          algorithm as long as the bounding box is smaller than this

        OUTPUT:

        The list of integral points in the polyhedron. If the
        polyhedron is not compact, a ``ValueError`` is raised.

        EXAMPLES::

            sage: Polyhedron(vertices=[(-1,-1), (1,0), (1,1), (0,1)],      # optional - pynormaliz
            ....:            backend='normaliz').integral_points()
            ((-1, -1), (0, 0), (0, 1), (1, 0), (1, 1))

            sage: simplex = Polyhedron([(1,2,3), (2,3,7), (-2,-3,-11)],    # optional - pynormaliz
            ....:                      backend='normaliz')
            sage: simplex.integral_points()                                # optional - pynormaliz
            ((-2, -3, -11), (0, 0, -2), (1, 2, 3), (2, 3, 7))

        The polyhedron need not be full-dimensional::

            sage: simplex = Polyhedron([(1,2,3,5), (2,3,7,5), (-2,-3,-11,5)],   # optional - pynormaliz
            ....:                      backend='normaliz')
            sage: simplex.integral_points()                                # optional - pynormaliz
            ((-2, -3, -11, 5), (0, 0, -2, 5), (1, 2, 3, 5), (2, 3, 7, 5))

            sage: point = Polyhedron([(2,3,7)],                            # optional - pynormaliz
            ....:                    backend='normaliz')
            sage: point.integral_points()                                  # optional - pynormaliz
            ((2, 3, 7),)

            sage: empty = Polyhedron(backend='normaliz')                   # optional - pynormaliz
            sage: empty.integral_points()                                  # optional - pynormaliz
            ()

        Here is a simplex where the naive algorithm of running over
        all points in a rectangular bounding box no longer works fast
        enough::

            sage: v = [(1,0,7,-1), (-2,-2,4,-3), (-1,-1,-1,4), (2,9,0,-5), (-2,-1,5,1)]
            sage: simplex = Polyhedron(v, backend='normaliz'); simplex     # optional - pynormaliz
            A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices
            sage: len(simplex.integral_points())                           # optional - pynormaliz
            49

        A rather thin polytope for which the bounding box method would
        be a very bad idea (note this is a rational (non-lattice)
        polytope, so the other backends use the bounding box method)::

            sage: P = Polyhedron(vertices=((0, 0), (178933,37121))) + 1/1000*polytopes.hypercube(2)
            sage: P = Polyhedron(vertices=P.vertices_list(),               # optional - pynormaliz
            ....:                backend='normaliz')
            sage: len(P.integral_points())                                 # optional - pynormaliz
            434

        Finally, the 3-d reflexive polytope number 4078::

            sage: v = [(1,0,0), (0,1,0), (0,0,1), (0,0,-1), (0,-2,1),
            ....:      (-1,2,-1), (-1,2,-2), (-1,1,-2), (-1,-1,2), (-1,-3,2)]
            sage: P = Polyhedron(v, backend='normaliz')                    # optional - pynormaliz
            sage: pts1 = P.integral_points()                               # optional - pynormaliz
            sage: all(P.contains(p) for p in pts1)                         # optional - pynormaliz
            True
            sage: pts2 = LatticePolytope(v).points()          # PALP
            sage: for p in pts1: p.set_immutable()                         # optional - pynormaliz
            sage: set(pts1) == set(pts2)                                   # optional - pynormaliz
            True

            sage: timeit('Polyhedron(v, backend='normaliz').integral_points()')   # not tested - random
            625 loops, best of 3: 1.41 ms per loop
            sage: timeit('LatticePolytope(v).points()')       # not tested - random
            25 loops, best of 3: 17.2 ms per loop

        TESTS:

        Test some trivial cases (see :trac:`17937`):

        Empty polyhedron in 1 dimension::

            sage: P = Polyhedron(ambient_dim=1, backend='normaliz')        # optional - pynormaliz
            sage: P.integral_points()                                      # optional - pynormaliz
            ()

        Empty polyhedron in 0 dimensions::

            sage: P = Polyhedron(ambient_dim=0, backend='normaliz')        # optional - pynormaliz
            sage: P.integral_points()                                      # optional - pynormaliz
            ()

        Single point in 1 dimension::

            sage: P = Polyhedron([[3]], backend='normaliz')                # optional - pynormaliz
            sage: P.integral_points()                                      # optional - pynormaliz
            ((3),)

        Single non-integral point in 1 dimension::

            sage: P = Polyhedron([[1/2]], backend='normaliz')              # optional - pynormaliz
            sage: P.integral_points()                                      # optional - pynormaliz
            ()

        Single point in 0 dimensions::

            sage: P = Polyhedron([[]], backend='normaliz')                 # optional - pynormaliz
            sage: P.integral_points()                                      # optional - pynormaliz
            ((),)

        A polytope with no integral points (:trac:`22938`)::

            sage: ieqs = [[1, 2, -1, 0], [0, -1, 2, -1], [0, 0, -1, 2],
            ....:         [0, -1, 0, 0], [0, 0, -1, 0],  [0, 0, 0, -1],
            ....:         [-1, -1, -1, -1], [1, 1, 0, 0], [1, 0, 1, 0],
            ....:         [1, 0, 0, 1]]
            sage: P = Polyhedron(ieqs=ieqs, backend='normaliz')            # optional - pynormaliz
            sage: P.bounding_box()                                         # optional - pynormaliz
            ((-3/4, -1/2, -1/4), (-1/2, -1/4, 0))
            sage: P.bounding_box(integral_hull=True)                       # optional - pynormaliz
            (None, None)
            sage: P.integral_points()                                      # optional - pynormaliz
            ()

        Check the polytopes from :trac:`22984`::

            sage: base = [[0, 2, 0, -1, 0, 0, 0, 0, 0],
            ....:         [0, 0, 2, 0, -1, 0, 0, 0, 0],
            ....:         [1, -1, 0, 2, -1, 0, 0, 0, 0],
            ....:         [0, 0, -1, -1, 2, -1, 0, 0, 0],
            ....:         [0, 0, 0, 0, -1, 2, -1, 0, 0],
            ....:         [0, 0, 0, 0, 0, -1, 2, -1, 0],
            ....:         [1, 0, 0, 0, 0, 0, -1, 2, -1],
            ....:         [0, 0, 0, 0, 0, 0, 0, -1, 2],
            ....:         [0, -1, 0, 0, 0, 0, 0, 0, 0],
            ....:         [0, 0, -1, 0, 0, 0, 0, 0, 0],
            ....:         [0, 0, 0, -1, 0, 0, 0, 0, 0],
            ....:         [0, 0, 0, 0, -1, 0, 0, 0, 0],
            ....:         [0, 0, 0, 0, 0, -1, 0, 0, 0],
            ....:         [0, 0, 0, 0, 0, 0, -1, 0, 0],
            ....:         [0, 0, 0, 0, 0, 0, 0, -1, 0],
            ....:         [0, 0, 0, 0, 0, 0, 0, 0, -1],
            ....:         [-1, -1, -1, -1, -1, -1, -1, -1, -1]]

            sage: ieqs = base + [
            ....:         [2, 1, 0, 0, 0, 0, 0, 0, 0],
            ....:         [4, 0, 1, 0, 0, 0, 0, 0, 0],
            ....:         [4, 0, 0, 1, 0, 0, 0, 0, 0],
            ....:         [7, 0, 0, 0, 1, 0, 0, 0, 0],
            ....:         [6, 0, 0, 0, 0, 1, 0, 0, 0],
            ....:         [4, 0, 0, 0, 0, 0, 1, 0, 0],
            ....:         [2, 0, 0, 0, 0, 0, 0, 1, 0],
            ....:         [1, 0, 0, 0, 0, 0, 0, 0, 1]]
            sage: P = Polyhedron(ieqs=ieqs, backend='normaliz')            # optional - pynormaliz
            sage: P.integral_points()                                      # optional - pynormaliz
            ((-2, -2, -4, -5, -4, -3, -2, -1),
             (-2, -2, -4, -5, -4, -3, -2, 0),
             (-1, -2, -3, -4, -3, -2, -2, -1),
             (-1, -2, -3, -4, -3, -2, -1, 0),
             (-1, -1, -2, -2, -2, -2, -2, -1),
             (-1, -1, -2, -2, -1, -1, -1, 0),
             (-1, -1, -2, -2, -1, 0, 0, 0),
             (-1, 0, -2, -2, -2, -2, -2, -1),
             (0, -1, -1, -2, -2, -2, -2, -1),
             (0, 0, -1, -1, -1, -1, -1, 0))

            sage: ieqs = base + [
            ....:         [3, 1, 0, 0, 0, 0, 0, 0, 0],
            ....:         [4, 0, 1, 0, 0, 0, 0, 0, 0],
            ....:         [6, 0, 0, 1, 0, 0, 0, 0, 0],
            ....:         [8, 0, 0, 0, 1, 0, 0, 0, 0],
            ....:         [6, 0, 0, 0, 0, 1, 0, 0, 0],
            ....:         [4, 0, 0, 0, 0, 0, 1, 0, 0],
            ....:         [2, 0, 0, 0, 0, 0, 0, 1, 0],
            ....:         [1, 0, 0, 0, 0, 0, 0, 0, 1]]
            sage: P = Polyhedron(ieqs=ieqs, backend='normaliz')            # optional - pynormaliz
            sage: P.integral_points()                                      # optional - pynormaliz
            ((-3, -4, -6, -8, -6, -4, -2, -1),
             (-3, -4, -6, -8, -6, -4, -2, 0),
             (-2, -2, -4, -5, -4, -3, -2, -1),
             (-2, -2, -4, -5, -4, -3, -2, 0),
             (-1, -2, -3, -4, -3, -2, -2, -1),
             (-1, -2, -3, -4, -3, -2, -1, 0),
             (-1, -1, -2, -2, -2, -2, -2, -1),
             (-1, -1, -2, -2, -1, -1, -1, 0),
             (-1, -1, -2, -2, -1, 0, 0, 0),
             (-1, 0, -2, -2, -2, -2, -2, -1),
             (0, -1, -1, -2, -2, -2, -2, -1),
             (0, 0, -1, -1, -1, -1, -1, 0))
        """
        import PyNormaliz
        if not self.is_compact():
            raise ValueError('can only enumerate points in a compact polyhedron')
        # Trivial cases: polyhedron with 0 or 1 vertices
        if self.n_vertices() == 0:
            return ()
        if self.n_vertices() == 1:
            v = self.vertices_list()[0]
            try:
                return (vector(ZZ, v),)
            except TypeError:  # vertex not integral
                return ()
        # for small bounding boxes, it is faster to naively iterate over the points of the box
        if threshold > 1:
            box_min, box_max = self.bounding_box(integral_hull=True)
            if box_min is None:
                return ()
            box_points = prod(max_coord-min_coord+1 for min_coord, max_coord in zip(box_min, box_max))
            if  box_points<threshold:
                from sage.geometry.integral_points import rectangular_box_points
                return rectangular_box_points(list(box_min), list(box_max), self)
        # Compute with normaliz
        points = []
        cone = self._normaliz_cone
        assert cone
        for g in PyNormaliz.NmzResult(cone, "ModuleGenerators"):
            assert g[-1] == 1
            points.append(vector(ZZ, g[:-1]))
        return tuple(points)
Example #40
0
def polyhedron_to_Hrep(P, separate_equality_constraints=False):
    r"""Extract half-space representation of polytope. 
    
    By default, returns matrices ``[A, b]`` representing `P` as `Ax \leq b`. 
    If ``separate_equality_constraints = True``, returns matrices ``[A, b, Aeq, beq]``, 
    with separated inequality and equality constraints.

    INPUT:

    * ``P`` - object of class polyhedron

    * ``separate_equality_constraints`` - (default = False) If True, returns ``Aeq``, ``beq`` containing the equality constraints, 
    removing the corresponding lines from ``A`` and ``b``.

    OUTPUT:

    * ``[A, b]`` - dense matrix and vector respectively (dense, ``RDF``).

    * ``[A, b, Aeq, beq]`` - (if the flag separate_equality_constraints is True), matrices and vectors (dense, ``RDF``). 
    
    NOTES:
        
    - Equality constraints are removed from A and put into Aeq.
    - This function is used to revert the job of ``polyhedron_from_Hrep(A, b, base_ring = RDF)``. 
    - However, it is not the inverse generally, because of: 
        - automatic reordering of rows (this is uncontrolled, internal to Polyhedron), and 
        - scaling. In the example of above, with a polyhedron in RDF we see reordering of rows.    
        
    EXAMPLES::

        sage: A = matrix(RDF, [[-1.0, 0.0,  0.0,  0.0,  0.0,  0.0],
        ....: [ 1.0,  0.0,  0.0,  0.0,  0.0,  0.0],
        ....: [ 0.0,  1.0,  0.0,  0.0,  0.0,  0.0],
        ....: [ 0.0, -1.0,  0.0,  0.0,  0.0,  0.0],
        ....: [0.0,  0.0, -1.0,  0.0,  0.0,  0.0],
        ....: [0.0,  0.0,  1.0,  0.0,  0.0,  0.0],
        ....: [0.0,  0.0,  0.0, -1.0,  0.0,  0.0],
        ....: [0.0,  0.0,  0.0,  1.0,  0.0,  0.0],
        ....: [0.0,  0.0,  0.0,  0.0,  1.0,  0.0],
        ....: [0.0,  0.0,  0.0,  0.0, -1.0,  0.0],
        ....: [0.0,  0.0,  0.0,  0.0,  0.0,  1.0],
        ....: [0.0,  0.0,  0.0,  0.0,  0.0, -1.0]])
        sage: b = vector(RDF, [0.0, 10.0, 0.0, 0.0, 0.2, 0.2, 0.1, 0.1, 0.0, 0.0, 0.0, 0.0])
        sage: from polyhedron_tools.misc import polyhedron_from_Hrep, polyhedron_to_Hrep
        sage: P = polyhedron_from_Hrep(A, b, base_ring = RDF); P
        A 3-dimensional polyhedron in RDF^6 defined as the convex hull of 8 vertices
        sage: [A, b] = polyhedron_to_Hrep(P)
        sage: A
        [-0.0  1.0 -0.0 -0.0 -0.0 -0.0]
        [ 0.0 -1.0  0.0  0.0  0.0  0.0]
        [-0.0 -0.0 -0.0 -0.0  1.0 -0.0]
        [ 0.0  0.0  0.0  0.0 -1.0  0.0]
        [-0.0 -0.0 -0.0 -0.0 -0.0  1.0]
        [ 0.0  0.0  0.0  0.0  0.0 -1.0]
        [-1.0 -0.0 -0.0 -0.0 -0.0 -0.0]
        [ 1.0 -0.0 -0.0 -0.0 -0.0 -0.0]
        [-0.0 -0.0 -1.0 -0.0 -0.0 -0.0]
        [-0.0 -0.0  1.0 -0.0 -0.0 -0.0]
        [-0.0 -0.0 -0.0 -1.0 -0.0 -0.0]
        [-0.0 -0.0 -0.0  1.0 -0.0 -0.0]
        sage: b
        (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 10.0, 0.2, 0.2, 0.1, 0.1) 
        
    TESTS:: 
    
    If we choose QQ, then we have both reordering and rescaling::

        sage: P = polyhedron_from_Hrep(A, b, base_ring = QQ); P
        A 3-dimensional polyhedron in QQ^6 defined as the convex hull of 8 vertices

        sage: [A, b] = polyhedron_to_Hrep(P)
        sage: A
        [  0.0   0.0   0.0   0.0   0.0  -1.0]
        [  0.0   0.0   0.0   0.0   0.0   1.0]
        [  0.0   0.0   0.0   0.0  -1.0   0.0]
        [  0.0   0.0   0.0   0.0   1.0   0.0]
        [  0.0  -1.0   0.0   0.0   0.0   0.0]
        [  0.0   1.0   0.0   0.0   0.0   0.0]
        [ -1.0   0.0   0.0   0.0   0.0   0.0]
        [  0.0   0.0   5.0   0.0   0.0   0.0]
        [  0.0   0.0  -5.0   0.0   0.0   0.0]
        [  0.0   0.0   0.0 -10.0   0.0   0.0]
        [  0.0   0.0   0.0  10.0   0.0   0.0]
        [  1.0   0.0   0.0   0.0   0.0   0.0]
        sage: b
        (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 10.0)

    The polytope P is not full-dimensional. To extract the equality constraints we use the flag ``separate_equality_constraints``::

        sage: [A, b, Aeq, beq] = polyhedron_to_Hrep(P, separate_equality_constraints = True)
        sage: A
        [ -1.0   0.0   0.0   0.0   0.0   0.0]
        [  0.0   0.0   5.0   0.0   0.0   0.0]
        [  0.0   0.0  -5.0   0.0   0.0   0.0]
        [  0.0   0.0   0.0 -10.0   0.0   0.0]
        [  0.0   0.0   0.0  10.0   0.0   0.0]
        [  1.0   0.0   0.0   0.0   0.0   0.0]
        sage: b
        (0.0, 1.0, 1.0, 1.0, 1.0, 10.0)
        sage: Aeq
        [ 0.0  0.0  0.0  0.0  0.0 -1.0]
        [ 0.0  0.0  0.0  0.0 -1.0  0.0]
        [ 0.0 -1.0  0.0  0.0  0.0  0.0]
        sage: beq
        (0.0, 0.0, 0.0)
"""
    if not separate_equality_constraints:
        # a priori I don't know number of equalities; actually m may be bigger than len(P.Hrepresentation()) !
        # for this, we should transform equalities into inequalities, so that Ax <= b is correct.
        b = list()
        A = list()

        P_gen = P.Hrep_generator()

        for pigen in P_gen:
            if (pigen.is_equation()):
                pi_vec = pigen.vector()
                A.append(-pi_vec[1:len(pi_vec)])
                b.append(pi_vec[0])

                A.append(pi_vec[1:len(pi_vec)])
                b.append(pi_vec[0])

            else:
                pi_vec = pigen.vector()
                A.append(-pi_vec[1:len(pi_vec)])
                b.append(pi_vec[0])

        return [matrix(RDF, A), vector(RDF, b)]

    else:
        b = list()
        A = list()
        beq = list()
        Aeq = list()

        P_gen = P.Hrep_generator()

        for pigen in P_gen:
            if (pigen.is_equation()):
                pi_vec = pigen.vector()
                Aeq.append(-pi_vec[1:len(pi_vec)])
                beq.append(pi_vec[0])

            else:
                pi_vec = pigen.vector()
                A.append(-pi_vec[1:len(pi_vec)])
                b.append(pi_vec[0])

    return [matrix(RDF, A), vector(RDF, b), matrix(RDF, Aeq), vector(RDF, beq)]
Example #41
0
def support_function(P, d, verbose=0, return_xopt=False, solver='GLPK'):
    r"""Compute support function of a convex polytope.

    It is defined as `\max_{x in P} \langle x, d \rangle` , where `d` is an input vector.

    INPUT:

    * ``P`` - an object of class Polyhedron. It can also be given as ``[A, b]`` meaning `Ax \leq b`.

    * ``d`` - a vector (or list) where the support function is evaluated.

    * ``verbose`` - (default: 0) If 1, print the status of the LP.

    * ``solver`` - (default: ``'GLPK'``) the LP solver used.

    * ``return_xopt`` - (default: False) If True, the optimal point is returned, and ``sf = [oval, opt]``.

    OUTPUT:

    * ``sf`` - the value of the support function.

    EXAMPLES::

        sage: from polyhedron_tools.misc import BoxInfty, support_function
        sage: P = BoxInfty([1,2,3], 1); P
        A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 8 vertices
        sage: support_function(P, [1,1,1], return_xopt=True)
        (9.0, {0: 2.0, 1: 3.0, 2: 4.0})

    NOTES:

    - The possibility of giving the input polytope as `[A, b]` instead of a 
    polyhedron is useful in cases when the dimensions are high (in practice, 
    above or around 20, but it depends on the particular system -number of 
    constraints- as well). 
    
    - If a different solver (e.g. ``guurobi``) is given, it should be installed properly.
    """
    from sage.numerical.mip import MixedIntegerLinearProgram

    if (type(P) == list):
        A = P[0]
        b = P[1]
    elif P.is_empty():
        # avoid formulating the LP if P = []
        return 0
    else:  #assuming some form of Polyhedra
        base_ring = P.base_ring()
        # extract the constraints from P
        m = len(P.Hrepresentation())
        n = len(vector(P.Hrepresentation()[0])) - 1
        b = vector(base_ring, m)
        A = matrix(base_ring, m, n)
        P_gen = P.Hrep_generator()
        i = 0
        for pigen in P_gen:
            pi_vec = pigen.vector()
            A.set_row(i, -pi_vec[1:len(pi_vec)])
            b[i] = pi_vec[0]
            i += 1

    s_LP = MixedIntegerLinearProgram(maximization=True, solver=solver)
    x = s_LP.new_variable(integer=False, nonnegative=False)

    # objective function
    obj = sum(d[i] * x[i] for i in range(len(d)))
    s_LP.set_objective(obj)

    s_LP.add_constraint(A * x <= b)

    if (verbose):
        print '**** Solve LP  ****'
        s_LP.show()

    oval = s_LP.solve()
    xopt = s_LP.get_values(x)

    if (verbose):
        print 'Objective Value:', oval
        for i, v in xopt.iteritems():
            print 'x_%s = %f' % (i, v)
        print '\n'

    if (return_xopt == True):
        return oval, xopt
    else:
        return oval
Example #42
0
def BoxInfty(lengths=None,
             center=None,
             radius=None,
             base_ring=QQ,
             return_HSpaceRep=False):
    r"""Generate a ball in the supremum norm, or more generally a hyper-rectangle in an Euclidean space.

    It can be constructed from its center and radius, in which case it is a box. 
    It can also be constructed giving the lengths of the sides, in which case it is an hyper-rectangle. 
    In all cases, it is defined as the Cartesian product of intervals.

    INPUT:

    * ``args`` - Available options are:

        * by center and radius:

            * ``center`` - a vector (or a list) containing the coordinates of the center of the ball.

            * ``radius`` - a number representing the radius of the ball.

        * by lengths:

            * ``lenghts`` - a list of tuples containing the length of each side with respect to the coordinate axes, in the form [(min_x1, max_x1), ..., (min_xn, max_xn)].

    * ``base_ring`` - (default: ``QQ``) base ring passed to the Polyhedron constructor. Valid choices are:

        * ``'QQ'``: rational

        * ``'RDF'``: real double field

    * ``return_HSpaceRep`` - (default: False) If True, it does not construct the Polyhedron `P`, and returns instead the pairs ``[A, b]`` corresponding to P in half-space representation, and is understood that `Ax \leq b`.

    OUTPUT:

    * ``P`` - a Polyhedron object. If the flag ``return_HSpaceRep`` is true, it is returned as ``[A, b]`` with `A` and `b` matrices, and is understood that `Ax \leq b`.

    EXAMPLES::

        sage: from polyhedron_tools.misc import BoxInfty
        sage: P = BoxInfty([1,2,3], 1); P
        A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 8 vertices
        sage: P.plot(aspect_ratio=1)    # not tested (plot)

    NOTES:

    - The possibility to output in matrix form [A, b] is especially interesting 
    for more than 15 variable systems.
    """
    # Guess input
    got_lengths, got_center_and_radius = False, False
    if (lengths is not None):
        if (center is None) and (radius is None):
            got_lengths = True
        elif (center is not None) and (radius is None):
            radius = center
            center = lengths
            lengths = []
            got_center_and_radius = True
    else:
        got_center_and_radius = True if (center is not None
                                         and radius is not None) else False

    # Given center and radius
    if got_center_and_radius:

        # cast (optional)
        center = [base_ring(xi) for xi in center]
        radius = base_ring(radius)

        ndim = len(center)
        A = matrix(base_ring, 2 * ndim, ndim)
        b = vector(base_ring, 2 * ndim)

        count = 0
        for i in range(ndim):
            diri = zero_vector(base_ring, ndim)
            diri[i] = 1

            # external bound
            A.set_row(count, diri)
            b[count] = center[i] + radius
            count += 1

            # internal bound
            A.set_row(count, -diri)
            b[count] = -(center[i] - radius)
            count += 1

    # Given the length of each side as tuples (min, max)
    elif got_lengths:

        # clean up the argument and cast
        lengths = _make_listlist(lengths)
        lengths = [[base_ring(xi) for xi in l] for l in lengths]

        ndim = len(lengths)
        A = matrix(base_ring, 2 * ndim, ndim)
        b = vector(base_ring, 2 * ndim)

        count = 0
        for i in range(ndim):
            diri = zero_vector(base_ring, ndim)
            diri[i] = 1

            # external bound
            A.set_row(count, diri)
            b[count] = lengths[i][1]
            count += 1

            # internal bound
            A.set_row(count, -diri)
            b[count] = -(lengths[i][0])
            count += 1

    else:
        raise ValueError(
            'You should specify either center and radius or length of the sides.'
        )

    if not return_HSpaceRep:
        P = polyhedron_from_Hrep(A, b, base_ring)
        return P
    else:
        return [A, b]
def compute_flowpipe(A=None, X0=None, B=None, U=None, **kwargs):
    r"""Implements LGG reachability algorithm for the linear continuous system dx/dx = Ax + Bu.

    INPUTS:

    * ``A`` -- coefficient matrix of the system

    * ``X0`` -- initial set

    * ``B`` -- transformation of the input

    * ``U`` -- input set

    * ``time_step`` -- (default = 1e-2) time step

    * ``initial_time`` -- (default = 0) the initial time

    * ``time_horizon`` -- (default = 1) the final time

    * ``number_of_time_steps`` -- (default = ceil(T/tau)) number of time steps

    * "directions" -- (default: random, and a box) dictionary

    * ``solver`` -- LP solver. Valid options are:
        * 'GLPK' (default).
        * 'Gurobi'

    * ``base_ring`` -- base ring where polyhedral computations are performed
        Valid options are:
        * QQ - (default) rational field
        * RDF - real double field

    OUTPUTS:

    * ``flowpipe``

    """

    # ################
    # Parse input    #
    # ################
    if A is None:
        raise ValueError('System matrix A is missing.')
    else:
        if 'sage.matrix' in str(type(A)):
            n = A.ncols()
        elif type(A) == np.ndarray:
            n = A.shape[0]

    base_ring = kwargs['base_ring'] if 'base_ring' in kwargs else QQ

    if X0 is None:
        raise ValueError('Initial state X0 is missing.')
    elif 'sage.geometry.polyhedron' not in str(type(X0)) and type(X0) == list:
        # If X0 is not some type of polyhedron, set an initial point
        X0 = Polyhedron(vertices = [X0], base_ring = base_ring)
    elif 'sage.geometry.polyhedron' not in str(type(X0)) and X0.is_vector():
        X0 = Polyhedron(vertices = [X0], base_ring = base_ring)
    elif 'sage.geometry.polyhedron' in str(type(X0)):
        # ensure that all input sets are on the same ring
        # not sure about this
        if 1==0:
            if X0.base_ring() != base_ring:
                [F, g] = polyhedron_to_Hrep(X0)
                X0 = polyhedron_from_Hrep(F, g, base_ring=base_ring)
    else:
        raise ValueError('Initial state X0 not understood')

    if B is None:
        # the system is homogeneous: dx/dt = Ax
        got_homogeneous = True
    else:
        got_homogeneous = False
        if U is None:
            raise ValueError('Input range U is missing.')

    tau = kwargs['time_step'] if 'time_step' in kwargs else 1e-2

    t0 = kwargs['initial_time'] if 'initial_time' in kwargs else 0

    T = kwargs['time_horizon'] if 'time_horizon' in kwargs else 1

    global N
    N = kwargs['number_of_time_steps'] if 'number_of_time_steps' in kwargs else ceil(T/tau)

    directions = kwargs['directions'] if 'directions' in kwargs else {'select':'box'}

    global solver
    solver = kwargs['solver'] if 'solver' in kwargs else 'GLPK'

    global verbose
    verbose = kwargs['verbose'] if 'verbose' in kwargs else 0

    # this involves the convex hull of X0 and a Minkowski sum
    #first_element_evaluation = kwargs['first_element_evaluation'] if 'first_element_evaluation' in kwargs else 'approximate'

    # #######################################################
    # Generate template directions                          #
    # #######################################################
    if directions['select'] == 'box':

        if n==2:
            theta = [0,pi/2,pi,3*pi/2] # box
            dList = [vector(RR,[cos(t), sin(t)]) for t in theta]

        else: # directions of hypercube
            dList = []
            dList += [-identity_matrix(n).column(i) for i in range(n)]
            dList += [identity_matrix(n).column(i) for i in range(n)]

    elif directions['select'] == 'oct':

        if n != 2:
            raise NotImplementedError('Directions select octagon not implemented for n other than 2. Try box.')

        theta = [i*pi/4 for i in range(8)] # octagon
        dList = [vector(RR,[cos(t), sin(t)]) for t in theta]

    elif directions['select'] == 'random':

        order = directions['order'] if 'order' in directions else 12

        if n == 2:
            theta = [random.uniform(0, 2*pi.n(digits=5)) for i in range(order)]
            dList = [vector(RR,[cos(theta[i]), sin(theta[i])]) for i in range(order)]
        else:
            raise NotImplementedError('Directions select random not implemented for n greater than 2. Try box.')

    elif directions['select'] == 'custom':

        dList = directions['dList']

    else:

        raise TypeError('Template directions not understood.')


    # transform directions to numpy array, and get number of directions
    dArray = np.array(dList)
    k = len(dArray)

    global Phi_tau, expX0, alpha_tau_B

    if got_homogeneous: # dx/dx = Ax

        # #######################################################
        # Compute first element of the approximating sequence   #
        # #######################################################

        # compute matrix exponential exp(A*tau)
        Phi_tau = expm(np.multiply(A, tau))

        # compute exp(tau*A)X0
        expX0 = Phi_tau * X0

        # compute the bloating factor
        Ainfty = A.norm(Infinity)
        RX0 = radius(X0)

        unitBall = BoxInfty(center = zero_vector(n), radius = 1, base_ring = base_ring)
        alpha_tau = (exp(tau*Ainfty) - 1 - tau*Ainfty)*(RX0)
        alpha_tau_B = (alpha_tau*np.identity(n)) * unitBall

        # now we have that:
        # Omega0 = X0.convex_hull(expX0.Minkowski_sum(alpha_tau_B))

        # compute the first element of the approximating sequence, Omega_0
        #if first_element_evaluation == 'exact':
        #    Omega0 = X0.convex_hull(expX0.Minkowski_sum(alpha_tau_B))

        #elif first_element_evaluation == 'approximate': # NOT TESTED!!!
            #Omega0_A = dArray
        #    Omega0_b = np.zeros(k)

        #    for i, d in enumerate(dArray):
        # rho_X0_d = supp_fun_polyhedron(X0, d, solver=solver, verbose=verbose)
        #        rho_expX0_d = supp_fun_polyhedron(expX0, d, solver=solver, verbose=verbose)
        #        rho_alpha_tau_B_d = supp_fun_polyhedron(alpha_tau_B, d, solver=solver, verbose=verbose)
        #        Omega0_b[i] = max(rho_X0_d, rho_expX0_d + rho_alpha_tau_B_d);

        #    Omega0 = PolyhedronFromHSpaceRep(dArray, Omega0_b);

        #W_tau = Polyhedron(vertices = [], ambient_dim=n)
        # since W_tau = [], supp_fun_polyhedron returns 0

        # ################################################
        # Build the sequence of approximations Omega_i   #
        # ################################################

        Omega_i_Family_SF = [_Omega_i_supports_hom(d, X0) for d in dArray]


    else: # dx/dx = Ax + Bu

        global tau_V, beta_tau_B

        # compute range of the input under B, V = BU
        V = B * U

        # compute matrix exponential exp(A*tau)
        Phi_tau = expm(np.multiply(A, tau))

        # compute exp(tau*A)X0
        expX0 = Phi_tau * X0

        # compute the initial over-approximation
        tau_V = (tau*np.identity(n)) * V

        # compute the bloating factor
        Ainfty = A.norm(Infinity)
        RX0 = radius(X0)
        RV = radius(V)

        unitBall = BoxInfty(center = zero_vector(n), radius = 1, base_ring = base_ring)
        alpha_tau = (exp(tau*Ainfty) - 1 - tau*Ainfty)*(RX0 + RV/Ainfty)
        alpha_tau_B = (alpha_tau*np.identity(n)) * unitBall

        # compute the first element of the approximating sequence, Omega_0
        #aux = expX0.Minkowski_sum(tau_V)
        #Omega0 = X0.convex_hull(aux.Minkowski_sum(alpha_tau_B))

        beta_tau = (exp(tau*Ainfty) - 1 - tau*Ainfty)*(RV/Ainfty)
        beta_tau_B = (beta_tau*np.identity(n)) * unitBall

        #W_tau = tau_V.Minkowski_sum(beta_tau_B)

        # ################################################
        # Build the sequence of approximations Omega_i   #
        # ################################################

        Omega_i_Family_SF = [_Omega_i_supports_inhom(d, X0) for d in dArray]


    # ################################################
    # Build the approximating polyhedra              #
    # ################################################

    # each polytope is built using the support functions over-approximation
    Omega_i_Poly = list()

    # This loop can be vectorized (?)
    for i in range(N):    # we have N polytopes

        # for each one, use all directions
        A = matrix(base_ring, k, n);
        b = vector(base_ring, k)

        for j in range(k): #run over directions
            s_fun = Omega_i_Family_SF[j][i]
            A.set_row(j, dList[j])
            b[j] = s_fun

        Omega_i_Poly.append( polyhedron_from_Hrep(A, b, base_ring = base_ring) )

    return Omega_i_Poly
Example #44
0
    def restricted_automorphism_group(self, vertex_labels=None):
        r"""
        Return the restricted automorphism group.

        First, let the linear automorphism group be the subgroup of
        the Euclidean group `E(d) = GL(d,\RR) \ltimes \RR^d`
        preserving the `d`-dimensional polyhedron. The Euclidean group
        acts in the usual way `\vec{x}\mapsto A\vec{x}+b` on the
        ambient space. The restricted automorphism group is the
        subgroup of the linear automorphism group generated by
        permutations of vertices. If the polytope is full-dimensional,
        it is equal to the full (unrestricted) automorphism group.

        INPUT:

        - ``vertex_labels`` -- a tuple or ``None`` (default). The
          labels of the vertices that will be used in the output
          permutation group. By default, the vertices are used
          themselves.

        OUTPUT:

        A
        :class:`PermutationGroup<sage.groups.perm_gps.permgroup.PermutationGroup_generic>`
        acting on the vertices (or the ``vertex_labels``, if
        specified).

        REFERENCES:

        ..  [BSS]
            David Bremner, Mathieu Dutour Sikiric, Achill Schuermann:
            Polyhedral representation conversion up to symmetries.
            http://arxiv.org/abs/math/0702239

        EXAMPLES::

            sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL
            sage: Z3square = LatticePolytope_PPL((0,0), (1,2), (2,1), (3,3))
            sage: Z3square.restricted_automorphism_group(vertex_labels=(1,2,3,4))
            Permutation Group with generators [(2,3), (1,2)(3,4), (1,4)]
            sage: G = Z3square.restricted_automorphism_group(); G
            Permutation Group with generators [((1,2),(2,1)),
            ((0,0),(1,2))((2,1),(3,3)), ((0,0),(3,3))]
            sage: tuple(G.domain()) == Z3square.vertices()
            True
            sage: G.orbit(Z3square.vertices()[0])
            ((0, 0), (1, 2), (3, 3), (2, 1))

            sage: cell24 = LatticePolytope_PPL(
            ...   (1,0,0,0),(0,1,0,0),(0,0,1,0),(0,0,0,1),(1,-1,-1,1),(0,0,-1,1),
            ...   (0,-1,0,1),(-1,0,0,1),(1,0,0,-1),(0,1,0,-1),(0,0,1,-1),(-1,1,1,-1),
            ...   (1,-1,-1,0),(0,0,-1,0),(0,-1,0,0),(-1,0,0,0),(1,-1,0,0),(1,0,-1,0),
            ...   (0,1,1,-1),(-1,1,1,0),(-1,1,0,0),(-1,0,1,0),(0,-1,-1,1),(0,0,0,-1))
            sage: cell24.restricted_automorphism_group().cardinality()
            1152
        """
        if not self.is_full_dimensional():
            return self.affine_lattice_polytope().\
                restricted_automorphism_group(vertex_labels=vertex_labels)
        if vertex_labels is None:
            vertex_labels = self.vertices()
        from sage.groups.perm_gps.permgroup import PermutationGroup
        from sage.graphs.graph import Graph
        # good coordinates for the vertices
        v_list = []
        for v in self.minimized_generators():
            assert v.divisor().is_one()
            v_coords = (1,) + v.coefficients()
            v_list.append(vector(v_coords))

        # Finally, construct the graph
        Qinv = sum( v.column() * v.row() for v in v_list ).inverse()
        G = Graph()
        for i in range(0,len(v_list)):
            for j in range(i+1,len(v_list)):
                v_i = v_list[i]
                v_j = v_list[j]
                G.add_edge(vertex_labels[i], vertex_labels[j], v_i * Qinv * v_j)
        return G.automorphism_group(edge_labels=True)