def ppl_convert(P): r""" Convert a Sage polyhedron to a ppl polyhedron EXAMPLES:: sage: from surface_dynamics.misc.ppl_utils import ppl_convert # optional - pplpy sage: P = ppl_convert(Polyhedron(vertices=[(0,1,0),(1,0,1)], rays=[(0,0,1),[3,2,1]])) # optional - pplpy sage: P.minimized_generators() # optional - pplpy Generator_System {ray(0, 0, 1), point(0/1, 1/1, 0/1), point(1/1, 0/1, 1/1), ray(3, 2, 1)} """ if isinstance(P, ppl.C_Polyhedron): return P gs = ppl.Generator_System() for v in P.vertices_list(): gs.insert(ppl.point(sum(int(j) * ppl.Variable(i) for i, j in enumerate(v)))) for r in P.rays_list(): gs.insert(ppl.ray(sum(int(j) * ppl.Variable(i) for i, j in enumerate(r)))) for l in P.lines_list(): gs.insert(ppl.line(sum(int(j) * ppl.Variable(i) for i, j in enumerate(l)))) return ppl.C_Polyhedron(gs)
def rays(self): """ **Description:** Returns the (not necessarily extremal) rays that generate the cone. **Arguments:** None. **Returns:** *(numpy.ndarray)* The list of rays that generate the cone. **Example:** We construct two cones and find their generating rays. ```python {3,6} c1 = Cone([[0,1],[1,1]]) c2 = Cone(hyperplanes=[[0,1],[1,1]]) c1.rays() # array([[0, 1], # [1, 1]]) c2.rays() # array([[ 1, 0], # [-1, 1]]) ``` """ if self._ext_rays is not None: return np.array(self._ext_rays) if self._rays is not None: return np.array(self._rays) if (self._ambient_dim >= 12 and len(self._hyperplanes) != self._ambient_dim): print("Warning: This operation might take a while for d > ~12 " "and is likely impossible for d > ~18.") cs = ppl.Constraint_System() vrs = [ppl.Variable(i) for i in range(self._ambient_dim)] for h in self.dual().extremal_rays(): cs.insert( sum(h[i] * vrs[i] for i in range(self._ambient_dim)) >= 0) cone = ppl.C_Polyhedron(cs) rays = [] for gen in cone.minimized_generators(): if gen.is_ray(): rays.append(tuple(int(c) for c in gen.coefficients())) elif gen.is_line(): rays.append(tuple(int(c) for c in gen.coefficients())) rays.append(tuple(-int(c) for c in gen.coefficients())) self._rays = np.array(rays, dtype=int) self._dim = np.linalg.matrix_rank(self._rays) return np.array(self._rays)
def ppl_cone(rays): r""" Convert the list ``rays`` into a ppl cone EXAMPLES:: sage: from surface_dynamics.misc.ppl_utils import ppl_cone # optional - pplpy sage: C = ppl_cone([(0,1,2),(1,1,1),(1,0,1)]) # optional - pplpy sage: C.minimized_generators() # optional - pplpy Generator_System {point(0/1, 0/1, 0/1), ray(0, 1, 2), ray(1, 0, 1), ray(1, 1, 1)} """ n = len(rays[0]) gs = ppl.Generator_System(ppl_zero_point(n)) for r in rays: gs.insert(ppl.ray(sum(j * ppl.Variable(i) for i, j in enumerate(r)))) return ppl.C_Polyhedron(gs)
def ppl_positive_cone(n): r""" Return the positive cone in R^n EXAMPLES:: sage: from surface_dynamics.misc.ppl_utils import ppl_positive_cone # optional - pplpy sage: C = ppl_positive_cone(3) # optional - pplpy sage: C.minimized_generators() # optional - pplpy Generator_System {point(0/1, 0/1, 0/1), ray(0, 0, 1), ray(0, 1, 0), ray(1, 0, 0)} """ gs = ppl.Generator_System(ppl_zero_point(n)) l = [0] * n for i in range(n): gs.insert(ppl.ray(ppl.Variable(i))) return ppl.C_Polyhedron(gs)
def hyperplanes(self): """ **Description:** Returns the inward-pointing normals to the hyperplanes that define the cone. **Arguments:** None. **Returns:** *(numpy.ndarray)* The list of inward-pointing normals to the hyperplanes that define the cone. **Example:** We construct two cones and find their hyperplane normals. ```python {3,6} c1 = Cone([[0,1],[1,1]]) c2 = Cone(hyperplanes=[[0,1],[1,1]]) c1.hyperplanes() # array([[ 1, 0], # [-1, 1]]) c2.hyperplanes() # array([[0, 1], # [1, 1]]) ``` """ if self._hyperplanes is not None: return np.array(self._hyperplanes) if self._ambient_dim >= 12 and len(self.rays()) != self._ambient_dim: print("Warning: This operation might take a while for d > ~12 " "and is likely impossible for d > ~18.") gs = ppl.Generator_System() vrs = [ppl.Variable(i) for i in range(self._ambient_dim)] gs.insert(ppl.point(0)) for r in self.extremal_rays(): gs.insert( ppl.ray(sum(r[i] * vrs[i] for i in range(self._ambient_dim)))) cone = ppl.C_Polyhedron(gs) hyperplanes = [] for cstr in cone.minimized_constraints(): hyperplanes.append(tuple(int(c) for c in cstr.coefficients())) if cstr.is_equality(): hyperplanes.append(tuple(-int(c) for c in cstr.coefficients())) self._hyperplanes = np.array(hyperplanes, dtype=int) return np.array(self._hyperplanes)
def DominationPolytope(i, LossMatrix): N, M = LossMatrix.shape # declare M ppl Variables p = [ppl.Variable(j) for j in range(M)] # declare polytope constraints cs = ppl.Constraint_System() # probabilies constraints on p cs.insert(sum(p[j] for j in range(M)) == 1) for j in range(M): cs.insert(p[j] >= 0) # strict Loss domination constraints Dom = scale_to_integers(domination_matrix(i, LossMatrix)) for a in range(N): if a != i: # p is such that for any action a Loss[i,...]*p <= Loss[a,...]*p #print "Domination line:", Dom[a,...], "inequality:", sum( (Dom[a,j]*p[j] for j in range(M)) ) <= 0 cs.insert(sum((Dom[a, j] * p[j] for j in range(M))) <= 0) return ppl.C_Polyhedron(cs)
#!/usr/bin/python # Copyright (C) 2001-2010 Roberto Bagnara <*****@*****.**> # Copyright (C) 2010-2016 BUGSENG srl (http://bugseng.com) # # This file is part of the Parma Polyhedra Library (PPL). # # The PPL is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 3 of the License, or (at your # option) any later version. # # The PPL is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA. # # For the most up-to-date information see the Parma Polyhedra Library # site: http://bugseng.com/products/ppl/ . import ppl var = ppl.Variable(4) print var
def parma_enumerate_vertices(A_ineq, b_ineq, A_eq=None, b_eq=None): """ Iterator over extreme points of polytope defined by linear equalities and inequalities, A_ineq x <= b_ineq, A_eq x = b_eq. This uses the Parma Polyhedral Library (PPL). Because PPL expects all data to be rational, we enforce that inequalities and equalities are specified by integer-valued matrices. Parameters ---------- A_ineq : np.array Inequalities matrix. Data type should be int, shape should be (num_inequalities x num_variables) b_ineq : np.array Inequalities values. Data type should be int, shape should be (num_inequalities) A_eq : np.array Equalities matrix. Data type should be int, shape should be (num_equalities x num_variables) b_eq : np.array Equalities values. Data type should be int, shape should be (num_equalities) """ try: import ppl except ImportError: raise Exception(""" This method requires the Parma Polyhedra Library (PPL) library to be installed. Normally, this can be done with pip3 install cysignals gmpy2 pip3 install pplpy Please see https://gitlab.com/videlec/pplpy for more information. """) def get_num_cons(A, b): # Check data for validity and return number of constraints if A is None: assert (b is None or len(b) == 0) num_cons = 0 else: assert (isinstance(A, np.ndarray)) assert (isinstance(b, np.ndarray)) assert (np.issubdtype(A.dtype, np.integer)) assert (np.issubdtype(b.dtype, np.integer)) num_cons = A.shape[0] assert (num_cons == len(b)) return num_cons num_ineq_cons = get_num_cons(A_ineq, b_ineq) num_eq_cons = get_num_cons(A_eq, b_eq) if num_eq_cons == 0 and num_ineq_cons == 0: raise Exception( "Must specify at least one inequality or equality constrants") if num_eq_cons > 0 and num_ineq_cons > 0: assert (A_eq.shape[1] == A_ineq.shape[1]) num_vars = (A_eq if num_eq_cons > 0 else A_ineq).shape[1] ppl_vars = [ppl.Variable(i) for i in range(num_vars)] cs = ppl.Constraint_System() for rowndx in range(num_ineq_cons): if np.all(A_ineq[rowndx] == 0): if b_ineq[rowndx] < 0: raise Exception( 'Inequality constraint {} involves no variables and is unsatisfiable' .format(rowndx)) else: continue # trivial constraint lhs = sum([ coef * ppl_vars[i] for i, coef in enumerate(A_ineq[rowndx]) if coef != 0 ]) cs.insert(lhs <= b_ineq[rowndx]) for rowndx in range(num_eq_cons): if np.all(A_eq[rowndx] == 0): if b_eq[rowndx] != 0: raise Exception( 'Equality constraint {} involves no variables and is unsatisfiable' .format(rowndx)) else: continue # trivial constraint lhs = sum([ coef * ppl_vars[i] for i, coef in enumerate(A_eq[rowndx]) if coef != 0 ]) cs.insert(lhs == b_eq[rowndx]) # convert linear inequalities into a list of extreme points poly_from_constraints = ppl.C_Polyhedron(cs) all_generators = poly_from_constraints.minimized_generators() for p in all_generators: if not p.is_point(): raise Exception( 'Returned solution not a point: {}. '.format(p) + 'Typically this means that linear constraints specify a cone, not a polytope' ) # Convert a solution vector in ppl format to a numpy array x = np.fromiter(p.coefficients(), dtype='double') x = x / float(p.divisor()) yield x
def is_solid(self, backend=None, c=0.01, verbose=0): """ **Description:** Returns True if the cone is solid, i.e. if it is full-dimensional. :::note If the generating rays are known then this can simply be checked by computing the dimension of the linear space that they span. However, when only the hyperplane inequalities are known this can be a difficult problem. When using PPL as the backend, the convex hull is explicitly constructed and checked. The other backends try to solve an optimization problem inside the stretched cone, which fails if the cone is not solid. The latter approach is much faster, but there can be extremely narrow cones where the optimization fails and this function returns a false negative. Mosek is recommended when using such extremely narrow cones. ::: **Arguments:** - `backend` *(str, optional)*: Specifies which backend to use. Available options are "ppl", "ortools", and any backends available for the [`tip_of_stretched_cone`](#tip_of_stretched_cone) function. If not specified, it tries all available backends. - `c` *(float, optional, default=0.01)*: A number used to create the stretched cone and try to find the tip. This is ignored when using PPL as the backend. - `verbose` *(int, optional, default=0)*: The verbosity level. - verbose = 0: Do not print anything. - verbose = 1: Print warnings when optimizers fail. **Returns:** *(bool)* The truth value of the cone being solid. **Example:** We construct two cones and check if they are solid. ```python {3,5} c1 = Cone([[1,0],[0,1]]) c2 = Cone([[1,0,0],[0,1,0]]) c1.is_solid() # True c2.is_solid() # False ``` """ if self._is_solid is not None: return self._is_solid if self._rays is not None: return np.linalg.matrix_rank(self._rays) == self._ambient_dim if backend is None: backend = ("mosek" if config.mosek_is_activated() else "ortools") if backend == "ppl": cs = ppl.Constraint_System() vrs = [ppl.Variable(i) for i in range(self._ambient_dim)] for h in self._hyperplanes: cs.insert( sum(h[i] * vrs[i] for i in range(self._ambient_dim)) >= 0) cone = ppl.C_Polyhedron(cs) self._is_solid = cone.affine_dimension() == self._ambient_dim return self._is_solid if backend == "ortools": hp = self._hyperplanes.tolist() solve_ctr = 0 while solve_ctr < 10: obj_vec = np.dot(np.random.random(size=len(hp)), hp) solver = pywraplp.Solver( "find_pt", pywraplp.Solver.GLOP_LINEAR_PROGRAMMING) obj = solver.Objective() var = [] for i in range(self._ambient_dim): var.append( solver.NumVar(-solver.infinity(), solver.infinity(), f"x_{i}")) obj.SetCoefficient(var[-1], obj_vec[i]) obj.SetMinimization() cons_list = [] for v in hp: cons_list.append(solver.Constraint(c, solver.infinity())) for j in range(self._ambient_dim): cons_list[-1].SetCoefficient(var[j], v[j]) status = solver.Solve() if status in (solver.FEASIBLE, solver.OPTIMAL): self._is_solid = True return self._is_solid elif status == solver.INFEASIBLE: self._is_solid = False return self._is_solid else: if verbose >= 1: print( f"Solver returned status: {status}. Trying again.") solve_ctr += 1 if verbose >= 1: print("Linear solver failed too many times. " "Assuming problem infeasible.") self._is_solid = False return self._is_solid if backend in ("all", "mosek", "cvxopt"): opt_res = None try: opt_res = self.tip_of_stretched_cone(c, backend=backend, verbose=verbose) except: pass self._is_solid = opt_res is not None return self._is_solid else: backends = ["ppl", "ortools", "all", "mosek", "cvxopt"] raise Exception(f"Available options for backends are: {backends}")