Skip to content

Commit

Permalink
Refactor Polyhedron interface.
Browse files Browse the repository at this point in the history
  • Loading branch information
mcmtroffaes committed Sep 14, 2024
1 parent 68b7f80 commit f3325f8
Show file tree
Hide file tree
Showing 12 changed files with 203 additions and 162 deletions.
5 changes: 5 additions & 0 deletions cython/_cdd.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

from typing import SupportsFloat

NumberType = float
SupportsNumberType = SupportsFloat

cdef extern from * nogil:
"#undef GMPRATIONAL"

Expand Down
11 changes: 8 additions & 3 deletions cython/_cddgmp.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

from fractions import Fraction
from typing import Union

from cdd import LPObjType, LPSolverType, LPStatusType, RepType

NumberType = Fraction
SupportsNumberType = Union[Fraction, int]

cdef extern from * nogil:
"#define GMPRATIONAL"

Expand All @@ -27,9 +35,6 @@ include "setoper.pxi"
include "mytype_gmp.pxi"
include "cdd.pxi"

from cdd import LPObjType, LPSolverType, LPStatusType, RepType


cdef dd_NumberType NUMBER_TYPE = dd_Rational

include "pycddlib.pxi"
114 changes: 70 additions & 44 deletions cython/pycddlib.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,8 @@ cdef class Matrix:

property array:
def __get__(self):
cdef dd_rowrange i
cdef dd_colrange j
return [
[
_get_mytype(self.dd_mat.matrix[i][j])
for j in range(self.dd_mat.colsize)
]
for i in range(self.dd_mat.rowsize)
]
cdef _Shape shape = _Shape(self.dd_mat.rowsize, self.dd_mat.colsize)
return _get_array_from_matrix(self.dd_mat.matrix, shape)

property lin_set:
def __get__(self):
Expand Down Expand Up @@ -213,14 +206,23 @@ cdef _array_shape(array):
raise ValueError("array too large")
return shape

cdef _set_matrix(mytype **pp, _Shape shape, array):
cdef _set_matrix_from_array(mytype **pp, _Shape shape, array):
for rowindex, row in enumerate(array):
if len(row) != shape.numcols:
raise ValueError("rows have different lengths")
for colindex, value in enumerate(row):
_set_mytype(pp[rowindex][colindex], value)


cdef _get_array_from_matrix(mytype **pp, _Shape shape):
cdef dd_rowrange i
cdef dd_colrange j
return [
[_get_mytype(pp[i][j]) for j in range(shape.numcols)]
for i in range(shape.numrows)
]


