From 90fe593be6d177e2bed9da985b4c87053fac6091 Mon Sep 17 00:00:00 2001 From: "Matthias C. M. Troffaes" Date: Fri, 13 Sep 2024 14:18:49 +0100 Subject: [PATCH] Major refactor of API to simplify typing. --- CHANGELOG.rst | 15 ++- cython/mytype_gmp.pxi | 2 +- cython/pycddlib.pxi | 180 +++++++++++++++++----------------- docs/source/linprog.rst | 8 +- docs/source/matrix.rst | 122 ++++++----------------- docs/source/polyhedron.rst | 6 +- pyproject.toml | 3 - src/cdd/__init__.pyi | 56 +++++------ src/cdd/gmp.pyi | 48 ++++----- test/gmp/__init__.py | 6 +- test/gmp/test_large_number.py | 4 +- test/gmp/test_matrix.py | 12 +-- test/test_adjacency_list.py | 17 ++-- test/test_incidence.py | 16 +-- test/test_issue20.py | 12 +-- test/test_issue25.py | 2 +- test/test_issue7.py | 2 +- test/test_large_file.py | 2 +- test/test_large_number.py | 4 +- test/test_linprog.py | 6 +- test/test_matrix.py | 46 ++++----- test/test_polyhedron.py | 32 +++--- 22 files changed, 267 insertions(+), 334 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3794881..ee6dcb9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -41,7 +41,17 @@ Fully detailed changes: This change was necessary to enable fully correct type checking for the library, and to allow a version of pycddlib to be installed without needing to compile gmp. -* Many methods have been refactored into functions +* BACKWARDS INCOMPATIBLE: + Under the hood, the old version used cython's ``__cinit__`` to initialize + ``Matrix``, ``LinProg``, and ``Polyhedron`` objects. + However, this function cannot handle exceptions correctly. + Instead, cython recommends using factory functions instead. + So, to construct these objects, new factory functions have been introduced: + ``matrix_create``, ``linprog_from_matrix``, and ``polyhedron_from_matrix``. + As a consequence, errors during construction are now correctly handled. + +* BACKWARDS INCOMPATIBLE: + For consistency, methods have been refactored into functions to more closely reflect the underlying cddlib interface. - ``Matrix.extend`` is now ``matrix_append_to`` and takes two matrices as argument, @@ -52,9 +62,6 @@ Fully detailed changes: - ``Matrix.canonicalize`` is now ``matrix_canonicalize``. - The old methods are still present, however they are deprecated and will be removed - eventually. - * Thanks to the reorganization, there now is a standalone Python package that installs just the floating point interface without needing the gmp or cddlib libraries installed. diff --git a/cython/mytype_gmp.pxi b/cython/mytype_gmp.pxi index d64ca42..ac0fd25 100644 --- a/cython/mytype_gmp.pxi +++ b/cython/mytype_gmp.pxi @@ -81,4 +81,4 @@ cdef _set_mytype(mytype target, value): if mpq_set_str(target, buf, 10) == -1: raise ValueError('could not convert %s to mpq_t' % value) else: - raise TypeError(f"value {value!r} is not Rational") + raise TypeError(f"must be Fraction or int, not {type(value).__name__}") diff --git a/cython/pycddlib.pxi b/cython/pycddlib.pxi index 7e70e81..202ca6e 100644 --- a/cython/pycddlib.pxi +++ b/cython/pycddlib.pxi @@ -15,7 +15,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -from typing_extensions import deprecated +from typing_extensions import deprecated # new in Python 3.13 cimport cpython.mem cimport cpython.unicode @@ -76,14 +76,14 @@ cdef _tmpread(libc.stdio.FILE *pfile): return result cdef _get_set(set_type set_): - # create Python set from given set_type + # create Python Set from given set_type cdef unsigned long elem return frozenset( elem for elem from 0 <= elem < set_[0] if set_member(elem + 1, set_) ) cdef _set_set(set_type set_, pset): - # set elements of set_type by elements from Python set + # set elements of set_type by elements from a Python Container cdef unsigned long elem for elem from 0 <= elem < set_[0]: if elem in pset: @@ -92,21 +92,20 @@ cdef _set_set(set_type set_, pset): set_delelem(set_, elem + 1) cdef _get_dd_setfam(dd_SetFamilyPtr setfam): - # create list of Python sets from dd_SetFamilyPtr, and + # create Python Sequence[Set] from dd_SetFamilyPtr, and # free the pointer; indexing of the sets start at 0, unlike the # string output from cddlib, which starts at 1 cdef long elem if setfam == NULL: raise ValueError("failed to get set family") - # note: must return immutable object - result = tuple( + result = [ frozenset( elem for elem from 0 <= elem < setfam.setsize if set_member(elem + 1, setfam.set[i]) ) for i from 0 <= i < setfam.famsize - ) + ] dd_FreeSetFamily(setfam) return result @@ -116,31 +115,22 @@ cdef _raise_error(dd_ErrorType error, msg): dd_WriteErrorMessages(pfile, error) raise RuntimeError(msg + "\n" + _tmpread(pfile).rstrip('\n')) -cdef _make_dd_matrix(dd_MatrixPtr dd_mat): - cdef Matrix mat - if dd_mat == NULL: - raise ValueError("failed to make matrix") - mat = Matrix([[]]) - dd_FreeMatrix(mat.dd_mat) - mat.dd_mat = dd_mat - return mat - # extension classes to wrap matrix, linear program, and polyhedron cdef class Matrix: - cdef dd_MatrixPtr dd_mat - property row_size: - def __get__(self): - return self.dd_mat.rowsize - - def __len__(self): - return self.dd_mat.rowsize - - property col_size: + property array: def __get__(self): - return self.dd_mat.colsize + 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) + ] property lin_set: def __get__(self): @@ -165,13 +155,12 @@ cdef class Matrix: property obj_func: def __get__(self): - # return an immutable tuple to prohibit item assignment - cdef int colindex - return tuple([_get_mytype(self.dd_mat.rowvec[colindex]) - for 0 <= colindex < self.dd_mat.colsize]) + cdef dd_colrange colindex + return [_get_mytype(self.dd_mat.rowvec[colindex]) + for colindex in range(self.dd_mat.colsize)] def __set__(self, obj_func): - cdef int colindex + cdef Py_ssize_t colindex if len(obj_func) != self.dd_mat.colsize: raise ValueError( "objective function does not match matrix column size") @@ -184,69 +173,78 @@ cdef class Matrix: dd_WriteMatrix(pfile, self.dd_mat) return _tmpread(pfile).rstrip('\n') - def __cinit__(self, rows, linear=False): - cdef Py_ssize_t numrows, numcols, rowindex, colindex - # reset pointers - self.dd_mat = NULL - # determine dimension - numrows = len(rows) - if numrows > 0: - numcols = len(rows[0]) - else: - numcols = 0 - # create new matrix, safely casting ranges - cdef dd_rowrange numrows2 = numrows - cdef dd_colrange numcols2 = numcols - if numrows2 != numrows or numcols2 != numcols: - raise ValueError("matrix too large") - self.dd_mat = dd_CreateMatrix(numrows2, numcols2) - # load data - for rowindex, row in enumerate(rows): - if len(row) != numcols: - raise ValueError("rows have different lengths") - for colindex, value in enumerate(row): - _set_mytype(self.dd_mat.matrix[rowindex][colindex], value) - if linear: - # set all constraints as linear - set_compl(self.dd_mat.linset, self.dd_mat.linset) + def __init__(self): + raise TypeError("This class cannot be instantiated directly.") def __dealloc__(self): dd_FreeMatrix(self.dd_mat) self.dd_mat = NULL - @deprecated("Use matrix_copy instead") - def copy(self): - return matrix_copy(self) - - @deprecated("Use matrix_append_to instead") - def extend(self, rows, linear=False): - cdef Matrix other - matrix_append_to(self, Matrix(rows, linear=linear)) - - @deprecated("Use matrix_canonicalize instead") - def canonicalize(self): - return matrix_canonicalize(self) - - def __getitem__(self, key): - cdef dd_rowrange rownum - cdef dd_rowrange j - if isinstance(key, slice): - indices = key.indices(len(self)) - return [self.__getitem__(i) for i in range(*indices)] - else: - rownum = key - if rownum < 0 or rownum >= self.dd_mat.rowsize: - raise IndexError("row index out of range") - return [_get_mytype(self.dd_mat.matrix[rownum][j]) - for 0 <= j < self.dd_mat.colsize] +# wrap pointer into Matrix class +# https://cython.readthedocs.io/en/latest/src/userguide/extension_types.html#instantiation-from-existing-c-c-pointers +cdef matrix_from_ptr(dd_MatrixPtr dd_mat): + if dd_mat == NULL: + raise MemoryError # assume malloc failed + cdef Matrix matrix = Matrix.__new__(Matrix) + matrix.dd_mat = dd_mat + return matrix + + +# 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( + array, + lin_set=(), + dd_RepresentationType rep_type=dd_Unspecified, + dd_LPObjectiveType obj_type=dd_LPnone, + obj_func=None, +): + cdef Py_ssize_t numrows, numcols, rowindex, colindex + cdef dd_MatrixPtr dd_mat + # determine dimension + numrows = len(array) + if numrows > 0: + numcols = len(array[0]) + else: + numcols = 0 + # safely cast ranges + cdef dd_rowrange numrows2 = numrows + cdef dd_colrange numcols2 = numcols + if numrows2 != numrows or numcols2 != numcols: + raise ValueError("matrix too large") + dd_mat = dd_CreateMatrix(numrows2, numcols2) + if dd_mat == NULL: + raise MemoryError + try: + for rowindex, row in enumerate(array): + if len(row) != numcols: + raise ValueError("rows have different lengths") + for colindex, value in enumerate(row): + _set_mytype(dd_mat.matrix[rowindex][colindex], value) + _set_set(dd_mat.linset, lin_set) + dd_mat.representation = rep_type + dd_mat.objective = obj_type + if obj_func is not None: + if len(obj_func) != dd_mat.colsize: + raise ValueError( + "objective function does not match matrix column size") + for colindex, value in enumerate(obj_func): + _set_mytype(dd_mat.rowvec[colindex], value) + except: # noqa: E722 + dd_FreeMatrix(dd_mat) + raise + return matrix_from_ptr(dd_mat) + def matrix_copy(Matrix matrix): - return _make_dd_matrix(dd_CopyMatrix(matrix.dd_mat)) + return matrix_from_ptr(dd_CopyMatrix(matrix.dd_mat)) + def matrix_append_to(Matrix matrix1, Matrix matrix2): if dd_MatrixAppendTo(&matrix1.dd_mat, matrix2.dd_mat) != 1: raise ValueError("cannot append because column sizes differ") + def matrix_canonicalize(Matrix matrix): cdef dd_rowset impl_linset cdef dd_rowset redset @@ -266,8 +264,8 @@ def matrix_canonicalize(Matrix matrix): _raise_error(error, "failed to canonicalize matrix") return result -cdef class LinProg: +cdef class LinProg: cdef dd_LPPtr dd_lp property status: @@ -280,15 +278,15 @@ cdef class LinProg: property primal_solution: def __get__(self): - cdef int colindex - return tuple([_get_mytype(self.dd_lp.sol[colindex]) - for 1 <= colindex < self.dd_lp.d]) + cdef dd_colrange colindex + return [_get_mytype(self.dd_lp.sol[colindex]) + for colindex in range(1, self.dd_lp.d)] property dual_solution: def __get__(self): - cdef int colindex - return tuple([_get_mytype(self.dd_lp.dsol[colindex]) - for 1 <= colindex < self.dd_lp.d]) + cdef dd_colrange colindex + return [_get_mytype(self.dd_lp.dsol[colindex]) + for colindex in range(1, self.dd_lp.d)] def __str__(self): cdef libc.stdio.FILE *pfile @@ -349,10 +347,10 @@ cdef class Polyhedron: self.dd_poly = NULL def get_inequalities(self): - return _make_dd_matrix(dd_CopyInequalities(self.dd_poly)) + return matrix_from_ptr(dd_CopyInequalities(self.dd_poly)) def get_generators(self): - return _make_dd_matrix(dd_CopyGenerators(self.dd_poly)) + return matrix_from_ptr(dd_CopyGenerators(self.dd_poly)) def get_adjacency(self): return _get_dd_setfam(dd_CopyAdjacency(self.dd_poly)) diff --git a/docs/source/linprog.rst b/docs/source/linprog.rst index 6f44761..c81b3ed 100644 --- a/docs/source/linprog.rst +++ b/docs/source/linprog.rst @@ -56,7 +56,7 @@ Methods and Attributes Example ------- ->>> mat = cdd.gmp.Matrix([[Fraction(4, 3),-2,-1],[Fraction(2, 3),0,-1],[0,1,0],[0,0,1]]) +>>> mat = cdd.gmp.matrix_from_array([[Fraction(4, 3),-2,-1],[Fraction(2, 3),0,-1],[0,1,0],[0,0,1]]) >>> mat.obj_type = cdd.LPObjType.MAX >>> mat.obj_func = (0,3,4) >>> print(mat) @@ -70,7 +70,7 @@ end maximize 0 3 4 >>> print(mat.obj_func) -(Fraction(0, 1), Fraction(3, 1), Fraction(4, 1)) +[Fraction(0, 1), Fraction(3, 1), Fraction(4, 1)] >>> lp = cdd.gmp.LinProg(mat) >>> lp.solve() >>> lp.status == cdd.LPStatusType.OPTIMAL @@ -78,6 +78,6 @@ True >>> lp.obj_value Fraction(11, 3) >>> lp.primal_solution -(Fraction(1, 3), Fraction(2, 3)) +[Fraction(1, 3), Fraction(2, 3)] >>> lp.dual_solution -(Fraction(3, 2), Fraction(5, 2)) +[Fraction(3, 2), Fraction(5, 2)] diff --git a/docs/source/matrix.rst b/docs/source/matrix.rst index e4a2a4d..18d43a9 100644 --- a/docs/source/matrix.rst +++ b/docs/source/matrix.rst @@ -4,13 +4,16 @@ import cdd.gmp from fractions import Fraction -.. currentmodule:: cdd - Sets of Linear Inequalities and Generators ========================================== -.. class:: cdd.Matrix(rows: Sequence[Sequence[SupportsFloat]], linear: bool = False) -.. class:: cdd.gmp.Matrix(rows: Sequence[Sequence[Union[Fraction, int]]], linear: bool = False) +.. function:: cdd.matrix_from_array( + array: Sequence[Sequence[SupportsFloat]], + lin_set: Container[int] = (), + rep_type: RepType = RepType.UNSPECIFIED, + obj_type: LPObjType = LPObjType.NONE, + obj_func: Optional[Sequence[SupportsFloat]] = None, + ) -> Matrix: ... A class for working with sets of linear constraints and extreme points. @@ -38,44 +41,29 @@ Sets of Linear Inequalities and Generators :math:`\mathrm{linspan}` is the linear span operator. All entries of :math:`t` must be either :math:`0` or :math:`1`. - :param rows: The rows of the matrix. - :param linear: Whether to add the rows to the :attr:`~cdd.Matrix.lin_set` or not. - .. warning:: With :mod:`cdd.gmp`, passing a :class:`float` will result in a :exc:`TypeError`: - >>> cdd.gmp.Matrix([[1.12]])[0][0] + >>> cdd.gmp.matrix_from_array([[1.12]]) Traceback (most recent call last): ... TypeError: value 1.12 is not Rational If the float represents a fraction, you must pass it as a fraction explicitly: - >>> print(cdd.gmp.Matrix([[Fraction(112, 100)]])[0][0]) - 28/25 + >>> print(cdd.gmp.matrix_from_array([[Fraction(112, 100)]]).array) + [[Fraction(28, 25)]] If you really must use a float as a fraction, pass it explicitly to the :class:`~fractions.Fraction` constructor: - >>> print(cdd.gmp.Matrix([[Fraction(1.12)]])[0][0]) - 1261007895663739/1125899906842624 + >>> print(cdd.gmp.matrix_from_array([[Fraction(1.12)]]).array) + [[Fraction(1261007895663739, 1125899906842624)]] As you can see from the output above, for typical use cases, you will not want to do this. -Methods and Attributes ----------------------- - -.. method:: cdd.Matrix.__getitem__(index: int) -> Sequence[float] - cdd.Matrix.__getitem__(index: slice) -> Sequence[Sequence[float]] -.. method:: cdd.gmp.Matrix.__getitem__(index: int) -> Sequence[Fraction] - cdd.gmp.Matrix.__getitem__(index: slice) -> Sequence[Sequence[Fraction]] - - Return a row, or a slice of rows, of the matrix. - - :param key: The row number, or slice of row numbers, to get. - .. function:: matrix_canonicalize(matrix: Matrix) -> tuple[Set[int], Set[int]] Transform to canonical representation by recognizing all @@ -93,17 +81,9 @@ Methods and Attributes The column size must be equal in the two input matrices. It raises a :exc:`ValueError` otherwise. -.. attribute:: Matrix.row_size - - Number of rows. - -.. attribute:: Matrix.col_size - - Number of columns. - .. attribute:: Matrix.lin_set - A :class:`frozenset` containing the rows of linearity + A :class:`Set` containing the rows of linearity (linear generators for the V-representation, and equalities for the H-representation). @@ -118,7 +98,7 @@ Methods and Attributes .. attribute:: Matrix.obj_func - A :class:`tuple` containing the linear programming objective + A :class:`Sequence` containing the linear programming objective function. Examples @@ -135,49 +115,29 @@ Fractions Declaring matrices, and checking some attributes: ->>> mat1 = cdd.Matrix([[1, 2],[3, 4]]) +>>> mat1 = cdd.gmp.matrix_from_array([[1, 2],[3, 4]]) >>> print(mat1) # doctest: +NORMALIZE_WHITESPACE begin - 2 2 real + 2 2 rational 1 2 3 4 end ->>> mat1.row_size -2 ->>> mat1.col_size -2 ->>> print(mat1[0]) -[1.0, 2.0] ->>> print(mat1[1]) -[3.0, 4.0] ->>> print(mat1[2]) # doctest: +ELLIPSIS -Traceback (most recent call last): - ... -IndexError: row index out of range ->>> cdd.matrix_append_to(mat1, cdd.Matrix([[5,6]])) ->>> mat1.row_size -3 +>>> print(mat1.array) +[[Fraction(1, 1), Fraction(2, 1)], [Fraction(3, 1), Fraction(4, 1)]] +>>> cdd.gmp.matrix_append_to(mat1, cdd.gmp.matrix_from_array([[5,6]])) >>> print(mat1) # doctest: +NORMALIZE_WHITESPACE begin - 3 2 real + 3 2 rational 1 2 3 4 5 6 end ->>> print(mat1[0]) -[1.0, 2.0] ->>> print(mat1[1]) -[3.0, 4.0] ->>> print(mat1[2]) -[5.0, 6.0] ->>> mat1[1:3] -[[3.0, 4.0], [5.0, 6.0]] ->>> mat1[:-1] -[[1.0, 2.0], [3.0, 4.0]] +>>> print(mat1.array) +[[Fraction(1, 1), Fraction(2, 1)], [Fraction(3, 1), Fraction(4, 1)], [Fraction(5, 1), Fraction(6, 1)]] Canonicalizing: ->>> mat = cdd.gmp.Matrix([[2, 1, 2, 3], [0, 1, 2, 3], [3, 0, 1, 2], [0, -2, -4, -6]]) +>>> mat = cdd.gmp.matrix_from_array([[2, 1, 2, 3], [0, 1, 2, 3], [3, 0, 1, 2], [0, -2, -4, -6]]) >>> cdd.gmp.matrix_canonicalize(mat) # oops... must specify rep_type! Traceback (most recent call last): ... @@ -185,7 +145,7 @@ ValueError: rep_type unspecified >>> mat.rep_type = cdd.RepType.INEQUALITY >>> cdd.gmp.matrix_canonicalize(mat) (frozenset(...1, 3...), frozenset(...0...)) ->>> print(mat) +>>> print(mat) # doctest: +NORMALIZE_WHITESPACE H-representation linearity 1 1 begin @@ -199,28 +159,16 @@ Floats Declaring matrices, and checking some attributes: ->>> mat1 = cdd.Matrix([[1,2],[3,4]]) +>>> mat1 = cdd.matrix_from_array([[1,2],[3,4]]) >>> print(mat1) # doctest: +NORMALIZE_WHITESPACE begin 2 2 real 1 2 3 4 end ->>> mat1.row_size -2 ->>> mat1.col_size -2 ->>> print(mat1[0]) -[1.0, 2.0] ->>> print(mat1[1]) -[3.0, 4.0] ->>> print(mat1[2]) # doctest: +ELLIPSIS -Traceback (most recent call last): - ... -IndexError: row index out of range ->>> cdd.matrix_append_to(mat1, cdd.Matrix([[5,6]])) ->>> mat1.row_size -3 +>>> print(mat1.array) +[[1.0, 2.0], [3.0, 4.0]] +>>> cdd.matrix_append_to(mat1, cdd.matrix_from_array([[5,6]])) >>> print(mat1) # doctest: +NORMALIZE_WHITESPACE begin 3 2 real @@ -228,20 +176,12 @@ begin 3 4 5 6 end ->>> print(mat1[0]) -[1.0, 2.0] ->>> print(mat1[1]) -[3.0, 4.0] ->>> print(mat1[2]) -[5.0, 6.0] ->>> mat1[1:3] -[[3.0, 4.0], [5.0, 6.0]] ->>> mat1[:-1] -[[1.0, 2.0], [3.0, 4.0]] +>>> print(mat1.array) +[[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]] Canonicalizing: ->>> mat = cdd.Matrix([[2, 1, 2, 3], [0, 1, 2, 3], [3, 0, 1, 2], [0, -2, -4, -6]]) +>>> mat = cdd.matrix_from_array([[2, 1, 2, 3], [0, 1, 2, 3], [3, 0, 1, 2], [0, -2, -4, -6]]) >>> cdd.matrix_canonicalize(mat) # oops... must specify rep_type! Traceback (most recent call last): ... diff --git a/docs/source/polyhedron.rst b/docs/source/polyhedron.rst index 09da467..cfe2ae0 100644 --- a/docs/source/polyhedron.rst +++ b/docs/source/polyhedron.rst @@ -94,7 +94,7 @@ Examples This is the sampleh1.ine example that comes with cddlib. ->>> mat = cdd.Matrix([[2,-1,-1,0],[0,1,0,0],[0,0,1,0]]) +>>> 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) >>> print(poly) # doctest: +NORMALIZE_WHITESPACE @@ -126,7 +126,7 @@ The following example illustrates how to get adjacencies and incidences. >>> # 0 <= 1 + x2 (face 1) >>> # 0 <= 1 - x1 (face 2) >>> # 0 <= 1 - x2 (face 3) ->>> mat = cdd.Matrix([[1, 1, 0], [1, 0, 1], [1, -1, 0], [1, 0, -1]]) +>>> 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) >>> # The V-representation can be printed in the usual way: @@ -175,7 +175,7 @@ end >>> print([list(x) for x in poly.get_input_incidence()]) [[2, 3], [0, 3], [0, 1], [1, 2], []] >>> # add a vertex, and construct new polyhedron ->>> cdd.matrix_append_to(gen, cdd.Matrix([[1, 0, 2]])) +>>> cdd.matrix_append_to(gen, cdd.matrix_from_array([[1, 0, 2]])) >>> vpoly = cdd.Polyhedron(gen) >>> print(vpoly.get_inequalities()) # doctest: +NORMALIZE_WHITESPACE H-representation diff --git a/pyproject.toml b/pyproject.toml index 49a0f48..a6ccc0f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,9 +23,6 @@ classifiers = [ "Operating System :: OS Independent", ] keywords = ["convex, polyhedron, linear programming, double description method"] -dependencies = [ - "typing-extensions>=4.9", -] [project.optional-dependencies] test = [ diff --git a/src/cdd/__init__.pyi b/src/cdd/__init__.pyi index dcce4d8..0e8ef48 100644 --- a/src/cdd/__init__.pyi +++ b/src/cdd/__init__.pyi @@ -1,8 +1,6 @@ -from collections.abc import Sequence, Set +from collections.abc import Sequence, Set, Container, Iterable from enum import IntFlag -from typing import ClassVar, SupportsFloat, overload - -from typing_extensions import deprecated # new in Python 3.13 +from typing import ClassVar, SupportsFloat, Optional class LPObjType(IntFlag): MAX: ClassVar[LPObjType] = ... @@ -28,33 +26,35 @@ class RepType(IntFlag): INEQUALITY: ClassVar[RepType] = ... UNSPECIFIED: ClassVar[RepType] = ... -class Matrix(Sequence[Sequence[float]]): +class Matrix: @property - def col_size(self) -> int: ... + def shape(self) -> tuple[int, int]: ... @property - def row_size(self) -> int: ... - lin_set: Set[int] - obj_func: Sequence[float] - obj_type: LPObjType - rep_type: RepType - - def __init__( - self, rows: Sequence[Sequence[SupportsFloat]], linear: bool = False - ) -> None: ... - @deprecated("Use matrix_canonicalize instead") - def canonicalize(self) -> None: ... - @deprecated("Use matrix_copy instead") - def copy(self) -> Matrix: ... - @deprecated("Use matrix_append_to instead") - def extend( - self, rows: Sequence[Sequence[SupportsFloat]], linear: bool = False - ) -> None: ... - @overload - def __getitem__(self, index: int) -> Sequence[float]: ... - @overload - def __getitem__(self, index: slice) -> Sequence[Sequence[float]]: ... - def __len__(self) -> int: ... + def array(self) -> Sequence[Sequence[float]]: ... + @property + def lin_set(self) -> Set[int]: ... + @lin_set.setter + def lin_set(self, value: Container[int]) -> None: ... + @property + def obj_func(self) -> Sequence[float]: ... + @obj_func.setter + def obj_func(self, value: Sequence[float]) -> None: ... + @property + def obj_type(self) -> LPObjType: ... + @obj_type.setter + def obj_type(self, value: LPObjType) -> None: ... + @property + def rep_type(self) -> RepType: ... + @rep_type.setter + def rep_type(self, value: RepType) -> None: ... +def matrix_from_array( + array: Sequence[Sequence[SupportsFloat]], + lin_set: Container[int] = (), + rep_type: RepType = RepType.UNSPECIFIED, + obj_type: LPObjType = LPObjType.NONE, + obj_func: Optional[Sequence[SupportsFloat]] = None, +) -> Matrix: ... def matrix_append_to(matrix1: Matrix, matrix2: Matrix) -> None: ... def matrix_canonicalize(matrix: Matrix) -> None: ... def matrix_copy(matrix: Matrix) -> Matrix: ... diff --git a/src/cdd/gmp.pyi b/src/cdd/gmp.pyi index 5396ce8..a2b6b89 100644 --- a/src/cdd/gmp.pyi +++ b/src/cdd/gmp.pyi @@ -1,38 +1,30 @@ -from collections.abc import Sequence, Set +from collections.abc import Sequence, Set, Container, Iterable from fractions import Fraction -from typing import Union, overload - -from typing_extensions import deprecated # new in Python 3.13 +from typing import Union, Optional from cdd import LPObjType, LPSolverType, LPStatusType, RepType -class Matrix(Sequence[Sequence[Fraction]]): +class Matrix: @property - def col_size(self) -> int: ... + def shape(self) -> tuple[int, int]: ... @property - def row_size(self) -> int: ... - lin_set: Set[int] - obj_func: Sequence[Fraction] - obj_type: LPObjType - rep_type: RepType - - def __init__( - self, rows: Sequence[Sequence[Union[Fraction, int]]], linear: bool = False - ) -> None: ... - @deprecated("Use matrix_canonicalize instead") - def canonicalize(self) -> None: ... - @deprecated("Use matrix_copy instead") - def copy(self) -> Matrix: ... - @deprecated("Use matrix_append_to instead") - def extend( - self, rows: Sequence[Sequence[Union[Fraction, int]]], linear: bool = False - ) -> None: ... - @overload - def __getitem__(self, index: int) -> Sequence[Fraction]: ... - @overload - def __getitem__(self, index: slice) -> Sequence[Sequence[Fraction]]: ... - def __len__(self) -> int: ... + def array(self) -> Sequence[Sequence[Fraction]]: ... + @property + def lin_set(self) -> Set[int]: ... + @property + def obj_func(self) -> Sequence[Fraction]: ... + @property + def obj_type(self) -> LPObjType: ... + @property + def rep_type(self) -> RepType: ... +def matrix_from_array( + array: Sequence[Sequence[Union[Fraction, int]]], + lin_set: Container[int] = (), + rep_type: RepType = RepType.UNSPECIFIED, + obj_type: LPObjType = LPObjType.NONE, + obj_func: Optional[Sequence[Union[Fraction, int]]] = None, +) -> Matrix: ... def matrix_append_to(matrix1: Matrix, matrix2: Matrix) -> None: ... def matrix_canonicalize(matrix: Matrix) -> None: ... def matrix_copy(matrix: Matrix) -> Matrix: ... diff --git a/test/gmp/__init__.py b/test/gmp/__init__.py index dd6da34..1360a0f 100644 --- a/test/gmp/__init__.py +++ b/test/gmp/__init__.py @@ -1,4 +1,4 @@ -from collections.abc import Sequence +from collections.abc import Iterable from fractions import Fraction from typing import Union @@ -10,12 +10,12 @@ def assert_exactly_equal(x: Rational, y: Rational) -> None: def assert_vector_exactly_equal( - vec1: Sequence[Rational], vec2: Sequence[Rational] + vec1: Iterable[Rational], vec2: Iterable[Rational] ) -> None: assert list(vec1) == list(vec2) def assert_matrix_exactly_equal( - mat1: Sequence[Sequence[Rational]], mat2: Sequence[Sequence[Rational]] + mat1: Iterable[Iterable[Rational]], mat2: Iterable[Iterable[Rational]] ) -> None: assert [list(row1) for row1 in mat1] == [list(row2) for row2 in mat2] diff --git a/test/gmp/test_large_number.py b/test/gmp/test_large_number.py index 332064f..3b1d6a7 100644 --- a/test/gmp/test_large_number.py +++ b/test/gmp/test_large_number.py @@ -6,5 +6,5 @@ def test_gmp_large_number() -> None: - mat = cdd.gmp.Matrix([[10**100, Fraction(10**100, 13**90)]]) - assert_matrix_exactly_equal(mat, [[10**100, Fraction(10**100, 13**90)]]) + mat = cdd.gmp.matrix_from_array([[10**100, Fraction(10**100, 13**90)]]) + assert_matrix_exactly_equal(mat.array, [[10**100, Fraction(10**100, 13**90)]]) diff --git a/test/gmp/test_matrix.py b/test/gmp/test_matrix.py index 06b28e3..d8684c7 100644 --- a/test/gmp/test_matrix.py +++ b/test/gmp/test_matrix.py @@ -6,9 +6,9 @@ def test_gmp_matrix_typing() -> None: - cdd.gmp.Matrix([[1]]) - cdd.gmp.Matrix([[Fraction(1, 1)]]) - with pytest.raises(TypeError, match="is not Rational"): - cdd.gmp.Matrix([[1.0]]) # type: ignore - with pytest.raises(TypeError, match="is not Rational"): - cdd.gmp.Matrix([["1"]]) # type: ignore + cdd.gmp.matrix_from_array([[1]]) + cdd.gmp.matrix_from_array([[Fraction(1, 1)]]) + with pytest.raises(TypeError, match="must be Fraction or int"): + cdd.gmp.matrix_from_array([[1.0]]) # type: ignore + with pytest.raises(TypeError, match="must be Fraction or int"): + cdd.gmp.matrix_from_array([["1"]]) # type: ignore diff --git a/test/test_adjacency_list.py b/test/test_adjacency_list.py index ba6993b..aaed3ed 100644 --- a/test/test_adjacency_list.py +++ b/test/test_adjacency_list.py @@ -6,7 +6,7 @@ def test_make_vertex_adjacency_list() -> None: # returns the correct adjacencies. # We start with the H-representation for a cube - mat = cdd.Matrix( + mat = cdd.matrix_from_array( [ [1, 1, 0, 0], [1, 0, 1, 0], @@ -14,9 +14,9 @@ def test_make_vertex_adjacency_list() -> None: [1, -1, 0, 0], [1, 0, -1, 0], [1, 0, 0, -1], - ] + ], + rep_type=cdd.RepType.INEQUALITY ) - mat.rep_type = cdd.RepType.INEQUALITY poly = cdd.Polyhedron(mat) adjacency = poly.get_adjacency() @@ -39,12 +39,12 @@ def test_make_vertex_adjacency_list() -> None: [1, 4, 7], [0, 5, 6], ] - assert adjacency == tuple(frozenset(x) for x in adjacency_list) + assert adjacency == [frozenset(x) for x in adjacency_list] def test_make_facet_adjacency_list() -> None: # This matrix is the same as in vtest_vo.ine - mat = cdd.Matrix( + mat = cdd.matrix_from_array( [ [0, 0, 0, 1], [5, -4, -2, 1], @@ -52,10 +52,9 @@ def test_make_facet_adjacency_list() -> None: [16, -8, 0, 1], [16, 0, -8, 1], [32, -8, -8, 1], - ] + ], + rep_type=cdd.RepType.INEQUALITY ) - - mat.rep_type = cdd.RepType.INEQUALITY poly = cdd.Polyhedron(mat) adjacency_list = [ @@ -69,4 +68,4 @@ def test_make_facet_adjacency_list() -> None: ] adjacency = poly.get_input_adjacency() - assert adjacency == tuple(frozenset(x) for x in adjacency_list) + assert adjacency == [frozenset(x) for x in adjacency_list] diff --git a/test/test_incidence.py b/test/test_incidence.py index bf71d93..265c8a3 100644 --- a/test/test_incidence.py +++ b/test/test_incidence.py @@ -6,7 +6,7 @@ def test_vertex_incidence_cube() -> None: # returns the correct incidences. # We start with the H-representation for a cube - mat = cdd.Matrix( + mat = cdd.matrix_from_array( [ [1, 1, 0, 0], [1, 0, 1, 0], @@ -39,12 +39,12 @@ def test_vertex_incidence_cube() -> None: {0, 1, 5}, {0, 1, 2}, ] - assert incidence == tuple(frozenset(x) for x in incidence_list) + assert incidence == [frozenset(x) for x in incidence_list] def test_vertex_incidence_vtest_vo() -> None: # This matrix is the same as in vtest_vo.ine - mat = cdd.Matrix( + mat = cdd.matrix_from_array( [ [0, 0, 0, 1], [5, -4, -2, 1], @@ -72,12 +72,12 @@ def test_vertex_incidence_vtest_vo() -> None: ] incidence = poly.get_incidence() - assert incidence == tuple(frozenset(x) for x in incidence_list) + assert incidence == [frozenset(x) for x in incidence_list] def test_facet_incidence_cube() -> None: # We start with the H-representation for a cube - mat = cdd.Matrix( + mat = cdd.matrix_from_array( [ [1, 1, 0, 0], [1, 0, 1, 0], @@ -111,12 +111,12 @@ def test_facet_incidence_cube() -> None: {1, 2, 4, 6}, set(), ] - assert incidence == tuple(frozenset(x) for x in incidence_list) + assert incidence == [frozenset(x) for x in incidence_list] def test_facet_incidence_vtest_vo() -> None: # This matrix is the same as in vtest_vo.ine - mat = cdd.Matrix( + mat = cdd.matrix_from_array( [ [0, 0, 0, 1], [5, -4, -2, 1], @@ -140,4 +140,4 @@ def test_facet_incidence_vtest_vo() -> None: {0, 4, 7, 8}, ] - assert poly.get_input_incidence() == tuple(frozenset(x) for x in incidence_list) + assert poly.get_input_incidence() == [frozenset(x) for x in incidence_list] diff --git a/test/test_issue20.py b/test/test_issue20.py index 4a7f907..9b7b2ad 100644 --- a/test/test_issue20.py +++ b/test/test_issue20.py @@ -5,14 +5,14 @@ def test_issue20() -> None: - arr: npt.NDArray[np.float64] = np.array( - [[1, 0, 0], [1, 1, 0], [1, 0, 1]], dtype=np.float64 + arr: npt.NDArray[np.float16] = np.array( + [[1, 0, 0], [1, 1, 0], [1, 0, 1]], dtype=np.float16 ) - ref_ineq: npt.NDArray[np.float64] = np.array( - [[1, -1, -1], [0, 1, 0], [0, 0, 1]], dtype=np.float64 + ref_ineq: npt.NDArray[np.float16] = np.array( + [[1, -1, -1], [0, 1, 0], [0, 0, 1]], dtype=np.float16 ) - mat = cdd.Matrix(arr) # type: ignore + mat = cdd.matrix_from_array(arr) # type: ignore mat.rep_type = cdd.RepType.GENERATOR cdd_poly = cdd.Polyhedron(mat) - ineq = np.array(cdd_poly.get_inequalities()) + ineq = np.array(cdd_poly.get_inequalities().array) assert ((ref_ineq - ineq) == 0).all() diff --git a/test/test_issue25.py b/test/test_issue25.py index aa56dd5..9384ead 100644 --- a/test/test_issue25.py +++ b/test/test_issue25.py @@ -3,5 +3,5 @@ # check that empty polyhedron does not cause segfault def test_issue25() -> None: - mat = cdd.Matrix([]) + mat = cdd.matrix_from_array([]) cdd.Polyhedron(mat) diff --git a/test/test_issue7.py b/test/test_issue7.py index 5127fbe..cb591ad 100644 --- a/test/test_issue7.py +++ b/test/test_issue7.py @@ -17,7 +17,7 @@ def test_issue7() -> None: [1.0, -4.0, 40.0, 4.0, -31.21985375, -4.91296, 17.90974622], [1.0, 4.0, 40.0, -4.0, -28.86014625, 4.91296, 20.26945371], ] - m2 = cdd.Matrix(m) + m2 = cdd.matrix_from_array(m) m2.rep_type = cdd.RepType.GENERATOR with pytest.raises(RuntimeError, match="inconsistency"): cdd.Polyhedron(m2) diff --git a/test/test_large_file.py b/test/test_large_file.py index 7d1be1f..10d57d8 100644 --- a/test/test_large_file.py +++ b/test/test_large_file.py @@ -2,7 +2,7 @@ def test_large_string() -> None: - mat = cdd.Matrix([[1] * 100] * 100) + mat = cdd.matrix_from_array([[1] * 100] * 100) assert ( str(mat) == """begin diff --git a/test/test_large_number.py b/test/test_large_number.py index a370f3b..6badcc8 100644 --- a/test/test_large_number.py +++ b/test/test_large_number.py @@ -6,5 +6,5 @@ def test_large_number() -> None: - mat = cdd.Matrix([[10**100, Fraction(10**100, 13**90)]]) - assert_matrix_almost_equal(mat, [[1e100, 0.556030087418433]]) + mat = cdd.matrix_from_array([[10**100, Fraction(10**100, 13**90)]]) + assert_matrix_almost_equal(mat.array, [[1e100, 0.556030087418433]]) diff --git a/test/test_linprog.py b/test/test_linprog.py index af09519..596b42c 100644 --- a/test/test_linprog.py +++ b/test/test_linprog.py @@ -8,7 +8,7 @@ def test_lin_prog_type() -> None: # -0.5 + x >= 0, 2 - x >= 0 - mat = cdd.Matrix([[-0.5, 1], [2, -1]]) + mat = cdd.matrix_from_array([[-0.5, 1], [2, -1]]) mat.rep_type = cdd.RepType.INEQUALITY mat.obj_type = cdd.LPObjType.MAX mat.obj_func = [2.0, -1.0] @@ -28,7 +28,7 @@ def test_lin_prog_type() -> None: def test_lp2() -> None: - mat = cdd.Matrix([[4 / 3, -2, -1], [2 / 3, 0, -1], [0, 1, 0], [0, 0, 1]]) + mat = cdd.matrix_from_array([[4 / 3, -2, -1], [2 / 3, 0, -1], [0, 1, 0], [0, 0, 1]]) mat.obj_type = cdd.LPObjType.MAX mat.obj_func = (0, 3, 4) lp = cdd.LinProg(mat) @@ -40,7 +40,7 @@ def test_lp2() -> None: def test_another() -> None: - mat = cdd.Matrix( + mat = cdd.matrix_from_array( [[1, -1, -1, -1], [-1, 1, 1, 1], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]] ) mat.obj_type = cdd.LPObjType.MIN diff --git a/test/test_matrix.py b/test/test_matrix.py index 34b4a24..fc075c3 100644 --- a/test/test_matrix.py +++ b/test/test_matrix.py @@ -10,12 +10,13 @@ def test_matrix_init_1() -> None: rows = [[1.1, 1.2, 1.3], [1.4, 1.5, 1.6]] - mat = cdd.Matrix(rows) - assert isinstance(mat.row_size, int) - assert isinstance(mat.col_size, int) - assert mat.row_size == 2 - assert mat.col_size == 3 - assert_matrix_almost_equal(mat, rows) + mat = cdd.matrix_from_array(rows) + assert isinstance(mat.array, Sequence) + assert len(mat.array) == 2 + for row in mat.array: + assert isinstance(row, Sequence) + assert len(row) == 3 + assert_matrix_almost_equal(mat.array, rows) assert isinstance(mat.lin_set, Set) assert not mat.lin_set assert isinstance(mat.rep_type, cdd.RepType) @@ -27,19 +28,19 @@ def test_matrix_init_1() -> None: def test_matrix_init_2() -> None: - rows = [[1.1, 1.2], [1.3, 1.4]] - mat = cdd.Matrix(rows, linear=True) - assert_matrix_almost_equal(mat, rows) + array = [[1.1, 1.2], [1.3, 1.4]] + mat = cdd.matrix_from_array(array, lin_set=[0, 1]) + assert_matrix_almost_equal(mat.array, array) assert mat.lin_set == {0, 1} def test_length() -> None: with pytest.raises(ValueError): - cdd.Matrix([[1], [1, 2]]) + cdd.matrix_from_array([[1], [1, 2]]) def test_obj_func() -> None: - mat = cdd.Matrix([[1], [2]]) + mat = cdd.matrix_from_array([[1], [2]]) with pytest.raises(ValueError): mat.obj_func = [0, 0] mat.obj_func = [7] @@ -47,19 +48,18 @@ def test_obj_func() -> None: def test_matrix_typing() -> None: - cdd.Matrix([[1]]) - cdd.Matrix([[Fraction(1, 1)]]) - cdd.Matrix([[1.0]]) + cdd.matrix_from_array([[1]]) + cdd.matrix_from_array([[Fraction(1, 1)]]) + cdd.matrix_from_array([[1.0]]) with pytest.raises(TypeError, match="must be real number"): - cdd.Matrix([["1"]]) # type: ignore + cdd.matrix_from_array([["1"]]) # type: ignore -@pytest.mark.filterwarnings("ignore", category=DeprecationWarning) -def test_matrix_deprecated() -> None: - mat = cdd.Matrix([[1, 1]]) # 0 <= 1 + x - mat.extend([[2, 1]]) # 0 <= 2 + x - assert_matrix_almost_equal(mat, [[1, 1], [2, 1]]) +def test_matrix_various() -> None: + mat = cdd.matrix_from_array([[1, 1]]) # 0 <= 1 + x + cdd.matrix_append_to(mat, cdd.matrix_from_array([[2, 1]])) # 0 <= 2 + x + assert_matrix_almost_equal(mat.array, [[1, 1], [2, 1]]) mat.rep_type = cdd.RepType.INEQUALITY - mat.canonicalize() - assert_matrix_almost_equal(mat, [[1, 1]]) - assert_matrix_almost_equal(mat.copy(), [[1, 1]]) + cdd.matrix_canonicalize(mat) + assert_matrix_almost_equal(mat.array, [[1, 1]]) + assert_matrix_almost_equal(cdd.matrix_copy(mat).array, [[1, 1]]) diff --git a/test/test_polyhedron.py b/test/test_polyhedron.py index dca383a..494c23b 100644 --- a/test/test_polyhedron.py +++ b/test/test_polyhedron.py @@ -7,7 +7,7 @@ def test_polyhedron_type() -> None: - mat = cdd.Matrix([[1, 1], [1, -1]]) + mat = cdd.matrix_from_array([[1, 1], [1, -1]]) mat.rep_type = cdd.RepType.INEQUALITY poly = cdd.Polyhedron(mat) assert isinstance(poly.get_generators(), cdd.Matrix) @@ -26,26 +26,26 @@ def test_polyhedron_type() -> None: def test_sampleh1() -> None: - mat = cdd.Matrix([[2, -1, -1, 0], [0, 1, 0, 0], [0, 0, 1, 0]]) + 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) ext = poly.get_generators() assert ext.rep_type == cdd.RepType.GENERATOR assert_matrix_almost_equal( - ext, [[1, 0, 0, 0], [1, 2, 0, 0], [1, 0, 2, 0], [0, 0, 0, 1]] + ext.array, [[1, 0, 0, 0], [1, 2, 0, 0], [1, 0, 2, 0], [0, 0, 0, 1]] ) # note: first row is 0, so fourth row is 3 assert ext.lin_set == {3} def test_testcdd2() -> None: - mat = cdd.Matrix([[7, -3, -0], [7, 0, -3], [1, 1, 0], [1, 0, 1]]) + mat = cdd.matrix_from_array([[7, -3, -0], [7, 0, -3], [1, 1, 0], [1, 0, 1]]) mat.rep_type = cdd.RepType.INEQUALITY - assert_matrix_almost_equal(mat, [(7, -3, -0), (7, 0, -3), (1, 1, 0), (1, 0, 1)]) + assert_matrix_almost_equal(mat.array, [(7, -3, -0), (7, 0, -3), (1, 1, 0), (1, 0, 1)]) gen = cdd.Polyhedron(mat).get_generators() assert gen.rep_type == cdd.RepType.GENERATOR assert_matrix_almost_equal( - gen, + gen.array, [ (1, Fraction(7, 3), -1), (1, -1, -1), @@ -54,33 +54,33 @@ def test_testcdd2() -> None: ], ) # add an equality and an inequality - cdd.matrix_append_to(mat, cdd.Matrix([[7, 1, -3]], linear=True)) - cdd.matrix_append_to(mat, cdd.Matrix([[7, -3, 1]])) + cdd.matrix_append_to(mat, cdd.matrix_from_array([[7, 1, -3]], lin_set={0})) + cdd.matrix_append_to(mat, cdd.matrix_from_array([[7, -3, 1]])) assert_matrix_almost_equal( - mat, + mat.array, [(7, -3, -0), (7, 0, -3), (1, 1, 0), (1, 0, 1), (7, 1, -3), (7, -3, 1)], ) assert mat.lin_set == {4} gen2 = cdd.Polyhedron(mat).get_generators() assert gen2.rep_type == cdd.RepType.GENERATOR - assert_matrix_almost_equal(gen2, [(1, -1, 2), (1, 0, Fraction(7, 3))]) + assert_matrix_almost_equal(gen2.array, [(1, -1, 2), (1, 0, Fraction(7, 3))]) def test_polyhedron_cube_1() -> None: generators = [[1, 0, 1], [1, 1, 0], [1, 1, 1], [1, 0, 0]] inequalities = [[0, 0, 1], [0, 1, 0], [1, 0, -1], [1, -1, 0]] - mat = cdd.Matrix(generators) + mat = cdd.matrix_from_array(generators) mat.rep_type = cdd.RepType.GENERATOR poly = cdd.Polyhedron(mat) - assert_matrix_almost_equal(poly.get_generators(), generators) - assert_matrix_almost_equal(poly.get_inequalities(), inequalities) + assert_matrix_almost_equal(poly.get_generators().array, generators) + assert_matrix_almost_equal(poly.get_inequalities().array, inequalities) def test_polyhedron_cube_2() -> None: generators = [[1, 1, 0], [1, 0, 0], [1, 0, 1], [1, 1, 1]] # same up to ordering inequalities = [[0, 0, 1], [0, 1, 0], [1, 0, -1], [1, -1, 0]] - mat = cdd.Matrix(inequalities) + mat = cdd.matrix_from_array(inequalities) mat.rep_type = cdd.RepType.INEQUALITY poly = cdd.Polyhedron(mat) - assert_matrix_almost_equal(poly.get_generators(), generators) - assert_matrix_almost_equal(poly.get_inequalities(), inequalities) + assert_matrix_almost_equal(poly.get_generators().array, generators) + assert_matrix_almost_equal(poly.get_inequalities().array, inequalities)