From 1fc3d4cf8050c70e07906f56f88929556748519d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 27 Nov 2020 19:01:43 -0800 Subject: [PATCH 001/280] build/pkgs/threejs: Install in /threejs-sage/rVERSION/ --- build/pkgs/threejs/package-version.txt | 2 +- build/pkgs/threejs/spkg-install.in | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build/pkgs/threejs/package-version.txt b/build/pkgs/threejs/package-version.txt index 98ffb8e64ea..f149e1131d2 100644 --- a/build/pkgs/threejs/package-version.txt +++ b/build/pkgs/threejs/package-version.txt @@ -1 +1 @@ -r122 +r122.p0 diff --git a/build/pkgs/threejs/spkg-install.in b/build/pkgs/threejs/spkg-install.in index 579161d67d9..0ae18ac7347 100644 --- a/build/pkgs/threejs/spkg-install.in +++ b/build/pkgs/threejs/spkg-install.in @@ -1 +1,2 @@ -sdh_install src/* "${SAGE_SHARE}/threejs" +sdh_install src/version "${SAGE_SHARE}/threejs-sage/" +sdh_install -T src/build "${SAGE_SHARE}/threejs-sage/$(cat src/version)" From c8e707c031cea525aad4d2bfbad7c5e77eed4462 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 27 Nov 2020 19:03:41 -0800 Subject: [PATCH 002/280] src/sage/repl/ipython_kernel/install.py: Change from threejs to threejs-sage --- src/sage/repl/ipython_kernel/install.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/repl/ipython_kernel/install.py b/src/sage/repl/ipython_kernel/install.py index 073fae573c8..e9e5bac9d4e 100644 --- a/src/sage/repl/ipython_kernel/install.py +++ b/src/sage/repl/ipython_kernel/install.py @@ -159,12 +159,12 @@ def use_local_threejs(self): sage: from sage.repl.ipython_kernel.install import SageKernelSpec sage: spec = SageKernelSpec(prefix=tmp_dir()) sage: spec.use_local_threejs() - sage: threejs = os.path.join(spec.nbextensions_dir, 'threejs') + sage: threejs = os.path.join(spec.nbextensions_dir, 'threejs-sage') sage: os.path.isdir(threejs) True """ src = THREEJS_DIR - dst = os.path.join(self.nbextensions_dir, 'threejs') + dst = os.path.join(self.nbextensions_dir, 'threejs-sage') self.symlink(src, dst) def _kernel_cmd(self): From 5e1ca4e066517147edc2c43dc291e2ebe126225b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 27 Nov 2020 19:08:32 -0800 Subject: [PATCH 003/280] src/sage/repl/ipython_kernel/install.py, src/sage/env.py, build/pkgs/sage_conf: Change from threejs to threejs-sage --- build/pkgs/sage_conf/src/sage_conf.py.in | 2 +- src/sage/env.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/sage_conf/src/sage_conf.py.in b/build/pkgs/sage_conf/src/sage_conf.py.in index b68efd38da5..ac0ea3155eb 100644 --- a/build/pkgs/sage_conf/src/sage_conf.py.in +++ b/build/pkgs/sage_conf/src/sage_conf.py.in @@ -16,7 +16,7 @@ CBLAS_PC_MODULES = "cblas" # Used in sage.repl.ipython_kernel.install JSMOL_DIR = "@prefix@/share/jsmol" MATHJAX_DIR = "@prefix@/share/mathjax" -THREEJS_DIR = "@prefix@/share/threejs" +THREEJS_DIR = "@prefix@/share/threejs-sage" # The following must not be used during build to determine source or installation # location of sagelib. See comments in SAGE_ROOT/src/Makefile.in diff --git a/src/sage/env.py b/src/sage/env.py index 83154eb6b1e..a17b1b08aa0 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -191,7 +191,7 @@ def var(key, *fallbacks, **kwds): var('JSMOL_DIR', join(SAGE_SHARE, 'jsmol')) var('MATHJAX_DIR', join(SAGE_SHARE, 'mathjax')) var('MTXLIB', join(SAGE_SHARE, 'meataxe')) -var('THREEJS_DIR', join(SAGE_SHARE, 'threejs')) +var('THREEJS_DIR', join(SAGE_SHARE, 'threejs-sage')) var('SINGULARPATH', join(SAGE_SHARE, 'singular')) var('PPLPY_DOCS', join(SAGE_SHARE, 'doc', 'pplpy')) var('MAXIMA', 'maxima') From 938bff40dbf6a3d5c96b5a90cffff6691c26db66 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 27 Nov 2020 19:33:28 -0800 Subject: [PATCH 004/280] Read required threejs version from ext_data --- src/sage/ext_data/threejs/threejs-version.txt | 1 + src/sage/repl/rich_output/backend_ipython.py | 3 ++- src/sage/repl/rich_output/display_manager.py | 15 +++++++++++++-- 3 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 src/sage/ext_data/threejs/threejs-version.txt diff --git a/src/sage/ext_data/threejs/threejs-version.txt b/src/sage/ext_data/threejs/threejs-version.txt new file mode 100644 index 00000000000..98ffb8e64ea --- /dev/null +++ b/src/sage/ext_data/threejs/threejs-version.txt @@ -0,0 +1 @@ +r122 diff --git a/src/sage/repl/rich_output/backend_ipython.py b/src/sage/repl/rich_output/backend_ipython.py index 1400b175ca9..4f5d86bfb19 100644 --- a/src/sage/repl/rich_output/backend_ipython.py +++ b/src/sage/repl/rich_output/backend_ipython.py @@ -413,8 +413,9 @@ def threejs_offline_scripts(self): '... """.format(version) From f847738d8367ea931af8f955a72cf1a4f64913ab Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 27 Nov 2020 19:38:18 -0800 Subject: [PATCH 005/280] src/sage/repl/rich_output/backend_ipython.py: Another change threejs -> threejs-sage --- src/sage/repl/rich_output/backend_ipython.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/repl/rich_output/backend_ipython.py b/src/sage/repl/rich_output/backend_ipython.py index 4f5d86bfb19..6090edf9fd0 100644 --- a/src/sage/repl/rich_output/backend_ipython.py +++ b/src/sage/repl/rich_output/backend_ipython.py @@ -597,12 +597,12 @@ def threejs_offline_scripts(self): sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook sage: backend = BackendIPythonNotebook() sage: backend.threejs_offline_scripts() - '... + From d13177d1a89cc6bb643411b8bb8b233f9e1a54a6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 27 Nov 2020 21:22:09 -0800 Subject: [PATCH 006/280] src/sage/repl/rich_output/display_manager.py: Fix up imports --- src/sage/repl/rich_output/display_manager.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/repl/rich_output/display_manager.py b/src/sage/repl/rich_output/display_manager.py index fd0818233b1..f8084ae09d5 100644 --- a/src/sage/repl/rich_output/display_manager.py +++ b/src/sage/repl/rich_output/display_manager.py @@ -52,6 +52,8 @@ def _required_threejs_version(): sage: _required_threejs_version() 'r...' """ + import os + import sage.env with open(os.path.join(sage.env.SAGE_EXTCODE, 'threejs', 'threejs-version.txt')) as f: return f.read().strip() @@ -758,9 +760,6 @@ def threejs_scripts(self, online): offline threejs graphics """ if online: - import sage.env - import re - import os version = _required_threejs_version() return """ From 47e795ba807db4eb4c742099f3d56cada76fc9b2 Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Sat, 28 Nov 2020 18:45:41 -0800 Subject: [PATCH 007/280] Fix 404 fetching three.min.js from Jupyter/JupyterLab --- src/sage/repl/rich_output/backend_ipython.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/repl/rich_output/backend_ipython.py b/src/sage/repl/rich_output/backend_ipython.py index 6090edf9fd0..bf56da3f5aa 100644 --- a/src/sage/repl/rich_output/backend_ipython.py +++ b/src/sage/repl/rich_output/backend_ipython.py @@ -597,13 +597,15 @@ def threejs_offline_scripts(self): sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook sage: backend = BackendIPythonNotebook() sage: backend.threejs_offline_scripts() - '...', r'<\/script>').replace('\n', ' \\\n') return """ - + - """.format(CDN_script.replace('', r'<\/script>').replace('\n', ' \\\n')) + """.format(_required_threejs_version(), CDN_script) From e4f7e7a3e41c3b3ea77f93cccfd612005f30a17b Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 14 Jan 2021 09:47:52 +0100 Subject: [PATCH 008/280] temporary commit --- .../combinatorial_polyhedron/base.pxd | 2 +- .../combinatorial_polyhedron/base.pyx | 39 ++-- .../face_iterator.pxd | 5 +- .../face_iterator.pyx | 167 +++++++++++++++++- .../face_list_data_structure.pxd | 2 - 5 files changed, 193 insertions(+), 22 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd index f417e368f84..379ed5bdc62 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd @@ -62,7 +62,7 @@ cdef class CombinatorialPolyhedron(SageObject): cdef tuple _mem_tuple cdef FaceIterator _face_iter(self, bint dual, int dimension) - cdef int _compute_f_vector(self) except -1 + cdef int _compute_f_vector(self, size_t num_threads, size_t parallelization_depth) except -1 cdef int _compute_edges(self, dual) except -1 cdef int _compute_ridges(self, dual) except -1 cdef int _compute_face_lattice_incidences(self) except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 86175a23769..c8e3e959c76 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -104,6 +104,7 @@ from cysignals.signals cimport sig_check, sig_block, sig_unblock from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense from .face_data_structure cimport face_len_atoms, face_init +from .face_iterator cimport iter_t, parallel_f_vector cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -1554,7 +1555,7 @@ cdef class CombinatorialPolyhedron(SageObject): return DiGraph([vertices, edges], format='vertices_and_edges', immutable=True) @cached_method - def f_vector(self): + def f_vector(self, num_threads=1, parallelization_depth=0): r""" Compute the ``f_vector`` of the polyhedron. @@ -1584,7 +1585,7 @@ cdef class CombinatorialPolyhedron(SageObject): """ if not self._f_vector: - self._compute_f_vector() + self._compute_f_vector(num_threads, parallelization_depth) if not self._f_vector: raise ValueError("could not determine f_vector") from sage.modules.free_module_element import vector @@ -2888,7 +2889,7 @@ cdef class CombinatorialPolyhedron(SageObject): # Internal methods. - cdef int _compute_f_vector(self) except -1: + cdef int _compute_f_vector(self, size_t num_threads, size_t parallelization_depth) except -1: r""" Compute the ``f_vector`` of the polyhedron. @@ -2897,6 +2898,18 @@ cdef class CombinatorialPolyhedron(SageObject): if self._f_vector: return 0 # There is no need to recompute the f_vector. + cdef int dim = self.dimension() + cdef int d # dimension of the current face of the iterator + cdef MemoryAllocator mem = MemoryAllocator() + + if num_threads == 0: + # No need to complain. + num_threads = 1 + + if parallelization_depth > dim - 1: + # Is a very bad choice anyway, but prevent segmenation faults. + parallelization_depth = dim - 1 + cdef bint dual if not self.is_bounded() or self.n_facets() <= self.n_Vrepresentation(): # In this case the non-dual approach is faster.. @@ -2904,25 +2917,21 @@ cdef class CombinatorialPolyhedron(SageObject): else: # In this case the dual approach is faster. dual = True - cdef FaceIterator face_iter = self._face_iter(dual, -2) - cdef int dim = self.dimension() - cdef int d # dimension of the current face of the iterator - cdef MemoryAllocator mem = MemoryAllocator() + face_iters = [self._face_iter(dual, -2) for _ in range(num_threads)] + cdef FaceIterator face_iter + cdef iter_t* structs = mem.allocarray(num_threads, sizeof(iter_t)) + cdef size_t i + for i in range(num_threads): + face_iter = face_iters[i] + structs[i][0] = face_iter.structure[0] # Initialize ``f_vector``. cdef size_t *f_vector = mem.calloc((dim + 2), sizeof(size_t)) f_vector[0] = 1 # Face iterator will only visit proper faces. f_vector[dim + 1] = 1 # Face iterator will only visit proper faces. - # For each face in the iterator, add `1` to the corresponding entry in - # ``f_vector``. - if self.n_facets() > 0 and dim > 0: - d = face_iter.next_dimension() - while (d < dim): - sig_check() - f_vector[d+1] += 1 - d = face_iter.next_dimension() + parallel_f_vector(structs, parallelization_depth, num_threads, f_vector) # Copy ``f_vector``. if dual: diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 56b062af495..c03d6ca7c8e 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -45,6 +45,8 @@ cdef struct iter_s: # The number of elements in newfaces[current_dimension], # that have not been visited yet. size_t yet_to_visit + size_t current_job_id + size_t n_coatoms ctypedef iter_s iter_t[1] @@ -82,6 +84,7 @@ cdef class FaceIterator_geom(FaceIterator_base): # Nogil definitions of crucial functions. -cdef int next_dimension(iter_t structure) nogil except -1 +cdef int next_dimension(iter_t structure, size_t parallelization_depth=?) nogil except -1 cdef int next_face_loop(iter_t structure) nogil except -1 cdef size_t n_atom_rep(iter_t structure) nogil except -1 +cdef int parallel_f_vector(iter_t *structures, size_t parallelization_depth, size_t n_threads, size_t *f_vector) except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 6d79766950e..4b4b8533737 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1,3 +1,5 @@ +# distutils: extra_compile_args = -fopenmp +# distutils: extra_link_args = -fopenmp r""" Face iterator for polyhedra @@ -183,6 +185,9 @@ from .base cimport CombinatorialPolyhedron from sage.geometry.polyhedron.face import combinatorial_face_to_polyhedral_face, PolyhedronFace from .face_list_data_structure cimport * +from cython.parallel cimport prange +cimport openmp + cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -308,6 +313,9 @@ cdef class FaceIterator_base(SageObject): self.structure.yet_to_visit = self.coatoms.n_faces() self.structure._index = 0 + self.structure.current_job_id = 0 + self.structure.n_coatoms = self.coatoms.n_faces() + if C.is_bounded() and ((dual and C.is_simplicial()) or (not dual and C.is_simple())): # We are in the comfortable situation that for our iterator # all intervals not containing the 0 element are boolean. @@ -351,6 +359,7 @@ cdef class FaceIterator_base(SageObject): self.structure.yet_to_visit = self.coatoms.n_faces() self.structure._index = 0 + self.structure.current_job_id = 0 def __next__(self): r""" @@ -1187,11 +1196,14 @@ cdef class FaceIterator_geom(FaceIterator_base): # Nogil definitions of crucial functions. -cdef inline int next_dimension(iter_t structure) nogil except -1: +cdef inline int next_dimension(iter_t structure, size_t parallelization_depth=0) nogil except -1: r""" See :meth:`FaceIterator.next_dimension`. + + ``parallelization_depth`` determines when to stop, + e.g. if it is ``1`` it will stop after having yield all faces of a facet """ - cdef int dim = structure.dimension + cdef int dim = structure.dimension - parallelization_depth structure.face_status = 0 while (not next_face_loop(structure)) and (structure.current_dimension < dim): sig_check() @@ -1205,7 +1217,8 @@ cdef inline int next_face_loop(iter_t structure) nogil except -1: if unlikely(structure.current_dimension == structure.dimension): # The function is not supposed to be called, # just prevent it from crashing. - raise StopIteration + with gil: + raise StopIteration # Getting ``[faces, n_faces, n_visited_all]`` according to dimension. cdef face_list_t* faces = &structure.new_faces[structure.current_dimension] @@ -1285,3 +1298,151 @@ cdef inline size_t n_atom_rep(iter_t structure) nogil except -1: # The face was not initialized properly. raise LookupError("``FaceIterator`` does not point to a face") + +cdef int parallel_f_vector(iter_t *structures, size_t parallelization_depth, size_t n_threads, size_t *f_vector) except -1: + cdef size_t n_jobs = structures[0].n_coatoms ** parallelization_depth + cdef size_t i + cdef size_t thread_id + for i in prange(n_jobs, schedule='dynamic', chunksize=1, num_threads=n_threads, nogil=True): + _parallel_f_vector(structures[openmp.omp_get_thread_num()], parallelization_depth, f_vector, i) + +cdef int _parallel_f_vector(iter_t structure, size_t parallelization_depth, size_t *f_vector, size_t job_id) nogil except -1: + cdef int max_dimension = structure.dimension - parallelization_depth + cdef int d + if prepare_face_iterator_for_partial_job(structure, parallelization_depth, job_id, f_vector): + d = next_dimension(structure, parallelization_depth) + while (d < max_dimension): + sig_check() + f_vector[d+1] += 1 + d = next_dimension(structure, parallelization_depth) + +cdef inline int prepare_face_iterator_for_partial_job(iter_t structure, size_t parallelization_depth, size_t job_id, size_t* f_vector=NULL) nogil except -1: + if not structure.first_time[structure.current_dimension]: + # Act as if we had not visited this face. + # Thus the job id is correct again. + structure.first_time[structure.current_dimension] = True + structure.new_faces[structure.current_dimension].n_faces += 1 + cdef size_t n_coatoms = structure.n_coatoms + cdef size_t current_depth + cdef size_t job_id_i + cdef size_t current_job_id_i + cdef size_t test_id + cdef size_t i + for current_depth in range(1, parallelization_depth + 1): + job_id_i = job_id_get(job_id, current_depth - 1, parallelization_depth, n_coatoms) + current_job_id_i = job_id_get(structure.current_job_id, current_depth - 1, parallelization_depth, n_coatoms) + if job_id_i > current_job_id_i: + job_id_set(&structure.current_job_id, job_id_i, current_depth - 1, parallelization_depth, n_coatoms) + structure.current_dimension = structure.dimension - current_depth + for i in range(current_job_id_i, job_id_i): + if not skip_next_face(structure): + job_id_set(&structure.current_job_id, i, current_depth - 1, parallelization_depth, n_coatoms) + # This job id does not exist. + return 0 + + if job_id_i < current_job_id_i: + job_id_set(&structure.current_job_id, job_id_i, current_depth - 1, parallelization_depth, n_coatoms) + structure.current_dimension = structure.dimension - current_depth + for i in range(job_id_i, current_job_id_i): + repeat_last_face(structure) + + # Appparently the face exists. We add it to the f-vector, if it is the very first job for the face. + test_id = job_id + job_id_set(&test_id, job_id_i, current_depth - 1, parallelization_depth, n_coatoms) + if test_id == job_id: + f_vector[structure.dimension - current_depth + 1] += 1 + + if structure.current_dimension == structure.dimension - current_depth: + structure.yet_to_visit = 0 + next_face_loop(structure) + if structure.current_dimension != structure.dimension - current_depth - 1: + return 0 + + if structure.current_dimension != structure.dimension - parallelization_depth - 1: + return 0 + + return 1 + +cdef inline int skip_next_face(iter_t structure) nogil except -1: + """ + Moves the iterator to the state it would be after having visited all + subfaces of ``structure.new_faces[structure.current_dimension]``. + + Return ``0`` if no farther faces of this dimension exist. + Return ``1`` otherwise. + + .. WARNING:: + + This is rather unsafe. It should be called only in two scenarios: + - ``structure.current_dimension`` was manually raised + - ``next_dimesion`` returned ``dimension - parallelization_depth`` + for ``parallelization_depth > 0`` + """ + cdef face_list_t* faces = &structure.new_faces[structure.current_dimension] + cdef face_list_t* visited_all = &structure.visited_all[structure.current_dimension] + + if faces[0].n_faces == 0: + return 0 + + if not structure.first_time[structure.current_dimension]: + # In this case there exists ``faces[0].faces[n_faces]``, of which we + # have visited all faces, but which was not added to + # ``visited_all`` yet. + add_face_shallow(visited_all[0], faces[0].faces[faces[0].n_faces]) + else: + # Once we have visited all faces of ``faces[n_faces]``, we want + # to add it to ``visited_all``. + structure.first_time[structure.current_dimension] = False + faces[0].n_faces -= 1 + if faces[0].n_faces == 0: + return 0 + return 1 + +cdef inline int repeat_last_face(iter_t structure) nogil except -1: + """ + The inverse function to ``skip_next_face``. + """ + cdef face_list_t* faces = &structure.new_faces[structure.current_dimension] + cdef face_list_t* visited_all = &structure.visited_all[structure.current_dimension] + if unlikely(faces[0].n_faces == faces[0].total_n_faces): + with gil: + raise MemoryError("wrong usage") + + if structure.first_time[structure.current_dimension]: + # faces[n_faces] is already in visited_all. + # As we want to revisit it, we should remove it from ``visited_all``. + visited_all[0].n_faces -= 1 + + faces[0].n_faces += 1 + # faces[n_faces] is already in visited_all and should not be readded. + structure.first_time[structure.current_dimension] = True + +# Basically, ``job_id`` represents an element in `[0,n_coatoms-1]^(parallelization_depth)`. +# We deal with this by obtaining/setting digits with base ``n_coatoms``. + +cdef inline size_t job_id_get(size_t job_id, size_t pos, size_t parallelization_depth, size_t n_coatoms) nogil: + """ + Get the digit ``pos`` of ``job_id`` with base ``n_coatoms`` + padding the number of digits to ``parallelization_depth``. + + Digits are enumerated started with ``0``. + """ + # Remove the last ``parallelization_depth - pos - 1`` digits. + cdef size_t current_output = job_id // n_coatoms**(parallelization_depth - pos - 1) + + # Remove all digits before our current digit, which is digit ``pos``. + return current_output % n_coatoms + +cdef inline void job_id_set(size_t* job_id, size_t val, size_t pos, size_t parallelization_depth, size_t n_coatoms) nogil: + """ + Set the digit ``pos`` of ``job_id`` with base ``n_coatoms`` + padding the number of digits to ``parallelization_depth``. + + Set all further digits to ``0``. + + Digits are enumerated started with ``0``. + """ + cdef size_t trailing = job_id[0] % n_coatoms**(parallelization_depth - pos) + job_id[0] -= trailing + job_id[0] += val * n_coatoms**(parallelization_depth - pos - 1) + 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 13c77a42060..8a88eaaf62e 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 @@ -273,12 +273,10 @@ cdef inline size_t get_next_level( face_list_t visited_all) nogil except -1: cdef size_t output - sig_on() if faces.polyhedron_is_simple: output = get_next_level_fused(faces, new_faces, visited_all, 0) else: output = get_next_level_fused(faces, new_faces, visited_all, 0) - sig_off() return output cdef inline size_t bit_rep_to_coatom_rep(face_t face, face_list_t coatoms, size_t *output): From bcb6607aed57923ff81583ba49310f6094a81f13 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 14 Jan 2021 14:33:59 +0100 Subject: [PATCH 009/280] again a working parallel version --- .../face_iterator.pxd | 3 +- .../face_iterator.pyx | 214 +++++++++--------- .../face_list_data_structure.pxd | 21 +- 3 files changed, 118 insertions(+), 120 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index c03d6ca7c8e..0515e2551bb 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -35,7 +35,7 @@ cdef struct iter_s: face_list_t* new_faces # After having visited a face completely, we want to add it to ``visited_all``. - # ``first_dim[i]`` will indicate, wether there is one more face in + # ``first_time[i]`` will indicate, wether there is one more face in # ``newfaces[i]`` then ``n_newfaces[i]`` suggests # that has to be added to ``visited_all``. # If ``first_time[i] == False``, we still need to @@ -45,7 +45,6 @@ cdef struct iter_s: # The number of elements in newfaces[current_dimension], # that have not been visited yet. size_t yet_to_visit - size_t current_job_id size_t n_coatoms ctypedef iter_s iter_t[1] diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 4b4b8533737..0a0e5ce2bbb 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -313,7 +313,6 @@ cdef class FaceIterator_base(SageObject): self.structure.yet_to_visit = self.coatoms.n_faces() self.structure._index = 0 - self.structure.current_job_id = 0 self.structure.n_coatoms = self.coatoms.n_faces() if C.is_bounded() and ((dual and C.is_simplicial()) or (not dual and C.is_simple())): @@ -359,7 +358,6 @@ cdef class FaceIterator_base(SageObject): self.structure.yet_to_visit = self.coatoms.n_faces() self.structure._index = 0 - self.structure.current_job_id = 0 def __next__(self): r""" @@ -1217,8 +1215,8 @@ cdef inline int next_face_loop(iter_t structure) nogil except -1: if unlikely(structure.current_dimension == structure.dimension): # The function is not supposed to be called, # just prevent it from crashing. - with gil: - raise StopIteration + # Actually raising an error here results in a bad branch prediction. + return -1 # Getting ``[faces, n_faces, n_visited_all]`` according to dimension. cdef face_list_t* faces = &structure.new_faces[structure.current_dimension] @@ -1272,7 +1270,7 @@ cdef inline int next_face_loop(iter_t structure) nogil except -1: # ``faces[n_faces]`` contains new faces. # We will visted them on next call, starting with codimension 1. - # Setting the variables correclty for next call of ``next_face_loop``. + # Setting the variables correctly for next call of ``next_face_loop``. structure.current_dimension -= 1 structure.first_time[structure.current_dimension] = True structure.visited_all[structure.current_dimension][0] = visited_all[0][0] @@ -1299,124 +1297,136 @@ cdef inline size_t n_atom_rep(iter_t structure) nogil except -1: # The face was not initialized properly. raise LookupError("``FaceIterator`` does not point to a face") +cdef struct parallel_f_s: + size_t* f_vector + size_t* current_job_id + size_t* original_n_faces + size_t* original_n_visited_all + +ctypedef parallel_f_s parallel_f_t[1] + cdef int parallel_f_vector(iter_t *structures, size_t parallelization_depth, size_t n_threads, size_t *f_vector) except -1: cdef size_t n_jobs = structures[0].n_coatoms ** parallelization_depth cdef size_t i + cdef int j + cdef int dim = structures[0].dimension cdef size_t thread_id + cdef MemoryAllocator mem = MemoryAllocator() + cdef parallel_f_t* parallel_structs = mem.allocarray(n_threads, sizeof(parallel_f_t)) + cdef parallel_f_s* parallel_structs_s = mem.allocarray(n_threads, sizeof(parallel_f_s)) + for i in range(n_threads): + parallel_structs[i][0] = parallel_structs_s[i] + parallel_structs[i].f_vector = mem.calloc(dim+2, sizeof(size_t)) + parallel_structs[i].current_job_id = mem.calloc(parallelization_depth+1, sizeof(size_t)) + parallel_structs[i].original_n_faces = mem.calloc(parallelization_depth+1, sizeof(size_t)) + parallel_structs[i].original_n_faces[0] = structures[0].new_faces[dim - 1].n_faces + parallel_structs[i].original_n_visited_all = mem.calloc(parallelization_depth+1, sizeof(size_t)) + parallel_structs[i].original_n_visited_all[0] = structures[0].visited_all[dim - 1].n_faces + for i in prange(n_jobs, schedule='dynamic', chunksize=1, num_threads=n_threads, nogil=True): - _parallel_f_vector(structures[openmp.omp_get_thread_num()], parallelization_depth, f_vector, i) + _parallel_f_vector(structures[openmp.omp_get_thread_num()], + parallelization_depth, + parallel_structs[openmp.omp_get_thread_num()], + i) -cdef int _parallel_f_vector(iter_t structure, size_t parallelization_depth, size_t *f_vector, size_t job_id) nogil except -1: + # Gather the results. + for i in range(n_threads): + for j in range(structures[0].dimension + 2): + f_vector[j] += parallel_structs[i].f_vector[j] + +cdef int _parallel_f_vector(iter_t structure, size_t parallelization_depth, parallel_f_t parallel_struct, size_t job_id) nogil except -1: cdef int max_dimension = structure.dimension - parallelization_depth cdef int d - if prepare_face_iterator_for_partial_job(structure, parallelization_depth, job_id, f_vector): + if prepare_face_iterator_for_partial_job(structure, parallelization_depth, parallel_struct, job_id): d = next_dimension(structure, parallelization_depth) while (d < max_dimension): - sig_check() - f_vector[d+1] += 1 + #sig_check() + parallel_struct.f_vector[d+1] += 1 d = next_dimension(structure, parallelization_depth) -cdef inline int prepare_face_iterator_for_partial_job(iter_t structure, size_t parallelization_depth, size_t job_id, size_t* f_vector=NULL) nogil except -1: - if not structure.first_time[structure.current_dimension]: - # Act as if we had not visited this face. - # Thus the job id is correct again. - structure.first_time[structure.current_dimension] = True - structure.new_faces[structure.current_dimension].n_faces += 1 - cdef size_t n_coatoms = structure.n_coatoms +cdef inline int prepare_face_iterator_for_partial_job(iter_t structure, size_t parallelization_depth, parallel_f_t parallel_struct, size_t job_id) nogil except -1: + cdef int d cdef size_t current_depth - cdef size_t job_id_i - cdef size_t current_job_id_i - cdef size_t test_id + if (not structure.first_time[structure.current_dimension] + and structure.current_dimension == structure.dimension - parallelization_depth): + # Act as if we had not visited faces in the last depth. + d = structure.dimension - parallelization_depth + current_depth = parallelization_depth + structure.new_faces[d].n_faces = parallel_struct.original_n_faces[current_depth -1] + structure.new_faces[d].n_faces = parallel_struct.original_n_visited_all[current_depth -1] + parallel_struct.current_job_id[current_depth -1] = 0 + parallel_struct.current_job_id[current_depth] = 0 + structure.first_time[d] = True + structure.yet_to_visit = 0 # just to be on the safe side + + cdef size_t n_coatoms = structure.n_coatoms + cdef size_t job_id_c cdef size_t i - for current_depth in range(1, parallelization_depth + 1): - job_id_i = job_id_get(job_id, current_depth - 1, parallelization_depth, n_coatoms) - current_job_id_i = job_id_get(structure.current_job_id, current_depth - 1, parallelization_depth, n_coatoms) - if job_id_i > current_job_id_i: - job_id_set(&structure.current_job_id, job_id_i, current_depth - 1, parallelization_depth, n_coatoms) - structure.current_dimension = structure.dimension - current_depth - for i in range(current_job_id_i, job_id_i): - if not skip_next_face(structure): - job_id_set(&structure.current_job_id, i, current_depth - 1, parallelization_depth, n_coatoms) - # This job id does not exist. - return 0 - - if job_id_i < current_job_id_i: - job_id_set(&structure.current_job_id, job_id_i, current_depth - 1, parallelization_depth, n_coatoms) - structure.current_dimension = structure.dimension - current_depth - for i in range(job_id_i, current_job_id_i): - repeat_last_face(structure) + cdef size_t diff + cdef size_t new_faces_counter - # Appparently the face exists. We add it to the f-vector, if it is the very first job for the face. - test_id = job_id - job_id_set(&test_id, job_id_i, current_depth - 1, parallelization_depth, n_coatoms) - if test_id == job_id: - f_vector[structure.dimension - current_depth + 1] += 1 + for current_depth in range(1, parallelization_depth + 1): + d = structure.dimension - current_depth + job_id_c = job_id_get(job_id, current_depth - 1, parallelization_depth, n_coatoms) + if job_id_c != parallel_struct.current_job_id[current_depth -1]: + structure.current_dimension = d + structure.new_faces[d].n_faces = parallel_struct.original_n_faces[current_depth -1] + structure.visited_all[d].n_faces = parallel_struct.original_n_visited_all[current_depth -1] + parallel_struct.current_job_id[current_depth -1] = 0 + parallel_struct.current_job_id[current_depth] = 0 + structure.first_time[d] = True + structure.yet_to_visit = 0 # just to be on the safe side + elif parallel_struct.current_job_id[current_depth] == -1: + # The job does not exist, we already settled this. + return 0 - if structure.current_dimension == structure.dimension - current_depth: - structure.yet_to_visit = 0 - next_face_loop(structure) - if structure.current_dimension != structure.dimension - current_depth - 1: + if job_id_c > parallel_struct.current_job_id[current_depth -1]: + if job_id_c >= structure.new_faces[d].n_faces: + # The job does not exist. return 0 - if structure.current_dimension != structure.dimension - parallelization_depth - 1: - return 0 + for i in range(job_id_c): + # Fast forwarding the jobs. + add_face_shallow(structure.visited_all[d], structure.new_faces[d].faces[structure.new_faces[d].n_faces -1]) + structure.new_faces[d].n_faces -= 1 - return 1 + parallel_struct.current_job_id[current_depth -1] = job_id_c -cdef inline int skip_next_face(iter_t structure) nogil except -1: - """ - Moves the iterator to the state it would be after having visited all - subfaces of ``structure.new_faces[structure.current_dimension]``. + # Appparently the face exists. We add it to the f-vector, if it is the very first job for the face. + if job_id == 0 or job_id_get(job_id -1, current_depth -1, parallelization_depth, n_coatoms) != job_id_c: + parallel_struct.f_vector[structure.dimension - current_depth + 1] += 1 - Return ``0`` if no farther faces of this dimension exist. - Return ``1`` otherwise. + if structure.current_dimension == d: + structure.yet_to_visit = 0 - .. WARNING:: + if structure.new_faces[d].n_faces <= 1: + # The job will not exist. + parallel_struct.current_job_id[current_depth] = -1 + return 0 - This is rather unsafe. It should be called only in two scenarios: - - ``structure.current_dimension`` was manually raised - - ``next_dimesion`` returned ``dimension - parallelization_depth`` - for ``parallelization_depth > 0`` - """ - cdef face_list_t* faces = &structure.new_faces[structure.current_dimension] - cdef face_list_t* visited_all = &structure.visited_all[structure.current_dimension] + new_faces_counter = get_next_level( + structure.new_faces[d], structure.new_faces[d-1], structure.visited_all[d]) + + if new_faces_counter: + # Setting the variables correctly, for the next call. + structure.current_dimension -= 1 + structure.first_time[d-1] = True + structure.visited_all[d-1][0] = structure.visited_all[d][0] + structure.yet_to_visit = new_faces_counter + for i in range(current_depth, parallelization_depth + 1): + parallel_struct.current_job_id[i] = 0 + parallel_struct.original_n_faces[current_depth] = new_faces_counter + parallel_struct.original_n_visited_all[current_depth] = structure.visited_all[d-1].n_faces + else: + # The job does not exist. + parallel_struct.current_job_id[current_depth] = -1 + return 0 - if faces[0].n_faces == 0: + if structure.current_dimension != structure.dimension - parallelization_depth - 1: return 0 - if not structure.first_time[structure.current_dimension]: - # In this case there exists ``faces[0].faces[n_faces]``, of which we - # have visited all faces, but which was not added to - # ``visited_all`` yet. - add_face_shallow(visited_all[0], faces[0].faces[faces[0].n_faces]) - else: - # Once we have visited all faces of ``faces[n_faces]``, we want - # to add it to ``visited_all``. - structure.first_time[structure.current_dimension] = False - faces[0].n_faces -= 1 - if faces[0].n_faces == 0: - return 0 return 1 -cdef inline int repeat_last_face(iter_t structure) nogil except -1: - """ - The inverse function to ``skip_next_face``. - """ - cdef face_list_t* faces = &structure.new_faces[structure.current_dimension] - cdef face_list_t* visited_all = &structure.visited_all[structure.current_dimension] - if unlikely(faces[0].n_faces == faces[0].total_n_faces): - with gil: - raise MemoryError("wrong usage") - - if structure.first_time[structure.current_dimension]: - # faces[n_faces] is already in visited_all. - # As we want to revisit it, we should remove it from ``visited_all``. - visited_all[0].n_faces -= 1 - - faces[0].n_faces += 1 - # faces[n_faces] is already in visited_all and should not be readded. - structure.first_time[structure.current_dimension] = True - # Basically, ``job_id`` represents an element in `[0,n_coatoms-1]^(parallelization_depth)`. # We deal with this by obtaining/setting digits with base ``n_coatoms``. @@ -1432,17 +1442,3 @@ cdef inline size_t job_id_get(size_t job_id, size_t pos, size_t parallelization_ # Remove all digits before our current digit, which is digit ``pos``. return current_output % n_coatoms - -cdef inline void job_id_set(size_t* job_id, size_t val, size_t pos, size_t parallelization_depth, size_t n_coatoms) nogil: - """ - Set the digit ``pos`` of ``job_id`` with base ``n_coatoms`` - padding the number of digits to ``parallelization_depth``. - - Set all further digits to ``0``. - - Digits are enumerated started with ``0``. - """ - cdef size_t trailing = job_id[0] % n_coatoms**(parallelization_depth - pos) - job_id[0] -= trailing - job_id[0] += val * n_coatoms**(parallelization_depth - pos - 1) - 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 8a88eaaf62e..5beeb9da6d6 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 @@ -11,6 +11,9 @@ Inline cython methods for lists of faces. # https://www.gnu.org/licenses/ # **************************************************************************** +cdef extern from "Python.h": + int unlikely(int) nogil # Defined by Cython + from .face_data_structure cimport * from libc.string cimport memset from cysignals.signals cimport sig_on, sig_off @@ -89,9 +92,9 @@ cdef inline int add_face_shallow(face_list_t faces, face_t face) nogil except -1 """ Add a face to faces. """ - if not faces.total_n_faces >= faces.n_faces + 1: - with gil: - raise AssertionError + if unlikely(not faces.total_n_faces >= faces.n_faces + 1): + # Actually raising an error here results in a bad branch prediction. + return -1 faces.faces[faces.n_faces][0] = face[0] faces.n_faces += 1 @@ -184,12 +187,12 @@ cdef inline int face_list_intersection_fused(face_list_t dest, face_list_t A, fa """ Set ``dest`` to be the intersection of each face of ``A`` with ``b``. """ - if not dest.total_n_faces >= A.n_faces: - with gil: - raise AssertionError - if not dest.n_atoms >= A.n_atoms: - with gil: - raise AssertionError + if unlikely(not dest.total_n_faces >= A.n_faces): + # Actually raising an error here results in a bad branch prediction. + return -1 + if unlikely(not dest.n_atoms >= A.n_atoms): + # Actually raising an error here results in a bad branch prediction. + return -1 dest.n_faces = A.n_faces dest.polyhedron_is_simple = A.polyhedron_is_simple From 28c8cac07952466c3aaae4b4b6b614f21ce053fc Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 14 Jan 2021 17:35:04 +0100 Subject: [PATCH 010/280] syntax error in documentation --- src/sage/parallel/map_reduce.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/parallel/map_reduce.py b/src/sage/parallel/map_reduce.py index d51d5d872ee..10a770b6df0 100644 --- a/src/sage/parallel/map_reduce.py +++ b/src/sage/parallel/map_reduce.py @@ -50,7 +50,7 @@ How can I use all that stuff? ----------------------------- -First, you need to set the environment variable `SAGE_NUM_THREADS` to the +First, you need to set the environment variable ``SAGE_NUM_THREADS`` to the desired number of parallel threads to be used: sage: import os # not tested From 197eeeea4ef62610a34561403abcf832d608ab52 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 14 Jan 2021 17:46:22 +0100 Subject: [PATCH 011/280] one more typo in documentation --- src/sage/parallel/map_reduce.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/parallel/map_reduce.py b/src/sage/parallel/map_reduce.py index 10a770b6df0..18ec7f41fe0 100644 --- a/src/sage/parallel/map_reduce.py +++ b/src/sage/parallel/map_reduce.py @@ -51,7 +51,7 @@ ----------------------------- First, you need to set the environment variable ``SAGE_NUM_THREADS`` to the -desired number of parallel threads to be used: +desired number of parallel threads to be used:: sage: import os # not tested sage: os.environ["SAGE_NUM_THREADS"] = '8' # not tested From 130b83f935b5c7f56dcfa99aaf055020233d5e1a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 15 Jan 2021 09:56:40 +0100 Subject: [PATCH 012/280] documentation and improvements to parallel f-vector --- src/sage/geometry/polyhedron/base.py | 28 +-- .../combinatorial_polyhedron/base.pyx | 39 ++++- .../face_iterator.pxd | 3 +- .../face_iterator.pyx | 164 ++++++++++++++---- .../face_list_data_structure.pxd | 5 +- 5 files changed, 184 insertions(+), 55 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 282f7e68aac..8b7fbb75b03 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): @@ -6892,10 +6892,18 @@ def facets(self): return self.faces(self.dimension()-1) @cached_method(do_pickle=True) - def f_vector(self): + def f_vector(self, num_threads=None, parallelization_depth=None): r""" Return the f-vector. + INPUT: + + - ``num_threads`` -- integer (optional); specify the number of threads; + otherwise determined by :func:`~sage.parallel.ncpus.ncpus` + + - ``parallelization_depth`` -- integer (optional); specify + how deep in the lattice the parallelization is done + OUTPUT: Return a vector whose `i`-th entry is the number of @@ -6951,7 +6959,7 @@ def f_vector(self): sage: Q.f_vector.is_in_cache() True """ - return self.combinatorial_polyhedron().f_vector() + return self.combinatorial_polyhedron().f_vector(num_threads, parallelization_depth) def flag_f_vector(self, *args): r""" @@ -10179,10 +10187,10 @@ def affine_hull_projection(self, as_affine_map=False, orthogonal=False, """ # handle trivial full-dimensional case if self.ambient_dim() == self.dim(): - if as_affine_map: - return linear_transformation(matrix(self.base_ring(), - self.dim(), - self.dim(), + if as_affine_map: + return linear_transformation(matrix(self.base_ring(), + self.dim(), + self.dim(), self.base_ring().one())), self.ambient_space().zero() return self diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index c8e3e959c76..f4379ccc0ff 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -1555,13 +1555,20 @@ cdef class CombinatorialPolyhedron(SageObject): return DiGraph([vertices, edges], format='vertices_and_edges', immutable=True) @cached_method - def f_vector(self, num_threads=1, parallelization_depth=0): + def f_vector(self, num_threads=None, parallelization_depth=None): r""" Compute the ``f_vector`` of the polyhedron. The ``f_vector`` contains the number of faces of dimension `k` for each `k` in ``range(-1, self.dimension() + 1)``. + INPUT: + + - ``num_threads`` -- integer (optional); specify the number of threads + + - ``parallelization_depth`` -- integer (optional); specify + how deep in the lattice the parallelization is done + .. NOTE:: To obtain edges and/or ridges as well, first do so. This might @@ -1579,11 +1586,33 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C.f_vector() (1, 10, 45, 120, 185, 150, 50, 1) + Using two threads:: + + sage: P = polytopes.permutahedron(5) + sage: C = CombinatorialPolyhedron(P) + sage: C.f_vector(num_threads=2) + (1, 120, 240, 150, 30, 1) + TESTS:: sage: type(C.f_vector()) """ + if num_threads is None: + from sage.parallel.ncpus import ncpus + num_threads = ncpus() + + if parallelization_depth is None: + # Setting some reasonable defaults. + if num_threads == 0: + parallelization_depth = 0 + elif num_threads <= 3: + parallelization_depth = 1 + elif num_threads <= 8: + parallelization_depth = 2 + else: + parallelization_depth = 3 + if not self._f_vector: self._compute_f_vector(num_threads, parallelization_depth) if not self._f_vector: @@ -2918,20 +2947,20 @@ cdef class CombinatorialPolyhedron(SageObject): # In this case the dual approach is faster. dual = True - face_iters = [self._face_iter(dual, -2) for _ in range(num_threads)] cdef FaceIterator face_iter cdef iter_t* structs = mem.allocarray(num_threads, sizeof(iter_t)) cdef size_t i + + # For each thread an independent structure. + face_iters = [self._face_iter(dual, -2) for _ in range(num_threads)] for i in range(num_threads): face_iter = face_iters[i] structs[i][0] = face_iter.structure[0] # Initialize ``f_vector``. cdef size_t *f_vector = mem.calloc((dim + 2), sizeof(size_t)) - f_vector[0] = 1 # Face iterator will only visit proper faces. - f_vector[dim + 1] = 1 # Face iterator will only visit proper faces. - parallel_f_vector(structs, parallelization_depth, num_threads, f_vector) + parallel_f_vector(structs, num_threads, parallelization_depth, f_vector) # Copy ``f_vector``. if dual: diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 0515e2551bb..c92c0ace7b1 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -81,9 +81,10 @@ cdef class FaceIterator_geom(FaceIterator_base): cdef object _requested_dim # Dimension requested on init. cdef readonly object P # The original polyhedron. +cdef int parallel_f_vector(iter_t* structures, size_t num_threads, size_t parallelization_depth, size_t *f_vector) except -1 + # Nogil definitions of crucial functions. cdef int next_dimension(iter_t structure, size_t parallelization_depth=?) nogil except -1 cdef int next_face_loop(iter_t structure) nogil except -1 cdef size_t n_atom_rep(iter_t structure) nogil except -1 -cdef int parallel_f_vector(iter_t *structures, size_t parallelization_depth, size_t n_threads, size_t *f_vector) except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 0a0e5ce2bbb..b8183f2a232 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1297,68 +1297,145 @@ cdef inline size_t n_atom_rep(iter_t structure) nogil except -1: # The face was not initialized properly. raise LookupError("``FaceIterator`` does not point to a face") +# Parallel iteration over the faces. +# Currently only the f-vector is implemented, but slight +# modifications would allow collecting other information as well. + cdef struct parallel_f_s: + # A structure carrying things that each thread should have exclusive access to. size_t* f_vector size_t* current_job_id + + # Keep track so that we can easily go from one job to the next. size_t* original_n_faces size_t* original_n_visited_all ctypedef parallel_f_s parallel_f_t[1] -cdef int parallel_f_vector(iter_t *structures, size_t parallelization_depth, size_t n_threads, size_t *f_vector) except -1: +cdef int parallel_f_vector(iter_t* structures, size_t num_threads, size_t parallelization_depth, size_t *f_vector) except -1: + """ + Compute the ``f_vector`` in parallel. + + INPUT: + + - ``structures`` -- one structure per thread + + - ``num_threads`` -- the number of threads to use + + - ``parallelization_depth`` -- the codimension at which the threads are released + + - ``f_vector`` -- where the ``f_vector`` is output + """ cdef size_t n_jobs = structures[0].n_coatoms ** parallelization_depth cdef size_t i cdef int j cdef int dim = structures[0].dimension + f_vector[0] = 1 # Face iterator will only visit proper faces. + f_vector[dim + 1] = 1 # Face iterator will only visit proper faces. + if dim <= 0 or structures[0].n_coatoms == 0: + # Iterator assumes at least one face and at least dimension 1. + return 0 + + if num_threads == 0: + num_threads = 1 + cdef size_t thread_id cdef MemoryAllocator mem = MemoryAllocator() - cdef parallel_f_t* parallel_structs = mem.allocarray(n_threads, sizeof(parallel_f_t)) - cdef parallel_f_s* parallel_structs_s = mem.allocarray(n_threads, sizeof(parallel_f_s)) - for i in range(n_threads): + + # Setting up for each thread some storage space. + cdef parallel_f_t* parallel_structs = \ + mem.allocarray(num_threads, sizeof(parallel_f_t)) + cdef parallel_f_s* parallel_structs_s = \ + mem.allocarray(num_threads, sizeof(parallel_f_s)) + + for i in range(num_threads): parallel_structs[i][0] = parallel_structs_s[i] - parallel_structs[i].f_vector = mem.calloc(dim+2, sizeof(size_t)) - parallel_structs[i].current_job_id = mem.calloc(parallelization_depth+1, sizeof(size_t)) - parallel_structs[i].original_n_faces = mem.calloc(parallelization_depth+1, sizeof(size_t)) - parallel_structs[i].original_n_faces[0] = structures[0].new_faces[dim - 1].n_faces - parallel_structs[i].original_n_visited_all = mem.calloc(parallelization_depth+1, sizeof(size_t)) - parallel_structs[i].original_n_visited_all[0] = structures[0].visited_all[dim - 1].n_faces - - for i in prange(n_jobs, schedule='dynamic', chunksize=1, num_threads=n_threads, nogil=True): + + # Partial f-vectors. + parallel_structs[i].f_vector = \ + mem.calloc(dim+2, sizeof(size_t)) + parallel_structs[i].current_job_id = \ + mem.calloc(parallelization_depth+1, sizeof(size_t)) + + # Keeping back of the original number of faces allows faster starting the next job. + parallel_structs[i].original_n_faces = \ + mem.calloc(parallelization_depth+1, sizeof(size_t)) + parallel_structs[i].original_n_faces[0] = \ + structures[0].new_faces[dim - 1].n_faces + + parallel_structs[i].original_n_visited_all = \ + mem.calloc(parallelization_depth+1, sizeof(size_t)) + parallel_structs[i].original_n_visited_all[0] = \ + structures[0].visited_all[dim - 1].n_faces + + for i in prange(n_jobs, schedule='dynamic', chunksize=1, + num_threads=num_threads, nogil=True): _parallel_f_vector(structures[openmp.omp_get_thread_num()], parallelization_depth, parallel_structs[openmp.omp_get_thread_num()], i) # Gather the results. - for i in range(n_threads): + for i in range(num_threads): for j in range(structures[0].dimension + 2): f_vector[j] += parallel_structs[i].f_vector[j] -cdef int _parallel_f_vector(iter_t structure, size_t parallelization_depth, parallel_f_t parallel_struct, size_t job_id) nogil except -1: +cdef int _parallel_f_vector(iter_t structure, size_t parallelization_depth, + parallel_f_t parallel_struct, size_t job_id) nogil except -1: + """ + Set up a job and then visit all faces. + """ cdef int max_dimension = structure.dimension - parallelization_depth cdef int d - if prepare_face_iterator_for_partial_job(structure, parallelization_depth, parallel_struct, job_id): + if prepare_face_iterator_for_partial_job(structure, parallelization_depth, + parallel_struct, job_id): d = next_dimension(structure, parallelization_depth) while (d < max_dimension): - #sig_check() parallel_struct.f_vector[d+1] += 1 d = next_dimension(structure, parallelization_depth) -cdef inline int prepare_face_iterator_for_partial_job(iter_t structure, size_t parallelization_depth, parallel_f_t parallel_struct, size_t job_id) nogil except -1: +cdef inline int prepare_face_iterator_for_partial_job( + iter_t structure, size_t parallelization_depth, + parallel_f_t parallel_struct, size_t job_id) nogil except -1: + """ + Set ``structure`` according to ``job_id``. + + ``job_id`` should be thought of as its digits with base ``structure.n_coatoms`` + padded to ``parallelization_depth``. + + The first digit determines which facet to visit. + The next digit determines which facet of the facet should be visited. + + OUTPUT: ``1`` if the job exists and ``0`` otherwise. + + In addition, the first job treating a face will "visit" this face + and increase the corresponding entry of the f-vector. + """ cdef int d cdef size_t current_depth if (not structure.first_time[structure.current_dimension] and structure.current_dimension == structure.dimension - parallelization_depth): # Act as if we had not visited faces in the last depth. + # Set ``current_job_id[parallelization_depth - 1] = 0``. d = structure.dimension - parallelization_depth current_depth = parallelization_depth - structure.new_faces[d].n_faces = parallel_struct.original_n_faces[current_depth -1] - structure.new_faces[d].n_faces = parallel_struct.original_n_visited_all[current_depth -1] - parallel_struct.current_job_id[current_depth -1] = 0 - parallel_struct.current_job_id[current_depth] = 0 + + # Recover all faces. + structure.new_faces[d].n_faces = \ + parallel_struct.original_n_faces[current_depth -1] + structure.visited_all[d].n_faces = \ + parallel_struct.original_n_visited_all[current_depth -1] structure.first_time[d] = True structure.yet_to_visit = 0 # just to be on the safe side + parallel_struct.current_job_id[current_depth -1] = 0 + + # If the job does not exist, we will set the next value to ``-1``. + if parallel_struct.original_n_faces[current_depth -1] == 0: + parallel_struct.current_job_id[current_depth] = -1 + else: + parallel_struct.current_job_id[current_depth] = 0 + cdef size_t n_coatoms = structure.n_coatoms cdef size_t job_id_c cdef size_t i @@ -1367,17 +1444,30 @@ cdef inline int prepare_face_iterator_for_partial_job(iter_t structure, size_t p for current_depth in range(1, parallelization_depth + 1): d = structure.dimension - current_depth - job_id_c = job_id_get(job_id, current_depth - 1, parallelization_depth, n_coatoms) - if job_id_c != parallel_struct.current_job_id[current_depth -1]: + + # Get the corresponding digit of ``job_id``. + job_id_c = get_digit(job_id, current_depth - 1, parallelization_depth, n_coatoms) + + # Set ``current_job_id[current_depth - 1]`` to ``job_id_c`` if possible. + + if job_id_c != parallel_struct.current_job_id[current_depth - 1]: + # Set ``current_job_id[current_depth -1] = 0``. structure.current_dimension = d structure.new_faces[d].n_faces = parallel_struct.original_n_faces[current_depth -1] structure.visited_all[d].n_faces = parallel_struct.original_n_visited_all[current_depth -1] parallel_struct.current_job_id[current_depth -1] = 0 - parallel_struct.current_job_id[current_depth] = 0 + + # If the job does not exist, we will set the next value to ``-1``. + if parallel_struct.original_n_faces[current_depth -1] == 0: + parallel_struct.current_job_id[current_depth] = -1 + else: + parallel_struct.current_job_id[current_depth] = 0 + structure.first_time[d] = True structure.yet_to_visit = 0 # just to be on the safe side - elif parallel_struct.current_job_id[current_depth] == -1: - # The job does not exist, we already settled this. + + if parallel_struct.current_job_id[current_depth] == -1: + # The job does not exist. return 0 if job_id_c > parallel_struct.current_job_id[current_depth -1]: @@ -1392,14 +1482,15 @@ cdef inline int prepare_face_iterator_for_partial_job(iter_t structure, size_t p parallel_struct.current_job_id[current_depth -1] = job_id_c - # Appparently the face exists. We add it to the f-vector, if it is the very first job for the face. - if job_id == 0 or job_id_get(job_id -1, current_depth -1, parallelization_depth, n_coatoms) != job_id_c: - parallel_struct.f_vector[structure.dimension - current_depth + 1] += 1 + # Apparently the face exists. We add it to the f-vector, if it is the very first job for the face. + if job_id == 0 or get_digit(job_id -1, current_depth -1, parallelization_depth, n_coatoms) != job_id_c: + # Visit ``structure.new_faces[d].faces[structure.new_faces[d].n_faces - 1] + parallel_struct.f_vector[d + 1] += 1 if structure.current_dimension == d: structure.yet_to_visit = 0 - if structure.new_faces[d].n_faces <= 1: + if structure.new_faces[d].n_faces == 0: # The job will not exist. parallel_struct.current_job_id[current_depth] = -1 return 0 @@ -1427,18 +1518,15 @@ cdef inline int prepare_face_iterator_for_partial_job(iter_t structure, size_t p return 1 -# Basically, ``job_id`` represents an element in `[0,n_coatoms-1]^(parallelization_depth)`. -# We deal with this by obtaining/setting digits with base ``n_coatoms``. - -cdef inline size_t job_id_get(size_t job_id, size_t pos, size_t parallelization_depth, size_t n_coatoms) nogil: +cdef inline size_t get_digit(size_t job_id, size_t pos, size_t padto, size_t base) nogil: """ - Get the digit ``pos`` of ``job_id`` with base ``n_coatoms`` - padding the number of digits to ``parallelization_depth``. + Get the digit ``pos`` of ``job_id`` with base ``base`` + padding the number of digits to ``pad_to``. Digits are enumerated started with ``0``. """ # Remove the last ``parallelization_depth - pos - 1`` digits. - cdef size_t current_output = job_id // n_coatoms**(parallelization_depth - pos - 1) + cdef size_t current_output = job_id / base ** (padto - pos - 1) # Remove all digits before our current digit, which is digit ``pos``. - return current_output % n_coatoms + return current_output % base 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 5beeb9da6d6..19d5aa859ab 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 @@ -16,7 +16,7 @@ cdef extern from "Python.h": from .face_data_structure cimport * from libc.string cimport memset -from cysignals.signals cimport sig_on, sig_off +from cysignals.signals cimport sig_check cdef struct face_list_s: face_t* faces @@ -249,7 +249,9 @@ cdef inline size_t get_next_level_fused( face_list_intersection_fused(new_faces, faces, faces.faces[n_faces], algorithm) cdef size_t j + sig_check() for j in range(n_faces): + sig_check() if (is_not_maximal_fused(new_faces, j, algorithm) or # Step 2 is_contained_in_one_fused(new_faces.faces[j], visited_all, algorithm)): # Step 3 is_not_new_face[j] = True @@ -276,6 +278,7 @@ cdef inline size_t get_next_level( face_list_t visited_all) nogil except -1: cdef size_t output + sig_check() if faces.polyhedron_is_simple: output = get_next_level_fused(faces, new_faces, visited_all, 0) else: From d051f283c08d117bd51d2167fb3d0ccf641dd620 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 15 Jan 2021 11:21:00 +0100 Subject: [PATCH 013/280] added a comment --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index b8183f2a232..58f7af086af 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1326,6 +1326,7 @@ cdef int parallel_f_vector(iter_t* structures, size_t num_threads, size_t parall - ``f_vector`` -- where the ``f_vector`` is output """ + # One job per face of codimension ``parallelization_depth``. cdef size_t n_jobs = structures[0].n_coatoms ** parallelization_depth cdef size_t i cdef int j From 0dae7a061df9a2f05f9ef2d43602cc04fb351fb0 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sun, 17 Jan 2021 10:36:00 +0100 Subject: [PATCH 014/280] remove redundant sig_checks --- .../combinatorial_polyhedron/face_list_data_structure.pxd | 2 -- 1 file changed, 2 deletions(-) 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 19d5aa859ab..5f7288560aa 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 @@ -249,7 +249,6 @@ cdef inline size_t get_next_level_fused( face_list_intersection_fused(new_faces, faces, faces.faces[n_faces], algorithm) cdef size_t j - sig_check() for j in range(n_faces): sig_check() if (is_not_maximal_fused(new_faces, j, algorithm) or # Step 2 @@ -278,7 +277,6 @@ cdef inline size_t get_next_level( face_list_t visited_all) nogil except -1: cdef size_t output - sig_check() if faces.polyhedron_is_simple: output = get_next_level_fused(faces, new_faces, visited_all, 0) else: From 928ea608dc56698cd8df3d71b8266e75c28670eb Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sun, 17 Jan 2021 18:18:51 +0100 Subject: [PATCH 015/280] remove redundant allocation --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 156ab4682f5..9fdaca65c6d 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1347,12 +1347,8 @@ cdef int parallel_f_vector(iter_t* structures, size_t num_threads, size_t parall # Setting up for each thread some storage space. cdef parallel_f_t* parallel_structs = \ mem.allocarray(num_threads, sizeof(parallel_f_t)) - cdef parallel_f_s* parallel_structs_s = \ - mem.allocarray(num_threads, sizeof(parallel_f_s)) for i in range(num_threads): - parallel_structs[i][0] = parallel_structs_s[i] - # Partial f-vectors. parallel_structs[i].f_vector = \ mem.calloc(dim+2, sizeof(size_t)) From baf80deaca74b037c11e5eee914009e216f3beed Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sun, 31 Jan 2021 16:27:58 +0100 Subject: [PATCH 016/280] try disabling CYSIGNALS_C_ATOMIC --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 9fdaca65c6d..52123d92dbf 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1,5 +1,6 @@ # distutils: extra_compile_args = -fopenmp # distutils: extra_link_args = -fopenmp +# distutils: undef_macros = CYSIGNALS_C_ATOMIC r""" Face iterator for polyhedra From da65d28f81ddb7a865d0af830d14bb4132c3c1cb Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 5 Mar 2021 23:48:18 +0100 Subject: [PATCH 017/280] Revert "try disabling CYSIGNALS_C_ATOMIC" This reverts commit baf80deaca74b037c11e5eee914009e216f3beed. --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 52123d92dbf..9fdaca65c6d 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1,6 +1,5 @@ # distutils: extra_compile_args = -fopenmp # distutils: extra_link_args = -fopenmp -# distutils: undef_macros = CYSIGNALS_C_ATOMIC r""" Face iterator for polyhedra From 27baa071a64d5f93ca49d5ca4c18ae8ca7fa892a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 15 Mar 2021 12:58:13 +0100 Subject: [PATCH 018/280] use OPENMP_CFLAGS --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 7c5fb7efafc..fd6280b48d7 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1,5 +1,5 @@ -# distutils: extra_compile_args = -fopenmp -# distutils: extra_link_args = -fopenmp +# distutils: extra_compile_args = OPENMP_CFLAGS +# distutils: extra_link_args = OPENMP_CFLAGS r""" Face iterator for polyhedra From 1f0b74f20bb3e444fc1fdfa3fb92b33c5b5cb06e Mon Sep 17 00:00:00 2001 From: John Cremona Date: Wed, 10 Mar 2021 09:47:38 +0000 Subject: [PATCH 019/280] #31443: update eclib package metadata --- build/pkgs/eclib/checksums.ini | 8 ++++---- build/pkgs/eclib/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 4daaf05d8b5..4e8b74dd38b 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,4 +1,4 @@ -tarball=eclib-20190909.tar.bz2 -sha1=0e994c0de95ef03ef19ad5030a2cacbb83c76bbd -md5=1a67217a7fa762646d43c7bec8a73028 -cksum=4240278408 +tarball=eclib-20210308.tar.bz2 +sha1=1ffefad598683ff95c6ad90be42488a8255e8e98 +md5=b284ea7df077ca680c9cd93fe3bd353b +cksum=2985037614 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index 4defdea11fc..73aebd5f103 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -20190909 +20210308 From 5fce79f65d4a5071a5fccd210a99cd07bdc8b30d Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 11 Mar 2021 16:02:47 +0000 Subject: [PATCH 020/280] #31443: update eclib package metadata again --- build/pkgs/eclib/checksums.ini | 8 ++++---- build/pkgs/eclib/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 4e8b74dd38b..b37bff93c05 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,4 +1,4 @@ -tarball=eclib-20210308.tar.bz2 -sha1=1ffefad598683ff95c6ad90be42488a8255e8e98 -md5=b284ea7df077ca680c9cd93fe3bd353b -cksum=2985037614 +tarball=eclib-20210310.tar.bz2 +sha1=73437ac8deae94f00e7713405b3251d9c81f95e4 +md5=4ac988bc46869866f076f7bea0fdaa6b +cksum=2130748042 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index 73aebd5f103..3384f6c7325 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -20210308 +20210310 From a46b6f9d9b3b2fb8a9ba0587dd6b71991cd0fec9 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 11 Mar 2021 16:04:30 +0000 Subject: [PATCH 021/280] #31443: adapt eclib library interface for version v20210310 --- src/sage/libs/eclib/__init__.pxd | 8 +- src/sage/libs/eclib/interface.py | 397 ++++++++++++++----------------- src/sage/libs/eclib/mwrank.pyx | 227 +++++++++--------- src/sage/libs/eclib/newforms.pyx | 1 + src/sage/libs/eclib/t | 0 src/sage/libs/eclib/wrap.cpp | 10 +- 6 files changed, 313 insertions(+), 330 deletions(-) create mode 100644 src/sage/libs/eclib/t diff --git a/src/sage/libs/eclib/__init__.pxd b/src/sage/libs/eclib/__init__.pxd index 3f99f998a50..d44d4fba865 100644 --- a/src/sage/libs/eclib/__init__.pxd +++ b/src/sage/libs/eclib/__init__.pxd @@ -12,9 +12,11 @@ from libcpp.pair cimport pair from sage.libs.ntl.types cimport ZZ_c -# NOTE: eclib includes have specific dependencies and must be included -# in a specific order. So we start by listing all relevant include files -# in the correct order. +# NOTE: eclib used to have specific dependencies, so that they had to +# be included in a specific order. Although this is no longer the +# case, we start by listing all relevant include files in the correct +# order. + cdef extern from "eclib/vector.h": pass cdef extern from "eclib/xmod.h": pass cdef extern from "eclib/svector.h": pass diff --git a/src/sage/libs/eclib/interface.py b/src/sage/libs/eclib/interface.py index e8984567205..493b5f13470 100644 --- a/src/sage/libs/eclib/interface.py +++ b/src/sage/libs/eclib/interface.py @@ -21,17 +21,16 @@ sage: [k for k in sys.modules if k.startswith("sage.libs.eclib")] [] sage: EllipticCurve('11a1').mwrank_curve() - y^2+ y = x^3 - x^2 - 10*x - 20 + y^2 + y = x^3 - x^2 - 10 x - 20 sage: [k for k in sys.modules if k.startswith("sage.libs.eclib")] ['...'] """ - +import sys from sage.structure.sage_object import SageObject from sage.rings.all import Integer from sage.rings.integer_ring import IntegerRing -from .mwrank import _Curvedata, _two_descent, _mw - +from .mwrank import _Curvedata, _two_descent, _mw, parse_point_list class mwrank_EllipticCurve(SageObject): r""" @@ -67,7 +66,7 @@ class mwrank_EllipticCurve(SageObject): sage: e = mwrank_EllipticCurve([3, -4]) sage: e - y^2 = x^3 + 3*x - 4 + y^2 = x^3 + 3 x - 4 sage: e.ainvs() [0, 0, 0, 3, -4] @@ -127,6 +126,7 @@ def __init__(self, ainvs, verbose=False): # place holders self.__saturate = -2 # not yet saturated + self.__descent = None def __reduce__(self): r""" @@ -137,12 +137,9 @@ def __reduce__(self): sage: E = mwrank_EllipticCurve([0,0,1,-7,6]) sage: E.__reduce__() (, ([0, 0, 1, -7, 6], False)) - - """ return mwrank_EllipticCurve, (self.__ainvs, self.__verbose) - def set_verbose(self, verbose): """ Set the verbosity of printing of output by the :meth:`two_descent()` and @@ -247,53 +244,27 @@ def __repr__(self): sage: E = mwrank_EllipticCurve([0,-1,1,0,0]) sage: E.__repr__() - 'y^2+ y = x^3 - x^2 ' + 'y^2 + y = x^3 - x^2' """ - # TODO: Is the use (or omission) of spaces here intentional? - a = self.ainvs() - s = "y^2" - if a[0] == -1: - s += "- x*y " - elif a[0] == 1: - s += "+ x*y " - elif a[0] != 0: - s += "+ %s*x*y "%a[0] - if a[2] == -1: - s += " - y" - elif a[2] == 1: - s += "+ y" - elif a[2] != 0: - s += "+ %s*y"%a[2] - s += " = x^3 " - if a[1] == -1: - s += "- x^2 " - elif a[1] == 1: - s += "+ x^2 " - elif a[1] != 0: - s += "+ %s*x^2 "%a[1] - if a[3] == -1: - s += "- x " - elif a[3] == 1: - s += "+ x " - elif a[3] != 0: - s += "+ %s*x "%a[3] - if a[4] == -1: - s += "-1" - elif a[4] == 1: - s += "+1" - elif a[4] != 0: - s += "+ %s"%a[4] - s = s.replace("+ -","- ") - return s - + a1, a2, a3, a4, a6 = self.__ainvs + # we do not assume a1, a2, a3 are reduced to {0,1}, {-1,0,1}, {0,1} + coeff = lambda a: ''.join([" +" if a > 0 else " -", + " " + str(abs(a)) if abs(a) > 1 else ""]) + return ''.join(['y^2', + ' '.join([coeff(a1), 'xy']) if a1 else '', + ' '.join([coeff(a3), 'y']) if a3 else '', + ' = x^3', + ' '.join([coeff(a2), 'x^2']) if a2 else '', + ' '.join([coeff(a4), 'x']) if a4 else '', + ' '.join([" +" if a6 > 0 else " -", str(abs(a6))]) if a6 else '']) def two_descent(self, - verbose = True, - selmer_only = False, - first_limit = 20, - second_limit = 8, - n_aux = -1, - second_descent = True): + verbose=True, + selmer_only=False, + first_limit=20, + second_limit=8, + n_aux=-1, + second_descent=True): r""" Compute 2-descent data for this curve. @@ -374,16 +345,14 @@ def two_descent(self, second_limit = int(second_limit) n_aux = int(n_aux) second_descent = int(second_descent) # convert from bool to (int) 0 or 1 - # TODO: Don't allow limits above some value...??? - # (since otherwise mwrank just sets limit tiny) self.__descent = _two_descent() self.__descent.do_descent(self.__curve, - verbose, - selmer_only, - first_limit, - second_limit, - n_aux, - second_descent) + verbose, + selmer_only, + first_limit, + second_limit, + n_aux, + second_descent) if not self.__descent.ok(): raise RuntimeError("A 2-descent did not complete successfully.") self.__saturate = -2 # not yet saturated @@ -398,11 +367,9 @@ def __two_descent_data(self): sage: E._mwrank_EllipticCurve__two_descent_data() """ - try: - return self.__descent - except AttributeError: + if self.__descent is None: self.two_descent(self.__verbose) - return self.__descent + return self.__descent def conductor(self): """ @@ -565,22 +532,24 @@ def regulator(self): R = self.__two_descent_data().regulator() return float(R) - def saturate(self, bound=-1): + def saturate(self, bound=-1, lower=2): """ - Compute the saturation of the Mordell-Weil group at all - primes up to ``bound``. + Compute the saturation of the Mordell-Weil group. INPUT: - - ``bound`` (int, default -1) -- Use `-1` (the default) to - saturate at *all* primes, `0` for no saturation, or `n` (a - positive integer) to saturate at all primes up to `n`. + - ``bound`` (int, default -1) -- If `-1`, saturate at *all* + primes by computing a bound on the saturation index, + otherwise saturate at all primes up to the minimum of + ``bound`` and the saturation index bound. + + - ``lower`` (int, default 2) -- Only saturate at primes not + less than this. EXAMPLES: Since the 2-descent automatically saturates at primes up to - 20, it is not easy to come up with an example where saturation - has any effect:: + 20, further saturation often has no effect:: sage: E = mwrank_EllipticCurve([0, 0, 0, -1002231243161, 0]) sage: E.gens() @@ -599,7 +568,7 @@ def saturate(self, bound=-1): """ bound = int(bound) if self.__saturate < bound: - self.__two_descent_data().saturate(bound) + self.__two_descent_data().saturate(bound, lower) self.__saturate = bound def gens(self): @@ -613,8 +582,7 @@ def gens(self): [[0, -1, 1]] """ self.saturate() - L = eval(self.__two_descent_data().getbasis().replace(":",",")) - return [[Integer(x), Integer(y), Integer(z)] for (x,y,z) in L] + return parse_point_list(self.__two_descent_data().getbasis()) def certain(self): r""" @@ -760,65 +728,37 @@ class mwrank_MordellWeil(SageObject): sage: EQ.search(1) P1 = [0:1:0] is torsion point, order 1 P1 = [-3:0:1] is generator number 1 - saturating up to 20...Checking 2-saturation - Points have successfully been 2-saturated (max q used = 7) - Checking 3-saturation - Points have successfully been 3-saturated (max q used = 7) - Checking 5-saturation - Points have successfully been 5-saturated (max q used = 23) - Checking 7-saturation - Points have successfully been 7-saturated (max q used = 41) - Checking 11-saturation - Points have successfully been 11-saturated (max q used = 17) - Checking 13-saturation - Points have successfully been 13-saturated (max q used = 43) - Checking 17-saturation - Points have successfully been 17-saturated (max q used = 31) - Checking 19-saturation - Points have successfully been 19-saturated (max q used = 37) + saturating up to 20...Saturation index bound (for points of good reduction) = 3 + Reducing saturation bound from given value 20 to computed index bound 3 + Checking saturation at [ 2 3 ] + Checking 2-saturation + Points were proved 2-saturated (max q used = 7) + Checking 3-saturation + Points were proved 3-saturated (max q used = 7) done P2 = [-2:3:1] is generator number 2 - saturating up to 20...Checking 2-saturation + saturating up to 20...Saturation index bound (for points of good reduction) = 4 + Reducing saturation bound from given value 20 to computed index bound 4 + Checking saturation at [ 2 3 ] + Checking 2-saturation possible kernel vector = [1,1] This point may be in 2E(Q): [14:-52:1] - ...and it is! + ...and it is! Replacing old generator #1 with new generator [1:-1:1] + Reducing index bound from 4 to 2 Points have successfully been 2-saturated (max q used = 7) Index gain = 2^1 - Checking 3-saturation - Points have successfully been 3-saturated (max q used = 13) - Checking 5-saturation - Points have successfully been 5-saturated (max q used = 67) - Checking 7-saturation - Points have successfully been 7-saturated (max q used = 53) - Checking 11-saturation - Points have successfully been 11-saturated (max q used = 73) - Checking 13-saturation - Points have successfully been 13-saturated (max q used = 103) - Checking 17-saturation - Points have successfully been 17-saturated (max q used = 113) - Checking 19-saturation - Points have successfully been 19-saturated (max q used = 47) - done (index = 2). + done, index = 2. Gained index 2, new generators = [ [1:-1:1] [-2:3:1] ] P3 = [-14:25:8] is generator number 3 - saturating up to 20...Checking 2-saturation - Points have successfully been 2-saturated (max q used = 11) - Checking 3-saturation - Points have successfully been 3-saturated (max q used = 13) - Checking 5-saturation - Points have successfully been 5-saturated (max q used = 71) - Checking 7-saturation - Points have successfully been 7-saturated (max q used = 101) - Checking 11-saturation - Points have successfully been 11-saturated (max q used = 127) - Checking 13-saturation - Points have successfully been 13-saturated (max q used = 151) - Checking 17-saturation - Points have successfully been 17-saturated (max q used = 139) - Checking 19-saturation - Points have successfully been 19-saturated (max q used = 179) - done (index = 1). + saturating up to 20...Saturation index bound (for points of good reduction) = 3 + Reducing saturation bound from given value 20 to computed index bound 3 + Checking saturation at [ 2 3 ] + Checking 2-saturation + Points were proved 2-saturated (max q used = 11) + Checking 3-saturation + Points were proved 3-saturated (max q used = 13) + done, index = 1. P4 = [-1:3:1] = -1*P1 + -1*P2 + -1*P3 (mod torsion) P4 = [0:2:1] = 2*P1 + 0*P2 + 1*P3 (mod torsion) P4 = [2:13:8] = -3*P1 + 1*P2 + -1*P3 (mod torsion) @@ -878,7 +818,7 @@ def __reduce__(self): sage: E = mwrank_EllipticCurve([0,0,1,-7,6]) sage: EQ = mwrank_MordellWeil(E) sage: EQ.__reduce__() - (, (y^2+ y = x^3 - 7*x + 6, True, 1, 999)) + (, (y^2 + y = x^3 - 7 x + 6, True, 1, 999)) """ return mwrank_MordellWeil, (self.__curve, self.__verbose, self.__pp, self.__maxr) @@ -902,12 +842,10 @@ def __repr__(self): """ return "Subgroup of Mordell-Weil group: %s"%self.__mw - def process(self, v, sat=0): - """ - This function allows one to add points to a :class:`mwrank_MordellWeil` object. + def process(self, v, saturation_bound=0): + """Process points in the list ``v``. - Process points in the list ``v``, with saturation at primes up to - ``sat``. If ``sat`` is zero (the default), do no saturation. + This function allows one to add points to a :class:`mwrank_MordellWeil` object. INPUT: @@ -915,8 +853,9 @@ def process(self, v, sat=0): list of triples of integers, which define points on the curve. - - ``sat`` (int, default 0) -- saturate at primes up to ``sat``, or at - *all* primes if ``sat`` is zero. + - ``saturation_bound`` (int, default 0) -- saturate at primes up to + ``saturation_bound``, or at *all* primes if ``saturation_bound`` is -1; when ``saturation_bound`` + is 0 (the default), do no saturation.. OUTPUT: @@ -939,11 +878,11 @@ def process(self, v, sat=0): sage: EQ.points() [[1, -1, 1], [-2, 3, 1], [-14, 25, 8]] - Example to illustrate the saturation parameter ``sat``:: + Example to illustrate the saturation parameter ``saturation_bound``:: sage: E = mwrank_EllipticCurve([0,0,1,-7,6]) sage: EQ = mwrank_MordellWeil(E) - sage: EQ.process([[1547, -2967, 343], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]], sat=20) + sage: EQ.process([[1547, -2967, 343], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]], saturation_bound=20) P1 = [1547:-2967:343] is generator number 1 ... Gained index 5, new generators = [ [-2:3:1] [-14:25:8] [1:-1:1] ] @@ -956,7 +895,7 @@ def process(self, v, sat=0): sage: E = mwrank_EllipticCurve([0,0,1,-7,6]) sage: EQ = mwrank_MordellWeil(E) - sage: EQ.process([[1547, -2967, 343], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]], sat=0) + sage: EQ.process([[1547, -2967, 343], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]], saturation_bound=0) P1 = [1547:-2967:343] is generator number 1 P2 = [2707496766203306:864581029138191:2969715140223272] is generator number 2 P3 = [-13422227300:-49322830557:12167000000] is generator number 3 @@ -965,55 +904,92 @@ def process(self, v, sat=0): sage: EQ.regulator() 375.42920288254555 sage: EQ.saturate(2) # points were not 2-saturated - saturating basis...Saturation index bound = 93 - WARNING: saturation at primes p > 2 will not be done; - ... + saturating basis...Saturation index bound (for points of good reduction) = 93 + Only p-saturating for p up to given value 2. + The resulting points may not be p-saturated for p between this and the computed index bound 93 + Checking saturation at [ 2 ] + Checking 2-saturation + possible kernel vector = [1,0,0] + This point may be in 2E(Q): [1547:-2967:343] + ...and it is! + Replacing old generator #1 with new generator [-2:3:1] + Reducing index bound from 93 to 46 + Points have successfully been 2-saturated (max q used = 11) + Index gain = 2^1 + done Gained index 2 - New regulator = 93.857... - (False, 2, '[ ]') + New regulator = 93.85730072 + (True, 2, '[ ]') sage: EQ.points() [[-2, 3, 1], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]] sage: EQ.regulator() 93.85730072063639 sage: EQ.saturate(3) # points were not 3-saturated - saturating basis...Saturation index bound = 46 - WARNING: saturation at primes p > 3 will not be done; - ... + saturating basis...Saturation index bound (for points of good reduction) = 46 + Only p-saturating for p up to given value 3. + The resulting points may not be p-saturated for p between this and the computed index bound 46 + Checking saturation at [ 2 3 ] + Checking 2-saturation + Points were proved 2-saturated (max q used = 11) + Checking 3-saturation + possible kernel vector = [0,1,0] + This point may be in 3E(Q): [2707496766203306:864581029138191:2969715140223272] + ...and it is! + Replacing old generator #2 with new generator [-14:25:8] + Reducing index bound from 46 to 15 + Points have successfully been 3-saturated (max q used = 13) + Index gain = 3^1 + done Gained index 3 - New regulator = 10.428... - (False, 3, '[ ]') + New regulator = 10.42858897 + (True, 3, '[ ]') sage: EQ.points() [[-2, 3, 1], [-14, 25, 8], [-13422227300, -49322830557, 12167000000]] sage: EQ.regulator() 10.4285889689596 sage: EQ.saturate(5) # points were not 5-saturated - saturating basis...Saturation index bound = 15 - WARNING: saturation at primes p > 5 will not be done; - ... + saturating basis...Saturation index bound (for points of good reduction) = 15 + Only p-saturating for p up to given value 5. + The resulting points may not be p-saturated for p between this and the computed index bound 15 + Checking saturation at [ 2 3 5 ] + Checking 2-saturation + Points were proved 2-saturated (max q used = 11) + Checking 3-saturation + Points were proved 3-saturated (max q used = 13) + Checking 5-saturation + possible kernel vector = [0,0,1] + This point may be in 5E(Q): [-13422227300:-49322830557:12167000000] + ...and it is! + Replacing old generator #3 with new generator [1:-1:1] + Reducing index bound from 15 to 3 + Points have successfully been 5-saturated (max q used = 71) + Index gain = 5^1 + done Gained index 5 - New regulator = 0.417... - (False, 5, '[ ]') + New regulator = 0.4171435588 + (True, 5, '[ ]') sage: EQ.points() [[-2, 3, 1], [-14, 25, 8], [1, -1, 1]] sage: EQ.regulator() 0.417143558758384 sage: EQ.saturate() # points are now saturated - saturating basis...Saturation index bound = 3 + saturating basis...Saturation index bound (for points of good reduction) = 3 + Tamagawa index primes are [ ] Checking saturation at [ 2 3 ] - Checking 2-saturation + Checking 2-saturation Points were proved 2-saturated (max q used = 11) - Checking 3-saturation + Checking 3-saturation Points were proved 3-saturated (max q used = 13) done (True, 1, '[ ]') """ if not isinstance(v, list): raise TypeError("v (=%s) must be a list"%v) - sat = int(sat) + saturation_bound = int(saturation_bound) for P in v: - if not isinstance(P, (list,tuple)) or len(P) != 3: + if not isinstance(P, (list, tuple)) or len(P) != 3: raise TypeError("v (=%s) must be a list of 3-tuples (or 3-element lists) of ints"%v) - self.__mw.process(P, sat) + self.__mw.process(P, saturation_bound) def regulator(self): """ @@ -1091,23 +1067,21 @@ def rank(self): """ return self.__mw.rank() - def saturate(self, max_prime=-1, odd_primes_only=False): - r""" - Saturate this subgroup of the Mordell-Weil group. + def saturate(self, max_prime=-1, min_prime=2): + r"""Saturate this subgroup of the Mordell-Weil group. INPUT: - - ``max_prime`` (int, default -1) -- saturation is performed for - all primes up to ``max_prime``. If `-1` (the default), an + - ``max_prime`` (int, default -1) -- If `-1` (the default), an upper bound is computed for the primes at which the subgroup - may not be saturated, and this is used; however, if the - computed bound is greater than a value set by the ``eclib`` - library (currently 97) then no saturation will be attempted - at primes above this. + may not be saturated, and saturation is performed for all + primes up to this bound. Otherwise, the bound used is the + minimum of ``max_prime`` and the computed bound. - - ``odd_primes_only`` (bool, default ``False``) -- only do - saturation at odd primes. (If the points have been found - via :meth:`two_descent` they should already be 2-saturated.) + - ``min_prime`` (int, default 2) -- only do saturation at + primes no less than this. (For example, if the points have + been found via :meth:`two_descent` they should already be + 2-saturated so a value of 3 is appropriate.) OUTPUT: @@ -1115,40 +1089,35 @@ def saturate(self, max_prime=-1, odd_primes_only=False): - ``ok`` (bool) -- ``True`` if and only if the saturation was provably successful at all primes attempted. If the default - was used for ``max_prime`` and no warning was output about - the computed saturation bound being too high, then ``True`` - indicates that the subgroup is saturated at *all* - primes. + was used for ``max_prime``, then ``True`` indicates that the + subgroup is saturated at *all* primes. - ``index`` (int) -- the index of the group generated by the original points in their saturation. - ``unsatlist`` (list of ints) -- list of primes at which - saturation could not be proved or achieved. Increasing the - precision should correct this, since it happens when - a linear combination of the points appears to be a multiple - of `p` but cannot be divided by `p`. (Note that ``eclib`` - uses floating point methods based on elliptic logarithms to - divide points.) + saturation could not be proved or achieved. .. note:: - We emphasize that if this function returns ``True`` as the - first return argument (``ok``), and if the default was used for the - parameter ``max_prime``, then the points in the basis after - calling this function are saturated at *all* primes, - i.e., saturating at the primes up to ``max_prime`` are - sufficient to saturate at all primes. Note that the - function might not have needed to saturate at all primes up - to ``max_prime``. It has worked out what prime you need to - saturate up to, and that prime might be smaller than ``max_prime``. + In versions up to v20190909, ``eclib`` used floating point + methods based on elliptic logarithms to divide points, and + did not compute the precision necessary, which could casue + failures. Since v20210310, ``eclib`` uses exact method based + on division polynomials, which should mean that such + failures does not happen. .. note:: - Currently (May 2010), this does not remember the result of - calling :meth:`search()`. So calling :meth:`search()` up - to height 20 then calling :meth:`saturate()` results in - another search up to height 18. + We emphasize that if this function returns ``True`` as the + first return argument (``ok``), and if the default was used + for the parameter ``max_prime``, then the points in the + basis after calling this function are saturated at *all* + primes, i.e., saturating at the primes up to ``max_prime`` + are sufficient to saturate at all primes. Note that the + function computes an upper bound for the index of + saturation, and does no work for primes greater than this + even if ``max_prime`` is larger. EXAMPLES:: @@ -1160,7 +1129,7 @@ def saturate(self, max_prime=-1, odd_primes_only=False): automatic saturation at this stage we set the parameter ``sat`` to 0 (which is in fact the default):: - sage: EQ.process([[1547, -2967, 343], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]], sat=0) + sage: EQ.process([[1547, -2967, 343], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]], saturation_bound=0) P1 = [1547:-2967:343] is generator number 1 P2 = [2707496766203306:864581029138191:2969715140223272] is generator number 2 P3 = [-13422227300:-49322830557:12167000000] is generator number 3 @@ -1172,12 +1141,12 @@ def saturate(self, max_prime=-1, odd_primes_only=False): Now we saturate at `p=2`, and gain index 2:: sage: EQ.saturate(2) # points were not 2-saturated - saturating basis...Saturation index bound = 93 - WARNING: saturation at primes p > 2 will not be done; + saturating basis...Saturation index bound (for points of good reduction) = 93 + Only p-saturating for p up to given value 2. ... Gained index 2 New regulator = 93.857... - (False, 2, '[ ]') + (True, 2, '[ ]') sage: EQ Subgroup of Mordell-Weil group: [[-2:3:1], [2707496766203306:864581029138191:2969715140223272], [-13422227300:-49322830557:12167000000]] sage: EQ.regulator() @@ -1186,12 +1155,12 @@ def saturate(self, max_prime=-1, odd_primes_only=False): Now we saturate at `p=3`, and gain index 3:: sage: EQ.saturate(3) # points were not 3-saturated - saturating basis...Saturation index bound = 46 - WARNING: saturation at primes p > 3 will not be done; + saturating basis...Saturation index bound (for points of good reduction) = 46 + Only p-saturating for p up to given value 3. ... Gained index 3 New regulator = 10.428... - (False, 3, '[ ]') + (True, 3, '[ ]') sage: EQ Subgroup of Mordell-Weil group: [[-2:3:1], [-14:25:8], [-13422227300:-49322830557:12167000000]] sage: EQ.regulator() @@ -1200,12 +1169,12 @@ def saturate(self, max_prime=-1, odd_primes_only=False): Now we saturate at `p=5`, and gain index 5:: sage: EQ.saturate(5) # points were not 5-saturated - saturating basis...Saturation index bound = 15 - WARNING: saturation at primes p > 5 will not be done; + saturating basis...Saturation index bound (for points of good reduction) = 15 + Only p-saturating for p up to given value 5. ... Gained index 5 New regulator = 0.417... - (False, 5, '[ ]') + (True, 5, '[ ]') sage: EQ Subgroup of Mordell-Weil group: [[-2:3:1], [-14:25:8], [1:-1:1]] sage: EQ.regulator() @@ -1215,7 +1184,8 @@ def saturate(self, max_prime=-1, odd_primes_only=False): the points are now provably saturated at all primes:: sage: EQ.saturate() # points are now saturated - saturating basis...Saturation index bound = 3 + saturating basis...Saturation index bound (for points of good reduction) = 3 + Tamagawa index primes are [ ] Checking saturation at [ 2 3 ] Checking 2-saturation Points were proved 2-saturated (max q used = 11) @@ -1229,7 +1199,7 @@ def saturate(self, max_prime=-1, odd_primes_only=False): sage: E = mwrank_EllipticCurve([0,0,1,-7,6]) sage: EQ = mwrank_MordellWeil(E) - sage: EQ.process([[1547, -2967, 343], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]], sat=5) + sage: EQ.process([[1547, -2967, 343], [2707496766203306, 864581029138191, 2969715140223272], [-13422227300, -49322830557, 12167000000]], saturation_bound=5) P1 = [1547:-2967:343] is generator number 1 ... Gained index 5, new generators = [ [-2:3:1] [-14:25:8] [1:-1:1] ] @@ -1242,7 +1212,8 @@ def saturate(self, max_prime=-1, odd_primes_only=False): verify that full saturation has been done:: sage: EQ.saturate() - saturating basis...Saturation index bound = 3 + saturating basis...Saturation index bound (for points of good reduction) = 3 + Tamagawa index primes are [ ] Checking saturation at [ 2 3 ] Checking 2-saturation Points were proved 2-saturated (max q used = 11) @@ -1255,8 +1226,9 @@ def saturate(self, max_prime=-1, odd_primes_only=False): index of the points in their saturation is at most 3, then proves saturation at 2 and at 3, by reducing the points modulo all primes of good reduction up to 11, respectively 13. + """ - ok, index, unsat = self.__mw.saturate(int(max_prime), odd_primes_only) + ok, index, unsat = self.__mw.saturate(int(max_prime), int(min_prime)) return bool(ok), int(str(index)), unsat def search(self, height_limit=18, verbose=False): @@ -1271,9 +1243,9 @@ def search(self, height_limit=18, verbose=False): .. note:: - On 32-bit machines, this *must* be < 21.48 else + On 32-bit machines, this *must* be < 21.48 (`31\log(2)`) else `\exp(h_{\text{lim}}) > 2^{31}` and overflows. On 64-bit machines, it - must be *at most* 43.668. However, this bound is a logarithmic + must be *at most* 43.668 (`63\log(2)`) . However, this bound is a logarithmic bound and increasing it by just 1 increases the running time by (roughly) `\exp(1.5)=4.5`, so searching up to even 20 takes a very long time. @@ -1320,8 +1292,10 @@ def search(self, height_limit=18, verbose=False): Subgroup of Mordell-Weil group: [[4413270:10381877:27000]] """ height_limit = float(height_limit) - if height_limit >= 21.4: # TODO: docstring says 21.48 (for 32-bit machines; what about 64-bit...?) - raise ValueError("The height limit must be < 21.4.") + int_bits = sys.maxsize.bit_length() + max_height_limit = int_bits * 0.693147 # log(2.0) = 0.693147 approx + if height_limit >= max_height_limit: + raise ValueError("The height limit must be < {} = {}log(2) on a {}-bit machine.".format(max_height_limit, int_bits, int_bits+1)) moduli_option = 0 # Use Stoll's sieving program... see strategies in ratpoints-1.4.c @@ -1352,5 +1326,4 @@ def points(self): [[1, -1, 1], [-2, 3, 1], [-14, 25, 8]] """ - L = eval(self.__mw.getbasis().replace(":",",")) - return [[Integer(x), Integer(y), Integer(z)] for (x,y,z) in L] + return self.__mw.getbasis() diff --git a/src/sage/libs/eclib/mwrank.pyx b/src/sage/libs/eclib/mwrank.pyx index b82831dc5fd..ce5090c80d5 100644 --- a/src/sage/libs/eclib/mwrank.pyx +++ b/src/sage/libs/eclib/mwrank.pyx @@ -28,6 +28,7 @@ from cysignals.signals cimport sig_on, sig_off from sage.cpython.string cimport char_to_str, str_to_bytes from sage.cpython.string import FS_ENCODING from sage.libs.eclib cimport bigint, Curvedata, mw, two_descent +from sage.rings.all import Integer cdef extern from "wrap.cpp": ### misc functions ### @@ -55,8 +56,8 @@ cdef extern from "wrap.cpp": char* mw_getbasis(mw* m) double mw_regulator(mw* m) int mw_rank(mw* m) - int mw_saturate(mw* m, bigint* index, char** unsat, - long sat_bd, int odd_primes_only) + int mw_saturate(mw* m, long* index, char** unsat, + long sat_bd, long sat_low_bd) void mw_search(mw* m, char* h_lim, int moduli_option, int verb) ### two_descent ### @@ -67,8 +68,7 @@ cdef extern from "wrap.cpp": long two_descent_get_rank(two_descent* t) long two_descent_get_rank_bound(two_descent* t) long two_descent_get_selmer_rank(two_descent* t) - void two_descent_saturate(two_descent* t, long sat_bd) - + void two_descent_saturate(two_descent* t, long sat_bd, long sat_low_bd) cdef object string_sigoff(char* s): sig_off() @@ -445,7 +445,6 @@ cdef class _Curvedata: # cython class wrapping eclib's Curvedata class -1269581104000000 """ sig_on() - from sage.rings.all import Integer return Integer(string_sigoff(Curvedata_getdiscr(self.x))) def conductor(self): @@ -467,7 +466,6 @@ cdef class _Curvedata: # cython class wrapping eclib's Curvedata class 126958110400 """ sig_on() - from sage.rings.all import Integer return Integer(string_sigoff(Curvedata_conductor(self.x))) def isogeny_class(self, verbose=False): @@ -503,6 +501,36 @@ cdef class _Curvedata: # cython class wrapping eclib's Curvedata class ############# _mw ################# +def parse_point_list(s): + r""" + Parse a string representing a list of points. + + INPUT: + + - ``s`` (string) -- string representation of a list of points, for + example '[]', '[[1:2:3]]', or '[[1:2:3],[4:5:6]]'. + + OUTPUT: + + (list) a list of triples of integers, for example [], [[1,2,3]], [[1,2,3],[4,5,6]]. + + EXAMPLES:: + + sage: from sage.libs.eclib.mwrank import parse_point_list + sage: parse_point_list('[]') + [] + sage: parse_point_list('[[1:2:3]]') + [[1, 2, 3]] + sage: parse_point_list('[[1:2:3],[4:5:6]]') + [[1, 2, 3], [4, 5, 6]] + + """ + s = s.replace(":", ",").replace(" ", "") + if s == '[]': + return [] + pts = s[2:-2].split('],[') + return [[Integer(x) for x in pt.split(",")] for pt in pts] + cdef class _mw: """ Cython class wrapping eclib's mw class. @@ -561,72 +589,37 @@ cdef class _mw: sage: EQ.search(1) P1 = [0:1:0] is torsion point, order 1 P1 = [-3:0:1] is generator number 1 - ... - P4 = [12:35:27] = 1*P1 + -1*P2 + -1*P3 (mod torsion) - - The previous command produces the following output:: - - P1 = [0:1:0] is torsion point, order 1 - P1 = [-3:0:1] is generator number 1 - saturating up to 20...Checking 2-saturation - Points have successfully been 2-saturated (max q used = 7) - Checking 3-saturation - Points have successfully been 3-saturated (max q used = 7) - Checking 5-saturation - Points have successfully been 5-saturated (max q used = 23) - Checking 7-saturation - Points have successfully been 7-saturated (max q used = 41) - Checking 11-saturation - Points have successfully been 11-saturated (max q used = 17) - Checking 13-saturation - Points have successfully been 13-saturated (max q used = 43) - Checking 17-saturation - Points have successfully been 17-saturated (max q used = 31) - Checking 19-saturation - Points have successfully been 19-saturated (max q used = 37) + saturating up to 20...Saturation index bound (for points of good reduction) = 3 + Reducing saturation bound from given value 20 to computed index bound 3 + Checking saturation at [ 2 3 ] + Checking 2-saturation + Points were proved 2-saturated (max q used = 7) + Checking 3-saturation + Points were proved 3-saturated (max q used = 7) done P2 = [-2:3:1] is generator number 2 - saturating up to 20...Checking 2-saturation + saturating up to 20...Saturation index bound (for points of good reduction) = 4 + Reducing saturation bound from given value 20 to computed index bound 4 + Checking saturation at [ 2 3 ] + Checking 2-saturation possible kernel vector = [1,1] This point may be in 2E(Q): [14:-52:1] - ...and it is! + ...and it is! Replacing old generator #1 with new generator [1:-1:1] + Reducing index bound from 4 to 2 Points have successfully been 2-saturated (max q used = 7) Index gain = 2^1 - Checking 3-saturation - Points have successfully been 3-saturated (max q used = 13) - Checking 5-saturation - Points have successfully been 5-saturated (max q used = 67) - Checking 7-saturation - Points have successfully been 7-saturated (max q used = 53) - Checking 11-saturation - Points have successfully been 11-saturated (max q used = 73) - Checking 13-saturation - Points have successfully been 13-saturated (max q used = 103) - Checking 17-saturation - Points have successfully been 17-saturated (max q used = 113) - Checking 19-saturation - Points have successfully been 19-saturated (max q used = 47) - done (index = 2). + done, index = 2. Gained index 2, new generators = [ [1:-1:1] [-2:3:1] ] P3 = [-14:25:8] is generator number 3 - saturating up to 20...Checking 2-saturation - Points have successfully been 2-saturated (max q used = 11) - Checking 3-saturation - Points have successfully been 3-saturated (max q used = 13) - Checking 5-saturation - Points have successfully been 5-saturated (max q used = 71) - Checking 7-saturation - Points have successfully been 7-saturated (max q used = 101) - Checking 11-saturation - Points have successfully been 11-saturated (max q used = 127) - Checking 13-saturation - Points have successfully been 13-saturated (max q used = 151) - Checking 17-saturation - Points have successfully been 17-saturated (max q used = 139) - Checking 19-saturation - Points have successfully been 19-saturated (max q used = 179) - done (index = 1). + saturating up to 20...Saturation index bound (for points of good reduction) = 3 + Reducing saturation bound from given value 20 to computed index bound 3 + Checking saturation at [ 2 3 ] + Checking 2-saturation + Points were proved 2-saturated (max q used = 11) + Checking 3-saturation + Points were proved 3-saturated (max q used = 13) + done, index = 1. P4 = [-1:3:1] = -1*P1 + -1*P2 + -1*P3 (mod torsion) P4 = [0:2:1] = 2*P1 + 0*P2 + 1*P3 (mod torsion) P4 = [2:13:8] = -3*P1 + 1*P2 + -1*P3 (mod torsion) @@ -687,7 +680,7 @@ cdef class _mw: sig_on() return string_sigoff(mw_getbasis(self.x)) - def process(self, point, sat=0): + def process(self, point, saturation_bound=0): """ Processes the given point, adding it to the mw group. @@ -697,10 +690,12 @@ cdef class _mw: An ``ArithmeticError`` is raised if the point is not on the curve. - - ``sat`` (int, default 0) --saturate at primes up to ``sat``. - No saturation is done if ``sat=0``. (Note that it is more - efficient to add several points at once and then saturate - just once at the end). + - ``saturation_bound`` (int, default 0) --saturate at primes up to ``saturation_bound``. + No saturation is done if ``saturation_bound=0``. If ``saturation_bound=-1`` then + saturation is done at all primes, by computing a bound on + the saturation index. Note that it is more efficient to add + several points at once and then saturate just once at the + end. .. NOTE:: @@ -746,7 +741,7 @@ cdef class _mw: cdef _bigint x,y,z sig_on() x,y,z = _bigint(point[0]), _bigint(point[1]), _bigint(point[2]) - r = mw_process(self.curve, self.x, x.x, y.x, z.x, sat) + r = mw_process(self.curve, self.x, x.x, y.x, z.x, saturation_bound) sig_off() if r != 0: raise ArithmeticError("point (=%s) not on curve." % point) @@ -757,8 +752,8 @@ cdef class _mw: OUTPUT: - (string) String representation of the points in the basis of - the mw group. + (list) list of integer triples giving the projective + coordinates of the points in the basis. EXAMPLES:: @@ -768,13 +763,13 @@ cdef class _mw: sage: EQ = _mw(E) sage: EQ.search(3) sage: EQ.getbasis() - '[[0:-1:1], [-1:1:1]]' + [[0, -1, 1], [-1, 1, 1]] sage: EQ.rank() 2 """ sig_on() s = string_sigoff(mw_getbasis(self.x)) - return s + return parse_point_list(s) def regulator(self): """ @@ -797,7 +792,7 @@ cdef class _mw: sage: EQ = _mw(E) sage: EQ.search(3) sage: EQ.getbasis() - '[[0:-1:1], [-1:1:1]]' + [[0, -1, 1], [-1, 1, 1]] sage: EQ.rank() 2 sage: EQ.regulator() @@ -824,39 +819,54 @@ cdef class _mw: sage: EQ = _mw(E) sage: EQ.search(3) sage: EQ.getbasis() - '[[0:-1:1], [-1:1:1]]' + [[0, -1, 1], [-1, 1, 1]] sage: EQ.rank() 2 """ sig_on() r = mw_rank(self.x) sig_off() - from sage.rings.all import Integer return Integer(r) - def saturate(self, int sat_bd=-1, int odd_primes_only=0): + def saturate(self, int sat_bd=-1, int sat_low_bd=2): """ Saturates the current subgroup of the mw group. INPUT: - - ``sat_bnd`` (int, default -1) -- bound on primes at which to - saturate. If -1 (default), compute a bound for the primes - which may not be saturated, and use that. + - ``sat_bnd`` (int, default -1) -- upper bound on primes at + which to saturate. If -1 (default), compute a bound for the + primes which may not be saturated, and use that. Otherwise, + the bound used is the minumum of the value of ``sat_bnd`` + and the computed bound. - - ``odd_primes_only`` (bool, default ``False``) -- only do - saturation at odd primes. (If the points have been found - via 2-descent they should already be 2-saturated.) + - ``sat_low_bd`` (int, default 2) -- only do saturation at + prime not less than this. For exampe, if the points have + been found via 2-descent they should already be 2-saturated, + and ``sat_low_bd=3`` is appropriate. OUTPUT: (tuple) (success flag, index, list) The success flag will be 1 unless something failed (usually an indication that the points - were not saturated but the precision is not high enough to - divide out successfully). The index is the index of the mw - group before saturation in the mw group after. The list is a - string representation of the primes at which saturation was - not proved or achieved. + were not saturated but eclib was not able to divide out + successfully). The index is the index of the mw group before + saturation in the mw group after. The list is a string + representation of the primes at which saturation was not + proved or achieved. + + .. NOTE:: + + ``eclib`` will compute a bound on the saturation index. If + the computed saturation bound is very large and ``sat_bnd`` is + -1, ``eclib`` may output a warning, but will still attempt to + saturate up to the computed bound. If a positive value of + ``sat_bnd`` is given which is greater than the computed bound, + `p`-saturation will only be carried out for primes up to the + compated bound. Setting ``sat_low_bnd`` to a value greater + than 2 allows for saturation to be done incrementally, or for + exactly one prime `p` by setting both ``sat_bd`` and + ``sat_low_bd`` to `p`. EXAMPLES:: @@ -872,34 +882,23 @@ cdef class _mw: sage: EQ [[-1:1:1]] - If we set the saturation bound at 2, then saturation will fail:: + If we set the saturation bound at 2, then saturation will not + enlarge the basis, but the success flag is still 1 (True) + since we did not ask to check 3-saturation:: sage: EQ = _mw(E) sage: EQ.process([494, -5720, 6859]) # 3 times another point sage: EQ.saturate(sat_bd=2) - Saturation index bound = 10 - WARNING: saturation at primes p > 2 will not be done; - points may be unsaturated at primes between 2 and index bound - Failed to saturate MW basis at primes [ ] - (0, 1, '[ ]') + (1, 1, '[ ]') sage: EQ [[494:-5720:6859]] - The following output is also seen in the preceding example:: - - Saturation index bound = 10 - WARNING: saturation at primes p > 2 will not be done; - points may be unsaturated at primes between 2 and index bound - Failed to saturate MW basis at primes [ ] - - """ - cdef _bigint index + cdef long index cdef char* s cdef int ok sig_on() - index = _bigint() - ok = mw_saturate(self.x, index.x, &s, sat_bd, odd_primes_only) + ok = mw_saturate(self.x, &index, &s, sat_bd, sat_low_bd) unsat = string_sigoff(s) return ok, index, unsat @@ -1094,7 +1093,6 @@ cdef class _two_descent: sig_on() r = two_descent_get_rank(self.x) sig_off() - from sage.rings.all import Integer return Integer(r) def getrankbound(self): @@ -1128,7 +1126,6 @@ cdef class _two_descent: sig_on() r = two_descent_get_rank_bound(self.x) sig_off() - from sage.rings.all import Integer return Integer(r) def getselmer(self): @@ -1161,7 +1158,6 @@ cdef class _two_descent: sig_on() r = two_descent_get_selmer_rank(self.x) sig_off() - from sage.rings.all import Integer return Integer(r) def ok(self): @@ -1222,10 +1218,21 @@ cdef class _two_descent: """ return two_descent_get_certain(self.x) - def saturate(self, saturation_bound=0): + def saturate(self, saturation_bound=0, lower=3): """ Carries out saturation of the points found by a 2-descent. + INPUT: + + - ``saturation_bound`` (int) -- an upper bound on the primes + `p` at which `p`-saturation will be carried out, or -1, in + which case ``eclib`` will compute an upper bound on the + saturation index. + + - ``lower`` (int, default 3) -- do no `p`-saturation for `p` + less than this. The default is 3 since the points found + during 2-descent will be 2-saturated. + OUTPUT: None. @@ -1257,7 +1264,7 @@ cdef class _two_descent: '[[1:-1:1], [-2:3:1], [-14:25:8]]' """ sig_on() - two_descent_saturate(self.x, saturation_bound) + two_descent_saturate(self.x, saturation_bound, 3) sig_off() def getbasis(self): diff --git a/src/sage/libs/eclib/newforms.pyx b/src/sage/libs/eclib/newforms.pyx index b50b6061aa2..96263cd0be9 100644 --- a/src/sage/libs/eclib/newforms.pyx +++ b/src/sage/libs/eclib/newforms.pyx @@ -140,6 +140,7 @@ cdef class ECModularSymbol: - ``nap`` - (int, default 1000): the number of ap of E to use in determining the normalisation of the modular symbols. + Note that eclib will increase this to 100*sqrt(N) if necessary. EXAMPLES:: diff --git a/src/sage/libs/eclib/t b/src/sage/libs/eclib/t new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/libs/eclib/wrap.cpp b/src/sage/libs/eclib/wrap.cpp index 58c18ab67b6..28e6da869b4 100644 --- a/src/sage/libs/eclib/wrap.cpp +++ b/src/sage/libs/eclib/wrap.cpp @@ -178,11 +178,11 @@ int mw_rank(struct mw* m) } /* Returns index and unsat long array, which user must deallocate */ -int mw_saturate(struct mw* m, bigint* index, char** unsat, - long sat_bd, int odd_primes_only) +int mw_saturate(struct mw* m, long* index, char** unsat, + long sat_bd, long sat_low_bd) { vector v; - int s = m->saturate(*index, v, sat_bd, odd_primes_only); + int s = m->saturate(*index, v, sat_bd, sat_low_bd); ostringstream instore; instore << v; *unsat = stringstream_to_char(instore); @@ -236,9 +236,9 @@ long two_descent_get_certain(const two_descent* t) return t->getcertain(); } -void two_descent_saturate(struct two_descent* t, long sat_bd) +void two_descent_saturate(struct two_descent* t, long sat_bd, long sat_low_bd) { - t->saturate(sat_bd); + t->saturate(sat_bd, sat_low_bd); } double two_descent_regulator(struct two_descent* t) From 453c793d1f17096695d7ee2cf53d6e0c9c462338 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 11 Mar 2021 16:06:15 +0000 Subject: [PATCH 022/280] #31443: simplify/improve saturation method for elliptic curves over Q --- .../elliptic_curves/ell_rational_field.py | 93 ++++++++++--------- 1 file changed, 51 insertions(+), 42 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index a792afce8dc..c894cbc766b 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -779,7 +779,7 @@ def mwrank_curve(self, verbose=False): sage: E = EllipticCurve('11a1') sage: EE = E.mwrank_curve() sage: EE - y^2+ y = x^3 - x^2 - 10*x - 20 + y^2 + y = x^3 - x^2 - 10 x - 20 sage: type(EE) sage: EE.isogeny_class() @@ -2525,7 +2525,7 @@ def regulator(self, proof=None, precision=53, **kwds): assert reg.parent() is R return reg - def saturation(self, points, verbose=False, max_prime=0, odd_primes_only=False): + def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): """ Given a list of rational points on E, compute the saturation in E(Q) of the subgroup they generate. @@ -2538,17 +2538,24 @@ def saturation(self, points, verbose=False, max_prime=0, odd_primes_only=False): - ``verbose (bool)`` - (default: ``False``), if ``True``, give verbose output - - ``max_prime (int)`` - (default: 0), saturation is - performed for all primes up to max_prime. If max_prime==0, - perform saturation at *all* primes, i.e., compute the true - saturation. + - ``max_prime`` (int, default -1) -- If `-1` (the default), an + upper bound is computed for the primes at which the subgroup + may not be saturated, and saturation is performed for all + primes up to this bound. Otherwise, the bound used is the + minimum of ``max_prime`` and the computed bound. - - ``odd_primes_only (bool)`` - only do saturation at - odd primes + - ``min_prime (int)`` - (default: 2), only do `p`-saturation + at primes `p` greater than or equal to this. + .. note:: - OUTPUT: + To saturate at a single prime `p`, set ``max_prime`` and + ``min_prime`` both to `p`. One situation where this is + useful is after mapping saturated points from another + elliptic curve by a `p`-isogeny, since the images may not + be `p`-saturated but with be saturated at all other primes. + OUTPUT: - ``saturation (list)`` - points that form a basis for the saturation @@ -2559,12 +2566,32 @@ def saturation(self, points, verbose=False, max_prime=0, odd_primes_only=False): - ``regulator (real with default precision)`` - regulator of saturated points. + ALGORITHM: Uses Cremona's ``eclib`` package, which computes a + bound on the saturation index. To `p`-saturate, or prove + `p`-saturation, we consider the reductions of the points + modulo primes `q` of good reduction such that `E(\FF_q)` has + order divisible by `p`. + + .. note:: + + In versons of ``eclib`` up to ``v20190909``, division of + points in ``eclib`` was done using floating point methods, + without automatic handling of precision, so that + `p`-saturation sometimes failed unless + ``mwrank_set_precision()`` was called in advance with a + suitably high bit precision. Since version ``v20210310`` + of ``eclib``, division is done using exact methods based on + division polynomials, and `p`-saturation cannot fail in + this way. + + .. note:: + + The computed index of saturation may be large, in which + case saturation may take a long time. For example, the + rank 4 curve ``EllipticCurve([0,1,1,-9872,374262])`` has a + saturation index bound of 86682 and takes around 15 minutes + to prove saturation. - ALGORITHM: Uses Cremona's ``mwrank`` package. With ``max_prime=0``, - we call ``mwrank`` with successively larger prime bounds until the full - saturation is provably found. The results of saturation at the - previous primes is stored in each case, so this should be - reasonably fast. EXAMPLES:: @@ -2577,7 +2604,9 @@ def saturation(self, points, verbose=False, max_prime=0, odd_primes_only=False): TESTS: - See :trac:`10590`. This example would loop forever at default precision:: + See :trac:`10590`. With ``eclib`` versions up to + ``v20190909``, this example would loop forever at default + precision. Since version ``v20210310`` it runs fine:: sage: E = EllipticCurve([1, 0, 1, -977842, -372252745]) sage: P = E([-192128125858676194585718821667542660822323528626273/336995568430319276695106602174283479617040716649, 70208213492933395764907328787228427430477177498927549075405076353624188436/195630373799784831667835900062564586429333568841391304129067339731164107, 1]) @@ -2585,7 +2614,7 @@ def saturation(self, points, verbose=False, max_prime=0, odd_primes_only=False): 113.302910926080 sage: E.saturation([P]) ([(-192128125858676194585718821667542660822323528626273/336995568430319276695106602174283479617040716649 : 70208213492933395764907328787228427430477177498927549075405076353624188436/195630373799784831667835900062564586429333568841391304129067339731164107 : 1)], 1, 113.302910926080) - sage: (Q,), ind, reg = E.saturation([2*P]) # needs higher precision, handled by eclib + sage: (Q,), ind, reg = E.saturation([2*P]) sage: 2*Q == 2*P True sage: ind @@ -2634,36 +2663,16 @@ def saturation(self, points, verbose=False, max_prime=0, odd_primes_only=False): c = Emin.mwrank_curve() from sage.libs.eclib.all import mwrank_MordellWeil mw = mwrank_MordellWeil(c, verbose) - mw.process(v) - repeat_until_saturated = False - if max_prime == 0: - repeat_until_saturated = True - max_prime = 9973 - from sage.libs.all import mwrank_get_precision, mwrank_set_precision - prec0 = mwrank_get_precision() - prec = 100 - if prec0 Date: Tue, 16 Mar 2021 14:26:10 +0000 Subject: [PATCH 023/280] #31443: fix two doctest output now that eclib no longer gives incorrect results --- .../elliptic_curves/ell_modular_symbols.py | 14 +++++++++++--- .../elliptic_curves/ell_rational_field.py | 17 ++++++++--------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py index a32f64ea47b..30a61e1635c 100644 --- a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py +++ b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py @@ -298,19 +298,27 @@ def __init__(self, E, sign, nap=1000): sage: m(0) 1/5 - If ``nap`` is too small, the normalization in eclib may be incorrect. See :trac:`31317`:: + If ``nap`` is too small, the normalization in eclib used to be + incorrect (see :trac:`31317`), but since ``eclib`` version + v20210310 the value of ``nap`` is increased automatically by + ``eclib``:: sage: from sage.schemes.elliptic_curves.ell_modular_symbols import ModularSymbolECLIB sage: E = EllipticCurve('1590g1') sage: m = ModularSymbolECLIB(E, sign=+1, nap=300) sage: [m(a/5) for a in [1..4]] - [1001/153, -1001/153, -1001/153, 1001/153] + [13/2, -13/2, -13/2, 13/2] - Those values are incorrect. The correct values are:: + These values are correct, and increasing ``nap`` has no + effect. The correct values may verified by the numerical + implementation:: sage: m = ModularSymbolECLIB(E, sign=+1, nap=400) sage: [m(a/5) for a in [1..4]] [13/2, -13/2, -13/2, 13/2] + sage: m = E.modular_symbol(implementation='num') + sage: [m(a/5) for a in [1..4]] + [13/2, -13/2, -13/2, 13/2] """ from sage.libs.eclib.newforms import ECModularSymbol diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index c894cbc766b..5a563898e68 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -1283,22 +1283,21 @@ def modular_symbol(self, sign=+1, normalize=None, implementation='eclib', nap=0) sage: [Mminus(1/i) for i in [1..11]] [0, 0, 1/2, 1/2, 0, 0, -1/2, -1/2, 0, 0, 0] - With the default 'eclib' implementation, if ``nap`` is too - small, the normalization may be computed incorrectly. See - :trac:`31317`:: + With older version of eclib, in the default 'eclib' + implementation, if ``nap`` is too small, the normalization may + be computed incorrectly (see :trac:`31317`). This was fixed + in eclib version v20210310, since now eclib increase ``nap`` + automatically. The following used to give incorrect results. + See :trac:`31443`:: sage: E = EllipticCurve('1590g1') sage: m = E.modular_symbol(nap=300) sage: [m(a/5) for a in [1..4]] - [1001/153, -1001/153, -1001/153, 1001/153] + [13/2, -13/2, -13/2, 13/2] - Those values are incorrect. The correct values may be - obtained by increasing ``nap``, as verified by the numerical + These values are correct, as verified by the numerical implementation:: - sage: m = E.modular_symbol(nap=400) - sage: [m(a/5) for a in [1..4]] - [13/2, -13/2, -13/2, 13/2] sage: m = E.modular_symbol(implementation='num') sage: [m(a/5) for a in [1..4]] [13/2, -13/2, -13/2, 13/2] From f4752618381fc589d3b992d67cd29e9a9c7086d5 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Wed, 17 Mar 2021 10:14:23 +0000 Subject: [PATCH 024/280] added upstream_url to eclib's checksums.ini --- build/pkgs/eclib/checksums.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index b37bff93c05..e639bc3f395 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -2,3 +2,4 @@ tarball=eclib-20210310.tar.bz2 sha1=73437ac8deae94f00e7713405b3251d9c81f95e4 md5=4ac988bc46869866f076f7bea0fdaa6b cksum=2130748042 +upstream_url=https://github.com/JohnCremona/eclib/archive/VERSION.tar.gz From 1bac7138020240bd3b6944e85bfc28676dcca6f1 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Wed, 17 Mar 2021 15:46:48 +0000 Subject: [PATCH 025/280] more changes to eclib build setup --- build/pkgs/eclib/checksums.ini | 10 +++++----- build/pkgs/eclib/package-version.txt | 2 +- build/pkgs/eclib/spkg-configure.m4 | 28 ++++++++-------------------- 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index e639bc3f395..7f3e3b9233c 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,5 +1,5 @@ -tarball=eclib-20210310.tar.bz2 -sha1=73437ac8deae94f00e7713405b3251d9c81f95e4 -md5=4ac988bc46869866f076f7bea0fdaa6b -cksum=2130748042 -upstream_url=https://github.com/JohnCremona/eclib/archive/VERSION.tar.gz +tarball=eclib-VERSION.tar.bz2 +sha1=500c3efcf0c8efbb065bbdb8f05cf8a91b8d5c24 +md5=9da2bba60ec2920b4524b355aff9333a +cksum=1435512890 +upstream_url=https://github.com/JohnCremona/eclib/archive/eclib-VERSION.tar.bz2 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index 3384f6c7325..d52e9bed28f 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -20210310 +v20210317 diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index 8cb8ed01ea7..e55566b1e73 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,23 +1,11 @@ SAGE_SPKG_CONFIGURE([eclib], [ SAGE_SPKG_DEPCHECK([ntl pari flint], [ - dnl header types.h appeared in v20180710 - AC_CHECK_HEADER([eclib/types.h], [ - AC_MSG_CHECKING([whether we can link and run a program using eclib]) - ECLIB_SAVED_LIBS="$LIBS" - LIBS="$LIBS -lec" - AC_RUN_IFELSE([ - AC_LANG_PROGRAM([[#include ] - [#include ]], - [[set_bit_precision(42); /* test for versions >= v20190226 */ - show_version(); - return 0;]] - )], [AC_MSG_RESULT([yes; use eclib from the system])], [ - AC_MSG_RESULT([no; install eclib]) - sage_spkg_install_eclib=yes - LIBS="$ECLIB_SAVED_LIBS" - ]) - ], [sage_spkg_install_eclib=yes]) - AC_PATH_PROG([MWRANK], [mwrank]) - AS_IF([test -z "$ac_cv_path_MWRANK"], [sage_spkg_install_eclib=yes]) - ]) + dnl Trac #31443: use existing eclib only if the version reported by pkg-config is correct + PKG_CHECK_MODULES([ECLIB], + [eclib = v20210317], + [sage_spkg_install_eclib=no], + [sage_spkg_install_eclib=yes]) + AC_PATH_PROG([MWRANK], [mwrank]) + AS_IF([test -z "$ac_cv_path_MWRANK"], [sage_spkg_install_eclib=yes]) + ]) ]) From 7db4910ebff9e37ccbdadebf0a4e365e064d0c3a Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 18 Mar 2021 14:01:20 +0000 Subject: [PATCH 026/280] remove dud empty file --- src/sage/libs/eclib/t | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/sage/libs/eclib/t diff --git a/src/sage/libs/eclib/t b/src/sage/libs/eclib/t deleted file mode 100644 index e69de29bb2d..00000000000 From 28408fc22d9d9525460e74492f9193e54a20bb15 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 18 Mar 2021 14:05:41 +0000 Subject: [PATCH 027/280] correct upstream_url for eclib --- build/pkgs/eclib/checksums.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 7f3e3b9233c..80f452946bc 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -2,4 +2,4 @@ tarball=eclib-VERSION.tar.bz2 sha1=500c3efcf0c8efbb065bbdb8f05cf8a91b8d5c24 md5=9da2bba60ec2920b4524b355aff9333a cksum=1435512890 -upstream_url=https://github.com/JohnCremona/eclib/archive/eclib-VERSION.tar.bz2 +upstream_url=https://johncremona.github.io/ftp/eclib-v20210317.tar.bz2 From 573bcf604b3ab69d19716b47627f3b378b0c081f Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Thu, 18 Mar 2021 14:33:31 +0000 Subject: [PATCH 028/280] fix m4 and tar URL, remove junk --- build/pkgs/eclib/checksums.ini | 2 +- build/pkgs/eclib/spkg-configure.m4 | 22 +++++++++++++++------- src/sage/libs/eclib/t | 0 3 files changed, 16 insertions(+), 8 deletions(-) delete mode 100644 src/sage/libs/eclib/t diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 7f3e3b9233c..94203b25d9d 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -2,4 +2,4 @@ tarball=eclib-VERSION.tar.bz2 sha1=500c3efcf0c8efbb065bbdb8f05cf8a91b8d5c24 md5=9da2bba60ec2920b4524b355aff9333a cksum=1435512890 -upstream_url=https://github.com/JohnCremona/eclib/archive/eclib-VERSION.tar.bz2 +upstream_url=https://johncremona.github.io/ftp/eclib-VERSION.tar.bz2 diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index e55566b1e73..377b66b0711 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,11 +1,19 @@ SAGE_SPKG_CONFIGURE([eclib], [ - SAGE_SPKG_DEPCHECK([ntl pari flint], [ + SAGE_SPKG_DEPCHECK([ntl pari flint], [ dnl Trac #31443: use existing eclib only if the version reported by pkg-config is correct - PKG_CHECK_MODULES([ECLIB], - [eclib = v20210317], - [sage_spkg_install_eclib=no], - [sage_spkg_install_eclib=yes]) - AC_PATH_PROG([MWRANK], [mwrank]) - AS_IF([test -z "$ac_cv_path_MWRANK"], [sage_spkg_install_eclib=yes]) + m4_pushdef([SAGE_ECLIB_VER],["v20210317"]) + PKG_CHECK_MODULES([ECLIB], [eclib = SAGE_ECLIB_VER], [ + AC_CACHE_CHECK([for mwrank version == SAGE_ECLIB_VER], [ac_cv_path_MWRANK], [ + AC_PATH_PROGS_FEATURE_CHECK([MWRANK], [mwrank], [ + mwrank_version=`$ac_path_MWRANK -V 2>&1` + AX_COMPARE_VERSION([$mwrank_version], [eq], [SAGE_ECLIB_VER], [ + ac_cv_path_MWRANK="$ac_path_MWRANK" + ]) + ]) + ]) + AS_IF([test -z "$ac_cv_path_MWRANK"], [sage_spkg_install_eclib=yes]) + ], [ + sage_spkg_install_eclib=yes]) ]) + m4_popdef([SAGE_ECLIB_VER]) ]) diff --git a/src/sage/libs/eclib/t b/src/sage/libs/eclib/t deleted file mode 100644 index e69de29bb2d..00000000000 From d32295ffeab20af1781b903fad1139c50527d94d Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 18 Mar 2021 15:03:17 +0000 Subject: [PATCH 029/280] fix spkg-configure for eclib --- build/pkgs/eclib/spkg-configure.m4 | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index e55566b1e73..377b66b0711 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,11 +1,19 @@ SAGE_SPKG_CONFIGURE([eclib], [ - SAGE_SPKG_DEPCHECK([ntl pari flint], [ + SAGE_SPKG_DEPCHECK([ntl pari flint], [ dnl Trac #31443: use existing eclib only if the version reported by pkg-config is correct - PKG_CHECK_MODULES([ECLIB], - [eclib = v20210317], - [sage_spkg_install_eclib=no], - [sage_spkg_install_eclib=yes]) - AC_PATH_PROG([MWRANK], [mwrank]) - AS_IF([test -z "$ac_cv_path_MWRANK"], [sage_spkg_install_eclib=yes]) + m4_pushdef([SAGE_ECLIB_VER],["v20210317"]) + PKG_CHECK_MODULES([ECLIB], [eclib = SAGE_ECLIB_VER], [ + AC_CACHE_CHECK([for mwrank version == SAGE_ECLIB_VER], [ac_cv_path_MWRANK], [ + AC_PATH_PROGS_FEATURE_CHECK([MWRANK], [mwrank], [ + mwrank_version=`$ac_path_MWRANK -V 2>&1` + AX_COMPARE_VERSION([$mwrank_version], [eq], [SAGE_ECLIB_VER], [ + ac_cv_path_MWRANK="$ac_path_MWRANK" + ]) + ]) + ]) + AS_IF([test -z "$ac_cv_path_MWRANK"], [sage_spkg_install_eclib=yes]) + ], [ + sage_spkg_install_eclib=yes]) ]) + m4_popdef([SAGE_ECLIB_VER]) ]) From 5a90432220bf69426b4f5c82562891292b07a1b2 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 18 Mar 2021 15:32:43 +0000 Subject: [PATCH 030/280] updated eclib build files to 20210318 --- build/pkgs/eclib/checksums.ini | 8 ++++---- build/pkgs/eclib/package-version.txt | 2 +- build/pkgs/eclib/spkg-configure.m4 | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 80f452946bc..1a8e002b78b 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,5 +1,5 @@ tarball=eclib-VERSION.tar.bz2 -sha1=500c3efcf0c8efbb065bbdb8f05cf8a91b8d5c24 -md5=9da2bba60ec2920b4524b355aff9333a -cksum=1435512890 -upstream_url=https://johncremona.github.io/ftp/eclib-v20210317.tar.bz2 +sha1=6954aa9553e61c48ba3f8565ac4ac88b31e761a4 +md5=26f7d78a4ba7263dbf59ef7466bbce34 +cksum=4022275109 +upstream_url=https://johncremona.github.io/ftp/eclib-VERSION.tar.bz2 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index d52e9bed28f..5a5e7e1b211 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -v20210317 +20210318 diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index 377b66b0711..748e6b8bf3e 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,7 +1,7 @@ SAGE_SPKG_CONFIGURE([eclib], [ SAGE_SPKG_DEPCHECK([ntl pari flint], [ dnl Trac #31443: use existing eclib only if the version reported by pkg-config is correct - m4_pushdef([SAGE_ECLIB_VER],["v20210317"]) + m4_pushdef([SAGE_ECLIB_VER],["20210318"]) PKG_CHECK_MODULES([ECLIB], [eclib = SAGE_ECLIB_VER], [ AC_CACHE_CHECK([for mwrank version == SAGE_ECLIB_VER], [ac_cv_path_MWRANK], [ AC_PATH_PROGS_FEATURE_CHECK([MWRANK], [mwrank], [ From 5d9e70a118b4cce4d2b526129a880ca7f4822485 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Mon, 22 Mar 2021 08:41:23 +0000 Subject: [PATCH 031/280] #31443: updated upstream tarball URL --- build/pkgs/eclib/checksums.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 1a8e002b78b..824f44f53cd 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -2,4 +2,4 @@ tarball=eclib-VERSION.tar.bz2 sha1=6954aa9553e61c48ba3f8565ac4ac88b31e761a4 md5=26f7d78a4ba7263dbf59ef7466bbce34 cksum=4022275109 -upstream_url=https://johncremona.github.io/ftp/eclib-VERSION.tar.bz2 +upstream_url=https://github.com/JohnCremona/eclib/releases/download/VERSION/eclib-VERSION.tar.bz2 \ No newline at end of file From 7781f3af64d2b509f4a7ebaaba927fb939ac31b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 24 Mar 2021 18:50:20 +0100 Subject: [PATCH 032/280] fix some NOTE and EXAMPLES --- src/sage/categories/sets_cat.py | 3 +-- src/sage/combinat/designs/covering_design.py | 16 +++++------ src/sage/combinat/sloane_functions.py | 2 +- src/sage/functions/prime_pi.pyx | 14 +++++----- .../geometry/polyhedron/representation.py | 26 +++++++++--------- .../graphs/graph_decompositions/rankwidth.pyx | 8 +++--- src/sage/matrix/matrix2.pyx | 27 ++++++++++--------- .../number_field/number_field_element.pyx | 11 ++++---- src/sage/rings/number_field/totallyreal.pyx | 7 ++--- .../rings/number_field/totallyreal_phc.py | 17 ++++++------ .../rings/padics/local_generic_element.pyx | 8 ++++-- src/sage/rings/padics/padic_generic.py | 13 ++++----- .../rings/padics/padic_generic_element.pyx | 10 +++---- src/sage/schemes/elliptic_curves/ell_point.py | 27 ++++++++++--------- .../elliptic_curves/ell_rational_field.py | 24 ++++++++--------- .../schemes/elliptic_curves/lseries_ell.py | 2 +- 16 files changed, 111 insertions(+), 104 deletions(-) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index 68ba29bfa7c..0efd3bf94dc 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -1458,7 +1458,7 @@ def _test_construction(self, **options): and if it returns the latter, then it is supposed that ``F(O)==self`. The test verifies this assumption. - EXAMPLE: + EXAMPLES: We create a parent that returns a wrong construction (its construction returns the rational field rather than the parent itself):: @@ -2601,7 +2601,6 @@ def example(self, base_ring = None, set = None): from sage.categories.examples.with_realizations import SubsetAlgebra return SubsetAlgebra(base_ring, set) - class ParentMethods: def _test_with_realizations(self, **options): diff --git a/src/sage/combinat/designs/covering_design.py b/src/sage/combinat/designs/covering_design.py index f6b69cc8180..8592a005ca6 100644 --- a/src/sage/combinat/designs/covering_design.py +++ b/src/sage/combinat/designs/covering_design.py @@ -128,18 +128,18 @@ def trivial_covering_design(v, k, t): 1 3 4 2 3 4 - NOTES: + .. NOTE:: - Cases are: + Cases are: - * `t=0`: This could be empty, but it's a useful convention to have - one block (which is empty if $k=0$). + * `t=0`: This could be empty, but it's a useful convention to have + one block (which is empty if $k=0$). - * `t=1` : This contains `\lceil v/k \rceil` blocks: - `[0, ..., k-1], [k, ..., 2k-1], ...`. The last block wraps around if - `k` does not divide `v`. + * `t=1` : This contains `\lceil v/k \rceil` blocks: + `[0, ..., k-1], [k, ..., 2k-1], ...`. The last block wraps around + if `k` does not divide `v`. - * anything else: Just use every `k`-subset of `[0, 1,..., v-1]`. + * anything else: Just use every `k`-subset of `[0, 1,..., v-1]`. """ if t == 0: # single block [0, ..., k-1] diff --git a/src/sage/combinat/sloane_functions.py b/src/sage/combinat/sloane_functions.py index ada6bf23094..bdeb3be26c1 100644 --- a/src/sage/combinat/sloane_functions.py +++ b/src/sage/combinat/sloane_functions.py @@ -84,7 +84,7 @@ # 1. Add a new class to Section II below, which you should # do by copying an existing class and modifying it. # Make sure to at least define _eval and _repr_. -# NOTES: (a) define the _eval method only, which you may +# NOTE: (a) define the _eval method only, which you may # assume has as input a *positive* Sage integer (offset > 0). # Each sequence in the OEIS has an offset >= 0, indicating the # value of the first index. The default offset = 1. diff --git a/src/sage/functions/prime_pi.pyx b/src/sage/functions/prime_pi.pyx index 60ddf604af1..7d70e1edcb1 100644 --- a/src/sage/functions/prime_pi.pyx +++ b/src/sage/functions/prime_pi.pyx @@ -1,4 +1,4 @@ -""" +r""" Counting Primes AUTHORS: @@ -121,10 +121,10 @@ cdef class PrimePi(BuiltinFunction): sage: P = plot(prime_pi, 50, 100) - NOTES: + .. NOTE:: - Uses a recursive implementation, using the optimizations described in - [Oha2011]_. + This uses a recursive implementation, using the optimizations + described in [Oha2011]_. AUTHOR: @@ -504,10 +504,10 @@ cpdef Integer legendre_phi(x, a): sage: legendre_phi(4215701455, 6450023226) 1 - NOTES: + .. NOTE:: - Uses a recursive implementation, using the optimizations described in - [Oha2011]_. + This uses a recursive implementation, using the optimizations + described in [Oha2011]_. AUTHOR: diff --git a/src/sage/geometry/polyhedron/representation.py b/src/sage/geometry/polyhedron/representation.py index 3c73a959ac7..eb752542378 100644 --- a/src/sage/geometry/polyhedron/representation.py +++ b/src/sage/geometry/polyhedron/representation.py @@ -280,13 +280,13 @@ def index(self): Return an arbitrary but fixed number according to the internal storage order. - NOTES: + .. NOTE:: - H-representation and V-representation objects are enumerated - independently. That is, amongst all vertices/rays/lines there - will be one with ``index()==0``, and amongst all - inequalities/equations there will be one with ``index()==0``, - unless the polyhedron is empty or spans the whole space. + H-representation and V-representation objects are enumerated + independently. That is, amongst all vertices/rays/lines there + will be one with ``index()==0``, and amongst all + inequalities/equations there will be one with ``index()==0``, + unless the polyhedron is empty or spans the whole space. EXAMPLES:: @@ -598,16 +598,16 @@ def __mul__(self, Vobj): def eval(self, Vobj): r""" - Evaluates the left hand side `A\vec{x}+b` on the given + Evaluate the left hand side `A\vec{x}+b` on the given vertex/ray/line. - NOTES: + .. NOTE:: - * Evaluating on a vertex returns `A\vec{x}+b` - * Evaluating on a ray returns `A\vec{r}`. Only the sign or - whether it is zero is meaningful. - * Evaluating on a line returns `A\vec{l}`. Only whether it - is zero or not is meaningful. + * Evaluating on a vertex returns `A\vec{x}+b` + * Evaluating on a ray returns `A\vec{r}`. Only the sign or + whether it is zero is meaningful. + * Evaluating on a line returns `A\vec{l}`. Only whether it + is zero or not is meaningful. EXAMPLES:: diff --git a/src/sage/graphs/graph_decompositions/rankwidth.pyx b/src/sage/graphs/graph_decompositions/rankwidth.pyx index 093d0121ac8..1a907e4bebe 100644 --- a/src/sage/graphs/graph_decompositions/rankwidth.pyx +++ b/src/sage/graphs/graph_decompositions/rankwidth.pyx @@ -30,7 +30,7 @@ achieving the minimal *rank-width*. **RW -- The original source code :** RW is a program that calculates rank-width and -rank-decompositions. It is based on ideas from : +rank-decompositions. It is based on ideas from: * "Computing rank-width exactly" by Sang-il Oum [Oum2009]_ * "Sopra una formula numerica" by Ernesto Pascal @@ -94,15 +94,15 @@ Methods ------- """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2011 Nathann Cohen # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from cysignals.memory cimport check_allocarray, sig_free from cysignals.signals cimport * diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 885fe8572e3..5497985083c 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -10665,19 +10665,19 @@ cdef class Matrix(Matrix1): correctness. Set this to ``False`` for a speedup if the eigenvalues are known to be correct. - NOTES: + .. NOTE:: - Currently, the Jordan normal form is not computed over inexact rings - in any but the trivial cases when the matrix is either `0 \times 0` - or `1 \times 1`. + Currently, the Jordan normal form is not computed over + inexact rings in any but the trivial cases when the matrix + is either `0 \times 0` or `1 \times 1`. - In the case of exact rings, this method does not compute any - generalized form of the Jordan normal form, but is only able to - compute the result if the characteristic polynomial of the matrix - splits over the specific base ring. + In the case of exact rings, this method does not compute any + generalized form of the Jordan normal form, but is only able to + compute the result if the characteristic polynomial of the matrix + splits over the specific base ring. - Note that the base ring must be a field or a ring with an implemented - fraction field. + Note that the base ring must be a field or a ring with an + implemented fraction field. EXAMPLES:: @@ -17589,16 +17589,17 @@ def _binomial(Py_ssize_t n, Py_ssize_t k): i, n, k = i + 1, n - 1, k - 1 return result + def _jordan_form_vector_in_difference(V, W): r""" Given two lists of vectors ``V`` and ``W`` over the same base field, returns a vector in the difference ``V - W``. If the difference is empty, returns ``None``. - NOTES: + .. NOTE:: - This is meant to be a private helper method for the ``jordan_form`` method - in the above class. + This is meant to be a private helper method for the + ``jordan_form`` method in the above class. TESTS:: diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 8167c524522..7462f0d986b 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -2494,9 +2494,9 @@ cdef class NumberFieldElement(FieldElement): x._reduce_c_() return x - #NOTES: In LiDIA, they build a multiplication table for the - #number field, so it's not necessary to reduce modulo the - #defining polynomial every time: + # NOTE: In LiDIA, they build a multiplication table for the + # number field, so it's not necessary to reduce modulo the + # defining polynomial every time: # src/number_fields/algebraic_num/order.cc: compute_table # but asymptotically fast poly multiplication means it's # actually faster to *not* build a table!?! @@ -4488,7 +4488,7 @@ cdef class NumberFieldElement(FieldElement): This means the different with respect to the base field `\QQ`. - EXAMPLE:: + EXAMPLES:: sage: K. = NumberFieldTower([x^2 - 17, x^3 - 2]) sage: a.absolute_different() @@ -4500,6 +4500,7 @@ cdef class NumberFieldElement(FieldElement): """ return self.different(K=QQ) + cdef class NumberFieldElement_absolute(NumberFieldElement): def _magma_init_(self, magma): @@ -5520,5 +5521,3 @@ cdef void _ntl_poly(f, ZZX_c *num, ZZ_c *den): for i from 0 <= i <= __num.degree(): mpz_to_ZZ(&coeff, (ZZ(__num[i])).value) ZZX_SetCoeff( num[0], i, coeff ) - - diff --git a/src/sage/rings/number_field/totallyreal.pyx b/src/sage/rings/number_field/totallyreal.pyx index 4ac05950d71..0512f0559fc 100644 --- a/src/sage/rings/number_field/totallyreal.pyx +++ b/src/sage/rings/number_field/totallyreal.pyx @@ -1,4 +1,4 @@ -""" +r""" Enumeration of Primitive Totally Real Fields This module contains functions for enumerating all primitive @@ -138,8 +138,9 @@ cpdef double odlyzko_bound_totallyreal(int n): - John Voight (2007-09-03) - NOTES: - The values are calculated by Martinet [Mar1980]_. + .. NOTE:: + + The values are calculated by Martinet [Mar1980]_. """ if n <= 10: diff --git a/src/sage/rings/number_field/totallyreal_phc.py b/src/sage/rings/number_field/totallyreal_phc.py index 6dbc0b60576..59467e5af40 100644 --- a/src/sage/rings/number_field/totallyreal_phc.py +++ b/src/sage/rings/number_field/totallyreal_phc.py @@ -7,15 +7,15 @@ * Zeroth attempt. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 William Stein and John Voight # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** import os import sage.misc.misc @@ -36,9 +36,9 @@ def coefficients_to_power_sums(n, m, a): list of integers. - NOTES: + .. NOTE:: - Uses Newton's relations, which are classical. + This uses Newton's relations, which are classical. AUTHORS: @@ -62,6 +62,7 @@ def __lagrange_bounds_phc(n, m, a, tmpfile=None): r""" This function determines the bounds on the roots in the enumeration of totally real fields via Lagrange multipliers. + It is used internally by the main function enumerate_totallyreal_fields_prim(), which should be consulted for further information. @@ -75,10 +76,10 @@ def __lagrange_bounds_phc(n, m, a, tmpfile=None): the lower and upper bounds as real numbers. - NOTES: + .. NOTE:: - See Cohen [Coh2000]_ for the general idea and unpublished work of the - author for more detail. + See Cohen [Coh2000]_ for the general idea and unpublished work of the + author for more detail. AUTHORS: diff --git a/src/sage/rings/padics/local_generic_element.pyx b/src/sage/rings/padics/local_generic_element.pyx index 71dfbc923c1..9cba6678cc4 100644 --- a/src/sage/rings/padics/local_generic_element.pyx +++ b/src/sage/rings/padics/local_generic_element.pyx @@ -612,9 +612,13 @@ cdef class LocalGenericElement(CommutativeRingElement): - boolean -- whether ``self`` is a unit - NOTES: + .. NOTE:: - For fields all nonzero elements are units. For DVR's, only those elements of valuation 0 are. An older implementation ignored the case of fields, and returned always the negation of self.valuation()==0. This behavior is now supported with self.is_padic_unit(). + For fields all nonzero elements are units. For DVR's, only + those elements of valuation 0 are. An older implementation + ignored the case of fields, and returned always the + negation of self.valuation()==0. This behavior is now + supported with self.is_padic_unit(). EXAMPLES:: diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index 650b5137bf5..a5facdeb950 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -12,7 +12,7 @@ """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007-2013 David Roe # William Stein # Julian Rueth @@ -21,8 +21,8 @@ # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.misc import some_tuples from copy import copy @@ -606,13 +606,14 @@ def teichmuller_system(self): sage: F.teichmuller_system()[3] (2*a + 2) + (4*a + 1)*5 + 4*5^2 + (2*a + 1)*5^3 + (4*a + 1)*5^4 + (2*a + 3)*5^5 + O(5^6) - NOTES: + .. NOTE:: - Should this return 0 as well? + Should this return 0 as well? """ R = self.residue_class_field() prec = self.precision_cap() - return [self.teichmuller(self(i).lift_to_precision(prec)) for i in R if i != 0] + return [self.teichmuller(self(i).lift_to_precision(prec)) + for i in R if i != 0] # def different(self): # raise NotImplementedError diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index deede842333..4cd227ec49f 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -2552,14 +2552,14 @@ cdef class pAdicGenericElement(LocalGenericElement): By default, we use the binary splitting if it is available. Otherwise we switch to the generic algorithm. - NOTES: + .. NOTE:: - What some other systems do: + What some other systems do: - - PARI: Seems to define the logarithm for units not congruent - to 1 as we do. + - PARI: Seems to define the logarithm for units not congruent + to 1 as we do. - - MAGMA: Only implements logarithm for 1-units (as of version 2.19-2) + - MAGMA: Only implements logarithm for 1-units (version 2.19-2) .. TODO:: diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index 038d105e1a7..dc14aa5caff 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -1748,14 +1748,14 @@ def tate_pairing(self, Q, n, k, q=None): sage: Px.weil_pairing(Qx, 41)^e == num/den True - NOTES: + .. NOTE:: - This function uses Miller's algorithm, followed by a naive - exponentiation. It does not do anything fancy. In the case - that there is an issue with `Q` being on one of the lines - generated in the `r*P` calculation, `Q` is offset by a random - point `R` and P.tate_pairing(Q+R,n,k)/P.tate_pairing(R,n,k) - is returned. + This function uses Miller's algorithm, followed by a naive + exponentiation. It does not do anything fancy. In the case + that there is an issue with `Q` being on one of the lines + generated in the `r*P` calculation, `Q` is offset by a random + point `R` and P.tate_pairing(Q+R,n,k)/P.tate_pairing(R,n,k) + is returned. AUTHORS: @@ -1945,13 +1945,14 @@ def ate_pairing(self, Q, n, k, t, q=None): ... ValueError: This point (14 : 10*a : 1) is not in Ker(pi - 1) - NOTES: + .. NOTE:: - First defined in the paper of [HSV2006]_, the ate pairing can be - computationally effective in those cases when the trace of the curve - over the base field is significantly smaller than the expected - value. This implementation is simply Miller's algorithm followed by a - naive exponentiation, and makes no claims towards efficiency. + First defined in the paper of [HSV2006]_, the ate pairing + can be computationally effective in those cases when the + trace of the curve over the base field is significantly + smaller than the expected value. This implementation is + simply Miller's algorithm followed by a naive + exponentiation, and makes no claims towards efficiency. AUTHORS: diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index eea4c883303..e9a51903725 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -2727,21 +2727,21 @@ def silverman_height_bound(self, algorithm='default'): - 'mwrank' -- use a C++ implementation in the mwrank library - NOTES: + .. NOTE:: - - The CPS_height_bound is often better (i.e. smaller) than - the Silverman bound, but it only applies for points over - the base field, whereas the Silverman bound works over - all number fields. + - The CPS_height_bound is often better (i.e. smaller) than + the Silverman bound, but it only applies for points over + the base field, whereas the Silverman bound works over + all number fields. - - The Silverman bound is also fairly straightforward to - compute over number fields, but isn't implemented here. + - The Silverman bound is also fairly straightforward to + compute over number fields, but isn't implemented here. - - Silverman's paper is 'The Difference Between the Weil - Height and the Canonical Height on Elliptic Curves', - Math. Comp., Volume 55, Number 192, pages 723-743. We - use a correction by Bremner with 0.973 replaced by 0.961, - as explained in the source code to mwrank (htconst.cc). + - Silverman's paper is 'The Difference Between the Weil + Height and the Canonical Height on Elliptic Curves', + Math. Comp., Volume 55, Number 192, pages 723-743. We + use a correction by Bremner with 0.973 replaced by 0.961, + as explained in the source code to mwrank (htconst.cc). EXAMPLES:: diff --git a/src/sage/schemes/elliptic_curves/lseries_ell.py b/src/sage/schemes/elliptic_curves/lseries_ell.py index 9aa647093f4..1fcd02f9ec2 100644 --- a/src/sage/schemes/elliptic_curves/lseries_ell.py +++ b/src/sage/schemes/elliptic_curves/lseries_ell.py @@ -871,7 +871,7 @@ def L_ratio(self): # and is a multiple of the degree of an isogeny between E # and the optimal curve. # - # NOTES: We *do* have to worry about the Manin constant, since + # NOTE: We *do* have to worry about the Manin constant, since # we are using the Neron model to compute omega, not the # newform. My theorem replaces the omega above by omega/c, # where c is the Manin constant, and the bound must be From 9d5b3c0fdcf0b7c5a5d61003e11c6cad313945be Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Mon, 29 Mar 2021 17:51:54 +0200 Subject: [PATCH 033/280] Add algorithm enumerating infinite repetitions --- src/doc/en/reference/references/index.rst | 10 + src/sage/combinat/words/finite_word.py | 32 ++ src/sage/combinat/words/morphism.py | 590 +++++++++++++++++++++- 3 files changed, 630 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 86b4fb35b48..afd4c8f234d 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -3439,6 +3439,11 @@ REFERENCES: .. [Knu2005] Lars R. Knudsen, *SMASH - A Cryptographic Hash Function*; in FSE'05, (2005), pp. 228-242. +.. [KO2000] Yuji Kobayashi and Friedrich Otto, + *Repetitiveness of languages generated by morphisms*. + Theoret. Comp. Sci. 240 (2000) 337--378. + :doi:`10.1016/S0304-3975(99)00238-8` + .. [Kob1993] Neal Koblitz, *Introduction to Elliptic Curves and Modular Forms*. Springer GTM 97, 1993. @@ -3560,6 +3565,11 @@ REFERENCES: algebra of type* `A`. Proc. Amer. Math. Soc. **138** (2010), no. 11, 3877--3889. +.. [KS2015] Karel Klouda and Štěpán Starosta, + *An Algorithm Enumerating All Infinite Repetitions in a D0L System*. + Journal of Discrete Algorithms, 33 (2015), 130-138. + :doi:`10.1016/j.jda.2015.03.006` + .. [KS2019] \J. Kliem and C. Stump. *A face iterator for polyhedra and more general finite locally branched lattices*. diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py index aec56d7fbd4..2c714fc3d29 100644 --- a/src/sage/combinat/words/finite_word.py +++ b/src/sage/combinat/words/finite_word.py @@ -7140,6 +7140,38 @@ def is_christoffel(self): else: return False + def minimal_conjugate(self): + r""" + Return the lexicographically minimal conjugate of this word (see + :wikipedia:`Lexicographically_minimal_string_rotation`). + + EXAMPLES:: + + sage: Word('213').minimal_conjugate() + word: 132 + sage: Word('11').minimal_conjugate() + word: 11 + sage: Word('12112').minimal_conjugate() + word: 11212 + sage: Word('211').minimal_conjugate() + word: 112 + sage: Word('211211211').minimal_conjugate() + word: 112112112 + + TESTS:: + + sage: Word().minimal_conjugate() + word: + """ + if not self: + return self + p = self.primitive() + q = self.length() // p.length() + end = 0 + for factor in (p ** 2).lyndon_factorization(): + end += factor.length() + if end >= p.length(): + return factor ** q ####################################################################### diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 1879e4e1833..556d96d93d7 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -89,14 +89,15 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from itertools import chain +from collections import Counter +from itertools import chain, count from sage.misc.callable_dict import CallableDict from sage.structure.sage_object import SageObject from sage.misc.cachefunc import cached_method from sage.misc.lazy_list import lazy_list from sage.sets.set import Set -from sage.rings.all import QQ +from sage.rings.all import ZZ, QQ from sage.rings.infinity import Infinity from sage.rings.integer_ring import IntegerRing from sage.rings.integer import Integer @@ -3184,3 +3185,588 @@ def abelian_rotation_subspace(self): basis.extend((factor[0])(M).right_kernel().basis()) return M._column_ambient_module().change_ring(QQ).subspace(basis) + + def is_injective(self): + """ + Return whether this morphism is injective. + + ALGORITHM: + + Uses a version of :wikipedia:`Sardinas–Patterson_algorithm`. + Time complexity is on average quadratic with regards to the size of the + morphism. + + EXAMPLES:: + + sage: WordMorphism('a->0,b->10,c->110,d->111').is_injective() + True + sage: WordMorphism('a->00,b->01,c->012,d->20001').is_injective() + False + """ + def check(u, v): + if u.is_prefix(v): + tail = v[u.length():] + if tail not in tails: + tails.add(tail) + todo.append(tail) + + images = self._morph.values() + if any(not x for x in images): + return False + tails = set() + todo = [] + + for i, u in enumerate(images): + for j, v in enumerate(images): + if i == j: + continue + check(u, v) + + while todo: + u = todo.pop() + for v in images: + if u == v: + return False + check(u, v) + check(v, u) + + return True + + def is_pushy(self, w=None): + r""" + Return whether the language `\{m^n(w) | n \ge 0\}` is pushy, + where `m` is this morphism and `w` is a word inputted as a parameter. + + Requires this morphism to be an endomorphism. + + A language created by iterating a morphism is pushy if its words + contain an infinite number of factors containing no growing letters. It + turns out that this is equivalent to having at least one infinite + repetition containing no growing letters. + + See :meth:`infinite_repetitions` and :meth:`is_growing`. + + INPUT: + + - ``w`` -- finite iterable representing a word used to start the + language, default is ``self.domain().alphabet()`` + + EXAMPLES:: + + sage: WordMorphism('a->abca,b->bc,c->').is_pushy() + False + sage: WordMorphism('a->abc,b->,c->bcb').is_pushy() + True + """ + return bool(self.infinite_repetitions_bounded(w)) + + def is_unboundedly_repetitive(self, w=None): + r""" + Return whether the language `\{m^n(w) | n \ge 0\}` is unboundedly repetitive, + where `m` is this morphism and `w` is a word inputted as a parameter. + + Requires this morphism to be an endomorphism. + + A language created by iterating a morphism is unboundedly repetitive if + it has at least one infinite repetition containing at least one growing + letter. + + See :meth:`infinite_repetitions` and :meth:`is_growing`. + + INPUT: + + - ``w`` -- finite iterable representing a word used to start the + language, default is ``self.domain().alphabet()`` + + EXAMPLES:: + + sage: WordMorphism('a->abca,b->bc,c->').is_unboundedly_repetitive() + True + sage: WordMorphism('a->abc,b->,c->bcb').is_unboundedly_repetitive() + False + """ + return bool(self.infinite_repetitions_growing(w)) + + def is_repetitive(self, w=None): + r""" + Return whether the language `\{m^n(w) | n \ge 0\}` is repetitive, + where `m` is this morphism and `w` is a word inputted as a parameter. + + Requires this morphism to be an endomorphism. + + A language is repetitive if for each positive integer `k` there exists + a word `u` such that `u^k` is a factor of some word of the language. + + It turns that for languages created by iterating a morphism this is + equivalent to having at least one infinite repetition (this property is + also known as strong repetitiveness). + + See :meth:`infinite_repetitions`. + + INPUT: + + - ``w`` -- finite iterable representing a word used to start the + language, default is ``self.domain().alphabet()`` + + EXAMPLES: + + This method can be used to check whether a purely morphic word is NOT + k-power free for all positive integers k. For example, the language + containing just the Thue-Morse word and its prefixes is not repetitive, + since the Thue-Morse word is cube-free:: + + sage: WordMorphism('a->ab,b->ba').is_repetitive('a') + False + + Similarly, the Hanoi word is square-free:: + + sage: WordMorphism('a->aC,A->ac,b->cB,B->cb,c->bA,C->ba').is_repetitive('a') + False + + However, this method solves a more general problem, as it can be called + on any morphism `m` and with any word `w`:: + + sage: WordMorphism('a->c,b->cda,c->a,d->abc').is_repetitive('bd') + True + """ + return self.is_pushy(w) or self.is_unboundedly_repetitive(w) + + def infinite_repetitions(self, w=None): + r""" + Return the set of primitive infinite repetitions (up to conjugacy) + from the language `\{m^n(w) | n \ge 0\}`, where `m` is this morphism + and `w` is a word inputted as a parameter. + + Requires this morphism to be an endomorphism. + + A non-empty word `v` is an infinite repetition (also known as an + infinite periodic factor) of a language if for each positive integer + `k` the word `v^k` is a factor of some word from the language. + + If `v` is an infinite repetition, then all its powers are also infinite + repetitions, therefore this method returns only the primitive ones. It + turns out that a language created by iterating a morphism has a finite + number of primitive infinite repetitions. + + Similarly, if `v` is an infinite repetition, then all its conjugates + are also infinite repetitions, therefore this method returns only the + lexicographically minimal one from each conjugacy class. + + INPUT: + + - ``w`` -- finite iterable representing a word used to start the + language, default is ``self.domain().alphabet()`` + + EXAMPLES:: + + sage: m = WordMorphism('a->aba,b->aba,c->cd,d->e,e->d') + sage: inf_reps = m.infinite_repetitions('ac') + sage: sorted(inf_reps) + [word: aab, word: de] + + Incomplete check that these words are indeed infinite repetitions:: + + sage: SL = m._language_naive(10, Word('ac')) + sage: all(x in SL for x in inf_reps) + True + sage: all(x^2 in SL for x in inf_reps) + True + sage: all(x^3 in SL for x in inf_reps) + True + + Larger example:: + + sage: m = WordMorphism('a->1b5,b->fcg,c->dae,d->432,e->678,f->f,g->g,1->2,2->3,3->4,4->1,5->6,6->7,7->8,8->5') + sage: sorted(m.infinite_repetitions('a')) + [word: 1432f2143f3214f4321f, word: 5678g8567g7856g6785g] + """ + return self.infinite_repetitions_bounded(w) | self.infinite_repetitions_growing(w) + + def infinite_repetitions_bounded(self, w=None): + r""" + Return the set of primitive infinite repetitions (up to conjugacy), + which contain no growing letters, + from the language `\{m^n(w) | n \ge 0\}`, where `m` is this morphism + and `w` is a word inputted as a parameter. + + Requires this morphism to be an endomorphism. + + See :meth:`infinite_repetitions` and :meth:`is_growing`. + + INPUT: + + - ``w`` -- finite iterable representing a word used to start the + language, default is ``self.domain().alphabet()`` + + ALGORITHM: + + The algorithm used is described in detail in [KS2015]_. + + EXAMPLES:: + + sage: m = WordMorphism('a->aba,b->aba,c->cd,d->e,e->d') + sage: sorted(m.infinite_repetitions_bounded()) + [word: de] + + sage: m = WordMorphism('c->d,d->c,e->fc,f->ed') + sage: sorted(m.infinite_repetitions_bounded()) + [word: c, word: d] + """ + def impl(): + U = {} + for x in unbounded: + hx = g.image(x) + for i, y in enumerate(hx): + if y in unbounded: + break + U[x] = y, hx[:i] + for cycle in get_cycles(lambda x: U[x][0], domain=unbounded): + if all(not U[x][1] for x in cycle): + continue + for cycle in g.domain()(cycle).conjugates_iterator(): + u = g.domain()() + for x in cycle: + u = g(u) + u = u + U[x][1] + history = dict({u: 0}) + for i in count(1): + u = g(u) + if u in history: + s = ZZ(history[u]) + t = ZZ(i - history[u]) + break + history[u] = i + q = len(cycle) + l0 = (s / q).ceil() * q + l1 = l0 + (t.lcm(q) / q) + gq = gb ** q + uql = gq(u, l0) + res = g.domain()() + for _ in range(l0 + 1, l1 + 1): + uql = gq(uql) + res = uql + res + yield k(res.primitive()).primitive() + + if w is None: + w = self._morph + f = self.restrict_domain(self.reach(w)) + g, _, k, _ = f.simplify_injective() + unbounded = set(g.growing_letters()) + gb = g.restrict_domain(set(g._morph) - unbounded) + + result = set() + for x in impl(): + result.add(x.minimal_conjugate()) + g, k = g.reversal(), k.reversal() + for x in impl(): + result.add(self.domain()(reversed(x)).minimal_conjugate()) + + return result + + def infinite_repetitions_growing(self, w=None): + r""" + Return the set of primitive infinite repetitions (up to conjugacy), + which contain at least one growing letter, + from the language `\{m^n(w) | n \ge 0\}`, where `m` is this morphism + and `w` is a word inputted as a parameter. + + Requires this morphism to be an endomorphism. + + See :meth:`infinite_repetitions` and :meth:`is_growing`. + + INPUT: + + - ``w`` -- finite iterable representing a word used to start the + language, default is ``self.domain().alphabet()`` + + ALGORITHM: + + The algorithm used is described in detail in [KS2015]_. + + EXAMPLES:: + + sage: m = WordMorphism('a->aba,b->aba,c->cd,d->e,e->d') + sage: sorted(m.infinite_repetitions_growing()) + [word: aab] + + sage: m = WordMorphism('a->bcb,b->ada,c->d,d->c') + sage: sorted(m.infinite_repetitions_growing()) + [word: ad, word: bc] + + sage: m = WordMorphism('b->c,c->bcb') + sage: sorted(m.infinite_repetitions_growing()) + [word: bc] + """ + if w is None: + w = self._morph + f = self.restrict_domain(self.reach(w)) + g, _, k, _ = f.simplify_injective() + unbounded = set(g.growing_letters()) + + result = set() + for periodic_orbit in g.periodic_points(): + q = len(periodic_orbit) + gq = g**q + for periodic_point in periodic_orbit: + # Check if this periodic point is a periodic infinite word. + periodic_point = periodic_point[:1] + letter_cnts = Counter(periodic_point) + for _ in g.domain().alphabet(): + previous_length = periodic_point.length() + periodic_point = gq(periodic_point) + letter_cnts.update(periodic_point[previous_length:]) + if any(letter_cnts[letter] >= 2 for letter in unbounded): + break + else: # nobreak + continue + if letter_cnts[periodic_point[0]] < 2: + continue + v = periodic_point[:periodic_point.find(periodic_point[0], start=1)] + vq = gq(v) + m = 0 + while vq[m * v.length() : (m + 1) * v.length()] == v: + m += 1 + if m >= 2 and m * v.length() == vq.length(): + result.add(k(v).primitive().minimal_conjugate()) + + return result + + def reach(self, w): + r""" + Return the set of letters which occur in words of + `\{m^n(w) | n \ge 0\}`, where `m` is this morphism and `w` is a word + (finite iterable is enough) inputted as a parameter. + + Requires this morphism to be an endomorphism. + + EXAMPLES:: + + sage: sorted(WordMorphism('a->ac,b->ce,c->bd,d->d,e->').reach('c')) + ['b', 'c', 'd', 'e'] + """ + if not self.is_endomorphism(): + raise TypeError(f'self ({self}) is not an endomorphism') + + visited = set(w) + todo = list(visited) + while todo: + a = todo.pop() + for b in self.image(a): + if b not in visited: + visited.add(b) + todo.append(b) + return visited + + def simplify(self, Z=None): + r""" + Return morphisms `h` and `k` such that this morphism is simplifiable + with respect to `h` and `k`. + + If this morphism is non-injective, this function always succeeds, but + can fail (raise ``ValueError``) if it is injective, even it if is + simplifiable. + + Let `f: X^* \rightarrow Y^*` be a morphism. Then `f` is simplifiable + with respect to morphisms `h: X^* \rightarrow Z^*` and + `k: Z^* \rightarrow Y^*`, if `f = k \circ h` and `|Z| < |X|`. If also + `Y \subseteq X`, then morphism `g: Z^* \rightarrow Z^* = h \circ k` is + a simplification of `f` (with respect to `h` and `k`). + + Therefore a morphism is simplifiable if it contains "more letters than + is needed". Simplification preserves some properties of the original + morphism (e.g. repetitiveness). + + Time complexity is on average quadratic with regards to the size of the + morphism. + + For more information see Section 3 in [KO2000]_. + + INPUT: + + - ``Z`` -- iterable, whose elements are used as an alphabet for the + simplification, default is ``self.domain().alphabet()`` + + EXAMPLES: + + Example of a simplifiable morphism:: + + sage: f = WordMorphism('a->bca,b->bcaa,c->bcaaa') + sage: h, k = f.simplify('xy') + sage: h + WordMorphism: a->xy, b->xyy, c->xyyy + sage: k + WordMorphism: x->bc, y->a + sage: k * h == f + True + sage: g = h * k; g + WordMorphism: x->xyyxyyy, y->xy + + Example of a non-simplifiable morphism:: + + sage: WordMorphism('a->aa').simplify() + Traceback (most recent call last): + ... + ValueError: failed to simplify a->aa + + Example of a simplifiable morphism that the function fails on:: + + sage: f = WordMorphism('a->abcc,b->abcd,c->abdc,d->abdd') + sage: f.simplify('xyz') + Traceback (most recent call last): + ... + ValueError: failed to simplify a->abcc, b->abcd, c->abdc, d->abdd + + Proof that the above morphism is simplifiable:: + + sage: k = WordMorphism('x->ab,y->c,z->d') + sage: h = WordMorphism('a->xyy,b->xyz,c->xzy,d->xzz') + sage: k * h == f + True + sage: g = h * k; g + WordMorphism: x->xyyxyz, y->xzy, z->xzz + + Example of an erasing morphism:: + + sage: f = WordMorphism('a->abc,b->cc,c->') + sage: h, k = f.simplify(); h, k + (WordMorphism: a->a, b->b, c->, WordMorphism: a->abc, b->cc) + sage: k * h == f + True + sage: g = h * k; g + WordMorphism: a->ab, b-> + + Example of a non-endomorphism:: + + sage: f = WordMorphism('a->xx,b->xy,c->yx,d->yy') + sage: h, k = f.simplify(ZZ); h, k + (WordMorphism: a->00, b->01, c->10, d->11, WordMorphism: 0->x, 1->y) + sage: k * h == f + True + sage: len(k.domain().alphabet()) < len(f.domain().alphabet()) + True + """ + X = self.domain().alphabet() + Y = self.codomain().alphabet() + f = self._morph + + if self.is_erasing(): # Trivial case #1. + k = {letter: image for letter, image in f.items() if image} + h = {letter: [letter] if image else [] for letter, image in f.items()} + elif len(Y) < len(X): # Trivial case #2. + k = {x: [y] for x, y in zip(X, Y)} + k_inverse = {y: x for y, x in zip(Y, X)} + h = {x: [k_inverse[y] for y in image] for x, image in f.items()} + else: # Non-trivial case. + k = dict(f) + to_do = set(k) + to_remove = [] + while to_do: + # min() and remove() instead of pop() to have deterministic output. + letter1 = min(to_do) + to_do.remove(letter1) + image1 = k[letter1] + for letter2, image2 in k.items(): + if letter1 == letter2: + continue + if image1 == image2: + to_remove.append(letter2) + to_do.discard(letter2) + elif image1.is_prefix(image2): + k[letter2] = image2[image1.length():] + to_do.add(letter2) + elif image1.is_suffix(image2): + k[letter2] = image2[:-image1.length()] + to_do.add(letter2) + elif image2.is_prefix(image1): + k[letter1] = image1[image2.length():] + to_do.add(letter1) + break + elif image2.is_suffix(image1): + k[letter1] = image1[:-image2.length()] + to_do.add(letter1) + break + for letter in to_remove: + del k[letter] + to_remove = [] + + if len(k) == len(f): + raise ValueError(f'failed to simplify {self}') + + h = {} + for letter1, image1 in f.items(): + image3 = [] + while image1: + for letter2, image2 in k.items(): + if image2.is_prefix(image1): + image1 = image1[image2.length():] + image3.append(letter2) + break + h[letter1] = image3 + + k = type(self)(k, codomain=self.codomain()) + h = type(self)(h, domain=self.domain(), codomain=k.domain()) + + if Z is not None: # Custom alphabet. + old_Z_star = k.domain() + old_Z = old_Z_star.alphabet() + Z = [z for z, _ in zip(Z, old_Z)] + if len(Z) < len(old_Z): + raise ValueError(f'Z should have length at least {len(old_Z)}, is {len(Z)}') + Z_star = FiniteWords(Z) + h_new = {old: [new] for old, new in zip(old_Z, Z)} + k_new = {new: [old] for new, old in zip(Z, old_Z)} + h_new = type(self)(h_new, domain=old_Z_star, codomain=Z_star) + k_new = type(self)(k_new, domain=Z_star, codomain=old_Z_star) + h = h_new * h + k = k * k_new + + return h, k + + def simplify_injective(self): + r""" + Return a quadruplet `(g, h, k, i)`, where `g` is an injective + simplification of this morphism with respect to `h`, `k` and `i`. + + Requires this morphism to be an endomorphism. + + Basically calls :meth:`simplify` until it throws an exception, which + means the input was injective. If already the first call raises an + exception, instead of reraising it a quadruplet `(g, h, k, i)` is still + returned, where `g` and `h` are equal to this morphism, `k` is the + identity morphism and `i` is 0. + + Let `f: X^* \rightarrow Y^*` be a morphism and `Y \subseteq X`. Then + `g: Z^* \rightarrow Z^*` is an injective simplification of `f` with + respect to morphisms `h: X^* \rightarrow Z^*` and + `k: Z^* \rightarrow Y^*` and a positive integer `i`, if `g` is + injective and `|Z| < |X|` and `g^i = h \circ k` and `f^i = k \circ h`. + + For more information see Section 4 in [KO2000]_. + + EXAMPLES:: + + sage: f = WordMorphism('a->abc,b->a,c->bc') + sage: g, h, k, i = f.simplify_injective(); g, h, k, i + (WordMorphism: a->aa, WordMorphism: a->aa, b->a, c->a, WordMorphism: a->abc, 2) + sage: g.is_injective() + True + sage: g ** i == h * k + True + sage: f ** i == k * h + True + """ + if not self.is_endomorphism(): + raise TypeError(f'self ({self}) is not an endomorphism') + + try: + h, k = self.simplify() + except ValueError: + return self, self, self.domain().identity_morphism(), 0 + g = h * k + + for i in count(start=1): + try: + h_new, k_new = g.simplify() + g, h, k = h_new * k_new, h_new * h, k * k_new + except ValueError: + return g, h, k, i From 6719d9e5d59addccd093d09d04a0f553520f580a Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Tue, 6 Apr 2021 15:54:11 +0200 Subject: [PATCH 034/280] Trac 31616: make quaternion_algebra() method of Brandt modules directly call QuaternionAlgebra() --- src/sage/modular/quatalg/brandt.py | 35 ++++++--------------- src/sage/schemes/elliptic_curves/heegner.py | 29 ++++++++++------- 2 files changed, 26 insertions(+), 38 deletions(-) diff --git a/src/sage/modular/quatalg/brandt.py b/src/sage/modular/quatalg/brandt.py index 568f28e6649..f88d44a4654 100644 --- a/src/sage/modular/quatalg/brandt.py +++ b/src/sage/modular/quatalg/brandt.py @@ -356,7 +356,7 @@ def maximal_order(A): sage: A = BrandtModule(17).quaternion_algebra() sage: sage.modular.quatalg.brandt.maximal_order(A) - Order of Quaternion Algebra (-17, -3) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, -1/3*j - 1/3*k, k) + Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/2*i, 1/2*j - 1/2*k, -1/3*i + 1/3*k, -k) sage: A = QuaternionAlgebra(17,names='i,j,k') sage: A.maximal_order() @@ -382,9 +382,9 @@ def basis_for_left_ideal(R, gens): sage: B = BrandtModule(17); A = B.quaternion_algebra(); i,j,k = A.gens() sage: sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [i+j,i-j,2*k,A(3)]) - [1/2 + 1/6*j + 2/3*k, 1/2*i + 1/2*k, 1/3*j + 1/3*k, k] + [1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k] sage: sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [3*(i+j),3*(i-j),6*k,A(3)]) - [3/2 + 1/2*j + 2*k, 3/2*i + 3/2*k, j + k, 3*k] + [3/2 + 1/2*i + k, i + 2*k, 3/2*j + 3/2*k, 3*k] """ return basis_for_quaternion_lattice([b * g for b in R.basis() for g in gens]) @@ -409,14 +409,14 @@ def right_order(R, basis): sage: B = BrandtModule(17); basis = sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), B.maximal_order().basis()) sage: sage.modular.quatalg.brandt.right_order(B.maximal_order(), basis) - Order of Quaternion Algebra (-17, -3) with base ring Rational Field with basis (1/2 + 1/6*j + 2/3*k, 1/2*i + 1/2*k, 1/3*j + 1/3*k, k) + Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k) sage: basis - [1/2 + 1/6*j + 2/3*k, 1/2*i + 1/2*k, 1/3*j + 1/3*k, k] + [1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k] sage: B = BrandtModule(17); A = B.quaternion_algebra(); i,j,k = A.gens() sage: basis = sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [i*j-j]) sage: sage.modular.quatalg.brandt.right_order(B.maximal_order(), basis) - Order of Quaternion Algebra (-17, -3) with base ring Rational Field with basis (1/2 + 1/2*i + 1/2*j + 17/2*k, i, j + 8*k, 9*k) + Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/6*i + 1/3*k, 1/3*i + 2/3*k, 1/2*j + 1/2*k, k) """ # Compute matrix of multiplication by each element of the basis. B = R.basis() @@ -790,26 +790,9 @@ def quaternion_algebra(self): sage: BrandtModule(5).quaternion_algebra() Quaternion Algebra (-2, -5) with base ring Rational Field sage: BrandtModule(17).quaternion_algebra() - Quaternion Algebra (-17, -3) with base ring Rational Field + Quaternion Algebra (-3, -17) with base ring Rational Field """ - p = self.N() - assert p.is_prime(), "we have only implemented the prime case" - if p == 2: - QA = -1 - QB = -1 - elif p % 4 == 3: - QA = -1 - QB = -p - elif p % 8 == 5: - QA = -2 - QB = -p - elif p % 8 == 1: - q = 3 - while q % 4 != 3 or kronecker(p, q) != -1: - q = next_prime(q) - QA = -p - QB = -q - return QuaternionAlgebra(QQ, QA, QB) + return QuaternionAlgebra(self.N()) @cached_method def maximal_order(self): @@ -819,7 +802,7 @@ def maximal_order(self): EXAMPLES:: sage: BrandtModule(17).maximal_order() - Order of Quaternion Algebra (-17, -3) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, -1/3*j - 1/3*k, k) + Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/2*i, 1/2*j - 1/2*k, -1/3*i + 1/3*k, -k) sage: BrandtModule(17).maximal_order() is BrandtModule(17).maximal_order() True """ diff --git a/src/sage/schemes/elliptic_curves/heegner.py b/src/sage/schemes/elliptic_curves/heegner.py index e0f6f9c6b91..97bd761d067 100644 --- a/src/sage/schemes/elliptic_curves/heegner.py +++ b/src/sage/schemes/elliptic_curves/heegner.py @@ -5226,13 +5226,18 @@ def kolyvagin_cyclic_subideals(self, I, p, alpha_quaternion): sage: N = 37; D = -7; ell = 17; c=5 sage: H = heegner_points(N).reduce_mod(ell) - sage: B = H.brandt_module(); I = B.right_ideals()[32] + sage: I = H.brandt_module().right_ideals()[49] sage: f = H.optimal_embeddings(D, 1, I.left_order())[1] sage: g = H.kolyvagin_generators(f.domain().number_field(), c) sage: alpha_quaternion = f(g[0]); alpha_quaternion - 1 - 5/128*i - 77/192*j + 137/384*k + 1 - 77/192*i - 5/128*j - 137/384*k sage: H.kolyvagin_cyclic_subideals(I, 5, alpha_quaternion) - [(Fractional ideal (2 + 874/3*j + 128356/3*k, 2*i + 932/3*j + 198806/3*k, 2560/3*j + 33280/3*k, 94720*k), 0), (Fractional ideal (2 + 462*j + 82892*k, 2*i + 932/3*j + 141974/3*k, 2560/3*j + 33280/3*k, 94720*k), 1), (Fractional ideal (2 + 2410/3*j + 261988/3*k, 2*i + 652*j + 89650*k, 2560/3*j + 33280/3*k, 94720*k), 2), (Fractional ideal (2 + 2410/3*j + 91492/3*k, 2*i + 1444/3*j + 148630/3*k, 2560/3*j + 33280/3*k, 94720*k), 3), (Fractional ideal (2 + 874/3*j + 71524/3*k, 2*i + 2468/3*j + 275606/3*k, 2560/3*j + 33280/3*k, 94720*k), 4), (Fractional ideal (2 + 462*j + 63948*k, 2*i + 2468/3*j + 218774/3*k, 2560/3*j + 33280/3*k, 94720*k), 5)] + [(Fractional ideal (2 + 2/3*i + 364*j + 231928/3*k, 4/3*i + 946*j + 69338/3*k, 1280*j + 49920*k, 94720*k), 0), + (Fractional ideal (2 + 2/3*i + 108*j + 31480/3*k, 4/3*i + 434*j + 123098/3*k, 1280*j + 49920*k, 94720*k), 1), + (Fractional ideal (2 + 2/3*i + 876*j + 7672/3*k, 4/3*i + 434*j + 236762/3*k, 1280*j + 49920*k, 94720*k), 2), + (Fractional ideal (2 + 2/3*i + 364*j + 61432/3*k, 4/3*i + 178*j + 206810/3*k, 1280*j + 49920*k, 94720*k), 3), + (Fractional ideal (2 + 2/3*i + 876*j + 178168/3*k, 4/3*i + 1202*j + 99290/3*k, 1280*j + 49920*k, 94720*k), 4), + (Fractional ideal (2 + 2/3*i + 1132*j + 208120/3*k, 4/3*i + 946*j + 183002/3*k, 1280*j + 49920*k, 94720*k), 5)] """ X = I.cyclic_right_subideals(p, alpha_quaternion) return [(J, i) for i, J in enumerate(X)] @@ -5257,7 +5262,7 @@ def kolyvagin_generator(self, K, p): sage: N = 37; D = -7; ell = 17; p=5 sage: H = heegner_points(N).reduce_mod(ell) - sage: B = H.brandt_module(); I = B.right_ideals()[32] + sage: I = H.brandt_module().right_ideals()[49] sage: f = H.optimal_embeddings(D, 1, I.left_order())[0] sage: H.kolyvagin_generator(f.domain().number_field(), 5) a + 1 @@ -5308,7 +5313,7 @@ def kolyvagin_generators(self, K, c): sage: N = 37; D = -7; ell = 17; p=5 sage: H = heegner_points(N).reduce_mod(ell) - sage: B = H.brandt_module(); I = B.right_ideals()[32] + sage: I = H.brandt_module().right_ideals()[49] sage: f = H.optimal_embeddings(D, 1, I.left_order())[0] sage: H.kolyvagin_generators(f.domain().number_field(), 5*17) [-34*a + 1, 35*a + 106] @@ -5386,17 +5391,17 @@ def kolyvagin_sigma_operator(self, D, c, r, bound=None): sage: N = 37; D = -7; ell = 17; c = 41; q = 3 sage: H = heegner_points(N).reduce_mod(ell) sage: H.heegner_divisor(D,1).element().nonzero_positions() - [32, 51] - sage: k32 = H.kolyvagin_sigma_operator(D, c, 32); k32 - (17, 12, 33, 33, 49, 108, 3, 0, 0, 33, 37, 49, 33, 33, 59, 54, 21, 30, 0, 0, 29, 12, 41, 38, 33, 15, 0, 0, 4, 0, 7, 0, 0, 0, 0, 34, 26, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + [49, 51] + sage: k49 = H.kolyvagin_sigma_operator(D, c, 49); k49 + (79, 32, 31, 11, 53, 37, 1, 23, 15, 7, 0, 0, 0, 64, 32, 34, 53, 0, 27, 27, 0, 0, 0, 26, 0, 0, 18, 0, 22, 0, 53, 19, 27, 10, 0, 0, 0, 30, 35, 38, 0, 0, 0, 53, 0, 0, 4, 0, 0, 0, 0, 0) sage: k51 = H.kolyvagin_sigma_operator(D, c, 51); k51 - (5, 13, 0, 0, 14, 0, 21, 0, 0, 0, 29, 0, 0, 45, 0, 6, 0, 40, 0, 61, 0, 0, 40, 32, 0, 9, 0, 0, 0, 0, 17, 0, 0, 0, 77, 40, 2, 10, 18, 0, 0, 61, 19, 45, 26, 80, 61, 35, 35, 19, 1, 0) + (20, 12, 57, 0, 0, 0, 0, 52, 23, 15, 0, 7, 0, 0, 19, 4, 0, 73, 11, 0, 104, 31, 0, 38, 31, 0, 0, 31, 5, 47, 0, 27, 35, 0, 57, 32, 24, 10, 0, 8, 0, 31, 41, 0, 0, 0, 16, 0, 0, 0, 0, 0) sage: V = H.modp_dual_elliptic_curve_factor(EllipticCurve('37a'), q, 5); V Vector space of degree 52 and dimension 2 over Ring of integers modulo 3 Basis matrix: 2 x 52 dense matrix over Ring of integers modulo 3 - sage: [b.dot_product(k32.element().change_ring(GF(q))) for b in V.basis()] - [2, 2] + sage: [b.dot_product(k49.element().change_ring(GF(q))) for b in V.basis()] + [1, 1] sage: [b.dot_product(k51.element().change_ring(GF(q))) for b in V.basis()] [1, 1] @@ -5573,7 +5578,7 @@ def kolyvagin_point_on_curve(self, D, c, E, p, bound=10): sage: N = 37; D = -7; ell = 17; c = 41; p = 3 sage: H = heegner_points(N).reduce_mod(ell) sage: H.kolyvagin_point_on_curve(D, c, EllipticCurve('37a'), p) - [2, 2] + [1, 1] """ k = self.rational_kolyvagin_divisor(D, c) V = self.modp_dual_elliptic_curve_factor(E, p, bound) From 6dcef989d9bf6549bb7e3015d087db88689afc78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Thu, 8 Apr 2021 11:59:06 +0200 Subject: [PATCH 035/280] 18119: moved itertools imports inside methods --- src/sage/combinat/words/morphism.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 556d96d93d7..62db520c71f 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -89,8 +89,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from collections import Counter -from itertools import chain, count +from itertools import chain from sage.misc.callable_dict import CallableDict from sage.structure.sage_object import SageObject @@ -3412,6 +3411,8 @@ def infinite_repetitions_bounded(self, w=None): sage: sorted(m.infinite_repetitions_bounded()) [word: c, word: d] """ + from itertools import count + def impl(): U = {} for x in unbounded: @@ -3497,6 +3498,8 @@ def infinite_repetitions_growing(self, w=None): sage: sorted(m.infinite_repetitions_growing()) [word: bc] """ + from collections import Counter + if w is None: w = self._morph f = self.restrict_domain(self.reach(w)) @@ -3764,6 +3767,7 @@ def simplify_injective(self): return self, self, self.domain().identity_morphism(), 0 g = h * k + from itertools import count for i in count(start=1): try: h_new, k_new = g.simplify() From ba0845df00dc7c76582403b479ca1b02549a1251 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Thu, 8 Apr 2021 20:30:44 +0200 Subject: [PATCH 036/280] 18119: Refactor inf_reps_bounded --- src/sage/combinat/words/morphism.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 62db520c71f..d757adc3093 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3410,25 +3410,30 @@ def infinite_repetitions_bounded(self, w=None): sage: m = WordMorphism('c->d,d->c,e->fc,f->ed') sage: sorted(m.infinite_repetitions_bounded()) [word: c, word: d] + + TESTS:: + + sage: m = WordMorphism('a->Cab,b->1c1,c->E2bd5,d->BbaA,5->6,6->7,7->8,8->9,9->5,1->2,2->1,A->B,B->C,C->D,D->E,E->') + sage: sorted(m.infinite_repetitions_bounded()) + [word: 1, word: 1519181716, word: 2, word: 2529282726] """ from itertools import count def impl(): U = {} for x in unbounded: - hx = g.image(x) - for i, y in enumerate(hx): + gx = g.image(x) + for i, y in enumerate(reversed(gx)): if y in unbounded: break - U[x] = y, hx[:i] + U[x] = y, gx[gx.length() - i:] for cycle in get_cycles(lambda x: U[x][0], domain=unbounded): if all(not U[x][1] for x in cycle): continue for cycle in g.domain()(cycle).conjugates_iterator(): u = g.domain()() for x in cycle: - u = g(u) - u = u + U[x][1] + u = U[x][1] + g(u) history = dict({u: 0}) for i in count(1): u = g(u) @@ -3438,14 +3443,12 @@ def impl(): break history[u] = i q = len(cycle) - l0 = (s / q).ceil() * q - l1 = l0 + (t.lcm(q) / q) gq = gb ** q - uql = gq(u, l0) + uq = gq(u, (s / q).ceil()) res = g.domain()() - for _ in range(l0 + 1, l1 + 1): - uql = gq(uql) - res = uql + res + for _ in range(t.lcm(q) / q): + uq = gq(uq) + res += uq yield k(res.primitive()).primitive() if w is None: @@ -3456,10 +3459,10 @@ def impl(): gb = g.restrict_domain(set(g._morph) - unbounded) result = set() - for x in impl(): + for x in impl(): # UR. result.add(x.minimal_conjugate()) g, k = g.reversal(), k.reversal() - for x in impl(): + for x in impl(): # UL. result.add(self.domain()(reversed(x)).minimal_conjugate()) return result From f564931ed03e8a1d93dab328f256f30353ded1f6 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Fri, 9 Apr 2021 09:45:24 +0100 Subject: [PATCH 037/280] updated eclib package version to 20210408 and checksums --- build/pkgs/eclib/checksums.ini | 6 +++--- build/pkgs/eclib/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 7f3e3b9233c..de58a9fbb75 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,5 +1,5 @@ tarball=eclib-VERSION.tar.bz2 -sha1=500c3efcf0c8efbb065bbdb8f05cf8a91b8d5c24 -md5=9da2bba60ec2920b4524b355aff9333a -cksum=1435512890 +sha1=d1c9add8c95ba502440e7d197da0c1e3329adc7d +md5=47e2c9b59c1d322709a5d99e438d09cd +cksum=2380455615 upstream_url=https://github.com/JohnCremona/eclib/archive/eclib-VERSION.tar.bz2 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index d52e9bed28f..c12f44c8f0c 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -v20210317 +20210408 From 07c5a20f8cfe7323fc5afb4c9a7a77472ac4d0b8 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Fri, 9 Apr 2021 10:45:19 +0100 Subject: [PATCH 038/280] correct a docstring after eclib upgrade --- src/sage/schemes/elliptic_curves/ell_rational_field.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 3d5d214109f..874589b7ff3 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -2525,8 +2525,7 @@ def regulator(self, proof=None, precision=53, **kwds): return reg def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): - """ - Given a list of rational points on E, compute the saturation in + """Given a list of rational points on E, compute the saturation in E(Q) of the subgroup they generate. INPUT: @@ -2552,7 +2551,7 @@ def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): ``min_prime`` both to `p`. One situation where this is useful is after mapping saturated points from another elliptic curve by a `p`-isogeny, since the images may not - be `p`-saturated but with be saturated at all other primes. + be `p`-saturated but will be saturated at all other primes. OUTPUT: @@ -2588,7 +2587,7 @@ def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): The computed index of saturation may be large, in which case saturation may take a long time. For example, the rank 4 curve ``EllipticCurve([0,1,1,-9872,374262])`` has a - saturation index bound of 86682 and takes around 15 minutes + saturation index bound of 11816 and takes around 40 seconds to prove saturation. From 9b1dedb02560d1b3fce444e5f88ac14f42786ce9 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Fri, 9 Apr 2021 12:23:21 +0100 Subject: [PATCH 039/280] correct typo and update eclib's m4 script --- build/pkgs/eclib/spkg-configure.m4 | 2 +- src/sage/libs/eclib/interface.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index 748e6b8bf3e..8da0723747d 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,7 +1,7 @@ SAGE_SPKG_CONFIGURE([eclib], [ SAGE_SPKG_DEPCHECK([ntl pari flint], [ dnl Trac #31443: use existing eclib only if the version reported by pkg-config is correct - m4_pushdef([SAGE_ECLIB_VER],["20210318"]) + m4_pushdef([SAGE_ECLIB_VER],["20210408"]) PKG_CHECK_MODULES([ECLIB], [eclib = SAGE_ECLIB_VER], [ AC_CACHE_CHECK([for mwrank version == SAGE_ECLIB_VER], [ac_cv_path_MWRANK], [ AC_PATH_PROGS_FEATURE_CHECK([MWRANK], [mwrank], [ diff --git a/src/sage/libs/eclib/interface.py b/src/sage/libs/eclib/interface.py index 493b5f13470..a163cc31093 100644 --- a/src/sage/libs/eclib/interface.py +++ b/src/sage/libs/eclib/interface.py @@ -1102,7 +1102,7 @@ def saturate(self, max_prime=-1, min_prime=2): In versions up to v20190909, ``eclib`` used floating point methods based on elliptic logarithms to divide points, and - did not compute the precision necessary, which could casue + did not compute the precision necessary, which could cause failures. Since v20210310, ``eclib`` uses exact method based on division polynomials, which should mean that such failures does not happen. From 49e0baae95e87d843473b33245f81267e583c2ad Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Fri, 9 Apr 2021 21:36:11 +0200 Subject: [PATCH 040/280] 18119: Refactor inf_reps_bounded (2) --- src/sage/combinat/words/morphism.py | 36 +++++++++++------------------ 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index d757adc3093..73aecfbc705 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -96,7 +96,7 @@ from sage.misc.cachefunc import cached_method from sage.misc.lazy_list import lazy_list from sage.sets.set import Set -from sage.rings.all import ZZ, QQ +from sage.rings.all import QQ from sage.rings.infinity import Infinity from sage.rings.integer_ring import IntegerRing from sage.rings.integer import Integer @@ -3417,39 +3417,29 @@ def infinite_repetitions_bounded(self, w=None): sage: sorted(m.infinite_repetitions_bounded()) [word: 1, word: 1519181716, word: 2, word: 2529282726] """ - from itertools import count - def impl(): U = {} for x in unbounded: - gx = g.image(x) - for i, y in enumerate(reversed(gx)): + xg = g.image(x) + for i, y in enumerate(reversed(xg)): if y in unbounded: break - U[x] = y, gx[gx.length() - i:] + U[x] = y, xg[xg.length() - i:] for cycle in get_cycles(lambda x: U[x][0], domain=unbounded): if all(not U[x][1] for x in cycle): continue + gq = gb ** len(cycle) for cycle in g.domain()(cycle).conjugates_iterator(): u = g.domain()() for x in cycle: - u = U[x][1] + g(u) - history = dict({u: 0}) - for i in count(1): - u = g(u) - if u in history: - s = ZZ(history[u]) - t = ZZ(i - history[u]) - break - history[u] = i - q = len(cycle) - gq = gb ** q - uq = gq(u, (s / q).ceil()) - res = g.domain()() - for _ in range(t.lcm(q) / q): - uq = gq(uq) - res += uq - yield k(res.primitive()).primitive() + u = U[x][1] + gb(u) + inf_rep = g.domain()() + history = set() + while u not in history: + history.add(u) + inf_rep += u + u = gq(u) + yield k(inf_rep.primitive()).primitive() if w is None: w = self._morph From 0e6e552495e1b53d2e95ee83de9f76a803bef390 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Fri, 9 Apr 2021 21:37:35 +0200 Subject: [PATCH 041/280] 18119: Refactor inf_reps_growing --- src/sage/combinat/words/morphism.py | 34 +++++++++++++++++------------ 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 73aecfbc705..5951cff3e95 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3490,9 +3490,11 @@ def infinite_repetitions_growing(self, w=None): sage: m = WordMorphism('b->c,c->bcb') sage: sorted(m.infinite_repetitions_growing()) [word: bc] - """ - from collections import Counter + sage: m = WordMorphism('a->abc,b->dab,c->abc,d->dab') + sage: sorted(m.infinite_repetitions_growing()) + [word: ababcd] + """ if w is None: w = self._morph f = self.restrict_domain(self.reach(w)) @@ -3501,29 +3503,33 @@ def infinite_repetitions_growing(self, w=None): result = set() for periodic_orbit in g.periodic_points(): - q = len(periodic_orbit) - gq = g**q + gq = g ** len(periodic_orbit) for periodic_point in periodic_orbit: # Check if this periodic point is a periodic infinite word. periodic_point = periodic_point[:1] - letter_cnts = Counter(periodic_point) + occurred = set(periodic_point) + one_unbounded_twice = False for _ in g.domain().alphabet(): previous_length = periodic_point.length() periodic_point = gq(periodic_point) - letter_cnts.update(periodic_point[previous_length:]) - if any(letter_cnts[letter] >= 2 for letter in unbounded): + for i, letter in enumerate(periodic_point[previous_length:]): + if letter in unbounded: + if letter in occurred: + one_unbounded_twice = True + break + occurred.add(letter) + if one_unbounded_twice: break - else: # nobreak - continue - if letter_cnts[periodic_point[0]] < 2: - continue - v = periodic_point[:periodic_point.find(periodic_point[0], start=1)] + if not one_unbounded_twice or letter != periodic_point[0]: + break + v = periodic_point[:previous_length + i] vq = gq(v) m = 0 while vq[m * v.length() : (m + 1) * v.length()] == v: m += 1 - if m >= 2 and m * v.length() == vq.length(): - result.add(k(v).primitive().minimal_conjugate()) + if m * v.length() != vq.length(): + break + result.add(k(v).primitive().minimal_conjugate()) return result From 3dca4e6c927053770c5e938f5a59be9047602cde Mon Sep 17 00:00:00 2001 From: John Cremona Date: Thu, 15 Apr 2021 15:20:07 +0100 Subject: [PATCH 042/280] #31443: undated pkg info for 20210415 --- build/pkgs/eclib/checksums.ini | 6 +++--- build/pkgs/eclib/package-version.txt | 2 +- build/pkgs/eclib/spkg-configure.m4 | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 1997cd68c7d..5d12b8a60e0 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,5 +1,5 @@ tarball=eclib-VERSION.tar.bz2 -sha1=d1c9add8c95ba502440e7d197da0c1e3329adc7d -md5=47e2c9b59c1d322709a5d99e438d09cd -cksum=2380455615 +sha1=2f0ea52fd59352fa0dec8cabf5d513b0f0cf7629 +md5=2a3d4b37ee5f504d46c010e29040d7a4 +cksum=2697309856 upstream_url=https://github.com/JohnCremona/eclib/releases/download/VERSION/eclib-VERSION.tar.bz2 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index c12f44c8f0c..e9d3aa7a596 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -20210408 +20210415 diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index 8da0723747d..edb759d45f5 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,7 +1,7 @@ SAGE_SPKG_CONFIGURE([eclib], [ SAGE_SPKG_DEPCHECK([ntl pari flint], [ dnl Trac #31443: use existing eclib only if the version reported by pkg-config is correct - m4_pushdef([SAGE_ECLIB_VER],["20210408"]) + m4_pushdef([SAGE_ECLIB_VER],["20210415"]) PKG_CHECK_MODULES([ECLIB], [eclib = SAGE_ECLIB_VER], [ AC_CACHE_CHECK([for mwrank version == SAGE_ECLIB_VER], [ac_cv_path_MWRANK], [ AC_PATH_PROGS_FEATURE_CHECK([MWRANK], [mwrank], [ From ab5c0c9bc2d3504693c6fd82805baf71426d39ec Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Thu, 22 Apr 2021 13:32:04 +0200 Subject: [PATCH 043/280] 18119: Refactor is_injective --- src/sage/combinat/words/morphism.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 5951cff3e95..bf02e9be0b4 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3209,18 +3209,18 @@ def check(u, v): tails.add(tail) todo.append(tail) - images = self._morph.values() + images = self.images() if any(not x for x in images): return False tails = set() todo = [] - for i, u in enumerate(images): - for j, v in enumerate(images): - if i == j: - continue - check(u, v) - + for i in range(len(images)): + for j in range(i + 1, len(images)): + if images[i] == images[j]: + return False + check(images[i], images[j]) + check(images[j], images[i]) while todo: u = todo.pop() for v in images: From 7a230ace15c4ee2d3df6e03d1700a13d75678061 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Thu, 22 Apr 2021 13:32:18 +0200 Subject: [PATCH 044/280] 18119: Refactor simplify --- src/sage/combinat/words/morphism.py | 165 +++++++++++++--------------- 1 file changed, 78 insertions(+), 87 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index bf02e9be0b4..c39cc1d23f0 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3561,25 +3561,22 @@ def reach(self, w): def simplify(self, Z=None): r""" - Return morphisms `h` and `k` such that this morphism is simplifiable - with respect to `h` and `k`. + If this morphism is simplifiable, return morphisms `h` and `k` such that + this morphism is simplifiable with respect to `h` and `k`, otherwise + raise ``ValueError``. - If this morphism is non-injective, this function always succeeds, but - can fail (raise ``ValueError``) if it is injective, even it if is - simplifiable. + This method is quite fast if this morphism is non-injective, but very + slow if it is injective. Let `f: X^* \rightarrow Y^*` be a morphism. Then `f` is simplifiable with respect to morphisms `h: X^* \rightarrow Z^*` and `k: Z^* \rightarrow Y^*`, if `f = k \circ h` and `|Z| < |X|`. If also - `Y \subseteq X`, then morphism `g: Z^* \rightarrow Z^* = h \circ k` is - a simplification of `f` (with respect to `h` and `k`). + `Y \subseteq X`, then the morphism `g: Z^* \rightarrow Z^* = h \circ k` + is a simplification of `f` (with respect to `h` and `k`). - Therefore a morphism is simplifiable if it contains "more letters than - is needed". Simplification preserves some properties of the original - morphism (e.g. repetitiveness). - - Time complexity is on average quadratic with regards to the size of the - morphism. + Loosely speaking a morphism is simplifiable if it contains "more letters + than is needed". Non-injectivity implies simplifiability. Simplification + preserves some properties of the original morphism (e.g. repetitiveness). For more information see Section 3 in [KO2000]_. @@ -3590,43 +3587,33 @@ def simplify(self, Z=None): EXAMPLES: - Example of a simplifiable morphism:: + Example of a simplifiable (non-injective) morphism:: - sage: f = WordMorphism('a->bca,b->bcaa,c->bcaaa') - sage: h, k = f.simplify('xy') - sage: h - WordMorphism: a->xy, b->xyy, c->xyyy - sage: k - WordMorphism: x->bc, y->a + sage: f = WordMorphism('a->aca,b->badc,c->acab,d->adc') + sage: h, k = f.simplify('xyz'); h, k + (WordMorphism: a->x, b->zy, c->xz, d->y, WordMorphism: x->aca, y->adc, z->b) sage: k * h == f True sage: g = h * k; g - WordMorphism: x->xyyxyyy, y->xy + WordMorphism: x->xxzx, y->xyxz, z->zy - Example of a non-simplifiable morphism:: - - sage: WordMorphism('a->aa').simplify() - Traceback (most recent call last): - ... - ValueError: failed to simplify a->aa - - Example of a simplifiable morphism that the function fails on:: + Example of a simplifiable (injective) morphism:: sage: f = WordMorphism('a->abcc,b->abcd,c->abdc,d->abdd') - sage: f.simplify('xyz') - Traceback (most recent call last): - ... - ValueError: failed to simplify a->abcc, b->abcd, c->abdc, d->abdd - - Proof that the above morphism is simplifiable:: - - sage: k = WordMorphism('x->ab,y->c,z->d') - sage: h = WordMorphism('a->xyy,b->xyz,c->xzy,d->xzz') + sage: h, k = f.simplify('xyz'); h, k + (WordMorphism: a->xyy, b->xyz, c->xzy, d->xzz, WordMorphism: x->ab, y->c, z->d) sage: k * h == f True sage: g = h * k; g WordMorphism: x->xyyxyz, y->xzy, z->xzz + Example of a non-simplifiable morphism:: + + sage: WordMorphism('a->aa').simplify() + Traceback (most recent call last): + ... + ValueError: self (a->aa) is not simplifiable + Example of an erasing morphism:: sage: f = WordMorphism('a->abc,b->cc,c->') @@ -3637,16 +3624,31 @@ def simplify(self, Z=None): sage: g = h * k; g WordMorphism: a->ab, b-> - Example of a non-endomorphism:: + Example of a morphism, that is not an endomorphism:: sage: f = WordMorphism('a->xx,b->xy,c->yx,d->yy') - sage: h, k = f.simplify(ZZ); h, k + sage: h, k = f.simplify(NN); h, k (WordMorphism: a->00, b->01, c->10, d->11, WordMorphism: 0->x, 1->y) sage: k * h == f True sage: len(k.domain().alphabet()) < len(f.domain().alphabet()) True """ + def try_create_h(f, k): + h = {} + for letter1, image1 in f.items(): + image3 = [] + while image1: + for letter2, image2 in k.items(): + if image2.is_prefix(image1): + image1 = image1[image2.length():] + image3.append(letter2) + break + else: # nobreak + return None + h[letter1] = image3 + return h + X = self.domain().alphabet() Y = self.codomain().alphabet() f = self._morph @@ -3658,11 +3660,11 @@ def simplify(self, Z=None): k = {x: [y] for x, y in zip(X, Y)} k_inverse = {y: x for y, x in zip(Y, X)} h = {x: [k_inverse[y] for y in image] for x, image in f.items()} - else: # Non-trivial case. + elif not self.is_injective(): # Non-trivial but a fast case. k = dict(f) to_do = set(k) - to_remove = [] while to_do: + to_remove = [] # min() and remove() instead of pop() to have deterministic output. letter1 = min(to_do) to_do.remove(letter1) @@ -3676,37 +3678,32 @@ def simplify(self, Z=None): elif image1.is_prefix(image2): k[letter2] = image2[image1.length():] to_do.add(letter2) - elif image1.is_suffix(image2): - k[letter2] = image2[:-image1.length()] - to_do.add(letter2) elif image2.is_prefix(image1): k[letter1] = image1[image2.length():] to_do.add(letter1) break - elif image2.is_suffix(image1): - k[letter1] = image1[:-image2.length()] - to_do.add(letter1) - break for letter in to_remove: del k[letter] - to_remove = [] - - if len(k) == len(f): - raise ValueError(f'failed to simplify {self}') - - h = {} - for letter1, image1 in f.items(): - image3 = [] - while image1: - for letter2, image2 in k.items(): - if image2.is_prefix(image1): - image1 = image1[image2.length():] - image3.append(letter2) - break - h[letter1] = image3 + h = try_create_h(f, k) + else: # Non-trivial and a slow case. + factors = set() + for image in f.values(): + factors.update(x.primitive() for x in image.factor_iterator()) + factors.remove(self.codomain()()) + factors = sorted(factors) # For deterministic output. + from itertools import combinations + for comb in combinations(factors, len(X) - 1): + if any(x.is_proper_prefix(y) for x in comb for y in comb): + continue + k = {x: image for x, image in zip(X, comb)} + h = try_create_h(f, k) + if h: + break + else: # nobreak + raise ValueError(f'self ({self}) is not simplifiable') - k = type(self)(k, codomain=self.codomain()) - h = type(self)(h, domain=self.domain(), codomain=k.domain()) + k = WordMorphism(k, codomain=self.codomain()) + h = WordMorphism(h, domain=self.domain(), codomain=k.domain()) if Z is not None: # Custom alphabet. old_Z_star = k.domain() @@ -3717,8 +3714,8 @@ def simplify(self, Z=None): Z_star = FiniteWords(Z) h_new = {old: [new] for old, new in zip(old_Z, Z)} k_new = {new: [old] for new, old in zip(Z, old_Z)} - h_new = type(self)(h_new, domain=old_Z_star, codomain=Z_star) - k_new = type(self)(k_new, domain=Z_star, codomain=old_Z_star) + h_new = WordMorphism(h_new, domain=old_Z_star, codomain=Z_star) + k_new = WordMorphism(k_new, domain=Z_star, codomain=old_Z_star) h = h_new * h k = k * k_new @@ -3731,17 +3728,16 @@ def simplify_injective(self): Requires this morphism to be an endomorphism. - Basically calls :meth:`simplify` until it throws an exception, which - means the input was injective. If already the first call raises an - exception, instead of reraising it a quadruplet `(g, h, k, i)` is still - returned, where `g` and `h` are equal to this morphism, `k` is the - identity morphism and `i` is 0. + This methods basically calls :meth:`simplify` until the returned + simplification is injective. If this morphism is already injective, a + quadruplet `(g, h, k, i)` is still returned, where `g` is this morphism, + `h` and `k` are the identity morphisms and `i` is 0. Let `f: X^* \rightarrow Y^*` be a morphism and `Y \subseteq X`. Then `g: Z^* \rightarrow Z^*` is an injective simplification of `f` with respect to morphisms `h: X^* \rightarrow Z^*` and `k: Z^* \rightarrow Y^*` and a positive integer `i`, if `g` is - injective and `|Z| < |X|` and `g^i = h \circ k` and `f^i = k \circ h`. + injective, `|Z| < |X|`, `g^i = h \circ k` and `f^i = k \circ h`. For more information see Section 4 in [KO2000]_. @@ -3760,16 +3756,11 @@ def simplify_injective(self): if not self.is_endomorphism(): raise TypeError(f'self ({self}) is not an endomorphism') - try: - h, k = self.simplify() - except ValueError: - return self, self, self.domain().identity_morphism(), 0 - g = h * k - - from itertools import count - for i in count(start=1): - try: - h_new, k_new = g.simplify() - g, h, k = h_new * k_new, h_new * h, k * k_new - except ValueError: - return g, h, k, i + g = self + h = self.domain().identity_morphism() + k = self.codomain().identity_morphism() + i = 0 + while not g.is_injective(): + h_new, k_new = g.simplify() + g, h, k, i = h_new * k_new, h_new * h, k * k_new, i + 1 + return g, h, k, i From e47204073b6b041c15430e215f267348229c3c99 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Thu, 22 Apr 2021 13:38:46 +0200 Subject: [PATCH 045/280] 18119: Refactor is_injective (2) --- src/sage/combinat/words/morphism.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index c39cc1d23f0..124c9972cf2 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3209,9 +3209,9 @@ def check(u, v): tails.add(tail) todo.append(tail) - images = self.images() - if any(not x for x in images): + if self.is_erasing(): return False + images = self.images() tails = set() todo = [] From 0354a41f5504017a2aedf0ad27ee12a27f4a1702 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 23 Apr 2021 10:07:00 +1000 Subject: [PATCH 046/280] Fix deprecation warning for viewing crystals with graphviz. --- src/sage/categories/loop_crystals.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/loop_crystals.py b/src/sage/categories/loop_crystals.py index 784ea2b3b43..06bd9f55a17 100644 --- a/src/sage/categories/loop_crystals.py +++ b/src/sage/categories/loop_crystals.py @@ -119,8 +119,11 @@ def digraph(self, subset=None, index_set=None): """ G = Crystals().parent_class.digraph(self, subset, index_set) if have_dot2tex(): - f = lambda u_v_label: ({"backward": u_v_label[2] == 0}) - G.set_latex_options(edge_options=f) + def eopt(u_v_label): + if u_v_label[2] == 0: + return {"dir": "back"} + return {} + G.set_latex_options(edge_options=eopt) return G # TODO: Should we make "regular" an axiom? From 0239765c1daa2ac22f92af03b231f6a6315b59c4 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Mon, 3 May 2021 20:55:25 +0200 Subject: [PATCH 047/280] 31774: let GAP handle errors in PrimitiveGroups --- src/sage/groups/perm_gps/permgroup_named.py | 50 ++++++++------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/src/sage/groups/perm_gps/permgroup_named.py b/src/sage/groups/perm_gps/permgroup_named.py index 0bc4baa4c36..753613884f8 100644 --- a/src/sage/groups/perm_gps/permgroup_named.py +++ b/src/sage/groups/perm_gps/permgroup_named.py @@ -2153,11 +2153,10 @@ class PrimitiveGroup(PermutationGroup_unique): Only primitive groups of "small" degree are available in GAP's database:: - sage: PrimitiveGroup(2500,1) + sage: PrimitiveGroup(2^12,1) Traceback (most recent call last): ... - NotImplementedError: Only the primitive groups of degree less - than 2500 are available in GAP's database + GAPError: Error, Primitive groups of degree 4096 are not known! """ def __init__(self, d, n): @@ -2252,14 +2251,12 @@ def PrimitiveGroups(d=None): sage: PrimitiveGroups() Primitive Groups - The database currently only contains primitive groups up to degree - 2499:: + The database is currently limited:: - sage: PrimitiveGroups(2500).cardinality() + sage: PrimitiveGroups(2^12).cardinality() Traceback (most recent call last): ... - NotImplementedError: Only the primitive groups of degree less - than 2500 are available in GAP's database + GAPError: Error, Primitive groups of degree 4096 are not known! .. TODO:: @@ -2423,10 +2420,7 @@ def __contains__(self, G): sage: 1 in PrimitiveGroups(4) False """ - if isinstance(G,PrimitiveGroup): - return G._d == self._degree - else: - False + return isinstance(G, PrimitiveGroup) and G._d == self._degree def __getitem__(self, n): r""" @@ -2486,35 +2480,29 @@ def cardinality(self): sage: [PrimitiveGroups(i).cardinality() for i in range(11)] [1, 1, 1, 2, 2, 5, 4, 7, 7, 11, 9] - GAP contains all primitive groups up to degree 2499:: - - sage: PrimitiveGroups(2500).cardinality() - Traceback (most recent call last): - ... - NotImplementedError: Only the primitive groups of degree less than - 2500 are available in GAP's database - TESTS:: sage: type(PrimitiveGroups(12).cardinality()) sage: type(PrimitiveGroups(0).cardinality()) + + Check for :trac:`31774`:: + + sage: PrimitiveGroups(2500).cardinality() + 34 + sage: PrimitiveGroups(2^12).cardinality() + Traceback (most recent call last): + ... + GAPError: Error, Primitive groups of degree 4096 are not known! """ - # gap.NrPrimitiveGroups(0) fails, so Sage needs to handle this - # While we are at it, and since Sage also handles the - # primitive group of degree 1, we may as well handle 1 if self._degree <= 1: + # gap.NrPrimitiveGroups(0) fails, so Sage needs to handle this + # While we are at it, and since Sage also handles the + # primitive group of degree 1, we may as well handle 1 return Integer(1) - elif self._degree >= 2500: - raise NotImplementedError("Only the primitive groups of degree less than 2500 are available in GAP's database") else: - try: - return Integer(libgap.NrPrimitiveGroups(self._degree)) - except RuntimeError: - from sage.misc.verbose import verbose - verbose("Error: PrimitiveGroups should be in GAP already.", level=0) - + return Integer(libgap.NrPrimitiveGroups(self._degree)) class PermutationGroup_plg(PermutationGroup_unique): def base_ring(self): From 9c216e05102cfcb161c8f3b832b3af9c65a1371b Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Tue, 4 May 2021 21:37:35 +0200 Subject: [PATCH 048/280] 18119: Work around some codomain issues --- src/sage/combinat/words/morphism.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 124c9972cf2..d9a0db1b2fe 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3416,6 +3416,9 @@ def infinite_repetitions_bounded(self, w=None): sage: m = WordMorphism('a->Cab,b->1c1,c->E2bd5,d->BbaA,5->6,6->7,7->8,8->9,9->5,1->2,2->1,A->B,B->C,C->D,D->E,E->') sage: sorted(m.infinite_repetitions_bounded()) [word: 1, word: 1519181716, word: 2, word: 2529282726] + + sage: WordMorphism('a->b,b->b', codomain=FiniteWords('ab')).infinite_repetitions() + set() """ def impl(): U = {} @@ -3444,7 +3447,9 @@ def impl(): if w is None: w = self._morph f = self.restrict_domain(self.reach(w)) + f._codomain = f._domain g, _, k, _ = f.simplify_injective() + g._codomain = g._domain unbounded = set(g.growing_letters()) gb = g.restrict_domain(set(g._morph) - unbounded) @@ -3498,7 +3503,9 @@ def infinite_repetitions_growing(self, w=None): if w is None: w = self._morph f = self.restrict_domain(self.reach(w)) + f._codomain = f._domain g, _, k, _ = f.simplify_injective() + g._codomain = g._domain unbounded = set(g.growing_letters()) result = set() From 9443892b2e86d4edf4f040fa7b1736583ab4c29a Mon Sep 17 00:00:00 2001 From: John Cremona Date: Wed, 5 May 2021 08:59:07 +0100 Subject: [PATCH 049/280] #31443: updated eclib tarball to 20210503 --- build/pkgs/eclib/checksums.ini | 6 +++--- build/pkgs/eclib/package-version.txt | 2 +- build/pkgs/eclib/spkg-configure.m4 | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 5d12b8a60e0..8342f43b6da 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,5 +1,5 @@ tarball=eclib-VERSION.tar.bz2 -sha1=2f0ea52fd59352fa0dec8cabf5d513b0f0cf7629 -md5=2a3d4b37ee5f504d46c010e29040d7a4 -cksum=2697309856 +sha1=b8e1bd10a28341fcd8146cf12fc38a12c798c555 +md5=d54562480e1ebd73ae3f343b949dfa66 +cksum=2657346485 upstream_url=https://github.com/JohnCremona/eclib/releases/download/VERSION/eclib-VERSION.tar.bz2 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index e9d3aa7a596..891293d2df1 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -20210415 +20210503 diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index edb759d45f5..4bced5f44ed 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,7 +1,7 @@ SAGE_SPKG_CONFIGURE([eclib], [ SAGE_SPKG_DEPCHECK([ntl pari flint], [ dnl Trac #31443: use existing eclib only if the version reported by pkg-config is correct - m4_pushdef([SAGE_ECLIB_VER],["20210415"]) + m4_pushdef([SAGE_ECLIB_VER],["20210503"]) PKG_CHECK_MODULES([ECLIB], [eclib = SAGE_ECLIB_VER], [ AC_CACHE_CHECK([for mwrank version == SAGE_ECLIB_VER], [ac_cv_path_MWRANK], [ AC_PATH_PROGS_FEATURE_CHECK([MWRANK], [mwrank], [ From f7cc8c970f8e0eac6abfae6829b168480f21029d Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Wed, 9 Sep 2020 23:28:17 +0200 Subject: [PATCH 050/280] 30755: implement precision for display of matrices --- src/sage/matrix/constructor.pyx | 38 +++++++++++++++++++++++ src/sage/matrix/matrix0.pyx | 49 +++++++++++++++++++++++++++++- src/sage/repl/display/formatter.py | 39 ++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 1 deletion(-) diff --git a/src/sage/matrix/constructor.pyx b/src/sage/matrix/constructor.pyx index c6712f37e3f..2525155b642 100644 --- a/src/sage/matrix/constructor.pyx +++ b/src/sage/matrix/constructor.pyx @@ -669,6 +669,30 @@ class options(GlobalOptions): sage: matrix(ZZ, 4, 6) 4 x 6 dense matrix over Integer Ring... sage: matrix.options._reset() + + The precision can also be set via the IPython magic:: + + sage: from sage.repl.interpreter import get_test_shell + sage: shell = get_test_shell() + sage: shell.run_cell('%precision 5') + '%.5f' + sage: matrix.options.precision + 5 + sage: A = matrix(RR, [[200/3]]); A + [66.667] + + The number format can be specified as well:: + + sage: matrix.options.format_numeric = '{:.{prec}e}' + sage: A + [6.66667e+1] + sage: matrix.options.format_numeric = '{:.{prec}f}' + sage: A + [66.66667] + sage: matrix.options.format_numeric = '{:+.{prec}g}' + sage: A + [+66.667] + sage: matrix.options._reset() """ NAME = 'Matrix' max_cols = dict(default=49, @@ -677,3 +701,17 @@ class options(GlobalOptions): max_rows = dict(default=19, description='maximum number of rows to display', checker=lambda val: val >= 0) + precision = \ + dict(default=None, + description='number of digits to display for floating point ' + 'entries; if ``None``, the exact representation is ' + 'used instead. This option is also set by the ' + '`IPython magic `_ ' + '``%precision``.', + checker=lambda val: val is None or val >= 0) + format_numeric = \ + dict(default='{:.{prec}}', + description='string used for formatting floating point numbers of' + ' an (optional) precision ``prec``; only supported ' + 'for entry types implementing ``__format__``', + checker=lambda val: isinstance(val.format(3.1415, prec=3), str)) diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index e58ee774efb..88d9c2af4d0 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -1875,6 +1875,25 @@ cdef class Matrix(sage.structure.element.Matrix): -0.35104242112828943 0.5084492941557279⎟ -0.9541798283979341 -0.8948790563276592⎠ + The number of floating point digits to display is controlled by + :obj:`matrix.options.precision <.constructor.options>` and can also be + set by the `IPython magic + `_ + ``%precision``. This does not affect the internal precision of the + represented data, but only the textual display of matrices:: + + sage: matrix.options.precision = 4 + sage: A = matrix(RR, [[1/3, 200/3], [-3, 1e6]]); A + [ 0.3333 66.67] + [ -3.000 1.000E+6] + sage: unicode_art(A) + ⎛ 0.3333 66.67⎞ + ⎝ -3.000 1.000E+6⎠ + sage: matrix.options.precision = None + sage: A + [ 0.333333333333333 66.6666666666667] + [ -3.00000000000000 1.00000000000000e6] + TESTS: Prior to :trac:`11544` this could take a full minute to run (2011). :: @@ -1898,6 +1917,19 @@ cdef class Matrix(sage.structure.element.Matrix): ⎛─⎞ ⎜1⎟ ⎝─⎠ + + Check that exact number types are not affected by the precision + option:: + + sage: matrix.options.precision = 4 + sage: matrix(ZZ, [[10^10]]) + [10000000000] + sage: matrix(QQ, [[2/3, 10^6]]) + [ 2/3 1000000] + sage: R. = QQ[[]] + sage: matrix(R, [[2/3 - 10^6 * x^3 + 3 * y + O(x, y)^4]]) + [2/3 + 3*y - 1000000*x^3 + O(x, y)^4] + sage: matrix.options._reset() """ cdef Py_ssize_t nr, nc, r, c nr = self._nrows @@ -1988,15 +2020,30 @@ cdef class Matrix(sage.structure.element.Matrix): if minus_one is not None: rep_mapping[-self.base_ring().one()] = minus_one + entries = self.list() + + # only use floating point formatting for inexact types that have + # custom implementation of __format__ + from .constructor import options + prec = options.precision() + if prec is None or callable(rep_mapping) or not entries \ + or type(entries[0]).__format__ is Element.__format__ \ + or self._base_ring.is_exact(): + fmt_numeric = None + else: + fmt_numeric = options.format_numeric() + # compute column widths S = [] - for x in self.list(): + for x in entries: # Override the usual representations with those specified if callable(rep_mapping): rep = rep_mapping(x) # avoid hashing entries, especially algebraic numbers elif rep_mapping and x in rep_mapping: rep = rep_mapping.get(x) + elif fmt_numeric is not None: + rep = fmt_numeric.format(x, prec=prec) else: rep = repr(x) S.append(rep) diff --git a/src/sage/repl/display/formatter.py b/src/sage/repl/display/formatter.py index 307da60c8d8..59456cb094a 100644 --- a/src/sage/repl/display/formatter.py +++ b/src/sage/repl/display/formatter.py @@ -104,6 +104,10 @@ def __init__(self, *args, **kwds): from sage.repl.rich_output.backend_ipython import BackendIPython self.dm.check_backend_class(BackendIPython) + pt_formatter = self.formatters[PLAIN_TEXT] + pt_formatter.observe(self._ipython_float_precision_changed, + names=['float_precision']) + def format(self, obj, include=None, exclude=None): r""" Use the Sage rich output instead of IPython @@ -195,6 +199,41 @@ def format(self, obj, include=None, exclude=None): ipy_metadata[PLAIN_TEXT] = sage_metadata[PLAIN_TEXT] return ipy_format, ipy_metadata + @staticmethod + def _ipython_float_precision_changed(change): + """ + Update the current float precision for the display of matrices in Sage. + + This function is called when the IPython ``%precision`` magic is + invoked. + + TESTS:: + + sage: from sage.repl.interpreter import get_test_shell + sage: shell = get_test_shell() + sage: shell.run_cell('%precision 4') + '%.4f' + sage: shell.run_cell('matrix.options.precision') # indirect doctest + 4 + sage: shell.run_cell('%precision') + '%r' + sage: shell.run_cell('matrix.options.precision') # indirect doctest + None + """ + from sage.matrix.constructor import options + s = change.new + if not s: + # unset the precision + options.precision = None + else: + try: + prec = int(s) + if prec >= 0: + options.precision = prec + # otherwise ignore the change + except ValueError: + pass + class SagePlainTextFormatter(PlainTextFormatter): From 2028258a800e84669fd085245c83dca602b81574 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 10 Apr 2021 19:13:08 -0700 Subject: [PATCH 051/280] sage.manifolds.subsets.closure, TopologicalSubmanifold.closure: New --- src/doc/en/reference/manifolds/manifold.rst | 2 + src/sage/manifolds/subset.py | 21 +++++- src/sage/manifolds/subsets/closure.py | 84 +++++++++++++++++++++ 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 src/sage/manifolds/subsets/closure.py diff --git a/src/doc/en/reference/manifolds/manifold.rst b/src/doc/en/reference/manifolds/manifold.rst index 02afb2460b4..c5dc02c0098 100644 --- a/src/doc/en/reference/manifolds/manifold.rst +++ b/src/doc/en/reference/manifolds/manifold.rst @@ -23,3 +23,5 @@ Topological Manifolds vector_bundle sage/manifolds/family + + sage/manifolds/subsets/closure diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index f915b66eaf2..26498522522 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2573,5 +2573,24 @@ def difference(self, other, name=None, latex_name=None, is_open=False): self.declare_union(other, diff, disjoint=True) return diff - #### End of construction of new sets from self + def closure(self, name=None, latex_name=None): + r""" + Return the topological closure of ``self`` as a subset of the manifold. + + EXAMPLES:: + + sage: M = Manifold(2, 'R^2', structure='topological') + sage: c_cart. = M.chart() # Cartesian coordinates on R^2 + sage: M.closure() is M + True + sage: D = M.open_subset('D', coord_def={c_cart: x^2+y^2<1}); D + Open subset D of the 2-dimensional topological manifold R^2 + sage: cl_D = D.closure(); cl_D + Topological closure cl_D of the Open subset D of the 2-dimensional topological manifold R^2 + """ + if self.manifold().is_subset(self): + return self + from .subsets.closure import ManifoldSubsetClosure + return ManifoldSubsetClosure(self, name=name, latex_name=latex_name) + #### End of construction of new sets from self diff --git a/src/sage/manifolds/subsets/closure.py b/src/sage/manifolds/subsets/closure.py new file mode 100644 index 00000000000..9954e6578cc --- /dev/null +++ b/src/sage/manifolds/subsets/closure.py @@ -0,0 +1,84 @@ +r""" +Topological Closures of Manifold Subsets + +:class:`ManifoldSubsetClosure` implements the topological closure +of a manifold subset in the topology of the manifold. + +""" + +# **************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.manifolds.subset import ManifoldSubset + +class ManifoldSubsetClosure(ManifoldSubset): + + """ + Topological closure of a manifold subset in the topology of the manifold. + + INPUT: + + - ``subset`` -- a :class:`ManifoldSubset` + - ``name`` -- (default: computed from the name of the subset) + string; name (symbol) given to the closure + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to + denote the subset; if none is provided, it is set to ``name`` + + """ + + def __init__(self, subset, name=None, latex_name=None): + r""" + Initialize a :class:`ManifoldSubsetClosure` instance. + + TESTS:: + + sage: from sage.manifolds.subsets.closure import ManifoldSubsetClosure + sage: M = Manifold(2, 'R^2', structure='topological') + sage: c_cart. = M.chart() # Cartesian coordinates on R^2 + sage: D = M.open_subset('D', coord_def={c_cart: x^2+y^2<1}); D + Open subset D of the 2-dimensional topological manifold R^2 + sage: cl_D = D.closure(); cl_D # indirect doctest + Topological closure cl_D of the + Open subset D of the 2-dimensional topological manifold R^2 + sage: also_cl_D = ManifoldSubsetClosure(D, name='also_cl_D'); also_cl_D + Topological closure also_cl_D of the + Open subset D of the 2-dimensional topological manifold R^2 + sage: cl_D is also_cl_D + False + sage: cl_D == also_cl_D + False + + """ + self._subset = subset + base_manifold = subset.manifold() + if latex_name is None: + if name is None: + latex_name = r'\mathop{\mathrm{cl}}(' + subset._latex_name + ')' + else: + latex_name = name + if name is None: + name = 'cl_' + subset._name + ManifoldSubset.__init__(self, base_manifold, name, latex_name=latex_name) + self.declare_superset(subset) + + def _repr_(self): + r""" + String representation of the object. + + TESTS:: + + sage: from sage.manifolds.subsets.closure import ManifoldSubsetClosure + sage: M = Manifold(2, 'R^2', structure='topological') + sage: D = M.open_subset('D') + sage: cl_D = D.closure(); cl_D # indirect doctest + Topological closure cl_D of the + Open subset D of the 2-dimensional topological manifold R^2 + """ + return "Topological closure {} of the {}".format(self._name, self._subset) From 38c9e96e08560fbf7fa28397bba2e46b16e87e0e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 8 May 2021 15:15:37 -0700 Subject: [PATCH 052/280] ManifoldSubset.is_closed, declare_closed: New --- src/sage/manifolds/subset.py | 102 ++++++++++++++++++++++++-- src/sage/manifolds/subsets/closure.py | 24 ++++++ 2 files changed, 121 insertions(+), 5 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 26498522522..e07d78266b4 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -520,6 +520,54 @@ def is_open(self): """ return False + def is_closed(self): + """ + Return if ``self`` is a closed set. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: M.is_closed() + True + sage: also_M = M.subset('also_M') + sage: M.declare_subset(also_M) + sage: also_M.is_closed() + True + + sage: A = M.subset('A') + sage: A.is_closed() + False + sage: A.declare_empty() + sage: A.is_closed() + True + + sage: N = M.open_subset('N') + sage: N.is_closed() + False + sage: complement_N = M.subset('complement_N') + sage: M.declare_union(N, complement_N, disjoint=True) + sage: complement_N.is_closed() + True + + """ + if self.manifold().is_subset(self): + return True + if self.is_empty(): + return True + for other_name, intersection in self._intersections.items(): + if intersection.is_empty(): + other = self.manifold().subset_family()[other_name] + if other.is_open(): + try: + union = self._unions[other_name] + except KeyError: + pass + else: + if union.is_open(): + # self is complement of open other in open union + return True + return False + def open_covers(self, trivial=True, supersets=False): r""" Generate the open covers of the current subset. @@ -1854,6 +1902,37 @@ def point(self, coords=None, chart=None, name=None, latex_name=None): return self.element_class(self, coords=coords, chart=chart, name=name, latex_name=latex_name) + def declare_closed(self): + r""" + Declare ``self`` to be a closed subset of the manifold. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B1 = A.subset('B1') + sage: B1.is_closed() + False + sage: B1.declare_closed() + sage: B1.is_closed() + True + + sage: B2 = A.subset('B2') + sage: cl_B2 = B2.closure() + sage: A.declare_closed() + sage: cl_B2.is_subset(A) + True + + """ + if self.is_closed(): + return + self.complement(is_open=True) + from .subsets.closure import ManifoldSubsetClosure + for closure in self.manifold().subsets(): + if isinstance(closure, ManifoldSubsetClosure): + if closure._subset.is_subset(self): + closure.declare_subset(self) + #### Construction of new sets from self: def subset(self, name, latex_name=None, is_open=False): @@ -2568,6 +2647,8 @@ def difference(self, other, name=None, latex_name=None, is_open=False): if name is None: name = "_minus_".join(S._name for S in (self, other)) + is_open = is_open or (self.is_open() and other.is_closed()) + diff = self.subset(name=name, latex_name=latex_name, is_open=is_open) diff.declare_equal(diffs) self.declare_union(other, diff, disjoint=True) @@ -2583,12 +2664,23 @@ def closure(self, name=None, latex_name=None): sage: c_cart. = M.chart() # Cartesian coordinates on R^2 sage: M.closure() is M True - sage: D = M.open_subset('D', coord_def={c_cart: x^2+y^2<1}); D - Open subset D of the 2-dimensional topological manifold R^2 - sage: cl_D = D.closure(); cl_D - Topological closure cl_D of the Open subset D of the 2-dimensional topological manifold R^2 + sage: D2 = M.open_subset('D2', coord_def={c_cart: x^2+y^2<2}); D2 + Open subset D2 of the 2-dimensional topological manifold R^2 + sage: cl_D2 = D2.closure(); cl_D2 + Topological closure cl_D2 of the + Open subset D2 of the 2-dimensional topological manifold R^2 + sage: cl_D2.is_closed() + True + sage: cl_D2 is cl_D2.closure() + True + + sage: D1 = D2.open_subset('D1'); D1 + Open subset D1 of the 2-dimensional topological manifold R^2 + sage: D1.closure().is_subset(D2.closure()) + True + """ - if self.manifold().is_subset(self): + if self.is_closed(): return self from .subsets.closure import ManifoldSubsetClosure return ManifoldSubsetClosure(self, name=name, latex_name=latex_name) diff --git a/src/sage/manifolds/subsets/closure.py b/src/sage/manifolds/subsets/closure.py index 9954e6578cc..f66f5dec589 100644 --- a/src/sage/manifolds/subsets/closure.py +++ b/src/sage/manifolds/subsets/closure.py @@ -67,6 +67,9 @@ def __init__(self, subset, name=None, latex_name=None): name = 'cl_' + subset._name ManifoldSubset.__init__(self, base_manifold, name, latex_name=latex_name) self.declare_superset(subset) + self.declare_subset(superset + for superset in subset.supersets() + if superset.is_closed()) def _repr_(self): r""" @@ -82,3 +85,24 @@ def _repr_(self): Open subset D of the 2-dimensional topological manifold R^2 """ return "Topological closure {} of the {}".format(self._name, self._subset) + + def is_closed(self): + """ + Return if ``self`` is a closed set. + + This implementation of the method always returns ``True``. + + EXAMPLES:: + + sage: from sage.manifolds.subsets.closure import ManifoldSubsetClosure + sage: M = Manifold(2, 'R^2', structure='topological') + sage: c_cart. = M.chart() # Cartesian coordinates on R^2 + sage: D = M.open_subset('D', coord_def={c_cart: x^2+y^2<1}); D + Open subset D of the 2-dimensional topological manifold R^2 + sage: cl_D = D.closure(); cl_D # indirect doctest + Topological closure cl_D of the Open subset D of the 2-dimensional topological manifold R^2 + sage: cl_D.is_closed() + True + + """ + return True From c8272c0bb80ef219597a3968ae1e6182ebc8f943 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Mon, 10 May 2021 20:16:51 +0200 Subject: [PATCH 053/280] Sync autodoc with upstream to fix deprecation warnings --- src/sage_docbuild/ext/sage_autodoc.py | 56 +++++++++++++++++++-------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/src/sage_docbuild/ext/sage_autodoc.py b/src/sage_docbuild/ext/sage_autodoc.py index ac9f8538bc7..c4523f2a531 100644 --- a/src/sage_docbuild/ext/sage_autodoc.py +++ b/src/sage_docbuild/ext/sage_autodoc.py @@ -34,13 +34,12 @@ from docutils.statemachine import ViewList import sphinx -from sphinx.ext.autodoc import mock +from sphinx.ext.autodoc import mock, ObjectMember from sphinx.ext.autodoc.importer import import_object, get_object_members, get_module_members from sphinx.locale import _, __ from sphinx.pycode import ModuleAnalyzer from sphinx.errors import PycodeError from sphinx.util import logging -from sphinx.util import rpartition, force_decode from sphinx.util.docstrings import prepare_docstring from sphinx.util.inspect import isdescriptor, \ safe_getattr, object_description, is_builtin_class_method, \ @@ -488,10 +487,7 @@ def get_doc(self, encoding=None, ignore=1): # make sure we have Unicode docstrings, then sanitize and split # into lines if isinstance(docstring, str): - return [prepare_docstring(docstring, ignore)] - elif isinstance(docstring, str): # this will not trigger on Py3 - return [prepare_docstring(force_decode(docstring, encoding), - ignore)] + return [prepare_docstring(docstring)] # ... else it is something strange, let's ignore it return [] @@ -536,7 +532,7 @@ def add_content(self, more_content, no_docstring=False): # add content from docstrings if not no_docstring: - encoding = self.analyzer and self.analyzer._encoding + encoding = self.analyzer docstrings = self.get_doc(encoding) if not docstrings: # append at least a dummy docstring, so that the event @@ -876,13 +872,42 @@ def add_directive_header(self, sig): if self.options.deprecated: self.add_line(u' :deprecated:', sourcename) + def get_module_members(self): + """Get members of target module.""" + if self.analyzer: + attr_docs = self.analyzer.attr_docs + else: + attr_docs = {} + + members = {} # type: Dict[str, ObjectMember] + for name in dir(self.object): + try: + value = safe_getattr(self.object, name, None) + docstring = attr_docs.get(('', name), []) + members[name] = ObjectMember(name, value, docstring="\n".join(docstring)) + except AttributeError: + continue + + # annotation only member (ex. attr: int) + try: + for name in inspect.getannotations(self.object): + if name not in members: + docstring = attr_docs.get(('', name), []) + members[name] = ObjectMember(name, INSTANCEATTR, + docstring="\n".join(docstring)) + except AttributeError: + pass + + return members + def get_object_members(self, want_all): # type: (bool) -> Tuple[bool, List[Tuple[unicode, object]]] + members = self.get_module_members() if want_all: if not hasattr(self.object, '__all__'): # for implicit module members, check __module__ to avoid # documenting imported objects - return True, get_module_members(self.object) + return True, list(members.values()) else: memberlist = self.object.__all__ # Sometimes __all__ is broken... @@ -893,14 +918,14 @@ def get_object_members(self, want_all): '(in module %s) -- ignoring __all__' % (memberlist, self.fullname)) # fall back to all members - return True, get_module_members(self.object) + return True, list(members.values()) else: memberlist = self.options.members or [] ret = [] for mname in memberlist: - try: - ret.append((mname, safe_getattr(self.object, mname))) - except AttributeError: + if mname in members: + ret.append(members[mname]) + else: logger.warning( 'missing attribute mentioned in :members: or __all__: ' 'module %s, attribute %s' % @@ -951,7 +976,7 @@ def resolve_name(self, modname, parents, path, base): # ... if still None, there's no way to know if mod_cls is None: return None, [] - modname, cls = rpartition(mod_cls, '.') # type: ignore + modname, _, cls = mod_cls.rpartition('.') # type: ignore parents = [cls] # if the module name is still missing, get it like above if not modname: @@ -1275,10 +1300,7 @@ def get_doc(self, encoding=None, ignore=1): doc = [] for docstring in docstrings: if isinstance(docstring, str): - doc.append(prepare_docstring(docstring, ignore)) - elif isinstance(docstring, str): # this will not trigger on Py3 - doc.append(prepare_docstring(force_decode(docstring, encoding), - ignore)) + doc.append(prepare_docstring(docstring)) return doc def add_content(self, more_content, no_docstring=False): From 1ed5bd7eab075e0ed3df2333b4ed5dec7bb793f8 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Mon, 10 May 2021 20:17:21 +0200 Subject: [PATCH 054/280] Remove duplicate modules, which throw a warning with sphinx 4 --- src/doc/en/reference/combinat/module_list.rst | 1 - src/doc/en/reference/modfrm/index.rst | 1 - 2 files changed, 2 deletions(-) diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index a95069590b4..f8bfe88c719 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -364,7 +364,6 @@ Comprehensive Module list sage/combinat/superpartition sage/combinat/symmetric_group_algebra sage/combinat/symmetric_group_representations - sage/combinat/super_tableau sage/combinat/tableau sage/combinat/tableau_residues sage/combinat/tableau_tuple diff --git a/src/doc/en/reference/modfrm/index.rst b/src/doc/en/reference/modfrm/index.rst index e1f8a483a3f..3d5e55dfd63 100644 --- a/src/doc/en/reference/modfrm/index.rst +++ b/src/doc/en/reference/modfrm/index.rst @@ -23,7 +23,6 @@ Module List sage/modular/modform/hecke_operator_on_qexp sage/modular/modform/numerical sage/modular/modform/vm_basis - sage/modular/modform/ambient sage/modular/modform/half_integral sage/modular/modform/find_generators sage/modular/modform/j_invariant From ad51564265ddef74f7feb5b7de32b2590a38308b Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Mon, 10 May 2021 20:18:01 +0200 Subject: [PATCH 055/280] Upgrade sphinx to 4.0.0 --- build/pkgs/sphinx/checksums.ini | 6 +++--- build/pkgs/sphinx/package-version.txt | 2 +- build/pkgs/sphinx/patches/environment.patch | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/sphinx/checksums.ini b/build/pkgs/sphinx/checksums.ini index ce072d1bb1e..567dc6e28bc 100644 --- a/build/pkgs/sphinx/checksums.ini +++ b/build/pkgs/sphinx/checksums.ini @@ -1,5 +1,5 @@ tarball=Sphinx-VERSION.tar.gz -sha1=9043e0f324d62a5c47a0773f562d423e66163f63 -md5=6e01aa4ab0f1dcbc8d65cc25c736e3ea -cksum=4106849897 +sha1=743988ef5dcf1f2dfa99435b93aa100b3a8d5674 +md5=e38a2e83c6ab6af2bd889da310a233b3 +cksum=3127931243 upstream_url=https://pypi.io/packages/source/s/sphinx/Sphinx-VERSION.tar.gz diff --git a/build/pkgs/sphinx/package-version.txt b/build/pkgs/sphinx/package-version.txt index 0be0b439969..3c82c17abed 100644 --- a/build/pkgs/sphinx/package-version.txt +++ b/build/pkgs/sphinx/package-version.txt @@ -1 +1 @@ -3.1.2.p0 +4.0.0.p0 diff --git a/build/pkgs/sphinx/patches/environment.patch b/build/pkgs/sphinx/patches/environment.patch index 4fbdac0cfe4..12627ec70f3 100644 --- a/build/pkgs/sphinx/patches/environment.patch +++ b/build/pkgs/sphinx/patches/environment.patch @@ -13,4 +13,4 @@ diff -ru a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py + continue if self.config[item.name] != item.value: self.config_status = CONFIG_CHANGED - break + self.config_status_extra = ' (%r)' % (item.name,) From b9e390f8402202ab61b8b313a990e4e4b862edc4 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Mon, 10 May 2021 20:21:53 +0200 Subject: [PATCH 056/280] Upgrade sphinx to 4.0.1 --- build/pkgs/sphinx/checksums.ini | 6 +++--- build/pkgs/sphinx/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/sphinx/checksums.ini b/build/pkgs/sphinx/checksums.ini index 567dc6e28bc..b42e84540d3 100644 --- a/build/pkgs/sphinx/checksums.ini +++ b/build/pkgs/sphinx/checksums.ini @@ -1,5 +1,5 @@ tarball=Sphinx-VERSION.tar.gz -sha1=743988ef5dcf1f2dfa99435b93aa100b3a8d5674 -md5=e38a2e83c6ab6af2bd889da310a233b3 -cksum=3127931243 +sha1=ac493c525509f639a4eeb784a85648b3066f1f40 +md5=4714d870be4e599280e2c06561b44430 +cksum=2654318749 upstream_url=https://pypi.io/packages/source/s/sphinx/Sphinx-VERSION.tar.gz diff --git a/build/pkgs/sphinx/package-version.txt b/build/pkgs/sphinx/package-version.txt index 3c82c17abed..f727dc16efb 100644 --- a/build/pkgs/sphinx/package-version.txt +++ b/build/pkgs/sphinx/package-version.txt @@ -1 +1 @@ -4.0.0.p0 +4.0.1.p0 From d5a374a612539fe8ca71ada9d8f82b8290dd4a04 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Tue, 11 May 2021 17:28:35 +0200 Subject: [PATCH 057/280] Clear LANGUAGE too Sphinx 4.x uses it to get the language of its messages, so we need to reset it for the warning filter to work properly --- build/bin/sage-site | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/bin/sage-site b/build/bin/sage-site index 400782164b3..5e00ec9a12f 100755 --- a/build/bin/sage-site +++ b/build/bin/sage-site @@ -153,8 +153,10 @@ if [ "$1" = "-docbuild" -o "$1" = "--docbuild" ]; then # proper UTF-8 operation in Python 3.6 or older. LC_ALL=$(locale -a | grep -E -i '^(c|en_us)[-.]utf-?8$' | head -n 1) LANG=$LC_ALL + LANGUAGE=$LC_ALL export LC_ALL export LANG + export LANGUAGE # See #30351: bugs in macOS implementations of openblas/libgopm can cause # docbuild to hang if multiple OpenMP threads are allowed. From 285ea99487b9dcf8fa917df21a235e5275732e77 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Tue, 11 May 2021 18:02:37 +0200 Subject: [PATCH 058/280] Update test for different sphinx output --- src/sage/misc/sagedoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 6bad826a882..33f28ada452 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -24,7 +24,7 @@ ....: for line in fobj: ....: if "#sage.symbolic.expression.Expression.numerical_approx" in line: ....: print(line) - numerical_approx(prec=None, digits=None, algorithm=None)... + numerical_approx(prec=None, digits=None, algorithm=None)... Check that sphinx is not imported at Sage start-up:: From ac5398499c314b8a59b5994c2cfa8b5945f3b311 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 11 May 2021 18:32:03 -0700 Subject: [PATCH 059/280] src/sage/manifolds/subsets/__init__.py: New --- src/sage/manifolds/subsets/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/sage/manifolds/subsets/__init__.py diff --git a/src/sage/manifolds/subsets/__init__.py b/src/sage/manifolds/subsets/__init__.py new file mode 100644 index 00000000000..e69de29bb2d From 901b789440ea1bdc2520aacaae8f3d5f8cf61370 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 13 May 2020 12:05:41 +0200 Subject: [PATCH 060/280] join_of_Vrep and meet_of_facets for face iterator --- .../face_iterator.pxd | 1 + .../face_iterator.pyx | 426 +++++++++++++++++- 2 files changed, 425 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 63ed7858024..f3a1d885fe8 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -69,6 +69,7 @@ cdef class FaceIterator_base(SageObject): cdef size_t set_coatom_rep(self) except -1 cdef size_t set_atom_rep(self) except -1 cdef int ignore_subsets(self) except -1 + cdef int find_face(self, face_t face) except -1 @cython.final cdef class FaceIterator(FaceIterator_base): diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index fc4929bf29f..74ddf1bdaae 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -176,7 +176,7 @@ AUTHOR: from sage.rings.integer cimport smallInteger from cysignals.signals cimport sig_check -from .conversions cimport bit_rep_to_Vrep_list +from .conversions cimport bit_rep_to_Vrep_list, Vrep_list_to_bit_rep from .conversions import facets_tuple_to_bit_rep_of_facets from .base cimport CombinatorialPolyhedron @@ -481,6 +481,391 @@ cdef class FaceIterator_base(SageObject): raise ValueError("only possible when in dual mode") self.ignore_subsets() + def meet_of_facets(self, *indices): + r""" + Construct the meet of the facets indicated by the indices. + + This is the largest face contained in all facets with the given indices. + + The iterator must be reseted if not newly initialized. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: it = P.face_generator() + sage: it.meet_of_facets(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it.meet_of_facets(1,2).ambient_H_indices() + (1, 2) + sage: it.meet_of_facets(1,3).ambient_H_indices() + (1, 3) + sage: it.meet_of_facets(1,5).ambient_H_indices() + (0, 1, 2, 3, 4, 5) + + sage: P = polytopes.cross_polytope(4) + sage: it = P.face_generator() + sage: it.meet_of_facets().ambient_H_indices() + () + sage: it.meet_of_facets(1,3).ambient_H_indices() + (1, 2, 3, 4) + sage: it.meet_of_facets(1,2).ambient_H_indices() + (1, 2) + sage: it.meet_of_facets(1,6).ambient_H_indices() + (1, 6) + sage: it.meet_of_facets(1,2,6).ambient_H_indices() + (1, 2, 6, 7) + sage: it.meet_of_facets(1,2,5,6).ambient_H_indices() + (0, 1, 2, 3, 4, 5, 6, 7) + + sage: s = cones.schur(4) + sage: C = CombinatorialPolyhedron(s) + sage: it = C.face_iter() + sage: it.meet_of_facets(1,2).ambient_H_indices() + (1, 2) + sage: it.meet_of_facets(1,2,3).ambient_H_indices() + Traceback (most recent call last): + ... + AssertionError: index out of range + + If the iterator has already been used, it must be reseted before:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator() + sage: _ = next(it), next(it) + sage: next(it).ambient_V_indices() + (15, 16, 17, 18, 19) + sage: it.meet_of_facets(9,11) + Traceback (most recent call last): + ... + ValueError: please reset the face iterator + sage: it.reset() + sage: it.meet_of_facets(9,11).ambient_H_indices() + (9, 11) + + """ + if self.dual: + return self._join_of_atoms(*indices) + else: + return self._meet_of_coatoms(*indices) + + def join_of_Vrep(self, *indices): + r""" + Construct the join of the Vrepresentatives indicated by the indices. + + This is the smallest face containing all Vrepresentatives with the given indices. + + The iterator must be reseted if not newly initialized. + + .. NOTE:: + + In case of unbounded polyhedra, the smallest face containing given Vrepresentatives + may not te well defined. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: it = P.face_generator() + sage: it.join_of_Vrep(1) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: it.join_of_Vrep(1,2).ambient_V_indices() + (1, 2) + sage: it.join_of_Vrep(1,3).ambient_V_indices() + (0, 1, 2, 3) + sage: it.join_of_Vrep(1,5).ambient_V_indices() + (0, 1, 5, 6) + + sage: P = polytopes.cross_polytope(4) + sage: it = P.face_generator() + sage: it.join_of_Vrep().ambient_V_indices() + () + sage: it.join_of_Vrep(1,3).ambient_V_indices() + (1, 3) + sage: it.join_of_Vrep(1,2).ambient_V_indices() + (1, 2) + sage: it.join_of_Vrep(1,6).ambient_V_indices() + (0, 1, 2, 3, 4, 5, 6, 7) + sage: it.join_of_Vrep(8) + Traceback (most recent call last): + ... + AssertionError: index out of range + + If the iterator has already been used, it must be reseted before:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator() + sage: _ = next(it), next(it) + sage: next(it).ambient_V_indices() + (15, 16, 17, 18, 19) + sage: it.join_of_Vrep(1,10) + Traceback (most recent call last): + ... + ValueError: please reset the face iterator + sage: it.reset() + sage: it.join_of_Vrep(1,10).ambient_V_indices() + (1, 10) + + In case of an unbounded polyhedron, we try to make sense of the input:: + + sage: P = polytopes.cube()*Polyhedron(lines=[[1]]) + sage: it = P.face_generator() + sage: it.join_of_Vrep(1) + A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 1 vertex and 1 line + sage: it.join_of_Vrep(0, 1) + A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 1 vertex and 1 line + sage: it.join_of_Vrep(0) + Traceback (most recent call last): + ... + ValueError: the join is not well-defined + + sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) + sage: it = P.face_generator() + sage: it.join_of_Vrep(0) + A 0-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex + sage: it.join_of_Vrep(1) + A 0-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex + sage: it.join_of_Vrep(2) + Traceback (most recent call last): + ... + ValueError: the join is not well-defined + sage: it.join_of_Vrep(0,2) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + + sage: P = Polyhedron(rays=[[1,0], [0,1]]) + sage: it = P.face_generator() + sage: it.join_of_Vrep(0) + A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex + sage: it.join_of_Vrep(1,2) + A 2-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 rays + """ + if not self.dual: + return self._join_of_atoms(*indices) + else: + return self._meet_of_coatoms(*indices) + + def _meet_of_coatoms(self, *indices): + r""" + Construct the meet of the coatoms indicated by the indices. + + The iterator must be reseted if not newly initialized. + + .. SEEALSO:: + + :meth:`meet_of_facets`, + :meth:`join_of_Vrep`. + + EXAMPLES: + + In non-dual mode we construct the meet of facets:: + + sage: P = polytopes.cube() + sage: it = P.face_generator(dual=False) + sage: it._meet_of_coatoms(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it._meet_of_coatoms(1,2,3) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: it._meet_of_coatoms(1,2,3).ambient_H_indices() + (1, 2, 3) + + In dual mode we construct the join of vertices/rays:: + + sage: P = polytopes.cube() + sage: it = P.face_generator(dual=True) + sage: it._meet_of_coatoms(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it._meet_of_coatoms(1,2,3) + A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices + sage: it._meet_of_coatoms(1) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + + The face iterator must not have the output dimension specified:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator(2) + sage: it._meet_of_coatoms(1,2) + Traceback (most recent call last): + ... + ValueError: face iterator must not have the output dimension specified + + TESTS: + + We prevent a segmentation fault:: + + sage: P = polytopes.simplex() + sage: it = P.face_generator() + sage: it._meet_of_coatoms(-1) + Traceback (most recent call last): + ... + OverflowError: can't convert negative value to size_t + sage: it._meet_of_coatoms(100) + Traceback (most recent call last): + ... + AssertionError: index out of range + + The empty face is detected correctly, even with lines or rays:: + + sage: P = polytopes.cube()*Polyhedron(lines=[[1]]) + sage: it = P.face_generator() + sage: it._meet_of_coatoms(1,2,4,5) + A -1-dimensional face of a Polyhedron in ZZ^4 + + sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) + sage: it = P.face_generator() + sage: it._meet_of_coatoms(0) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 2 vertices + sage: it._meet_of_coatoms(1) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + sage: it._meet_of_coatoms(2) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + sage: it._meet_of_coatoms(1, 2) + A -1-dimensional face of a Polyhedron in QQ^2 + """ + if unlikely(self.structure.face_status != 0): + raise ValueError("please reset the face iterator") + if unlikely(self.structure.output_dimension != -2): + raise ValueError("face iterator must not have the output dimension specified") + + cdef size_t n_atoms = self.coatoms.n_atoms() + cdef size_t n_coatoms = self.coatoms.n_faces() + cdef ListOfFaces coatoms = self.coatoms + + cdef ListOfFaces face_mem = ListOfFaces(1, n_atoms, n_coatoms) + cdef face_t face = face_mem.data.faces[0] + cdef size_t i + + # Initialize the full polyhedron. + for i in range(n_atoms): + face_add_atom(face, i) + + for i in indices: + assert 0 <= i < n_coatoms, "index out of range" + face_intersection(face, face, coatoms.data.faces[i]) + + if not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): + # The meet is contained in the far face and therefore is the empty face. + face_clear(face) + + self.find_face(face) + output = self.current() + self.reset() + return output + + def _join_of_atoms(self, *indices): + r""" + Construct the join of atoms indicated by the indices. + + The iterator must be reseted if not newly initialized. + + .. SEEALSO:: + + :meth:`meet_of_facets`, + :meth:`join_of_Vrep`. + + EXAMPLES: + + In dual mode we construct the meet of facets:: + + sage: P = polytopes.cube() + sage: it = P.face_generator(dual=True) + sage: it._join_of_atoms(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it._join_of_atoms(1,2,3) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: it._join_of_atoms(1,2,3).ambient_H_indices() + (1, 2, 3) + + In non-dual mode we construct the join of vertices/rays:: + + sage: P = polytopes.cube() + sage: it = P.face_generator(dual=False) + sage: it._join_of_atoms(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it._join_of_atoms(1,2,3) + A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices + sage: it._join_of_atoms(1) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + + If the iterator has already been used, it must be reseted before:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator() + sage: _ = next(it), next(it) + sage: next(it).ambient_V_indices() + (15, 16, 17, 18, 19) + sage: it._join_of_atoms(1,10) + Traceback (most recent call last): + ... + ValueError: please reset the face iterator + sage: it.reset() + sage: it._join_of_atoms(1,10).ambient_V_indices() + (1, 10) + + The face iterator must not have the output dimension specified:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator(2) + sage: it._join_of_atoms(1,2) + Traceback (most recent call last): + ... + ValueError: face iterator must not have the output dimension specified + + TESTS: + + We prevent a segmentation fault:: + + sage: P = polytopes.simplex() + sage: it = P.face_generator() + sage: it._join_of_atoms(-1) + Traceback (most recent call last): + ... + AssertionError: index out of range + sage: it._join_of_atoms(100) + Traceback (most recent call last): + ... + AssertionError: index out of range + """ + if unlikely(self.structure.face_status != 0): + raise ValueError("please reset the face iterator") + if unlikely(self.structure.output_dimension != -2): + raise ValueError("face iterator must not have the output dimension specified") + + cdef size_t n_atoms = self.coatoms.n_atoms() + cdef size_t n_coatoms = self.coatoms.n_faces() + cdef ListOfFaces coatoms = self.coatoms + + cdef ListOfFaces face_mem = ListOfFaces(2, n_atoms, n_coatoms) + cdef face_t face = face_mem.data.faces[0] + cdef face_t pseudo_face = face_mem.data.faces[1] + cdef size_t i + + assert all(i in range(n_atoms) for i in indices), "index out of range" + + # Initialize a pseudo_face as indicated by the indices. + for i in indices: + face_add_atom(pseudo_face, i) + + # Initialize the full polyhedron. + for i in range(n_atoms): + face_add_atom(face, i) + + # Now we intersect all faces that contain our pseudo_face. + for i in range(n_coatoms): + if face_issubset(pseudo_face, coatoms.data.faces[i]): + face_intersection(face, face, coatoms.data.faces[i]) + + if not indices: + # The neutral element of the join. + face_clear(face) + elif not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): + # The join is not well-defined. + # We allow for unbounded polyhedra to compute the join, even with rays. + # However, the result is not necesarrily well-defined. + raise ValueError("the join is not well-defined") + + self.find_face(face) + output = self.current() + self.reset() + return output + cdef int ignore_subsets(self) except -1: r""" Ignore sub-/supfaces of the current face. @@ -572,6 +957,43 @@ cdef class FaceIterator_base(SageObject): """ return bit_rep_to_Vrep_list(self.structure.face, self.structure.atom_rep) + cdef int find_face(self, face_t face) except -1: + """ + Iterate until the current face is ``face``. + + The value can then be obtained with :meth:`current`. + """ + cdef size_t n_atoms = face_len_atoms(face) + + if n_atoms == self.coatoms.n_atoms(): + # The face is the universe. + self.structure.face[0] = face[0] + self.structure.face_status = 1 + self.structure.current_dimension = self.structure.dimension + return 0 + elif n_atoms == 0: + # The face is the empty face. + self.structure.face[0] = face[0] + self.structure.face_status = 1 + self.structure.current_dimension = -1 + return 0 + + cdef int d = self.next_dimension() + while self.structure.current_dimension != self.structure.dimension: + if face_issubset(face, self.structure.face): + if face_issubset(self.structure.face, face): + # Found our face. + return 0 + else: + # The face is not a subface/supface of the current face. + self.ignore_subsets() + + d = self.next_dimension() + + raise ValueError("the face appears to be incorrect") + + + cdef class FaceIterator(FaceIterator_base): r""" A class to iterate over all combinatorial faces of a polyhedron. @@ -1036,7 +1458,7 @@ cdef class FaceIterator_geom(FaceIterator_base): .. SEEALSO:: - See :class:`FaceIterator`. + :class:`FaceIterator_base`. """ def __init__(self, P, dual=None, output_dimension=None): r""" From 2a5a7821e5575651b8cb18b2c23977af172368e4 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 13 May 2020 12:21:02 +0200 Subject: [PATCH 061/280] expose in combinatorial_polyhedron --- .../combinatorial_polyhedron/base.pyx | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index d11e8577d88..b5f60202be2 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2248,6 +2248,56 @@ cdef class CombinatorialPolyhedron(SageObject): return (False, None) return False + def join_of_Vrep(self, *indices): + r""" + Return the smallest face containing all Vrepresentatives indicated by the indices. + + .. SEEALSO:: + + :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator_base.join_of_Vrep`. + + EXAMPLES:: + + sage: P = polytopes.permutahedron(4) + sage: C = CombinatorialPolyhedron(P) + sage: C.join_of_Vrep(0,1) + A 1-dimensional face of a 3-dimensional combinatorial polyhedron + sage: C.join_of_Vrep(0,3).ambient_V_indices() + (0, 1, 2, 3, 4, 5) + sage: C.join_of_Vrep(8).ambient_V_indices() + (8,) + sage: C.join_of_Vrep().ambient_V_indices() + () + """ + return self.face_iter().join_of_Vrep(*indices) + + def meet_of_facets(self, *indices): + r""" + Return the largest face contained all facets indicated by the indices. + + .. SEEALSO:: + + :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator_base.meet_of_facets`. + + EXAMPLES:: + + sage: P = polytopes.dodecahedron() + sage: C = CombinatorialPolyhedron(P) + sage: C.meet_of_facets(0) + A 2-dimensional face of a 3-dimensional combinatorial polyhedron + sage: C.meet_of_facets(0).ambient_H_indices() + (0,) + sage: C.meet_of_facets(0,1).ambient_H_indices() + (0, 1) + sage: C.meet_of_facets(0,3).ambient_H_indices() + (0, 3) + sage: C.meet_of_facets(0,2,3).ambient_H_indices() + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) + sage: C.meet_of_facets().ambient_H_indices() + () + """ + return self.face_iter().meet_of_facets(*indices) + def face_iter(self, dimension=None, dual=None): r""" Iterator over all proper faces of specified dimension. From 6cecfd7442e4b27ced16f554137687cdb226f9d6 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 13 May 2020 13:44:48 +0200 Subject: [PATCH 062/280] raise index error for index error; fix doctests --- src/sage/geometry/polyhedron/base.py | 6 ++++++ .../polyhedron/combinatorial_polyhedron/base.pyx | 8 ++++---- .../combinatorial_polyhedron/face_iterator.pyx | 16 +++++++++------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 843fe204cb2..f811b337133 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6891,6 +6891,12 @@ def facets(self): return () return self.faces(self.dimension()-1) + def join_of_Vrep(self, *Vrepresentatives): + return self.face_generator().join_of_Vrep(*Vrepresentatives) + + def meet_of_facets(self, *facets): + return self.face_generator().meet_of_facets(*facets) + @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 b5f60202be2..56aac10efd1 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2262,8 +2262,8 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C = CombinatorialPolyhedron(P) sage: C.join_of_Vrep(0,1) A 1-dimensional face of a 3-dimensional combinatorial polyhedron - sage: C.join_of_Vrep(0,3).ambient_V_indices() - (0, 1, 2, 3, 4, 5) + sage: C.join_of_Vrep(0,11).ambient_V_indices() + (0, 1, 10, 11, 12, 13) sage: C.join_of_Vrep(8).ambient_V_indices() (8,) sage: C.join_of_Vrep().ambient_V_indices() @@ -2289,8 +2289,8 @@ cdef class CombinatorialPolyhedron(SageObject): (0,) sage: C.meet_of_facets(0,1).ambient_H_indices() (0, 1) - sage: C.meet_of_facets(0,3).ambient_H_indices() - (0, 3) + sage: C.meet_of_facets(0,2).ambient_H_indices() + (0, 2) sage: C.meet_of_facets(0,2,3).ambient_H_indices() (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) sage: C.meet_of_facets().ambient_H_indices() diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 74ddf1bdaae..d102c944dfa 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -525,7 +525,7 @@ cdef class FaceIterator_base(SageObject): sage: it.meet_of_facets(1,2,3).ambient_H_indices() Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: coatoms out of range If the iterator has already been used, it must be reseted before:: @@ -587,7 +587,7 @@ cdef class FaceIterator_base(SageObject): sage: it.join_of_Vrep(8) Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: coatoms out of range If the iterator has already been used, it must be reseted before:: @@ -699,7 +699,7 @@ cdef class FaceIterator_base(SageObject): sage: it._meet_of_coatoms(100) Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: coatoms out of range The empty face is detected correctly, even with lines or rays:: @@ -737,7 +737,8 @@ cdef class FaceIterator_base(SageObject): face_add_atom(face, i) for i in indices: - assert 0 <= i < n_coatoms, "index out of range" + if not 0 <= i < n_coatoms: + raise IndexError("coatoms out of range") face_intersection(face, face, coatoms.data.faces[i]) if not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): @@ -817,11 +818,11 @@ cdef class FaceIterator_base(SageObject): sage: it._join_of_atoms(-1) Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: atoms out of range sage: it._join_of_atoms(100) Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: atoms out of range """ if unlikely(self.structure.face_status != 0): raise ValueError("please reset the face iterator") @@ -837,7 +838,8 @@ cdef class FaceIterator_base(SageObject): cdef face_t pseudo_face = face_mem.data.faces[1] cdef size_t i - assert all(i in range(n_atoms) for i in indices), "index out of range" + if not all(i in range(n_atoms) for i in indices): + raise IndexError("atoms out of range") # Initialize a pseudo_face as indicated by the indices. for i in indices: From 9a66e324a1e5dcd3b176e4a78e49599149bc4c93 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 13 May 2020 14:24:53 +0200 Subject: [PATCH 063/280] expose in Polyhedron_base --- .../geometry/polyhedra_quickref.rst | 2 + src/sage/geometry/polyhedron/base.py | 136 +++++++++++++++++- 2 files changed, 136 insertions(+), 2 deletions(-) diff --git a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst index d684c689c3e..968800e3925 100644 --- a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst +++ b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst @@ -172,6 +172,8 @@ List of Polyhedron methods :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.face_generator` | a generator over the faces :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.faces` | the list of faces :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.facets` | the list of facets + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.join_of_Vrep` | smallest face containing specified Vrepresentatives + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.meet_of_facets` | largest face contained specified facets :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.normal_fan` | returns the fan spanned by the normals of the supporting hyperplanes of the polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.gale_transform` | returns the (affine) Gale transform of the vertices of the polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.hyperplane_arrangement` | returns the hyperplane arrangement given by the defining facets of the polyhedron diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index f811b337133..cfa7a6ffb4d 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6892,10 +6892,142 @@ def facets(self): return self.faces(self.dimension()-1) def join_of_Vrep(self, *Vrepresentatives): - return self.face_generator().join_of_Vrep(*Vrepresentatives) + r""" + Return the smallest face that contains in ``Vrepresentatives``. + + INPUT: + + - ``Vrepresentatives`` -- vertices/rays/lines or indices of such + + OUTPUT: a :class:`~sage.geometry.polyhedron.face.PolyhedronFace` + + .. NOTE:: + + In case of unbounded polyhedra, the join of rays etc. may not be well-defined. + + EXAMPLES:: + + sage: P = polytopes.permutahedron(5) + sage: P.join_of_Vrep(1) + A 0-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 1 vertex + sage: P.join_of_Vrep() + A -1-dimensional face of a Polyhedron in ZZ^5 + sage: P.join_of_Vrep(1,3,4).ambient_V_indices() + (0, 1, 2, 3, 4, 5) + + The input is flexible:: + + sage: P.join_of_Vrep(2, P.vertices()[3], P.Vrepresentation(4)) + A 2-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 6 vertices + + In case of an unbounded polyhedron, the join may not be well-defined:: + + sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) + sage: P.join_of_Vrep(0) + A 0-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex + sage: P.join_of_Vrep(0,1) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 2 vertices + sage: P.join_of_Vrep(0,2) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + sage: P.join_of_Vrep(1,2) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + sage: P.join_of_Vrep(2) + Traceback (most recent call last): + ... + ValueError: the join is not well-defined + """ + from sage.geometry.polyhedron.representation import Vrepresentation + from sage.geometry.polyhedron.face import PolyhedronFace + + new_indices = [0]*len(Vrepresentatives) + for i, v in enumerate(Vrepresentatives): + if isinstance(v, PolyhedronFace) and facet.dim() == 0: + v = v.ambient_V_indices()[0] + + if v in ZZ: + new_indices[i] = v + elif isinstance(v, Vrepresentation): + new_indices[i] = v.index() + else: + raise ValueError("{} is not a Vrepresentative".format(v)) + + return self.face_generator().join_of_Vrep(*new_indices) def meet_of_facets(self, *facets): - return self.face_generator().meet_of_facets(*facets) + r""" + Return the largest face that is contained in ``facets``. + + INPUT: + + - ``facets`` -- facets or indices of facets; + the indices are assumed to be the indices of the Hrepresentation + + OUTPUT: a :class:`~sage.geometry.polyhedron.face.PolyhedronFace` + + EXAMPLES:: + + sage: P = polytopes.permutahedron(5) + sage: P.meet_of_facets() + A 4-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 120 vertices + sage: P.meet_of_facets(1) + A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 24 vertices + sage: P.meet_of_facets(2) + A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 12 vertices + sage: P.meet_of_facets(2,3,4) + A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices + sage: P.meet_of_facets(2,3,4).ambient_H_indices() + (0, 2, 3, 4) + + The indices are the indices of the Hrepresentation:: + + sage: P.meet_of_facets(0) + Traceback (most recent call last): + ... + ValueError: 0 is not a facet + + The input is flexible:: + + sage: P.meet_of_facets(P.facets()[-1], P.inequalities()[1], 3) + A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices + + TESTS: + + The offset is taken correctly:: + + sage: P = polytopes.permutahedron(3, backend='field') + sage: P.Hrepresentation() + (An inequality (1, 1, 0) x - 3 >= 0, + An inequality (1, 0, 0) x - 1 >= 0, + An inequality (0, -1, 0) x + 3 >= 0, + An inequality (0, 2, 0) x - 2 >= 0, + An inequality (-4, -4, 0) x + 20 >= 0, + An inequality (-8, 0, 0) x + 24 >= 0, + An equation (-1/6, -1/6, -1/6) x + 1 == 0) + sage: P.meet_of_facets(0) + A 1-dimensional face of a Polyhedron in QQ^3 defined as the convex hull of 2 vertices + """ + from sage.geometry.polyhedron.representation import Inequality + from sage.geometry.polyhedron.face import PolyhedronFace + + # Equations are ignored by combinatorial polyhedron for indexing. + offset = 0 + if self.n_equations() and self.Hrepresentation(0).is_equation(): + offset = self.n_equations() + + new_indices = [0]*len(facets) + for i, facet in enumerate(facets): + if isinstance(facet, PolyhedronFace) and facet.dim() + 1 == self.dim(): + H_indices = facet.ambient_H_indices() + facet = H_indices[0] if H_indices[0] >= offset else H_indices[-1] + + if facet in ZZ and facet >= offset: + new_indices[i] = facet - offset + elif isinstance(facet, Inequality): + new_indices[i] = facet.index() - offset + else: + raise ValueError("{} is not a facet".format(facet)) + + return self.face_generator().meet_of_facets(*new_indices) @cached_method(do_pickle=True) def f_vector(self): From 977c088966acadcb1b7fd6589e996a68793db697 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 7 Sep 2020 15:36:08 +0200 Subject: [PATCH 064/280] fix doctests --- src/sage/geometry/polyhedron/base.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index cfa7a6ffb4d..1538c31cdc3 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6912,8 +6912,8 @@ def join_of_Vrep(self, *Vrepresentatives): A 0-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 1 vertex sage: P.join_of_Vrep() A -1-dimensional face of a Polyhedron in ZZ^5 - sage: P.join_of_Vrep(1,3,4).ambient_V_indices() - (0, 1, 2, 3, 4, 5) + sage: P.join_of_Vrep(0,12,13).ambient_V_indices() + (0, 12, 13, 68) The input is flexible:: @@ -6971,12 +6971,12 @@ def meet_of_facets(self, *facets): A 4-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 120 vertices sage: P.meet_of_facets(1) A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 24 vertices - sage: P.meet_of_facets(2) + sage: P.meet_of_facets(4) A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 12 vertices - sage: P.meet_of_facets(2,3,4) + sage: P.meet_of_facets(1,3,7) A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices - sage: P.meet_of_facets(2,3,4).ambient_H_indices() - (0, 2, 3, 4) + sage: P.meet_of_facets(1,3,7).ambient_H_indices() + (0, 1, 3, 7) The indices are the indices of the Hrepresentation:: @@ -6987,7 +6987,7 @@ def meet_of_facets(self, *facets): The input is flexible:: - sage: P.meet_of_facets(P.facets()[-1], P.inequalities()[1], 3) + sage: P.meet_of_facets(P.facets()[-1], P.inequalities()[2], 7) A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices TESTS: @@ -6996,13 +6996,13 @@ def meet_of_facets(self, *facets): sage: P = polytopes.permutahedron(3, backend='field') sage: P.Hrepresentation() - (An inequality (1, 1, 0) x - 3 >= 0, + (An inequality (0, 0, 1) x - 1 >= 0, + An inequality (0, 1, 0) x - 1 >= 0, + An inequality (0, 1, 1) x - 3 >= 0, An inequality (1, 0, 0) x - 1 >= 0, - An inequality (0, -1, 0) x + 3 >= 0, - An inequality (0, 2, 0) x - 2 >= 0, - An inequality (-4, -4, 0) x + 20 >= 0, - An inequality (-8, 0, 0) x + 24 >= 0, - An equation (-1/6, -1/6, -1/6) x + 1 == 0) + An inequality (1, 0, 1) x - 3 >= 0, + An inequality (1, 1, 0) x - 3 >= 0, + An equation (1, 1, 1) x - 6 == 0) sage: P.meet_of_facets(0) A 1-dimensional face of a Polyhedron in QQ^3 defined as the convex hull of 2 vertices """ From 15e2c58d57980e142c8ac90e7081d4e6256a5e84 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 16 Jan 2021 21:33:30 +0100 Subject: [PATCH 065/280] improved documentation --- src/sage/geometry/polyhedron/base.py | 35 +++++++++++++------ .../face_iterator.pyx | 18 +++++----- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 1538c31cdc3..19db7fd6ebc 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): @@ -6992,7 +6992,8 @@ def meet_of_facets(self, *facets): TESTS: - The offset is taken correctly:: + Equations are not considered by the combinatorial polyhedron. + We check that the index corresponds to the Hrepresentation index:: sage: P = polytopes.permutahedron(3, backend='field') sage: P.Hrepresentation() @@ -7003,8 +7004,20 @@ def meet_of_facets(self, *facets): An inequality (1, 0, 1) x - 3 >= 0, An inequality (1, 1, 0) x - 3 >= 0, An equation (1, 1, 1) x - 6 == 0) - sage: P.meet_of_facets(0) - A 1-dimensional face of a Polyhedron in QQ^3 defined as the convex hull of 2 vertices + sage: P.meet_of_facets(0).ambient_Hrepresentation() + (An equation (1, 1, 1) x - 6 == 0, An inequality (0, 0, 1) x - 1 >= 0) + + sage: P = polytopes.permutahedron(3, backend='ppl') + sage: P.Hrepresentation() + (An equation (1, 1, 1) x - 6 == 0, + An inequality (1, 1, 0) x - 3 >= 0, + An inequality (-1, -1, 0) x + 5 >= 0, + An inequality (0, 1, 0) x - 1 >= 0, + An inequality (-1, 0, 0) x + 3 >= 0, + An inequality (1, 0, 0) x - 1 >= 0, + An inequality (0, -1, 0) x + 3 >= 0) + sage: P.meet_of_facets(1).ambient_Hrepresentation() + (An equation (1, 1, 1) x - 6 == 0, An inequality (1, 1, 0) x - 3 >= 0) """ from sage.geometry.polyhedron.representation import Inequality from sage.geometry.polyhedron.face import PolyhedronFace @@ -10323,10 +10336,10 @@ def affine_hull_projection(self, as_affine_map=False, orthogonal=False, """ # handle trivial full-dimensional case if self.ambient_dim() == self.dim(): - if as_affine_map: - return linear_transformation(matrix(self.base_ring(), - self.dim(), - self.dim(), + if as_affine_map: + return linear_transformation(matrix(self.base_ring(), + self.dim(), + self.dim(), self.base_ring().one())), self.ambient_space().zero() return self diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index d102c944dfa..49c7d1b7e05 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -487,7 +487,7 @@ cdef class FaceIterator_base(SageObject): This is the largest face contained in all facets with the given indices. - The iterator must be reseted if not newly initialized. + The iterator must be reset if not newly initialized. EXAMPLES:: @@ -527,7 +527,7 @@ cdef class FaceIterator_base(SageObject): ... IndexError: coatoms out of range - If the iterator has already been used, it must be reseted before:: + If the iterator has already been used, it must be reset before:: sage: P = polytopes.dodecahedron() sage: it = P.face_generator() @@ -554,11 +554,11 @@ cdef class FaceIterator_base(SageObject): This is the smallest face containing all Vrepresentatives with the given indices. - The iterator must be reseted if not newly initialized. + The iterator must be reset if not newly initialized. .. NOTE:: - In case of unbounded polyhedra, the smallest face containing given Vrepresentatives + In the case of unbounded polyhedra, the smallest face containing given Vrepresentatives may not te well defined. EXAMPLES:: @@ -589,7 +589,7 @@ cdef class FaceIterator_base(SageObject): ... IndexError: coatoms out of range - If the iterator has already been used, it must be reseted before:: + If the iterator has already been used, it must be reset before:: sage: P = polytopes.dodecahedron() sage: it = P.face_generator() @@ -604,7 +604,7 @@ cdef class FaceIterator_base(SageObject): sage: it.join_of_Vrep(1,10).ambient_V_indices() (1, 10) - In case of an unbounded polyhedron, we try to make sense of the input:: + In the case of an unbounded polyhedron, we try to make sense of the input:: sage: P = polytopes.cube()*Polyhedron(lines=[[1]]) sage: it = P.face_generator() @@ -646,7 +646,7 @@ cdef class FaceIterator_base(SageObject): r""" Construct the meet of the coatoms indicated by the indices. - The iterator must be reseted if not newly initialized. + The iterator must be reset if not newly initialized. .. SEEALSO:: @@ -754,7 +754,7 @@ cdef class FaceIterator_base(SageObject): r""" Construct the join of atoms indicated by the indices. - The iterator must be reseted if not newly initialized. + The iterator must be reset if not newly initialized. .. SEEALSO:: @@ -785,7 +785,7 @@ cdef class FaceIterator_base(SageObject): sage: it._join_of_atoms(1) A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex - If the iterator has already been used, it must be reseted before:: + If the iterator has already been used, it must be reset before:: sage: P = polytopes.dodecahedron() sage: it = P.face_generator() From deca8e0aa9df9eb7d4f77d9e6e984962c07c1543 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 13 May 2021 20:38:18 +0200 Subject: [PATCH 066/280] fix a variable name and add a doctest --- src/sage/geometry/polyhedron/base.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 19db7fd6ebc..9613fdecf01 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6920,6 +6920,13 @@ def join_of_Vrep(self, *Vrepresentatives): sage: P.join_of_Vrep(2, P.vertices()[3], P.Vrepresentation(4)) A 2-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 6 vertices + :: + + sage: P = polytopes.cube() + sage: a, b = P.faces(0)[:2] + sage: P.join_of_Vrep(a, b) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + In case of an unbounded polyhedron, the join may not be well-defined:: sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) @@ -6941,7 +6948,7 @@ def join_of_Vrep(self, *Vrepresentatives): new_indices = [0]*len(Vrepresentatives) for i, v in enumerate(Vrepresentatives): - if isinstance(v, PolyhedronFace) and facet.dim() == 0: + if isinstance(v, PolyhedronFace) and v.dim() == 0: v = v.ambient_V_indices()[0] if v in ZZ: From be121ad494ccdd78ae2e4e4ca7a15ae9a64c0d94 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 11:19:34 +0200 Subject: [PATCH 067/280] implement only subfaces/supfaces for face iterator --- .../face_data_structure.pxd | 3 + .../face_iterator.pxd | 2 + .../face_iterator.pyx | 187 +++++++++++++++++- 3 files changed, 184 insertions(+), 8 deletions(-) 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..f166a7d8fea 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd @@ -186,3 +186,6 @@ cdef inline void swap_faces(face_t a, face_t b) nogil: tmp[0] = a[0] a[0] = b[0] b[0] = tmp[0] + +cdef inline bint faces_are_identical(face_t a, face_t b) nogil: + return a.atoms.limbs == b.atoms.limbs diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index f3a1d885fe8..c515c755b19 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -16,6 +16,7 @@ cdef struct iter_s: int dimension # dimension of the polyhedron int output_dimension # only faces of this (dual?) dimension are considered int lowest_dimension # don't consider faces below this (dual?) dimension + int highest_dimension # don't consider faces above this (dual?) dimension size_t _index # this counts the number of seen faces, useful for hasing the faces # ``visited_all`` points to faces, of which we have visited all faces already. @@ -69,6 +70,7 @@ cdef class FaceIterator_base(SageObject): cdef size_t set_coatom_rep(self) except -1 cdef size_t set_atom_rep(self) except -1 cdef int ignore_subsets(self) except -1 + cdef int only_subsets(self) except -1 cdef int find_face(self, face_t face) except -1 @cython.final diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 49c7d1b7e05..4ef16257eb9 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -223,7 +223,8 @@ cdef class FaceIterator_base(SageObject): self.structure.dual = dual self.structure.face_status = 0 self.structure.dimension = C.dimension() - self.structure.current_dimension = self.structure.dimension -1 + self.structure.current_dimension = self.structure.dimension - 1 + self.structure.highest_dimension = self.structure.dimension - 1 self._mem = MemoryAllocator() # We will not yield the empty face. @@ -347,6 +348,7 @@ cdef class FaceIterator_base(SageObject): self.structure.face_status = 0 self.structure.new_faces[self.structure.dimension - 1].n_faces = self.coatoms.n_faces() self.structure.current_dimension = self.structure.dimension - 1 + self.structure.highest_dimension = self.structure.dimension - 1 self.structure.first_time[self.structure.dimension - 1] = True self.structure.yet_to_visit = self.coatoms.n_faces() @@ -451,6 +453,33 @@ cdef class FaceIterator_base(SageObject): ....: sage: n_non_simplex_faces 127 + + Face iterator must not be in dual mode:: + + sage: it = C.face_iter(dual=True) + sage: _ = next(it) + sage: it.ignore_subfaces() + Traceback (most recent call last): + ... + ValueError: only possible when not in dual mode + + Cannot run ``ignore_subfaces`` after ``only_subfaces:: + + sage: it = C.face_iter(dual=False) + sage: _ = next(it) + sage: it.only_subfaces() + sage: it.ignore_subfaces() + Traceback (most recent call last): + ... + ValueError: cannot ignore a face after setting iterator to only visit subsets + + Face iterator must be set to a face first:: + + sage: it = C.face_iter(dual=False) + sage: it.ignore_subfaces() + Traceback (most recent call last): + ... + ValueError: iterator not set to a face yet """ if unlikely(self.dual): raise ValueError("only possible when not in dual mode") @@ -458,9 +487,9 @@ cdef class FaceIterator_base(SageObject): def ignore_supfaces(self): r""" - The iterator will not visit any faces of the current face. + The iterator will not visit any faces containing the current face. - Only possible when not in dual mode. + Only possible when in dual mode. EXAMPLES:: @@ -476,6 +505,15 @@ cdef class FaceIterator_base(SageObject): ....: sage: n_faces_with_non_simplex_quotient 4845 + + Face iterator must be in dual mode:: + + sage: it = C.face_iter(dual=False) + sage: _ = next(it) + sage: it.ignore_supfaces() + Traceback (most recent call last): + ... + ValueError: only possible when in dual mode """ if unlikely(not self.dual): raise ValueError("only possible when in dual mode") @@ -880,6 +918,8 @@ cdef class FaceIterator_base(SageObject): """ if unlikely(self.structure.face_status == 0): raise ValueError("iterator not set to a face yet") + if unlikely(self.structure.face_status == 3): + raise ValueError("cannot ignore a face after setting iterator to only visit subsets") if unlikely(self.structure.face_status == 2): # Nothing to do. return 0 @@ -891,13 +931,144 @@ cdef class FaceIterator_base(SageObject): add_face_shallow(self.structure.visited_all[self.structure.current_dimension], self.structure.face) self.structure.face_status = 2 + def only_subfaces(self): + r""" + The iterator will visit all (remaining) subfaces of the current face and then terminate. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: it = P.face_generator() + sage: next(it).ambient_H_indices() + () + sage: next(it).ambient_H_indices() + (0, 1, 2, 3, 4, 5) + sage: next(it).ambient_H_indices() + (5,) + sage: next(it).ambient_H_indices() + (4,) + sage: it.only_subfaces() + sage: list(f.ambient_H_indices() for f in it) + [(4, 5), (3, 4), (1, 4), (0, 4), (3, 4, 5), (0, 4, 5), (1, 3, 4), (0, 1, 4)] + + :: + + sage: P = polytopes.Birkhoff_polytope(4) + sage: C = P.combinatorial_polyhedron() + sage: it = C.face_iter() + sage: next(it).ambient_H_indices() + (15,) + sage: next(it).ambient_H_indices() + (14,) + sage: it.only_subfaces() + sage: all(14 in f.ambient_H_indices() for f in it) + True + + Face iterator needs to be set to a face first:: + + sage: it = C.face_iter() + sage: it.only_subfaces() + Traceback (most recent call last): + ... + ValueError: iterator not set to a face yet + + Face iterator must not be in dual mode:: + + sage: it = C.face_iter(dual=True) + sage: _ = next(it) + sage: it.only_subfaces() + Traceback (most recent call last): + ... + ValueError: only possible when not in dual mode + + Cannot run ``only_subfaces`` after ``ignore_subfaces:: + + sage: it = C.face_iter() + sage: _ = next(it) + sage: it.ignore_subfaces() + sage: it.only_subfaces() + Traceback (most recent call last): + ... + ValueError: cannot visit subsets after ignoring a face + """ + if unlikely(self.dual): + raise ValueError("only possible when not in dual mode") + self.only_subsets() + + def only_supfaces(self): + r""" + The iterator will visit all (remaining) faces + containing the current face and then terminate. + + EXAMPLES:: + + sage: P = polytopes.cross_polytope(3) + sage: it = P.face_generator() + sage: next(it).ambient_V_indices() + (0, 1, 2, 3, 4, 5) + sage: next(it).ambient_V_indices() + () + sage: next(it).ambient_V_indices() + (5,) + sage: next(it).ambient_V_indices() + (4,) + sage: it.only_supfaces() + sage: list(f.ambient_V_indices() for f in it) + [(4, 5), (3, 4), (2, 4), (0, 4), (3, 4, 5), (2, 4, 5), (0, 3, 4), (0, 2, 4)] + + :: + + sage: P = polytopes.Birkhoff_polytope(4) + sage: C = P.combinatorial_polyhedron() + sage: it = C.face_iter(dual=True) + sage: next(it).ambient_V_indices() + (23,) + sage: next(it).ambient_V_indices() + (22,) + sage: it.only_supfaces() + sage: all(22 in f.ambient_V_indices() for f in it) + True + """ + if unlikely(not self.dual): + raise ValueError("only possible when in dual mode") + self.only_subsets() + + cdef int only_subsets(self) except -1: + r""" + Only visit sub-/supfaces of the current face and then + terminate. + + See :meth:`FaceIterator_base.only_subfaces` and + :meth:`FaceIterator_base.only_supfaces`. + """ + if unlikely(self.structure.face_status == 0): + raise ValueError("iterator not set to a face yet") + if unlikely(self.structure.face_status == 2): + raise ValueError("cannot only visit subsets after ignoring a face") + + cdef face_list_t* faces = &self.structure.new_faces[self.structure.current_dimension] + cdef size_t yet_to_visit = self.structure.yet_to_visit + + if unlikely(yet_to_visit >= faces[0].n_faces + or not faces_are_identical(faces[0].faces[yet_to_visit], self.structure.face)): + raise ValueError("iterator is not set to the correct face") + + swap_faces(faces[0].faces[yet_to_visit], faces[0].faces[faces[0].n_faces - 1]) + + self.structure.face_status = 3 + self.structure.yet_to_visit = 0 + # This will work: + # ``next_dimension`` will first call ``next_face_loop`` and then check + # for the dimension. By this time the current dimension has changed. + self.structure.highest_dimension = self.structure.current_dimension - 1 + cdef inline CombinatorialFace next_face(self): r""" Set attribute ``face`` to the next face and return it as :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace`. """ self.next_dimension() - if unlikely(self.structure.current_dimension == self.structure.dimension): + if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): return None return CombinatorialFace(self) @@ -1295,7 +1466,7 @@ cdef class FaceIterator(FaceIterator_base): A 1-dimensional face of a 3-dimensional combinatorial polyhedron] """ cdef CombinatorialFace face = self.next_face() - if unlikely(self.structure.current_dimension == self.structure.dimension): + if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): raise StopIteration return face @@ -1588,7 +1759,7 @@ cdef class FaceIterator_geom(FaceIterator_base): return PolyhedronFace(self.P, [], range(self.P.n_Hrepresentation())) self.next_dimension() - if unlikely(self.structure.current_dimension == self.structure.dimension): + if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): raise StopIteration return self.current() @@ -1616,9 +1787,9 @@ cdef inline int next_dimension(iter_t structure) nogil except -1: r""" See :meth:`FaceIterator.next_dimension`. """ - cdef int dim = structure.dimension + cdef int max_dim = structure.highest_dimension structure.face_status = 0 - while (not next_face_loop(structure)) and (structure.current_dimension < dim): + while (not next_face_loop(structure)) and (structure.current_dimension <= max_dim): sig_check() structure._index += 1 return structure.current_dimension From ee42e9895eb8830089e328fae674616ca3e9245c Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 11:47:26 +0200 Subject: [PATCH 068/280] fix bug revealed by a_maximal_chain --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 4ef16257eb9..51f01c56ece 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1068,7 +1068,7 @@ cdef class FaceIterator_base(SageObject): :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace`. """ self.next_dimension() - if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): + if unlikely(self.structure.current_dimension > self.structure.highest_dimension): return None return CombinatorialFace(self) @@ -1466,7 +1466,7 @@ cdef class FaceIterator(FaceIterator_base): A 1-dimensional face of a 3-dimensional combinatorial polyhedron] """ cdef CombinatorialFace face = self.next_face() - if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): + if unlikely(self.structure.current_dimension > self.structure.highest_dimension): raise StopIteration return face @@ -1759,7 +1759,7 @@ cdef class FaceIterator_geom(FaceIterator_base): return PolyhedronFace(self.P, [], range(self.P.n_Hrepresentation())) self.next_dimension() - if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): + if unlikely(self.structure.current_dimension > self.structure.highest_dimension): raise StopIteration return self.current() From 05ec5b29b59e078d5601c4cfa5b1828aa940eab8 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 11:47:47 +0200 Subject: [PATCH 069/280] fix error message in doctest --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 51f01c56ece..352a5761afc 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -989,7 +989,7 @@ cdef class FaceIterator_base(SageObject): sage: it.only_subfaces() Traceback (most recent call last): ... - ValueError: cannot visit subsets after ignoring a face + ValueError: cannot only visit subsets after ignoring a face """ if unlikely(self.dual): raise ValueError("only possible when not in dual mode") From 6ddf2284a5ae25d4680e78615e881df6ef5bf0d9 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 16:49:56 +0200 Subject: [PATCH 070/280] fix the order of the coatoms when resetting the face iterator --- .../face_iterator.pyx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 352a5761afc..1e6c103c75f 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -333,6 +333,25 @@ cdef class FaceIterator_base(SageObject): sage: it.reset() sage: next(it).ambient_V_indices() (0, 3, 4, 5) + + TESTS: + + Resetting will fix the order of the coatoms after ``only_subsets``:: + + sage: P = polytopes.Birkhoff_polytope(3) + sage: C = P.combinatorial_polyhedron() + sage: it = C.face_iter(dual=False) + sage: face = next(it) + sage: face.ambient_H_indices() + (8,) + sage: face = next(it) + sage: face.ambient_H_indices() + (7,) + sage: it.only_subfaces() + sage: it.reset() + sage: face = next(it) + sage: face.ambient_H_indices() + (8,) """ if self.structure.dimension == 0 or self.coatoms.n_faces() == 0: # As we will only yield proper faces, @@ -354,6 +373,9 @@ cdef class FaceIterator_base(SageObject): self.structure.yet_to_visit = self.coatoms.n_faces() self.structure._index = 0 + # ``only_subsets`` might have messed up the coatoms. + face_list_shallow_copy(self.structure.new_faces[self.structure.dimension-1], self.coatoms.data) + def __next__(self): r""" Must be implemented by a derived class. From f0a0afa8353b11cc6594425a7376ca04a8582a37 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 15:16:39 +0200 Subject: [PATCH 071/280] implement is_subface for combinatorial face --- .../combinatorial_face.pyx | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 1d790726f23..aa2d066c23c 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -72,7 +72,7 @@ from .conversions cimport bit_rep_to_Vrep_list from .base cimport CombinatorialPolyhedron 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_data_structure cimport face_len_atoms, face_init, face_copy, face_issubset from .face_list_data_structure cimport bit_rep_to_coatom_rep cdef extern from "Python.h": @@ -347,6 +347,46 @@ cdef class CombinatorialFace(SageObject): # They are faces of the same polyhedron obtained in the same way. return hash(self) < hash(other) + def is_subface(self, other): + r""" + Return whether ``self`` is contained in ``other``. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: C = P.combinatorial_polyhedron() + sage: P = polytopes.cube() + sage: C = P.combinatorial_polyhedron() + sage: it = C.face_iter() + sage: face = next(it) + sage: face.ambient_V_indices() + (0, 3, 4, 5) + sage: face2 = next(it) + sage: face2.ambient_V_indices() + (0, 1, 5, 6) + sage: face.is_subface(face2) + False + sage: face2.is_subface(face) + False + sage: it.only_subfaces() + sage: face3 = next(it) + sage: face3.ambient_V_indices() + (0, 5) + sage: face3.is_subface(face2) + True + sage: face3.is_subface(face) + True + """ + cdef CombinatorialFace other_face + if isinstance(other, CombinatorialFace): + other_face = other + if not self._dual: + return face_issubset(self.face, other_face.face) + else: + return face_issubset(other_face.face, self.face) + else: + raise ValueError("other must be a face") + def dimension(self): r""" Return the dimension of the face. From c632f5609f5e898cebcfb285fd015ad89b89203a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 13 May 2021 09:20:30 +0200 Subject: [PATCH 072/280] only allow subface check for faces of identical combinatorial polyhedron --- .../combinatorial_face.pyx | 65 ++++++++++++++++++- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index aa2d066c23c..4c1641a7b7a 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -376,14 +376,73 @@ cdef class CombinatorialFace(SageObject): True sage: face3.is_subface(face) True + + Works for faces of the same combinatorial polyhedron; + also from different iterators:: + + sage: it = C.face_iter(dual=True) + sage: v7 = next(it); v7.ambient_V_indices() + (7,) + sage: v6 = next(it); v6.ambient_V_indices() + (6,) + sage: v5 = next(it); v5.ambient_V_indices() + (5,) + sage: face.ambient_V_indices() + (0, 3, 4, 5) + sage: face.is_subface(v7) + False + sage: v7.is_subface(face) + False + sage: v6.is_subface(face) + False + sage: v5.is_subface(face) + True + sage: face2.ambient_V_indices() + (0, 1, 5, 6) + sage: face2.is_subface(v7) + False + sage: v7.is_subface(face2) + False + sage: v6.is_subface(face2) + True + sage: v5.is_subface(face2) + True + + Only implemented for faces of the same combintatorial polyhedron:: + + sage: P1 = polytopes.cube() + sage: C1 = P1.combinatorial_polyhedron() + sage: it = C1.face_iter() + sage: other_face = next(it) + sage: other_face.ambient_V_indices() + (0, 3, 4, 5) + sage: face.ambient_V_indices() + (0, 3, 4, 5) + sage: C is C1 + False + sage: face.is_subface(other_face) + Traceback (most recent call last): + ... + NotImplementedError: is_subface only implemented for faces of the same polyhedron """ cdef CombinatorialFace other_face if isinstance(other, CombinatorialFace): other_face = other - if not self._dual: - return face_issubset(self.face, other_face.face) + if self._dual == other_face._dual: + if self.atoms is other_face.atoms: + if not self._dual: + return face_issubset(self.face, other_face.face) + else: + return face_issubset(other_face.face, self.face) + else: + raise NotImplementedError("is_subface only implemented for faces of the same polyhedron") else: - return face_issubset(other_face.face, self.face) + if self.atoms is other_face.coatoms: + self_indices = self.ambient_V_indices() + other_indices = other.ambient_V_indices() + return all(i in other_indices for i in self_indices) + else: + raise NotImplementedError("is_subface only implemented for faces of the same polyhedron") else: raise ValueError("other must be a face") From b49a061c2960e10fa19d8d322ab10cf42dadd82d Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 18 May 2021 10:06:59 +0200 Subject: [PATCH 073/280] equalities -> equations --- .../combinatorial_polyhedron/base.pxd | 9 ++- .../combinatorial_polyhedron/base.pyx | 75 ++++++++++++------- .../combinatorial_face.pxd | 2 +- .../combinatorial_face.pyx | 12 +-- .../face_iterator.pxd | 2 +- .../face_iterator.pyx | 2 +- .../polyhedron_face_lattice.pxd | 2 +- .../polyhedron_face_lattice.pyx | 4 +- 8 files changed, 64 insertions(+), 44 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd index 59c77ec0faa..cc928cd0af8 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd @@ -12,12 +12,12 @@ cdef class CombinatorialPolyhedron(SageObject): # Do not assume any of those attributes to be initialized, use the corresponding methods instead. cdef tuple _Vrep # the names of VRep, if they exist - cdef tuple _facet_names # the names of HRep without equalities, if they exist - cdef tuple _equalities # stores equalities, given on input (might belong to Hrep) + cdef tuple _facet_names # the names of HRep without equations, if they exist + cdef tuple _equations # stores equations, given on input (might belong to Hrep) cdef int _dimension # stores dimension, -2 on init - cdef unsigned int _n_Hrepresentation # Hrep might include equalities + cdef unsigned int _n_Hrepresentation # Hrep might include equations cdef unsigned int _n_Vrepresentation # Vrep might include rays/lines - cdef size_t _n_facets # length Hrep without equalities + cdef size_t _n_facets # length Hrep without equations cdef bint _bounded # ``True`` iff Polyhedron is bounded cdef ListOfFaces _bitrep_facets # facets in bit representation cdef ListOfFaces _bitrep_Vrep # vertices in bit representation @@ -44,6 +44,7 @@ cdef class CombinatorialPolyhedron(SageObject): cdef tuple Vrep(self) cdef tuple facet_names(self) + cdef tuple equations(self) cdef tuple equalities(self) cdef unsigned int n_Vrepresentation(self) cdef unsigned int n_Hrepresentation(self) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index d11e8577d88..bd2cf9a79be 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -12,7 +12,7 @@ the ridges and the face lattice. Terminology used in this module: - Vrep -- ``[vertices, rays, lines]`` of the polyhedron. -- Hrep -- inequalities and equalities of the polyhedron. +- Hrep -- inequalities and equations of the polyhedron. - Facets -- facets of the polyhedron. - Vrepresentation -- represents a face by the list of Vrep it contains. - Hrepresentation -- represents a face by a list of Hrep it is contained in. @@ -337,7 +337,7 @@ cdef class CombinatorialPolyhedron(SageObject): self._edges = NULL self._ridges = NULL self._face_lattice_incidences = NULL - self._equalities = () + self._equations = () self._all_faces = None self._mem_tuple = () cdef MemoryAllocator mem @@ -413,20 +413,20 @@ cdef class CombinatorialPolyhedron(SageObject): Vinv = None if facets: - # store facets names and compute equalities + # store facets names and compute equations facets = tuple(facets) - test = [1] * len(facets) # 0 if that facet is an equality + test = [1] * len(facets) # 0 if that facet is an equation for i in range(len(facets)): if hasattr(facets[i], "is_inequality"): - # We remove equalities. - # At the moment only equalities with this attribute ``True`` + # We remove equations. + # At the moment only equations with this attribute ``True`` # will be detected. if not facets[i].is_inequality(): test[i] = 0 self._facet_names = tuple(facets[i] for i in range(len(facets)) if test[i]) - self._equalities = tuple(facets[i] for i in range(len(facets)) if not test[i]) + self._equations = tuple(facets[i] for i in range(len(facets)) if not test[i]) else: self._facet_names = None @@ -682,7 +682,7 @@ cdef class CombinatorialPolyhedron(SageObject): def Hrepresentation(self): r""" - Return a list of names of facets and possibly some equalities. + Return a list of names of facets and possibly some equations. EXAMPLES:: @@ -716,7 +716,7 @@ cdef class CombinatorialPolyhedron(SageObject): (M(0, 1), M(1, 0)) """ if self.facet_names() is not None: - return self.equalities() + self.facet_names() + return self.equations() + self.facet_names() else: return tuple(smallInteger(i) for i in range(self.n_Hrepresentation())) @@ -1091,17 +1091,17 @@ cdef class CombinatorialPolyhedron(SageObject): incidence_matrix.set_immutable() return incidence_matrix - # If equalities are present, we add them as first columns. - n_equalities = 0 + # If equations are present, we add them as first columns. + n_equations = 0 if self.facet_names() is not None: - n_equalities = len(self.equalities()) - for Hindex in range(n_equalities): + n_equations = len(self.equations()) + for Hindex in range(n_equations): for Vindex in range(self.n_Vrepresentation()): incidence_matrix.set_unsafe_si(Vindex, Hindex, 1) facet_iter = self.face_iter(self.dimension() - 1, dual=False) for facet in facet_iter: - Hindex = facet.ambient_H_indices()[0] + n_equalities + Hindex = facet.ambient_H_indices()[0] + n_equations for Vindex in facet.ambient_V_indices(): incidence_matrix.set_unsafe_si(Vindex, Hindex, 1) @@ -1249,7 +1249,7 @@ cdef class CombinatorialPolyhedron(SageObject): deprecation(28603, "the method edge_graph of CombinatorialPolyhedron is deprecated; use vertex_graph", 3) return Graph(self.edges(names=names), format="list_of_edges") - def ridges(self, add_equalities=False, names=True): + def ridges(self, add_equations=False, names=True, add_equalities=False): r""" Return the ridges. @@ -1263,7 +1263,7 @@ cdef class CombinatorialPolyhedron(SageObject): INPUT: - - ``add_equalities`` -- if ``True``, then equalities of the polyhedron + - ``add_equations`` -- if ``True``, then equations of the polyhedron will be added (only applicable when ``names`` is ``True``) - ``names`` -- if ``False``, then the facets are given by their indices @@ -1279,7 +1279,7 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C = CombinatorialPolyhedron(P) sage: C.ridges() ((An inequality (1, 0) x - 1 >= 0, An inequality (-1, 0) x + 2 >= 0),) - sage: C.ridges(add_equalities=True) + sage: C.ridges(add_equations=True) (((An inequality (1, 0) x - 1 >= 0, An equation (1, 1) x - 3 == 0), (An inequality (-1, 0) x + 2 >= 0, An equation (1, 1) x - 3 == 0)),) @@ -1330,12 +1330,26 @@ cdef class CombinatorialPolyhedron(SageObject): TESTS: - Testing that ``add_equalities`` is ignored if ``names`` is ``False``:: + Testing that ``add_equations`` is ignored if ``names`` is ``False``:: sage: C = CombinatorialPolyhedron(polytopes.simplex()) - sage: C.ridges(names=False, add_equalities=True) + sage: C.ridges(names=False, add_equations=True) ((2, 3), (1, 3), (0, 3), (1, 2), (0, 2), (0, 1)) + + The keyword ``add_equalities`` is deprecated:: + + sage: C = CombinatorialPolyhedron(polytopes.simplex()) + sage: r = C.ridges(add_equations=True) + sage: r1 = C.ridges(add_equalities=True) + doctest:...: DeprecationWarning: the keyword ``add_equalities`` is deprecated; use ``add_equations`` + See https://trac.sagemath.org/31834 for details. + sage: r == r1 + True """ + if add_equalities: + from sage.misc.superseded import deprecation + deprecation(31834, "the keyword ``add_equalities`` is deprecated; use ``add_equations``", 3) + add_equations = True if self._ridges is NULL: # compute the ridges. if not self.is_bounded(): @@ -1364,11 +1378,11 @@ cdef class CombinatorialPolyhedron(SageObject): return f(self._get_edge(self._ridges, i, 1)) cdef size_t j - if add_equalities and names: - # Also getting the equalities for each facet. + if add_equations and names: + # Also getting the equations for each facet. return tuple( - (((facet_one(i),) + self.equalities()), - ((facet_two(i),) + self.equalities())) + (((facet_one(i),) + self.equations()), + ((facet_two(i),) + self.equations())) for i in range(n_ridges)) else: return tuple((facet_one(i), facet_two(i)) @@ -1414,7 +1428,7 @@ cdef class CombinatorialPolyhedron(SageObject): V = list(facet.ambient_Hrepresentation() for facet in face_iter) else: V = list(facet.ambient_V_indices() for facet in face_iter) - E = self.ridges(names=names, add_equalities=True) + E = self.ridges(names=names, add_equations=True) if not names: # If names is false, the ridges are given as tuple of indices, # i.e. (1,2) instead of (('f1',), ('f2',)). @@ -2691,13 +2705,18 @@ cdef class CombinatorialPolyhedron(SageObject): """ return self._facet_names - cdef tuple equalities(self): + cdef tuple equations(self): r""" - Return the names of the equalities. + Return the names of the equations. - If not equalities are given, return ``None``. + If not equations are given, return ``None``. """ - return self._equalities + return self._equations + + cdef tuple equalities(self): + from sage.misc.superseded import deprecation + deprecation(31834, "the method equalities of CombinatorialPolyhedron is deprecated; use equations", 3) + return self.equations() cdef unsigned int n_Vrepresentation(self): r""" diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd index 64dd767cc94..4c50daeaf55 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd @@ -23,7 +23,7 @@ cdef class CombinatorialFace(SageObject): cdef bint _initialized_from_face_lattice # some copies from ``CombinatorialPolyhedron`` - cdef tuple _ambient_Vrep, _ambient_facets, _equalities + cdef tuple _ambient_Vrep, _ambient_facets, _equations # 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..7157d72ff77 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -181,7 +181,7 @@ cdef class CombinatorialFace(SageObject): self._ambient_dimension = it.structure.dimension self._ambient_Vrep = it._Vrep self._ambient_facets = it._facet_names - self._equalities = it._equalities + self._equations = it._equations self._hash_index = it.structure._index self._initialized_from_face_lattice = False @@ -206,7 +206,7 @@ cdef class CombinatorialFace(SageObject): self._ambient_dimension = all_faces.dimension self._ambient_Vrep = all_faces._Vrep self._ambient_facets = all_faces._facet_names - self._equalities = all_faces._equalities + self._equations = all_faces._equations self._initialized_from_face_lattice = True @@ -580,7 +580,7 @@ cdef class CombinatorialFace(SageObject): defining the face. It consists of the facets/inequalities that contain the face - and the equalities defining the ambient polyhedron. + and the equations defining the ambient polyhedron. EXAMPLES:: @@ -626,12 +626,12 @@ cdef class CombinatorialFace(SageObject): # if not dual, the facet-representation corresponds to the coatom-representation length = self.set_coatom_rep() # fill self.coatom_repr_face return tuple(self._ambient_facets[self.coatom_rep[i]] - for i in range(length)) + self._equalities + for i in range(length)) + self._equations else: # if dual, the facet-representation corresponds to the atom-representation length = self.set_atom_rep() # fill self.atom_repr_face return tuple(self._ambient_facets[self.atom_rep[i]] - for i in range(length)) + self._equalities + for i in range(length)) + self._equations def ambient_H_indices(self): r""" @@ -694,7 +694,7 @@ cdef class CombinatorialFace(SageObject): and equations of the face. The facet-representation consists of the facets - that contain the face and of the equalities of the polyhedron. + that contain the face and of the equations of the polyhedron. INPUT: diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 63ed7858024..fe334403672 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -55,7 +55,7 @@ cdef class FaceIterator_base(SageObject): cdef MemoryAllocator _mem # some copies from ``CombinatorialPolyhedron`` - cdef tuple _Vrep, _facet_names, _equalities + cdef tuple _Vrep, _facet_names, _equations cdef bint _bounded # Atoms and coatoms are the vertices/facets of the Polyedron. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index fc4929bf29f..a1438030ea3 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -252,7 +252,7 @@ cdef class FaceIterator_base(SageObject): self.atoms = C.bitrep_Vrep() self._Vrep = C.Vrep() self._facet_names = C.facet_names() - self._equalities = C.equalities() + self._equations = C.equations() self._bounded = C.is_bounded() self.structure.atom_rep = self._mem.allocarray(self.coatoms.n_atoms(), sizeof(size_t)) 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..35423d468cb 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd @@ -15,7 +15,7 @@ cdef class PolyhedronFaceLattice: cdef size_t *coatom_rep # a place where coatom-representation of face will be stored # some copies from CombinatorialPolyhedron - cdef tuple _Vrep, _facet_names, _equalities + cdef tuple _Vrep, _facet_names, _equations # 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..c2c37a6c850 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx @@ -9,7 +9,7 @@ the face lattice of a polyhedron. Terminology in this module: - Vrep -- ``[vertices, rays, lines]`` of the polyhedron. -- Hrep -- inequalities and equalities of the polyhedron. +- Hrep -- inequalities and equations of the polyhedron. - Facets -- facets of the polyhedron. - Coatoms -- the faces from which all others are constructed in the face iterator. This will be facets or Vrep. @@ -138,7 +138,7 @@ cdef class PolyhedronFaceLattice: cdef FaceIterator face_iter = C._face_iter(self.dual, -2) self._Vrep = C.Vrep() self._facet_names = C.facet_names() - self._equalities = C.equalities() + self._equations = C.equations() # copy f_vector for later use f_vector = C.f_vector() From 9dc75c26e4d3089e46444eec12aec0bba9e9b4d8 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 18 May 2021 12:17:56 +0200 Subject: [PATCH 074/280] have equations always last --- .../combinatorial_polyhedron/base.pyx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index bd2cf9a79be..0610e050460 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -689,13 +689,13 @@ cdef class CombinatorialPolyhedron(SageObject): sage: P = polytopes.permutahedron(3) sage: C = CombinatorialPolyhedron(P) sage: C.Hrepresentation() - (An equation (1, 1, 1) x - 6 == 0, - An inequality (1, 1, 0) x - 3 >= 0, + (An inequality (1, 1, 0) x - 3 >= 0, An inequality (-1, -1, 0) x + 5 >= 0, An inequality (0, 1, 0) x - 1 >= 0, An inequality (-1, 0, 0) x + 3 >= 0, An inequality (1, 0, 0) x - 1 >= 0, - An inequality (0, -1, 0) x + 3 >= 0) + An inequality (0, -1, 0) x + 3 >= 0, + An equation (1, 1, 1) x - 6 == 0) sage: points = [(1,0,0), (0,1,0), (0,0,1), ....: (-1,0,0), (0,-1,0), (0,0,-1)] @@ -716,7 +716,7 @@ cdef class CombinatorialPolyhedron(SageObject): (M(0, 1), M(1, 0)) """ if self.facet_names() is not None: - return self.equations() + self.facet_names() + return self.facet_names() + self.equations() else: return tuple(smallInteger(i) for i in range(self.n_Hrepresentation())) @@ -1041,7 +1041,7 @@ cdef class CombinatorialPolyhedron(SageObject): :: - sage: P = polytopes.permutahedron(5) + sage: P = polytopes.permutahedron(5, backend='field') sage: C = P.combinatorial_polyhedron() sage: C.incidence_matrix.clear_cache() sage: C.incidence_matrix() == P.incidence_matrix() @@ -1091,17 +1091,17 @@ cdef class CombinatorialPolyhedron(SageObject): incidence_matrix.set_immutable() return incidence_matrix - # If equations are present, we add them as first columns. - n_equations = 0 + # If equations are present, we add them as last columns. + n_facets = self.n_facets() if self.facet_names() is not None: n_equations = len(self.equations()) - for Hindex in range(n_equations): + for Hindex in range(n_facets, n_facets + n_equations): for Vindex in range(self.n_Vrepresentation()): incidence_matrix.set_unsafe_si(Vindex, Hindex, 1) facet_iter = self.face_iter(self.dimension() - 1, dual=False) for facet in facet_iter: - Hindex = facet.ambient_H_indices()[0] + n_equations + Hindex = facet.ambient_H_indices()[0] for Vindex in facet.ambient_V_indices(): incidence_matrix.set_unsafe_si(Vindex, Hindex, 1) From a7d13f5d8f7eb14dc755f3e4bd41cdfda01e6d8a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 18 May 2021 12:42:32 +0200 Subject: [PATCH 075/280] make Hrep methods of combinatorial face more consistent --- .../combinatorial_polyhedron/base.pyx | 6 +-- .../combinatorial_face.pyx | 54 +++++++++++++++---- .../face_iterator.pyx | 3 +- src/sage/geometry/polyhedron/face.py | 4 +- 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 0610e050460..624bf7b5928 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2253,7 +2253,7 @@ cdef class CombinatorialPolyhedron(SageObject): vertex_iter = self._face_iter(True, 0) n_facets = self.n_facets() for vertex in vertex_iter: - if vertex.n_ambient_Hrepresentation() == n_facets - 1: + if vertex.n_ambient_Hrepresentation(add_equations=False) == n_facets - 1: if certificate: return (True, vertex.ambient_Vrepresentation()[0]) return True @@ -2310,11 +2310,11 @@ cdef class CombinatorialPolyhedron(SageObject): An inequality (0, 0, 0, -1, 0) x + 5 >= 0, An equation (1, 1, 1, 1, 1) x - 15 == 0) sage: face.ambient_H_indices() - (25, 29) + (25, 29, 30) sage: face = next(it); face A 2-dimensional face of a 4-dimensional combinatorial polyhedron sage: face.ambient_H_indices() - (24, 29) + (24, 29, 30) sage: face.ambient_V_indices() (32, 89, 90, 94) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 7157d72ff77..77856af971b 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -633,19 +633,23 @@ cdef class CombinatorialFace(SageObject): return tuple(self._ambient_facets[self.atom_rep[i]] for i in range(length)) + self._equations - def ambient_H_indices(self): + def ambient_H_indices(self, add_equations=True): r""" Return the indices of the Hrepresentation objects of the ambient polyhedron defining the face. + INPUT: + + - ``add_equations`` -- boolean (default: ``True``); whether or not to include the equations + EXAMPLES:: sage: P = polytopes.permutahedron(5) sage: C = CombinatorialPolyhedron(P) sage: it = C.face_iter(2) - sage: next(it).ambient_H_indices() + sage: next(it).ambient_H_indices(add_equations=False) (28, 29) - sage: next(it).ambient_H_indices() + sage: next(it).ambient_H_indices(add_equations=False) (25, 29) sage: P = polytopes.cyclic_polytope(4,6) @@ -664,21 +668,37 @@ cdef class CombinatorialFace(SageObject): sage: face.ambient_H_indices() (4, 5, 7) + Add the indices of the equation:: + + sage: face.ambient_H_indices(add_equations=True) + (4, 5, 7) + .. SEEALSO:: :meth:`ambient_Hrepresentation`. """ - cdef size_t length + cdef size_t length, i + cdef size_t n_facets, n_equations + cdef tuple equations + + if add_equations and self._ambient_facets: + n_facets = len(self._ambient_facets) + n_equations = len(self._equations) + equations = tuple(smallInteger(i) + for i in range(n_facets, n_facets + n_equations)) + else: + equations = () + if not self._dual: # if not dual, the facet-representation corresponds to the coatom-representation length = self.set_coatom_rep() # fill self.coatom_repr_face return tuple(smallInteger(self.coatom_rep[i]) - for i in range(length)) + for i in range(length)) + equations else: # if dual, the facet-representation corresponds to the atom-representation length = self.set_atom_rep() # fill self.atom_repr_face return tuple(smallInteger(self.atom_rep[i]) - for i in range(length)) + for i in range(length)) + equations def Hrepr(self, names=True): r""" @@ -720,12 +740,16 @@ cdef class CombinatorialFace(SageObject): else: return self.ambient_H_indices() - def n_ambient_Hrepresentation(self): + def n_ambient_Hrepresentation(self, add_equations=True): r""" Return the length of the :meth:`CombinatorialFace.ambient_H_indices`. Might be faster than then using ``len``. + INPUT: + + - ``add_equations`` -- boolean (default: ``True``); whether or not to count the equations + EXAMPLES:: sage: P = polytopes.cube() @@ -734,6 +758,17 @@ cdef class CombinatorialFace(SageObject): sage: all(face.n_ambient_Hrepresentation() == len(face.ambient_Hrepresentation()) for face in it) True + Specifying whether to count the equations or not:: + + sage: P = polytopes.permutahedron(5) + sage: C = CombinatorialPolyhedron(P) + sage: it = C.face_iter(2) + sage: f = next(it) + sage: f.n_ambient_Hrepresentation(add_equations=True) + 3 + sage: f.n_ambient_Hrepresentation(add_equations=False) + 2 + TESTS:: sage: P = polytopes.cube() @@ -744,10 +779,11 @@ cdef class CombinatorialFace(SageObject): doctest:...: DeprecationWarning: n_Hrepr is deprecated. Please use n_ambient_Hrepresentation instead. See https://trac.sagemath.org/28614 for details. """ + cdef size_t n_equations = len(self._equations) if add_equations else 0 if not self._dual: - return smallInteger(self.set_coatom_rep()) + return smallInteger(self.set_coatom_rep() + n_equations) else: - return smallInteger(self.n_atom_rep()) + return smallInteger(self.n_atom_rep() + n_equations) n_Hrepr = deprecated_function_alias(28614, n_ambient_Hrepresentation) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index a1438030ea3..3e83ff5edb9 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -469,7 +469,8 @@ cdef class FaceIterator_base(SageObject): sage: it = C.face_iter(dual=True) sage: n_faces_with_non_simplex_quotient = 1 sage: for face in it: - ....: if face.n_ambient_Hrepresentation() > C.dimension() - face.dimension() + 1: + ....: n_facets = face.n_ambient_Hrepresentation(add_equations=False) + ....: if n_facets > C.dimension() - face.dimension() + 1: ....: n_faces_with_non_simplex_quotient += 1 ....: else: ....: it.ignore_supfaces() diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 7a00f46ab19..1877ceab5fb 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -848,12 +848,12 @@ def combinatorial_face_to_polyhedral_face(polyhedron, combinatorial_face): if polyhedron.backend() in ('ppl',): # Equations before inequalities in Hrep. H_indices = tuple(range(n_equations)) - H_indices += tuple(x+n_equations for x in combinatorial_face.ambient_H_indices()) + H_indices += tuple(x+n_equations for x in combinatorial_face.ambient_H_indices(add_equations=False)) elif polyhedron.backend() in ('normaliz', 'cdd', 'field', 'polymake'): # Equations after the inequalities in Hrep. n_ieqs = polyhedron.n_inequalities() H_indices = tuple(range(n_ieqs, n_ieqs + n_equations)) - H_indices += tuple(x for x in combinatorial_face.ambient_H_indices()) + H_indices += tuple(x for x in combinatorial_face.ambient_H_indices(add_equations=False)) else: raise NotImplementedError("unknown backend") From d2ee51ed4e3267241e127e39a6e79d72548b5e95 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 18 May 2021 14:14:52 +0200 Subject: [PATCH 076/280] meaningful doctest for ambient_H_indices --- .../combinatorial_polyhedron/combinatorial_face.pyx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 77856af971b..a71b5024f62 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -670,8 +670,13 @@ cdef class CombinatorialFace(SageObject): Add the indices of the equation:: - sage: face.ambient_H_indices(add_equations=True) - (4, 5, 7) + sage: P = polytopes.permutahedron(5) + sage: C = CombinatorialPolyhedron(P) + sage: it = C.face_iter(2) + sage: next(it).ambient_H_indices(add_equations=True) + (28, 29, 30) + sage: next(it).ambient_H_indices(add_equations=False) + (25, 29) .. SEEALSO:: From 0111d1fa10c593d5be99d961901d3735e2aebc1a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 18 May 2021 14:39:38 +0200 Subject: [PATCH 077/280] make add_equalities more stable --- .../combinatorial_face.pxd | 1 + .../combinatorial_face.pyx | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd index 4c50daeaf55..0d7731f4265 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, _equations + cdef size_t _n_equations, _n_ambient_facets # 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 a71b5024f62..65979ba866e 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -182,6 +182,7 @@ cdef class CombinatorialFace(SageObject): self._ambient_Vrep = it._Vrep self._ambient_facets = it._facet_names self._equations = it._equations + self._n_equations = len(self._equations) if self._equations else 0 self._hash_index = it.structure._index self._initialized_from_face_lattice = False @@ -207,6 +208,7 @@ cdef class CombinatorialFace(SageObject): self._ambient_Vrep = all_faces._Vrep self._ambient_facets = all_faces._facet_names self._equations = all_faces._equations + self._n_equations = len(self._equations) if self._equations else 0 self._initialized_from_face_lattice = True @@ -226,6 +228,10 @@ cdef class CombinatorialFace(SageObject): # Reverse the hash index in dual mode to respect inclusion of faces. self._hash_index = -self._hash_index - 1 + self._n_ambient_facets = self.atoms.n_faces() + else: + self._n_ambient_facets = self.coatoms.n_faces() + def _repr_(self): r""" Return a description of the combinatorial face. @@ -686,11 +692,10 @@ cdef class CombinatorialFace(SageObject): cdef size_t n_facets, n_equations cdef tuple equations - if add_equations and self._ambient_facets: - n_facets = len(self._ambient_facets) - n_equations = len(self._equations) + if add_equations and self._equations: equations = tuple(smallInteger(i) - for i in range(n_facets, n_facets + n_equations)) + for i in range(self._n_ambient_facets, + self._n_ambient_facets + self._n_equations)) else: equations = () @@ -784,7 +789,7 @@ cdef class CombinatorialFace(SageObject): doctest:...: DeprecationWarning: n_Hrepr is deprecated. Please use n_ambient_Hrepresentation instead. See https://trac.sagemath.org/28614 for details. """ - cdef size_t n_equations = len(self._equations) if add_equations else 0 + cdef size_t n_equations = self._n_equations if add_equations else 0 if not self._dual: return smallInteger(self.set_coatom_rep() + n_equations) else: From 78882fda4b40a5d4038f220ebdba1059f0518456 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 18 May 2021 18:23:35 +0200 Subject: [PATCH 078/280] improved documentation and removed redundant definitions --- .../combinatorial_face.pyx | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 65979ba866e..439a6f7d7bf 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -653,11 +653,22 @@ cdef class CombinatorialFace(SageObject): sage: P = polytopes.permutahedron(5) sage: C = CombinatorialPolyhedron(P) sage: it = C.face_iter(2) - sage: next(it).ambient_H_indices(add_equations=False) + sage: face = next(it) + sage: face.ambient_H_indices(add_equations=False) (28, 29) - sage: next(it).ambient_H_indices(add_equations=False) + sage: face2 = next(it) + sage: face2.ambient_H_indices(add_equations=False) (25, 29) + Add the indices of the equation:: + + sage: face.ambient_H_indices(add_equations=True) + (28, 29, 30) + sage: face2.ambient_H_indices(add_equations=True) + (25, 29, 30) + + Another example:: + sage: P = polytopes.cyclic_polytope(4,6) sage: C = CombinatorialPolyhedron(P) sage: it = C.face_iter() @@ -674,22 +685,11 @@ cdef class CombinatorialFace(SageObject): sage: face.ambient_H_indices() (4, 5, 7) - Add the indices of the equation:: - - sage: P = polytopes.permutahedron(5) - sage: C = CombinatorialPolyhedron(P) - sage: it = C.face_iter(2) - sage: next(it).ambient_H_indices(add_equations=True) - (28, 29, 30) - sage: next(it).ambient_H_indices(add_equations=False) - (25, 29) - .. SEEALSO:: :meth:`ambient_Hrepresentation`. """ cdef size_t length, i - cdef size_t n_facets, n_equations cdef tuple equations if add_equations and self._equations: From a5276e9dda61b8bb0c29ad30c51517a0685cc710 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Wed, 19 May 2021 13:37:48 +0100 Subject: [PATCH 079/280] Fixed minor typos in documentation. --- src/sage/numerical/gauss_legendre.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index fba5a931d2e..2fcc2d7c797 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -53,7 +53,7 @@ def nodes(degree,prec): A list of (node,weight) pairs. - EXAMPLES: + EXAMPLES:: The nodes for the Gauss-Legendre scheme are roots of Legendre polynomials. The weights can be computed by a straightforward formula (note that evaluating @@ -183,11 +183,11 @@ def integrate_vector(f,prec,epsilon=None): INPUT: - - `f` -- callable. Vector-valued integrand. + - ``f`` -- callable. Vector-valued integrand. - - `prec` -- integer. Binary precision to be used. + - ``prec`` -- integer. Binary precision to be used. - - `epsilon` -- Multiprecision float. Target error bound. + - ``epsilon`` -- Multiprecision float. Target error bound. OUTPUT: From 25de863dde75e7ab0e4997e14d74cfd6ac7e797b Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 19 May 2021 21:53:56 +0200 Subject: [PATCH 080/280] pycodestyle and fix order of equations of faces --- src/sage/geometry/polyhedron/backend_cdd.py | 4 +-- src/sage/geometry/polyhedron/base.py | 16 +++++----- src/sage/geometry/polyhedron/face.py | 34 +++++++++++++-------- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_cdd.py b/src/sage/geometry/polyhedron/backend_cdd.py index 44236fee102..d839bc3ca5e 100644 --- a/src/sage/geometry/polyhedron/backend_cdd.py +++ b/src/sage/geometry/polyhedron/backend_cdd.py @@ -252,8 +252,8 @@ def _init_from_cdd_output(self, cddout): An inequality (1, 0) x - 1 >= 0, An equation (1, 1) x - 3 == 0) sage: [x.ambient_Hrepresentation() for x in P.facets()] - [(An equation (1, 1) x - 3 == 0, An inequality (1, 0) x - 1 >= 0), - (An equation (1, 1) x - 3 == 0, An inequality (0, 1) x - 1 >= 0)] + [(An inequality (1, 0) x - 1 >= 0, An equation (1, 1) x - 3 == 0), + (An inequality (0, 1) x - 1 >= 0, An equation (1, 1) x - 3 == 0)] """ cddout = cddout.splitlines() diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 843fe204cb2..a2293ec9c8b 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): @@ -10185,10 +10185,10 @@ def affine_hull_projection(self, as_affine_map=False, orthogonal=False, """ # handle trivial full-dimensional case if self.ambient_dim() == self.dim(): - if as_affine_map: - return linear_transformation(matrix(self.base_ring(), - self.dim(), - self.dim(), + if as_affine_map: + return linear_transformation(matrix(self.base_ring(), + self.dim(), + self.dim(), self.base_ring().one())), self.ambient_space().zero() return self diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 1877ceab5fb..7e0780fea79 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -614,22 +614,32 @@ def _repr_(self): if self.n_vertices() > 0: desc += ' defined as the convex hull of ' desc += repr(self.n_vertices()) - if self.n_vertices() == 1: desc += ' vertex' - else: desc += ' vertices' + if self.n_vertices() == 1: + desc += ' vertex' + else: + desc += ' vertices' if self.n_rays() > 0: - if self.n_lines() > 0: desc += ", " - else: desc += " and " + if self.n_lines() > 0: + desc += ", " + else: + desc += " and " desc += repr(self.n_rays()) - if self.n_rays() == 1: desc += ' ray' - else: desc += ' rays' + if self.n_rays() == 1: + desc += ' ray' + else: + desc += ' rays' if self.n_lines() > 0: - if self.n_rays() > 0: desc += ", " - else: desc += " and " + if self.n_rays() > 0: + desc += ", " + else: + desc += " and " desc += repr(self.n_lines()) - if self.n_lines() == 1: desc += ' line' - else: desc += ' lines' + if self.n_lines() == 1: + desc += ' line' + else: + desc += ' lines' return desc @@ -852,8 +862,8 @@ def combinatorial_face_to_polyhedral_face(polyhedron, combinatorial_face): elif polyhedron.backend() in ('normaliz', 'cdd', 'field', 'polymake'): # Equations after the inequalities in Hrep. n_ieqs = polyhedron.n_inequalities() - H_indices = tuple(range(n_ieqs, n_ieqs + n_equations)) - H_indices += tuple(x for x in combinatorial_face.ambient_H_indices(add_equations=False)) + H_indices = tuple(x for x in combinatorial_face.ambient_H_indices(add_equations=False)) + H_indices += tuple(range(n_ieqs, n_ieqs + n_equations)) else: raise NotImplementedError("unknown backend") From fdbe95f9cf2b4787a59a459231074a7c528397f3 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 20 May 2021 10:48:00 +0200 Subject: [PATCH 081/280] use cython.parallel.threadid instead of omp_get_thread_num --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index fd6280b48d7..c6b07ec048f 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -185,8 +185,7 @@ from .base cimport CombinatorialPolyhedron from sage.geometry.polyhedron.face import combinatorial_face_to_polyhedral_face, PolyhedronFace from .face_list_data_structure cimport * -from cython.parallel cimport prange -cimport openmp +from cython.parallel cimport prange, threadid cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -1368,9 +1367,9 @@ cdef int parallel_f_vector(iter_t* structures, size_t num_threads, size_t parall for i in prange(n_jobs, schedule='dynamic', chunksize=1, num_threads=num_threads, nogil=True): - _parallel_f_vector(structures[openmp.omp_get_thread_num()], + _parallel_f_vector(structures[threadid()], parallelization_depth, - parallel_structs[openmp.omp_get_thread_num()], + parallel_structs[threadid()], i) # Gather the results. From da0536b759b98143a162aef6948a489b4c32aa25 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Fri, 21 May 2021 11:23:31 +0200 Subject: [PATCH 082/280] Replace reach with _language_naive --- src/sage/combinat/words/morphism.py | 32 ++++------------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index d9a0db1b2fe..ca927d607b5 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3446,7 +3446,8 @@ def impl(): if w is None: w = self._morph - f = self.restrict_domain(self.reach(w)) + reach = self._language_naive(2, self._domain(w)) + f = self.restrict_domain([x[0] for x in reach]) f._codomain = f._domain g, _, k, _ = f.simplify_injective() g._codomain = g._domain @@ -3502,7 +3503,8 @@ def infinite_repetitions_growing(self, w=None): """ if w is None: w = self._morph - f = self.restrict_domain(self.reach(w)) + reach = self._language_naive(2, self._domain(w)) + f = self.restrict_domain([x[0] for x in reach]) f._codomain = f._domain g, _, k, _ = f.simplify_injective() g._codomain = g._domain @@ -3540,32 +3542,6 @@ def infinite_repetitions_growing(self, w=None): return result - def reach(self, w): - r""" - Return the set of letters which occur in words of - `\{m^n(w) | n \ge 0\}`, where `m` is this morphism and `w` is a word - (finite iterable is enough) inputted as a parameter. - - Requires this morphism to be an endomorphism. - - EXAMPLES:: - - sage: sorted(WordMorphism('a->ac,b->ce,c->bd,d->d,e->').reach('c')) - ['b', 'c', 'd', 'e'] - """ - if not self.is_endomorphism(): - raise TypeError(f'self ({self}) is not an endomorphism') - - visited = set(w) - todo = list(visited) - while todo: - a = todo.pop() - for b in self.image(a): - if b not in visited: - visited.add(b) - todo.append(b) - return visited - def simplify(self, Z=None): r""" If this morphism is simplifiable, return morphisms `h` and `k` such that From 17720f3c5240919ec231fc2f7402aefb13b89694 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 22 May 2021 13:59:13 +0200 Subject: [PATCH 083/280] delete duplication in doctest --- .../polyhedron/combinatorial_polyhedron/combinatorial_face.pyx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 4c1641a7b7a..2481c5ff352 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -353,8 +353,6 @@ cdef class CombinatorialFace(SageObject): EXAMPLES:: - sage: P = polytopes.cube() - sage: C = P.combinatorial_polyhedron() sage: P = polytopes.cube() sage: C = P.combinatorial_polyhedron() sage: it = C.face_iter() From 8ed706f7cbdb539ced4ec3a780d28da8f799273b Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sun, 23 May 2021 20:30:25 +0200 Subject: [PATCH 084/280] assume type to safe some time --- .../combinatorial_face.pyx | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 2481c5ff352..ce7a97635ab 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -347,7 +347,7 @@ cdef class CombinatorialFace(SageObject): # They are faces of the same polyhedron obtained in the same way. return hash(self) < hash(other) - def is_subface(self, other): + def is_subface(self, CombinatorialFace other): r""" Return whether ``self`` is contained in ``other``. @@ -423,26 +423,21 @@ cdef class CombinatorialFace(SageObject): ... NotImplementedError: is_subface only implemented for faces of the same polyhedron """ - cdef CombinatorialFace other_face - if isinstance(other, CombinatorialFace): - other_face = other - if self._dual == other_face._dual: - if self.atoms is other_face.atoms: - if not self._dual: - return face_issubset(self.face, other_face.face) - else: - return face_issubset(other_face.face, self.face) + if self._dual == other._dual: + if self.atoms is other.atoms: + if not self._dual: + return face_issubset(self.face, other.face) else: - raise NotImplementedError("is_subface only implemented for faces of the same polyhedron") + return face_issubset(other.face, self.face) else: - if self.atoms is other_face.coatoms: - self_indices = self.ambient_V_indices() - other_indices = other.ambient_V_indices() - return all(i in other_indices for i in self_indices) - else: - raise NotImplementedError("is_subface only implemented for faces of the same polyhedron") + raise NotImplementedError("is_subface only implemented for faces of the same polyhedron") else: - raise ValueError("other must be a face") + if self.atoms is other.coatoms: + self_indices = self.ambient_V_indices() + other_indices = other.ambient_V_indices() + return all(i in other_indices for i in self_indices) + else: + raise NotImplementedError("is_subface only implemented for faces of the same polyhedron") def dimension(self): r""" From c0c8295950d26fd59fe3a0311a9f7df7a873e05a Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sun, 23 May 2021 21:12:45 +0200 Subject: [PATCH 085/280] optimized code --- .../combinatorial_face.pxd | 1 + .../combinatorial_face.pyx | 36 ++++++++++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd index 64dd767cc94..ba98b97ac9b 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd @@ -29,6 +29,7 @@ cdef class CombinatorialFace(SageObject): # If ``dual == 0``, then coatoms are facets, atoms vertices and vice versa. cdef ListOfFaces atoms, coatoms + cpdef dimension(self) cdef size_t n_atom_rep(self) except -1 cdef size_t set_coatom_rep(self) except -1 cdef size_t set_atom_rep(self) except -1 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index ce7a97635ab..afb83598398 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -423,6 +423,10 @@ cdef class CombinatorialFace(SageObject): ... NotImplementedError: is_subface only implemented for faces of the same polyhedron """ + cdef size_t length_self, length_other, counter_self, counter_other + cdef size_t* self_v_indices + cdef size_t* other_v_indices + if self._dual == other._dual: if self.atoms is other.atoms: if not self._dual: @@ -433,13 +437,37 @@ cdef class CombinatorialFace(SageObject): raise NotImplementedError("is_subface only implemented for faces of the same polyhedron") else: if self.atoms is other.coatoms: - self_indices = self.ambient_V_indices() - other_indices = other.ambient_V_indices() - return all(i in other_indices for i in self_indices) + if self.dimension() > other.dimension(): + return False + if self._dual: + length_self = self.set_coatom_rep() + self_v_indices = self.coatom_rep + length_other = other.set_atom_rep() + other_v_indices = other.atom_rep + else: + length_self = self.set_atom_rep() + self_v_indices = self.atom_rep + length_other = other.set_coatom_rep() + other_v_indices = other.coatom_rep + if length_self > length_other: + return False + + # Check if every element in self_v_indices is contained in other_v_indices. + counter_self = 0 + counter_other = 0 + while counter_self < length_self and counter_other < length_other: + if self_v_indices[counter_self] > other_v_indices[counter_other]: + counter_other += 1 + elif self_v_indices[counter_self] == other_v_indices[counter_other]: + counter_self += 1 + counter_other += 1 + else: + return False + return counter_self == length_self else: raise NotImplementedError("is_subface only implemented for faces of the same polyhedron") - def dimension(self): + cpdef dimension(self): r""" Return the dimension of the face. From 5de63bee53fdefbdb1a99ed978c72db2b611410d Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 25 May 2021 09:06:08 +0200 Subject: [PATCH 086/280] cache atom_rep and coatom_rep of combinatorial face --- .../combinatorial_polyhedron/combinatorial_face.pxd | 2 ++ .../combinatorial_polyhedron/combinatorial_face.pyx | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd index ba98b97ac9b..378ecad5b00 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd @@ -12,7 +12,9 @@ cdef class CombinatorialFace(SageObject): cdef MemoryAllocator _mem cdef size_t *atom_rep # a place where atom-representation of face will be stored + cdef size_t _n_atom_rep cdef size_t *coatom_rep # a place where coatom-representation of face will be stored + cdef size_t _n_coatom_rep cdef int _dimension # dimension of current face, dual dimension if ``dual`` cdef int _ambient_dimension # dimension of the polyhedron diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index afb83598398..482335fca2b 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -876,6 +876,8 @@ cdef class CombinatorialFace(SageObject): Compute the number of atoms in the current face by counting the number of set bits. """ + if self.atom_rep is not NULL: + return self._n_atom_rep return face_len_atoms(self.face) cdef size_t set_coatom_rep(self) except -1: @@ -883,16 +885,18 @@ cdef class CombinatorialFace(SageObject): Set ``coatom_rep`` to be the coatom-representation of the current face. Return its length. """ - if not self.coatom_rep: + if self.coatom_rep is NULL: self.coatom_rep = self._mem.allocarray(self.coatoms.n_faces(), sizeof(size_t)) - return bit_rep_to_coatom_rep(self.face, self.coatoms.data, self.coatom_rep) + self._n_coatom_rep = bit_rep_to_coatom_rep(self.face, self.coatoms.data, self.coatom_rep) + return self._n_coatom_rep cdef size_t set_atom_rep(self) except -1: r""" Set ``atom_rep`` to be the atom-representation of the current face. Return its length. """ - if not self.atom_rep: + if self.atom_rep is NULL: self.atom_rep = self._mem.allocarray(self.coatoms.n_atoms(), sizeof(size_t)) - return bit_rep_to_Vrep_list(self.face, self.atom_rep) + self._n_atom_rep = bit_rep_to_Vrep_list(self.face, self.atom_rep) + return self._n_atom_rep From 4fe09c451cd72675be7e88846acd8e95b682fadd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 25 May 2021 17:28:52 +0200 Subject: [PATCH 087/280] add method stack_sort for permutations --- src/sage/combinat/permutation.py | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 42f858f8cb1..6a1e031ba78 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -49,6 +49,7 @@ :meth:`~sage.combinat.permutation.Permutation.rank` | Returns the rank of ``self`` in lexicographic ordering (on the symmetric group containing ``self``). :meth:`~sage.combinat.permutation.Permutation.to_inversion_vector` | Returns the inversion vector of a permutation ``self``. :meth:`~sage.combinat.permutation.Permutation.inversions` | Returns a list of the inversions of permutation ``self``. + :meth:`~sage.combinat.permutation.Permutation.stack_sort` | Returns the permutation obtained by sorting ``self`` through one stack. :meth:`~sage.combinat.permutation.Permutation.to_digraph` | Return a digraph representation of ``self``. :meth:`~sage.combinat.permutation.Permutation.show` | Displays the permutation as a drawing. :meth:`~sage.combinat.permutation.Permutation.number_of_inversions` | Returns the number of inversions in the permutation ``self``. @@ -1597,6 +1598,49 @@ def inversions(self): return [tuple([i+1,j+1]) for i in range(n-1) for j in range(i+1,n) if p[i]>p[j]] + def stack_sort(self) -> "Permutation": + """ + Return the stack sort of a permutation. + + This is another permutation obtained through the + process of sorting using one stack. If the result is the identity + permutation, the original permutation is *stack-sortable*. + + See :wikipedia:`Stack-sortable_permutation` + + EXAMPLES:: + + sage: p = Permutation([2,1,5,3,4,9,7,8,6]) + sage: p.stack_sort() + [1, 2, 3, 4, 5, 7, 6, 8, 9] + + sage: S5 = Permutations(5) + sage: len([1 for s in S5 if s.stack_sort() == S5.one()]) + 42 + + TESTS:: + + sage: p = Permutation([]) + sage: p.stack_sort() + [] + sage: p = Permutation([1]) + sage: p.stack_sort() + [1] + """ + stack = [] + sorted_p = [] + for j in self: + if stack: + for i in reversed(stack): + if i < j: + sorted_p.append(i) + stack.pop() + else: + break + stack.append(j) + sorted_p.extend(reversed(stack)) + return Permutation(sorted_p) + def to_digraph(self): r""" Return a digraph representation of ``self``. From b7b63517d416115733a8e2e57d4409d93a3c3176 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Thu, 27 May 2021 11:09:24 +0100 Subject: [PATCH 088/280] Added gauss_legendre reference file sage/numerical/gauss_legendre was added to the index.rst file. Documentation is now built for the reference manual. --- src/doc/en/reference/numerical/index.rst | 1 + src/sage/numerical/gauss_legendre.pyx | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/numerical/index.rst b/src/doc/en/reference/numerical/index.rst index 254faadec8f..6a17afe2dfb 100644 --- a/src/doc/en/reference/numerical/index.rst +++ b/src/doc/en/reference/numerical/index.rst @@ -13,6 +13,7 @@ Numerical Optimization sage/numerical/linear_tensor_constraints sage/numerical/optimize sage/numerical/interactive_simplex_method + sage/numerical/gauss_legendre Linear Optimization (LP) and Mixed Integer Linear Optimization (MIP) Solver backends ------------------------------------------------------------------------------------ diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index 2fcc2d7c797..d03280070df 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -3,12 +3,12 @@ Gauss-Legendre integration for vector-valued functions Routine to perform Gauss-Legendre integration for vector-functions. +EXAMPLES:: + AUTHORS: - Nils Bruin (2017-06-06): initial version -EXAMPLES:: - NOTE: The code here is directly based on mpmath (see http://mpmath.org), but has a highly From 02aaa8ffd497212b6a42320c52ff5bd9b83f89c7 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Sat, 29 May 2021 11:12:07 +0200 Subject: [PATCH 089/280] Rename simplify methods --- src/sage/combinat/words/morphism.py | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index ca927d607b5..85f1fab5597 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3449,7 +3449,7 @@ def impl(): reach = self._language_naive(2, self._domain(w)) f = self.restrict_domain([x[0] for x in reach]) f._codomain = f._domain - g, _, k, _ = f.simplify_injective() + g, _, k, _ = f.simplify_until_injective() g._codomain = g._domain unbounded = set(g.growing_letters()) gb = g.restrict_domain(set(g._morph) - unbounded) @@ -3506,7 +3506,7 @@ def infinite_repetitions_growing(self, w=None): reach = self._language_naive(2, self._domain(w)) f = self.restrict_domain([x[0] for x in reach]) f._codomain = f._domain - g, _, k, _ = f.simplify_injective() + g, _, k, _ = f.simplify_until_injective() g._codomain = g._domain unbounded = set(g.growing_letters()) @@ -3542,7 +3542,7 @@ def infinite_repetitions_growing(self, w=None): return result - def simplify(self, Z=None): + def simplify_alphabet_size(self, Z=None): r""" If this morphism is simplifiable, return morphisms `h` and `k` such that this morphism is simplifiable with respect to `h` and `k`, otherwise @@ -3573,7 +3573,7 @@ def simplify(self, Z=None): Example of a simplifiable (non-injective) morphism:: sage: f = WordMorphism('a->aca,b->badc,c->acab,d->adc') - sage: h, k = f.simplify('xyz'); h, k + sage: h, k = f.simplify_alphabet_size('xyz'); h, k (WordMorphism: a->x, b->zy, c->xz, d->y, WordMorphism: x->aca, y->adc, z->b) sage: k * h == f True @@ -3583,7 +3583,7 @@ def simplify(self, Z=None): Example of a simplifiable (injective) morphism:: sage: f = WordMorphism('a->abcc,b->abcd,c->abdc,d->abdd') - sage: h, k = f.simplify('xyz'); h, k + sage: h, k = f.simplify_alphabet_size('xyz'); h, k (WordMorphism: a->xyy, b->xyz, c->xzy, d->xzz, WordMorphism: x->ab, y->c, z->d) sage: k * h == f True @@ -3592,7 +3592,7 @@ def simplify(self, Z=None): Example of a non-simplifiable morphism:: - sage: WordMorphism('a->aa').simplify() + sage: WordMorphism('a->aa').simplify_alphabet_size() Traceback (most recent call last): ... ValueError: self (a->aa) is not simplifiable @@ -3600,7 +3600,7 @@ def simplify(self, Z=None): Example of an erasing morphism:: sage: f = WordMorphism('a->abc,b->cc,c->') - sage: h, k = f.simplify(); h, k + sage: h, k = f.simplify_alphabet_size(); h, k (WordMorphism: a->a, b->b, c->, WordMorphism: a->abc, b->cc) sage: k * h == f True @@ -3610,7 +3610,7 @@ def simplify(self, Z=None): Example of a morphism, that is not an endomorphism:: sage: f = WordMorphism('a->xx,b->xy,c->yx,d->yy') - sage: h, k = f.simplify(NN); h, k + sage: h, k = f.simplify_alphabet_size(NN); h, k (WordMorphism: a->00, b->01, c->10, d->11, WordMorphism: 0->x, 1->y) sage: k * h == f True @@ -3704,17 +3704,17 @@ def try_create_h(f, k): return h, k - def simplify_injective(self): + def simplify_until_injective(self): r""" Return a quadruplet `(g, h, k, i)`, where `g` is an injective simplification of this morphism with respect to `h`, `k` and `i`. Requires this morphism to be an endomorphism. - This methods basically calls :meth:`simplify` until the returned - simplification is injective. If this morphism is already injective, a - quadruplet `(g, h, k, i)` is still returned, where `g` is this morphism, - `h` and `k` are the identity morphisms and `i` is 0. + This methods basically calls :meth:`simplify_alphabet_size` until the + returned simplification is injective. If this morphism is already + injective, a quadruplet `(g, h, k, i)` is still returned, where `g` + is this morphism, `h` and `k` are the identity morphisms and `i` is 0. Let `f: X^* \rightarrow Y^*` be a morphism and `Y \subseteq X`. Then `g: Z^* \rightarrow Z^*` is an injective simplification of `f` with @@ -3727,7 +3727,7 @@ def simplify_injective(self): EXAMPLES:: sage: f = WordMorphism('a->abc,b->a,c->bc') - sage: g, h, k, i = f.simplify_injective(); g, h, k, i + sage: g, h, k, i = f.simplify_until_injective(); g, h, k, i (WordMorphism: a->aa, WordMorphism: a->aa, b->a, c->a, WordMorphism: a->abc, 2) sage: g.is_injective() True @@ -3744,6 +3744,6 @@ def simplify_injective(self): k = self.codomain().identity_morphism() i = 0 while not g.is_injective(): - h_new, k_new = g.simplify() + h_new, k_new = g.simplify_alphabet_size() g, h, k, i = h_new * k_new, h_new * h, k * k_new, i + 1 return g, h, k, i From 0d5f94abffe1013c181dce6134c2058a72171402 Mon Sep 17 00:00:00 2001 From: Martin Rejmon Date: Sat, 29 May 2021 12:51:04 +0200 Subject: [PATCH 090/280] Merge infinite_repetitions* methods --- src/sage/combinat/words/morphism.py | 265 ++++++++++++---------------- 1 file changed, 108 insertions(+), 157 deletions(-) diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 85f1fab5597..51280ff3b03 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -3238,17 +3238,17 @@ def is_pushy(self, w=None): Requires this morphism to be an endomorphism. - A language created by iterating a morphism is pushy if its words + A language created by iterating a morphism is pushy, if its words contain an infinite number of factors containing no growing letters. It turns out that this is equivalent to having at least one infinite repetition containing no growing letters. - See :meth:`infinite_repetitions` and :meth:`is_growing`. + See :meth:`infinite_repetitions_primitive_roots` and :meth:`is_growing`. INPUT: - - ``w`` -- finite iterable representing a word used to start the - language, default is ``self.domain().alphabet()`` + - ``w`` -- finite iterable (default: ``self.domain().alphabet()``). + Represents a word used to start the language. EXAMPLES:: @@ -3257,7 +3257,7 @@ def is_pushy(self, w=None): sage: WordMorphism('a->abc,b->,c->bcb').is_pushy() True """ - return bool(self.infinite_repetitions_bounded(w)) + return bool(self.infinite_repetitions_primitive_roots(w, False)) def is_unboundedly_repetitive(self, w=None): r""" @@ -3266,16 +3266,16 @@ def is_unboundedly_repetitive(self, w=None): Requires this morphism to be an endomorphism. - A language created by iterating a morphism is unboundedly repetitive if + A language created by iterating a morphism is unboundedly repetitive, if it has at least one infinite repetition containing at least one growing letter. - See :meth:`infinite_repetitions` and :meth:`is_growing`. + See :meth:`infinite_repetitions_primitive_roots` and :meth:`is_growing`. INPUT: - - ``w`` -- finite iterable representing a word used to start the - language, default is ``self.domain().alphabet()`` + - ``w`` -- finite iterable (default: ``self.domain().alphabet()``). + Represents a word used to start the language. EXAMPLES:: @@ -3284,7 +3284,7 @@ def is_unboundedly_repetitive(self, w=None): sage: WordMorphism('a->abc,b->,c->bcb').is_unboundedly_repetitive() False """ - return bool(self.infinite_repetitions_growing(w)) + return bool(self.infinite_repetitions_primitive_roots(w, True)) def is_repetitive(self, w=None): r""" @@ -3293,23 +3293,23 @@ def is_repetitive(self, w=None): Requires this morphism to be an endomorphism. - A language is repetitive if for each positive integer `k` there exists + A language is repetitive, if for each positive integer `k` there exists a word `u` such that `u^k` is a factor of some word of the language. - It turns that for languages created by iterating a morphism this is + It turns out that for languages created by iterating a morphism this is equivalent to having at least one infinite repetition (this property is also known as strong repetitiveness). - See :meth:`infinite_repetitions`. + See :meth:`infinite_repetitions_primitive_roots`. INPUT: - - ``w`` -- finite iterable representing a word used to start the - language, default is ``self.domain().alphabet()`` + - ``w`` -- finite iterable (default: ``self.domain().alphabet()``). + Represents a word used to start the language. EXAMPLES: - This method can be used to check whether a purely morphic word is NOT + This method can be used to check whether a purely morphic word is not k-power free for all positive integers k. For example, the language containing just the Thue-Morse word and its prefixes is not repetitive, since the Thue-Morse word is cube-free:: @@ -3330,40 +3330,55 @@ def is_repetitive(self, w=None): """ return self.is_pushy(w) or self.is_unboundedly_repetitive(w) - def infinite_repetitions(self, w=None): + def infinite_repetitions_primitive_roots(self, w=None, allow_growing=None): r""" - Return the set of primitive infinite repetitions (up to conjugacy) - from the language `\{m^n(w) | n \ge 0\}`, where `m` is this morphism - and `w` is a word inputted as a parameter. + Return the set of primitive roots (up to conjugacy) of infinite + repetitions from the language `\{m^n(w) | n \ge 0\}`, where `m` is this + morphism and `w` is a word inputted as a parameter. Requires this morphism to be an endomorphism. - A non-empty word `v` is an infinite repetition (also known as an - infinite periodic factor) of a language if for each positive integer - `k` the word `v^k` is a factor of some word from the language. + The word `v^\omega` is an infinite repetition (in other words, an + infinite periodic factor) of a language, if `v` is a non-empty word and + for each positive integer `k` the word `v^k` is a factor of some word + from the language. It turns out that a language created by iterating a + morphism has a finite number of primitive roots of infinite repetitions. - If `v` is an infinite repetition, then all its powers are also infinite - repetitions, therefore this method returns only the primitive ones. It - turns out that a language created by iterating a morphism has a finite - number of primitive infinite repetitions. - - Similarly, if `v` is an infinite repetition, then all its conjugates - are also infinite repetitions, therefore this method returns only the - lexicographically minimal one from each conjugacy class. + If `v` is a primitive root of an infinite repetition, then all its + conjugations are also primitive roots of an infinite repetition. For + simplicity's sake this method returns only the lexicographically minimal + one from each conjugacy class. INPUT: - - ``w`` -- finite iterable representing a word used to start the - language, default is ``self.domain().alphabet()`` + - ``w`` -- finite iterable (default: ``self.domain().alphabet()``). + Represents a word used to start the language. + + - ``allow_growing`` -- boolean or ``None`` (default: ``None``). If + ``False``, return only the primitive roots that contain no growing + letters. If ``True``, return only the primitive roots that contain at + least one growing letter. If ``None``, return both. + + ALGORITHM: + + The algorithm used is described in detail in [KS2015]_. EXAMPLES:: sage: m = WordMorphism('a->aba,b->aba,c->cd,d->e,e->d') - sage: inf_reps = m.infinite_repetitions('ac') + sage: inf_reps = m.infinite_repetitions_primitive_roots('ac') sage: sorted(inf_reps) [word: aab, word: de] - Incomplete check that these words are indeed infinite repetitions:: + ``allow_growing`` parameter:: + + sage: sorted(m.infinite_repetitions_primitive_roots('ac', True)) + [word: aab] + sage: sorted(m.infinite_repetitions_primitive_roots('ac', False)) + [word: de] + + Incomplete check that these words are indeed the primitive roots of + infinite repetitions:: sage: SL = m._language_naive(10, Word('ac')) sage: all(x in SL for x in inf_reps) @@ -3373,54 +3388,39 @@ def infinite_repetitions(self, w=None): sage: all(x^3 in SL for x in inf_reps) True - Larger example:: + Large example:: sage: m = WordMorphism('a->1b5,b->fcg,c->dae,d->432,e->678,f->f,g->g,1->2,2->3,3->4,4->1,5->6,6->7,7->8,8->5') - sage: sorted(m.infinite_repetitions('a')) + sage: sorted(m.infinite_repetitions_primitive_roots('a')) [word: 1432f2143f3214f4321f, word: 5678g8567g7856g6785g] - """ - return self.infinite_repetitions_bounded(w) | self.infinite_repetitions_growing(w) - - def infinite_repetitions_bounded(self, w=None): - r""" - Return the set of primitive infinite repetitions (up to conjugacy), - which contain no growing letters, - from the language `\{m^n(w) | n \ge 0\}`, where `m` is this morphism - and `w` is a word inputted as a parameter. - - Requires this morphism to be an endomorphism. - - See :meth:`infinite_repetitions` and :meth:`is_growing`. - - INPUT: - - - ``w`` -- finite iterable representing a word used to start the - language, default is ``self.domain().alphabet()`` - - ALGORITHM: - The algorithm used is described in detail in [KS2015]_. + TESTS:: - EXAMPLES:: + sage: m = WordMorphism('a->Cab,b->1c1,c->E2bd5,d->BbaA,5->6,6->7,7->8,8->9,9->5,1->2,2->1,A->B,B->C,C->D,D->E,E->') + sage: sorted(m.infinite_repetitions_primitive_roots()) + [word: 1, word: 1519181716, word: 2, word: 2529282726] - sage: m = WordMorphism('a->aba,b->aba,c->cd,d->e,e->d') - sage: sorted(m.infinite_repetitions_bounded()) - [word: de] + sage: m = WordMorphism('a->b,b->b', codomain=FiniteWords('ab')) + sage: m.infinite_repetitions_primitive_roots() + set() sage: m = WordMorphism('c->d,d->c,e->fc,f->ed') - sage: sorted(m.infinite_repetitions_bounded()) + sage: sorted(m.infinite_repetitions_primitive_roots()) [word: c, word: d] - TESTS:: + sage: m = WordMorphism('a->bcb,b->ada,c->d,d->c') + sage: sorted(m.infinite_repetitions_primitive_roots()) + [word: ad, word: bc] - sage: m = WordMorphism('a->Cab,b->1c1,c->E2bd5,d->BbaA,5->6,6->7,7->8,8->9,9->5,1->2,2->1,A->B,B->C,C->D,D->E,E->') - sage: sorted(m.infinite_repetitions_bounded()) - [word: 1, word: 1519181716, word: 2, word: 2529282726] + sage: m = WordMorphism('b->c,c->bcb') + sage: sorted(m.infinite_repetitions_primitive_roots()) + [word: bc] - sage: WordMorphism('a->b,b->b', codomain=FiniteWords('ab')).infinite_repetitions() - set() + sage: m = WordMorphism('a->abc,b->dab,c->abc,d->dab') + sage: sorted(m.infinite_repetitions_primitive_roots()) + [word: ababcd] """ - def impl(): + def impl_no_growing(g, k): U = {} for x in unbounded: xg = g.image(x) @@ -3452,93 +3452,44 @@ def impl(): g, _, k, _ = f.simplify_until_injective() g._codomain = g._domain unbounded = set(g.growing_letters()) - gb = g.restrict_domain(set(g._morph) - unbounded) - result = set() - for x in impl(): # UR. - result.add(x.minimal_conjugate()) - g, k = g.reversal(), k.reversal() - for x in impl(): # UL. - result.add(self.domain()(reversed(x)).minimal_conjugate()) - - return result - - def infinite_repetitions_growing(self, w=None): - r""" - Return the set of primitive infinite repetitions (up to conjugacy), - which contain at least one growing letter, - from the language `\{m^n(w) | n \ge 0\}`, where `m` is this morphism - and `w` is a word inputted as a parameter. - - Requires this morphism to be an endomorphism. - - See :meth:`infinite_repetitions` and :meth:`is_growing`. - - INPUT: - - - ``w`` -- finite iterable representing a word used to start the - language, default is ``self.domain().alphabet()`` - - ALGORITHM: - - The algorithm used is described in detail in [KS2015]_. - - EXAMPLES:: - - sage: m = WordMorphism('a->aba,b->aba,c->cd,d->e,e->d') - sage: sorted(m.infinite_repetitions_growing()) - [word: aab] - - sage: m = WordMorphism('a->bcb,b->ada,c->d,d->c') - sage: sorted(m.infinite_repetitions_growing()) - [word: ad, word: bc] - sage: m = WordMorphism('b->c,c->bcb') - sage: sorted(m.infinite_repetitions_growing()) - [word: bc] - - sage: m = WordMorphism('a->abc,b->dab,c->abc,d->dab') - sage: sorted(m.infinite_repetitions_growing()) - [word: ababcd] - """ - if w is None: - w = self._morph - reach = self._language_naive(2, self._domain(w)) - f = self.restrict_domain([x[0] for x in reach]) - f._codomain = f._domain - g, _, k, _ = f.simplify_until_injective() - g._codomain = g._domain - unbounded = set(g.growing_letters()) - - result = set() - for periodic_orbit in g.periodic_points(): - gq = g ** len(periodic_orbit) - for periodic_point in periodic_orbit: - # Check if this periodic point is a periodic infinite word. - periodic_point = periodic_point[:1] - occurred = set(periodic_point) - one_unbounded_twice = False - for _ in g.domain().alphabet(): - previous_length = periodic_point.length() - periodic_point = gq(periodic_point) - for i, letter in enumerate(periodic_point[previous_length:]): - if letter in unbounded: - if letter in occurred: - one_unbounded_twice = True - break - occurred.add(letter) - if one_unbounded_twice: + if allow_growing is not True: + gb = g.restrict_domain(set(g._morph) - unbounded) + for x in impl_no_growing(g, k): # UR. + result.add(x.minimal_conjugate()) + for x in impl_no_growing(g.reversal(), k.reversal()): # UL. + result.add(self.domain()(reversed(x)).minimal_conjugate()) + + if allow_growing is not False: + for periodic_orbit in g.periodic_points(): + gq = g ** len(periodic_orbit) + for periodic_point in periodic_orbit: + # Check if this periodic point is a periodic infinite word. + periodic_point = periodic_point[:1] + occurred = set(periodic_point) + one_unbounded_twice = False + for _ in g.domain().alphabet(): + previous_length = periodic_point.length() + periodic_point = gq(periodic_point) + for i, letter in enumerate(periodic_point[previous_length:]): + if letter in unbounded: + if letter in occurred: + one_unbounded_twice = True + break + occurred.add(letter) + if one_unbounded_twice: + break + if not one_unbounded_twice or letter != periodic_point[0]: break - if not one_unbounded_twice or letter != periodic_point[0]: - break - v = periodic_point[:previous_length + i] - vq = gq(v) - m = 0 - while vq[m * v.length() : (m + 1) * v.length()] == v: - m += 1 - if m * v.length() != vq.length(): - break - result.add(k(v).primitive().minimal_conjugate()) + v = periodic_point[:previous_length + i] + vq = gq(v) + m = 0 + while vq[m * v.length() : (m + 1) * v.length()] == v: + m += 1 + if m * v.length() != vq.length(): + break + result.add(k(v).primitive().minimal_conjugate()) return result @@ -3557,7 +3508,7 @@ def simplify_alphabet_size(self, Z=None): `Y \subseteq X`, then the morphism `g: Z^* \rightarrow Z^* = h \circ k` is a simplification of `f` (with respect to `h` and `k`). - Loosely speaking a morphism is simplifiable if it contains "more letters + Loosely speaking, a morphism is simplifiable if it contains "more letters than is needed". Non-injectivity implies simplifiability. Simplification preserves some properties of the original morphism (e.g. repetitiveness). @@ -3565,8 +3516,8 @@ def simplify_alphabet_size(self, Z=None): INPUT: - - ``Z`` -- iterable, whose elements are used as an alphabet for the - simplification, default is ``self.domain().alphabet()`` + - ``Z`` -- iterable (default: ``self.domain().alphabet()``), whose + elements are used as an alphabet for the simplification. EXAMPLES: From fb33c0f4f26e907ba58a75224efaf263f245d69d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 29 May 2021 12:50:45 -0700 Subject: [PATCH 091/280] RealSet: Allow initialization from RealLine and OpenInterval instances and OpenInterval closures --- src/sage/sets/real_set.py | 44 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..d6a2b504560 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -857,7 +857,25 @@ def rel_to_interval(op, val): else: raise ValueError(str(arg) + ' does not determine real interval') else: - raise ValueError(str(arg) + ' does not determine real interval') + from sage.manifolds.differentiable.examples.real_line import OpenInterval + from sage.manifolds.subsets.closure import ManifoldSubsetClosure + if isinstance(arg, OpenInterval): + lower, upper = RealSet._prep(arg.lower_bound(), arg.upper_bound()) + intervals.append(InternalRealInterval(lower, False, upper, False)) + elif (isinstance(arg, ManifoldSubsetClosure) + and isinstance(arg._subset, OpenInterval)): + interval = arg._subset + lower, upper = RealSet._prep(interval.lower_bound(), + interval.upper_bound()) + ambient = interval.manifold() + ambient_lower, ambient_upper = RealSet._prep(ambient.lower_bound(), + ambient.upper_bound()) + lower_closed = ambient_lower < lower + upper_closed = upper < ambient_upper + intervals.append(InternalRealInterval(lower, lower_closed, + upper, upper_closed)) + else: + raise ValueError(str(arg) + ' does not determine real interval') intervals = RealSet.normalize(intervals) return UniqueRepresentation.__classcall__(cls, *intervals) @@ -885,6 +903,30 @@ def __init__(self, *intervals): (0, 1) + (3, 4) sage: RealSet(i, [3,4]) # list of two numbers = closed set (0, 1) + [3, 4] + + Initialization from manifold objects:: + + sage: R = RealLine(); R + Real number line R + sage: RealSet(R) + (-oo, +oo) + sage: I02 = OpenInterval(0, 2); I + I + sage: RealSet(I02) + (0, 2) + sage: I01_of_R = OpenInterval(0, 1, ambient_interval=R); I01_of_R + Real interval (0, 1) + sage: RealSet(I01_of_R) + (0, 1) + sage: RealSet(I01_of_R.closure()) + [0, 1] + sage: I01_of_I02 = OpenInterval(0, 1, ambient_interval=I02); I01_of_I02 + Real interval (0, 1) + sage: RealSet(I01_of_I02) + (0, 1) + sage: RealSet(I01_of_I02.closure()) + (0, 1] + """ Parent.__init__(self, category = Sets()) self._intervals = intervals From 4b09685667a8dddec24847dd14067abbe2d50076 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 29 May 2021 15:01:21 -0700 Subject: [PATCH 092/280] RealSet: Put it in a suitable subcategory of TopologicalSpaces() --- src/sage/sets/real_set.py | 86 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..b27e39fd25b 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -88,7 +88,7 @@ class RealSet. from sage.structure.richcmp import richcmp, richcmp_method from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -from sage.categories.sets_cat import Sets +from sage.categories.topological_spaces import TopologicalSpaces from sage.rings.all import ZZ from sage.rings.real_lazy import LazyFieldElement, RLF from sage.rings.infinity import infinity, minus_infinity @@ -885,8 +885,37 @@ def __init__(self, *intervals): (0, 1) + (3, 4) sage: RealSet(i, [3,4]) # list of two numbers = closed set (0, 1) + [3, 4] + + Real sets belong to a subcategory of topological spaces:: + + sage: RealSet().category() + Join of Category of finite sets and Category of subobjects of sets and Category of connected topological spaces + sage: RealSet.point(1).category() + Join of Category of finite sets and Category of subobjects of sets and Category of connected topological spaces + sage: RealSet([1, 2]).category() + Join of Category of infinite sets and Category of compact topological spaces and Category of subobjects of sets and Category of connected topological spaces + sage: RealSet((1, 2), (3, 4)).category() + Join of Category of infinite sets and Category of subobjects of sets and Category of topological spaces + """ - Parent.__init__(self, category = Sets()) + category = TopologicalSpaces() + if len(intervals) <= 1: + category = category.Connected() + if all(i.is_point() for i in intervals): + category = category.Subobjects().Finite() + else: + # Have at least one non-degenerate interval + category = category.Infinite() + inf = intervals[0].lower() + sup = intervals[-1].upper() + if not (len(intervals) == 1 and inf is minus_infinity and sup is infinity): + category = category.Subobjects() # subobject of real line + if inf is not minus_infinity and sup is not infinity: + # Bounded + if all(i.lower_closed() and i.upper_closed() + for i in intervals): + category = category.Compact() + Parent.__init__(self, category=category) self._intervals = intervals def __richcmp__(self, other, op): @@ -1017,6 +1046,59 @@ def get_interval(self, i): __getitem__ = get_interval + # ParentMethods of Subobjects + + @staticmethod + def ambient(): + """ + Construct the real line + + EXAMPLES:: + + sage: s = RealSet(RealSet.open_closed(0,1), RealSet.closed_open(2,3)) + sage: s.ambient() + (-oo, +oo) + """ + return RealSet(minus_infinity, infinity) + + def lift(self, x): + """ + Lift ``x`` to the ambient space for ``self``. + + This version of the method just returns ``x``. + + EXAMPLES:: + + sage: s = RealSet(0, 2); s + (0, 2) + sage: s.lift(1) + 1 + """ + return x + + def retract(self, x): + """ + Retract ``x`` to ``self``. + + It raises an error if ``x`` does not lie in the set ``self``. + + EXAMPLES:: + + sage: s = RealSet(0, 2); s + (0, 2) + sage: s.retract(1) + 1 + sage: s.retract(2) + Traceback (most recent call last): + ... + ValueError: 2 is not an element of (0, 2) + """ + if x not in self: + raise ValueError(f'{x} is not an element of {self}') + return x + + # + @staticmethod def normalize(intervals): """ From 5b8ceca516427f34634ab0eeb17a53a70d62f0bd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 11:01:36 -0700 Subject: [PATCH 093/280] InternalRealInterval, RealSet: Add _latex_ methods --- src/sage/sets/real_set.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..7aedf03abce 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -415,6 +415,17 @@ def _sympy_condition_(self, variable): upper_condition = true return lower_condition & upper_condition + def _latex_(self): + from sage.misc.latex import latex + if self.is_point(): + return r'\{' + latex(self.lower()) + r'\}' + s = '[' if self._lower_closed else '(' + s += latex(self.lower()) + s += ', ' + s += latex(self.upper()) + s += ']' if self._upper_closed else ')' + return s + def closure(self): """ Return the closure @@ -1095,6 +1106,21 @@ def _repr_(self): return ' + '.join(map(repr, self._intervals)) # return u' ∪ '.join(map(repr, self._intervals)) # py3 only + def _latex_(self): + """ + EXAMPLES:: + + sage: from cutgeneratingfunctionology.spam.real_set import RealSet + sage: latex(RealSet(0, 1)) + ( 0 , 1 ) + sage: latex((RealSet(0, 1).union(RealSet.unbounded_above_closed(2)))) + ( 0 , 1 ) \cup [ 2 , +\infty ) + """ + from sage.misc.latex import latex + if self.n_components() == 0: + return r'\emptyset' + else: + return r' \cup '.join(map(latex, self._intervals)) def _sympy_condition_(self, variable): """ From 69ca8543ccd948f9c01143dda49f574710efe0c4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 11:24:46 -0700 Subject: [PATCH 094/280] RealSet._repr_: Use unicode cup sign instead of + --- src/sage/sets/real_set.py | 84 +++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 7aedf03abce..170b762a5af 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -10,7 +10,7 @@ sage: RealSet(0,1) (0, 1) sage: RealSet((0,1), [2,3]) - (0, 1) + [2, 3] + (0, 1) ∪ [2, 3] sage: RealSet(-oo, oo) (-oo, +oo) @@ -42,7 +42,7 @@ Relations containing symbols and numeric values or constants:: sage: RealSet(x != 0) - (-oo, 0) + (0, +oo) + (-oo, 0) ∪ (0, +oo) sage: RealSet(x == pi) {pi} sage: RealSet(x < 1/2) @@ -753,12 +753,12 @@ def __classcall__(cls, *args): EXAMPLES:: sage: R = RealSet(RealSet.open_closed(0,1), RealSet.closed_open(2,3)); R - (0, 1] + [2, 3) + (0, 1] ∪ [2, 3) :: sage: RealSet(x != 0) - (-oo, 0) + (0, +oo) + (-oo, 0) ∪ (0, +oo) sage: RealSet(x == pi) {pi} sage: RealSet(x < 1/2) @@ -893,9 +893,9 @@ def __init__(self, *intervals): sage: RealSet(i) # interval (0, 1) sage: RealSet(i, (3,4)) # tuple of two numbers = open set - (0, 1) + (3, 4) + (0, 1) ∪ (3, 4) sage: RealSet(i, [3,4]) # list of two numbers = closed set - (0, 1) + [3, 4] + (0, 1) ∪ [3, 4] """ Parent.__init__(self, category = Sets()) self._intervals = intervals @@ -1058,9 +1058,9 @@ def normalize(intervals): sage: RealSet((0, 1), [1, 2], (2, 3)) (0, 3) sage: RealSet((0, 1), (1, 2), (2, 3)) - (0, 1) + (1, 2) + (2, 3) + (0, 1) ∪ (1, 2) ∪ (2, 3) sage: RealSet([0, 1], [2, 3]) - [0, 1] + [2, 3] + [0, 1] ∪ [2, 3] sage: RealSet((0, 2), (1, 3)) (0, 3) sage: RealSet(0,0) @@ -1088,7 +1088,7 @@ def normalize(intervals): def _repr_(self): """ - Return a string representation + Return a string representation of ``self``. OUTPUT: @@ -1102,9 +1102,7 @@ def _repr_(self): if self.n_components() == 0: return '{}' else: - # Switch to u'\u222A' (cup sign) with Python 3 - return ' + '.join(map(repr, self._intervals)) - # return u' ∪ '.join(map(repr, self._intervals)) # py3 only + return ' ∪ '.join(map(repr, self._intervals)) def _latex_(self): """ @@ -1437,26 +1435,26 @@ def intersection(self, *other): EXAMPLES:: sage: s1 = RealSet(0,2) + RealSet.unbounded_above_closed(10); s1 - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s2 = RealSet(1,3) + RealSet.unbounded_below_closed(-10); s2 - (-oo, -10] + (1, 3) + (-oo, -10] ∪ (1, 3) sage: s1.intersection(s2) (1, 2) sage: s1 & s2 # syntactic sugar (1, 2) sage: s1 = RealSet((0, 1), (2, 3)); s1 - (0, 1) + (2, 3) + (0, 1) ∪ (2, 3) sage: s2 = RealSet([0, 1], [2, 3]); s2 - [0, 1] + [2, 3] + [0, 1] ∪ [2, 3] sage: s3 = RealSet([1, 2]); s3 [1, 2] sage: s1.intersection(s2) - (0, 1) + (2, 3) + (0, 1) ∪ (2, 3) sage: s1.intersection(s3) {} sage: s2.intersection(s3) - {1} + {2} + {1} ∪ {2} """ other = RealSet(*other) # TODO: this can be done in linear time since the intervals are already sorted @@ -1479,12 +1477,12 @@ def inf(self): EXAMPLES:: sage: s1 = RealSet(0,2) + RealSet.unbounded_above_closed(10); s1 - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s1.inf() 0 sage: s2 = RealSet(1,3) + RealSet.unbounded_below_closed(-10); s2 - (-oo, -10] + (1, 3) + (-oo, -10] ∪ (1, 3) sage: s2.inf() -Infinity """ @@ -1503,12 +1501,12 @@ def sup(self): EXAMPLES:: sage: s1 = RealSet(0,2) + RealSet.unbounded_above_closed(10); s1 - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s1.sup() +Infinity sage: s2 = RealSet(1,3) + RealSet.unbounded_below_closed(-10); s2 - (-oo, -10] + (1, 3) + (-oo, -10] ∪ (1, 3) sage: s2.sup() 3 """ @@ -1527,17 +1525,17 @@ def complement(self): EXAMPLES:: sage: RealSet(0,1).complement() - (-oo, 0] + [1, +oo) + (-oo, 0] ∪ [1, +oo) sage: s1 = RealSet(0,2) + RealSet.unbounded_above_closed(10); s1 - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s1.complement() - (-oo, 0] + [2, 10) + (-oo, 0] ∪ [2, 10) sage: s2 = RealSet(1,3) + RealSet.unbounded_below_closed(-10); s2 - (-oo, -10] + (1, 3) + (-oo, -10] ∪ (1, 3) sage: s2.complement() - (-10, 1] + [3, +oo) + (-10, 1] ∪ [3, +oo) """ n = self.n_components() if n == 0: @@ -1575,19 +1573,19 @@ def difference(self, *other): EXAMPLES:: sage: s1 = RealSet(0,2) + RealSet.unbounded_above_closed(10); s1 - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s2 = RealSet(1,3) + RealSet.unbounded_below_closed(-10); s2 - (-oo, -10] + (1, 3) + (-oo, -10] ∪ (1, 3) sage: s1.difference(s2) - (0, 1] + [10, +oo) + (0, 1] ∪ [10, +oo) sage: s1 - s2 # syntactic sugar - (0, 1] + [10, +oo) + (0, 1] ∪ [10, +oo) sage: s2.difference(s1) - (-oo, -10] + [2, 3) + (-oo, -10] ∪ [2, 3) sage: s2 - s1 # syntactic sugar - (-oo, -10] + [2, 3) + (-oo, -10] ∪ [2, 3) sage: s1.difference(1,11) - (0, 1] + [11, +oo) + (0, 1] ∪ [11, +oo) """ other = RealSet(*other) return self.intersection(other.complement()) @@ -1609,7 +1607,7 @@ def contains(self, x): EXAMPLES:: sage: s = RealSet(0,2) + RealSet.unbounded_above_closed(10); s - (0, 2) + [10, +oo) + (0, 2) ∪ [10, +oo) sage: s.contains(1) True sage: s.contains(0) @@ -1703,7 +1701,7 @@ def is_disjoint_from(self, *other): EXAMPLES:: sage: s1 = RealSet((0, 1), (2, 3)); s1 - (0, 1) + (2, 3) + (0, 1) ∪ (2, 3) sage: s2 = RealSet([1, 2]); s2 [1, 2] sage: s1.is_disjoint_from(s2) @@ -1808,15 +1806,15 @@ def __mul__(self, right): EXAMPLES:: sage: A = RealSet([0, 1/2], (2, infinity)); A - [0, 1/2] + (2, +oo) + [0, 1/2] ∪ (2, +oo) sage: 2 * A - [0, 1] + (4, +oo) + [0, 1] ∪ (4, +oo) sage: A * 100 - [0, 50] + (200, +oo) + [0, 50] ∪ (200, +oo) sage: 1.5 * A - [0.000000000000000, 0.750000000000000] + (3.00000000000000, +oo) + [0.000000000000000, 0.750000000000000] ∪ (3.00000000000000, +oo) sage: (-2) * A - (-oo, -4) + [-1, 0] + (-oo, -4) ∪ [-1, 0] """ if not isinstance(right, RealSet): return RealSet(*[e * right for e in self]) @@ -1832,8 +1830,8 @@ def __rmul__(self, other): TESTS:: sage: A = RealSet([0, 1/2], RealSet.unbounded_above_closed(2)); A - [0, 1/2] + [2, +oo) + [0, 1/2] ∪ [2, +oo) sage: pi * A - [0, 1/2*pi] + [2*pi, +oo) + [0, 1/2*pi] ∪ [2*pi, +oo) """ return self * other From dbdfc068f2d2ac39ae8171ea8737b2b1a535e021 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 11:26:42 -0700 Subject: [PATCH 095/280] InternalRealInterval, RealSet: Remove extra whitespace in latex, add documentation --- src/sage/sets/real_set.py | 47 ++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 170b762a5af..9106db527d4 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -382,6 +382,31 @@ def _repr_(self): s += ']' if self._upper_closed else ')' return s + def _latex_(self): + """ + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: RealSet.open_closed(1/2, pi)._latex_() + '(\\frac{1}{2}, \\pi]' + sage: (RealSet.point(sqrt(2)))._latex_() + '\\{\\sqrt{2}\\}' + """ + from sage.misc.latex import latex + if self.is_point(): + # Converting to str avoids the extra whitespace + # that LatexExpr add on concenation. We do not need + # the whitespace because we are wrapping it in + # non-letter characters. + return r'\{' + str(latex(self.lower())) + r'\}' + s = '[' if self._lower_closed else '(' + s += str(latex(self.lower())) + s += ', ' + s += str(latex(self.upper())) + s += ']' if self._upper_closed else ')' + return s + def _sympy_condition_(self, variable): """ Convert to a sympy conditional expression. @@ -415,17 +440,6 @@ def _sympy_condition_(self, variable): upper_condition = true return lower_condition & upper_condition - def _latex_(self): - from sage.misc.latex import latex - if self.is_point(): - return r'\{' + latex(self.lower()) + r'\}' - s = '[' if self._lower_closed else '(' - s += latex(self.lower()) - s += ', ' - s += latex(self.upper()) - s += ']' if self._upper_closed else ')' - return s - def closure(self): """ Return the closure @@ -1105,20 +1119,21 @@ def _repr_(self): return ' ∪ '.join(map(repr, self._intervals)) def _latex_(self): - """ + r""" + Return a latex representation of ``self``. + EXAMPLES:: - sage: from cutgeneratingfunctionology.spam.real_set import RealSet sage: latex(RealSet(0, 1)) - ( 0 , 1 ) + (0, 1) sage: latex((RealSet(0, 1).union(RealSet.unbounded_above_closed(2)))) - ( 0 , 1 ) \cup [ 2 , +\infty ) + (0, 1) \cup [2, +\infty) """ from sage.misc.latex import latex if self.n_components() == 0: return r'\emptyset' else: - return r' \cup '.join(map(latex, self._intervals)) + return r' \cup '.join(latex(i) for i in self._intervals) def _sympy_condition_(self, variable): """ From 181b128686bf125fb0bb9a11210543dacce73ebe Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 13:49:43 -0700 Subject: [PATCH 096/280] RealSet.is_open, is_closed, closure, interior, boundary: New --- src/sage/sets/real_set.py | 101 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 9106db527d4..042b9bf6239 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -85,6 +85,8 @@ class RealSet. # http://www.gnu.org/licenses/ #***************************************************************************** +import itertools + from sage.structure.richcmp import richcmp, richcmp_method from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation @@ -480,6 +482,23 @@ def interior(self): """ return InternalRealInterval(self._lower, False, self._upper, False) + def boundary_points(self): + """ + Generate the boundary points of ``self`` + + EXAMPLES:: + + sage: list(RealSet.open_closed(-oo, 1)[0].boundary_points()) + [1] + sage: list(RealSet.open(1, 2)[0].boundary_points()) + [1, 2] + + """ + if self._lower != minus_infinity: + yield self._lower + if self._upper != infinity: + yield self._upper + def is_connected(self, other): """ Test whether two intervals are connected @@ -1701,6 +1720,88 @@ def an_element(self): return i.upper() return (i.lower() + i.upper())/ZZ(2) + def is_open(self): + """ + Return whether ``self`` is an open set. + + EXAMPLES:: + + sage: RealSet().is_open() + True + sage: RealSet.point(1).is_open() + False + sage: RealSet((1, 2)).is_open() + True + sage: RealSet([1, 2], (3, 4)).is_open() + False + + """ + return all(not i.lower_closed() + and not i.upper_closed() + for i in self._intervals) + + def is_closed(self): + """ + Return whether ``self`` is a closed set. + + EXAMPLES:: + + sage: RealSet().is_closed() + True + sage: RealSet.point(1).is_closed() + True + sage: RealSet([1, 2]).is_closed() + True + sage: RealSet([1, 2], (3, 4)).is_closed() + False + """ + return all((i.lower_closed() or i.lower() is minus_infinity) + and (i.upper_closed() or i.upper() is infinity) + for i in self._intervals) + + def closure(self): + """ + Return the topological closure of ``self``. + + EXAMPLES:: + + sage: RealSet(-oo, oo).closure() + (-oo, +oo) + sage: RealSet((1, 2), (2, 3)).closure() + [1, 3] + """ + return RealSet(*[i.closure() for i in self._intervals]) + + def interior(self): + """ + Return the topological interior of ``self``. + + EXAMPLES:: + + sage: RealSet(-oo, oo).interior() + (-oo, +oo) + sage: RealSet.point(2).interior() + {} + sage: RealSet([1, 2], (3, 4)).interior() + (1, 2) ∪ (3, 4) + """ + return RealSet(*[i.interior() for i in self._intervals]) + + def boundary(self): + """ + Return the topological boundary of ``self``. + + EXAMPLES:: + + sage: RealSet(-oo, oo).boundary() + {} + sage: RealSet.point(2).boundary() + {2} + sage: RealSet([1, 2], (3, 4)).boundary() + {1} ∪ {2} ∪ {3} ∪ {4} + """ + return RealSet(*[RealSet.point(x) for i in self._intervals for x in i.boundary_points()]) + def is_disjoint_from(self, *other): """ Test whether the two sets are disjoint From 7f563381617c2b016a8b849f83d3387ccc25d621 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 16:09:43 -0700 Subject: [PATCH 097/280] PiecewiseFunction: Adjust doctests for changed RealSet repr --- src/sage/functions/piecewise.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index 41f5876616c..a0276824a3a 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -527,7 +527,7 @@ def restriction(self, parameters, variable, restricted_domain): sage: f = piecewise([((-oo, oo), x)]); f piecewise(x|-->x on (-oo, +oo); x) sage: f.restriction([[-1,1], [3,3]]) - piecewise(x|-->x on [-1, 1] + {3}; x) + piecewise(x|-->x on [-1, 1] ∪ {3}; x) """ restricted_domain = RealSet(*restricted_domain) new_param = [] @@ -559,7 +559,7 @@ def extension(self, parameters, variable, extension, extension_domain=None): ValueError: point 3 is not in the domain sage: g = f.extension(0); g - piecewise(x|-->x on (-1, 1), x|-->0 on (-oo, -1] + [1, +oo); x) + piecewise(x|-->x on (-1, 1), x|-->0 on (-oo, -1] ∪ [1, +oo); x) sage: g(3) 0 @@ -583,7 +583,7 @@ def unextend_zero(self, parameters, variable): sage: f = piecewise([((-1,1), x)]); f piecewise(x|-->x on (-1, 1); x) sage: g = f.extension(0); g - piecewise(x|-->x on (-1, 1), x|-->0 on (-oo, -1] + [1, +oo); x) + piecewise(x|-->x on (-1, 1), x|-->0 on (-oo, -1] ∪ [1, +oo); x) sage: g(3) 0 sage: h = g.unextend_zero() @@ -652,7 +652,7 @@ def piecewise_add(self, parameters, variable, other): sage: f = piecewise([([0,1], 1), ((2,3), x)]) sage: g = piecewise([((1/2, 2), x)]) sage: f.piecewise_add(g).unextend_zero() - piecewise(x|-->1 on (0, 1/2], x|-->x + 1 on (1/2, 1], x|-->x on (1, 2) + (2, 3); x) + piecewise(x|-->1 on (0, 1/2], x|-->x + 1 on (1/2, 1], x|-->x on (1, 2) ∪ (2, 3); x) """ points = ([minus_infinity] + sorted(set(self.end_points() + other.end_points())) + From 46eed0eeb3fc4e9509b7c55df967455ae49984d0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 16:25:17 -0700 Subject: [PATCH 098/280] RealSet.ambient: Change to a normal method --- src/sage/sets/real_set.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index b27e39fd25b..5d39fbe0029 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -1048,10 +1048,9 @@ def get_interval(self, i): # ParentMethods of Subobjects - @staticmethod - def ambient(): + def ambient(self): """ - Construct the real line + Return the ambient space (the real line). EXAMPLES:: From 8fdb104f7c0b84b0db5a280a0265f690d1a867b5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 16:37:07 -0700 Subject: [PATCH 099/280] RealSet.boundary: Add another doctest --- src/sage/sets/real_set.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 042b9bf6239..433cd77cfb3 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -1799,6 +1799,9 @@ def boundary(self): {2} sage: RealSet([1, 2], (3, 4)).boundary() {1} ∪ {2} ∪ {3} ∪ {4} + sage: RealSet((1, 2), (2, 3)).boundary() + {1} ∪ {2} ∪ {3} + """ return RealSet(*[RealSet.point(x) for i in self._intervals for x in i.boundary_points()]) From cc1ba6c69d07021bf276d8613457e46a53d4d356 Mon Sep 17 00:00:00 2001 From: Kiran Kedlaya Date: Sun, 30 May 2021 18:14:33 -0700 Subject: [PATCH 100/280] Correct use of long longs in hypergeometric_misc --- src/sage/modular/hypergeometric_misc.pyx | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/sage/modular/hypergeometric_misc.pyx b/src/sage/modular/hypergeometric_misc.pyx index 0faa55e3701..7f3c8ee494d 100644 --- a/src/sage/modular/hypergeometric_misc.pyx +++ b/src/sage/modular/hypergeometric_misc.pyx @@ -24,12 +24,20 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, sage: D = 1 sage: hgm_coeffs(7, 1, 2, gamma, [0]*6, D, gtable, prec, False) [7, 2*7, 6*7, 7, 6, 4*7] + + Check issue from :trac:`28404`:: + + sage: H = Hyp(cyclotomic=[[10,2],[1,1,1,1,1]]) + sage: u = H.euler_factor(2,79) # indirect doctest + sage: u.reverse().is_weil_polynomial() + True + """ from sage.rings.padics.factory import Zp cdef int gl, j, k, l, v, gv cdef long long i, q1, w, w1, w2, q2, r, r1 - cdef bint flip + cdef bint flip, need_lift q1 = p ** f - 1 gl = len(gamma) @@ -55,6 +63,14 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, gtab2 = array.array('l', [0]) * q1 for r in range(q1): gtab2[r] = gtable[r].lift() % q2 + else: + gtab2 = array.array('q', [0]) * q1 + try: + for r in range(q1): + gtab2[r] = gtable[r] + except TypeError: + for r in range(q1): + gtab2[r] = gtable[r].lift() if f == 1: for r in range(q1): digit_count[r] = r @@ -108,7 +124,7 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, for j in range(-gv): w1 = w1 * w2 % q2 else: - w2 = gtable[r1] + w2 = gtab2[r1] if gv > 0: for j in range(gv): u *= w2 From 1942c714237d5f2996587bfeda80404b57720696 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 May 2021 19:22:48 -0700 Subject: [PATCH 101/280] ScalarField.codomain: New, put scalar fields in category of continuous maps --- src/sage/manifolds/differentiable/manifold.py | 4 ++-- src/sage/manifolds/manifold.py | 6 +++--- src/sage/manifolds/scalarfield.py | 15 +++++++++++++++ src/sage/manifolds/scalarfield_algebra.py | 7 ++++--- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index 61d94a414f8..d7af8ab4efc 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -196,7 +196,7 @@ Algebra of differentiable scalar fields on the 2-dimensional differentiable manifold S^2 sage: f.parent().category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces A differentiable manifold has a default vector frame, which, unless otherwise specified, is the coordinate frame associated with the first defined chart:: @@ -376,7 +376,7 @@ Algebra of differentiable scalar fields on the 1-dimensional complex manifold C* sage: f.parent().category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces A vector field on the Riemann sphere:: diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 41fb5a18234..aaa137fd7ef 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -188,7 +188,7 @@ sage: f.parent() Algebra of scalar fields on the 2-dimensional topological manifold S^2 sage: f.parent().category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces .. RUBRIC:: Example 2: the Riemann sphere as a topological manifold of @@ -295,7 +295,7 @@ Algebra of scalar fields on the Complex 1-dimensional topological manifold C* sage: f.parent().category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces AUTHORS: @@ -1833,7 +1833,7 @@ def scalar_field_algebra(self): sage: CU = U.scalar_field_algebra() ; CU Algebra of scalar fields on the Open subset U of the 3-dimensional topological manifold M sage: CU.category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces sage: CU.zero() Scalar field zero on the Open subset U of the 3-dimensional topological manifold M diff --git a/src/sage/manifolds/scalarfield.py b/src/sage/manifolds/scalarfield.py index 98e931e212e..42ad3fcba91 100644 --- a/src/sage/manifolds/scalarfield.py +++ b/src/sage/manifolds/scalarfield.py @@ -1580,6 +1580,21 @@ def domain(self): """ return self._domain + def codomain(self): + r""" + Return the codomain of the scalar field. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: c_xy. = M.chart() + sage: f = M.scalar_field(x+2*y) + sage: f.codomain() + Real Field with 53 bits of precision + + """ + return self._domain.base_field() + def copy(self, name=None, latex_name=None): r""" Return an exact copy of the scalar field. diff --git a/src/sage/manifolds/scalarfield_algebra.py b/src/sage/manifolds/scalarfield_algebra.py index 3ee4b383f9d..3e05fb0371f 100644 --- a/src/sage/manifolds/scalarfield_algebra.py +++ b/src/sage/manifolds/scalarfield_algebra.py @@ -35,6 +35,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.misc.cachefunc import cached_method from sage.categories.commutative_algebras import CommutativeAlgebras +from sage.categories.topological_spaces import TopologicalSpaces from sage.symbolic.ring import SR from sage.manifolds.scalarfield import ScalarField @@ -86,11 +87,11 @@ class ScalarFieldAlgebra(UniqueRepresentation, Parent): :class:`~sage.symbolic.ring.SymbolicRing`):: sage: CM.category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces sage: CM.base_ring() Symbolic Ring sage: CW.category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces sage: CW.base_ring() Symbolic Ring @@ -383,7 +384,7 @@ def __init__(self, domain): if domain.base_field_type() in ['real', 'complex']: base_field = SR Parent.__init__(self, base=base_field, - category=CommutativeAlgebras(base_field)) + category=CommutativeAlgebras(base_field) & TopologicalSpaces().Homsets()) self._domain = domain self._populate_coercion_lists_() From b50d9c6047c1ad5167af7db8a8127f72ab18a853 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Mon, 31 May 2021 01:52:42 -0500 Subject: [PATCH 102/280] Fix doc building suggestion by slelievre --- src/sage/schemes/elliptic_curves/ell_rational_field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 874589b7ff3..ad776c61cf6 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -2567,7 +2567,7 @@ def saturation(self, points, verbose=False, max_prime=-1, min_prime=2): ALGORITHM: Uses Cremona's ``eclib`` package, which computes a bound on the saturation index. To `p`-saturate, or prove `p`-saturation, we consider the reductions of the points - modulo primes `q` of good reduction such that `E(\FF_q)` has + modulo primes `q` of good reduction such that `E(\GF{q})` has order divisible by `p`. .. note:: From a089bf7954dfd24446279ce5f9ab77c838f062e7 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Mon, 31 May 2021 01:48:54 -0500 Subject: [PATCH 103/280] allow cross compiling --- build/pkgs/brial/spkg-configure.m4 | 36 ++++++++++++++++++++++-- build/pkgs/curl/spkg-configure.m4 | 7 ++++- build/pkgs/flint/spkg-configure.m4 | 3 +- build/pkgs/lcalc/spkg-configure.m4 | 18 ++++++++++-- build/pkgs/libbraiding/spkg-configure.m4 | 20 +++++++++++++ build/pkgs/mpfi/spkg-configure.m4 | 3 +- build/pkgs/ntl/spkg-configure.m4 | 4 +++ build/pkgs/openblas/spkg-configure.m4 | 3 +- build/pkgs/pari/spkg-configure.m4 | 11 +++++--- build/pkgs/sqlite/spkg-configure.m4 | 3 +- build/pkgs/symmetrica/spkg-configure.m4 | 3 +- 11 files changed, 96 insertions(+), 15 deletions(-) diff --git a/build/pkgs/brial/spkg-configure.m4 b/build/pkgs/brial/spkg-configure.m4 index a0f00838c75..6a94a42721b 100644 --- a/build/pkgs/brial/spkg-configure.m4 +++ b/build/pkgs/brial/spkg-configure.m4 @@ -7,7 +7,7 @@ SAGE_SPKG_CONFIGURE([brial], [ AC_LANG_PUSH(C++) SAVED_LIBS=$LIBS LIBS="$LIBS -lbrial -lbrial_groebner" - AC_MSG_CHECKING([if we can link against brial libraries]) + AC_MSG_CHECKING([if we can link against brial libraries and run]) AC_RUN_IFELSE([ AC_LANG_PROGRAM([ #include @@ -48,11 +48,43 @@ SAGE_SPKG_CONFIGURE([brial], [ ], [ AC_MSG_RESULT([yes]) sage_spkg_install_brial=no - ]) + ], [AC_MSG_RESULT([cross compiling. Assume yes]) + sage_spkg_install_brial=no]) ], [ AC_MSG_RESULT([no]) sage_spkg_install_brial=yes + ], + [ + AC_MSG_CHECKING([if we can link against brial libraries]) + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([ + #include + #include + USING_NAMESPACE_PBORI + USING_NAMESPACE_PBORIGB + + class MyConstant : public BooleConstant{ + public: void negate() { this->m_value = !this->m_value; } + }; + ],[ + BoolePolyRing r = BoolePolyRing(2, COrderEnums::dlex); + ReductionStrategy rs = ReductionStrategy(r); + rs.llReduceAll(); // uses groebner lib + if (2 != r.nVariables()) { return 1; } + if (r.constant(true) == r.constant(false)) { return 2; } + MyConstant f = MyConstant(); + f.negate(); // ensures v1.1.0+ if m_value isn't const + if (!f.isOne()) { return 3; } + return 0; + ]) + ],[ + AC_MSG_RESULT([yes]) + sage_spkg_install_brial=yes + ],[ + AC_MSG_RESULT([no]) + sage_spkg_install_brial=no + ]) ]) LIBS=$SAVED_LIBS AC_LANG_POP diff --git a/build/pkgs/curl/spkg-configure.m4 b/build/pkgs/curl/spkg-configure.m4 index d88561511b8..b938bd7547d 100644 --- a/build/pkgs/curl/spkg-configure.m4 +++ b/build/pkgs/curl/spkg-configure.m4 @@ -20,7 +20,12 @@ SAGE_SPKG_CONFIGURE([curl], [ AC_RUN_IFELSE([AC_LANG_PROGRAM([[#include ]],[[ curl_easy_setopt(NULL,CURLOPT_URL,NULL); - ]])], sage_libcurl_cv_lib_curl_executable=yes, sage_libcurl_cv_lib_curl_executable=no) + ]])], sage_libcurl_cv_lib_curl_executable=yes, sage_libcurl_cv_lib_curl_executable=no, [ + dnl cross compiling. link only + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]],[[ + curl_easy_setopt(NULL,CURLOPT_URL,NULL); + ]])], sage_libcurl_cv_lib_curl_executable=yes, sage_libcurl_cv_lib_curl_executable=no)] + ) ]) AS_IF([test "$sage_libcurl_cv_lib_curl_executable" = "no"], [sage_spkg_install_curl=yes]) ]) diff --git a/build/pkgs/flint/spkg-configure.m4 b/build/pkgs/flint/spkg-configure.m4 index b697e478d74..de3ad59cc2f 100644 --- a/build/pkgs/flint/spkg-configure.m4 +++ b/build/pkgs/flint/spkg-configure.m4 @@ -22,7 +22,8 @@ SAGE_SPKG_CONFIGURE([flint], [ [#endif]])], [AC_MSG_RESULT([GC not enabled. Good.])], [AC_MSG_RESULT([GC enabled. Incompatible with Sage.]) - sage_spkg_install_flint=yes]) + sage_spkg_install_flint=yes], + [AC_MSG_RESULT(["cross compiling. assuming GC is not enabled"])]) ], [sage_spkg_install_flint=yes]) ], [sage_spkg_install_flint=yes]) ], [sage_spkg_install_flint=yes]) diff --git a/build/pkgs/lcalc/spkg-configure.m4 b/build/pkgs/lcalc/spkg-configure.m4 index 19a87c8d304..4225eef4e7a 100644 --- a/build/pkgs/lcalc/spkg-configure.m4 +++ b/build/pkgs/lcalc/spkg-configure.m4 @@ -24,10 +24,22 @@ SAGE_SPKG_CONFIGURE([lcalc], [ AC_MSG_RESULT([no; install lcalc]) sage_spkg_install_lcalc=yes LIBS=$LCALC_SAVED_LIBS + ], [ + AC_MSG_RESULT([cross compiling; check linking only]) + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[#include ]], + [[initialize_globals(); + Complex x; + x = Pi*I; + L_function L4; + return 0;]] + )], [AC_MSG_RESULT([yes; use lcalc from the system])], [ + AC_MSG_RESULT([no; install lcalc]) + sage_spkg_install_lcalc=yes + LIBS=$LCALC_SAVED_LIBS + ]) + ]) ]) - ], [ - AC_MSG_RESULT([no. Install lcalc]) - sage_spkg_install_lcalc=yes]) ]) ]) m4_popdef([SAGE_LCALC_MINVER]) diff --git a/build/pkgs/libbraiding/spkg-configure.m4 b/build/pkgs/libbraiding/spkg-configure.m4 index 81f4d57f225..8fd86aa20ea 100644 --- a/build/pkgs/libbraiding/spkg-configure.m4 +++ b/build/pkgs/libbraiding/spkg-configure.m4 @@ -24,6 +24,26 @@ SAGE_SPKG_CONFIGURE([libbraiding], [ [ AC_MSG_RESULT([no]) sage_spkg_install_libbraiding=yes + ],[ + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([ + #include + #include + using namespace Braiding; + ],[ + // Mimic BraidGroup(2)([1,1]).thurston_type() in SageMath. + // thurstontype == 1 corresponds to "periodic" + if (thurstontype(2, {1,1}) == 1) { return 0; } else { return 1; } + ]) + ], + [ + AC_MSG_RESULT([yes]) + sage_spkg_install_libbraiding=no + ], + [ + AC_MSG_RESULT([no]) + sage_spkg_install_libbraiding=yes + ]) ]) LIBS=$SAVED_LIBS AC_LANG_POP diff --git a/build/pkgs/mpfi/spkg-configure.m4 b/build/pkgs/mpfi/spkg-configure.m4 index 7805ef4a2dd..0cdb10a920e 100644 --- a/build/pkgs/mpfi/spkg-configure.m4 +++ b/build/pkgs/mpfi/spkg-configure.m4 @@ -28,7 +28,8 @@ SAGE_SPKG_CONFIGURE([mpfi], [ else return 1; ]])], [AC_MSG_RESULT([yes])], [ AC_MSG_RESULT([no]) - sage_spkg_install_mpfi=yes]) + sage_spkg_install_mpfi=yes], + [AC_MSG_RESULT([cross compiling. assume yes])]) AC_LANG_POP(C)], [sage_spkg_install_mpfi=yes]) fi diff --git a/build/pkgs/ntl/spkg-configure.m4 b/build/pkgs/ntl/spkg-configure.m4 index 48e41de6ea7..228c672cc4e 100644 --- a/build/pkgs/ntl/spkg-configure.m4 +++ b/build/pkgs/ntl/spkg-configure.m4 @@ -29,6 +29,10 @@ SAGE_SPKG_CONFIGURE([ntl], [ ], [ AC_MSG_RESULT([no]) sage_spkg_install_ntl=yes + ], [ + dnl assume that the person running cross-compiling + dnl knows what they are doing + AC_MSG_RESULT([yes]) ]) ]) diff --git a/build/pkgs/openblas/spkg-configure.m4 b/build/pkgs/openblas/spkg-configure.m4 index 912248c474c..177bbb1d4ff 100644 --- a/build/pkgs/openblas/spkg-configure.m4 +++ b/build/pkgs/openblas/spkg-configure.m4 @@ -75,7 +75,8 @@ SAGE_SPKG_CONFIGURE([openblas], [ + 100 * ]]SAGE_OPENBLAS_MIN_VERSION_MINOR[[ + ]]SAGE_OPENBLAS_MIN_VERSION_MICRO[[) return 1;]]) - ], [AS_VAR_SET([HAVE_OPENBLAS], [yes])], [AS_VAR_SET([HAVE_OPENBLAS], [no])]) + ], [AS_VAR_SET([HAVE_OPENBLAS], [yes])], [AS_VAR_SET([HAVE_OPENBLAS], [no])], + [AS_VAR_SET([HAVE_OPENBLAS], [yes])]) AC_LANG_POP([C]) AC_MSG_RESULT([$HAVE_OPENBLAS]) ]) diff --git a/build/pkgs/pari/spkg-configure.m4 b/build/pkgs/pari/spkg-configure.m4 index 9a1b0c7db8c..70d0a8a590e 100644 --- a/build/pkgs/pari/spkg-configure.m4 +++ b/build/pkgs/pari/spkg-configure.m4 @@ -119,7 +119,8 @@ SAGE_SPKG_CONFIGURE([pari], [ [return vers!=$gp_version;]])], [AC_MSG_RESULT([libpari's and GP's versions match. Good])], [AC_MSG_RESULT([libpari's version does not match GP's version. Not good]) - sage_spkg_install_pari=yes]) + sage_spkg_install_pari=yes], + [AC_MSG_RESULT([cross compiling. Assume they match])]) AC_MSG_CHECKING([is GP's version good enough? ]) AX_COMPARE_VERSION([$gp_version], [ge], [$SAGE_PARI_MINVER], [ AC_MSG_RESULT([yes]) @@ -142,13 +143,15 @@ SAGE_SPKG_CONFIGURE([pari], [ [[return strcmp(PARI_MT_ENGINE, "pthread") != 0]])], [AC_MSG_RESULT([yes. Good])], [AC_MSG_RESULT([no. Not good]) - sage_spkg_install_pari=yes]) + sage_spkg_install_pari=yes], + [AC_MSG_RESULT([cross compiling. Assume yes])]) ], [AC_MSG_RESULT([libpari's datadir does not match GP's datadir. Not good]) - sage_spkg_install_pari=yes]) + sage_spkg_install_pari=yes], + [AC_MSG_RESULT([cross compiling. Assume yes])]) ], [ AC_MSG_RESULT([no]) - sage_spkg_install_pari=yes]) + sage_spkg_install_pari=yes], [AC_MSG_RESULT([cross compiling. Assume yes])]) AC_LANG_POP() ], [sage_spkg_install_pari=yes]) fi dnl end main PARI test diff --git a/build/pkgs/sqlite/spkg-configure.m4 b/build/pkgs/sqlite/spkg-configure.m4 index c7fd821f593..901e27f19e3 100644 --- a/build/pkgs/sqlite/spkg-configure.m4 +++ b/build/pkgs/sqlite/spkg-configure.m4 @@ -26,7 +26,8 @@ SAGE_SPKG_CONFIGURE([sqlite], [ [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) LIBS="$SQLITE_SAVED_LIBS" - sage_spkg_install_sqlite=yes]) + sage_spkg_install_sqlite=yes], + [AC_MSG_RESULT([cross compiling. assume yes])]) m4_popdef([SAGE_SQLITE3_MIN_VERSION_MAJOR]) m4_popdef([SAGE_SQLITE3_MIN_VERSION_MINOR]) m4_popdef([SAGE_SQLITE3_MIN_VERSION_MICRO]) diff --git a/build/pkgs/symmetrica/spkg-configure.m4 b/build/pkgs/symmetrica/spkg-configure.m4 index 7dc77df703e..0a9b0d5c375 100644 --- a/build/pkgs/symmetrica/spkg-configure.m4 +++ b/build/pkgs/symmetrica/spkg-configure.m4 @@ -21,7 +21,8 @@ dnl check for one of its many functions [ende();]])], [AC_MSG_RESULT([appears to be a well-patched version.])], [AC_MSG_RESULT([buggy version. Sage will build its own.]) - sage_spkg_install_symmetrica=yes]) + sage_spkg_install_symmetrica=yes], + [AC_MSG_RESULT([cross compiling. Assume not buggy.])]) ], [sage_spkg_install_symmetrica=yes]) ], [sage_spkg_install_symmetrica=yes]) AC_LANG_POP(C) From b550a1d0639ba6bcad34ca678eeb8313ddb49f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Mon, 31 May 2021 16:25:36 +0200 Subject: [PATCH 104/280] 31886: Refresh graphs/graph_plot.py --- src/sage/graphs/graph_plot.py | 967 +++++++++++++++++++--------------- 1 file changed, 534 insertions(+), 433 deletions(-) diff --git a/src/sage/graphs/graph_plot.py b/src/sage/graphs/graph_plot.py index 073d36fd56e..b107ab586cc 100644 --- a/src/sage/graphs/graph_plot.py +++ b/src/sage/graphs/graph_plot.py @@ -8,20 +8,20 @@ sage: G = graphs.WheelGraph(15) sage: P = G.plot() - sage: P.show() # long time + sage: P.show() # long time .. PLOT:: sphinx_plot(graphs.WheelGraph(15)) -If you create a graph in Sage using the ``Graph`` command, then plot that graph, -the positioning of nodes is determined using the spring-layout algorithm. For -the special graph constructors, which you get using ``graphs.[tab]``, the -positions are preset. For example, consider the Petersen graph with default node -positioning vs. the Petersen graph constructed by this database:: +When plotting a graph created using Sage's ``Graph`` command, +node positions are determined using the spring-layout algorithm. +Special graphs available from ``graphs.*`` have preset positions. +For example, compare the two plots of the Petersen graph, +as obtained using ``Graph`` or as obtained from that database:: sage: petersen_spring = Graph(':I`ES@obGkqegW~') - sage: petersen_spring.show() # long time + sage: petersen_spring.show() # long time .. PLOT:: @@ -31,15 +31,15 @@ :: sage: petersen_database = graphs.PetersenGraph() - sage: petersen_database.show() # long time + sage: petersen_database.show() # long time .. PLOT:: petersen_database = graphs.PetersenGraph() sphinx_plot(petersen_database) -For all the constructors in this database (except some random graphs), the -position dictionary is filled in, instead of using the spring-layout algorithm. +All constructors in this database (except some random graphs) prefill +the position dictionary, bypassing the spring-layout positioning algorithm. **Plot options** @@ -65,21 +65,22 @@ Obviously, these values are overruled when arguments are given explicitly. -Here is how to define the default size of a graph drawing to be ``[6,6]``. The -first two calls to :meth:`~sage.graphs.generic_graph.GenericGraph.show` use this -option, while the third does not (a value for ``figsize`` is explicitly given):: +Here is how to define the default size of a graph drawing to be ``(6, 6)``. +The first two calls to :meth:`~sage.graphs.generic_graph.GenericGraph.show` +use this option, while the third does not (a value for ``figsize`` +is explicitly given):: sage: import sage.graphs.graph_plot - sage: sage.graphs.graph_plot.DEFAULT_SHOW_OPTIONS['figsize'] = [6,6] - sage: graphs.PetersenGraph().show() # long time + sage: sage.graphs.graph_plot.DEFAULT_SHOW_OPTIONS['figsize'] = (6, 6) + sage: graphs.PetersenGraph().show() # long time sage: graphs.ChvatalGraph().show() # long time - sage: graphs.PetersenGraph().show(figsize=[4,4]) # long time + sage: graphs.PetersenGraph().show(figsize=(4, 4)) # long time We can now reset the default to its initial value, and now display graphs as previously:: - sage: sage.graphs.graph_plot.DEFAULT_SHOW_OPTIONS['figsize'] = [4,4] - sage: graphs.PetersenGraph().show() # long time + sage: sage.graphs.graph_plot.DEFAULT_SHOW_OPTIONS['figsize'] = (4, 4) + sage: graphs.PetersenGraph().show() # long time sage: graphs.ChvatalGraph().show() # long time .. NOTE:: @@ -91,7 +92,7 @@ lines to `Sage's startup scripts <../../../repl/startup.html>`_. Example:: sage: import sage.graphs.graph_plot - sage: sage.graphs.graph_plot.DEFAULT_SHOW_OPTIONS['figsize'] = [4,4] + sage: sage.graphs.graph_plot.DEFAULT_SHOW_OPTIONS['figsize'] = (4, 4) **Index of methods and functions** @@ -108,76 +109,6 @@ :meth:`GraphPlot.layout_tree` | Compute a nice layout of a tree. """ -layout_options = { - 'layout': 'A layout algorithm -- one of : "acyclic", "circular" (plots the ' - 'graph with vertices evenly distributed on a circle), "ranked", ' - '"graphviz", "planar", "spring" (traditional spring layout, using the ' - 'graph\'s current positions as initial positions), or "tree" (the tree ' - 'will be plotted in levels, depending on minimum distance for the root).', - 'iterations': 'The number of times to execute the spring layout algorithm.', - 'heights': 'A dictionary mapping heights to the list of vertices at this height.', - 'spring': 'Use spring layout to finalize the current layout.', - 'tree_root': 'A vertex designation for drawing trees. A vertex of the tree ' - 'to be used as the root for the ``layout=\'tree\'`` option. If no root ' - 'is specified, then one is chosen close to the center of the tree. ' - 'Ignored unless ``layout=\'tree\'``.', - 'forest_roots': 'An iterable specifying which vertices to use as roots for ' - 'the ``layout=\'forest\'`` option. If no root is specified for a tree, ' - 'then one is chosen close to the center of the tree. ' - 'Ignored unless ``layout=\'forest\'``.', - 'tree_orientation': 'The direction of tree branches -- \'up\', \'down\', ' - '\'left\' or \'right\'.', - 'save_pos': 'Whether or not to save the computed position for the graph.', - 'dim': 'The dimension of the layout -- 2 or 3.', - 'prog': 'Which graphviz layout program to use -- one of "circo", "dot", ' - '"fdp", "neato", or "twopi".', - 'by_component': 'Whether to do the spring layout by connected component ' - '-- a boolean.', - } - -graphplot_options = layout_options.copy() - -graphplot_options.update( - {'pos': 'The position dictionary of vertices', - 'vertex_labels': 'Whether or not to draw vertex labels.', - 'vertex_color': 'Default color for vertices not listed ' - 'in vertex_colors dictionary.', - 'vertex_colors': 'Dictionary of vertex coloring : each ' - 'key is a color recognizable by matplotlib, and each ' - 'corresponding entry is a list of vertices. ', - 'vertex_size': 'The size to draw the vertices.', - 'vertex_shape': 'The shape to draw the vertices. ' - 'Currently unavailable for Multi-edged DiGraphs.', - 'edge_labels': 'Whether or not to draw edge labels.', - 'edge_style': 'The linestyle of the edges. It should be ' - 'one of "solid", "dashed", "dotted", dashdot", or ' - '"-", "--", ":", "-.", respectively. ', - 'edge_thickness': 'The thickness of the edges.', - 'edge_color': 'The default color for edges not listed in edge_colors.', - 'edge_colors': 'a dictionary specifying edge colors: each ' - 'key is a color recognized by matplotlib, and each ' - 'entry is a list of edges.', - 'color_by_label': 'Whether to color the edges according ' - 'to their labels. This also accepts a function or ' - 'dictionary mapping labels to colors.', - 'partition': 'A partition of the vertex set. If specified, ' - 'plot will show each cell in a different color. ' - 'vertex_colors takes precedence.', - 'loop_size': 'The radius of the smallest loop.', - 'dist': 'The distance between multiedges.', - 'max_dist': 'The max distance range to allow multiedges.', - 'talk': 'Whether to display the vertices in talk mode ' - '(larger and white).', - 'graph_border': 'Whether or not to draw a frame around the graph.', - 'edge_labels_background' : 'The color of the background of the edge labels'}) - - -_PLOT_OPTIONS_TABLE = "" -for key, value in graphplot_options.items(): - _PLOT_OPTIONS_TABLE += " ``"+str(key)+"`` | "+str(value)+"\n" -__doc__ = __doc__.format(PLOT_OPTIONS_TABLE=_PLOT_OPTIONS_TABLE) - - # **************************************************************************** # Copyright (C) 2009 Emily Kirkman # 2009 Robert L. Miller @@ -193,34 +124,135 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** + +from collections import defaultdict +from math import sqrt, cos, sin, acos, atan, pi from sage.structure.sage_object import SageObject -from sage.plot.all import Graphics, scatter_plot, bezier_path, line, arrow, text, circle -from math import sqrt, cos, sin, atan, pi +from sage.plot.all import ( + Graphics, scatter_plot, bezier_path, line, arrow, text, arc, circle) + +layout_options = { + 'layout': + 'A layout algorithm -- one of : "acyclic", "circular" (plots the ' + 'graph with vertices evenly distributed on a circle), "ranked", ' + '"graphviz", "planar", "spring" (traditional spring layout, using ' + 'the graph\'s current positions as initial positions), or "tree" ' + '(the tree will be plotted in levels, depending on minimum distance ' + 'for the root).', + 'iterations': + 'The number of times to execute the spring layout algorithm.', + 'heights': + 'A dictionary mapping heights to the list of vertices at this height.', + 'spring': + 'Use spring layout to finalize the current layout.', + 'tree_root': + 'A vertex designation for drawing trees. A vertex of the tree to ' + 'be used as the root for the ``layout=\'tree\'`` option. If no root ' + 'is specified, then one is chosen close to the center of the tree. ' + 'Ignored unless ``layout=\'tree\'``.', + 'forest_roots': + 'An iterable specifying which vertices to use as roots for the ' + '``layout=\'forest\'`` option. If no root is specified for a tree, ' + 'then one is chosen close to the center of the tree. ' + 'Ignored unless ``layout=\'forest\'``.', + 'tree_orientation': + 'The direction of tree branches -- \'up\', \'down\', ' + '\'left\' or \'right\'.', + 'save_pos': + 'Whether or not to save the computed position for the graph.', + 'dim': + 'The dimension of the layout -- 2 or 3.', + 'prog': + 'Which graphviz layout program to use -- one of ' + '"circo", "dot", "fdp", "neato", or "twopi".', + 'by_component': + 'Whether to do the spring layout by connected component -- a boolean.', + } + +graphplot_options = layout_options.copy() + +graphplot_options.update({ + 'pos': + 'The position dictionary of vertices.', + 'vertex_labels': + 'Whether or not to draw vertex labels.', + 'vertex_color': + 'Default color for vertices not listed ' + 'in vertex_colors dictionary.', + 'vertex_colors': + 'A dictionary specifying vertex colors: ' + 'each key is a color recognizable by matplotlib, ' + 'and each corresponding value is a list of vertices.', + 'vertex_size': + 'The size to draw the vertices.', + 'vertex_shape': + 'The shape to draw the vertices. ' + 'Currently unavailable for Multi-edged DiGraphs.', + 'edge_labels': + 'Whether or not to draw edge labels.', + 'edge_style': + 'The linestyle of the edges. It should be ' + 'one of "solid", "dashed", "dotted", dashdot", ' + 'or "-", "--", ":", "-.", respectively. ', + 'edge_thickness': + 'The thickness of the edges.', + 'edge_color': + 'The default color for edges not listed in edge_colors.', + 'edge_colors': + 'A dictionary specifying edge colors: ' + 'each key is a color recognized by matplotlib, ' + 'and each corresponding value is a list of edges.', + 'color_by_label': + 'Whether to color the edges according to their labels. This also ' + 'accepts a function or dictionary mapping labels to colors.', + 'partition': + 'A partition of the vertex set. If specified, plot will show each ' + 'cell in a different color; vertex_colors takes precedence.', + 'loop_size': + 'The radius of the smallest loop.', + 'dist': + 'The distance between multiedges.', + 'max_dist': + 'The max distance range to allow multiedges.', + 'talk': + 'Whether to display the vertices in talk mode (larger and white).', + 'graph_border': + 'Whether or not to draw a frame around the graph.', + 'edge_labels_background': + 'The color of the background of the edge labels.', + }) + +_PLOT_OPTIONS_TABLE = "" + +for key, value in graphplot_options.items(): + _PLOT_OPTIONS_TABLE += f" ``{key}`` | {value}\n" + +__doc__ = __doc__.format(PLOT_OPTIONS_TABLE=_PLOT_OPTIONS_TABLE) DEFAULT_SHOW_OPTIONS = { - "figsize" : [4,4] + 'figsize' : (4, 4) } DEFAULT_PLOT_OPTIONS = { - "vertex_size" : 200, - "vertex_labels" : True, - "layout" : None, - "edge_style" : 'solid', - "edge_thickness" : 1, - "edge_color" : 'black', - "edge_colors" : None, - "edge_labels" : False, - "iterations" : 50, - "tree_orientation" : 'down', - "heights" : None, - "graph_border" : False, - "talk" : False, - "color_by_label" : False, - "partition" : None, - "dist" : .075, - "max_dist" : 1.5, - "loop_size" : .075, - "edge_labels_background" : "white" + 'vertex_size' : 200, + 'vertex_labels' : True, + 'layout' : None, + 'edge_style' : 'solid', + 'edge_thickness' : 1, + 'edge_color' : 'black', + 'edge_colors' : None, + 'edge_labels' : False, + 'iterations' : 50, + 'tree_orientation' : 'down', + 'heights' : None, + 'graph_border' : False, + 'talk' : False, + 'color_by_label' : False, + 'partition' : None, + 'dist' : .075, + 'max_dist' : 1.5, + 'loop_size' : .075, + 'edge_labels_background' : 'white' } class GraphPlot(SageObject): @@ -237,27 +269,26 @@ def __init__(self, graph, options): sage: from sage.graphs.graph_plot import GraphPlot sage: options = { - ....: 'vertex_size': 200, - ....: 'vertex_labels': True, - ....: 'layout': None, - ....: 'edge_style': 'solid', - ....: 'edge_color': 'black', - ....: 'edge_colors': None, - ....: 'edge_labels': False, - ....: 'iterations': 50, - ....: 'tree_orientation': 'down', - ....: 'heights': None, - ....: 'graph_border': False, - ....: 'talk': False, - ....: 'color_by_label': False, - ....: 'partition': None, - ....: 'dist': .075, - ....: 'max_dist': 1.5, - ....: 'loop_size': .075, - ....: 'edge_labels_background': 'transparent'} - sage: g = Graph({0:[1, 2], 2:[3], 4:[0, 1]}) + ....: 'vertex_size': 200, + ....: 'vertex_labels': True, + ....: 'layout': None, + ....: 'edge_style': 'solid', + ....: 'edge_color': 'black', + ....: 'edge_colors': None, + ....: 'edge_labels': False, + ....: 'iterations': 50, + ....: 'tree_orientation': 'down', + ....: 'heights': None, + ....: 'graph_border': False, + ....: 'talk': False, + ....: 'color_by_label': False, + ....: 'partition': None, + ....: 'dist': .075, + ....: 'max_dist': 1.5, + ....: 'loop_size': .075, + ....: 'edge_labels_background': 'transparent'} + sage: g = Graph({0: [1, 2], 2: [3], 4: [0, 1]}) sage: GP = GraphPlot(g, options) - """ # Setting the default values if needed for k, value in DEFAULT_PLOT_OPTIONS.items(): @@ -267,11 +298,11 @@ def __init__(self, graph, options): self._nodelist = list(graph) self._graph = graph self._options = options # contains both plot and show options - self.set_pos() self._arcs = self._graph.has_multiple_edges(to_undirected=True) self._loops = self._graph.has_loops() self._arcdigraph = self._graph.is_directed() and self._arcs + self.set_pos() self.set_vertices() self.set_edges() @@ -283,11 +314,11 @@ def _repr_(self): This function is called implicitly by the code below:: - sage: g = Graph({0:[1,2], 2:[3], 4:[0,1]}) - sage: g.graphplot() # indirect doctest + sage: g = Graph({0: [1, 2], 2: [3], 4: [0, 1]}) + sage: g.graphplot() # indirect doctest GraphPlot object for Graph on 5 vertices """ - return "GraphPlot object for %s"%self._graph + return f"GraphPlot object for {self._graph}" def set_pos(self): """ @@ -297,8 +328,8 @@ def set_pos(self): This function is called implicitly by the code below:: - sage: g = Graph({0:[1,2], 2:[3], 4:[0,1]}) - sage: g.graphplot(save_pos=True, layout='circular') # indirect doctest + sage: g = Graph({0: [1, 2], 2: [3], 4: [0, 1]}) + sage: g.graphplot(save_pos=True, layout='circular') # indirect doctest GraphPlot object for Graph on 5 vertices The following illustrates the format of a position dictionary, but due @@ -315,24 +346,24 @@ def set_pos(self): sage: T = list(graphs.trees(7)) sage: t = T[3] - sage: t.plot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}) + sage: t.plot(heights={0: [0], 1: [4, 5, 1], 2: [2], 3: [3, 6]}) Graphics object consisting of 14 graphics primitives .. PLOT:: - g = Graph({0:[1,2], 2:[3], 4:[0,1]}) - g.graphplot(save_pos=True, layout='circular') # indirect doctest + g = Graph({0: [1, 2], 2: [3], 4: [0, 1]}) + g.graphplot(save_pos=True, layout='circular') # indirect doctest T = list(graphs.trees(7)) t = T[3] - P = t.plot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}) + P = t.plot(heights={0: [0], 1: [4, 5, 1], 2: [2], 3: [3, 6]}) sphinx_plot(P) TESTS: - Make sure that vertex locations are floats. Not being floats isn't a - bug in itself but makes it too easy to accidentally introduce a bug + Make sure that vertex locations are floats. Not being floats isn't + a bug in itself but made it too easy to accidentally introduce a bug elsewhere, such as in :meth:`set_edges` (:trac:`10124`), via silent - truncating division of integers:: + truncating division of Python 2 integers:: sage: g = graphs.FruchtGraph() sage: gp = g.graphplot() @@ -349,7 +380,7 @@ def set_pos(self): Graphics object consisting of 6 graphics primitives """ self._pos = self._graph.layout(**self._options) - # make sure the positions are floats (trac #10124) + # Make sure the positions are floats (trac #10124) self._pos = {k: (float(v[0]), float(v[1])) for k, v in self._pos.items()} @@ -357,17 +388,18 @@ def set_vertices(self, **vertex_options): """ Set the vertex plotting parameters for this ``GraphPlot``. - This function is called by the constructor but can also be called to - make updates to the vertex options of an existing ``GraphPlot`` object. - Note that the changes are cumulative. + This function is called by the constructor but can also be + called to make updates to the vertex options of an existing + ``GraphPlot`` object. Note that the changes are cumulative. EXAMPLES:: sage: g = Graph({}, loops=True, multiedges=True, sparse=True) - sage: g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - ....: (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sage: GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, - ....: edge_style='dashed') + sage: g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + ....: (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + ....: (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + sage: GP = g.graphplot(vertex_size=100, edge_labels=True, + ....: color_by_label=True, edge_style='dashed') sage: GP.set_vertices(talk=True) sage: GP.plot() Graphics object consisting of 22 graphics primitives @@ -377,25 +409,26 @@ def set_vertices(self, **vertex_options): .. PLOT:: - g = Graph({}, loops=True, multiedges=True, sparse=True) - g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'),(0,1,'e'),(0,1,'f'), - (0,1,'f'),(2,1,'g'),(2,2,'h')]) - GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, - edge_style='dashed') - GP.set_vertices(talk=True) - sphinx_plot(GP) + g = Graph({}, loops=True, multiedges=True, sparse=True) + g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + GP = g.graphplot(vertex_size=100, edge_labels=True, + color_by_label=True, edge_style='dashed') + GP.set_vertices(talk=True) + sphinx_plot(GP) .. PLOT:: - g = Graph({}, loops=True, multiedges=True, sparse=True) - g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'),(0,1,'e'),(0,1,'f'), - (0,1,'f'),(2,1,'g'),(2,2,'h')]) - GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, - edge_style='dashed') - GP.set_vertices(talk=True) - GP.set_vertices(vertex_color='green', vertex_shape='^') - sphinx_plot(GP) - + g = Graph({}, loops=True, multiedges=True, sparse=True) + g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + GP = g.graphplot(vertex_size=100, edge_labels=True, + color_by_label=True, edge_style='dashed') + GP.set_vertices(talk=True) + GP.set_vertices(vertex_color='green', vertex_shape='^') + sphinx_plot(GP) """ # Handle base vertex options voptions = {} @@ -412,12 +445,14 @@ def set_vertices(self, **vertex_options): else: voptions['markersize'] = self._options['vertex_size'] - if 'vertex_color' not in self._options or self._options['vertex_color'] is None: + if ('vertex_color' not in self._options + or self._options['vertex_color'] is None): vertex_color = '#fec7b8' else: vertex_color = self._options['vertex_color'] - if 'vertex_colors' not in self._options or self._options['vertex_colors'] is None: + if ('vertex_colors' not in self._options + or self._options['vertex_colors'] is None): if self._options['partition'] is not None: from sage.plot.colors import rainbow partition = self._options['partition'] @@ -442,67 +477,61 @@ def set_vertices(self, **vertex_options): if not isinstance(vertex_colors, dict): voptions['facecolor'] = vertex_colors + pos = list(self._pos.values()) if self._arcdigraph: - self._plot_components['vertices'] = [circle(center, - self._vertex_radius, - fill=True, - facecolor=vertex_colors, - edgecolor='black', - clip=False) - for center in self._pos.values()] + self._plot_components['vertices'] = [ + circle(p, self._vertex_radius, fill=True, clip=False, + edgecolor='black', facecolor=vertex_colors) + for p in pos] else: - self._plot_components['vertices'] = scatter_plot(list(self._pos.values()), - clip=False, **voptions) + self._plot_components['vertices'] = ( + scatter_plot(pos, clip=False, **voptions)) else: # Color list must be ordered: pos = [] colors = [] for i in vertex_colors: - pos += [self._pos[j] for j in vertex_colors[i]] - colors += [i] * len(vertex_colors[i]) + pos.extend([self._pos[j] for j in vertex_colors[i]]) + colors.extend([i] * len(vertex_colors[i])) # If all the vertices have not been assigned a color if len(self._pos) != len(pos): leftovers = [j for j in self._pos.values() if j not in pos] - pos += leftovers - colors += [vertex_color] * len(leftovers) + pos.extend(leftovers) + colors.extend([vertex_color] * len(leftovers)) if self._arcdigraph: - self._plot_components['vertices'] = [circle(pos[i], - self._vertex_radius, - fill=True, - facecolor=colors[i], - edgecolor='black', - clip=False) - for i in range(len(pos))] + self._plot_components['vertices'] = [ + circle(p, self._vertex_radius, fill=True, clip=False, + facecolor=colors[i], edgecolor='black') + for i, p in enumerate(pos)] else: - self._plot_components['vertices'] = scatter_plot(pos, - facecolor=colors, - clip=False, **voptions) + self._plot_components['vertices'] = scatter_plot( + pos, facecolor=colors, clip=False, **voptions) if self._options['vertex_labels']: self._plot_components['vertex_labels'] = [] # TODO: allow text options for v in self._nodelist: - self._plot_components['vertex_labels'].append(text(str(v), - self._pos[v], rgbcolor=(0,0,0), zorder=8)) + self._plot_components['vertex_labels'].append( + text(str(v), self._pos[v], rgbcolor=(0, 0, 0), zorder=8)) def set_edges(self, **edge_options): """ - Set the edge (or arrow) plotting parameters for the ``GraphPlot`` - object. + Set edge plotting parameters for the ``GraphPlot`` object. This function is called by the constructor but can also be called to - make updates to the vertex options of an existing ``GraphPlot`` object. + update the vertex options of an existing ``GraphPlot`` object. Note that the changes are cumulative. EXAMPLES:: sage: g = Graph(loops=True, multiedges=True, sparse=True) - sage: g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - ....: (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sage: GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, - ....: edge_style='dashed') + sage: g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + ....: (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + ....: (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + sage: GP = g.graphplot(vertex_size=100, edge_labels=True, + ....: color_by_label=True, edge_style='dashed') sage: GP.set_edges(edge_style='solid') sage: GP.plot() Graphics object consisting of 22 graphics primitives @@ -510,10 +539,11 @@ def set_edges(self, **edge_options): .. PLOT:: g = Graph(loops=True, multiedges=True, sparse=True) - g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) + g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) GP = g.graphplot(vertex_size=100, edge_labels=True, - color_by_label=True, edge_style='dashed') + color_by_label=True, edge_style='dashed') GP.set_edges(edge_style='solid') sphinx_plot(GP) @@ -526,10 +556,11 @@ def set_edges(self, **edge_options): .. PLOT:: g = Graph(loops=True, multiedges=True, sparse=True) - g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) + g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) GP = g.graphplot(vertex_size=100, edge_labels=True, - color_by_label=True, edge_style='dashed') + color_by_label=True, edge_style='dashed') GP.set_edges(edge_style='solid') GP.set_edges(edge_color='black') sphinx_plot(GP) @@ -537,10 +568,11 @@ def set_edges(self, **edge_options): :: sage: d = DiGraph(loops=True, multiedges=True, sparse=True) - sage: d.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - ....: (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sage: GP = d.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, - ....: edge_style='dashed') + sage: d.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + ....: (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + ....: (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + sage: GP = d.graphplot(vertex_size=100, edge_labels=True, + ....: color_by_label=True, edge_style='dashed') sage: GP.set_edges(edge_style='solid') sage: GP.plot() Graphics object consisting of 24 graphics primitives @@ -548,10 +580,11 @@ def set_edges(self, **edge_options): .. PLOT:: d = DiGraph(loops=True, multiedges=True, sparse=True) - d.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - GP = d.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, - edge_style='dashed') + d.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + GP = d.graphplot(vertex_size=100, edge_labels=True, + color_by_label=True, edge_style='dashed') GP.set_edges(edge_style='solid') sphinx_plot(GP) @@ -564,10 +597,11 @@ def set_edges(self, **edge_options): .. PLOT:: d = DiGraph(loops=True, multiedges=True, sparse=True) - d.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - GP = d.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, - edge_style='dashed') + d.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + GP = d.graphplot(vertex_size=100, edge_labels=True, + color_by_label=True, edge_style='dashed') GP.set_edges(edge_style='solid') GP.set_edges(edge_color='black') sphinx_plot(GP) @@ -575,10 +609,11 @@ def set_edges(self, **edge_options): TESTS:: sage: G = Graph("Fooba") - sage: G.show(edge_colors={'red':[(3,6),(2,5)]}) + sage: G.show(edge_colors={'red':[(3, 6), (2, 5)]}) - Verify that default edge labels are pretty close to being between the vertices - in some cases where they weren't due to truncating division (:trac:`10124`):: + Check default edge labels are pretty close to halfway between + the vertices in some cases where they weren't due to Python 2 + truncating division (:trac:`10124`):: sage: test_graphs = graphs.FruchtGraph(), graphs.BullGraph() sage: tol = 0.001 @@ -593,8 +628,8 @@ def set_edges(self, **edge_options): ....: textobj = elab[0] ....: x, y, s = textobj.x, textobj.y, textobj.string ....: v0, v1 = map(int, s.split()) - ....: vn = vector(((x-(vx[v0]+vx[v1])/2.), y-(vy[v0]+vy[v1])/2.)).norm() - ....: assert vn < tol + ....: m = sum(vector((vx[v], vy[v])) for v in (v0, v1))/2 + ....: assert (vector((x, y)) - m).norm() < tol Ticket :trac:`24051` is fixed:: @@ -604,11 +639,11 @@ def set_edges(self, **edge_options): Ticket :trac:`31542` is fixed:: - sage: stnc = 'ABCCCCDABCDABCDA' + sage: s = 'ABCCCCDABCDABCDA' sage: g = DiGraph({}, loops=True, multiedges=True) - sage: for a, b in [(stnc[i], stnc[i + 1]) for i in range(len(stnc) - 1)]: + sage: for a, b in [(s[i], u) for i, u in enumerate(s[1:])]: ....: g.add_edge(a, b, b) - sage: g.plot(color_by_label=True, edge_style='solid', layout='circular') + sage: g.plot(color_by_label=True, layout='circular') Graphics object consisting of 23 graphics primitives """ for arg in edge_options: @@ -635,69 +670,68 @@ def set_edges(self, **edge_options): self._plot_components['edge_labels'] = [] # Make dict collection of all edges (keep label and edge color) - edges_to_draw = {} + edges_to_draw = defaultdict(list) - def append_or_set(key, label, color, head): - if key in edges_to_draw: - edges_to_draw[key].append((label, color, head)) - else: - edges_to_draw[key] = [(label, color, head)] + def update(key, label, color, head): + edges_to_draw[key].append((label, color, head)) v_to_int = {v: i for i, v in enumerate(self._graph)} - if self._options['color_by_label'] or isinstance(self._options['edge_colors'], dict): + if (self._options['color_by_label'] + or isinstance(self._options['edge_colors'], dict)): if self._options['color_by_label']: - edge_colors = self._graph._color_by_label(format=self._options['color_by_label']) + edge_colors = self._graph._color_by_label( + format=self._options['color_by_label']) else: edge_colors = self._options['edge_colors'] edges_drawn = [] for color in edge_colors: for edge in edge_colors[color]: - if v_to_int[edge[0]] < v_to_int[edge[1]]: - key = (edge[0], edge[1]) + a, b = edge[0], edge[1] + if v_to_int[a] < v_to_int[b]: + key = a, b head = 1 else: - key = (edge[1], edge[0]) + key = b, a head = 0 if len(edge) < 3: - label = self._graph.edge_label(edge[0], edge[1]) + label = self._graph.edge_label(a, b) if isinstance(label, list): - append_or_set(key, label[-1], color, head) - edges_drawn.append((edge[0], edge[1], label[-1])) - for i in range(len(label) - 1): - edges_to_draw[key].append((label[i], color, head)) - edges_drawn.append((edge[0], edge[1], label[i])) + update(key, label[-1], color, head) + edges_drawn.append((a, b, label[-1])) + for lab in label[:-1]: + edges_to_draw[key].append((lab, color, head)) + edges_drawn.append((a, b, lab)) else: - append_or_set(key, label, color, head) - edges_drawn.append((edge[0], edge[1], label)) + update(key, label, color, head) + edges_drawn.append((a, b, label)) else: label = edge[2] - append_or_set(key, label, color, head) - edges_drawn.append((edge[0], edge[1], label)) + update(key, label, color, head) + edges_drawn.append((a, b, label)) # Add unspecified edges (default color black set in DEFAULT_PLOT_OPTIONS) - for edge in self._graph.edge_iterator(): - if ((edge[0], edge[1], edge[2]) not in edges_drawn and - (self._graph.is_directed() or - (edge[1], edge[0], edge[2]) not in edges_drawn - )): - if v_to_int[edge[0]] < v_to_int[edge[1]]: - key = (edge[0], edge[1]) + for a, b, c in self._graph.edge_iterator(): + if ((a, b, c) not in edges_drawn + and (self._graph.is_directed() + or (b, a, c) not in edges_drawn)): + if v_to_int[a] < v_to_int[b]: + key = (a, b) head = 1 else: - key = (edge[1], edge[0]) + key = (b, a) head = 0 - append_or_set(key, edge[2], self._options['edge_color'], head) + update(key, c, self._options['edge_color'], head) else: - for edge in self._graph.edge_iterator(): - if v_to_int[edge[0]] < v_to_int[edge[1]]: - key = (edge[0], edge[1]) + for a, b, c in self._graph.edge_iterator(): + if v_to_int[a] < v_to_int[b]: + key = a, b head = 1 else: - key = (edge[1], edge[0]) + key = b, a head = 0 - append_or_set(key, edge[2], self._options['edge_color'], head) + update(key, c, self._options['edge_color'], head) if edges_to_draw: self._plot_components['edges'] = [] @@ -707,7 +741,7 @@ def append_or_set(key, label, color, head): # Check for multi-edges or loops if self._arcs or self._loops: tmp = edges_to_draw.copy() - dist = self._options['dist'] * 2. + dist = self._options['dist'] * 2 loop_size = self._options['loop_size'] max_dist = self._options['max_dist'] from sage.functions.all import sqrt @@ -719,50 +753,55 @@ def append_or_set(key, label, color, head): len_local_labels = len(local_labels) if len_local_labels * dist > max_dist: distance = float(max_dist) / len_local_labels - curr_loop_size = loop_size - for i in range(len_local_labels): - self._plot_components['edges'].append(circle((self._pos[a][0], - self._pos[a][1]-curr_loop_size), curr_loop_size, - rgbcolor=local_labels[i][1], **eoptions)) + r = loop_size # current loop size + for lab, col, _ in local_labels: + c = circle((self._pos[a][0], self._pos[a][1] - r), r, + rgbcolor=col, **eoptions) + self._plot_components['edges'].append(c) if labels: - self._plot_components['edge_labels'].append(text(local_labels[i][0], - (self._pos[a][0], self._pos[a][1] - 2 * curr_loop_size), - background_color=self._options['edge_labels_background'])) - curr_loop_size += distance / 4 - elif len(edges_to_draw[a,b]) > 1: + bg = self._options['edge_labels_background'] + t = text(lab, + (self._pos[a][0], self._pos[a][1] - 2 * r), + background_color=bg) + self._plot_components['edge_labels'].append(t) + r += distance / 4 + elif len(edges_to_draw[a, b]) > 1: # Multi-edge local_labels = edges_to_draw.pop((a,b)) # Compute perpendicular bisector p1 = self._pos[a] p2 = self._pos[b] - M = ((p1[0]+p2[0])/2., (p1[1]+p2[1])/2.) # midpoint + p12 = (float(p2[0] - p1[0]), float(p2[1] - p1[1])) + m = ((p1[0] + p2[0])/2., (p1[1] + p2[1])/2.) # midpoint if not p1[1] == p2[1]: - S = float(p1[0]-p2[0]) / (p2[1]-p1[1]) # perp slope - y = lambda x: S*x-S*M[0]+M[1] # perp bisector line + s = -p12[0]/p12[1] # perp slope + y = lambda x: s*(x - m[0]) + m[1] # perp bisector line - # f,g are functions of distance d to determine x values - # on line y at d from point M - f = lambda d: sqrt(d**2/(1.+S**2)) + M[0] - g = lambda d: -sqrt(d**2/(1.+S**2)) + M[0] + # f, g are functions to determine x-values of point + # on line y at distance d from point m (on each side) + f = lambda d: sqrt(d**2/(1. + s**2)) + m[0] + g = lambda d: -sqrt(d**2/(1. + s**2)) + m[0] odd_x = f even_x = g if p1[0] == p2[0]: - odd_y = lambda d: M[1] + odd_y = lambda d: m[1] even_y = odd_y else: odd_y = lambda x: y(f(x)) even_y = lambda x: y(g(x)) else: - odd_x = lambda d: M[0] + odd_x = lambda d: m[0] even_x = odd_x - odd_y = lambda d: M[1] + d - even_y = lambda d: M[1] - d + odd_y = lambda d: m[1] + d + even_y = lambda d: m[1] - d + odd_xy = lambda d: (odd_x(d), odd_y(d)) + even_xy = lambda d: (even_x(d), even_y(d)) - # We now have the control points for each bezier curve + # We now have the control points for each Bezier curve # in terms of distance parameter d. - # Also note that the label for each edge should be drawn at d/2. + # Also note each edge label should be drawn at d/2. # (This is because we're using the perp bisectors). distance = dist len_local_labels = len(local_labels) @@ -771,75 +810,95 @@ def append_or_set(key, label, color, head): for i in range(len_local_labels // 2): k = (i + 1.0) * distance if self._arcdigraph: - odd_start = self._polar_hack_for_multidigraph(p1, - [odd_x(k), odd_y(k)], self._vertex_radius)[0] - odd_end = self._polar_hack_for_multidigraph([odd_x(k), odd_y(k)], - p2, self._vertex_radius)[1] - even_start = self._polar_hack_for_multidigraph(p1, - [even_x(k), even_y(k)], self._vertex_radius)[0] - even_end = self._polar_hack_for_multidigraph([even_x(k), even_y(k)], - p2, self._vertex_radius)[1] - self._plot_components['edges'].append(arrow(path=[[odd_start, - [odd_x(k), odd_y(k)], odd_end]], head=local_labels[2*i][2], - zorder=1, rgbcolor=local_labels[2*i][1], **eoptions)) - self._plot_components['edges'].append(arrow(path=[[even_start, - [even_x(k), even_y(k)], even_end]], head=local_labels[2*i+1][2], - zorder=1, rgbcolor=local_labels[2*i+1][1], **eoptions)) + vr = self._vertex_radius + ph = self._polar_hack_for_multidigraph + odd_start = ph(p1, odd_xy(k), vr)[0] + odd_end = ph(odd_xy(k), p2, vr)[1] + even_start = ph(p1, even_xy(k), vr)[0] + even_end = ph(even_xy(k), p2, vr)[1] + self._plot_components['edges'].append( + arrow(path=[[odd_start, odd_xy(k), odd_end]], + head=local_labels[2*i][2], zorder=1, + rgbcolor=local_labels[2*i][1], + **eoptions)) + self._plot_components['edges'].append( + arrow(path=[[even_start, even_xy(k), even_end]], + head=local_labels[2*i + 1][2], zorder=1, + rgbcolor=local_labels[2*i + 1][1], + **eoptions)) else: - self._plot_components['edges'].append(bezier_path([[p1, - [odd_x(k), odd_y(k)], p2]], zorder=1, - rgbcolor=local_labels[2*i][1], **eoptions)) - self._plot_components['edges'].append(bezier_path([[p1, - [even_x(k), even_y(k)], p2]], zorder=1, - rgbcolor=local_labels[2*i+1][1], **eoptions)) + self._plot_components['edges'].append( + bezier_path([[p1, odd_xy(k), p2]], zorder=1, + rgbcolor=local_labels[2*i][1], + **eoptions)) + self._plot_components['edges'].append( + bezier_path([[p1, even_xy(k), p2]], zorder=1, + rgbcolor=local_labels[2*i + 1][1], + **eoptions)) if labels: j = k / 2.0 - self._plot_components['edge_labels'].append(text(local_labels[2*i][0], - [odd_x(j), odd_y(j)], background_color=self._options['edge_labels_background'])) - self._plot_components['edge_labels'].append(text(local_labels[2*i+1][0], - [even_x(j), even_y(j)], - background_color=self._options['edge_labels_background'])) + bg = self._options['edge_labels_background'] + self._plot_components['edge_labels'].append( + text(local_labels[2*i][0], odd_xy(j), + background_color=bg)) + self._plot_components['edge_labels'].append( + text(local_labels[2*i + 1][0], even_xy(j), + background_color=bg)) if len_local_labels % 2: - edges_to_draw[a,b] = [local_labels[-1]] # draw line for last odd + # draw line for last odd + edges_to_draw[a, b] = [local_labels[-1]] is_directed = self._graph.is_directed() for a,b in edges_to_draw: if self._arcdigraph: - C, D = self._polar_hack_for_multidigraph(self._pos[a], self._pos[b], self._vertex_radius) - self._plot_components['edges'].append(arrow(C, D, - rgbcolor=edges_to_draw[a,b][0][1], head=edges_to_draw[a,b][0][2], - **eoptions)) + ph = self._polar_hack_for_multidigraph + C, D = ph(self._pos[a], self._pos[b], self._vertex_radius) + self._plot_components['edges'].append( + arrow(C, D, + rgbcolor=edges_to_draw[a, b][0][1], + head=edges_to_draw[a, b][0][2], + **eoptions)) if labels: - self._plot_components['edge_labels'].append(text(str(edges_to_draw[a,b][0][0]), - [(C[0]+D[0])/2., (C[1]+D[1])/2.], - background_color=self._options['edge_labels_background'])) + bg = self._options['edge_labels_background'] + self._plot_components['edge_labels'].append( + text(str(edges_to_draw[a, b][0][0]), + [(C[0] + D[0])/2., (C[1] + D[1])/2.], + background_color=bg)) elif is_directed: - self._plot_components['edges'].append(arrow(self._pos[a], self._pos[b], - rgbcolor=edges_to_draw[a,b][0][1], arrowshorten=self._arrowshorten, - head=edges_to_draw[a,b][0][2], **eoptions)) + self._plot_components['edges'].append( + arrow(self._pos[a], self._pos[b], + rgbcolor=edges_to_draw[a, b][0][1], + arrowshorten=self._arrowshorten, + head=edges_to_draw[a, b][0][2], + **eoptions)) else: - self._plot_components['edges'].append(line([self._pos[a], self._pos[b]], - rgbcolor=edges_to_draw[a,b][0][1], **eoptions)) + self._plot_components['edges'].append( + line([self._pos[a], self._pos[b]], + rgbcolor=edges_to_draw[a, b][0][1], + **eoptions)) if labels and not self._arcdigraph: - self._plot_components['edge_labels'].append(text(str(edges_to_draw[a,b][0][0]), - [(self._pos[a][0] + self._pos[b][0])/2., - (self._pos[a][1] + self._pos[b][1])/2.], - background_color=self._options['edge_labels_background'])) + bg = self._options['edge_labels_background'] + self._plot_components['edge_labels'].append( + text(str(edges_to_draw[a, b][0][0]), + [(self._pos[a][0] + self._pos[b][0])/2., + (self._pos[a][1] + self._pos[b][1])/2.], + background_color=bg)) def _polar_hack_for_multidigraph(self, A, B, VR): """ - Helper function to quickly compute the two points of intersection of a - line segment from A to B (xy tuples) and circles centered at A and B, - both with radius VR. Returns a tuple of xy tuples representing the two - points. + Helper function to quickly compute the two points of intersection + of a line segment from ``A`` to ``B`` (provided as xy pairs) and + circles centered at ``A`` and ``B``, both with radius ``VR``. + Returns a pair of xy pairs representing the two points. EXAMPLES:: sage: d = DiGraph(loops=True, multiedges=True, sparse=True) - sage: d.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - ....: (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sage: GP = d.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, - ....: edge_style='dashed') + sage: d.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + ....: (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + ....: (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + sage: GP = d.graphplot(vertex_size=100, edge_labels=True, + ....: color_by_label=True, edge_style='dashed') sage: GP._polar_hack_for_multidigraph((0, 1), (1, 1), .1) ([0.10..., 1.00...], [0.90..., 1.00...]) @@ -850,7 +909,8 @@ def _polar_hack_for_multidigraph(self, A, B, VR): sage: GP = DiGraph().graphplot() sage: GP._polar_hack_for_multidigraph((0, 1), (2, 2), .1) ([0.08..., 1.04...], [1.91..., 1.95...]) - sage: GP._polar_hack_for_multidigraph((int(0), int(1)), (int(2), int(2)), .1) + sage: GP._polar_hack_for_multidigraph((int(0), int(1)), + ....: (int(2), int(2)), .1) ([0.08..., 1.04...], [1.91..., 1.95...]) """ @@ -872,7 +932,7 @@ def _polar_hack_for_multidigraph(self, A, B, VR): def show(self, **kwds): """ - Show the (Di)Graph associated with this ``GraphPlot`` object. + Show the (di)graph associated with this ``GraphPlot`` object. INPUT: @@ -881,8 +941,8 @@ def show(self, **kwds): .. NOTE:: - - See :mod:`the module's documentation ` for - information on default values of this method. + - See :mod:`the module's documentation ` + for information on default values of this method. - Any options not used by plot will be passed on to the :meth:`~sage.plot.graphics.Graphics.show` method. @@ -890,13 +950,15 @@ def show(self, **kwds): EXAMPLES:: sage: C = graphs.CubeGraph(8) - sage: P = C.graphplot(vertex_labels=False, vertex_size=0, graph_border=True) + sage: P = C.graphplot(vertex_labels=False, vertex_size=0, + ....: graph_border=True) sage: P.show() .. PLOT:: C = graphs.CubeGraph(8) - P = C.graphplot(vertex_labels=False, vertex_size=0, graph_border=True) + P = C.graphplot(vertex_labels=False, vertex_size=0, + graph_border=True) sphinx_plot(P) """ @@ -913,9 +975,9 @@ def plot(self, **kwds): INPUT: - The options accepted by this method are to be found in the documentation - of the :mod:`sage.graphs.graph_plot` module, and the - :meth:`~sage.plot.graphics.Graphics.show` method. + The options accepted by this method are to be found in the + documentation of the :mod:`sage.graphs.graph_plot` module, + and the :meth:`~sage.plot.graphics.Graphics.show` method. .. NOTE:: @@ -926,15 +988,15 @@ def plot(self, **kwds): sage: from math import sin, cos, pi sage: P = graphs.PetersenGraph() - sage: d = {'#FF0000':[0,5], '#FF9900':[1,6], '#FFFF00':[2,7], '#00FF00':[3,8], - ....: '#0000FF':[4,9]} + sage: d = {'#FF0000': [0, 5], '#FF9900': [1, 6], '#FFFF00': [2, 7], + ....: '#00FF00': [3, 8], '#0000FF': [4,9]} sage: pos_dict = {} sage: for i in range(5): ....: x = float(cos(pi/2 + ((2*pi)/5)*i)) ....: y = float(sin(pi/2 + ((2*pi)/5)*i)) ....: pos_dict[i] = [x,y] ... - sage: for i in range(5,10): + sage: for i in range(5, 10): ....: x = float(0.5*cos(pi/2 + ((2*pi)/5)*i)) ....: y = float(0.5*sin(pi/2 + ((2*pi)/5)*i)) ....: pos_dict[i] = [x,y] @@ -946,15 +1008,15 @@ def plot(self, **kwds): from math import sin, cos, pi P = graphs.PetersenGraph() - d = {'#FF0000':[0,5], '#FF9900':[1,6], '#FFFF00':[2,7], '#00FF00':[3,8], - '#0000FF':[4,9]} + d = {'#FF0000': [0, 5], '#FF9900': [1, 6], '#FFFF00': [2, 7], + '#00FF00': [3, 8], '#0000FF': [4,9]} pos_dict = {} for i in range(5): x = float(cos(pi/2 + ((2*pi)/5)*i)) y = float(sin(pi/2 + ((2*pi)/5)*i)) pos_dict[i] = [x,y] - for i in range(5,10): + for i in range(5, 10): x = float(0.5*cos(pi/2 + ((2*pi)/5)*i)) y = float(0.5*sin(pi/2 + ((2*pi)/5)*i)) pos_dict[i] = [x,y] @@ -965,47 +1027,53 @@ def plot(self, **kwds): Here are some more common graphs with typical options:: sage: C = graphs.CubeGraph(8) - sage: P = C.graphplot(vertex_labels=False, vertex_size=0, graph_border=True) + sage: P = C.graphplot(vertex_labels=False, vertex_size=0, + ....: graph_border=True) sage: P.show() .. PLOT:: C = graphs.CubeGraph(8) - P = C.graphplot(vertex_labels=False, vertex_size=0, graph_border=True) + P = C.graphplot(vertex_labels=False, vertex_size=0, + graph_border=True) sphinx_plot(P) :: sage: G = graphs.HeawoodGraph().copy(sparse=True) - sage: for u,v,l in G.edges(): - ....: G.set_edge_label(u,v,'(' + str(u) + ',' + str(v) + ')') + sage: for u, v, l in G.edges(): + ....: G.set_edge_label(u, v, f'({u},{v})') sage: G.graphplot(edge_labels=True).show() .. PLOT:: G = graphs.HeawoodGraph().copy(sparse=True) - for u,v,l in G.edges(): - G.set_edge_label(u,v,'(' + str(u) + ',' + str(v) + ')') + for u, v, l in G.edges(): + G.set_edge_label(u, v, f'({u},{v})') sphinx_plot(G.graphplot(edge_labels=True)) The options for plotting also work with directed graphs:: - sage: D = DiGraph( { 0: [1, 10, 19], 1: [8, 2], 2: [3, 6], 3: [19, 4], - ....: 4: [17, 5], 5: [6, 15], 6: [7], 7: [8, 14], 8: [9], 9: [10, 13], - ....: 10: [11], 11: [12, 18], 12: [16, 13], 13: [14], 14: [15], 15: [16], - ....: 16: [17], 17: [18], 18: [19], 19: []}) - sage: for u,v,l in D.edges(): - ....: D.set_edge_label(u,v,'(' + str(u) + ',' + str(v) + ')') + sage: D = DiGraph({ + ....: 0: [1, 10, 19], 1: [8, 2], 2: [3, 6], 3: [19, 4], + ....: 4: [17, 5], 5: [6, 15], 6: [7], 7: [8, 14], 8: [9], + ....: 9: [10, 13], 10: [11], 11: [12, 18], 12: [16, 13], + ....: 13: [14], 14: [15], 15: [16], 16: [17], 17: [18], + ....: 18: [19], 19: []}) + sage: for u, v, l in D.edges(): + ....: D.set_edge_label(u, v, f'({u},{v})') sage: D.graphplot(edge_labels=True, layout='circular').show() .. PLOT:: - D = DiGraph( { 0: [1, 10, 19], 1: [8, 2], 2: [3, 6], 3: [19, 4], 4: [17, 5], - 5: [6, 15], 6: [7], 7: [8, 14], 8: [9], 9: [10, 13], 10: [11], - 11: [12, 18],12: [16, 13], 13: [14], 14: [15], 15: [16], 16: [17], - 17: [18], 18: [19], 19: []}) - for u,v,l in D.edges(): - D.set_edge_label(u,v,'(' + str(u) + ',' + str(v) + ')') + D = DiGraph({ + 0: [1, 10, 19], 1: [8, 2], 2: [3, 6], 3: [19, 4], + 4: [17, 5], 5: [6, 15], 6: [7], 7: [8, 14], 8: [9], + 9: [10, 13], 10: [11], 11: [12, 18], 12: [16, 13], + 13: [14], 14: [15], 15: [16], 16: [17], 17: [18], + 18: [19], 19: []}) + for u, v, l in D.edges(): + D.set_edge_label(u, v, f'({u},{v})') sphinx_plot(D.graphplot(edge_labels=True, layout='circular')) This example shows off the coloring of edges:: @@ -1015,12 +1083,13 @@ def plot(self, **kwds): sage: R = rainbow(5) sage: edge_colors = {} sage: for i in range(5): - ....: edge_colors[R[i]] = [] - sage: for u,v,l in C.edges(): - ....: for i in range(5): - ....: if u[i] != v[i]: - ....: edge_colors[R[i]].append((u,v,l)) - sage: C.graphplot(vertex_labels=False, vertex_size=0, edge_colors=edge_colors).show() + ....: edge_colors[R[i]] = [] + sage: for u, v, l in C.edges(): + ....: for i in range(5): + ....: if u[i] != v[i]: + ....: edge_colors[R[i]].append((u, v, l)) + sage: C.graphplot(vertex_labels=False, vertex_size=0, + ....: edge_colors=edge_colors).show() .. PLOT:: @@ -1030,24 +1099,26 @@ def plot(self, **kwds): edge_colors = {} for i in range(5): edge_colors[R[i]] = [] - for u,v,l in C.edges(): + for u, v, l in C.edges(): for i in range(5): if u[i] != v[i]: - edge_colors[R[i]].append((u,v,l)) + edge_colors[R[i]].append((u, v, l)) sphinx_plot(C.graphplot(vertex_labels=False, vertex_size=0, - edge_colors=edge_colors)) + edge_colors=edge_colors)) With the ``partition`` option, we can separate out same-color groups of vertices:: sage: D = graphs.DodecahedralGraph() - sage: Pi = [[6,5,15,14,7],[16,13,8,2,4],[12,17,9,3,1],[0,19,18,10,11]] + sage: Pi = [[6, 5, 15, 14, 7], [16, 13, 8, 2, 4], + ....: [12, 17, 9, 3, 1], [0, 19, 18, 10, 11]] sage: D.show(partition=Pi) .. PLOT:: D = graphs.DodecahedralGraph() - Pi = [[6,5,15,14,7],[16,13,8,2,4],[12,17,9,3,1],[0,19,18,10,11]] + Pi = [[6, 5, 15, 14, 7], [16, 13, 8, 2, 4], + [12, 17, 9, 3, 1], [0, 19, 18, 10, 11]] sphinx_plot(D.plot(partition=Pi)) Loops are also plotted correctly:: @@ -1068,27 +1139,29 @@ def plot(self, **kwds): sage: D = DiGraph({0:[0,1], 1:[2], 2:[3]}, loops=True) sage: D.show() - sage: D.show(edge_colors={(0,1,0):[(0,1,None),(1,2,None)],(0,0,0):[(2,3,None)]}) + sage: D.show(edge_colors={(0, 1, 0): [(0, 1, None), (1, 2, None)], + ....: (0, 0, 0): [(2, 3, None)]}) .. PLOT:: D = DiGraph({0:[0,1], 1:[2], 2:[3]}, loops=True) - P = D.plot(edge_colors={(0,1,0):[(0,1,None),(1,2,None)],(0,0,0):[(2,3,None)]}) + P = D.plot(edge_colors={(0, 1, 0): [(0, 1, None), (1, 2, None)], + (0, 0, 0): [(2, 3, None)]}) sphinx_plot(P) More options:: - sage: pos = {0:[0.0, 1.5], 1:[-0.8, 0.3], 2:[-0.6, -0.8], - ....: 3:[0.6, -0.8], 4:[0.8, 0.3]} - sage: g = Graph({0:[1], 1:[2], 2:[3], 3:[4], 4:[0]}) + sage: pos = {0: [0.0, 1.5], 1: [-0.8, 0.3], 2: [-0.6, -0.8], + ....: 3:[0.6, -0.8], 4:[0.8, 0.3]} + sage: g = Graph({0: [1], 1: [2], 2: [3], 3: [4], 4: [0]}) sage: g.graphplot(pos=pos, layout='spring', iterations=0).plot() Graphics object consisting of 11 graphics primitives .. PLOT:: - pos = {0:[0.0, 1.5], 1:[-0.8, 0.3], 2:[-0.6, -0.8], - 3:[0.6, -0.8], 4:[0.8, 0.3]} - g = Graph({0:[1], 1:[2], 2:[3], 3:[4], 4:[0]}) + pos = {0: [0.0, 1.5], 1: [-0.8, 0.3], 2: [-0.6, -0.8], + 3: [0.6, -0.8], 4:[0.8, 0.3]} + g = Graph({0: [1], 1: [2], 2: [3], 3: [4], 4: [0]}) P = g.graphplot(pos=pos, layout='spring', iterations=0).plot() sphinx_plot(P) @@ -1107,52 +1180,63 @@ def plot(self, **kwds): sage: T = list(graphs.trees(7)) sage: t = T[3] - sage: t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}).plot() + sage: t.graphplot(heights={0: [0], 1: [4, 5, 1], + ....: 2: [2], 3: [3, 6]} + ....: ).plot() Graphics object consisting of 14 graphics primitives .. PLOT:: T = list(graphs.trees(7)) t = T[3] - sphinx_plot(t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]})) + sphinx_plot(t.graphplot(heights={0: [0], 1: [4, 5, 1], + 2: [2], 3: [3, 6]})) :: sage: T = list(graphs.trees(7)) sage: t = T[3] - sage: t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}).plot() + sage: t.graphplot(heights={0: [0], 1: [4, 5, 1], + ....: 2: [2], 3: [3, 6]} + ....: ).plot() Graphics object consisting of 14 graphics primitives .. PLOT:: T = list(graphs.trees(7)) t = T[3] - sphinx_plot(t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]})) + sphinx_plot(t.graphplot(heights={0: [0], 1: [4, 5, 1], + 2: [2], 3: [3, 6]})) :: - sage: t.set_edge_label(0,1,-7) - sage: t.set_edge_label(0,5,3) - sage: t.set_edge_label(0,5,99) - sage: t.set_edge_label(1,2,1000) - sage: t.set_edge_label(3,2,'spam') - sage: t.set_edge_label(2,6,3/2) - sage: t.set_edge_label(0,4,66) - sage: t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}, edge_labels=True).plot() + sage: t.set_edge_label(0, 1, -7) + sage: t.set_edge_label(0, 5, 3) + sage: t.set_edge_label(0, 5, 99) + sage: t.set_edge_label(1, 2, 1000) + sage: t.set_edge_label(3, 2, 'spam') + sage: t.set_edge_label(2, 6, 3/2) + sage: t.set_edge_label(0, 4, 66) + sage: t.graphplot(heights={0: [0], 1: [4, 5, 1], + ....: 2: [2], 3: [3, 6]}, + ....: edge_labels=True + ....: ).plot() Graphics object consisting of 20 graphics primitives .. PLOT:: T = list(graphs.trees(7)) t = T[3] - t.set_edge_label(0,1,-7) - t.set_edge_label(0,5,3) - t.set_edge_label(0,5,99) - t.set_edge_label(1,2,1000) - t.set_edge_label(3,2,'spam') - t.set_edge_label(2,6,3/2) - t.set_edge_label(0,4,66) - sphinx_plot(t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}, edge_labels=True)) + t.set_edge_label(0, 1, -7) + t.set_edge_label(0, 5, 3) + t.set_edge_label(0, 5, 99) + t.set_edge_label(1, 2, 1000) + t.set_edge_label(3, 2, 'spam') + t.set_edge_label(2, 6, 3/2) + t.set_edge_label(0, 4, 66) + sphinx_plot(t.graphplot(heights={0: [0], 1: [4, 5, 1], + 2: [2], 3: [3, 6]}, + edge_labels=True)) :: @@ -1169,12 +1253,15 @@ def plot(self, **kwds): The tree layout is also useful:: sage: t = DiGraph('JCC???@A??GO??CO??GO??') - sage: t.graphplot(layout='tree', tree_root=0, tree_orientation="up").show() + sage: t.graphplot(layout='tree', tree_root=0, + ....: tree_orientation="up" + ....: ).show() .. PLOT:: t = DiGraph('JCC???@A??GO??CO??GO??') - sphinx_plot(t.graphplot(layout='tree', tree_root=0, tree_orientation="up")) + sphinx_plot(t.graphplot(layout='tree', tree_root=0, + tree_orientation="up")) More examples:: @@ -1190,37 +1277,50 @@ def plot(self, **kwds): sage: D = DiGraph(multiedges=True, sparse=True) sage: for i in range(5): - ....: D.add_edge((i,i+1,'a')) - ....: D.add_edge((i,i-1,'b')) - sage: D.graphplot(edge_labels=True,edge_colors=D._color_by_label()).plot() + ....: D.add_edge((i, i + 1, 'a')) + ....: D.add_edge((i, i - 1, 'b')) + sage: D.graphplot(edge_labels=True, + ....: edge_colors=D._color_by_label() + ....: ).plot() Graphics object consisting of 34 graphics primitives .. PLOT:: D = DiGraph(multiedges=True, sparse=True) for i in range(5): - D.add_edge((i,i+1,'a')) - D.add_edge((i,i-1,'b')) - sphinx_plot(D.graphplot(edge_labels=True,edge_colors=D._color_by_label())) + D.add_edge((i, i + 1, 'a')) + D.add_edge((i, i - 1, 'b')) + sphinx_plot(D.graphplot(edge_labels=True, + edge_colors=D._color_by_label())) :: sage: g = Graph({}, loops=True, multiedges=True, sparse=True) - sage: g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - ....: (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sage: g.graphplot(edge_labels=True, color_by_label=True, edge_style='dashed').plot() + sage: g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + ....: (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + ....: (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + sage: g.graphplot(edge_labels=True, + ....: color_by_label=True, + ....: edge_style='dashed' + ....: ).plot() Graphics object consisting of 22 graphics primitives .. PLOT:: g = Graph({}, loops=True, multiedges=True, sparse=True) - g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sphinx_plot(g.graphplot(edge_labels=True, color_by_label=True, edge_style='dashed')) + g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), + (0, 1, 'd'), (0, 1, 'e'), (0, 1, 'f'), + (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) + sphinx_plot(g.graphplot(edge_labels=True, + color_by_label=True, + edge_style='dashed')) The ``edge_style`` option may be provided in the short format too:: - sage: g.graphplot(edge_labels=True, color_by_label=True, edge_style='--').plot() + sage: g.graphplot(edge_labels=True, + ....: color_by_label=True, + ....: edge_style='--' + ....: ).plot() Graphics object consisting of 22 graphics primitives TESTS: @@ -1240,16 +1340,18 @@ def plot(self, **kwds): Make sure that no graphics primitive is clipped:: - sage: tadpole = Graph({0:[0,1]}).plot() + sage: tadpole = Graph({0: [0, 1]}).plot() sage: bbox = tadpole.get_minmax_data() sage: for part in tadpole: ....: part_bbox = part.get_minmax_data() - ....: assert bbox['xmin'] <= part_bbox['xmin'] <= part_bbox['xmax'] <= bbox['xmax'] - ....: assert bbox['ymin'] <= part_bbox['ymin'] <= part_bbox['ymax'] <= bbox['ymax'] + ....: assert (bbox['xmin'] <= part_bbox['xmin'] + ....: <= part_bbox['xmax'] <= bbox['xmax']) + ....: assert (bbox['ymin'] <= part_bbox['ymin'] + ....: <= part_bbox['ymax'] <= bbox['ymax']) Check that one can plot immutable graphs (:trac:`17340`):: - sage: Graph({0:[0]},immutable=True).plot() + sage: Graph({0: [0]}, immutable=True).plot() Graphics object consisting of 3 graphics primitives """ G = Graphics() @@ -1306,17 +1408,18 @@ def layout_tree(self, root, orientation): sage: G = graphs.HoffmanSingletonGraph() sage: T = Graph() sage: T.add_edges(G.min_spanning_tree(starting_vertex=0)) - sage: T.show(layout='tree', tree_root=0) # indirect doctest + sage: T.show(layout='tree', tree_root=0) # indirect doctest """ T = self._graph if not self._graph.is_tree(): - raise RuntimeError("Cannot use tree layout on this graph: self.is_tree() returns False.") + raise RuntimeError("Cannot use tree layout on this graph: " + "self.is_tree() returns False.") children = {root: T.neighbors(root)} - #always make a copy of the children because they get eaten + # Always make a copy of the children because they get eaten stack = [[u for u in children[root]]] stick = [root] parent = {u: root for u in children[root]} @@ -1333,7 +1436,6 @@ def slide(v, dx): Precondition: v and its descendents have already had their positions computed. - """ level = [v] while level: @@ -1344,7 +1446,6 @@ def slide(v, dx): obstruction[y] = max(x + 1, obstruction[y]) pos[u] = x, y nextlevel += children[u] - level = nextlevel while stack: From cd3ca79ab94f48321cc9aa3f0b34c1b3db4e405e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 31 May 2021 23:57:05 -0700 Subject: [PATCH 105/280] Update doctests for refined category of ScalarField --- .../vector_calculus/vector_calc_advanced.rst | 2 +- .../thematic_tutorials/vector_calculus/vector_calc_plane.rst | 2 +- src/sage/manifolds/differentiable/scalarfield_algebra.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_advanced.rst b/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_advanced.rst index ee4e6e95d12..f828b94d602 100644 --- a/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_advanced.rst +++ b/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_advanced.rst @@ -311,7 +311,7 @@ The set `C^\infty(\mathbb{E}^3)` of all smooth scalar fields on sage: CE Algebra of differentiable scalar fields on the Euclidean space E^3 sage: CE.category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces sage: f in CE True diff --git a/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_plane.rst b/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_plane.rst index 19ee71e386e..b3705c288f1 100644 --- a/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_plane.rst +++ b/src/doc/en/thematic_tutorials/vector_calculus/vector_calc_plane.rst @@ -612,7 +612,7 @@ on `\mathbb{E}^2`, `C^\infty(\mathbb{E}^2)`:: sage: CE is XE.base_ring() True sage: CE.category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces sage: rank(XE) 2 diff --git a/src/sage/manifolds/differentiable/scalarfield_algebra.py b/src/sage/manifolds/differentiable/scalarfield_algebra.py index c0a0a3b59fa..4ea29402c18 100644 --- a/src/sage/manifolds/differentiable/scalarfield_algebra.py +++ b/src/sage/manifolds/differentiable/scalarfield_algebra.py @@ -93,11 +93,11 @@ class DiffScalarFieldAlgebra(ScalarFieldAlgebra): algebras over `\RR` (represented here by Sage's Symbolic Ring):: sage: CM.category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces sage: CM.base_ring() Symbolic Ring sage: CW.category() - Category of commutative algebras over Symbolic Ring + Join of Category of commutative algebras over Symbolic Ring and Category of homsets of topological spaces sage: CW.base_ring() Symbolic Ring From 95e3da9e57add7608f05d871534a127e18d631ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Tue, 1 Jun 2021 12:10:59 +0200 Subject: [PATCH 106/280] 31886: Address reviewer comments --- src/sage/graphs/graph_plot.py | 44 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/sage/graphs/graph_plot.py b/src/sage/graphs/graph_plot.py index b107ab586cc..558eb4f22c8 100644 --- a/src/sage/graphs/graph_plot.py +++ b/src/sage/graphs/graph_plot.py @@ -672,9 +672,6 @@ def set_edges(self, **edge_options): # Make dict collection of all edges (keep label and edge color) edges_to_draw = defaultdict(list) - def update(key, label, color, head): - edges_to_draw[key].append((label, color, head)) - v_to_int = {v: i for i, v in enumerate(self._graph)} if (self._options['color_by_label'] @@ -689,25 +686,25 @@ def update(key, label, color, head): for edge in edge_colors[color]: a, b = edge[0], edge[1] if v_to_int[a] < v_to_int[b]: - key = a, b + key = (a, b) head = 1 else: - key = b, a + key = (b, a) head = 0 if len(edge) < 3: label = self._graph.edge_label(a, b) if isinstance(label, list): - update(key, label[-1], color, head) + edges_to_draw[key].append((label[-1], color, head)) edges_drawn.append((a, b, label[-1])) for lab in label[:-1]: edges_to_draw[key].append((lab, color, head)) edges_drawn.append((a, b, lab)) else: - update(key, label, color, head) + edges_to_draw[key].append((label, color, head)) edges_drawn.append((a, b, label)) else: label = edge[2] - update(key, label, color, head) + edges_to_draw[key].append((label, color, head)) edges_drawn.append((a, b, label)) # Add unspecified edges (default color black set in DEFAULT_PLOT_OPTIONS) @@ -721,17 +718,17 @@ def update(key, label, color, head): else: key = (b, a) head = 0 - update(key, c, self._options['edge_color'], head) + edges_to_draw[key].append((c, self._options['edge_color'], head)) else: for a, b, c in self._graph.edge_iterator(): if v_to_int[a] < v_to_int[b]: - key = a, b + key = (a, b) head = 1 else: - key = b, a + key = (b, a) head = 0 - update(key, c, self._options['edge_color'], head) + edges_to_draw[key].append((c, self._options['edge_color'], head)) if edges_to_draw: self._plot_components['edges'] = [] @@ -742,29 +739,31 @@ def update(key, label, color, head): if self._arcs or self._loops: tmp = edges_to_draw.copy() dist = self._options['dist'] * 2 - loop_size = self._options['loop_size'] + min_loop_size = self._options['loop_size'] max_dist = self._options['max_dist'] from sage.functions.all import sqrt for a, b in tmp: if a == b: - # Loops + # Multiple loops need varying loop radius starting at + # minimum loop size and respecting other distances + loop_size = min_loop_size # current loop radius distance = dist local_labels = edges_to_draw.pop((a, b)) len_local_labels = len(local_labels) if len_local_labels * dist > max_dist: distance = float(max_dist) / len_local_labels - r = loop_size # current loop size + loop_size_increment = distance / 4 + # Now add all the loops at this vertex, varying their size for lab, col, _ in local_labels: - c = circle((self._pos[a][0], self._pos[a][1] - r), r, - rgbcolor=col, **eoptions) + x, y = self._pos[a][0], self._pos[a][1] - loop_size + c = circle((x, y), loop_size, rgbcolor=col, **eoptions) self._plot_components['edges'].append(c) if labels: bg = self._options['edge_labels_background'] - t = text(lab, - (self._pos[a][0], self._pos[a][1] - 2 * r), - background_color=bg) + y -= loop_size # place label at bottom of loop + t = text(lab, (x, y), background_color=bg) self._plot_components['edge_labels'].append(t) - r += distance / 4 + loop_size += loop_size_increment elif len(edges_to_draw[a, b]) > 1: # Multi-edge local_labels = edges_to_draw.pop((a,b)) @@ -772,10 +771,9 @@ def update(key, label, color, head): # Compute perpendicular bisector p1 = self._pos[a] p2 = self._pos[b] - p12 = (float(p2[0] - p1[0]), float(p2[1] - p1[1])) m = ((p1[0] + p2[0])/2., (p1[1] + p2[1])/2.) # midpoint if not p1[1] == p2[1]: - s = -p12[0]/p12[1] # perp slope + s = (p1[0] - p2[0])/(p2[1] - p1[1]) # perp slope y = lambda x: s*(x - m[0]) + m[1] # perp bisector line # f, g are functions to determine x-values of point From b9909c061046a204354341833e3b3fcb4c6d7341 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 2 Jun 2021 09:58:10 -0700 Subject: [PATCH 107/280] ManifoldSubsetClosure: Add examples, fix docstring markup --- src/sage/manifolds/subsets/closure.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/sage/manifolds/subsets/closure.py b/src/sage/manifolds/subsets/closure.py index f66f5dec589..aa536bfafc9 100644 --- a/src/sage/manifolds/subsets/closure.py +++ b/src/sage/manifolds/subsets/closure.py @@ -20,17 +20,34 @@ class ManifoldSubsetClosure(ManifoldSubset): - """ + r""" Topological closure of a manifold subset in the topology of the manifold. INPUT: - ``subset`` -- a :class:`ManifoldSubset` - ``name`` -- (default: computed from the name of the subset) - string; name (symbol) given to the closure + string; name (symbol) given to the closure - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to denote the subset; if none is provided, it is set to ``name`` + EXAMPLES:: + + sage: from sage.manifolds.subsets.closure import ManifoldSubsetClosure + sage: M = Manifold(2, 'R^2', structure='topological') + sage: c_cart. = M.chart() # Cartesian coordinates on R^2 + sage: D = M.open_subset('D', coord_def={c_cart: x^2+y^2<1}); D + Open subset D of the 2-dimensional topological manifold R^2 + sage: cl_D = ManifoldSubsetClosure(D) + + The closure of the subset `D` is a subset of every closed superset + of `D`:: + + sage: S = D.superset('S') + sage: S.declare_closed() + sage: cl_D.is_subset(S) + True + """ def __init__(self, subset, name=None, latex_name=None): @@ -87,7 +104,7 @@ def _repr_(self): return "Topological closure {} of the {}".format(self._name, self._subset) def is_closed(self): - """ + r""" Return if ``self`` is a closed set. This implementation of the method always returns ``True``. From 16c6a7219848d2045fe92b1ca50eb0195a05d91f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 3 Jun 2021 11:03:22 -0700 Subject: [PATCH 108/280] ManifoldSubsetClosure, ManifoldSubset.closure: Improve documentation --- src/sage/manifolds/subset.py | 15 +++++++++++++++ src/sage/manifolds/subsets/closure.py | 12 ++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py index 3872def36d5..dfac550f302 100644 --- a/src/sage/manifolds/subset.py +++ b/src/sage/manifolds/subset.py @@ -2742,6 +2742,21 @@ def closure(self, name=None, latex_name=None): r""" Return the topological closure of ``self`` as a subset of the manifold. + INPUT: + + - ``name`` -- (default: ``None``) name given to the difference in the + case the latter has to be created; the default prepends ``cl_`` + to ``self._name`` + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + difference in the case the latter has to be created; the default + is built upon the operator `\mathrm{cl}` + + OUTPUT: + + - if ``self`` is already known to be closed (see :meth:`is_closed`), + ``self``; otherwise, an instance of + :class:`~sage.manifolds.subsets.closure.ManifoldSubsetClosure` + EXAMPLES:: sage: M = Manifold(2, 'R^2', structure='topological') diff --git a/src/sage/manifolds/subsets/closure.py b/src/sage/manifolds/subsets/closure.py index aa536bfafc9..ec60272cb8e 100644 --- a/src/sage/manifolds/subsets/closure.py +++ b/src/sage/manifolds/subsets/closure.py @@ -33,12 +33,20 @@ class ManifoldSubsetClosure(ManifoldSubset): EXAMPLES:: - sage: from sage.manifolds.subsets.closure import ManifoldSubsetClosure sage: M = Manifold(2, 'R^2', structure='topological') sage: c_cart. = M.chart() # Cartesian coordinates on R^2 sage: D = M.open_subset('D', coord_def={c_cart: x^2+y^2<1}); D Open subset D of the 2-dimensional topological manifold R^2 - sage: cl_D = ManifoldSubsetClosure(D) + sage: cl_D = D.closure() + sage: cl_D + Topological closure cl_D of the Open subset D of the 2-dimensional + topological manifold R^2 + sage: latex(cl_D) + \mathop{\mathrm{cl}}(D) + sage: type(cl_D) + + sage: cl_D.category() + Category of subobjects of sets The closure of the subset `D` is a subset of every closed superset of `D`:: From 8455aab6a2b5193539aea0a0c783aa02c43e6bfd Mon Sep 17 00:00:00 2001 From: Eric Gourgoulhon Date: Fri, 4 Jun 2021 10:56:24 +0200 Subject: [PATCH 109/280] Fix bug #31904 in pullback --- src/sage/manifolds/differentiable/diff_map.py | 174 ++++++++++-------- 1 file changed, 93 insertions(+), 81 deletions(-) diff --git a/src/sage/manifolds/differentiable/diff_map.py b/src/sage/manifolds/differentiable/diff_map.py index 8caab15c093..bc772ca1f13 100644 --- a/src/sage/manifolds/differentiable/diff_map.py +++ b/src/sage/manifolds/differentiable/diff_map.py @@ -13,6 +13,7 @@ AUTHORS: - Eric Gourgoulhon, Michal Bejger (2013-2015): initial version +- Marco Mancini (2018): pullback parallelization REFERENCES: @@ -22,8 +23,9 @@ """ # **************************************************************************** -# Copyright (C) 2015 Eric Gourgoulhon +# Copyright (C) 2015-2021 Eric Gourgoulhon # Copyright (C) 2015 Michal Bejger +# Copyright (C) 2018 Marco Mancini # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -917,13 +919,27 @@ def pullback(self, tensor): sage: pa.display() # should be zero (as any 3-form on a 2-dimensional manifold) Phi^*(A) = 0 + TESTS: + + Check that :trac:`31904` is fixed:: + + sage: E. = EuclideanSpace() + sage: polar. = E.polar_coordinates() + sage: g = E.metric() + sage: M = Manifold(1, 'M') + sage: Ct. = M.chart() + sage: F = M.diff_map(E, coord_functions={(Ct, polar): (1 + cos(t), t)}) + sage: gM = F.pullback(g) + sage: gM.display() + (2*cos(t) + 2) dt*dt + """ from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal from sage.manifolds.differentiable.vectorframe import CoordFrame from sage.tensor.modules.comp import (Components, CompWithSym, CompFullySym, CompFullyAntiSym) - def _pullback_chart(diff_map, tensor): + def _pullback_chart(diff_map, tensor, chart1, chart2): r""" Helper function performing the pullback on chart domains only. @@ -931,9 +947,13 @@ def _pullback_chart(diff_map, tensor): INPUT: - ``diff_map`` -- a restriction of ``self``, whose both - domain and codomain are chart domains + domain and codomain are chart domains, corresponding + respectively to ``chart1`` and ``chart2`` - ``tensor`` -- a covariant tensor field, whose domain is - the codomain of ``diff_map`` + the codomain of ``diff_map`` and whose components are known + in ``chart2.frame()`` + - ``chart1`` -- chart on the domain of ``diff_map`` + - ``chart2`` -- chart on the codomain of ``diff_map`` OUTPUT: @@ -963,82 +983,72 @@ def _pullback_chart(diff_map, tensor): nproc = Parallelism().get('tensor') ind_old_list = list(dom2.manifold().index_generator(ncov)) - for frame2 in tensor._components: - if isinstance(frame2, CoordFrame): - chart2 = frame2._chart - for chart1 in dom1._atlas: - if (chart1._domain is dom1 and (chart1, chart2) in - diff_map._coord_expression): - # Computation at the component level: - frame1 = chart1._frame - tcomp = tensor._components[frame2] - if isinstance(tcomp, CompFullySym): - ptcomp = CompFullySym(ring1, frame1, ncov, - start_index=si1, - output_formatter=of1) - elif isinstance(tcomp, CompFullyAntiSym): - ptcomp = CompFullyAntiSym(ring1, frame1, ncov, - start_index=si1, - output_formatter=of1) - elif isinstance(tcomp, CompWithSym): - ptcomp = CompWithSym(ring1, frame1, ncov, - start_index=si1, - output_formatter=of1, - sym=tcomp.sym, - antisym=tcomp.antisym) - else: - ptcomp = Components(ring1, frame1, ncov, - start_index=si1, - output_formatter=of1) - phi = diff_map._coord_expression[(chart1, chart2)] - jacob = phi.jacobian() - # X2 coordinates expressed in terms of - # X1 ones via the diff. map: - coord2_1 = phi(*(chart1._xx)) - - if nproc != 1: - # Parallel computation - lol = lambda lst, sz: [lst[i:i+sz] for i in range(0, len(lst), sz)] - ind_list = [ind for ind in ptcomp.non_redundant_index_generator()] - ind_step = max(1, int(len(ind_list)/nproc/2)) - local_list = lol(ind_list, ind_step) - # list of input parameters - listParalInput = [(tcomp,chart1,chart2,coord2_1,jacob, - ind_old_list,si1,si2,ncov,ind_part) for ind_part in local_list] - - @parallel(p_iter='multiprocessing', ncpus=nproc) - def paral_comp(tcomp,chart1,chart2,coord2_1,jacob, - ind_old_list,si1,si2,ncov,local_list_ind): - partial = [] - for ind_new in local_list_ind: - res = 0 - for ind_old in ind_old_list: - ff = tcomp[[ind_old]].coord_function(chart2) - t = chart1.function(ff(*coord2_1)) - for i in range(ncov): - t *= jacob[ind_old[i]-si2, ind_new[i]-si1] - res += t - partial.append([ind_new, res]) - return partial - - for ii, val in paral_comp(listParalInput): - for jj in val: - ptcomp[[jj[0]]] = jj[1] - - else: - # Sequential computation - for ind_new in ptcomp.non_redundant_index_generator(): - res = 0 - for ind_old in ind_old_list: - ff = tcomp[[ind_old]].coord_function(chart2) - t = chart1.function(ff(*coord2_1)) - for i in range(ncov): - t *= jacob[ind_old[i]-si2, ind_new[i]-si1] - res += t - ptcomp[ind_new] = res - - resu._components[frame1] = ptcomp - return resu + frame1 = chart1.frame() + frame2 = chart2.frame() + + tcomp = tensor._components[frame2] + if isinstance(tcomp, CompFullySym): + ptcomp = CompFullySym(ring1, frame1, ncov, start_index=si1, + output_formatter=of1) + elif isinstance(tcomp, CompFullyAntiSym): + ptcomp = CompFullyAntiSym(ring1, frame1, ncov, start_index=si1, + output_formatter=of1) + elif isinstance(tcomp, CompWithSym): + ptcomp = CompWithSym(ring1, frame1, ncov, start_index=si1, + output_formatter=of1, sym=tcomp.sym, + antisym=tcomp.antisym) + else: + ptcomp = Components(ring1, frame1, ncov, start_index=si1, + output_formatter=of1) + phi = diff_map._coord_expression[(chart1, chart2)] + jacob = phi.jacobian() + # X2 coordinates expressed in terms of X1 ones via the diff. map: + coord2_1 = phi(*(chart1._xx)) + + if nproc != 1: + # Parallel computation + lol = lambda lst, sz: [lst[i:i+sz] for i in range(0, len(lst), sz)] + ind_list = [ind for ind in ptcomp.non_redundant_index_generator()] + ind_step = max(1, int(len(ind_list)/nproc/2)) + local_list = lol(ind_list, ind_step) + # list of input parameters + listParalInput = [(tcomp, chart1, chart2, coord2_1, jacob, + ind_old_list, si1, si2, ncov, ind_part) + for ind_part in local_list] + + @parallel(p_iter='multiprocessing', ncpus=nproc) + def paral_comp(tcomp, chart1, chart2, coord2_1, jacob, + ind_old_list, si1, si2, ncov, local_list_ind): + partial = [] + for ind_new in local_list_ind: + res = 0 + for ind_old in ind_old_list: + ff = tcomp[[ind_old]].coord_function(chart2) + t = chart1.function(ff(*coord2_1)) + for i in range(ncov): + t *= jacob[ind_old[i]-si2, ind_new[i]-si1] + res += t + partial.append([ind_new, res]) + return partial + + for ii, val in paral_comp(listParalInput): + for jj in val: + ptcomp[[jj[0]]] = jj[1] + + else: + # Sequential computation + for ind_new in ptcomp.non_redundant_index_generator(): + res = 0 + for ind_old in ind_old_list: + ff = tcomp[[ind_old]].coord_function(chart2) + t = chart1.function(ff(*coord2_1)) + for i in range(ncov): + t *= jacob[ind_old[i]-si2, ind_new[i]-si1] + res += t + ptcomp[ind_new] = res + + resu._components[frame1] = ptcomp + return resu # End of function _pullback_chart # Special case of the identity map: @@ -1091,7 +1101,9 @@ def paral_comp(tcomp,chart1,chart2,coord2_1,jacob, if ch2dom.is_subset(tdom): self_r = self.restrict(chart1._domain, subcodomain=ch2dom) tensor_r = tensor.restrict(ch2dom) - resu_rst.append(_pullback_chart(self_r, tensor_r)) + if chart2.frame() in tensor_r._components: + resu_rst.append(_pullback_chart(self_r, tensor_r, + chart1, chart2)) dom_resu = resu_rst[0]._domain for rst in resu_rst[1:]: dom_resu = dom_resu.union(rst._domain) From 5bf10ddda635e5cec09d539938a7b950befb5234 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Fri, 4 Jun 2021 20:18:46 +0200 Subject: [PATCH 110/280] Update docutils to 0.17.1 --- build/pkgs/docutils/checksums.ini | 7 ++++--- build/pkgs/docutils/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/pkgs/docutils/checksums.ini b/build/pkgs/docutils/checksums.ini index 79c8595503c..7eb275b5d93 100644 --- a/build/pkgs/docutils/checksums.ini +++ b/build/pkgs/docutils/checksums.ini @@ -1,4 +1,5 @@ tarball=docutils-VERSION.tar.gz -sha1=32cefb69ac3dab5b04c4d150776f35419cc4c863 -md5=c53768d63db3873b7d452833553469de -cksum=2981697109 +sha1=f423535c12fcd2a68d4fc52525fbe36020a58ab5 +md5=ed810564c25063e9dac10dd0893ead47 +cksum=3160620183 +upstream_url=https://pypi.io/packages/source/d/docutils/docutils-VERSION.tar.gz diff --git a/build/pkgs/docutils/package-version.txt b/build/pkgs/docutils/package-version.txt index 948a5472708..7cca7711a0d 100644 --- a/build/pkgs/docutils/package-version.txt +++ b/build/pkgs/docutils/package-version.txt @@ -1 +1 @@ -0.14 +0.17.1 From bd9a33c55b3d711de607701c6196fb03271c18ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Fri, 4 Jun 2021 23:51:26 +0200 Subject: [PATCH 111/280] 31909: Update distro prereq links in README --- README.md | 49 ++++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index e48e6f1730b..020bfba5a66 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,11 @@ https://www.sagemath.org -The Sage Library is GPLv2+, and included packages have [compatible OSS -licenses](./COPYING.txt). [Over 400 people](https://www.sagemath.org/development-map.html) -have contributed code to Sage. In many cases, documentation for modules -and functions list the authors. +The Sage Library is GPLv2+, and included packages have +[compatible OSS licenses](./COPYING.txt). +[Over 400 people](https://www.sagemath.org/development-map.html) +have contributed code to Sage. In many cases, documentation +for modules and functions list the authors. Getting Started --------------- @@ -22,7 +23,7 @@ If you downloaded a [binary](https://www.sagemath.org/download.html) Sage is ready to start -- just open a terminal in the directory where you extracted the binary archive and type: - ./sage + $ ./sage (Note that the first run will take more time, as Sage needs to get itself ready.) @@ -118,9 +119,9 @@ virtual appliance](https://wiki.sagemath.org/SageAppliance). ------------------------------ Make sure you have installed the most current version of Xcode -supported on your version of macOS. If you don't, go to +supported on your version of macOS. If you don't, either go to https://developer.apple.com/, sign up, and download the free Xcode -package. +package, or get it from Apple's app store. You also need to install the "command line tools": After installing Xcode, run `xcode-select --install` from a terminal window; then click @@ -131,7 +132,7 @@ and then "Install" the Command Line Tools.) Optionally, you can consider installing Homebrew ("the missing package manager for macOS") from https://brew.sh/, which can provide libraries -such gfortran, gmp, etc. +such as gfortran, gmp, etc. Instructions to Build from Source --------------------------------- @@ -173,11 +174,12 @@ Guide](https://doc.sagemath.org/html/en/installation). - [Git] Alternatively, clone the Sage git repository: - $ git clone -c core.symlinks=true --branch master https://github.com/sagemath/sage.git + $ ORIG=https://github.com/sagemath/sage.git + $ git clone -c core.symlinks=true --branch master $ORIG - This will create the subdirectory `sage`. `cd sage/` and pick the branch you need - by doing `git checkout` - typically you want the latest development branch, thus do - `git checkout develop`. + This will create the subdirectory `sage`. `cd sage/` and pick + the branch you need by doing `git checkout` - typically you want + the latest development branch, thus do `git checkout develop`. - [Windows] The Sage source tree contains symbolic links, and the build will not work if Windows line endings rather than UNIX @@ -186,8 +188,8 @@ Guide](https://doc.sagemath.org/html/en/installation). Therefore it is crucial that you unpack the source tree from the Cygwin (or WSL) `bash` using the Cygwin (or WSL) `tar` utility and not using other Windows tools (including mingw). Likewise, - when using `git`, it is recommended (but not necessary) to use the Cygwin (or WSL) - version of `git`. + when using `git`, it is recommended (but not necessary) to use + the Cygwin (or WSL) version of `git`. 3. `cd` into the source/build directory: @@ -224,11 +226,16 @@ Guide](https://doc.sagemath.org/html/en/installation). avoid having to build Sage's own copy of Python 3. We have collected lists of system packages that provide these build - prerequisites. See [build/pkgs/arch.txt](build/pkgs/arch.txt), - [cygwin.txt](build/pkgs/cygwin.txt), - [debian.txt](build/pkgs/debian.txt) (also for Ubuntu, Linux Mint, - etc.), [fedora.txt](build/pkgs/fedora.txt) (also for Red Hat, - CentOS), and [slackware.txt](build/pkgs/slackware.txt). + prerequisites. See, in the folder + [build/pkgs/_prereq/distros](build/pkgs/_prereq/distros), + the files + [arch.txt](build/pkgs/_prereq/distros/arch.txt), + [cygwin.txt](build/pkgs/_prereq/distros/cygwin.txt), + [debian.txt](build/pkgs/_prereq/distros/debian.txt) + (also for Ubuntu, Linux Mint, etc.), + [fedora.txt](build/pkgs/_prereq/distros/fedora.txt) + (also for Red Hat, CentOS), and + [slackware.txt](build/pkgs/_prereq/distros/slackware.txt). 7. Optional, but highly recommended: Make sure your system has an SSL library and its development files installed. @@ -245,7 +252,7 @@ Guide](https://doc.sagemath.org/html/en/installation). 9. Optionally, review the configuration options, which includes many optional packages: - ./configure --help + $ ./configure --help 10. Optional, but highly recommended: Set some environment variables to customize the build. @@ -470,7 +477,7 @@ do. 2. (**Obsolete, probably broken**) To make your own source tarball of Sage, type: - sage --sdist + $ sage --sdist The result is placed in the directory `dist/`. From f6383fa0766b7ed2e7e9bd0650343282bec37307 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 4 Jun 2021 20:53:38 -0700 Subject: [PATCH 112/280] src/sage/functions/min_max.py: Remove py2-ish special casing of None --- src/sage/functions/min_max.py | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/src/sage/functions/min_max.py b/src/sage/functions/min_max.py index 771ec204020..4d9901d03d2 100644 --- a/src/sage/functions/min_max.py +++ b/src/sage/functions/min_max.py @@ -64,23 +64,10 @@ def eval_helper(self, this_f, builtin_f, initial_val, args): symb_args.append(x) else: num_non_symbolic_args += 1 - if res is None: - # Any argument is greater or less than None - res = x - else: - res = builtin_f(res, x) + res = builtin_f(res, x) # if no symbolic arguments, return the result if len(symb_args) == 0: - if res is None: - # this is a hack to get the function to return None to the user - # the convention to leave a given symbolic function unevaluated - # is to return None from the _eval_ function, so we need - # a trick to indicate that the return value of the function is - # really None - # this is caught in the __call__ method, which knows to return - # None in this case - raise ValueError("return None") return res # if all arguments were symbolic return @@ -119,17 +106,6 @@ def __call__(self, *args, **kwds): ... ValueError: number of arguments must be > 0 - Check if we return None, when the builtin function would:: - - sage: max_symbolic([None]) is None # py2 on Python 3 None is not ordered - True - sage: max_symbolic([None, None]) is None # py2 - True - sage: min_symbolic([None]) is None # py2 - True - sage: min_symbolic([None, None]) is None # py2 - True - Check if a single argument which is not iterable works:: sage: max_symbolic(None) @@ -164,9 +140,7 @@ def __call__(self, *args, **kwds): try: return BuiltinFunction.__call__(self, *args, **kwds) except ValueError as e: - if e.args[0] == "return None": - return None - + pass class MaxSymbolic(MinMax_base): def __init__(self): From 86605538360f5505ed99c78e8f9bc857076b6db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 5 Jun 2021 10:42:15 +0200 Subject: [PATCH 113/280] various lgtm fixes about unused variables --- .../orthogonal_arrays_build_recursive.py | 6 +-- src/sage/combinat/ribbon_tableau.py | 11 +++-- src/sage/crypto/mq/sr.py | 10 ++--- src/sage/databases/stein_watkins.py | 3 -- .../quadratic_forms/genera/normal_form.py | 4 +- .../quadratic_form__split_local_covering.py | 43 +++---------------- .../projective/projective_rational_point.py | 8 ++-- 7 files changed, 21 insertions(+), 64 deletions(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py index 64bb91d61ca..443202c7487 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py @@ -1075,12 +1075,10 @@ def product_with_parallel_classes(OA1,k,g1,g2,g1_parall,parall,check=True): # Check our stuff before we return it if check: - profile = [i for i in range(g2*g1) for _ in range(g1)] for classs in new_g1_parallel_classes: - assert_c_partition(classs,k,g2*g1,g1) - profile = list(range(g2*g1)) + assert_c_partition(classs, k, g2 * g1, g1) for classs in new_parallel_classes: - assert_c_partition(classs,k,g2*g1,1) + assert_c_partition(classs, k, g2 * g1, 1) return new_g1_parallel_classes, new_parallel_classes diff --git a/src/sage/combinat/ribbon_tableau.py b/src/sage/combinat/ribbon_tableau.py index 0212acd458f..f164a886729 100644 --- a/src/sage/combinat/ribbon_tableau.py +++ b/src/sage/combinat/ribbon_tableau.py @@ -1110,14 +1110,13 @@ def __iter__(self): parts = self._shape mu = self._weight - #Splitting the partition - s = [ p.size() for p in parts ] + # Splitting the partition + s = [p.size() for p in parts] parts = [p.to_list() for p in parts] - #Gluing the partitions + # Gluing the partitions parttmp = parts[0] - i = 1 - for i in range(1,len(parts)): + for i in range(1, len(parts)): trans = parttmp[0][0] current_part = parts[i] current_part[1] += [0]*(len(current_part[0])-len(current_part[1])) @@ -1125,7 +1124,7 @@ def __iter__(self): outer_current = [ trans + j for j in current_part[0] ] parttmp = [ outer_current + parttmp[0], inner_current + parttmp[1] ] - #List the corresponding skew tableaux + # List the corresponding skew tableaux l = [ st.to_word() for st in SemistandardSkewTableaux(parttmp, mu) ] S = SkewTableaux() diff --git a/src/sage/crypto/mq/sr.py b/src/sage/crypto/mq/sr.py index ab047b5f6cb..0b390e7d3cf 100644 --- a/src/sage/crypto/mq/sr.py +++ b/src/sage/crypto/mq/sr.py @@ -1315,11 +1315,9 @@ def __call__(self, P, K): R[10].output 3925841D02DC09FBDC118597196A0B32 sage: set_verbose(0) """ - r,c,e = self.r,self.c,self.e + r, c, e = self.r, self.c, self.e F = self.base_ring() - _type = self.state_array - if isinstance(P, str): P = self.state_array([F.fetch_int(ZZ(P[i:i+2], 16)) for i in range(0, len(P), 2)]) if isinstance(K, str): @@ -2831,14 +2829,12 @@ def _mul_matrix(self, x): sage: (a^2 + 1)*(a+1) a^3 + a^2 + a + 1 """ - a = self.k.gen() k = self.k e = self.e a = k.gen() - columns = [] - for i in reversed(range(e)): - columns.append( list(reversed((x * a**i)._vector_())) ) + columns = [list(reversed((x * a**i)._vector_())) + for i in reversed(range(e))] return Matrix(GF(2), e, e, columns).transpose() def _square_matrix(self): diff --git a/src/sage/databases/stein_watkins.py b/src/sage/databases/stein_watkins.py index d8fe4c73351..f326925ac95 100644 --- a/src/sage/databases/stein_watkins.py +++ b/src/sage/databases/stein_watkins.py @@ -346,7 +346,6 @@ def ecdb_num_curves(max_level=200000): 0, 8, 0, 6, 11, 4] """ i = 0 - N = 1 d = SteinWatkinsAllData(i) v = [int(0) for _ in range(max_level + 1)] while True: @@ -361,5 +360,3 @@ def ecdb_num_curves(max_level=200000): break v[N] += len(C.curves) return v - - diff --git a/src/sage/quadratic_forms/genera/normal_form.py b/src/sage/quadratic_forms/genera/normal_form.py index 394835cc53e..db46afe0d76 100644 --- a/src/sage/quadratic_forms/genera/normal_form.py +++ b/src/sage/quadratic_forms/genera/normal_form.py @@ -259,14 +259,12 @@ def p_adic_normal_form(G, p, precision=None, partial=False, debug=False): precision = G.det().valuation(p) + 4 R = Zp(p, prec=precision, type='fixed-mod') G = G.change_ring(R) - G.set_immutable() # is not changed during computation - D = copy(G) # is transformed into jordan form + G.set_immutable() # is not changed during computation n = G.ncols() # The trivial case if n == 0: return G.parent().zero(), G.parent().zero() # the transformation matrix is called B - B = Matrix.identity(R, n) if p == 2: D, B = _jordan_2_adic(G) else: diff --git a/src/sage/quadratic_forms/quadratic_form__split_local_covering.py b/src/sage/quadratic_forms/quadratic_form__split_local_covering.py index 92102fcbd1e..764f68c2273 100644 --- a/src/sage/quadratic_forms/quadratic_form__split_local_covering.py +++ b/src/sage/quadratic_forms/quadratic_form__split_local_covering.py @@ -186,11 +186,11 @@ def vectors_by_length(self, bound): ## (So theta_vec[i] will have all vectors v with Q(v) = i.) theta_vec = [[] for i in range(bound + 1)] - ## Initialize Q with zeros and Copy the Cholesky array into Q + # Initialize Q with zeros and Copy the Cholesky array into Q Q = self.cholesky_decomposition() - ## 1. Initialize + # 1. Initialize T = n * [RDF(0)] ## Note: We index the entries as 0 --> n-1 U = n * [RDF(0)] i = n-1 @@ -199,36 +199,21 @@ def vectors_by_length(self, bound): L = n * [0] x = n * [0] - Z = RDF(0) - ## 2. Compute bounds + # 2. Compute bounds Z = (T[i] / Q[i][i]).sqrt(extend=False) L[i] = ( Z - U[i]).floor() x[i] = (-Z - U[i]).ceil() done_flag = False - Q_val_double = RDF(0) Q_val = 0 ## WARNING: Still need a good way of checking overflow for this value... - ## Big loop which runs through all vectors + # Big loop which runs through all vectors while not done_flag: ## 3b. Main loop -- try to generate a complete vector x (when i=0) while (i > 0): - #print " i = ", i - #print " T[i] = ", T[i] - #print " Q[i][i] = ", Q[i][i] - #print " x[i] = ", x[i] - #print " U[i] = ", U[i] - #print " x[i] + U[i] = ", (x[i] + U[i]) - #print " T[i-1] = ", T[i-1] - T[i-1] = T[i] - Q[i][i] * (x[i] + U[i]) * (x[i] + U[i]) - - #print " T[i-1] = ", T[i-1] - #print " x = ", x - #print - i = i - 1 U[i] = 0 for j in range(i+1, n): @@ -247,29 +232,20 @@ def vectors_by_length(self, bound): i += 1 x[i] += 1 - ## 4. Solution found (This happens when i = 0) - #print "-- Solution found! --" - #print " x = ", x - #print " Q_val = Q(x) = ", Q_val + # 4. Solution found (This happens when i = 0) Q_val_double = Theta_Precision - T[0] + Q[0][0] * (x[0] + U[0]) * (x[0] + U[0]) Q_val = Q_val_double.round() - ## SANITY CHECK: Roundoff Error is < 0.001 + # SANITY CHECK: Roundoff Error is < 0.001 if abs(Q_val_double - Q_val) > 0.001: print(" x = ", x) print(" Float = ", Q_val_double, " Long = ", Q_val) raise RuntimeError("The roundoff error is bigger than 0.001, so we should use more precision somewhere...") - #print " Float = ", Q_val_double, " Long = ", Q_val, " XX " - #print " The float value is ", Q_val_double - #print " The associated long value is ", Q_val - if (Q_val <= bound): - #print " Have vector ", x, " with value ", Q_val theta_vec[Q_val].append(deepcopy(x)) - - ## 5. Check if x = 0, for exit condition. =) + # 5. Check if x = 0, for exit condition. =) j = 0 done_flag = True while (j < n): @@ -277,20 +253,15 @@ def vectors_by_length(self, bound): done_flag = False j += 1 - ## 3a. Increment (and carry if we go out of bounds) x[i] += 1 while (x[i] > L[i]) and (i < n-1): i += 1 x[i] += 1 - - #print " Leaving ThetaVectors()" return theta_vec - - def complementary_subform_to_vector(self, v): """ Finds the `(n-1)`-dim'l quadratic form orthogonal to the vector `v`. diff --git a/src/sage/schemes/projective/projective_rational_point.py b/src/sage/schemes/projective/projective_rational_point.py index a75958eae6a..6196eaa1021 100644 --- a/src/sage/schemes/projective/projective_rational_point.py +++ b/src/sage/schemes/projective/projective_rational_point.py @@ -343,7 +343,7 @@ def sieve(X, bound): sage: from sage.schemes.projective.projective_rational_point import sieve sage: P.=ProjectiveSpace(QQ,3) sage: Y=P.subscheme([x^2-3^2*y^2+z*q,x+z+4*q]) - sage: sorted(sieve(Y, 12)) + sage: sorted(sieve(Y, 12)) # long time [(-4 : -4/3 : 0 : 1), (-4 : 4/3 : 0 : 1), (-1 : -1/3 : 1 : 0), (-1 : 1/3 : 1 : 0)] @@ -351,7 +351,7 @@ def sieve(X, bound): sage: from sage.schemes.projective.projective_rational_point import sieve sage: E = EllipticCurve('37a') - sage: sorted(sieve(E, 14)) + sage: sorted(sieve(E, 14)) # long time [(-1 : -1 : 1), (-1 : 0 : 1), (0 : -1 : 1), (0 : 0 : 1), (0 : 1 : 0), (1/4 : -5/8 : 1), (1/4 : -3/8 : 1), (1 : -1 : 1), (1 : 0 : 1), @@ -539,12 +539,11 @@ def lift_all_points(): primes_list = good_primes(B.ceil()) modulo_points = points_modulo_primes(X, primes_list) - len_modulo_points = [len(_) for _ in modulo_points] + len_modulo_points = [len(pt) for pt in modulo_points] len_primes = len(primes_list) prod_primes = prod(primes_list) # stores final result - rat_points = set() for i in range(N + 1): w = [0 for _ in range(N + 1)] @@ -554,4 +553,3 @@ def lift_all_points(): rat_points = lift_all_points() return sorted(rat_points) - From aea4554d90de3a3cb39c0e3704edc0807d8b4109 Mon Sep 17 00:00:00 2001 From: Eric Gourgoulhon Date: Sat, 5 Jun 2021 19:11:51 +0200 Subject: [PATCH 114/280] #31904: Fix indentation in _pullback_chart --- src/sage/manifolds/differentiable/diff_map.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/manifolds/differentiable/diff_map.py b/src/sage/manifolds/differentiable/diff_map.py index bc772ca1f13..ad9a2a94f95 100644 --- a/src/sage/manifolds/differentiable/diff_map.py +++ b/src/sage/manifolds/differentiable/diff_map.py @@ -935,7 +935,6 @@ def pullback(self, tensor): """ from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal - from sage.manifolds.differentiable.vectorframe import CoordFrame from sage.tensor.modules.comp import (Components, CompWithSym, CompFullySym, CompFullyAntiSym) @@ -1029,7 +1028,7 @@ def paral_comp(tcomp, chart1, chart2, coord2_1, jacob, t *= jacob[ind_old[i]-si2, ind_new[i]-si1] res += t partial.append([ind_new, res]) - return partial + return partial for ii, val in paral_comp(listParalInput): for jj in val: From 6489ba720aca1c9ab7b130eb549b910943e6206d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Sat, 5 Jun 2021 19:44:35 +0200 Subject: [PATCH 115/280] 31915: Add xz dependency for symmetrica --- build/pkgs/symmetrica/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/symmetrica/dependencies b/build/pkgs/symmetrica/dependencies index 7a7b9cf8a80..0fef19aa992 100644 --- a/build/pkgs/symmetrica/dependencies +++ b/build/pkgs/symmetrica/dependencies @@ -1,4 +1,4 @@ -| xz +xz xz is needed for unpacking the tarball when sage-bootstrap-python is ancient From 5ae501f6773263ff67a1f290b7fd32f214f5659d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 5 Jun 2021 20:08:08 +0200 Subject: [PATCH 116/280] remove unused import --- src/sage/combinat/designs/orthogonal_arrays_build_recursive.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py index 443202c7487..7a63bac3c11 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py @@ -783,7 +783,6 @@ def thwart_lemma_4_1(k,n,m,explain_construction=False): T. G. Ostrom and F. A. Sherk. Canad. Math. Bull vol7 num.4 (1964) """ - from sage.combinat.designs.designs_pyx import is_orthogonal_array from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.arith.all import is_prime_power from .block_design import DesarguesianProjectivePlaneDesign From 695dd5e892b80814540fcec9e33a8dda52d5db38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Sat, 5 Jun 2021 20:29:13 +0200 Subject: [PATCH 117/280] 31910: Improve code style compliance --- src/sage/functions/min_max.py | 67 ++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/src/sage/functions/min_max.py b/src/sage/functions/min_max.py index 4d9901d03d2..f38c991d405 100644 --- a/src/sage/functions/min_max.py +++ b/src/sage/functions/min_max.py @@ -7,22 +7,22 @@ Here you can see some differences:: - sage: max(x,x^2) + sage: max(x, x^2) x - sage: max_symbolic(x,x^2) + sage: max_symbolic(x, x^2) max(x, x^2) - sage: f(x) = max_symbolic(x,x^2); f(1/2) + sage: f(x) = max_symbolic(x, x^2); f(1/2) 1/2 This works as expected for more than two entries:: - sage: max(3,5,x) + sage: max(3, 5, x) 5 - sage: min(3,5,x) + sage: min(3, 5, x) 3 - sage: max_symbolic(3,5,x) + sage: max_symbolic(3, 5, x) max(x, 5) - sage: min_symbolic(3,5,x) + sage: min_symbolic(3, 5, x) min(x, 3) """ @@ -45,9 +45,9 @@ def eval_helper(self, this_f, builtin_f, initial_val, args): """ EXAMPLES:: - sage: max_symbolic(3,5,x) # indirect doctest + sage: max_symbolic(3, 5, x) # indirect doctest max(x, 5) - sage: min_symbolic(3,5,x) + sage: min_symbolic(3, 5, x) min(x, 3) """ # __call__ ensures that if args is a singleton, the element is iterable @@ -74,27 +74,28 @@ def eval_helper(self, this_f, builtin_f, initial_val, args): if num_non_symbolic_args <= 1 and not arg_is_iter: return None - if res is not None: symb_args.append(res) + if res is not None: + symb_args.append(res) return this_f(*symb_args) def __call__(self, *args, **kwds): """ EXAMPLES:: - sage: max_symbolic(3,5,x) + sage: max_symbolic(3, 5, x) max(x, 5) - sage: max_symbolic(3,5,x, hold=True) + sage: max_symbolic(3, 5, x, hold=True) max(3, 5, x) - sage: max_symbolic([3,5,x]) + sage: max_symbolic([3, 5, x]) max(x, 5) :: - sage: min_symbolic(3,5,x) + sage: min_symbolic(3, 5, x) min(x, 3) - sage: min_symbolic(3,5,x, hold=True) + sage: min_symbolic(3, 5, x, hold=True) min(3, 5, x) - sage: min_symbolic([3,5,x]) + sage: min_symbolic([3, 5, x]) min(x, 3) TESTS: @@ -134,12 +135,12 @@ def __call__(self, *args, **kwds): if len(args) == 1: try: args = (SR._force_pyobject(iter(args[0])),) - except TypeError as e: - raise e + except TypeError: + raise try: return BuiltinFunction.__call__(self, *args, **kwds) - except ValueError as e: + except ValueError: pass class MaxSymbolic(MinMax_base): @@ -159,14 +160,14 @@ def __init__(self): 5 sage: max_symbolic(3, 5, x) max(x, 5) - sage: max_symbolic([3,5,x]) + sage: max_symbolic([3, 5, x]) max(x, 5) TESTS:: - sage: loads(dumps(max_symbolic(x,5))) + sage: loads(dumps(max_symbolic(x, 5))) max(x, 5) - sage: latex(max_symbolic(x,5)) + sage: latex(max_symbolic(x, 5)) \max\left(x, 5\right) sage: max_symbolic(x, 5)._sympy_() Max(5, x) @@ -180,17 +181,17 @@ def _eval_(self, *args): sage: t = max_symbolic(x, 5); t max(x, 5) - sage: t.subs(x=3) # indirect doctest + sage: t.subs(x=3) # indirect doctest 5 - sage: max_symbolic(5,3) + sage: max_symbolic(5, 3) 5 - sage: u = max_symbolic(*(list(range(10))+[x])); u + sage: u = max_symbolic(*(list(range(10)) + [x])); u max(x, 9) sage: u.subs(x=-1) 9 sage: u.subs(x=10) 10 - sage: max_symbolic([0,x]) + sage: max_symbolic([0, x]) max(x, 0) TESTS:: @@ -250,14 +251,14 @@ def __init__(self): 3 sage: min_symbolic(3, 5, x) min(x, 3) - sage: min_symbolic([3,5,x]) + sage: min_symbolic([3, 5, x]) min(x, 3) TESTS:: - sage: loads(dumps(min_symbolic(x,5))) + sage: loads(dumps(min_symbolic(x, 5))) min(x, 5) - sage: latex(min_symbolic(x,5)) + sage: latex(min_symbolic(x, 5)) \min\left(x, 5\right) sage: min_symbolic(x, 5)._sympy_() Min(5, x) @@ -271,17 +272,17 @@ def _eval_(self, *args): sage: t = min_symbolic(x, 5); t min(x, 5) - sage: t.subs(x=3) # indirect doctest + sage: t.subs(x=3) # indirect doctest 3 - sage: min_symbolic(5,3) + sage: min_symbolic(5, 3) 3 - sage: u = min_symbolic(*(list(range(10))+[x])); u + sage: u = min_symbolic(*(list(range(10)) + [x])); u min(x, 0) sage: u.subs(x=-1) -1 sage: u.subs(x=10) 0 - sage: min_symbolic([3,x]) + sage: min_symbolic([3, x]) min(x, 3) TESTS:: From 3459eccd99670a176eb8df0f201ec6016dc6122b Mon Sep 17 00:00:00 2001 From: Kiran Kedlaya Date: Sat, 5 Jun 2021 15:29:01 -0700 Subject: [PATCH 118/280] Add function is_easy_sn_an --- .../polynomial/polynomial_rational_flint.pyx | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index 2dfe570bd12..f9724ea41d3 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -2513,4 +2513,42 @@ cdef class Polynomial_rational_flint(Polynomial): # Alias for discriminant disc = discriminant + def is_easy_sn_an(self, num_trials=50, assume_irreducible=False): + """ + Use the Davenport-Smith test to attempt to certify that `f` has Galois group A_n or S_n. + + Return 1 if the Galois group is certified as S_n, 2 if A_n, or 0 if no conclusion is reached. + + By default, we first check that `f` is irreducible. For extra efficiency, one can override this + by specifying `assume_irreducible=True`; this yields undefined results if `f` is not irreducible. + + EXAMPLES:: + + sage: P. = QQ[] + sage: u = x^7 + x + 1 + sage: u.is_easy_sn_an() + 1 + sage: u = x^7 - x^4 - x^3 + 3*x^2 - 1 + sage: u.is_easy_sn_an() + 2 + sage: u = x^7 - 2 + sage: u.is_easy_sn_an() + 0 + + """ + from sage.arith.misc import primes_first_n + from sage.rings.finite_rings.integer_mod_ring import IntegerModRing + if not assume_irreducible and not self.is_irreducible(): + return 0 + d = self.degree() + for p in primes_first_n(num_trials): + fp = self.change_ring(IntegerModRing(p)) + g = fp.factor()[-1][0] + d1 = g.degree() + # Here we use the fact that a transitive permutation representation with a long prime cycle + # must have image at least as big as A_n. + if (d1 <= 7 and (d,d1) in ((1,1),(2,2),(3,2),(3,3),(4,3),(5,3),(5,4),(6,5),(7,5))) or\ + (d1 > d/2 and d1 < d-2 and d1.is_prime()): + return (2 if self.disc().is_square() else 1) + return 0 From e9c670c00a492f8f50f3e21e55895a0d3c1d55f2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 5 Jun 2021 19:05:53 -0700 Subject: [PATCH 119/280] sage.geometry.polyhedron.relint, Polyhedron_base.relative_interior, Polyhedron_base.interior: New --- src/sage/geometry/polyhedron/base.py | 54 ++++++++++++++++++++++++++ src/sage/geometry/polyhedron/relint.py | 36 +++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/sage/geometry/polyhedron/relint.py diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 23ffed867da..8e36e1dda63 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -53,6 +53,7 @@ from sage.graphs.graph import Graph from .constructor import Polyhedron +from .relint import RelativeInterior from sage.categories.sets_cat import EmptySetError ######################################################################### @@ -8394,6 +8395,38 @@ def contains(self, point): __contains__ = contains + @cached_method + def interior(self): + """ + The interior of ``self``. + + OUTPUT: + + - either an empty polyhedron or an instance of + :class:`~sage.geometry.polyhedra.relint.RelativeInterior` + + EXAMPLES: + + If the polyhedron is full-dimensional, the result is the + same as that of :meth:`relative_interior`:: + + sage: P_full = Polyhedron(vertices=[[0,0],[1,1],[1,-1]]) + sage: P_full.interior() + Relative interior of + a 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices + + If the polyhedron is of strictly smaller dimension than the + ambient space, its interior is empty:: + + sage: P_lower = Polyhedron(vertices=[[0,1], [0,-1]]) + sage: P_lower.interior() + The empty polyhedron in ZZ^2 + + """ + if not self.is_full_dimensional(): + return self.parent().element_class(self.parent(), None, None) + return self.relative_interior() + def interior_contains(self, point): """ Test whether the interior of the polyhedron contains the @@ -8451,6 +8484,27 @@ def interior_contains(self, point): return False return True + @cached_method + def relative_interior(self): + """ + Return the relative interior of ``self``. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(1,0), (-1,0)]) + sage: ri_P = P.relative_interior(); ri_P + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: (0, 0) in ri_P + True + sage: (1, 0) in ri_P + False + + """ + if self.is_empty() or self.is_universe(): + return self + return RelativeInterior(self) + def relative_interior_contains(self, point): """ Test whether the relative interior of the polyhedron diff --git a/src/sage/geometry/polyhedron/relint.py b/src/sage/geometry/polyhedron/relint.py new file mode 100644 index 00000000000..6075ba970ac --- /dev/null +++ b/src/sage/geometry/polyhedron/relint.py @@ -0,0 +1,36 @@ +r""" +Relative interiors of polyhedra +""" + +# **************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.structure.sage_object import SageObject + +class RelativeInterior(SageObject): + + """ + The relative interior of a polyhedron + """ + + def __init__(self, polyhedron): + self._polyhedron = polyhedron + + def __contains__(self, point): + return self._polyhedron.relative_interior_contains(point) + + def closure(self): + return self._polyhedron + + def _repr_(self): + repr_P = repr(self._polyhedron) + if repr_P.startswith('A '): + repr_P = 'a ' + repr_P[2:] + return 'Relative interior of ' + repr_P From 68796e16d1ce83ff7551c0729a74244050bff084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Sun, 6 Jun 2021 12:45:54 +0200 Subject: [PATCH 120/280] 31917: fix typo prescripted -> prescribed --- src/sage/categories/pushout.py | 2 +- src/sage/rings/ring.pyx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 7120901bfd2..7a15885b609 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -3055,7 +3055,7 @@ def __init__(self, polys, names, embeddings=None, structures=None, sage: F2(GF(5)) Traceback (most recent call last): ... - NotImplementedError: ring extension with prescripted embedding is not implemented + NotImplementedError: ring extension with prescribed embedding is not implemented When applying a number field constructor to the ring of integers, an order (not necessarily maximal) of that field is diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 0408a5bcabe..5de398d0895 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -1515,9 +1515,9 @@ cdef class CommutativeRing(Ring): name = str(poly.parent().gen(0)) for key, val in kwds.items(): if key not in ['structure', 'implementation', 'prec', 'embedding', 'latex_name', 'latex_names']: - raise TypeError("extension() got an unexpected keyword argument '%s'"%key) + raise TypeError("extension() got an unexpected keyword argument '%s'" % key) if not (val is None or isinstance(val, list) and all(c is None for c in val)): - raise NotImplementedError("ring extension with prescripted %s is not implemented"%key) + raise NotImplementedError("ring extension with prescribed %s is not implemented" % key) R = self[name] I = R.ideal(R(poly.list())) return R.quotient(I, name) From ee487beff2037c2f235ce514a95b7389066bdc8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 6 Jun 2021 14:20:36 +0200 Subject: [PATCH 121/280] various details about combinat (range and isinstance) --- src/sage/combinat/composition.py | 10 +++++----- src/sage/combinat/crystals/alcove_path.py | 5 ++--- src/sage/combinat/designs/steiner_quadruple_systems.py | 4 ++-- src/sage/combinat/integer_list_old.py | 2 +- src/sage/combinat/k_tableau.py | 2 +- src/sage/combinat/matrices/latin.py | 10 +++++----- src/sage/combinat/ncsf_qsym/qsym.py | 4 ++-- src/sage/combinat/partition_tuple.py | 2 +- src/sage/combinat/root_system/ambient_space.py | 4 ++-- src/sage/combinat/root_system/branching_rules.py | 2 +- src/sage/combinat/similarity_class_type.py | 2 +- src/sage/combinat/tableau_tuple.py | 7 +++---- src/sage/combinat/words/finite_word.py | 4 ++-- 13 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index 05833955854..cfdce06dabc 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -251,7 +251,7 @@ def conjugate(self): cocjg = [] for i in range(n-1): - cocjg += [i+1 for _ in range(0, (coofcp[n-i-1]-coofcp[n-i-2]))] + cocjg += [i + 1 for _ in range(coofcp[n-i-1]-coofcp[n-i-2])] cocjg += [n for j in range(coofcp[0])] return self.parent()([cocjg[0]] + [cocjg[i]-cocjg[i-1]+1 for i in range(1,len(cocjg))]) @@ -854,11 +854,11 @@ def fatten(self, grouping): sage: c.fatten(Composition([3,1,1])).__class__ == c.__class__ True """ - result = [None] * len(grouping) + result = [] j = 0 - for i in range(len(grouping)): - result[i] = sum(self[j:j+grouping[i]]) - j += grouping[i] + for i, gi in enumerate(grouping): + result.append(sum(self[j:j + gi])) + j += gi return Compositions()(result) def fatter(self): diff --git a/src/sage/combinat/crystals/alcove_path.py b/src/sage/combinat/crystals/alcove_path.py index 1a9c5f0a127..6ef9d20720d 100644 --- a/src/sage/combinat/crystals/alcove_path.py +++ b/src/sage/combinat/crystals/alcove_path.py @@ -250,9 +250,8 @@ def __classcall_private__(cls, starting_weight, cartan_type=None, """ if isinstance(cartan_type, bool): # new style signature, optional arguments leak over highest_weight_crystal = cartan_type - - elif isinstance(cartan_type, list) or isinstance(cartan_type, tuple): #old style signature - #switch positional arguments + elif isinstance(cartan_type, (list, tuple)): # old style signature + # switch positional arguments cartan_type, starting_weight = CartanType(starting_weight), cartan_type if highest_weight_crystal is False: diff --git a/src/sage/combinat/designs/steiner_quadruple_systems.py b/src/sage/combinat/designs/steiner_quadruple_systems.py index 35834d93e50..5f49cdd3daf 100644 --- a/src/sage/combinat/designs/steiner_quadruple_systems.py +++ b/src/sage/combinat/designs/steiner_quadruple_systems.py @@ -637,7 +637,7 @@ def barP_system(m): # pairs. Those are added to 'last', a new list of pairs last = [] - for n in range(0, (m-3)//2+1): + for n in range((m - 3) // 2 + 1): pairs.append([p for p in P(2*n,m) if not isequal(p,(2*n,(4*n+1)%(2*m)))]) last.append((2*n,(4*n+1)%(2*m))) pairs.append([p for p in P(2*n+1,m) if not isequal(p,(2*m-2-2*n,2*m-3-4*n))]) @@ -659,7 +659,7 @@ def barP_system(m): # Now the points must be relabeled relabel = {} - for n in range(0, (m-3)//2+1): + for n in range((m - 3) // 2 + 1): relabel[2*n] = (4*n)%(2*m) relabel[4*n+1] = (4*n+1)%(2*m) relabel[2*m-2-2*n] = (4*n+2)%(2*m) diff --git a/src/sage/combinat/integer_list_old.py b/src/sage/combinat/integer_list_old.py index 99c6dcfa3e7..cdd420bb79e 100644 --- a/src/sage/combinat/integer_list_old.py +++ b/src/sage/combinat/integer_list_old.py @@ -1209,6 +1209,6 @@ def __contains__(self, v): sage: all(v in C for v in C) True """ - if isinstance(v, self.element_class) or isinstance(v, list): + if isinstance(v, (self.element_class, list)): return is_a(v, *(self.build_args())) and sum(v) in self.n_range return False diff --git a/src/sage/combinat/k_tableau.py b/src/sage/combinat/k_tableau.py index 490f212d415..005f3504456 100644 --- a/src/sage/combinat/k_tableau.py +++ b/src/sage/combinat/k_tableau.py @@ -1818,7 +1818,7 @@ def straighten_input(t, k): """ W = WeylGroup(['A', k, 1], prefix='s') if len(t) > 0: - if isinstance(t[0], list) or isinstance(t[0], tuple): + if isinstance(t[0], (list, tuple)): w_tuple = tuple(W.from_reduced_word(p) for p in t) else: w_tuple = tuple(W(r) for r in t) diff --git a/src/sage/combinat/matrices/latin.py b/src/sage/combinat/matrices/latin.py index 4183a520b26..765b2065cae 100644 --- a/src/sage/combinat/matrices/latin.py +++ b/src/sage/combinat/matrices/latin.py @@ -177,11 +177,11 @@ def __init__(self, *args): [0 1] [2 3] """ - - if len(args) == 1 and (isinstance(args[0], Integer) or isinstance(args[0], int)): + if len(args) == 1 and isinstance(args[0], (Integer, int)): self.square = matrix(ZZ, args[0], args[0]) self.clear_cells() - elif len(args) == 2 and (isinstance(args[0], Integer) or isinstance(args[0], int)) and (isinstance(args[1], Integer) or isinstance(args[1], int)): + elif len(args) == 2 and all(isinstance(a, (Integer, int)) + for a in args): self.square = matrix(ZZ, args[0], args[1]) self.clear_cells() elif len(args) == 1 and isinstance(args[0], Matrix_integer_dense): @@ -1513,8 +1513,8 @@ def isotopism(p): """ # Identity isotopism on p points: - if isinstance(p, Integer) or isinstance(p, int): - return Permutation(range(1, p+1)) + if isinstance(p, (Integer, int)): + return Permutation(range(1, p + 1)) if isinstance(p, PermutationGroupElement): # fixme Ask the Sage mailing list about the tuple/list issue! diff --git a/src/sage/combinat/ncsf_qsym/qsym.py b/src/sage/combinat/ncsf_qsym/qsym.py index 44aae642397..af6efe920f4 100644 --- a/src/sage/combinat/ncsf_qsym/qsym.py +++ b/src/sage/combinat/ncsf_qsym/qsym.py @@ -1806,7 +1806,7 @@ def coproduct_on_basis(self, compo): """ return self.tensor_square().sum_of_monomials((self._indices(compo[:i]), self._indices(compo[i:])) - for i in range(0,len(compo)+1)) + for i in range(len(compo)+1)) def lambda_of_monomial(self, I, n): r""" @@ -2758,7 +2758,7 @@ def coproduct_on_basis(self, compo): """ return self.tensor_square().sum_of_monomials((self._indices(compo[:i]), self._indices(compo[i:])) - for i in range(0,len(compo)+1)) + for i in range(len(compo)+1)) def product_on_basis(self, I, J): r""" diff --git a/src/sage/combinat/partition_tuple.py b/src/sage/combinat/partition_tuple.py index 54d5784eb3e..34b5a9e46ff 100644 --- a/src/sage/combinat/partition_tuple.py +++ b/src/sage/combinat/partition_tuple.py @@ -1971,7 +1971,7 @@ def __contains__(self, mu): sage: 1 in PartitionTuples() False """ - if isinstance(mu, PartitionTuple) or isinstance(mu, Partition): + if isinstance(mu, (PartitionTuple, Partition)): return True if isinstance(mu, (tuple, list)): if not mu: diff --git a/src/sage/combinat/root_system/ambient_space.py b/src/sage/combinat/root_system/ambient_space.py index 67088b3197b..e1d4adea0ea 100644 --- a/src/sage/combinat/root_system/ambient_space.py +++ b/src/sage/combinat/root_system/ambient_space.py @@ -85,11 +85,11 @@ def __init__(self, root_system, base_ring, index_set=None): """ self.root_system = root_system if index_set is None: - index_set = tuple(range(0, self.dimension())) + index_set = tuple(range(self.dimension())) CombinatorialFreeModule.__init__(self, base_ring, index_set, prefix='e', - category = WeightLatticeRealizations(base_ring)) + category=WeightLatticeRealizations(base_ring)) coroot_lattice = self.root_system.coroot_lattice() coroot_lattice.module_morphism(self.simple_coroot, codomain=self).register_as_coercion() diff --git a/src/sage/combinat/root_system/branching_rules.py b/src/sage/combinat/root_system/branching_rules.py index 1a2d6cbd8b0..ded9b53460b 100644 --- a/src/sage/combinat/root_system/branching_rules.py +++ b/src/sage/combinat/root_system/branching_rules.py @@ -979,7 +979,7 @@ def rule(x): sage: A3(0,1,0).branch(C2,rule=br) C2(0,0) + C2(0,1) """ - if isinstance(rule, str) or isinstance(rule, list): + if isinstance(rule, (str, list)): rule = branching_rule(R._cartan_type, S._cartan_type, rule) if hasattr(rule, "_S"): if rule._S != S.cartan_type(): diff --git a/src/sage/combinat/similarity_class_type.py b/src/sage/combinat/similarity_class_type.py index 857c52c4fe0..e500595082f 100644 --- a/src/sage/combinat/similarity_class_type.py +++ b/src/sage/combinat/similarity_class_type.py @@ -308,7 +308,7 @@ def centralizer_algebra_dim(la): If it is a list, ``la`` is expected to be sorted in decreasing order. """ - return sum([(2*i + 1)*la[i] for i in range(0, len(la))]) + return sum([(2 * i + 1) * la[i] for i in range(len(la))]) @cached_function diff --git a/src/sage/combinat/tableau_tuple.py b/src/sage/combinat/tableau_tuple.py index f2d5b91a660..9e8e689ac0f 100644 --- a/src/sage/combinat/tableau_tuple.py +++ b/src/sage/combinat/tableau_tuple.py @@ -1080,16 +1080,15 @@ def row_stabilizer(self): sage: rs.one().domain() [1, 2, 3, 4, 5, 6, 7, 8, 9] """ - # Ensure that the permutations involve all elements of the # tableau, by including the identity permutation on the set [1..n]. n = max(self.entries()) gens = [list(range(1, n + 1))] for t in self: for i in range(len(t)): - for j in range(0, len(t[i])-1): - gens.append( (t[i][j], t[i][j+1]) ) - return PermutationGroup( gens ) + for j in range(len(t[i]) - 1): + gens.append((t[i][j], t[i][j + 1])) + return PermutationGroup(gens) def column_stabilizer(self): """ diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py index aec56d7fbd4..b546105c7c7 100644 --- a/src/sage/combinat/words/finite_word.py +++ b/src/sage/combinat/words/finite_word.py @@ -7047,8 +7047,8 @@ def is_cube_free(self): L = self.length() if L < 3: return True - for start in range(0, L - 2): - for end in range(start+3, L+1, 3): + for start in range(L - 2): + for end in range(start + 3, L + 1, 3): if self[start:end].is_cube(): return False return True From 2967d9f99397c6e2fa213dc68ff440d874e8f284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 6 Jun 2021 14:26:50 +0200 Subject: [PATCH 122/280] one detail --- src/sage/combinat/composition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index cfdce06dabc..a1b2f97d396 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -856,7 +856,7 @@ def fatten(self, grouping): """ result = [] j = 0 - for i, gi in enumerate(grouping): + for gi in grouping: result.append(sum(self[j:j + gi])) j += gi return Compositions()(result) From e53845eb6e396c68cb51118ab3093712affef1b3 Mon Sep 17 00:00:00 2001 From: Marius Gerbershagen Date: Sun, 6 Jun 2021 20:01:43 +0200 Subject: [PATCH 123/280] Don't pollute the global namespace in sagelib code --- src/sage/coding/grs_code.py | 4 ++-- src/sage/combinat/multiset_partition_into_sets_ordered.py | 4 ++-- src/sage/combinat/sine_gordon.py | 4 ++-- src/sage/rings/number_field/S_unit_solver.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/coding/grs_code.py b/src/sage/coding/grs_code.py index 9bc6248c4a9..803cdb0943d 100644 --- a/src/sage/coding/grs_code.py +++ b/src/sage/coding/grs_code.py @@ -65,7 +65,7 @@ from copy import copy from sage.functions.other import binomial, floor -from sage.calculus.var import var +from sage.symbolic.ring import SR from .linear_code import AbstractLinearCode from .encoder import Encoder @@ -534,7 +534,7 @@ def weight_distribution(self): d = self.minimum_distance() n = self.length() q = self.base_ring().order() - s = var('s') + s = SR.var('s') wd = [1] + [0] * (d - 1) for i in range(d, n+1): tmp = binomial(n, i) * (q - 1) diff --git a/src/sage/combinat/multiset_partition_into_sets_ordered.py b/src/sage/combinat/multiset_partition_into_sets_ordered.py index 42aeb20da66..1d6eb8b37f6 100755 --- a/src/sage/combinat/multiset_partition_into_sets_ordered.py +++ b/src/sage/combinat/multiset_partition_into_sets_ordered.py @@ -81,7 +81,7 @@ from sage.rings.infinity import infinity from sage.rings.integer_ring import ZZ from sage.functions.other import binomial -from sage.calculus.var import var +from sage.symbolic.ring import SR from sage.combinat.subset import Subsets_sk from sage.combinat.composition import Composition, Compositions, composition_iterator_fast @@ -2067,7 +2067,7 @@ def cardinality(self): # # The 2-regular partitions have a nice generating function (see OEIS:A000009). # Below, we take (products of) coefficients of polynomials to compute cardinality. - t = var('t') + t = SR.var('t') partspoly = prod(1+t**k for k in range(1,self._n+1)).coefficients() deg = 0 for alpha in composition_iterator_fast(self._n): diff --git a/src/sage/combinat/sine_gordon.py b/src/sage/combinat/sine_gordon.py index 309c07fcd23..a3c63e7b2d8 100644 --- a/src/sage/combinat/sine_gordon.py +++ b/src/sage/combinat/sine_gordon.py @@ -58,7 +58,7 @@ from sage.functions.log import exp from sage.functions.other import ceil from sage.misc.flatten import flatten -from sage.calculus.var import var +from sage.symbolic.ring import SR from sage.functions.other import real_part, imag_part from sage.misc.cachefunc import cached_method @@ -516,7 +516,7 @@ def plot_arc(radius, p, q, **opts): # plot the arc from p to q differently depending on the type of self p = ZZ(p) q = ZZ(q) - t = var('t') + t = SR.var('t') if p - q in [1, -1]: def f(t): return (radius * cos(t), radius * sin(t)) diff --git a/src/sage/rings/number_field/S_unit_solver.py b/src/sage/rings/number_field/S_unit_solver.py index 5c0a33eb74f..e19a9b5580d 100644 --- a/src/sage/rings/number_field/S_unit_solver.py +++ b/src/sage/rings/number_field/S_unit_solver.py @@ -55,7 +55,7 @@ from sage.rings.all import Infinity -from sage.calculus.var import var +from sage.symbolic.ring import SR from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.rings.real_mpfr import RealField, RR @@ -675,7 +675,7 @@ def Yu_bound(SUK, v, prec=106): else: # K and v don't satisfy the theorem hypotheses, and we must move to a quadratic extension L. # For justification of this next bound, see [AKMRVW]. - x = var('x') + x = SR.var('x') if p == 2: L_over_K = K.extension(x**2 + x + 1, 'xi0') else: From 9df21042f5cb76869b14d54c501bda4c7ef2cbc1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 11:26:07 -0700 Subject: [PATCH 124/280] sage.geometry.relative_interior: Move here from sage.geometry.polyhedron.relint --- src/sage/geometry/polyhedron/base.py | 4 ++-- .../relint.py => relative_interior.py} | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) rename src/sage/geometry/{polyhedron/relint.py => relative_interior.py} (65%) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 8e36e1dda63..1175d21925f 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -53,7 +53,7 @@ from sage.graphs.graph import Graph from .constructor import Polyhedron -from .relint import RelativeInterior +from sage.geometry.relative_interior import RelativeInterior from sage.categories.sets_cat import EmptySetError ######################################################################### @@ -8403,7 +8403,7 @@ def interior(self): OUTPUT: - either an empty polyhedron or an instance of - :class:`~sage.geometry.polyhedra.relint.RelativeInterior` + :class:`~sage.geometry.relative_interior.RelativeInterior` EXAMPLES: diff --git a/src/sage/geometry/polyhedron/relint.py b/src/sage/geometry/relative_interior.py similarity index 65% rename from src/sage/geometry/polyhedron/relint.py rename to src/sage/geometry/relative_interior.py index 6075ba970ac..3c20f90d1dd 100644 --- a/src/sage/geometry/polyhedron/relint.py +++ b/src/sage/geometry/relative_interior.py @@ -1,5 +1,5 @@ r""" -Relative interiors of polyhedra +Relative Interiors of Polyhedra and Cones """ # **************************************************************************** @@ -17,7 +17,7 @@ class RelativeInterior(SageObject): """ - The relative interior of a polyhedron + The relative interior of a polyhedron or cone """ def __init__(self, polyhedron): @@ -30,6 +30,18 @@ def closure(self): return self._polyhedron def _repr_(self): + """ + Return a description of ``self``. + + EXAMPLES:: + + sage: P = Polyhedron(vertices = [[1,2,3,4],[2,1,3,4],[4,3,2,1]]) + sage: P.relative_interior()._repr_() + 'Relative interior of a 2-dimensional polyhedron in ZZ^4 defined as the convex hull of 3 vertices' + sage: P.rename('A') + sage: P.relative_interior()._repr_() + 'Relative interior of A' + """ repr_P = repr(self._polyhedron) if repr_P.startswith('A '): repr_P = 'a ' + repr_P[2:] From b8bfe200f6e698dec855394af9adaaa63c282abd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 11:26:57 -0700 Subject: [PATCH 125/280] ConvexRationalPolyhedralCone: Add methods interior, relative_interior --- src/sage/geometry/cone.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 79c75ad7841..e7813040521 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -205,6 +205,7 @@ from sage.geometry.toric_lattice import (ToricLattice, is_ToricLattice, is_ToricLatticeQuotient) from sage.geometry.toric_plotter import ToricPlotter, label_list +from sage.geometry.relative_interior import RelativeInterior from sage.graphs.digraph import DiGraph from sage.matrix.all import column_matrix, matrix, MatrixSpace from sage.misc.all import cached_method, flatten, latex @@ -1711,6 +1712,14 @@ def interior_contains(self, *args): point = point[0] return self._contains(point, 'interior') + def interior(self): + r""" + Return the interior of ``self``. + """ + if self.is_solid(): + return self.relative_interior() + return Polyhedron(ambient_dim=self.lattice_dim()) + def relative_interior_contains(self, *args): r""" Check if a given point is contained in the relative interior of ``self``. @@ -1752,6 +1761,14 @@ def relative_interior_contains(self, *args): point = point[0] return self._contains(point, 'relative interior') + def relative_interior(self): + r""" + Return the relative interior of ``self``. + """ + if self.is_full_space(): + return self + return RelativeInterior(self) + def cartesian_product(self, other, lattice=None): r""" Return the Cartesian product of ``self`` with ``other``. From 6869673199fb80af0a09caf039873e27d50d2741 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 11:45:02 -0700 Subject: [PATCH 126/280] relative_interior: Fix for dimension 0 --- src/sage/geometry/cone.py | 4 +++- src/sage/geometry/polyhedron/base.py | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index e7813040521..a4e11c5a9d3 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1712,6 +1712,7 @@ def interior_contains(self, *args): point = point[0] return self._contains(point, 'interior') + @cached_method def interior(self): r""" Return the interior of ``self``. @@ -1761,11 +1762,12 @@ def relative_interior_contains(self, *args): point = point[0] return self._contains(point, 'relative interior') + @cached_method def relative_interior(self): r""" Return the relative interior of ``self``. """ - if self.is_full_space(): + if self.is_trivial() or self.is_full_space(): return self return RelativeInterior(self) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 1175d21925f..985a9f5c6c5 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8500,8 +8500,15 @@ def relative_interior(self): sage: (1, 0) in ri_P False + sage: P0 = Polyhedron(vertices=[[1, 2]]) + sage: P0.relative_interior() is P0 + True + + sage: Empty = Polyhedron(ambient_dim=2) + sage: Empty.relative_interior() is Empty + True """ - if self.is_empty() or self.is_universe(): + if self.dim() <= 0 or self.is_universe(): return self return RelativeInterior(self) From 021d073e758d37f0abdb2466e3dc59e3383ed05b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 12:40:46 -0700 Subject: [PATCH 127/280] RelativeInterior: Add documentation, tests, comparison methods, method relative_interior --- src/sage/geometry/cone.py | 15 ++- src/sage/geometry/relative_interior.py | 127 ++++++++++++++++++++++++- 2 files changed, 137 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index a4e11c5a9d3..29007febb11 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -181,9 +181,18 @@ """ # **************************************************************************** -# Copyright (C) 2010 Volker Braun -# Copyright (C) 2012 Andrey Novoseltsev -# Copyright (C) 2010 William Stein +# Copyright (C) 2010-2014 Volker Braun +# Copyright (C) 2010-2018 Andrey Novoseltsev +# Copyright (C) 2010 William Stein +# Copyright (C) 2012 Christian Stump +# Copyright (C) 2014-2018 Frédéric Chapoton +# Copyright (C) 2014 Peter Bruin +# Copyright (C) 2015-2017 Jori Mäntysalo +# Copyright (C) 2015-2020 Michael Orlitzky +# Copyright (C) 2016-2020 John H. Palmieri +# Copyright (C) 2018 David Coudert +# Copyright (C) 2019-2020 Jonathan Kliem +# Copyright (C) 2020-2021 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 3c20f90d1dd..d715b9f708d 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -16,21 +16,96 @@ class RelativeInterior(SageObject): - """ + r""" The relative interior of a polyhedron or cone + + This class should not be used directly. Use methods + :meth:`~sage.geometry.polyhedron.Polyhedron_base.relative_interior`, + :meth:`~sage.geometry.polyhedron.Polyhedron_base.interior`, + :meth:`~sage.geometry.cone.ConvexRationalPolyhedralCone.relative_interior`, + :meth:`~sage.geometry.cone.ConvexRationalPolyhedralCone.interior` instead. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: segment.relative_interior() + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)]) + sage: octant.relative_interior() + Relative interior of 3-d cone in 3-d lattice N + """ def __init__(self, polyhedron): + r""" + Initialize ``self``. + + INPUT: + + - ``polyhedron`` - an instance of :class:`Polyhedron_base` or + :class:`ConvexRationalPolyhedralCone`. + + TESTS:: + + sage: P = Polyhedron() + sage: from sage.geometry.relative_interior import RelativeInterior + sage: TestSuite(RelativeInterior(P)).run() + + """ self._polyhedron = polyhedron def __contains__(self, point): + r""" + Return whether ``self`` contains ``point``. + + EXAMPLES:: + + sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)]) + sage: ri_octant = octant.relative_interior(); ri_octant + Relative interior of 3-d cone in 3-d lattice N + sage: (1, 1, 1) in ri_octant + True + sage: (1, 0, 0) in ri_octant + False + """ return self._polyhedron.relative_interior_contains(point) + def relative_interior(self): + r""" + Return the relative interior of ``self``. + + As ``self`` is already relatively open, this method just returns ``self``. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.relative_interior() is ri_segment + True + """ + return self + def closure(self): + r""" + Return the topological closure of ``self``. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.closure() is segment + True + + """ return self._polyhedron def _repr_(self): - """ + r""" Return a description of ``self``. EXAMPLES:: @@ -46,3 +121,51 @@ def _repr_(self): if repr_P.startswith('A '): repr_P = 'a ' + repr_P[2:] return 'Relative interior of ' + repr_P + + def __eq__(self, other): + r""" + Compare ``self`` and ``other``. + + INPUT: + + - ``other`` -- a polyhedron + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: segment2 = Polyhedron([[1, 2], [3, 4]], base_ring=AA) + sage: ri_segment2 = segment2.relative_interior(); ri_segment2 + Relative interior of + a 1-dimensional polyhedron in AA^2 defined as the convex hull of 2 vertices + sage: ri_segment == ri_segment2 + True + + """ + return self._polyhedron == other._polyhedron + + def __ne__(self, other): + r""" + Compare ``self`` and ``other``. + + INPUT: + + - ``other`` -- a polyhedron + + TESTS:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: segment2 = Polyhedron([[1, 2], [3, 4]], base_ring=AA) + sage: ri_segment2 = segment2.relative_interior(); ri_segment2 + Relative interior of + a 1-dimensional polyhedron in AA^2 defined as the convex hull of 2 vertices + sage: ri_segment != ri_segment2 + False + + """ + return self._polyhedron != other._polyhedron From 8f38e0475edfd5913bf25ede90162b04597db64c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 13:02:44 -0700 Subject: [PATCH 128/280] ConvexRationalPolyhedralCone.interior, relative_interior: Add doctests --- src/sage/geometry/cone.py | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 29007febb11..6d2a49eec66 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1725,6 +1725,34 @@ def interior_contains(self, *args): def interior(self): r""" Return the interior of ``self``. + + OUTPUT: + + - either ``self``, an empty polyhedron, or an instance of + :class:`~sage.geometry.relative_interior.RelativeInterior`. + + EXAMPLES:: + + sage: c = Cone([(1,0,0), (0,1,0)]); c + 2-d cone in 3-d lattice N + sage: c.interior() + The empty polyhedron in ZZ^3 + + sage: origin = cones.trivial(2); origin + 0-d cone in 2-d lattice N + sage: origin.interior() + The empty polyhedron in ZZ^2 + + sage: K = cones.nonnegative_orthant(2); K + 2-d cone in 2-d lattice N + sage: K.interior() + Relative interior of 2-d cone in 2-d lattice N + + sage: K2 = Cone([(1,0),(-1,0),(0,1),(0,-1)]); K2 + 2-d cone in 2-d lattice N + sage: K2.interior() is K2 + True + """ if self.is_solid(): return self.relative_interior() @@ -1775,6 +1803,29 @@ def relative_interior_contains(self, *args): def relative_interior(self): r""" Return the relative interior of ``self``. + + OUTPUT: + + - either ``self`` or an instance of + :class:`~sage.geometry.relative_interior.RelativeInterior`. + + EXAMPLES:: + + sage: c = Cone([(1,0,0), (0,1,0)]); c + 2-d cone in 3-d lattice N + sage: c.relative_interior() + Relative interior of 2-d cone in 3-d lattice N + + sage: origin = cones.trivial(2); origin + 0-d cone in 2-d lattice N + sage: origin.relative_interior() is origin + True + + sage: K2 = Cone([(1,0),(-1,0),(0,1),(0,-1)]); K2 + 2-d cone in 2-d lattice N + sage: K2.relative_interior() is K2 + True + """ if self.is_trivial() or self.is_full_space(): return self From 669a161dfb16a5e725183df86b30619a452f3e6e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 15:36:10 -0700 Subject: [PATCH 129/280] ConvexSet_{base,closed,relatively_open}: New; make Polyhedron_base, LatticePolytopeClass, ConvexRationalPolyhedralCone subclasses --- src/sage/geometry/cone.py | 3 +- src/sage/geometry/convex_set.py | 157 ++++++++++++++++++++++++++ src/sage/geometry/lattice_polytope.py | 5 +- src/sage/geometry/polyhedron/base.py | 3 +- 4 files changed, 165 insertions(+), 3 deletions(-) create mode 100644 src/sage/geometry/convex_set.py diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 79c75ad7841..243314c173c 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -213,6 +213,7 @@ from sage.structure.all import SageObject, parent from sage.structure.richcmp import richcmp_method, richcmp from sage.geometry.integral_points import parallelotope_points +from sage.geometry.convex_set import ConvexSet_closed from sage.misc.lazy_import import lazy_import from sage.features import PythonModule @@ -1375,7 +1376,7 @@ def classify_cone_2d(ray0, ray1, check=True): # and ``ambient_ray_indices`` keyword parameters. See ``intersection`` method # for an example why this is needed. @richcmp_method -class ConvexRationalPolyhedralCone(IntegralRayCollection, Container): +class ConvexRationalPolyhedralCone(IntegralRayCollection, Container, ConvexSet_closed): r""" Create a convex rational polyhedral cone. diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py new file mode 100644 index 00000000000..ed8a72ca225 --- /dev/null +++ b/src/sage/geometry/convex_set.py @@ -0,0 +1,157 @@ +r""" +Convex Sets +""" + +# **************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.structure.sage_object import SageObject +from sage.misc.abstract_method import abstract_method + +class ConvexSet_base(SageObject): + + """ + Abstract base class for convex sets. + """ + + @abstract_method + def is_empty(self): + r""" + Test whether ``self`` is the empty set + + OUTPUT: + + Boolean. + """ + + @abstract_method + def is_universe(self): + r""" + Test whether ``self`` is the whole ambient space + + OUTPUT: + + Boolean. + """ + + @abstract_method + def is_full_dimensional(self): + r""" + Return whether ``self`` is full dimensional. + + OUTPUT: + + Boolean. Whether the polyhedron is not contained in any strict + affine subspace. + + """ + + @abstract_method + def is_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + + def is_relatively_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + if self.is_open(): + return True + raise NotImplementedError + + @abstract_method + def is_closed(self): + r""" + Return whether ``self`` is closed. + + OUTPUT: + + Boolean. + + """ + + def closure(self): + r""" + Return the topological closure of ``self``. + """ + if self.is_closed(): + return self + raise NotImplementedError + + def interior(self): + r""" + Return the topological interior of ``self``. + """ + if self.is_closed(): + return self + raise NotImplementedError + + @abstract_method(optional=True) + def affine_hull(self): + r""" + Return the affine hull of ``self``. + """ + + +class ConvexSet_closed(ConvexSet_base): + + r""" + Abstract base class for closed convex sets. + """ + + def is_closed(self): + r""" + Return whether ``self`` is closed. + + OUTPUT: + + Boolean. + """ + return True + + def is_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + return self.is_empty() or self.is_universe() + + +class ConvexSet_relatively_open(ConvexSet_base): + + r""" + Abstract base class for relatively open sets. + """ + + def is_relatively_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + return True diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index cc5d4303897..0456f5cc14b 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -135,6 +135,7 @@ from sage.structure.all import Sequence from sage.structure.sage_object import SageObject from sage.structure.richcmp import richcmp_method, richcmp +from sage.geometry.convex_set import ConvexSet_closed from copy import copy from collections.abc import Hashable @@ -464,7 +465,7 @@ def is_LatticePolytope(x): return isinstance(x, LatticePolytopeClass) @richcmp_method -class LatticePolytopeClass(SageObject, Hashable): +class LatticePolytopeClass(ConvexSet_closed, Hashable): r""" Create a lattice polytope. @@ -517,6 +518,8 @@ def __init__(self, points=None, compute_vertices=None, sage: LatticePolytope([(1,2,3), (4,5,6)]) # indirect test 1-d lattice polytope in 3-d lattice M + sage: TestSuite(_).run() + """ if ambient is None: self._ambient = self diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 23ffed867da..5cfb0278aef 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -51,6 +51,7 @@ from sage.functions.other import sqrt, floor, ceil from sage.groups.matrix_gps.finitely_generated import MatrixGroup from sage.graphs.graph import Graph +from sage.geometry.convex_set import ConvexSet_closed from .constructor import Polyhedron from sage.categories.sets_cat import EmptySetError @@ -98,7 +99,7 @@ def is_Polyhedron(X): ######################################################################### -class Polyhedron_base(Element): +class Polyhedron_base(Element, ConvexSet_closed): """ Base class for Polyhedron objects From 39e1f18e81532191faf2cf48f431005f79f53fa9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 20:06:35 -0700 Subject: [PATCH 130/280] MinMax_base: Fix for numeric arguments --- src/sage/functions/min_max.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sage/functions/min_max.py b/src/sage/functions/min_max.py index f38c991d405..746c1067acc 100644 --- a/src/sage/functions/min_max.py +++ b/src/sage/functions/min_max.py @@ -47,8 +47,13 @@ def eval_helper(self, this_f, builtin_f, initial_val, args): sage: max_symbolic(3, 5, x) # indirect doctest max(x, 5) + sage: max_symbolic([5.0r]) # indirect doctest + 5.0 sage: min_symbolic(3, 5, x) min(x, 3) + sage: min_symbolic([5.0r]) # indirect doctest + 5.0 + """ # __call__ ensures that if args is a singleton, the element is iterable arg_is_iter = False @@ -64,7 +69,10 @@ def eval_helper(self, this_f, builtin_f, initial_val, args): symb_args.append(x) else: num_non_symbolic_args += 1 - res = builtin_f(res, x) + if res is None: + res = x + else: + res = builtin_f(res, x) # if no symbolic arguments, return the result if len(symb_args) == 0: From e67f75377b140110ccd28fc9fa54b6e090b077cd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 20:33:52 -0700 Subject: [PATCH 131/280] ConvexRationalPolyhedralCone: Add method is_empty and aliases is_full_dimensional, is_universe --- src/sage/geometry/cone.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 243314c173c..8838d58b654 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -3191,6 +3191,14 @@ def is_smooth(self): return False return self.rays().matrix().elementary_divisors() == [1] * self.nrays() + def is_empty(self): + """ + Return whether ``self`` is the empty set. + + Because a cone always contains the origin, this method returns ``False``. + """ + return False + def is_trivial(self): """ Checks if the cone has no rays. @@ -4448,6 +4456,8 @@ def is_solid(self): A cone is said to be solid if it has nonempty interior. That is, if its extreme rays span the entire ambient space. + An alias is :meth:`is_full_dimensional`. + OUTPUT: ``True`` if this cone is solid, and ``False`` otherwise. @@ -4487,6 +4497,8 @@ def is_solid(self): """ return (self.dim() == self.lattice_dim()) + is_full_dimensional = is_solid + def is_proper(self): r""" Check if this cone is proper. @@ -4537,6 +4549,8 @@ def is_full_space(self): r""" Check if this cone is equal to its ambient vector space. + An alias is :meth:`is_universe`. + OUTPUT: ``True`` if this cone equals its entire ambient vector @@ -4574,6 +4588,8 @@ def is_full_space(self): """ return self.linear_subspace() == self.lattice().vector_space() + is_universe = is_full_space + def lineality(self): r""" Return the lineality of this cone. From 770fdcd9125c8bb78dd45573613e6489220664a0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 22:52:29 -0700 Subject: [PATCH 132/280] LatticePolytopeClass, IntegralRayCollection: Make ambient_dim an alias for lattice_dim --- src/sage/geometry/cone.py | 4 ++++ src/sage/geometry/convex_set.py | 5 +++++ src/sage/geometry/lattice_polytope.py | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 8838d58b654..07df0fc575e 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1010,6 +1010,8 @@ def lattice_dim(self): r""" Return the dimension of the ambient lattice of ``self``. + An alias is :meth:`ambient_dim`. + OUTPUT: - integer. @@ -1024,6 +1026,8 @@ def lattice_dim(self): """ return self.lattice().dimension() + ambient_dim = lattice_dim + def nrays(self): r""" Return the number of rays of ``self``. diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index ed8a72ca225..197aecac9be 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -42,6 +42,11 @@ def is_universe(self): """ @abstract_method + def ambient_dim(self): + r""" + Return the dimension of the ambient space. + """ + def is_full_dimensional(self): r""" Return whether ``self`` is full dimensional. diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index 0456f5cc14b..10866bc8c31 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -2599,6 +2599,8 @@ def lattice_dim(self): r""" Return the dimension of the ambient lattice of ``self``. + An alias is :meth:`ambient_dim`. + OUTPUT: - integer. @@ -2613,6 +2615,8 @@ def lattice_dim(self): """ return self.lattice().dimension() + ambient_dim = lattice_dim + def linearly_independent_vertices(self): r""" Return a maximal set of linearly independent vertices. From b2ac6396c075f8ad4d71a379de367fd9cbaa3561 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 22:54:22 -0700 Subject: [PATCH 133/280] ConvexSet_base.dim: New --- src/sage/geometry/convex_set.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 197aecac9be..56189f2518c 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -21,7 +21,6 @@ class ConvexSet_base(SageObject): Abstract base class for convex sets. """ - @abstract_method def is_empty(self): r""" Test whether ``self`` is the empty set @@ -30,8 +29,8 @@ def is_empty(self): Boolean. """ + return self.dim() < 0 - @abstract_method def is_universe(self): r""" Test whether ``self`` is the whole ambient space @@ -40,6 +39,15 @@ def is_universe(self): Boolean. """ + if not self.is_full_dimensional(): + return False + raise NotImplementedError + + @abstract_method + def dim(self): + r""" + Return the dimension of ``self``. + """ @abstract_method def ambient_dim(self): @@ -55,8 +63,8 @@ def is_full_dimensional(self): Boolean. Whether the polyhedron is not contained in any strict affine subspace. - """ + return self.dim() == self.ambient_dim() @abstract_method def is_open(self): @@ -105,7 +113,7 @@ def interior(self): r""" Return the topological interior of ``self``. """ - if self.is_closed(): + if self.is_open(): return self raise NotImplementedError From ccc84213906ff6bf34d2e5c6b50d146a2901ec9c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 6 Jun 2021 22:54:52 -0700 Subject: [PATCH 134/280] ConvexSet_base.is_compact, ConvexSet_compact: New --- src/sage/geometry/convex_set.py | 43 +++++++++++++++++++++++++++ src/sage/geometry/lattice_polytope.py | 4 +-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 56189f2518c..37ef89ae39e 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -101,6 +101,21 @@ def is_closed(self): """ + def is_compact(self): + r""" + Return whether ``self`` is compact. + + OUTPUT: + + Boolean. + + """ + if not self.is_closed(): + return False + if self.dimension() < 1: + return True + raise NotImplementedError + def closure(self): r""" Return the topological closure of ``self``. @@ -152,6 +167,34 @@ def is_open(self): return self.is_empty() or self.is_universe() +class ConvexSet_compact(ConvexSet_closed): + + r""" + Abstract base class for compact convex sets. + """ + + def is_universe(self): + r""" + Return whether ``self`` is the whole ambient space + + OUTPUT: + + Boolean. + """ + return self.ambient_dim() == 0 and not self.is_empty() + + def is_compact(self): + r""" + Return whether ``self`` is compact. + + OUTPUT: + + Boolean. + + """ + return True + + class ConvexSet_relatively_open(ConvexSet_base): r""" diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index 10866bc8c31..a063657e30a 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -135,7 +135,7 @@ from sage.structure.all import Sequence from sage.structure.sage_object import SageObject from sage.structure.richcmp import richcmp_method, richcmp -from sage.geometry.convex_set import ConvexSet_closed +from sage.geometry.convex_set import ConvexSet_compact from copy import copy from collections.abc import Hashable @@ -465,7 +465,7 @@ def is_LatticePolytope(x): return isinstance(x, LatticePolytopeClass) @richcmp_method -class LatticePolytopeClass(ConvexSet_closed, Hashable): +class LatticePolytopeClass(ConvexSet_compact, Hashable): r""" Create a lattice polytope. From 923196adb6c2e75ef44c4e9ce4df171cf607585c Mon Sep 17 00:00:00 2001 From: Eric Gourgoulhon Date: Mon, 7 Jun 2021 16:54:18 +0200 Subject: [PATCH 135/280] #31923: initialize the inverse of the inverse to self --- src/sage/manifolds/chart.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 6fb017d0082..24f73f7a585 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -3123,7 +3123,11 @@ def __call__(self, *coords): def inverse(self): r""" - Compute the inverse coordinate transformation. + Return the inverse coordinate transformation. + + If the inverse is not already known, it is computed here. If the + computation fails, the inverse can be set by hand via the method + :meth:`set_inverse`. OUTPUT: @@ -3156,6 +3160,16 @@ def inverse(self): (Chart (M, (x, y)), Chart (M, (u, v))): Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v))} + The result is cached:: + + sage: xy_to_uv.inverse() is uv_to_xy + True + + We have as well:: + + sage: uv_to_xy.inverse() is xy_to_uv + True + """ from sage.symbolic.relation import solve from sage.symbolic.assumptions import assumptions @@ -3236,6 +3250,7 @@ def inverse(self): "manually") x2_to_x1 = list_x2_to_x1[0] self._inverse = type(self)(self._chart2, self._chart1, *x2_to_x1) + self._inverse._inverse = self # Some cleaning: the local symbolic variables (xxxx0, xxxx1, ...) are # removed from the list of assumptions for asm in assumptions(): @@ -3338,7 +3353,14 @@ def set_inverse(self, *transformations, **kwds): u == u *passed* v == v *passed* - TESTS:: + TESTS: + + Check that :trac:`31923` is fixed:: + + sage: X1_to_X2.inverse().inverse() is X1_to_X2 + True + + Check of keyword arguments:: sage: X1_to_X2.set_inverse((u+v)/2, (u-v)/2, bla=3) Traceback (most recent call last): @@ -3353,6 +3375,7 @@ def set_inverse(self, *transformations, **kwds): "argument".format(unknown_key)) self._inverse = type(self)(self._chart2, self._chart1, *transformations) + self._inverse._inverse = self if check: infos = ["Check of the inverse coordinate transformation:"] x1 = self._chart1._xx From 5f9c8526d013234f9caf33c50b9117812fbaad55 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 11:55:20 -0700 Subject: [PATCH 136/280] RelativeInterior.interior: New --- src/sage/geometry/relative_interior.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index d715b9f708d..65fd898ffd1 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -71,6 +71,27 @@ def __contains__(self, point): """ return self._polyhedron.relative_interior_contains(point) + def interior(self): + r""" + Return the interior of ``self``. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.interior() + The empty polyhedron in ZZ^2 + + sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)]) + sage: ri_octant = octant.relative_interior(); ri_octant + Relative interior of 3-d cone in 3-d lattice N + sage: ri_octant.interior() is ri_octant + True + """ + return self._polyhedron.interior() + def relative_interior(self): r""" Return the relative interior of ``self``. From 5c089ec4926664d426e5d46735cb8c1a9b1d1017 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 12:01:39 -0700 Subject: [PATCH 137/280] RelativeInterior.__eq__, __ne__: Handle comparisons with objects of other types --- src/sage/geometry/relative_interior.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 65fd898ffd1..73e65d4361e 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -149,7 +149,7 @@ def __eq__(self, other): INPUT: - - ``other`` -- a polyhedron + - ``other`` -- any object EXAMPLES:: @@ -164,7 +164,15 @@ def __eq__(self, other): sage: ri_segment == ri_segment2 True + TESTS:: + + sage: empty = Polyhedron(ambient_dim=2) + sage: ri_segment == empty + False + """ + if type(self) != type(other): + return False return self._polyhedron == other._polyhedron def __ne__(self, other): @@ -173,7 +181,7 @@ def __ne__(self, other): INPUT: - - ``other`` -- a polyhedron + - ``other`` -- any object TESTS:: @@ -189,4 +197,4 @@ def __ne__(self, other): False """ - return self._polyhedron != other._polyhedron + return not (self == other) From 86ce301c02936f4b56f6ab821520e8542bd5fbc1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 12:38:05 -0700 Subject: [PATCH 138/280] Polyhedron_base.is_relatively_open: New; fix relative_interior for affine subspaces --- src/sage/geometry/polyhedron/base.py | 39 +++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 985a9f5c6c5..b49ab2a8d38 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8484,6 +8484,39 @@ def interior_contains(self, point): return False return True + def is_relatively_open(self): + r""" + Return whether ``self`` is relatively open. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(1,0), (-1,0)]); P + A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: P.is_relatively_open() + False + + sage: P0 = Polyhedron(vertices=[[1, 2]]); P0 + A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex + sage: P0.is_relatively_open() + True + + sage: Empty = Polyhedron(ambient_dim=2); Empty + The empty polyhedron in ZZ^2 + sage: Empty.is_relatively_open() + True + + sage: Line = Polyhedron(vertices=[(1, 1)], lines=[(1, 0)]); Line + A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line + sage: Line.is_relatively_open() + True + + """ + return not self.inequalities() + @cached_method def relative_interior(self): """ @@ -8507,8 +8540,12 @@ def relative_interior(self): sage: Empty = Polyhedron(ambient_dim=2) sage: Empty.relative_interior() is Empty True + + sage: Line = Polyhedron(vertices=[(1, 1)], lines=[(1, 0)]) + sage: Line.relative_interior() is Line + True """ - if self.dim() <= 0 or self.is_universe(): + if self.is_relatively_open(): return self return RelativeInterior(self) From 4bac2fe2db4519478d6e5633e86b29098d9aa0c4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 12:53:33 -0700 Subject: [PATCH 139/280] RelativeInterior: Subclass ConvexSet_relatively_open, add missing methods --- src/sage/geometry/convex_set.py | 13 +++++- src/sage/geometry/relative_interior.py | 59 +++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 37ef89ae39e..5a531dd9c44 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -203,7 +203,7 @@ class ConvexSet_relatively_open(ConvexSet_base): def is_relatively_open(self): r""" - Return whether ``self`` is open. + Return whether ``self`` is relatively open. OUTPUT: @@ -211,3 +211,14 @@ def is_relatively_open(self): """ return True + + def is_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + return self.is_full_dimensional() diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 73e65d4361e..c41012835eb 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -12,9 +12,9 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.structure.sage_object import SageObject +from sage.geometry.convex_set import ConvexSet_relatively_open -class RelativeInterior(SageObject): +class RelativeInterior(ConvexSet_relatively_open): r""" The relative interior of a polyhedron or cone @@ -71,6 +71,40 @@ def __contains__(self, point): """ return self._polyhedron.relative_interior_contains(point) + def ambient_dim(self): + r""" + Return the dimension of the ambient space. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: segment.ambient_dim() + 2 + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.ambient_dim() + 2 + + """ + return self._polyhedron.ambient_dim() + + def dim(self): + r""" + Return the dimension of ``self``. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: segment.dim() + 1 + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.dim() + 1 + + """ + return self._polyhedron.dim() + def interior(self): r""" Return the interior of ``self``. @@ -125,6 +159,27 @@ def closure(self): """ return self._polyhedron + def is_closed(self): + r""" + Return whether ``self`` is closed. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.is_closed() + False + """ + # Relies on ``self`` not set up for polyhedra that are already + # relatively open themselves. + assert not self._polyhedron.is_relatively_open() + return False + def _repr_(self): r""" Return a description of ``self``. From 216cb811d16bdf1bedfb2d7e04339ed4387f7212 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 13:01:25 -0700 Subject: [PATCH 140/280] ConvexRationalPolyhedralCone.is_relatively_open: New, fix ConvexRationalPolyhedralCone.relative_interior for linear subspaces --- src/sage/geometry/cone.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 6d2a49eec66..46b7d88b2e9 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1821,13 +1821,18 @@ def relative_interior(self): sage: origin.relative_interior() is origin True + sage: K1 = Cone([(1,0), (-1,0)]); K1 + 1-d cone in 2-d lattice N + sage: K1.relative_interior() is K1 + True + sage: K2 = Cone([(1,0),(-1,0),(0,1),(0,-1)]); K2 2-d cone in 2-d lattice N sage: K2.relative_interior() is K2 True """ - if self.is_trivial() or self.is_full_space(): + if self.is_relatively_open(): return self return RelativeInterior(self) @@ -4724,6 +4729,29 @@ def lineality(self): """ return self.linear_subspace().dimension() + def is_relatively_open(self): + + r""" + Return whether ``self`` is relatively open. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: K = cones.nonnegative_orthant(3) + sage: K.is_relatively_open() + False + + sage: K1 = Cone([(1,0), (-1,0)]); K1 + 1-d cone in 2-d lattice N + sage: K1.is_relatively_open() + True + + """ + return self.lineality() == self.dim() + @cached_method def discrete_complementarity_set(self): r""" From 44cde1e3dbea9086e75f235c0f61164278e4e451 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 13:24:42 -0700 Subject: [PATCH 141/280] src/doc/en/reference/discrete_geometry/index.rst: Add sage/geometry/relative_interior --- src/doc/en/reference/discrete_geometry/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/en/reference/discrete_geometry/index.rst b/src/doc/en/reference/discrete_geometry/index.rst index 3d027326933..88dbc812caf 100644 --- a/src/doc/en/reference/discrete_geometry/index.rst +++ b/src/doc/en/reference/discrete_geometry/index.rst @@ -107,6 +107,7 @@ Miscellaneous sage/geometry/linear_expression sage/geometry/newton_polygon + sage/geometry/relative_interior sage/geometry/ribbon_graph sage/geometry/pseudolines sage/geometry/voronoi_diagram From fa4c2d238a8f16a61f02e7cfa89ba3f17d8f4980 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 15:07:47 -0700 Subject: [PATCH 142/280] Whitespace fixes --- src/sage/geometry/cone.py | 3 --- src/sage/geometry/relative_interior.py | 7 +------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 46b7d88b2e9..8e3e73d75dd 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1830,7 +1830,6 @@ def relative_interior(self): 2-d cone in 2-d lattice N sage: K2.relative_interior() is K2 True - """ if self.is_relatively_open(): return self @@ -4730,7 +4729,6 @@ def lineality(self): return self.linear_subspace().dimension() def is_relatively_open(self): - r""" Return whether ``self`` is relatively open. @@ -4748,7 +4746,6 @@ def is_relatively_open(self): 1-d cone in 2-d lattice N sage: K1.is_relatively_open() True - """ return self.lineality() == self.dim() diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 73e65d4361e..0850ea4860c 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -14,8 +14,8 @@ from sage.structure.sage_object import SageObject -class RelativeInterior(SageObject): +class RelativeInterior(SageObject): r""" The relative interior of a polyhedron or cone @@ -34,7 +34,6 @@ class RelativeInterior(SageObject): sage: octant = Cone([(1,0,0), (0,1,0), (0,0,1)]) sage: octant.relative_interior() Relative interior of 3-d cone in 3-d lattice N - """ def __init__(self, polyhedron): @@ -51,7 +50,6 @@ def __init__(self, polyhedron): sage: P = Polyhedron() sage: from sage.geometry.relative_interior import RelativeInterior sage: TestSuite(RelativeInterior(P)).run() - """ self._polyhedron = polyhedron @@ -121,7 +119,6 @@ def closure(self): a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices sage: ri_segment.closure() is segment True - """ return self._polyhedron @@ -169,7 +166,6 @@ def __eq__(self, other): sage: empty = Polyhedron(ambient_dim=2) sage: ri_segment == empty False - """ if type(self) != type(other): return False @@ -195,6 +191,5 @@ def __ne__(self, other): a 1-dimensional polyhedron in AA^2 defined as the convex hull of 2 vertices sage: ri_segment != ri_segment2 False - """ return not (self == other) From dede9de1a8e01114b289149bccb51c466223d027 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 7 Jun 2021 16:47:05 -0700 Subject: [PATCH 143/280] src/doc/en/reference/discrete_geometry/index.rst: Add sage/geometry/convex_set --- src/doc/en/reference/discrete_geometry/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/en/reference/discrete_geometry/index.rst b/src/doc/en/reference/discrete_geometry/index.rst index 88dbc812caf..6975fa144a7 100644 --- a/src/doc/en/reference/discrete_geometry/index.rst +++ b/src/doc/en/reference/discrete_geometry/index.rst @@ -105,6 +105,7 @@ Miscellaneous .. toctree:: :maxdepth: 1 + sage/geometry/convex_set sage/geometry/linear_expression sage/geometry/newton_polygon sage/geometry/relative_interior From e1f4093f60c7349e740fc79302e9808057b05151 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Mon, 7 Jun 2021 18:49:23 -0700 Subject: [PATCH 144/280] trac 31925: move files into sage/topology edit to reflect their new locations --- src/doc/en/reference/homology/index.rst | 24 +--- src/doc/en/reference/topology/conf.py | 1 + src/doc/en/reference/topology/index.rst | 26 ++++ .../{homology => topology}/media/klein.png | Bin .../{homology => topology}/media/rp2.png | Bin .../media/simplices.png | Bin .../{homology => topology}/media/torus.png | Bin .../media/torus_labelled.png | Bin src/sage/all.py | 1 + src/sage/categories/finite_monoids.py | 2 +- src/sage/categories/simplicial_sets.py | 22 ++-- src/sage/combinat/posets/posets.py | 6 +- src/sage/combinat/subword_complex.py | 2 +- src/sage/ext_data/kenzo/README.txt | 2 +- src/sage/geometry/polyhedron/base.py | 2 +- src/sage/geometry/triangulation/element.py | 4 +- src/sage/graphs/generators/random.py | 2 +- src/sage/graphs/graph.py | 4 +- .../homology/algebraic_topological_model.py | 4 +- src/sage/homology/all.py | 16 --- src/sage/homology/chains.py | 18 +-- src/sage/homology/homology_morphism.py | 4 +- .../homology_vector_space_with_basis.py | 16 +-- src/sage/homology/tests.py | 2 +- src/sage/interfaces/chomp.py | 12 +- src/sage/interfaces/kenzo.py | 10 +- .../differentiable/examples/sphere.py | 2 +- src/sage/matroids/matroid.pyx | 2 +- src/sage/sandpiles/sandpile.py | 2 +- src/sage/schemes/toric/divisor.py | 4 +- src/sage/topology/__init__.py | 1 + src/sage/topology/all.py | 16 +++ .../{homology => topology}/cell_complex.py | 36 +++--- .../{homology => topology}/cubical_complex.py | 52 ++++---- .../{homology => topology}/delta_complex.py | 6 +- .../simplicial_complex.py | 22 ++-- .../simplicial_complex_catalog.py} | 53 ++++---- .../simplicial_complex_examples.py} | 18 +-- .../simplicial_complex_homset.py | 6 +- .../simplicial_complex_morphism.py | 8 +- .../{homology => topology}/simplicial_set.py | 122 +++++++++--------- .../simplicial_set_catalog.py | 0 .../simplicial_set_constructions.py | 56 ++++---- .../simplicial_set_examples.py | 8 +- .../simplicial_set_morphism.py | 16 +-- 45 files changed, 312 insertions(+), 298 deletions(-) create mode 120000 src/doc/en/reference/topology/conf.py create mode 100644 src/doc/en/reference/topology/index.rst rename src/doc/en/reference/{homology => topology}/media/klein.png (100%) rename src/doc/en/reference/{homology => topology}/media/rp2.png (100%) rename src/doc/en/reference/{homology => topology}/media/simplices.png (100%) rename src/doc/en/reference/{homology => topology}/media/torus.png (100%) rename src/doc/en/reference/{homology => topology}/media/torus_labelled.png (100%) create mode 100644 src/sage/topology/__init__.py create mode 100644 src/sage/topology/all.py rename src/sage/{homology => topology}/cell_complex.py (97%) rename src/sage/{homology => topology}/cubical_complex.py (97%) rename src/sage/{homology => topology}/delta_complex.py (99%) rename src/sage/{homology => topology}/simplicial_complex.py (99%) rename src/sage/{homology/simplicial_complexes_catalog.py => topology/simplicial_complex_catalog.py} (58%) rename src/sage/{homology/examples.py => topology/simplicial_complex_examples.py} (98%) rename src/sage/{homology => topology}/simplicial_complex_homset.py (96%) rename src/sage/{homology => topology}/simplicial_complex_morphism.py (99%) rename src/sage/{homology => topology}/simplicial_set.py (97%) rename src/sage/{homology => topology}/simplicial_set_catalog.py (100%) rename src/sage/{homology => topology}/simplicial_set_constructions.py (98%) rename src/sage/{homology => topology}/simplicial_set_examples.py (99%) rename src/sage/{homology => topology}/simplicial_set_morphism.py (99%) diff --git a/src/doc/en/reference/homology/index.rst b/src/doc/en/reference/homology/index.rst index bf98e0841fd..32424286980 100644 --- a/src/doc/en/reference/homology/index.rst +++ b/src/doc/en/reference/homology/index.rst @@ -1,12 +1,8 @@ -Cell complexes and their homology -================================= +Chain complexes and homology +============================ -Sage includes some tools for algebraic topology: from the algebraic -side, chain complexes and their homology, and from the topological -side, simplicial complexes, `\Delta`-complexes, cubical complexes, and -simplicial sets. A class of generic cell complexes is also available, -mainly for developers who want to use it as a base for other types of -cell complexes. +Sage includes some tools for algebraic topology, and in particular +computing homology groups. .. toctree:: :maxdepth: 2 @@ -16,18 +12,6 @@ cell complexes. sage/homology/chain_complex_morphism sage/homology/chain_homotopy sage/homology/chain_complex_homspace - sage/homology/simplicial_complex - sage/homology/simplicial_complex_morphism - sage/homology/simplicial_complex_homset - sage/homology/examples - sage/homology/delta_complex - sage/homology/cubical_complex - sage/homology/simplicial_set - sage/homology/simplicial_set_constructions - sage/homology/simplicial_set_examples - sage/homology/simplicial_set_catalog - sage/homology/simplicial_set_morphism - sage/homology/cell_complex sage/homology/koszul_complex sage/homology/hochschild_complex sage/homology/homology_group diff --git a/src/doc/en/reference/topology/conf.py b/src/doc/en/reference/topology/conf.py new file mode 120000 index 00000000000..2bdf7e68470 --- /dev/null +++ b/src/doc/en/reference/topology/conf.py @@ -0,0 +1 @@ +../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/reference/topology/index.rst b/src/doc/en/reference/topology/index.rst new file mode 100644 index 00000000000..bf3eff5149b --- /dev/null +++ b/src/doc/en/reference/topology/index.rst @@ -0,0 +1,26 @@ +Topology +======== + +Sage includes some tools for topology, and in particular cell +complexes: simplicial complexes, `\Delta`-complexes, cubical +complexes, and simplicial sets. A class of generic cell complexes is +also available, mainly for developers who want to use it as a base for +other types of cell complexes. + +.. toctree:: + :maxdepth: 2 + + sage/topology/simplicial_complex + sage/topology/simplicial_complex_morphism + sage/topology/simplicial_complex_homset + sage/topology/simplicial_complex_examples + sage/topology/delta_complex + sage/topology/cubical_complex + sage/topology/simplicial_set + sage/topology/simplicial_set_constructions + sage/topology/simplicial_set_examples + sage/topology/simplicial_set_catalog + sage/topology/simplicial_set_morphism + sage/topology/cell_complex + +.. include:: ../footer.txt diff --git a/src/doc/en/reference/homology/media/klein.png b/src/doc/en/reference/topology/media/klein.png similarity index 100% rename from src/doc/en/reference/homology/media/klein.png rename to src/doc/en/reference/topology/media/klein.png diff --git a/src/doc/en/reference/homology/media/rp2.png b/src/doc/en/reference/topology/media/rp2.png similarity index 100% rename from src/doc/en/reference/homology/media/rp2.png rename to src/doc/en/reference/topology/media/rp2.png diff --git a/src/doc/en/reference/homology/media/simplices.png b/src/doc/en/reference/topology/media/simplices.png similarity index 100% rename from src/doc/en/reference/homology/media/simplices.png rename to src/doc/en/reference/topology/media/simplices.png diff --git a/src/doc/en/reference/homology/media/torus.png b/src/doc/en/reference/topology/media/torus.png similarity index 100% rename from src/doc/en/reference/homology/media/torus.png rename to src/doc/en/reference/topology/media/torus.png diff --git a/src/doc/en/reference/homology/media/torus_labelled.png b/src/doc/en/reference/topology/media/torus_labelled.png similarity index 100% rename from src/doc/en/reference/homology/media/torus_labelled.png rename to src/doc/en/reference/topology/media/torus_labelled.png diff --git a/src/sage/all.py b/src/sage/all.py index 52d59fadf2e..e3813c2adab 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -183,6 +183,7 @@ from sage.dynamics.all import * from sage.homology.all import * +from sage.topology.all import * from sage.quadratic_forms.all import * diff --git a/src/sage/categories/finite_monoids.py b/src/sage/categories/finite_monoids.py index ffc959d3b6b..69e4a6bc83e 100644 --- a/src/sage/categories/finite_monoids.py +++ b/src/sage/categories/finite_monoids.py @@ -157,7 +157,7 @@ def nerve(self): 3: Vector space of dimension 1 over Finite Field of size 5, 4: Vector space of dimension 1 over Finite Field of size 5} """ - from sage.homology.simplicial_set_examples import Nerve + from sage.topology.simplicial_set_examples import Nerve return Nerve(self) def rhodes_radical_congruence(self, base_ring=None): diff --git a/src/sage/categories/simplicial_sets.py b/src/sage/categories/simplicial_sets.py index 7f40f791097..56ca3e7b76d 100644 --- a/src/sage/categories/simplicial_sets.py +++ b/src/sage/categories/simplicial_sets.py @@ -85,7 +85,7 @@ def is_pointed(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: e = AbstractSimplex(1) @@ -109,7 +109,7 @@ def set_base_point(self, point): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v_0') sage: w = AbstractSimplex(0, name='w_0') sage: e = AbstractSimplex(1) @@ -136,7 +136,7 @@ def set_base_point(self, point): ... ValueError: the point is not a simplex in this simplicial set """ - from sage.homology.simplicial_set import SimplicialSet + from sage.topology.simplicial_set import SimplicialSet if point.dimension() != 0: raise ValueError('the "point" is not a zero-simplex') if point not in self._simplices: @@ -158,7 +158,7 @@ def one(self): Simplicial set endomorphism of Torus Defn: Identity map """ - from sage.homology.simplicial_set_morphism import SimplicialSetMorphism + from sage.topology.simplicial_set_morphism import SimplicialSetMorphism return SimplicialSetMorphism(domain=self.domain(), codomain=self.codomain(), identity=True) @@ -196,7 +196,7 @@ def base_point(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='*') sage: e = AbstractSimplex(1) sage: S1 = SimplicialSet({e: (v, v)}, base_point=v) @@ -220,7 +220,7 @@ def base_point_map(self, domain=None): - ``domain`` -- optional, default ``None``. Use this to specify a particular one-point space as the domain. The default behavior is to use the - :func:`sage.homology.simplicial_set.Point` + :func:`sage.topology.simplicial_set.Point` function to use a standard one-point space. EXAMPLES:: @@ -250,7 +250,7 @@ def base_point_map(self, domain=None): To: Classifying space of Multiplicative Abelian group isomorphic to C5 Defn: Constant map at 1 """ - from sage.homology.simplicial_set_examples import Point + from sage.topology.simplicial_set_examples import Point if domain is None: domain = Point() else: @@ -491,7 +491,7 @@ def unset_base_point(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v_0') sage: w = AbstractSimplex(0, name='w_0') sage: e = AbstractSimplex(1) @@ -504,7 +504,7 @@ def unset_base_point(self): sage: Z.is_pointed() False """ - from sage.homology.simplicial_set import SimplicialSet + from sage.topology.simplicial_set import SimplicialSet return SimplicialSet(self.face_data()) def fat_wedge(self, n): @@ -530,7 +530,7 @@ def fat_wedge(self, n): sage: S1.fat_wedge(4).homology() {0: 0, 1: Z x Z x Z x Z, 2: Z^6, 3: Z x Z x Z x Z} """ - from sage.homology.simplicial_set_examples import Point + from sage.topology.simplicial_set_examples import Point if n == 0: return Point() if n == 1: @@ -561,6 +561,6 @@ def smash_product(self, *others): sage: X.homology(reduced=False) {0: Z, 1: 0, 2: Z x Z, 3: Z} """ - from sage.homology.simplicial_set_constructions import SmashProductOfSimplicialSets_finite + from sage.topology.simplicial_set_constructions import SmashProductOfSimplicialSets_finite return SmashProductOfSimplicialSets_finite((self,) + others) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 0a2ce11d568..024b89e97cf 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -6858,7 +6858,7 @@ def order_complex(self, on_ints=False): sage: P.order_complex(on_ints=True) Simplicial complex with vertex set (0, 1, 2, 3) and facets {(0, 1, 3), (0, 2, 3)} """ - from sage.homology.simplicial_complex import SimplicialComplex + from sage.topology.simplicial_complex import SimplicialComplex L = self.list() if on_ints: iso = dict([(L[i], i) for i in range(len(L))]) @@ -7052,7 +7052,7 @@ def f_polynomial(self): .. SEEALSO:: :meth:`is_bounded`, :meth:`h_polynomial`, :meth:`order_complex`, - :meth:`sage.homology.cell_complex.GenericCellComplex.f_vector` + :meth:`sage.topology.cell_complex.GenericCellComplex.f_vector` TESTS:: @@ -7120,7 +7120,7 @@ def h_polynomial(self): .. SEEALSO:: :meth:`is_bounded`, :meth:`f_polynomial`, :meth:`order_complex`, - :meth:`sage.homology.simplicial_complex.SimplicialComplex.h_vector` + :meth:`sage.topology.simplicial_complex.SimplicialComplex.h_vector` """ q = polygen(ZZ, 'q') hasse = self._hasse_diagram diff --git a/src/sage/combinat/subword_complex.py b/src/sage/combinat/subword_complex.py index dd4926c414e..2e447d7076a 100644 --- a/src/sage/combinat/subword_complex.py +++ b/src/sage/combinat/subword_complex.py @@ -116,7 +116,7 @@ from sage.misc.cachefunc import cached_method from sage.structure.element import Element from sage.structure.unique_representation import UniqueRepresentation -from sage.homology.simplicial_complex import SimplicialComplex, Simplex +from sage.topology.simplicial_complex import SimplicialComplex, Simplex from sage.categories.simplicial_complexes import SimplicialComplexes from sage.geometry.polyhedron.constructor import Polyhedron from sage.geometry.cone import Cone diff --git a/src/sage/ext_data/kenzo/README.txt b/src/sage/ext_data/kenzo/README.txt index 9943fbd25fb..601f7e7f523 100644 --- a/src/sage/ext_data/kenzo/README.txt +++ b/src/sage/ext_data/kenzo/README.txt @@ -10,7 +10,7 @@ Kenzo: - https://www-fourier.ujf-grenoble.fr/~sergerar/Kenzo/ - https://github.com/gheber/kenzo -The results for CP^2, CP^3, and CP^4 have been saved in the corresponding text files. The file S4.txt includes the 4-sphere, just for testing purposes. These files can be processed by the function "simplicial_data_from_kenzo_output" in sage/homology/simplicial_set.py. To get a simplicial set structure for CP^n using Kenzo in sbcl, do the following. +The results for CP^2, CP^3, and CP^4 have been saved in the corresponding text files. The file S4.txt includes the 4-sphere, just for testing purposes. These files can be processed by the function "simplicial_data_from_kenzo_output" in sage/topology/simplicial_set.py. To get a simplicial set structure for CP^n using Kenzo in sbcl, do the following. ;; ;; Start Kenzo. diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 23ffed867da..003461995db 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -2764,7 +2764,7 @@ def boundary_complex(self): ... ValueError: self should be compact """ - from sage.homology.simplicial_complex import SimplicialComplex + from sage.topology.simplicial_complex import SimplicialComplex if not self.is_compact(): raise ValueError("self should be compact") diff --git a/src/sage/geometry/triangulation/element.py b/src/sage/geometry/triangulation/element.py index fe7a6560946..b19b9170258 100644 --- a/src/sage/geometry/triangulation/element.py +++ b/src/sage/geometry/triangulation/element.py @@ -576,7 +576,7 @@ def simplicial_complex(self): OUTPUT: - A :class:`~sage.homology.simplicial_complex.SimplicialComplex`. + A :class:`~sage.topology.simplicial_complex.SimplicialComplex`. EXAMPLES:: @@ -590,7 +590,7 @@ def simplicial_complex(self): sage: sc.homology() {0: 0, 1: 0, 2: 0, 3: 0} """ - from sage.homology.simplicial_complex import SimplicialComplex + from sage.topology.simplicial_complex import SimplicialComplex return SimplicialComplex(self) diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py index 31c1a8810f5..e06bc8a6d87 100644 --- a/src/sage/graphs/generators/random.py +++ b/src/sage/graphs/generators/random.py @@ -1798,7 +1798,7 @@ def RandomTriangulation(n, set_position=False, k=3): .. SEEALSO:: :meth:`~sage.graphs.graph_generators.GraphGenerators.triangulations`, - :func:`~sage.homology.examples.RandomTwoSphere`. + :func:`~sage.topology.simplicial_complex_examples.RandomTwoSphere`. EXAMPLES:: diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 0f1216047a5..c63df92f844 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -7037,8 +7037,8 @@ def clique_complex(self): """ if self.is_directed() or self.has_loops() or self.has_multiple_edges(): raise ValueError("Self must be an undirected simple graph to have a clique complex.") - import sage.homology.simplicial_complex - C = sage.homology.simplicial_complex.SimplicialComplex(self.cliques_maximal(), maximality_check=True) + import sage.topology.simplicial_complex + C = sage.topology.simplicial_complex.SimplicialComplex(self.cliques_maximal(), maximality_check=True) C._graph = self return C diff --git a/src/sage/homology/algebraic_topological_model.py b/src/sage/homology/algebraic_topological_model.py index bfa6b18c80f..b14de9bdfa3 100644 --- a/src/sage/homology/algebraic_topological_model.py +++ b/src/sage/homology/algebraic_topological_model.py @@ -76,14 +76,14 @@ def algebraic_topological_model(K, base_ring=None): of `K`, as described in [GDR2003]_ and [PR2015]_. Implementation details: the cell complex `K` must have an - :meth:`~sage.homology.cell_complex.GenericCellComplex.n_cells` + :meth:`~sage.topology.cell_complex.GenericCellComplex.n_cells` method from which we can extract a list of cells in each dimension. Combining the lists in increasing order of dimension then defines a filtration of the complex: a list of cells in which the boundary of each cell consists of cells earlier in the list. This is required by Pilarczyk and Réal's algorithm. There must also be a - :meth:`~sage.homology.cell_complex.GenericCellComplex.chain_complex` + :meth:`~sage.topology.cell_complex.GenericCellComplex.chain_complex` method, to construct the chain complex `C` associated to this chain complex. diff --git a/src/sage/homology/all.py b/src/sage/homology/all.py index 7708a0cc57c..d9306c19daa 100644 --- a/src/sage/homology/all.py +++ b/src/sage/homology/all.py @@ -1,22 +1,6 @@ - from .chain_complex import ChainComplex from .chain_complex_morphism import ChainComplexMorphism -from .simplicial_complex import SimplicialComplex, Simplex - -from .simplicial_complex_morphism import SimplicialComplexMorphism - -from .delta_complex import DeltaComplex, delta_complexes - -from .cubical_complex import CubicalComplex, cubical_complexes - from sage.misc.lazy_import import lazy_import lazy_import('sage.homology.koszul_complex', 'KoszulComplex') -lazy_import('sage.homology', 'simplicial_complexes_catalog', 'simplicial_complexes') -lazy_import('sage.homology', 'simplicial_set_catalog', 'simplicial_sets') - - -# For taking care of old pickles -from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.homology.examples', 'SimplicialSurface', SimplicialComplex) diff --git a/src/sage/homology/chains.py b/src/sage/homology/chains.py index 7d1ad790137..35df385f23d 100644 --- a/src/sage/homology/chains.py +++ b/src/sage/homology/chains.py @@ -4,7 +4,7 @@ This module implements formal linear combinations of cells of a given cell complex (:class:`Chains`) and their dual (:class:`Cochains`). It -is closely related to the :mod:`sage.homology.chain_complex` +is closely related to the :mod:`sage.topology.chain_complex` module. The main differences are that chains and cochains here are of homogeneous dimension only, and that they reference their cell complex. @@ -235,7 +235,7 @@ def to_complex(self): sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ) - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: chain = C1(Cube([[1, 1], [0, 1]])) sage: chain.to_complex() Chain(1:(0, 0, 0, 1)) @@ -261,7 +261,7 @@ def boundary(self): sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ) - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: chain = C1(Cube([[1, 1], [0, 1]])) - 2 * C1(Cube([[0, 1], [0, 0]])) sage: chain -2*[0,1] x [0,0] + [1,1] x [0,1] @@ -288,7 +288,7 @@ def is_cycle(self): sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ) - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: chain = C1(Cube([[1, 1], [0, 1]])) - C1(Cube([[0, 1], [0, 0]])) sage: chain.is_cycle() False @@ -315,7 +315,7 @@ def is_boundary(self): sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ) - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: chain = C1(Cube([[1, 1], [0, 1]])) - C1(Cube([[0, 1], [0, 0]])) sage: chain.is_boundary() False @@ -476,7 +476,7 @@ def to_complex(self): sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ, cochains=True) - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: cochain = C1(Cube([[1, 1], [0, 1]])) sage: cochain.to_complex() Chain(1:(0, 0, 0, 1)) @@ -502,7 +502,7 @@ def coboundary(self): sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ, cochains=True) - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: cochain = C1(Cube([[1, 1], [0, 1]])) - 2 * C1(Cube([[0, 1], [0, 0]])) sage: cochain -2*\chi_[0,1] x [0,0] + \chi_[1,1] x [0,1] @@ -529,7 +529,7 @@ def is_cocycle(self): sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ, cochains=True) - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: cochain = C1(Cube([[1, 1], [0, 1]])) - C1(Cube([[0, 1], [0, 0]])) sage: cochain.is_cocycle() True @@ -556,7 +556,7 @@ def is_coboundary(self): sage: square = cubical_complexes.Cube(2) sage: C1 = square.n_chains(1, QQ, cochains=True) - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: cochain = C1(Cube([[1, 1], [0, 1]])) - C1(Cube([[0, 1], [0, 0]])) sage: cochain.is_coboundary() True diff --git a/src/sage/homology/homology_morphism.py b/src/sage/homology/homology_morphism.py index eb3b8a08ff5..7dfd5fc8b08 100644 --- a/src/sage/homology/homology_morphism.py +++ b/src/sage/homology/homology_morphism.py @@ -32,7 +32,7 @@ from sage.categories.morphism import Morphism from sage.categories.homset import Hom from sage.rings.rational_field import QQ -from sage.homology.simplicial_complex import SimplicialComplex +from sage.topology.simplicial_complex import SimplicialComplex class InducedHomologyMorphism(Morphism): r""" @@ -52,7 +52,7 @@ class InducedHomologyMorphism(Morphism): This is not intended to be used directly by the user, but instead via the method - :meth:`~sage.homology.simplicial_complex_morphism.SimplicialComplexMorphism.induced_homology_morphism`. + :meth:`~sage.topology.simplicial_complex_morphism.SimplicialComplexMorphism.induced_homology_morphism`. EXAMPLES:: diff --git a/src/sage/homology/homology_vector_space_with_basis.py b/src/sage/homology/homology_vector_space_with_basis.py index 4282e88d273..8861ee20ad9 100644 --- a/src/sage/homology/homology_vector_space_with_basis.py +++ b/src/sage/homology/homology_vector_space_with_basis.py @@ -31,8 +31,8 @@ from sage.categories.modules import Modules from sage.combinat.free_module import CombinatorialFreeModule from sage.sets.family import Family -from .simplicial_complex import SimplicialComplex -from .simplicial_set import SimplicialSet_arbitrary +from sage.topology.simplicial_complex import SimplicialComplex +from sage.topology.simplicial_set import SimplicialSet_arbitrary class HomologyVectorSpaceWithBasis(CombinatorialFreeModule): r""" @@ -49,10 +49,10 @@ class HomologyVectorSpaceWithBasis(CombinatorialFreeModule): This is not intended to be created directly by the user, but instead via the methods - :meth:`~sage.homology.cell_complex.GenericCellComplex.homology_with_basis` and - :meth:`~sage.homology.cell_complex.GenericCellComplex.cohomology_ring` + :meth:`~sage.topology.cell_complex.GenericCellComplex.homology_with_basis` and + :meth:`~sage.topology.cell_complex.GenericCellComplex.cohomology_ring` for the class of :class:`cell - complexes`. + complexes`. INPUT: @@ -414,9 +414,9 @@ class CohomologyRing(HomologyVectorSpaceWithBasis): This is not intended to be created directly by the user, but instead via the - :meth:`cohomology ring` + :meth:`cohomology ring` of a :class:`cell - complex`. + complex`. INPUT: @@ -570,7 +570,7 @@ def product_on_basis(self, li, ri): and simplicial sets:: - sage: from sage.homology.simplicial_set_examples import RealProjectiveSpace + sage: from sage.topology.simplicial_set_examples import RealProjectiveSpace sage: RP5 = RealProjectiveSpace(5) sage: x = RP5.cohomology_ring(GF(2)).basis()[1,0] sage: x**4 diff --git a/src/sage/homology/tests.py b/src/sage/homology/tests.py index b95adea8e30..467d9f35d59 100644 --- a/src/sage/homology/tests.py +++ b/src/sage/homology/tests.py @@ -22,7 +22,7 @@ from sage.matrix.constructor import random_matrix from sage.homology.chain_complex import ChainComplex from sage.rings.integer_ring import ZZ -from sage.homology.examples import RandomComplex +from sage.topology.simplicial_complex_examples import RandomComplex def random_chain_complex(level=1): """ diff --git a/src/sage/interfaces/chomp.py b/src/sage/interfaces/chomp.py index 1416a79f70d..00f4b3f93b3 100644 --- a/src/sage/interfaces/chomp.py +++ b/src/sage/interfaces/chomp.py @@ -134,8 +134,8 @@ def __call__(self, program, complex, subcomplex=None, **kwds): {0: 0, 1: Z x Z, 2: Z} """ from sage.misc.temporary_file import tmp_filename - from sage.homology.all import CubicalComplex, cubical_complexes - from sage.homology.all import SimplicialComplex, Simplex + from sage.topology.cubical_complex import CubicalComplex, cubical_complexes + from sage.topology.simplicial_complex import SimplicialComplex, Simplex from sage.homology.chain_complex import HomologyGroup from subprocess import Popen, PIPE from sage.rings.all import QQ, ZZ @@ -476,7 +476,7 @@ def homsimpl(complex=None, subcomplex=None, **kwds): sage: homsimpl(S1.join(S1), generators=True, base_ring=GF(2))[3][1] # optional - CHomP [('L0', 'L1', 'R0', 'R1') + ('L0', 'L1', 'R0', 'R2') + ('L0', 'L1', 'R1', 'R2') + ('L0', 'L2', 'R0', 'R1') + ('L0', 'L2', 'R0', 'R2') + ('L0', 'L2', 'R1', 'R2') + ('L1', 'L2', 'R0', 'R1') + ('L1', 'L2', 'R0', 'R2') + ('L1', 'L2', 'R1', 'R2')] """ - from sage.homology.all import SimplicialComplex + from sage.topology.simplicial_complex import SimplicialComplex help = kwds.get('help', False) if help: return CHomP().help('homsimpl') @@ -529,7 +529,7 @@ def homcubes(complex=None, subcomplex=None, **kwds): sage: homcubes(cubical_complexes.Sphere(1), generators=True, base_ring=GF(2))[1][1] # optional - CHomP [[[1,1] x [0,1]] + [[0,1] x [1,1]] + [[0,1] x [0,0]] + [[0,0] x [0,1]]] """ - from sage.homology.all import CubicalComplex + from sage.topology.cubical_complex import CubicalComplex help = kwds.get('help', False) if help: return CHomP().help('homcubes') @@ -648,7 +648,7 @@ def process_generators_cubical(gen_string, dim): sage: len(process_generators_cubical(s, 1)) # only one generator 1 """ - from sage.homology.cubical_complex import Cube + from sage.topology.cubical_complex import Cube # each dim in gen_string starts with "The generator for # H_3 follows:". So search for "T" to find the # end of the current list of generators. @@ -751,7 +751,7 @@ def process_generators_simplicial(gen_string, dim, complex): sage: process_generators_simplicial(s, 1, simplicial_complexes.Torus()) [[(-1, (1, 6)), (1, (1, 4))]] """ - from sage.homology.all import Simplex + from sage.topology.simplicial_complex import Simplex # each dim in gen_string starts with "The generator for H_3 # follows:". So search for "T" to find the end of the current # list of generators. diff --git a/src/sage/interfaces/kenzo.py b/src/sage/interfaces/kenzo.py index b501749cb89..e301f9b7899 100644 --- a/src/sage/interfaces/kenzo.py +++ b/src/sage/interfaces/kenzo.py @@ -35,7 +35,7 @@ from sage.matrix.all import matrix from sage.homology.chain_complex import ChainComplex -from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet +from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet from sage.libs.ecl import EclObject, ecl_eval, EclListIterator from sage.features.kenzo import Kenzo @@ -1190,7 +1190,7 @@ def KAbstractSimplex(simplex): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: from sage.interfaces.kenzo import KAbstractSimplex,\ ....: SAbstractSimplex # optional - kenzo sage: SAbSm = AbstractSimplex(1, (2,0,3,2,1), name = 'SAbSm') # optional - kenzo @@ -1219,7 +1219,7 @@ def KFiniteSimplicialSet(sset): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: from sage.interfaces.kenzo import KFiniteSimplicialSet # optional - kenzo sage: s0 = AbstractSimplex(0, name='s0') sage: s1 = AbstractSimplex(0, name='s1') @@ -1241,7 +1241,7 @@ def KFiniteSimplicialSet(sset): sage: KS1vS3.homology(3) # optional - kenzo Z """ - from sage.homology.simplicial_set_constructions import ProductOfSimplicialSets + from sage.topology.simplicial_set_constructions import ProductOfSimplicialSets if isinstance(sset, ProductOfSimplicialSets): f0 = KFiniteSimplicialSet(sset.factor(0)) for f1 in sset.factors()[1:]: @@ -1287,7 +1287,7 @@ def SFiniteSimplicialSet(ksimpset, limit): EXAMPLES:: - sage: from sage.homology.simplicial_set import SimplicialSet + sage: from sage.topology.simplicial_set import SimplicialSet sage: from sage.interfaces.kenzo import AbstractSimplex,\ ....: KFiniteSimplicialSet, SFiniteSimplicialSet, Sphere # optional - kenzo sage: s0 = AbstractSimplex(0, name='s0') # optional - kenzo diff --git a/src/sage/manifolds/differentiable/examples/sphere.py b/src/sage/manifolds/differentiable/examples/sphere.py index 67b40872c7b..5d1b9d7e72d 100644 --- a/src/sage/manifolds/differentiable/examples/sphere.py +++ b/src/sage/manifolds/differentiable/examples/sphere.py @@ -1145,7 +1145,7 @@ def minimal_triangulation(self): 2 """ - from sage.homology.examples import Sphere as SymplicialSphere + from sage.topology.simplicial_complex_examples import Sphere as SymplicialSphere return SymplicialSphere(self._dim) def center(self): diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 61693cc330a..c0fd7fc8191 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -7800,7 +7800,7 @@ cdef class Matroid(SageObject): Simplicial complex with vertex set (1, 2, 3, 4, 5) and facets {(1, 3, 5), (1, 4, 5), (2, 3, 5), (2, 4, 5)} """ - from sage.homology.simplicial_complex import SimplicialComplex + from sage.topology.simplicial_complex import SimplicialComplex return SimplicialComplex(self.no_broken_circuits_sets(ordering)) def union(self, matroids): diff --git a/src/sage/sandpiles/sandpile.py b/src/sage/sandpiles/sandpile.py index ad1cc562762..7506047d5ff 100644 --- a/src/sage/sandpiles/sandpile.py +++ b/src/sage/sandpiles/sandpile.py @@ -335,7 +335,7 @@ from sage.graphs.all import DiGraph, Graph from sage.graphs.digraph_generators import digraphs from sage.probability.probability_distribution import GeneralDiscreteDistribution -from sage.homology.simplicial_complex import SimplicialComplex +from sage.topology.simplicial_complex import SimplicialComplex from sage.interfaces.singular import singular from sage.matrix.constructor import matrix, identity_matrix from sage.misc.all import prod, det, tmp_filename, exists, denominator diff --git a/src/sage/schemes/toric/divisor.py b/src/sage/schemes/toric/divisor.py index e03b724ae6f..bd4a77b4b38 100644 --- a/src/sage/schemes/toric/divisor.py +++ b/src/sage/schemes/toric/divisor.py @@ -170,7 +170,7 @@ from sage.geometry.cone import is_Cone from sage.geometry.polyhedron.constructor import Polyhedron from sage.geometry.toric_lattice_element import is_ToricLatticeElement -from sage.homology.simplicial_complex import SimplicialComplex +from sage.topology.simplicial_complex import SimplicialComplex from sage.matrix.constructor import matrix from sage.misc.all import cached_method, flatten, latex, prod from sage.modules.all import vector @@ -1552,7 +1552,7 @@ def _sheaf_complex(self, m): OUTPUT: - - :class:`simplicial complex `. + - :class:`simplicial complex `. EXAMPLES:: diff --git a/src/sage/topology/__init__.py b/src/sage/topology/__init__.py new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/src/sage/topology/__init__.py @@ -0,0 +1 @@ + diff --git a/src/sage/topology/all.py b/src/sage/topology/all.py new file mode 100644 index 00000000000..c4451e79f3c --- /dev/null +++ b/src/sage/topology/all.py @@ -0,0 +1,16 @@ +from .simplicial_complex import SimplicialComplex, Simplex + +from .simplicial_complex_morphism import SimplicialComplexMorphism + +from .delta_complex import DeltaComplex, delta_complexes + +from .cubical_complex import CubicalComplex, cubical_complexes + +from sage.misc.lazy_import import lazy_import +lazy_import('sage.topology', 'simplicial_complex_catalog', 'simplicial_complexes') +lazy_import('sage.topology', 'simplicial_set_catalog', 'simplicial_sets') + + +# # For taking care of old pickles +# from sage.misc.persist import register_unpickle_override +# register_unpickle_override('sage.topology.simplicial_complex_examples', 'SimplicialSurface', SimplicialComplex) diff --git a/src/sage/homology/cell_complex.py b/src/sage/topology/cell_complex.py similarity index 97% rename from src/sage/homology/cell_complex.py rename to src/sage/topology/cell_complex.py index 01e85104c45..3a518cf585e 100644 --- a/src/sage/homology/cell_complex.py +++ b/src/sage/topology/cell_complex.py @@ -77,7 +77,7 @@ class :class:`~sage.homology.delta_complex.DeltaComplex` It's hard to give informative examples of the base class, since essentially nothing is implemented. :: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex() """ def __eq__(self,right): @@ -86,7 +86,7 @@ def __eq__(self,right): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex(); B = GenericCellComplex() sage: A == B # indirect doctest Traceback (most recent call last): @@ -101,7 +101,7 @@ def __ne__(self,right): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex(); B = GenericCellComplex() sage: A != B # indirect doctest Traceback (most recent call last): @@ -136,7 +136,7 @@ def cells(self, subcomplex=None): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex() sage: A.cells() Traceback (most recent call last): @@ -308,7 +308,7 @@ def product(self, right, rename_vertices=True): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex(); B = GenericCellComplex() sage: A.product(B) Traceback (most recent call last): @@ -327,7 +327,7 @@ def disjoint_union(self, right): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex(); B = GenericCellComplex() sage: A.disjoint_union(B) Traceback (most recent call last): @@ -347,7 +347,7 @@ def wedge(self, right): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex(); B = GenericCellComplex() sage: A.wedge(B) Traceback (most recent call last): @@ -372,7 +372,7 @@ def join(self, right): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex(); B = GenericCellComplex() sage: A.join(B) Traceback (most recent call last): @@ -439,7 +439,7 @@ def chain_complex(self, subcomplex=None, augmented=False, EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex() sage: A.chain_complex() Traceback (most recent call last): @@ -561,8 +561,8 @@ def homology(self, dim=None, base_ring=ZZ, subcomplex=None, {0: [], 1: 0, 2: [(Z, sigma_2)]} """ from sage.interfaces.chomp import have_chomp, homcubes, homsimpl - from sage.homology.cubical_complex import CubicalComplex - from sage.homology.simplicial_complex import SimplicialComplex + from sage.topology.cubical_complex import CubicalComplex + from sage.topology.simplicial_complex import SimplicialComplex from sage.modules.all import VectorSpace from sage.homology.homology_group import HomologyGroup @@ -845,7 +845,7 @@ def algebraic_topological_model(self, base_ring=QQ): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex() sage: A.algebraic_topological_model(QQ) Traceback (most recent call last): @@ -904,7 +904,7 @@ def homology_with_basis(self, base_ring=QQ, cohomology=False): sage: list(H.basis(3)) [h^{3,0}] """ - from .homology_vector_space_with_basis import HomologyVectorSpaceWithBasis + from sage.homology.homology_vector_space_with_basis import HomologyVectorSpaceWithBasis return HomologyVectorSpaceWithBasis(base_ring, self, cohomology) def cohomology_ring(self, base_ring=QQ): @@ -1008,7 +1008,7 @@ def cohomology_ring(self, base_ring=QQ): Cohomology ring of Simplicial complex with 9 vertices and 18 facets over Rational Field """ - from .homology_vector_space_with_basis import CohomologyRing + from sage.homology.homology_vector_space_with_basis import CohomologyRing return CohomologyRing(base_ring, self) @abstract_method @@ -1053,7 +1053,7 @@ def alexander_whitney(self, cell, dim_left): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex() sage: A.alexander_whitney(None, 2) Traceback (most recent call last): @@ -1111,7 +1111,7 @@ def graph(self): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex() sage: A.graph() Traceback (most recent call last): @@ -1164,7 +1164,7 @@ def n_skeleton(self, n): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: A = GenericCellComplex() sage: A.n_skeleton(3) Traceback (most recent call last): @@ -1189,7 +1189,7 @@ def _string_constants(self): EXAMPLES:: - sage: from sage.homology.cell_complex import GenericCellComplex + sage: from sage.topology.cell_complex import GenericCellComplex sage: GenericCellComplex()._string_constants() ('Cell', 'cell', 'cells') sage: delta_complexes.Sphere(0)._string_constants() diff --git a/src/sage/homology/cubical_complex.py b/src/sage/topology/cubical_complex.py similarity index 97% rename from src/sage/homology/cubical_complex.py rename to src/sage/topology/cubical_complex.py index a1a1e32cc72..2a0721bdd5e 100644 --- a/src/sage/homology/cubical_complex.py +++ b/src/sage/topology/cubical_complex.py @@ -67,7 +67,7 @@ """ from copy import copy -from sage.homology.cell_complex import GenericCellComplex +from .cell_complex import GenericCellComplex from sage.structure.sage_object import SageObject from sage.rings.integer import Integer from sage.sets.set import Set @@ -107,7 +107,7 @@ class Cube(SageObject): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]); C [1,2] x [5,5] x [6,7] x [-1,0] sage: C.dimension() # number of nondegenerate intervals @@ -129,7 +129,7 @@ def __init__(self, data): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]); C # indirect doctest [1,2] x [5,5] x [6,7] x [-1,0] sage: C == loads(dumps(C)) @@ -166,7 +166,7 @@ def tuple(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]) sage: C.tuple() ((1, 2), (5, 5), (6, 7), (-1, 0)) @@ -179,7 +179,7 @@ def is_face(self, other): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[1,2], [5,], [6,7], [-1, 0]]) sage: C2 = Cube([[1,2], [5,], [6,], [-1, 0]]) sage: C1.is_face(C2) @@ -216,7 +216,7 @@ def _translate(self, vec): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]) sage: C._translate((-12,)) [-11,-10] x [5,5] x [6,7] x [-1,0] @@ -241,7 +241,7 @@ def __getitem__(self, n): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]) sage: C[2] (6, 7) @@ -256,7 +256,7 @@ def __iter__(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]) sage: [x[0] for x in C] [1, 5, 6, -1] @@ -273,7 +273,7 @@ def __add__(self, other): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [3,]]) sage: D = Cube([[4], [0,1]]) sage: C.product(D) @@ -298,7 +298,7 @@ def nondegenerate_intervals(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]) sage: C.nondegenerate_intervals() [0, 2, 3] @@ -314,7 +314,7 @@ def dimension(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]) sage: C.dimension() 3 @@ -342,7 +342,7 @@ def face(self, n, upper=True): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [5,], [6,7], [-1, 0]]); C [1,2] x [5,5] x [6,7] x [-1,0] sage: C.face(0) @@ -374,7 +374,7 @@ def faces(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [3,4]]) sage: C.faces() [[2,2] x [3,4], [1,2] x [4,4], [1,1] x [3,4], [1,2] x [3,3]] @@ -390,7 +390,7 @@ def faces_as_pairs(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [3,4]]) sage: C.faces_as_pairs() [([2,2] x [3,4], [1,1] x [3,4]), ([1,2] x [4,4], [1,2] x [3,3])] @@ -443,7 +443,7 @@ def _compare_for_gluing(self, other): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[0,1], [3], [4], [6,7]]) sage: C2 = Cube([[2], [7,8], [9], [1,2], [0], [5]]) sage: C1._compare_for_gluing(C2) @@ -529,7 +529,7 @@ def _triangulation_(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C = Cube([[1,2], [3,4]]); C [1,2] x [3,4] sage: C._triangulation_() @@ -538,7 +538,7 @@ def _triangulation_(self): sage: len(C._triangulation_()) 6 """ - from sage.homology.simplicial_complex import Simplex + from .simplicial_complex import Simplex if self.dimension() < 0: # the empty cube return [Simplex(())] # the empty simplex v = tuple([max(j) for j in self.tuple()]) @@ -576,7 +576,7 @@ def alexander_whitney(self, dim): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[0,1], [3,4]]) sage: C1.alexander_whitney(0) [(1, [0,0] x [3,3], [0,1] x [3,4])] @@ -621,7 +621,7 @@ def __eq__(self, other): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[1,1], [2,3], [4,5]]) sage: C2 = Cube([[1], [2,3], [4,5]]) sage: C3 = Cube([[0], [2,3], [4,5]]) @@ -640,7 +640,7 @@ def __ne__(self, other): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[1,1], [2,3], [4,5]]) sage: C2 = Cube([[1], [2,3], [4,5]]) sage: C3 = Cube([[0], [2,3], [4,5]]) @@ -660,7 +660,7 @@ def __lt__(self, other): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[1,1], [2,3], [4,5]]) sage: C2 = Cube([[1], [2,3], [4,5]]) sage: C3 = Cube([[0], [2,3], [4,5]]) @@ -696,7 +696,7 @@ def __hash__(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[1,1], [2,3], [4,5]]) sage: hash(C1) == hash(((1,1),(2,3),(4,5))) True @@ -709,7 +709,7 @@ def _repr_(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[1,1], [2,3], [4,5]]) sage: C1 [1,1] x [2,3] x [4,5] @@ -725,7 +725,7 @@ def _latex_(self): EXAMPLES:: - sage: from sage.homology.cubical_complex import Cube + sage: from sage.topology.cubical_complex import Cube sage: C1 = Cube([[1,1], [2,3], [4,5]]) sage: latex(C1) [1,1] \times [2,3] \times [4,5] @@ -1664,7 +1664,7 @@ def algebraic_topological_model(self, base_ring=None): 1: Vector space of dimension 2 over Rational Field, 2: Vector space of dimension 1 over Rational Field} """ - from .algebraic_topological_model import algebraic_topological_model + from sage.homology.algebraic_topological_model import algebraic_topological_model if base_ring is None: base_ring = QQ return algebraic_topological_model(self, base_ring) @@ -1741,7 +1741,7 @@ def _simplicial_(self): sage: SimplicialComplex(S4) # calls S4._simplicial_() Simplicial complex with 32 vertices and 240 facets """ - from sage.homology.simplicial_complex import SimplicialComplex + from .simplicial_complex import SimplicialComplex simplices = [] for C in self.maximal_cells(): simplices.extend(C._triangulation_()) diff --git a/src/sage/homology/delta_complex.py b/src/sage/topology/delta_complex.py similarity index 99% rename from src/sage/homology/delta_complex.py rename to src/sage/topology/delta_complex.py index 91c3bca27f0..9e4b08262cc 100644 --- a/src/sage/homology/delta_complex.py +++ b/src/sage/topology/delta_complex.py @@ -50,13 +50,13 @@ """ from copy import copy -from sage.homology.cell_complex import GenericCellComplex +from sage.topology.cell_complex import GenericCellComplex from sage.homology.chains import Chains, Cochains from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.integer import Integer from sage.matrix.constructor import matrix -from sage.homology.simplicial_complex import Simplex, lattice_paths, SimplicialComplex +from .simplicial_complex import Simplex, lattice_paths, SimplicialComplex from sage.homology.chain_complex import ChainComplex from sage.graphs.graph import Graph from sage.arith.all import binomial @@ -1590,7 +1590,7 @@ def algebraic_topological_model(self, base_ring=None): 1: Vector space of dimension 2 over Rational Field, 2: Vector space of dimension 1 over Rational Field} """ - from .algebraic_topological_model import algebraic_topological_model_delta_complex + from sage.homology.algebraic_topological_model import algebraic_topological_model_delta_complex if base_ring is None: base_ring = QQ return algebraic_topological_model_delta_complex(self, base_ring) diff --git a/src/sage/homology/simplicial_complex.py b/src/sage/topology/simplicial_complex.py similarity index 99% rename from src/sage/homology/simplicial_complex.py rename to src/sage/topology/simplicial_complex.py index 8db479fd549..6908b925904 100644 --- a/src/sage/homology/simplicial_complex.py +++ b/src/sage/topology/simplicial_complex.py @@ -69,9 +69,9 @@ .. NOTE:: This class derives from - :class:`~sage.homology.cell_complex.GenericCellComplex`, and so + :class:`~sage.topology.cell_complex.GenericCellComplex`, and so inherits its methods. Some of those methods are not listed here; - see the :mod:`Generic Cell Complex ` + see the :mod:`Generic Cell Complex ` page instead. EXAMPLES:: @@ -161,7 +161,7 @@ from copy import copy from sage.misc.lazy_import import lazy_import from sage.misc.cachefunc import cached_method -from sage.homology.cell_complex import GenericCellComplex +from .cell_complex import GenericCellComplex from sage.structure.sage_object import SageObject from sage.structure.parent import Parent from sage.rings.integer import Integer @@ -211,7 +211,7 @@ def lattice_paths(t1, t2, length=None): EXAMPLES:: - sage: from sage.homology.simplicial_complex import lattice_paths + sage: from sage.topology.simplicial_complex import lattice_paths sage: lattice_paths([0,1,2], [0,1,2]) [[(0, 0), (0, 1), (0, 2), (1, 2), (2, 2)], [(0, 0), (0, 1), (1, 1), (1, 2), (2, 2)], @@ -308,7 +308,7 @@ def rename_vertex(n, keep, left=True): EXAMPLES:: - sage: from sage.homology.simplicial_complex import rename_vertex + sage: from sage.topology.simplicial_complex import rename_vertex sage: rename_vertex(6, [5, 6, 7]) 1 sage: rename_vertex(3, [5, 6, 7, 8, 9]) @@ -413,7 +413,7 @@ def tuple(self): sage: type(Simplex(3).tuple()) <... 'tuple'> sage: type(Simplex(3)) - + """ return self.__tuple @@ -2451,7 +2451,7 @@ def algebraic_topological_model(self, base_ring=None): 1: Vector space of dimension 2 over Rational Field, 2: Vector space of dimension 1 over Rational Field} """ - from .algebraic_topological_model import algebraic_topological_model + from sage.homology.algebraic_topological_model import algebraic_topological_model if base_ring is None: base_ring = QQ return algebraic_topological_model(self, base_ring) @@ -3913,7 +3913,7 @@ def _cubical_(self): sage: Tc.homology() {0: 0, 1: Z x Z, 2: Z} """ - from sage.homology.cubical_complex import CubicalComplex + from .cubical_complex import CubicalComplex V = self.vertices() embed = len(V) # dictionary to translate vertices to the numbers 1, ..., embed @@ -4309,7 +4309,7 @@ def _Hom_(self, other, category=None): """ if not category.is_subcategory(SimplicialComplexes()): raise TypeError("{} is not a subcategory of SimplicialComplexes()".format(category)) - from sage.homology.simplicial_complex_homset import SimplicialComplexHomset + from sage.topology.simplicial_complex_homset import SimplicialComplexHomset return SimplicialComplexHomset(self, other) # @cached_method when we switch to immutable SimplicialComplex @@ -4737,7 +4737,7 @@ def facets_for_RP4(): EXAMPLES:: - sage: from sage.homology.simplicial_complex import facets_for_RP4 + sage: from sage.topology.simplicial_complex import facets_for_RP4 sage: A = facets_for_RP4() # long time (1 or 2 seconds) sage: SimplicialComplex(A) == simplicial_complexes.RealProjectiveSpace(4) # long time True @@ -4775,7 +4775,7 @@ def facets_for_K3(): EXAMPLES:: - sage: from sage.homology.simplicial_complex import facets_for_K3 + sage: from sage.topology.simplicial_complex import facets_for_K3 sage: A = facets_for_K3() # long time (a few seconds) sage: SimplicialComplex(A) == simplicial_complexes.K3Surface() # long time True diff --git a/src/sage/homology/simplicial_complexes_catalog.py b/src/sage/topology/simplicial_complex_catalog.py similarity index 58% rename from src/sage/homology/simplicial_complexes_catalog.py rename to src/sage/topology/simplicial_complex_catalog.py index 955d5716703..01ade2e3667 100644 --- a/src/sage/homology/simplicial_complexes_catalog.py +++ b/src/sage/topology/simplicial_complex_catalog.py @@ -21,31 +21,31 @@ All of these examples are accessible by typing ``simplicial_complexes.NAME``, where ``NAME`` is the name of the example. -- :meth:`~sage.homology.examples.BarnetteSphere` -- :meth:`~sage.homology.examples.BrucknerGrunbaumSphere` -- :meth:`~sage.homology.examples.ChessboardComplex` -- :meth:`~sage.homology.examples.ComplexProjectivePlane` -- :meth:`~sage.homology.examples.DunceHat` -- :meth:`~sage.homology.examples.FareyMap` -- :meth:`~sage.homology.examples.K3Surface` -- :meth:`~sage.homology.examples.KleinBottle` -- :meth:`~sage.homology.examples.MatchingComplex` -- :meth:`~sage.homology.examples.MooreSpace` -- :meth:`~sage.homology.examples.NotIConnectedGraphs` -- :meth:`~sage.homology.examples.PoincareHomologyThreeSphere` -- :meth:`~sage.homology.examples.PseudoQuaternionicProjectivePlane` -- :meth:`~sage.homology.examples.RandomComplex` -- :meth:`~sage.homology.examples.RandomTwoSphere` -- :meth:`~sage.homology.examples.RealProjectivePlane` -- :meth:`~sage.homology.examples.RealProjectiveSpace` -- :meth:`~sage.homology.examples.RudinBall` -- :meth:`~sage.homology.examples.ShiftedComplex` -- :meth:`~sage.homology.examples.Simplex` -- :meth:`~sage.homology.examples.Sphere` -- :meth:`~sage.homology.examples.SumComplex` -- :meth:`~sage.homology.examples.SurfaceOfGenus` -- :meth:`~sage.homology.examples.Torus` -- :meth:`~sage.homology.examples.ZieglerBall` +- :meth:`~sage.topology.examples.BarnetteSphere` +- :meth:`~sage.topology.examples.BrucknerGrunbaumSphere` +- :meth:`~sage.topology.examples.ChessboardComplex` +- :meth:`~sage.topology.examples.ComplexProjectivePlane` +- :meth:`~sage.topology.examples.DunceHat` +- :meth:`~sage.topology.examples.FareyMap` +- :meth:`~sage.topology.examples.K3Surface` +- :meth:`~sage.topology.examples.KleinBottle` +- :meth:`~sage.topology.examples.MatchingComplex` +- :meth:`~sage.topology.examples.MooreSpace` +- :meth:`~sage.topology.examples.NotIConnectedGraphs` +- :meth:`~sage.topology.examples.PoincareHomologyThreeSphere` +- :meth:`~sage.topology.examples.PseudoQuaternionicProjectivePlane` +- :meth:`~sage.topology.examples.RandomComplex` +- :meth:`~sage.topology.examples.RandomTwoSphere` +- :meth:`~sage.topology.examples.RealProjectivePlane` +- :meth:`~sage.topology.examples.RealProjectiveSpace` +- :meth:`~sage.topology.examples.RudinBall` +- :meth:`~sage.topology.examples.ShiftedComplex` +- :meth:`~sage.topology.examples.Simplex` +- :meth:`~sage.topology.examples.Sphere` +- :meth:`~sage.topology.examples.SumComplex` +- :meth:`~sage.topology.examples.SurfaceOfGenus` +- :meth:`~sage.topology.examples.Torus` +- :meth:`~sage.topology.examples.ZieglerBall` You can also get a list by typing ``simplicial_complexes.`` and hitting the TAB key. @@ -64,7 +64,8 @@ {0: 0, 1: Z^16, 2: 0} """ -from sage.homology.examples import (Sphere, Simplex, Torus, ProjectivePlane, +from sage.topology.simplicial_complex_examples import (Sphere, Simplex, Torus, + ProjectivePlane, RealProjectivePlane, KleinBottle, FareyMap, SurfaceOfGenus, MooreSpace, ComplexProjectivePlane, PseudoQuaternionicProjectivePlane, diff --git a/src/sage/homology/examples.py b/src/sage/topology/simplicial_complex_examples.py similarity index 98% rename from src/sage/homology/examples.py rename to src/sage/topology/simplicial_complex_examples.py index 9f542ce8fe3..5b49a8de836 100644 --- a/src/sage/homology/examples.py +++ b/src/sage/topology/simplicial_complex_examples.py @@ -64,12 +64,12 @@ {0: 0, 1: Z^16, 2: 0} """ -from sage.homology.simplicial_complex import SimplicialComplex +from .simplicial_complex import SimplicialComplex from sage.structure.unique_representation import UniqueRepresentation # Below we define a function Simplex to construct a simplex as a # simplicial complex. We also need to use actual simplices as # simplices, hence: -from sage.homology.simplicial_complex import Simplex as TrueSimplex +from .simplicial_complex import Simplex as TrueSimplex from sage.sets.set import Set from sage.misc.functional import is_even from sage.combinat.subset import Subsets @@ -96,7 +96,7 @@ def facets_for_RP4(): EXAMPLES:: - sage: from sage.homology.examples import facets_for_RP4 + sage: from sage.topology.simplicial_complex_examples import facets_for_RP4 sage: A = facets_for_RP4() # long time (1 or 2 seconds) sage: SimplicialComplex(A) == simplicial_complexes.RealProjectiveSpace(4) # long time True @@ -134,7 +134,7 @@ def facets_for_K3(): EXAMPLES:: - sage: from sage.homology.examples import facets_for_K3 + sage: from sage.topology.simplicial_complex_examples import facets_for_K3 sage: A = facets_for_K3() # long time (a few seconds) sage: SimplicialComplex(A) == simplicial_complexes.K3Surface() # long time True @@ -160,7 +160,7 @@ def matching(A, B): EXAMPLES:: - sage: from sage.homology.examples import matching + sage: from sage.topology.simplicial_complex_examples import matching sage: matching([1, 2], [3, 4]) [{(1, 3), (2, 4)}, {(1, 4), (2, 3)}] sage: matching([0, 2], [0]) @@ -195,7 +195,7 @@ class UniqueSimplicialComplex(SimplicialComplex, UniqueRepresentation): EXAMPLES:: - sage: from sage.homology.examples import UniqueSimplicialComplex + sage: from sage.topology.simplicial_complex_examples import UniqueSimplicialComplex sage: SimplicialComplex([[0, 1]]) is SimplicialComplex([[0, 1]]) False sage: UniqueSimplicialComplex([[0, 1]]) is UniqueSimplicialComplex([[0, 1]]) @@ -210,7 +210,7 @@ def __classcall__(self, maximal_faces=None, name=None, **kwds): """ TESTS:: - sage: from sage.homology.examples import UniqueSimplicialComplex + sage: from sage.topology.simplicial_complex_examples import UniqueSimplicialComplex sage: UniqueSimplicialComplex([[1, 2, 3], [0, 1, 3]]) is UniqueSimplicialComplex([(1, 2, 3), (0, 1, 3)]) True sage: X = UniqueSimplicialComplex([[1, 2, 3], [0, 1, 3]]) @@ -250,7 +250,7 @@ def __init__(self, maximal_faces=None, name=None, **kwds): """ TESTS:: - sage: from sage.homology.examples import UniqueSimplicialComplex + sage: from sage.topology.simplicial_complex_examples import UniqueSimplicialComplex sage: UniqueSimplicialComplex([[1, 2, 3], [0, 1, 3]], is_mutable=True).is_mutable() False """ @@ -271,7 +271,7 @@ def _repr_(self): TESTS:: - sage: from sage.homology.examples import UniqueSimplicialComplex + sage: from sage.topology.simplicial_complex_examples import UniqueSimplicialComplex sage: UniqueSimplicialComplex([[0, 1]]) Simplicial complex with vertex set (0, 1) and facets {(0, 1)} sage: UniqueSimplicialComplex([[0, 1]], name='Joe') diff --git a/src/sage/homology/simplicial_complex_homset.py b/src/sage/topology/simplicial_complex_homset.py similarity index 96% rename from src/sage/homology/simplicial_complex_homset.py rename to src/sage/topology/simplicial_complex_homset.py index df181a80938..f97db05461d 100644 --- a/src/sage/homology/simplicial_complex_homset.py +++ b/src/sage/topology/simplicial_complex_homset.py @@ -26,7 +26,7 @@ False sage: x.image() Simplicial complex with vertex set (0, 1, 3) and facets {(0, 1), (0, 3), (1, 3)} - sage: from sage.homology.simplicial_complex import Simplex + sage: from sage.topology.simplicial_complex import Simplex sage: s = Simplex([1,2]) sage: x(s) (1, 3) @@ -58,7 +58,7 @@ #***************************************************************************** import sage.categories.homset -from sage.homology.simplicial_complex_morphism import SimplicialComplexMorphism +from .simplicial_complex_morphism import SimplicialComplexMorphism def is_SimplicialComplexHomset(x): """ @@ -73,7 +73,7 @@ def is_SimplicialComplexHomset(x): Set of Morphisms from Simplicial complex with vertex set () and facets {()} to Simplicial complex with vertex set () and facets {()} in Category of finite simplicial complexes - sage: from sage.homology.simplicial_complex_homset import is_SimplicialComplexHomset + sage: from sage.topology.simplicial_complex_homset import is_SimplicialComplexHomset sage: is_SimplicialComplexHomset(H) True """ diff --git a/src/sage/homology/simplicial_complex_morphism.py b/src/sage/topology/simplicial_complex_morphism.py similarity index 99% rename from src/sage/homology/simplicial_complex_morphism.py rename to src/sage/topology/simplicial_complex_morphism.py index 7d1cd628aef..272cae39f14 100644 --- a/src/sage/homology/simplicial_complex_morphism.py +++ b/src/sage/topology/simplicial_complex_morphism.py @@ -102,7 +102,7 @@ # #***************************************************************************** -from sage.homology.simplicial_complex import Simplex, SimplicialComplex +from .simplicial_complex import Simplex, SimplicialComplex from sage.matrix.constructor import matrix, zero_matrix from sage.rings.integer_ring import ZZ from sage.homology.chain_complex_morphism import ChainComplexMorphism @@ -119,7 +119,7 @@ def is_SimplicialComplexMorphism(x): EXAMPLES:: - sage: from sage.homology.simplicial_complex_morphism import is_SimplicialComplexMorphism + sage: from sage.topology.simplicial_complex_morphism import is_SimplicialComplexMorphism sage: S = SimplicialComplex([[0,1],[3,4]], is_mutable=False) sage: H = Hom(S,S) sage: f = {0:0,1:1,3:3,4:4} @@ -231,7 +231,7 @@ def __call__(self,x,orientation=False): sage: f = {0:0,1:1,2:2,3:3} sage: H = Hom(S,T) sage: x = H(f) - sage: from sage.homology.simplicial_complex import Simplex + sage: from sage.topology.simplicial_complex import Simplex sage: x(Simplex([0,2,3])) (0, 2, 3) @@ -733,7 +733,7 @@ def induced_homology_morphism(self, base_ring=None, cohomology=False): True sage: h = Hom(S,S2)({0: 0, 1:1, 2:2}).induced_homology_morphism() """ - from .homology_morphism import InducedHomologyMorphism + from sage.homology.homology_morphism import InducedHomologyMorphism return InducedHomologyMorphism(self, base_ring, cohomology) def is_contiguous_to(self, other): diff --git a/src/sage/homology/simplicial_set.py b/src/sage/topology/simplicial_set.py similarity index 97% rename from src/sage/homology/simplicial_set.py rename to src/sage/topology/simplicial_set.py index 7f6403299cb..4b58d63684c 100644 --- a/src/sage/homology/simplicial_set.py +++ b/src/sage/topology/simplicial_set.py @@ -221,7 +221,7 @@ constructed "by hand": first define some simplices, then define a simplicial set by specifying their faces:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') @@ -263,11 +263,11 @@ from sage.rings.rational_field import QQ from sage.structure.parent import Parent from sage.structure.sage_object import SageObject +from sage.homology.algebraic_topological_model import algebraic_topological_model_delta_complex +from sage.homology.chain_complex import ChainComplex +from sage.homology.chains import Chains, Cochains -from .algebraic_topological_model import algebraic_topological_model_delta_complex from .cell_complex import GenericCellComplex -from .chain_complex import ChainComplex -from .chains import Chains, Cochains from .delta_complex import DeltaComplex from .simplicial_complex import SimplicialComplex @@ -312,7 +312,7 @@ def __init__(self, dim, degeneracies=(), underlying=None, name=None, TESTS:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: AbstractSimplex(3, (1,2)) s_3 s_1 Delta^3 sage: AbstractSimplex(3, None) @@ -342,7 +342,7 @@ def __init__(self, dim, degeneracies=(), underlying=None, name=None, Distinct non-degenerate simplices should never be equal, even if they have the same starting data:: - sage: from sage.homology.simplicial_set import AbstractSimplex_class + sage: from sage.topology.simplicial_set import AbstractSimplex_class sage: AbstractSimplex_class(3) == AbstractSimplex_class(3) False sage: AbstractSimplex(3) == AbstractSimplex(3) @@ -407,7 +407,7 @@ def __hash__(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: hash(v) == hash(w) @@ -431,7 +431,7 @@ def __eq__(self, other): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: v == w @@ -457,7 +457,7 @@ def __ne__(self, other): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: v != w @@ -483,7 +483,7 @@ def __lt__(self, other): TESTS:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) @@ -585,7 +585,7 @@ def __gt__(self, other): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: e = AbstractSimplex(1, (1,0), name='e') sage: f = AbstractSimplex(1, (2,1), name='f') sage: e > f @@ -599,7 +599,7 @@ def __le__(self, other): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: e = AbstractSimplex(1, (1,0), name='e') sage: f = AbstractSimplex(1, (2,1), name='f') sage: e <= f @@ -613,7 +613,7 @@ def __ge__(self, other): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: e = AbstractSimplex(1, (1,0), name='e') sage: f = AbstractSimplex(1, (2,1), name='f') sage: e >= f @@ -629,7 +629,7 @@ def nondegenerate(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0, name='v') sage: sigma = v.apply_degeneracies(1, 0) sage: sigma.nondegenerate() @@ -656,7 +656,7 @@ def degeneracies(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: AbstractSimplex(4, (0,0,0)).degeneracies() [2, 1, 0] sage: AbstractSimplex(4, None).degeneracies() @@ -670,7 +670,7 @@ def is_degenerate(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: AbstractSimplex(3, (2,1)).is_degenerate() True sage: AbstractSimplex(3, None).is_degenerate() @@ -684,7 +684,7 @@ def is_nondegenerate(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: AbstractSimplex(3, (2,1)).is_nondegenerate() False sage: AbstractSimplex(3, None).is_nondegenerate() @@ -700,7 +700,7 @@ def dimension(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: AbstractSimplex(3, (2,1)).dimension() 5 sage: AbstractSimplex(3, None).dimension() @@ -720,7 +720,7 @@ def apply_degeneracies(self, *args): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0) sage: e = v.apply_degeneracies(0) sage: e.nondegenerate() == v @@ -766,7 +766,7 @@ def __copy__(self): TESTS:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0) sage: copy(v) == v False @@ -809,7 +809,7 @@ def __deepcopy__(self, memo): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: import copy sage: v = AbstractSimplex(0) sage: copy.deepcopy(v) == v @@ -820,7 +820,7 @@ def __deepcopy__(self, memo): The purpose for this method is to be able to make distinct copies of simplicial sets:: - sage: from sage.homology.simplicial_set import SimplicialSet + sage: from sage.topology.simplicial_set import SimplicialSet sage: RP3 = simplicial_sets.RealProjectiveSpace(3) sage: dict(copy.copy(RP3._data)) == dict(RP3._data) True @@ -848,7 +848,7 @@ def _repr_(self): TESTS:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: AbstractSimplex(3, None) Delta^3 sage: AbstractSimplex(3, (0,)) @@ -878,7 +878,7 @@ def _latex_(self): TESTS:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: latex(AbstractSimplex(18, None)) \Delta^{18} sage: latex(AbstractSimplex(3, (0, 0,))) @@ -929,12 +929,12 @@ def __init__(self, dim, name=None, latex_name=None): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: v = AbstractSimplex(0, name='v') sage: v v sage: type(v) - + Distinct non-degenerate simplices should never be equal, even if they have the same starting data. :: @@ -944,7 +944,7 @@ def __init__(self, dim, name=None, latex_name=None): sage: AbstractSimplex(3) == AbstractSimplex(3) False - sage: from sage.homology.simplicial_set import NonDegenerateSimplex + sage: from sage.topology.simplicial_set import NonDegenerateSimplex sage: x = NonDegenerateSimplex(0, name='x') sage: x == NonDegenerateSimplex(0, name='x') False @@ -992,7 +992,7 @@ def AbstractSimplex(dim, degeneracies=(), underlying=None, EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: AbstractSimplex(3, (3, 1)) s_3 s_1 Delta^3 sage: AbstractSimplex(3, None) @@ -1168,7 +1168,7 @@ def faces(self, simplex): sage: S2.faces(v_0) is None True - sage: from sage.homology.simplicial_set import AbstractSimplex + sage: from sage.topology.simplicial_set import AbstractSimplex sage: w = AbstractSimplex(0) sage: S2.faces(w) Traceback (most recent call last): @@ -1236,7 +1236,7 @@ def __contains__(self, x): sage: v0 in S1 False - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: e = AbstractSimplex(1) sage: K = SimplicialSet({e: (v, v)}) # the circle @@ -1331,7 +1331,7 @@ def nondegenerate_simplices(self, max_dim=None): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: S0 = SimplicialSet({v: None, w: None}) @@ -1398,7 +1398,7 @@ def cells(self, subcomplex=None, max_dim=None): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: S0 = SimplicialSet({v: None, w: None}) @@ -1500,7 +1500,7 @@ def _an_element_(self): v_0 sage: S4._an_element_() in S4 True - sage: from sage.homology.simplicial_set_examples import Empty + sage: from sage.topology.simplicial_set_examples import Empty sage: Empty()._an_element_() is None True """ @@ -1515,7 +1515,7 @@ def all_n_simplices(self, n): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: degen = v.apply_degeneracies(0) @@ -1566,7 +1566,7 @@ def _map_from_empty_set(self): To: Torus Defn: [] --> [] """ - from sage.homology.simplicial_set_examples import Empty + from sage.topology.simplicial_set_examples import Empty return Empty().Hom(self)({}) def identity(self): @@ -1635,7 +1635,7 @@ def constant_map(self, codomain=None, point=None): ... ValueError: codomain is not pointed, so specify a target for the constant map """ - from sage.homology.simplicial_set_examples import Point + from sage.topology.simplicial_set_examples import Point if codomain is None: codomain = Point() return self.Hom(codomain).constant_map(point) @@ -1749,7 +1749,7 @@ def subsimplicial_set(self, simplices): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') @@ -2199,7 +2199,7 @@ def quotient(self, subcomplex, vertex_name='*'): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') @@ -2309,7 +2309,7 @@ def disjoint_union(self, *others): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') @@ -2341,7 +2341,7 @@ def disjoint_union(self, *others): Empty factors are ignored:: - sage: from sage.homology.simplicial_set_examples import Empty + sage: from sage.topology.simplicial_set_examples import Empty sage: E = Empty() sage: K = S2.disjoint_union(S2, E, E, S2) sage: K == S2.disjoint_union(S2, S2) @@ -2438,7 +2438,7 @@ def product(self, *others): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') @@ -2746,7 +2746,7 @@ def wedge(self, *others): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: e = AbstractSimplex(1, name='e') sage: w = AbstractSimplex(0, name='w') @@ -2822,7 +2822,7 @@ def cone(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: e = AbstractSimplex(1, name='e') sage: X = SimplicialSet({e: (v, v)}) @@ -3012,7 +3012,7 @@ def _Hom_(self, other, category=None): Set of Morphisms from S^3 to 4-simplex in Category of finite simplicial sets """ # Import this here to prevent circular imports. - from sage.homology.simplicial_set_morphism import SimplicialSetHomset + from sage.topology.simplicial_set_morphism import SimplicialSetHomset # Error-checking on the ``category`` argument is done when # calling Hom(X,Y), so no need to do it again here. if category is None: @@ -3039,7 +3039,7 @@ def rename_latex(self, s): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: X = SimplicialSet({v: None}, latex_name='*') sage: latex(X) @@ -3060,7 +3060,7 @@ def _latex_(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: X = SimplicialSet({v: None}, latex_name='*') sage: latex(X) @@ -3085,7 +3085,7 @@ def _repr_(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: degen = v.apply_degeneracies(0) @@ -3158,7 +3158,7 @@ class SimplicialSet_finite(SimplicialSet_arbitrary, GenericCellComplex): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: u = AbstractSimplex(0, name='u') sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') @@ -3182,7 +3182,7 @@ def __init__(self, data, base_point=None, name=None, check=True, r""" TESTS:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: e = AbstractSimplex(1) sage: SimplicialSet({e: (v, v, v)}) @@ -3364,7 +3364,7 @@ def __eq__(self, other): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: e = AbstractSimplex(1) @@ -3393,7 +3393,7 @@ def __ne__(self, other): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: X = SimplicialSet({v: None}) @@ -3419,7 +3419,7 @@ def __hash__(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: X = SimplicialSet({v: None}) sage: degen = v.apply_degeneracies(0) @@ -3460,7 +3460,7 @@ def face_data(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') @@ -3489,7 +3489,7 @@ def n_skeleton(self, n): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: degen = v.apply_degeneracies(0) @@ -3545,7 +3545,7 @@ def f_vector(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: w = AbstractSimplex(0) sage: S0 = SimplicialSet({v: None, w: None}) @@ -3617,7 +3617,7 @@ def chain_complex(self, dimensions=None, base_ring=ZZ, augmented=False, EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0) sage: degen = v.apply_degeneracies(1, 0) # s_1 s_0 applied to v sage: sigma = AbstractSimplex(3) @@ -3830,7 +3830,7 @@ def standardize_degeneracies(*L): EXAMPLES:: - sage: from sage.homology.simplicial_set import standardize_degeneracies + sage: from sage.topology.simplicial_set import standardize_degeneracies sage: standardize_degeneracies(0, 0) (1, 0) sage: standardize_degeneracies(0, 0, 0, 0) @@ -3895,7 +3895,7 @@ def all_degeneracies(n, l=1): EXAMPLES:: - sage: from sage.homology.simplicial_set import all_degeneracies + sage: from sage.topology.simplicial_set import all_degeneracies sage: all_degeneracies(0, 3) {(2, 1, 0)} sage: all_degeneracies(1, 1) @@ -3934,7 +3934,7 @@ def standardize_face_maps(*L): EXAMPLES:: - sage: from sage.homology.simplicial_set import standardize_face_maps + sage: from sage.topology.simplicial_set import standardize_face_maps sage: standardize_face_maps(0, 1) (0, 0) sage: standardize_face_maps(0, 2) @@ -3988,7 +3988,7 @@ def face_degeneracies(m, I): EXAMPLES:: - sage: from sage.homology.simplicial_set import face_degeneracies + sage: from sage.topology.simplicial_set import face_degeneracies sage: face_degeneracies(0, (1, 0)) ([0], None) sage: face_degeneracies(1, (1, 0)) @@ -4031,7 +4031,7 @@ def shrink_simplicial_complex(K): EXAMPLES:: - sage: from sage.homology.simplicial_set import shrink_simplicial_complex + sage: from sage.topology.simplicial_set import shrink_simplicial_complex sage: K = simplicial_complexes.Simplex(3) sage: X = shrink_simplicial_complex(K) sage: X.f_vector() diff --git a/src/sage/homology/simplicial_set_catalog.py b/src/sage/topology/simplicial_set_catalog.py similarity index 100% rename from src/sage/homology/simplicial_set_catalog.py rename to src/sage/topology/simplicial_set_catalog.py diff --git a/src/sage/homology/simplicial_set_constructions.py b/src/sage/topology/simplicial_set_constructions.py similarity index 98% rename from src/sage/homology/simplicial_set_constructions.py rename to src/sage/topology/simplicial_set_constructions.py index 2d7be9d16ea..ab59a2c370d 100644 --- a/src/sage/homology/simplicial_set_constructions.py +++ b/src/sage/topology/simplicial_set_constructions.py @@ -23,7 +23,7 @@ sage: eta = simplicial_sets.HopfMap() sage: CP2 = eta.mapping_cone() sage: type(CP2) - + See the main documentation for simplicial sets, as well as for the classes for pushouts, pullbacks, etc., for more details. @@ -106,7 +106,7 @@ def __classcall__(self, data, ambient=None): TESTS:: - sage: from sage.homology.simplicial_set_constructions import SubSimplicialSet + sage: from sage.topology.simplicial_set_constructions import SubSimplicialSet sage: K = simplicial_sets.Simplex(2) sage: e = K.n_cells(1)[0] sage: A = SubSimplicialSet({e: K.faces(e)}, ambient=K) @@ -232,7 +232,7 @@ def __classcall_private__(self, maps=None): """ TESTS:: - sage: from sage.homology.simplicial_set_constructions import PullbackOfSimplicialSets + sage: from sage.topology.simplicial_set_constructions import PullbackOfSimplicialSets sage: S2 = simplicial_sets.Sphere(2) sage: one = S2.Hom(S2).identity() sage: PullbackOfSimplicialSets([one, one]) == PullbackOfSimplicialSets((one, one)) @@ -290,7 +290,7 @@ def __init__(self, maps=None): [(v_0, v_0), (sigma_2, sigma_2)] """ # Import this here to prevent circular imports. - from sage.homology.simplicial_set_morphism import SimplicialSetMorphism + from sage.topology.simplicial_set_morphism import SimplicialSetMorphism if maps and any(not isinstance(f, SimplicialSetMorphism) for f in maps): raise ValueError('the maps must be morphisms of simplicial sets') @@ -416,7 +416,7 @@ def __classcall_private__(self, maps=None): """ TESTS:: - sage: from sage.homology.simplicial_set_constructions import PullbackOfSimplicialSets_finite + sage: from sage.topology.simplicial_set_constructions import PullbackOfSimplicialSets_finite sage: S2 = simplicial_sets.Sphere(2) sage: one = S2.Hom(S2).identity() sage: PullbackOfSimplicialSets_finite([one, one]) == PullbackOfSimplicialSets_finite((one, one)) @@ -462,7 +462,7 @@ def __init__(self, maps=None): Defn: Identity map """ # Import this here to prevent circular imports. - from sage.homology.simplicial_set_morphism import SimplicialSetMorphism + from sage.topology.simplicial_set_morphism import SimplicialSetMorphism if maps and any(not isinstance(f, SimplicialSetMorphism) for f in maps): raise ValueError('the maps must be morphisms of simplicial sets') if not maps: @@ -757,7 +757,7 @@ def __classcall__(cls, factors=None): """ TESTS:: - sage: from sage.homology.simplicial_set_constructions import ProductOfSimplicialSets + sage: from sage.topology.simplicial_set_constructions import ProductOfSimplicialSets sage: S2 = simplicial_sets.Sphere(2) sage: ProductOfSimplicialSets([S2, S2]) == ProductOfSimplicialSets((S2, S2)) True @@ -800,7 +800,7 @@ def __init__(self, factors=None): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') @@ -1004,7 +1004,7 @@ def __init__(self, factors=None): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: e = AbstractSimplex(1) sage: X = SimplicialSet({e: (v, v)}) @@ -1059,7 +1059,7 @@ def wedge_as_subset(self): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: e = AbstractSimplex(1, name='e') sage: w = AbstractSimplex(0, name='w') @@ -1122,7 +1122,7 @@ def __classcall_private__(cls, maps=None, vertex_name=None): """ TESTS:: - sage: from sage.homology.simplicial_set_constructions import PushoutOfSimplicialSets + sage: from sage.topology.simplicial_set_constructions import PushoutOfSimplicialSets sage: S2 = simplicial_sets.Sphere(2) sage: one = S2.Hom(S2).identity() sage: PushoutOfSimplicialSets([one, one]) == PushoutOfSimplicialSets((one, one)) @@ -1171,7 +1171,7 @@ def __init__(self, maps=None, vertex_name=None): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: a = AbstractSimplex(0, name='a') sage: b = AbstractSimplex(0, name='b') @@ -1249,7 +1249,7 @@ def __init__(self, maps=None, vertex_name=None): Z x Z x Z """ # Import this here to prevent circular imports. - from sage.homology.simplicial_set_morphism import SimplicialSetMorphism + from sage.topology.simplicial_set_morphism import SimplicialSetMorphism if maps and any(not isinstance(f, SimplicialSetMorphism) for f in maps): raise ValueError('the maps must be morphisms of simplicial sets') Cat = SimplicialSets() @@ -1385,7 +1385,7 @@ def __classcall_private__(cls, maps=None, vertex_name=None): """ TESTS:: - sage: from sage.homology.simplicial_set_constructions import PushoutOfSimplicialSets_finite + sage: from sage.topology.simplicial_set_constructions import PushoutOfSimplicialSets_finite sage: S2 = simplicial_sets.Sphere(2) sage: one = S2.Hom(S2).identity() sage: PushoutOfSimplicialSets_finite([one, one]) == PushoutOfSimplicialSets_finite((one, one)) @@ -1409,7 +1409,7 @@ def __init__(self, maps=None, vertex_name=None): EXAMPLES:: - sage: from sage.homology.simplicial_set_constructions import PushoutOfSimplicialSets_finite + sage: from sage.topology.simplicial_set_constructions import PushoutOfSimplicialSets_finite sage: T = simplicial_sets.Torus() sage: S2 = simplicial_sets.Sphere(2) sage: PushoutOfSimplicialSets_finite([T.base_point_map(), S2.base_point_map()]).n_cells(0)[0] @@ -1418,7 +1418,7 @@ def __init__(self, maps=None, vertex_name=None): v """ # Import this here to prevent circular imports. - from sage.homology.simplicial_set_morphism import SimplicialSetMorphism + from sage.topology.simplicial_set_morphism import SimplicialSetMorphism if maps and any(not isinstance(f, SimplicialSetMorphism) for f in maps): raise ValueError('the maps must be morphisms of simplicial sets') if not maps: @@ -1612,7 +1612,7 @@ def universal_property(self, *maps): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: x = AbstractSimplex(0, name='x') @@ -1882,7 +1882,7 @@ def __classcall__(cls, factors=None): """ TESTS:: - sage: from sage.homology.simplicial_set_constructions import SmashProductOfSimplicialSets_finite as Smash + sage: from sage.topology.simplicial_set_constructions import SmashProductOfSimplicialSets_finite as Smash sage: S2 = simplicial_sets.Sphere(2) sage: Smash([S2, S2]) == Smash((S2, S2)) True @@ -1958,7 +1958,7 @@ def __classcall__(cls, factors=None): """ TESTS:: - sage: from sage.homology.simplicial_set_constructions import WedgeOfSimplicialSets + sage: from sage.topology.simplicial_set_constructions import WedgeOfSimplicialSets sage: S2 = simplicial_sets.Sphere(2) sage: WedgeOfSimplicialSets([S2, S2]) == WedgeOfSimplicialSets((S2, S2)) True @@ -2075,7 +2075,7 @@ def __init__(self, factors=None): EXAMPLES:: - sage: from sage.homology.simplicial_set_constructions import WedgeOfSimplicialSets_finite + sage: from sage.topology.simplicial_set_constructions import WedgeOfSimplicialSets_finite sage: K = simplicial_sets.Simplex(3) sage: WedgeOfSimplicialSets_finite((K,K)) Traceback (most recent call last): @@ -2147,8 +2147,8 @@ def __classcall__(cls, factors=None): """ TESTS:: - sage: from sage.homology.simplicial_set_constructions import DisjointUnionOfSimplicialSets - sage: from sage.homology.simplicial_set_examples import Empty + sage: from sage.topology.simplicial_set_constructions import DisjointUnionOfSimplicialSets + sage: from sage.topology.simplicial_set_examples import Empty sage: S2 = simplicial_sets.Sphere(2) sage: DisjointUnionOfSimplicialSets([S2, S2]) == DisjointUnionOfSimplicialSets((S2, S2)) True @@ -2283,8 +2283,8 @@ def __init__(self, factors=None): EXAMPLES:: - sage: from sage.homology.simplicial_set_constructions import DisjointUnionOfSimplicialSets_finite - sage: from sage.homology.simplicial_set_examples import Empty + sage: from sage.topology.simplicial_set_constructions import DisjointUnionOfSimplicialSets_finite + sage: from sage.topology.simplicial_set_examples import Empty sage: S = simplicial_sets.Sphere(4) sage: DisjointUnionOfSimplicialSets_finite((S,S,S)) Disjoint union: (S^4 u S^4 u S^4) @@ -2336,7 +2336,7 @@ def __init__(self, base): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: e = AbstractSimplex(1, name='e') sage: X = SimplicialSet({e: (v, v)}) @@ -2429,7 +2429,7 @@ def __init__(self, base): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: e = AbstractSimplex(1, name='e') sage: X = SimplicialSet({e: (v, v)}) @@ -2525,7 +2525,7 @@ def __init__(self, base): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: e = AbstractSimplex(1, name='e') sage: X = SimplicialSet({e: (v, v)}) @@ -2622,7 +2622,7 @@ def __init__(self, base): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: e = AbstractSimplex(1, name='e') sage: X = SimplicialSet({e: (v, v)}) diff --git a/src/sage/homology/simplicial_set_examples.py b/src/sage/topology/simplicial_set_examples.py similarity index 99% rename from src/sage/homology/simplicial_set_examples.py rename to src/sage/topology/simplicial_set_examples.py index 9374ba03cae..32d392e5294 100644 --- a/src/sage/homology/simplicial_set_examples.py +++ b/src/sage/topology/simplicial_set_examples.py @@ -43,7 +43,7 @@ from .simplicial_set import AbstractSimplex, \ SimplicialSet_arbitrary, SimplicialSet_finite -import sage.homology.simplicial_complexes_catalog as simplicial_complexes +import sage.topology.simplicial_complex_catalog as simplicial_complexes from sage.misc.lazy_import import lazy_import lazy_import('sage.categories.simplicial_sets', 'SimplicialSets') @@ -447,7 +447,7 @@ def Empty(): EXAMPLES:: - sage: from sage.homology.simplicial_set_examples import Empty + sage: from sage.topology.simplicial_set_examples import Empty sage: E = Empty() sage: E Empty simplicial set @@ -639,8 +639,8 @@ def simplicial_data_from_kenzo_output(filename): EXAMPLES:: - sage: from sage.homology.simplicial_set_examples import simplicial_data_from_kenzo_output - sage: from sage.homology.simplicial_set import SimplicialSet + sage: from sage.topology.simplicial_set_examples import simplicial_data_from_kenzo_output + sage: from sage.topology.simplicial_set import SimplicialSet sage: sphere = os.path.join(SAGE_ENV['SAGE_EXTCODE'], 'kenzo', 'S4.txt') sage: S4 = SimplicialSet(simplicial_data_from_kenzo_output(sphere)) sage: S4.homology(reduced=False) diff --git a/src/sage/homology/simplicial_set_morphism.py b/src/sage/topology/simplicial_set_morphism.py similarity index 99% rename from src/sage/homology/simplicial_set_morphism.py rename to src/sage/topology/simplicial_set_morphism.py index 7e2c5046ef9..a320703001c 100644 --- a/src/sage/homology/simplicial_set_morphism.py +++ b/src/sage/topology/simplicial_set_morphism.py @@ -39,8 +39,8 @@ from sage.misc.latex import latex from sage.rings.integer_ring import ZZ -from .chain_complex_morphism import ChainComplexMorphism -from .homology_morphism import InducedHomologyMorphism +from sage.homology.chain_complex_morphism import ChainComplexMorphism +from sage.homology.homology_morphism import InducedHomologyMorphism from .simplicial_set import SimplicialSet_arbitrary class SimplicialSetHomset(Homset): @@ -55,7 +55,7 @@ class SimplicialSetHomset(Homset): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') @@ -346,7 +346,7 @@ def __init__(self, data=None, domain=None, codomain=None, EXAMPLES:: - sage: from sage.homology.simplicial_set_morphism import SimplicialSetMorphism + sage: from sage.topology.simplicial_set_morphism import SimplicialSetMorphism sage: K = simplicial_sets.Simplex(1) sage: S1 = simplicial_sets.Sphere(1) sage: v0 = K.n_cells(0)[0] @@ -908,7 +908,7 @@ def pushout(self, *others): which must all equal that of ``self``. This returns the pushout as a simplicial set. See - :class:`sage.homology.simplicial_set_constructions.PushoutOfSimplicialSets` + :class:`sage.topology.simplicial_set_constructions.PushoutOfSimplicialSets` for more documentation and examples. EXAMPLES:: @@ -944,7 +944,7 @@ def pullback(self, *others): which must all equal that of ``self``. This returns the pullback as a simplicial set. See - :class:`sage.homology.simplicial_set_constructions.PullbackOfSimplicialSets` + :class:`sage.topology.simplicial_set_constructions.PullbackOfSimplicialSets` for more documentation and examples. EXAMPLES:: @@ -990,7 +990,7 @@ def equalizer(self, other): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: x = AbstractSimplex(0, name='x') @@ -1374,7 +1374,7 @@ def induced_homology_morphism(self, base_ring=None, cohomology=False): EXAMPLES:: - sage: from sage.homology.simplicial_set import AbstractSimplex, SimplicialSet + sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet sage: v = AbstractSimplex(0, name='v') sage: w = AbstractSimplex(0, name='w') sage: e = AbstractSimplex(1, name='e') From 3e2c0417ed5a072067996fd767190f080313da28 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Mon, 7 Jun 2021 18:50:04 -0700 Subject: [PATCH 145/280] trac 31925: add deprecated versions --- src/sage/homology/cell_complex.py | 12 ++++++ src/sage/homology/cubical_complex.py | 17 ++++++++ src/sage/homology/delta_complex.py | 15 +++++++ src/sage/homology/examples.py | 41 +++++++++++++++++++ src/sage/homology/simplicial_complex.py | 16 ++++++++ .../homology/simplicial_complex_homset.py | 13 ++++++ .../homology/simplicial_complex_morphism.py | 13 ++++++ src/sage/homology/simplicial_set.py | 21 ++++++++++ .../homology/simplicial_set_constructions.py | 31 ++++++++++++++ src/sage/homology/simplicial_set_examples.py | 25 +++++++++++ src/sage/homology/simplicial_set_morphism.py | 12 ++++++ 11 files changed, 216 insertions(+) create mode 100644 src/sage/homology/cell_complex.py create mode 100644 src/sage/homology/cubical_complex.py create mode 100644 src/sage/homology/delta_complex.py create mode 100644 src/sage/homology/examples.py create mode 100644 src/sage/homology/simplicial_complex.py create mode 100644 src/sage/homology/simplicial_complex_homset.py create mode 100644 src/sage/homology/simplicial_complex_morphism.py create mode 100644 src/sage/homology/simplicial_set.py create mode 100644 src/sage/homology/simplicial_set_constructions.py create mode 100644 src/sage/homology/simplicial_set_examples.py create mode 100644 src/sage/homology/simplicial_set_morphism.py diff --git a/src/sage/homology/cell_complex.py b/src/sage/homology/cell_complex.py new file mode 100644 index 00000000000..23292d902e3 --- /dev/null +++ b/src/sage/homology/cell_complex.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +r""" +Generic cell complexes: deprecated + +The current version is :mod:`sage.topology.cell_complexes`. +""" + +from sage.misc.superseded import deprecated_function_alias +import sage.topology.cell_complex + +GenericCellComplex = deprecated_function_alias(31925, + sage.topology.cell_complex.GenericCellComplex) diff --git a/src/sage/homology/cubical_complex.py b/src/sage/homology/cubical_complex.py new file mode 100644 index 00000000000..1149da028c0 --- /dev/null +++ b/src/sage/homology/cubical_complex.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +r""" +Finite cubical complexes: deprecated + +The current version is :mod:`sage.topology.cubical_complexes`. +""" + +from sage.misc.superseded import deprecated_function_alias +import sage.topology.cubical_complex + +Cube = deprecated_function_alias(31925, sage.topology.cubical_complex.Cube) +CubicalComplex = deprecated_function_alias(31925, + sage.topology.cubical_complex.CubicalComplex) +CubicalComplexExamples = deprecated_function_alias(31925, + sage.topology.cubical_complex.CubicalComplexExamples) +cubical_complexes = deprecated_function_alias(31925, + sage.topology.cubical_complex.cubical_complexes) diff --git a/src/sage/homology/delta_complex.py b/src/sage/homology/delta_complex.py new file mode 100644 index 00000000000..cfd5fc6119f --- /dev/null +++ b/src/sage/homology/delta_complex.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +r""" +Finite Delta-complexes: deprecated + +The current version is :mod:`sage.topology.delta_complexes`. +""" +from sage.misc.superseded import deprecated_function_alias +import sage.topology.delta_complex + +DeltaComplex = deprecated_function_alias(31925, + sage.topology.delta_complex.DeltaComplex) +DeltaComplexExamples = deprecated_function_alias(31925, + sage.topology.delta_complex.DeltaComplexExamples) +delta_complexes = deprecated_function_alias(31925, + sage.topology.delta_complex.delta_complexes) diff --git a/src/sage/homology/examples.py b/src/sage/homology/examples.py new file mode 100644 index 00000000000..704b1219189 --- /dev/null +++ b/src/sage/homology/examples.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +""" +Examples of simplicial complexes: deprecated + +The current version is :mod:`sage.topology.simplicial_complex_examples`. +""" + +from sage.misc.superseded import deprecated_function_alias +import sage.topology.simplicial_complex_examples + +for f in ['facets_for_RP4', + 'facets_for_K3', + 'matching', + 'UniqueSimplicialComplex', + 'Sphere', + 'Simplex', + 'Torus', + 'RealProjectivePlane', + 'ProjectivePlane', + 'KleinBottle', + 'SurfaceOfGenus', + 'MooreSpace', + 'ComplexProjectivePlane', + 'PseudoQuaternionicProjectivePlane', + 'PoincareHomologyThreeSphere', + 'RealProjectiveSpace', + 'K3Surface', + 'BarnetteSphere', + 'BrucknerGrunbaumSphere', + 'NotIConnectedGraphs', + 'MatchingComplex', + 'ChessboardComplex', + 'RandomComplex', + 'SumComplex', + 'RandomTwoSphere', + 'ShiftedComplex', + 'RudinBall', + 'ZieglerBall', + 'DunceHat', + 'FareyMap']: + exec('{} = deprecated_function_alias(31925, sage.topology.simplicial_complex_examples.{})'.format(f, f)) diff --git a/src/sage/homology/simplicial_complex.py b/src/sage/homology/simplicial_complex.py new file mode 100644 index 00000000000..8c8399d3b24 --- /dev/null +++ b/src/sage/homology/simplicial_complex.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +r""" +Finite simplicial complexes: deprecated + +The current version is :mod:`sage.topology.simplicial_complexes`. +""" +from sage.misc.superseded import deprecated_function_alias +import sage.topology.simplicial_complex + +for f in ['lattice_paths', + 'rename_vertex', + 'Simplex', + 'SimplicialComplex', + 'facets_for_RP4', + 'facets_for_K3']: + exec('{} = deprecated_function_alias(31925, sage.topology.simplicial_complex.{})'.format(f, f)) diff --git a/src/sage/homology/simplicial_complex_homset.py b/src/sage/homology/simplicial_complex_homset.py new file mode 100644 index 00000000000..6a9d78f69f1 --- /dev/null +++ b/src/sage/homology/simplicial_complex_homset.py @@ -0,0 +1,13 @@ +r""" +Homsets between simplicial complexes: deprecated + +The current version is :mod:`sage.topology.simplicial_complex_homset`. +""" +from sage.misc.superseded import deprecated_function_alias +import sage.topology.simplicial_complex_homset + +is_SimplicialComplexHomset = deprecated_function_alias(31925, + sage.topology.simplicial_complex_homset.is_SimplicialComplexHomset) +SimplicialComplexHomset = deprecated_function_alias(31925, + sage.topology.simplicial_complex_homset.SimplicialComplexHomset) + diff --git a/src/sage/homology/simplicial_complex_morphism.py b/src/sage/homology/simplicial_complex_morphism.py new file mode 100644 index 00000000000..4673be7d414 --- /dev/null +++ b/src/sage/homology/simplicial_complex_morphism.py @@ -0,0 +1,13 @@ +r""" +Morphisms of simplicial complexes: deprecated + +The current version is :mod:`sage.topology.simplicial_complex_morphism`. +""" + +from sage.misc.superseded import deprecated_function_alias +import sage.topology.simplicial_complex_morphism + +is_SimplicialComplexMorphism = deprecated_function_alias(31925, + sage.topology.simplicial_complex_morphism.is_SimplicialComplexMorphism) +SimplicialComplexMorphism = deprecated_function_alias(31925, + sage.topology.simplicial_complex_morphism.SimplicialComplexMorphism) diff --git a/src/sage/homology/simplicial_set.py b/src/sage/homology/simplicial_set.py new file mode 100644 index 00000000000..51e1a36047a --- /dev/null +++ b/src/sage/homology/simplicial_set.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +r""" +Simplicial sets: deprecated + +The current version is :mod:`sage.topology.simplicial_set`. +""" +from sage.misc.superseded import deprecated_function_alias +import sage.topology.simplicial_set + +for f in ['AbstractSimplex_class', + 'NonDegenerateSimplex', + 'AbstractSimplex', + 'SimplicialSet_arbitrary', + 'SimplicialSet_finite', + 'SimplicialSet', + 'standardize_degeneracies', + 'all_degeneracies', + 'standardize_face_maps', + 'face_degeneracies', + 'shrink_simplicial_complex']: + exec('{} = deprecated_function_alias(31925, sage.topology.simplicial_set.{})'.format(f, f)) diff --git a/src/sage/homology/simplicial_set_constructions.py b/src/sage/homology/simplicial_set_constructions.py new file mode 100644 index 00000000000..c8943f62034 --- /dev/null +++ b/src/sage/homology/simplicial_set_constructions.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +r""" +Methods of constructing simplicial sets: deprecated + +The current version is :mod:`sage.topology.simplicial_set_constructions`. +""" +from sage.misc.superseded import deprecated_function_alias +import sage.topology.simplicial_set_constructions + +for f in ['SubSimplicialSet', + 'PullbackOfSimplicialSets', + 'PullbackOfSimplicialSets_finite', + 'Factors', + 'ProductOfSimplicialSets', + 'ProductOfSimplicialSets_finite', + 'PushoutOfSimplicialSets', + 'PushoutOfSimplicialSets_finite', + 'QuotientOfSimplicialSet', + 'QuotientOfSimplicialSet_finite', + 'SmashProductOfSimplicialSets_finite', + 'WedgeOfSimplicialSets', + 'WedgeOfSimplicialSets_finite', + 'DisjointUnionOfSimplicialSets', + 'DisjointUnionOfSimplicialSets_finite', + 'ConeOfSimplicialSet', + 'ConeOfSimplicialSet_finite', + 'ReducedConeOfSimplicialSet', + 'ReducedConeOfSimplicialSet_finite', + 'SuspensionOfSimplicialSet', + 'SuspensionOfSimplicialSet_finite']: + exec('{} = deprecated_function_alias(31925, sage.topology.simplicial_set_constructions.{})'.format(f, f)) diff --git a/src/sage/homology/simplicial_set_examples.py b/src/sage/homology/simplicial_set_examples.py new file mode 100644 index 00000000000..8a19cef5a86 --- /dev/null +++ b/src/sage/homology/simplicial_set_examples.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +r""" +Examples of simplicial sets: deprecated + +The current version is :mod:`sage.topology.simplicial_set_examples`. +""" + +from sage.misc.superseded import deprecated_function_alias +import sage.topology.simplicial_set_examples + +for f in ['Nerve', + 'Sphere', + 'ClassifyingSpace', + 'RealProjectiveSpace', + 'KleinBottle', + 'Torus', + 'Simplex', + 'Empty', + 'Point', + 'Horn', + 'ComplexProjectiveSpace', + 'simplicial_data_from_kenzo_output', + 'HopfMap']: + exec('{} = deprecated_function_alias(31925, sage.topology.simplicial_set_examples.{})'.format(f, f)) + diff --git a/src/sage/homology/simplicial_set_morphism.py b/src/sage/homology/simplicial_set_morphism.py new file mode 100644 index 00000000000..9df5cb9a40d --- /dev/null +++ b/src/sage/homology/simplicial_set_morphism.py @@ -0,0 +1,12 @@ +r""" +Morphisms and homsets for simplicial sets: deprecated + +The current version is :mod:`sage.topology.simplicial_set_morphism`. +""" +from sage.misc.superseded import deprecated_function_alias +import sage.topology.simplicial_set_morphism + +deprecated_function_alias(31925, + sage.topology.simplicial_set_morphism.SimplicialSetHomset) +deprecated_function_alias(31925, + sage.topology.simplicial_set_morphism.SimplicialSetMorphism) From 007e6fda9fb5dfccf109b06d15f8799f7f03ac5f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 13:33:20 -0700 Subject: [PATCH 146/280] ConvexRationalPolyhedralCone.is_empty: Add doctest --- src/sage/geometry/cone.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index dc43db1a6a0..aa1acf765ac 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -3283,6 +3283,13 @@ def is_empty(self): Return whether ``self`` is the empty set. Because a cone always contains the origin, this method returns ``False``. + + EXAMPLES:: + + sage: trivial_cone = cones.trivial(3) + sage: trivial_cone.is_empty() + False + """ return False From 9fcf32ea544d7e841a41deda364c179205108e9b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 17:06:15 -0700 Subject: [PATCH 147/280] Sets.CartesianProducts.ParentMethods, FreeModule_ambient, IntegerRing_class, InternalRealInterval, RealSet, NonNegativeIntegers, IntegerRing_class, PositiveIntegers, RationalField: Add _sympy_ methods --- src/sage/categories/sets_cat.py | 15 ++++++ src/sage/modules/free_module.py | 14 ++++++ src/sage/rings/integer_ring.pyx | 12 +++++ src/sage/rings/rational_field.py | 12 +++++ src/sage/sets/non_negative_integers.py | 13 ++++++ src/sage/sets/positive_integers.py | 12 +++++ src/sage/sets/real_set.py | 63 ++++++++++++++++++++++++++ 7 files changed, 141 insertions(+) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index 68ba29bfa7c..6b2f57e18cf 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -2438,6 +2438,21 @@ def _cartesian_product_of_elements(self, elements): (42, 47, 42) """ + def _sympy_(self): + """ + Return a SymPy ``ProductSet`` corresponding to ``self``. + + EXAMPLES:: + + sage: ZZ3 = cartesian_product([ZZ, ZZ, ZZ]) + sage: sZZ3 = ZZ3._sympy_(); sZZ3 + ProductSet(Integers, Integers, Integers) + sage: (1, 2, 3) in sZZ3 + True + """ + from sympy import ProductSet + return ProductSet(*self.cartesian_factors()) + class ElementMethods: def cartesian_projection(self, i): diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index b4ce5196e48..36f668d99fe 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -5306,6 +5306,20 @@ def gen(self, i=0): v.set_immutable() return v + def _sympy_(self): + """ + Return a SymPy ``ProductSet`` corresponding to ``self``. + + EXAMPLES:: + + sage: sZZ3 = (ZZ^3)._sympy_(); sZZ3 + ProductSet(Integers, Integers, Integers) + sage: (1, 2, 3) in sZZ3 + True + """ + from sympy import ProductSet + return ProductSet(*([self.coordinate_ring()] * self.rank())) + ############################################################################### # diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index ae0b0f41327..737dda9ecb3 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -1467,6 +1467,18 @@ cdef class IntegerRing_class(PrincipalIdealDomain): """ return '"Integer"' + def _sympy_(self): + r""" + Return the SymPy set ``Integers``. + + EXAMPLES:: + + sage: ZZ._sympy_() + Integers + """ + from sympy import Integers + return Integers + def _sage_input_(self, sib, coerced): r""" Produce an expression which will reproduce this value when diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index e7959e21a01..2a4237f0dfc 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -1576,6 +1576,18 @@ def _polymake_init_(self): """ return '"Rational"' + def _sympy_(self): + r""" + Return the SymPy set ``Rationals``. + + EXAMPLES:: + + sage: QQ._sympy_() + Rationals + """ + from sympy import Rationals + return Rationals + def _sage_input_(self, sib, coerced): r""" Produce an expression which will reproduce this value when evaluated. diff --git a/src/sage/sets/non_negative_integers.py b/src/sage/sets/non_negative_integers.py index 9d3579a5f46..adabc89a987 100644 --- a/src/sage/sets/non_negative_integers.py +++ b/src/sage/sets/non_negative_integers.py @@ -221,3 +221,16 @@ def unrank(self, rnk): 100 """ return self.from_integer(rnk) + + def _sympy_(self): + r""" + Return the SymPy set ``Naturals0``. + + EXAMPLES:: + + sage: NN = NonNegativeIntegers() + sage: NN._sympy_() + Naturals0 + """ + from sympy import Naturals0 + return Naturals0 diff --git a/src/sage/sets/positive_integers.py b/src/sage/sets/positive_integers.py index c800d201e95..693ca04706c 100644 --- a/src/sage/sets/positive_integers.py +++ b/src/sage/sets/positive_integers.py @@ -75,3 +75,15 @@ def an_element(self): 42 """ return Integer(42) + + def _sympy_(self): + r""" + Return the SymPy set ``Naturals``. + + EXAMPLES:: + + sage: PositiveIntegers()._sympy_() + Naturals + """ + from sympy import Naturals + return Naturals diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..f2eb7d97202 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -415,6 +415,28 @@ def _sympy_condition_(self, variable): upper_condition = true return lower_condition & upper_condition + def _sympy_(self): + r""" + Return the SymPy set corresponding to ``self``. + + EXAMPLES:: + + sage: RealSet.open_closed(0, 1)[0]._sympy_() + Interval.Lopen(0, 1) + sage: RealSet.point(0)[0]._sympy_() + FiniteSet(0) + sage: RealSet.open(0,1)[0]._sympy_() + Interval.open(0, 1) + sage: RealSet.open(-oo,1)[0]._sympy_() + Interval.open(-oo, 1) + sage: RealSet.open(0, oo)[0]._sympy_() + Interval.open(0, oo) + """ + from sympy import Interval + return Interval(self.lower(), self.upper(), + left_open=not self._lower_closed, + right_open=not self._upper_closed) + def closure(self): """ Return the closure @@ -986,6 +1008,17 @@ def is_empty(self): """ return len(self._intervals) == 0 + def is_universe(self): + """ + Return whether the set is the ambient space (the real line). + + EXAMPLES:: + + sage: RealSet().ambient().is_universe() + True + """ + return self == self.ambient() + def get_interval(self, i): """ Return the ``i``-th connected component. @@ -1811,3 +1844,33 @@ def __rmul__(self, other): [0, 1/2*pi] + [2*pi, +oo) """ return self * other + + def _sympy_(self): + r""" + Return the SymPy set corresponding to ``self``. + + EXAMPLES:: + + sage: RealSet()._sympy_() + EmptySet + sage: RealSet.point(5)._sympy_() + FiniteSet(5) + sage: (RealSet.point(1).union(RealSet.point(2)))._sympy_() + FiniteSet(1, 2) + sage: (RealSet(1, 2).union(RealSet.closed(3, 4)))._sympy_() + Union(Interval.open(1, 2), Interval(3, 4)) + sage: RealSet(-oo, oo)._sympy_() + Reals + + Infinities are not elements:: + + sage: import sympy + sage: RealSet(-oo, oo)._sympy_().contains(sympy.oo) + False + """ + from sympy import Reals, Union + if self.is_universe(): + return Reals + else: + return Union(*[interval._sympy_() + for interval in self._intervals]) From 6e5cac6491592b1d451b400de5e4f1b722db9c20 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 19:08:01 -0700 Subject: [PATCH 148/280] sage.interfaces.sympy_wrapper, Sets.ParentMethods._sympy_: New --- src/sage/categories/sets_cat.py | 26 +++++++++++++++ src/sage/interfaces/sympy_wrapper.py | 50 ++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 src/sage/interfaces/sympy_wrapper.py diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index 68ba29bfa7c..c99f026dd0f 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -1686,6 +1686,32 @@ def algebra(self, base_ring, category=None, **kwds): result.__doc__ = Sets.ParentMethods.algebra.__doc__ return result + def _sympy_(self): + """ + Return an instance of a subclass of SymPy ``Set`` corresponding to ``self``. + + EXAMPLES:: + + sage: F = FiniteEnumeratedSets().example(); F + An example of a finite enumerated set: {1,2,3} + sage: sF = F._sympy_(); sF + SageSet(An example of a finite enumerated set: {1,2,3}) + sage: bool(sF) + True + sage: len(sF) + 3 + sage: list(sF) + [1, 2, 3] + sage: from sympy import FiniteSet + sage: FiniteSet.fromiter(sF) + FiniteSet(1, 2, 3) + + sage: RR._sympy_().is_finite_set + False + """ + from sage.interfaces.sympy_wrapper import SageSet + return SageSet(self) + class ElementMethods: ## Should eventually contain the basic operations which are no math ## latex, hash, ... diff --git a/src/sage/interfaces/sympy_wrapper.py b/src/sage/interfaces/sympy_wrapper.py new file mode 100644 index 00000000000..19e9b7ce610 --- /dev/null +++ b/src/sage/interfaces/sympy_wrapper.py @@ -0,0 +1,50 @@ +""" +Wrapper Class for Sage Sets as SymPy Sets +""" + +# **************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sympy.core.basic import Basic +from sympy.core.decorators import sympify_method_args +from sympy.core.sympify import sympify +from sympy.sets.sets import Set + +@sympify_method_args +class SageSet(Set): + + def __new__(cls, sage_set): + return Basic.__new__(cls, sage_set) + + def _sage_(self): + return self._args[0] + + @property + def is_empty(self): + return self._sage_().is_empty() + + @property + def is_finite_set(self): + return self._sage_().is_finite() + + @property + def is_iterable(self): + from sage.categories.enumerated_sets import EnumeratedSets + return self._sage_() in EnumeratedSets() + + def __iter__(self): + for element in self._sage_(): + yield sympify(element) + + def _contains(self, other): + return other in self._sage_() + + def __len__(self): + return len(self._sage_()) From 72565f2720115475272c030db26b1144b87377bb Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Tue, 2 Jan 2018 10:15:15 +0100 Subject: [PATCH 149/280] 24171: Formal set membership function --- src/sage/functions/other.py | 55 +++++++++++++++++++++++++++++++ src/sage/libs/pynac/pynac.pyx | 2 +- src/sage/sets/real_set.py | 15 +++++++++ src/sage/symbolic/random_tests.py | 6 ++-- src/sage/symbolic/ring.pyx | 23 +++++++++++++ 5 files changed, 97 insertions(+), 4 deletions(-) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 506cf52c218..1749c4712d9 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -2285,3 +2285,58 @@ def _evalf_(self, poly, index, parent=None, algorithm=None): complex_root_of = Function_crootof() + +class Function_elementof(BuiltinFunction): + """ + Formal set membership function that is only accessible internally. + + This function is called to express a set membership statement, + usually as part of a solution set returned by ``solve()``. + See :class:`sage.sets.set.Set` and :class:`sage.sets.real_set.RealSet` + for possible set arguments. + + EXAMPLES:: + + sage: from sage.functions.other import element_of + sage: element_of(x, SR(ZZ)) + element_of(x, Integer Ring) + sage: element_of(sin(x), SR(QQ)) + element_of(sin(x), Rational Field) + sage: element_of(x, SR(RealSet.open_closed(0,1))) + element_of(x, (0, 1]) + sage: element_of(x, SR(Set([4,6,8]))) + element_of(x, {8, 4, 6}) + """ + def __init__(self): + """ + EXAMPLES:: + + sage: from sage.functions.other import element_of + sage: loads(dumps(element_of)) + element_of + """ + BuiltinFunction.__init__(self, "element_of", nargs=2) + + def _latex_(self): + r""" + EXAMPLES:: + + sage: from sage.functions.other import element_of + sage: latex(element_of) + \in + """ + return r'\in' + + def _print_latex_(self, ex, s): + r""" + EXAMPLES:: + + sage: from sage.functions.other import element_of + sage: latex(element_of(x, SR(ZZ))) + x \in \Bold{Z} + sage: latex(element_of(x, SR(Set([4,6,8])))) + x \in \left\{8, 4, 6\right\} + """ + return r"{} \in {}".format(latex(ex), latex(s)) + +element_of = Function_elementof() diff --git a/src/sage/libs/pynac/pynac.pyx b/src/sage/libs/pynac/pynac.pyx index 4c645545ac5..7dac2b54247 100644 --- a/src/sage/libs/pynac/pynac.pyx +++ b/src/sage/libs/pynac/pynac.pyx @@ -1190,7 +1190,7 @@ cdef bint py_is_real(a): return False except NotImplementedError: return False - except AttributeError: + except (TypeError, AttributeError): pass return py_imag(a) == 0 diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..e4151d625b2 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -986,6 +986,21 @@ def is_empty(self): """ return len(self._intervals) == 0 + def is_finite(self): + """ + Return whether the set is finite. + + EXAMPLES:: + + sage: RealSet().is_finite() + True + sage: RealSet(0, 0).is_finite() + True + sage: RealSet(0, 1).is_finite() + False + """ + return self.is_empty() or all(i.is_point() for i in self._intervals) + def get_interval(self, i): """ Return the ``i``-th connected component. diff --git a/src/sage/symbolic/random_tests.py b/src/sage/symbolic/random_tests.py index 81ac9310f40..fef6932f8e5 100644 --- a/src/sage/symbolic/random_tests.py +++ b/src/sage/symbolic/random_tests.py @@ -20,7 +20,7 @@ from sage.symbolic.constants import (pi, e, golden_ratio, log2, euler_gamma, catalan, khinchin, twinprime, mertens) from sage.functions.hypergeometric import hypergeometric -from sage.functions.other import cases +from sage.functions.other import (cases, element_of) from sage.symbolic.comparison import mixed_order ################################################################### @@ -48,13 +48,13 @@ def _mk_full_functions(): Note that this doctest will produce different output whenever a symbolic function is added or removed. """ + excluded = [hypergeometric, cases, element_of] items = sorted(symbol_table['functions'].items()) return [(1.0, f, f.number_of_arguments()) for (name, f) in items if hasattr(f, 'number_of_arguments') and f.number_of_arguments() > 0 and - f != hypergeometric and - f != cases] + f not in excluded] # For creating simple expressions diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 692266db00c..b5b788af6e9 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -254,6 +254,19 @@ cdef class SymbolicRing(CommutativeRing): sage: SR(complex(2,-3)) (2-3j) + Any proper subset of the complex numbers:: + + sage: SR(NN) + Non negative integer semiring + sage: SR(ZZ) + Integer Ring + sage: SR(Set([1/2, 2/3, 3/4])) + {3/4, 2/3, 1/2} + sage: SR(QQ.completion(oo, oo)) + Real Field + sage: SR(RealSet(0, 1)) + (0, 1) + TESTS:: sage: SR._coerce_(int(5)) @@ -364,6 +377,7 @@ cdef class SymbolicRing(CommutativeRing): from sage.rings.infinity import (infinity, minus_infinity, unsigned_infinity) from sage.structure.factorization import Factorization + from sage.categories.sets_cat import Sets if isinstance(x, RealNumber): if x.is_NaN(): @@ -392,6 +406,15 @@ cdef class SymbolicRing(CommutativeRing): elif isinstance(x, Factorization): from sage.misc.all import prod return prod([SR(p)**e for p,e in x], SR(x.unit())) + elif x in Sets(): + from sage.rings.all import NN, ZZ, QQ, AA + from sage.sets.real_set import RealSet + oo = infinity + if (x.is_finite() or x in (NN, ZZ, QQ, AA, QQ.completion(oo,oo)) + or isinstance(x, RealSet)): + exp = x + else: + raise TypeError(f"unable to convert {x!r} to a symbolic expression") else: raise TypeError(f"unable to convert {x!r} to a symbolic expression") From 5e623bb508d45a0ac79538c93bc115562b6269e0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 20:22:03 -0700 Subject: [PATCH 150/280] RealSet.is_finite: Remove, inherited from category --- src/sage/sets/real_set.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 57f6b4b73e0..5d39fbe0029 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -1015,21 +1015,6 @@ def is_empty(self): """ return len(self._intervals) == 0 - def is_finite(self): - """ - Return whether the set is finite. - - EXAMPLES:: - - sage: RealSet().is_finite() - True - sage: RealSet(0, 0).is_finite() - True - sage: RealSet(0, 1).is_finite() - False - """ - return self.is_empty() or all(i.is_point() for i in self._intervals) - def get_interval(self, i): """ Return the ``i``-th connected component. From 7d19916c0fbf2bf76054401df372fbedde7cdbb6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 20:35:40 -0700 Subject: [PATCH 151/280] Do not use QQ.completion(oo,oo) - not implemented --- src/sage/symbolic/ring.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index b5b788af6e9..e113bd44b78 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -410,7 +410,7 @@ cdef class SymbolicRing(CommutativeRing): from sage.rings.all import NN, ZZ, QQ, AA from sage.sets.real_set import RealSet oo = infinity - if (x.is_finite() or x in (NN, ZZ, QQ, AA, QQ.completion(oo,oo)) + if (x.is_finite() or x in (NN, ZZ, QQ, AA) or isinstance(x, RealSet)): exp = x else: From 9b4e2246708b683b3bbcee95eef1a96223a250c3 Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Fri, 10 Nov 2017 07:39:52 +0100 Subject: [PATCH 152/280] 24176: check set argument of element_of() --- src/sage/functions/other.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 1749c4712d9..b83b95d4fad 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -2317,6 +2317,22 @@ def __init__(self): """ BuiltinFunction.__init__(self, "element_of", nargs=2) + def _eval_(self, x, s): + """ + EXAMPLES:: + + sage: from sage.functions.other import element_of + sage: element_of(x, SR(RealSet(-oo, oo))) + element_of(x, (-oo, +oo)) + sage: element_of(x, 0) + Traceback (most recent call last): + ... + ValueError: not a set: 0 + """ + from sage.sets.set import is_Set + if not is_Set(s): + raise ValueError("not a set: {}".format(s)) + def _latex_(self): r""" EXAMPLES:: From 5310e04bae60c574eafe491ebcea366e57513822 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 20:55:07 -0700 Subject: [PATCH 153/280] Function_elementof: Remove use of is_Set --- src/sage/functions/other.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index b83b95d4fad..23f726b239c 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -2329,8 +2329,8 @@ def _eval_(self, x, s): ... ValueError: not a set: 0 """ - from sage.sets.set import is_Set - if not is_Set(s): + from sage.sets.sets_cat import Sets + if not s in Sets(): raise ValueError("not a set: {}".format(s)) def _latex_(self): From 43cabe82529c55b1ab3add68304f3b2b73971cba Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 21:49:46 -0700 Subject: [PATCH 154/280] Do not use QQ.completion(oo,oo) - not implemented (fixup) --- src/sage/symbolic/ring.pyx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index e113bd44b78..5836c26e173 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -262,8 +262,6 @@ cdef class SymbolicRing(CommutativeRing): Integer Ring sage: SR(Set([1/2, 2/3, 3/4])) {3/4, 2/3, 1/2} - sage: SR(QQ.completion(oo, oo)) - Real Field sage: SR(RealSet(0, 1)) (0, 1) @@ -409,7 +407,6 @@ cdef class SymbolicRing(CommutativeRing): elif x in Sets(): from sage.rings.all import NN, ZZ, QQ, AA from sage.sets.real_set import RealSet - oo = infinity if (x.is_finite() or x in (NN, ZZ, QQ, AA) or isinstance(x, RealSet)): exp = x From c793fcc4f9def7070c36785603958c48cf2710b0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 21:53:35 -0700 Subject: [PATCH 155/280] Function_elementof: Remove use of is_Set (fixup) --- src/sage/functions/other.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 23f726b239c..57023dd0084 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -2329,7 +2329,7 @@ def _eval_(self, x, s): ... ValueError: not a set: 0 """ - from sage.sets.sets_cat import Sets + from sage.categories.sets_cat import Sets if not s in Sets(): raise ValueError("not a set: {}".format(s)) From a8c94cab974c8983dad63c2c6e989936984de49a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 22:04:28 -0700 Subject: [PATCH 156/280] Function_elementof: Add conversion to sympy --- src/sage/functions/other.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 57023dd0084..709f2399676 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -2315,7 +2315,8 @@ def __init__(self): sage: loads(dumps(element_of)) element_of """ - BuiltinFunction.__init__(self, "element_of", nargs=2) + BuiltinFunction.__init__(self, "element_of", nargs=2, + conversions=dict(sympy='Contains')) def _eval_(self, x, s): """ From d68a69f42ab90e51eb1b7ad1c938ddb05539114f Mon Sep 17 00:00:00 2001 From: Ralf Stephan Date: Wed, 8 Nov 2017 08:55:47 +0100 Subject: [PATCH 157/280] RealSet.__bool__: New (split out from #24171) --- src/sage/sets/real_set.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..f36379548a4 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -1017,6 +1017,20 @@ def get_interval(self, i): __getitem__ = get_interval + def __bool__(self): + """ + A set is considered True unless it is empty, in which case it is + considered to be False. + + EXAMPLES:: + + sage: bool(RealSet(0, 1)) + True + sage: bool(RealSet()) + False + """ + return not self.is_empty() + @staticmethod def normalize(intervals): """ From 9079a1030aabaa3042a968dab7cf12526597cb29 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 8 Jun 2021 22:57:43 -0700 Subject: [PATCH 158/280] RealSet.is_subset: Rename from is_included_in --- src/sage/sets/real_set.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index a4621569133..e7c53f53320 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -93,6 +93,7 @@ class RealSet. from sage.rings.real_lazy import LazyFieldElement, RLF from sage.rings.infinity import infinity, minus_infinity +from sage.misc.superseded import deprecated_function_alias @richcmp_method class InternalRealInterval(UniqueRepresentation, Parent): @@ -1599,13 +1600,13 @@ def contains(self, x): __contains__ = contains - def is_included_in(self, *other): + def is_subset(self, *other): r""" - Tests interval inclusion + Return whether ``self`` is a subset of ``other``. INPUT: - - ``*args`` -- a :class:`RealSet` or something that defines + - ``*other`` -- a :class:`RealSet` or something that defines one. OUTPUT: @@ -1617,13 +1618,15 @@ def is_included_in(self, *other): sage: I = RealSet((1,2)) sage: J = RealSet((1,3)) sage: K = RealSet((2,3)) - sage: I.is_included_in(J) + sage: I.is_subset(J) True - sage: J.is_included_in(K) + sage: J.is_subset(K) False """ return RealSet(*other).intersection(self) == self + is_included_in = deprecated_function_alias(31927, is_subset) + def an_element(self): """ Return a point of the set From 3d857863ae9de66e5ca94a95e0c5f67f87e33f79 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 9 Jun 2021 09:52:08 +0200 Subject: [PATCH 159/280] make modules fuzz ready --- src/sage/modules/fg_pid/fgp_module.py | 40 +++++++-- src/sage/modules/filtered_vector_space.py | 13 ++- src/sage/modules/free_module.py | 74 +++++++++++----- src/sage/modules/free_module_element.pyx | 77 ++++++++++------ src/sage/modules/free_module_integer.py | 87 ++++++------------- .../modules/multi_filtered_vector_space.py | 14 ++- src/sage/modules/vector_integer_dense.pyx | 7 +- src/sage/modules/vector_mod2_dense.pyx | 80 ++++++++--------- 8 files changed, 228 insertions(+), 164 deletions(-) diff --git a/src/sage/modules/fg_pid/fgp_module.py b/src/sage/modules/fg_pid/fgp_module.py index 38b95d40a39..9349f2fd42a 100644 --- a/src/sage/modules/fg_pid/fgp_module.py +++ b/src/sage/modules/fg_pid/fgp_module.py @@ -1698,8 +1698,13 @@ def random_element(self, *args, **kwds): sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2]) sage: Q = V/W - sage: Q.random_element() - (1, 10) + sage: Q.random_element().parent() is Q + True + sage: Q.cardinality() + 48 + sage: S = set() + sage: while len(S) < 48: + ....: S.add(Q.random_element()) """ return self(self._V.random_element(*args, **kwds)) @@ -1921,7 +1926,23 @@ def random_fgp_module(n, R=ZZ, finite=False): sage: import sage.modules.fg_pid.fgp_module as fgp sage: fgp.random_fgp_module(4) - Finitely generated module V/W over Integer Ring with invariants (4) + Finitely generated module V/W over Integer Ring with invariants (...) + + In most cases the cardinality is small or infinite:: + + sage: for g in (1, 2, 3, +Infinity): + ....: while fgp.random_fgp_module(4).cardinality() != 1: + ....: pass + + One can force a finite module:: + + sage: fgp.random_fgp_module(4, finite=True).is_finite() + True + + Larger finite modules appear:: + + sage: while fgp.random_fgp_module(4, finite=True).cardinality() < 100: + ....: pass """ K = R.fraction_field() V = K**n @@ -1948,9 +1969,18 @@ def random_fgp_morphism_0(*args, **kwds): EXAMPLES:: sage: import sage.modules.fg_pid.fgp_module as fgp - sage: fgp.random_fgp_morphism_0(4) - Morphism from module over Integer Ring with invariants (4,) to module with invariants (4,) that sends the generators to [(0)] + sage: mor = fgp.random_fgp_morphism_0(4) + sage: mor.domain() == mor.codomain() + True + sage: fgp.is_FGP_Module(mor.domain()) + True + + Each generator is sent to a random multiple of itself:: + sage: gens = mor.domain().gens() + sage: im_gens = mor.im_gens() + sage: all(im_gens[i] == sum(im_gens[i])*gens[i] for i in range(len(gens))) + True """ A = random_fgp_module(*args, **kwds) return A.hom([ZZ.random_element() * g for g in A.smith_form_gens()]) diff --git a/src/sage/modules/filtered_vector_space.py b/src/sage/modules/filtered_vector_space.py index fa67153e75b..4d0e451806e 100644 --- a/src/sage/modules/filtered_vector_space.py +++ b/src/sage/modules/filtered_vector_space.py @@ -1254,10 +1254,15 @@ def random_deformation(self, epsilon=None): [1 0 0] sage: G = F.random_deformation(1/50); G QQ^3 >= QQ^1 >= QQ^1 >= 0 - sage: G.get_degree(2) - Vector space of degree 3 and dimension 1 over Rational Field - Basis matrix: - [ 1 -15/304 0] + sage: D = G.get_degree(2) + sage: D.degree() + 3 + sage: v = D.basis_matrix()[0] + sage: v[0] + 1 + + sage: while F.random_deformation(1/50).get_degree(2).matrix() == matrix([1, 0, 0]): + ....: pass """ from sage.modules.free_module_element import random_vector R = self.base_ring() diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index b4ce5196e48..c5099442012 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -2362,18 +2362,28 @@ def random_element(self, prob=1.0, *args, **kwds): EXAMPLES:: - sage: M = FreeModule(ZZ, 2).span([[1,1]]) - sage: M.random_element() - (-1, -1) - sage: M.random_element() - (2, 2) - sage: M.random_element() - (1, 1) + sage: M = FreeModule(ZZ, 2).span([[1, 1]]) + sage: v = M.random_element() + sage: v.parent() is M + True + sage: v in M + True + + Small entries are likely:: + + sage: for i in [-2, -1, 0, 1, 2]: + ....: while vector([i, i]) != M.random_element(): + ....: pass + + Large entries appear as well:: + + sage: while abs(M.random_element()[0]) < 100: + ....: pass Passes extra positional or keyword arguments through:: - sage: M.random_element(5,10) - (9, 9) + sage: all(i in range(5, 10) for i in M.random_element(1.0, 5, 10)) + True """ rand = current_randstate().python_random().random R = self.base_ring() @@ -5226,26 +5236,46 @@ def random_element(self, prob=1.0, *args, **kwds): EXAMPLES:: sage: M = FreeModule(ZZ, 3) - sage: M.random_element() - (-1, 2, 1) - sage: M.random_element() - (-95, -1, -2) - sage: M.random_element() - (-12, 0, 0) + sage: M.random_element().parent() is M + True Passes extra positional or keyword arguments through:: - sage: M.random_element(5,10) - (5, 5, 5) - + sage: all(i in range(5, 10) for i in M.random_element(1.0, 5, 10)) + True :: sage: M = FreeModule(ZZ, 16) - sage: M.random_element() - (-6, 5, 0, 0, -2, 0, 1, -4, -6, 1, -1, 1, 1, -1, 1, -1) - sage: M.random_element(prob=0.3) - (0, 0, 0, 0, -3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, -3) + sage: M.random_element().parent() is M + True + + sage: def add_sample(**kwds): + ....: global total, zeros + ....: v = M.random_element(**kwds) + ....: total += M.rank() + ....: zeros += sum(i == 0 for i in v) + + sage: total = 0 + sage: zeros = 0 + sage: add_sample() + sage: expected = 1/5 + sage: while abs(zeros/total - expected) > 0.01: + ....: add_sample() + + sage: total = 0 + sage: zeros = 0 + sage: add_sample(prob=0.3) + sage: expected = 1/5 * 3/10 + 7/10 + sage: while abs(zeros/total - expected) > 0.01: + ....: add_sample(prob=0.3) + + sage: total = 0 + sage: zeros = 0 + sage: add_sample(prob=0.7) + sage: expected = 1/5 * 7/10 + 3/10 + sage: while abs(zeros/total - expected) > 0.01: + ....: add_sample(prob=0.7) """ rand = current_randstate().python_random().random R = self.base_ring() diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 1bbe475695c..e6487e6f17a 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -776,58 +776,66 @@ def random_vector(ring, degree=None, *args, **kwds): :meth:`sage.rings.integer_ring.IntegerRing_class.random_element` for several other variants. :: - sage: random_vector(10) - (-8, 2, 0, 0, 1, -1, 2, 1, -95, -1) + sage: random_vector(10).parent() + Ambient free module of rank 10 over the principal ideal domain Integer Ring + sage: random_vector(20).parent() + Ambient free module of rank 20 over the principal ideal domain Integer Ring - sage: sorted(random_vector(20)) - [-12, -6, -4, -4, -2, -2, -2, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 1, 4, 5] - - sage: random_vector(ZZ, 20, x=4) - (2, 0, 3, 0, 1, 0, 2, 0, 2, 3, 0, 3, 1, 2, 2, 2, 1, 3, 2, 3) - - sage: random_vector(ZZ, 20, x=-20, y=100) - (43, 47, 89, 31, 56, -20, 23, 52, 13, 53, 49, -12, -2, 94, -1, 95, 60, 83, 28, 63) + sage: v = random_vector(ZZ, 20, x=4) + sage: all(i in range(4) for i in v) + True - sage: random_vector(ZZ, 20, distribution="1/n") - (0, -1, -2, 0, -1, -2, 0, 0, 27, -1, 1, 1, 0, 2, -1, 1, -1, -2, -1, 3) + sage: v = random_vector(ZZ, 20, x=-20, y=100) + sage: all(i in range(-20, 100) for i in v) + True If the ring is not specified, the default is the integers, and parameters for the random distribution may be passed without using keywords. This is a random vector with 20 entries uniformly distributed between -20 and 100. :: - sage: random_vector(20, -20, 100) - (70, 19, 98, 2, -18, 88, 36, 66, 76, 52, 82, 99, 55, -17, 82, -15, 36, 28, 79, 18) + sage: random_vector(20, -20, 100).parent() + Ambient free module of rank 20 over the principal ideal domain Integer Ring Now over the rationals. Note that bounds on the numerator and denominator may be specified. See :meth:`sage.rings.rational_field.RationalField.random_element` for documentation. :: - sage: random_vector(QQ, 10) - (0, -1, -4/3, 2, 0, -13, 2/3, 0, -4/5, -1) + sage: random_vector(QQ, 10).parent() + Vector space of dimension 10 over Rational Field - sage: random_vector(QQ, 10, num_bound = 15, den_bound = 5) - (-12/5, 9/4, -13/3, -1/3, 1, 5/4, 4, 1, -15, 10/3) + sage: v = random_vector(QQ, 10, num_bound=15, den_bound=5) + sage: v.parent() + Vector space of dimension 10 over Rational Field + sage: all(q.numerator() <= 15 and q.denominator() <= 5 for q in v) + True Inexact rings may be used as well. The reals have uniform distributions, with the range `(-1,1)` as the default. More at: :meth:`sage.rings.real_mpfr.RealField_class.random_element` :: - sage: random_vector(RR, 5) - (0.248997268533725, -0.112200126330480, 0.776829203293064, -0.899146461031406, 0.534465018743125) + sage: v = random_vector(RR, 5) + sage: v.parent() + Vector space of dimension 5 over Real Field with 53 bits of precision + sage: all(-1 <= r <= 1 for r in v) + True - sage: random_vector(RR, 5, min = 8, max = 14) - (8.43260944052606, 8.34129413391087, 8.92391495103829, 11.5784799413416, 11.0973561568002) + sage: v = random_vector(RR, 5, min=8, max=14) + sage: v.parent() + Vector space of dimension 5 over Real Field with 53 bits of precision + sage: all(8 <= r <= 14 for r in v) + True Any ring with a ``random_element()`` method may be used. :: sage: F = FiniteField(23) sage: hasattr(F, 'random_element') True - sage: random_vector(F, 10) - (21, 6, 5, 2, 6, 2, 18, 9, 9, 7) + sage: v = random_vector(F, 10) + sage: v.parent() + Vector space of dimension 10 over Finite Field of size 23 The default implementation is a dense representation, equivalent to setting ``sparse=False``. :: @@ -840,6 +848,25 @@ def random_vector(ring, degree=None, *args, **kwds): sage: w.is_sparse() True + The elements are chosen using the ring's ``random_element`` method:: + + sage: from sage.misc.randstate import current_randstate + sage: seed = current_randstate().seed() + sage: set_random_seed(seed) + sage: v1 = random_vector(ZZ, 20, distribution="1/n") + sage: v2 = random_vector(ZZ, 15, x=-1000, y=1000) + sage: v3 = random_vector(QQ, 10) + sage: v4 = random_vector(FiniteField(17), 10) + sage: v5 = random_vector(RR, 10) + sage: set_random_seed(seed) + sage: w1 = vector(ZZ.random_element(distribution="1/n") for _ in range(20)) + sage: w2 = vector(ZZ.random_element(x=-1000, y=1000) for _ in range(15)) + sage: w3 = vector(QQ.random_element() for _ in range(10)) + sage: w4 = vector(FiniteField(17).random_element() for _ in range(10)) + sage: w5 = vector(RR.random_element() for _ in range(10)) + sage: [v1, v2, v3, v4, v5] == [w1, w2, w3, w4, w5] + True + Inputs get checked before constructing the vector. :: sage: random_vector('junk') @@ -2570,7 +2597,7 @@ cdef class FreeModuleElement(Vector): # abstract base class sage: u.dot_product(w) 0 - The cross product is defined for degree seven vectors as well: + The cross product is defined for degree seven vectors as well: see :wikipedia:`Cross_product`. The 3-D cross product is achieved using the quaternions, whereas the 7-D cross product is achieved using the octonions. :: diff --git a/src/sage/modules/free_module_integer.py b/src/sage/modules/free_module_integer.py index 443083a3e11..3da0f6feacb 100644 --- a/src/sage/modules/free_module_integer.py +++ b/src/sage/modules/free_module_integer.py @@ -254,14 +254,10 @@ def __init__(self, ambient, basis, check=True, echelonize=False, [ 1 -2 0] [ 2 2 1] - sage: IntegerLattice(random_matrix(ZZ, 5, 5, x=-2^20, y=2^20)) - Free module of degree 5 and rank 5 over Integer Ring - User basis matrix: - [ -7945 -381123 85872 -225065 12924] - [-158254 120252 189195 -262144 -345323] - [ 232388 -49556 306585 -31340 401528] - [-353460 213748 310673 158140 172810] - [-287787 333937 -145713 -482137 186529] + sage: M = random_matrix(ZZ, 5, 5, x=-2^20, y=2^20) + sage: L = IntegerLattice(M) + sage: M.row_space() == L.matrix().row_space() + True sage: K. = NumberField(x^8+1) sage: O = K.ring_of_integers() @@ -307,32 +303,20 @@ def reduced_basis(self): EXAMPLES:: sage: from sage.modules.free_module_integer import IntegerLattice - sage: L = IntegerLattice(random_matrix(ZZ, 10, 10), lll_reduce=False) - sage: L.reduced_basis - [ -8 2 0 0 1 -1 2 1 -95 -1] - [ -2 -12 0 0 1 -1 1 -1 -2 -1] - [ 4 -4 -6 5 0 0 -2 0 1 -4] - [ -6 1 -1 1 1 -1 1 -1 -3 1] - [ 1 0 0 -3 2 -2 0 -2 1 0] - [ -1 1 0 0 1 -1 4 -1 1 -1] - [ 14 1 -5 4 -1 0 2 4 1 1] - [ -2 -1 0 4 -3 1 -5 0 -2 -1] - [ -9 -1 -1 3 2 1 -1 1 -2 1] - [ -1 2 -7 1 0 2 3 -1955 -22 -1] - - sage: _ = L.LLL() - sage: L.reduced_basis - [ 1 0 0 -3 2 -2 0 -2 1 0] - [ -1 1 0 0 1 -1 4 -1 1 -1] - [ -2 0 0 1 0 -2 -1 -3 0 -2] - [ -2 -2 0 -1 3 0 -2 0 2 0] - [ 1 1 1 2 3 -2 -2 0 3 1] - [ -4 1 -1 0 1 1 2 2 -3 3] - [ 1 -3 -7 2 3 -1 0 0 -1 -1] - [ 1 -9 1 3 1 -3 1 -1 -1 0] - [ 8 5 19 3 27 6 -3 8 -25 -22] - [ 172 -25 57 248 261 793 76 -839 -41 376] + sage: M = random_matrix(ZZ, 10, 10) + sage: while M.rank() < 10: + ....: M = random_matrix(ZZ, 10, 10) + sage: L = IntegerLattice(M, lll_reduce=False) + sage: L.reduced_basis == M + True + sage: LLL = L.LLL() + sage: LLL == L.reduced_basis + True + sage: bool(min(a.norm() for a in LLL) == LLL[0].norm()) + True + sage: bool(LLL[0].norm() <= M[0].norm()) + True """ return self._reduced_basis @@ -376,36 +360,19 @@ def LLL(self, *args, **kwds): sage: from sage.modules.free_module_integer import IntegerLattice sage: A = random_matrix(ZZ, 10, 10, x=-2000, y=2000) + sage: while A.rank() < 10: + ....: A = random_matrix(ZZ, 10, 10) sage: L = IntegerLattice(A, lll_reduce=False); L Free module of degree 10 and rank 10 over Integer Ring User basis matrix: - [ -645 -1037 -1775 -1619 1721 -1434 1766 1701 1669 1534] - [ 1303 960 1998 -1838 1683 -1332 149 327 -849 -1562] - [-1113 -1366 1379 669 54 1214 -1750 -605 -1566 1626] - [-1367 1651 926 1731 -913 627 669 -1437 -132 1712] - [ -549 1327 -1353 68 1479 -1803 -456 1090 -606 -317] - [ -221 -1920 -1361 1695 1139 111 -1792 1925 -656 1992] - [-1934 -29 88 890 1859 1820 -1912 -1614 -1724 1606] - [ -590 -1380 1768 774 656 760 -746 -849 1977 -1576] - [ 312 -242 -1732 1594 -439 -1069 458 -1195 1715 35] - [ 391 1229 -1815 607 -413 -860 1408 1656 1651 -628] - sage: min(v.norm().n() for v in L.reduced_basis) - 3346.57... - - sage: L.LLL() - [ -888 53 -274 243 -19 431 710 -83 928 347] - [ 448 -330 370 -511 242 -584 -8 1220 502 183] - [ -524 -460 402 1338 -247 -279 -1038 -28 -159 -794] - [ 166 -190 -162 1033 -340 -77 -1052 1134 -843 651] - [ -47 -1394 1076 -132 854 -151 297 -396 -580 -220] - [-1064 373 -706 601 -587 -1394 424 796 -22 -133] - [-1126 398 565 -1418 -446 -890 -237 -378 252 247] - [ -339 799 295 800 425 -605 -730 -1160 808 666] - [ 755 -1206 -918 -192 -1063 -37 -525 -75 338 400] - [ 382 -199 -1839 -482 984 -15 -695 136 682 563] - sage: L.reduced_basis[0].norm().n() - 1613.74... - + ... + sage: L.reduced_basis == A + True + sage: old_min = min(v.norm().n() for v in L.reduced_basis) + sage: _ = L.LLL() + sage: new_min = L.reduced_basis[0].norm().n() + sage: new_min <= old_min + True """ basis = self.reduced_basis basis = [v for v in basis.LLL(*args, **kwds) if v] diff --git a/src/sage/modules/multi_filtered_vector_space.py b/src/sage/modules/multi_filtered_vector_space.py index 05973ccf7ed..0d0e4b316e7 100644 --- a/src/sage/modules/multi_filtered_vector_space.py +++ b/src/sage/modules/multi_filtered_vector_space.py @@ -717,10 +717,16 @@ def random_deformation(self, epsilon=None): Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: [1 0] - sage: V.random_deformation(1/100).get_degree('b',1) - Vector space of degree 2 and dimension 1 over Rational Field - Basis matrix: - [ 1 8/1197] + sage: D = V.random_deformation(1/100).get_degree('b',1) + sage: D.degree() + 2 + sage: D.dimension() + 1 + sage: D.matrix()[0, 0] + 1 + + sage: while V.random_deformation(1/100).get_degree('b',1).matrix() == matrix([1, 0]): + ....: pass """ filtrations = {key: value.random_deformation(epsilon) for key, value in self._filt.items()} diff --git a/src/sage/modules/vector_integer_dense.pyx b/src/sage/modules/vector_integer_dense.pyx index 114c8868264..9b24aecd965 100644 --- a/src/sage/modules/vector_integer_dense.pyx +++ b/src/sage/modules/vector_integer_dense.pyx @@ -316,10 +316,9 @@ cdef class Vector_integer_dense(free_module_element.FreeModuleElement): sage: A = random_matrix(ZZ,1,3) sage: v = A.row(0) - sage: vs = singular(v); vs - -8, - 2, - 0 + sage: vs = singular(v) + sage: vs._repr_() == '{},\n{},\n{}'.format(*v) + True sage: vs.type() 'intvec' """ diff --git a/src/sage/modules/vector_mod2_dense.pyx b/src/sage/modules/vector_mod2_dense.pyx index d9bd624078a..3c08d14f2d7 100644 --- a/src/sage/modules/vector_mod2_dense.pyx +++ b/src/sage/modules/vector_mod2_dense.pyx @@ -14,12 +14,13 @@ AUTHOR: EXAMPLES:: sage: VS = GF(2)^3 - sage: e = VS.random_element(); e - (1, 0, 0) - sage: f = VS.random_element(); f - (0, 1, 1) - sage: e + f - (1, 1, 1) + sage: e = VS.random_element() + sage: e.parent() is VS + True + sage: S = set(vector(v, immutable=True) for v in VS) + sage: S1 = set() + sage: while S != S1: + ....: S1.add(vector(VS.random_element(), immutable=True)) TESTS:: @@ -94,10 +95,11 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): sage: w = copy(v) sage: w == v True - sage: v[:10] - (1, 0, 0, 0, 1, 1, 1, 0, 0, 1) - sage: w[:10] - (1, 0, 0, 0, 1, 1, 1, 0, 0, 1) + sage: v[:10] == w[:10] + True + sage: v[5] += 1 + sage: v == w + False """ cdef Vector_mod2_dense y = self._new_c() if self._degree: @@ -274,11 +276,12 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): EXAMPLES:: sage: VS = VectorSpace(GF(2),4) - sage: v = VS.random_element(); v - (1, 0, 0, 0) - sage: v[0] = 0; v - (0, 0, 0, 0) - sage: v[1:3] = [1, 1]; v + sage: v = VS.random_element() + sage: v[0] = 0; v[0] + 0 + sage: v[1:3] = [1, 1]; v[1:3] + (1, 1) + sage: v[3] = 0; v (0, 1, 1, 0) sage: v[4] = 0 Traceback (most recent call last): @@ -398,12 +401,11 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): EXAMPLES:: sage: VS = VectorSpace(GF(2),10) - sage: e = VS.random_element(); e - (1, 0, 0, 0, 1, 1, 1, 0, 0, 1) - sage: f = VS.random_element(); f - (1, 1, 0, 1, 1, 1, 0, 0, 0, 1) - sage: e.pairwise_product(f) #indirect doctest - (1, 0, 0, 0, 1, 1, 0, 0, 0, 1) + sage: e = VS.random_element() + sage: f = VS.random_element() + sage: g = e.pairwise_product(f) #indirect doctest + sage: all(g[i] == e[i]*f[i] for i in range(10)) + True """ cdef Vector_mod2_dense z, r r = right @@ -418,26 +420,24 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): EXAMPLES:: sage: VS = VectorSpace(GF(2),10) - sage: e = VS.random_element(); e - (1, 0, 0, 0, 1, 1, 1, 0, 0, 1) + sage: e = VS.random_element() sage: 0 * e (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - sage: 1 * e - (1, 0, 0, 0, 1, 1, 1, 0, 0, 1) - sage: 2 * e #indirect doctest + sage: 1 * e == e + True + sage: 2 * e # indirect doctest (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) :: - sage: VS = VectorSpace(GF(2),10) - sage: e = VS.random_element(); e - (1, 1, 0, 1, 1, 1, 0, 0, 0, 1) - sage: e * 0 #indirect doctest - (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - sage: e * 1 - (1, 1, 0, 1, 1, 1, 0, 0, 0, 1) - sage: e * 2 - (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + sage: VS = VectorSpace(GF(2), 100) + sage: e = VS.random_element() + sage: e * 0 == 0 # indirect doctest + True + sage: e * 1 == e + True + sage: e * 2 == 0 + True """ cdef IntegerMod_int a @@ -467,11 +467,11 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): EXAMPLES:: - sage: VS = VectorSpace(GF(2),10) - sage: e = VS.random_element(); e - (1, 0, 0, 0, 1, 1, 1, 0, 0, 1) - sage: e.list() - [1, 0, 0, 0, 1, 1, 1, 0, 0, 1] + sage: VS = VectorSpace(GF(2), 10) + sage: entries = [GF(2).random_element() for _ in range(10)] + sage: e = VS(entries) + sage: e.list() == entries + True """ cdef Py_ssize_t d = self._degree cdef Py_ssize_t i From 4bc5a2c98192b207c8578928cefbc144a7e14a7e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 06:05:36 -0700 Subject: [PATCH 160/280] RealSet.is_disjoint: Rename from is_disjoint_from --- src/sage/sets/real_set.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 75ecf0d4018..db8f30be731 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -1808,7 +1808,7 @@ def boundary(self): """ return RealSet(*[RealSet.point(x) for i in self._intervals for x in i.boundary_points()]) - def is_disjoint_from(self, *other): + def is_disjoint(self, *other): """ Test whether the two sets are disjoint @@ -1826,14 +1826,16 @@ def is_disjoint_from(self, *other): (0, 1) ∪ (2, 3) sage: s2 = RealSet([1, 2]); s2 [1, 2] - sage: s1.is_disjoint_from(s2) + sage: s1.is_disjoint(s2) True - sage: s1.is_disjoint_from([1, 2]) + sage: s1.is_disjoint([1, 2]) True """ other = RealSet(*other) return self.intersection(other).is_empty() + is_disjoint_from = deprecated_function_alias(31927, is_disjoint) + @staticmethod def are_pairwise_disjoint(*real_set_collection): """ @@ -1865,7 +1867,7 @@ def are_pairwise_disjoint(*real_set_collection): for j in range(i): si = sets[i] sj = sets[j] - if not si.is_disjoint_from(sj): + if not si.is_disjoint(sj): return False return True From 3cac256c1af8a67ec21b90fbcb206a5001b635e8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 07:23:23 -0700 Subject: [PATCH 161/280] sage.interfaces.sympy_wrapper: Add doctests --- src/sage/interfaces/sympy_wrapper.py | 89 ++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/sage/interfaces/sympy_wrapper.py b/src/sage/interfaces/sympy_wrapper.py index 19e9b7ce610..35d5fed5ba1 100644 --- a/src/sage/interfaces/sympy_wrapper.py +++ b/src/sage/interfaces/sympy_wrapper.py @@ -17,34 +17,123 @@ from sympy.core.sympify import sympify from sympy.sets.sets import Set + @sympify_method_args class SageSet(Set): + r""" + Wrapper for a Sage set providing the SymPy Set API. + + Parents in the category :class:`sage.categories.sets_cat.Sets`, unless + a more specific method is implemented, convert to SymPy by creating + an instance of this class. + + EXAMPLES:: + + sage: F = Family([2, 3, 5, 7]); F + Family (2, 3, 5, 7) + sage: sF = F._sympy_(); sF # indirect doctest + SageSet(Family (2, 3, 5, 7)) + sage: sF._sage_() is F + True + sage: bool(sF) + True + sage: len(sF) + 4 + sage: list(sF) + [2, 3, 5, 7] + sage: sF.is_finite_set + True + """ def __new__(cls, sage_set): return Basic.__new__(cls, sage_set) def _sage_(self): + r""" + Return the underlying Sage set of the wrapper ``self``. + + EXAMPLES:: + + sage: F = Set([1, 2]) + sage: F is Set([1, 2]) + False + sage: sF = F._sympy_(); sF + SageSet({1, 2}) + sage: sF._sage_() is F + True + """ return self._args[0] @property def is_empty(self): + r""" + EXAMPLES:: + + sage: Empty = Set([]) + sage: sEmpty = Empty._sympy_() + sage: sEmpty.is_empty + True + """ return self._sage_().is_empty() @property def is_finite_set(self): + r""" + EXAMPLES:: + + sage: W = WeylGroup(["A",1,1]) + sage: sW = W._sympy_(); sW + SageSet(Weyl Group of type ['A', 1, 1] (as a matrix group acting on the root space)) + sage: sW.is_finite_set + False + """ return self._sage_().is_finite() @property def is_iterable(self): + r""" + EXAMPLES:: + + sage: W = WeylGroup(["A",1,1]) + sage: sW = W._sympy_(); sW + SageSet(Weyl Group of type ['A', 1, 1] (as a matrix group acting on the root space)) + sage: sW.is_iterable + True + """ from sage.categories.enumerated_sets import EnumeratedSets return self._sage_() in EnumeratedSets() def __iter__(self): + r""" + EXAMPLES:: + + sage: sPrimes = Primes()._sympy_(); sPrimes + SageSet(Set of all prime numbers: 2, 3, 5, 7, ...) + sage: iter_sPrimes = iter(sPrimes) + sage: next(iter_sPrimes), next(iter_sPrimes), next(iter_sPrimes) + (2, 3, 5) + """ for element in self._sage_(): yield sympify(element) def _contains(self, other): + """ + EXAMPLES:: + + sage: sPrimes = Primes()._sympy_(); sPrimes + SageSet(Set of all prime numbers: 2, 3, 5, 7, ...) + sage: 91 in sPrimes + False + """ return other in self._sage_() def __len__(self): + """ + EXAMPLES:: + + sage: sB3 = WeylGroup(["B", 3])._sympy_(); sB3 + SageSet(Weyl Group of type ['B', 3] (as a matrix group acting on the ambient space)) + sage: len(sB3) + 48 + """ return len(self._sage_()) From eef604e8e5e6441eed11fa4c86c6277fd8318277 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 08:34:06 -0700 Subject: [PATCH 162/280] SageSet: Finish docstrings; handle symbolic _contains --- src/sage/categories/sets_cat.py | 13 +++++++++++ src/sage/interfaces/sympy_wrapper.py | 32 ++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index c99f026dd0f..c1e6fb98e5a 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -1690,12 +1690,17 @@ def _sympy_(self): """ Return an instance of a subclass of SymPy ``Set`` corresponding to ``self``. + The default implementation creates an instance of + :class:`~sage.interfaces.sympy_wrapper`. + EXAMPLES:: sage: F = FiniteEnumeratedSets().example(); F An example of a finite enumerated set: {1,2,3} sage: sF = F._sympy_(); sF SageSet(An example of a finite enumerated set: {1,2,3}) + sage: sF.is_finite_set + True sage: bool(sF) True sage: len(sF) @@ -1708,6 +1713,14 @@ def _sympy_(self): sage: RR._sympy_().is_finite_set False + + sage: F = Set([1, 2]) + sage: F is Set([1, 2]) + False + sage: sF = F._sympy_(); sF + SageSet({1, 2}) + sage: sF._sage_() is F + True """ from sage.interfaces.sympy_wrapper import SageSet return SageSet(self) diff --git a/src/sage/interfaces/sympy_wrapper.py b/src/sage/interfaces/sympy_wrapper.py index 35d5fed5ba1..6c22b47f9c3 100644 --- a/src/sage/interfaces/sympy_wrapper.py +++ b/src/sage/interfaces/sympy_wrapper.py @@ -46,6 +46,9 @@ class SageSet(Set): """ def __new__(cls, sage_set): + r""" + Construct a wrapper for a Sage set. + """ return Basic.__new__(cls, sage_set) def _sage_(self): @@ -67,6 +70,8 @@ def _sage_(self): @property def is_empty(self): r""" + Return whether the set ``self`` is empty. + EXAMPLES:: sage: Empty = Set([]) @@ -79,6 +84,8 @@ def is_empty(self): @property def is_finite_set(self): r""" + Return whether the set ``self`` is finite. + EXAMPLES:: sage: W = WeylGroup(["A",1,1]) @@ -92,6 +99,8 @@ def is_finite_set(self): @property def is_iterable(self): r""" + Return whether the set ``self`` is iterable. + EXAMPLES:: sage: W = WeylGroup(["A",1,1]) @@ -105,6 +114,8 @@ def is_iterable(self): def __iter__(self): r""" + Iterator for the set ``self``. + EXAMPLES:: sage: sPrimes = Primes()._sympy_(); sPrimes @@ -116,19 +127,36 @@ def __iter__(self): for element in self._sage_(): yield sympify(element) - def _contains(self, other): + def _contains(self, element): """ + Return whether ``element`` is an element of the set ``self``. + EXAMPLES:: sage: sPrimes = Primes()._sympy_(); sPrimes SageSet(Set of all prime numbers: 2, 3, 5, 7, ...) sage: 91 in sPrimes False + + sage: from sympy.abc import p + sage: sPrimes.contains(p) + Contains(p, SageSet(Set of all prime numbers: 2, 3, 5, 7, ...)) + + sage: p in sPrimes + Traceback (most recent call last): + ... + TypeError: did not evaluate to a bool: None + """ - return other in self._sage_() + if element.is_symbol: + # keep symbolic + return None + return element in self._sage_() def __len__(self): """ + Return the cardinality of the finite set ``self``. + EXAMPLES:: sage: sB3 = WeylGroup(["B", 3])._sympy_(); sB3 From 2debc4957d4ef15c635abeaa217736aa80152a2b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 13:37:03 -0700 Subject: [PATCH 163/280] Matrix._sympy_: New --- src/sage/interfaces/sympy.py | 16 ++++++ src/sage/matrix/matrix1.pyx | 97 ++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/src/sage/interfaces/sympy.py b/src/sage/interfaces/sympy.py index b7419ab9f47..bb2e90fcd70 100644 --- a/src/sage/interfaces/sympy.py +++ b/src/sage/interfaces/sympy.py @@ -748,6 +748,17 @@ def _sympysage_crootof(self): from sage.symbolic.ring import SR return complex_root_of(self.args[0]._sage_(), SR(self.args[1])) +def _sympysage_matrix(self): + try: + return self._sage_object + except AttributeError: + from sympy.matrices import SparseMatrix + from sage.matrix.constructor import matrix + rows, cols = self.shape() + return matrix(rows, cols, self.todok(), + sparse=isinstance(self, SparseMatrix), + immutable=True) + def _sympysage_relational(self): """ EXAMPLES:: @@ -846,6 +857,7 @@ def sympy_init(): from sympy.integrals.integrals import Integral from sympy.polys.rootoftools import CRootOf from sympy.series.order import Order + from sympy.matrices import ImmutableMatrix, ImmutableSparseMatrix, Matrix, SparseMatrix Float._sage_ = _sympysage_float Integer._sage_ = _sympysage_integer @@ -854,6 +866,10 @@ def sympy_init(): NegativeInfinity._sage_ = _sympysage_ninfty ComplexInfinity._sage_ = _sympysage_uinfty sympy_nan._sage_ = _sympysage_nan + ImmutableMatrix._sage_ = _sympysage_matrix + ImmutableSparseMatrix._sage_ = _sympysage_matrix + Matrix._sage_ = _sympysage_matrix + SparseMatrix._sage_ = _sympysage_matrix Relational._sage_ = _sympysage_relational Exp1._sage_ = _sympysage_e Pi._sage_ = _sympysage_pi diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index f3007973a9d..8d522f9d239 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -524,6 +524,103 @@ cdef class Matrix(Matrix0): """ return scilab(self._scilab_init_()) + def _sympy_(self): + r""" + Return a SymPy matrix corresponding to ``self``. + + OUTPUT: + + - An instance of either an ``ImmutableMatrix`` or ``ImmutableSparseMatrix``, + regardless of whether ``self`` is mutable or not. + + EXAMPLES:: + + sage: A = matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]); A + [1 2 3] + [4 5 6] + [7 8 9] + sage: sA = A._sympy_(); sA + Matrix([ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9]]) + sage: type(sA) + + + sage: I = MatrixSpace(QQ, 5, 5, sparse=True).identity_matrix() + sage: sI = I._sympy_(); sI + Matrix([ + [1, 0, 0, 0, 0], + [0, 1, 0, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1]]) + sage: type(sI) + + + If ``self`` was immutable, then converting the result to Sage gives + back ``self``:: + + sage: immA = matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]], immutable=True) + sage: immA._sympy_()._sage_() is immA + True + + If ``self`` is mutable, then converting back to Sage creates a new matrix:: + + sage: sA._sage_() is A + Traceback (most recent call last): + ... + TypeError: 'tuple' object is not callable + True + sage: sA._sage_() == A + Traceback (most recent call last): + ... + TypeError: 'tuple' object is not callable + True + + Symbolic matrices are supported:: + + sage: M = matrix([[sin(x), cos(x)], [-cos(x), sin(x)]]); M + [ sin(x) cos(x)] + [-cos(x) sin(x)] + sage: sM = M._sympy_(); sM + Matrix([ + [ sin(x), cos(x)], + [-cos(x), sin(x)]]) + sage: sM.subs(x, pi/4) + Matrix([ + [ sqrt(2)/2, sqrt(2)/2], + [-sqrt(2)/2, sqrt(2)/2]]) + + TESTS: + + Dense 0-column/0-row matrices:: + + sage: ZeroCol = matrix(QQ, 3, 0, sparse=False); ZeroCol + [] + sage: sZeroCol = ZeroCol._sympy_(); sZeroCol + Matrix(3, 0, []) + + sage: ZeroRow = matrix(QQ, 0, 2, sparse=False); ZeroRow + [] + sage: sZeroRow = ZeroRow._sympy_(); sZeroRow + Matrix(0, 2, []) + + """ + from sage.interfaces.sympy import sympy_init + sympy_init() + from sympy.matrices import ImmutableMatrix, ImmutableSparseMatrix + if self.is_sparse(): + matrix = ImmutableSparseMatrix(self.nrows(), self.ncols(), + self.dict(copy=False)) + else: + if not self.nrows() or not self.ncols(): + matrix = ImmutableMatrix(self.nrows(), self.ncols(), ()) + else: + matrix = ImmutableMatrix(self.rows()) + if self.is_immutable(): + matrix._sage_object = self + return matrix def _sage_input_(self, sib, coerce): r""" From 2baae58a51cd4f136cf0f89e3e6484804983908b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 13:48:08 -0700 Subject: [PATCH 164/280] Sets.ParentMethods._sympy_: Call sympy_init --- src/sage/categories/sets_cat.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index c1e6fb98e5a..288a5204793 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -1723,6 +1723,8 @@ def _sympy_(self): True """ from sage.interfaces.sympy_wrapper import SageSet + from sage.interfaces.sympy import sympy_init + sympy_init() return SageSet(self) class ElementMethods: From 93fbb2be5b07dfa18b4b1aec6d30c3c2895c54ab Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 13:52:18 -0700 Subject: [PATCH 165/280] Call sympy_init in all added _sympy_ methods --- src/sage/categories/sets_cat.py | 2 ++ src/sage/modules/free_module.py | 2 ++ src/sage/rings/integer_ring.pyx | 2 ++ src/sage/rings/rational_field.py | 2 ++ src/sage/sets/non_negative_integers.py | 2 ++ src/sage/sets/positive_integers.py | 2 ++ src/sage/sets/real_set.py | 4 ++++ 7 files changed, 16 insertions(+) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index 6b2f57e18cf..4b17b8f23d2 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -2451,6 +2451,8 @@ def _sympy_(self): True """ from sympy import ProductSet + from sage.interfaces.sympy import sympy_init + sympy_init() return ProductSet(*self.cartesian_factors()) class ElementMethods: diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 36f668d99fe..c8617d1885b 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -5318,6 +5318,8 @@ def _sympy_(self): True """ from sympy import ProductSet + from sage.interfaces.sympy import sympy_init + sympy_init() return ProductSet(*([self.coordinate_ring()] * self.rank())) diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index 737dda9ecb3..2d4aa4eb8af 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -1477,6 +1477,8 @@ cdef class IntegerRing_class(PrincipalIdealDomain): Integers """ from sympy import Integers + from sage.interfaces.sympy import sympy_init + sympy_init() return Integers def _sage_input_(self, sib, coerced): diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index 2a4237f0dfc..c79d2655535 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -1586,6 +1586,8 @@ def _sympy_(self): Rationals """ from sympy import Rationals + from sage.interfaces.sympy import sympy_init + sympy_init() return Rationals def _sage_input_(self, sib, coerced): diff --git a/src/sage/sets/non_negative_integers.py b/src/sage/sets/non_negative_integers.py index adabc89a987..9b01ad6f3d4 100644 --- a/src/sage/sets/non_negative_integers.py +++ b/src/sage/sets/non_negative_integers.py @@ -233,4 +233,6 @@ def _sympy_(self): Naturals0 """ from sympy import Naturals0 + from sage.interfaces.sympy import sympy_init + sympy_init() return Naturals0 diff --git a/src/sage/sets/positive_integers.py b/src/sage/sets/positive_integers.py index 693ca04706c..7ed7d9657e9 100644 --- a/src/sage/sets/positive_integers.py +++ b/src/sage/sets/positive_integers.py @@ -86,4 +86,6 @@ def _sympy_(self): Naturals """ from sympy import Naturals + from sage.interfaces.sympy import sympy_init + sympy_init() return Naturals diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index f2eb7d97202..1f594c59752 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -433,6 +433,8 @@ def _sympy_(self): Interval.open(0, oo) """ from sympy import Interval + from sage.interfaces.sympy import sympy_init + sympy_init() return Interval(self.lower(), self.upper(), left_open=not self._lower_closed, right_open=not self._upper_closed) @@ -1869,6 +1871,8 @@ def _sympy_(self): False """ from sympy import Reals, Union + from sage.interfaces.sympy import sympy_init + sympy_init() if self.is_universe(): return Reals else: From bd4b6699689e5fc1162d8c6d3f6bd887cdc91929 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:06:22 -0700 Subject: [PATCH 166/280] sage.interfaces.sympy._sympysage_matrix: Complete implementation --- src/sage/interfaces/sympy.py | 44 ++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/sage/interfaces/sympy.py b/src/sage/interfaces/sympy.py index bb2e90fcd70..4c271e20b06 100644 --- a/src/sage/interfaces/sympy.py +++ b/src/sage/interfaces/sympy.py @@ -749,13 +749,53 @@ def _sympysage_crootof(self): return complex_root_of(self.args[0]._sage_(), SR(self.args[1])) def _sympysage_matrix(self): + """ + Convert SymPy matrix ``self`` to Sage. + + EXAMPLES:: + + sage: from sympy.matrices import Matrix, SparseMatrix + sage: from sage.interfaces.sympy import sympy_init + sage: from sympy.abc import x + sage: sympy_init() + sage: sM = Matrix([[1, x + 1], [x - 1, 1]]); sM + Matrix([ + [ 1, x + 1], + [x - 1, 1]]) + sage: M = sM._sage_(); M + [ 1 x + 1] + [x - 1 1] + sage: M.parent() + Full MatrixSpace of 2 by 2 dense matrices over Symbolic Ring + + sage: sN = SparseMatrix.eye(3); sN + Matrix([ + [1, 0, 0], + [0, 1, 0], + [0, 0, 1]]) + sage: N = sN._sage_(); N + [1 0 0] + [0 1 0] + [0 0 1] + sage: N.parent() + Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring + """ try: return self._sage_object except AttributeError: from sympy.matrices import SparseMatrix from sage.matrix.constructor import matrix - rows, cols = self.shape() - return matrix(rows, cols, self.todok(), + from sage.structure.element import get_coercion_model + from sage.symbolic.ring import SR + + rows, cols = self.shape + d = {row_col: value._sage_() + for row_col, value in self.todok().items()} + coercion_model = get_coercion_model() + base_ring = coercion_model.common_parent(*d.values()) + if base_ring is None: + base_ring = SR + return matrix(base_ring, rows, cols, d, sparse=isinstance(self, SparseMatrix), immutable=True) From 26ce752f37ef75ace9432260ac55240fcc884289 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:09:34 -0700 Subject: [PATCH 167/280] FreeModuleElement._sympy_: New --- src/sage/matrix/matrix1.pyx | 2 +- src/sage/modules/free_module_element.pyx | 66 ++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index 8d522f9d239..eff0a61037f 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -565,7 +565,7 @@ cdef class Matrix(Matrix0): sage: immA._sympy_()._sage_() is immA True - If ``self`` is mutable, then converting back to Sage creates a new matrix:: + If ``self`` was mutable, then converting back to Sage creates a new matrix:: sage: sA._sage_() is A Traceback (most recent call last): diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 1bbe475695c..e17ec2a6241 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -3543,6 +3543,72 @@ cdef class FreeModuleElement(Vector): # abstract base class """ return '{' + ', '.join(x._mathematica_init_() for x in self.list()) + '}' + def _sympy_(self): + """ + Return a SymPy column vector (matrix) corresponding to ``self``. + + OUTPUT: + + - An instance of either an ``ImmutableMatrix`` or ``ImmutableSparseMatrix``, + regardless of whether ``self`` is mutable or not. + + EXAMPLES:: + + sage: v = vector([1, 2, 3]); v + (1, 2, 3) + sage: sv = v._sympy_(); sv + Matrix([ + [1], + [2], + [3]]) + sage: type(sv) + + + sage: w = vector({1: 1, 5: -1}, sparse=True) + sage: sw = w._sympy_(); sw + Matrix([ + [ 0], + [ 1], + [ 0], + [ 0], + [ 0], + [-1]]) + sage: type(sw) + + + If ``self`` was immutable, then converting the result to Sage gives + back ``self``:: + + sage: immv = vector([1, 2, 3], immutable=True) + sage: immv._sympy_()._sage_() is immv + True + + If ``self`` was mutable, then converting back to Sage creates a new + matrix (column vector):: + + sage: sv._sage_() + [1] + [2] + [3] + sage: sv._sage_() is v + False + sage: sv._sage_() == v + False + """ + from sage.interfaces.sympy import sympy_init + sympy_init() + from sympy.matrices import ImmutableMatrix, ImmutableSparseMatrix + if self.is_sparse(): + matrix = ImmutableSparseMatrix(self._degree, 1, + {(i, 0): v + for i, v in self.dict(copy=False).items()}) + else: + matrix = ImmutableMatrix(self._degree, 1, + self.list(copy=False)) + if self.is_immutable(): + matrix._sage_object = self + return matrix + def nonzero_positions(self): """ Return the sorted list of integers ``i`` such that ``self[i] != 0``. From 9e0f73634dfff54ee42b2c30670cfa62d12de98f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:10:55 -0700 Subject: [PATCH 168/280] Matrix._sympy_: Fix up doctest --- src/sage/matrix/matrix1.pyx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index eff0a61037f..e56b1e8518b 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -568,14 +568,8 @@ cdef class Matrix(Matrix0): If ``self`` was mutable, then converting back to Sage creates a new matrix:: sage: sA._sage_() is A - Traceback (most recent call last): - ... - TypeError: 'tuple' object is not callable - True + False sage: sA._sage_() == A - Traceback (most recent call last): - ... - TypeError: 'tuple' object is not callable True Symbolic matrices are supported:: From c06c965d81713d698d6e6a73c69f3473aa26e1b2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:16:06 -0700 Subject: [PATCH 169/280] sage.interfaces.sympy_wrapper.SageSet: Add another doctest --- src/sage/interfaces/sympy_wrapper.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/interfaces/sympy_wrapper.py b/src/sage/interfaces/sympy_wrapper.py index 6c22b47f9c3..00f7b65dd27 100644 --- a/src/sage/interfaces/sympy_wrapper.py +++ b/src/sage/interfaces/sympy_wrapper.py @@ -48,6 +48,14 @@ class SageSet(Set): def __new__(cls, sage_set): r""" Construct a wrapper for a Sage set. + + TESTS:: + + sage: from sage.interfaces.sympy_wrapper import SageSet + sage: F = Set([1, 2]); F + {1, 2} + sage: sF = SageSet(F); sF + SageSet({1, 2}) """ return Basic.__new__(cls, sage_set) From 8abdc8b16f9762041c1c60bcb4672fefea12f4b4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:22:24 -0700 Subject: [PATCH 170/280] src/sage/functions/piecewise.py: Add coding header --- src/sage/functions/piecewise.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index a0276824a3a..4428543bf6b 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Piecewise-defined Functions From 9d41f0e3e56ba43797296074ffb9ac4fc36ce8c3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:24:57 -0700 Subject: [PATCH 171/280] src/sage/sets/real_set.py: Remove unnecessary import --- src/sage/sets/real_set.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/sets/real_set.py b/src/sage/sets/real_set.py index 433cd77cfb3..de987727717 100644 --- a/src/sage/sets/real_set.py +++ b/src/sage/sets/real_set.py @@ -85,8 +85,6 @@ class RealSet. # http://www.gnu.org/licenses/ #***************************************************************************** -import itertools - from sage.structure.richcmp import richcmp, richcmp_method from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation From 7af909561cb10213c8af97e77d05f3694060cf25 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:43:17 -0700 Subject: [PATCH 172/280] sage.interfaces.sympy._sympysage_matrix: Handle TypeError from common_parent --- src/sage/interfaces/sympy.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/sage/interfaces/sympy.py b/src/sage/interfaces/sympy.py index 4c271e20b06..dc568808e63 100644 --- a/src/sage/interfaces/sympy.py +++ b/src/sage/interfaces/sympy.py @@ -779,22 +779,39 @@ def _sympysage_matrix(self): [0 0 1] sage: N.parent() Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring + + sage: sO = SparseMatrix.zeros(3); sO + Matrix([ + [0, 0, 0], + [0, 0, 0], + [0, 0, 0]]) + sage: O = sO._sage_(); O + [0 0 0] + [0 0 0] + [0 0 0] + sage: O.parent() + Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring """ try: return self._sage_object except AttributeError: from sympy.matrices import SparseMatrix from sage.matrix.constructor import matrix - from sage.structure.element import get_coercion_model - from sage.symbolic.ring import SR rows, cols = self.shape d = {row_col: value._sage_() for row_col, value in self.todok().items()} - coercion_model = get_coercion_model() - base_ring = coercion_model.common_parent(*d.values()) - if base_ring is None: - base_ring = SR + if not d: + from sage.rings.integer_ring import ZZ + base_ring = ZZ + else: + from sage.structure.element import get_coercion_model + from sage.symbolic.ring import SR + coercion_model = get_coercion_model() + try: + base_ring = coercion_model.common_parent(*d.values()) + except TypeError: # no common canonical parent + base_ring = SR return matrix(base_ring, rows, cols, d, sparse=isinstance(self, SparseMatrix), immutable=True) From c365f1877c0369000930d4bd6f750db7a3c09e3d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 9 Jun 2021 17:51:05 -0700 Subject: [PATCH 173/280] sage.interfaces.sympy._sympysage_matrix: Cache the result if self is immutable --- src/sage/interfaces/sympy.py | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/sage/interfaces/sympy.py b/src/sage/interfaces/sympy.py index dc568808e63..a64a58c8fa2 100644 --- a/src/sage/interfaces/sympy.py +++ b/src/sage/interfaces/sympy.py @@ -754,7 +754,7 @@ def _sympysage_matrix(self): EXAMPLES:: - sage: from sympy.matrices import Matrix, SparseMatrix + sage: from sympy.matrices import Matrix, SparseMatrix, ImmutableMatrix sage: from sage.interfaces.sympy import sympy_init sage: from sympy.abc import x sage: sympy_init() @@ -791,11 +791,33 @@ def _sympysage_matrix(self): [0 0 0] sage: O.parent() Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring + + If ``self`` is immutable, the result is cached:: + + sage: sImmM = ImmutableMatrix([[1, x + 1], [x - 1, 1]]); sImmM + Matrix([ + [ 1, x + 1], + [x - 1, 1]]) + sage: ImmM = sImmM._sage_(); ImmM + [ 1 x + 1] + [x - 1 1] + sage: ImmM is sImmM._sage_() + True + + If ``self`` is mutable, the conversion is redone every time:: + + sage: sM[0, 0] = 1000 + sage: MutatedM = sM._sage_(); MutatedM + [ 1000 x + 1] + [x - 1 1] + sage: M == MutatedM + False + """ try: return self._sage_object except AttributeError: - from sympy.matrices import SparseMatrix + from sympy.matrices import SparseMatrix, ImmutableMatrix from sage.matrix.constructor import matrix rows, cols = self.shape @@ -812,9 +834,12 @@ def _sympysage_matrix(self): base_ring = coercion_model.common_parent(*d.values()) except TypeError: # no common canonical parent base_ring = SR - return matrix(base_ring, rows, cols, d, - sparse=isinstance(self, SparseMatrix), - immutable=True) + result = matrix(base_ring, rows, cols, d, + sparse=isinstance(self, SparseMatrix), + immutable=True) + if isinstance(self, ImmutableMatrix): + self._sage_object = result + return result def _sympysage_relational(self): """ From 50c1ded26ccc4a7eb76f007dd28c4693620c41b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 10 Jun 2021 13:00:02 +0200 Subject: [PATCH 174/280] =?UTF-8?q?Poincar=C3=A9=20polynomial=20of=20modul?= =?UTF-8?q?i=20space=20of=20semi-stable=20quiver=20representations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../combinat/cluster_algebra_quiver/quiver.py | 111 +++++++++++++++++- 1 file changed, 109 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver.py b/src/sage/combinat/cluster_algebra_quiver/quiver.py index 850f5531c66..04615355c10 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Quiver @@ -41,6 +42,13 @@ from sage.rings.all import ZZ, CC, infinity from sage.graphs.all import Graph, DiGraph from sage.graphs.views import EdgesView +from sage.arith.misc import gcd +from sage.modules.free_module_element import vector +from sage.matrix.constructor import matrix +from sage.categories.cartesian_product import cartesian_product +from sage.misc.misc_c import prod +from sage.rings.rational_field import QQ +from sage.rings.polynomial.polynomial_ring import polygen from sage.combinat.cluster_algebra_quiver.quiver_mutation_type import QuiverMutationType, QuiverMutationType_Irreducible, QuiverMutationType_Reducible, _edge_list_to_matrix from sage.combinat.cluster_algebra_quiver.mutation_class import _principal_part, _digraph_mutate, _matrix_to_digraph, _dg_canonical_form, _mutation_class_iter, _digraph_to_dig6, _dig6_to_matrix from sage.combinat.cluster_algebra_quiver.mutation_type import _connected_mutation_type, _mutation_type_from_data, is_mutation_finite @@ -1919,7 +1927,7 @@ def is_mutation_finite( self, nr_of_checks=None, return_path=False ): # and getting the corresponding matrix M = _dig6_to_matrix(dig6) - is_finite, path = is_mutation_finite(M,nr_of_checks=nr_of_checks) + is_finite, path = is_mutation_finite(M, nr_of_checks=nr_of_checks) if return_path: return is_finite, path else: @@ -1992,7 +2000,8 @@ def relabel(self, relabelling, inplace=True): # If the key is in the old vertices, use that mapping digraph_labels[key] = val # And place it in the right order for our dictionary - loc = [i for i,x in enumerate(old_vertices) if x == key][0] + loc = [i for i, x in enumerate(old_vertices) + if x == key][0] dict_labels[loc] = val elif isinstance(key, int) and len(old_vertices) > key: # If the key is an integer, grab that particular vertex @@ -2004,6 +2013,104 @@ def relabel(self, relabelling, inplace=True): quiver._vertex_dictionary = dict_labels return quiver + def poincare_semistable(self, theta, d): + r""" + Return the Poincaré polynomial of the moduli space of semi-stable + representations of dimension vector `d`. + + INPUT: + + - ``theta`` -- stability weight, as list or vector of rationals + - ``d`` -- dimension vector, as list or vector of coprime integers + + The semi-stability is taken with respect to the slope function + + .. MATH:: + + \mu(d) = \theta(d) / \operatorname{dim}(d) + + where `d` is a dimension vector. + + This uses the matrix-inversion algorithm from [Rei2002]_. + + EXAMPLES:: + + sage: Q = ClusterQuiver(['A',2]) + sage: Q.poincare_semistable([1,0],[1,0]) + 1 + sage: Q.poincare_semistable([1,0],[1,1]) + 1 + + sage: K2 = ClusterQuiver(matrix([[0,2],[-2,0]])) + sage: theta = (1, 0) + sage: K2.poincare_semistable(theta, [1,0]) + 1 + sage: K2.poincare_semistable(theta, [1,1]) + v^2 + 1 + sage: K2.poincare_semistable(theta, [1,2]) + 1 + sage: K2.poincare_semistable(theta, [1,3]) + 0 + + sage: K3 = ClusterQuiver(matrix([[0,3],[-3,0]])) + sage: theta = (1, 0) + sage: K3.poincare_semistable(theta, (2,3)) + v^12 + v^10 + 3*v^8 + 3*v^6 + 3*v^4 + v^2 + 1 + sage: K3.poincare_semistable(theta, (3,4))(1) + 68 + + TESTS:: + + sage: Q = ClusterQuiver(['A',2]) + sage: Q.poincare_semistable([1,0],[2,2]) + Traceback (most recent call last): + ... + ValueError: dimension vector d is not coprime + + REFERENCES: + + .. [Rei2002] Markus Reineke, *The Harder-Narasimhan system in quantum + groups and cohomology of quiver moduli*, :arxiv:`math/0204059` + """ + if gcd([x for x in d if x]) != 1: + raise ValueError("dimension vector d is not coprime") + d = vector(ZZ, d) + theta = vector(theta) + + n = self.n() + b_mat = self.b_matrix() + Eu = matrix(ZZ, n, n, + lambda i, j: -b_mat[i, j] if b_mat[i, j] > 0 else 0) + Eu = 1 + Eu + + mu_d = theta.dot_product(d) / sum(d) + + Li = [0 * d] + it = (vector(e) for e in cartesian_product([range(d_i + 1) + for d_i in d])) + Li += [e for e in it if e.dot_product(theta) > mu_d * sum(e)] + Li.append(d) + + v = polygen(QQ, 'v') + q = v**2 + + def cardinal_RG(d): + cardinal_G = prod(q**d_i - q**k for d_i in d for k in range(d_i)) + cardinal_R = prod(q**(b_mat[i, j] * d[i] * d[j]) + for i, j in self.digraph().edges(labels=False)) + return cardinal_R / cardinal_G + + Reineke_mat = matrix(v.parent().fraction_field(), len(Li), len(Li), 1) + + for i, e in enumerate(Li): + for j, f in enumerate(Li): + f_e = f - e + if all(x >= 0 for x in f_e): + power = (-f_e) * Eu * e + Reineke_mat[i, j] = q**power * cardinal_RG(f_e) + + return ((1 - v**2) * Reineke_mat.inverse()[0, -1]).numerator() + def d_vector_fan(self): r""" Return the d-vector fan associated with the quiver. From 73e39ce9c9121c34da6d3ced36b36262ae4e9026 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:43:40 -0700 Subject: [PATCH 175/280] ConvexRationalPolyhedralCone.is_compact: Define --- src/sage/geometry/cone.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index aa1acf765ac..8e42648aef5 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -3311,6 +3311,8 @@ def is_trivial(self): """ return self.nrays() == 0 + is_compact = is_trivial + def is_strictly_convex(self): r""" Check if ``self`` is strictly convex. From 92f0610e33b15cba97247b787bbcc0a9593d9d34 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:45:44 -0700 Subject: [PATCH 176/280] ConvexSet_open: New --- src/sage/geometry/convex_set.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 5a531dd9c44..f77498b4fb1 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -196,9 +196,8 @@ def is_compact(self): class ConvexSet_relatively_open(ConvexSet_base): - r""" - Abstract base class for relatively open sets. + Abstract base class for relatively open convex sets. """ def is_relatively_open(self): @@ -222,3 +221,31 @@ def is_open(self): """ return self.is_full_dimensional() + + +class ConvexSet_open(ConvexSet_relatively_open): + r""" + Abstract base class for open convex sets. + """ + + def is_open(self): + r""" + Return whether ``self`` is open. + + OUTPUT: + + Boolean. + + """ + return True + + def is_closed(self): + r""" + Return whether ``self`` is closed. + + OUTPUT: + + Boolean. + + """ + return self.is_empty() or self.is_universe() From 03a31efc560cffebe2cf270ae7d460302307ac95 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:48:50 -0700 Subject: [PATCH 177/280] Polyhedron_base.is_full_dimensional: Merge into ConvexSet_base.is_full_dimensional --- src/sage/geometry/convex_set.py | 11 +++++++++++ src/sage/geometry/polyhedron/base.py | 18 ------------------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index f77498b4fb1..cd8dd1d9911 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -63,6 +63,17 @@ def is_full_dimensional(self): Boolean. Whether the polyhedron is not contained in any strict affine subspace. + + EXAMPLES:: + + sage: c = Cone([(1,0)]) + sage: c.is_full_dimensional() + False + + sage: polytopes.hypercube(3).is_full_dimensional() + True + sage: Polyhedron(vertices=[(1,2,3)], rays=[(1,0,0)]).is_full_dimensional() + False """ return self.dim() == self.ambient_dim() diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 03ee54f8077..8e242132313 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -9814,24 +9814,6 @@ def edge_label(i, j, c_ij): else: return MatrixGroup(matrices) - def is_full_dimensional(self): - """ - Return whether the polyhedron is full dimensional. - - OUTPUT: - - Boolean. Whether the polyhedron is not contained in any strict - affine subspace. - - EXAMPLES:: - - sage: polytopes.hypercube(3).is_full_dimensional() - True - sage: Polyhedron(vertices=[(1,2,3)], rays=[(1,0,0)]).is_full_dimensional() - False - """ - return self.dim() == self.ambient_dim() - def is_combinatorially_isomorphic(self, other, algorithm='bipartite_graph'): r""" Return whether the polyhedron is combinatorially isomorphic to another polyhedron. From a71507e11c33cbc2c61600b76098a814e6132f1d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:49:54 -0700 Subject: [PATCH 178/280] ConvexSet_base: Add some doctests --- src/sage/geometry/convex_set.py | 41 +++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index cd8dd1d9911..53caea0b12b 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -16,7 +16,6 @@ from sage.misc.abstract_method import abstract_method class ConvexSet_base(SageObject): - """ Abstract base class for convex sets. """ @@ -28,6 +27,14 @@ def is_empty(self): OUTPUT: Boolean. + + EXAMPLES:: + + sage: p = LatticePolytope([], lattice=ToricLattice(3).dual()); p + -1-d lattice polytope in 3-d lattice M + sage: p.is_empty() + True + """ return self.dim() < 0 @@ -90,12 +97,24 @@ def is_open(self): def is_relatively_open(self): r""" - Return whether ``self`` is open. + Return whether ``self`` is relatively open. + + The default implementation of this method only knows that open + sets are also relatively open. OUTPUT: Boolean. + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def is_open(self): + ....: return True + sage: ExampleSet().is_relatively_open() + True + """ if self.is_open(): return True @@ -123,13 +142,20 @@ def is_compact(self): """ if not self.is_closed(): return False - if self.dimension() < 1: + if self.dim() < 1: return True raise NotImplementedError def closure(self): r""" Return the topological closure of ``self``. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_closed + sage: C = ConvexSet_closed() + sage: C.closure() is C + True """ if self.is_closed(): return self @@ -138,6 +164,13 @@ def closure(self): def interior(self): r""" Return the topological interior of ``self``. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_open + sage: C = ConvexSet_open() + sage: C.interior() is C + True """ if self.is_open(): return self @@ -151,7 +184,6 @@ def affine_hull(self): class ConvexSet_closed(ConvexSet_base): - r""" Abstract base class for closed convex sets. """ @@ -179,7 +211,6 @@ def is_open(self): class ConvexSet_compact(ConvexSet_closed): - r""" Abstract base class for compact convex sets. """ From 3a831826846c776c95a51133e1b4e216d248acc3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:50:23 -0700 Subject: [PATCH 179/280] ConvexSet_base.relative_interior: New --- src/sage/geometry/convex_set.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 53caea0b12b..114018dc629 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -176,6 +176,21 @@ def interior(self): return self raise NotImplementedError + def relative_interior(self): + r""" + Return the relative interior of ``self``. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_relatively_open + sage: C = ConvexSet_relatively_open() + sage: C.relative_interior() is C + True + """ + if self.is_relatively_open(): + return self + raise NotImplementedError + @abstract_method(optional=True) def affine_hull(self): r""" From 6a0baac5f679d90a1acd3696f3613350af5af9f1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 11:50:51 -0700 Subject: [PATCH 180/280] ConvexSet_base._test_convex_set: New --- src/sage/geometry/convex_set.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 114018dc629..8c5a0019c16 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -197,6 +197,37 @@ def affine_hull(self): Return the affine hull of ``self``. """ + def _test_convex_set(self, tester=None, **options): + """ + Run some tests on the methods of :class:`ConvexSet_base`. + """ + if tester is None: + tester = self._tester(**options) + dim = self.dim() + tester.assertTrue(dim <= self.ambient_dim()) + if self.is_empty(): + tester.assertTrue(dim == -1) + if self.is_universe(): + tester.assertTrue(self.is_full_dimensional()) + cl_self = self.closure() + try: + int_self = self.interior() + except NotImplementedError: + int_self = None + try: + relint_self = self.relative_interior() + except NotImplementedError: + relint_self = None + if self.is_full_dimensional(): + tester.assertTrue(int_self == relint_self) + if self.is_relatively_open(): + tester.assertTrue(self == relint_self) + if self.is_open(): + tester.assertTrue(self == int_self) + if self.is_closed(): + tester.assertTrue(self == cl_self) + if self.is_compact(): + tester.assertTrue(self.is_closed()) class ConvexSet_closed(ConvexSet_base): r""" From 0495bb05c37f55257b892c6790234d9fb5dd77f5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 16:34:03 -0700 Subject: [PATCH 181/280] RelativeInterior: Fix up doctest --- src/sage/geometry/relative_interior.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index e0dfc9ce6b9..795ef408692 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -47,7 +47,7 @@ def __init__(self, polyhedron): TESTS:: - sage: P = Polyhedron() + sage: P = Polyhedron([[1, 2], [3, 4]]) sage: from sage.geometry.relative_interior import RelativeInterior sage: TestSuite(RelativeInterior(P)).run() """ From 9a7ce3a53d63ace6ed56f4addc92f61b85549b32 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 17:22:27 -0700 Subject: [PATCH 182/280] src/sage/geometry/convex_set.py: More examples and tests --- src/sage/geometry/convex_set.py | 141 ++++++++++++++++++++++++++++++-- 1 file changed, 133 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 8c5a0019c16..9ea892c99b9 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -22,7 +22,7 @@ class ConvexSet_base(SageObject): def is_empty(self): r""" - Test whether ``self`` is the empty set + Test whether ``self`` is the empty set. OUTPUT: @@ -34,13 +34,12 @@ def is_empty(self): -1-d lattice polytope in 3-d lattice M sage: p.is_empty() True - """ return self.dim() < 0 def is_universe(self): r""" - Test whether ``self`` is the whole ambient space + Test whether ``self`` is the whole ambient space. OUTPUT: @@ -84,23 +83,39 @@ def is_full_dimensional(self): """ return self.dim() == self.ambient_dim() - @abstract_method def is_open(self): r""" Return whether ``self`` is open. + The default implementation of this method only knows that the + empty set and the ambient space are open. + OUTPUT: Boolean. + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def is_empty(self): + ....: return False + ....: def is_universe(self): + ....: return True + sage: ExampleSet().is_open() + True """ + if self.is_empty() or self.is_universe(): + return True + raise NotImplementedError def is_relatively_open(self): r""" Return whether ``self`` is relatively open. The default implementation of this method only knows that open - sets are also relatively open. + sets are also relatively open, and in addition singletons are + relatively open. OUTPUT: @@ -114,31 +129,55 @@ def is_relatively_open(self): ....: return True sage: ExampleSet().is_relatively_open() True - """ if self.is_open(): return True + if self.dim() == 0: + return True raise NotImplementedError - @abstract_method def is_closed(self): r""" Return whether ``self`` is closed. + The default implementation of this method only knows that the + empty set, a singleton set, and the ambient space are closed. + OUTPUT: Boolean. + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def dim(self): + ....: return 0 + sage: ExampleSet().is_closed() + True """ + if self.is_empty() or self.dim() == 0 or self.is_universe(): + return True + raise NotImplementedError def is_compact(self): r""" Return whether ``self`` is compact. + The default implementation of this method only knows that a + non-closed set cannot be compact, and that the empty set and + a singleton set are compact. + OUTPUT: Boolean. + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def dim(self): + ....: return 0 + sage: ExampleSet().is_compact() + True """ if not self.is_closed(): return False @@ -200,6 +239,32 @@ def affine_hull(self): def _test_convex_set(self, tester=None, **options): """ Run some tests on the methods of :class:`ConvexSet_base`. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_open + sage: class FaultyConvexSet(ConvexSet_open): + ....: def is_universe(self): + ....: return True + ....: def dim(self): + ....: return 42 + ....: def ambient_dim(self): + ....: return 91 + sage: TestSuite(FaultyConvexSet()).run(skip='_test_pickling') + Traceback (most recent call last): + ... + The following tests failed: _test_convex_set + + sage: class BiggerOnTheInside(ConvexSet_open): + ....: def dim(self): + ....: return 100000 + ....: def ambient_dim(self): + ....: return 3 + sage: TestSuite(BiggerOnTheInside()).run(skip='_test_pickling') + Traceback (most recent call last): + ... + The following tests failed: _test_convex_set + """ if tester is None: tester = self._tester(**options) @@ -241,6 +306,12 @@ def is_closed(self): OUTPUT: Boolean. + + EXAMPLES:: + + sage: hcube = polytopes.hypercube(5) + sage: hcube.is_closed() + True """ return True @@ -252,6 +323,15 @@ def is_open(self): Boolean. + EXAMPLES:: + + sage: hcube = polytopes.hypercube(5) + sage: hcube.is_open() + False + + sage: zerocube = polytopes.hypercube(0) + sage: zerocube.is_open() + True """ return self.is_empty() or self.is_universe() @@ -268,6 +348,16 @@ def is_universe(self): OUTPUT: Boolean. + + EXAMPLES:: + + sage: cross3 = lattice_polytope.cross_polytope(3) + sage: cross3.is_universe() + False + sage: point0 = LatticePolytope([[]]); point0 + 0-d reflexive polytope in 0-d lattice M + sage: point0.is_universe() + True """ return self.ambient_dim() == 0 and not self.is_empty() @@ -279,9 +369,16 @@ def is_compact(self): Boolean. + EXAMPLES:: + + sage: cross3 = lattice_polytope.cross_polytope(3) + sage: cross3.is_compact() + True """ return True + is_relatively_open = ConvexSet_closed.is_open + class ConvexSet_relatively_open(ConvexSet_base): r""" @@ -296,6 +393,12 @@ def is_relatively_open(self): Boolean. + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior() + sage: ri_segment.is_relatively_open() + True """ return True @@ -307,8 +410,14 @@ def is_open(self): Boolean. + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior() + sage: ri_segment.is_open() + False """ - return self.is_full_dimensional() + return self.is_empty() or self.is_full_dimensional() class ConvexSet_open(ConvexSet_relatively_open): @@ -324,6 +433,12 @@ def is_open(self): Boolean. + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_open + sage: b = ConvexSet_open() + sage: b.is_open() + True """ return True @@ -335,5 +450,15 @@ def is_closed(self): Boolean. + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_open + sage: class OpenBall(ConvexSet_open): + ....: def dim(self): + ....: return 3 + ....: def is_universe(self): + ....: return False + sage: OpenBall().is_closed() + False """ return self.is_empty() or self.is_universe() From e2b0ef7390426c7d8f6f7f0a1382b326d5c9bc6e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 18:39:17 -0700 Subject: [PATCH 183/280] ConvexSet_base._test_convex_set: Fix doctest output --- src/sage/geometry/convex_set.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 9ea892c99b9..4372d90a1cd 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -251,7 +251,7 @@ def _test_convex_set(self, tester=None, **options): ....: def ambient_dim(self): ....: return 91 sage: TestSuite(FaultyConvexSet()).run(skip='_test_pickling') - Traceback (most recent call last): + Failure in _test_convex_set: ... The following tests failed: _test_convex_set @@ -261,7 +261,7 @@ def _test_convex_set(self, tester=None, **options): ....: def ambient_dim(self): ....: return 3 sage: TestSuite(BiggerOnTheInside()).run(skip='_test_pickling') - Traceback (most recent call last): + Failure in _test_convex_set: ... The following tests failed: _test_convex_set From 45c840a98ea222b30847b6ae8411d52f7cd778ee Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 21:41:58 -0700 Subject: [PATCH 184/280] ConvexSet_base.codim, codimension: New --- src/sage/geometry/cone.py | 3 +++ src/sage/geometry/convex_set.py | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index 8e42648aef5..eab23498e85 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -1225,8 +1225,11 @@ def codim(self): sage: K.codim() == K.dual().lineality() True """ + # same as ConvexSet_base.codim; the main point is the much more detailed + # docstring. return (self.lattice_dim() - self.dim()) + codimension = codim def span(self, base_ring=None): r""" diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 4372d90a1cd..dab6e61b25e 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -61,6 +61,21 @@ def ambient_dim(self): Return the dimension of the ambient space. """ + def codimension(self): + r""" + Return the codimension of ``self``. + + An alias is :meth:`codim`. + + EXAMPLES:: + + sage: Polyhedron(vertices=[(1,2,3)], rays=[(1,0,0)]).codimension() + 2 + """ + return self.ambient_dim() - self.dim() + + codim = codimension + def is_full_dimensional(self): r""" Return whether ``self`` is full dimensional. @@ -269,7 +284,10 @@ def _test_convex_set(self, tester=None, **options): if tester is None: tester = self._tester(**options) dim = self.dim() + codim = self.codim() tester.assertTrue(dim <= self.ambient_dim()) + if dim >= 0: + tester.assertTrue(dim + codim == self.ambient_dim()) if self.is_empty(): tester.assertTrue(dim == -1) if self.is_universe(): From 17467c498978ca9f165b0619b30d5ab2fc84272b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 21:43:20 -0700 Subject: [PATCH 185/280] ConvexSet_base: Make dimension, ambient_dimension aliases for dim, ambient_dim --- src/sage/geometry/convex_set.py | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index dab6e61b25e..d2ae244dd3c 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -55,12 +55,46 @@ def dim(self): Return the dimension of ``self``. """ + def dimension(self): + r""" + Return the dimension of ``self``. + + This is the same as :meth:`dim`. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def dim(self): + ....: return 42 + sage: ExampleSet().dimension() + 42 + """ + return self.dim() + @abstract_method def ambient_dim(self): r""" Return the dimension of the ambient space. """ + def ambient_dimension(self): + r""" + Return the dimension of ``self``. + + This is the same as :meth:`ambient_dim`. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def ambient_dim(self): + ....: return 91 + sage: ExampleSet().ambient_dimension() + 91 + """ + return self.ambient_dim() + def codimension(self): r""" Return the codimension of ``self``. From fa5dc6eb2c696c0094261c76b72a16cb0bc92846 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 21:47:03 -0700 Subject: [PATCH 186/280] ConvexSet_base.cartesian_product: New --- src/sage/geometry/convex_set.py | 29 ++++++++++++++++++++++------ src/sage/geometry/polyhedron/base.py | 2 ++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index d2ae244dd3c..465e975c15c 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -279,12 +279,6 @@ def relative_interior(self): return self raise NotImplementedError - @abstract_method(optional=True) - def affine_hull(self): - r""" - Return the affine hull of ``self``. - """ - def _test_convex_set(self, tester=None, **options): """ Run some tests on the methods of :class:`ConvexSet_base`. @@ -346,6 +340,29 @@ def _test_convex_set(self, tester=None, **options): if self.is_compact(): tester.assertTrue(self.is_closed()) + # Optional methods + + @abstract_method(optional=True) + def affine_hull(self): + r""" + Return the affine hull of ``self``. + """ + + @abstract_method(optional=True) + def cartesian_product(self, other): + """ + Return the Cartesian product. + + INPUT: + + - ``other`` -- another convex set + + OUTPUT: + + The Cartesian product of ``self`` and ``other``. + """ + + class ConvexSet_closed(ConvexSet_base): r""" Abstract base class for closed convex sets. diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 8e242132313..1d17d27df09 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -4817,6 +4817,8 @@ def product(self, other): _mul_ = product + cartesian_product = product + def _test_product(self, tester=None, **options): """ Run tests on the method :meth:`.product`. From f4bdffda473c608289d6b8c90a3687484d24f3be Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 10 Jun 2021 21:58:07 -0700 Subject: [PATCH 187/280] ConvexSet_base.contains, intersection: New --- src/sage/geometry/convex_set.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 465e975c15c..6827b0a10d8 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -362,6 +362,30 @@ def cartesian_product(self, other): The Cartesian product of ``self`` and ``other``. """ + @abstract_method(optional=True) + def contains(self, point): + """ + Test whether ``self`` contains the given ``point``. + + INPUT: + + - ``point`` -- a point or its coordinates + """ + + @abstract_method(optional=True) + def intersection(self, other): + r""" + Return the intersection of ``self`` and ``other``. + + INPUT: + + - ``other`` -- another convex set + + OUTPUT: + + The intersection. + """ + class ConvexSet_closed(ConvexSet_base): r""" From 2f0f4c05692a26463c41c9032728e04e6d3aa158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 11 Jun 2021 08:12:41 +0200 Subject: [PATCH 188/280] =?UTF-8?q?some=20details=20in=20Poincar=C3=A9=5Fs?= =?UTF-8?q?emistable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../combinat/cluster_algebra_quiver/quiver.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver.py b/src/sage/combinat/cluster_algebra_quiver/quiver.py index 04615355c10..1a6c2aedef6 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver.py @@ -2067,6 +2067,10 @@ def poincare_semistable(self, theta, d): ... ValueError: dimension vector d is not coprime + sage: Q = ClusterQuiver(['A',3]) + sage: Q.poincare_semistable([1,1,0],[2,3,4]) + 0 + REFERENCES: .. [Rei2002] Markus Reineke, *The Harder-Narasimhan system in quantum @@ -2082,6 +2086,7 @@ def poincare_semistable(self, theta, d): Eu = matrix(ZZ, n, n, lambda i, j: -b_mat[i, j] if b_mat[i, j] > 0 else 0) Eu = 1 + Eu + edges = list(self.digraph().edges(labels=False)) mu_d = theta.dot_product(d) / sum(d) @@ -2091,16 +2096,15 @@ def poincare_semistable(self, theta, d): Li += [e for e in it if e.dot_product(theta) > mu_d * sum(e)] Li.append(d) - v = polygen(QQ, 'v') - q = v**2 + q = polygen(QQ, 'v') # q stands for v**2 until the last line def cardinal_RG(d): cardinal_G = prod(q**d_i - q**k for d_i in d for k in range(d_i)) cardinal_R = prod(q**(b_mat[i, j] * d[i] * d[j]) - for i, j in self.digraph().edges(labels=False)) + for i, j in edges) return cardinal_R / cardinal_G - Reineke_mat = matrix(v.parent().fraction_field(), len(Li), len(Li), 1) + Reineke_mat = matrix(q.parent().fraction_field(), len(Li), len(Li), 1) for i, e in enumerate(Li): for j, f in enumerate(Li): @@ -2109,7 +2113,8 @@ def cardinal_RG(d): power = (-f_e) * Eu * e Reineke_mat[i, j] = q**power * cardinal_RG(f_e) - return ((1 - v**2) * Reineke_mat.inverse()[0, -1]).numerator() + poly = ((1 - q) * Reineke_mat.inverse()[0, -1]).numerator() + return poly(q**2) # replacing q by v**2 def d_vector_fan(self): r""" From 20e4de05a463ebdf843b2dffe22b1e2f19654341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 11 Jun 2021 08:58:18 +0200 Subject: [PATCH 189/280] =?UTF-8?q?using=20a=20det=20for=20the=20Poincar?= =?UTF-8?q?=C3=A9=20poly=20of=20moduli=20space?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/combinat/cluster_algebra_quiver/quiver.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver.py b/src/sage/combinat/cluster_algebra_quiver/quiver.py index 1a6c2aedef6..dedef28db11 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver.py @@ -2095,6 +2095,7 @@ def poincare_semistable(self, theta, d): for d_i in d])) Li += [e for e in it if e.dot_product(theta) > mu_d * sum(e)] Li.append(d) + N = len(Li) - 1 q = polygen(QQ, 'v') # q stands for v**2 until the last line @@ -2104,16 +2105,19 @@ def cardinal_RG(d): for i, j in edges) return cardinal_R / cardinal_G - Reineke_mat = matrix(q.parent().fraction_field(), len(Li), len(Li), 1) + Reineke_submat = matrix(q.parent().fraction_field(), N, N) - for i, e in enumerate(Li): - for j, f in enumerate(Li): + for i, e in enumerate(Li[:-1]): + for j, f in enumerate(Li[1:]): + if e == f: + Reineke_submat[i, j] = 1 + continue f_e = f - e if all(x >= 0 for x in f_e): power = (-f_e) * Eu * e - Reineke_mat[i, j] = q**power * cardinal_RG(f_e) + Reineke_submat[i, j] = q**power * cardinal_RG(f_e) - poly = ((1 - q) * Reineke_mat.inverse()[0, -1]).numerator() + poly = (-1)**N * ((1 - q) * Reineke_submat.det()).numerator() return poly(q**2) # replacing q by v**2 def d_vector_fan(self): From 7fbdf5d2d9b177bfa812427dcc83931e83c62be4 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 11 Jun 2021 21:13:58 +0200 Subject: [PATCH 190/280] replace unstable doctest by doctest according to code and documentation --- src/sage/modules/free_module_integer.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/sage/modules/free_module_integer.py b/src/sage/modules/free_module_integer.py index 3da0f6feacb..a3b201657a6 100644 --- a/src/sage/modules/free_module_integer.py +++ b/src/sage/modules/free_module_integer.py @@ -311,11 +311,7 @@ def reduced_basis(self): True sage: LLL = L.LLL() - sage: LLL == L.reduced_basis - True - sage: bool(min(a.norm() for a in LLL) == LLL[0].norm()) - True - sage: bool(LLL[0].norm() <= M[0].norm()) + sage: LLL == L.reduced_basis or bool(LLL[0].norm() >= M[0].norm()) True """ return self._reduced_basis From ee00642b382baf7b5ee7ed3b42e5bf6dcaa9fb64 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 14:12:19 -0700 Subject: [PATCH 191/280] ConvexSet_base.ambient: New; clarify ambient_dim, ambient_dimension, codimension --- src/sage/geometry/convex_set.py | 16 +++++++++++++--- src/sage/geometry/polyhedron/base.py | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 6827b0a10d8..f34bcb78493 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -72,15 +72,21 @@ def dimension(self): """ return self.dim() + @abstract_method + def ambient(self): + r""" + Return the ambient convex set or space. + """ + @abstract_method def ambient_dim(self): r""" - Return the dimension of the ambient space. + Return the dimension of the ambient convex set or space. """ def ambient_dimension(self): r""" - Return the dimension of ``self``. + Return the dimension of the ambient convex set or space. This is the same as :meth:`ambient_dim`. @@ -97,7 +103,7 @@ def ambient_dimension(self): def codimension(self): r""" - Return the codimension of ``self``. + Return the codimension of ``self`` in `self.ambient()``. An alias is :meth:`codim`. @@ -287,6 +293,8 @@ def _test_convex_set(self, tester=None, **options): sage: from sage.geometry.convex_set import ConvexSet_open sage: class FaultyConvexSet(ConvexSet_open): + ....: def ambient(self): + ....: return QQ^55 ....: def is_universe(self): ....: return True ....: def dim(self): @@ -301,6 +309,8 @@ def _test_convex_set(self, tester=None, **options): sage: class BiggerOnTheInside(ConvexSet_open): ....: def dim(self): ....: return 100000 + ....: def ambient(self): + ....: return QQ^3 ....: def ambient_dim(self): ....: return 3 sage: TestSuite(BiggerOnTheInside()).run(skip='_test_pickling') diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 1d17d27df09..0f9d65c4594 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -2478,7 +2478,7 @@ def Vrepresentation_space(self): """ return self.parent().Vrepresentation_space() - ambient_space = Vrepresentation_space + ambient = ambient_space = Vrepresentation_space def Hrepresentation_space(self): r""" From 2c756d43f92db60d12be732c515fee2a0928ea99 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 14:12:41 -0700 Subject: [PATCH 192/280] PolyhedronFace: Make it a subclass of ConvexSet_closed --- src/sage/geometry/polyhedron/face.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 7a00f46ab19..12baa5425b8 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -78,12 +78,12 @@ from sage.misc.all import cached_method from sage.modules.free_module_element import vector from sage.matrix.constructor import matrix - +from sage.geometry.convex_set import ConvexSet_closed ######################################################################### @richcmp_method -class PolyhedronFace(SageObject): +class PolyhedronFace(ConvexSet_closed): r""" A face of a polyhedron. @@ -121,6 +121,11 @@ class PolyhedronFace(SageObject): (An inequality (1, 1, 1) x + 1 >= 0,) sage: face.ambient_Vrepresentation() (A vertex at (-1, 0, 0), A vertex at (0, -1, 0), A vertex at (0, 0, -1)) + + TESTS:: + + sage: TestSuite(face).run() + """ def __init__(self, polyhedron, V_indices, H_indices): @@ -147,6 +152,7 @@ def __init__(self, polyhedron, V_indices, H_indices): sage: from sage.geometry.polyhedron.face import PolyhedronFace sage: PolyhedronFace(Polyhedron(), [], []) # indirect doctest A -1-dimensional face of a Polyhedron in ZZ^0 + sage: TestSuite(_).run() """ self._polyhedron = polyhedron self._ambient_Vrepresentation_indices = tuple(V_indices) @@ -649,6 +655,8 @@ def polyhedron(self): """ return self._polyhedron + ambient = polyhedron + @cached_method def as_polyhedron(self): """ From 8d77b3e67c27e24ccae8e529eddb4beece0e8a30 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 14:47:17 -0700 Subject: [PATCH 193/280] PolyhedronFace.is_relatively_open, is_compact: New, add doctests --- src/sage/geometry/polyhedron/face.py | 61 +++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 12baa5425b8..31f0de091e1 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -124,7 +124,7 @@ class PolyhedronFace(ConvexSet_closed): TESTS:: - sage: TestSuite(face).run() + sage: TestSuite(face).run(skip='_test_pickling') """ @@ -152,7 +152,7 @@ def __init__(self, polyhedron, V_indices, H_indices): sage: from sage.geometry.polyhedron.face import PolyhedronFace sage: PolyhedronFace(Polyhedron(), [], []) # indirect doctest A -1-dimensional face of a Polyhedron in ZZ^0 - sage: TestSuite(_).run() + sage: TestSuite(_).run(skip='_test_pickling') """ self._polyhedron = polyhedron self._ambient_Vrepresentation_indices = tuple(V_indices) @@ -186,6 +186,10 @@ def vertex_generator(self): A vertex at (1, 1) sage: type(face.vertex_generator()) <... 'generator'> + + TESTS:: + + sage: TestSuite(face).run(skip='_test_pickling') """ for V in self.ambient_Vrepresentation(): if V.is_vertex(): @@ -206,6 +210,10 @@ def vertices(self): sage: face = triangle.faces(1)[2] sage: face.vertices() (A vertex at (0, 1), A vertex at (1, 0)) + + TESTS:: + + sage: TestSuite(face).run(skip='_test_pickling') """ return tuple(self.vertex_generator()) @@ -224,6 +232,10 @@ def n_vertices(self): sage: face = Q.faces(2)[0] sage: face.n_vertices() 3 + + TESTS:: + + sage: TestSuite(face).run(skip='_test_pickling') """ return len(self.vertices()) @@ -237,6 +249,10 @@ def ray_generator(self): sage: face = pi.faces(1)[1] sage: next(face.ray_generator()) A ray in the direction (1, 0) + + TESTS:: + + sage: TestSuite(face).run(skip='_test_pickling') """ for V in self.ambient_Vrepresentation(): if V.is_ray(): @@ -288,6 +304,10 @@ def line_generator(self): sage: face = pr.faces(1)[0] sage: next(face.line_generator()) A line in the direction (1, 0) + + TESTS:: + + sage: TestSuite(face).run(skip='_test_pickling') """ for V in self.ambient_Vrepresentation(): if V.is_line(): @@ -657,6 +677,43 @@ def polyhedron(self): ambient = polyhedron + def is_relatively_open(self): + r""" + Return whether ``self`` is relatively open. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: half_plane = Polyhedron(ieqs=[(0,1,0)]) + sage: line = half_plane.faces(1)[0]; line + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line + sage: line.is_relatively_open() + True + """ + return self.as_polyhedron().is_relatively_open() + + def is_compact(self): + r""" + Return whether ``self`` is compact. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: half_plane = Polyhedron(ieqs=[(0,1,0)]) + sage: line = half_plane.faces(1)[0]; line + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line + sage: line.is_compact() + False + """ + return not any(V.is_ray() or V.is_line() + for V in self.ambient_Vrepresentation()) + @cached_method def as_polyhedron(self): """ From 6ba5d9c2bd5dca867d632b9f569407ac5f758e6f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 16:23:51 -0700 Subject: [PATCH 194/280] ConvexSet_base.ambient_vector_space: New --- src/sage/geometry/cone.py | 20 ++++++++++++++++ src/sage/geometry/convex_set.py | 10 ++++++++ src/sage/geometry/lattice_polytope.py | 20 ++++++++++++++++ src/sage/geometry/polyhedron/base.py | 34 +++++++++++++++++++++++++-- src/sage/geometry/polyhedron/face.py | 23 ++++++++++++++++++ 5 files changed, 105 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index eab23498e85..fd2074676c7 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -981,6 +981,26 @@ def lattice(self): """ return self._lattice + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + + It is the ambient lattice (:meth:`lattice`) tensored with a field. + + INPUT:: + + - ``base_field`` -- (default: the rationals) a field. + + EXAMPLES:: + + sage: c = Cone([(1,0)]) + sage: c.ambient_vector_space() + Vector space of dimension 2 over Rational Field + sage: c.ambient_vector_space(AA) + Vector space of dimension 2 over Algebraic Real Field + """ + return self.lattice().vector_space(base_field=base_field) + @cached_method def dual_lattice(self): r""" diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index f34bcb78493..fbb297d4ae7 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -72,6 +72,12 @@ def dimension(self): """ return self.dim() + @abstract_method + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + """ + @abstract_method def ambient(self): r""" @@ -295,6 +301,8 @@ def _test_convex_set(self, tester=None, **options): sage: class FaultyConvexSet(ConvexSet_open): ....: def ambient(self): ....: return QQ^55 + ....: def ambient_vector_space(self, base_field=None): + ....: return QQ^16 ....: def is_universe(self): ....: return True ....: def dim(self): @@ -309,6 +317,8 @@ def _test_convex_set(self, tester=None, **options): sage: class BiggerOnTheInside(ConvexSet_open): ....: def dim(self): ....: return 100000 + ....: def ambient_vector_space(self): + ....: return QQ^3 ....: def ambient(self): ....: return QQ^3 ....: def ambient_dim(self): diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index a063657e30a..bde2f0a6be7 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -2617,6 +2617,26 @@ def lattice_dim(self): ambient_dim = lattice_dim + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + + It is the ambient lattice (:meth:`lattice`) tensored with a field. + + INPUT:: + + - ``base_field`` -- (default: the rationals) a field. + + EXAMPLES:: + + sage: p = LatticePolytope([(1,0)]) + sage: p.ambient_vector_space() + Vector space of dimension 2 over Rational Field + sage: p.ambient_vector_space(AA) + Vector space of dimension 2 over Algebraic Real Field + """ + return self.lattice().vector_space(base_field=base_field) + def linearly_independent_vertices(self): r""" Return a maximal set of linearly independent vertices. diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 0f9d65c4594..67208a3b5d1 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -2462,7 +2462,7 @@ def bounded_edges(self): def Vrepresentation_space(self): r""" - Return the ambient vector space. + Return the ambient free module. OUTPUT: @@ -2478,7 +2478,37 @@ def Vrepresentation_space(self): """ return self.parent().Vrepresentation_space() - ambient = ambient_space = Vrepresentation_space + ambient_space = Vrepresentation_space + + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + + It is the ambient free module (:meth:`Vrepresentation_space`) tensored + with a field. + + INPUT:: + + - ``base_field`` -- (default: the fraction field of the base ring) a field. + + EXAMPLES:: + + sage: poly_test = Polyhedron(vertices = [[1,0,0,0],[0,1,0,0]]) + sage: poly_test.ambient_vector_space() + Vector space of dimension 4 over Rational Field + sage: poly_test.ambient_vector_space() is poly_test.ambient() + True + + sage: poly_test.ambient_vector_space(AA) + Vector space of dimension 4 over Algebraic Real Field + sage: poly_test.ambient_vector_space(RR) + Vector space of dimension 4 over Real Field with 53 bits of precision + sage: poly_test.ambient_vector_space(SR) + Vector space of dimension 4 over Symbolic Ring + """ + return self.Vrepresentation_space().vector_space(base_field=base_field) + + ambient = ambient_vector_space def Hrepresentation_space(self): r""" diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 31f0de091e1..af317977e5a 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -677,6 +677,29 @@ def polyhedron(self): ambient = polyhedron + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + + It is the ambient free module of the containing polyhedron tensored + with a field. + + INPUT:: + + - ``base_field`` -- (default: the fraction field of the base ring) a field. + + EXAMPLES:: + + sage: half_plane = Polyhedron(ieqs=[(0,1,0)]) + sage: line = half_plane.faces(1)[0]; line + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line + sage: line.ambient_vector_space() + Vector space of dimension 2 over Rational Field + sage: line.ambient_vector_space(AA) + Vector space of dimension 2 over Algebraic Real Field + """ + return self.polyhedron().ambient_vector_space(base_field=base_field) + def is_relatively_open(self): r""" Return whether ``self`` is relatively open. From e66448ea0b3de1286595ef4af19fe95c5d9ef18e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Jun 2021 16:24:30 -0700 Subject: [PATCH 195/280] PolyhedronFace.contains, ConvexSet_base._test_contains: New --- src/sage/geometry/convex_set.py | 16 +++++++++-- src/sage/geometry/polyhedron/face.py | 43 ++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index fbb297d4ae7..518ed2bc51f 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -309,7 +309,7 @@ def _test_convex_set(self, tester=None, **options): ....: return 42 ....: def ambient_dim(self): ....: return 91 - sage: TestSuite(FaultyConvexSet()).run(skip='_test_pickling') + sage: TestSuite(FaultyConvexSet()).run(skip=('_test_pickling', '_test_contains')) Failure in _test_convex_set: ... The following tests failed: _test_convex_set @@ -323,7 +323,7 @@ def _test_convex_set(self, tester=None, **options): ....: return QQ^3 ....: def ambient_dim(self): ....: return 3 - sage: TestSuite(BiggerOnTheInside()).run(skip='_test_pickling') + sage: TestSuite(BiggerOnTheInside()).run(skip=('_test_pickling', '_test_contains')) Failure in _test_convex_set: ... The following tests failed: _test_convex_set @@ -392,6 +392,18 @@ def contains(self, point): - ``point`` -- a point or its coordinates """ + def _test_contains(self, tester=None, **options): + """ + Test the ``contains`` method. + """ + if tester is None: + tester = self._tester(**options) + space = self.ambient_vector_space() + point = space.an_element() + coords = space.coordinates(point) + if self.contains != NotImplemented: + tester.assertEqual(self.contains(point), self.contains(coords)) + @abstract_method(optional=True) def intersection(self, other): r""" diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index af317977e5a..56305d035e8 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -764,6 +764,49 @@ def as_polyhedron(self): Vrep = (self.vertices(), self.rays(), self.lines()) return P.__class__(parent, Vrep, None) + def contains(self, point): + """ + Test whether the polyhedron contains the given ``point``. + + INPUT: + + - ``point`` -- a point or its coordinates + + EXAMPLES:: + + sage: half_plane = Polyhedron(ieqs=[(0,1,0)]) + sage: line = half_plane.faces(1)[0]; line + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line + sage: line.contains([0, 1]) + True + + As a shorthand, one may use the usual ``in`` operator:: + + sage: [5, 7] in line + False + """ + # preprocess in the same way as Polyhedron_base.contains + try: + p = vector(point) + except TypeError: # point not iterable or no common ring for elements + if len(point) > 0: + return False + else: + p = vector(self.polyhedron().base_ring(), []) + + if len(p) != self.ambient_dim(): + return False + + if not self.polyhedron().contains(p): + return False + + for H in self.ambient_Hrepresentation(): + if H.eval(p) != 0: + return False + return True + + __contains__ = contains + @cached_method def normal_cone(self, direction='outer'): """ From 240095296e65214ba8ee7dcefc77c8cba0ad0cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 12 Jun 2021 09:02:56 +0200 Subject: [PATCH 196/280] finer category for affine linear groups --- src/sage/groups/affine_gps/affine_group.py | 76 +++++++++++++++++++--- 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/src/sage/groups/affine_gps/affine_group.py b/src/sage/groups/affine_gps/affine_group.py index 516cdb9cd13..6c1629f6dc8 100644 --- a/src/sage/groups/affine_gps/affine_group.py +++ b/src/sage/groups/affine_gps/affine_group.py @@ -18,6 +18,9 @@ from sage.groups.group import Group +from sage.categories.groups import Groups +from sage.groups.matrix_gps.linear import GL +from sage.categories.rings import Rings from sage.matrix.all import MatrixSpace from sage.modules.all import FreeModule from sage.structure.unique_representation import UniqueRepresentation @@ -202,9 +205,21 @@ def __init__(self, degree, ring): sage: G = AffineGroup(2, GF(5)); G Affine Group of degree 2 over Finite Field of size 5 sage: TestSuite(G).run() + sage: G.category() + Category of finite groups + + sage: Aff6 = AffineGroup(6, QQ) + sage: Aff6.category() + Category of infinite groups """ self._degree = degree - Group.__init__(self, base=ring) + cat = Groups() + if degree == 0 or ring in Rings().Finite(): + cat = cat.Finite() + elif ring in Rings().Infinite(): + cat = cat.Infinite() + self._GL = GL(degree, ring) + Group.__init__(self, base=ring, category=cat) Element = AffineGroupElement @@ -253,7 +268,8 @@ def _latex_(self): sage: latex(G) \mathrm{Aff}_{6}(\Bold{F}_{5}) """ - return "\\mathrm{Aff}_{%s}(%s)"%(self.degree(), self.base_ring()._latex_()) + return "\\mathrm{Aff}_{%s}(%s)" % (self.degree(), + self.base_ring()._latex_()) def _repr_(self): """ @@ -264,7 +280,22 @@ def _repr_(self): sage: AffineGroup(6, GF(5)) Affine Group of degree 6 over Finite Field of size 5 """ - return "Affine Group of degree %s over %s"%(self.degree(), self.base_ring()) + return "Affine Group of degree %s over %s" % (self.degree(), + self.base_ring()) + + def cardinality(self): + """ + Return the cardinality of ``self``. + + EXAMPLES:: + + sage: AffineGroup(6, GF(5)).cardinality() + 172882428468750000000000000000 + sage: AffineGroup(6, ZZ).cardinality() + +Infinity + """ + card_GL = self._GL.cardinality() + return card_GL * self.base_ring().cardinality()**self.degree() def degree(self): """ @@ -448,9 +479,7 @@ def random_element(self): sage: G.random_element() in G True """ - A = self.matrix_space().random_element() - while not A.is_invertible(): # a generic matrix is invertible - A.randomize() + A = self._GL.random_element() b = self.vector_space().random_element() return self.element_class(self, A, b, check=False, convert=False) @@ -465,9 +494,38 @@ def _an_element_(self): sage: G.an_element() in G True """ - A = self.matrix_space().an_element() - while not A.is_invertible(): # a generic matrix is not always invertible - A.randomize() + A = self._GL.an_element() b = self.vector_space().an_element() return self.element_class(self, A, b, check=False, convert=False) + def some_elements(self): + """ + Return some elements. + + EXAMPLES:: + + sage: G = AffineGroup(4,5) + sage: G.some_elements() + [ [2 0 0 0] [1] + [0 1 0 0] [0] + x |-> [0 0 1 0] x + [0] + [0 0 0 1] [0], + [2 0 0 0] [0] + [0 1 0 0] [0] + x |-> [0 0 1 0] x + [0] + [0 0 0 1] [0], + [2 0 0 0] [0] + [0 1 0 0] [2] + x |-> [0 0 1 0] x + [0] + [0 0 0 1] [1]] + + sage: G = AffineGroup(2,QQ) + sage: G.some_elements() + [ [1 0] [1] + x |-> [0 1] x + [0], + ...] + """ + mats = self._GL.some_elements() + vecs = self.vector_space().some_elements() + return [self.element_class(self, A, b, check=False, convert=False) + for A in mats for b in vecs] From 52725203ad5af01d8b503c99dea1ca6f11ade854 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 12 Jun 2021 14:23:54 +0200 Subject: [PATCH 197/280] Ignore warnings from docutils about missing rST localisation --- src/sage_docbuild/sphinxbuild.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage_docbuild/sphinxbuild.py b/src/sage_docbuild/sphinxbuild.py index f58f6c61d76..d917c3e9d41 100644 --- a/src/sage_docbuild/sphinxbuild.py +++ b/src/sage_docbuild/sphinxbuild.py @@ -110,6 +110,7 @@ def _init_chatter(self): re.compile('WARNING: Any IDs not assiend for figure node'), re.compile('WARNING: .* is not referenced'), re.compile('WARNING: Build finished'), + re.compile('WARNING: rST localisation for language .* not found') ) # The warning "unknown config value 'multidoc_first_pass'..." # should only appear when building the documentation for a From 1f7826d757f80ba7bae0aa64fb34c57206b2ad8f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 08:42:08 -0700 Subject: [PATCH 198/280] ConvexSet_base._test_contains: Expand and add doctests --- src/sage/geometry/convex_set.py | 53 +++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 518ed2bc51f..56ff0d75bc4 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -395,14 +395,61 @@ def contains(self, point): def _test_contains(self, tester=None, **options): """ Test the ``contains`` method. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_closed + sage: class FaultyConvexSet(ConvexSet_closed): + ....: def ambient_vector_space(self, base_field=QQ): + ....: return base_field^2 + ....: ambient = ambient_vector_space + ....: def contains(self, point): + ....: if isinstance(point, (tuple, list)): + ....: return all(x in ZZ for x in point) + ....: return point.parent() == ZZ^2 + sage: FaultyConvexSet()._test_contains() + Traceback (most recent call last): + ... + AssertionError: False != True + + sage: class AlsoFaultyConvexSet(ConvexSet_closed): + ....: def ambient_vector_space(self, base_field=QQ): + ....: return base_field^2 + ....: def ambient(self): + ....: return ZZ^2 + ....: def contains(self, point): + ....: return point in ZZ^2 + sage: AlsoFaultyConvexSet()._test_contains() + Traceback (most recent call last): + ... + AssertionError: True != False """ if tester is None: tester = self._tester(**options) + ambient = self.ambient() space = self.ambient_vector_space() - point = space.an_element() - coords = space.coordinates(point) + try: + ambient_point = ambient.an_element() + except (AttributeError, NotImplementedError): + ambient_point = None + space_point = space.an_element() + else: + space_point = space(ambient_point) + space_coords = space.coordinates(space_point) if self.contains != NotImplemented: - tester.assertEqual(self.contains(point), self.contains(coords)) + contains_space_point = self.contains(space_point) + if ambient_point is not None: + tester.assertEqual(contains_space_point, self.contains(ambient_point)) + tester.assertEqual(contains_space_point, self.contains(space_coords)) + from sage.rings.qqbar import AA + ext_space = self.ambient_vector_space(AA) + ext_space_point = ext_space(space_point) + tester.assertEqual(contains_space_point, self.contains(ext_space_point)) + from sage.symbolic.ring import SR + symbolic_space = self.ambient_vector_space(SR) + symbolic_space_point = symbolic_space(space_point) + # Only test that it can accept SR vectors without error. + self.contains(symbolic_space_point) @abstract_method(optional=True) def intersection(self, other): From 142fb462e22a0598eb56c5b61f6b7b602fcbbaac Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 08:45:28 -0700 Subject: [PATCH 199/280] ConvexSet_base.codimension: Add doctest for alias 'codim' --- src/sage/geometry/convex_set.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 6827b0a10d8..7945296471d 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -99,11 +99,15 @@ def codimension(self): r""" Return the codimension of ``self``. - An alias is :meth:`codim`. - EXAMPLES:: - sage: Polyhedron(vertices=[(1,2,3)], rays=[(1,0,0)]).codimension() + sage: P = Polyhedron(vertices=[(1,2,3)], rays=[(1,0,0)]) + sage: P.codimension() + 2 + + An alias is :meth:`codim`:: + + sage: P.codim() 2 """ return self.ambient_dim() - self.dim() From 30ebaf5a657040cb787bea0f5c2540ca08115284 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 08:49:31 -0700 Subject: [PATCH 200/280] ConvexSet_base.ambient_dim, ambient_dimension: Fix doc --- src/sage/geometry/convex_set.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 7945296471d..b7793476cf8 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -75,12 +75,12 @@ def dimension(self): @abstract_method def ambient_dim(self): r""" - Return the dimension of the ambient space. + Return the dimension of the ambient convex set or space. """ def ambient_dimension(self): r""" - Return the dimension of ``self``. + Return the dimension of the ambient convex set or space. This is the same as :meth:`ambient_dim`. From fcf2a32a768097f9248cda456624784cc9f199c6 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 12 Jun 2021 14:30:04 +0200 Subject: [PATCH 201/280] fix coverage --- src/sage/geometry/convex_set.py | 63 +++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index b7793476cf8..8ef66c68285 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -44,6 +44,15 @@ def is_universe(self): OUTPUT: Boolean. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.is_universe() + Traceback (most recent call last): + ... + NotImplementedError: """ if not self.is_full_dimensional(): return False @@ -53,6 +62,15 @@ def is_universe(self): def dim(self): r""" Return the dimension of ``self``. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.dim() + Traceback (most recent call last): + ... + NotImplementedError: """ def dimension(self): @@ -76,6 +94,15 @@ def dimension(self): def ambient_dim(self): r""" Return the dimension of the ambient convex set or space. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.ambient_dim() + Traceback (most recent call last): + ... + NotImplementedError: """ def ambient_dimension(self): @@ -350,6 +377,15 @@ def _test_convex_set(self, tester=None, **options): def affine_hull(self): r""" Return the affine hull of ``self``. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.affine_hull() + Traceback (most recent call last): + ... + TypeError: 'NotImplementedType' object is not callable """ @abstract_method(optional=True) @@ -364,6 +400,15 @@ def cartesian_product(self, other): OUTPUT: The Cartesian product of ``self`` and ``other``. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.cartesian_product(C) + Traceback (most recent call last): + ... + TypeError: 'NotImplementedType' object is not callable """ @abstract_method(optional=True) @@ -374,6 +419,15 @@ def contains(self, point): INPUT: - ``point`` -- a point or its coordinates + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.contains(vector([0, 0])) + Traceback (most recent call last): + ... + TypeError: 'NotImplementedType' object is not callable """ @abstract_method(optional=True) @@ -388,6 +442,15 @@ def intersection(self, other): OUTPUT: The intersection. + + TESTS:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.intersection(C) + Traceback (most recent call last): + ... + TypeError: 'NotImplementedType' object is not callable """ From 6bef52ba121da086a07e2fe2b6e5f9b7ff3cbb51 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 09:20:46 -0700 Subject: [PATCH 202/280] ConvexSet_base._test_convex_set: Run the testsuite of relint --- src/sage/geometry/convex_set.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 8ef66c68285..f55a4cd17b5 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -370,6 +370,12 @@ def _test_convex_set(self, tester=None, **options): tester.assertTrue(self == cl_self) if self.is_compact(): tester.assertTrue(self.is_closed()) + from sage.misc.sage_unittest import TestSuite + if relint_self is not None and relint_self is not self: + tester.info("\n Running the test suite of self.relative_interior()") + TestSuite(relint_self).run(verbose=tester._verbose, + prefix=tester._prefix + " ") + tester.info(tester._prefix + " ", newline=False) # Optional methods From 6ab5677085b94f2d7453a4b619e16806cff3b233 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 12:44:58 -0700 Subject: [PATCH 203/280] RelativeInterior.is_universe: New --- src/sage/geometry/relative_interior.py | 28 ++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 795ef408692..3fd2f6c6b54 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -79,7 +79,8 @@ def ambient_dim(self): sage: segment.ambient_dim() 2 sage: ri_segment = segment.relative_interior(); ri_segment - Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices sage: ri_segment.ambient_dim() 2 """ @@ -95,7 +96,8 @@ def dim(self): sage: segment.dim() 1 sage: ri_segment = segment.relative_interior(); ri_segment - Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices sage: ri_segment.dim() 1 """ @@ -154,6 +156,28 @@ def closure(self): """ return self._polyhedron + def is_universe(self): + r""" + Return whether ``self`` is the whole ambient space + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.is_universe() + False + """ + # Relies on ``self`` not set up for polyhedra that are already + # relatively open themselves. + assert not self._polyhedron.is_universe() + return False + def is_closed(self): r""" Return whether ``self`` is closed. From c085d30d1c10ceacd9980520d0f3c7e2a78d531c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 09:33:31 -0700 Subject: [PATCH 204/280] Polyhedron_base.interior: Handle the empty polyhedron correctly --- src/sage/geometry/polyhedron/base.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 1d17d27df09..50274eced76 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -8425,7 +8425,15 @@ def interior(self): sage: P_lower.interior() The empty polyhedron in ZZ^2 + TESTS:: + + sage: Empty = Polyhedron(ambient_dim=2); Empty + The empty polyhedron in ZZ^2 + sage: Empty.interior() is Empty + True """ + if self.is_open(): + return self if not self.is_full_dimensional(): return self.parent().element_class(self.parent(), None, None) return self.relative_interior() From 686d0afbeba9f5d33131ecbe20a907c20635faa5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 09:39:53 -0700 Subject: [PATCH 205/280] Polyhedron_base.product: Add doctest for alias 'cartesian_product' --- src/sage/geometry/polyhedron/base.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 50274eced76..fe4253fe3bc 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -4767,6 +4767,11 @@ def product(self, other): sage: P1 * 2.0 A 1-dimensional polyhedron in RDF^1 defined as the convex hull of 2 vertices + An alias is :meth:`cartesian_product`:: + + sage: P1.cartesian_product(P2) == P1.product(P2) + True + TESTS: Check that :trac:`15253` is fixed:: From fdcef38fda74712c715c515fb7bf715f9223e89d Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 12 Jun 2021 22:35:04 +0200 Subject: [PATCH 206/280] some E701, E702 --- src/sage/interacts/library.py | 48 ++++++++++++++++---------- src/sage/knots/link.py | 3 +- src/sage/rings/padics/local_generic.py | 48 +++++++++++++++++--------- src/sage/rings/padics/padic_generic.py | 44 +++++++++++++---------- 4 files changed, 88 insertions(+), 55 deletions(-) diff --git a/src/sage/interacts/library.py b/src/sage/interacts/library.py index 3ce3bb6b152..dd46fda92e5 100644 --- a/src/sage/interacts/library.py +++ b/src/sage/interacts/library.py @@ -810,7 +810,9 @@ def _bisection_method(f, a, b, maxn, eps): c = (b+a)/two if abs(f(c)) < h or round >= maxn: break - fa = f(a); fb = f(b); fc = f(c) + fa = f(a) + fb = f(b) + fc = f(c) if abs(fc) < eps: return c, intervals if fa*fc < 0: @@ -1150,7 +1152,8 @@ def parabola(a, b, c): K = solve([A*a[0]**2+B*a[0]+C==a[1], A*b[0]**2+B*b[0]+C==b[1], A*c[0]**2+B*c[0]+C==c[1]], [A, B, C], solution_dict=True)[0] f = K[A]*x**2+K[B]*x+K[C] return f - xs = []; ys = [] + xs = [] + ys = [] dx = float(interval[1]-interval[0])/n for i in range(n+1): @@ -1357,14 +1360,17 @@ def function_tool(f=sin(x), g=cos(x), xrange=range_slider(-3,3,default=(0,1),lab """ x = SR.var('x') try: - f = SR(f); g = SR(g); a = SR(a) + f = SR(f) + g = SR(g) + a = SR(a) except TypeError as msg: print(msg[-200:]) print("Unable to make sense of f,g, or a as symbolic expressions in single variable x.") return if not (isinstance(xrange, tuple) and len(xrange) == 2): xrange = (0,1) - h = 0; lbl = '' + h = 0 + lbl = '' if action == 'f': h = f lbl = 'f' @@ -1659,33 +1665,37 @@ def polar_prime_spiral( list2 = [] if not show_factors: for i in srange(start, end, include_endpoint = True): - if Integer(i).is_pseudoprime(): list.append(f(i-start+1)) #Primes list - else: list2.append(f(i-start+1)) #Composites list + if Integer(i).is_pseudoprime(): + list.append(f(i-start+1)) # Primes list + else: + list2.append(f(i-start+1)) # Composites list P = points(list) - R = points(list2, alpha = .1) #Faded Composites + R = points(list2, alpha = .1) # Faded Composites else: for i in srange(start, end, include_endpoint = True): - list.append(disk((f(i-start+1)),0.05*pow(2,len(factor(i))-1), (0,2*pi))) #resizes each of the dots depending of the number of factors of each number - if Integer(i).is_pseudoprime() and highlight_primes: list2.append(f(i-start+1)) + list.append(disk((f(i-start+1)),0.05*pow(2,len(factor(i))-1), (0,2*pi))) # resizes each of the dots depending of the number of factors of each number + if Integer(i).is_pseudoprime() and highlight_primes: + list2.append(f(i-start+1)) P = Graphics() for g in list: P += g - p_size = 5 #the orange dot size of the prime markers - if not highlight_primes: list2 = [(f(n-start+1))] + p_size = 5 # the orange dot size of the prime markers + if not highlight_primes: + list2 = [(f(n-start+1))] R = points(list2, hue = .1, pointsize = p_size) if n > 0: html('$n = %s$' % factor(n)) p = 1 - #The X which marks the given n + # The X which marks the given n W1 = disk((f(n-start+1)), p, (pi/6, 2*pi/6), alpha=.1) W2 = disk((f(n-start+1)), p, (4*pi/6, 5*pi/6), alpha=.1) W3 = disk((f(n-start+1)), p, (7*pi/6, 8*pi/6), alpha=.1) W4 = disk((f(n-start+1)), p, (10*pi/6, 11*pi/6), alpha=.1) Q = W1 + W2 + W3 + W4 - n = n - start +1 #offsets the n for different start values to ensure accurate plotting + n = n - start +1 # offsets the n for different start values to ensure accurate plotting if show_curves: begin_curve = 0 t = SR.var('t') @@ -1704,7 +1714,7 @@ def polar_prime_spiral( r = symbolic_expression(sqrt(g(m))).function(m) theta = symbolic_expression(r(m)- m*sqrt(a)).function(m) S1 = parametric_plot(((r(t))*cos(2*pi*(theta(t))),(r(t))*sin(2*pi*(theta(t)))), - (begin_curve, ceil(sqrt(end-start))), color=hue(0.8), thickness = .3) #Pink Line + (begin_curve, ceil(sqrt(end-start))), color=hue(0.8), thickness=.3) #Pink Line b = 1 c = c2; @@ -1712,8 +1722,10 @@ def polar_prime_spiral( r = symbolic_expression(sqrt(g(m))).function(m) theta = symbolic_expression(r(m)- m*sqrt(a)).function(m) S2 = parametric_plot(((r(t))*cos(2*pi*(theta(t))),(r(t))*sin(2*pi*(theta(t)))), - (begin_curve, ceil(sqrt(end-start))), color=hue(0.6), thickness = .3) #Green Line + (begin_curve, ceil(sqrt(end-start))), color=hue(0.6), thickness=.3) #Green Line - show(R+P+S1+S2+Q, aspect_ratio = 1, axes = False, dpi = dpi) - else: show(R+P+Q, aspect_ratio = 1, axes = False, dpi = dpi) - else: show(R+P, aspect_ratio = 1, axes = False, dpi = dpi) + show(R+P+S1+S2+Q, aspect_ratio=1, axes=False, dpi=dpi) + else: + show(R+P+Q, aspect_ratio=1, axes=False, dpi=dpi) + else: + show(R+P, aspect_ratio=1, axes=False, dpi=dpi) diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index 3792ec5556a..6b5a864b0ff 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -3785,7 +3785,8 @@ def answer(L): return L chiral = True - ach = L.is_amphicheiral(); achp = L.is_amphicheiral(positive=True) + ach = L.is_amphicheiral() + achp = L.is_amphicheiral(positive=True) if ach is None and achp is None: if unique: raise NotImplementedError('this link cannot be uniquely determined (unknown chirality)%s' %non_unique_hint) diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 63960d81fd6..9bbc314b3b0 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -1127,7 +1127,7 @@ def _matrix_flatten_precision(self, M): This method is useful for increasing the numerical stability. It is called by :meth:`_matrix_smith_form` - and :meth:`_matrix_determinant` + and :meth:`_matrix_determinant` Only for internal use. @@ -1151,18 +1151,21 @@ def _matrix_flatten_precision(self, M): """ parent = M.base_ring() cap = parent.precision_cap() - n = M.nrows(); m = M.ncols() + n = M.nrows() + m = M.ncols() shift_rows = n * [ ZZ(0) ] shift_cols = m * [ ZZ(0) ] for i in range(n): prec = min(M[i,j].precision_absolute() for j in range(m)) - if prec is Infinity or prec == cap: continue + if prec is Infinity or prec == cap: + continue shift_rows[i] = s = cap - prec for j in range(m): M[i,j] <<= s for j in range(m): prec = min(M[i,j].precision_absolute() for i in range(n)) - if prec is Infinity or prec == cap: continue + if prec is Infinity or prec == cap: + continue shift_cols[j] = s = cap - prec for i in range(n): M[i,j] <<= s @@ -1341,10 +1344,13 @@ def _matrix_smith_form(self, M, transformation, integral, exact): if exact: # we only care in this case allexact = allexact and Sij.precision_absolute() is infinity if v < curval: - pivi = i; pivj = j + pivi = i + pivj = j curval = v - if v == val: break - else: continue + if v == val: + break + else: + continue break val = curval @@ -1362,8 +1368,10 @@ def _matrix_smith_form(self, M, transformation, integral, exact): for i in range(i,n): for j in range(piv,m): allexact = allexact and S[i,j].precision_absolute() is infinity - if not allexact: break - else: continue + if not allexact: + break + else: + continue break if not allexact: raise PrecisionError("some elementary divisors indistinguishable from zero (try exact=False)") @@ -1549,11 +1557,11 @@ def _matrix_determinant(self, M): O(37) """ n = M.nrows() - + # For 2x2 matrices, we use the formula if n == 2: return M[0,0]*M[1,1] - M[0,1]*M[1,0] - + R = M.base_ring() track_precision = R._prec_type() in ['capped-rel','capped-abs'] @@ -1572,7 +1580,8 @@ def _matrix_determinant(self, M): for j in range(piv,n): v = S[i,j].valuation() if v < curval: - pivi = i; pivj = j + pivi = i + pivj = j curval = v if v == val: break @@ -1588,9 +1597,11 @@ def _matrix_determinant(self, M): valdet += val S.swap_rows(pivi,piv) - if pivi > piv: sign = -sign + if pivi > piv: + sign = -sign S.swap_columns(pivj,piv) - if pivj > piv: sign = -sign + if pivj > piv: + sign = -sign det *= S[piv,piv] inv = ~(S[piv,piv] >> val) @@ -1608,9 +1619,12 @@ def _matrix_determinant(self, M): for j in range(n): prec = min(prec, S[i,j].precision_absolute()) prec -= S[i,i].valuation() - if prec < relprec: relprec = prec - if prec < 0: relprec_neg += prec - if relprec_neg < 0: relprec = relprec_neg + if prec < relprec: + relprec = prec + if prec < 0: + relprec_neg += prec + if relprec_neg < 0: + relprec = relprec_neg det = (sign*det).add_bigoh(valdet+relprec) else: det = sign*det diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index ae15159be84..5c81ce18e77 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -837,7 +837,8 @@ def _test_invert(self, **options): y = ~x except (ZeroDivisionError, PrecisionError, ValueError): tester.assertFalse(x.is_unit()) - if not self.is_fixed_mod(): tester.assertTrue(x.is_zero()) + if not self.is_fixed_mod(): + tester.assertTrue(x.is_zero()) else: try: e = y * x @@ -904,8 +905,10 @@ def _test_div(self, **options): try: z = x / y except (ZeroDivisionError, PrecisionError, ValueError): - if self.is_fixed_mod(): tester.assertFalse(y.is_unit()) - else: tester.assertTrue(y.is_zero()) + if self.is_fixed_mod(): + tester.assertFalse(y.is_unit()) + else: + tester.assertTrue(y.is_zero()) else: try: xx = z*y @@ -1011,7 +1014,8 @@ def _test_log(self, **options): """ tester = self._tester(**options) for x in tester.some_elements(): - if x.is_zero(): continue + if x.is_zero(): + continue try: l = x.log(p_branch=0) tester.assertIs(l.parent(), self) @@ -1026,14 +1030,16 @@ def _test_log(self, **options): if self.is_capped_absolute() or self.is_capped_relative(): # In the fixed modulus setting, rounding errors may occur for x, y, b in tester.some_elements(repeat=3): - if (x*y).is_zero(): continue + if (x*y).is_zero(): + continue r1 = x.log(pi_branch=b) + y.log(pi_branch=b) r2 = (x*y).log(pi_branch=b) tester.assertEqual(r1, r2) p = self.prime() for x in tester.some_elements(): - if x.is_zero(): continue + if x.is_zero(): + continue if p == 2: a = 4 * x.unit_part() else: @@ -1416,7 +1422,7 @@ def roots_of_unity(self, n=None): 3 + 3*5 + 2*5^2 + 3*5^3 + 5^4 + 2*5^6 + 5^7 + 4*5^8 + 5^9 + O(5^10), 4 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + 4*5^5 + 4*5^6 + 4*5^7 + 4*5^8 + 4*5^9 + O(5^10)] - In general, there might be more roots of unity (it happens when the ring has non + In general, there might be more roots of unity (it happens when the ring has non trivial ``p``-th roots of unity):: sage: W. = Zq(3^2, 2) @@ -1426,7 +1432,7 @@ def roots_of_unity(self, n=None): sage: roots = R.roots_of_unity(); roots [1 + O(pi^4), a + 2*a*pi + 2*a*pi^2 + a*pi^3 + O(pi^4), - ... + ... 1 + pi + O(pi^4), a + a*pi^2 + 2*a*pi^3 + O(pi^4), ... @@ -1455,23 +1461,23 @@ def _roots_univariate_polynomial(self, P, ring, multiplicities, algorithm, secur - ``ring`` -- a ring into which this ring coerces - - ``multiplicities`` -- a boolean (default: ``True``); + - ``multiplicities`` -- a boolean (default: ``True``); whether we have to return the multiplicities of each root or not - - ``algorithm`` -- ``"pari"``, ``"sage"`` or ``None`` (default: - ``None``); Sage provides an implementation for any extension of - `Q_p` whereas only roots of polynomials over `\QQ_p` is implemented - in Pari; the default is ``"pari"`` if ``ring`` is `\ZZ_p` or `\QQ_p`, + - ``algorithm`` -- ``"pari"``, ``"sage"`` or ``None`` (default: + ``None``); Sage provides an implementation for any extension of + `Q_p` whereas only roots of polynomials over `\QQ_p` is implemented + in Pari; the default is ``"pari"`` if ``ring`` is `\ZZ_p` or `\QQ_p`, ``"sage"`` otherwise. - ``secure`` -- a boolean (default: ``False``) NOTE: - When ``secure`` is ``True``, this method raises an error when - the precision on the input polynomial is not enough to determine - the number of roots in the ground field. This happens when two + When ``secure`` is ``True``, this method raises an error when + the precision on the input polynomial is not enough to determine + the number of roots in the ground field. This happens when two roots cannot be separated. A typical example is the polynomial @@ -1479,14 +1485,14 @@ def _roots_univariate_polynomial(self, P, ring, multiplicities, algorithm, secur (1 + O(p^10))*X^2 + O(p^10)*X + O(p^10) - Indeed its discriminant might be any `p`-adic integer divisible - by `p^{10}` (resp. `p^{11}` when `p=2`) and so can be as well + Indeed its discriminant might be any `p`-adic integer divisible + by `p^{10}` (resp. `p^{11}` when `p=2`) and so can be as well zero, a square and a non-square. In the first case, the polynomial has one double root; in the second case, it has two roots; in the third case, it has no root in `\QQ_p`. - When ``secure`` is ``False``, this method assumes that two + When ``secure`` is ``False``, this method assumes that two inseparable roots actually collapse. In the above example, it then answers that the given polynomial has a double root `O(p^5)`. From 8cf18ccb90bdf9c90ef4fc4a7eeccb4abfd8702f Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 12 Jun 2021 22:43:36 +0200 Subject: [PATCH 207/280] some E701, E702 --- src/sage/rings/padics/factory.py | 6 +- src/sage/rings/padics/generic_nodes.py | 3 +- src/sage/rings/padics/lattice_precision.py | 137 ++++++++++-------- .../polynomial_padic_capped_relative_dense.py | 9 +- .../polynomial/polynomial_element_generic.py | 9 +- .../rings/polynomial/polynomial_fateman.py | 3 +- 6 files changed, 97 insertions(+), 70 deletions(-) diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index 38546c6d262..90c54a2524a 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -1320,8 +1320,10 @@ def Qq(q, prec = None, type = 'capped-rel', modulus = None, names=None, q = tuple(q) p,k = q - if not isinstance(p, Integer): p = Integer(p) - if not isinstance(k, Integer): k = Integer(k) + if not isinstance(p, Integer): + p = Integer(p) + if not isinstance(k, Integer): + k = Integer(k) if check: if not p.is_prime() or k <=0: diff --git a/src/sage/rings/padics/generic_nodes.py b/src/sage/rings/padics/generic_nodes.py index 6a1da3b4747..640eef1ca04 100644 --- a/src/sage/rings/padics/generic_nodes.py +++ b/src/sage/rings/padics/generic_nodes.py @@ -682,7 +682,8 @@ def convert_multiple(self, *elts): except PrecisionError: raise NotImplementedError("multiple conversion of a set of variables for which the module precision is not a lattice is not implemented yet") for j in range(len(L)): - x = L[j]; dx = [ ] + x = L[j] + dx = [ ] for i in range(j): dx.append([L[i], lattice[i,j]]) prec = lattice[j,j].valuation(p) diff --git a/src/sage/rings/padics/lattice_precision.py b/src/sage/rings/padics/lattice_precision.py index b2901ea7008..ade4a6950d0 100644 --- a/src/sage/rings/padics/lattice_precision.py +++ b/src/sage/rings/padics/lattice_precision.py @@ -179,7 +179,7 @@ def reduce(self, prec): exp -= valdenom if prec > exp: modulo = self.p ** (prec - exp) - # probably we should use Newton iteration instead + # probably we should use Newton iteration instead # (but it is actually slower for now - Python implementation) _, inv, _ = denom.xgcd(modulo) x = (num*inv) % modulo @@ -219,7 +219,7 @@ def reduce_relative(self, prec): def normalize(self): r""" - Normalize this element, i.e. write it as ``p^v * u`` where + Normalize this element, i.e. write it as ``p^v * u`` where ``u`` is coprime to `p`. TESTS:: @@ -311,8 +311,10 @@ def __add__(self, other): p = self.p sexp = self.exponent oexp = other.exponent - if sexp is Infinity: return other - if oexp is Infinity: return self + if sexp is Infinity: + return other + if oexp is Infinity: + return self if self._valuation is None or other._valuation is None: val = None elif self._valuation < other._valuation: @@ -560,7 +562,7 @@ def value(self): def list(self, prec): r""" - Return the list of the digits of this element (written in radix + Return the list of the digits of this element (written in radix `p`) up to position ``prec``. The first zeros are omitted. @@ -747,7 +749,7 @@ def prime(self): def _index(self, ref): r""" - Return the index of the column in the precision matrix that + Return the index of the column in the precision matrix that corresponds to ``ref``. Only for internal use. @@ -877,15 +879,15 @@ def _record_collected_element(self, ref): @abstract_method def del_elements(self, threshold=None): r""" - Delete (or mark for future deletion) the columns of precision - matrix corresponding to elements that were collected by the + Delete (or mark for future deletion) the columns of precision + matrix corresponding to elements that were collected by the garbage collector. INPUT: - ``threshold`` -- an integer or ``None`` (default: ``None``): a column whose distance to the right is greater than the - threshold is not erased but marked for deletion; + threshold is not erased but marked for deletion; if ``None``, always erase (never mark for deletion). EXAMPLES:: @@ -988,7 +990,7 @@ def precision_lattice(self, elements=None): def diffused_digits(self, elements=None): r""" - Return the number of diffused digits of precision within a + Return the number of diffused digits of precision within a subset of elements. A diffused digit of precision is a known digit which is not @@ -997,7 +999,7 @@ def diffused_digits(self, elements=None): The number of diffused digits of precision quantifies the quality of the approximation of the lattice precision by a - jagged precision (that is a precision which is split over + jagged precision (that is a precision which is split over all variables). We refer to [CRV2018]_ for a detail exposition of the notion of @@ -1110,7 +1112,7 @@ def history_enable(self): r""" Enable history. - We refer to the documentation of the method :meth:`history` for + We refer to the documentation of the method :meth:`history` for a complete documentation (including examples) about history. TESTS:: @@ -1126,7 +1128,7 @@ def history_enable(self): sage: prec.history_enable() sage: print(prec.history()) Timings - --- + --- .. SEEALSO:: @@ -1140,7 +1142,7 @@ def history_disable(self): r""" Disable history. - We refer to the documentation of the method :meth:`history` for + We refer to the documentation of the method :meth:`history` for a complete documentation (including examples) about history. TESTS:: @@ -1156,7 +1158,7 @@ def history_disable(self): sage: prec.history_enable() sage: print(prec.history()) Timings - --- + --- sage: prec.history_disable() sage: print(prec.history()) @@ -1174,7 +1176,7 @@ def history_clear(self): r""" Clear history. - We refer to the documentation of the method :meth:`history` for + We refer to the documentation of the method :meth:`history` for a complete documentation (including examples) about history. TESTS:: @@ -1215,18 +1217,18 @@ def history_clear(self): def _format_history(self, time, status, timings): r""" Return a formatted output for the history. - + This is a helper function for the method :meth:`history`. - + TESTS:: - + sage: R = ZpLC(2, label='history_en') sage: prec = R.precision() sage: prec._format_history(1.23456789, ['o', 'o', 'o', 'o', 'o', 'o', '~', 'o', 'o'], true) '1.234568s oooooo~oo' sage: prec._format_history(1.23456789, ['o', 'o', 'o', 'o', 'o', 'o', '~', 'o', 'o'], false) 'oooooo~oo' - + sage: prec._format_history(12.3456789, ['o', 'o', 'o', 'o', 'o', 'o', '~', 'o', 'o'], true) ' >= 10s oooooo~oo' sage: prec._format_history(10^(-10), ['o', 'o', 'o', 'o', 'o', 'o', '~', 'o', 'o'], true) @@ -1253,12 +1255,12 @@ def history(self, compact=True, separate_reduce=False, timings=True, output_type r""" Show history. - The history records creations and deletions of elements attached + The history records creations and deletions of elements attached to this precision lattice, together with many timings. INPUT: - - ``compact`` -- a boolean (default: ``True``); if true, all + - ``compact`` -- a boolean (default: ``True``); if true, all consecutive operations of the same type appear on a single row - ``separate_reduce`` -- a boolean (default: ``False``); specify @@ -1288,7 +1290,7 @@ def history(self, compact=True, separate_reduce=False, timings=True, output_type At the beginning, the history is of course empty:: sage: print(prec.history()) - Timings + Timings --- Now we start creating and deleting elements:: @@ -1347,8 +1349,8 @@ def history(self, compact=True, separate_reduce=False, timings=True, output_type Timings for automatic reduction do not appear because they are included in the timings for deletion. - The symbol ``R`` is used to symbolize a column which is under full - Hermite reduction. Note that full Hermite reduction are never performed + The symbol ``R`` is used to symbolize a column which is under full + Hermite reduction. Note that full Hermite reduction are never performed automatically but needs to be called by hand:: sage: prec.reduce() @@ -1442,7 +1444,8 @@ def history(self, compact=True, separate_reduce=False, timings=True, output_type if separate_reduce: if status: hist.append(self._format_history(total_time, status, timings)) - if event == 'partial reduce': code = 'r' + if event == 'partial reduce': + code = 'r' else: code = 'R' status_red = status[:index] + (len(status) - index) * [code] hist.append(self._format_history(tme, status_red, timings)) @@ -1474,13 +1477,13 @@ def history(self, compact=True, separate_reduce=False, timings=True, output_type def timings(self, action=None): r""" - Return cumulated timings (grouped by actions) since the last + Return cumulated timings (grouped by actions) since the last time history has been cleared. INPUT: - ``action`` -- ``None`` (the default), ``add``, ``mark``, ``del``, - ``partial reduce`` or ``full reduce``; if not None, return the + ``partial reduce`` or ``full reduce``; if not None, return the cumulated timing corresponding to this action; otherwise, return a dictionary @@ -1584,20 +1587,20 @@ def __reduce__(self): def _index(self, ref): r""" Return the index of the element whose reference is ``ref``. - + TESTS:: - + sage: from sage.rings.padics.lattice_precision import pAdicLatticeElementWeakProxy sage: R = ZpLC(2, label="index") sage: prec = R.precision() sage: x = R(1, 10) sage: y = R(1, 5) - + sage: prec._index(pAdicLatticeElementWeakProxy(x)) 0 sage: prec._index(pAdicLatticeElementWeakProxy(y)) 1 - + sage: del x sage: prec.del_elements() sage: prec._index(pAdicLatticeElementWeakProxy(y)) @@ -1636,8 +1639,8 @@ def reduce(self, index=0, partial=False): NOTE: - The partial reduction has cost `O(m^2)` where `m` is the number of - rows that need to be reduced (that is the difference between the + The partial reduction has cost `O(m^2)` where `m` is the number of + rows that need to be reduced (that is the difference between the total number of rows and ``index``). The full Hermite reduction has cost `O(m^3)`. @@ -1686,7 +1689,8 @@ def reduce(self, index=0, partial=False): for i in range(index, j): reduced = col[i].reduce(valpivot) scalar = (col[i] - reduced) >> valpivot - if scalar.is_zero(): continue + if scalar.is_zero(): + continue col[i] = reduced col[i].normalize() for j2 in range(j+1, n): @@ -1711,18 +1715,18 @@ def _new_element(self, x, dx, bigoh, dx_mode='linear_combination', capped=False) - ``dx`` -- a dictionary representing the differential of ``x`` - - ``bigoh`` -- an integer or ``None`` (default: ``None``): the + - ``bigoh`` -- an integer or ``None`` (default: ``None``): the bigoh to be added to the precision of ``x``; if ``None``, the default cap is used. - ``dx_mode`` -- a string, either ``linear_combination`` (the default) or ``values`` - - ``capped`` -- a boolean, whether this element has been capped + - ``capped`` -- a boolean, whether this element has been capped according to the parent's cap - If ``dx_mode`` is ``linear_combination``, the dictionary ``dx`` - encodes the expression of the differential of ``x``. + If ``dx_mode`` is ``linear_combination``, the dictionary ``dx`` + encodes the expression of the differential of ``x``. For example, if ``x`` was defined as ``x = y*z`` then: .. MATH:: @@ -1838,8 +1842,10 @@ def del_elements(self, threshold=None): self._marked_for_deletion.sort(reverse=True) count = 0 for index in self._marked_for_deletion: - if threshold is not None and index < n - threshold: break - n -= 1; count += 1 + if threshold is not None and index < n - threshold: + break + n -= 1 + count += 1 tme = walltime() ref = self._elements[index] @@ -1856,7 +1862,7 @@ def del_elements(self, threshold=None): self._capped[ref], capped = capped, capped or self._capped[ref] else: capped = capped or self._capped[ref] - + d, u, v = col[i].xgcd(col[i+1]) up, vp = col[i+1]/d, col[i]/d col[i] = d @@ -1894,7 +1900,7 @@ def _lift_to_precision(self, x, prec): p^{prec} \Z_p dx \oplus \bigoplus_{y \neq x} \Q_p dy - This function may change at the same time the precision of + This function may change at the same time the precision of other elements having the same parent. .. NOTE:: @@ -1937,7 +1943,8 @@ def _lift_to_precision(self, x, prec): rows = rows_by_val[v] piv = max(rows) for i in rows: - if i == piv: continue + if i == piv: + continue # We clear the entry on the i-th row scalar = (col[i]/col[piv]).reduce(prec-v) for j in range(piv,n): @@ -2116,7 +2123,8 @@ def precision_lattice(self, elements=None): col = self._matrix[ref] row = [ x.value() for x in col ] valcol = min([ x.valuation() for x in col ]) - if valcol < val: val = valcol + if valcol < val: + val = valcol row += (n-len(row)) * [ZZ(0)] rows.append(row) from sage.matrix.constructor import matrix @@ -2153,7 +2161,7 @@ def __init__(self, p, label, prec): NOTE: - The precision module is automatically initialized at the + The precision module is automatically initialized at the creation of the parent. TESTS:: @@ -2191,7 +2199,7 @@ def internal_prec(self): internally. It is slightly greater than the actual precision and increases - a bit (at a logarithmic rate) when new elements are created + a bit (at a logarithmic rate) when new elements are created and/or computed. EXAMPLES:: @@ -2237,7 +2245,7 @@ def dimension(self): sage: prec.dimension() 2 - Of course, it may also decrease when a sufficient + Of course, it may also decrease when a sufficient number of variables are collected:: sage: del x, y, u @@ -2293,7 +2301,7 @@ def _new_element(self, x, dx, bigoh, dx_mode='linear_combination'): - ``dx`` -- a dictionary representing the differential of ``x`` - - ``bigoh`` -- an integer or ``None`` (default: ``None``): the + - ``bigoh`` -- an integer or ``None`` (default: ``None``): the bigoh to be added to the precision of ``x``; if ``None``, the default cap is used. @@ -2387,7 +2395,7 @@ def del_elements(self, threshold=None): INPUT: - ``threshold`` -- an integer or ``None`` (default: ``None``): - a non-pivot column whose distance to the right is greater than + a non-pivot column whose distance to the right is greater than the threshold is not erased but only marked for future deletion EXAMPLES:: @@ -2447,8 +2455,10 @@ def del_elements(self, threshold=None): self._marked_for_deletion.sort(reverse=True) count = 0 for index in self._marked_for_deletion: - if threshold is not None and index < n - threshold: break - n -= 1; count += 1 + if threshold is not None and index < n - threshold: + break + n -= 1 + count += 1 tme = walltime() @@ -2462,7 +2472,7 @@ def del_elements(self, threshold=None): end = n while i < n: col = self._matrix[self._elements[i]] - if len(col) > length: + if len(col) > length: end = i break v = col[-1].valuation() @@ -2478,7 +2488,8 @@ def del_elements(self, threshold=None): # No pivot was found. We re-echelonize for i in range(start, end): del self._matrix[self._elements[i]][-1] - if end == n: break + if end == n: + break # col is the column of index "end" # its size is (length + 1) d, u, v = col[length-1].xgcd(col[length]) @@ -2488,8 +2499,11 @@ def del_elements(self, threshold=None): start = end + 1 for j in range(start, n): col = self._matrix[self._elements[j]] - a1 = u*col[length-1]; a2 = v*col[length]; a = a1 + a2 - b1 = up*col[length-1]; b2 = vp*col[length]; b = b1 + b2 + a1 = u*col[length-1] + a2 = v*col[length] + a = a1 + a2 + b1 = up*col[length-1] + b2 = vp*col[length]; b = b1 + b2 if a.valuation() > min(a1.valuation(), a2.valuation()) + self._zero_cap: col[length-1] = self._approx_zero else: @@ -2525,7 +2539,7 @@ def _lift_to_precision(self, x, prec): p^{prec} \Z_p dx \oplus \bigoplus_{y \neq x} \Q_p dy - This function may change at the same time the precision of + This function may change at the same time the precision of other elements having the same parent. .. NOTE:: @@ -2569,7 +2583,8 @@ def _lift_to_precision(self, x, prec): rows = rows_by_val[v] piv = max(rows) for i in rows: - if i == piv: continue + if i == piv: + continue # We clear the entry on the i-th row scalar = (col[i]/col[piv]).reduce(prec-v) for j in range(n): @@ -2696,12 +2711,14 @@ def precision_lattice(self, elements=None): else: elements = list_of_padics(elements) n = len(self._elements) - rows = [ ]; val = 0 + rows = [ ] + val = 0 for ref in elements: col = self._matrix[ref] row = [ x.value() for x in col ] valcol = min([ x.valuation() for x in col ]) - if valcol < val: val = valcol + if valcol < val: + val = valcol row += (n-len(row)) * [ZZ(0)] rows.append(row) from sage.matrix.constructor import matrix diff --git a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py index 760fde7c563..13d09467427 100644 --- a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py +++ b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py @@ -1245,13 +1245,16 @@ def is_eisenstein(self, secure=False): valaddeds = self._valaddeds relprecs = self._relprecs if relprecs[0] <= compval: # not enough precision - if valaddeds[0] < relprecs[0]: return False + if valaddeds[0] < relprecs[0]: + return False raise PrecisionError("Not enough precision on the constant coefficient") else: - if valaddeds[0] != compval: return False + if valaddeds[0] != compval: + return False for i in range(1, deg): if relprecs[i] < compval: # not enough precision - if valaddeds[i] < relprecs[i]: return False + if valaddeds[i] < relprecs[i]: + return False if secure: if i == 1: raise PrecisionError("Not enough precision on the coefficient of %s" % self.variable_name()) diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index 67de318829e..d9578903c64 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -1199,7 +1199,8 @@ def hensel_lift(self, a): b = ~dera while(True): na = a - selfa * b - if na == a: return a + if na == a: + return a a = na selfa = self(a) dera = der(a) @@ -1477,7 +1478,8 @@ def _roots(self, secure, minval, hint): continue if hint is not None and slope == minval: rootsbar = hint - if not rootsbar: continue + if not rootsbar: + continue if i < len(vertices) - 1: F = P._factor_of_degree(deg_right - deg) P = P // F @@ -1491,7 +1493,8 @@ def _roots(self, secure, minval, hint): if hint is None or slope != minval: Fbar = Pk([ F[j] >> (val - j*slope) for j in range(F.degree()+1) ]) rootsbar = [ r for (r, _) in Fbar.roots() ] - if not rootsbar: continue + if not rootsbar: + continue rbar = rootsbar.pop() shift = K(rbar).lift_to_precision() << slope # probably we should choose a better lift roots += [(r+shift, m) for (r, m) in F(x+shift)._roots(secure, slope, [r-rbar for r in rootsbar])] # recursive call diff --git a/src/sage/rings/polynomial/polynomial_fateman.py b/src/sage/rings/polynomial/polynomial_fateman.py index 557417d4470..580cee21d63 100644 --- a/src/sage/rings/polynomial/polynomial_fateman.py +++ b/src/sage/rings/polynomial/polynomial_fateman.py @@ -88,7 +88,8 @@ def _mul_fateman_mul(f,g): n_f = z_poly_f(1< Date: Sat, 12 Jun 2021 22:44:56 +0200 Subject: [PATCH 208/280] W605 --- src/sage/rings/padics/padic_base_leaves.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/padic_base_leaves.py b/src/sage/rings/padics/padic_base_leaves.py index cbf71588bfd..4d4f44735d3 100644 --- a/src/sage/rings/padics/padic_base_leaves.py +++ b/src/sage/rings/padics/padic_base_leaves.py @@ -1101,7 +1101,7 @@ def random_element(self, prec=None, integral=False): ######### class pAdicRingRelaxed(pAdicRelaxedGeneric, pAdicRingBaseGeneric): - """ + r""" An implementation of relaxed arithmetics over `\ZZ_p`. INPUT: @@ -1136,7 +1136,7 @@ def __init__(self, p, prec, print_mode, names): self._element_class_prefix = "pAdicRelaxedElement_" class pAdicFieldRelaxed(pAdicRelaxedGeneric, pAdicFieldBaseGeneric): - """ + r""" An implementation of relaxed arithmetics over `\QQ_p`. INPUT: From 94a60f9ee19e53e3cdadd5c46b325b4dc390a039 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 12 Jun 2021 23:05:55 +0200 Subject: [PATCH 209/280] E701, E702 for algebras, crypto, quadratic form, structure --- src/sage/algebras/free_algebra_quotient_element.py | 6 ++++-- .../algebras/lie_algebras/nilpotent_lie_algebra.py | 9 ++++++--- src/sage/crypto/lattice.py | 9 ++++++--- src/sage/crypto/mq/sr.py | 3 ++- src/sage/quadratic_forms/binary_qf.py | 3 ++- src/sage/structure/list_clone_timings.py | 12 ++++++++---- 6 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/sage/algebras/free_algebra_quotient_element.py b/src/sage/algebras/free_algebra_quotient_element.py index 9e291010fa2..0b364492741 100644 --- a/src/sage/algebras/free_algebra_quotient_element.py +++ b/src/sage/algebras/free_algebra_quotient_element.py @@ -244,7 +244,8 @@ def monomial_product(X,w,m): mats = X._FreeAlgebraQuotient__matrix_action for (j,k) in m._element_list: M = mats[int(j)] - for l in range(k): w *= M + for l in range(k): + w *= M return w u = self.__vector.__copy__() v = y.__vector @@ -252,7 +253,8 @@ def monomial_product(X,w,m): B = A.monomial_basis() for i in range(A.dimension()): c = v[i] - if c != 0: z.__vector += monomial_product(A,c*u,B[i]) + if c != 0: + z.__vector += monomial_product(A,c*u,B[i]) return z def _rmul_(self, c): diff --git a/src/sage/algebras/lie_algebras/nilpotent_lie_algebra.py b/src/sage/algebras/lie_algebras/nilpotent_lie_algebra.py index d2919917d33..54a0fb97d2a 100644 --- a/src/sage/algebras/lie_algebras/nilpotent_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/nilpotent_lie_algebra.py @@ -113,10 +113,13 @@ def __classcall_private__(cls, R, s_coeff, names=None, index_set=None, # extract names from structural coefficients names = [] for (X, Y), d in s_coeff.items(): - if X not in names: names.append(X) - if Y not in names: names.append(Y) + if X not in names: + names.append(X) + if Y not in names: + names.append(Y) for k in d: - if k not in names: names.append(k) + if k not in names: + names.append(k) from sage.structure.indexed_generators import standardize_names_index_set names, index_set = standardize_names_index_set(names, index_set) diff --git a/src/sage/crypto/lattice.py b/src/sage/crypto/lattice.py index 84da16e3401..b593bac9b03 100644 --- a/src/sage/crypto/lattice.py +++ b/src/sage/crypto/lattice.py @@ -222,7 +222,8 @@ def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, set_random_seed(seed) if type == 'random': - if n != 1: raise ValueError('random bases require n = 1') + if n != 1: + raise ValueError('random bases require n = 1') ZZ = IntegerRing() ZZ_q = IntegerModRing(q) @@ -272,8 +273,10 @@ def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, # switch from representatives 0,...,(q-1) to (1-q)/2,....,(q-1)/2 def minrep(a): - if abs(a-q) < abs(a): return a-q - else: return a + if abs(a-q) < abs(a): + return a-q + else: + return a A_prime = A[n:m].lift().apply_map(minrep) if not dual: diff --git a/src/sage/crypto/mq/sr.py b/src/sage/crypto/mq/sr.py index ab047b5f6cb..448583ea3a2 100644 --- a/src/sage/crypto/mq/sr.py +++ b/src/sage/crypto/mq/sr.py @@ -2650,7 +2650,8 @@ def phi(self, l, diffusion_matrix=False): return tuple(ret) elif is_Matrix(l): return Matrix(GF(2), l.ncols(), l.nrows()*self.e, ret).transpose() - else: raise TypeError + else: + raise TypeError def antiphi(self, l): r""" diff --git a/src/sage/quadratic_forms/binary_qf.py b/src/sage/quadratic_forms/binary_qf.py index a853f46651d..4a143e41487 100755 --- a/src/sage/quadratic_forms/binary_qf.py +++ b/src/sage/quadratic_forms/binary_qf.py @@ -1664,7 +1664,8 @@ def BinaryQF_reduced_representatives(D, primitive_only=False, proper=True): a4 = 4*a s = D + a*a4 w = 1+(s-1).isqrt() if s > 0 else 0 - if w%2 != D%2: w += 1 + if w%2 != D%2: + w += 1 for b in xsrange(w, a+1, 2): t = b*b-D if t % a4 == 0: diff --git a/src/sage/structure/list_clone_timings.py b/src/sage/structure/list_clone_timings.py index 73f5da69f24..efe5d20efc7 100644 --- a/src/sage/structure/list_clone_timings.py +++ b/src/sage/structure/list_clone_timings.py @@ -128,7 +128,8 @@ def add1_internal(bla): """ blo = bla.__copy__() lst = blo._get_list() - for i in range(len(blo)): lst[i] += 1 + for i in range(len(blo)): + lst[i] += 1 blo.set_immutable() blo.check() return blo @@ -142,7 +143,8 @@ def add1_immutable(bla): [2, 5, 6] """ lbla = bla[:] - for i in range(len(lbla)): lbla[i] += 1 + for i in range(len(lbla)): + lbla[i] += 1 return bla.__class__(bla.parent(), lbla) def add1_mutable(bla): @@ -154,7 +156,8 @@ def add1_mutable(bla): [2, 5, 6] """ blo = bla.__copy__() - for i in range(len(blo)): blo[i] += 1 + for i in range(len(blo)): + blo[i] += 1 blo.set_immutable() blo.check() return blo @@ -168,5 +171,6 @@ def add1_with(bla): [2, 5, 6] """ with bla.clone() as blo: - for i in range(len(blo)): blo[i] += 1 + for i in range(len(blo)): + blo[i] += 1 return blo From 1d8a6d68eaef586246625afc9512f483c03d7b60 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 12 Jun 2021 23:19:56 +0200 Subject: [PATCH 210/280] E701, E702 for interfaces --- src/sage/interfaces/expect.py | 3 ++- src/sage/interfaces/maxima.py | 3 ++- src/sage/interfaces/qepcad.py | 35 ++++++++++++++++++++++----------- src/sage/interfaces/singular.py | 3 ++- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/sage/interfaces/expect.py b/src/sage/interfaces/expect.py index bfd2817cc53..055b7c4b79d 100644 --- a/src/sage/interfaces/expect.py +++ b/src/sage/interfaces/expect.py @@ -1453,7 +1453,8 @@ class ExpectElement(InterfaceElement): def __init__(self, parent, value, is_name=False, name=None): RingElement.__init__(self, parent) self._create = value - if parent is None: return # means "invalid element" + if parent is None: + return # means "invalid element" # idea: Joe Wetherell -- try to find out if the output # is too long and if so get it using file, otherwise # don't. diff --git a/src/sage/interfaces/maxima.py b/src/sage/interfaces/maxima.py index a3192fffa3f..db362ae0997 100644 --- a/src/sage/interfaces/maxima.py +++ b/src/sage/interfaces/maxima.py @@ -839,7 +839,8 @@ def _synchronize(self): 4 """ marker = '__SAGE_SYNCHRO_MARKER_' - if self._expect is None: return + if self._expect is None: + return r = randrange(2147483647) s = marker + str(r+1) diff --git a/src/sage/interfaces/qepcad.py b/src/sage/interfaces/qepcad.py index 4d61ea1306e..222be41d285 100644 --- a/src/sage/interfaces/qepcad.py +++ b/src/sage/interfaces/qepcad.py @@ -1214,7 +1214,8 @@ def make_cells(self, text): for line in lines: if 'Information about the cell' in line: in_cell = True - if in_cell: cell_lines.append(line) + if in_cell: + cell_lines.append(line) if line == '----------------------------------------------------': cells.append(QepcadCell(self, cell_lines)) cell_lines = [] @@ -1285,10 +1286,12 @@ def _eval_line(self, cmd, restart_if_needed=False): result = self._qex._eval_line(cmd + ' &') nl = result.find('\n') - if nl < 0: nl = len(result) + if nl < 0: + nl = len(result) amp = result.find('&', 0, nl) - if amp > 0: result = result[amp+1:] + if amp > 0: + result = result[amp+1:] result = result.strip() @@ -1802,15 +1805,23 @@ def _normalize_op(self, op): '=' """ import operator - if op == operator.eq: return '=' - if op == operator.ne: return '/=' - if op == operator.lt: return '<' - if op == operator.gt: return '>' - if op == operator.le: return '<=' - if op == operator.ge: return '>=' - - if op == '==': return '=' - if op == '!=': return '/=' + if op == operator.eq: + return '=' + if op == operator.ne: + return '/=' + if op == operator.lt: + return '<' + if op == operator.gt: + return '>' + if op == operator.le: + return '<=' + if op == operator.ge: + return '>=' + + if op == '==': + return '=' + if op == '!=': + return '/=' return op diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index bc29515ccba..89b687d3182 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -1365,7 +1365,8 @@ def __init__(self, parent, type, value, is_name=False): 2 """ RingElement.__init__(self, parent) - if parent is None: return + if parent is None: + return if not is_name: try: self._name = parent._create(value, type) From a93d8f414ba2924a2ba98c3be676fad7dcd77699 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 12 Jun 2021 23:22:08 +0200 Subject: [PATCH 211/280] E701, E702 for groups --- src/sage/groups/perm_gps/cubegroup.py | 72 ++++++++++++++++++--------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/src/sage/groups/perm_gps/cubegroup.py b/src/sage/groups/perm_gps/cubegroup.py index 8be5b5611b3..4ace0ce13e3 100644 --- a/src/sage/groups/perm_gps/cubegroup.py +++ b/src/sage/groups/perm_gps/cubegroup.py @@ -429,30 +429,54 @@ def cubie_colors(label, state0): # colors of the cubies on the F,U, R faces clr_any = named_colors['white'] state = inv_list(state0) - if label == 1: return [clr_any, named_colors[color_of_square(state[1-1])], clr_any] #ulb, - if label == 2: return [clr_any,named_colors[color_of_square(state[2-1])],clr_any] # ub, - if label == 3: return [clr_any, named_colors[color_of_square(state[3-1])], named_colors[color_of_square(state[27-1])]] # ubr, - if label == 4: return [clr_any, named_colors[color_of_square(state[4-1])], clr_any] # ul, - if label == 5: return [clr_any, named_colors[color_of_square(state[5-1])], named_colors[color_of_square(state[26-1])]] # ur, - if label == 6: return [named_colors[color_of_square(state[17-1])], named_colors[color_of_square(state[6-1])], clr_any] # ufl, - if label == 7: return [named_colors[color_of_square(state[18-1])], named_colors[color_of_square(state[7-1])], clr_any] # uf, - if label == 8: return [named_colors[color_of_square(state[19-1])], named_colors[color_of_square(state[8-1])], named_colors[color_of_square(state[25-1])]] # urf, - if label == 17: return [named_colors[color_of_square(state[17-1])], named_colors[color_of_square(state[6-1])], clr_any] # flu - if label == 18: return [named_colors[color_of_square(state[18-1])], named_colors[color_of_square(state[7-1])], clr_any] # fu - if label == 19: return [named_colors[color_of_square(state[19-1])], named_colors[color_of_square(state[8-1])], named_colors[color_of_square(state[25-1])]] # fur - if label == 20: return [named_colors[color_of_square(state[20-1])], clr_any, clr_any] # fl - if label == 21: return [named_colors[color_of_square(state[21-1])], clr_any, named_colors[color_of_square(state[28-1])]] # fr - if label == 22: return [named_colors[color_of_square(state[22-1])], clr_any, clr_any] # fdl - if label == 23: return [named_colors[color_of_square(state[23-1])], clr_any, clr_any] # fd - if label == 24: return [named_colors[color_of_square(state[24-1])], clr_any, named_colors[color_of_square(state[30-1])]] # frd - if label == 25: return [named_colors[color_of_square(state[19-1])],named_colors[color_of_square(state[8-1])],named_colors[color_of_square(state[25-1])]] #rfu, - if label == 26: return [clr_any,named_colors[color_of_square(state[5-1])],named_colors[color_of_square(state[26-1])]] # ru, - if label == 27: return [clr_any,named_colors[color_of_square(state[3-1])],named_colors[color_of_square(state[27-1])]] # rub, - if label == 28: return [named_colors[color_of_square(state[21-1])],clr_any,named_colors[color_of_square(state[28-1])]] # rf, - if label == 29: return [clr_any,clr_any,named_colors[color_of_square(state[29-1])]] # rb, - if label == 30: return [named_colors[color_of_square(state[24-1])],clr_any,named_colors[color_of_square(state[30-1])]] # rdf, - if label == 31: return [clr_any,clr_any,named_colors[color_of_square(state[31-1])]] # rd, - if label == 32: return [clr_any,clr_any,named_colors[color_of_square(state[32-1])]] #rbd, + if label == 1: + return [clr_any, named_colors[color_of_square(state[1-1])], clr_any] #ulb, + if label == 2: + return [clr_any,named_colors[color_of_square(state[2-1])],clr_any] # ub, + if label == 3: + return [clr_any, named_colors[color_of_square(state[3-1])], named_colors[color_of_square(state[27-1])]] # ubr, + if label == 4: + return [clr_any, named_colors[color_of_square(state[4-1])], clr_any] # ul, + if label == 5: + return [clr_any, named_colors[color_of_square(state[5-1])], named_colors[color_of_square(state[26-1])]] # ur, + if label == 6: + return [named_colors[color_of_square(state[17-1])], named_colors[color_of_square(state[6-1])], clr_any] # ufl, + if label == 7: + return [named_colors[color_of_square(state[18-1])], named_colors[color_of_square(state[7-1])], clr_any] # uf, + if label == 8: + return [named_colors[color_of_square(state[19-1])], named_colors[color_of_square(state[8-1])], named_colors[color_of_square(state[25-1])]] # urf, + if label == 17: + return [named_colors[color_of_square(state[17-1])], named_colors[color_of_square(state[6-1])], clr_any] # flu + if label == 18: + return [named_colors[color_of_square(state[18-1])], named_colors[color_of_square(state[7-1])], clr_any] # fu + if label == 19: + return [named_colors[color_of_square(state[19-1])], named_colors[color_of_square(state[8-1])], named_colors[color_of_square(state[25-1])]] # fur + if label == 20: + return [named_colors[color_of_square(state[20-1])], clr_any, clr_any] # fl + if label == 21: + return [named_colors[color_of_square(state[21-1])], clr_any, named_colors[color_of_square(state[28-1])]] # fr + if label == 22: + return [named_colors[color_of_square(state[22-1])], clr_any, clr_any] # fdl + if label == 23: + return [named_colors[color_of_square(state[23-1])], clr_any, clr_any] # fd + if label == 24: + return [named_colors[color_of_square(state[24-1])], clr_any, named_colors[color_of_square(state[30-1])]] # frd + if label == 25: + return [named_colors[color_of_square(state[19-1])],named_colors[color_of_square(state[8-1])],named_colors[color_of_square(state[25-1])]] #rfu, + if label == 26: + return [clr_any,named_colors[color_of_square(state[5-1])],named_colors[color_of_square(state[26-1])]] # ru, + if label == 27: + return [clr_any,named_colors[color_of_square(state[3-1])],named_colors[color_of_square(state[27-1])]] # rub, + if label == 28: + return [named_colors[color_of_square(state[21-1])],clr_any,named_colors[color_of_square(state[28-1])]] # rf, + if label == 29: + return [clr_any,clr_any,named_colors[color_of_square(state[29-1])]] # rb, + if label == 30: + return [named_colors[color_of_square(state[24-1])],clr_any,named_colors[color_of_square(state[30-1])]] # rdf, + if label == 31: + return [clr_any,clr_any,named_colors[color_of_square(state[31-1])]] # rd, + if label == 32: + return [clr_any,clr_any,named_colors[color_of_square(state[32-1])]] #rbd, def plot3d_cubie(cnt, clrs): From 3d0726c72e69935a3312c94631c818111977f547 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 12 Jun 2021 23:26:09 +0200 Subject: [PATCH 212/280] E701, E702 for databases --- src/sage/databases/cremona.py | 24 ++++++++++++++++-------- src/sage/databases/sql_db.py | 21 ++++++++++++++------- src/sage/databases/symbolic_data.py | 3 ++- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/sage/databases/cremona.py b/src/sage/databases/cremona.py index 680543209de..e388d0558b0 100644 --- a/src/sage/databases/cremona.py +++ b/src/sage/databases/cremona.py @@ -1437,7 +1437,8 @@ def _init_allcurves(self, ftpdata, largest_conductor=0): curve_data = [] for L in open(ftpdata + "/" + F).readlines(): N, iso, num, ainvs, r, tor = L.split() - if largest_conductor and int(N) > largest_conductor: break + if largest_conductor and int(N) > largest_conductor: + break cls = N+iso cur = cls+num if num == "1": @@ -1452,7 +1453,8 @@ def _init_allcurves(self, ftpdata, largest_conductor=0): print("Committing...") print("num_iso_classes =", num_iso_classes) self.commit() - if largest_conductor and int(N) > largest_conductor: break + if largest_conductor and int(N) > largest_conductor: + break return num_curves, num_iso_classes @@ -1586,13 +1588,15 @@ def _init_degphi(self, ftpdata, largest_conductor=0): class_data = [] for L in open(ftpdata + "/" + F).readlines(): N, iso, num, degree, primes, curve = L.split() - if largest_conductor and int(N) > largest_conductor: break + if largest_conductor and int(N) > largest_conductor: + break class_data.append((degree,N+iso)) con.executemany('UPDATE t_class SET deg=? WHERE class=?', class_data) print("Committing...") self.commit() - if largest_conductor and int(N) > largest_conductor: break + if largest_conductor and int(N) > largest_conductor: + break def _init_allbsd(self, ftpdata, largest_conductor=0): """ @@ -1620,7 +1624,8 @@ def _init_allbsd(self, ftpdata, largest_conductor=0): class_data = [] for L in open(ftpdata + "/" + F).readlines(): N, iso, num, eqn, rank, tor, cp, om, L, reg, sha = L.split() - if largest_conductor and int(N) > largest_conductor: break + if largest_conductor and int(N) > largest_conductor: + break cls = N+iso if num == "1": class_data.append((L,cls)) @@ -1630,7 +1635,8 @@ def _init_allbsd(self, ftpdata, largest_conductor=0): + "curve=?", curve_data) print("Committing...") self.commit() - if largest_conductor and int(N) > largest_conductor: break + if largest_conductor and int(N) > largest_conductor: + break def _init_allgens(self, ftpdata, largest_conductor=0): """ @@ -1657,13 +1663,15 @@ def _init_allgens(self, ftpdata, largest_conductor=0): curve_data = [] for L in open(ftpdata + "/" + F).readlines(): v = L.split() - if largest_conductor and int(v[0]) > largest_conductor: break + if largest_conductor and int(v[0]) > largest_conductor: + break gens = '['+','.join(v[6:6+int(v[4])]).replace(':',',')+']' curve_data.append((gens,''.join(v[:3]))) con.executemany("UPDATE t_curve SET gens=? WHERE curve=?", curve_data) print("Committing...") - if largest_conductor and int(v[0]) > largest_conductor: break + if largest_conductor and int(v[0]) > largest_conductor: + break _db = None diff --git a/src/sage/databases/sql_db.py b/src/sage/databases/sql_db.py index 7567ada4c2c..6189e4d3d91 100644 --- a/src/sage/databases/sql_db.py +++ b/src/sage/databases/sql_db.py @@ -672,7 +672,8 @@ def show(self, **kwds): C^ [2, 2, 3, 3] C~ [3, 3, 3, 3] """ - if not self.__query_string__: return self.__database__.show() + if not self.__query_string__: + return self.__database__.show() try: cur = self.__database__.__connection__.cursor() @@ -761,13 +762,16 @@ def intersect(self, other, join_table=None, join_dict=None, \ if not self.__query_string__: self.__query_string__ = other.__query_string__ self.__param_tuple__ = other.__param_tuple__ - elif not other.__query_string__: return + elif not other.__query_string__: + return else: self._merge_queries(other, self, join_table, join_dict, 'AND') else: from copy import copy - if not self.__query_string__: return copy(other) - if not other.__query_string__: return copy(self) + if not self.__query_string__: + return copy(other) + if not other.__query_string__: + return copy(self) return self._merge_queries(other, copy(self), join_table, \ join_dict, 'AND') @@ -882,8 +886,10 @@ def union(self, other, join_table=None, join_dict=None, in_place=False): self._merge_queries(other, self, join_table, join_dict, 'OR') else: from copy import copy - if not self.__query_string__: return copy(self) - if not other.__query_string__: return copy(other) + if not self.__query_string__: + return copy(self) + if not other.__query_string__: + return copy(other) return self._merge_queries(other, copy(self), join_table, \ join_dict, 'OR') @@ -1637,7 +1643,8 @@ def _rebuild_table(self, table_name, col_name=None, default=''): self.__skeleton__[table_name] if \ self.__skeleton__[table_name][col]['index'] and not \ self.__skeleton__[table_name][col]['primary_key']]) - if index_statement: self.__connection__.executescript(index_statement) + if index_statement: + self.__connection__.executescript(index_statement) # Now we can plop our data into the *new* table: self.__connection__.executescript(""" diff --git a/src/sage/databases/symbolic_data.py b/src/sage/databases/symbolic_data.py index 7f636c498c6..ccd7fb3eadc 100644 --- a/src/sage/databases/symbolic_data.py +++ b/src/sage/databases/symbolic_data.py @@ -201,7 +201,8 @@ def trait_names(self): 'Curves__curve10_20', 'Curves__curve10_30'] """ - if hasattr(self,"__ideals"): return self.__ideals + if hasattr(self,"__ideals"): + return self.__ideals try: __ideals = [s.replace('.xml','') for s in os.listdir(self.__intpath)] __ideals += [s.replace('.xml','') for s in os.listdir(self.__genpath)] From 1af0980d128b6685f6e6c0447b62b799842cb51c Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sun, 13 Jun 2021 08:07:27 +0200 Subject: [PATCH 213/280] some fixes in the lines touched anyway --- src/sage/interacts/library.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/interacts/library.py b/src/sage/interacts/library.py index dd46fda92e5..13c9e6d73ff 100644 --- a/src/sage/interacts/library.py +++ b/src/sage/interacts/library.py @@ -1670,7 +1670,7 @@ def polar_prime_spiral( else: list2.append(f(i-start+1)) # Composites list P = points(list) - R = points(list2, alpha = .1) # Faded Composites + R = points(list2, alpha=.1) # Faded Composites else: for i in srange(start, end, include_endpoint = True): list.append(disk((f(i-start+1)),0.05*pow(2,len(factor(i))-1), (0,2*pi))) # resizes each of the dots depending of the number of factors of each number @@ -1714,7 +1714,8 @@ def polar_prime_spiral( r = symbolic_expression(sqrt(g(m))).function(m) theta = symbolic_expression(r(m)- m*sqrt(a)).function(m) S1 = parametric_plot(((r(t))*cos(2*pi*(theta(t))),(r(t))*sin(2*pi*(theta(t)))), - (begin_curve, ceil(sqrt(end-start))), color=hue(0.8), thickness=.3) #Pink Line + (begin_curve, ceil(sqrt(end-start))), + color=hue(0.8), thickness=.3) # Pink Line b = 1 c = c2; @@ -1722,7 +1723,8 @@ def polar_prime_spiral( r = symbolic_expression(sqrt(g(m))).function(m) theta = symbolic_expression(r(m)- m*sqrt(a)).function(m) S2 = parametric_plot(((r(t))*cos(2*pi*(theta(t))),(r(t))*sin(2*pi*(theta(t)))), - (begin_curve, ceil(sqrt(end-start))), color=hue(0.6), thickness=.3) #Green Line + (begin_curve, ceil(sqrt(end-start))), + color=hue(0.6), thickness=.3) # Green Line show(R+P+S1+S2+Q, aspect_ratio=1, axes=False, dpi=dpi) else: From d61b13a7a911c8643b04049d6e008f97e4b46597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Leli=C3=A8vre?= Date: Sun, 13 Jun 2021 23:31:27 +0200 Subject: [PATCH 214/280] 31968: Improve imports and docstrings - Improve imports in `sage/interacts/library.py` - Make docstrings raw - Fix input blocks in docstrings - Fix a few typos such as swith -> switch --- src/sage/interacts/library.py | 309 ++++++++++++++----------- src/sage/knots/link.py | 50 ++-- src/sage/rings/padics/local_generic.py | 92 ++++---- src/sage/rings/padics/padic_generic.py | 258 +++++++++++---------- 4 files changed, 376 insertions(+), 333 deletions(-) diff --git a/src/sage/interacts/library.py b/src/sage/interacts/library.py index 13c9e6d73ff..f4abde285a4 100644 --- a/src/sage/interacts/library.py +++ b/src/sage/interacts/library.py @@ -1,4 +1,4 @@ -""" +r""" Sage Interacts Sage interacts are applications of the `@interact decorator <../../sagenb/notebook/interact.html>`_. @@ -20,23 +20,53 @@ AUTHORS: - William Stein - -- Harald Schilly, Robert Marik (2011-01-16): added many examples (#9623) partially based on work by Lauri Ruotsalainen - +- Harald Schilly, Robert Marik (2011-01-16): added many examples (#9623) + partially based on work by Lauri Ruotsalainen """ -#***************************************************************************** +# ***************************************************************************** # Copyright (C) 2009 William Stein # Copyright (C) 2011 Harald Schilly # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# +# https://www.gnu.org/licenses/ +# ***************************************************************************** + +from sage.arith.misc import factor +from sage.arith.srange import srange +from sage.calculus.all import symbolic_expression +from sage.calculus.functional import derivative +from sage.calculus.integration import numerical_integral as integral_numerical +from sage.ext.fast_callable import fast_callable +from sage.functions.log import exp +from sage.functions.other import sqrt +from sage.functions.trig import (acos, cos, sin, tan) +from sage.misc.decorators import sage_wraps +from sage.misc.functional import N +from sage.misc.latex import latex +from sage.misc.sage_eval import sage_eval +from sage.misc.table import table +from sage.plot.circle import circle +from sage.plot.complex_plot import complex_plot +from sage.plot.disk import disk +from sage.plot.graphics import Graphics +from sage.plot.line import (line, line2d) +from sage.plot.matrix_plot import matrix_plot +from sage.plot.plot import (graphics_array, parametric_plot, plot) +from sage.plot.point import (point, points) +from sage.plot.polygon import polygon2d +from sage.plot.text import text +from sage.repl.rich_output.pretty_print import (pretty_print, show) +from sage.rings.complex_double import CDF +from sage.rings.integer import Integer +from sage.symbolic.constants import pi +from sage.symbolic.relation import solve +from sage.symbolic.ring import SR +import math -from sage.all import * x = SR.var('x') # It is important that this file is lazily imported for this to work @@ -46,7 +76,7 @@ input_box, input_grid, range_slider, selector, slider, text_control) def library_interact(f): - """ + r""" This is a decorator for using interacts in the Sage library. This is just the ``interact`` function wrapped in an additional @@ -71,7 +101,7 @@ def library_wrapper(): def html(obj): - """ + r""" Shorthand to pretty print HTML EXAMPLES:: @@ -86,13 +116,13 @@ def html(obj): @library_interact def demo(n=slider(range(10)), m=slider(range(10))): - """ + r""" This is a demo interact that sums two numbers. INPUT: - - ``n`` -- integer slider - - ``m`` -- integer slider + - ``n`` -- integer slider + - ``m`` -- integer slider EXAMPLES: @@ -112,12 +142,14 @@ def demo(n=slider(range(10)), m=slider(range(10))): def taylor_polynomial( title = text_control('

Taylor polynomial

'), f=input_box(sin(x)*exp(-x),label="$f(x)=$"), order=slider(range(1,13))): - """ - An interact which illustrates the Taylor polynomial approximation + r""" + Illustrate the Taylor polynomial approximation of various orders around `x=0`. - - ``f`` -- function expression - - ```order``` -- integer slider + INPUT: + + - ``f`` -- function expression + - ``order`` -- integer slider EXAMPLES: @@ -151,16 +183,16 @@ def definite_integral( interval = range_slider(-10,10,default=(0,3), label="Interval"), x_range = range_slider(-10,10,default=(0,3), label = "plot range (x)"), selection = selector(["f", "g", "f and g", "f - g"], default="f and g", label="Select")): - """ + r""" This is a demo interact for plotting the definite integral of a function based on work by Lauri Ruotsalainen, 2010. INPUT: - - ``function`` -- input box, function in x - - ``interval`` -- interval for the definite integral - - ``x_range`` -- range slider for plotting range - - ``selection`` -- selector on how to visualize the integrals + - ``function`` -- input box, function in x + - ``interval`` -- interval for the definite integral + - ``x_range`` -- range slider for plotting range + - ``selection`` -- selector on how to visualize the integrals EXAMPLES: @@ -176,7 +208,8 @@ def definite_integral( g: EvalText(value=u'x^2', description=u'$g(x)=$', layout=Layout(max_width=u'81em')) interval: IntRangeSlider(value=(0, 3), description=u'Interval', max=10, min=-10) x_range: IntRangeSlider(value=(0, 3), description=u'plot range (x)', max=10, min=-10) - selection: Dropdown(description=u'Select', index=2, options=('f', 'g', 'f and g', 'f - g'), value='f and g') + selection: Dropdown(description=u'Select', index=2, + options=('f', 'g', 'f and g', 'f - g'), value='f and g') """ x = SR.var('x') f = symbolic_expression(f).function(x) @@ -233,7 +266,7 @@ def function_derivative( function = input_box(default="x^5-3*x^3+1", label="Function:"), x_range = range_slider(-15,15,0.1, default=(-2,2), label="Range (x)"), y_range = range_slider(-15,15,0.1, default=(-8,6), label="Range (y)")): - """ + r""" This is a demo interact for plotting derivatives of a function based on work by Lauri Ruotsalainen, 2010. @@ -279,16 +312,16 @@ def difference_quotient( interval= range_slider(0, 10, 0.1, default=(0.0,10.0), label="Range"), a = slider(0, 10, None, 5.5, label = '$a$'), x0 = slider(0, 10, None, 2.5, label = '$x_0$ (start point)')): - """ + r""" This is a demo interact for difference quotient based on work by Lauri Ruotsalainen, 2010. INPUT: - - ``f`` -- input box, function in `x` - - ``interval`` -- range slider for plotting - - ``a`` -- slider for `a` - - ``x0`` -- slider for starting point `x_0` + - ``f`` -- input box, function in `x` + - ``interval`` -- range slider for plotting + - ``a`` -- slider for `a` + - ``x0`` -- slider for starting point `x_0` EXAMPLES: @@ -340,15 +373,15 @@ def difference_quotient( @library_interact def quadratic_equation(A = slider(-7, 7, 1, 1), B = slider(-7, 7, 1, 1), C = slider(-7, 7, 1, -2)): - """ + r""" This is a demo interact for solving quadratic equations based on work by Lauri Ruotsalainen, 2010. INPUT: - - ``A`` -- integer slider - - ``B`` -- integer slider - - ``C`` -- integer slider + - ``A`` -- integer slider + - ``B`` -- integer slider + - ``C`` -- integer slider EXAMPLES: @@ -401,15 +434,15 @@ def trigonometric_properties_triangle( a0 = slider(0, 360, 1, 30, label="A"), a1 = slider(0, 360, 1, 180, label="B"), a2 = slider(0, 360, 1, 300, label="C")): - """ + r""" This is an interact for demonstrating trigonometric properties in a triangle based on work by Lauri Ruotsalainen, 2010. INPUT: - - ``a0`` -- angle - - ``a1`` -- angle - - ``a2`` -- angle + - ``a0`` -- angle + - ``a1`` -- angle + - ``a2`` -- angle EXAMPLES: @@ -481,14 +514,14 @@ def area(alpha, a, b): def unit_circle( function = selector([(0, sin(x)), (1, cos(x)), (2, tan(x))]), x = slider(0,2*pi, 0.005*pi, 0)): - """ + r""" This is an interact for Sin, Cos and Tan in the Unit Circle based on work by Lauri Ruotsalainen, 2010. INPUT: - - ``function`` -- select Sin, Cos or Tan - - ``x`` -- slider to select angle in unit circle + - ``function`` -- select Sin, Cos or Tan + - ``x`` -- slider to select angle in unit circle EXAMPLES: @@ -563,21 +596,21 @@ def special_points( show_ab = checkbox(False, label="Angle Bisectors"), show_incircle = checkbox(False, label="Incircle"), show_euler = checkbox(False, label="Euler's Line")): - """ + r""" This interact demo shows special points in a triangle based on work by Lauri Ruotsalainen, 2010. INPUT: - - ``a0`` -- angle - - ``a1`` -- angle - - ``a2`` -- angle - - ``show_median`` -- checkbox - - ``show_pb`` -- checkbox to show perpendicular bisectors - - ``show_alt`` -- checkbox to show altitudes - - ``show_ab`` -- checkbox to show angle bisectors - - ``show_incircle`` -- checkbox to show incircle - - ``show_euler`` -- checkbox to show euler's line + - ``a0`` -- angle + - ``a1`` -- angle + - ``a2`` -- angle + - ``show_median`` -- checkbox + - ``show_pb`` -- checkbox to show perpendicular bisectors + - ``show_alt`` -- checkbox to show altitudes + - ``show_ab`` -- checkbox to show angle bisectors + - ``show_incircle`` -- checkbox to show incircle + - ``show_euler`` -- checkbox to show euler's line EXAMPLES: @@ -732,7 +765,7 @@ def line_to_points(x1_y1, x2_y2, **plot_kwargs): @library_interact def coin(n = slider(2,10000, 100, default=1000, label="Number of Tosses"), interval = range_slider(0, 1, default=(0.45, 0.55), label="Plotting range (y)")): - """ + r""" This interact demo simulates repeated tosses of a coin, based on work by Lauri Ruotsalainen, 2010. @@ -744,9 +777,8 @@ def coin(n = slider(2,10000, 100, default=1000, label="Number of Tosses"), inter INPUT: - - ``n`` -- number of tosses - - ``interval`` -- plot range along - vertical axis + - ``n`` -- number of tosses + - ``interval`` -- plot range along vertical axis EXAMPLES: @@ -776,16 +808,16 @@ def bisection_method( interval = range_slider(-5,5,default=(0, 4), label="range"), d = slider(1, 8, 1, 3, label="$10^{-d}$ precision"), maxn = slider(0,50,1,10, label="max iterations")): - """ + r""" Interact explaining the bisection method, based on similar interact explaining secant method and Wiliam Stein's example from wiki. INPUT: - - ``f`` -- function - - ``interval`` -- range slider for the search interval - - ``d`` -- slider for the precision (`10^{-d}`) - - ``maxn`` -- max number of iterations + - ``f`` -- function + - ``interval`` -- range slider for the search interval + - ``d`` -- slider for the precision (`10^{-d}`) + - ``maxn`` -- max number of iterations EXAMPLES: @@ -853,17 +885,17 @@ def secant_method( interval = range_slider(-5,5,default=(0, 4), label="range"), d = slider(1, 16, 1, 3, label="10^-d precision"), maxn = slider(0,15,1,10, label="max iterations")): - """ + r""" Interact explaining the secant method, based on work by Lauri Ruotsalainen, 2010. Originally this is based on work by William Stein. INPUT: - - ``f`` -- function - - ``interval`` -- range slider for the search interval - - ``d`` -- slider for the precision (10^-d) - - ``maxn`` -- max number of iterations + - ``f`` -- function + - ``interval`` -- range slider for the search interval + - ``d`` -- slider for the precision (10^-d) + - ``maxn`` -- max number of iterations EXAMPLES: @@ -922,19 +954,19 @@ def newton_method( maxn = slider(0, 15, 1, 10, label="max iterations"), interval = range_slider(-10,10, default = (0,6), label="Interval"), list_steps = checkbox(default=False, label="List steps")): - """ + r""" Interact explaining the Newton method, based on work by Lauri Ruotsalainen, 2010. Originally this is based on work by William Stein. INPUT: - - ``f`` -- function - - ``c`` -- starting position (`x`) - - ``d`` -- slider for the precision (`10^{-d}`) - - ``maxn`` -- max number of iterations - - ``interval`` -- range slider for the search interval - - ``list_steps`` -- checkbox, if true shows the steps numerically + - ``f`` -- function + - ``c`` -- starting position (`x`) + - ``d`` -- slider for the precision (`10^{-d}`) + - ``maxn`` -- max number of iterations + - ``interval`` -- range slider for the search interval + - ``list_steps`` -- checkbox, if true shows the steps numerically EXAMPLES: @@ -995,19 +1027,21 @@ def trapezoid_integration( interval_g = input_grid(1,2,default=[[0,8]], label="keyboard: "), output_form = selector(['traditional','table','none'], label='Computations form', buttons=True) ): - """ - Interact explaining the trapezoid method for definite integrals, based on work by + r""" + Interact explaining the trapezoid method for definite integrals. + + Based on work by Lauri Ruotsalainen, 2010 (based on the application "Numerical integrals with various rules" by Marshall Hampton and Nick Alexander) INPUT: - - ``f`` -- function of variable x to integrate - - ``n`` -- number of divisions - - ``interval_input`` -- swithes the input for interval between slider and keyboard - - ``interval_s`` -- slider for interval to integrate - - ``interval_g`` -- input grid for interval to integrate - - ``output_form`` -- the computation is formatted in a traditional form, in a table or missing + - ``f`` -- function of variable x to integrate + - ``n`` -- number of divisions + - ``interval_input`` -- switches the input for interval between slider and keyboard + - ``interval_s`` -- slider for interval to integrate + - ``interval_g`` -- input grid for interval to integrate + - ``output_form`` -- the computation is formatted in a traditional form, in a table or missing EXAMPLES: @@ -1109,19 +1143,21 @@ def simpson_integration( interval_s = range_slider(-10,10,default=(0,10), label="slider: "), interval_g = input_grid(1,2,default=[[0,10]], label="keyboard: "), output_form = selector(['traditional','table','none'], label='Computations form', buttons=True)): - """ - Interact explaining the simpson method for definite integrals, based on work by + r""" + Interact explaining the simpson method for definite integrals. + + Based on work by Lauri Ruotsalainen, 2010 (based on the application "Numerical integrals with various rules" by Marshall Hampton and Nick Alexander) INPUT: - - ``f`` -- function of variable x to integrate - - ``n`` -- number of divisions (mult. of 2) - - ``interval_input`` -- swithes the input for interval between slider and keyboard - - ``interval_s`` -- slider for interval to integrate - - ``interval_g`` -- input grid for interval to integrate - - ``output_form`` -- the computation is formatted in a traditional form, in a table or missing + - ``f`` -- function of variable x to integrate + - ``n`` -- number of divisions (mult. of 2) + - ``interval_input`` -- switches the input for interval between slider and keyboard + - ``interval_s`` -- slider for interval to integrate + - ``interval_g`` -- input grid for interval to integrate + - ``output_form`` -- the computation is formatted in a traditional form, in a table or missing EXAMPLES: @@ -1242,14 +1278,14 @@ def riemann_sum( hr2 = text_control('
'), list_table = checkbox(default=False, label="List table"), auto_update = False): - """ + r""" Interact explaining the definition of Riemann integral INPUT: - ``f`` -- function of variable x to integrate - ``n`` -- number of divisions - - ``interval_input`` -- swithes the input for interval between slider and keyboard + - ``interval_input`` -- switches the input for interval between slider and keyboard - ``interval_s`` -- slider for interval to integrate - ``interval_g`` -- input grid for interval to integrate - ``list_table`` -- print table with values of the function @@ -1275,7 +1311,7 @@ def riemann_sum( AUTHORS: - - Robert Marik (08-2010) + - Robert Marik (2010-08) """ x = SR.var('x') from random import random @@ -1327,19 +1363,19 @@ def function_tool(f=sin(x), g=cos(x), xrange=range_slider(-3,3,default=(0,1),lab 'f+g', 'f-g', 'f*g', 'f/g', 'f(g)'], width=15, nrows=5, label="h = "), do_plot = ("Draw Plots", True)): - """ + r""" `Function Plotting Tool `_ (by William Stein (?)) INPUT: - - ``f`` -- function f(x) - - ``g`` -- function g(x) - - ``xrange`` -- range for plotting (x) - - ``yrange`` -- range for plotting ('auto' is default, otherwise a tuple) - - ``a`` -- factor ``a`` - - ``action`` -- select given operation on or combination of functions - - ``do_plot`` -- if true, a plot is drawn + - ``f`` -- function f(x) + - ``g`` -- function g(x) + - ``xrange`` -- range for plotting (x) + - ``yrange`` -- range for plotting ('auto' is default, otherwise a tuple) + - ``a`` -- factor ``a`` + - ``action`` -- select given operation on or combination of functions + - ``do_plot`` -- if true, a plot is drawn EXAMPLES: @@ -1390,7 +1426,7 @@ def function_tool(f=sin(x), g=cos(x), xrange=range_slider(-3,3,default=(0,1),lab h = 1/f lbl = r'\frac{1}{f}' elif action == 'finv': - h = solve(f == var('y'), x)[0].rhs() + h = solve(f == SR.var('y'), x)[0].rhs() lbl = 'f^{-1}(y)' elif action == 'f+a': h = f+a @@ -1453,20 +1489,20 @@ def julia(expo = slider(-10,10,0.1,2), zoom_y = range_slider(-2,2,0.01,(-1.5,1.5), label='Zoom Y'), plot_points = slider(20,400,20, default=150, label='plot points'), dpi = slider(20, 200, 10, default=80, label='dpi')): - """ + r""" Julia Fractal, based on `Julia by Harald Schilly `_. INPUT: - - ``exponent`` -- exponent ``e`` in `z^e+c` - - ``c_real`` -- real part of the constant ``c`` - - ``c_imag`` -- imaginary part of the constant ``c`` - - ``iterations`` -- number of iterations - - ``zoom_x`` -- range slider for zoom in x direction - - ``zoom_y`` -- range slider for zoom in y direction - - ``plot_points`` -- number of points to plot - - ``dpi`` -- dots-per-inch parameter for the plot + - ``exponent`` -- exponent ``e`` in `z^e+c` + - ``c_real`` -- real part of the constant ``c`` + - ``c_imag`` -- imaginary part of the constant ``c`` + - ``iterations`` -- number of iterations + - ``zoom_x`` -- range slider for zoom in x direction + - ``zoom_y`` -- range slider for zoom in y direction + - ``plot_points`` -- number of points to plot + - ``dpi`` -- dots-per-inch parameter for the plot EXAMPLES: @@ -1504,18 +1540,18 @@ def mandelbrot(expo = slider(-10,10,0.1,2), zoom_y = range_slider(-2,2,0.01,(-1.5,1.5), label='Zoom Y'), plot_points = slider(20,400,20, default=150, label='plot points'), dpi = slider(20, 200, 10, default=80, label='dpi')): - """ + r""" Mandelbrot Fractal, based on `Mandelbrot by Harald Schilly `_. INPUT: - - ``exponent`` -- exponent ``e`` in `z^e+c` - - ``iterations`` -- number of iterations - - ``zoom_x`` -- range slider for zoom in x direction - - ``zoom_y`` -- range slider for zoom in y direction - - ``plot_points`` -- number of points to plot - - ``dpi`` -- dots-per-inch parameter for the plot + - ``exponent`` -- exponent ``e`` in `z^e+c` + - ``iterations`` -- number of iterations + - ``zoom_x`` -- range slider for zoom in x direction + - ``zoom_y`` -- range slider for zoom in y direction + - ``plot_points`` -- number of points to plot + - ``dpi`` -- dots-per-inch parameter for the plot EXAMPLES: @@ -1549,7 +1585,7 @@ def cellular_automaton( N=slider(1,500,1,label='Number of iterations',default=100), rule_number=slider(0, 255, 1, default=110, label='Rule number'), size = slider(1, 11, step_size=1, default=6, label='size of graphic')): - """ + r""" Yields a matrix showing the evolution of a `Wolfram's cellular automaton `_. @@ -1557,9 +1593,9 @@ def cellular_automaton( INPUT: - - ``N`` -- iterations - - ``rule_number`` -- rule number (0 to 255) - - ``size`` -- size of the shown picture + - ``N`` -- iterations + - ``rule_number`` -- rule number (0 to 255) + - ``size`` -- size of the shown picture EXAMPLES: @@ -1604,7 +1640,7 @@ def polar_prime_spiral( show_curves = True, n = slider(1,200, 1, default=89, label="number $n$"), dpi = slider(10,300, 10, default=100, label="dpi")): - """ + r""" Polar Prime Spiral interact, based on work by David Runde. For more information about the factors in the spiral, @@ -1612,12 +1648,12 @@ def polar_prime_spiral( INPUT: - - ``interval`` -- range slider to specify start and end - - ``show_factors`` -- if true, show factors - - ``highlight_primes`` -- if true, prime numbers are highlighted - - ``show_curves`` -- if true, curves are plotted - - ``n`` -- number `n` - - ``dpi`` -- dots per inch resolution for plotting + - ``interval`` -- range slider to specify start and end + - ``show_factors`` -- if true, show factors + - ``highlight_primes`` -- if true, prime numbers are highlighted + - ``show_curves`` -- if true, curves are plotted + - ``n`` -- number `n` + - ``dpi`` -- dots per inch resolution for plotting EXAMPLES: @@ -1666,14 +1702,15 @@ def polar_prime_spiral( if not show_factors: for i in srange(start, end, include_endpoint = True): if Integer(i).is_pseudoprime(): - list.append(f(i-start+1)) # Primes list + list.append(f(i-start+1)) # primes list else: - list2.append(f(i-start+1)) # Composites list + list2.append(f(i-start+1)) # composites list P = points(list) - R = points(list2, alpha=.1) # Faded Composites + R = points(list2, alpha=.1) # faded composites else: for i in srange(start, end, include_endpoint = True): - list.append(disk((f(i-start+1)),0.05*pow(2,len(factor(i))-1), (0,2*pi))) # resizes each of the dots depending of the number of factors of each number + # Resize each of the dots depending of the number of factors of each number + list.append(disk((f(i-start+1)),0.05*pow(2,len(factor(i))-1), (0,2*pi))) if Integer(i).is_pseudoprime() and highlight_primes: list2.append(f(i-start+1)) P = Graphics() @@ -1695,12 +1732,12 @@ def polar_prime_spiral( W4 = disk((f(n-start+1)), p, (10*pi/6, 11*pi/6), alpha=.1) Q = W1 + W2 + W3 + W4 - n = n - start +1 # offsets the n for different start values to ensure accurate plotting + n -= start - 1 # offset n for different start values to ensure accurate plotting if show_curves: begin_curve = 0 t = SR.var('t') - a=1.0 - b=0.0 + a = 1.0 + b = 0.0 S = int(sqrt(n)) if n <= S * (S + 1): c = n - S**2 @@ -1715,7 +1752,7 @@ def polar_prime_spiral( theta = symbolic_expression(r(m)- m*sqrt(a)).function(m) S1 = parametric_plot(((r(t))*cos(2*pi*(theta(t))),(r(t))*sin(2*pi*(theta(t)))), (begin_curve, ceil(sqrt(end-start))), - color=hue(0.8), thickness=.3) # Pink Line + color=hue(0.8), thickness=.3) # pink line b = 1 c = c2; @@ -1724,7 +1761,7 @@ def polar_prime_spiral( theta = symbolic_expression(r(m)- m*sqrt(a)).function(m) S2 = parametric_plot(((r(t))*cos(2*pi*(theta(t))),(r(t))*sin(2*pi*(theta(t)))), (begin_curve, ceil(sqrt(end-start))), - color=hue(0.6), thickness=.3) # Green Line + color=hue(0.6), thickness=.3) # green line show(R+P+S1+S2+Q, aspect_ratio=1, axes=False, dpi=dpi) else: diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index 6b5a864b0ff..38ba588d882 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -48,6 +48,7 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. +# # https://www.gnu.org/licenses/ # **************************************************************************** @@ -277,7 +278,7 @@ class Link(SageObject): """ def __init__(self, data): - """ + r""" Initialize ``self``. TESTS:: @@ -509,7 +510,7 @@ def fundamental_group(self, presentation='wirtinger'): return F.quotient(rels) def _repr_(self): - """ + r""" Return a string representation. OUTPUT: string representation @@ -533,7 +534,7 @@ def _repr_(self): return 'Link with {} component{} represented by {} crossings'.format(number_of_components, plural, pd_len) def __eq__(self, other): - """ + r""" Check equality. TESTS:: @@ -558,7 +559,7 @@ def __eq__(self, other): return self.braid() == other.braid() def __hash__(self): - """ + r""" Return the hash of ``self``. EXAMPLES:: @@ -570,7 +571,7 @@ def __hash__(self): return hash(self.braid()) def __ne__(self, other): - """ + r""" Check inequality. TESTS:: @@ -588,7 +589,7 @@ def __ne__(self, other): def braid(self): - """ + r""" Return a braid representation of ``self``. OUTPUT: an element in the braid group @@ -1214,7 +1215,7 @@ def khovanov_homology(self, ring=ZZ, height=None, degree=None): return homologies def oriented_gauss_code(self): - """ + r""" Return the oriented Gauss code of ``self``. The oriented Gauss code has two parts: @@ -1301,7 +1302,7 @@ def oriented_gauss_code(self): return self._oriented_gauss_code def pd_code(self): - """ + r""" Return the planar diagram code of ``self``. The planar diagram is returned in the following format. @@ -1409,7 +1410,7 @@ def pd_code(self): raise AssertionError("invalid state") def gauss_code(self): - """ + r""" Return the Gauss code of ``self``. The Gauss code is generated by the following procedure: @@ -1436,7 +1437,7 @@ def gauss_code(self): return self.oriented_gauss_code()[0] def dowker_notation(self): - """ + r""" Return the Dowker notation of ``self``. Similar to the PD code we number the components, so every crossing @@ -1470,7 +1471,7 @@ def dowker_notation(self): return dn def _braid_word_components(self): - """ + r""" Return the disjoint braid components, if any, else return the braid of ``self``. @@ -1519,7 +1520,7 @@ def _braid_word_components(self): return tuple([a for a in x if a]) def _braid_word_components_vector(self): - """ + r""" The list from the :meth:`_braid_word_components` is flattened to give out the vector form. @@ -1586,7 +1587,7 @@ def _homology_generators(self): @cached_method def seifert_matrix(self): - """ + r""" Return the Seifert matrix associated with ``self``. ALGORITHM: @@ -1641,7 +1642,7 @@ def seifert_matrix(self): @cached_method def number_of_components(self): - """ + r""" Return the number of connected components of ``self``. OUTPUT: number of connected components @@ -1674,7 +1675,7 @@ def number_of_components(self): return G.connected_components_number() def is_knot(self): - """ + r""" Return ``True`` if ``self`` is a knot. Every knot is a link but the converse is not true. @@ -1693,7 +1694,7 @@ def is_knot(self): return self.number_of_components() == 1 def genus(self): - """ + r""" Return the genus of ``self``. EXAMPLES:: @@ -1746,7 +1747,7 @@ def genus(self): return sum(g, ZZ.zero()) def signature(self): - """ + r""" Return the signature of ``self``. This is defined as the signature of the symmetric matrix @@ -1816,7 +1817,7 @@ def omega_signature(self, omega): return ZZ.sum(j.real().sign() for j in m.eigenvalues()) def alexander_polynomial(self, var='t'): - """ + r""" Return the Alexander polynomial of ``self``. INPUT: @@ -1891,7 +1892,7 @@ def alexander_polynomial(self, var='t'): return f def determinant(self): - """ + r""" Return the determinant of ``self``. EXAMPLES:: @@ -1922,9 +1923,8 @@ def determinant(self): raise NotImplementedError("determinant implemented only for knots") def is_alternating(self): - """ - Return ``True`` if the given knot diagram is alternating else - returns ``False``. + r""" + Return whether the given knot diagram is alternating. Alternating diagram implies every overcross is followed by an undercross or the vice-versa. @@ -2022,7 +2022,7 @@ def orientation(self): return orientation def seifert_circles(self): - """ + r""" Return the Seifert circles from the link diagram of ``self``. Seifert circles are the circles obtained by smoothing all crossings @@ -2092,7 +2092,7 @@ def seifert_circles(self): return result def regions(self): - """ + r""" Return the regions from the link diagram of ``self``. Regions are obtained always turning left at each crossing. @@ -2264,7 +2264,7 @@ def mirror_image(self): return type(self)(pd) def writhe(self): - """ + r""" Return the writhe of ``self``. EXAMPLES:: diff --git a/src/sage/rings/padics/local_generic.py b/src/sage/rings/padics/local_generic.py index 9bbc314b3b0..69cc9026bb9 100644 --- a/src/sage/rings/padics/local_generic.py +++ b/src/sage/rings/padics/local_generic.py @@ -1,4 +1,4 @@ -""" +r""" Local Generic Superclass for `p`-adic and power series rings. @@ -8,7 +8,7 @@ - David Roe """ -#***************************************************************************** +# ***************************************************************************** # Copyright (C) 2007-2013 David Roe # William Stein # @@ -16,8 +16,8 @@ # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** from copy import copy from sage.rings.ring import CommutativeRing @@ -30,8 +30,8 @@ class LocalGeneric(CommutativeRing): def __init__(self, base, prec, names, element_class, category=None): - """ - Initializes self. + r""" + Initialize ``self``. EXAMPLES:: @@ -75,7 +75,7 @@ def __init__(self, base, prec, names, element_class, category=None): Parent.__init__(self, base, names=(names,), normalize=False, category=category) def is_capped_relative(self): - """ + r""" Return whether this `p`-adic ring bounds precision in a capped relative fashion. @@ -100,7 +100,7 @@ def is_capped_relative(self): return False def is_capped_absolute(self): - """ + r""" Return whether this `p`-adic ring bounds precision in a capped absolute fashion. @@ -125,7 +125,7 @@ def is_capped_absolute(self): return False def is_fixed_mod(self): - """ + r""" Return whether this `p`-adic ring bounds precision in a fixed modulus fashion. @@ -152,7 +152,7 @@ def is_fixed_mod(self): return False def is_floating_point(self): - """ + r""" Return whether this `p`-adic ring bounds precision in a floating point fashion. @@ -177,7 +177,7 @@ def is_floating_point(self): return False def is_lattice_prec(self): - """ + r""" Return whether this `p`-adic ring bounds precision using a lattice model. @@ -206,7 +206,7 @@ def is_lattice_prec(self): return False def is_relaxed(self): - """ + r""" Return whether this `p`-adic ring bounds precision in a relaxed fashion. @@ -456,7 +456,7 @@ def get_unramified_modulus(q, res_name): functor.extras['names'] = kwds.pop('names') elif functor.extras['names'][0] == curpstr: functor.extras['names'] = (str(p),) - # labels for lattice precision + # Labels for lattice precision if 'label' in kwds: functor.extras['label'] = kwds.pop('label') elif 'label' in functor.extras and functor.type not in ['lattice-cap','lattice-float']: @@ -590,7 +590,7 @@ def residue_characteristic(self): OUTPUT: - - integer -- the characteristic of the residue field. + The characteristic of the residue field. EXAMPLES:: @@ -646,7 +646,7 @@ def ground_ring(self): OUTPUT: - - the ground ring of ``self``, i.e., itself + The ground ring of ``self``, i.e., itself. EXAMPLES:: @@ -671,7 +671,7 @@ def ground_ring_of_tower(self): OUTPUT: - - the ground ring of the tower for ``self``, i.e., itself + The ground ring of the tower for ``self``, i.e., itself. EXAMPLES:: @@ -683,8 +683,8 @@ def ground_ring_of_tower(self): def absolute_degree(self): - """ - Return the degree of this extension over the prime p-adic field/ring + r""" + Return the degree of this extension over the prime p-adic field/ring. EXAMPLES:: @@ -699,8 +699,8 @@ def absolute_degree(self): return self.absolute_e() * self.absolute_f() def relative_degree(self): - """ - Return the degree of this extension over its base field/ring + r""" + Return the degree of this extension over its base field/ring. EXAMPLES:: @@ -715,7 +715,7 @@ def relative_degree(self): return self.absolute_degree() // self.base_ring().absolute_degree() def degree(self): - """ + r""" Return the degree of this extension. Raise an error if the base ring/field is itself an extension. @@ -737,8 +737,8 @@ def degree(self): def absolute_e(self): - """ - Return the absolute ramification index of this ring/field + r""" + Return the absolute ramification index of this ring/field. EXAMPLES:: @@ -757,8 +757,8 @@ def absolute_e(self): return self.base_ring().absolute_e() def absolute_ramification_index(self): - """ - Return the absolute ramification index of this ring/field + r""" + Return the absolute ramification index of this ring/field. EXAMPLES:: @@ -773,8 +773,8 @@ def absolute_ramification_index(self): return self.absolute_e() def relative_e(self): - """ - Return the ramification index of this extension over its base ring/field + r""" + Return the ramification index of this extension over its base ring/field. EXAMPLES:: @@ -789,8 +789,8 @@ def relative_e(self): return self.absolute_e() // self.base_ring().absolute_e() def relative_ramification_index(self): - """ - Return the ramification index of this extension over its base ring/field + r""" + Return the ramification index of this extension over its base ring/field. EXAMPLES:: @@ -805,7 +805,7 @@ def relative_ramification_index(self): return self.relative_e() def e(self): - """ + r""" Return the ramification index of this extension. Raise an error if the base ring/field is itself an extension. @@ -826,7 +826,7 @@ def e(self): raise NotImplementedError("For a relative p-adic ring or field you must use relative_e or absolute_e as appropriate") def ramification_index(self): - """ + r""" Return the ramification index of this extension. Raise an error if the base ring/field is itself an extension. @@ -845,9 +845,9 @@ def ramification_index(self): def absolute_f(self): - """ + r""" Return the degree of the residue field of this ring/field - over its prime subfield + over its prime subfield. EXAMPLES:: @@ -866,9 +866,9 @@ def absolute_f(self): return self.base_ring().absolute_f() def absolute_inertia_degree(self): - """ + r""" Return the degree of the residue field of this ring/field - over its prime subfield + over its prime subfield. EXAMPLES:: @@ -883,8 +883,8 @@ def absolute_inertia_degree(self): return self.absolute_f() def relative_f(self): - """ - Return the degree of the residual extension over its base ring/field + r""" + Return the degree of the residual extension over its base ring/field. EXAMPLES:: @@ -899,8 +899,8 @@ def relative_f(self): return self.absolute_f() // self.base_ring().absolute_f() def relative_inertia_degree(self): - """ - Return the degree of the residual extension over its base ring/field + r""" + Return the degree of the residual extension over its base ring/field. EXAMPLES:: @@ -915,7 +915,7 @@ def relative_inertia_degree(self): return self.relative_f() def f(self): - """ + r""" Return the degree of the residual extension. Raise an error if the base ring/field is itself an extension. @@ -936,7 +936,7 @@ def f(self): raise NotImplementedError("For a relative p-adic ring or field you must use relative_f or absolute_f as appropriate") def inertia_degree(self): - """ + r""" Return the degree of the residual extension. Raise an error if the base ring/field is itself an extension. @@ -1000,7 +1000,7 @@ def maximal_unramified_subextension(self): # raise NotImplementedError def uniformiser(self): - """ + r""" Return a uniformiser for ``self``, ie a generator for the unique maximal ideal. EXAMPLES:: @@ -1017,7 +1017,7 @@ def uniformiser(self): return self.uniformizer() def uniformiser_pow(self, n): - """ + r""" Return the `n`th power of the uniformiser of ``self`` (as an element of ``self``). EXAMPLES:: @@ -1029,8 +1029,8 @@ def uniformiser_pow(self, n): return self.uniformizer_pow(n) def ext(self, *args, **kwds): - """ - Constructs an extension of self. See ``extension`` for more details. + r""" + Construct an extension of self. See :meth:`extension` for more details. EXAMPLES:: @@ -1120,7 +1120,7 @@ def _test_residue(self, **options): tester.assertEqual(x, z) def _matrix_flatten_precision(self, M): - """ + r""" Rescale rows and columns of ``M`` so that the minimal absolute precision of each row and column is equal to the cap. diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index 5c81ce18e77..d2d52ee3fed 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -1,4 +1,4 @@ -""" +r""" p-Adic Generic A generic superclass for all p-adic parents. @@ -9,10 +9,9 @@ - Genya Zaytman: documentation - David Harvey: doctests - Julian Rueth (2013-03-16): test methods for basic arithmetic - """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007-2013 David Roe # William Stein # Julian Rueth @@ -21,8 +20,8 @@ # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.misc import some_tuples from copy import copy @@ -44,20 +43,20 @@ class pAdicGeneric(PrincipalIdealDomain, LocalGeneric): def __init__(self, base, p, prec, print_mode, names, element_class, category=None): - """ - Initialization. + r""" + Initialize ``self``. INPUT: - - base -- Base ring. - - p -- prime - - print_mode -- dictionary of print options - - names -- how to print the uniformizer - - element_class -- the class for elements of this ring + - ``base`` -- Base ring. + - ``p`` -- prime + - ``print_mode`` -- dictionary of print options + - ``names`` -- how to print the uniformizer + - ``element_class`` -- the class for elements of this ring EXAMPLES:: - sage: R = Zp(17) #indirect doctest + sage: R = Zp(17) # indirect doctest """ if category is None: if self.is_field(): @@ -71,7 +70,7 @@ def __init__(self, base, p, prec, print_mode, names, element_class, category=Non def some_elements(self): r""" - Returns a list of elements in this ring. + Return a list of elements in this ring. This is typically used for running generic tests (see :class:`TestSuite`). @@ -91,14 +90,16 @@ def some_elements(self): return L def _modified_print_mode(self, print_mode): - """ - Returns a dictionary of print options, starting with self's + r""" + Return a dictionary of print options, starting with self's print options but modified by the options in the dictionary print_mode. INPUT: - - print_mode -- dictionary with keys in ['mode', 'pos', 'ram_name', 'unram_name', 'var_name', 'max_ram_terms', 'max_unram_terms', 'max_terse_terms', 'sep', 'alphabet'] + - ``print_mode`` -- dictionary with keys in ['mode', 'pos', 'ram_name', + 'unram_name', 'var_name', 'max_ram_terms', 'max_unram_terms', + 'max_terse_terms', 'sep', 'alphabet'] EXAMPLES:: @@ -110,14 +111,16 @@ def _modified_print_mode(self, print_mode): print_mode = {} elif isinstance(print_mode, str): print_mode = {'mode': print_mode} - for option in ['mode', 'pos', 'ram_name', 'unram_name', 'var_name', 'max_ram_terms', 'max_unram_terms', 'max_terse_terms', 'sep', 'alphabet', 'show_prec']: + for option in ['mode', 'pos', 'ram_name', 'unram_name', 'var_name', + 'max_ram_terms', 'max_unram_terms', 'max_terse_terms', + 'sep', 'alphabet', 'show_prec']: if option not in print_mode: print_mode[option] = self._printer.dict()[option] return print_mode def ngens(self): - """ - Returns the number of generators of self. + r""" + Return the number of generators of self. We conventionally define this as 1: for base rings, we take a uniformizer as the generator; for extension rings, we take a @@ -133,8 +136,8 @@ def ngens(self): return 1 def gens(self): - """ - Returns a list of generators. + r""" + Return a list of generators. EXAMPLES:: @@ -148,7 +151,7 @@ def gens(self): return [self.gen()] def __richcmp__(self, other, op): - """ + r""" Return 0 if self == other, and 1 or -1 otherwise. We consider two p-adic rings or fields to be equal if they are @@ -182,25 +185,25 @@ def __richcmp__(self, other, op): return self._printer.richcmp_modes(other._printer, op) - #def ngens(self): - # return 1 + # def ngens(self): + # return 1 - #def gen(self, n = 0): - # if n != 0: - # raise IndexError, "only one generator" - # return self(self.prime()) + # def gen(self, n = 0): + # if n != 0: + # raise IndexError, "only one generator" + # return self(self.prime()) def print_mode(self): r""" - Returns the current print mode as a string. + Return the current print mode as a string. INPUT: - self -- a p-adic field + - ``self`` -- a p-adic field OUTPUT: - string -- self's print mode + The print mode for this p-adic field, as a string. EXAMPLES:: @@ -212,15 +215,13 @@ def print_mode(self): def characteristic(self): r""" - Returns the characteristic of self, which is always 0. + Return the characteristic of self, which is always 0. INPUT: - self -- a p-adic parent + - ``self`` -- a p-adic parent - OUTPUT: - - integer -- self's characteristic, i.e., 0 + OUTPUT: self's characteristic, i.e., 0. EXAMPLES:: @@ -230,16 +231,16 @@ def characteristic(self): return Integer(0) def prime(self): - """ - Returns the prime, ie the characteristic of the residue field. + r""" + Return the prime, ie the characteristic of the residue field. INPUT: - self -- a p-adic parent + - ``self`` -- a p-adic parent OUTPUT: - integer -- the characteristic of the residue field + The characteristic of the residue field. EXAMPLES:: @@ -250,10 +251,10 @@ def prime(self): return self.prime_pow._prime() def uniformizer_pow(self, n): - """ - Returns p^n, as an element of self. + r""" + Return `p^n`, as an element of ``self``. - If n is infinity, returns 0. + If `n` is infinity, returns 0. EXAMPLES:: @@ -268,8 +269,9 @@ def uniformizer_pow(self, n): return self(self.prime_pow.pow_Integer_Integer(n)) def _unram_print(self): - """ - For printing. Will be None if the unramified subextension of self is of degree 1 over Z_p or Q_p. + r""" + For printing. Will be ``None`` if the unramified subextension + of self is of degree 1 over `\ZZ_p` or `\QQ_p`. EXAMPLES:: @@ -278,12 +280,12 @@ def _unram_print(self): return None def residue_characteristic(self): - """ + r""" Return the prime, i.e., the characteristic of the residue field. OUTPUT: - integer -- the characteristic of the residue field + The characteristic of the residue field. EXAMPLES:: @@ -294,16 +296,16 @@ def residue_characteristic(self): return self.prime() def residue_class_field(self): - """ - Returns the residue class field. + r""" + Return the residue class field. INPUT: - self -- a p-adic ring + - ``self`` -- a p-adic ring OUTPUT: - the residue field + The residue field. EXAMPLES:: @@ -316,16 +318,16 @@ def residue_class_field(self): return GF(self.prime()) def residue_field(self): - """ - Returns the residue class field. + r""" + Return the residue class field. INPUT: - self -- a p-adic ring + - ``self`` -- a p-adic ring OUTPUT: - the residue field + The residue field. EXAMPLES:: @@ -337,8 +339,8 @@ def residue_field(self): return self.residue_class_field() def residue_ring(self, n): - """ - Returns the quotient of the ring of integers by the nth power of the maximal ideal. + r""" + Return the quotient of the ring of integers by the nth power of the maximal ideal. EXAMPLES:: @@ -350,16 +352,16 @@ def residue_ring(self, n): return Zmod(self.prime()**n) def residue_system(self): - """ - Returns a list of elements representing all the residue classes. + r""" + Return a list of elements representing all the residue classes. INPUT: - self -- a p-adic ring + - ``self`` -- a p-adic ring OUTPUT: - list of elements -- a list of elements representing all the residue classes + A list of elements representing all the residue classes. EXAMPLES:: @@ -370,8 +372,9 @@ def residue_system(self): return [self(i) for i in self.residue_class_field()] def _fraction_field_key(self, print_mode=None): - """ - Changes print_mode from a dictionary to a tuple and raises a deprecation warning if it is present. + r""" + Change print_mode from a dictionary to a tuple, + raising a deprecation warning if it is present. EXAMPLES:: @@ -391,7 +394,7 @@ def _fraction_field_key(self, print_mode=None): @cached_method(key=_fraction_field_key) def fraction_field(self, print_mode=None): r""" - Returns the fraction field of this ring or field. + Return the fraction field of this ring or field. For `\ZZ_p`, this is the `p`-adic field with the same options, and for extensions, it is just the extension of the fraction @@ -407,7 +410,7 @@ def fraction_field(self, print_mode=None): OUTPUT: - - the fraction field of this ring. + The fraction field of this ring. EXAMPLES:: @@ -454,7 +457,7 @@ def fraction_field(self, print_mode=None): def integer_ring(self, print_mode=None): r""" - Returns the ring of integers of this ring or field. + Return the ring of integers of this ring or field. For `\QQ_p`, this is the `p`-adic ring with the same options, and for extensions, it is just the extension of the ring @@ -467,7 +470,7 @@ def integer_ring(self, print_mode=None): OUTPUT: - - the ring of elements of this field with nonnegative valuation. + The ring of elements of this field with nonnegative valuation. EXAMPLES:: @@ -507,7 +510,9 @@ def integer_ring(self, print_mode=None): sage: R.integer_ring({'mode':'series'}) is R True """ - #Currently does not support fields with non integral defining polynomials. This should change when the padic_general_extension framework gets worked out. + # Currently does not support fields with non integral defining + # polynomials. This should change when the padic_general_extension + # framework gets worked out. if not self.is_field() and print_mode is None: return self if print_mode is None: @@ -519,16 +524,16 @@ def integer_ring(self, print_mode=None): def teichmuller(self, x, prec = None): r""" - Returns the teichmuller representative of x. + Return the Teichmüller representative of `x`. INPUT: - - self -- a p-adic ring - - x -- something that can be cast into self + - ``self`` -- a p-adic ring + - ``x`` -- something that can be cast into ``self`` OUTPUT: - - element -- the teichmuller lift of x + The Teichmüller lift of `x`. EXAMPLES:: @@ -567,10 +572,10 @@ def teichmuller(self, x, prec = None): AUTHORS: - Initial version: David Roe - - Quadratic time version: Kiran Kedlaya (3/27/07) + - Quadratic time version: Kiran Kedlaya (2007-03-27) """ ans = self(x) if prec is None else self(x, prec) - # Since teichmuller representatives are defined at infinite precision, + # Since Teichmüller representatives are defined at infinite precision, # we can lift to precision prec, as long as the absolute precision of ans is positive. if ans.precision_absolute() <= 0: raise ValueError("Not enough precision to determine Teichmuller representative") @@ -584,15 +589,17 @@ def teichmuller(self, x, prec = None): def teichmuller_system(self): r""" - Returns a set of teichmuller representatives for the invertible elements of `\ZZ / p\ZZ`. + Return a set of Teichmüller representatives for the invertible + elements of `\ZZ / p\ZZ`. INPUT: - - self -- a p-adic ring + - ``self`` -- a p-adic ring OUTPUT: - - list of elements -- a list of teichmuller representatives for the invertible elements of `\ZZ / p\ZZ` + A list of Teichmüller representatives for the invertible elements + of `\ZZ / p\ZZ`. EXAMPLES:: @@ -636,7 +643,7 @@ def teichmuller_system(self): # raise NotImplementedError def extension(self, modulus, prec = None, names = None, print_mode = None, implementation='FLINT', **kwds): - """ + r""" Create an extension of this p-adic ring. EXAMPLES:: @@ -689,7 +696,7 @@ def extension(self, modulus, prec = None, names = None, print_mode = None, imple return ExtensionFactory(base=self, modulus=modulus, prec=prec, names=names, check = True, implementation=implementation, **print_mode) def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): - """ + r""" Check whether the given images and map on the base ring determine a valid homomorphism to the codomain. @@ -733,7 +740,7 @@ def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): return f(im_gens[0]) == 0 def _test_add(self, **options): - """ + r""" Test addition of elements of this ring. INPUT: @@ -747,7 +754,6 @@ def _test_add(self, **options): .. SEEALSO:: :class:`TestSuite` - """ tester = self._tester(**options) elements = tester.some_elements() @@ -773,7 +779,7 @@ def _test_add(self, **options): tester.assertTrue(x.is_equal_to(z-y,zprec)) def _test_sub(self, **options): - """ + r""" Test subtraction on elements of this ring. INPUT: @@ -787,7 +793,6 @@ def _test_sub(self, **options): .. SEEALSO:: :class:`TestSuite` - """ tester = self._tester(**options) @@ -852,7 +857,7 @@ def _test_invert(self, **options): tester.assertEqual(y.valuation(), -x.valuation()) def _test_mul(self, **options): - """ + r""" Test multiplication of elements of this ring. INPUT: @@ -866,7 +871,6 @@ def _test_mul(self, **options): .. SEEALSO:: :class:`TestSuite` - """ tester = self._tester(**options) @@ -882,7 +886,7 @@ def _test_mul(self, **options): tester.assertEqual(z.valuation(), x.valuation() + y.valuation()) def _test_div(self, **options): - """ + r""" Test division of elements of this ring. INPUT: @@ -896,7 +900,6 @@ def _test_div(self, **options): .. SEEALSO:: :class:`TestSuite` - """ tester = self._tester(**options) @@ -924,7 +927,7 @@ def _test_div(self, **options): tester.assertEqual(xx, x) def _test_neg(self, **options): - """ + r""" Test the negation operator on elements of this ring. INPUT: @@ -997,7 +1000,7 @@ def _test_shift(self, **options): def _test_log(self, **options): - """ + r""" Test the log operator on elements of this ring. INPUT: @@ -1050,8 +1053,8 @@ def _test_log(self, **options): tester.assertEqual(1+a, c) def _test_teichmuller(self, **options): - """ - Test Teichmuller lifts. + r""" + Test Teichmüller lifts. INPUT: @@ -1139,7 +1142,10 @@ def _log_unit_part_p(self): return self(self.prime()).unit_part().log() def frobenius_endomorphism(self, n=1): - """ + r""" + Return the `n`-th power of the absolute arithmetic Frobeninus + endomorphism on this field. + INPUT: - ``n`` -- an integer (default: 1) @@ -1153,19 +1159,22 @@ def frobenius_endomorphism(self, n=1): sage: K.
= Qq(3^5) sage: Frob = K.frobenius_endomorphism(); Frob - Frobenius endomorphism on 3-adic Unramified Extension ... lifting a |--> a^3 on the residue field + Frobenius endomorphism on 3-adic Unramified Extension + ... lifting a |--> a^3 on the residue field sage: Frob(a) == a.frobenius() True We can specify a power:: sage: K.frobenius_endomorphism(2) - Frobenius endomorphism on 3-adic Unramified Extension ... lifting a |--> a^(3^2) on the residue field + Frobenius endomorphism on 3-adic Unramified Extension + ... lifting a |--> a^(3^2) on the residue field The result is simplified if possible:: sage: K.frobenius_endomorphism(6) - Frobenius endomorphism on 3-adic Unramified Extension ... lifting a |--> a^3 on the residue field + Frobenius endomorphism on 3-adic Unramified Extension + ... lifting a |--> a^3 on the residue field sage: K.frobenius_endomorphism(5) Identity endomorphism of 3-adic Unramified Extension ... @@ -1178,9 +1187,11 @@ def frobenius_endomorphism(self, n=1): return FrobeniusEndomorphism_padics(self, n) def _test_elements_eq_transitive(self, **options): - """ - The operator ``==`` is not transitive for `p`-adic numbers. We disable - the check of the category framework by overriding this method. + r""" + The operator ``==`` is not transitive for `p`-adic numbers. + + We disable the check of the category framework by overriding + this method. EXAMPLES:: @@ -1229,13 +1240,12 @@ def valuation(self): :meth:`NumberField_generic.valuation() `, :meth:`Order.valuation() ` - """ from sage.rings.padics.padic_valuation import pAdicValuation return pAdicValuation(self) def _primitive_qth_root_of_unity(self, exponent): - """ + r""" Compute the ``p^exponent``-th roots of unity in this ring. INPUT: @@ -1244,13 +1254,11 @@ def _primitive_qth_root_of_unity(self, exponent): OUTPUT: - A triple ``(zeta,n,nextzeta)`` where + A triple ``(zeta, n, nextzeta)`` where - ``zeta`` is a generator of the group of ``p^exponent``-th roots of unity in this ring, and - - ``p^n`` is the order of ``zeta``. - - ``nextzeta`` is the result of ``zeta._inverse_pth_root()`` if ``n`` is positive and ``None`` otherwise @@ -1310,7 +1318,7 @@ def _primitive_qth_root_of_unity(self, exponent): return self._qth_roots_of_unity[-2][0], n-2, self._qth_roots_of_unity[-1] def primitive_root_of_unity(self, n=None, order=False): - """ + r""" Return a generator of the group of ``n``-th roots of unity in this ring. @@ -1358,7 +1366,6 @@ def primitive_root_of_unity(self, n=None, order=False): 6 sage: zeta.multiplicative_order() 6 - """ p = self.prime() k = self.residue_field() @@ -1393,7 +1400,7 @@ def primitive_root_of_unity(self, n=None, order=False): return zeta def roots_of_unity(self, n=None): - """ + r""" Return all the ``n``-th roots of unity in this ring. INPUT: @@ -1414,7 +1421,7 @@ def roots_of_unity(self, n=None): [1 + O(5^10), 4 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + 4*5^5 + 4*5^6 + 4*5^7 + 4*5^8 + 4*5^9 + O(5^10)] - In this case, the roots of unity are the Teichmuller representatives:: + In this case, the roots of unity are the Teichmüller representatives:: sage: R.teichmuller_system() [1 + O(5^10), @@ -1445,8 +1452,8 @@ def roots_of_unity(self, n=None): We check that the logarithm of each root of unity vanishes:: sage: for root in roots: - ....: if root.log() != 0: raise ValueError - + ....: if root.log() != 0: + ....: raise ValueError """ zeta, order = self.primitive_root_of_unity(n, order=True) return [ zeta**i for i in range(order) ] @@ -1467,7 +1474,7 @@ def _roots_univariate_polynomial(self, P, ring, multiplicities, algorithm, secur - ``algorithm`` -- ``"pari"``, ``"sage"`` or ``None`` (default: ``None``); Sage provides an implementation for any extension of - `Q_p` whereas only roots of polynomials over `\QQ_p` is implemented + `\QQ_p` whereas only roots of polynomials over `\QQ_p` is implemented in Pari; the default is ``"pari"`` if ``ring`` is `\ZZ_p` or `\QQ_p`, ``"sage"`` otherwise. @@ -1604,7 +1611,6 @@ def _roots_univariate_polynomial(self, P, ring, multiplicities, algorithm, secur Traceback (most recent call last): ... ArithmeticError: factorization of 0 is not defined - """ if P.is_zero(): raise ArithmeticError("factorization of 0 is not defined") @@ -1635,7 +1641,7 @@ def _roots_univariate_polynomial(self, P, ring, multiplicities, algorithm, secur class ResidueReductionMap(Morphism): - """ + r""" Reduction map from a p-adic ring or field to its residue field or ring. These maps must be created using the :meth:`_create_` method in order @@ -1652,7 +1658,7 @@ class ResidueReductionMap(Morphism): """ @staticmethod def _create_(R, k): - """ + r""" Initialization. We have to implement this as a static method in order to call ``__make_element_class__``. @@ -1691,7 +1697,7 @@ def _create_(R, k): return f def is_surjective(self): - """ + r""" The reduction map is surjective. EXAMPLES:: @@ -1702,7 +1708,7 @@ def is_surjective(self): return True def is_injective(self): - """ + r""" The reduction map is far from injective. EXAMPLES:: @@ -1713,7 +1719,7 @@ def is_injective(self): return False def _call_(self, x): - """ + r""" Evaluate this morphism. EXAMPLES:: @@ -1733,7 +1739,7 @@ def _call_(self, x): return x.residue(self._n, field=self._field, check_prec=self._field) def section(self): - """ + r""" Returns the section from the residue ring or field back to the p-adic ring or field. @@ -1747,7 +1753,7 @@ def section(self): return ResidueLiftingMap._create_(self.codomain(), self.domain()) def _repr_type(self): - """ + r""" Type of morphism, for printing. EXAMPLES:: @@ -1775,10 +1781,10 @@ def _richcmp_(self, other, op): return NotImplemented return richcmp((self.domain(), self.codomain()), (other.domain(), other.codomain()), op) -# A class for the Teichmuller lift would also be reasonable.... +# A class for the Teichmüller lift would also be reasonable.... class ResidueLiftingMap(Morphism): - """ + r""" Lifting map to a p-adic ring or field from its residue field or ring. These maps must be created using the :meth:`_create_` method in order @@ -1795,7 +1801,7 @@ class ResidueLiftingMap(Morphism): """ @staticmethod def _create_(k, R): - """ + r""" Initialization. We have to implement this as a static method in order to call ``__make_element_class__``. @@ -1823,7 +1829,7 @@ def _create_(k, R): return f def _call_(self, x): - """ + r""" Evaluate this morphism. EXAMPLES:: @@ -1853,7 +1859,7 @@ def _call_(self, x): raise NotImplementedError def _call_with_args(self, x, args=(), kwds={}): - """ + r""" Evaluate this morphism with extra arguments. EXAMPLES:: @@ -1880,7 +1886,7 @@ def _call_with_args(self, x, args=(), kwds={}): raise NotImplementedError def _repr_type(self): - """ + r""" Type of morphism, for printing. EXAMPLES:: @@ -1924,7 +1930,7 @@ def local_print_mode(obj, print_options, pos = None, ram_name = None): .. NOTE:: - For more documentation see ``localvars`` in parent_gens.pyx + For more documentation see :class:`sage.structure.parent_gens.localvars`. """ if isinstance(print_options, str): print_options = {'mode': print_options} From 2d59e5bad611d8ca3344817a197d3f6fa53e1582 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 14 Jun 2021 10:13:14 +0200 Subject: [PATCH 215/280] [ ] -> [] --- src/sage/rings/padics/generic_nodes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/padics/generic_nodes.py b/src/sage/rings/padics/generic_nodes.py index 640eef1ca04..4b09b5678c1 100644 --- a/src/sage/rings/padics/generic_nodes.py +++ b/src/sage/rings/padics/generic_nodes.py @@ -643,9 +643,9 @@ def convert_multiple(self, *elts): p = self.prime() # We sort elements by precision lattice - elt_by_prec = { } - elt_other = [ ] - indices = { } + elt_by_prec = {} + elt_other = [] + indices = {} for i in range(len(elts)): x = elts[i] idx = id(x) @@ -683,7 +683,7 @@ def convert_multiple(self, *elts): raise NotImplementedError("multiple conversion of a set of variables for which the module precision is not a lattice is not implemented yet") for j in range(len(L)): x = L[j] - dx = [ ] + dx = [] for i in range(j): dx.append([L[i], lattice[i,j]]) prec = lattice[j,j].valuation(p) From 06e892e20709a632c65215ac17ca4a2220e175d8 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 14 Jun 2021 13:04:41 +0200 Subject: [PATCH 216/280] [ ] -> [] --- src/sage/rings/padics/lattice_precision.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/padics/lattice_precision.py b/src/sage/rings/padics/lattice_precision.py index ade4a6950d0..9c8b8b55a11 100644 --- a/src/sage/rings/padics/lattice_precision.py +++ b/src/sage/rings/padics/lattice_precision.py @@ -595,7 +595,7 @@ def list(self, prec): return [] p = self.p x = ZZ(self.x * p**(self.exponent - val)) - l = [ ] + l = [] for _ in range(val, prec): x, digit = x.quo_rem(p) l.append(digit) From 7323b10d6c628c32adfeb62025879f909f707c61 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 14 Jun 2021 15:50:57 -0700 Subject: [PATCH 217/280] ConvexSet_base._test_contains: Only test extension to AA for exact base rings --- src/sage/geometry/convex_set.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index 8e672a94c68..d256ae1671b 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -505,10 +505,11 @@ def _test_contains(self, tester=None, **options): if ambient_point is not None: tester.assertEqual(contains_space_point, self.contains(ambient_point)) tester.assertEqual(contains_space_point, self.contains(space_coords)) - from sage.rings.qqbar import AA - ext_space = self.ambient_vector_space(AA) - ext_space_point = ext_space(space_point) - tester.assertEqual(contains_space_point, self.contains(ext_space_point)) + if space.base_ring().is_exact(): + from sage.rings.qqbar import AA + ext_space = self.ambient_vector_space(AA) + ext_space_point = ext_space(space_point) + tester.assertEqual(contains_space_point, self.contains(ext_space_point)) from sage.symbolic.ring import SR symbolic_space = self.ambient_vector_space(SR) symbolic_space_point = symbolic_space(space_point) From fafed318b79fd1fdba1b61aa1b794e39cc20c991 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 15 Jun 2021 16:21:29 -0500 Subject: [PATCH 218/280] Add _mul_ to allow for algebras and add ability for Representation class to take category as a keyword argument. --- src/sage/modules/with_basis/representation.py | 56 +++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index 3057de93f6c..097e5c5ca9d 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -152,7 +152,7 @@ class Representation(Representation_abstract): - :wikipedia:`Group_representation` """ - def __init__(self, semigroup, module, on_basis, side="left"): + def __init__(self, semigroup, module, on_basis, side="left", **kwargs): """ Initialize ``self``. @@ -164,18 +164,31 @@ def __init__(self, semigroup, module, on_basis, side="left"): sage: on_basis = lambda g,m: M.term(m, g.sign()) sage: R = Representation(G, M, on_basis) sage: R._test_representation() + + sage: G = SymmetricGroup(4) + sage: A = SymmetricGroup(4).algebra(QQ) + sage: from sage.categories.algebras import Algebras + sage: from sage.modules.with_basis.representation import Representation + sage: action = lambda g,x: A.monomial(g*x) + sage: category = Algebras(QQ).WithBasis().FiniteDimensional() + sage: R = Representation(G, A, action, 'left', category=category) + sage: r = R.an_element() + sage: r^2 + 14*() + 2*(2,3,4) + (2,4,3) + 12*(1,2)(3,4) + 3*(1,2,4) + 2*(1,3,2) + 4*(1,3)(2,4) + 5*(1,4,3) + 6*(1,4)(2,3) + """ + + category = kwargs.pop('category', Modules(module.base_ring()).WithBasis()) if side not in ["left", "right"]: raise ValueError('side must be "left" or "right"') self._left_repr = (side == "left") self._on_basis = on_basis self._module = module indices = module.basis().keys() - cat = Modules(module.base_ring()).WithBasis() if 'FiniteDimensional' in module.category().axioms(): - cat = cat.FiniteDimensional() + category = category.FiniteDimensional() Representation_abstract.__init__(self, semigroup, module.base_ring(), indices, - category=cat, **module.print_options()) + category=category, **module.print_options()) def _test_representation(self, **options): """ @@ -369,10 +382,45 @@ def _acted_upon_(self, scalar, self_on_left=False): ret += P.linear_combination(((P._on_basis(ms, m), cs*c) for m,c in self), not self_on_left) return ret + return CombinatorialFreeModule.Element._acted_upon_(self, scalar, self_on_left) _rmul_ = _lmul_ = _acted_upon_ + def _mul_(self, other): + """ + EXAMPLES:: + + sage: G = SymmetricGroup(4) + sage: A = G.algebra(QQ) + sage: from sage.categories.algebras import Algebras + sage: from sage.modules.with_basis.representation import Representation + sage: action = lambda g,x: A.monomial(g*x) + sage: category = Algebras(QQ).WithBasis().FiniteDimensional() + sage: R = Representation(G, A, action, 'left', category=category) + sage: r = R.an_element() + sage: r^2 + 14*() + 2*(2,3,4) + (2,4,3) + 12*(1,2)(3,4) + 3*(1,2,4) + 2*(1,3,2) + 4*(1,3)(2,4) + 5*(1,4,3) + 6*(1,4)(2,3) + + TESTS:: + + sage: G = SymmetricGroup(4) + sage: M = CombinatorialFreeModule(QQ, ['a','b','c','d']) + sage: from sage.modules.with_basis.representation import Representation + sage: action = lambda g,x: M.monomial( g(x.to_vector())) + sage: R = Representation(G, M, action, 'left') + sage: r = R.an_element() + sage: r*r + Traceback (most recent call last): + ... + AttributeError: 'CombinatorialFreeModule_with_category' object has no attribute '_product_from_product_on_basis_multiply' + """ + + if self.parent() is not other.parent(): + return NotImplemented + + return self.parent()._module._product_from_product_on_basis_multiply(self,other) + class RegularRepresentation(Representation): r""" The regular representation of a semigroup. From 94e68582fde0f5bb8b2c57e5f7aa56dbaa5e825a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Jun 2021 09:21:21 -0700 Subject: [PATCH 219/280] RelativeInterior.ambient, ambient_vector_space, is_universe: New --- src/sage/geometry/relative_interior.py | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index 3fd2f6c6b54..c714faabcb9 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -69,6 +69,34 @@ def __contains__(self, point): """ return self._polyhedron.relative_interior_contains(point) + def ambient(self): + r""" + Return the ambient convex set or space. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.ambient() + """ + return self._polyhedron.ambient() + + def ambient_vector_space(self, base_field=None): + r""" + Return the ambient vector space. + + EXAMPLES:: + + sage: segment = Polyhedron([[1, 2], [3, 4]]) + sage: ri_segment = segment.relative_interior(); ri_segment + Relative interior of + a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices + sage: ri_segment.ambient_vector_space() + """ + return self._polyhedron.ambient_vector_space(base_field=base_field) + def ambient_dim(self): r""" Return the dimension of the ambient space. From 0c9bc945a59ffbf38c59d3679036bf4c90a516fd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 16 Jun 2021 09:16:27 -0700 Subject: [PATCH 220/280] ConvexSet_base: Add default implementations of ambient, ambient_dim; add doctests --- src/sage/geometry/convex_set.py | 45 +++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index d256ae1671b..c6dc7967e4d 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -63,6 +63,8 @@ def dim(self): r""" Return the dimension of ``self``. + Subclasses must provide an implementation of this method. + TESTS:: sage: from sage.geometry.convex_set import ConvexSet_base @@ -94,28 +96,55 @@ def dimension(self): def ambient_vector_space(self, base_field=None): r""" Return the ambient vector space. + + Subclasses must provide an implementation of this method. + + The default implementations of :meth:`ambient`, :meth:`ambient_dim`, + :meth:`ambient_dimension` use this method. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: C = ConvexSet_base() + sage: C.ambient_vector_space() + Traceback (most recent call last): + ... + NotImplementedError: """ - @abstract_method def ambient(self): r""" Return the ambient convex set or space. + + The default implementation delegates to :meth:`ambient_vector_space`. + + EXAMPLES:: + + sage: from sage.geometry.convex_set import ConvexSet_base + sage: class ExampleSet(ConvexSet_base): + ....: def ambient_vector_space(self, base_field=None): + ....: return (base_field or QQ)^2001 + sage: ExampleSet().ambient_dim() + 2001 """ + return self.ambient_vector_space() - @abstract_method def ambient_dim(self): r""" Return the dimension of the ambient convex set or space. - TESTS:: + The default implementation obtains it from :meth:`ambient`. + + EXAMPLES:: sage: from sage.geometry.convex_set import ConvexSet_base - sage: C = ConvexSet_base() - sage: C.ambient_dim() - Traceback (most recent call last): - ... - NotImplementedError: + sage: class ExampleSet(ConvexSet_base): + ....: def ambient(self): + ....: return QQ^7 + sage: ExampleSet().ambient_dim() + 7 """ + return self.ambient().dimension() def ambient_dimension(self): r""" From e767248a97138680b8a25a971880cd8fb1cecfc5 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 16 Jun 2021 22:54:40 -0500 Subject: [PATCH 221/280] Fix multiplication and add tests --- src/sage/modules/with_basis/representation.py | 100 ++++++++++++------ 1 file changed, 66 insertions(+), 34 deletions(-) diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index 097e5c5ca9d..09eb8cd6748 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -165,6 +165,17 @@ def __init__(self, semigroup, module, on_basis, side="left", **kwargs): sage: R = Representation(G, M, on_basis) sage: R._test_representation() + sage: G = CyclicPermutationGroup(3) + sage: M = algebras.Exterior(QQ, 'x', 3) + sage: from sage.modules.with_basis.representation import Representation + sage: on_basis = lambda g,m: E.monomial(tuple(g(i+1)-1 for i in m[0])) #cyclically permute generators + sage: from sage.categories.algebras import Algebras + sage: R = Representation(G, M, on_basis, category = Algebras(QQ).WithBasis().FiniteDimensional()) + sage: r = R.an_element(); r + 1 + 2*x0 + x0*x1 + 3*x1 + sage: r*r + 1 + 4*x0 + 2*x0*x1 + 6*x1 + sage: G = SymmetricGroup(4) sage: A = SymmetricGroup(4).algebra(QQ) sage: from sage.categories.algebras import Algebras @@ -177,16 +188,25 @@ def __init__(self, semigroup, module, on_basis, side="left", **kwargs): 14*() + 2*(2,3,4) + (2,4,3) + 12*(1,2)(3,4) + 3*(1,2,4) + 2*(1,3,2) + 4*(1,3)(2,4) + 5*(1,4,3) + 6*(1,4)(2,3) """ + try: + self.product_on_basis = module.product_on_basis + except AttributeError: + pass category = kwargs.pop('category', Modules(module.base_ring()).WithBasis()) + if side not in ["left", "right"]: raise ValueError('side must be "left" or "right"') + self._left_repr = (side == "left") self._on_basis = on_basis self._module = module + indices = module.basis().keys() + if 'FiniteDimensional' in module.category().axioms(): category = category.FiniteDimensional() + Representation_abstract.__init__(self, semigroup, module.base_ring(), indices, category=category, **module.print_options()) @@ -286,6 +306,51 @@ def _element_constructor_(self, x): return self._from_dict(x.monomial_coefficients(copy=False), remove_zeros=False) return super(Representation, self)._element_constructor_(x) + def product_by_coercion(self, left, right): + """ + Return the product of ``left`` and ``right`` by passing to ``self._module`` + and then building a new element of ``self``. + + EXAMPLES:: + + sage: G = groups.permutation.KleinFour() + sage: E = algebras.Exterior(QQ,'e',4) + sage: on_basis = lambda g,m: E.monomial(m) # the trivial representation + sage: from sage.modules.with_basis.representation import Representation + sage: R = Representation(G, E, on_basis) + sage: r = R.an_element(); r + 1 + 2*e0 + 3*e1 + e1*e2 + sage: g = G.an_element(); + sage: g*r == r + True + sage: r*r + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for *: + 'Representation of The Klein 4 group of order 4, as a permutation + group indexed by Subsets of {0, 1, 2, 3} over Rational Field' and + 'Representation of The Klein 4 group of order 4, as a permutation + group indexed by Subsets of {0, 1, 2, 3} over Rational Field' + + sage: from sage.categories.algebras import Algebras + sage: category = Algebras(QQ).FiniteDimensional().WithBasis() + sage: T = Representation(G, E, on_basis, category = category) + sage: t = T.an_element(); t + 1 + 2*e0 + 3*e1 + e1*e2 + sage: g*t == t + True + sage: t*t + 1 + 4*e0 + 4*e0*e1*e2 + 6*e1 + 2*e1*e2 + + """ + M = self._module + + # Multiply in self._module + p = M._from_dict(left._monomial_coefficients, False, False) * M._from_dict(right._monomial_coefficients, False, False) + + # Convert from a term in self._module to a term in self + return self._from_dict(p.monomial_coefficients(copy=False), False, False) + def side(self): """ Return whether ``self`` is a left or a right representation. @@ -306,6 +371,7 @@ def side(self): """ return "left" if self._left_repr else "right" + class Element(CombinatorialFreeModule.Element): def _acted_upon_(self, scalar, self_on_left=False): """ @@ -387,40 +453,6 @@ def _acted_upon_(self, scalar, self_on_left=False): _rmul_ = _lmul_ = _acted_upon_ - def _mul_(self, other): - """ - EXAMPLES:: - - sage: G = SymmetricGroup(4) - sage: A = G.algebra(QQ) - sage: from sage.categories.algebras import Algebras - sage: from sage.modules.with_basis.representation import Representation - sage: action = lambda g,x: A.monomial(g*x) - sage: category = Algebras(QQ).WithBasis().FiniteDimensional() - sage: R = Representation(G, A, action, 'left', category=category) - sage: r = R.an_element() - sage: r^2 - 14*() + 2*(2,3,4) + (2,4,3) + 12*(1,2)(3,4) + 3*(1,2,4) + 2*(1,3,2) + 4*(1,3)(2,4) + 5*(1,4,3) + 6*(1,4)(2,3) - - TESTS:: - - sage: G = SymmetricGroup(4) - sage: M = CombinatorialFreeModule(QQ, ['a','b','c','d']) - sage: from sage.modules.with_basis.representation import Representation - sage: action = lambda g,x: M.monomial( g(x.to_vector())) - sage: R = Representation(G, M, action, 'left') - sage: r = R.an_element() - sage: r*r - Traceback (most recent call last): - ... - AttributeError: 'CombinatorialFreeModule_with_category' object has no attribute '_product_from_product_on_basis_multiply' - """ - - if self.parent() is not other.parent(): - return NotImplemented - - return self.parent()._module._product_from_product_on_basis_multiply(self,other) - class RegularRepresentation(Representation): r""" The regular representation of a semigroup. From ce91e44231c897ad00c4d838d2e4f081afcc6ff9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 14 Jun 2021 20:08:10 -0700 Subject: [PATCH 222/280] src/sage/geometry/relative_interior.py: Fix doctest output --- src/sage/geometry/relative_interior.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/geometry/relative_interior.py b/src/sage/geometry/relative_interior.py index c714faabcb9..9e49420a5e4 100644 --- a/src/sage/geometry/relative_interior.py +++ b/src/sage/geometry/relative_interior.py @@ -80,6 +80,7 @@ def ambient(self): Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices sage: ri_segment.ambient() + Vector space of dimension 2 over Rational Field """ return self._polyhedron.ambient() @@ -94,6 +95,7 @@ def ambient_vector_space(self, base_field=None): Relative interior of a 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices sage: ri_segment.ambient_vector_space() + Vector space of dimension 2 over Rational Field """ return self._polyhedron.ambient_vector_space(base_field=base_field) From f0e7c5864eeea17190b0657accf9534fcafa0c89 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 17 Jun 2021 05:35:13 -0700 Subject: [PATCH 223/280] ambient_vector_space docstring: Fix bad blocks --- src/sage/geometry/cone.py | 2 +- src/sage/geometry/lattice_polytope.py | 2 +- src/sage/geometry/polyhedron/base.py | 2 +- src/sage/geometry/polyhedron/face.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index fd2074676c7..78d0cb0444f 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -987,7 +987,7 @@ def ambient_vector_space(self, base_field=None): It is the ambient lattice (:meth:`lattice`) tensored with a field. - INPUT:: + INPUT: - ``base_field`` -- (default: the rationals) a field. diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index bde2f0a6be7..9d42d71f275 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -2623,7 +2623,7 @@ def ambient_vector_space(self, base_field=None): It is the ambient lattice (:meth:`lattice`) tensored with a field. - INPUT:: + INPUT: - ``base_field`` -- (default: the rationals) a field. diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 82af7e683fc..b9c45c511d1 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -2487,7 +2487,7 @@ def ambient_vector_space(self, base_field=None): It is the ambient free module (:meth:`Vrepresentation_space`) tensored with a field. - INPUT:: + INPUT: - ``base_field`` -- (default: the fraction field of the base ring) a field. diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index 56305d035e8..ede09a3e1d9 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -684,7 +684,7 @@ def ambient_vector_space(self, base_field=None): It is the ambient free module of the containing polyhedron tensored with a field. - INPUT:: + INPUT: - ``base_field`` -- (default: the fraction field of the base ring) a field. From 200d967982e2d4c600658354ef80a15f1ed0ccd8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 17 Jun 2021 05:58:51 -0700 Subject: [PATCH 224/280] ConvexSet_base.ambient doctest: Actually test the method --- src/sage/geometry/convex_set.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/convex_set.py b/src/sage/geometry/convex_set.py index c6dc7967e4d..d43ba7dbfff 100644 --- a/src/sage/geometry/convex_set.py +++ b/src/sage/geometry/convex_set.py @@ -124,8 +124,8 @@ def ambient(self): sage: class ExampleSet(ConvexSet_base): ....: def ambient_vector_space(self, base_field=None): ....: return (base_field or QQ)^2001 - sage: ExampleSet().ambient_dim() - 2001 + sage: ExampleSet().ambient() + Vector space of dimension 2001 over Rational Field """ return self.ambient_vector_space() From 1f65c2d903775ded4e91cfd630d17a1b86cc2592 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Thu, 17 Jun 2021 16:13:09 +0100 Subject: [PATCH 225/280] Documentation edited and integrate_vector changed As per trac ticket 30698, the documentation was edited to include an example at the beginning. Moreover, the integrate_vector method was changed to allow the use to give the desired number of nodes rather than a desirec error bound. This change was reflected in the documentation. --- src/sage/numerical/gauss_legendre.pyx | 86 +++++++++++++++++---------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index d03280070df..f236b3968a4 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -1,18 +1,31 @@ r""" -Gauss-Legendre integration for vector-valued functions +Gauss-Legendre Integration for Vector-Valued Functions Routine to perform Gauss-Legendre integration for vector-functions. -EXAMPLES:: +EXAMPLES: + +We verify that `\int_0^1 n x^{n-1} \, dx = 1` for `n=1, \dots, 4`:: + + sage: from sage.numerical.gauss_legendre import integrate_vector + sage: prec = 100 + sage: K = RealField(prec) + sage: N = 4 + sage: V = VectorSpace(K,N) + sage: f = lambda x: V([(n+1)*x^n for n in range(N)]) + sage: I = integrate_vector(f,prec) + sage: max([c.abs() for c in I-V(N*[1])]) + 0.00000000000000000000000000000 AUTHORS: - Nils Bruin (2017-06-06): initial version + - Linden Disney-Hogg (2021-06-17): documentation and integrate_vector method changes -NOTE: +NOTE:: -The code here is directly based on mpmath (see http://mpmath.org), but has a highly -optimized routine to compute the nodes. + The code here is directly based on mpmath (see http://mpmath.org), but has a highly + optimized routine to compute the nodes. """ # **************************************************************************** @@ -39,9 +52,9 @@ from sage.misc.cachefunc import cached_function from sage.rings.real_mpfr cimport RealNumber, RealField_class @cached_function -def nodes(degree,prec): +def nodes(degree, prec): r""" - Compute the integration nodes and weights for the Gauss-Legendre quadrature scheme. + Compute the integration nodes and weights for the Gauss-Legendre quadrature scheme using a version of the REC algorithm. INPUT: @@ -53,7 +66,7 @@ def nodes(degree,prec): A list of (node,weight) pairs. - EXAMPLES:: + EXAMPLES: The nodes for the Gauss-Legendre scheme are roots of Legendre polynomials. The weights can be computed by a straightforward formula (note that evaluating @@ -61,10 +74,10 @@ def nodes(degree,prec): from this routine are actually more accurate than what the values the closed formula produces):: sage: from sage.numerical.gauss_legendre import nodes - sage: L1=nodes(24,53) - sage: P=RR['x'](sage.functions.orthogonal_polys.legendre_P(24,x)) - sage: Pdif=P.diff() - sage: L2=[( (r+1)/2,1/(1-r^2)/Pdif(r)^2) for r,_ in RR['x'](P).roots()] + sage: L1 = nodes(24,53) + sage: P = RR['x'](sage.functions.orthogonal_polys.legendre_P(24,x)) + sage: Pdif = P.diff() + sage: L2 = [((r+1)/2,1/(1-r^2)/Pdif(r)^2) for r,_ in RR['x'](P).roots()] sage: all((a[0]-b[0]).abs() < 10^-15 and (a[1]-b[1]).abs() < 10^-9 for a,b in zip(L1,L2)) True """ @@ -121,7 +134,7 @@ def nodes(degree,prec): mpfr_clear(v) return nodes -def estimate_error(results,prec,epsilon): +def estimate_error(results, prec, epsilon): r""" Routine to estimate the error in a list of quadrature approximations. @@ -174,12 +187,12 @@ def estimate_error(results,prec,epsilon): e.append(D4.exp()) return max(e) -def integrate_vector(f,prec,epsilon=None): +def integrate_vector(f, prec, N=None, epsilon=None): r""" Integrate a one-argument vector-valued function numerically using Gauss-Legendre. This function uses the Gauss-Legendre quadrature scheme to approximate - the integral of f(t) for t=0..1. + the integral `\int_0^1 f(t) \, dt`. INPUT: @@ -187,7 +200,9 @@ def integrate_vector(f,prec,epsilon=None): - ``prec`` -- integer. Binary precision to be used. - - ``epsilon`` -- Multiprecision float. Target error bound. + - ``N`` -- integer. Number of nodes to use. If specificed, the target error ``epsilon`` is ignored. + + - ``epsilon`` -- multiprecision float (default: `2^{(-\text{prec}+3)}`). Target error bound. OUTPUT: @@ -196,38 +211,45 @@ def integrate_vector(f,prec,epsilon=None): EXAMPLES:: sage: from sage.numerical.gauss_legendre import integrate_vector - sage: prec=200 - sage: K=RealField(prec) - sage: V=VectorSpace(K,2) - sage: epsilon=K(2^(-prec+4)) - sage: f=lambda t:V((1+t^2,1/(1+t^2))) - sage: I=integrate_vector(f,prec,epsilon) - sage: J=V((4/3,pi/4)) + sage: prec = 200 + sage: K = RealField(prec) + sage: V = VectorSpace(K,2) + sage: epsilon = K(2^(-prec+4)) + sage: f = lambda t:V((1+t^2,1/(1+t^2))) + sage: I = integrate_vector(f, prec, epsilon) + sage: J = V((4/3,pi/4)) sage: max(c.abs() for c in (I-J)) < epsilon True We can also use complex-valued integrands:: - sage: prec=200 - sage: Kreal=RealField(prec) - sage: K=ComplexField(prec) - sage: V=VectorSpace(K,2) - sage: epsilon=Kreal(2^(-prec+4)) - sage: f=lambda t: V((t,K(exp(2*pi*t*K.0)))) - sage: I=integrate_vector(f,prec,epsilon) - sage: J=V((1/2,0)) + sage: prec = 200 + sage: Kreal = RealField(prec) + sage: K = ComplexField(prec) + sage: V = VectorSpace(K,2) + sage: epsilon = Kreal(2^(-prec+4)) + sage: f = lambda t: V((t,K(exp(2*pi*t*K.0)))) + sage: I = integrate_vector(f, prec, epsilon) + sage: J = V((1/2,0)) sage: max(c.abs() for c in (I-J)) < epsilon True """ results = [] cdef long degree = 3 Rout = RealField(prec) + if N is not None: + nodelist = nodes(N,prec) + I = nodelist[0][1]*f(nodelist[0][0]) + for i in range(1,len(nodelist)): + I += nodelist[i][1]*f(nodelist[i][0]) + return I + if epsilon is None: epsilon = Rout(2)**(-prec+3) while True: nodelist = nodes(degree,prec) I = nodelist[0][1]*f(nodelist[0][0]) - for i in xrange(1,len(nodelist)): + for i in range(1,len(nodelist)): I += nodelist[i][1]*f(nodelist[i][0]) results.append(I) if degree > 3: From 374207fcc1597e811b9cc9b94790f56d17d36d97 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 17 Jun 2021 20:57:00 -0500 Subject: [PATCH 226/280] Fix cyclic action on generators of ExteriorAlgebra, add tests for the multiplication of group elements on the group algebra as a representation --- src/sage/modules/with_basis/representation.py | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index 09eb8cd6748..96fabf36c6e 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -168,13 +168,24 @@ def __init__(self, semigroup, module, on_basis, side="left", **kwargs): sage: G = CyclicPermutationGroup(3) sage: M = algebras.Exterior(QQ, 'x', 3) sage: from sage.modules.with_basis.representation import Representation - sage: on_basis = lambda g,m: E.monomial(tuple(g(i+1)-1 for i in m[0])) #cyclically permute generators + sage: on_basis = lambda g,m: M.prod([M.monomial(tuple([g(j+1)-1])) for j in m]) #cyclically permute generators sage: from sage.categories.algebras import Algebras sage: R = Representation(G, M, on_basis, category = Algebras(QQ).WithBasis().FiniteDimensional()) sage: r = R.an_element(); r 1 + 2*x0 + x0*x1 + 3*x1 sage: r*r 1 + 4*x0 + 2*x0*x1 + 6*x1 + sage: x0, x1, x2 = M.gens() + sage: s = R(x0*x1) + sage: g = G.an_element() + sage: g*s + x1*x2 + sage: g*R(x1*x2) + -x0*x2 + sage: g*r + 1 + 2*x1 + x1*x2 + 3*x2 + sage: g^2*r + 1 + 3*x0 - x0*x2 + 2*x2 sage: G = SymmetricGroup(4) sage: A = SymmetricGroup(4).algebra(QQ) @@ -182,11 +193,15 @@ def __init__(self, semigroup, module, on_basis, side="left", **kwargs): sage: from sage.modules.with_basis.representation import Representation sage: action = lambda g,x: A.monomial(g*x) sage: category = Algebras(QQ).WithBasis().FiniteDimensional() - sage: R = Representation(G, A, action, 'left', category=category) - sage: r = R.an_element() + sage: R = Representation(G, A, action, 'left', category = category) + sage: r = R.an_element(); r + () + (2,3,4) + 2*(1,3)(2,4) + 3*(1,4)(2,3) sage: r^2 14*() + 2*(2,3,4) + (2,4,3) + 12*(1,2)(3,4) + 3*(1,2,4) + 2*(1,3,2) + 4*(1,3)(2,4) + 5*(1,4,3) + 6*(1,4)(2,3) - + sage: g = G.an_element(); g + (2,3,4) + sage: g*r + (2,3,4) + (2,4,3) + 2*(1,3,2) + 3*(1,4,3) """ try: self.product_on_basis = module.product_on_basis From f02ca284d4c7b886f5b185db5e6b6d6a8bc4a039 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 18 Jun 2021 10:45:30 -0700 Subject: [PATCH 227/280] src/sage/geometry/polyhedron/face.py: Remove unused import --- src/sage/geometry/polyhedron/face.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/face.py b/src/sage/geometry/polyhedron/face.py index ede09a3e1d9..61644b896f4 100644 --- a/src/sage/geometry/polyhedron/face.py +++ b/src/sage/geometry/polyhedron/face.py @@ -73,7 +73,6 @@ # http://www.gnu.org/licenses/ ######################################################################## -from sage.structure.sage_object import SageObject from sage.structure.richcmp import richcmp_method, richcmp from sage.misc.all import cached_method from sage.modules.free_module_element import vector From bc84af8c795b7da433d2000afc3626ee65ba28b8 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Fri, 18 Jun 2021 14:37:48 -0700 Subject: [PATCH 228/280] trac 31696: fixes for Japanese documentation, PDF build --- src/doc/ja/a_tour_of_sage/conf.py | 10 ---------- src/doc/ja/tutorial/conf.py | 13 ------------- 2 files changed, 23 deletions(-) diff --git a/src/doc/ja/a_tour_of_sage/conf.py b/src/doc/ja/a_tour_of_sage/conf.py index edcad0cf018..ab57a150cbb 100644 --- a/src/doc/ja/a_tour_of_sage/conf.py +++ b/src/doc/ja/a_tour_of_sage/conf.py @@ -37,13 +37,3 @@ ('index', name + '.tex', project, 'The Sage Group', 'manual'), ] - -# LaTeX の docclass 設定 -latex_docclass = {'manual': 'jsbook'} - -# Additional LaTeX stuff for the French version -#latex_elements['preamble'] += '\\DeclareUnicodeCharacter{00A0}{\\nobreakspace}\n' - -# the definition of \\at in the standard preamble of the sphinx doc -# conflicts with that in babel/french[b] -latex_elements['preamble'] += '\\let\\at\\undefined' diff --git a/src/doc/ja/tutorial/conf.py b/src/doc/ja/tutorial/conf.py index 141629139f0..520f4cc46f3 100644 --- a/src/doc/ja/tutorial/conf.py +++ b/src/doc/ja/tutorial/conf.py @@ -37,16 +37,3 @@ ('index', name + '.tex', project, 'The Sage Group', 'manual'), ] - -# LaTeX の docclass 設定 -latex_docclass = {'manual': 'jsbook'} - -# Additional LaTeX stuff for the French version -#latex_elements['preamble'] += '\\DeclareUnicodeCharacter{00A0}{\\nobreakspace}\n' - -# the definition of \\at in the standard preamble of the sphinx doc -# conflicts with that in babel/french[b] -latex_elements['preamble'] += '\\let\\at\\undefined' - -# -# html_use_smartypants = False From 5351ebbfdd14067d008ae30b9acb76ba8bead936 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Fri, 18 Jun 2021 17:53:45 -0500 Subject: [PATCH 229/280] Fix PEP8 spacing issue, simplify tuple --- src/sage/modules/with_basis/representation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py index 96fabf36c6e..27fffed163d 100644 --- a/src/sage/modules/with_basis/representation.py +++ b/src/sage/modules/with_basis/representation.py @@ -168,9 +168,9 @@ def __init__(self, semigroup, module, on_basis, side="left", **kwargs): sage: G = CyclicPermutationGroup(3) sage: M = algebras.Exterior(QQ, 'x', 3) sage: from sage.modules.with_basis.representation import Representation - sage: on_basis = lambda g,m: M.prod([M.monomial(tuple([g(j+1)-1])) for j in m]) #cyclically permute generators + sage: on_basis = lambda g,m: M.prod([M.monomial((g(j+1)-1,)) for j in m]) #cyclically permute generators sage: from sage.categories.algebras import Algebras - sage: R = Representation(G, M, on_basis, category = Algebras(QQ).WithBasis().FiniteDimensional()) + sage: R = Representation(G, M, on_basis, category=Algebras(QQ).WithBasis().FiniteDimensional()) sage: r = R.an_element(); r 1 + 2*x0 + x0*x1 + 3*x1 sage: r*r @@ -193,7 +193,7 @@ def __init__(self, semigroup, module, on_basis, side="left", **kwargs): sage: from sage.modules.with_basis.representation import Representation sage: action = lambda g,x: A.monomial(g*x) sage: category = Algebras(QQ).WithBasis().FiniteDimensional() - sage: R = Representation(G, A, action, 'left', category = category) + sage: R = Representation(G, A, action, 'left', category=category) sage: r = R.an_element(); r () + (2,3,4) + 2*(1,3)(2,4) + 3*(1,4)(2,3) sage: r^2 @@ -349,7 +349,7 @@ def product_by_coercion(self, left, right): sage: from sage.categories.algebras import Algebras sage: category = Algebras(QQ).FiniteDimensional().WithBasis() - sage: T = Representation(G, E, on_basis, category = category) + sage: T = Representation(G, E, on_basis, category=category) sage: t = T.an_element(); t 1 + 2*e0 + 3*e1 + e1*e2 sage: g*t == t From 4c0a4ae74c28680e48080c39c5e3b171a79a9ad3 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 19 Jun 2021 13:04:17 +0200 Subject: [PATCH 230/280] initialize do_f_vector --- src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 8f2864f77af..54f9c66460b 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -3004,7 +3004,7 @@ cdef class CombinatorialPolyhedron(SageObject): cdef size_t current_length = 1 # dynamically enlarge **edges cdef int output_dim_init = 1 if do_edges else dim - 2 - cdef bint do_f_vector + cdef bint do_f_vector = False cdef size_t* f_vector if dim == 1 and (do_edges or self.n_facets() > 1): From 297792afd93108f916b60674e7a748bad2609087 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sun, 20 Jun 2021 01:16:58 +0200 Subject: [PATCH 231/280] equalities -> equations for #31821 as well --- src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 530ee8dea22..0dc365fe999 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2785,7 +2785,7 @@ cdef class CombinatorialPolyhedron(SageObject): return (self.n_facets() == other.n_facets() and self.Vrepresentation() == other.Vrepresentation() and self.facet_names() == other_C.facet_names() - and self.equalities() == other_C.equalities() + and self.equations() == other_C.equations() and self.dimension() == other.dimension() and self.far_face_tuple() == other_C.far_face_tuple() and self.incidence_matrix() == other.incidence_matrix()) From e1b5b5e178cc887bbad14daaafcd417aeef988be Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 13 May 2020 12:05:41 +0200 Subject: [PATCH 232/280] join_of_Vrep and meet_of_facets for face iterator --- .../face_iterator.pxd | 1 + .../face_iterator.pyx | 426 +++++++++++++++++- 2 files changed, 425 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 25a7014c33e..2bfd0d5f114 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -69,6 +69,7 @@ cdef class FaceIterator_base(SageObject): cdef size_t set_coatom_rep(self) except -1 cdef size_t set_atom_rep(self) except -1 cdef int ignore_subsets(self) except -1 + cdef int find_face(self, face_t face) except -1 @cython.final cdef class FaceIterator(FaceIterator_base): diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 3e83ff5edb9..2ce3dc7f513 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -176,7 +176,7 @@ AUTHOR: from sage.rings.integer cimport smallInteger from cysignals.signals cimport sig_check -from .conversions cimport bit_rep_to_Vrep_list +from .conversions cimport bit_rep_to_Vrep_list, Vrep_list_to_bit_rep from .conversions import facets_tuple_to_bit_rep_of_facets from .base cimport CombinatorialPolyhedron @@ -482,6 +482,391 @@ cdef class FaceIterator_base(SageObject): raise ValueError("only possible when in dual mode") self.ignore_subsets() + def meet_of_facets(self, *indices): + r""" + Construct the meet of the facets indicated by the indices. + + This is the largest face contained in all facets with the given indices. + + The iterator must be reseted if not newly initialized. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: it = P.face_generator() + sage: it.meet_of_facets(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it.meet_of_facets(1,2).ambient_H_indices() + (1, 2) + sage: it.meet_of_facets(1,3).ambient_H_indices() + (1, 3) + sage: it.meet_of_facets(1,5).ambient_H_indices() + (0, 1, 2, 3, 4, 5) + + sage: P = polytopes.cross_polytope(4) + sage: it = P.face_generator() + sage: it.meet_of_facets().ambient_H_indices() + () + sage: it.meet_of_facets(1,3).ambient_H_indices() + (1, 2, 3, 4) + sage: it.meet_of_facets(1,2).ambient_H_indices() + (1, 2) + sage: it.meet_of_facets(1,6).ambient_H_indices() + (1, 6) + sage: it.meet_of_facets(1,2,6).ambient_H_indices() + (1, 2, 6, 7) + sage: it.meet_of_facets(1,2,5,6).ambient_H_indices() + (0, 1, 2, 3, 4, 5, 6, 7) + + sage: s = cones.schur(4) + sage: C = CombinatorialPolyhedron(s) + sage: it = C.face_iter() + sage: it.meet_of_facets(1,2).ambient_H_indices() + (1, 2) + sage: it.meet_of_facets(1,2,3).ambient_H_indices() + Traceback (most recent call last): + ... + AssertionError: index out of range + + If the iterator has already been used, it must be reseted before:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator() + sage: _ = next(it), next(it) + sage: next(it).ambient_V_indices() + (15, 16, 17, 18, 19) + sage: it.meet_of_facets(9,11) + Traceback (most recent call last): + ... + ValueError: please reset the face iterator + sage: it.reset() + sage: it.meet_of_facets(9,11).ambient_H_indices() + (9, 11) + + """ + if self.dual: + return self._join_of_atoms(*indices) + else: + return self._meet_of_coatoms(*indices) + + def join_of_Vrep(self, *indices): + r""" + Construct the join of the Vrepresentatives indicated by the indices. + + This is the smallest face containing all Vrepresentatives with the given indices. + + The iterator must be reseted if not newly initialized. + + .. NOTE:: + + In case of unbounded polyhedra, the smallest face containing given Vrepresentatives + may not te well defined. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: it = P.face_generator() + sage: it.join_of_Vrep(1) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: it.join_of_Vrep(1,2).ambient_V_indices() + (1, 2) + sage: it.join_of_Vrep(1,3).ambient_V_indices() + (0, 1, 2, 3) + sage: it.join_of_Vrep(1,5).ambient_V_indices() + (0, 1, 5, 6) + + sage: P = polytopes.cross_polytope(4) + sage: it = P.face_generator() + sage: it.join_of_Vrep().ambient_V_indices() + () + sage: it.join_of_Vrep(1,3).ambient_V_indices() + (1, 3) + sage: it.join_of_Vrep(1,2).ambient_V_indices() + (1, 2) + sage: it.join_of_Vrep(1,6).ambient_V_indices() + (0, 1, 2, 3, 4, 5, 6, 7) + sage: it.join_of_Vrep(8) + Traceback (most recent call last): + ... + AssertionError: index out of range + + If the iterator has already been used, it must be reseted before:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator() + sage: _ = next(it), next(it) + sage: next(it).ambient_V_indices() + (15, 16, 17, 18, 19) + sage: it.join_of_Vrep(1,10) + Traceback (most recent call last): + ... + ValueError: please reset the face iterator + sage: it.reset() + sage: it.join_of_Vrep(1,10).ambient_V_indices() + (1, 10) + + In case of an unbounded polyhedron, we try to make sense of the input:: + + sage: P = polytopes.cube()*Polyhedron(lines=[[1]]) + sage: it = P.face_generator() + sage: it.join_of_Vrep(1) + A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 1 vertex and 1 line + sage: it.join_of_Vrep(0, 1) + A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 1 vertex and 1 line + sage: it.join_of_Vrep(0) + Traceback (most recent call last): + ... + ValueError: the join is not well-defined + + sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) + sage: it = P.face_generator() + sage: it.join_of_Vrep(0) + A 0-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex + sage: it.join_of_Vrep(1) + A 0-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex + sage: it.join_of_Vrep(2) + Traceback (most recent call last): + ... + ValueError: the join is not well-defined + sage: it.join_of_Vrep(0,2) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + + sage: P = Polyhedron(rays=[[1,0], [0,1]]) + sage: it = P.face_generator() + sage: it.join_of_Vrep(0) + A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex + sage: it.join_of_Vrep(1,2) + A 2-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 rays + """ + if not self.dual: + return self._join_of_atoms(*indices) + else: + return self._meet_of_coatoms(*indices) + + def _meet_of_coatoms(self, *indices): + r""" + Construct the meet of the coatoms indicated by the indices. + + The iterator must be reseted if not newly initialized. + + .. SEEALSO:: + + :meth:`meet_of_facets`, + :meth:`join_of_Vrep`. + + EXAMPLES: + + In non-dual mode we construct the meet of facets:: + + sage: P = polytopes.cube() + sage: it = P.face_generator(dual=False) + sage: it._meet_of_coatoms(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it._meet_of_coatoms(1,2,3) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: it._meet_of_coatoms(1,2,3).ambient_H_indices() + (1, 2, 3) + + In dual mode we construct the join of vertices/rays:: + + sage: P = polytopes.cube() + sage: it = P.face_generator(dual=True) + sage: it._meet_of_coatoms(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it._meet_of_coatoms(1,2,3) + A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices + sage: it._meet_of_coatoms(1) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + + The face iterator must not have the output dimension specified:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator(2) + sage: it._meet_of_coatoms(1,2) + Traceback (most recent call last): + ... + ValueError: face iterator must not have the output dimension specified + + TESTS: + + We prevent a segmentation fault:: + + sage: P = polytopes.simplex() + sage: it = P.face_generator() + sage: it._meet_of_coatoms(-1) + Traceback (most recent call last): + ... + OverflowError: can't convert negative value to size_t + sage: it._meet_of_coatoms(100) + Traceback (most recent call last): + ... + AssertionError: index out of range + + The empty face is detected correctly, even with lines or rays:: + + sage: P = polytopes.cube()*Polyhedron(lines=[[1]]) + sage: it = P.face_generator() + sage: it._meet_of_coatoms(1,2,4,5) + A -1-dimensional face of a Polyhedron in ZZ^4 + + sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) + sage: it = P.face_generator() + sage: it._meet_of_coatoms(0) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 2 vertices + sage: it._meet_of_coatoms(1) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + sage: it._meet_of_coatoms(2) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + sage: it._meet_of_coatoms(1, 2) + A -1-dimensional face of a Polyhedron in QQ^2 + """ + if unlikely(self.structure.face_status != 0): + raise ValueError("please reset the face iterator") + if unlikely(self.structure.output_dimension != -2): + raise ValueError("face iterator must not have the output dimension specified") + + cdef size_t n_atoms = self.coatoms.n_atoms() + cdef size_t n_coatoms = self.coatoms.n_faces() + cdef ListOfFaces coatoms = self.coatoms + + cdef ListOfFaces face_mem = ListOfFaces(1, n_atoms, n_coatoms) + cdef face_t face = face_mem.data.faces[0] + cdef size_t i + + # Initialize the full polyhedron. + for i in range(n_atoms): + face_add_atom(face, i) + + for i in indices: + assert 0 <= i < n_coatoms, "index out of range" + face_intersection(face, face, coatoms.data.faces[i]) + + if not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): + # The meet is contained in the far face and therefore is the empty face. + face_clear(face) + + self.find_face(face) + output = self.current() + self.reset() + return output + + def _join_of_atoms(self, *indices): + r""" + Construct the join of atoms indicated by the indices. + + The iterator must be reseted if not newly initialized. + + .. SEEALSO:: + + :meth:`meet_of_facets`, + :meth:`join_of_Vrep`. + + EXAMPLES: + + In dual mode we construct the meet of facets:: + + sage: P = polytopes.cube() + sage: it = P.face_generator(dual=True) + sage: it._join_of_atoms(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it._join_of_atoms(1,2,3) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: it._join_of_atoms(1,2,3).ambient_H_indices() + (1, 2, 3) + + In non-dual mode we construct the join of vertices/rays:: + + sage: P = polytopes.cube() + sage: it = P.face_generator(dual=False) + sage: it._join_of_atoms(1,2) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + sage: it._join_of_atoms(1,2,3) + A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices + sage: it._join_of_atoms(1) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + + If the iterator has already been used, it must be reseted before:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator() + sage: _ = next(it), next(it) + sage: next(it).ambient_V_indices() + (15, 16, 17, 18, 19) + sage: it._join_of_atoms(1,10) + Traceback (most recent call last): + ... + ValueError: please reset the face iterator + sage: it.reset() + sage: it._join_of_atoms(1,10).ambient_V_indices() + (1, 10) + + The face iterator must not have the output dimension specified:: + + sage: P = polytopes.dodecahedron() + sage: it = P.face_generator(2) + sage: it._join_of_atoms(1,2) + Traceback (most recent call last): + ... + ValueError: face iterator must not have the output dimension specified + + TESTS: + + We prevent a segmentation fault:: + + sage: P = polytopes.simplex() + sage: it = P.face_generator() + sage: it._join_of_atoms(-1) + Traceback (most recent call last): + ... + AssertionError: index out of range + sage: it._join_of_atoms(100) + Traceback (most recent call last): + ... + AssertionError: index out of range + """ + if unlikely(self.structure.face_status != 0): + raise ValueError("please reset the face iterator") + if unlikely(self.structure.output_dimension != -2): + raise ValueError("face iterator must not have the output dimension specified") + + cdef size_t n_atoms = self.coatoms.n_atoms() + cdef size_t n_coatoms = self.coatoms.n_faces() + cdef ListOfFaces coatoms = self.coatoms + + cdef ListOfFaces face_mem = ListOfFaces(2, n_atoms, n_coatoms) + cdef face_t face = face_mem.data.faces[0] + cdef face_t pseudo_face = face_mem.data.faces[1] + cdef size_t i + + assert all(i in range(n_atoms) for i in indices), "index out of range" + + # Initialize a pseudo_face as indicated by the indices. + for i in indices: + face_add_atom(pseudo_face, i) + + # Initialize the full polyhedron. + for i in range(n_atoms): + face_add_atom(face, i) + + # Now we intersect all faces that contain our pseudo_face. + for i in range(n_coatoms): + if face_issubset(pseudo_face, coatoms.data.faces[i]): + face_intersection(face, face, coatoms.data.faces[i]) + + if not indices: + # The neutral element of the join. + face_clear(face) + elif not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): + # The join is not well-defined. + # We allow for unbounded polyhedra to compute the join, even with rays. + # However, the result is not necesarrily well-defined. + raise ValueError("the join is not well-defined") + + self.find_face(face) + output = self.current() + self.reset() + return output + cdef int ignore_subsets(self) except -1: r""" Ignore sub-/supfaces of the current face. @@ -573,6 +958,43 @@ cdef class FaceIterator_base(SageObject): """ return bit_rep_to_Vrep_list(self.structure.face, self.structure.atom_rep) + cdef int find_face(self, face_t face) except -1: + """ + Iterate until the current face is ``face``. + + The value can then be obtained with :meth:`current`. + """ + cdef size_t n_atoms = face_len_atoms(face) + + if n_atoms == self.coatoms.n_atoms(): + # The face is the universe. + self.structure.face[0] = face[0] + self.structure.face_status = 1 + self.structure.current_dimension = self.structure.dimension + return 0 + elif n_atoms == 0: + # The face is the empty face. + self.structure.face[0] = face[0] + self.structure.face_status = 1 + self.structure.current_dimension = -1 + return 0 + + cdef int d = self.next_dimension() + while self.structure.current_dimension != self.structure.dimension: + if face_issubset(face, self.structure.face): + if face_issubset(self.structure.face, face): + # Found our face. + return 0 + else: + # The face is not a subface/supface of the current face. + self.ignore_subsets() + + d = self.next_dimension() + + raise ValueError("the face appears to be incorrect") + + + cdef class FaceIterator(FaceIterator_base): r""" A class to iterate over all combinatorial faces of a polyhedron. @@ -1037,7 +1459,7 @@ cdef class FaceIterator_geom(FaceIterator_base): .. SEEALSO:: - See :class:`FaceIterator`. + :class:`FaceIterator_base`. """ def __init__(self, P, dual=None, output_dimension=None): r""" From 99a1d1645cce1728dfa2c8fec3a30a5dc48d1a5c Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 13 May 2020 12:21:02 +0200 Subject: [PATCH 233/280] expose in combinatorial_polyhedron --- .../combinatorial_polyhedron/base.pyx | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 0dc365fe999..18b84d148f1 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2262,6 +2262,56 @@ cdef class CombinatorialPolyhedron(SageObject): return (False, None) return False + def join_of_Vrep(self, *indices): + r""" + Return the smallest face containing all Vrepresentatives indicated by the indices. + + .. SEEALSO:: + + :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator_base.join_of_Vrep`. + + EXAMPLES:: + + sage: P = polytopes.permutahedron(4) + sage: C = CombinatorialPolyhedron(P) + sage: C.join_of_Vrep(0,1) + A 1-dimensional face of a 3-dimensional combinatorial polyhedron + sage: C.join_of_Vrep(0,3).ambient_V_indices() + (0, 1, 2, 3, 4, 5) + sage: C.join_of_Vrep(8).ambient_V_indices() + (8,) + sage: C.join_of_Vrep().ambient_V_indices() + () + """ + return self.face_iter().join_of_Vrep(*indices) + + def meet_of_facets(self, *indices): + r""" + Return the largest face contained all facets indicated by the indices. + + .. SEEALSO:: + + :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator_base.meet_of_facets`. + + EXAMPLES:: + + sage: P = polytopes.dodecahedron() + sage: C = CombinatorialPolyhedron(P) + sage: C.meet_of_facets(0) + A 2-dimensional face of a 3-dimensional combinatorial polyhedron + sage: C.meet_of_facets(0).ambient_H_indices() + (0,) + sage: C.meet_of_facets(0,1).ambient_H_indices() + (0, 1) + sage: C.meet_of_facets(0,3).ambient_H_indices() + (0, 3) + sage: C.meet_of_facets(0,2,3).ambient_H_indices() + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) + sage: C.meet_of_facets().ambient_H_indices() + () + """ + return self.face_iter().meet_of_facets(*indices) + def face_iter(self, dimension=None, dual=None): r""" Iterator over all proper faces of specified dimension. From cc9f594650a2f6ed8ff1057cec46e5baf1b1adb3 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 13 May 2020 13:44:48 +0200 Subject: [PATCH 234/280] raise index error for index error; fix doctests --- src/sage/geometry/polyhedron/base.py | 6 ++++++ .../polyhedron/combinatorial_polyhedron/base.pyx | 8 ++++---- .../combinatorial_polyhedron/face_iterator.pyx | 16 +++++++++------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index e082c73d111..999b8f91bce 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6937,6 +6937,12 @@ def facets(self): return () return self.faces(self.dimension()-1) + def join_of_Vrep(self, *Vrepresentatives): + return self.face_generator().join_of_Vrep(*Vrepresentatives) + + def meet_of_facets(self, *facets): + return self.face_generator().meet_of_facets(*facets) + @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 18b84d148f1..0ff427befd5 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2276,8 +2276,8 @@ cdef class CombinatorialPolyhedron(SageObject): sage: C = CombinatorialPolyhedron(P) sage: C.join_of_Vrep(0,1) A 1-dimensional face of a 3-dimensional combinatorial polyhedron - sage: C.join_of_Vrep(0,3).ambient_V_indices() - (0, 1, 2, 3, 4, 5) + sage: C.join_of_Vrep(0,11).ambient_V_indices() + (0, 1, 10, 11, 12, 13) sage: C.join_of_Vrep(8).ambient_V_indices() (8,) sage: C.join_of_Vrep().ambient_V_indices() @@ -2303,8 +2303,8 @@ cdef class CombinatorialPolyhedron(SageObject): (0,) sage: C.meet_of_facets(0,1).ambient_H_indices() (0, 1) - sage: C.meet_of_facets(0,3).ambient_H_indices() - (0, 3) + sage: C.meet_of_facets(0,2).ambient_H_indices() + (0, 2) sage: C.meet_of_facets(0,2,3).ambient_H_indices() (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) sage: C.meet_of_facets().ambient_H_indices() diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 2ce3dc7f513..045abf26f89 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -526,7 +526,7 @@ cdef class FaceIterator_base(SageObject): sage: it.meet_of_facets(1,2,3).ambient_H_indices() Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: coatoms out of range If the iterator has already been used, it must be reseted before:: @@ -588,7 +588,7 @@ cdef class FaceIterator_base(SageObject): sage: it.join_of_Vrep(8) Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: coatoms out of range If the iterator has already been used, it must be reseted before:: @@ -700,7 +700,7 @@ cdef class FaceIterator_base(SageObject): sage: it._meet_of_coatoms(100) Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: coatoms out of range The empty face is detected correctly, even with lines or rays:: @@ -738,7 +738,8 @@ cdef class FaceIterator_base(SageObject): face_add_atom(face, i) for i in indices: - assert 0 <= i < n_coatoms, "index out of range" + if not 0 <= i < n_coatoms: + raise IndexError("coatoms out of range") face_intersection(face, face, coatoms.data.faces[i]) if not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): @@ -818,11 +819,11 @@ cdef class FaceIterator_base(SageObject): sage: it._join_of_atoms(-1) Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: atoms out of range sage: it._join_of_atoms(100) Traceback (most recent call last): ... - AssertionError: index out of range + IndexError: atoms out of range """ if unlikely(self.structure.face_status != 0): raise ValueError("please reset the face iterator") @@ -838,7 +839,8 @@ cdef class FaceIterator_base(SageObject): cdef face_t pseudo_face = face_mem.data.faces[1] cdef size_t i - assert all(i in range(n_atoms) for i in indices), "index out of range" + if not all(i in range(n_atoms) for i in indices): + raise IndexError("atoms out of range") # Initialize a pseudo_face as indicated by the indices. for i in indices: From 800ad8dcfac5e8813106349fa16d8257848cb168 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 13 May 2020 14:24:53 +0200 Subject: [PATCH 235/280] expose in Polyhedron_base --- .../geometry/polyhedra_quickref.rst | 2 + src/sage/geometry/polyhedron/base.py | 136 +++++++++++++++++- 2 files changed, 136 insertions(+), 2 deletions(-) diff --git a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst index d684c689c3e..968800e3925 100644 --- a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst +++ b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst @@ -172,6 +172,8 @@ List of Polyhedron methods :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.face_generator` | a generator over the faces :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.faces` | the list of faces :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.facets` | the list of facets + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.join_of_Vrep` | smallest face containing specified Vrepresentatives + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.meet_of_facets` | largest face contained specified facets :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.normal_fan` | returns the fan spanned by the normals of the supporting hyperplanes of the polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.gale_transform` | returns the (affine) Gale transform of the vertices of the polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.hyperplane_arrangement` | returns the hyperplane arrangement given by the defining facets of the polyhedron diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 999b8f91bce..81a4923ec6f 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6938,10 +6938,142 @@ def facets(self): return self.faces(self.dimension()-1) def join_of_Vrep(self, *Vrepresentatives): - return self.face_generator().join_of_Vrep(*Vrepresentatives) + r""" + Return the smallest face that contains in ``Vrepresentatives``. + + INPUT: + + - ``Vrepresentatives`` -- vertices/rays/lines or indices of such + + OUTPUT: a :class:`~sage.geometry.polyhedron.face.PolyhedronFace` + + .. NOTE:: + + In case of unbounded polyhedra, the join of rays etc. may not be well-defined. + + EXAMPLES:: + + sage: P = polytopes.permutahedron(5) + sage: P.join_of_Vrep(1) + A 0-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 1 vertex + sage: P.join_of_Vrep() + A -1-dimensional face of a Polyhedron in ZZ^5 + sage: P.join_of_Vrep(1,3,4).ambient_V_indices() + (0, 1, 2, 3, 4, 5) + + The input is flexible:: + + sage: P.join_of_Vrep(2, P.vertices()[3], P.Vrepresentation(4)) + A 2-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 6 vertices + + In case of an unbounded polyhedron, the join may not be well-defined:: + + sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) + sage: P.join_of_Vrep(0) + A 0-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex + sage: P.join_of_Vrep(0,1) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 2 vertices + sage: P.join_of_Vrep(0,2) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + sage: P.join_of_Vrep(1,2) + A 1-dimensional face of a Polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray + sage: P.join_of_Vrep(2) + Traceback (most recent call last): + ... + ValueError: the join is not well-defined + """ + from sage.geometry.polyhedron.representation import Vrepresentation + from sage.geometry.polyhedron.face import PolyhedronFace + + new_indices = [0]*len(Vrepresentatives) + for i, v in enumerate(Vrepresentatives): + if isinstance(v, PolyhedronFace) and facet.dim() == 0: + v = v.ambient_V_indices()[0] + + if v in ZZ: + new_indices[i] = v + elif isinstance(v, Vrepresentation): + new_indices[i] = v.index() + else: + raise ValueError("{} is not a Vrepresentative".format(v)) + + return self.face_generator().join_of_Vrep(*new_indices) def meet_of_facets(self, *facets): - return self.face_generator().meet_of_facets(*facets) + r""" + Return the largest face that is contained in ``facets``. + + INPUT: + + - ``facets`` -- facets or indices of facets; + the indices are assumed to be the indices of the Hrepresentation + + OUTPUT: a :class:`~sage.geometry.polyhedron.face.PolyhedronFace` + + EXAMPLES:: + + sage: P = polytopes.permutahedron(5) + sage: P.meet_of_facets() + A 4-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 120 vertices + sage: P.meet_of_facets(1) + A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 24 vertices + sage: P.meet_of_facets(2) + A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 12 vertices + sage: P.meet_of_facets(2,3,4) + A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices + sage: P.meet_of_facets(2,3,4).ambient_H_indices() + (0, 2, 3, 4) + + The indices are the indices of the Hrepresentation:: + + sage: P.meet_of_facets(0) + Traceback (most recent call last): + ... + ValueError: 0 is not a facet + + The input is flexible:: + + sage: P.meet_of_facets(P.facets()[-1], P.inequalities()[1], 3) + A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices + + TESTS: + + The offset is taken correctly:: + + sage: P = polytopes.permutahedron(3, backend='field') + sage: P.Hrepresentation() + (An inequality (1, 1, 0) x - 3 >= 0, + An inequality (1, 0, 0) x - 1 >= 0, + An inequality (0, -1, 0) x + 3 >= 0, + An inequality (0, 2, 0) x - 2 >= 0, + An inequality (-4, -4, 0) x + 20 >= 0, + An inequality (-8, 0, 0) x + 24 >= 0, + An equation (-1/6, -1/6, -1/6) x + 1 == 0) + sage: P.meet_of_facets(0) + A 1-dimensional face of a Polyhedron in QQ^3 defined as the convex hull of 2 vertices + """ + from sage.geometry.polyhedron.representation import Inequality + from sage.geometry.polyhedron.face import PolyhedronFace + + # Equations are ignored by combinatorial polyhedron for indexing. + offset = 0 + if self.n_equations() and self.Hrepresentation(0).is_equation(): + offset = self.n_equations() + + new_indices = [0]*len(facets) + for i, facet in enumerate(facets): + if isinstance(facet, PolyhedronFace) and facet.dim() + 1 == self.dim(): + H_indices = facet.ambient_H_indices() + facet = H_indices[0] if H_indices[0] >= offset else H_indices[-1] + + if facet in ZZ and facet >= offset: + new_indices[i] = facet - offset + elif isinstance(facet, Inequality): + new_indices[i] = facet.index() - offset + else: + raise ValueError("{} is not a facet".format(facet)) + + return self.face_generator().meet_of_facets(*new_indices) @cached_method(do_pickle=True) def f_vector(self): From 510bbc6ede25c83a0591572c04f289761486d9a0 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 7 Sep 2020 15:36:08 +0200 Subject: [PATCH 236/280] fix doctests --- src/sage/geometry/polyhedron/base.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 81a4923ec6f..c23f4afc820 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6958,8 +6958,8 @@ def join_of_Vrep(self, *Vrepresentatives): A 0-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 1 vertex sage: P.join_of_Vrep() A -1-dimensional face of a Polyhedron in ZZ^5 - sage: P.join_of_Vrep(1,3,4).ambient_V_indices() - (0, 1, 2, 3, 4, 5) + sage: P.join_of_Vrep(0,12,13).ambient_V_indices() + (0, 12, 13, 68) The input is flexible:: @@ -7017,12 +7017,12 @@ def meet_of_facets(self, *facets): A 4-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 120 vertices sage: P.meet_of_facets(1) A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 24 vertices - sage: P.meet_of_facets(2) + sage: P.meet_of_facets(4) A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 12 vertices - sage: P.meet_of_facets(2,3,4) + sage: P.meet_of_facets(1,3,7) A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices - sage: P.meet_of_facets(2,3,4).ambient_H_indices() - (0, 2, 3, 4) + sage: P.meet_of_facets(1,3,7).ambient_H_indices() + (0, 1, 3, 7) The indices are the indices of the Hrepresentation:: @@ -7033,7 +7033,7 @@ def meet_of_facets(self, *facets): The input is flexible:: - sage: P.meet_of_facets(P.facets()[-1], P.inequalities()[1], 3) + sage: P.meet_of_facets(P.facets()[-1], P.inequalities()[2], 7) A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices TESTS: @@ -7042,13 +7042,13 @@ def meet_of_facets(self, *facets): sage: P = polytopes.permutahedron(3, backend='field') sage: P.Hrepresentation() - (An inequality (1, 1, 0) x - 3 >= 0, + (An inequality (0, 0, 1) x - 1 >= 0, + An inequality (0, 1, 0) x - 1 >= 0, + An inequality (0, 1, 1) x - 3 >= 0, An inequality (1, 0, 0) x - 1 >= 0, - An inequality (0, -1, 0) x + 3 >= 0, - An inequality (0, 2, 0) x - 2 >= 0, - An inequality (-4, -4, 0) x + 20 >= 0, - An inequality (-8, 0, 0) x + 24 >= 0, - An equation (-1/6, -1/6, -1/6) x + 1 == 0) + An inequality (1, 0, 1) x - 3 >= 0, + An inequality (1, 1, 0) x - 3 >= 0, + An equation (1, 1, 1) x - 6 == 0) sage: P.meet_of_facets(0) A 1-dimensional face of a Polyhedron in QQ^3 defined as the convex hull of 2 vertices """ From bacc3e88dde6cea6f99d4dbd631acb8d0bb55a94 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 16 Jan 2021 21:33:30 +0100 Subject: [PATCH 237/280] improved documentation --- src/sage/geometry/polyhedron/base.py | 19 ++++++++++++++++--- .../face_iterator.pyx | 18 +++++++++--------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index c23f4afc820..4a6f84ebb96 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -7038,7 +7038,8 @@ def meet_of_facets(self, *facets): TESTS: - The offset is taken correctly:: + Equations are not considered by the combinatorial polyhedron. + We check that the index corresponds to the Hrepresentation index:: sage: P = polytopes.permutahedron(3, backend='field') sage: P.Hrepresentation() @@ -7049,8 +7050,20 @@ def meet_of_facets(self, *facets): An inequality (1, 0, 1) x - 3 >= 0, An inequality (1, 1, 0) x - 3 >= 0, An equation (1, 1, 1) x - 6 == 0) - sage: P.meet_of_facets(0) - A 1-dimensional face of a Polyhedron in QQ^3 defined as the convex hull of 2 vertices + sage: P.meet_of_facets(0).ambient_Hrepresentation() + (An equation (1, 1, 1) x - 6 == 0, An inequality (0, 0, 1) x - 1 >= 0) + + sage: P = polytopes.permutahedron(3, backend='ppl') + sage: P.Hrepresentation() + (An equation (1, 1, 1) x - 6 == 0, + An inequality (1, 1, 0) x - 3 >= 0, + An inequality (-1, -1, 0) x + 5 >= 0, + An inequality (0, 1, 0) x - 1 >= 0, + An inequality (-1, 0, 0) x + 3 >= 0, + An inequality (1, 0, 0) x - 1 >= 0, + An inequality (0, -1, 0) x + 3 >= 0) + sage: P.meet_of_facets(1).ambient_Hrepresentation() + (An equation (1, 1, 1) x - 6 == 0, An inequality (1, 1, 0) x - 3 >= 0) """ from sage.geometry.polyhedron.representation import Inequality from sage.geometry.polyhedron.face import PolyhedronFace diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 045abf26f89..ccf31c6ee9e 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -488,7 +488,7 @@ cdef class FaceIterator_base(SageObject): This is the largest face contained in all facets with the given indices. - The iterator must be reseted if not newly initialized. + The iterator must be reset if not newly initialized. EXAMPLES:: @@ -528,7 +528,7 @@ cdef class FaceIterator_base(SageObject): ... IndexError: coatoms out of range - If the iterator has already been used, it must be reseted before:: + If the iterator has already been used, it must be reset before:: sage: P = polytopes.dodecahedron() sage: it = P.face_generator() @@ -555,11 +555,11 @@ cdef class FaceIterator_base(SageObject): This is the smallest face containing all Vrepresentatives with the given indices. - The iterator must be reseted if not newly initialized. + The iterator must be reset if not newly initialized. .. NOTE:: - In case of unbounded polyhedra, the smallest face containing given Vrepresentatives + In the case of unbounded polyhedra, the smallest face containing given Vrepresentatives may not te well defined. EXAMPLES:: @@ -590,7 +590,7 @@ cdef class FaceIterator_base(SageObject): ... IndexError: coatoms out of range - If the iterator has already been used, it must be reseted before:: + If the iterator has already been used, it must be reset before:: sage: P = polytopes.dodecahedron() sage: it = P.face_generator() @@ -605,7 +605,7 @@ cdef class FaceIterator_base(SageObject): sage: it.join_of_Vrep(1,10).ambient_V_indices() (1, 10) - In case of an unbounded polyhedron, we try to make sense of the input:: + In the case of an unbounded polyhedron, we try to make sense of the input:: sage: P = polytopes.cube()*Polyhedron(lines=[[1]]) sage: it = P.face_generator() @@ -647,7 +647,7 @@ cdef class FaceIterator_base(SageObject): r""" Construct the meet of the coatoms indicated by the indices. - The iterator must be reseted if not newly initialized. + The iterator must be reset if not newly initialized. .. SEEALSO:: @@ -755,7 +755,7 @@ cdef class FaceIterator_base(SageObject): r""" Construct the join of atoms indicated by the indices. - The iterator must be reseted if not newly initialized. + The iterator must be reset if not newly initialized. .. SEEALSO:: @@ -786,7 +786,7 @@ cdef class FaceIterator_base(SageObject): sage: it._join_of_atoms(1) A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex - If the iterator has already been used, it must be reseted before:: + If the iterator has already been used, it must be reset before:: sage: P = polytopes.dodecahedron() sage: it = P.face_generator() From 9cbd0159a5567fab7078494a53ffe935542a217f Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 13 May 2021 20:38:18 +0200 Subject: [PATCH 238/280] fix a variable name and add a doctest --- src/sage/geometry/polyhedron/base.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 4a6f84ebb96..d6cdf34120f 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6966,6 +6966,13 @@ def join_of_Vrep(self, *Vrepresentatives): sage: P.join_of_Vrep(2, P.vertices()[3], P.Vrepresentation(4)) A 2-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 6 vertices + :: + + sage: P = polytopes.cube() + sage: a, b = P.faces(0)[:2] + sage: P.join_of_Vrep(a, b) + A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices + In case of an unbounded polyhedron, the join may not be well-defined:: sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) @@ -6987,7 +6994,7 @@ def join_of_Vrep(self, *Vrepresentatives): new_indices = [0]*len(Vrepresentatives) for i, v in enumerate(Vrepresentatives): - if isinstance(v, PolyhedronFace) and facet.dim() == 0: + if isinstance(v, PolyhedronFace) and v.dim() == 0: v = v.ambient_V_indices()[0] if v in ZZ: From 4612fe5b59faeac318e42211839d7df408aed8b9 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 13 May 2021 21:06:37 +0200 Subject: [PATCH 239/280] typos --- src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst | 2 +- src/sage/geometry/polyhedron/base.py | 4 ++-- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst index 968800e3925..9a5a113d0c9 100644 --- a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst +++ b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst @@ -173,7 +173,7 @@ List of Polyhedron methods :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.faces` | the list of faces :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.facets` | the list of facets :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.join_of_Vrep` | smallest face containing specified Vrepresentatives - :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.meet_of_facets` | largest face contained specified facets + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.meet_of_facets` | largest face contained in specified facets :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.normal_fan` | returns the fan spanned by the normals of the supporting hyperplanes of the polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.gale_transform` | returns the (affine) Gale transform of the vertices of the polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.hyperplane_arrangement` | returns the hyperplane arrangement given by the defining facets of the polyhedron diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index d6cdf34120f..6be46ff330c 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6949,7 +6949,7 @@ def join_of_Vrep(self, *Vrepresentatives): .. NOTE:: - In case of unbounded polyhedra, the join of rays etc. may not be well-defined. + In the case of unbounded polyhedra, the join of rays etc. may not be well-defined. EXAMPLES:: @@ -6973,7 +6973,7 @@ def join_of_Vrep(self, *Vrepresentatives): sage: P.join_of_Vrep(a, b) A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices - In case of an unbounded polyhedron, the join may not be well-defined:: + In the case of an unbounded polyhedron, the join may not be well-defined:: sage: P = Polyhedron(vertices=[[1,0], [0,1]], rays=[[1,1]]) sage: P.join_of_Vrep(0) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index ccf31c6ee9e..445d79ea54a 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -560,7 +560,7 @@ cdef class FaceIterator_base(SageObject): .. NOTE:: In the case of unbounded polyhedra, the smallest face containing given Vrepresentatives - may not te well defined. + may not be well defined. EXAMPLES:: From 3b09b688994db6c116595d9f54b7a846723ccc77 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 13 May 2021 21:08:17 +0200 Subject: [PATCH 240/280] another typo --- src/sage/geometry/polyhedron/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 6be46ff330c..d5df6d2e380 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6939,7 +6939,7 @@ def facets(self): def join_of_Vrep(self, *Vrepresentatives): r""" - Return the smallest face that contains in ``Vrepresentatives``. + Return the smallest face that contains ``Vrepresentatives``. INPUT: From fe428aa3f3935c89b70e8e41512c99e47ad1db34 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 17 May 2021 12:07:13 +0200 Subject: [PATCH 241/280] improved documentation and check for elements to be of the correct polyhedron --- src/sage/geometry/polyhedron/base.py | 62 ++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index d5df6d2e380..cab134f220e 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -6943,7 +6943,7 @@ def join_of_Vrep(self, *Vrepresentatives): INPUT: - - ``Vrepresentatives`` -- vertices/rays/lines or indices of such + - ``Vrepresentatives`` -- vertices/rays/lines of ``self`` or indices of such OUTPUT: a :class:`~sage.geometry.polyhedron.face.PolyhedronFace` @@ -6988,6 +6988,25 @@ def join_of_Vrep(self, *Vrepresentatives): Traceback (most recent call last): ... ValueError: the join is not well-defined + + The ``Vrepresentatives`` must be of ``self``:: + + sage: P = polytopes.cube(backend='ppl') + sage: Q = polytopes.cube(backend='field') + sage: v = P.vertices()[0] + sage: P.join_of_Vrep(v) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: Q.join_of_Vrep(v) + Traceback (most recent call last): + ... + ValueError: not a Vrepresentative of ``self`` + sage: f = P.faces(0)[0] + sage: P.join_of_Vrep(v) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: Q.join_of_Vrep(v) + Traceback (most recent call last): + ... + ValueError: not a Vrepresentative of ``self`` """ from sage.geometry.polyhedron.representation import Vrepresentation from sage.geometry.polyhedron.face import PolyhedronFace @@ -6995,11 +7014,14 @@ def join_of_Vrep(self, *Vrepresentatives): new_indices = [0]*len(Vrepresentatives) for i, v in enumerate(Vrepresentatives): if isinstance(v, PolyhedronFace) and v.dim() == 0: - v = v.ambient_V_indices()[0] - - if v in ZZ: + if v.polyhedron() is not self: + raise ValueError("not a Vrepresentative of ``self``") + new_indices[i] = v.ambient_V_indices()[0] + elif v in ZZ: new_indices[i] = v elif isinstance(v, Vrepresentation): + if v.polyhedron() is not self: + raise ValueError("not a Vrepresentative of ``self``") new_indices[i] = v.index() else: raise ValueError("{} is not a Vrepresentative".format(v)) @@ -7012,7 +7034,7 @@ def meet_of_facets(self, *facets): INPUT: - - ``facets`` -- facets or indices of facets; + - ``facets`` -- facets or indices of facets of ``self``; the indices are assumed to be the indices of the Hrepresentation OUTPUT: a :class:`~sage.geometry.polyhedron.face.PolyhedronFace` @@ -7031,18 +7053,38 @@ def meet_of_facets(self, *facets): sage: P.meet_of_facets(1,3,7).ambient_H_indices() (0, 1, 3, 7) - The indices are the indices of the Hrepresentation:: + The indices are the indices of the Hrepresentation. + ``0`` corresponds to an equation and is not permitted as input:: sage: P.meet_of_facets(0) Traceback (most recent call last): ... - ValueError: 0 is not a facet + ValueError: 0 is the index of an equation and not of an inequality The input is flexible:: sage: P.meet_of_facets(P.facets()[-1], P.inequalities()[2], 7) A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices + The facets must be facets of ``self``:: + + sage: P = polytopes.cube(backend='ppl') + sage: Q = polytopes.cube(backend='field') + sage: f = P.facets()[0] + sage: P.meet_of_facets(f) + A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices + sage: Q.meet_of_facets(f) + Traceback (most recent call last): + ... + ValueError: not a facet of ``self`` + sage: f = P.inequalities()[0] + sage: P.meet_of_facets(f) + A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices + sage: Q.meet_of_facets(f) + Traceback (most recent call last): + ... + ValueError: not a facet of ``self`` + TESTS: Equations are not considered by the combinatorial polyhedron. @@ -7083,15 +7125,19 @@ def meet_of_facets(self, *facets): new_indices = [0]*len(facets) for i, facet in enumerate(facets): if isinstance(facet, PolyhedronFace) and facet.dim() + 1 == self.dim(): + if facet.polyhedron() is not self: + raise ValueError("not a facet of ``self``") H_indices = facet.ambient_H_indices() facet = H_indices[0] if H_indices[0] >= offset else H_indices[-1] if facet in ZZ and facet >= offset: new_indices[i] = facet - offset elif isinstance(facet, Inequality): + if facet.polyhedron() is not self: + raise ValueError("not a facet of ``self``") new_indices[i] = facet.index() - offset else: - raise ValueError("{} is not a facet".format(facet)) + raise ValueError("{} is the index of an equation and not of an inequality".format(facet)) return self.face_generator().meet_of_facets(*new_indices) From dd79e1c91067a981f22fee01c1e6f1cca5e3df51 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 17 May 2021 13:01:52 +0200 Subject: [PATCH 242/280] typo --- src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 0ff427befd5..9e24a4cd683 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2287,7 +2287,7 @@ cdef class CombinatorialPolyhedron(SageObject): def meet_of_facets(self, *indices): r""" - Return the largest face contained all facets indicated by the indices. + Return the largest face contained in all facets indicated by the indices. .. SEEALSO:: From 56c66a4439323f647687848ea6a00c4243efc098 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Tue, 18 May 2021 22:06:23 +0200 Subject: [PATCH 243/280] ignore equations; make aliases --- .../geometry/polyhedra_quickref.rst | 4 +- src/sage/geometry/polyhedron/base.py | 78 ++++++++++++------- .../combinatorial_polyhedron/base.pyx | 18 ++--- .../combinatorial_face.pyx | 11 +-- .../face_iterator.pxd | 1 + .../face_iterator.pyx | 42 ++++++---- 6 files changed, 95 insertions(+), 59 deletions(-) diff --git a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst index 9a5a113d0c9..c8f6b40b171 100644 --- a/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst +++ b/src/doc/en/thematic_tutorials/geometry/polyhedra_quickref.rst @@ -172,8 +172,8 @@ List of Polyhedron methods :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.face_generator` | a generator over the faces :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.faces` | the list of faces :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.facets` | the list of facets - :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.join_of_Vrep` | smallest face containing specified Vrepresentatives - :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.meet_of_facets` | largest face contained in specified facets + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.join_of_Vrep`, :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.least_common_superface_of_Vrep` | smallest face containing specified Vrepresentatives + :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.meet_of_Hrep`, :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.greatest_common_subface_of_Hrep` | largest face contained in specified Hrepresentatives :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.normal_fan` | returns the fan spanned by the normals of the supporting hyperplanes of the polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.gale_transform` | returns the (affine) Gale transform of the vertices of the polyhedron :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.hyperplane_arrangement` | returns the hyperplane arrangement given by the defining facets of the polyhedron diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index cab134f220e..1533a8bcb15 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -7007,6 +7007,15 @@ def join_of_Vrep(self, *Vrepresentatives): Traceback (most recent call last): ... ValueError: not a Vrepresentative of ``self`` + + TESTS: + + ``least_common_superface_of_Vrep`` is an alias:: + + sage: P.least_common_superface_of_Vrep(v) + A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex + sage: P.least_common_superface_of_Vrep == P.join_of_Vrep + True """ from sage.geometry.polyhedron.representation import Vrepresentation from sage.geometry.polyhedron.face import PolyhedronFace @@ -7028,13 +7037,15 @@ def join_of_Vrep(self, *Vrepresentatives): return self.face_generator().join_of_Vrep(*new_indices) - def meet_of_facets(self, *facets): + least_common_superface_of_Vrep = join_of_Vrep + + def meet_of_Hrep(self, *Hrepresentatives): r""" Return the largest face that is contained in ``facets``. INPUT: - - ``facets`` -- facets or indices of facets of ``self``; + - ``Hrepresentatives`` -- facets or indices of Hrepresentatives; the indices are assumed to be the indices of the Hrepresentation OUTPUT: a :class:`~sage.geometry.polyhedron.face.PolyhedronFace` @@ -7042,45 +7053,43 @@ def meet_of_facets(self, *facets): EXAMPLES:: sage: P = polytopes.permutahedron(5) - sage: P.meet_of_facets() + sage: P.meet_of_Hrep() A 4-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 120 vertices - sage: P.meet_of_facets(1) + sage: P.meet_of_Hrep(1) A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 24 vertices - sage: P.meet_of_facets(4) + sage: P.meet_of_Hrep(4) A 3-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 12 vertices - sage: P.meet_of_facets(1,3,7) + sage: P.meet_of_Hrep(1,3,7) A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices - sage: P.meet_of_facets(1,3,7).ambient_H_indices() + sage: P.meet_of_Hrep(1,3,7).ambient_H_indices() (0, 1, 3, 7) The indices are the indices of the Hrepresentation. - ``0`` corresponds to an equation and is not permitted as input:: + ``0`` corresponds to an equation and is ignored:: - sage: P.meet_of_facets(0) - Traceback (most recent call last): - ... - ValueError: 0 is the index of an equation and not of an inequality + sage: P.meet_of_Hrep(0) + A 4-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 120 vertices The input is flexible:: - sage: P.meet_of_facets(P.facets()[-1], P.inequalities()[2], 7) + sage: P.meet_of_Hrep(P.facets()[-1], P.inequalities()[2], 7) A 1-dimensional face of a Polyhedron in ZZ^5 defined as the convex hull of 2 vertices - The facets must be facets of ``self``:: + The ``Hrepresentatives`` must belong to ``self``:: sage: P = polytopes.cube(backend='ppl') sage: Q = polytopes.cube(backend='field') sage: f = P.facets()[0] - sage: P.meet_of_facets(f) + sage: P.meet_of_Hrep(f) A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices - sage: Q.meet_of_facets(f) + sage: Q.meet_of_Hrep(f) Traceback (most recent call last): ... ValueError: not a facet of ``self`` sage: f = P.inequalities()[0] - sage: P.meet_of_facets(f) + sage: P.meet_of_Hrep(f) A 2-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 4 vertices - sage: Q.meet_of_facets(f) + sage: Q.meet_of_Hrep(f) Traceback (most recent call last): ... ValueError: not a facet of ``self`` @@ -7099,7 +7108,7 @@ def meet_of_facets(self, *facets): An inequality (1, 0, 1) x - 3 >= 0, An inequality (1, 1, 0) x - 3 >= 0, An equation (1, 1, 1) x - 6 == 0) - sage: P.meet_of_facets(0).ambient_Hrepresentation() + sage: P.meet_of_Hrep(0).ambient_Hrepresentation() (An equation (1, 1, 1) x - 6 == 0, An inequality (0, 0, 1) x - 1 >= 0) sage: P = polytopes.permutahedron(3, backend='ppl') @@ -7111,10 +7120,17 @@ def meet_of_facets(self, *facets): An inequality (-1, 0, 0) x + 3 >= 0, An inequality (1, 0, 0) x - 1 >= 0, An inequality (0, -1, 0) x + 3 >= 0) - sage: P.meet_of_facets(1).ambient_Hrepresentation() + sage: P.meet_of_Hrep(1).ambient_Hrepresentation() + (An equation (1, 1, 1) x - 6 == 0, An inequality (1, 1, 0) x - 3 >= 0) + + ``greatest_common_subface_of_Hrep`` is an alias:: + + sage: P.greatest_common_subface_of_Hrep(1).ambient_Hrepresentation() (An equation (1, 1, 1) x - 6 == 0, An inequality (1, 1, 0) x - 3 >= 0) + sage: P.greatest_common_subface_of_Hrep == P.meet_of_Hrep + True """ - from sage.geometry.polyhedron.representation import Inequality + from sage.geometry.polyhedron.representation import Inequality, Equation from sage.geometry.polyhedron.face import PolyhedronFace # Equations are ignored by combinatorial polyhedron for indexing. @@ -7122,8 +7138,8 @@ def meet_of_facets(self, *facets): if self.n_equations() and self.Hrepresentation(0).is_equation(): offset = self.n_equations() - new_indices = [0]*len(facets) - for i, facet in enumerate(facets): + new_indices = [] + for i, facet in enumerate(Hrepresentatives): if isinstance(facet, PolyhedronFace) and facet.dim() + 1 == self.dim(): if facet.polyhedron() is not self: raise ValueError("not a facet of ``self``") @@ -7131,15 +7147,25 @@ def meet_of_facets(self, *facets): facet = H_indices[0] if H_indices[0] >= offset else H_indices[-1] if facet in ZZ and facet >= offset: - new_indices[i] = facet - offset + # Note that ``CombinatorialPolyhedron`` ignores indices of equations + # and has equations last. + new_indices.append(facet - offset) elif isinstance(facet, Inequality): if facet.polyhedron() is not self: raise ValueError("not a facet of ``self``") - new_indices[i] = facet.index() - offset + new_indices.append(facet.index() - offset) + elif isinstance(facet, Equation): + # Ignore equations. + continue + elif facet in ZZ and 0 <= facet < offset: + # Ignore equations. + continue else: raise ValueError("{} is the index of an equation and not of an inequality".format(facet)) - return self.face_generator().meet_of_facets(*new_indices) + return self.face_generator().meet_of_Hrep(*new_indices) + + greatest_common_subface_of_Hrep = meet_of_Hrep @cached_method(do_pickle=True) def f_vector(self): diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 9e24a4cd683..a8e184f17c9 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -2285,32 +2285,32 @@ cdef class CombinatorialPolyhedron(SageObject): """ return self.face_iter().join_of_Vrep(*indices) - def meet_of_facets(self, *indices): + def meet_of_Hrep(self, *indices): r""" Return the largest face contained in all facets indicated by the indices. .. SEEALSO:: - :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator_base.meet_of_facets`. + :meth:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator_base.meet_of_Hrep`. EXAMPLES:: sage: P = polytopes.dodecahedron() sage: C = CombinatorialPolyhedron(P) - sage: C.meet_of_facets(0) + sage: C.meet_of_Hrep(0) A 2-dimensional face of a 3-dimensional combinatorial polyhedron - sage: C.meet_of_facets(0).ambient_H_indices() + sage: C.meet_of_Hrep(0).ambient_H_indices() (0,) - sage: C.meet_of_facets(0,1).ambient_H_indices() + sage: C.meet_of_Hrep(0,1).ambient_H_indices() (0, 1) - sage: C.meet_of_facets(0,2).ambient_H_indices() + sage: C.meet_of_Hrep(0,2).ambient_H_indices() (0, 2) - sage: C.meet_of_facets(0,2,3).ambient_H_indices() + sage: C.meet_of_Hrep(0,2,3).ambient_H_indices() (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) - sage: C.meet_of_facets().ambient_H_indices() + sage: C.meet_of_Hrep().ambient_H_indices() () """ - return self.face_iter().meet_of_facets(*indices) + return self.face_iter().meet_of_Hrep(*indices) def face_iter(self, dimension=None, dual=None): r""" diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx index 439a6f7d7bf..f9f3798ca21 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx @@ -181,8 +181,9 @@ cdef class CombinatorialFace(SageObject): self._ambient_dimension = it.structure.dimension self._ambient_Vrep = it._Vrep self._ambient_facets = it._facet_names + self._n_ambient_facets = it._n_facets self._equations = it._equations - self._n_equations = len(self._equations) if self._equations else 0 + self._n_equations = it._n_equations self._hash_index = it.structure._index self._initialized_from_face_lattice = False @@ -209,6 +210,10 @@ cdef class CombinatorialFace(SageObject): self._ambient_facets = all_faces._facet_names self._equations = all_faces._equations self._n_equations = len(self._equations) if self._equations else 0 + if self._dual: + self._n_ambient_facets = self.atoms.n_faces() + else: + self._n_ambient_facets = self.coatoms.n_faces() self._initialized_from_face_lattice = True @@ -228,10 +233,6 @@ cdef class CombinatorialFace(SageObject): # Reverse the hash index in dual mode to respect inclusion of faces. self._hash_index = -self._hash_index - 1 - self._n_ambient_facets = self.atoms.n_faces() - else: - self._n_ambient_facets = self.coatoms.n_faces() - def _repr_(self): r""" Return a description of the combinatorial face. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index 2bfd0d5f114..a72ceb45c57 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -56,6 +56,7 @@ cdef class FaceIterator_base(SageObject): # some copies from ``CombinatorialPolyhedron`` cdef tuple _Vrep, _facet_names, _equations + cdef size_t _n_equations, _n_facets cdef bint _bounded # Atoms and coatoms are the vertices/facets of the Polyedron. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 445d79ea54a..3ab044dae17 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -252,7 +252,12 @@ cdef class FaceIterator_base(SageObject): self.atoms = C.bitrep_Vrep() self._Vrep = C.Vrep() self._facet_names = C.facet_names() + self._n_facets = C.bitrep_facets().n_faces() self._equations = C.equations() + if self._equations: + self._n_equations = len(self._equations) + else: + self._n_equations = 0 self._bounded = C.is_bounded() self.structure.atom_rep = self._mem.allocarray(self.coatoms.n_atoms(), sizeof(size_t)) @@ -482,7 +487,7 @@ cdef class FaceIterator_base(SageObject): raise ValueError("only possible when in dual mode") self.ignore_subsets() - def meet_of_facets(self, *indices): + def meet_of_Hrep(self, *indices): r""" Construct the meet of the facets indicated by the indices. @@ -494,36 +499,36 @@ cdef class FaceIterator_base(SageObject): sage: P = polytopes.cube() sage: it = P.face_generator() - sage: it.meet_of_facets(1,2) + sage: it.meet_of_Hrep(1,2) A 1-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 2 vertices - sage: it.meet_of_facets(1,2).ambient_H_indices() + sage: it.meet_of_Hrep(1,2).ambient_H_indices() (1, 2) - sage: it.meet_of_facets(1,3).ambient_H_indices() + sage: it.meet_of_Hrep(1,3).ambient_H_indices() (1, 3) - sage: it.meet_of_facets(1,5).ambient_H_indices() + sage: it.meet_of_Hrep(1,5).ambient_H_indices() (0, 1, 2, 3, 4, 5) sage: P = polytopes.cross_polytope(4) sage: it = P.face_generator() - sage: it.meet_of_facets().ambient_H_indices() + sage: it.meet_of_Hrep().ambient_H_indices() () - sage: it.meet_of_facets(1,3).ambient_H_indices() + sage: it.meet_of_Hrep(1,3).ambient_H_indices() (1, 2, 3, 4) - sage: it.meet_of_facets(1,2).ambient_H_indices() + sage: it.meet_of_Hrep(1,2).ambient_H_indices() (1, 2) - sage: it.meet_of_facets(1,6).ambient_H_indices() + sage: it.meet_of_Hrep(1,6).ambient_H_indices() (1, 6) - sage: it.meet_of_facets(1,2,6).ambient_H_indices() + sage: it.meet_of_Hrep(1,2,6).ambient_H_indices() (1, 2, 6, 7) - sage: it.meet_of_facets(1,2,5,6).ambient_H_indices() + sage: it.meet_of_Hrep(1,2,5,6).ambient_H_indices() (0, 1, 2, 3, 4, 5, 6, 7) sage: s = cones.schur(4) sage: C = CombinatorialPolyhedron(s) sage: it = C.face_iter() - sage: it.meet_of_facets(1,2).ambient_H_indices() + sage: it.meet_of_Hrep(1,2).ambient_H_indices() (1, 2) - sage: it.meet_of_facets(1,2,3).ambient_H_indices() + sage: it.meet_of_Hrep(1,2,3).ambient_H_indices() Traceback (most recent call last): ... IndexError: coatoms out of range @@ -535,15 +540,18 @@ cdef class FaceIterator_base(SageObject): sage: _ = next(it), next(it) sage: next(it).ambient_V_indices() (15, 16, 17, 18, 19) - sage: it.meet_of_facets(9,11) + sage: it.meet_of_Hrep(9,11) Traceback (most recent call last): ... ValueError: please reset the face iterator sage: it.reset() - sage: it.meet_of_facets(9,11).ambient_H_indices() + sage: it.meet_of_Hrep(9,11).ambient_H_indices() (9, 11) """ + # Ignore equations. + indices = [i for i in indices + if not (self._n_facets <= i < self._n_facets + self._n_equations)] if self.dual: return self._join_of_atoms(*indices) else: @@ -651,7 +659,7 @@ cdef class FaceIterator_base(SageObject): .. SEEALSO:: - :meth:`meet_of_facets`, + :meth:`meet_of_Hrep`, :meth:`join_of_Vrep`. EXAMPLES: @@ -759,7 +767,7 @@ cdef class FaceIterator_base(SageObject): .. SEEALSO:: - :meth:`meet_of_facets`, + :meth:`meet_of_Hrep`, :meth:`join_of_Vrep`. EXAMPLES: From 80c80e54e725b5f677534bfe121c8beee7a11ede Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 19 May 2021 21:47:28 +0200 Subject: [PATCH 244/280] small fixes --- src/sage/geometry/polyhedron/base.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 1533a8bcb15..6efab021a0f 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -7016,6 +7016,13 @@ def join_of_Vrep(self, *Vrepresentatives): A 0-dimensional face of a Polyhedron in ZZ^3 defined as the convex hull of 1 vertex sage: P.least_common_superface_of_Vrep == P.join_of_Vrep True + + Error message for invalid input:: + + sage: P.join_of_Vrep('foo') + Traceback (most recent call last): + ... + ValueError: foo is not a Vrepresentative """ from sage.geometry.polyhedron.representation import Vrepresentation from sage.geometry.polyhedron.face import PolyhedronFace @@ -7041,7 +7048,7 @@ def join_of_Vrep(self, *Vrepresentatives): def meet_of_Hrep(self, *Hrepresentatives): r""" - Return the largest face that is contained in ``facets``. + Return the largest face that is contained in ``Hrepresentatives``. INPUT: @@ -7109,7 +7116,7 @@ def meet_of_Hrep(self, *Hrepresentatives): An inequality (1, 1, 0) x - 3 >= 0, An equation (1, 1, 1) x - 6 == 0) sage: P.meet_of_Hrep(0).ambient_Hrepresentation() - (An equation (1, 1, 1) x - 6 == 0, An inequality (0, 0, 1) x - 1 >= 0) + (An inequality (0, 0, 1) x - 1 >= 0, An equation (1, 1, 1) x - 6 == 0) sage: P = polytopes.permutahedron(3, backend='ppl') sage: P.Hrepresentation() @@ -7129,6 +7136,13 @@ def meet_of_Hrep(self, *Hrepresentatives): (An equation (1, 1, 1) x - 6 == 0, An inequality (1, 1, 0) x - 3 >= 0) sage: P.greatest_common_subface_of_Hrep == P.meet_of_Hrep True + + Error message for invalid input:: + + sage: P.meet_of_Hrep('foo') + Traceback (most recent call last): + ... + ValueError: foo is not a Hrepresentative """ from sage.geometry.polyhedron.representation import Inequality, Equation from sage.geometry.polyhedron.face import PolyhedronFace @@ -7161,7 +7175,7 @@ def meet_of_Hrep(self, *Hrepresentatives): # Ignore equations. continue else: - raise ValueError("{} is the index of an equation and not of an inequality".format(facet)) + raise ValueError("{} is not a Hrepresentative".format(facet)) return self.face_generator().meet_of_Hrep(*new_indices) From 9afb2761d7459190701d5d896c922c23397c8dc5 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 19 May 2021 23:03:23 +0200 Subject: [PATCH 245/280] fix segementation fault --- .../face_iterator.pyx | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 3ab044dae17..462246147ed 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -548,6 +548,26 @@ cdef class FaceIterator_base(SageObject): sage: it.meet_of_Hrep(9,11).ambient_H_indices() (9, 11) + TESTS: + + Check that things work fine, if the face iterator was never properly initialized:: + + sage: P = Polyhedron() + sage: P.meet_of_Hrep() + A -1-dimensional face of a Polyhedron in ZZ^0 + sage: P = Polyhedron([[0,0]]) + sage: P.meet_of_Hrep() + A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex + sage: P.meet_of_Hrep(0) + A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex + sage: P = Polyhedron(lines=[[1]]) + sage: P.meet_of_Hrep() + A 1-dimensional face of a Polyhedron in ZZ^1 defined as the convex hull of 1 vertex and 1 line + sage: P = Polyhedron(lines=[[1, 1]]) + sage: P.meet_of_Hrep() + A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 1 line + sage: P.meet_of_Hrep(0) + A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 1 line """ # Ignore equations. indices = [i for i in indices @@ -645,6 +665,33 @@ cdef class FaceIterator_base(SageObject): A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex sage: it.join_of_Vrep(1,2) A 2-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 rays + + TESTS: + + Check that things work fine, if the face iterator was never properly initialized:: + + sage: P = Polyhedron() + sage: P.join_of_Vrep() + A -1-dimensional face of a Polyhedron in ZZ^0 + sage: P = Polyhedron([[0,0]]) + sage: P.join_of_Vrep() + A -1-dimensional face of a Polyhedron in ZZ^2 + sage: P.join_of_Vrep(0) + A 0-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex + sage: P = Polyhedron(lines=[[1]]) + sage: P.join_of_Vrep() + A -1-dimensional face of a Polyhedron in ZZ^1 + sage: P.join_of_Vrep(0) + Traceback (most recent call last): + ... + ValueError: the join is not well-defined + sage: P = Polyhedron(lines=[[1, 1]]) + sage: P.join_of_Vrep() + A -1-dimensional face of a Polyhedron in ZZ^2 + sage: P.join_of_Vrep(0) + Traceback (most recent call last): + ... + ValueError: the join is not well-defined """ if not self.dual: return self._join_of_atoms(*indices) @@ -750,7 +797,10 @@ cdef class FaceIterator_base(SageObject): raise IndexError("coatoms out of range") face_intersection(face, face, coatoms.data.faces[i]) - if not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): + if not self.coatoms.n_faces(): + # Prevent segmentation fault. + pass + elif not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): # The meet is contained in the far face and therefore is the empty face. face_clear(face) @@ -866,6 +916,11 @@ cdef class FaceIterator_base(SageObject): if not indices: # The neutral element of the join. face_clear(face) + elif not self._bounded and not self.coatoms.n_faces(): + # Note: It is important to catch this and not to run ``face_issubset`` to prevent a segmentation fault. + if face_len_atoms(face): + # Contained in the far face, if it contains anything. + raise ValueError("the join is not well-defined") elif not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): # The join is not well-defined. # We allow for unbounded polyhedra to compute the join, even with rays. From ef5c95e4a73e7f31a53befa1adc0f6a61573ea8e Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 20 May 2021 01:06:41 +0200 Subject: [PATCH 246/280] check with a nice error for negative indices --- .../combinatorial_polyhedron/face_iterator.pyx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 462246147ed..68d1606c598 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -751,7 +751,7 @@ cdef class FaceIterator_base(SageObject): sage: it._meet_of_coatoms(-1) Traceback (most recent call last): ... - OverflowError: can't convert negative value to size_t + IndexError: coatoms out of range sage: it._meet_of_coatoms(100) Traceback (most recent call last): ... @@ -786,11 +786,12 @@ cdef class FaceIterator_base(SageObject): cdef ListOfFaces face_mem = ListOfFaces(1, n_atoms, n_coatoms) cdef face_t face = face_mem.data.faces[0] - cdef size_t i + cdef int i + cdef size_t j # Initialize the full polyhedron. - for i in range(n_atoms): - face_add_atom(face, i) + for j in range(n_atoms): + face_add_atom(face, j) for i in indices: if not 0 <= i < n_coatoms: @@ -895,9 +896,10 @@ cdef class FaceIterator_base(SageObject): cdef ListOfFaces face_mem = ListOfFaces(2, n_atoms, n_coatoms) cdef face_t face = face_mem.data.faces[0] cdef face_t pseudo_face = face_mem.data.faces[1] + cdef int j cdef size_t i - if not all(i in range(n_atoms) for i in indices): + if not all(0 <= j < n_atoms for j in indices): raise IndexError("atoms out of range") # Initialize a pseudo_face as indicated by the indices. From 99ddc2f635e0546be6ee4f9813fea85486c79817 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 20 May 2021 09:15:20 +0200 Subject: [PATCH 247/280] better check for the far face containment --- .../face_iterator.pxd | 1 + .../face_iterator.pyx | 36 ++++++++++--------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index a72ceb45c57..a1a21d6dde4 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -58,6 +58,7 @@ cdef class FaceIterator_base(SageObject): cdef tuple _Vrep, _facet_names, _equations cdef size_t _n_equations, _n_facets cdef bint _bounded + cdef face_t _far_face # 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/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 68d1606c598..53a8c09a73b 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -259,6 +259,7 @@ cdef class FaceIterator_base(SageObject): else: self._n_equations = 0 self._bounded = C.is_bounded() + self._far_face[0] = C._far_face[0] self.structure.atom_rep = self._mem.allocarray(self.coatoms.n_atoms(), sizeof(size_t)) self.structure.coatom_rep = self._mem.allocarray(self.coatoms.n_faces(), sizeof(size_t)) @@ -304,7 +305,7 @@ cdef class FaceIterator_base(SageObject): # needs to be at most ``n_facets - 1``. # Hence it is fine to use the first entry already for the far face, # as ``self.visited_all`` holds ``n_facets`` pointers. - add_face_shallow(self.structure.visited_all[self.structure.dimension-1], C._far_face) + add_face_shallow(self.structure.visited_all[self.structure.dimension-1], self._far_face) # Initialize ``first_time``. self.structure.first_time = self._mem.allocarray(self.structure.dimension, sizeof(bint)) @@ -682,16 +683,25 @@ cdef class FaceIterator_base(SageObject): sage: P.join_of_Vrep() A -1-dimensional face of a Polyhedron in ZZ^1 sage: P.join_of_Vrep(0) - Traceback (most recent call last): - ... - ValueError: the join is not well-defined + A 1-dimensional face of a Polyhedron in ZZ^1 defined as the convex hull of 1 vertex and 1 line sage: P = Polyhedron(lines=[[1, 1]]) sage: P.join_of_Vrep() A -1-dimensional face of a Polyhedron in ZZ^2 + sage: P.Vrepresentation() + (A line in the direction (1, 1), A vertex at (0, 0)) sage: P.join_of_Vrep(0) - Traceback (most recent call last): - ... - ValueError: the join is not well-defined + A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 1 line + sage: P.join_of_Vrep(1) + A 1-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 1 line + sage: P = Polyhedron(lines=[[1, 0], [0, 1]]) + sage: P.join_of_Vrep() + A -1-dimensional face of a Polyhedron in ZZ^2 + sage: P.join_of_Vrep(0) + A 2-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines + sage: P.join_of_Vrep(0, 1) + A 2-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines + sage: P.join_of_Vrep(0, 1, 2) + A 2-dimensional face of a Polyhedron in ZZ^2 defined as the convex hull of 1 vertex and 2 lines """ if not self.dual: return self._join_of_atoms(*indices) @@ -798,10 +808,7 @@ cdef class FaceIterator_base(SageObject): raise IndexError("coatoms out of range") face_intersection(face, face, coatoms.data.faces[i]) - if not self.coatoms.n_faces(): - # Prevent segmentation fault. - pass - elif not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): + if not self._bounded and face_issubset(face, self._far_face): # The meet is contained in the far face and therefore is the empty face. face_clear(face) @@ -918,12 +925,7 @@ cdef class FaceIterator_base(SageObject): if not indices: # The neutral element of the join. face_clear(face) - elif not self._bounded and not self.coatoms.n_faces(): - # Note: It is important to catch this and not to run ``face_issubset`` to prevent a segmentation fault. - if face_len_atoms(face): - # Contained in the far face, if it contains anything. - raise ValueError("the join is not well-defined") - elif not self._bounded and face_issubset(face, self.structure.visited_all[self.structure.dimension-1].faces[0]): + elif not self._bounded and face_issubset(face, self._far_face): # The join is not well-defined. # We allow for unbounded polyhedra to compute the join, even with rays. # However, the result is not necesarrily well-defined. From 0f78d2a6c05e0460cab8dea296c5d32ac25d2835 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 11:19:34 +0200 Subject: [PATCH 248/280] implement only subfaces/supfaces for face iterator --- .../face_data_structure.pxd | 3 + .../face_iterator.pxd | 2 + .../face_iterator.pyx | 187 +++++++++++++++++- 3 files changed, 184 insertions(+), 8 deletions(-) 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 4270885d515..627d8dd550a 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd @@ -186,3 +186,6 @@ cdef inline void swap_faces(face_t a, face_t b) nogil: tmp[0] = a[0] a[0] = b[0] b[0] = tmp[0] + +cdef inline bint faces_are_identical(face_t a, face_t b) nogil: + return a.atoms.limbs == b.atoms.limbs diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index a1a21d6dde4..d53ac95fe28 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -16,6 +16,7 @@ cdef struct iter_s: int dimension # dimension of the polyhedron int output_dimension # only faces of this (dual?) dimension are considered int lowest_dimension # don't consider faces below this (dual?) dimension + int highest_dimension # don't consider faces above this (dual?) dimension size_t _index # this counts the number of seen faces, useful for hasing the faces # ``visited_all`` points to faces, of which we have visited all faces already. @@ -71,6 +72,7 @@ cdef class FaceIterator_base(SageObject): cdef size_t set_coatom_rep(self) except -1 cdef size_t set_atom_rep(self) except -1 cdef int ignore_subsets(self) except -1 + cdef int only_subsets(self) except -1 cdef int find_face(self, face_t face) except -1 @cython.final diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 53a8c09a73b..e4b02fae715 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -223,7 +223,8 @@ cdef class FaceIterator_base(SageObject): self.structure.dual = dual self.structure.face_status = 0 self.structure.dimension = C.dimension() - self.structure.current_dimension = self.structure.dimension -1 + self.structure.current_dimension = self.structure.dimension - 1 + self.structure.highest_dimension = self.structure.dimension - 1 self._mem = MemoryAllocator() # We will not yield the empty face. @@ -353,6 +354,7 @@ cdef class FaceIterator_base(SageObject): self.structure.face_status = 0 self.structure.new_faces[self.structure.dimension - 1].n_faces = self.coatoms.n_faces() self.structure.current_dimension = self.structure.dimension - 1 + self.structure.highest_dimension = self.structure.dimension - 1 self.structure.first_time[self.structure.dimension - 1] = True self.structure.yet_to_visit = self.coatoms.n_faces() @@ -457,6 +459,33 @@ cdef class FaceIterator_base(SageObject): ....: sage: n_non_simplex_faces 127 + + Face iterator must not be in dual mode:: + + sage: it = C.face_iter(dual=True) + sage: _ = next(it) + sage: it.ignore_subfaces() + Traceback (most recent call last): + ... + ValueError: only possible when not in dual mode + + Cannot run ``ignore_subfaces`` after ``only_subfaces:: + + sage: it = C.face_iter(dual=False) + sage: _ = next(it) + sage: it.only_subfaces() + sage: it.ignore_subfaces() + Traceback (most recent call last): + ... + ValueError: cannot ignore a face after setting iterator to only visit subsets + + Face iterator must be set to a face first:: + + sage: it = C.face_iter(dual=False) + sage: it.ignore_subfaces() + Traceback (most recent call last): + ... + ValueError: iterator not set to a face yet """ if unlikely(self.dual): raise ValueError("only possible when not in dual mode") @@ -464,9 +493,9 @@ cdef class FaceIterator_base(SageObject): def ignore_supfaces(self): r""" - The iterator will not visit any faces of the current face. + The iterator will not visit any faces containing the current face. - Only possible when not in dual mode. + Only possible when in dual mode. EXAMPLES:: @@ -483,6 +512,15 @@ cdef class FaceIterator_base(SageObject): ....: sage: n_faces_with_non_simplex_quotient 4845 + + Face iterator must be in dual mode:: + + sage: it = C.face_iter(dual=False) + sage: _ = next(it) + sage: it.ignore_supfaces() + Traceback (most recent call last): + ... + ValueError: only possible when in dual mode """ if unlikely(not self.dual): raise ValueError("only possible when in dual mode") @@ -948,6 +986,8 @@ cdef class FaceIterator_base(SageObject): """ if unlikely(self.structure.face_status == 0): raise ValueError("iterator not set to a face yet") + if unlikely(self.structure.face_status == 3): + raise ValueError("cannot ignore a face after setting iterator to only visit subsets") if unlikely(self.structure.face_status == 2): # Nothing to do. return 0 @@ -959,13 +999,144 @@ cdef class FaceIterator_base(SageObject): add_face_shallow(self.structure.visited_all[self.structure.current_dimension], self.structure.face) self.structure.face_status = 2 + def only_subfaces(self): + r""" + The iterator will visit all (remaining) subfaces of the current face and then terminate. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: it = P.face_generator() + sage: next(it).ambient_H_indices() + () + sage: next(it).ambient_H_indices() + (0, 1, 2, 3, 4, 5) + sage: next(it).ambient_H_indices() + (5,) + sage: next(it).ambient_H_indices() + (4,) + sage: it.only_subfaces() + sage: list(f.ambient_H_indices() for f in it) + [(4, 5), (3, 4), (1, 4), (0, 4), (3, 4, 5), (0, 4, 5), (1, 3, 4), (0, 1, 4)] + + :: + + sage: P = polytopes.Birkhoff_polytope(4) + sage: C = P.combinatorial_polyhedron() + sage: it = C.face_iter() + sage: next(it).ambient_H_indices() + (15,) + sage: next(it).ambient_H_indices() + (14,) + sage: it.only_subfaces() + sage: all(14 in f.ambient_H_indices() for f in it) + True + + Face iterator needs to be set to a face first:: + + sage: it = C.face_iter() + sage: it.only_subfaces() + Traceback (most recent call last): + ... + ValueError: iterator not set to a face yet + + Face iterator must not be in dual mode:: + + sage: it = C.face_iter(dual=True) + sage: _ = next(it) + sage: it.only_subfaces() + Traceback (most recent call last): + ... + ValueError: only possible when not in dual mode + + Cannot run ``only_subfaces`` after ``ignore_subfaces:: + + sage: it = C.face_iter() + sage: _ = next(it) + sage: it.ignore_subfaces() + sage: it.only_subfaces() + Traceback (most recent call last): + ... + ValueError: cannot visit subsets after ignoring a face + """ + if unlikely(self.dual): + raise ValueError("only possible when not in dual mode") + self.only_subsets() + + def only_supfaces(self): + r""" + The iterator will visit all (remaining) faces + containing the current face and then terminate. + + EXAMPLES:: + + sage: P = polytopes.cross_polytope(3) + sage: it = P.face_generator() + sage: next(it).ambient_V_indices() + (0, 1, 2, 3, 4, 5) + sage: next(it).ambient_V_indices() + () + sage: next(it).ambient_V_indices() + (5,) + sage: next(it).ambient_V_indices() + (4,) + sage: it.only_supfaces() + sage: list(f.ambient_V_indices() for f in it) + [(4, 5), (3, 4), (2, 4), (0, 4), (3, 4, 5), (2, 4, 5), (0, 3, 4), (0, 2, 4)] + + :: + + sage: P = polytopes.Birkhoff_polytope(4) + sage: C = P.combinatorial_polyhedron() + sage: it = C.face_iter(dual=True) + sage: next(it).ambient_V_indices() + (23,) + sage: next(it).ambient_V_indices() + (22,) + sage: it.only_supfaces() + sage: all(22 in f.ambient_V_indices() for f in it) + True + """ + if unlikely(not self.dual): + raise ValueError("only possible when in dual mode") + self.only_subsets() + + cdef int only_subsets(self) except -1: + r""" + Only visit sub-/supfaces of the current face and then + terminate. + + See :meth:`FaceIterator_base.only_subfaces` and + :meth:`FaceIterator_base.only_supfaces`. + """ + if unlikely(self.structure.face_status == 0): + raise ValueError("iterator not set to a face yet") + if unlikely(self.structure.face_status == 2): + raise ValueError("cannot only visit subsets after ignoring a face") + + cdef face_list_t* faces = &self.structure.new_faces[self.structure.current_dimension] + cdef size_t yet_to_visit = self.structure.yet_to_visit + + if unlikely(yet_to_visit >= faces[0].n_faces + or not faces_are_identical(faces[0].faces[yet_to_visit], self.structure.face)): + raise ValueError("iterator is not set to the correct face") + + swap_faces(faces[0].faces[yet_to_visit], faces[0].faces[faces[0].n_faces - 1]) + + self.structure.face_status = 3 + self.structure.yet_to_visit = 0 + # This will work: + # ``next_dimension`` will first call ``next_face_loop`` and then check + # for the dimension. By this time the current dimension has changed. + self.structure.highest_dimension = self.structure.current_dimension - 1 + cdef inline CombinatorialFace next_face(self): r""" Set attribute ``face`` to the next face and return it as :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace`. """ self.next_dimension() - if unlikely(self.structure.current_dimension == self.structure.dimension): + if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): return None return CombinatorialFace(self) @@ -1363,7 +1534,7 @@ cdef class FaceIterator(FaceIterator_base): A 1-dimensional face of a 3-dimensional combinatorial polyhedron] """ cdef CombinatorialFace face = self.next_face() - if unlikely(self.structure.current_dimension == self.structure.dimension): + if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): raise StopIteration return face @@ -1656,7 +1827,7 @@ cdef class FaceIterator_geom(FaceIterator_base): return PolyhedronFace(self.P, [], range(self.P.n_Hrepresentation())) self.next_dimension() - if unlikely(self.structure.current_dimension == self.structure.dimension): + if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): raise StopIteration return self.current() @@ -1684,9 +1855,9 @@ cdef inline int next_dimension(iter_t structure) nogil except -1: r""" See :meth:`FaceIterator.next_dimension`. """ - cdef int dim = structure.dimension + cdef int max_dim = structure.highest_dimension structure.face_status = 0 - while (not next_face_loop(structure)) and (structure.current_dimension < dim): + while (not next_face_loop(structure)) and (structure.current_dimension <= max_dim): sig_check() structure._index += 1 return structure.current_dimension From 6c88068c4c78450513ac5a84d482f3ef33c8ba4e Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 11:47:26 +0200 Subject: [PATCH 249/280] fix bug revealed by a_maximal_chain --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index e4b02fae715..57dae081101 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1136,7 +1136,7 @@ cdef class FaceIterator_base(SageObject): :class:`sage.geometry.polyhedron.combinatorial_polyhedron.combinatorial_face.CombinatorialFace`. """ self.next_dimension() - if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): + if unlikely(self.structure.current_dimension > self.structure.highest_dimension): return None return CombinatorialFace(self) @@ -1534,7 +1534,7 @@ cdef class FaceIterator(FaceIterator_base): A 1-dimensional face of a 3-dimensional combinatorial polyhedron] """ cdef CombinatorialFace face = self.next_face() - if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): + if unlikely(self.structure.current_dimension > self.structure.highest_dimension): raise StopIteration return face @@ -1827,7 +1827,7 @@ cdef class FaceIterator_geom(FaceIterator_base): return PolyhedronFace(self.P, [], range(self.P.n_Hrepresentation())) self.next_dimension() - if unlikely(self.structure.current_dimension == self.structure.highest_dimension + 1): + if unlikely(self.structure.current_dimension > self.structure.highest_dimension): raise StopIteration return self.current() From b72578bee43e31df4edf99cd1f140a607c8d714c Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 11:47:47 +0200 Subject: [PATCH 250/280] fix error message in doctest --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 57dae081101..858a9311406 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1057,7 +1057,7 @@ cdef class FaceIterator_base(SageObject): sage: it.only_subfaces() Traceback (most recent call last): ... - ValueError: cannot visit subsets after ignoring a face + ValueError: cannot only visit subsets after ignoring a face """ if unlikely(self.dual): raise ValueError("only possible when not in dual mode") From ad75abb5acdf3ac6fbd6e652781e1fd555a5e6c2 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 12 May 2021 16:49:56 +0200 Subject: [PATCH 251/280] fix the order of the coatoms when resetting the face iterator --- .../face_iterator.pyx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 858a9311406..7a433794b65 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -339,6 +339,25 @@ cdef class FaceIterator_base(SageObject): sage: it.reset() sage: next(it).ambient_V_indices() (0, 3, 4, 5) + + TESTS: + + Resetting will fix the order of the coatoms after ``only_subsets``:: + + sage: P = polytopes.Birkhoff_polytope(3) + sage: C = P.combinatorial_polyhedron() + sage: it = C.face_iter(dual=False) + sage: face = next(it) + sage: face.ambient_H_indices() + (8,) + sage: face = next(it) + sage: face.ambient_H_indices() + (7,) + sage: it.only_subfaces() + sage: it.reset() + sage: face = next(it) + sage: face.ambient_H_indices() + (8,) """ if self.structure.dimension == 0 or self.coatoms.n_faces() == 0: # As we will only yield proper faces, @@ -360,6 +379,9 @@ cdef class FaceIterator_base(SageObject): self.structure.yet_to_visit = self.coatoms.n_faces() self.structure._index = 0 + # ``only_subsets`` might have messed up the coatoms. + face_list_shallow_copy(self.structure.new_faces[self.structure.dimension-1], self.coatoms.data) + def __next__(self): r""" Must be implemented by a derived class. From 3d158adfbc8eaa1fdc88559ea68a1debb37a317e Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 20 May 2021 09:35:23 +0200 Subject: [PATCH 252/280] do not display equations for doctests illustrating how the iterator works --- .../combinatorial_polyhedron/face_iterator.pyx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 7a433794b65..8af94d6966a 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -348,15 +348,15 @@ cdef class FaceIterator_base(SageObject): sage: C = P.combinatorial_polyhedron() sage: it = C.face_iter(dual=False) sage: face = next(it) - sage: face.ambient_H_indices() + sage: face.ambient_H_indices(add_equations=False) (8,) sage: face = next(it) - sage: face.ambient_H_indices() + sage: face.ambient_H_indices(add_equations=False) (7,) sage: it.only_subfaces() sage: it.reset() sage: face = next(it) - sage: face.ambient_H_indices() + sage: face.ambient_H_indices(add_equations=False) (8,) """ if self.structure.dimension == 0 or self.coatoms.n_faces() == 0: @@ -1046,9 +1046,9 @@ cdef class FaceIterator_base(SageObject): sage: P = polytopes.Birkhoff_polytope(4) sage: C = P.combinatorial_polyhedron() sage: it = C.face_iter() - sage: next(it).ambient_H_indices() + sage: next(it).ambient_H_indices(add_equations=False) (15,) - sage: next(it).ambient_H_indices() + sage: next(it).ambient_H_indices(add_equations=False) (14,) sage: it.only_subfaces() sage: all(14 in f.ambient_H_indices() for f in it) From 353313c3189b8d4da69980d0eba1cb9fcbc9ad3e Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 20 May 2021 09:38:03 +0200 Subject: [PATCH 253/280] update face status --- .../polyhedron/combinatorial_polyhedron/face_iterator.pxd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd index d53ac95fe28..f99e71b7013 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd @@ -9,7 +9,7 @@ from .combinatorial_face cimport CombinatorialFace cdef struct iter_s: bint dual # if 1, then iterate over dual Polyhedron face_t face # the current face of the iterator - int face_status # 0 not initialized, 1 initialized, 2 added to visited_all + int face_status # 0 not initialized, 1 initialized, 2 added to visited_all, 3 only visit subsets size_t *atom_rep # a place where atom-representaion of face will be stored size_t *coatom_rep # a place where coatom-representaion of face will be stored int current_dimension # dimension of current face, dual dimension if ``dual`` From 842fb8efea3c6c694a6469d19a52a5799a114d2c Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 20 May 2021 09:44:06 +0200 Subject: [PATCH 254/280] consume the iterator if running ignore_subsets after only_subsets --- .../combinatorial_polyhedron/face_iterator.pyx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 8af94d6966a..9a525bbb5a9 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -491,15 +491,14 @@ cdef class FaceIterator_base(SageObject): ... ValueError: only possible when not in dual mode - Cannot run ``ignore_subfaces`` after ``only_subfaces:: + Ignoring the same face as was requested to visit only consumes the iterator:: sage: it = C.face_iter(dual=False) sage: _ = next(it) sage: it.only_subfaces() sage: it.ignore_subfaces() - Traceback (most recent call last): - ... - ValueError: cannot ignore a face after setting iterator to only visit subsets + sage: list(it) + [] Face iterator must be set to a face first:: @@ -1009,7 +1008,10 @@ cdef class FaceIterator_base(SageObject): if unlikely(self.structure.face_status == 0): raise ValueError("iterator not set to a face yet") if unlikely(self.structure.face_status == 3): - raise ValueError("cannot ignore a face after setting iterator to only visit subsets") + # The iterator is consumed, if it was just set to visit only subsets + # next thing to ignore subsets. + self.structure.current_dimension = self.structure.dimension + return 0 if unlikely(self.structure.face_status == 2): # Nothing to do. return 0 From 250f0e41942ef94855d4d4391e7467cd191d2754 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 20 May 2021 09:53:01 +0200 Subject: [PATCH 255/280] specify for `find_face` that we assume the iterator to be clean --- .../polyhedron/combinatorial_polyhedron/face_iterator.pyx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx index 9a525bbb5a9..2757ac73c41 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx @@ -1227,6 +1227,10 @@ cdef class FaceIterator_base(SageObject): Iterate until the current face is ``face``. The value can then be obtained with :meth:`current`. + + The iterator is assumed to be newly initialized or reset. + See :meth:`FaceIterator_base._join_of_atoms` and + :meth:`FaceIterator_base._meet_of_coatoms`. """ cdef size_t n_atoms = face_len_atoms(face) From 3f1d44a5e7ef3f97b1cfce97c6413dd64bd837d3 Mon Sep 17 00:00:00 2001 From: Marius Gerbershagen Date: Sun, 6 Jun 2021 20:05:04 +0200 Subject: [PATCH 256/280] Use different names in conversion of derivatives of symbolic functions These are less likely to clash with user-defined variables and lead to bugs such as sage: var('x t0') sage: function('f') sage: diff(f(x,1 + t0),x).subs({x:t0}).simplify_full() D[0](f)(t0, t0 + 1) + 4*D[1](f)(t0, t0 + 1) Note that we cannot use newly generated unique names here (such as those obtained from SR.symbol()), because multiple conversions of the same expression must be equal. --- src/sage/interfaces/maxima_abstract.py | 2 +- src/sage/interfaces/maxima_lib.py | 10 +++++--- src/sage/symbolic/expression_conversions.py | 28 +++++++++++++-------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/sage/interfaces/maxima_abstract.py b/src/sage/interfaces/maxima_abstract.py index 1200c2fa0c2..d8102801ba8 100644 --- a/src/sage/interfaces/maxima_abstract.py +++ b/src/sage/interfaces/maxima_abstract.py @@ -1766,7 +1766,7 @@ def _latex_(self): sage: y,d = var('y,d') sage: f = function('f') sage: latex(maxima(derivative(f(x*y), x))) - \left(\left.{{{\it \partial}}\over{{\it \partial}\, {\it t}_{0}}}\,f\left({\it t}_{0}\right) \right|_{{\it t}_{0}={\it x}\, {\it y}}\right)\,{\it y} + \left(\left.{{{\it \partial}}\over{{\it \partial}\, {\it \_symbol}_{0}}}\,f\left( {\it \_symbol}_{0}\right)\right|_{ {\it \_symbol}_{0}={\it x}\, {\it y}}\right)\,{\it y} sage: latex(maxima(derivative(f(x,y,d), d,x,x,y))) {{{\it \partial}^4}\over{{\it \partial}\,{\it d}\, {\it \partial}\,{\it x}^2\,{\it \partial}\, {\it y}}}\,f\left({\it x} , {\it y} , {\it d}\right) sage: latex(maxima(d/(d-2))) diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 40367c52426..7066b10da66 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -1581,10 +1581,12 @@ def sr_to_max(expr): # An evaluated derivative of the form f'(1) is not a # symbolic variable, yet we would like to treat it # like one. So, we replace the argument `1` with a - # temporary variable e.g. `t0` and then evaluate the - # derivative f'(t0) symbolically at t0=1. See trac - # #12796. - temp_args = [SR.var("t%s"%i) for i in range(len(args))] + # temporary variable e.g. `_symbol0` and then evaluate + # the derivative f'(_symbol0) symbolically at + # _symbol0=1. See trac #12796. Note that we cannot use + # SR.temp_var here since two conversions of the same + # expression have to be equal. + temp_args = [SR.symbol("_symbol%s"%i) for i in range(len(args))] f = sr_to_max(op.function()(*temp_args)) params = op.parameter_set() deriv_max = [[mdiff],f] diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index 9cf695317fc..8168b86bc7f 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -509,7 +509,7 @@ def derivative(self, ex, operator): sage: t D[0](f)(x*y) sage: m.derivative(t, t.operator()) - "at(diff('f(_SAGE_VAR_t0), _SAGE_VAR_t0, 1), [_SAGE_VAR_t0 = (_SAGE_VAR_x)*(_SAGE_VAR_y)])" + "at(diff('f(_SAGE_VAR__symbol0), _SAGE_VAR__symbol0, 1), [_SAGE_VAR__symbol0 = (_SAGE_VAR_x)*(_SAGE_VAR_y)])" TESTS: @@ -533,7 +533,7 @@ def derivative(self, ex, operator): sage: a = df.subs(x=exp(x)); a D[0](f)(e^x) sage: b = maxima(a); b - %at('diff('f(_SAGE_VAR_t0),_SAGE_VAR_t0,1),_SAGE_VAR_t0=%e^_SAGE_VAR_x) + %at('diff('f(_SAGE_VAR__symbol0),_SAGE_VAR__symbol0,1),_SAGE_VAR__symbol0=%e^_SAGE_VAR_x) sage: bool(b.sage() == a) True @@ -542,7 +542,7 @@ def derivative(self, ex, operator): sage: a = df.subs(x=4); a D[0](f)(4) sage: b = maxima(a); b - %at('diff('f(_SAGE_VAR_t0),_SAGE_VAR_t0,1),_SAGE_VAR_t0=4) + %at('diff('f(_SAGE_VAR__symbol0),_SAGE_VAR__symbol0,1),_SAGE_VAR__symbol0=4) sage: bool(b.sage() == a) True @@ -562,7 +562,7 @@ def derivative(self, ex, operator): sage: a = f_x.subs(x=4); a D[0](f)(4, y) sage: b = maxima(a); b - %at('diff('f(_SAGE_VAR_t0,_SAGE_VAR_y),_SAGE_VAR_t0,1),_SAGE_VAR_t0=4) + %at('diff('f(_SAGE_VAR__symbol0,_SAGE_VAR_y),_SAGE_VAR__symbol0,1),_SAGE_VAR__symbol0=4) sage: bool(b.sage() == a) True @@ -571,7 +571,7 @@ def derivative(self, ex, operator): sage: a = f_x.subs(x=4).subs(y=8); a D[0](f)(4, 8) sage: b = maxima(a); b - %at('diff('f(_SAGE_VAR_t0,8),_SAGE_VAR_t0,1),_SAGE_VAR_t0=4) + %at('diff('f(_SAGE_VAR__symbol0,8),_SAGE_VAR__symbol0,1),_SAGE_VAR__symbol0=4) sage: bool(b.sage() == a) True @@ -594,9 +594,12 @@ def derivative(self, ex, operator): # An evaluated derivative of the form f'(1) is not a # symbolic variable, yet we would like to treat it like # one. So, we replace the argument `1` with a temporary - # variable e.g. `t0` and then evaluate the derivative - # f'(t0) symbolically at t0=1. See trac #12796. - temp_args = [SR.var("t%s"%i) for i in range(len(args))] + # variable e.g. `_symbol0` and then evaluate the + # derivative f'(_symbol0) symbolically at _symbol0=1. See + # trac #12796. Note that we cannot use SR.temp_var here + # since two conversions of the same expression have to be + # equal. + temp_args = [SR.symbol("_symbol%s"%i) for i in range(len(args))] f = operator.function()(*temp_args) params = operator.parameter_set() params = ["%s, %s"%(temp_args[i]._maxima_init_(), params.count(i)) for i in set(params)] @@ -1002,9 +1005,12 @@ def derivative(self, ex, operator): # An evaluated derivative of the form f'(1) is not a # symbolic variable, yet we would like to treat it like # one. So, we replace the argument `1` with a temporary - # variable e.g. `t0` and then evaluate the derivative - # f'(t0) symbolically at t0=1. See trac #12796. - temp_args = [SR.var("t%s" % i) for i in range(len(args))] + # variable e.g. `_symbol0` and then evaluate the + # derivative f'(_symbol0) symbolically at _symbol0=1. See + # trac #12796. Note that we cannot use SR.temp_var here + # since two conversions of the same expression have to be + # equal. + temp_args = [SR.symbol("_symbol%s" % i) for i in range(len(args))] f = operator.function()(*temp_args) vars = ",".join(temp_args[i]._fricas_init_() for i in params_set) subs = ",".join("%s = %s" % (t._fricas_init_(), a._fricas_init_()) From 30c17886739e1277e5d7a117c8b41529b27c10d6 Mon Sep 17 00:00:00 2001 From: Marius Gerbershagen Date: Sun, 6 Jun 2021 20:12:35 +0200 Subject: [PATCH 257/280] Introduce a mechanism for creating temporary variables with unique names In various places in sagemath, temporary symbolic variables are needed. Previously, these variables were created in an ad-hoc manner and could clash with user-defined variables. This commit introduces a unified mechanism for creating these variables and cleaning up afterwards (removing the variable from the list of symbols in the symbolic ring and forgetting any assumptions about it). --- src/sage/calculus/desolvers.py | 203 +++++++++--------- .../complex_dynamics/mandel_julia_helper.pyx | 7 +- src/sage/functions/piecewise.py | 22 +- .../parametrized_surface3d.py | 46 ++-- src/sage/libs/pynac/pynac.pxd | 1 + src/sage/manifolds/chart.py | 11 +- src/sage/manifolds/continuous_map.py | 3 +- .../differentiable/characteristic_class.py | 40 ++-- ...otics_multivariate_generating_functions.py | 7 +- src/sage/symbolic/expression.pyx | 61 ++++-- src/sage/symbolic/operators.py | 2 +- src/sage/symbolic/ring.pyx | 111 +++++++++- 12 files changed, 323 insertions(+), 191 deletions(-) diff --git a/src/sage/calculus/desolvers.py b/src/sage/calculus/desolvers.py index ea0688c80c0..b180e504016 100644 --- a/src/sage/calculus/desolvers.py +++ b/src/sage/calculus/desolvers.py @@ -78,7 +78,7 @@ from sage.interfaces.maxima import Maxima from sage.plot.all import line from sage.symbolic.expression import is_SymbolicEquation -from sage.symbolic.ring import is_SymbolicVariable +from sage.symbolic.ring import SR, is_SymbolicVariable from sage.calculus.functional import diff from sage.misc.functional import N from sage.rings.real_mpfr import RealField @@ -1351,6 +1351,9 @@ def desolve_rk4(de, dvar, ics=None, ivar=None, end_points=None, step=0.1, output if ics is None: raise ValueError("No initial conditions, specify with ics=[x0,y0].") + if output not in ['list', 'plot', 'slope_field']: + raise ValueError("Option output should be 'list', 'plot' or 'slope_field'.") + if ivar is None: ivars = de.variables() ivars = [t for t in ivars if t != dvar] @@ -1358,66 +1361,65 @@ def desolve_rk4(de, dvar, ics=None, ivar=None, end_points=None, step=0.1, output raise ValueError("Unable to determine independent variable, please specify.") ivar = ivars[0] + step=abs(step) + + def desolve_rk4_inner(de, dvar): + de0=de._maxima_() + maxima("load('dynamics)") + lower_bound,upper_bound=desolve_rk4_determine_bounds(ics,end_points) + sol_1, sol_2 = [],[] + if lower_boundics[0]: + cmd="rk(%s,%s,%s,[%s,%s,%s,%s])\ + "%(de0.str(),'_SAGE_VAR_'+str(dvar),str(ics[1]),'_SAGE_VAR_'+str(ivar),str(ics[0]),upper_bound,step) + sol_2=maxima(cmd).sage() + sol_2.pop(0) + sol=sol_1 + sol.extend([[ics[0],ics[1]]]) + sol.extend(sol_2) + + if output == 'list': + return sol + from sage.plot.plot import list_plot + from sage.plot.plot_field import plot_slope_field + R = list_plot(sol, plotjoined=True, **kwds) + if output == 'plot': + return R + if output == 'slope_field': + XMIN = sol[0][0] + YMIN = sol[0][1] + XMAX = XMIN + YMAX = YMIN + for s, t in sol: + if s > XMAX: + XMAX = s + if s < XMIN: + XMIN = s + if t > YMAX: + YMAX = t + if t < YMIN: + YMIN = t + return plot_slope_field(de, (ivar,XMIN,XMAX), (dvar,YMIN,YMAX))+R + if not is_SymbolicVariable(dvar): from sage.symbolic.ring import SR from sage.calculus.all import diff from sage.symbolic.relation import solve if is_SymbolicEquation(de): de = de.lhs() - de.rhs() - dummy_dvar = SR.var('dummy_dvar') # consider to add warning if the solution is not unique de=solve(de,diff(dvar,ivar),solution_dict=True) if len(de) != 1: raise NotImplementedError("Sorry, cannot find explicit formula for right-hand side of the ODE.") - de=de[0][diff(dvar,ivar)].subs(dvar==dummy_dvar) + with SR.temp_var() as dummy_dvar: + return desolve_rk4_inner(de[0][diff(dvar,ivar)].subs({dvar:dummy_dvar}), dummy_dvar) else: - dummy_dvar=dvar - - step=abs(step) - de0=de._maxima_() - maxima("load('dynamics)") - lower_bound,upper_bound=desolve_rk4_determine_bounds(ics,end_points) - sol_1, sol_2 = [],[] - if lower_boundics[0]: - cmd="rk(%s,%s,%s,[%s,%s,%s,%s])\ - "%(de0.str(),'_SAGE_VAR_'+str(dummy_dvar),str(ics[1]),'_SAGE_VAR_'+str(ivar),str(ics[0]),upper_bound,step) - sol_2=maxima(cmd).sage() - sol_2.pop(0) - sol=sol_1 - sol.extend([[ics[0],ics[1]]]) - sol.extend(sol_2) - - if output == 'list': - return sol - from sage.plot.plot import list_plot - from sage.plot.plot_field import plot_slope_field - R = list_plot(sol, plotjoined=True, **kwds) - if output == 'plot': - return R - if output == 'slope_field': - XMIN = sol[0][0] - YMIN = sol[0][1] - XMAX = XMIN - YMAX = YMIN - for s, t in sol: - if s > XMAX: - XMAX = s - if s < XMIN: - XMIN = s - if t > YMAX: - YMAX = t - if t < YMIN: - YMIN = t - return plot_slope_field(de, (ivar,XMIN,XMAX), (dummy_dvar,YMIN,YMAX))+R - - raise ValueError("Option output should be 'list', 'plot' or 'slope_field'.") - + return desolve_rk4_inner(de, dvar) def desolve_system_rk4(des, vars, ics=None, ivar=None, end_points=None, step=0.1): r""" @@ -1655,6 +1657,49 @@ def desolve_odeint(des, ics, times, dvars, ivar=None, compute_jac=False, args=() from sage.ext.fast_eval import fast_float from sage.calculus.functions import jacobian + def desolve_odeint_inner(ivar): + # one-dimensional systems: + if is_SymbolicVariable(dvars): + func = fast_float(des, dvars, ivar) + if not compute_jac: + Dfun = None + else: + J = diff(des, dvars) + J = fast_float(J, dvars, ivar) + + def Dfun(y, t): + return [J(y, t)] + + # n-dimensional systems: + else: + desc = [] + variabs = dvars[:] + variabs.append(ivar) + for de in des: + desc.append(fast_float(de,*variabs)) + + def func(y,t): + v = list(y[:]) + v.append(t) + return [dec(*v) for dec in desc] + + if not compute_jac: + Dfun=None + else: + J = jacobian(des,dvars) + J = [list(v) for v in J] + J = fast_float(J,*variabs) + def Dfun(y,t): + v = list(y[:]) + v.append(t) + return [[element(*v) for element in row] for row in J] + + + sol=odeint(func, ics, times, args=args, Dfun=Dfun, rtol=rtol, atol=atol, + tcrit=tcrit, h0=h0, hmax=hmax, hmin=hmin, ixpr=ixpr, mxstep=mxstep, + mxhnil=mxhnil, mxordn=mxordn, mxords=mxords, printmessg=printmessg) + return sol + if ivar is None: if len(dvars)==0 or len(dvars)==1: if len(dvars)==1: @@ -1671,59 +1716,17 @@ def desolve_odeint(des, ics, times, dvars, ivar=None, compute_jac=False, args=() ivars = all_vars - set(dvars) if len(ivars)==1: - ivar = ivars.pop() + return desolve_odeint_inner(ivars[0]) elif not ivars: - try: - safe_names = [ 't_' + str(dvar) for dvar in dvars ] - except TypeError: # not iterable - safe_names = [ 't_' + str(dvars) ] - from sage.symbolic.ring import SR - ivar = [SR.var(name) for name in safe_names] + if is_SymbolicVariable(dvars): + with SR.temp_var() as ivar: + return desolve_odeint_inner(ivar) + else: + with SR.temp_var(n=len(dvars)) as ivar: + return desolve_odeint_inner(ivar) else: raise ValueError("Unable to determine independent variable, please specify.") - - # one-dimensional systems: - if is_SymbolicVariable(dvars): - func = fast_float(des, dvars, ivar) - if not compute_jac: - Dfun = None - else: - J = diff(des, dvars) - J = fast_float(J, dvars, ivar) - - def Dfun(y, t): - return [J(y, t)] - - # n-dimensional systems: - else: - desc = [] - variabs = dvars[:] - variabs.append(ivar) - for de in des: - desc.append(fast_float(de,*variabs)) - - def func(y,t): - v = list(y[:]) - v.append(t) - return [dec(*v) for dec in desc] - - if not compute_jac: - Dfun=None - else: - J = jacobian(des,dvars) - J = [list(v) for v in J] - J = fast_float(J,*variabs) - def Dfun(y,t): - v = list(y[:]) - v.append(t) - return [[element(*v) for element in row] for row in J] - - - sol=odeint(func, ics, times, args=args, Dfun=Dfun, rtol=rtol, atol=atol, - tcrit=tcrit, h0=h0, hmax=hmax, hmin=hmin, ixpr=ixpr, mxstep=mxstep, - mxhnil=mxhnil, mxordn=mxordn, mxords=mxords, printmessg=printmessg) - - return sol + return desolve_odeint_inner(ivar) def desolve_mintides(f, ics, initial, final, delta, tolrel=1e-16, tolabs=1e-16): r""" diff --git a/src/sage/dynamics/complex_dynamics/mandel_julia_helper.pyx b/src/sage/dynamics/complex_dynamics/mandel_julia_helper.pyx index 7cff9ac6347..7a6ee5aa51a 100644 --- a/src/sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +++ b/src/sage/dynamics/complex_dynamics/mandel_julia_helper.pyx @@ -34,6 +34,7 @@ from sage.rings.real_double import RDF from sage.rings.complex_double import CDF from sage.ext.fast_callable import fast_callable from sage.calculus.all import symbolic_expression +from sage.symbolic.ring import SR from sage.calculus.var import var from sage.rings.fraction_field import is_FractionField from sage.categories.function_fields import FunctionFields @@ -843,9 +844,9 @@ cpdef polynomial_mandelbrot(f, parameter=None, double x_center=0, # critical points for each c. else: # Solve for critical points symbollically. - w = var('w') - df = f.derivative(z).polynomial(z).subs({z:w}) - critical_pts = solve(symbolic_expression(df)==0, w) + with SR.temp_var() as w: + df = f.derivative(z).polynomial(z).subs({z:w}) + critical_pts = solve(symbolic_expression(df)==0, w) c_pts = [] for pt in critical_pts: c_pts.append(fast_callable(pt.rhs(), vars=[c], domain=CDF)) diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index 41f5876616c..37b2e2fc727 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -946,8 +946,6 @@ def convolution(self, parameters, variable, other): g = other if len(f.end_points())*len(g.end_points()) == 0: raise ValueError('one of the piecewise functions is nowhere defined') - tt = SR.var('tt') - uu = SR.var('uu') fd, f0 = parameters[0] gd, g0 = next(other.items()) if len(f)==1 and len(g)==1: @@ -957,12 +955,14 @@ def convolution(self, parameters, variable, other): a2 = fd[0].upper() b1 = gd[0].lower() b2 = gd[0].upper() - i1 = f0.subs({variable: uu}) - i2 = g0.subs({variable: tt-uu}) - fg1 = definite_integral(i1*i2, uu, a1, tt-b1).subs(tt = variable) - fg2 = definite_integral(i1*i2, uu, tt-b2, tt-b1).subs(tt = variable) - fg3 = definite_integral(i1*i2, uu, tt-b2, a2).subs(tt = variable) - fg4 = definite_integral(i1*i2, uu, a1, a2).subs(tt = variable) + with SR.temp_var() as tt: + with SR.temp_var() as uu: + i1 = f0.subs({variable: uu}) + i2 = g0.subs({variable: tt-uu}) + fg1 = definite_integral(i1*i2, uu, a1, tt-b1).subs({tt:variable}) + fg2 = definite_integral(i1*i2, uu, tt-b2, tt-b1).subs({tt:variable}) + fg3 = definite_integral(i1*i2, uu, tt-b2, a2).subs({tt:variable}) + fg4 = definite_integral(i1*i2, uu, a1, a2).subs({tt:variable}) if a1-b1": pass diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index 5a2b7070867..56b49bba55f 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -3162,7 +3162,6 @@ def inverse(self): """ from sage.symbolic.relation import solve - from sage.symbolic.assumptions import assumptions if self._inverse is not None: return self._inverse # The computation is necessary: @@ -3186,8 +3185,7 @@ def inverse(self): for i in range(n2): if x2[i].is_positive(): coord_domain[i] = 'positive' - xp2 = [ SR.var('xxxx' + str(i), domain=coord_domain[i]) - for i in range(n2) ] + xp2 = [ SR.temp_var(domain=coord_domain[i]) for i in range(n2) ] xx2 = self._transf.expr() equations = [xp2[i] == xx2[i] for i in range(n2)] try: @@ -3240,12 +3238,7 @@ def inverse(self): "manually") x2_to_x1 = list_x2_to_x1[0] self._inverse = type(self)(self._chart2, self._chart1, *x2_to_x1) - # Some cleaning: the local symbolic variables (xxxx0, xxxx1, ...) are - # removed from the list of assumptions - for asm in assumptions(): - for xxxx in xp2: - if asm.has(xxxx): - asm.forget() + SR.cleanup_var(xp2) return self._inverse def set_inverse(self, *transformations, **kwds): diff --git a/src/sage/manifolds/continuous_map.py b/src/sage/manifolds/continuous_map.py index 7991191b900..04a29d9f4f0 100644 --- a/src/sage/manifolds/continuous_map.py +++ b/src/sage/manifolds/continuous_map.py @@ -1963,7 +1963,7 @@ def __invert__(self): n2 = len(chart2._xx) # New symbolic variables (different from chart2._xx to allow for a # correct solution even when chart2 = chart1): - x2 = [SR.var('xxxx' + str(i)) for i in range(n2)] + x2 = SR.temp_var(n=n2) equations = [x2[i] == coord_map._functions[i].expr() for i in range(n2)] solutions = solve(equations, chart1._xx, solution_dict=True) @@ -1983,6 +1983,7 @@ def __invert__(self): except AttributeError: pass coord_functions[(chart2, chart1)] = inv_functions + SR.cleanup_var(x2) if self._name is None: name = None else: diff --git a/src/sage/manifolds/differentiable/characteristic_class.py b/src/sage/manifolds/differentiable/characteristic_class.py index f4155b2577c..dfa68faa604 100644 --- a/src/sage/manifolds/differentiable/characteristic_class.py +++ b/src/sage/manifolds/differentiable/characteristic_class.py @@ -498,28 +498,28 @@ def _get_coeff_list(self, distinct_real=True): pow_range = self._base_space._dim // 2 def_var = self._func.default_variable() # Use a complex variable without affecting the old one: - new_var = SR.symbol('x_char_class_', domain='complex') - if self._vbundle._field_type == 'real' and distinct_real: - if self._class_type == 'additive': - func = self._func.subs({def_var: new_var ** 2}) / 2 - elif self._class_type == 'multiplicative': - # This could case problems in the real domain, where sqrt(x^2) - # is simplified to |x|. However, the variable must be complex - # anyway. - func = self._func.subs({def_var : new_var**2}).sqrt() - elif self._class_type == 'Pfaffian': - # There are no canonical Pfaffian classes, however, consider the - # projection onto the odd part of the function to keep the - # matrices skew: - func = (self._func.subs({def_var: new_var}) - - self._func.subs({def_var: -new_var})) / 2 - else: - func = self._func.subs({def_var: new_var}) + with SR.temp_var(domain='complex') as new_var: + if self._vbundle._field_type == 'real' and distinct_real: + if self._class_type == 'additive': + func = self._func.subs({def_var: new_var ** 2}) / 2 + elif self._class_type == 'multiplicative': + # This could case problems in the real domain, where sqrt(x^2) + # is simplified to |x|. However, the variable must be complex + # anyway. + func = self._func.subs({def_var : new_var**2}).sqrt() + elif self._class_type == 'Pfaffian': + # There are no canonical Pfaffian classes, however, consider the + # projection onto the odd part of the function to keep the + # matrices skew: + func = (self._func.subs({def_var: new_var}) - + self._func.subs({def_var: -new_var})) / 2 + else: + func = self._func.subs({def_var: new_var}) - if self._vbundle._field_type == 'real' and not distinct_real: - pow_range = pow_range // 2 + if self._vbundle._field_type == 'real' and not distinct_real: + pow_range = pow_range // 2 - return func.taylor(new_var, 0, pow_range).coefficients(sparse=False) + return func.taylor(new_var, 0, pow_range).coefficients(sparse=False) def _init_derived(self): r""" diff --git a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py index 1975374e2ac..9bd8b03df28 100644 --- a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +++ b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py @@ -3681,6 +3681,7 @@ def diff_prod(f_derivs, u, g, X, interval, end, uderivs, atc): D = {} rhs = [] lhs = [] + new_vars = [] for t in combinations_with_replacement(X, l): t = list(t) s = t + end @@ -3689,13 +3690,15 @@ def diff_prod(f_derivs, u, g, X, interval, end, uderivs, atc): # Since Sage's solve command can't take derivatives as variable # names, make new variables based on t to stand in for # diff(u, t) and store them in D. - D[diff(u, t).subs(atc)] = var('zing' + - ''.join(str(x) for x in t)) + new_var = SR.temp_var() + new_vars.append(new_var) + D[diff(u, t).subs(atc)] = new_var eqns = [lhs[i] == rhs[i].subs(uderivs).subs(D) for i in range(len(lhs))] variables = D.values() sol = solve(eqns, *variables, solution_dict=True) uderivs.update(subs_all(D, sol[ZZ.zero()])) + SR.cleanup_var(new_vars) return uderivs diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index f81c4ff9717..d43f288a262 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -767,6 +767,29 @@ cdef class Expression(CommutativeRingElement): """ return new_Expression_from_GEx(self._parent, self._gobj) + def __enter__(self): + """ + Method used by temporary variables with Python `with` to + automatically clean up after themselves. + """ + return self + + def __exit__(self, *args): + """ + Method used by temporary variables with Python `with` to + automatically clean up after themselves. + + TESTS:: + + sage: symbols_copy = SR.symbols.copy() + sage: with SR.temp_var() as t: pass + sage: symbols_copy == SR.symbols + True + + """ + SR.cleanup_var(self) + return False + def _repr_(self): r""" Return string representation of this symbolic expression. @@ -12885,25 +12908,25 @@ cdef class Expression(CommutativeRingElement): if not self.has(Y): raise ValueError("Expression {} contains no {} terms".format(self, Y)) - x = SR.symbol() - yy = SR.symbol() - y = SymbolicFunction('y', 1)(x) - f = SymbolicFunction('f', 2)(x, yy) - Fx = f.diff(x) - Fy = f.diff(yy) - G = -(Fx/Fy) - G = G.subs({yy: y}) - di = {y.diff(x): -self.diff(X)/self.diff(Y)} - R = G - S = G.diff(x, n - 1) - for i in range(n + 1): - di[y.diff(x, i + 1).subs({x: x})] = R - S = S.subs(di) - R = G.diff(x, i) - for j in range(n + 1 - i): - di[f.diff(x, i, yy, j).subs({x: x, yy: y})] = self.diff(X, i, Y, j) - S = S.subs(di) - return S + with SR.temp_var() as x: + with SR.temp_var() as yy: + y = SymbolicFunction('y', 1)(x) + f = SymbolicFunction('f', 2)(x, yy) + Fx = f.diff(x) + Fy = f.diff(yy) + G = -(Fx/Fy) + G = G.subs({yy: y}) + di = {y.diff(x): -self.diff(X)/self.diff(Y)} + R = G + S = G.diff(x, n - 1) + for i in range(n + 1): + di[y.diff(x, i + 1).subs({x: x})] = R + S = S.subs(di) + R = G.diff(x, i) + for j in range(n + 1 - i): + di[f.diff(x, i, yy, j).subs({x: x, yy: y})] = self.diff(X, i, Y, j) + S = S.subs(di) + return S def solve_diophantine(f, *args, **kwds): """ diff --git a/src/sage/symbolic/operators.py b/src/sage/symbolic/operators.py index 368d6f68f89..2fa839149dc 100644 --- a/src/sage/symbolic/operators.py +++ b/src/sage/symbolic/operators.py @@ -114,7 +114,7 @@ def __call__(self, *args): # temporary variable e.g. `t0` and then evaluate the # derivative f'(t0) symbolically at t0=1. See trac # #12796. - temp_args=[SR.var("t%s"%i) for i in range(len(args))] + temp_args=SR.temp_var(n=len(args)) vars=[temp_args[i] for i in self._parameter_set] return self._f(*temp_args).diff(*vars).function(*temp_args)(*args) vars = [args[i] for i in self._parameter_set] diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 692266db00c..47c2fe55d99 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -730,6 +730,7 @@ cdef class SymbolicRing(CommutativeRing): if name is None: # Check if we need a temporary anonymous new symbol symb = ginac_new_symbol() + name = symb.get_name().decode('ascii') if domain is not None: symb.set_domain(sage_domain_to_ginac_domain(domain)) else: @@ -741,14 +742,102 @@ cdef class SymbolicRing(CommutativeRing): ginac_domain = domain_complex symb = ginac_symbol(str_to_bytes(name), str_to_bytes(latex_name), ginac_domain) - self.symbols[name] = e e._gobj = GEx(symb) + self.symbols[name] = e if domain is not None: send_sage_domain_to_maxima(e, domain) return e + def temp_var(self, n=None, domain=None): + """ + Return one or multiple new unique symbolic variables as an element + of the symbolic ring. Use this instead of SR.var() if there is a + possibility of name clashes occuring. Call SR.cleanup_var() once + the variables are no longer needed or use a `with SR.temp_var() + as ...` construct. + + INPUT: + + - ``n`` -- (optional) positive integer; number of symbolic variables + + - ``domain`` -- (optional) specify the domain of the variable(s); + + EXAMPLES: + + Simple definition of a functional derivative:: + + sage: def functional_derivative(expr,f,x): + ....: with SR.temp_var() as a: + ....: return expr.subs({f(x):a}).diff(a).subs({a:f(x)}) + sage: f = function('f') + sage: a = var('a') + sage: functional_derivative(f(a)^2+a,f,a) + 2*f(a) + + Contrast this to a similar implementation using SR.var(), + which gives a wrong result in our example:: + + sage: def functional_derivative(expr,f,x): + ....: a = SR.var('a') + ....: return expr.subs({f(x):a}).diff(a).subs({a:f(x)}) + sage: f = function('f') + sage: a = var('a') + sage: functional_derivative(f(a)^2+a,f,a) + 2*f(a) + 1 + + TESTS: + + sage: x = SR.temp_var() + sage: y = SR.temp_var() + sage: bool(x == x) + True + sage: bool(x == y) + False + sage: bool(x.parent()(x._maxima_()) == x) + True + + """ + if (n == None): + return self.symbol(None, domain=domain) + return TemporaryVariables([self.temp_var(domain=domain) for i in range(n)]) + + def cleanup_var(self, symbol): + """ + Cleans up a variable, removing assumptions about the + variable and allowing for it to be garbage collected + + INPUT: + + - ``symbol`` -- a variable or a list of variables + + TESTS: + + sage: from sage.symbolic.assumptions import assumptions + sage: symbols_copy = SR.symbols.copy() + sage: assumptions_copy = assumptions().copy() + sage: x = SR.temp_var(domain='real') + sage: SR.cleanup_var(x) + sage: symbols_copy == SR.symbols + True + sage: assumptions_copy == assumptions() + True + """ + from sage.symbolic.assumptions import assumptions + if isinstance(symbol,list) or isinstance(symbol,tuple): + for s in symbol: + self.cleanup_var(s) + else: + try: + name = self._repr_element_(symbol) + del self.symbols[name] + except KeyError: + pass + for asm in assumptions(): + if asm.has(symbol): + asm.forget() + def var(self, name, latex_name=None, n=None, domain=None): """ Return a symbolic variable as an element of the symbolic ring. @@ -1424,3 +1513,23 @@ def isidentifier(x): if x in KEYWORDS: return False return x.isidentifier() + +class TemporaryVariables(tuple): + """ + Instances of this class can be used with Python `with` to + automatically clean up after themselves. + """ + def __enter__(self): + return self + + def __exit__(self, *args): + """ + TESTS:: + + sage: symbols_copy = SR.symbols.copy() + sage: with SR.temp_var(n=2) as temp_vars: pass + sage: symbols_copy == SR.symbols + True + """ + SR.cleanup_var(self) + return False From efc463a31d42afffcf3585f872d03630d22324fc Mon Sep 17 00:00:00 2001 From: Marius Gerbershagen Date: Sun, 6 Jun 2021 20:35:48 +0200 Subject: [PATCH 258/280] Warn on potential name clashes in SR.var() --- src/sage/symbolic/ring.pyx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 47c2fe55d99..337e11e192a 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -977,6 +977,15 @@ cdef class SymbolicRing(CommutativeRing): for s in names_list: if not isidentifier(s): raise ValueError(f'The name "{s}" is not a valid Python identifier.') + # warn on bad symbol names, but only once + # symbol... names are temporary variables created with + # SR.temp_var + # _symbol... names are used in the conversion of + # derivatives of symbolic functions to maxima and other + # external libraries + if self.symbols.get(s) is None and ((s.startswith('symbol') and s[6:].isdigit()) or (s.startswith('_symbol') and s[7:].isdigit())): + import warnings + warnings.warn(f'The name "{name}" may clash with names used internally in sagemath. It is recommended to choose a different name for your variable.') formatted_latex_name = None if latex_name is not None and n is None: From 011f59a15f0d7f59e92c91655a8855aafa3c2b97 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 20 Jun 2021 08:23:30 -0700 Subject: [PATCH 259/280] src/sage/docs/conf.py: Add more \DeclareUnicodeCharacter --- src/sage/docs/conf.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/sage/docs/conf.py b/src/sage/docs/conf.py index d249f2cdafe..04056fef012 100644 --- a/src/sage/docs/conf.py +++ b/src/sage/docs/conf.py @@ -400,18 +400,29 @@ def set_intersphinx_mappings(app, config): \DeclareUnicodeCharacter{03A5}{\ensuremath{\Upsilon}} \DeclareUnicodeCharacter{2113}{\ell} - \DeclareUnicodeCharacter{221A}{\ensuremath{\sqrt{}}} - \DeclareUnicodeCharacter{2264}{\leq} - \DeclareUnicodeCharacter{2265}{\geq} - \DeclareUnicodeCharacter{221E}{\infty} - \DeclareUnicodeCharacter{2211}{\sum} + \DeclareUnicodeCharacter{2148}{\id} + \DeclareUnicodeCharacter{2202}{\partial} + \DeclareUnicodeCharacter{2205}{\ensuremath{\emptyset}} \DeclareUnicodeCharacter{2208}{\in} \DeclareUnicodeCharacter{2209}{\notin} - \DeclareUnicodeCharacter{2202}{\partial} + \DeclareUnicodeCharacter{2211}{\sum} + \DeclareUnicodeCharacter{221A}{\ensuremath{\sqrt{}}} + \DeclareUnicodeCharacter{221E}{\infty} + \DeclareUnicodeCharacter{2227}{\ensuremath{\wedge}} + \DeclareUnicodeCharacter{2228}{\ensuremath{\vee}} + \DeclareUnicodeCharacter{2229}{\ensuremath{\cap}} + \DeclareUnicodeCharacter{222A}{\ensuremath{\cup}} \DeclareUnicodeCharacter{222B}{\ensuremath{\int}} - \DeclareUnicodeCharacter{2148}{\id} \DeclareUnicodeCharacter{2248}{\approx} \DeclareUnicodeCharacter{2260}{\neq} + \DeclareUnicodeCharacter{2264}{\leq} + \DeclareUnicodeCharacter{2265}{\geq} + \DeclareUnicodeCharacter{2293}{\ensuremath{\sqcap}} + \DeclareUnicodeCharacter{2294}{\ensuremath{\sqcup}} + \DeclareUnicodeCharacter{22C0}{\ensuremath{\bigwedge}} + \DeclareUnicodeCharacter{22C1}{\ensuremath{\bigvee}} + \DeclareUnicodeCharacter{22C2}{\ensuremath{\bigcap}} + \DeclareUnicodeCharacter{22C3}{\ensuremath{\bigcup}} \DeclareUnicodeCharacter{00B1}{\pm} \DeclareUnicodeCharacter{2A02}{\otimes} \DeclareUnicodeCharacter{2A01}{\oplus} @@ -449,6 +460,8 @@ def set_intersphinx_mappings(app, config): \DeclareUnicodeCharacter{2089}{\ensuremath{{}_9}} \DeclareUnicodeCharacter{208A}{\ensuremath{{}_+}} \DeclareUnicodeCharacter{208B}{\ensuremath{{}_-}} + \DeclareUnicodeCharacter{1D62}{\ensuremath{{}_i}} + \DeclareUnicodeCharacter{2C7C}{\ensuremath{{}_j}} \newcommand{\sageMexSymbol}[1] {{\fontencoding{OMX}\fontfamily{cmex}\selectfont\raisebox{0.75em}{\symbol{#1}}}} From c985f42cd1184fa1227a38dbdd8353ce392b3285 Mon Sep 17 00:00:00 2001 From: Kiran Kedlaya Date: Sun, 20 Jun 2021 19:15:28 -0700 Subject: [PATCH 260/280] Changed is_easy_sn_an to descriptive name --- .../rings/polynomial/polynomial_rational_flint.pyx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index f9724ea41d3..55cfd8a4819 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -2513,7 +2513,7 @@ cdef class Polynomial_rational_flint(Polynomial): # Alias for discriminant disc = discriminant - def is_easy_sn_an(self, num_trials=50, assume_irreducible=False): + def galois_group_davenport_smith_test(self, num_trials=50, assume_irreducible=False): """ Use the Davenport-Smith test to attempt to certify that `f` has Galois group A_n or S_n. @@ -2521,18 +2521,20 @@ cdef class Polynomial_rational_flint(Polynomial): By default, we first check that `f` is irreducible. For extra efficiency, one can override this by specifying `assume_irreducible=True`; this yields undefined results if `f` is not irreducible. + + A corresponding function in Magma is `IsEasySnAn`. EXAMPLES:: sage: P. = QQ[] sage: u = x^7 + x + 1 - sage: u.is_easy_sn_an() + sage: u.galois_group_davenport_smith_test() 1 sage: u = x^7 - x^4 - x^3 + 3*x^2 - 1 - sage: u.is_easy_sn_an() + sage: u.galois_group_davenport_smith_test() 2 sage: u = x^7 - 2 - sage: u.is_easy_sn_an() + sage: u.galois_group_davenport_smith_test() 0 """ From b55bec31bbfed772def9b95ed4e1921f06c9ce9f Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Mon, 21 Jun 2021 13:32:21 +0100 Subject: [PATCH 261/280] Format changes As per a comment by slelivere, some pep8 formatting of the examples was done. --- src/sage/numerical/gauss_legendre.pyx | 50 ++++++++++++++------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index f236b3968a4..010247b3a49 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -11,9 +11,9 @@ We verify that `\int_0^1 n x^{n-1} \, dx = 1` for `n=1, \dots, 4`:: sage: prec = 100 sage: K = RealField(prec) sage: N = 4 - sage: V = VectorSpace(K,N) + sage: V = VectorSpace(K, N) sage: f = lambda x: V([(n+1)*x^n for n in range(N)]) - sage: I = integrate_vector(f,prec) + sage: I = integrate_vector(f, prec) sage: max([c.abs() for c in I-V(N*[1])]) 0.00000000000000000000000000000 @@ -22,7 +22,7 @@ AUTHORS: - Nils Bruin (2017-06-06): initial version - Linden Disney-Hogg (2021-06-17): documentation and integrate_vector method changes -NOTE:: +.. NOTE:: The code here is directly based on mpmath (see http://mpmath.org), but has a highly optimized routine to compute the nodes. @@ -74,11 +74,13 @@ def nodes(degree, prec): from this routine are actually more accurate than what the values the closed formula produces):: sage: from sage.numerical.gauss_legendre import nodes - sage: L1 = nodes(24,53) - sage: P = RR['x'](sage.functions.orthogonal_polys.legendre_P(24,x)) + sage: L1 = nodes(24, 53) + sage: P = RR['x'](sage.functions.orthogonal_polys.legendre_P(24, x)) sage: Pdif = P.diff() - sage: L2 = [((r+1)/2,1/(1-r^2)/Pdif(r)^2) for r,_ in RR['x'](P).roots()] - sage: all((a[0]-b[0]).abs() < 10^-15 and (a[1]-b[1]).abs() < 10^-9 for a,b in zip(L1,L2)) + sage: L2 = [((r + 1)/2, 1/(1 - r^2)/Pdif(r)^2) + for r, _ in RR['x'](P).roots()] + sage: all((a[0] - b[0]).abs() < 1e-15 and (a[1] - b[1]).abs() < 1e-9 + for a, b in zip(L1, L2)) True """ cdef long j,j1,n @@ -163,11 +165,11 @@ def estimate_error(results, prec, epsilon): sage: from sage.numerical.gauss_legendre import estimate_error sage: prec = 200 sage: K = RealField(prec) - sage: V = VectorSpace(K,2) - sage: a = V([1,-1]) - sage: b = V([1,1/2]) - sage: L = [a+2^(-2^i)*b for i in [0..5]] - sage: estimate_error(L,prec,K(2^(-prec))) + sage: V = VectorSpace(K, 2) + sage: a = V([1, -1]) + sage: b = V([1, 1/2]) + sage: L = [a + 2^(-2^i)*b for i in [0..5]] + sage: estimate_error(L, prec, K(2^(-prec))) 2.328235...e-10 """ if len(results)==2: @@ -213,12 +215,12 @@ def integrate_vector(f, prec, N=None, epsilon=None): sage: from sage.numerical.gauss_legendre import integrate_vector sage: prec = 200 sage: K = RealField(prec) - sage: V = VectorSpace(K,2) - sage: epsilon = K(2^(-prec+4)) - sage: f = lambda t:V((1+t^2,1/(1+t^2))) - sage: I = integrate_vector(f, prec, epsilon) - sage: J = V((4/3,pi/4)) - sage: max(c.abs() for c in (I-J)) < epsilon + sage: V = VectorSpace(K, 2) + sage: epsilon = K(2^(-prec + 4)) + sage: f = lambda t:V((1 + t^2, 1/(1 + t^2))) + sage: I = integrate_vector(f, prec, epsilon=epsilon) + sage: J = V((4/3, pi/4)) + sage: max(c.abs() for c in (I - J)) < epsilon True We can also use complex-valued integrands:: @@ -226,12 +228,12 @@ def integrate_vector(f, prec, N=None, epsilon=None): sage: prec = 200 sage: Kreal = RealField(prec) sage: K = ComplexField(prec) - sage: V = VectorSpace(K,2) - sage: epsilon = Kreal(2^(-prec+4)) - sage: f = lambda t: V((t,K(exp(2*pi*t*K.0)))) - sage: I = integrate_vector(f, prec, epsilon) - sage: J = V((1/2,0)) - sage: max(c.abs() for c in (I-J)) < epsilon + sage: V = VectorSpace(K, 2) + sage: epsilon = Kreal(2^(-prec + 4)) + sage: f = lambda t: V((t, K(exp(2*pi*t*K.0)))) + sage: I = integrate_vector(f, prec, epsilon=epsilon) + sage: J = V((1/2, 0)) + sage: max(c.abs() for c in (I - J)) < epsilon True """ results = [] From 87886ccda6687881688947100019df24d8e06d48 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Mon, 21 Jun 2021 14:08:51 +0100 Subject: [PATCH 262/280] Added reference for REC algorithm The REC algorithm is an algorithm for computing the weights and nodes of the Gauss-Legendre scheme, described in Neu2018. This references is added to the references index as well as to the GL documentation. --- src/doc/en/reference/references/index.rst | 5 +++++ src/sage/numerical/gauss_legendre.pyx | 11 ++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index befb73990a5..e678a0fdad7 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -4348,6 +4348,11 @@ REFERENCES: Proceedings of the National Academy of Sciences 36.1 (1950): 48-49. +.. [Neu2018] Christian Neurohr, *Efficient Integration on Riemann Surfaces & + Applications*, + PhD Thesis, Carl von Ossietzky Universität Oldenburg + http://oops.uni-oldenburg.de/3607. + .. [New2003] Newman, M.E.J. *The Structure and function of complex networks*, SIAM Review vol. 45, no. 2 (2003), pp. 167-256. :doi:`10.1137/S003614450342480`. diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index 010247b3a49..2e1ab03859c 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -54,17 +54,22 @@ from sage.rings.real_mpfr cimport RealNumber, RealField_class @cached_function def nodes(degree, prec): r""" - Compute the integration nodes and weights for the Gauss-Legendre quadrature scheme using a version of the REC algorithm. + Compute the integration nodes and weights for the Gauss-Legendre quadrature + scheme + + We use the recurrence relations for Legendre polynomials to compute their values. + This is a version of the algorithm that in [Neu2018]_ is called the REC algorithm. INPUT: - ``degree`` -- integer. The number of nodes. Must be 3 or even. - - ``prec`` -- integer (minimal value 53). Binary precision with which the nodes and weights are computed. + - ``prec`` -- integer (minimal value 53). Binary precision with which the + nodes and weights are computed. OUTPUT: - A list of (node,weight) pairs. + A list of (node, weight) pairs. EXAMPLES: From f8c19a22f0cf7029f24d5b0e29c27c5fcf6f1119 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Mon, 21 Jun 2021 14:50:45 +0100 Subject: [PATCH 263/280] Added TODO for nodes method The Arb library has code for calculating the nodes and weights. The added TODO suggests investigating if this code is faster. --- src/sage/numerical/gauss_legendre.pyx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index 2e1ab03859c..f7bbc6d2317 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -87,6 +87,12 @@ def nodes(degree, prec): sage: all((a[0] - b[0]).abs() < 1e-15 and (a[1] - b[1]).abs() < 1e-9 for a, b in zip(L1, L2)) True + + .. TODO:: + + It may be worth testing if using the Arb algorithm for finding the + nodes and weights in ``arb/acb_calc/integrate_gl_auto_deg.c`` has better + performance. """ cdef long j,j1,n cdef RealNumber r,t1,t2,t3,t4,a,w From 53438dd4cd04f9392220515b1bca87ff6ad48499 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 21 Jun 2021 08:58:32 -0700 Subject: [PATCH 264/280] build/pkgs/pip: Update to 21.1.2 --- build/pkgs/pip/checksums.ini | 6 +++--- build/pkgs/pip/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pip/checksums.ini b/build/pkgs/pip/checksums.ini index fabc83e4fa0..858cd7a1023 100644 --- a/build/pkgs/pip/checksums.ini +++ b/build/pkgs/pip/checksums.ini @@ -1,5 +1,5 @@ tarball=pip-VERSION.tar.gz -sha1=85a0cca517b854373dfcf43c3cac4ff726120c7f -md5=7fe80e6b3f94b5350284ed536d5b3a23 -cksum=3136634190 +sha1=b26e4afd5524f5613edf5b1b94dd1b6e9a2e1f87 +md5=a867fd51eacfd5293f5b7e0c2e7867a7 +cksum=2004742197 upstream_url=https://pypi.io/packages/source/p/pip/pip-VERSION.tar.gz diff --git a/build/pkgs/pip/package-version.txt b/build/pkgs/pip/package-version.txt index 2d978e312b5..9fcf356b68f 100644 --- a/build/pkgs/pip/package-version.txt +++ b/build/pkgs/pip/package-version.txt @@ -1 +1 @@ -21.1.1 +21.1.2 From a56b3c1333bce409e672b2f7de09d65591acf800 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Tue, 22 Jun 2021 12:48:54 +0100 Subject: [PATCH 265/280] Undid changes to integrate_vector Previous edits made to integrate_vector which are now the remit of ticket #32014 have been removed. This should (hopefully) allow the two tickets to not conflict. --- src/sage/numerical/gauss_legendre.pyx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index f7bbc6d2317..27bcb8b8b71 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -200,7 +200,7 @@ def estimate_error(results, prec, epsilon): e.append(D4.exp()) return max(e) -def integrate_vector(f, prec, N=None, epsilon=None): +def integrate_vector(f, prec, epsilon=None): r""" Integrate a one-argument vector-valued function numerically using Gauss-Legendre. @@ -213,8 +213,6 @@ def integrate_vector(f, prec, N=None, epsilon=None): - ``prec`` -- integer. Binary precision to be used. - - ``N`` -- integer. Number of nodes to use. If specificed, the target error ``epsilon`` is ignored. - - ``epsilon`` -- multiprecision float (default: `2^{(-\text{prec}+3)}`). Target error bound. OUTPUT: @@ -250,12 +248,6 @@ def integrate_vector(f, prec, N=None, epsilon=None): results = [] cdef long degree = 3 Rout = RealField(prec) - if N is not None: - nodelist = nodes(N,prec) - I = nodelist[0][1]*f(nodelist[0][0]) - for i in range(1,len(nodelist)): - I += nodelist[i][1]*f(nodelist[i][0]) - return I if epsilon is None: epsilon = Rout(2)**(-prec+3) From d0319dc9c0336bfed5f3d811f06fc74d5f54e48c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 22 Jun 2021 07:06:46 -0700 Subject: [PATCH 266/280] build/pkgs/pip/patches/10029.patch: New --- build/pkgs/pip/patches/10029.patch | 71 ++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 build/pkgs/pip/patches/10029.patch diff --git a/build/pkgs/pip/patches/10029.patch b/build/pkgs/pip/patches/10029.patch new file mode 100644 index 00000000000..cfd93403298 --- /dev/null +++ b/build/pkgs/pip/patches/10029.patch @@ -0,0 +1,71 @@ +From b8f1fcf863081fde0b9d558759c0e3c46ce09a12 Mon Sep 17 00:00:00 2001 +From: Ben Darnell <> +Date: Fri, 28 May 2021 16:01:41 +0000 +Subject: [PATCH] Avoid importing a non-vendored version of Tornado + +Code depending on this conditional import could break if an old +version of Tornado is present in the environment, rendering pip +unusable. +--- + news/10020.bugfix.rst | 1 + + src/pip/_vendor/tenacity/__init__.py | 10 ++++++---- + tools/vendoring/patches/tenacity.patch | 21 +++++++++++++++++++++ + 3 files changed, 28 insertions(+), 4 deletions(-) + create mode 100644 news/10020.bugfix.rst + create mode 100644 tools/vendoring/patches/tenacity.patch + +diff --git a/news/10020.bugfix.rst b/news/10020.bugfix.rst +new file mode 100644 +index 00000000000..9425626fb07 +--- /dev/null ++++ b/news/10020.bugfix.rst +@@ -0,0 +1 @@ ++Remove unused optional ``tornado`` import in vendored ``tenacity`` to prevent old versions of Tornado from breaking pip. +diff --git a/src/pip/_vendor/tenacity/__init__.py b/src/pip/_vendor/tenacity/__init__.py +index 5f8cb505896..42e9d8940b1 100644 +--- a/src/pip/_vendor/tenacity/__init__.py ++++ b/src/pip/_vendor/tenacity/__init__.py +@@ -22,10 +22,12 @@ + except ImportError: + iscoroutinefunction = None + +-try: +- import tornado +-except ImportError: +- tornado = None ++# Replace a conditional import with a hard-coded None so that pip does ++# not attempt to use tornado even if it is present in the environment. ++# If tornado is non-None, tenacity will attempt to execute some code ++# that is sensitive to the version of tornado, which could break pip ++# if an old version is found. ++tornado = None + + import sys + import threading +diff --git a/tools/vendoring/patches/tenacity.patch b/tools/vendoring/patches/tenacity.patch +new file mode 100644 +index 00000000000..006588b3653 +--- /dev/null ++++ b/tools/vendoring/patches/tenacity.patch +@@ -0,0 +1,21 @@ ++diff --git a/src/pip/_vendor/tenacity/__init__.py b/src/pip/_vendor/tenacity/__init__.py ++index 5f8cb5058..42e9d8940 100644 ++--- a/src/pip/_vendor/tenacity/__init__.py +++++ b/src/pip/_vendor/tenacity/__init__.py ++@@ -22,10 +22,12 @@ try: ++ except ImportError: ++ iscoroutinefunction = None ++ ++-try: ++- import tornado ++-except ImportError: ++- tornado = None +++# Replace a conditional import with a hard-coded None so that pip does +++# not attempt to use tornado even if it is present in the environment. +++# If tornado is non-None, tenacity will attempt to execute some code +++# that is sensitive to the version of tornado, which could break pip +++# if an old version is found. +++tornado = None ++ ++ import sys ++ import threading From f307b11af55cd9ee7302d8983773a4d71703b6ff Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 22 Jun 2021 11:42:47 -0700 Subject: [PATCH 267/280] build/pkgs/setuptools/spkg-install.in: Do not nuke packages in site-packages before install --- build/pkgs/setuptools/spkg-install.in | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/build/pkgs/setuptools/spkg-install.in b/build/pkgs/setuptools/spkg-install.in index efc63964f5c..09a8b8f65ce 100644 --- a/build/pkgs/setuptools/spkg-install.in +++ b/build/pkgs/setuptools/spkg-install.in @@ -1,19 +1,5 @@ -# distribute doesn't allow itself to be replaced by setuptools -# so we manually have to delete it -# (pip actually can uninstall setuptools but we may not have pip -# install yet) -rm -rf "$SAGE_LOCAL"/lib/python*/site-packages/setuptools* -rm -rf "$SAGE_LOCAL"/lib/python*/site-packages/distribute* - -export PYTHON_EGG_CACHE="$DOT_SAGE/.python-eggs" - cd src - -versions=3 - -# Prevent setuptools from installing itself with easy_install -for vers in $versions; do - python${vers} setup.py --no-user-cfg install \ +# Use --single-version-externally-managed to prevent setuptools from installing itself with easy_install +python3 setup.py --no-user-cfg install \ --single-version-externally-managed --root="$SAGE_DESTDIR" || \ sdh_die "Error building / installing setuptools for Python ${vers}" -done From b3a3c6dc6101cc70fb7d5c7b4383d937a48d0c95 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 22 Jun 2021 19:56:43 -0700 Subject: [PATCH 268/280] tox.ini, .github/workflows/tox.yml: Add ubuntu-impish, linuxmint-20.2, fedora-35 --- .github/workflows/tox.yml | 2 +- tox.ini | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 767a12e279d..f14be3ee2e0 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -38,7 +38,7 @@ jobs: fail-fast: false max-parallel: 20 matrix: - tox_system_factor: [ubuntu-trusty, ubuntu-xenial, ubuntu-bionic, ubuntu-focal, ubuntu-groovy, ubuntu-hirsute, debian-jessie, debian-stretch, debian-buster, debian-bullseye, debian-sid, linuxmint-17, linuxmint-18, linuxmint-19, linuxmint-19.3, linuxmint-20.1, fedora-26, fedora-27, fedora-28, fedora-29, fedora-30, fedora-31, fedora-32, fedora-33, fedora-34, centos-7, centos-8, gentoo, gentoo-python3.7, archlinux-latest, opensuse-15, opensuse-15.3, opensuse-tumbleweed, slackware-14.2, conda-forge, ubuntu-bionic-i386, manylinux-2_24-i686, debian-buster-i386, centos-7-i386] + tox_system_factor: [ubuntu-trusty, ubuntu-xenial, ubuntu-bionic, ubuntu-focal, ubuntu-groovy, ubuntu-hirsute, ubuntu-impish, debian-jessie, debian-stretch, debian-buster, debian-bullseye, debian-sid, linuxmint-17, linuxmint-18, linuxmint-19, linuxmint-19.3, linuxmint-20.1, linuxmint-20.2, fedora-26, fedora-27, fedora-28, fedora-29, fedora-30, fedora-31, fedora-32, fedora-33, fedora-34, fedora-35, centos-7, centos-8, gentoo, gentoo-python3.7, archlinux-latest, opensuse-15, opensuse-15.3, opensuse-tumbleweed, slackware-14.2, conda-forge, ubuntu-bionic-i386, manylinux-2_24-i686, debian-buster-i386, centos-7-i386] tox_packages_factor: [minimal, standard] env: TOX_ENV: docker-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} diff --git a/tox.ini b/tox.ini index 26e36e582c3..21e342350d5 100644 --- a/tox.ini +++ b/tox.ini @@ -153,7 +153,7 @@ setenv = docker: BASE_TAG=latest # # https://hub.docker.com/_/ubuntu?tab=description - # as of 2020-11, latest=focal=20.04, rolling=groovy=20.10, devel=hirsute=21.04 + # as of 2021-06, latest=focal=20.04, rolling=hirsute=21.04, impish=devel=21.10 # ubuntu: SYSTEM=debian ubuntu: BASE_IMAGE=ubuntu @@ -167,6 +167,9 @@ setenv = ubuntu-groovy: BASE_TAG=groovy ubuntu-groovy: IGNORE_MISSING_SYSTEM_PACKAGES=no ubuntu-hirsute: BASE_TAG=hirsute + ubuntu-hirsute: IGNORE_MISSING_SYSTEM_PACKAGES=no + ubuntu-impish: BASE_TAG=impish + ubuntu-impish: IGNORE_MISSING_SYSTEM_PACKAGES=yes # # https://hub.docker.com/_/debian # debian-bullseye does not have libgiac-dev @@ -194,9 +197,10 @@ setenv = linuxmint-19.3: BASE_IMAGE=linuxmintd/mint19.3 linuxmint-20: BASE_IMAGE=linuxmintd/mint20 linuxmint-20.1: BASE_IMAGE=linuxmintd/mint20.1 + linuxmint-20.2: BASE_IMAGE=linuxmintd/mint20.2 # # https://hub.docker.com/_/fedora - # as of 2020-11, latest=33, rawhide=34 + # as of 2021-06, latest=34, rawhide=35 fedora: SYSTEM=fedora fedora: BASE_IMAGE=fedora fedora-26: BASE_TAG=26 @@ -213,6 +217,9 @@ setenv = fedora-33: BASE_TAG=33 fedora-33: IGNORE_MISSING_SYSTEM_PACKAGES=no fedora-34: BASE_TAG=34 + fedora-34: IGNORE_MISSING_SYSTEM_PACKAGES=no + fedora-35: BASE_TAG=35 + fedora-35: IGNORE_MISSING_SYSTEM_PACKAGES=yes # # https://hub.docker.com/r/scientificlinux/sl # From de25ce550477a3c8efeb4e50c1b9c230c3908bbd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 23 Jun 2021 10:48:31 -0700 Subject: [PATCH 269/280] build/pkgs/python3/spkg-build.in, spkg-configure.m4: Do not check for the readline module --- build/pkgs/python3/spkg-build.in | 3 ++- build/pkgs/python3/spkg-configure.m4 | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build/pkgs/python3/spkg-build.in b/build/pkgs/python3/spkg-build.in index 8c7aee8ae3f..161b9199ee2 100644 --- a/build/pkgs/python3/spkg-build.in +++ b/build/pkgs/python3/spkg-build.in @@ -110,7 +110,8 @@ fi echo "Testing importing of various modules..." import_errors=false -test_modules="ctypes math hashlib crypt readline socket zlib sqlite3" +# Trac #31160: We no longer check for readline here. +test_modules="ctypes math hashlib crypt socket zlib sqlite3" if [ "$UNAME" = "Darwin" ]; then test_modules="$test_modules _scproxy" fi diff --git a/build/pkgs/python3/spkg-configure.m4 b/build/pkgs/python3/spkg-configure.m4 index a76f27e3cb9..1a835b37a0a 100644 --- a/build/pkgs/python3/spkg-configure.m4 +++ b/build/pkgs/python3/spkg-configure.m4 @@ -22,7 +22,8 @@ SAGE_SPKG_CONFIGURE([python3], [ SAGE_SPKG_DEPCHECK([bzip2 xz libffi], [ dnl Check if we can do venv with a system python3 dnl instead of building our own copy. - check_modules="sqlite3, ctypes, math, hashlib, crypt, readline, socket, zlib, distutils.core" + dnl Trac #31160: We no longer check for readline here. + check_modules="sqlite3, ctypes, math, hashlib, crypt, socket, zlib, distutils.core" AC_CACHE_CHECK([for python3 >= ]MIN_VERSION[, < ]LT_VERSION[ with modules $check_modules], [ac_cv_path_PYTHON3], [ AS_IF([test x"$ac_path_PYTHON3" != x], [dnl checking explicitly specified $with_python AC_MSG_RESULT([]) From 346e492ec95b0bcdb2e712163dfbb5dfdf2c8b85 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Thu, 24 Jun 2021 10:03:59 +0200 Subject: [PATCH 270/280] document that coordinate vector is not unique unless reduced --- src/sage/modules/fg_pid/fgp_module.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/sage/modules/fg_pid/fgp_module.py b/src/sage/modules/fg_pid/fgp_module.py index 8213af301ed..db8fd8cabb7 100644 --- a/src/sage/modules/fg_pid/fgp_module.py +++ b/src/sage/modules/fg_pid/fgp_module.py @@ -1271,8 +1271,11 @@ def coordinate_vector(self, x, reduce=False): sage: V = span([[1/2,0,0],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2]) sage: Q = V/W; Q Finitely generated module V/W over Integer Ring with invariants (4, 12) - sage: Q.coordinate_vector(Q.0 - Q.1) - (1, -1) + sage: Q.coordinate_vector(Q.0 - Q.1, reduce=True) + (1, 11) + sage: a, b = Q.coordinate_vector(Q.0 - Q.1) + sage: (a % 4, b % 12) + (1, 11) sage: O, X = Q.optimized() sage: O.V() @@ -1285,14 +1288,16 @@ def coordinate_vector(self, x, reduce=False): (0, 8) sage: Q.coordinate_vector(x, reduce=True) (0, 8) - sage: Q.coordinate_vector(-x, reduce=False) # random - (0, -8) + sage: a, b = Q.coordinate_vector(-x, reduce=False) + sage: (a % 4, b % 12) + (0, 4) sage: x == 8*Q.1 True sage: x = Q(V.1); x (0, 11) - sage: Q.coordinate_vector(x) - (0, -1) + sage: a, b = Q.coordinate_vector(x) + sage: (a % 4, b % 12) + (0, 11) sage: x == -Q.1 True sage: x = Q(V.2); x From 7bed91d083c7a6b97b37a4af18579be9cedb80e2 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Thu, 24 Jun 2021 11:06:51 +0100 Subject: [PATCH 271/280] allow 32-bit boxes to complain --- src/sage/libs/eclib/interface.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/libs/eclib/interface.py b/src/sage/libs/eclib/interface.py index a163cc31093..de2c48f307d 100644 --- a/src/sage/libs/eclib/interface.py +++ b/src/sage/libs/eclib/interface.py @@ -553,7 +553,9 @@ def saturate(self, bound=-1, lower=2): sage: E = mwrank_EllipticCurve([0, 0, 0, -1002231243161, 0]) sage: E.gens() - [[-1001107, -4004428, 1]] + [[-1001107, -4004428, 1]] # 64-bit + ... # 32-bit + [[-1001107, -4004428, 1]] # 32-bit sage: E.saturate() sage: E.gens() [[-1001107, -4004428, 1]] From a9c3c18820b8c96cd2336b90dc480417eaefd6d6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 12 Mar 2021 10:32:09 -0800 Subject: [PATCH 272/280] build/pkgs/openblas/spkg-install.in: Save the configuration, reuse it in spkg-check.in --- build/pkgs/openblas/spkg-check.in | 33 +++++------------------------ build/pkgs/openblas/spkg-install.in | 4 ++++ 2 files changed, 9 insertions(+), 28 deletions(-) diff --git a/build/pkgs/openblas/spkg-check.in b/build/pkgs/openblas/spkg-check.in index 3032da767f5..de401dfc0af 100644 --- a/build/pkgs/openblas/spkg-check.in +++ b/build/pkgs/openblas/spkg-check.in @@ -1,33 +1,10 @@ -cd src - -# OpenBlas has no proper configure script +# OpenBLAS has no proper configure script # Options are directly passed to make # And the name static library archive produced depends on them # And the tests directly link to that archive rather than through a symlink -# Therefore the following is copied from spkg-install -# We could also patch the Makefile to use a generic symlink pointing -# to the archive with a specific name +# So we use the saved configuration from spkg-install.in +. ./set_openblas_configure || sdh_die "Saved configuration not found" -if [ `sage-bootstrap-python -c "from __future__ import print_function; import platform; print(platform.architecture()[0])"` = "32bit" ]; then - OPENBLAS_CONFIGURE="$OPENBLAS_CONFIGURE BINARY=32" -fi - -if [ "x$SAGE_FAT_BINARY" = "xyes" ]; then - # See https://github.com/xianyi/OpenBLAS/issues/510 - OPENBLAS_CONFIGURE="$OPENBLAS_CONFIGURE TARGET=PRESCOTT" -fi +cd src -${MAKE:-make} tests $OPENBLAS_CONFIGURE -if [ $? -ne 0 ]; then - # First make sure we already didn't set a target - if [[ $OPENBLAS_CONFIGURE == *"TARGET"* ]]; then - sdh_die "Failures while running the OpenBLAS testsuite ... exiting" - else - # The recommended TARGET is ATOM if CPU fails - # See https://github.com/xianyi/OpenBLAS/issues/1204 - OPENBLAS_CONFIGURE="$OPENBLAS_CONFIGURE TARGET=ATOM" - echo "Failures while testing the OpenBLAS testsuite" - echo "Retrying the OpenBLAS testsuite with TARGET=ATOM" - ${MAKE:-make} tests $OPENBLAS_CONFIGURE - fi -fi +${MAKE:-make} tests $OPENBLAS_CONFIGURE || sdh_die "'make tests' failed" diff --git a/build/pkgs/openblas/spkg-install.in b/build/pkgs/openblas/spkg-install.in index 70ad7b5d189..b06a55d3130 100644 --- a/build/pkgs/openblas/spkg-install.in +++ b/build/pkgs/openblas/spkg-install.in @@ -45,6 +45,10 @@ sdh_make_install PREFIX="$SAGE_LOCAL" NO_STATIC=1 $OPENBLAS_CONFIGURE cd .. ./write_pc_file.py +# Save configuration for spkg-check +echo >&2 "Writing configuration to $(pwd)/set_openblas_configure" +echo OPENBLAS_CONFIGURE=\'"$OPENBLAS_CONFIGURE"\' > set_openblas_configure + # OpenBLAS's Makefile has a bug w.r.t. calling install_name_tool when # DESTDIR is set. It should *not* include the DESTDIR in the library's # install_name; we set the correct install_name here From 8d319830fd177b18aaefdd9a22c92696fef5cc73 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 24 Jun 2021 14:57:45 -0700 Subject: [PATCH 273/280] build/make/Makefile.in: Uninstall setuptools before reinstalling python3 --- build/make/Makefile.in | 5 +++++ build/pkgs/python3/spkg-build.in | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 8ba89d78c74..a249cc3c546 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -283,6 +283,11 @@ all-toolchain: base-toolchain # given as a prerequisite to any pip-installed packages PYTHON_TOOLCHAIN = setuptools pip setuptools_scm wheel setuptools_wheel +# Trac #32056: Avoid installed setuptools leaking into the build of python3 by uninstalling it. +# It will have to be reinstalled anyway because of its dependency on $(PYTHON). +python3-SAGE_LOCAL-no-deps: setuptools-clean +python3-SAGE_VENV-no-deps: setuptools-clean + # Everything needed to start up Sage using "./sage". Of course, not # every part of Sage will work. It does not include Maxima for example. SAGERUNTIME = sagelib $(inst_ipython) $(inst_pexpect) \ diff --git a/build/pkgs/python3/spkg-build.in b/build/pkgs/python3/spkg-build.in index 8c7aee8ae3f..e15072fd8da 100644 --- a/build/pkgs/python3/spkg-build.in +++ b/build/pkgs/python3/spkg-build.in @@ -72,15 +72,11 @@ rm -f "$SAGE_LOCAL/lib/python" rm -f "$SAGE_LOCAL"/lib/lib"$PKG_BASE"* -if [ "$PKG_BASE" = "python2" ]; then - PYTHON_CONFIGURE="$PYTHON_CONFIGURE --enable-unicode=ucs4" -else # Note: --without-ensurepip ensures that setuptools+pip are *not* installed # automatically when installing python3. They will be installed instead by # the separate setuptools and pip packages; see # https://trac.sagemath.org/ticket/23398 PYTHON_CONFIGURE="$PYTHON_CONFIGURE --without-ensurepip" -fi sdh_configure --enable-shared $PYTHON_CONFIGURE From 789550ca04c94acfb1e803251538996a34962038 Mon Sep 17 00:00:00 2001 From: John Cremona Date: Fri, 25 Jun 2021 13:50:28 +0100 Subject: [PATCH 274/280] 31443 updated for eclib version 20210625 --- build/pkgs/eclib/checksums.ini | 6 +++--- build/pkgs/eclib/package-version.txt | 2 +- build/pkgs/eclib/spkg-configure.m4 | 2 +- src/sage/libs/eclib/interface.py | 4 +--- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/build/pkgs/eclib/checksums.ini b/build/pkgs/eclib/checksums.ini index 8342f43b6da..a76b8f40fde 100644 --- a/build/pkgs/eclib/checksums.ini +++ b/build/pkgs/eclib/checksums.ini @@ -1,5 +1,5 @@ tarball=eclib-VERSION.tar.bz2 -sha1=b8e1bd10a28341fcd8146cf12fc38a12c798c555 -md5=d54562480e1ebd73ae3f343b949dfa66 -cksum=2657346485 +sha1=e25f1aa6b7450f17bcff643fac9473d326012e29 +md5=b1288dc5eb981d45db1db0e11987468a +cksum=2713659223 upstream_url=https://github.com/JohnCremona/eclib/releases/download/VERSION/eclib-VERSION.tar.bz2 diff --git a/build/pkgs/eclib/package-version.txt b/build/pkgs/eclib/package-version.txt index 891293d2df1..ed06b627c4d 100644 --- a/build/pkgs/eclib/package-version.txt +++ b/build/pkgs/eclib/package-version.txt @@ -1 +1 @@ -20210503 +20210625 diff --git a/build/pkgs/eclib/spkg-configure.m4 b/build/pkgs/eclib/spkg-configure.m4 index 4bced5f44ed..e49e0b74ae3 100644 --- a/build/pkgs/eclib/spkg-configure.m4 +++ b/build/pkgs/eclib/spkg-configure.m4 @@ -1,7 +1,7 @@ SAGE_SPKG_CONFIGURE([eclib], [ SAGE_SPKG_DEPCHECK([ntl pari flint], [ dnl Trac #31443: use existing eclib only if the version reported by pkg-config is correct - m4_pushdef([SAGE_ECLIB_VER],["20210503"]) + m4_pushdef([SAGE_ECLIB_VER],["20210625"]) PKG_CHECK_MODULES([ECLIB], [eclib = SAGE_ECLIB_VER], [ AC_CACHE_CHECK([for mwrank version == SAGE_ECLIB_VER], [ac_cv_path_MWRANK], [ AC_PATH_PROGS_FEATURE_CHECK([MWRANK], [mwrank], [ diff --git a/src/sage/libs/eclib/interface.py b/src/sage/libs/eclib/interface.py index de2c48f307d..a163cc31093 100644 --- a/src/sage/libs/eclib/interface.py +++ b/src/sage/libs/eclib/interface.py @@ -553,9 +553,7 @@ def saturate(self, bound=-1, lower=2): sage: E = mwrank_EllipticCurve([0, 0, 0, -1002231243161, 0]) sage: E.gens() - [[-1001107, -4004428, 1]] # 64-bit - ... # 32-bit - [[-1001107, -4004428, 1]] # 32-bit + [[-1001107, -4004428, 1]] sage: E.saturate() sage: E.gens() [[-1001107, -4004428, 1]] From 4550eb6a753930c5818b63049f2eec197470ed1f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 3 Feb 2021 15:59:40 -0800 Subject: [PATCH 275/280] sage.env.sage_include_directories: Do not fail if numpy cannot be imported --- src/sage/env.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sage/env.py b/src/sage/env.py index e792ca80450..ccf92b70e1a 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -317,7 +317,7 @@ def _get_shared_lib_path(*libnames: str) -> Optional[str]: GAP_SO = var("GAP_SO", _get_shared_lib_path("gap", "")) # post process -if ' ' in DOT_SAGE: +if DOT_SAGE is not None and ' ' in DOT_SAGE: if UNAME[:6] == 'CYGWIN': # on windows/cygwin it is typical for the home directory # to have a space in it. Fortunately, users also have @@ -378,14 +378,18 @@ def sage_include_directories(use_sources=False): sage: any(os.path.isfile(os.path.join(d, file)) for d in dirs) True """ - import numpy import distutils.sysconfig TOP = SAGE_SRC if use_sources else SAGE_LIB - return [TOP, - distutils.sysconfig.get_python_inc(), - numpy.get_include()] + dirs = [TOP, + distutils.sysconfig.get_python_inc()] + try: + import numpy + dirs.append(numpy.get_include()) + except ModuleNotFoundError: + pass + return dirs def get_cblas_pc_module_name() -> str: """ From 6df6144cd06af7691315a78550e1e1ff57f09820 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 27 Jun 2021 23:46:58 -0700 Subject: [PATCH 276/280] src/sage/doctest/forker.py: Do not crash if readline cannot be imported --- src/sage/doctest/forker.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 9dcc1d6b549..6bc3518983a 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -231,7 +231,11 @@ def init_sage(controller=None): # We import readline before forking, otherwise Pdb doesn't work # on OS X: http://trac.sagemath.org/14289 - import readline + try: + import readline + except ModuleNotFoundError: + # Do not require readline for running doctests (Trac #31160). + pass try: import sympy From e74a32b3a68b78bde03c6e15608436dee3012bd0 Mon Sep 17 00:00:00 2001 From: "Linden Disney-Hogg (Work)" Date: Mon, 28 Jun 2021 10:43:09 +0100 Subject: [PATCH 277/280] Added continuation prompts Continuation prompts were missing from an example, and this error was rectified. --- src/sage/numerical/gauss_legendre.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index 27bcb8b8b71..1234405acaa 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -83,9 +83,9 @@ def nodes(degree, prec): sage: P = RR['x'](sage.functions.orthogonal_polys.legendre_P(24, x)) sage: Pdif = P.diff() sage: L2 = [((r + 1)/2, 1/(1 - r^2)/Pdif(r)^2) - for r, _ in RR['x'](P).roots()] + ....: for r, _ in RR['x'](P).roots()] sage: all((a[0] - b[0]).abs() < 1e-15 and (a[1] - b[1]).abs() < 1e-9 - for a, b in zip(L1, L2)) + ....: for a, b in zip(L1, L2)) True .. TODO:: From 473cd41f19ec23df7e207391cfb0cf41c7c4ef46 Mon Sep 17 00:00:00 2001 From: Release Manager Date: Thu, 1 Jul 2021 22:47:24 +0200 Subject: [PATCH 278/280] Updated SageMath version to 9.4.beta4 --- .zenodo.json | 8 ++++---- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sagelib/package-version.txt | 2 +- src/VERSION.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index e1658133ddd..c40fda4109b 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.4.beta3", - "version": "9.4.beta3", + "title": "sagemath/sage: 9.4.beta4", + "version": "9.4.beta4", "upload_type": "software", - "publication_date": "2021-06-21", + "publication_date": "2021-07-01", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.4.beta3", + "identifier": "https://github.com/sagemath/sage/tree/9.4.beta4", "relation": "isSupplementTo" }, { diff --git a/VERSION.txt b/VERSION.txt index 1ad0a4e8860..2f40e816f49 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.4.beta3, Release Date: 2021-06-21 +SageMath version 9.4.beta4, Release Date: 2021-07-01 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 5644d267bbb..8dfcdb4fa2a 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=6c57f29a40da0d59930469bb1f42d4bc5f597ce7 -md5=8071fb87383998629b18fd416b6be631 -cksum=1343198753 +sha1=7b88d740d7ed702c15cb4048ae08c2faf2e9852c +md5=6252f2920838a7ea42b17845aa9782a2 +cksum=2536572108 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index fc1fc1f6be6..e457c20289b 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -f325f588ed6fe34ecacae25cde4dd8ace2551c79 +06ec5d949d4ac37fffbebba5ab2ad5294cb141e1 diff --git a/build/pkgs/sagelib/package-version.txt b/build/pkgs/sagelib/package-version.txt index f9ffedae371..0c2ffe14533 100644 --- a/build/pkgs/sagelib/package-version.txt +++ b/build/pkgs/sagelib/package-version.txt @@ -1 +1 @@ -9.4.beta3 +9.4.beta4 diff --git a/src/VERSION.txt b/src/VERSION.txt index f9ffedae371..0c2ffe14533 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -9.4.beta3 +9.4.beta4 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 72225680ccb..f9935624875 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.4.beta3' -SAGE_RELEASE_DATE='2021-06-21' -SAGE_VERSION_BANNER='SageMath version 9.4.beta3, Release Date: 2021-06-21' +SAGE_VERSION='9.4.beta4' +SAGE_RELEASE_DATE='2021-07-01' +SAGE_VERSION_BANNER='SageMath version 9.4.beta4, Release Date: 2021-07-01' diff --git a/src/sage/version.py b/src/sage/version.py index 6bde12d24e0..6cc84191475 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.4.beta3' -date = '2021-06-21' -banner = 'SageMath version 9.4.beta3, Release Date: 2021-06-21' +version = '9.4.beta4' +date = '2021-07-01' +banner = 'SageMath version 9.4.beta4, Release Date: 2021-07-01' From 2582707776ec1b7441a7635700a73d40c503b158 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 6 Jul 2021 12:13:01 -0700 Subject: [PATCH 279/280] build/pkgs/ptyprocess: Revert upgrade to 0.7.0; pin version to 0.5.1 --- build/pkgs/ptyprocess/checksums.ini | 6 +++--- build/pkgs/ptyprocess/dependencies | 2 +- build/pkgs/ptyprocess/install-requires.txt | 4 +++- build/pkgs/ptyprocess/package-version.txt | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/build/pkgs/ptyprocess/checksums.ini b/build/pkgs/ptyprocess/checksums.ini index d55046be142..7c8cf7bd652 100644 --- a/build/pkgs/ptyprocess/checksums.ini +++ b/build/pkgs/ptyprocess/checksums.ini @@ -1,5 +1,5 @@ tarball=ptyprocess-VERSION.tar.gz -sha1=2d8830d1025c8e33149c7723c2f283122f9488c1 -md5=9da200c397cb1752209a6b718b6cfc68 -cksum=2094263560 +sha1=3290062d67ef8a2f136bff9c2cd106673ff21316 +md5=94e537122914cc9ec9c1eadcd36e73a1 +cksum=1748633415 upstream_url=https://pypi.io/packages/source/p/ptyprocess/ptyprocess-VERSION.tar.gz diff --git a/build/pkgs/ptyprocess/dependencies b/build/pkgs/ptyprocess/dependencies index 97701a95eec..15df0c4d6d8 100644 --- a/build/pkgs/ptyprocess/dependencies +++ b/build/pkgs/ptyprocess/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) flit_core toml +$(PYTHON) | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/ptyprocess/install-requires.txt b/build/pkgs/ptyprocess/install-requires.txt index 1f0ce3f011d..de89260872b 100644 --- a/build/pkgs/ptyprocess/install-requires.txt +++ b/build/pkgs/ptyprocess/install-requires.txt @@ -1 +1,3 @@ -ptyprocess >=0.5.1 +ptyprocess ==0.5.1 +# https://trac.sagemath.org/ticket/31280#comment:42 and following +# sagelib is not compatible with ptyprocess 0.5.2, 0.6, and 0.7 diff --git a/build/pkgs/ptyprocess/package-version.txt b/build/pkgs/ptyprocess/package-version.txt index faef31a4357..dfd7f4b3ab3 100644 --- a/build/pkgs/ptyprocess/package-version.txt +++ b/build/pkgs/ptyprocess/package-version.txt @@ -1 +1 @@ -0.7.0 +0.5.1.p0 From 45cf01395aa1f4c7145cf6450e6ba1102a0c8d6f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 25 Jun 2021 10:17:54 -0700 Subject: [PATCH 280/280] build/pkgs/rst2ipynb/patches: Remove --- .../pkgs/rst2ipynb/patches/check-pandoc.patch | 58 ------------------- 1 file changed, 58 deletions(-) delete mode 100644 build/pkgs/rst2ipynb/patches/check-pandoc.patch diff --git a/build/pkgs/rst2ipynb/patches/check-pandoc.patch b/build/pkgs/rst2ipynb/patches/check-pandoc.patch deleted file mode 100644 index 72e0b7bb5e1..00000000000 --- a/build/pkgs/rst2ipynb/patches/check-pandoc.patch +++ /dev/null @@ -1,58 +0,0 @@ -See https://github.com/nthiery/rst-to-ipynb/pull/4 - -commit 08fbca3f57607f49bd40e9254106aecc1ac0dbe7 -Author: Jeroen Demeyer -Date: Mon Oct 9 12:38:19 2017 +0200 - - Check pandoc dependency - -diff --git a/setup.py b/setup.py -index e2e2ed3..5afaa33 100644 ---- a/setup.py -+++ b/setup.py -@@ -4,17 +4,35 @@ reST to Jupyter notebook converter - """ - - # Always prefer setuptools over distutils --from setuptools import setup, find_packages -+from setuptools import setup -+from setuptools.command.install import install -+from distutils.errors import DistutilsExecError - # To use a consistent encoding - from codecs import open --from os import path -+import os - --here = path.abspath(path.dirname(__file__)) -+ -+here = os.path.dirname(__file__) - - # Get the long description from the README file --with open(path.join(here, 'README.rst'), encoding='utf-8') as f: -+with open(os.path.join(here, 'README.rst'), encoding='utf-8') as f: - long_description = f.read() - -+ -+class check_install(install): -+ "Check that pandoc is installed on the system" -+ def run(self): -+ import subprocess -+ try: -+ # Hide stdout but allow stderr -+ subprocess.check_call(["pandoc", "-v"], stdout=open(os.devnull)) -+ except subprocess.CalledProcessError: -+ raise DistutilsExecError("rst2ipynb requires the Haskell program 'pandoc'. It seems to be installed, but it did not work properly.") -+ except OSError: -+ raise DistutilsExecError("rst2ipynb requires the Haskell program 'pandoc'. You need to install it on your system.") -+ install.run(self) -+ -+ - setup( - name='rst2ipynb', - version='0.2.2', -@@ -35,4 +53,5 @@ setup( - install_requires=['notedown', 'pandocfilters'], - #setup_requires=['pytest-runner'], - #tests_require=['pytest'], -+ cmdclass=dict(install=check_install) - )