# create matrix and wrap into Matrix class
# https://cython.readthedocs.io/en/latest/src/userguide/extension_types.html#instantiation-from-existing-c-c-pointers
def matrix_from_array(
Expand All @@ -237,7 +239,7 @@ def matrix_from_array(
if dd_mat == NULL:
raise MemoryError
try:
_set_matrix(dd_mat.matrix, shape, array)
_set_matrix_from_array(dd_mat.matrix, shape, array)
_set_set(dd_mat.linset, lin_set)
dd_mat.representation = rep_type
dd_mat.objective = obj_type
Expand Down Expand Up @@ -287,15 +289,8 @@ cdef class LinProg:

property array:
def __get__(self):
cdef dd_rowrange i
cdef dd_colrange j
return [
[
_get_mytype(self.dd_lp.A[i][j])
for j in range(self.dd_lp.d)
]
for i in range(self.dd_lp.m)
]
cdef _Shape shape = _Shape(self.dd_lp.m, self.dd_lp.d)
return _get_array_from_matrix(self.dd_lp.A, shape)

property obj_type:
def __get__(self):
Expand Down Expand Up @@ -383,7 +378,7 @@ def linprog_from_array(array, dd_LPObjectiveType obj_type):
if dd_lp == NULL:
raise MemoryError
try:
_set_matrix(dd_lp.A, shape, array)
_set_matrix_from_array(dd_lp.A, shape, array)
except: # noqa: E722
dd_FreeLPData(dd_lp)
raise
Expand All @@ -398,48 +393,79 @@ def linprog_solve(LinProg linprog, dd_LPSolverType solver=dd_DualSimplex):


cdef class Polyhedron:

cdef dd_PolyhedraPtr dd_poly

property rep_type:
def __get__(self):
return RepType(self.dd_poly.representation)

def __str__(self):
cdef libc.stdio.FILE *pfile
pfile = _tmpfile()
dd_WritePolyFile(pfile, self.dd_poly)
return _tmpread(pfile).rstrip('\n')

def __cinit__(self, Matrix mat):
cdef dd_ErrorType error = dd_NoError
# initialize pointers
self.dd_poly = NULL
# read matrix
self.dd_poly = dd_DDMatrix2Poly(mat.dd_mat, &error)
if self.dd_poly == NULL or error != dd_NoError:
# do not call dd_FreePolyhedra(self.dd_poly)
# see issue #7
_raise_error(error, "failed to load polyhedra")
def __init__(self):
raise TypeError("This class cannot be instantiated directly.")

def __dealloc__(self):
if self.dd_poly:
dd_FreePolyhedra(self.dd_poly)
self.dd_poly = NULL

def get_inequalities(self):
return matrix_from_ptr(dd_CopyInequalities(self.dd_poly))
cdef polyhedron_from_ptr(dd_PolyhedraPtr dd_poly):
if dd_poly == NULL:
raise MemoryError # assume malloc failed
cdef Polyhedron poly = Polyhedron.__new__(Polyhedron)
poly.dd_poly = dd_poly
return poly


def polyhedron_from_matrix(Matrix mat):
if mat.rep_type != dd_Inequality and mat.rep_type != dd_Generator:
raise ValueError("rep_type must be INEQUALITY or GENERATOR")
cdef dd_ErrorType error = dd_NoError
dd_poly = dd_DDMatrix2Poly(mat.dd_mat, &error)
if dd_poly == NULL:
raise MemoryError
if error != dd_NoError:
# TODO should call dd_FreePolyhedra(dd_poly)... see issue #7
_raise_error(error, "failed to load polyhedra")
return polyhedron_from_ptr(dd_poly)


def copy_input(Polyhedron poly):
return matrix_from_ptr(dd_CopyInput(poly.dd_poly))


def copy_output(Polyhedron poly):
return matrix_from_ptr(dd_CopyOutput(poly.dd_poly))



def copy_inequalities(Polyhedron poly):
return matrix_from_ptr(dd_CopyInequalities(poly.dd_poly))


def copy_generators(Polyhedron poly):
return matrix_from_ptr(dd_CopyGenerators(poly.dd_poly))


def copy_adjacency(Polyhedron poly):
return _get_dd_setfam(dd_CopyAdjacency(poly.dd_poly))


def copy_input_adjacency(Polyhedron poly):
return _get_dd_setfam(dd_CopyInputAdjacency(poly.dd_poly))

def get_generators(self):
return matrix_from_ptr(dd_CopyGenerators(self.dd_poly))

def get_adjacency(self):
return _get_dd_setfam(dd_CopyAdjacency(self.dd_poly))
def copy_incidence(Polyhedron poly):
return _get_dd_setfam(dd_CopyIncidence(poly.dd_poly))

def get_input_adjacency(self):
return _get_dd_setfam(dd_CopyInputAdjacency(self.dd_poly))

def get_incidence(self):
return _get_dd_setfam(dd_CopyIncidence(self.dd_poly))
def copy_input_incidence(Polyhedron poly):
return _get_dd_setfam(dd_CopyInputIncidence(poly.dd_poly))

def get_input_incidence(self):
return _get_dd_setfam(dd_CopyInputIncidence(self.dd_poly))

# module initialization code comes here
# initialize module constants
Expand Down
26 changes: 13 additions & 13 deletions docs/source/polyhedron.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,15 @@ This is the sampleh1.ine example that comes with cddlib.

>>> mat = cdd.matrix_from_array([[2,-1,-1,0],[0,1,0,0],[0,0,1,0]])
>>> mat.rep_type = cdd.RepType.INEQUALITY
>>> poly = cdd.Polyhedron(mat)
>>> poly = cdd.polyhedron_from_matrix(mat)
>>> print(poly) # doctest: +NORMALIZE_WHITESPACE
begin
3 4 real
2 -1 -1 0
0 1 0 0
0 0 1 0
end
>>> ext = poly.get_generators()
>>> ext = cdd.copy_generators(poly)
>>> print(ext) # doctest: +NORMALIZE_WHITESPACE
V-representation
linearity 1 4
Expand All @@ -128,9 +128,9 @@ The following example illustrates how to get adjacencies and incidences.
>>> # 0 <= 1 - x2 (face 3)
>>> mat = cdd.matrix_from_array([[1, 1, 0], [1, 0, 1], [1, -1, 0], [1, 0, -1]])
>>> mat.rep_type = cdd.RepType.INEQUALITY
>>> poly = cdd.Polyhedron(mat)
>>> poly = cdd.polyhedron_from_matrix(mat)
>>> # The V-representation can be printed in the usual way:
>>> gen = poly.get_generators()
>>> gen = cdd.copy_generators(poly)
>>> print(gen) # doctest: +NORMALIZE_WHITESPACE
V-representation
begin
Expand All @@ -154,30 +154,30 @@ end
>>> # vertex 1 is adjacent to vertices 0 and 2
>>> # vertex 2 is adjacent to vertices 1 and 3
>>> # vertex 3 is adjacent to vertices 0 and 2
>>> print([list(x) for x in poly.get_adjacency()])
>>> print([list(x) for x in cdd.copy_adjacency(poly)])
[[1, 3], [0, 2], [1, 3], [0, 2]]
>>> # vertex 0 is the intersection of faces (1) and (2)
>>> # vertex 1 is the intersection of faces (2) and (3)
>>> # vertex 2 is the intersection of faces (0) and (3)
>>> # vertex 3 is the intersection of faces (0) and (1)
>>> print([list(x) for x in poly.get_incidence()])
>>> print([list(x) for x in cdd.copy_incidence(poly)])
[[1, 2], [2, 3], [0, 3], [0, 1]]
>>> # face (0) is adjacent to faces (1) and (3)
>>> # face (1) is adjacent to faces (0) and (2)
>>> # face (2) is adjacent to faces (1) and (3)
>>> # face (3) is adjacent to faces (0) and (2)
>>> print([list(x) for x in poly.get_input_adjacency()])
>>> print([list(x) for x in cdd.copy_input_adjacency(poly)])
[[1, 3], [0, 2], [1, 3], [0, 2], []]
>>> # face (0) intersects with vertices 2 and 3
>>> # face (1) intersects with vertices 0 and 3
>>> # face (2) intersects with vertices 0 and 1
>>> # face (3) intersects with vertices 1 and 2
>>> print([list(x) for x in poly.get_input_incidence()])
>>> print([list(x) for x in cdd.copy_input_incidence(poly)])
[[2, 3], [0, 3], [0, 1], [1, 2], []]
>>> # add a vertex, and construct new polyhedron
>>> cdd.matrix_append_to(gen, cdd.matrix_from_array([[1, 0, 2]]))
>>> vpoly = cdd.Polyhedron(gen)
>>> print(vpoly.get_inequalities()) # doctest: +NORMALIZE_WHITESPACE
>>> print(cdd.copy_inequalities(vpoly)) # doctest: +NORMALIZE_WHITESPACE
H-representation
begin
5 3 real
Expand Down Expand Up @@ -210,14 +210,14 @@ end
>>> # 3---(0)---0
>>> #
>>> # for each face, list adjacent faces
>>> print([list(x) for x in vpoly.get_adjacency()])
>>> print([list(x) for x in cdd.copy_adjacency(vpoly)])
[[2, 4], [2, 3], [0, 1], [1, 4], [0, 3]]
>>> # for each face, list adjacent vertices
>>> print([list(x) for x in vpoly.get_incidence()])
>>> print([list(x) for x in cdd.copy_incidence(vpoly)])
[[0, 3], [2, 4], [2, 3], [1, 4], [0, 1]]
>>> # for each vertex, list adjacent vertices
>>> print([list(x) for x in vpoly.get_input_adjacency()])
>>> print([list(x) for x in cdd.copy_input_adjacency(vpoly)])
[[1, 3], [0, 4], [3, 4], [0, 2], [1, 2]]
>>> # for each vertex, list adjacent faces
>>> print([list(x) for x in vpoly.get_input_incidence()])
>>> print([list(x) for x in cdd.copy_input_incidence(vpoly)])
[[0, 4], [3, 4], [1, 2], [0, 2], [1, 3]]
Loading

0 comments on commit f3325f8

Please sign in to comment.