def interFacePolytope(a, b, 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 for both a and b Doma = scale_to_integers(domination_matrix(a, LossMatrix)) Domb = scale_to_integers(domination_matrix(b, LossMatrix)) for i in range(N): if i != a: # p is such that for any action i Loss[a,...]*p <= Loss[a,...]*p cs.insert(sum((Doma[i, j] * p[j] for j in range(M))) <= 0) if i != b: # p is such that for any action i Loss[b,...]*p <= Loss[a,...]*p cs.insert(sum((Domb[i, j] * p[j] for j in range(M))) <= 0) return ppl.C_Polyhedron(cs)
def __init__(self, id, nbP, nbT, nbPar): self.id = id self.places = [Place("p" + str(i)) for i in range(nbP)] self.transitions = [Transition("t" + str(i)) for i in range(nbT)] self.params = [Parameter(i) for i in range(nbPar)] self.constraints = ppl.Constraint_System() for param in self.params: self.constraints.insert(param >= 0)
def is_enabled(self, t): """ Ask whether a transition can be fired or not. """ context = ppl.Constraint_System(self.constraints) for c in list(t.get_firing_constraint()): context.insert(c) poly = ppl.NNC_Polyhedron(context) return not poly.is_empty()
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 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)
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 get_firing_constraint(self): cs = ppl.Constraint_System() for arc in self.pre: cs.insert(arc.get_firing_constraint()) return cs
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}")