示例#1
0
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)
示例#2
0
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))
    for i in range(n):
        gs.insert(ppl.ray(ppl.Variable(i)))
    return ppl.C_Polyhedron(gs)
示例#3
0
    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)
示例#4
0
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)
示例#5
0
def ppl_check_non_negative_cone(C):
    r"""
    Check whether the input cone ``C`` is a subcone of the positive cone

    EXAMPLES::

        sage: from surface_dynamics.misc.ppl_utils import ppl_check_non_negative_cone # optional - pplpy
        sage: import ppl    # optional - pplpy

        sage: gs = ppl.Generator_System()     # optional - pplpy
        sage: gs.insert(ppl.point())          # optional - pplpy
        sage: gs.insert(ppl.ray(ppl.Variable(0) + ppl.Variable(2))) # optional - pplpy
        sage: ppl_check_non_negative_cone(ppl.C_Polyhedron(gs)) # optional - pplpy

        sage: gs.insert(ppl.ray(-ppl.Variable(0) - ppl.Variable(2))) # optional - pplpy
        sage: ppl_check_non_negative_cone(ppl.C_Polyhedron(gs)) # optional - pplpy
        Traceback (most recent call last):
        ...
        ValueError: C must be a subcone of the non-negative cone

        sage: gs = ppl.Generator_System() # optional - pplpy
        sage: gs.insert(ppl.point(ppl.Variable(2))) # optional - pplpy
        sage: ppl_check_non_negative_cone(ppl.C_Polyhedron(gs)) # optional - pplpy
        Traceback (most recent call last):
        ...
        ValueError: the cone does not contain zero
        sage: gs.insert(ppl.point()) # optional - pplpy
        sage: ppl_check_non_negative_cone(ppl.C_Polyhedron(gs)) # optional - pplpy
        Traceback (most recent call last):
        ...
        ValueError: should have only zero as vertices
    """
    if not isinstance(C, ppl.C_Polyhedron):
        raise ValueError('C must be a polyhedron in the right ambient space')

    n = C.space_dimension()
    if not C.contains(ppl.C_Polyhedron(ppl_zero_point(n))):
        raise ValueError('the cone does not contain zero')
    if not ppl_positive_cone(n).contains(C):
        raise ValueError('C must be a subcone of the non-negative cone')
    for g in C.generators():
        if g.is_point() and not g.is_equivalent_to(ppl_zero_point(n)):
            raise ValueError('should have only zero as vertices'.format(g))
        if g.is_line():
            raise ValueError('the cone contains a line')
示例#6
0
    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)
示例#7
0
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(j * ppl.Variable(i) for i, j in enumerate(v))))
    for r in P.rays_list():
        gs.insert(ppl.ray(sum(j * ppl.Variable(i) for i, j in enumerate(r))))
    for l in P.lines_list():
        gs.insert(ppl.line(sum(j * ppl.Variable(i) for i, j in enumerate(l))))
    return ppl.C_Polyhedron(gs)
示例#8
0
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)
示例#9
0
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
示例#10
0
    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}")