diff --git a/.zenodo.json b/.zenodo.json index 8a8109e2961..0aeb3c81cf5 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -1,10 +1,10 @@ { "description": "Mirror of the Sage https://sagemath.org/ source tree", "license": "other-open", - "title": "sagemath/sage: 9.3.rc5", - "version": "9.3.rc5", + "title": "sagemath/sage: 9.3", + "version": "9.3", "upload_type": "software", - "publication_date": "2021-04-30", + "publication_date": "2021-05-09", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.3.rc5", + "identifier": "https://github.com/sagemath/sage/tree/9.3", "relation": "isSupplementTo" }, { diff --git a/VERSION.txt b/VERSION.txt index d6663666ddc..c4a47dfc279 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.3.rc5, Release Date: 2021-04-30 +SageMath version 9.3, Release Date: 2021-05-09 diff --git a/build/pkgs/brial/spkg-configure.m4 b/build/pkgs/brial/spkg-configure.m4 index 3d69c88f82d..a0f00838c75 100644 --- a/build/pkgs/brial/spkg-configure.m4 +++ b/build/pkgs/brial/spkg-configure.m4 @@ -1,5 +1,6 @@ SAGE_SPKG_CONFIGURE([brial], [ - SAGE_SPKG_DEPCHECK([boost m4ri], [ + dnl Trac #31624: Avoid C++ ABI issues + SAGE_SPKG_DEPCHECK([gcc boost m4ri], [ # If we're using the system m4ri and boost, ensure that we can # compile and run an executable linked against both libbrial and # libbrial_groebner (both are used by SageMath). diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 43dd3899ddc..a39456c6909 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=4a3ebd8c296607fb48548489fd6f45c6c1c0ca64 -md5=0d839b97966e72b83522932b05be89ea -cksum=481169191 +sha1=2d5d2d8a2a6dbc721935f6b3d3ada9a6bd977e19 +md5=d84b8fcb2ddd6949d3461f49fae01702 +cksum=2508666370 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 8b51da17e64..b644f6ab59f 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -b570ac37fdb82f0c3525ce24c8ee6ab7afbbbf8e +3f4547f2cc40338ba42329d53f7fb48c31ff9fc8 diff --git a/build/pkgs/fplll/spkg-configure.m4 b/build/pkgs/fplll/spkg-configure.m4 index 20a64a58e45..f870e4dbf72 100644 --- a/build/pkgs/fplll/spkg-configure.m4 +++ b/build/pkgs/fplll/spkg-configure.m4 @@ -1,5 +1,5 @@ SAGE_SPKG_CONFIGURE([fplll], [ - SAGE_SPKG_DEPCHECK([mpfr], [ + SAGE_SPKG_DEPCHECK([gcc mpfr], [ dnl If we're using the system mpfr, use pkgconfig to determine dnl if there's a usable system copy of fplll. Unless there's dnl a system that ships fplll without fplll.pc file, falling diff --git a/build/pkgs/fplll/spkg-install.in b/build/pkgs/fplll/spkg-install.in index 619a3e8d53c..dda663d433c 100644 --- a/build/pkgs/fplll/spkg-install.in +++ b/build/pkgs/fplll/spkg-install.in @@ -20,6 +20,11 @@ if [ "x$SAGE_DEBUG" = "xyes" ]; then CONFIGUREFLAGS="$CONFIGUREFLAGS --enable-debug" fi +if [ -x "$SAGE_LOCAL"/bin/gcc ]; then + # Trac #31624: Avoid C++ ABI issues + CONFIGUREFLAGS="$CONFIGUREFLAGS --without-qd" +fi + export CXXFLAGS="$CXXFLAGS" export CPPFLAGS="$CPPFLAGS" export CXX="$CXX" diff --git a/build/pkgs/freetype/spkg-configure.m4 b/build/pkgs/freetype/spkg-configure.m4 index 846bcb368ce..06bef2b302c 100644 --- a/build/pkgs/freetype/spkg-configure.m4 +++ b/build/pkgs/freetype/spkg-configure.m4 @@ -1,5 +1,5 @@ SAGE_SPKG_CONFIGURE([freetype], [ - SAGE_SPKG_DEPCHECK([libpng], [ + SAGE_SPKG_DEPCHECK([gcc libpng], [ dnl freetype versions are libtool's ones, cf trac #30014 PKG_CHECK_MODULES([FREETYPE], [freetype2 >= 16.1], [], [sage_spkg_install_freetype=yes]) ]) diff --git a/build/pkgs/libgd/spkg-configure.m4 b/build/pkgs/libgd/spkg-configure.m4 index 8591449fce4..0e7baedd82c 100644 --- a/build/pkgs/libgd/spkg-configure.m4 +++ b/build/pkgs/libgd/spkg-configure.m4 @@ -1,21 +1,6 @@ SAGE_SPKG_CONFIGURE([libgd], [ - AC_REQUIRE([SAGE_SPKG_CONFIGURE_LIBPNG]) - AC_REQUIRE([SAGE_SPKG_CONFIGURE_FREETYPE]) - AC_MSG_CHECKING([Installing freetype? ]) - if test x$sage_spkg_install_freetype = xyes; then - AC_MSG_RESULT([Yes. Install libgd as well.]) - sage_spkg_install_libgd=yes - else - dnl do not just rely on libpng being a dependency of freetype - AC_MSG_CHECKING([Installing libpng? ]) - if test x$sage_spkg_install_libpng = xyes; then - AC_MSG_RESULT([Yes. Install libgd as well.]) - sage_spkg_install_libgd=yes - else - AC_MSG_RESULT([No.]) + dnl Trac #31624: Avoid C++ ABI issues + SAGE_SPKG_DEPCHECK([gcc libpng freetype], [ PKG_CHECK_MODULES([LIBGD], [gdlib >= 2.1], [], [sage_spkg_install_libgd=yes]) - fi - fi + ]) ]) - - diff --git a/build/pkgs/ntl/spkg-configure.m4 b/build/pkgs/ntl/spkg-configure.m4 index 5f153f19600..48e41de6ea7 100644 --- a/build/pkgs/ntl/spkg-configure.m4 +++ b/build/pkgs/ntl/spkg-configure.m4 @@ -1,17 +1,7 @@ SAGE_SPKG_CONFIGURE([ntl], [ - AC_REQUIRE([SAGE_SPKG_CONFIGURE_GMP]) - AC_MSG_CHECKING([installing gmp/mpir? ]) - if test x$sage_spkg_install_mpir = xyes -o x$sage_spkg_install_gmp = xyes; then - AC_MSG_RESULT([yes; install ntl as well]) - sage_spkg_install_ntl=yes - else - AC_MSG_RESULT([no]) - fi - m4_pushdef(SAGE_NTL_VERSION_MAJOR, [10]) m4_pushdef(SAGE_NTL_VERSION_MINOR, [3]) - - if test x$sage_spkg_install_ntl != xyes; then + SAGE_SPKG_DEPCHECK([gmp mpir gcc], [ AC_CHECK_HEADER([NTL/ZZ.h], [], [sage_spkg_install_ntl=yes]) AC_MSG_CHECKING([whether we can link a program using NTL]) NTL_SAVED_LIBS=$LIBS @@ -40,7 +30,7 @@ SAGE_SPKG_CONFIGURE([ntl], [ AC_MSG_RESULT([no]) sage_spkg_install_ntl=yes ]) - fi + ]) m4_popdef([SAGE_NTL_VERSION_MAJOR]) m4_popdef([SAGE_NTL_VERSION_MINOR]) diff --git a/build/pkgs/ppl/spkg-configure.m4 b/build/pkgs/ppl/spkg-configure.m4 index 257fb777acc..484dbd2d670 100644 --- a/build/pkgs/ppl/spkg-configure.m4 +++ b/build/pkgs/ppl/spkg-configure.m4 @@ -1,5 +1,5 @@ SAGE_SPKG_CONFIGURE([ppl], [ - SAGE_SPKG_DEPCHECK([glpk gmp mpir], [ + SAGE_SPKG_DEPCHECK([gcc glpk gmp mpir], [ # If our dependencies come from the system, then we can use the # system ppl, too. This macro works sort-of like the # PKG_CHECK_MODULES macro, defining e.g. PPL_CFLAGS when a diff --git a/build/pkgs/sagelib/package-version.txt b/build/pkgs/sagelib/package-version.txt index d528a52f962..c3cae12bcc1 100644 --- a/build/pkgs/sagelib/package-version.txt +++ b/build/pkgs/sagelib/package-version.txt @@ -1 +1 @@ -9.3.rc5 +9.3 diff --git a/build/pkgs/zeromq/spkg-configure.m4 b/build/pkgs/zeromq/spkg-configure.m4 index a584a74cc21..8c0fd29e891 100644 --- a/build/pkgs/zeromq/spkg-configure.m4 +++ b/build/pkgs/zeromq/spkg-configure.m4 @@ -1,3 +1,6 @@ SAGE_SPKG_CONFIGURE([zeromq], [ - AX_ZMQ([4.2.5], [], [sage_spkg_install_zeromq=yes]) + dnl Trac #31624: Avoid C++ ABI issues + SAGE_SPKG_DEPCHECK([gcc], [ + AX_ZMQ([4.2.5], [], [sage_spkg_install_zeromq=yes]) + ]) ]) diff --git a/src/VERSION.txt b/src/VERSION.txt index d528a52f962..c3cae12bcc1 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -9.3.rc5 +9.3 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index fbd2fa27430..461cb470923 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,5 +1,5 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='9.3.rc5' -SAGE_RELEASE_DATE='2021-04-30' -SAGE_VERSION_BANNER='SageMath version 9.3.rc5, Release Date: 2021-04-30' +SAGE_VERSION='9.3' +SAGE_RELEASE_DATE='2021-05-09' +SAGE_VERSION_BANNER='SageMath version 9.3, Release Date: 2021-05-09' diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 843fe204cb2..58d2c60d98a 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -1024,10 +1024,10 @@ def plot(self, sage: fcube = polytopes.hypercube(4) sage: tfcube = fcube.face_truncation(fcube.faces(0)[0]) sage: sp = tfcube.schlegel_projection() - sage: for face in tfcube.faces(2): - ....: vertices = face.ambient_Vrepresentation() - ....: indices = [sp.coord_index_of(vector(x)) for x in vertices] - ....: projected_vertices = [sp.transformed_coords[i] for i in indices] + sage: for face in tfcube.faces(2): + ....: vertices = face.ambient_Vrepresentation() + ....: indices = [sp.coord_index_of(vector(x)) for x in vertices] + ....: projected_vertices = [sp.transformed_coords[i] for i in indices] ....: assert Polyhedron(projected_vertices).dim() == 2 """ def merge_options(*opts): @@ -6891,6 +6891,50 @@ def facets(self): return () return self.faces(self.dimension()-1) + def _test_combinatorial_face_as_combinatorial_polyhedron(self, tester=None, **options): + """ + Run tests on obtaining the combinatorial face as combinatorial polyhedron. + + TESTS:: + + sage: polytopes.cross_polytope(3)._test_combinatorial_face_as_combinatorial_polyhedron() + """ + if not self.is_compact(): + return + if self.dim() > 7 or self.n_vertices() > 100 or self.n_facets() > 100: + # Avoid very long tests. + return + if self.dim() < 1: + # Avoid trivial cases. + return + + from sage.misc.prandom import random + + if tester is None: + tester = self._tester(**options) + + it = self.face_generator() + _ = next(it), next(it) # get rid of non_proper faces + C1 = self.combinatorial_polyhedron() + it1 = C1.face_iter() + C2 = C1.dual() + it2 = C2.face_iter(dual=not it1.dual) + + for f in it: + f1 = next(it1) + f2 = next(it2) + if random() < 0.95: + # Only test a random 5 percent of the faces. + continue + + P = f.as_polyhedron() + D1 = f1.as_combinatorial_polyhedron() + D2 = f2.as_combinatorial_polyhedron(quotient=True).dual() + D1._test_bitsets(tester, **options) + D2._test_bitsets(tester, **options) + tester.assertTrue(P.combinatorial_polyhedron().vertex_facet_graph().is_isomorphic(D1.vertex_facet_graph())) + tester.assertTrue(P.combinatorial_polyhedron().vertex_facet_graph().is_isomorphic(D2.vertex_facet_graph())) + @cached_method(do_pickle=True) def f_vector(self): r""" diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 1449a3831f0..6a19da56be8 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -659,6 +659,24 @@ cdef class CombinatorialPolyhedron(Element): return (CombinatorialPolyhedron, (self.facets(), self.Vrepresentation(), self.Hrepresentation())) + def _test_bitsets(self, tester=None, **options): + """ + Test if the bitsets are consistent. + + TESTS:: + + sage: P = polytopes.cube() + sage: C = CombinatorialPolyhedron(P) + sage: C._test_bitsets() + """ + if tester is None: + tester = self._tester(**options) + + cdef ListOfFaces facets = self.bitrep_facets() + cdef ListOfFaces Vrep = self.bitrep_Vrep() + + tester.assertEqual(facets.matrix(), Vrep.matrix().transpose()) + def Vrepresentation(self): r""" Return a list of names of ``[vertices, rays, lines]``. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd index 64dd767cc94..a2bbfa4f1e9 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd @@ -24,6 +24,7 @@ cdef class CombinatorialFace(SageObject): # some copies from ``CombinatorialPolyhedron`` cdef tuple _ambient_Vrep, _ambient_facets, _equalities + cdef bint _ambient_bounded # Atoms and coatoms are the vertices/facets of the Polyedron. # If ``dual == 0``, then coatoms are facets, atoms vertices and vice versa. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 1d790726f23..8d068de872c 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -74,6 +74,7 @@ from .face_iterator cimport FaceIterator_base from .polyhedron_face_lattice cimport PolyhedronFaceLattice from .face_data_structure cimport face_len_atoms, face_init, face_copy from .face_list_data_structure cimport bit_rep_to_coatom_rep +from .list_of_faces cimport face_as_combinatorial_polyhedron cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -183,6 +184,7 @@ cdef class CombinatorialFace(SageObject): self._ambient_facets = it._facet_names self._equalities = it._equalities self._hash_index = it.structure._index + self._ambient_bounded = it._bounded self._initialized_from_face_lattice = False @@ -207,6 +209,7 @@ cdef class CombinatorialFace(SageObject): self._ambient_Vrep = all_faces._Vrep self._ambient_facets = all_faces._facet_names self._equalities = all_faces._equalities + self._ambient_bounded = all_faces._bounded self._initialized_from_face_lattice = True @@ -751,6 +754,155 @@ cdef class CombinatorialFace(SageObject): n_Hrepr = deprecated_function_alias(28614, n_ambient_Hrepresentation) + def as_combinatorial_polyhedron(self, quotient=False): + r""" + Return ``self`` as combinatorial polyhedron. + + If ``quotient`` is ``True``, return the quotient of the + polyhedron by ``self``. + Let ``G`` be the face corresponding to ``self`` in the dual/polar polytope. + The ``quotient`` is the dual/polar of ``G``. + + Let `[\hat{0], \hat{1}]` be the face lattice of the ambient polyhedron + and `F` be ``self`` as element of the face lattice. + The face lattice of ``self`` as polyhedron corresponds to + `[\hat{0}, F]` and the face lattice of the quotient by ``self`` + corresponds to `[F, \hat{1}]`. + + EXAMPLES:: + + sage: P = polytopes.cyclic_polytope(7,11) + sage: C = CombinatorialPolyhedron(P) + sage: it = C.face_iter(4) + sage: f = next(it); f + A 4-dimensional face of a 7-dimensional combinatorial polyhedron + sage: F = f.as_combinatorial_polyhedron(); F + A 4-dimensional combinatorial polyhedron with 5 facets + sage: F.f_vector() + (1, 5, 10, 10, 5, 1) + sage: F_alt = polytopes.cyclic_polytope(4,5).combinatorial_polyhedron() + sage: F_alt.vertex_facet_graph().is_isomorphic(F.vertex_facet_graph()) + True + + Obtaining the quotient:: + + sage: Q = f.as_combinatorial_polyhedron(quotient=True); Q + A 2-dimensional combinatorial polyhedron with 6 facets + sage: Q + A 2-dimensional combinatorial polyhedron with 6 facets + sage: Q.f_vector() + (1, 6, 6, 1) + + The Vrepresentation of the face as polyhedron is given by the + ambient Vrepresentation of the face in that order:: + + sage: P = polytopes.cube() + sage: C = CombinatorialPolyhedron(P) + sage: it = C.face_iter(2) + sage: f = next(it) + sage: F = f.as_combinatorial_polyhedron() + sage: C.Vrepresentation() + (A vertex at (1, -1, -1), + A vertex at (1, 1, -1), + A vertex at (1, 1, 1), + A vertex at (1, -1, 1), + A vertex at (-1, -1, 1), + A vertex at (-1, -1, -1), + A vertex at (-1, 1, -1), + A vertex at (-1, 1, 1)) + sage: f.ambient_Vrepresentation() + (A vertex at (1, -1, -1), + A vertex at (1, -1, 1), + A vertex at (-1, -1, 1), + A vertex at (-1, -1, -1)) + sage: F.Vrepresentation() + (0, 1, 2, 3) + + To obtain the facets of the face as polyhedron, + we compute the meet of each facet with the face. + The first representative of each element strictly + contained in the face is kept:: + + sage: C.facets(names=False) + ((0, 1, 2, 3), + (1, 2, 6, 7), + (2, 3, 4, 7), + (4, 5, 6, 7), + (0, 1, 5, 6), + (0, 3, 4, 5)) + sage: F.facets(names=False) + ((0, 1), (1, 2), (2, 3), (0, 3)) + + The Hrepresentation of the quotient by the face is given by the + ambient Hrepresentation of the face in that order:: + + sage: it = C.face_iter(1) + sage: f = next(it) + sage: Q = f.as_combinatorial_polyhedron(quotient=True) + sage: C.Hrepresentation() + (An inequality (-1, 0, 0) x + 1 >= 0, + An inequality (0, -1, 0) x + 1 >= 0, + An inequality (0, 0, -1) x + 1 >= 0, + An inequality (1, 0, 0) x + 1 >= 0, + An inequality (0, 0, 1) x + 1 >= 0, + An inequality (0, 1, 0) x + 1 >= 0) + sage: f.ambient_Hrepresentation() + (An inequality (0, 0, 1) x + 1 >= 0, An inequality (0, 1, 0) x + 1 >= 0) + sage: Q.Hrepresentation() + (0, 1) + + To obtain the vertices of the face as polyhedron, + we compute the join of each vertex with the face. + The first representative of each element strictly + containing the face is kept:: + + sage: [g.ambient_H_indices() for g in C.face_iter(0)] + [(3, 4, 5), + (0, 4, 5), + (2, 3, 5), + (0, 2, 5), + (1, 3, 4), + (0, 1, 4), + (1, 2, 3), + (0, 1, 2)] + sage: [g.ambient_H_indices() for g in Q.face_iter(0)] + [(1,), (0,)] + + The method is not implemented for unbounded polyhedra:: + + sage: P = Polyhedron(rays=[[0,1]])*polytopes.cube() + sage: C = CombinatorialPolyhedron(P) + sage: it = C.face_iter(2) + sage: f = next(it) + sage: f.as_combinatorial_polyhedron() + Traceback (most recent call last): + ... + NotImplementedError: only implemented for bounded polyhedra + + REFERENCES: + + For more information, see Exercise 2.9 of [Zie2007]_. + + .. NOTE:: + + This method is tested in + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base._test_combinatorial_face_as_combinatorial_polyhedron`. + """ + if not self._ambient_bounded: + raise NotImplementedError("only implemented for bounded polyhedra") + + cdef ListOfFaces facets = self.atoms if self._dual else self.coatoms + cdef ListOfFaces Vrep = self.atoms if not self._dual else self.coatoms + + if not quotient: + return CombinatorialPolyhedron(face_as_combinatorial_polyhedron(facets, Vrep, self.face, self._dual)) + else: + # We run ``face_as_combinatorial_polyhedron`` for the dual setting. + + # We then interchange the output of it, to obtain the quotient. + new_Vrep, new_facets = face_as_combinatorial_polyhedron(Vrep, facets, self.face, not self._dual) + return CombinatorialPolyhedron((new_facets, new_Vrep)) + cdef size_t n_atom_rep(self) except -1: r""" Compute the number of atoms in the current face by counting the diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd index 2b509aaec01..e91a4a0bd86 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd @@ -151,6 +151,14 @@ cdef inline long face_next_atom(face_t face, mp_bitcnt_t n): """ return bitset_next(face.atoms, n) +cdef inline long face_first_missing_atom(face_t face): + """ + Return the index of the first atom not in ``face``. + + In case there are none, return ``-1``. + """ + return bitset_first_in_complement(face.atoms) + cdef inline long face_len_atoms(face_t face) nogil: """ Calculate the number of atoms in the face. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd index fa28c8c5473..cd910d91cbe 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd @@ -103,6 +103,42 @@ cdef inline int add_face_deep(face_list_t faces, face_t face) except -1: face_copy(faces.faces[faces.n_faces], face) faces.n_faces += 1 +cdef inline void face_list_delete_faces_by_array(face_list_t faces, bint *delete): + r""" + Remove face ``i`` if and only if ``delete[i]`` decreasing ``faces.n_faces``. + + .. WARNING:: + + ``delete`` is assumed to be of length ``faces.n_faces``. + """ + cdef size_t n_newfaces = 0 + cdef size_t i + for i in range(faces.n_faces): + if not delete[i]: + faces.faces[n_newfaces][0] = faces.faces[i][0] + n_newfaces += 1 + + faces.n_faces = n_newfaces + +cdef inline void face_list_delete_faces_by_face(face_list_t faces, face_t face): + r""" + Remove all faces such that the ``i``-th bit in ``face`` is not set + descreasing ``faces.n_faces``. + + .. WARNING:: + + ``face`` is assumed to contain ``self.n_faces`` atoms. + """ + cdef size_t n_newfaces = 0 + cdef size_t i + for i in range(faces.n_faces): + if face_atom_in(face, i): + faces.faces[n_newfaces][0] = faces.faces[i][0] + n_newfaces += 1 + + faces.n_faces = n_newfaces + + ############################################################################# # Face Comparison ############################################################################# diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd index a571b8c1c84..3f3c05f08ab 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd @@ -1,6 +1,6 @@ cimport cython from sage.ext.memory_allocator cimport MemoryAllocator -from .face_list_data_structure cimport face_list_t +from .face_list_data_structure cimport face_list_t, face_t @cython.final cdef class ListOfFaces: @@ -10,6 +10,8 @@ cdef class ListOfFaces: # It will be of "type" ``uint64_t[n_faces][face_length]`` cdef face_list_t data + cpdef ListOfFaces __copy__(self) + cpdef int compute_dimension(self) except -2 cdef inline size_t n_faces(self): @@ -20,3 +22,11 @@ cdef class ListOfFaces: return self.data.n_coatoms cpdef ListOfFaces pyramid(self) + + cdef ListOfFaces delete_atoms_unsafe(self, bint* delete, face_t face) # not in place + cdef void delete_faces_unsafe(self, bint* delete, face_t face) # in place + + cdef void get_not_inclusion_maximal_unsafe(self, bint *not_inclusion_maximal) + cdef void get_faces_all_set_unsafe(self, bint *all_set) + +cdef tuple face_as_combinatorial_polyhedron(ListOfFaces facets, ListOfFaces Vrep, face_t face, bint dual) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx index 8974a80ab3a..98b3d4df828 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx @@ -91,7 +91,6 @@ AUTHOR: # **************************************************************************** from sage.structure.element import is_Matrix - from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense from .face_list_data_structure cimport * @@ -154,7 +153,7 @@ cdef class ListOfFaces: """ assert face_list_check_alignment(self.data) - def __copy__(self): + cpdef ListOfFaces __copy__(self): r""" Return a copy of self. @@ -357,6 +356,101 @@ cdef class ListOfFaces: return copy + cdef ListOfFaces delete_atoms_unsafe(self, bint *delete, face_t face): + r""" + Return a copy of ``self`` where bits in ``delete`` have been + removed/contracted. + + The the remaining bits will be shifted to the left. + + If ``delete`` is ``NULL``, keep exactly the bits set in ``face``. + + .. WARNING:: + + ``delete`` is assumed to be of length ``self.n_atoms`` or NULL. + ``face`` is assumed to be of length ``self.face_length`` if ``delete`` is not ``NULL``. + """ + + cdef output_n_atoms + cdef size_t i, j + if delete is NULL: + output_n_atoms = face_len_atoms(face) + else: + output_n_atoms = self.n_atoms() + for i in range(self.n_atoms()): + if delete[i]: + output_n_atoms -= 1 + + cdef ListOfFaces output = ListOfFaces(self.n_faces(), output_n_atoms, self.n_coatoms()) + cdef size_t counter = 0 + cdef size_t n_atoms = self.n_atoms() + cdef size_t n_faces = self.n_faces() + for i in range(n_atoms): + if ((delete is NULL and face_atom_in(face, i)) or + (delete is not NULL and not delete[i])): + # The atom will be kept. + for j in range(n_faces): + if face_atom_in(self.data.faces[j], i): + face_add_atom(output.data.faces[j], counter) + counter += 1 + + return output + + cdef void delete_faces_unsafe(self, bint *delete, face_t face): + r""" + Deletes face ``i`` if and only if ``delete[i]``. + + Alternatively, deletes all faces such that the ``i``-th bit in ``face`` is not set. + + This will modify ``self``. + + .. WARNING:: + + ``delete`` is assumed to be of length ``self.n_faces()`` or NULL. + ``face`` is assumed to contain ``self.n_faces()`` atoms if ``delete`` is not ``NULL``. + """ + if delete is not NULL: + face_list_delete_faces_by_array(self.data, delete) + else: + face_list_delete_faces_by_face(self.data, face) + + cdef void get_not_inclusion_maximal_unsafe(self, bint *not_inclusion_maximal): + r""" + Get all faces that are not inclusion maximal. + + Set ``not_inclusion_maximal[i]`` to one if ``self.data[i]`` is not + an inclusion-maximal face, otherwise to zero. + + If there are duplicates, all but the last duplicate will be marked as + not inclusion maximal. + + .. WARNING:: + + ``not_inclusion_maximal`` is assumed to be at least of length ``self.n_atoms`` or NULL. + """ + cdef size_t i + memset(not_inclusion_maximal, 0, sizeof(bint)*self.n_faces()) + for i in range(self.n_faces()): + not_inclusion_maximal[i] = is_not_maximal_fused(self.data, i, 0, not_inclusion_maximal) + + cdef void get_faces_all_set_unsafe(self, bint *all_set): + r""" + Get the faces that have all ``bits`` set. + + Set ``all_set[i]`` to one if ``self.data[i]`` + has all bits set, otherwise to zero. + + .. WARNING:: + + ``all_set`` is assumed to be at least of length ``self.n_atoms`` or NULL. + """ + cdef size_t i + for i in range(self.n_faces()): + if face_first_missing_atom(self.data.faces[i]) == -1: + all_set[i] = 1 + else: + all_set[i] = 0 + def matrix(self): r""" Obtain the matrix of self. @@ -398,3 +492,62 @@ cdef class ListOfFaces: M.set_immutable() return M + +cdef tuple face_as_combinatorial_polyhedron(ListOfFaces facets, ListOfFaces Vrep, face_t face, bint dual): + r""" + Obtain facets and Vrepresentation of ``face`` as new combinatorial polyhedron. + + INPUT: + + - ``facets`` -- facets of the polyhedron + - ``Vrep`` -- Vrepresentation of the polyhedron + - ``face`` -- face in Vrepresentation or ``NULL`` + - ``dual`` -- boolean + + OUTPUT: A tuple of new facets and new Vrepresentation as :class:`ListOfFaces`. + """ + cdef ListOfFaces new_facets, new_Vrep + cdef bint* delete + cdef MemoryAllocator mem = MemoryAllocator() + cdef size_t i + cdef face_t null_face + + # Delete all atoms not in the face. + if not dual: + new_facets = facets.delete_atoms_unsafe(NULL, face) + new_Vrep = Vrep.__copy__() + new_Vrep.delete_faces_unsafe(NULL, face) + + delete = mem.allocarray(new_facets.n_faces(), sizeof(bint)) + else: + delete = mem.allocarray(max(facets.n_faces(), facets.n_atoms()), sizeof(bint)) + + # Set ``delete[i]`` to one if ``i`` is not an vertex of ``face``. + for i in range(Vrep.n_faces()): + if face_issubset(face, Vrep.data.faces[i]): + delete[i] = 0 + else: + delete[i] = 1 + + new_facets = facets.delete_atoms_unsafe(delete, null_face) + new_Vrep = Vrep.__copy__() + new_Vrep.delete_faces_unsafe(delete, null_face) + + # Delete all facets that define the face. + new_facets.get_faces_all_set_unsafe(delete) + new_facets.delete_faces_unsafe(delete, null_face) + new_Vrep = new_Vrep.delete_atoms_unsafe(delete, null_face) + + # Now delete all facets that are not inclusion maximal. + # the last copy of each duplicate will remain. + new_facets.get_not_inclusion_maximal_unsafe(delete) + new_facets.delete_faces_unsafe(delete, null_face) + new_Vrep = new_Vrep.delete_atoms_unsafe(delete, null_face) + + # Finally set coatoms of the output. + for i in range(new_facets.n_faces()): + facet_set_coatom(new_facets.data.faces[i], i) + for i in range(new_Vrep.n_faces()): + facet_set_coatom(new_Vrep.data.faces[i], i) + + return (new_facets, new_Vrep) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd index 951fd46db41..b9bc4d80c0c 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd @@ -16,6 +16,7 @@ cdef class PolyhedronFaceLattice: # some copies from CombinatorialPolyhedron cdef tuple _Vrep, _facet_names, _equalities + cdef bint _bounded # Atoms and coatoms are the Vrep/facets of the Polyedron. # If ``dual == 0``, then coatoms are facets, atoms Vrepresentatives and vice versa. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx index 6c9f5c678b5..f01b38b94eb 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx @@ -139,6 +139,7 @@ cdef class PolyhedronFaceLattice: self._Vrep = C.Vrep() self._facet_names = C.facet_names() self._equalities = C.equalities() + self._bounded = C.is_bounded() # copy f_vector for later use f_vector = C.f_vector() diff --git a/src/sage/version.py b/src/sage/version.py index 43fe9e98b50..fd6e15e31ff 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '9.3.rc5' -date = '2021-04-30' -banner = 'SageMath version 9.3.rc5, Release Date: 2021-04-30' +version = '9.3' +date = '2021-05-09' +banner = 'SageMath version 9.3, Release Date: 2021-05-09'