From 829bcc02ed2ef6b3530a27565851edfb01f639a7 Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Wed, 15 Apr 2020 21:26:03 +0200 Subject: [PATCH 001/204] 29514: implement formatting of RDF/CDF elements --- src/sage/rings/complex_double.pyx | 17 ++++++++++++++++- src/sage/rings/real_double.pyx | 13 +++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index dc6662ddb4a..cbc9d3b8052 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -965,7 +965,7 @@ cdef class ComplexDoubleElement(FieldElement): 1.4142135623730951 """ if self._complex.imag: - raise TypeError(f"unable to convert {self} to float; use abs() or real_part() as desired") + raise TypeError(f"unable to convert {self!s} to float; use abs() or real_part() as desired") return self._complex.real def __complex__(self): @@ -1079,6 +1079,21 @@ cdef class ComplexDoubleElement(FieldElement): return s + double_repr(y) + "*I" + def __format__(self, format_spec): + """ + Return a formatted string representation of this complex number. + + EXAMPLES:: + + sage: format(CDF(32/3, 0), ' .4f') + ' 10.6667+0.0000j' + sage: format(CDF(-2/3, -2/3), '.4e') + '-6.6667e-01-6.6667e-01j' + sage: '{: 18}'.format(CDF(-1/80, -1/2)) + ' (-0.0125-0.5j)' + """ + return format(complex(self), format_spec) + def _latex_(self): """ Return a latex representation of ``self``. diff --git a/src/sage/rings/real_double.pyx b/src/sage/rings/real_double.pyx index 3011f32c645..ff4fa57b039 100644 --- a/src/sage/rings/real_double.pyx +++ b/src/sage/rings/real_double.pyx @@ -1084,6 +1084,19 @@ cdef class RealDoubleElement(FieldElement): """ return double_repr(self._value) + def __format__(self, format_spec): + """ + Return a formatted string representation of this real number. + + EXAMPLES:: + + sage: format(RDF(32/3), '.4f') + '10.6667' + sage: '{:.4e}'.format(RDF(2/3)) + '6.6667e-01' + """ + return format(float(self), format_spec) + def _latex_(self): r""" Return a latex representation of ``self``. From 51a9736237f27229e5c9624c7fbb6d53ec898725 Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Thu, 16 Apr 2020 18:29:41 +0200 Subject: [PATCH 002/204] 29514: implement custom __format__ matching the CDF representation --- src/sage/rings/complex_double.pyx | 78 ++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index cbc9d3b8052..89f8ce84c70 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -965,7 +965,7 @@ cdef class ComplexDoubleElement(FieldElement): 1.4142135623730951 """ if self._complex.imag: - raise TypeError(f"unable to convert {self!s} to float; use abs() or real_part() as desired") + raise TypeError(f"unable to convert {self} to float; use abs() or real_part() as desired") return self._complex.real def __complex__(self): @@ -1083,16 +1083,82 @@ cdef class ComplexDoubleElement(FieldElement): """ Return a formatted string representation of this complex number. + INPUT: + + - ``format_spec`` -- string; a floating point format specificier as + defined by :python:`the format specification mini-language + ` in Python + EXAMPLES:: sage: format(CDF(32/3, 0), ' .4f') - ' 10.6667+0.0000j' + ' 10.6667 + 0.0000*I' sage: format(CDF(-2/3, -2/3), '.4e') - '-6.6667e-01-6.6667e-01j' - sage: '{: 18}'.format(CDF(-1/80, -1/2)) - ' (-0.0125-0.5j)' + '-6.6667e-01 - 6.6667e-01*I' + sage: format(CDF(3, 0), '.4g') + '3 + 0*I' + sage: format(CDF(3, 0), '#.4g') + '3.000 + 0.000*I' + + If the representation type character is absent, the output matches the + string representation of the complex number. This has the effect that + real and imaginary part are only shown if they are not zero:: + + sage: format(CDF(0, 2/3), '.4') + '0.6667*I' + sage: format(CDF(2, 0), '.4') + '2.0' + sage: format(CDF(0, 0), '+#.4') + '+0.000' + + TESTS:: + + sage: s = format(CDF(1/80, -1/2), '25'); s + ' 0.0125 - 0.5*I' + sage: len(s) == 25 + True + sage: '{:=^ 25}'.format(CDF(1/80, -1/2)) + '===== 0.0125 - 0.5*I=====' + sage: format(float(3), '#.4') == format(CDF(3, 0), '#.4') + True + sage: format(CDF(1, 2), '=+20') + Traceback (most recent call last): + ... + ValueError: '=' alignment not allowed in complex format specifier """ - return format(complex(self), format_spec) + import re + match = re.match(r'^(.?[><=^])?' # 1: fill and align + r'([ +-]?)' # 2: sign + r'[^\d\.]*?0?(\d*)' # 3: width + r'.*?([eEfFgGn%])?$', # 4: type + format_spec) + if not match: + raise ValueError("invalid format specifier %s" % format_spec) + + # format floats without align and width + float_format = (format_spec[match.start(2):match.start(3)] + + format_spec[match.end(3):]) + + use_str_format = not match.group(4) + if use_str_format and self._complex.imag == 0: + result = format(self._complex.real, float_format) + elif use_str_format and self._complex.real == 0: + result = format(self._complex.imag, float_format) + '*I' + else: + real = format(self._complex.real, float_format) + imag = format(self._complex.imag, + '+' + format_spec[match.end(2):match.start(3)] + + format_spec[match.end(3):]) + result = f"{real} {imag[:1]} {imag[1:]}*I" + + width = match.group(3) + if width: + align = match.group(1) or '>' + if align.endswith('='): + raise ValueError("'=' alignment not allowed in " + "complex format specifier") + result = format(result, align + width) + return result def _latex_(self): """ From 36cb7386fd035bc67c644bf06993997851bbdd89 Mon Sep 17 00:00:00 2001 From: Jean-Florent Raymond Date: Mon, 27 Apr 2020 16:58:01 +0200 Subject: [PATCH 003/204] Added function layout_forest. Changed output of layout_tree from pairs to lists of size 2. Added a test for empty graph in layout_tree. --- src/sage/graphs/generic_graph.py | 82 ++++++++++++++++++++++++--- src/sage/graphs/generic_graph_pyx.pyx | 25 +++++--- src/sage/graphs/graph_plot.py | 3 +- 3 files changed, 95 insertions(+), 15 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index f4635557606..f62368d7b84 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -289,6 +289,7 @@ :meth:`~GenericGraph.layout_extend_randomly` | Extend randomly a partial layout :meth:`~GenericGraph.layout_circular` | Return a circular layout for this graph :meth:`~GenericGraph.layout_tree` | Return an ordered tree layout for this graph + :meth:`~GenericGraph.layout_tree` | Return an ordered forest layout for this graph :meth:`~GenericGraph.layout_graphviz` | Call ``graphviz`` to compute a layout of the vertices of this graph. :meth:`~GenericGraph._circle_embedding` | Set some vertices on a circle in the embedding of this graph. :meth:`~GenericGraph._line_embedding` | Set some vertices on a line in the embedding of this graph. @@ -19123,8 +19124,8 @@ def layout(self, layout=None, pos=None, dim=2, save_pos=False, **options): - ``layout`` -- string (default: ``None``); specifies a layout algorithm among ``"acyclic"``, ``"acyclic_dummy"``, ``"circular"``, - ``"ranked"``, ``"graphviz"``, ``"planar"``, ``"spring"``, or - ``"tree"`` + ``"ranked"``, ``"graphviz"``, ``"planar"``, ``"spring"``, + ``"forest"`` or ``"tree"`` - ``pos`` -- dictionary (default: ``None``); a dictionary of positions @@ -19186,6 +19187,7 @@ def layout(self, layout=None, pos=None, dim=2, save_pos=False, **options): ....: print("option {} : {}".format(key, value)) option by_component : Whether to do the spring layout by connected component -- a boolean. option dim : The dimension of the layout -- 2 or 3. + option 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'``. option heights : A dictionary mapping heights to the list of vertices at this height. option iterations : The number of times to execute the spring layout algorithm. option 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). @@ -19438,12 +19440,72 @@ def layout_circular(self, dim=2, center=(0, 0), radius=1, shift=0, angle=0, **op return self._circle_embedding(self.vertices(), center=(0, 0), radius=1, shift=0, angle=pi/2, return_dict=True) + def layout_forest(self, tree_orientation="down", forest_roots=None, + **options): + """ + Return an ordered forest layout for this graph. + + The function relies on :meth:`~GenericGraph.layout_tree` to deal with + each connected component. + + INPUT: + + - ``forest_roots`` -- an iterable of vertices (default: ``None``); + the root vertices of the trees in the forest; a vertex is chosen + close to the center of each component for which no root is specified + in ``forest_roots`` or if ``forest_roots`` is ``None`` + + - ``tree_orientation`` -- string (default: ``'down'``); the direction in + which the tree is growing, can be ``'up'``, ``'down'``, ``'left'`` or + ``'right'`` + + - ``**options`` -- other parameters ignored here + + EXAMPLES:: + + sage: G = graphs.RandomTree(4) + graphs.RandomTree(5) + graphs.RandomTree(6) + sage: p = G.layout_forest() + sage: G.plot(pos=p) # random + Graphics object consisting of 28 graphics primitives + + sage: H = graphs.PathGraph(5) + graphs.PathGraph(5) + graphs.BalancedTree(2,2) + sage: p = H.layout_forest(forest_roots=[14,3]) + sage: H.plot(pos=p) + Graphics object consisting of 32 graphics primitives + + TESTS:: + + sage: G = Graph(0) + sage: G.plot(layout='tree') + Graphics object consisting of 0 graphics primitives + """ + if not self: + return dict() + elif self.is_connected(): + if forest_roots is not None: + try: + tree_root = next(iter(forest_roots)) + except TypeError: + raise ValueError("forest_roots should be an iterable of vertices") + else: + tree_root = None + return layout_tree(self, tree_orientation, tree_root, dim, **options) + else: + # Compute the layout component by component + return layout_split(self.__class__.layout_tree, + self, + tree_orientation=tree_orientation, + forest_roots=forest_roots, + **options) + def layout_tree(self, tree_orientation="down", tree_root=None, dim=2, **options): r""" Return an ordered tree layout for this graph. - The graph must be a tree (no non-oriented cycles). + The graph must be a tree (no non-oriented cycles). In case of doubt + whether the graph is connected or not, prefer + :meth:`~GenericGraph.layout_forest`. INPUT: @@ -19519,10 +19581,16 @@ def layout_tree(self, tree_orientation="down", tree_root=None, Traceback (most recent call last): ... RuntimeError: cannot use tree layout on this graph: self.is_tree() returns False + sage: G = Graph(0) + sage: G.plot(layout='tree') + Graphics object consisting of 0 graphics primitives """ if dim != 2: raise ValueError('only implemented in 2D') + if not self: + return dict() + from sage.graphs.all import Graph if not Graph(self).is_tree(): raise RuntimeError("cannot use tree layout on this graph: " @@ -19579,7 +19647,7 @@ def slide(v, dx): x, y = pos[u] x += dx obstruction[y] = max(x + 1, obstruction[y]) - pos[u] = x, y + pos[u] = [x, y] nextlevel += children[u] level = nextlevel @@ -19598,12 +19666,12 @@ def slide(v, dx): # If p has no children, we draw it at the leftmost position # which has not been forbidden x = obstruction[y] - pos[p] = x, y + pos[p] = [x, y] else: # If p has children, we put v on a vertical line going # through the barycenter of its children x = sum(pos[c][0] for c in cp) / len(cp) - pos[p] = x, y + pos[p] = [x, y] ox = obstruction[y] if x < ox: slide(p, ox - x) @@ -19635,7 +19703,7 @@ def slide(v, dx): stick.append(t) if tree_orientation in ['right', 'left']: - return {p: (py, px) for p, (px, py) in iteritems(pos)} + return {p: [py, px] for p, [px, py] in iteritems(pos)} return pos diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index 0611477abd8..573f2f45a53 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -79,19 +79,30 @@ def layout_split(layout_function, G, **options): left = 0 buffer = 1/sqrt(len(G)) + forest_roots = list(options.get('forest_roots', [])) + on_embedding = options.get('on_embedding', None) + + if forest_roots or on_embedding: + options = copy(options) + options.pop('forest_roots', None) + options.pop('on_embedding', None) + for g in Gs: - if options.get('on_embedding', None): - em = options['on_embedding'] - options_g = copy(options) - # Restriction of `on_embedding` to `g` - options_g['on_embedding'] = {v: em[v] for v in g} - cur_pos = layout_function(g, **options_g) + if on_embedding: + # Restrict ``on_embedding`` to ``g`` + embedding_g = {v: on_embedding[v] for v in g} + cur_pos = layout_function(g, on_embedding=embedding_g, **options) + elif forest_roots: + # Find a root for ``g`` (if any) + tree_root = next((v for v in forest_roots if v in g), None) + cur_pos = layout_function(g, tree_root=tree_root, **options) else: cur_pos = layout_function(g, **options) + xmin = min(x[0] for x in cur_pos.values()) xmax = max(x[0] for x in cur_pos.values()) if len(g) > 1: - buffer = (xmax - xmin)/sqrt(len(g)) + buffer = max(1, (xmax - xmin)/sqrt(len(g))) for v, loc in cur_pos.items(): loc[0] += left - xmin + buffer pos[v] = loc diff --git a/src/sage/graphs/graph_plot.py b/src/sage/graphs/graph_plot.py index 8bb7708370f..8d308929e7a 100644 --- a/src/sage/graphs/graph_plot.py +++ b/src/sage/graphs/graph_plot.py @@ -113,7 +113,8 @@ '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\'``', + '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.', From e1cc6dcf99ff9ea5edb878b6efb7307dc56b4d0d Mon Sep 17 00:00:00 2001 From: Jean-Florent Raymond Date: Mon, 27 Apr 2020 17:34:07 +0200 Subject: [PATCH 004/204] fixed failing doctests --- src/sage/graphs/generic_graph.py | 34 +++++++++++++-------------- src/sage/graphs/generic_graph_pyx.pyx | 3 ++- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index f62368d7b84..528131c31b7 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -19195,7 +19195,7 @@ def layout(self, layout=None, pos=None, dim=2, save_pos=False, **options): option save_pos : Whether or not to save the computed position for the graph. option spring : Use spring layout to finalize the current layout. option tree_orientation : The direction of tree branches -- 'up', 'down', 'left' or 'right'. - option 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'`` + option 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'``. Some of them only apply to certain layout algorithms. For details, see :meth:`.layout_acyclic`, :meth:`.layout_planar`, @@ -19544,13 +19544,13 @@ def layout_tree(self, tree_orientation="down", tree_root=None, sage: G = graphs.BalancedTree(2, 2) sage: G.layout_tree(tree_root=0) - {0: (1.5, 0), - 1: (2.5, -1), - 2: (0.5, -1), - 3: (3.0, -2), - 4: (2.0, -2), - 5: (1.0, -2), - 6: (0.0, -2)} + {0: [1.5, 0], + 1: [2.5, -1], + 2: [0.5, -1], + 3: [3.0, -2], + 4: [2.0, -2], + 5: [1.0, -2], + 6: [0.0, -2]} sage: G = graphs.BalancedTree(2, 4) sage: G.plot(layout="tree", tree_root=0, tree_orientation="up") @@ -19562,15 +19562,15 @@ def layout_tree(self, tree_orientation="down", tree_root=None, sage: T.set_embedding({0: [1, 6, 3], 1: [2, 5, 0], 2: [1], 3: [4, 7, 8, 0], ....: 4: [3], 5: [1], 6: [0], 7: [3], 8: [3]}) sage: T.layout_tree() - {0: (2.166..., 0), - 1: (3.5, -1), - 2: (4.0, -2), - 3: (1.0, -1), - 4: (2.0, -2), - 5: (3.0, -2), - 6: (2.0, -1), - 7: (1.0, -2), - 8: (0.0, -2)} + {0: [2.166..., 0], + 1: [3.5, -1], + 2: [4.0, -2], + 3: [1.0, -1], + 4: [2.0, -2], + 5: [3.0, -2], + 6: [2.0, -1], + 7: [1.0, -2], + 8: [0.0, -2]} sage: T.plot(layout="tree", tree_root=3) Graphics object consisting of 18 graphics primitives diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index 573f2f45a53..2bd641f323a 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -79,7 +79,8 @@ def layout_split(layout_function, G, **options): left = 0 buffer = 1/sqrt(len(G)) - forest_roots = list(options.get('forest_roots', [])) + forest_roots = options.get('forest_roots', None) + forest_roots = list(forest_roots) if forest_roots else None on_embedding = options.get('on_embedding', None) if forest_roots or on_embedding: From c882cf4111ae86d60d82287f7777ef0e0c808143 Mon Sep 17 00:00:00 2001 From: Jean-Florent Raymond Date: Mon, 27 Apr 2020 20:43:44 +0200 Subject: [PATCH 005/204] one doctest + cut long lines --- src/sage/graphs/generic_graph.py | 15 ++++++++++++--- src/sage/graphs/generic_graph_pyx.pyx | 1 - 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 528131c31b7..625d2d8fe61 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -289,7 +289,7 @@ :meth:`~GenericGraph.layout_extend_randomly` | Extend randomly a partial layout :meth:`~GenericGraph.layout_circular` | Return a circular layout for this graph :meth:`~GenericGraph.layout_tree` | Return an ordered tree layout for this graph - :meth:`~GenericGraph.layout_tree` | Return an ordered forest layout for this graph + :meth:`~GenericGraph.layout_forest` | Return an ordered forest layout for this graph :meth:`~GenericGraph.layout_graphviz` | Call ``graphviz`` to compute a layout of the vertices of this graph. :meth:`~GenericGraph._circle_embedding` | Set some vertices on a circle in the embedding of this graph. :meth:`~GenericGraph._line_embedding` | Set some vertices on a line in the embedding of this graph. @@ -19478,6 +19478,14 @@ def layout_forest(self, tree_orientation="down", forest_roots=None, sage: G = Graph(0) sage: G.plot(layout='tree') Graphics object consisting of 0 graphics primitives + + The parameter ``forest_roots`` should be an iterable (or ``None``):: + + sage: H = graphs.PathGraph(5) + sage: p = H.layout_forest(forest_roots=3) + Traceback (most recent call last): + ... + TypeError: forest_roots should be an iterable of vertices """ if not self: return dict() @@ -19486,10 +19494,11 @@ def layout_forest(self, tree_orientation="down", forest_roots=None, try: tree_root = next(iter(forest_roots)) except TypeError: - raise ValueError("forest_roots should be an iterable of vertices") + raise TypeError('forest_roots should be an iterable' + ' of vertices') else: tree_root = None - return layout_tree(self, tree_orientation, tree_root, dim, **options) + return layout_tree(self, tree_orientation, tree_root, **options) else: # Compute the layout component by component return layout_split(self.__class__.layout_tree, diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index 2bd641f323a..be9913ddde2 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -109,7 +109,6 @@ def layout_split(layout_function, G, **options): pos[v] = loc left += xmax - xmin + buffer - if options.get('set_embedding', None): embedding = dict() for g in Gs: From d87d4e00c1660626c1389bf75dd52286f43aad43 Mon Sep 17 00:00:00 2001 From: Jean-Florent Raymond Date: Tue, 28 Apr 2020 10:52:11 +0200 Subject: [PATCH 006/204] trac 29605: fixed the last remaining failing doctest --- src/sage/graphs/generic_graph.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 625d2d8fe61..4133f52047c 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -19256,12 +19256,12 @@ def layout_spring(self, by_component=True, **options): sage: g = graphs.LadderGraph(3) #TODO!!!! sage: g.layout_spring() - {0: [0.73..., -0.29...], - 1: [1.37..., 0.30...], - 2: [2.08..., 0.89...], - 3: [1.23..., -0.83...], - 4: [1.88..., -0.30...], - 5: [2.53..., 0.22...]} + {0: [1.0, -0.29...], + 1: [1.64..., 0.30...], + 2: [2.34..., 0.89...], + 3: [1.49..., -0.83...], + 4: [2.14..., -0.30...], + 5: [2.80..., 0.22...]} sage: g = graphs.LadderGraph(7) sage: g.plot(layout="spring") Graphics object consisting of 34 graphics primitives From 16b56a8ab1030548f61ada5ee35517897d7f3542 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Fri, 11 Jan 2019 15:24:43 -0800 Subject: [PATCH 007/204] new methods for FusionRings: s-matrix, q_dimension, twists etc --- src/doc/en/reference/references/index.rst | 8 + .../combinat/root_system/weyl_characters.py | 260 +++++++++++++++++- 2 files changed, 253 insertions(+), 15 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index f488f0a8cbb..311720794a8 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -4040,6 +4040,10 @@ REFERENCES: **N** +.. [NaiRow2011] Naidu and Rowell, A finiteness property for braided fusion + categories. Algebr. Represent. Theory 14 (2011), no. 5, 837–855. + :arXiv:`0903.4157`. + .. [Nas1950] John Nash. *Equilibrium points in n-person games.* Proceedings of the National Academy of Sciences 36.1 (1950): 48-49. @@ -4529,6 +4533,10 @@ REFERENCES: .. [Rot2006] Ron Roth, Introduction to Coding Theory, Cambridge University Press, 2006 +.. [Row2006] Eric Rowell, From quantum groups to unitary modular tensor categories. + In Representations of algebraic groups, quantum groups, and Lie algebras, + Contemp. Math., 413, Amer. Math. Soc., Providence, RI, 2006. + :arXiv:`math/0503226`. .. [RR1997] Arun Ram and Jeffrey Remmel. *Applications of the Frobenius formulas and the characters of the symmetric group and the diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 744fcda226e..bd0d0e8b200 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -11,16 +11,21 @@ from __future__ import print_function import sage.combinat.root_system.branching_rules +from operator import mul from sage.categories.all import Category, Algebras, AlgebrasWithBasis from sage.combinat.free_module import CombinatorialFreeModule +from sage.combinat.q_analogues import q_int from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.root_system.root_system import RootSystem +from sage.matrix.special import diagonal_matrix +from sage.matrix.constructor import matrix from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet from sage.misc.functional import is_even from sage.misc.misc import inject_variable -from sage.rings.all import ZZ +from sage.rings.all import ZZ, CC +from sage.rings.number_field.number_field import CyclotomicField class WeylCharacterRing(CombinatorialFreeModule): @@ -92,7 +97,7 @@ class WeylCharacterRing(CombinatorialFreeModule): https://doc.sagemath.org/html/en/thematic_tutorials/lie.html """ @staticmethod - def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None): + def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False): """ TESTS:: @@ -108,9 +113,9 @@ def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None): prefix = ct[0]+str(ct[1]) else: prefix = repr(ct) - return super(WeylCharacterRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k) + return super(WeylCharacterRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate) - def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None): + def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False): """ EXAMPLES:: @@ -146,7 +151,7 @@ def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None): def next_level(wt): return [wt + la for la in fw if self.level(wt + la) <= k] B = list(RecursivelyEnumeratedSet([self._space.zero()], next_level)) - B = [self._space.from_vector_notation(wt, style=style) for wt in B] + B = [self._space.from_vector_notation(wt, style="coroots") for wt in B] else: B = self._space @@ -162,6 +167,22 @@ def next_level(wt): # Register the partial inverse as a conversion self.register_conversion(self.retract) + # Record properties of the FusionRing + if k is not None: + #TODO: implement conjugate functionality + if ct[0] in ['A','D','E']: + self._m_g = 1 + self._nf = 1 + elif ct[0] in ['B', 'C', 'F']: + self._m_g = 2 + self._nf = 2 + else: + self._m_g = 3 + self._nf = 1 + h_check = ct.dual_coxeter_number() + self._l = self._m_g * (self._k + h_check) + self._conj = (-1)**conjugate + @cached_method def ambient(self): """ @@ -195,7 +216,7 @@ def lift_on_basis(self, irr): sage: A2.lift_on_basis(v) 2*a2(1,1,1) + a2(1,2,0) + a2(1,0,2) + a2(2,1,0) + a2(2,0,1) + a2(0,1,2) + a2(0,2,1) - This is consistent with the analogous calculation with symmetric + This is consistent with the analoguous calculation with symmetric Schur functions:: sage: s = SymmetricFunctions(QQ).s() @@ -1221,13 +1242,12 @@ def dual(self): for k in d) def highest_weight(self): - r""" - Return the parametrizing dominant weight of an irreducible - character or simple element of a FusionRing. - - This method is only available for basis elements. + """ + This method is only available for basis elements. Returns the + parametrizing dominant weight of an irreducible character or + simple element of a FusionRing. - EXAMPLES:: + Examples:: sage: G2 = WeylCharacterRing("G2", style="coroots") sage: [x.highest_weight() for x in [G2(1,0),G2(0,1)]] @@ -1598,6 +1618,76 @@ def multiplicity(self, other): raise ValueError("{} is not irreducible".format(other)) return self.coefficient(other.support()[0]) + def is_simple_obj(self): + """ + Determine whether element is a simple object of the FusionRing. + """ + return self.parent()._k is not None and len(self.monomial_coefficients())==1 + + def twist(self): + r""" + Compute the object's twist. Returns a rational number `h_X` such that + `e^{(i \pi h_X)}` is the twist of `X`. + + We compute the twists following p.7 of [Row2006]_, noting that the bilinear form + is normalized so that `\langle\alpha, \alpha\rangle = 2` for SHORT roots. + """ + if not self.is_simple_obj(): + raise ValueError("quantum twist is only available for simple objects of a FusionRing") + rho = sum(self.parent().positive_roots())/2 + lam = self.highest_weight() + inner = lam.inner_product(lam+2*rho) + twist = self.parent()._conj*self.parent()._nf*inner/self.parent().fusion_l() + #Reduce to canonical form + while twist > 2: + twist -= 2 + while twist < 0: + twist += 2 + return twist + + def q_dimension(self): + r"""" + INPUT: + + - ``b`` -- a fusion ring basis element. + + This returns the quantum dimension as an element of the cyclotomic + field of the `2l`-th roots of unity, where `l = m(k+h^\vee)` + with `m=1,2,3` depending on whether type is simply, doubly or + triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. + + EXAMPLE:: + + sage: B22=FusionRing("B2",2) + sage: [(b.q_dimension())^2 for b in B22.basis()] + [1, 5, 4, 1, 5, 4] + """ + if not self.is_simple_obj(): + raise ValueError("quantum twist is only available for simple objects of a FusionRing") + lam = self.highest_weight() + space = self.parent().space() + rho = space.rho() + l = self.parent().fusion_l() + num = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(lam+rho)) for alpha in space.positive_roots()], 1) + den = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(rho)) for alpha in space.positive_roots()], 1) + expr = num/den + pr = expr.parent().ring() + q = pr.gen() + expr = pr(expr) + expr = expr.substitute(q=q**2)/q**(expr.degree()) + zet = self.parent().q_field.gen() + return expr.substitute(q=zet) + + def fusion_matrix(self): + """ + Return a matrix containing the object's fusion coefficients. + """ + if not self.is_simple_obj(): + raise ValueError("fusion matrix is only available for simple objects of a FusionRing") + ord_basis = self.parent().ordered_basis() + wts = [x.highest_weight() for x in ord_basis] + rows = [[wt in (self*obj).monomial_coefficients() for wt in wts] for obj in ord_basis] + return matrix(rows) def irreducible_character_freudenthal(hwv, debug=False): r""" @@ -2254,7 +2344,7 @@ class FusionRing(WeylCharacterRing): - [Walton1990]_ """ @staticmethod - def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots"): + def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False): """ Normalize input to ensure a unique representation. @@ -2279,7 +2369,22 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots"): sage: TestSuite(D41).run() """ return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, - prefix=prefix, style=style, k=k) + prefix=prefix, style=style, k=k, conjugate=conjugate) + + @lazy_attribute + def q_field(self): + """ + Return the generator of the cyclotomic field of 2l-th roots of unity, where + l is the fusion_l of the category (see above). + + This field contains the twists, categorical dimensions, and the entries of the + S-matrix. + """ + self._K = CyclotomicField(2*self._l) + return self._K + + def _element_constructor(self,weight): + return self._parent._element_constructor(self._parent,weight) def some_elements(self): """ @@ -2294,8 +2399,133 @@ def some_elements(self): return [self.monomial(x) for x in self.fundamental_weights() if self.level(x) <= self._k] - def fusion_labels(self, labels=None, key=str): + def fusion_k(self): r""" + Return the level of the FusionRing. + + EXAMPLES:: + sage: B22=FusionRing('B2',2) + sage: B22.fusion_k() + 2 + """ + return self._k + + def fusion_l(self): + r""" + Returns the product `m_g(k + h^\vee)`, where `m_g` denotes the + square of the ratio of the lengths of long to short roots of + the underlying Lie algebra, `k` denotes the level of the FusionRing, + and `h^\vee` denotes the dual Coxeter number of the underlying Lie + algebra. + + This value is used to define the associated root of unity q. + + EXAMPLES:: + sage: B22=FusionRing('B2',2) + sage: B22.fusion_l() + 10 + sage: D52=FusionRing('D5',2) + sage: D52.fusion_l() + 10 + """ + return self._l + + def twists_matrix(self): + r""" + Return a diagonal matrix describing the twist corresponding to + each simple object in the ``FusionRing``. + + EXAMPLES:: + + sage: B22=FusionRing('B2',2) + sage: B22.twists_matrix() + [ 0 0 0 0 0 0] + [ 0 2 0 0 0 0] + [ 0 0 4/5 0 0 0] + [ 0 0 0 6/5 0 0] + [ 0 0 0 0 1/2 0] + [ 0 0 0 0 0 3/2] + """ + return diagonal_matrix(obj.twist() for obj in self.ordered_basis()) + + def q_dims(self): + """ + Return a list of quantum dimensions of the simple objects. + """ + return [x.q_dimension() for x in self.ordered_basis()] + + def ordered_basis(self): + """ + Returns a basis of simple objects ordered according to + [NaiRow2011]_. For type A level 1, the basis is ordered by the + fundamental weights. The order is described in [Fuchs1994]_. + + """ + ct = self._cartan_type[0] + k = self._k + r = self.rank() + wts = self.fundamental_weights() + ord_basis = list() + #Order bases for dimension 2 algebras + if self.dimension() == 2: + ord_basis.append(self(wts[1]*0)) + for wt in self.basis(): + wt = wt.highest_weight() + if wt.inner_product(wt) > 0: + ord_basis.append(self(wt)) + if ct == 'A' and k == 1: + ord_basis = [self(wts[1]*0)] + ord_basis += [self(wts[i+1]) for i in range(r)] + if ct == 'A' and k == 2: + #TODO: generalize to higher rank + if r == 1: + ord_basis = [self(wts[1]*0), self(wts[1]), self(wts[1]*2)] + if ct == 'B' and k == 2: + ord_basis = [self(wts[1]*0), self(2*wts[1])] + ord_basis += [self(wts[i]) for i in range(1, r)] + ord_basis += [self(2*wts[r]), self(wts[r]), self(wts[1]+wts[r])] + if ct == 'D' and k == 1: + if r % 2 == 0: + ord_basis = sorted(self.basis().values()) + else: + temp = sorted(self.basis().values(), reverse=1) + ord_basis = [temp.pop()] + ord_basis.extend(temp) + if ct == 'D' and k == 2: + ord_basis = [self(wts[1]*0), self(2*wts[1]), self(2*wts[r-1]), self(2*wts[r])] + ord_basis += [self(wts[i]) for i in range(1, r-1)] + ord_basis += [self(wts[r-1] + wts[r]), self(wts[r-1]), self(wts[r])] + ord_basis += [self(wts[1] + wts[r-1]), self(wts[1] + wts[r])] + if ct == 'E' and k == 1: + if r == 6: + ord_basis = [self(wts[1]*0), self(wts[1]), self(wts[6])] + if ct == 'E' and k == 2: + if r == 8: + ord_basis = [self(wts[1]*0), self(wts[8]), self(wts[1])] + if not ord_basis: + raise ValueError("ordered basis not yet available for this FusionRing") + return ord_basis + + def s_matrix(self): + """ + Return the S-matrix of the FusionRing. + """ + dims = self.q_dims() + ord_basis = self.ordered_basis() + twists = [x.twist() for x in ord_basis] + fusion_mats = [x.fusion_matrix() for x in ord_basis] + rng = range(len(ord_basis)) + #TODO: find smallest field containing entries of S + q = self.q_field + S = matrix(CC, len(ord_basis)) + for i in rng: + for j in rng: + S[i,j] = sum(fusion_mats[i][k,j]*(-1)**twists[k]*dims[k] for k in rng) + S[i,j] /= (-1)**(twists[i]+twists[j]) + return S + + def fusion_labels(self, labels=None): + """ Set the labels of the basis. INPUT: From dd4f94820316cea0c3bd7e96f6fc2d3cb2126a60 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Thu, 17 Jan 2019 13:59:38 -0800 Subject: [PATCH 008/204] created FusionRing.Element class --- .../combinat/root_system/weyl_characters.py | 142 +++++++++--------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index bd0d0e8b200..b930149674b 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -1618,77 +1618,6 @@ def multiplicity(self, other): raise ValueError("{} is not irreducible".format(other)) return self.coefficient(other.support()[0]) - def is_simple_obj(self): - """ - Determine whether element is a simple object of the FusionRing. - """ - return self.parent()._k is not None and len(self.monomial_coefficients())==1 - - def twist(self): - r""" - Compute the object's twist. Returns a rational number `h_X` such that - `e^{(i \pi h_X)}` is the twist of `X`. - - We compute the twists following p.7 of [Row2006]_, noting that the bilinear form - is normalized so that `\langle\alpha, \alpha\rangle = 2` for SHORT roots. - """ - if not self.is_simple_obj(): - raise ValueError("quantum twist is only available for simple objects of a FusionRing") - rho = sum(self.parent().positive_roots())/2 - lam = self.highest_weight() - inner = lam.inner_product(lam+2*rho) - twist = self.parent()._conj*self.parent()._nf*inner/self.parent().fusion_l() - #Reduce to canonical form - while twist > 2: - twist -= 2 - while twist < 0: - twist += 2 - return twist - - def q_dimension(self): - r"""" - INPUT: - - - ``b`` -- a fusion ring basis element. - - This returns the quantum dimension as an element of the cyclotomic - field of the `2l`-th roots of unity, where `l = m(k+h^\vee)` - with `m=1,2,3` depending on whether type is simply, doubly or - triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. - - EXAMPLE:: - - sage: B22=FusionRing("B2",2) - sage: [(b.q_dimension())^2 for b in B22.basis()] - [1, 5, 4, 1, 5, 4] - """ - if not self.is_simple_obj(): - raise ValueError("quantum twist is only available for simple objects of a FusionRing") - lam = self.highest_weight() - space = self.parent().space() - rho = space.rho() - l = self.parent().fusion_l() - num = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(lam+rho)) for alpha in space.positive_roots()], 1) - den = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(rho)) for alpha in space.positive_roots()], 1) - expr = num/den - pr = expr.parent().ring() - q = pr.gen() - expr = pr(expr) - expr = expr.substitute(q=q**2)/q**(expr.degree()) - zet = self.parent().q_field.gen() - return expr.substitute(q=zet) - - def fusion_matrix(self): - """ - Return a matrix containing the object's fusion coefficients. - """ - if not self.is_simple_obj(): - raise ValueError("fusion matrix is only available for simple objects of a FusionRing") - ord_basis = self.parent().ordered_basis() - wts = [x.highest_weight() for x in ord_basis] - rows = [[wt in (self*obj).monomial_coefficients() for wt in wts] for obj in ord_basis] - return matrix(rows) - def irreducible_character_freudenthal(hwv, debug=False): r""" Return the dictionary of multiplicities for the irreducible @@ -2577,3 +2506,74 @@ def fusion_labels(self, labels=None): d[t] = labels[j] inject_variable(labels[j], b) self._fusion_labels = d + + class Element(WeylCharacterRing.Element): + """ + A class for FusionRing elements + """ + def is_simple_obj(self): + """ + Determine whether element is a simple object of the FusionRing. + """ + return self.parent()._k is not None and len(self.monomial_coefficients())==1 + + def twist(self): + r""" + Compute the object's twist. Returns a rational number `h_X` such that + `e^{(i \pi h_X)}` is the twist of `X`. + + We compute the twists following p.7 of [Row2006]_, noting that the bilinear form + is normalized so that `\langle\alpha, \alpha\rangle = 2` for SHORT roots. + """ + if not self.is_simple_obj(): + raise ValueError("quantum twist is only available for simple objects of a FusionRing") + rho = sum(self.parent().positive_roots())/2 + lam = self.highest_weight() + inner = lam.inner_product(lam+2*rho) + twist = self.parent()._conj*self.parent()._nf*inner/self.parent().fusion_l() + #Reduce to canonical form + while twist > 2: + twist -= 2 + while twist < 0: + twist += 2 + return twist + + def q_dimension(self): + r"""" + This returns the quantum dimension as an element of the cyclotomic + field of the `2l`-th roots of unity, where `l = m(k+h^\vee)` + with `m=1,2,3` depending on whether type is simply, doubly or + triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. + + EXAMPLE:: + + sage: B22=FusionRing("B2",2) + sage: [(b.q_dimension())^2 for b in B22.basis()] + [1, 5, 4, 1, 5, 4] + """ + if not self.is_simple_obj(): + raise ValueError("quantum twist is only available for simple objects of a FusionRing") + lam = self.highest_weight() + space = self.parent().space() + rho = space.rho() + l = self.parent().fusion_l() + num = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(lam+rho)) for alpha in space.positive_roots()], 1) + den = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(rho)) for alpha in space.positive_roots()], 1) + expr = num/den + pr = expr.parent().ring() + q = pr.gen() + expr = pr(expr) + expr = expr.substitute(q=q**2)/q**(expr.degree()) + zet = self.parent().q_field.gen() + return expr.substitute(q=zet) + + def fusion_matrix(self): + """ + Return a matrix containing the object's fusion coefficients. + """ + if not self.is_simple_obj(): + raise ValueError("fusion matrix is only available for simple objects of a FusionRing") + ord_basis = self.parent().ordered_basis() + wts = [x.highest_weight() for x in ord_basis] + rows = [[wt in (self*obj).monomial_coefficients() for wt in wts] for obj in ord_basis] + return matrix(rows) From eb09e3feb3278d2a0579a4eb1a84024becca14a6 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Tue, 29 Jan 2019 12:28:01 -0800 Subject: [PATCH 009/204] doctests added, updated q_field, and S-matrix computation --- .../combinat/root_system/weyl_characters.py | 107 ++++++++++++++++-- 1 file changed, 96 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index b930149674b..52daa76c8e9 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -2308,8 +2308,17 @@ def q_field(self): This field contains the twists, categorical dimensions, and the entries of the S-matrix. + + EXAMPLES:: + + sage: B22=FusionRing("B2",2) + sage: B22.q_field + Cyclotomic Field of order 40 and degree 16 + sage: A11=FusionRing('A1',1) + sage: A11.q_field + Cyclotomic Field of order 12 and degree 4 """ - self._K = CyclotomicField(2*self._l) + self._K = CyclotomicField(4*self._l) return self._K def _element_constructor(self,weight): @@ -2333,6 +2342,7 @@ def fusion_k(self): Return the level of the FusionRing. EXAMPLES:: + sage: B22=FusionRing('B2',2) sage: B22.fusion_k() 2 @@ -2350,6 +2360,7 @@ def fusion_l(self): This value is used to define the associated root of unity q. EXAMPLES:: + sage: B22=FusionRing('B2',2) sage: B22.fusion_l() 10 @@ -2378,8 +2389,17 @@ def twists_matrix(self): return diagonal_matrix(obj.twist() for obj in self.ordered_basis()) def q_dims(self): - """ + r""" Return a list of quantum dimensions of the simple objects. + + EXAMPLES:: + + sage: F41=FusionRing('F4',1,conjugate=True) + sage: F41.q_dims() + [1, -zeta80^24 + zeta80^16 + 1] + sage: B22=FusionRing("B2",2) + sage: B22.q_dims() + [1, 1, 2, 2, -2*zeta40^12 + 2*zeta40^8 + 1, -2*zeta40^12 + 2*zeta40^8 + 1] """ return [x.q_dimension() for x in self.ordered_basis()] @@ -2435,22 +2455,54 @@ def ordered_basis(self): raise ValueError("ordered basis not yet available for this FusionRing") return ord_basis - def s_matrix(self): + def s_ij(self, elt_i, elt_j, fusion_mat_i=[]): """ - Return the S-matrix of the FusionRing. + Return the element of the S-matrix of this FusionRing corresponding to + the given elements. + + EXAMPLES:: + + #TODO: update docstring using next iteration of ordered basis method """ dims = self.q_dims() ord_basis = self.ordered_basis() twists = [x.twist() for x in ord_basis] + rng = range(len(ord_basis)) + j = ord_basis.index(elt_j) + fusion_matrix = fusion_mat_i if fusion_mat_i else elt_i.fusion_matrix() + q = self.q_field.gen() + l = self.fusion_l() + s_ij = sum(fusion_matrix[k,j]*q**(2*l*twists[k])*dims[k] for k in rng) + s_ij *= q**(-2*l*(elt_i.twist() + elt_j.twist())) + return s_ij + + def s_matrix(self): + r""" + Return the S-matrix of this FusionRing. + + EXAMPLES:: + + sage: D91=FusionRing('D9',1) + sage: D91.s_matrix() + [ 1 1 1 1] + [ 1 1 -1 -1] + [ 1 -1 -zeta68^17 zeta68^17] + [ 1 -1 zeta68^17 -zeta68^17] + + sage: D41=FusionRing('D4',1) + sage: D41.s_matrix() + [ 1 1 1 1] + [ 1 1 -1 -1] + [ 1 -1 1 -1] + [ 1 -1 -1 1] + """ + ord_basis = self.ordered_basis() fusion_mats = [x.fusion_matrix() for x in ord_basis] rng = range(len(ord_basis)) - #TODO: find smallest field containing entries of S - q = self.q_field - S = matrix(CC, len(ord_basis)) + S = matrix(self.q_field, len(ord_basis)) for i in rng: for j in rng: - S[i,j] = sum(fusion_mats[i][k,j]*(-1)**twists[k]*dims[k] for k in rng) - S[i,j] /= (-1)**(twists[i]+twists[j]) + S[i,j] = self.s_ij(ord_basis[i], ord_basis[j], fusion_mats[i]) return S def fusion_labels(self, labels=None): @@ -2514,6 +2566,17 @@ class Element(WeylCharacterRing.Element): def is_simple_obj(self): """ Determine whether element is a simple object of the FusionRing. + + EXAMPLES:: + + sage: B22=FusionRing("B2",2) + sage: elt=B22.some_elements()[0] + sage: elt.is_simple_obj() + True + sage: elt**2 + B22(0,0) + B22(0,2) + B22(2,0) + sage: (elt**2).is_simple_obj() + False """ return self.parent()._k is not None and len(self.monomial_coefficients())==1 @@ -2524,6 +2587,19 @@ def twist(self): We compute the twists following p.7 of [Row2006]_, noting that the bilinear form is normalized so that `\langle\alpha, \alpha\rangle = 2` for SHORT roots. + + EXAMPLES:: + + sage: G21=FusionRing('G2',1) + sage: G21.basis() + Finite family {(0, 0, 0): G21(0,0), (1, 0, -1): G21(1,0)} + sage: G21(1,0).twist() + 4/5 + sage: F41=FusionRing('F4',1,conjugate=True) + sage: F41.basis() + Finite family {(0, 0, 0, 0): F41(0,0,0,0), (1, 0, 0, 0): F41(0,0,0,1)} + sage: F41(0,0,0,1).twist() + 4/5 """ if not self.is_simple_obj(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") @@ -2561,15 +2637,24 @@ def q_dimension(self): den = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(rho)) for alpha in space.positive_roots()], 1) expr = num/den pr = expr.parent().ring() - q = pr.gen() + q = pr.gen()**2 expr = pr(expr) expr = expr.substitute(q=q**2)/q**(expr.degree()) zet = self.parent().q_field.gen() return expr.substitute(q=zet) def fusion_matrix(self): - """ + r""" Return a matrix containing the object's fusion coefficients. + + EXAMPLES:: + + sage: G21=FusionRing('G2',1) + sage: G21.basis() + Finite family {(0, 0, 0): G21(0,0), (1, 0, -1): G21(1,0)} + sage: G21(1,0).fusion_matrix() + [0 1] + [1 1] """ if not self.is_simple_obj(): raise ValueError("fusion matrix is only available for simple objects of a FusionRing") From 7be38baf1ea4689f38445e530c322638c7c8533f Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Fri, 1 Feb 2019 13:42:01 -0800 Subject: [PATCH 010/204] revised doctest --- src/sage/combinat/root_system/weyl_characters.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 52daa76c8e9..6fb11e4d11a 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -2408,7 +2408,6 @@ def ordered_basis(self): Returns a basis of simple objects ordered according to [NaiRow2011]_. For type A level 1, the basis is ordered by the fundamental weights. The order is described in [Fuchs1994]_. - """ ct = self._cartan_type[0] k = self._k @@ -2569,13 +2568,14 @@ def is_simple_obj(self): EXAMPLES:: - sage: B22=FusionRing("B2",2) - sage: elt=B22.some_elements()[0] - sage: elt.is_simple_obj() + sage: A22=FusionRing("A2",2) + sage: x = A22(1,0); x + A22(1,0) + sage: x.is_simple_obj() True - sage: elt**2 - B22(0,0) + B22(0,2) + B22(2,0) - sage: (elt**2).is_simple_obj() + sage: x^2 + A22(0,1) + A22(2,0) + sage: (x^2).is_simple_obj() False """ return self.parent()._k is not None and len(self.monomial_coefficients())==1 From a5bc48ff83422c2a3c85eae779d5cf0d5a9da940 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Fri, 1 Feb 2019 15:24:17 -0800 Subject: [PATCH 011/204] work on revising self.s_ij --- .../combinat/root_system/weyl_characters.py | 66 ++++++++++++------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 6fb11e4d11a..eec82c8fa07 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -2301,10 +2301,19 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug prefix=prefix, style=style, k=k, conjugate=conjugate) @lazy_attribute + def _q_field(self): + """ + The cyclotomic field of 4l-th roots of unity, where + l is the fusion_l of the category (see above). Call this + lazy attribute via the method `self.q_field()`. + """ + self._K = CyclotomicField(4*self._l) + return self._K + def q_field(self): """ - Return the generator of the cyclotomic field of 2l-th roots of unity, where - l is the fusion_l of the category (see above). + Return the cyclotomic field of 4l-th roots of unity, where + `l` is the ``fusion_l`` of the category (see above). This field contains the twists, categorical dimensions, and the entries of the S-matrix. @@ -2312,14 +2321,13 @@ def q_field(self): EXAMPLES:: sage: B22=FusionRing("B2",2) - sage: B22.q_field + sage: B22.q_field() Cyclotomic Field of order 40 and degree 16 sage: A11=FusionRing('A1',1) - sage: A11.q_field + sage: A11.q_field() Cyclotomic Field of order 12 and degree 4 """ - self._K = CyclotomicField(4*self._l) - return self._K + return self._q_field def _element_constructor(self,weight): return self._parent._element_constructor(self._parent,weight) @@ -2454,14 +2462,9 @@ def ordered_basis(self): raise ValueError("ordered basis not yet available for this FusionRing") return ord_basis - def s_ij(self, elt_i, elt_j, fusion_mat_i=[]): + def s_ij_legacy(self, elt_i, elt_j, fusion_mat_i=[]): """ - Return the element of the S-matrix of this FusionRing corresponding to - the given elements. - - EXAMPLES:: - - #TODO: update docstring using next iteration of ordered basis method + Remove this soon """ dims = self.q_dims() ord_basis = self.ordered_basis() @@ -2469,12 +2472,31 @@ def s_ij(self, elt_i, elt_j, fusion_mat_i=[]): rng = range(len(ord_basis)) j = ord_basis.index(elt_j) fusion_matrix = fusion_mat_i if fusion_mat_i else elt_i.fusion_matrix() - q = self.q_field.gen() + q = self.q_field().gen() l = self.fusion_l() s_ij = sum(fusion_matrix[k,j]*q**(2*l*twists[k])*dims[k] for k in rng) s_ij *= q**(-2*l*(elt_i.twist() + elt_j.twist())) return s_ij + def s_ij(self, elt_i, elt_j): + """ + Return the element of the S-matrix of this FusionRing corresponding to + the given elements. + + INPUT: + + - ``elt_i``, ``elt_j`` -- elements of the fusion basis + + EXAMPLES:: + + #TODO: update docstring using next iteration of ordered basis method + """ + l = self.fusion_l() + K = self.q_field() + q = K.gen() + ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) + return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) + def s_matrix(self): r""" Return the S-matrix of this FusionRing. @@ -2498,7 +2520,7 @@ def s_matrix(self): ord_basis = self.ordered_basis() fusion_mats = [x.fusion_matrix() for x in ord_basis] rng = range(len(ord_basis)) - S = matrix(self.q_field, len(ord_basis)) + S = matrix(self.q_field(), len(ord_basis)) for i in rng: for j in rng: S[i,j] = self.s_ij(ord_basis[i], ord_basis[j], fusion_mats[i]) @@ -2562,7 +2584,7 @@ class Element(WeylCharacterRing.Element): """ A class for FusionRing elements """ - def is_simple_obj(self): + def is_simple_object(self): """ Determine whether element is a simple object of the FusionRing. @@ -2571,11 +2593,11 @@ def is_simple_obj(self): sage: A22=FusionRing("A2",2) sage: x = A22(1,0); x A22(1,0) - sage: x.is_simple_obj() + sage: x.is_simple_object() True sage: x^2 A22(0,1) + A22(2,0) - sage: (x^2).is_simple_obj() + sage: (x^2).is_simple_object() False """ return self.parent()._k is not None and len(self.monomial_coefficients())==1 @@ -2601,7 +2623,7 @@ def twist(self): sage: F41(0,0,0,1).twist() 4/5 """ - if not self.is_simple_obj(): + if not self.is_simple_object(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") rho = sum(self.parent().positive_roots())/2 lam = self.highest_weight() @@ -2627,7 +2649,7 @@ def q_dimension(self): sage: [(b.q_dimension())^2 for b in B22.basis()] [1, 5, 4, 1, 5, 4] """ - if not self.is_simple_obj(): + if not self.is_simple_object(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") lam = self.highest_weight() space = self.parent().space() @@ -2640,7 +2662,7 @@ def q_dimension(self): q = pr.gen()**2 expr = pr(expr) expr = expr.substitute(q=q**2)/q**(expr.degree()) - zet = self.parent().q_field.gen() + zet = self.parent().q_field().gen() return expr.substitute(q=zet) def fusion_matrix(self): @@ -2656,7 +2678,7 @@ def fusion_matrix(self): [0 1] [1 1] """ - if not self.is_simple_obj(): + if not self.is_simple_object(): raise ValueError("fusion matrix is only available for simple objects of a FusionRing") ord_basis = self.parent().ordered_basis() wts = [x.highest_weight() for x in ord_basis] From 5438494edc15effd7dce7ef5c3658411ceac243a Mon Sep 17 00:00:00 2001 From: Galit Anikeeva Date: Tue, 28 Apr 2020 19:45:06 -0700 Subject: [PATCH 012/204] Update to py3. Fix doctests. --- src/sage/combinat/root_system/weyl_characters.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index eec82c8fa07..ee40a4bdc06 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -10,6 +10,7 @@ # **************************************************************************** from __future__ import print_function +from functools import reduce import sage.combinat.root_system.branching_rules from operator import mul from sage.categories.all import Category, Algebras, AlgebrasWithBasis @@ -2523,10 +2524,10 @@ def s_matrix(self): S = matrix(self.q_field(), len(ord_basis)) for i in rng: for j in rng: - S[i,j] = self.s_ij(ord_basis[i], ord_basis[j], fusion_mats[i]) + S[i,j] = self.s_ij(ord_basis[i], ord_basis[j]) return S - def fusion_labels(self, labels=None): + def fusion_labels(self, labels=None, key=str): """ Set the labels of the basis. @@ -2647,7 +2648,7 @@ def q_dimension(self): sage: B22=FusionRing("B2",2) sage: [(b.q_dimension())^2 for b in B22.basis()] - [1, 5, 4, 1, 5, 4] + [1, 4, 5, 1, 5, 4] """ if not self.is_simple_object(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") From 68004038f3bb392291e2e28ddf6dafb35a1d5ef1 Mon Sep 17 00:00:00 2001 From: Jean-Florent Raymond Date: Fri, 1 May 2020 10:32:11 +0200 Subject: [PATCH 013/204] trac 29605: Fixed bug in the base case when self is a tree + added corresponding doctest + simplified method --- src/sage/graphs/generic_graph.py | 19 ++++++++----------- src/sage/graphs/generic_graph_pyx.pyx | 7 +++++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 4133f52047c..62ba07fe45b 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -19476,9 +19476,16 @@ def layout_forest(self, tree_orientation="down", forest_roots=None, TESTS:: sage: G = Graph(0) - sage: G.plot(layout='tree') + sage: G.plot(layout='forest') Graphics object consisting of 0 graphics primitives + Works for forests that are trees:: + + sage: g = graphs.StarGraph(4) + sage: p = g.layout_forest(forest_roots=[1]) + sage: sorted(p.items()) + [(0, [2.0, -1]), (1, [2.0, 0]), (2, [3.0, -2]), (3, [2.0, -2]), (4, [1.0, -2])] + The parameter ``forest_roots`` should be an iterable (or ``None``):: sage: H = graphs.PathGraph(5) @@ -19489,16 +19496,6 @@ def layout_forest(self, tree_orientation="down", forest_roots=None, """ if not self: return dict() - elif self.is_connected(): - if forest_roots is not None: - try: - tree_root = next(iter(forest_roots)) - except TypeError: - raise TypeError('forest_roots should be an iterable' - ' of vertices') - else: - tree_root = None - return layout_tree(self, tree_orientation, tree_root, **options) else: # Compute the layout component by component return layout_split(self.__class__.layout_tree, diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index be9913ddde2..3ec3373edfe 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -79,9 +79,12 @@ def layout_split(layout_function, G, **options): left = 0 buffer = 1/sqrt(len(G)) - forest_roots = options.get('forest_roots', None) - forest_roots = list(forest_roots) if forest_roots else None on_embedding = options.get('on_embedding', None) + forest_roots = options.get('forest_roots', None) + try: + forest_roots = list(forest_roots) if forest_roots else None + except TypeError: + raise TypeError('forest_roots should be an iterable of vertices') if forest_roots or on_embedding: options = copy(options) From 14bcaca22d72849e89860691fb43229a90c194b8 Mon Sep 17 00:00:00 2001 From: Jean-Florent Raymond Date: Sat, 2 May 2020 11:36:00 +0200 Subject: [PATCH 014/204] fixed patchbot warning in graph_plot.py --- src/sage/graphs/graph_plot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/graph_plot.py b/src/sage/graphs/graph_plot.py index 8d308929e7a..38c30cdecfc 100644 --- a/src/sage/graphs/graph_plot.py +++ b/src/sage/graphs/graph_plot.py @@ -593,7 +593,8 @@ def set_edges(self, **edge_options): """ for arg in edge_options: self._options[arg] = edge_options[arg] - if 'edge_colors' in edge_options: self._options['color_by_label'] = False + if 'edge_colors' in edge_options: + self._options['color_by_label'] = False if self._options['edge_labels_background'] == "transparent": self._options['edge_labels_background'] = "None" From 9c80475a16339998495daf43a8c4579289788ca3 Mon Sep 17 00:00:00 2001 From: Kiran Kedlaya Date: Fri, 8 May 2020 11:07:13 -0700 Subject: [PATCH 015/204] Add map_coefficients for Laurent polynomials --- .../rings/polynomial/laurent_polynomial.pyx | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index 17904ad8b17..909da378312 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -11,6 +11,7 @@ Elements of Laurent polynomial rings # **************************************************************************** from sage.rings.integer cimport Integer +from sage.categories.map cimport Map from sage.structure.element import is_Element, coerce_binop from sage.misc.misc import union from sage.structure.factorization import Factorization @@ -236,6 +237,66 @@ cdef class LaurentPolynomial(CommutativeAlgebraElement): """ raise NotImplementedError + def map_coefficients(self, f, new_base_ring=None): + """ + Returns the polynomial obtained by applying ``f`` to the nonzero + coefficients of ``self``. + + If ``f`` is a :class:`sage.categories.map.Map`, then the resulting + polynomial will be defined over the codomain of ``f``. Otherwise, the + resulting polynomial will be over the same ring as ``self``. Set + ``new_base_ring`` to override this behavior. + + INPUT: + + - ``f`` -- a callable that will be applied to the coefficients of ``self``. + + - ``new_base_ring`` (optional) -- if given, the resulting polynomial + will be defined over this ring. + + EXAMPLES:: + + sage: k. = GF(9) + sage: R. = LaurentPolynomialRing(k) + sage: f = x*a + a + sage: f.map_coefficients(lambda a : a + 1) + (a + 1) + (a + 1)*x + sage: R. = LaurentPolynomialRing(k, 2) + sage: f = x*a + 2*x^3*y*a + a + sage: f.map_coefficients(lambda a : a + 1) + (2*a + 1)*x^3*y + (a + 1)*x + a + 1 + + Examples with different base ring:: + + sage: R. = GF(9); S. = GF(81) + sage: h = Hom(R,S)[0]; h + Ring morphism: + From: Finite Field in r of size 3^2 + To: Finite Field in s of size 3^4 + Defn: r |--> 2*s^3 + 2*s^2 + 1 + sage: T. = LaurentPolynomialRing(R, 2) + sage: f = r*X+Y + sage: g = f.map_coefficients(h); g + (2*s^3 + 2*s^2 + 1)*X + Y + sage: g.parent() + Multivariate Laurent Polynomial Ring in X, Y over Finite Field in s of size 3^4 + sage: h = lambda x: x.trace() + sage: g = f.map_coefficients(h); g + X - Y + sage: g.parent() + Multivariate Laurent Polynomial Ring in X, Y over Finite Field in r of size 3^2 + sage: g = f.map_coefficients(h, new_base_ring=GF(3)); g + X - Y + sage: g.parent() + Multivariate Laurent Polynomial Ring in X, Y over Finite Field of size 3 + + """ + R = self.parent() + if new_base_ring is not None: + R = R.change_ring(new_base_ring) + elif isinstance(f, Map): + R = R.change_ring(f.codomain()) + return R(dict([(k,f(v)) for (k,v) in self.dict().items()])) cdef class LaurentPolynomial_univariate(LaurentPolynomial): """ From 5e1f5efad072f785e49492c5f434d2ee54d54293 Mon Sep 17 00:00:00 2001 From: Kiran Kedlaya Date: Fri, 8 May 2020 15:03:54 -0700 Subject: [PATCH 016/204] Fix docstring formatting --- src/sage/rings/polynomial/laurent_polynomial.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index 909da378312..bcc56333974 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -247,7 +247,7 @@ cdef class LaurentPolynomial(CommutativeAlgebraElement): resulting polynomial will be over the same ring as ``self``. Set ``new_base_ring`` to override this behavior. - INPUT: + INPUT:: - ``f`` -- a callable that will be applied to the coefficients of ``self``. From e26622ad9ef99404c04cc25360214504cfa6feb7 Mon Sep 17 00:00:00 2001 From: Kiran Kedlaya Date: Fri, 8 May 2020 19:23:38 -0700 Subject: [PATCH 017/204] Fix docstring formatting (for real this time) --- src/sage/rings/polynomial/laurent_polynomial.pyx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index bcc56333974..3c69902f5d0 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -239,15 +239,14 @@ cdef class LaurentPolynomial(CommutativeAlgebraElement): def map_coefficients(self, f, new_base_ring=None): """ - Returns the polynomial obtained by applying ``f`` to the nonzero - coefficients of ``self``. + Apply ``f`` to the coefficients of ``self``. If ``f`` is a :class:`sage.categories.map.Map`, then the resulting polynomial will be defined over the codomain of ``f``. Otherwise, the resulting polynomial will be over the same ring as ``self``. Set ``new_base_ring`` to override this behavior. - INPUT:: + INPUT: - ``f`` -- a callable that will be applied to the coefficients of ``self``. From 4778e85121dbb4af2854a5b40e59c6d9bb4e9a84 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 24 May 2020 19:44:18 +0200 Subject: [PATCH 018/204] Fix line ending on Windows --- .gitattributes | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..fb91e2c6ec0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# Force LF normalization +* text=auto eol=lf +# except for Windows batch files +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf \ No newline at end of file From bed3abb1364df5a4e0588953f535b3cc9b563766 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 27 May 2020 21:20:33 +0200 Subject: [PATCH 019/204] add doctest for 8111 --- src/sage/rings/fraction_field_element.pyx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index 3e3c19bfa24..ae38b406a1e 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -181,6 +181,15 @@ cdef class FractionFieldElement(FieldElement): (x^2 + 2.0*x + 1.0)/(x + 1.0) sage: f.reduce(); f x + 1.0 + + TESTS: + + Check that :trac:`8111` is fixed:: + + sage: K.= QQ[] + sage: frac = (64*k^2+128)/(64*k^3+256) + sage: frac.reduce(); frac + (k^2 + 2)/(k^3 + 4) """ if self._is_reduced: return From a7e3ac5ea81450450d5bf22772b3999030f24347 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Wed, 27 May 2020 21:29:46 +0200 Subject: [PATCH 020/204] document integer overflow bug --- src/sage/rings/polynomial/multi_polynomial_ideal.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 35c2374fbcb..31ea509dbba 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -1228,6 +1228,12 @@ def vector_space_dimension(self): sage: I.vector_space_dimension() +Infinity + Due to integer overflow, the result is correct only modulo ``2^32``, see :trac:`8586`:: + + sage: P. = PolynomialRing(GF(32003),3) + sage: sage.rings.ideal.FieldIdeal(P).vector_space_dimension() # known bug + -1973539045 + TESTS: Check that this method works over QQbar (:trac:`25351`):: From aba4268fff3cad467dcb709495e12895b3cabfb4 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Tue, 2 Jun 2020 17:14:32 +0200 Subject: [PATCH 021/204] Streamline readme based on changes --- README.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 64318b4da6d..6d36f218fbf 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ Guide](https://doc.sagemath.org/html/en/installation). - [Git] Alternatively, clone the Sage git repository: - $ git clone --branch master git://trac.sagemath.org/sage.git + $ git clone -c core.symlinks=true git://trac.sagemath.org/sage.git This will create the subdirectory `sage`. @@ -183,12 +183,9 @@ 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 crucial that you use the Cygwin (or WSL) - version of `git`, and that you configure it as follows first: - - $ git config --global core.autocrlf false - $ git config --global core.symlinks true + 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`. 3. `cd` into the source/build directory: From b95fa2c28a49c9b3c3e2fa3dc2eb55699dcbbcfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 3 Jun 2020 17:36:59 +0200 Subject: [PATCH 022/204] annihilate the oeis specific and ever-changing results --- src/sage/databases/oeis.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/sage/databases/oeis.py b/src/sage/databases/oeis.py index b8fa05dfb12..9911d9549bd 100644 --- a/src/sage/databases/oeis.py +++ b/src/sage/databases/oeis.py @@ -472,24 +472,22 @@ def find_by_description(self, description, max_results=3, first_result=0): EXAMPLES:: sage: oeis.find_by_description('prime gap factorization') # optional -- internet - 0: A073491: Numbers having no prime gaps in their factorization. - 1: A073485: Product of any number of consecutive primes; squarefree numbers with no gaps in their prime factorization. - 2: A073490: Number of prime gaps in factorization of n. + 0: A...: ... + 1: A...: ... + 2: A...: ... sage: prime_gaps = _[2] ; prime_gaps # optional -- internet A073490: Number of prime gaps in factorization of n. - :: - sage: oeis('beaver') # optional -- internet - 0: A028444: Busy Beaver sequence, or Rado's sigma function: ... - 1: A060843: Busy Beaver problem: a(n) = maximal number of steps ... - 2: A131956: Busy Beaver variation: maximum number of steps for ... + 0: A...: ... + 1: A...: ... + 2: A...: ... sage: oeis('beaver', max_results=4, first_result=2) # optional -- internet - 0: A131956: Busy Beaver variation: maximum number of steps for ... - 1: A141475: Number of Turing machines with n states following ... - 2: A131957: Busy Beaver sigma variation: maximum number of 1's ... + 0: A...: ... + 1: A...: ... + 2: A...: ... 3: A...: ... """ options = {'q': description, From cf800be33067884454dddcf7dac4760d288862e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 4 Jun 2020 08:37:09 +0200 Subject: [PATCH 023/204] still test something in the oeis ever-changing doctests --- src/sage/databases/oeis.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/databases/oeis.py b/src/sage/databases/oeis.py index 9911d9549bd..538a9692954 100644 --- a/src/sage/databases/oeis.py +++ b/src/sage/databases/oeis.py @@ -480,15 +480,15 @@ def find_by_description(self, description, max_results=3, first_result=0): A073490: Number of prime gaps in factorization of n. sage: oeis('beaver') # optional -- internet - 0: A...: ... - 1: A...: ... - 2: A...: ... + 0: A...: ...eaver... + 1: A...: ...eaver... + 2: A...: ...eaver... sage: oeis('beaver', max_results=4, first_result=2) # optional -- internet - 0: A...: ... - 1: A...: ... - 2: A...: ... - 3: A...: ... + 0: A...: ...eaver... + 1: A...: ...eaver... + 2: A...: ...eaver... + 3: A...: ...eaver... """ options = {'q': description, 'n': str(max_results), From d68be74ed1b2c27903da4c16167c541faf2265f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 4 Jun 2020 19:29:28 +0200 Subject: [PATCH 024/204] spring cleanup in one modular symbol file (pep8) --- src/sage/modular/modsym/space.py | 415 ++++++++++++++++--------------- 1 file changed, 209 insertions(+), 206 deletions(-) diff --git a/src/sage/modular/modsym/space.py b/src/sage/modular/modsym/space.py index 912c1ceb5a7..e93d967b6e0 100644 --- a/src/sage/modular/modsym/space.py +++ b/src/sage/modular/modsym/space.py @@ -7,7 +7,7 @@ """ from __future__ import absolute_import -#***************************************************************************** +# **************************************************************************** # Sage: System for Algebra and Geometry Experimentation # # Copyright (C) 2005 William Stein @@ -21,32 +21,34 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** +from sage.categories.fields import Fields import sage.modules.free_module as free_module import sage.matrix.matrix_space as matrix_space -from sage.modules.free_module_element import is_FreeModuleElement +from sage.modules.free_module_element import FreeModuleElement from sage.modules.free_module import EchelonMatrixKey from sage.misc.all import verbose, prod import sage.modular.hecke.all as hecke -import sage.arith.all as arith -import sage.rings.fast_arith as fast_arith -from sage.rings.all import PowerSeriesRing, Integer, O, QQ, ZZ, infinity, Zmod +from sage.arith.all import divisors, next_prime +from sage.rings.fast_arith import prime_range +from sage.rings.all import PowerSeriesRing, Integer, QQ, ZZ, infinity, Zmod from sage.rings.number_field.number_field_base import is_NumberField -from sage.structure.all import Sequence, SageObject +from sage.structure.all import Sequence, SageObject from sage.structure.richcmp import (richcmp_method, richcmp, rich_to_bool, richcmp_not_equal) -from sage.modular.arithgroup.all import Gamma0, is_Gamma0 # for Sturm bound given a character +from sage.modular.arithgroup.all import Gamma0, is_Gamma0 # for Sturm bound given a character from sage.modular.modsym.element import ModularSymbolsElement from . import hecke_operator from sage.misc.cachefunc import cached_method + def is_ModularSymbolsSpace(x): r""" - Return True if x is a space of modular symbols. + Return ``True`` if ``x`` is a space of modular symbols. EXAMPLES:: @@ -85,7 +87,7 @@ def __init__(self, group, weight, character, sign, base_ring, category=None): def __richcmp__(self, other, op): """ - Compare self and other. + Compare ``self`` and ``other``. When spaces are in a common ambient space, we order lexicographically by the sequence of traces of Hecke operators @@ -153,7 +155,7 @@ def __richcmp__(self, other, op): # distinguish using Hecke operators, if possible. try: - for p in fast_arith.prime_range(self.hecke_bound()): + for p in prime_range(self.hecke_bound()): ap = self.hecke_matrix(p).trace() bp = other.hecke_matrix(p).trace() if ap != bp: @@ -166,7 +168,7 @@ def __richcmp__(self, other, op): def _hecke_operator_class(self): """ Return the class to be used for instantiating Hecke operators - acting on self. + acting on ``self``. EXAMPLES:: @@ -178,16 +180,17 @@ def _hecke_operator_class(self): def compact_system_of_eigenvalues(self, v, names='alpha', nz=None): r""" Return a compact system of eigenvalues `a_n` for - `n\in v`. This should only be called on simple factors of - modular symbols spaces. + `n\in v`. + + This should only be called on simple factors of modular + symbols spaces. INPUT: - ``v`` - a list of positive integers - - ``nz`` - (default: None); if given specifies a column index + - ``nz`` - (default: ``None``); if given specifies a column index such that the dual module has that column nonzero. - OUTPUT: - ``E`` - matrix such that E\*v is a vector with components @@ -236,7 +239,7 @@ def compact_system_of_eigenvalues(self, v, names='alpha', nz=None): def character(self): """ - Return the character associated to self. + Return the character associated to ``self``. EXAMPLES:: @@ -249,11 +252,11 @@ def character(self): def cuspidal_submodule(self): """ - Return the cuspidal submodule of self. + Return the cuspidal submodule of ``self``. - .. note:: + .. NOTE:: - This should be overridden by all derived classes. + This should be overridden by all derived classes. EXAMPLES:: @@ -353,9 +356,9 @@ def dual_star_involution_matrix(self): Return the matrix of the dual star involution, which is induced by complex conjugation on the linear dual of modular symbols. - .. note:: + .. NOTE:: - This should be overridden in all derived classes. + This should be overridden in all derived classes. EXAMPLES:: @@ -372,21 +375,18 @@ def dual_star_involution_matrix(self): def group(self): """ - Returns the group of this modular symbols space. + Return the group of this modular symbols space. INPUT: - - ``ModularSymbols self`` - an arbitrary space of modular symbols - OUTPUT: - ``CongruenceSubgroup`` - the congruence subgroup that this is a space of modular symbols for. - ALGORITHM: The group is recorded when this space is created. EXAMPLES:: @@ -399,7 +399,7 @@ def group(self): def is_ambient(self): """ - Return True if self is an ambient space of modular symbols. + Return ``True`` if ``self`` is an ambient space of modular symbols. EXAMPLES:: @@ -413,11 +413,11 @@ def is_ambient(self): def is_cuspidal(self): """ - Return True if self is a cuspidal space of modular symbols. + Return ``True`` if ``self`` is a cuspidal space of modular symbols. - .. note:: + .. NOTE:: - This should be overridden in all derived classes. + This should be overridden in all derived classes. EXAMPLES:: @@ -491,11 +491,11 @@ def multiplicity(self, S, check_simple=True): d = C.rank() n = S.rank() assert d % n == 0, "the dimension of intersection must be a multiple of dimension of simple space. bug!" - return d//n + return d // n def ngens(self): """ - The number of generators of self. + Return the number of generators of ``self``. INPUT: @@ -506,7 +506,6 @@ def ngens(self): - ``int`` - the number of generators, which is the same as the dimension of self. - ALGORITHM: Call the dimension function. EXAMPLES:: @@ -605,13 +604,13 @@ def set_precision(self, prec): def q_expansion_basis(self, prec=None, algorithm='default'): r""" - Returns a basis of q-expansions (as power series) to precision prec - of the space of modular forms associated to self. The q-expansions - are defined over the same base ring as self, and a put in echelon - form. + Return a basis of q-expansions (as power series) to precision prec + of the space of modular forms associated to ``self``. - INPUT: + The q-expansions are defined over the same base ring as ``self``, + and a put in echelon form. + INPUT: - ``self`` - a space of CUSPIDAL modular symbols @@ -713,7 +712,7 @@ def q_expansion_basis(self, prec=None, algorithm='default'): prec = Integer(prec) if prec < 1: - raise ValueError("prec (=%s) must be >= 1"%prec) + raise ValueError("prec (=%s) must be >= 1" % prec) if not self.is_cuspidal(): raise ArithmeticError("space must be cuspidal") @@ -735,18 +734,19 @@ def q_expansion_basis(self, prec=None, algorithm='default'): B1 = self._q_expansion_basis_hecke_dual(prec) B2 = self._q_expansion_basis_eigen(prec, 'alpha') if B1 != B2: - raise RuntimeError("There is a bug in q_expansion_basis -- basis computed differently with two algorithms:\n%s\n%s\n"%(B1, B2,)) + raise RuntimeError("There is a bug in q_expansion_basis -- basis computed differently with two algorithms:\n%s\n%s\n" % (B1, B2,)) return Sequence(B1, cr=True) else: - raise ValueError("no algorithm '%s'"%algorithm) + raise ValueError("no algorithm '%s'" % algorithm) - def q_expansion_module(self, prec = None, R=None): + def q_expansion_module(self, prec=None, R=None): r""" Return a basis over R for the space spanned by the coefficient - vectors of the `q`-expansions corresponding to self. If R - is not the base ring of self, returns the restriction of scalars - down to R (for this, self must have base ring `\QQ` - or a number field). + vectors of the `q`-expansions corresponding to ``self``. + + If R is not the base ring of ``self``, this returns the + restriction of scalars down to R (for this, self must have + base ring `\QQ` or a number field). INPUT: @@ -760,9 +760,11 @@ def q_expansion_module(self, prec = None, R=None): OUTPUT: A free module over R. - TODO - extend to more general R (though that is fairly easy for the - user to get by just doing base_extend or change_ring on the - output of this function). + .. TODO:: + + extend to more general R (though that is fairly easy for the + user to get by just doing base_extend or change_ring on the + output of this function). Note that the prec needed to distinguish elements of the restricted-down-to-R basis may be bigger than ``self.hecke_bound()``, @@ -885,7 +887,7 @@ def q_expansion_module(self, prec = None, R=None): EXAMPLES WITH SIGN 0 and R=QQ: - TODO - this doesn't work yet as it's not implemented!! + .. TODO:: This doesn't work yet as it's not implemented!! :: @@ -916,16 +918,17 @@ def q_expansion_module(self, prec = None, R=None): elif R == QQ: return self._q_expansion_module_rational(prec) elif R is None or R == self.base_ring(): - ## names is never used in this case + # names is never used in this case return self._q_expansion_module(prec) else: raise NotImplementedError("R must be ZZ, QQ, or the base ring of the modular symbols space.") def _q_eigenform_images(self, A, prec, names): """ - Return list of images in space corresponding to self of eigenform - corresponding to A under the degeneracy maps. This is mainly a - helper function for other internal functions. + Return list of images in space corresponding to ``self`` of eigenform + corresponding to A under the degeneracy maps. + + This is mainly a helper function for other internal functions. INPUT: @@ -936,7 +939,6 @@ def _q_eigenform_images(self, A, prec, names): - ``prec`` - a positive integer - EXAMPLES:: sage: M = ModularSymbols(33,2,sign=1) @@ -948,16 +950,17 @@ def _q_eigenform_images(self, A, prec, names): f = A.q_eigenform(prec, names) if A.level() == self.level(): return [f] - D = arith.divisors(self.level() // A.level()) + D = divisors(self.level() // A.level()) q = f.parent().gen() return [f] + [f(q**d) for d in D if d > 1] def _q_expansion_module(self, prec, algorithm='hecke'): """ - Return module spanned by the `q`-expansions corresponding to self. See - the docstring for ``q_expansion_module`` (without underscore) for - further details. Note that this will not work if ``algorithm=eigen`` - and the sign is 0. + Return module spanned by the `q`-expansions corresponding to ``self``. + + See the docstring for ``q_expansion_module`` (without + underscore) for further details. Note that this will not work + if ``algorithm=eigen`` and the sign is 0. EXAMPLES:: @@ -969,12 +972,11 @@ def _q_expansion_module(self, prec, algorithm='hecke'): Vector space of degree 4 and dimension 1 over Number Field in b with defining polynomial x^2 + 7 with b = 2.645751311064591?*I Basis matrix: [ 0 1 -2 -1] - """ if not self.is_cuspidal(): raise ValueError("self must be cuspidal") R = self.base_ring() - if not R.is_field(): + if R not in Fields(): if R == ZZ: return self._q_expansion_module_integral(prec) raise NotImplementedError("base ring must be a field (or ZZ).") @@ -984,10 +986,11 @@ def _q_expansion_module(self, prec, algorithm='hecke'): return V.span([f.padded_list(prec) for f in self.q_expansion_basis(prec, algorithm)]) if algorithm != 'eigen': - raise ValueError("unknown algorithm '%s'"%algorithm) + raise ValueError("unknown algorithm '%s'" % algorithm) def q_eigen_gens(d, f): - r""" Temporary function for internal use. + r""" + Temporary function for internal use. EXAMPLES:: @@ -1019,12 +1022,12 @@ def q_eigen_gens(d, f): def _q_expansion_module_rational(self, prec): r""" Return a vector space over `\QQ` for the space spanned by the - `q`-expansions corresponding to self. + `q`-expansions corresponding to ``self``. - The base ring of self must be - `\QQ` or a number field, and self must be cuspidal. The returned space - is a `\QQ`-vector space, where the coordinates are the coefficients of - `q`-expansions. + The base ring of ``self`` must be `\QQ` or a number field, and + ``self`` must be cuspidal. The returned space is a + `\QQ`-vector space, where the coordinates are the coefficients + of `q`-expansions. INPUT: @@ -1072,7 +1075,8 @@ def q_eigen_gens(d, f): # This looks like it might be really slow -- though # perhaps it's nothing compared to the time taken by # whatever computed this in the first place. - return [[(X[i].list())[j][k] for i in range(prec)] for j in range(d) for k in range(n)] + return [[(X[i].list())[j][k] for i in range(prec)] + for j in range(d) for k in range(n)] if self.sign() == 0: X = self.plus_submodule(compute_dual=True) else: @@ -1087,11 +1091,11 @@ def q_eigen_gens(d, f): def _q_expansion_module_integral(self, prec): r""" Return module over `\ZZ` for the space spanned by - the `q`-expansions corresponding to self. The base ring of - self must be `\QQ` or a number field, and self must - be cuspidal. The returned space is a `\ZZ`-module, - where the coordinates are the coefficients of - `q`-expansions. + the `q`-expansions corresponding to ``self``. + + The base ring of ``self`` must be `\QQ` or a number field, and + self must be cuspidal. The returned space is a `\ZZ`-module, + where the coordinates are the coefficients of `q`-expansions. EXAMPLES:: @@ -1109,11 +1113,10 @@ def _q_expansion_module_integral(self, prec): V = self.q_expansion_module(prec, QQ) return free_module.FreeModule(ZZ, V.degree()).span(V.basis()).saturation() - def congruence_number(self, other, prec=None): r""" Given two cuspidal spaces of modular symbols, compute the - congruence number, using prec terms of the `q`-expansions. + congruence number, using ``prec`` terms of the `q`-expansions. The congruence number is defined as follows. If `V` is the submodule of integral cusp forms corresponding to self (saturated in @@ -1122,7 +1125,7 @@ def congruence_number(self, other, prec=None): the congruence number is the index of `V+W` in its saturation in `\ZZ[[q]]`. - If prec is not given it is set equal to the max of the + If ``prec`` is not given it is set equal to the max of the ``hecke_bound`` function called on each space. EXAMPLES:: @@ -1139,9 +1142,9 @@ def congruence_number(self, other, prec=None): prec = max(self.hecke_bound(), other.hecke_bound()) prec = int(prec) - V = self.q_expansion_module(prec, ZZ) + V = self.q_expansion_module(prec, ZZ) W = other.q_expansion_module(prec, ZZ) - K = V+W + K = V + W return K.index_in_saturation() ######################################################################### @@ -1219,13 +1222,16 @@ def q_eigenform_character(self, names=None): G = DirichletGroup(self.level(), K) M = self.ambient_module() # act on right since v is a in the dual - b = [(M.diamond_bracket_matrix(u)*v)[i] / v[i] for u in G.unit_gens()] + b = [(M.diamond_bracket_matrix(u) * v)[i] / v[i] + for u in G.unit_gens()] return G(b) def q_eigenform(self, prec, names=None): """ - Returns the q-expansion to precision prec of a new eigenform - associated to self, where self must be new, cuspidal, and simple. + Return the q-expansion to precision ``prec`` of a new eigenform + associated to ``self``. + + Here ``self`` must be new, cuspidal, and simple. EXAMPLES:: @@ -1255,11 +1261,11 @@ def q_eigenform(self, prec, names=None): a2 = self.eigenvalue(2, names) R = PowerSeriesRing(a2.parent(), "q") q = R.gen(0) - f = q + a2*q**2 + O(q**3) + f = (q + a2 * q**2).O(3) if f.prec() < prec: R = f.parent() - ext = [self.eigenvalue(n, names) for n in range(f.prec(),prec)] + ext = [self.eigenvalue(n, names) for n in range(f.prec(), prec)] f = R(f.padded_list(f.prec()) + ext) self._q_expansion_dict[names] = f.add_bigoh(prec) return self._q_expansion_dict[names] @@ -1280,12 +1286,12 @@ def _q_expansion_basis_eigen(self, prec, names): # should we perhaps check at this point if self is new? f = self.q_eigenform(prec, names) R = PowerSeriesRing(self.base_ring(), 'q') - B = [R([f[i][j] for i in range(prec)],prec) for j in range(self.rank())] + B = [R([f[i][j] for i in range(prec)], prec) + for j in range(self.rank())] return B else: raise NotImplementedError - ######################################################################### # # Computation of a basis using linear functionals on the Hecke algebra. @@ -1294,12 +1300,12 @@ def _q_expansion_basis_eigen(self, prec, names): def q_expansion_cuspforms(self, prec=None): r""" - Returns a function f(i,j) such that each value f(i,j) is the + Return a function f(i,j) such that each value f(i,j) is the q-expansion, to the given precision, of an element of the - corresponding space `S` of cusp forms. Together these - functions span `S`. Here `i,j` are integers with - `0\leq i,j < d`, where `d` is the dimension of - self. + corresponding space `S` of cusp forms. + + Together these functions span `S`. Here `i,j` are integers + with `0\leq i,j < d`, where `d` is the dimension of ``self``. For a reduced echelon basis, use the function ``q_expansion_basis`` instead. @@ -1307,7 +1313,7 @@ def q_expansion_cuspforms(self, prec=None): More precisely, this function returns the `q`-expansions obtained by taking the `ij` entry of the matrices of the Hecke operators `T_n` acting on the subspace of the linear - dual of modular symbols corresponding to self. + dual of modular symbols corresponding to ``self``. EXAMPLES:: @@ -1347,9 +1353,9 @@ def q_expansion_cuspforms(self, prec=None): prec = self.default_prec() if not self.is_cuspidal(): raise ArithmeticError("self must be cuspidal") - T = [self.dual_hecke_matrix(n) for n in range(1,prec)] + T = [self.dual_hecke_matrix(n) for n in range(1, prec)] R = PowerSeriesRing(self.base_ring(), 'q') - return lambda i, j: R([0] + [t[i,j] for t in T], prec) + return lambda i, j: R([0] + [t[i, j] for t in T], prec) def _q_expansion_basis_hecke_dual(self, prec): r""" @@ -1368,35 +1374,34 @@ def _q_expansion_basis_hecke_dual(self, prec): d = self.dimension_of_associated_cuspform_space() prec = Integer(prec) if prec < 1: - raise ValueError("prec (=%s) must be >= 1"%prec) - if d >= prec-1: - d = prec-1 + raise ValueError("prec (=%s) must be >= 1" % prec) + if d >= prec - 1: + d = prec - 1 K = self.base_ring() - A = free_module.VectorSpace(K, prec-1) - M = matrix_space.MatrixSpace(K, prec-1, self.dimension()) + A = free_module.VectorSpace(K, prec - 1) + M = matrix_space.MatrixSpace(K, prec - 1, self.dimension()) V = A.zero_submodule() - i = self.dimension()-1 + i = self.dimension() - 1 j = 0 - t = verbose('computing basis to precision %s'%prec) + t = verbose('computing basis to precision %s' % prec) while V.dimension() < d and i >= 0: - v = [self.dual_hecke_matrix(n).column(i) for n in range(1,prec)] - t = verbose('iteration: %s'%j,t) + v = [self.dual_hecke_matrix(n).column(i) for n in range(1, prec)] + t = verbose('iteration: %s' % j, t) X = M(v).transpose() V += X.row_space() - t = verbose('addition of row space: %s'%j,t) + t = verbose('addition of row space: %s' % j, t) i -= 1 j += 1 R = PowerSeriesRing(K, 'q') B = V.basis() if len(B) < d: - B += [V(0)] * (d-len(B)) + B += [V(0)] * (d - len(B)) return [R([0] + b.list(), prec) for b in B] - ######################################################################### # # Decomposition of spaces @@ -1405,7 +1410,7 @@ def _q_expansion_basis_hecke_dual(self, prec): # def factorization(self): # """ -# Returns a list of pairs `(S,e)` where `S` is simple +# Return a list of pairs `(S,e)` where `S` is simple # spaces of modular symbols and self is isomorphic to the direct sum # of the `S^e` as a module over the *anemic* Hecke algebra # adjoin the star involution. @@ -1430,7 +1435,7 @@ def hecke_module_of_level(self, level): def sign(self): r""" - Return the sign of self. + Return the sign of ``self``. For efficiency reasons, it is often useful to compute in the (largest) quotient of modular symbols where the \* involution acts @@ -1438,15 +1443,12 @@ def sign(self): INPUT: - - ``ModularSymbols self`` - arbitrary space of modular symbols. - OUTPUT: - - - ``int`` - the sign of self, either -1, 0, or 1. + - ``int`` - the sign of ``self``, either -1, 0, or 1. - ``-1`` - if this is factor of quotient where \* acts as -1, @@ -1457,7 +1459,6 @@ def sign(self): - ``0`` - if this is full space of modular symbols (no quotient). - EXAMPLES:: sage: m = ModularSymbols(33) @@ -1480,10 +1481,11 @@ def sign(self): def simple_factors(self): """ - Returns a list modular symbols spaces `S` where `S` + Return a list modular symbols spaces `S` where `S` is simple spaces of modular symbols (for the anemic Hecke algebra) - and self is isomorphic to the direct sum of the `S` with + and ``self`` is isomorphic to the direct sum of the `S` with some multiplicities, as a module over the *anemic* Hecke algebra. + For the multiplicities use factorization() instead. ASSUMPTION: self is a module over the anemic Hecke algebra. @@ -1497,11 +1499,11 @@ def simple_factors(self): Modular Symbols subspace of dimension 1 of Modular Symbols space of dimension 3 for Gamma_0(1) of weight 16 with sign 0 over Finite Field of size 5, Modular Symbols subspace of dimension 1 of Modular Symbols space of dimension 3 for Gamma_0(1) of weight 16 with sign 0 over Finite Field of size 5] """ - return [S for S,_ in self.factorization()] + return [S for S, _ in self.factorization()] def star_eigenvalues(self): """ - Returns the eigenvalues of the star involution acting on self. + Return the eigenvalues of the star involution acting on ``self``. EXAMPLES:: @@ -1536,7 +1538,7 @@ def star_eigenvalues(self): def star_decomposition(self): r""" - Decompose self into subspaces which are eigenspaces for the star + Decompose ``self`` into subspaces which are eigenspaces for the star involution. EXAMPLES:: @@ -1590,14 +1592,16 @@ def integral_structure(self): def intersection_number(self, M): """ - Given modular symbols spaces self and M in some common ambient - space, returns the intersection number of these two spaces. This is - the index in their saturation of the sum of their underlying - integral structures. + Given modular symbols spaces ``self`` and ``M`` in some common ambient + space, returns the intersection number of these two spaces. + + This is the index in their saturation of the sum of their + underlying integral structures. - If self and M are of weight two and defined over QQ, and correspond - to newforms f and g, then this number equals the order of the - intersection of the modular abelian varieties attached to f and g. + If ``self`` and ``M`` are of weight two and defined over QQ, + and correspond to newforms f and g, then this number equals + the order of the intersection of the modular abelian varieties + attached to f and g. EXAMPLES:: @@ -1617,7 +1621,7 @@ def intersection_number(self, M): raise ValueError("self and M must be in the same ambient space.") A = self.integral_structure() B = M.integral_structure() - return (A+B).index_in_saturation() + return (A + B).index_in_saturation() def integral_basis(self): r""" @@ -1681,13 +1685,14 @@ def integral_basis(self): self.__integral_basis = tuple([self(b) for b in B]) return self.__integral_basis - def integral_hecke_matrix(self, n): r""" Return the matrix of the `n`th Hecke operator acting on the integral - structure on self (as returned by ``self.integral_structure()``). This - is often (but not always) different from the matrix returned by - ``self.hecke_matrix``, even if the latter has integral entries. + structure on ``self`` (as returned by ``self.integral_structure()``). + + This is often (but not always) different from the matrix + returned by ``self.hecke_matrix``, even if the latter has + integral entries. EXAMPLES:: @@ -1714,7 +1719,6 @@ def integral_hecke_matrix(self, n): self.__integral_hecke_matrix = {} except KeyError: pass - #raise NotImplementedError, "code past this point is broken / not done" # todo A = self.ambient_hecke_module() T = A.hecke_matrix(n) S = T.restrict(self.integral_structure()).change_ring(ZZ) @@ -1723,7 +1727,7 @@ def integral_hecke_matrix(self, n): def sturm_bound(self): r""" - Returns the Sturm bound for this space of modular symbols. + Return the Sturm bound for this space of modular symbols. Type ``sturm_bound?`` for more details. @@ -1752,19 +1756,15 @@ def sturm_bound(self): self.__sturm_bound = self.group().sturm_bound(self.weight()) return self.__sturm_bound - def plus_submodule(self, compute_dual=True): """ - Return the subspace of self on which the star involution acts as - +1. + Return the subspace of ``self`` on which the star involution acts as +1. INPUT: - - - ``compute_dual`` - bool (default: True) also + - ``compute_dual`` - bool (default: ``True``) also compute dual subspace. This are useful for many algorithms. - OUTPUT: subspace of modular symbols EXAMPLES:: @@ -1778,12 +1778,10 @@ def plus_submodule(self, compute_dual=True): def minus_submodule(self, compute_dual=True): """ - Return the subspace of self on which the star involution acts as - -1. + Return the subspace of ``self`` on which the star involution acts as -1. INPUT: - - ``compute_dual`` - bool (default: True) also compute dual subspace. This are useful for many algorithms. @@ -1800,15 +1798,16 @@ def minus_submodule(self, compute_dual=True): def _compute_sign_submodule(self, sign, compute_dual=True): r""" - Compute the submodule of self which is an eigenspace for the star involution with the given sign. + Compute the submodule of ``self`` which is an eigenspace for the star involution with the given sign. INPUT: - sign (integer): 1 or -1 - - compute_dual (True or False, default True): also compute the dual submodule (useful for some algorithms) + - compute_dual (boolean, default ``True``): also compute the dual + submodule (useful for some algorithms) - OUTPUT: a submodule of self + OUTPUT: a submodule of ``self`` EXAMPLES:: @@ -1852,24 +1851,21 @@ def _set_sign(self, sign): sage: M._set_sign(0) """ sign = int(sign) - if not (sign in [-1,0,1]): - raise ValueError("sign (=%s) must be -1, 0, or 1"%sign) + if sign not in [-1, 0, 1]: + raise ValueError("sign (=%s) must be -1, 0, or 1" % sign) self.__sign = sign def sign_submodule(self, sign, compute_dual=True): """ - Return the subspace of self that is fixed under the star - involution. + Return the subspace of ``self`` that is fixed under the star involution. INPUT: - - ``sign`` - int (either -1, 0 or +1) - - ``compute_dual`` - bool (default: True) also + - ``compute_dual`` - bool (default: ``True``) also compute dual subspace. This are useful for many algorithms. - OUTPUT: subspace of modular symbols EXAMPLES:: @@ -1883,7 +1879,7 @@ def sign_submodule(self, sign, compute_dual=True): -1 """ sign = int(sign) - if not sign in [-1, 0, 1]: + if sign not in [-1, 0, 1]: raise ValueError("sign must be -1, 0 or 1") if self.sign() == sign: # an easy case return self @@ -1901,14 +1897,15 @@ def sign_submodule(self, sign, compute_dual=True): pass P = self._compute_sign_submodule(sign, compute_dual) P.__star_eigenvalue = sign - self.__plus_submodule[(sign,compute_dual)] = P + self.__plus_submodule[(sign, compute_dual)] = P return P def star_involution(self): """ - Return the star involution on self, which is induced by complex - conjugation on modular symbols. Not implemented in this abstract base - class. + Return the star involution on ``self``, which is induced by complex + conjugation on modular symbols. + + Not implemented in this abstract base class. EXAMPLES:: @@ -1925,11 +1922,9 @@ def abelian_variety(self): INPUT: - - ``self`` - modular symbols space of weight 2 for a congruence subgroup such as Gamma0, Gamma1 or GammaH. - EXAMPLES:: sage: ModularSymbols(Gamma0(11)).cuspidal_submodule().abelian_variety() @@ -1964,13 +1959,13 @@ def abelian_variety(self): def rational_period_mapping(self): r""" - Return the rational period mapping associated to self. + Return the rational period mapping associated to ``self``. - This is a homomorphism to a vector space whose kernel is the same as - the kernel of the period mapping associated to self. For this to exist, - self must be Hecke equivariant. + This is a homomorphism to a vector space whose kernel is the + same as the kernel of the period mapping associated to + ``self``. For this to exist, self must be Hecke equivariant. - Use ``integral_period_mapping`` to obtain a homomorphism to a + Use :meth:`integral_period_mapping` to obtain a homomorphism to a `\ZZ`-module, normalized so the image of integral modular symbols is exactly `\ZZ^n`. @@ -2012,15 +2007,13 @@ def rational_period_mapping(self): self.__rational_period_mapping = R return R - def integral_period_mapping(self): r""" - Return the integral period mapping associated to self. + Return the integral period mapping associated to ``self``. This is a homomorphism to a vector space whose kernel is the same - as the kernel of the period mapping associated to self, normalized - so the image of integral modular symbols is exactly - `\ZZ^n`. + as the kernel of the period mapping associated to ``self``, normalized + so the image of integral modular symbols is exactly `\ZZ^n`. EXAMPLES:: @@ -2084,13 +2077,12 @@ def integral_period_mapping(self): @cached_method def modular_symbols_of_sign(self, sign, bound=None): """ - Returns a space of modular symbols with the same defining + Return a space of modular symbols with the same defining properties (weight, level, etc.) and Hecke eigenvalues as this space except with given sign. INPUT: - - ``self`` - a cuspidal space of modular symbols - ``sign`` - an integer, one of -1, 0, or 1 @@ -2098,7 +2090,6 @@ def modular_symbols_of_sign(self, sign, bound=None): - ``bound`` - integer (default: None); if specified only use Hecke operators up to the given bound. - EXAMPLES:: sage: S = ModularSymbols(Gamma0(11),2,sign=0).cuspidal_subspace() @@ -2148,22 +2139,22 @@ def modular_symbols_of_sign(self, sign, bound=None): return self if sign != 0: if self.sign() == 0: - d = d//2 + d = d // 2 elif sign == 0: # self has nonzero sign - d = 2*d + d = 2 * d B = self.ambient_module().modular_symbols_of_sign(sign) p = 2 if bound is None: bound = self.hecke_bound() while B.dimension() > d and p <= bound: while self.level() % p == 0: - p = arith.next_prime(p) + p = next_prime(p) f = self.hecke_polynomial(p) - g = prod(g for g,_ in f.factor()) # square free part + g = prod(g for g, _ in f.factor()) # square free part t = B.hecke_operator(p) s = g(t) B = s.kernel() - p = arith.next_prime(p) + p = next_prime(p) return B ######################################################### @@ -2177,8 +2168,8 @@ def abvarquo_cuspidal_subgroup(self): the relevant modular Jacobian attached to this modular symbols space. - We assume that self is defined over QQ and has weight 2. If - the sign of self is not 0, then the power of 2 may be wrong. + We assume that ``self`` is defined over QQ and has weight 2. If + the sign of ``self`` is not 0, then the power of 2 may be wrong. EXAMPLES:: @@ -2194,8 +2185,10 @@ def abvarquo_cuspidal_subgroup(self): sage: [A.abvarquo_cuspidal_subgroup().invariants() for A in D] [(), (), ()] """ - try: return self.__abvarquo_cuspidal_subgroup - except AttributeError: pass + try: + return self.__abvarquo_cuspidal_subgroup + except AttributeError: + pass if self.base_ring() != QQ: raise ValueError("base ring must be QQ") if self.weight() != 2: @@ -2208,14 +2201,14 @@ def abvarquo_cuspidal_subgroup(self): # Compute the images of the cusp classes (c)-(oo) in the # rational homology of the quotient modular abelian variety. - ims = [phi(M([c,infinity])) for c in P] + ims = [phi(M([c, infinity])) for c in P] # Take the span of the ims over ZZ A = phi.codomain().span(ims, ZZ) # The cuspidal subgroup is then the quotient of that module + # H_1(A) by H_1(A) - C = (A.ambient_module() + A)/A.ambient_module() + C = (A.ambient_module() + A) / A.ambient_module() self.__abvarquo_cuspidal_subgroup = C return C @@ -2225,12 +2218,14 @@ def abvarquo_rational_cuspidal_subgroup(self): Compute the rational subgroup of the cuspidal subgroup (as an abstract abelian group) of the abelian variety quotient A of the relevant modular Jacobian attached to this modular symbols - space. If C is the subgroup of A generated by differences of + space. + + If C is the subgroup of A generated by differences of cusps, then C is equipped with an action of Gal(Qbar/Q), and this function computes the fixed subgroup, i.e., C(Q). - We assume that self is defined over QQ and has weight 2. If - the sign of self is not 0, then the power of 2 may be wrong. + We assume that ``self`` is defined over QQ and has weight 2. If + the sign of ``self`` is not 0, then the power of 2 may be wrong. EXAMPLES: @@ -2281,8 +2276,10 @@ def abvarquo_rational_cuspidal_subgroup(self): sage: [A.abelian_variety().rational_torsion_subgroup().multiple_of_order() for A in D] [1, 5, 5] """ - try: return self.__abvarquo_rational_cuspidal_subgroup - except AttributeError: pass + try: + return self.__abvarquo_rational_cuspidal_subgroup + except AttributeError: + pass if self.base_ring() != QQ: raise ValueError("base ring must be QQ") if self.weight() != 2: @@ -2294,7 +2291,7 @@ def abvarquo_rational_cuspidal_subgroup(self): if N.is_squarefree(): return self.abvarquo_cuspidal_subgroup() - M = self.ambient_module() + M = self.ambient_module() phi = self.integral_period_mapping() # Make a list of all the finite cusps. @@ -2307,17 +2304,17 @@ def abvarquo_rational_cuspidal_subgroup(self): # Compute the images of the cusp classes (c)-(oo) in the # rational homology of the quotient modular abelian variety. - ims = [phi(M([c,infinity])) for c in P] + ims = [phi(M([c, infinity])) for c in P] # Take the span of the ims over ZZ A = phi.codomain().span(ims, ZZ) # The cuspidal subgroup is then the quotient of that module + # H_1(A) by H_1(A) - C = (A.ambient_module() + A)/A.ambient_module() + C = (A.ambient_module() + A) / A.ambient_module() # Make fgp module version of V. - D = V/V.zero_submodule() + D = V / V.zero_submodule() psi = D.hom([C(x) for x in ims]) # The rational cuspidal subgroup is got by intersecting kernels @@ -2326,7 +2323,8 @@ def abvarquo_rational_cuspidal_subgroup(self): CQ = C for t in G: T = self._matrix_of_galois_action(t, P) - 1 - if not T: continue + if not T: + continue im_gens = [psi(psi.lift(g).lift() * T) for g in CQ.gens()] h = CQ.hom(im_gens) CQ = h.kernel() @@ -2340,9 +2338,10 @@ def _matrix_of_galois_action(self, t, P): """ Compute the matrix of the action of the element of the cyclotomic Galois group defined by t on the set of cusps in P - (which is the set of finite cusps). This function is used - internally by the (rational) cuspidal subgroup and quotient - functions. + (which is the set of finite cusps). + + This function is used internally by the (rational) cuspidal + subgroup and quotient functions. INPUT: @@ -2385,15 +2384,18 @@ def _matrix_of_galois_action(self, t, P): d = c.galois_action(t, N) for j, e in enumerate(P): if d.is_gamma0_equiv(e, N, False): - A[i,j] = 1 + A[i, j] = 1 A.set_immutable() return A + class PeriodMapping(SageObject): r""" Base class for representing a period mapping attached to a space of modular - symbols. To be used via the derived classes RationalPeriodMapping and - IntegralPeriodMapping. + symbols. + + To be used via the derived classes :class:`RationalPeriodMapping` and + :class:`IntegralPeriodMapping`. """ def __init__(self, modsym, A): r""" @@ -2437,11 +2439,11 @@ def __call__(self, x): sage: M(vector([1,0,2])) (0, 9/4) """ - if is_FreeModuleElement(x): + if isinstance(x, FreeModuleElement): v = x else: v = self.__domain(x).element() - return v*self.__A + return v * self.__A def matrix(self): r""" @@ -2485,27 +2487,28 @@ def codomain(self): """ return self.__A.row_module() + class RationalPeriodMapping(PeriodMapping): def _repr_(self): """ - Return the string representation of self. + Return the string representation of ``self``. EXAMPLES:: sage: ModularSymbols(40,2).rational_period_mapping()._repr_() 'Rational period mapping associated to Modular Symbols space of dimension 13 for Gamma_0(40) of weight 2 with sign 0 over Rational Field' """ - return "Rational period mapping associated to %s"%self.modular_symbols_space() + return "Rational period mapping associated to %s" % self.modular_symbols_space() class IntegralPeriodMapping(PeriodMapping): def _repr_(self): """ - Return the string representation of self. + Return the string representation of ``self``. EXAMPLES:: sage: ModularSymbols(40,2).cuspidal_submodule().integral_period_mapping()._repr_() 'Integral period mapping associated to Modular Symbols subspace of dimension 6 of Modular Symbols space of dimension 13 for Gamma_0(40) of weight 2 with sign 0 over Rational Field' """ - return "Integral period mapping associated to %s"%self.modular_symbols_space() + return "Integral period mapping associated to %s" % self.modular_symbols_space() From a9dd3081e05e858e9a19dca973d51968130ec2db Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Fri, 5 Jun 2020 21:53:33 +0200 Subject: [PATCH 025/204] 29808: fix left/right permutation actions on matrices --- .../groups/perm_gps/permgroup_element.pyx | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/sage/groups/perm_gps/permgroup_element.pyx b/src/sage/groups/perm_gps/permgroup_element.pyx index 3393e7e902f..9f843d84f2f 100644 --- a/src/sage/groups/perm_gps/permgroup_element.pyx +++ b/src/sage/groups/perm_gps/permgroup_element.pyx @@ -1177,22 +1177,23 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): cpdef _act_on_(self, x, bint self_on_left): """ - Return the right action of self on left. + Return the result of the action of ``self`` on ``x``. - For example, if f=left is a polynomial, then this function returns - f(sigma\*x), which is image of f under the right action of sigma on + For example, if ``x=f(z)`` is a polynomial, then this function returns + f(sigma\*z), which is the image of f under the right action of sigma on the indeterminates. This is a right action since the image of - f(sigma\*x) under tau is f(sigma\*tau\*x). + f(sigma\*z) under tau is f(sigma\*tau\*z). - Additionally, if ``left`` is a matrix, then sigma acts on the matrix - by permuting the rows. + Additionally, if ``x`` is a matrix, then sigma acts on the matrix + by permuting the columns when acting from the right and by permuting + the rows when acting from the left. INPUT: + - ``x`` -- element of space on which permutations act - - ``left`` - element of space on which permutations - act from the right - + - ``self_on_left`` -- if ``True``, this permutation acts on ``x`` from + the left, otherwise from the right EXAMPLES:: @@ -1210,13 +1211,18 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): 2*x^2 - y^2 + z^2 + u^2 sage: M = matrix(ZZ,[[1,0,0,0,0],[0,2,0,0,0],[0,0,3,0,0],[0,0,0,4,0],[0,0,0,0,5]]) - sage: M*sigma + sage: sigma * M [0 2 0 0 0] [0 0 3 0 0] [1 0 0 0 0] [0 0 0 0 5] [0 0 0 4 0] - + sage: (M * sigma) * tau == M * (sigma * tau) + True + sage: (M * sigma) * tau == (M * sigma.matrix()) * tau.matrix() + True + sage: (tau * sigma) * M == tau * (sigma * M) + True """ if not self_on_left: left = x @@ -1235,7 +1241,11 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): left.parent())) return left(tuple(sigma_x)) elif is_Matrix(left): - return left.with_permuted_rows(self) + return left.with_permuted_columns(~self) + else: + if is_Matrix(x): + return x.with_permuted_rows(self) + def __mul__(left, right): r""" From 1f027c921ab769d059aada4986754c5617bf6455 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 7 Jun 2020 20:24:03 +0200 Subject: [PATCH 026/204] Readd branch statement to clone --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d36f218fbf..8b7c0cdfec9 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ Guide](https://doc.sagemath.org/html/en/installation). - [Git] Alternatively, clone the Sage git repository: - $ git clone -c core.symlinks=true git://trac.sagemath.org/sage.git + $ git clone -c core.symlinks=true --branch master git://trac.sagemath.org/sage.git This will create the subdirectory `sage`. From 2dba9ed96c7e5ea829fed1a3978c3b4608dac850 Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Tue, 9 Jun 2020 01:51:24 +0530 Subject: [PATCH 027/204] eccentricity method added for unweighted graphs --- src/sage/graphs/distances_all_pairs.pyx | 130 +++++++++++++++++++++++- 1 file changed, 127 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 802d4a8f6e2..566adcbeb9d 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -801,6 +801,121 @@ cdef uint32_t * c_eccentricity_bounding(G, vertex_list=None) except NULL: return LB +cdef uint32_t * all_eccentricity_DHV(G, vertex_list=None): + r""" + Returns vector of all eccentricities of unweighted graph `G`. + + This method computes the eccentricity of all vertices of + unweighted undirected graph using the algorithm proposed in [Dragan2018]_. + + EXAMPLES:: + + sage: from sage.graphs.distances_all_pairs import eccentricity + sage: G = graphs.PathGraph(5) + sage: eccentricity(G, algorithm='DHV') + [4, 3, 2, 3, 4] + + TESTS: + + sage: G = graphs.RandomBarabasiAlbert(11,7) + sage: eccentricity(G) == eccentricity(G, algorithm ='DHV') + True + """ + if G.is_directed(): + raise ValueError("this algorithm only works on undirected graphs.") + + cdef uint32_t n = G.order() + if not n: + return NULL + + cdef short_digraph sd + init_short_digraph(sd, G, edge_labelled=False, vertex_list=vertex_list) + + cdef MemoryAllocator mem = MemoryAllocator() + cdef uint32_t * distances = mem.malloc(4 * n * sizeof(uint32_t)) + if not distances: + raise MemoryError() + + cdef uint32_t * waiting_list = distances + n + + # For storing upper and lower bounds on eccentricity of nodes + cdef uint32_t * ecc_upper_bound = distances + 2 * n + cdef uint32_t * ecc_lower_bound = distances + 3 * n + memset(ecc_upper_bound, -1, n * sizeof(uint32_t)) + memset(ecc_lower_bound, 0, n * sizeof(uint32_t)) + + cdef uint32_t u, ecc_u + cdef uint32_t antipode, ecc_antipode + cdef uint32_t v, tmp + cdef size_t i, idx + cdef bint flag = 0 + cdef bitset_t seen + bitset_init(seen,n) + + cdef list active = list(range(n)) + + # Algorithm + while active: + # Select vertex with minimum eccentricity in active and update + # eccentricity upper bounds. + # For this, we select u with minimum eccentricity lower bound in active + # if ecc_u == ecc_lb[u], we are done. Otherwise, we update eccentricity + # lower bounds and repeat + while active: + # Select u with minimum eccentricity lower bound + tmp = UINT32_MAX + for i, v in enumerate(active): + if ecc_lower_bound[v] < tmp: + tmp = ecc_lower_bound[v] + idx = i + active[idx], active[-1] = active[-1], active[idx] + u = active.pop() + ecc_u = simple_BFS(sd, u, distances, NULL, waiting_list, seen) + ecc_upper_bound[u] = ecc_u + + if ecc_u == UINT32_MAX: # Disconnected graph + flag = 1 + break + + if ecc_u == ecc_lower_bound[u]: + # Update eccentricity upper bounds of the remaining vertices + # and break. + for v in active: + ecc_upper_bound[v] = min(ecc_upper_bound[v], distances[v] + ecc_u) + break + + else: + # u was not a good choice. + # We use its antipode to update eccentricity lower bounds. + # Observe that this antipode might have already been seen. + antipode = waiting_list[n-1] + for i, v in enumerate(active): + if v == antipode: + active[i] = active[-1] + active.pop() + break + + ecc_antipode = simple_BFS(sd, antipode, distances, NULL, waiting_list, seen) + ecc_upper_bound[antipode] = ecc_antipode + + for v in active: + ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v]) + + if flag: # Disconnected graph + break + + # Check if there exist any vertex in active, whose eccentricity bounds + # can be further updated. + flag = 1 + for v in active: + if ecc_upper_bound[v] > ecc_lower_bound[v]: + flag = 0 + break + if flag: # We are done. + break + + return ecc_upper_bound + def eccentricity(G, algorithm="standard", vertex_list=None): r""" Return the vector of eccentricities in G. @@ -813,9 +928,16 @@ def eccentricity(G, algorithm="standard", vertex_list=None): - ``G`` -- a Graph or a DiGraph. - ``algorithm`` -- string (default: ``'standard'``); name of the method used - to compute the eccentricity of the vertices. Available algorithms are - ``'standard'`` which performs a BFS from each vertex and ``'bounds'`` - which uses the fast algorithm proposed in [TK2013]_ for undirected graphs. + to compute the eccentricity of the vertices. + + - ``'standard'`` -- Computes eccentricity by performing a BFS from each + vertex. + + - ``'bounds'`` -- Computes eccentricity using the fast algorithm proposed + in [TK2013]_ for undirected graphs. + + - ``'DHV'`` -- Computes all eccentricities of undirected graph using the + algorithm proposed in [Dragan2018]_. - ``vertex_list`` -- list (default: ``None``); a list of `n` vertices specifying a mapping from `(0, \ldots, n-1)` to vertex labels in `G`. When @@ -906,6 +1028,8 @@ def eccentricity(G, algorithm="standard", vertex_list=None): ecc = c_eccentricity_bounding(G, vertex_list=int_to_vertex) elif algorithm == "standard": ecc = c_eccentricity(G, vertex_list=int_to_vertex) + elif algorithm == "DHV": + ecc = c_eccentricity(G,vertex_list=int_to_vertex) else: raise ValueError("unknown algorithm '{}', please contribute".format(algorithm)) From 3763266aa13d3e637292e2091c360a670bf441bf Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Tue, 9 Jun 2020 02:22:25 +0530 Subject: [PATCH 028/204] Without exposing in method eccentricity --- src/sage/graphs/distances_all_pairs.pyx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 566adcbeb9d..e3218280bcc 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -801,7 +801,7 @@ cdef uint32_t * c_eccentricity_bounding(G, vertex_list=None) except NULL: return LB -cdef uint32_t * all_eccentricity_DHV(G, vertex_list=None): +def all_eccentricity_DHV(G, vertex_list=None): r""" Returns vector of all eccentricities of unweighted graph `G`. @@ -826,7 +826,7 @@ cdef uint32_t * all_eccentricity_DHV(G, vertex_list=None): cdef uint32_t n = G.order() if not n: - return NULL + return [] cdef short_digraph sd init_short_digraph(sd, G, edge_labelled=False, vertex_list=vertex_list) @@ -839,8 +839,8 @@ cdef uint32_t * all_eccentricity_DHV(G, vertex_list=None): cdef uint32_t * waiting_list = distances + n # For storing upper and lower bounds on eccentricity of nodes - cdef uint32_t * ecc_upper_bound = distances + 2 * n - cdef uint32_t * ecc_lower_bound = distances + 3 * n + cdef uint32_t * ecc_lower_bound = distances + 2 * n + cdef uint32_t * ecc_upper_bound = distances + 3 * n memset(ecc_upper_bound, -1, n * sizeof(uint32_t)) memset(ecc_lower_bound, 0, n * sizeof(uint32_t)) @@ -914,7 +914,10 @@ cdef uint32_t * all_eccentricity_DHV(G, vertex_list=None): if flag: # We are done. break - return ecc_upper_bound + l = [] + for i in range(n): + l.append(ecc_upper_bound[i]) + return l def eccentricity(G, algorithm="standard", vertex_list=None): r""" @@ -1028,8 +1031,8 @@ def eccentricity(G, algorithm="standard", vertex_list=None): ecc = c_eccentricity_bounding(G, vertex_list=int_to_vertex) elif algorithm == "standard": ecc = c_eccentricity(G, vertex_list=int_to_vertex) - elif algorithm == "DHV": - ecc = c_eccentricity(G,vertex_list=int_to_vertex) + #elif algorithm == "DHV": + # ecc = all_eccentricity_DHV(G,vertex_list=int_to_vertex) else: raise ValueError("unknown algorithm '{}', please contribute".format(algorithm)) From 6529e6f8281aba63143d17e211b93abe7aa1713e Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Tue, 9 Jun 2020 02:33:21 +0530 Subject: [PATCH 029/204] exposed in eccentricity method --- src/sage/graphs/distances_all_pairs.pyx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index e3218280bcc..9d86f5ac739 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -801,7 +801,7 @@ cdef uint32_t * c_eccentricity_bounding(G, vertex_list=None) except NULL: return LB -def all_eccentricity_DHV(G, vertex_list=None): +cdef uint32_t * all_eccentricity_DHV(G, vertex_list=None): r""" Returns vector of all eccentricities of unweighted graph `G`. @@ -826,7 +826,7 @@ def all_eccentricity_DHV(G, vertex_list=None): cdef uint32_t n = G.order() if not n: - return [] + return NULL cdef short_digraph sd init_short_digraph(sd, G, edge_labelled=False, vertex_list=vertex_list) @@ -914,10 +914,7 @@ def all_eccentricity_DHV(G, vertex_list=None): if flag: # We are done. break - l = [] - for i in range(n): - l.append(ecc_upper_bound[i]) - return l + return ecc_upper_bound def eccentricity(G, algorithm="standard", vertex_list=None): r""" @@ -1031,8 +1028,8 @@ def eccentricity(G, algorithm="standard", vertex_list=None): ecc = c_eccentricity_bounding(G, vertex_list=int_to_vertex) elif algorithm == "standard": ecc = c_eccentricity(G, vertex_list=int_to_vertex) - #elif algorithm == "DHV": - # ecc = all_eccentricity_DHV(G,vertex_list=int_to_vertex) + elif algorithm == "DHV": + ecc = all_eccentricity_DHV(G,vertex_list=int_to_vertex) else: raise ValueError("unknown algorithm '{}', please contribute".format(algorithm)) From d2fd21f41280e5c3aeae934e8df440a3b8dad473 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 8 Jun 2020 18:04:50 -0400 Subject: [PATCH 030/204] Fix for conversion from ZZ[] to Qp[] --- .../polynomial/padics/polynomial_padic_capped_relative_dense.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9ef55881d35..72a1dc07bd4 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 @@ -92,8 +92,8 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct = False, self._adjust_prec_info(absprec, relprec) return elif x.base_ring() is ZZ: - self._poly = x self._valbase = Integer(0) + self._poly = PolynomialRing(ZZ, parent.variable_name())([a >> self._valbase for a in x]) p = parentbr.prime() self._relprecs = [c.valuation(p) + parentbr.precision_cap() for c in x.list()] self._comp_valaddeds() From 12a0454ca0bafefe2fa038bf8af733a6ceb70260 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Mon, 8 Jun 2020 21:53:49 -0400 Subject: [PATCH 031/204] Better fix --- .../polynomial/padics/polynomial_padic_capped_relative_dense.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 72a1dc07bd4..64018c909fe 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 @@ -92,8 +92,8 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct = False, self._adjust_prec_info(absprec, relprec) return elif x.base_ring() is ZZ: + self._poly = PolynomialRing(ZZ, parent.variable_name())(x) self._valbase = Integer(0) - self._poly = PolynomialRing(ZZ, parent.variable_name())([a >> self._valbase for a in x]) p = parentbr.prime() self._relprecs = [c.valuation(p) + parentbr.precision_cap() for c in x.list()] self._comp_valaddeds() From e8523050bac734eec13d82bffa0050c0e0e53550 Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Tue, 9 Jun 2020 10:54:59 +0530 Subject: [PATCH 032/204] Fixed and finalized --- src/sage/graphs/distances_all_pairs.pyx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 9d86f5ac739..06c37ea3de9 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -831,16 +831,18 @@ cdef uint32_t * all_eccentricity_DHV(G, vertex_list=None): cdef short_digraph sd init_short_digraph(sd, G, edge_labelled=False, vertex_list=vertex_list) - cdef MemoryAllocator mem = MemoryAllocator() - cdef uint32_t * distances = mem.malloc(4 * n * sizeof(uint32_t)) - if not distances: + cdef uint32_t * distances = sig_malloc(3 * n * sizeof(uint32_t)) + # For storing upper bounds on eccentricity of nodes + cdef uint32_t * ecc_upper_bound = sig_calloc(n, sizeof(uint32_t)) + if not distances or not ecc_upper_bound: + sig_free(distances) + sig_free(ecc_upper_bound) + free_short_digraph(sd) raise MemoryError() cdef uint32_t * waiting_list = distances + n - - # For storing upper and lower bounds on eccentricity of nodes + # For storing lower bounds on eccentricity of nodes cdef uint32_t * ecc_lower_bound = distances + 2 * n - cdef uint32_t * ecc_upper_bound = distances + 3 * n memset(ecc_upper_bound, -1, n * sizeof(uint32_t)) memset(ecc_lower_bound, 0, n * sizeof(uint32_t)) @@ -914,6 +916,10 @@ cdef uint32_t * all_eccentricity_DHV(G, vertex_list=None): if flag: # We are done. break + free_short_digraph(sd) + sig_free(distances) + bitset_free(seen) + return ecc_upper_bound def eccentricity(G, algorithm="standard", vertex_list=None): From 0bc8de4e2bfcaaade98ece4df3578f1d6c8fa022 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Tue, 9 Jun 2020 09:54:04 +0200 Subject: [PATCH 033/204] trac #27934: review improvements --- src/sage/graphs/distances_all_pairs.pyx | 130 +++++++++++++----------- 1 file changed, 69 insertions(+), 61 deletions(-) diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 06c37ea3de9..e8d392b0ce1 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -801,12 +801,24 @@ cdef uint32_t * c_eccentricity_bounding(G, vertex_list=None) except NULL: return LB -cdef uint32_t * all_eccentricity_DHV(G, vertex_list=None): +cdef uint32_t * c_eccentricity_DHV(G, vertex_list=None): r""" - Returns vector of all eccentricities of unweighted graph `G`. + Return the vector of eccentricities using the algorithm of [Dragan2018]_. - This method computes the eccentricity of all vertices of - unweighted undirected graph using the algorithm proposed in [Dragan2018]_. + The array returned is of length `n`, and by default its `i`-th component is + the eccentricity of the `i`-th vertex in ``G.vertices()``. + + Optional parameter ``vertex_list`` is a list of `n` vertices specifying a + mapping from `(0, \ldots, n-1)` to vertex labels in `G`. When set, + ``ecc[i]`` is the eccentricity of vertex ``vertex_list[i]``. + + The algorithm proposed in [Dragan2018]_ is an improvement of the algorithm + proposed in [TK2013]_. It is also is based on the observation that for all + nodes `v,w\in V`, we have `\max(ecc[v]-d(v,w), d(v,w))\leq ecc[w] \leq + ecc[v] + d(v,w)`. Also the algorithms iteratively improves upper and lower + bounds on the eccentricity of each vertex until no further improvements can + be done. The difference with [TK2013]_ is in the order in which improvements + are done. EXAMPLES:: @@ -817,12 +829,12 @@ cdef uint32_t * all_eccentricity_DHV(G, vertex_list=None): TESTS: - sage: G = graphs.RandomBarabasiAlbert(11,7) - sage: eccentricity(G) == eccentricity(G, algorithm ='DHV') + sage: G = graphs.RandomBarabasiAlbert(50, 2) + sage: eccentricity(G, algorithm='bounds') == eccentricity(G, algorithm='DHV') True """ if G.is_directed(): - raise ValueError("this algorithm only works on undirected graphs.") + raise ValueError("the 'DHV' algorithm only works on undirected graphs") cdef uint32_t n = G.order() if not n: @@ -831,11 +843,11 @@ cdef uint32_t * all_eccentricity_DHV(G, vertex_list=None): cdef short_digraph sd init_short_digraph(sd, G, edge_labelled=False, vertex_list=vertex_list) - cdef uint32_t * distances = sig_malloc(3 * n * sizeof(uint32_t)) + cdef MemoryAllocator mem = MemoryAllocator() + cdef uint32_t * distances = mem.malloc(3 * n * sizeof(uint32_t)) # For storing upper bounds on eccentricity of nodes cdef uint32_t * ecc_upper_bound = sig_calloc(n, sizeof(uint32_t)) if not distances or not ecc_upper_bound: - sig_free(distances) sig_free(ecc_upper_bound) free_short_digraph(sd) raise MemoryError() @@ -863,61 +875,54 @@ cdef uint32_t * all_eccentricity_DHV(G, vertex_list=None): # For this, we select u with minimum eccentricity lower bound in active # if ecc_u == ecc_lb[u], we are done. Otherwise, we update eccentricity # lower bounds and repeat - while active: - # Select u with minimum eccentricity lower bound - tmp = UINT32_MAX - for i, v in enumerate(active): - if ecc_lower_bound[v] < tmp: - tmp = ecc_lower_bound[v] - idx = i - active[idx], active[-1] = active[-1], active[idx] - u = active.pop() - ecc_u = simple_BFS(sd, u, distances, NULL, waiting_list, seen) - ecc_upper_bound[u] = ecc_u - - if ecc_u == UINT32_MAX: # Disconnected graph - flag = 1 - break - - if ecc_u == ecc_lower_bound[u]: - # Update eccentricity upper bounds of the remaining vertices - # and break. - for v in active: - ecc_upper_bound[v] = min(ecc_upper_bound[v], distances[v] + ecc_u) - break - else: - # u was not a good choice. - # We use its antipode to update eccentricity lower bounds. - # Observe that this antipode might have already been seen. - antipode = waiting_list[n-1] - for i, v in enumerate(active): - if v == antipode: - active[i] = active[-1] - active.pop() - break - - ecc_antipode = simple_BFS(sd, antipode, distances, NULL, waiting_list, seen) - ecc_upper_bound[antipode] = ecc_antipode - - for v in active: - ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v]) - - if flag: # Disconnected graph + tmp = UINT32_MAX + for i, v in enumerate(active): + if ecc_lower_bound[v] < tmp: + tmp = ecc_lower_bound[v] + idx = i + active[idx], active[-1] = active[-1], active[idx] + u = active.pop() + ecc_u = simple_BFS(sd, u, distances, NULL, waiting_list, seen) + ecc_upper_bound[u] = ecc_u + + if ecc_u == UINT32_MAX: # Disconnected graph break - # Check if there exist any vertex in active, whose eccentricity bounds - # can be further updated. - flag = 1 - for v in active: - if ecc_upper_bound[v] > ecc_lower_bound[v]: - flag = 0 - break - if flag: # We are done. - break + if ecc_u == ecc_lower_bound[u]: + # We found the good vertex. + # Update eccentricity upper bounds of the remaining vertices + for v in active: + ecc_upper_bound[v] = min(ecc_upper_bound[v], distances[v] + ecc_u) + + else: + # u was not a good choice. + # We use its antipode to update eccentricity lower bounds. + # Observe that this antipode might have already been seen. + antipode = waiting_list[n-1] + for i, v in enumerate(active): + if v == antipode: + active[i] = active[-1] + active.pop() + break + + ecc_antipode = simple_BFS(sd, antipode, distances, NULL, waiting_list, seen) + ecc_upper_bound[antipode] = ecc_antipode + + for v in active: + ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v]) + + # We remove from active vertices for which the gap is closed + i = 0 + while i < len(active): + v = active[i] + if ecc_upper_bound[v] == ecc_lower_bound[v]: + active[i] = active[-1] + active.pop() + else: + i += 1 free_short_digraph(sd) - sig_free(distances) bitset_free(seen) return ecc_upper_bound @@ -970,7 +975,10 @@ def eccentricity(G, algorithm="standard", vertex_list=None): sage: from sage.graphs.distances_all_pairs import eccentricity sage: g = graphs.RandomGNP(50, .1) - sage: eccentricity(g, algorithm='standard') == eccentricity(g, algorithm='bounds') + sage: ecc = eccentricity(g, algorithm='standard') + sage: ecc == eccentricity(g, algorithm='bounds') + True + sage: ecc == eccentricity(g, algorithm='DHV') True Case of not (strongly) connected (directed) graph:: @@ -1035,7 +1043,7 @@ def eccentricity(G, algorithm="standard", vertex_list=None): elif algorithm == "standard": ecc = c_eccentricity(G, vertex_list=int_to_vertex) elif algorithm == "DHV": - ecc = all_eccentricity_DHV(G,vertex_list=int_to_vertex) + ecc = c_eccentricity_DHV(G, vertex_list=int_to_vertex) else: raise ValueError("unknown algorithm '{}', please contribute".format(algorithm)) From d7975d52f621ebde29084b462f88f6cc13004b83 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Tue, 9 Jun 2020 14:44:08 -0400 Subject: [PATCH 034/204] Added doctest --- .../padics/polynomial_padic_capped_relative_dense.py | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 64018c909fe..4a911df1e45 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 @@ -52,6 +52,16 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct = False, sage: R(f.dict()) 0 + Check that :trac:`29829` has been fixed:: + + sage: R. = PolynomialRing(ZZ) + sage: f = x + 5 + sage: S. = PolynomialRing(Qp(5)) + sage: g2 = S(f) + sage: L = g2.base_ring() + sage: pow = L.uniformiser_pow(2) + sage: pow*g2 + (5^2 + O(5^22))*y + 5^3 + O(5^23) """ Polynomial.__init__(self, parent, is_gen=is_gen) self._polygon = None From 726c83b4c3ea482666d3785c7ebfb78e6fd68384 Mon Sep 17 00:00:00 2001 From: Alexander Galarraga Date: Tue, 9 Jun 2020 16:24:28 -0400 Subject: [PATCH 035/204] Better test --- .../padics/polynomial_padic_capped_relative_dense.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 4a911df1e45..11b2508f476 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 @@ -58,9 +58,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct = False, sage: f = x + 5 sage: S. = PolynomialRing(Qp(5)) sage: g2 = S(f) - sage: L = g2.base_ring() - sage: pow = L.uniformiser_pow(2) - sage: pow*g2 + sage: 25*g2 (5^2 + O(5^22))*y + 5^3 + O(5^23) """ Polynomial.__init__(self, parent, is_gen=is_gen) From 66d3471f6dbef8deaa8f396ddef6a15c275333e5 Mon Sep 17 00:00:00 2001 From: paulmasson Date: Tue, 9 Jun 2020 16:46:35 -0700 Subject: [PATCH 036/204] Add viewpoint option --- src/doc/en/reference/plot3d/threejs.rst | 3 +++ .../ext_data/threejs/threejs_template.html | 18 +++++++++++++++++- src/sage/plot/plot3d/base.pyx | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/doc/en/reference/plot3d/threejs.rst b/src/doc/en/reference/plot3d/threejs.rst index 82ddf609f71..377575e74a4 100644 --- a/src/doc/en/reference/plot3d/threejs.rst +++ b/src/doc/en/reference/plot3d/threejs.rst @@ -59,6 +59,9 @@ Options currently supported by the viewer: - ``thickness`` -- (default: 1) numeric value for thickness of lines +- ``viewpoint`` -- (default: None) string of the form '[x,y,z],angle' setting the initial viewpoint + of the scene; can be determined using the 'Get Viewpoint' option of the information menu + Clicking on the information icon in the lower right-hand corner of the viewer opens a menu of available actions. These include saving the three-dimensional scene as a static PNG image or as complete HTML source code. diff --git a/src/sage/ext_data/threejs/threejs_template.html b/src/sage/ext_data/threejs/threejs_template.html index 5f5715276ab..1e926bad329 100644 --- a/src/sage/ext_data/threejs/threejs_template.html +++ b/src/sage/ext_data/threejs/threejs_template.html @@ -146,7 +146,23 @@ var camera = createCamera(); camera.up.set( 0, 0, 1 ); - camera.position.set( a[0]*(xMid+xRange), a[1]*(yMid+yRange), a[2]*(zMid+zRange) ); + camera.position.set( a[0]*xMid, a[1]*yMid, a[2]*zMid ); + + var offset = new THREE.Vector3( a[0]*xRange, a[1]*yRange, a[2]*zRange ); + + if ( options.viewpoint ) { + + var aa = options.viewpoint.replace('[','').replace(']','').split(','); + var axis = new THREE.Vector3( aa[0], aa[1], aa[2] ); + var angle = aa[3] * Math.PI / 180; + var q = new THREE.Quaternion().setFromAxisAngle( axis, angle ).inverse(); + + offset.set( 0, 0, offset.length() ); + offset.applyQuaternion( q ); + + } + + camera.position.add( offset ); function createCamera() { diff --git a/src/sage/plot/plot3d/base.pyx b/src/sage/plot/plot3d/base.pyx index 2d53ec23945..73444ad6688 100644 --- a/src/sage/plot/plot3d/base.pyx +++ b/src/sage/plot/plot3d/base.pyx @@ -374,6 +374,7 @@ cdef class Graphics3d(SageObject): js_options['decimals'] = options.get('decimals', 2) js_options['frame'] = options.get('frame', True) js_options['projection'] = options.get('projection', 'perspective') + js_options['viewpoint'] = options.get('viewpoint', False) if js_options['projection'] not in ['perspective', 'orthographic']: import warnings From 99df77458c7f5b698713de5fc00409ebcd390c72 Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Wed, 10 Jun 2020 18:51:11 +0530 Subject: [PATCH 037/204] minor changes --- src/sage/graphs/distances_all_pairs.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index e8d392b0ce1..4775cd59e56 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -862,7 +862,6 @@ cdef uint32_t * c_eccentricity_DHV(G, vertex_list=None): cdef uint32_t antipode, ecc_antipode cdef uint32_t v, tmp cdef size_t i, idx - cdef bint flag = 0 cdef bitset_t seen bitset_init(seen,n) @@ -912,7 +911,7 @@ cdef uint32_t * c_eccentricity_DHV(G, vertex_list=None): for v in active: ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v]) - # We remove from active vertices for which the gap is closed + # We remove those vertices from active for which gap is closed i = 0 while i < len(active): v = active[i] From e84d18b22adfb266f44e98ede1b013fbeee86118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 19 May 2020 22:13:07 +0200 Subject: [PATCH 038/204] a whole bunch of *micro* optimisations for comparisons to [] --- src/sage/algebras/cluster_algebra.py | 2 +- src/sage/categories/coxeter_groups.py | 2 +- src/sage/categories/rings.py | 2 +- src/sage/combinat/dyck_word.py | 2 +- src/sage/combinat/words/finite_word.py | 4 ++-- src/sage/combinat/words/suffix_trees.py | 4 ++-- src/sage/databases/stein_watkins.py | 4 ++-- .../endPN_automorphism_group.py | 14 +++++++++----- src/sage/graphs/bipartite_graph.py | 4 ++-- src/sage/graphs/comparability.pyx | 2 +- src/sage/graphs/independent_sets.pyx | 2 +- src/sage/graphs/isgci.py | 3 +-- src/sage/manifolds/chart.py | 8 +++++--- .../differentiable/affine_connection.py | 2 +- src/sage/manifolds/scalarfield.py | 4 ++-- src/sage/modular/modform/find_generators.py | 19 ++++++++++++------- .../schemes/elliptic_curves/mod_sym_num.pyx | 2 +- src/sage/sets/family.py | 2 +- src/sage/tests/finite_poset.py | 14 +++++++++----- 19 files changed, 55 insertions(+), 41 deletions(-) diff --git a/src/sage/algebras/cluster_algebra.py b/src/sage/algebras/cluster_algebra.py index e3c7b1ea99d..770dd68b442 100644 --- a/src/sage/algebras/cluster_algebra.py +++ b/src/sage/algebras/cluster_algebra.py @@ -1063,7 +1063,7 @@ def mutate(self, direction, **kwargs): raise ValueError('cannot mutate in direction ' + str(k)) # store new mutation path - if to_mutate._path != [] and to_mutate._path[-1] == k: + if to_mutate._path and to_mutate._path[-1] == k: to_mutate._path.pop() else: to_mutate._path.append(k) diff --git a/src/sage/categories/coxeter_groups.py b/src/sage/categories/coxeter_groups.py index 5826337bf17..2301383e6d4 100644 --- a/src/sage/categories/coxeter_groups.py +++ b/src/sage/categories/coxeter_groups.py @@ -800,7 +800,7 @@ def bruhat_interval(self, x, y): if not x.bruhat_le(y): return ret ret.append([y]) - while ret[-1] != []: + while ret[-1]: nextlayer = [] for z in ret[-1]: for t in z.bruhat_lower_covers(): diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index ff0f66b3aeb..e07bd633a34 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -1055,7 +1055,7 @@ def normalize_arg(arg): # 1. If arg is a list, try to return a power series ring. if isinstance(arg, list): - if arg == []: + if not arg: raise TypeError("power series rings must have at least one variable") elif len(arg) == 1: # R[["a,b"]], R[[(a,b)]]... diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index a87cc7b2001..438b68ef939 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -1580,7 +1580,7 @@ def to_standard_tableau(self): else: close_positions.append(i + 1) from sage.combinat.tableau import StandardTableau - return StandardTableau([x for x in [open_positions, close_positions] if x != []]) + return StandardTableau([x for x in [open_positions, close_positions] if x]) def to_tamari_sorting_tuple(self): """ diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py index 9406087ff8e..91d00310f52 100644 --- a/src/sage/combinat/words/finite_word.py +++ b/src/sage/combinat/words/finite_word.py @@ -4747,8 +4747,8 @@ def is_quasiperiodic(self): return False for i in range(1, l - 1): return_lengths = [x.length() for x in self.return_words(self[:i])] - if return_lengths != []: - if (max(return_lengths) <= i and self[l-i:l] == self[:i]): + if return_lengths: + if max(return_lengths) <= i and self[l-i:l] == self[:i]: return True return False diff --git a/src/sage/combinat/words/suffix_trees.py b/src/sage/combinat/words/suffix_trees.py index daaa592cb73..9a61e4e4b36 100644 --- a/src/sage/combinat/words/suffix_trees.py +++ b/src/sage/combinat/words/suffix_trees.py @@ -833,8 +833,8 @@ def to_digraph(self, word_labels=False): sage: t.to_digraph() Digraph on 8 vertices """ - if self._letters == []: - d = {0:{}} + if not self._letters: + d = {0: {}} return DiGraph(d) d = self.transition_function_dictionary() for u in d: diff --git a/src/sage/databases/stein_watkins.py b/src/sage/databases/stein_watkins.py index 45a45c8472d..00fe7181c82 100644 --- a/src/sage/databases/stein_watkins.py +++ b/src/sage/databases/stein_watkins.py @@ -297,11 +297,11 @@ def iter_levels(self): try: E = next(it) except StopIteration: - if C != []: + if C: yield C return if E.conductor != N: - if C != []: + if C: yield C C = [E] N = E.conductor diff --git a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py index 5f855cd915f..a065a324394 100644 --- a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +++ b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py @@ -1390,14 +1390,18 @@ def order_p_automorphisms(rational_function, pre_image): u = F(1) / (z - pt[0]) u_inv = pt[0] + F(1)/z for i in range(1,m): - if M[0] == [F(1),F(0)]: uy1 = 0 - else: uy1 = u(M[0][0]) - if M[i] == [F(1),F(0)]: uy2 = 0 - else: uy2 = u(M[i][0]) + if M[0] == [F(1),F(0)]: + uy1 = 0 + else: + uy1 = u(M[0][0]) + if M[i] == [F(1),F(0)]: + uy2 = 0 + else: + uy2 = u(M[i][0]) s = u_inv( u(z) + uy2 - uy1 ) if s(phi(z)) == phi(s(z)): automorphisms_p.append(s) - elif T==[]: + elif not T: # create the extension field generated by pre-images of the unique fixed point T_poly = pre_image[0][2] e = lcm([x[0].degree() for x in T_poly.factor()])*F.degree() diff --git a/src/sage/graphs/bipartite_graph.py b/src/sage/graphs/bipartite_graph.py index 28cf2877462..50ce4a70f20 100644 --- a/src/sage/graphs/bipartite_graph.py +++ b/src/sage/graphs/bipartite_graph.py @@ -1553,14 +1553,14 @@ def matching(self, value_only=False, algorithm=None, sage: B = BipartiteGraph() sage: algorithms = ["Hopcroft-Karp", "Eppstein", "Edmonds", "LP"] - sage: all(B.matching(algorithm=algo) == [] for algo in algorithms) + sage: not any(B.matching(algorithm=algo) for algo in algorithms) True sage: all(B.matching(algorithm=algo, value_only=True) == 0 for algo in algorithms) True sage: B.add_vertex(1, left=True) sage: B.add_vertex(2, left=True) sage: B.add_vertex(3, right=True) - sage: all(B.matching(algorithm=algo) == [] for algo in algorithms) + sage: not any(B.matching(algorithm=algo) for algo in algorithms) True sage: all(B.matching(algorithm=algo, value_only=True) == 0 for algo in algorithms) True diff --git a/src/sage/graphs/comparability.pyx b/src/sage/graphs/comparability.pyx index fc19042f23c..44247d26ab0 100644 --- a/src/sage/graphs/comparability.pyx +++ b/src/sage/graphs/comparability.pyx @@ -752,7 +752,7 @@ def is_transitive(g, certificate=False): sage: cert = D.is_transitive(certificate=True) sage: D.has_edge(*cert) False - sage: D.shortest_path(*cert) != [] + sage: bool(D.shortest_path(*cert)) True sage: digraphs.RandomDirectedGNP(20,.2).transitive_closure().is_transitive() True diff --git a/src/sage/graphs/independent_sets.pyx b/src/sage/graphs/independent_sets.pyx index 63cf7b7ac97..35fb6562fe8 100644 --- a/src/sage/graphs/independent_sets.pyx +++ b/src/sage/graphs/independent_sets.pyx @@ -370,7 +370,7 @@ cdef class IndependentSets: True """ if not self.n: - return S == [] + return not S cdef int i # Set of vertices as a bitset diff --git a/src/sage/graphs/isgci.py b/src/sage/graphs/isgci.py index ce6211aa104..b1023c093cb 100644 --- a/src/sage/graphs/isgci.py +++ b/src/sage/graphs/isgci.py @@ -500,9 +500,8 @@ def __ge__(self, other): sage: graph_classes.Chordal >= graph_classes.Tree True """ - inclusion_digraph = GraphClasses().inclusion_digraph() - if inclusion_digraph.shortest_path(self._gc_id,other._gc_id) != []: + if inclusion_digraph.shortest_path(self._gc_id,other._gc_id): return True else: return Unknown diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py index f1a8cb3f58d..07e3fa94aee 100644 --- a/src/sage/manifolds/chart.py +++ b/src/sage/manifolds/chart.py @@ -1649,8 +1649,10 @@ def _init_coordinates(self, coord_list): coord_symb = coord_properties[0].strip() # the coordinate symbol # default values, possibly redefined below: coord_latex = None - xmin = -Infinity; xmin_included = False - xmax = +Infinity; xmax_included = False + xmin = -Infinity + xmin_included = False + xmax = +Infinity + xmax_included = False # scan of the properties other than the symbol: is_periodic = False for prop in coord_properties[1:]: @@ -2912,7 +2914,7 @@ def _plot_xx_list(xx_list, rem_coords, ranges, steps, number_values): first_invalid = False # next invalid point will not # be the first one xc += dx - if curve != []: + if curve: resu += line(curve, color=color_c, linestyle=style_c, thickness=thickness_c) diff --git a/src/sage/manifolds/differentiable/affine_connection.py b/src/sage/manifolds/differentiable/affine_connection.py index 3cb22403aa3..5cb8d5a6f96 100644 --- a/src/sage/manifolds/differentiable/affine_connection.py +++ b/src/sage/manifolds/differentiable/affine_connection.py @@ -1450,7 +1450,7 @@ def _derive_paral(self, tensor): # Component computation in the common frame: tc = tensor._components[frame] gam = self._coefficients[frame] - if tensor._sym == [] and tensor._antisym == []: + if not tensor._sym and not tensor._antisym: resc = Components(tdom.scalar_field_algebra(), frame, tensor._tensor_rank+1, start_index=self._domain._sindex, diff --git a/src/sage/manifolds/scalarfield.py b/src/sage/manifolds/scalarfield.py index 6cf8a987cd6..edb52ecd954 100644 --- a/src/sage/manifolds/scalarfield.py +++ b/src/sage/manifolds/scalarfield.py @@ -2262,7 +2262,7 @@ def common_charts(self, other): if (chart2, chart1) in coord_changes: self.coord_function(chart2, from_chart=chart1) resu.append(chart2) - if resu == []: + if not resu: return None else: return resu @@ -2689,7 +2689,7 @@ def _lmul_(self, number): var_not_in_chart = [s for s in var if not s in chart_coords] any_in_other_chart = False - if var_not_in_chart != []: + if var_not_in_chart: for other_chart in self._domain.atlas(): other_chart_coords = other_chart[:] for s in var_not_in_chart: diff --git a/src/sage/modular/modform/find_generators.py b/src/sage/modular/modform/find_generators.py index c593213bc04..5d510776a38 100644 --- a/src/sage/modular/modform/find_generators.py +++ b/src/sage/modular/modform/find_generators.py @@ -111,7 +111,8 @@ def _span_of_forms_in_weight(forms, weight, prec, stop_dim=None, use_random=Fals for c in range(N): w = V(prod(shortforms[i]**wts[c][i] for i in range(n)).padded_list(prec)) - if w in W: continue + if w in W: + continue W = V.span(list(W.gens()) + [w]) if stop_dim and W.rank() == stop_dim: if R != ZZ or W.index_in_saturation() == 1: @@ -541,7 +542,8 @@ def _find_generators(self, maxweight, start_gens, start_weight): G.append((k, f, F)) k = start_weight - if increment == 2 and (k % 2) == 1: k += 1 + if increment == 2 and (k % 2) == 1: + k += 1 while k <= maxweight: @@ -549,12 +551,12 @@ def _find_generators(self, maxweight, start_gens, start_weight): k += increment continue - verbose('Looking at k = %s'%k) + verbose('Looking at k = %s' % k) M = self.modular_forms_of_weight(k) # 1. Multiply together all forms in G that give an element # of M. - if G != []: + if G: F = _span_of_forms_in_weight(G, k, M.sturm_bound(), None, False) else: F = (self.base_ring() ** M.sturm_bound()).zero_submodule() @@ -574,7 +576,8 @@ def _find_generators(self, maxweight, start_gens, start_weight): # try adding basis elements of M into G. verbose("Known generators span a subspace of dimension %s of space of dimension %s" % (F.dimension(), M.dimension())) - if self.base_ring() == ZZ: verbose("saturation index is %s" % F.index_in_saturation()) + if self.base_ring() == ZZ: + verbose("saturation index is %s" % F.index_in_saturation()) t = verbose("Computing more modular forms at weight %s" % k) kprec = M.sturm_bound() @@ -650,7 +653,8 @@ def q_expansion_basis(self, weight, prec=None, use_random=True): [1 + O(q^5), q + O(q^5), q^2 + O(q^5), q^3 + O(q^5), q^4 + O(q^5), O(q^5), O(q^5), O(q^5), O(q^5), O(q^5)] """ d = self.modular_forms_of_weight(weight).dimension() - if d == 0: return [] + if d == 0: + return [] if prec is None: prec=self.modular_forms_of_weight(weight).sturm_bound() @@ -786,7 +790,8 @@ def cuspidal_submodule_q_expansion_basis(self, weight, prec=None): True """ d = self.modular_forms_of_weight(weight).cuspidal_submodule().dimension() - if d == 0: return [] + if d == 0: + return [] minprec = self.modular_forms_of_weight(weight).sturm_bound() if prec is None: diff --git a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx index f56d3f424b2..9ea1c138eea 100644 --- a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx +++ b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx @@ -3784,7 +3784,7 @@ def _test_against_table(range_of_conductors, other_implementation="sage", list_o # """finds all n with y(n) > B for some bound B""" # li = [1] # old = [1] -# while old != []: +# while old: # new = [] # p = 1 # boo = True diff --git a/src/sage/sets/family.py b/src/sage/sets/family.py index ed9b550a0b9..083551c398d 100644 --- a/src/sage/sets/family.py +++ b/src/sage/sets/family.py @@ -378,7 +378,7 @@ def Family(indices, function=None, hidden_keys=[], hidden_function=None, lazy=Fa assert(isinstance(hidden_keys, list)) assert(isinstance(lazy, bool)) - if hidden_keys == []: + if not hidden_keys: if hidden_function is not None: raise ValueError("hidden_function keyword only makes sense " "together with hidden_keys keyword !") diff --git a/src/sage/tests/finite_poset.py b/src/sage/tests/finite_poset.py index fca907d8681..fc1213d4ecf 100644 --- a/src/sage/tests/finite_poset.py +++ b/src/sage/tests/finite_poset.py @@ -204,7 +204,8 @@ def test_finite_lattice(L): # Return value must be a pair with correct result as first element. for p_ in all_props: # Dirty fix first - if p_[:9] == 'doubling_' or p_[:5] == 'uniq_': continue + if p_[:9] == 'doubling_' or p_[:5] == 'uniq_': + continue p = "is_"+p_ if 'certificate' in sage_getargspec(getattr(L, p)).args: res = attrcall(p, certificate=True)(L) @@ -248,7 +249,7 @@ def test_finite_lattice(L): if not P['complemented']: a = L.is_complemented(certificate=True)[1] - if L.complements(a) != []: + if L.complements(a): raise ValueError("compl. error 1") if not P['sectionally_complemented']: a, b = L.is_sectionally_complemented(certificate=True)[1] @@ -262,7 +263,7 @@ def test_finite_lattice(L): L_ = L.sublattice(L.interval(a, L.top())) if L_.is_complemented(): raise ValueError("cosec. compl. error 1") - if len(L_.complements(b)) > 0: + if L_.complements(b): raise ValueError("cosec. compl. error 2") if not P['relatively_complemented']: a, b, c = L.is_relatively_complemented(certificate=True)[1] @@ -335,7 +336,8 @@ def test_finite_lattice(L): if not P['subdirectly_reducible']: x, y = L.is_subdirectly_reducible(certificate=True)[1] - a = L.random_element(); b = L.random_element() + a = L.random_element() + b = L.random_element() c = L.congruence([[a, b]]) if len(c) != L.cardinality(): for c_ in c: @@ -433,10 +435,12 @@ def test_finite_lattice(L): # Misc misc e = L.neutral_elements() e = e[randint(0, len(e)-1)] - a = L.random_element(); b = L.random_element() + a = L.random_element() + b = L.random_element() if not L.sublattice([e, a, b]).is_distributive(): raise ValueError("error in neutral_elements") + def test_finite_poset(P): """ Test several functions on a given finite poset. From 37b0c870f371685be673d22d5d6c1526144b4c49 Mon Sep 17 00:00:00 2001 From: paulmasson Date: Thu, 11 Jun 2020 17:19:54 -0700 Subject: [PATCH 039/204] Update viewpoint option --- src/doc/en/reference/plot3d/threejs.rst | 2 +- src/sage/ext_data/threejs/threejs_template.html | 6 +++--- src/sage/plot/plot3d/base.pyx | 9 +++++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/doc/en/reference/plot3d/threejs.rst b/src/doc/en/reference/plot3d/threejs.rst index 377575e74a4..fe67f6b5b71 100644 --- a/src/doc/en/reference/plot3d/threejs.rst +++ b/src/doc/en/reference/plot3d/threejs.rst @@ -59,7 +59,7 @@ Options currently supported by the viewer: - ``thickness`` -- (default: 1) numeric value for thickness of lines -- ``viewpoint`` -- (default: None) string of the form '[x,y,z],angle' setting the initial viewpoint +- ``viewpoint`` -- (default: None) string of the form [[x,y,z],angle] setting the initial viewpoint of the scene; can be determined using the 'Get Viewpoint' option of the information menu Clicking on the information icon in the lower right-hand corner of the viewer opens diff --git a/src/sage/ext_data/threejs/threejs_template.html b/src/sage/ext_data/threejs/threejs_template.html index 1e926bad329..37043b112a4 100644 --- a/src/sage/ext_data/threejs/threejs_template.html +++ b/src/sage/ext_data/threejs/threejs_template.html @@ -152,9 +152,9 @@ if ( options.viewpoint ) { - var aa = options.viewpoint.replace('[','').replace(']','').split(','); - var axis = new THREE.Vector3( aa[0], aa[1], aa[2] ); - var angle = aa[3] * Math.PI / 180; + var aa = options.viewpoint; + var axis = new THREE.Vector3( aa[0][0], aa[0][1], aa[0][2] ).normalize(); + var angle = aa[1] * Math.PI / 180; var q = new THREE.Quaternion().setFromAxisAngle( axis, angle ).inverse(); offset.set( 0, 0, offset.length() ); diff --git a/src/sage/plot/plot3d/base.pyx b/src/sage/plot/plot3d/base.pyx index 73444ad6688..ed1e9d73869 100644 --- a/src/sage/plot/plot3d/base.pyx +++ b/src/sage/plot/plot3d/base.pyx @@ -385,6 +385,15 @@ cdef class Graphics3d(SageObject): js_options['aspectRatio'] = [float(i) for i in js_options['aspectRatio']] js_options['decimals'] = int(js_options['decimals']) + if js_options['viewpoint']: + import warnings + if len(js_options['viewpoint']) != 2 or len(js_options['viewpoint'][0]) != 3: + warnings.warn('viewpoint must be of the form [[x,y,z],angle]') + js_options['viewpoint'] = False + else: + js_options['viewpoint'][0] = [float(i) for i in js_options['viewpoint'][0]] + js_options['viewpoint'][1] = float(js_options['viewpoint'][1]) + if not js_options['frame']: js_options['axesLabels'] = False From f96ccdca5f66ef1f55a84f32d00289e9a39c408a Mon Sep 17 00:00:00 2001 From: paulmasson Date: Thu, 11 Jun 2020 18:10:16 -0700 Subject: [PATCH 040/204] Further refine viewpoint option --- src/doc/en/reference/plot3d/threejs.rst | 3 ++- src/sage/plot/plot3d/base.pyx | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/plot3d/threejs.rst b/src/doc/en/reference/plot3d/threejs.rst index fe67f6b5b71..a8395c4ba72 100644 --- a/src/doc/en/reference/plot3d/threejs.rst +++ b/src/doc/en/reference/plot3d/threejs.rst @@ -60,7 +60,8 @@ Options currently supported by the viewer: - ``thickness`` -- (default: 1) numeric value for thickness of lines - ``viewpoint`` -- (default: None) string of the form [[x,y,z],angle] setting the initial viewpoint - of the scene; can be determined using the 'Get Viewpoint' option of the information menu + of the scene, where angle is in degrees; can be determined using the 'Get Viewpoint' option + of the information menu Clicking on the information icon in the lower right-hand corner of the viewer opens a menu of available actions. These include saving the three-dimensional scene as a static diff --git a/src/sage/plot/plot3d/base.pyx b/src/sage/plot/plot3d/base.pyx index ed1e9d73869..a2740add101 100644 --- a/src/sage/plot/plot3d/base.pyx +++ b/src/sage/plot/plot3d/base.pyx @@ -386,11 +386,15 @@ cdef class Graphics3d(SageObject): js_options['decimals'] = int(js_options['decimals']) if js_options['viewpoint']: - import warnings if len(js_options['viewpoint']) != 2 or len(js_options['viewpoint'][0]) != 3: + import warnings warnings.warn('viewpoint must be of the form [[x,y,z],angle]') js_options['viewpoint'] = False else: + if type(js_options['viewpoint']) is tuple: + js_options['viewpoint'] = list(js_options['viewpoint']) + if type(js_options['viewpoint'][0]) is tuple: + js_options['viewpoint'][0] = list(js_options['viewpoint'][0]) js_options['viewpoint'][0] = [float(i) for i in js_options['viewpoint'][0]] js_options['viewpoint'][1] = float(js_options['viewpoint'][1]) From 850c59a8faa3bbc48492dee2b19550244ba9be84 Mon Sep 17 00:00:00 2001 From: paulmasson Date: Thu, 11 Jun 2020 19:33:46 -0700 Subject: [PATCH 041/204] Update documentation --- src/doc/en/reference/plot3d/threejs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/reference/plot3d/threejs.rst b/src/doc/en/reference/plot3d/threejs.rst index a8395c4ba72..0eec0f64b95 100644 --- a/src/doc/en/reference/plot3d/threejs.rst +++ b/src/doc/en/reference/plot3d/threejs.rst @@ -59,7 +59,7 @@ Options currently supported by the viewer: - ``thickness`` -- (default: 1) numeric value for thickness of lines -- ``viewpoint`` -- (default: None) string of the form [[x,y,z],angle] setting the initial viewpoint +- ``viewpoint`` -- (default: None) list or tuple of the form [[x,y,z],angle] setting the initial viewpoint of the scene, where angle is in degrees; can be determined using the 'Get Viewpoint' option of the information menu From 3b7fc0c1b3c2131cd5a904737e62914ab4b1a540 Mon Sep 17 00:00:00 2001 From: paulmasson Date: Thu, 11 Jun 2020 19:35:38 -0700 Subject: [PATCH 042/204] Update documentation --- src/doc/en/reference/plot3d/threejs.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/en/reference/plot3d/threejs.rst b/src/doc/en/reference/plot3d/threejs.rst index 0eec0f64b95..3d33c8fa78a 100644 --- a/src/doc/en/reference/plot3d/threejs.rst +++ b/src/doc/en/reference/plot3d/threejs.rst @@ -59,9 +59,9 @@ Options currently supported by the viewer: - ``thickness`` -- (default: 1) numeric value for thickness of lines -- ``viewpoint`` -- (default: None) list or tuple of the form [[x,y,z],angle] setting the initial viewpoint - of the scene, where angle is in degrees; can be determined using the 'Get Viewpoint' option - of the information menu +- ``viewpoint`` -- (default: None) list or tuple of the form [[x,y,z],angle] setting the initial + viewpoint of the scene, where angle is in degrees; can be determined using the 'Get Viewpoint' + option of the information menu Clicking on the information icon in the lower right-hand corner of the viewer opens a menu of available actions. These include saving the three-dimensional scene as a static From c1c6712afdfac22fae1daa1ea6ad7114fd00f894 Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Sat, 13 Jun 2020 01:02:26 +0530 Subject: [PATCH 043/204] added for weighted graphs --- src/sage/graphs/base/boost_graph.pyx | 178 ++++++++++++++++++++++++ src/sage/graphs/distances_all_pairs.pyx | 2 +- 2 files changed, 179 insertions(+), 1 deletion(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 6ba0a44c9af..0b60699d223 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -1603,3 +1603,181 @@ cpdef min_cycle_basis(g_sage, weight_function=None, by_weight=False): if len(orth_set[j] & new_cycle) % 2: orth_set[j] = orth_set[j] ^ base return cycle_basis + +cpdef eccentricity_DHV(g, vertex_list=None, weight_function=None, check_weight=True): + r""" + Return the vector of eccentricities using the algorithm of [Dragan2018]_. + + The array returned is of length `n`, and by default its `i`-th component is + the eccentricity of the `i`-th vertex in ``g.vertices()``, + if ``vertex_list is None``, otherwise ``ecc[i]`` is the eccentricity of + vertex ``vertex_list[i]``. + + The algorithm proposed in [Dragan2018]_ is based on the observation that for + all nodes `v,w\in V`, we have `\max(ecc[v]-d(v,w), d(v,w))\leq ecc[w] \leq + ecc[v] + d(v,w)`. Also the algorithm iteratively improves upper and lower + bounds on the eccentricity of each vertex until no further improvements can + be done. + + INPUT: + + - ``g`` -- the input Sage graph. + + - ``vertex_list`` -- list (default: ``None``); a list of `n` vertices + specifying a mapping from `(0, \ldots, n-1)` to vertex labels in `g`. When + set, ``ecc[i]`` is the eccentricity of vertex ``vertex_list[i]``. When + ``vertex_list`` is ``None``, ``ecc[i]`` is the eccentricity of vertex + ``g.vertices()[i]``. + + - ``weight_function`` -- function (default: ``None``); a function that + associates a weight to each edge. If ``None`` (default), the weights of + ``g`` are used, if ``g.weighted()==True``, otherwise all edges have + weight 1. + + - ``check_weight`` -- boolean (default: ``True``); if ``True``, we check + that the ``weight_function`` outputs a number for each edge + + EXAMPLES:: + + sage: from sage.graphs.base.boost_graph import eccentricity_DHV + sage: G = graphs.BullGraph() + sage: eccentricity_DHV(G) + [2.0, 2.0, 2.0, 3.0, 3.0] + + TESTS: + + sage: G = Graph(2) + sage: eccentricity_DHV(G) + [+Infinity, +Infinity] + sage: G = graphs.RandomGNP(20,0.7) + sage: eccentricity_DHV(G) == G.eccentricity() + True + sage: G = Graph([(0,1,-1)], weighted=True) + sage: eccentricity_DHV(G) + Traceback (most recent call last): + ... + ValueError: graph contains negative edge weights, use Johnson_Boost instead + """ + if g.is_directed(): + raise TypeError("the 'DHV' algorithm only works on undirected graphs") + + cdef int n = g.order() + if not n: + return [] + + if weight_function and check_weight: + g._check_weight_function(weight_function) + + if weight_function is not None: + for e in g.edge_iterator(): + if float(weight_function(e)) < 0: + raise ValueError("graph contains negative edge weights, use Johnson_Boost instead") + elif g.weighted(): + for _,_,w in g.edge_iterator(): + if w and float(w) < 0: + raise ValueError("graph contains negative edge weights, use Johnson_Boost instead") + + if vertex_list is None: + vertex_list = g.vertices() + elif not len(vertex_list) == n or not set(vertex_list) == set(g): + raise ValueError("parameter vertex_list is incorrect for this graph") + + # These variables are automatically deleted when the function terminates. + cdef dict v_to_int = {vv: vi for vi, vv in enumerate(vertex_list)} + cdef BoostVecWeightedGraph g_boost + boost_weighted_graph_from_sage_graph(&g_boost, g, v_to_int, weight_function) + + import sys + cdef v_index u, antipode, v + cdef double ecc_u, ecc_antipode, tmp + cdef size_t i, idx + + cdef list active = list(range(n)) + cdef vector[double] ecc_lower_bound, ecc_upper_bound, distances + + for i in range(n): + ecc_lower_bound.push_back(0) + ecc_upper_bound.push_back(sys.float_info.max) + + # Algorithm + while active: + # Select vertex with minimum eccentricity in active and update + # eccentricity upper bounds. + # For this, we select u with minimum eccentricity lower bound in active + # if ecc_u == ecc_lb[u], we are done. Otherwise, we update eccentricity + # lower bounds and repeat + + tmp = sys.float_info.max + for i, v in enumerate(active): + if ecc_lower_bound[v] < tmp: + tmp = ecc_lower_bound[v] + idx = i + active[idx], active[-1] = active[-1], active[idx] + u = active.pop() + + # compute distances from u + sig_on() + distances = g_boost.dijkstra_shortest_paths(u).distances + sig_off() + + # Compute eccentricity of u + ecc_u = 0 + for v in range(n): + if ecc_u < distances[v]: + ecc_u = distances[v] + antipode = v + ecc_upper_bound[u] = ecc_u + + if ecc_u == sys.float_info.max: # Disconnected graph + break + + if ecc_u == ecc_lower_bound[u]: + # We found the good vertex. + # Update eccentricity upper bounds of the remaining vertices + for v in active: + ecc_upper_bound[v] = min(ecc_upper_bound[v], distances[v] + ecc_u) + + else: + # u was not a good choice. + # We use its antipode to update eccentricity lower bounds. + # Observe that this antipode might have already been seen. + for i, v in enumerate(active): + if v == antipode: + active[i] = active[-1] + active.pop() + break + + # Compute distances from antipode + sig_on() + distances = g_boost.dijkstra_shortest_paths(antipode).distances + sig_off() + + # Compute eccentricity of antipode + ecc_antipode = 0 + for v in range(n): + ecc_antipode = max(ecc_antipode, distances[v]) + ecc_upper_bound[antipode] = ecc_antipode + + # Update eccentricity lower bounds. + for v in active: + ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v]) + + # We remove those vertices from active for which gap is closed + i = 0 + while i < len(active): + v = active[i] + if ecc_upper_bound[v] == ecc_lower_bound[v]: + active[i] = active[-1] + active.pop() + else: + i += 1 + + from sage.rings.infinity import Infinity + cdef list eccentricity = [] + for i in range(n): + if ecc_upper_bound[i] != sys.float_info.max: + eccentricity.append(ecc_upper_bound[i]) + else: + eccentricity.append(+Infinity) + + return eccentricity \ No newline at end of file diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 4775cd59e56..a6a189c6b01 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -813,7 +813,7 @@ cdef uint32_t * c_eccentricity_DHV(G, vertex_list=None): ``ecc[i]`` is the eccentricity of vertex ``vertex_list[i]``. The algorithm proposed in [Dragan2018]_ is an improvement of the algorithm - proposed in [TK2013]_. It is also is based on the observation that for all + proposed in [TK2013]_. It is also based on the observation that for all nodes `v,w\in V`, we have `\max(ecc[v]-d(v,w), d(v,w))\leq ecc[w] \leq ecc[v] + d(v,w)`. Also the algorithms iteratively improves upper and lower bounds on the eccentricity of each vertex until no further improvements can From a4c6ff89dc8cc984cc0eead75616424f51117f59 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 13 Jun 2020 11:45:51 +0200 Subject: [PATCH 044/204] trac #27934: minor improvements --- src/sage/graphs/base/boost_graph.pyx | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 0b60699d223..29c3a8087ea 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -1649,7 +1649,7 @@ cpdef eccentricity_DHV(g, vertex_list=None, weight_function=None, check_weight=T sage: G = Graph(2) sage: eccentricity_DHV(G) [+Infinity, +Infinity] - sage: G = graphs.RandomGNP(20,0.7) + sage: G = graphs.RandomGNP(20, 0.7) sage: eccentricity_DHV(G) == G.eccentricity() True sage: G = Graph([(0,1,-1)], weighted=True) @@ -1664,6 +1664,8 @@ cpdef eccentricity_DHV(g, vertex_list=None, weight_function=None, check_weight=T cdef int n = g.order() if not n: return [] + if n == 1: + return [0] if weight_function and check_weight: g._check_weight_function(weight_function) @@ -1693,11 +1695,12 @@ cpdef eccentricity_DHV(g, vertex_list=None, weight_function=None, check_weight=T cdef size_t i, idx cdef list active = list(range(n)) - cdef vector[double] ecc_lower_bound, ecc_upper_bound, distances + cdef vector[double] ecc_lower_bound + cdef vector[double] ecc_upper_bound + cdef vector[double] distances - for i in range(n): - ecc_lower_bound.push_back(0) - ecc_upper_bound.push_back(sys.float_info.max) + ecc_lower_bound.assign(n, 0) + ecc_upper_bound.assign(n, sys.float_info.max) # Algorithm while active: @@ -1758,14 +1761,12 @@ cpdef eccentricity_DHV(g, vertex_list=None, weight_function=None, check_weight=T ecc_antipode = max(ecc_antipode, distances[v]) ecc_upper_bound[antipode] = ecc_antipode - # Update eccentricity lower bounds. - for v in active: - ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v]) - - # We remove those vertices from active for which gap is closed + # Update eccentricity lower bounds and remove from active those + # vertices for which the gap is closed i = 0 while i < len(active): v = active[i] + ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v]) if ecc_upper_bound[v] == ecc_lower_bound[v]: active[i] = active[-1] active.pop() @@ -1780,4 +1781,4 @@ cpdef eccentricity_DHV(g, vertex_list=None, weight_function=None, check_weight=T else: eccentricity.append(+Infinity) - return eccentricity \ No newline at end of file + return eccentricity From 7762f96bdd81462e66df7c933c99e123c977619c Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Sun, 14 Jun 2020 01:42:08 +0530 Subject: [PATCH 045/204] Tried something --- src/sage/graphs/base/boost_graph.pyx | 12 +++++++-- src/sage/graphs/distances_all_pairs.pyx | 33 +++++++++++++++---------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 29c3a8087ea..b8d8eacd4aa 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -1736,9 +1736,17 @@ cpdef eccentricity_DHV(g, vertex_list=None, weight_function=None, check_weight=T if ecc_u == ecc_lower_bound[u]: # We found the good vertex. - # Update eccentricity upper bounds of the remaining vertices - for v in active: + # Update eccentricity upper bounds and remove from active those + # vertices for which gap is closed + i = 0 + while i < len(active): + v = active[i] ecc_upper_bound[v] = min(ecc_upper_bound[v], distances[v] + ecc_u) + if ecc_upper_bound[v] == ecc_lower_bound[v]: + active[i] = active[-1] + active.pop() + else: + i += 1 else: # u was not a good choice. diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index a6a189c6b01..fdfcf8ebaa2 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -890,9 +890,17 @@ cdef uint32_t * c_eccentricity_DHV(G, vertex_list=None): if ecc_u == ecc_lower_bound[u]: # We found the good vertex. - # Update eccentricity upper bounds of the remaining vertices - for v in active: + # Update eccentricity upper bounds and remove from active those + # vertices for which gap is closed + i = 0 + while i < len(active): + v = active[i] ecc_upper_bound[v] = min(ecc_upper_bound[v], distances[v] + ecc_u) + if ecc_upper_bound[v] == ecc_lower_bound[v]: + active[i] = active[-1] + active.pop() + else: + i += 1 else: # u was not a good choice. @@ -908,18 +916,17 @@ cdef uint32_t * c_eccentricity_DHV(G, vertex_list=None): ecc_antipode = simple_BFS(sd, antipode, distances, NULL, waiting_list, seen) ecc_upper_bound[antipode] = ecc_antipode - for v in active: + # Update eccentricity lower bounds and remove from active those + # vertices for which the gap is closed + i = 0 + while i < len(active): + v = active[i] ecc_lower_bound[v] = max(ecc_lower_bound[v], distances[v]) - - # We remove those vertices from active for which gap is closed - i = 0 - while i < len(active): - v = active[i] - if ecc_upper_bound[v] == ecc_lower_bound[v]: - active[i] = active[-1] - active.pop() - else: - i += 1 + if ecc_upper_bound[v] == ecc_lower_bound[v]: + active[i] = active[-1] + active.pop() + else: + i += 1 free_short_digraph(sd) bitset_free(seen) From 1e7becc551c3bad53ce67fa8457c5c3d977cad71 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 13 Jun 2020 18:17:29 -0700 Subject: [PATCH 046/204] tox.ini [debian-buster, -sid]: IGNORE_MISSING_SYSTEM_PACKAGES=yes because of libpython3.7-dev --- tox.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tox.ini b/tox.ini index b260e8193da..961be5dd067 100644 --- a/tox.ini +++ b/tox.ini @@ -152,8 +152,11 @@ setenv = debian-stretch: BASE_TAG=stretch debian-stretch: IGNORE_MISSING_SYSTEM_PACKAGES=yes debian-buster: BASE_TAG=buster + # debian-bullseye and -sid do not have libpython3.7-dev any more debian-bullseye: BASE_TAG=bullseye + debian-bullseye: IGNORE_MISSING_SYSTEM_PACKAGES=yes debian-sid: BASE_TAG=sid + debian-sid: IGNORE_MISSING_SYSTEM_PACKAGES=yes # # https://hub.docker.com/u/linuxmintd # From 041c9e808a4f28253c232762b3efa37ea306e3c4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 12 Jun 2020 22:34:52 -0700 Subject: [PATCH 047/204] sage_setup.command.sage_build_cython: Use SAGE_GMP_PREFIX, SAGE_NTL_PREFIX for header dependencies instead of SAGE_INC --- src/sage_setup/command/sage_build_cython.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/sage_setup/command/sage_build_cython.py b/src/sage_setup/command/sage_build_cython.py index b177f1a8aac..5ee0d3baef2 100644 --- a/src/sage_setup/command/sage_build_cython.py +++ b/src/sage_setup/command/sage_build_cython.py @@ -18,16 +18,22 @@ from sage_setup.find import find_extra_files from sage_setup.library_order import library_order -from sage.env import (SAGE_INC, cython_aliases, sage_include_directories) +from sage.env import (cython_aliases, sage_include_directories) # Do not put all, but only the most common libraries and their headers # (that are likely to change on an upgrade) here: # [At least at the moment. Make sure the headers aren't copied with "-p", # or explicitly touch them in the respective spkg's spkg-install.] -lib_headers = { "gmp": [ os.path.join(SAGE_INC, 'gmp.h') ], # cf. #8664, #9896 - "gmpxx": [ os.path.join(SAGE_INC, 'gmpxx.h') ], - "ntl": [ os.path.join(SAGE_INC, 'NTL', 'config.h') ] - } +lib_headers = dict() + +# Set by build/bin/sage-build-env-config. Empty if the system package is used. +gmp_prefix = os.environ.get("SAGE_GMP_PREFIX", "") +if gmp_prefix: + lib_headers["gmp"] = os.path.join(gmp_prefix, 'include', 'gmp.h') # cf. #8664, #9896 + lib_headers["gmpxx"] = os.path.join(gmp_prefix, 'include', 'gmpxx.h') +ntl_prefix = os.environ.get("SAGE_NTL_PREFIX", "") +if ntl_prefix: + lib_headers["ntl"] = os.path.join(ntl_prefix, 'include', 'NTL', 'config.h') # Manually add -fno-strict-aliasing, which is needed to compile Cython # and disappears from the default flags if the user has set CFLAGS. From 3909892cbe59e106637ab1ce75c07d37120974ae Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Sun, 14 Jun 2020 17:16:31 -0700 Subject: [PATCH 048/204] trac 29862: change Python 2.7 -> 3.7 in installation instructios for conda --- src/doc/en/installation/conda.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/installation/conda.rst b/src/doc/en/installation/conda.rst index f7ce7cf0a91..0b090cb2089 100644 --- a/src/doc/en/installation/conda.rst +++ b/src/doc/en/installation/conda.rst @@ -11,7 +11,7 @@ then type in the following commands in a terminal: * Add the conda-forge channel: ``conda config --add channels conda-forge`` * Create a new environment containing SageMath: ``conda create -n sage sage python=X``, where - ``X`` is version of Python, e.g. ``2.7`` + ``X`` is version of Python, e.g. ``3.7`` * Enter the new environment: ``conda activate sage`` * Start SageMath: ``sage`` From 76d3388f9cb48e34516147ebf0a40bcb7c6587e0 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 15 Jun 2020 09:51:06 +0200 Subject: [PATCH 049/204] show the correct result for the known bug --- src/sage/rings/polynomial/multi_polynomial_ideal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 31ea509dbba..49db6fe4b49 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -1232,7 +1232,7 @@ def vector_space_dimension(self): sage: P. = PolynomialRing(GF(32003),3) sage: sage.rings.ideal.FieldIdeal(P).vector_space_dimension() # known bug - -1973539045 + 32777216864027 TESTS: From b721abd01ad2cc80f5f4bb2b5d473e0ae8cb85e6 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Mon, 15 Jun 2020 14:43:13 +0200 Subject: [PATCH 050/204] Remove __div__ methods from cython modules --- .../lie_algebras/lie_algebra_element.pyx | 2 +- src/sage/ext/fast_callable.pyx | 22 --- src/sage/ext/fast_eval.pyx | 11 -- src/sage/libs/mpmath/ext_main.pyx | 13 -- src/sage/libs/ntl/ntl_GF2.pyx | 3 - src/sage/libs/ntl/ntl_GF2E.pyx | 3 - src/sage/libs/ntl/ntl_GF2X.pyx | 3 - src/sage/libs/ntl/ntl_ZZX.pyx | 3 - src/sage/libs/ntl/ntl_ZZ_pEX.pyx | 3 - src/sage/libs/ntl/ntl_ZZ_pX.pyx | 3 - src/sage/libs/ntl/ntl_lzz_p.pyx | 3 - src/sage/libs/ntl/ntl_lzz_pX.pyx | 3 - src/sage/matrix/matrix_gfpn_dense.pyx | 2 +- src/sage/matroids/matroid.pyx | 12 -- src/sage/misc/lazy_import.pyx | 13 -- .../modules/with_basis/indexed_element.pyx | 14 -- src/sage/quivers/algebra_elements.pyx | 3 - src/sage/rings/asymptotic/growth_group.py | 4 +- src/sage/rings/complex_mpc.pyx | 4 +- src/sage/rings/complex_number.pyx | 4 +- src/sage/rings/integer.pyx | 2 +- .../rings/polynomial/polynomial_element.pyx | 3 - src/sage/rings/rational.pyx | 2 +- .../elliptic_curves/period_lattice_region.pyx | 3 - src/sage/structure/category_object.pyx | 26 ---- src/sage/structure/element.pyx | 127 ------------------ 26 files changed, 10 insertions(+), 281 deletions(-) diff --git a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx index d519d37b618..6a477f755ce 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx +++ b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx @@ -403,7 +403,7 @@ cdef class LieAlgebraElementWrapper(ElementWrapper): right = ( right).lift() return left * right - def __div__(self, x): + def __truediv__(self, x): """ Division by coefficients. diff --git a/src/sage/ext/fast_callable.pyx b/src/sage/ext/fast_callable.pyx index 86033d983be..43dafee7421 100644 --- a/src/sage/ext/fast_callable.pyx +++ b/src/sage/ext/fast_callable.pyx @@ -960,28 +960,6 @@ cdef class Expression: """ return _expression_binop_helper(s, o, op_truediv) - def __div__(s, o): - r""" - Compute a quotient of two Expressions. - - EXAMPLES:: - - sage: from sage.ext.fast_callable import ExpressionTreeBuilder - sage: etb = ExpressionTreeBuilder(vars=(x,)) - sage: x = etb(x) - sage: x/x - div(v_0, v_0) - sage: x/1 - div(v_0, 1) - sage: 1/x - div(1, v_0) - sage: x.__div__(1) # py2 - div(v_0, 1) - sage: x.__rdiv__(1) # py2 - div(1, v_0) - """ - return _expression_binop_helper(s, o, op_div) - def __floordiv__(s, o): r""" Compute the floordiv (the floor of the quotient) of two Expressions. diff --git a/src/sage/ext/fast_eval.pyx b/src/sage/ext/fast_eval.pyx index 1d2c1bf4470..d4e736b83ec 100644 --- a/src/sage/ext/fast_eval.pyx +++ b/src/sage/ext/fast_eval.pyx @@ -797,17 +797,6 @@ cdef class FastDoubleFunc: """ return binop(left, right, DIV) - def __div__(left, right): - """ - EXAMPLES:: - - sage: from sage.ext.fast_eval import fast_float_arg - sage: f = fast_float_arg(0) / 7 - sage: f(14) - 2.0 - """ - return binop(left, right, DIV) - def __pow__(FastDoubleFunc left, right, dummy): """ EXAMPLES:: diff --git a/src/sage/libs/mpmath/ext_main.pyx b/src/sage/libs/mpmath/ext_main.pyx index 298d289feea..29704c09049 100644 --- a/src/sage/libs/mpmath/ext_main.pyx +++ b/src/sage/libs/mpmath/ext_main.pyx @@ -1590,19 +1590,6 @@ cdef class mpnumber: """ return binop(OP_MUL, self, other, global_opts) - def __div__(self, other): - """ - Division of mpmath numbers. Compatible numerical types - are automatically converted to mpmath numbers :: - - sage: from mpmath import mpf, mpc - sage: mpf(10) / mpc(5) - mpc(real='2.0', imag='0.0') - sage: float(9) / mpf(3) - mpf('3.0') - """ - return binop(OP_DIV, self, other, global_opts) - def __truediv__(self, other): """ Division of mpmath numbers. Compatible numerical types diff --git a/src/sage/libs/ntl/ntl_GF2.pyx b/src/sage/libs/ntl/ntl_GF2.pyx index 5aa701f451f..92742493ea4 100644 --- a/src/sage/libs/ntl/ntl_GF2.pyx +++ b/src/sage/libs/ntl/ntl_GF2.pyx @@ -150,9 +150,6 @@ cdef class ntl_GF2(object): GF2_div(r.x, (self).x, (other).x) return r - def __div__(self, other): - return self / other - def __sub__(self, other): """ sage: o = ntl.GF2(1) diff --git a/src/sage/libs/ntl/ntl_GF2E.pyx b/src/sage/libs/ntl/ntl_GF2E.pyx index ee6ab1c6adc..97005292e9e 100644 --- a/src/sage/libs/ntl/ntl_GF2E.pyx +++ b/src/sage/libs/ntl/ntl_GF2E.pyx @@ -281,9 +281,6 @@ cdef class ntl_GF2E(object): GF2E_div(r.x, self.x, (other).x) return r - def __div__(self, other): - return self / other - def __neg__(ntl_GF2E self): """ EXAMPLES:: diff --git a/src/sage/libs/ntl/ntl_GF2X.pyx b/src/sage/libs/ntl/ntl_GF2X.pyx index 19dee747696..255ba604669 100644 --- a/src/sage/libs/ntl/ntl_GF2X.pyx +++ b/src/sage/libs/ntl/ntl_GF2X.pyx @@ -221,9 +221,6 @@ cdef class ntl_GF2X(object): raise ArithmeticError("self (=%s) is not divisible by b (=%s)" % (self, b)) return q - def __div__(self, other): - return self / other - def DivRem(ntl_GF2X self, b): """ EXAMPLES:: diff --git a/src/sage/libs/ntl/ntl_ZZX.pyx b/src/sage/libs/ntl/ntl_ZZX.pyx index 228da080442..ef8ce2ba2c3 100644 --- a/src/sage/libs/ntl/ntl_ZZX.pyx +++ b/src/sage/libs/ntl/ntl_ZZX.pyx @@ -357,9 +357,6 @@ cdef class ntl_ZZX(object): result = make_ZZX_sig_off(q) return result - def __div__(self, other): - return self / other - def __mod__(ntl_ZZX self, ntl_ZZX other): """ Given polynomials a, b in ZZ[X], there exist polynomials q, r diff --git a/src/sage/libs/ntl/ntl_ZZ_pEX.pyx b/src/sage/libs/ntl/ntl_ZZ_pEX.pyx index 07efc9ea163..e6f8b32b866 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pEX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pEX.pyx @@ -376,9 +376,6 @@ cdef class ntl_ZZ_pEX(object): raise ArithmeticError("self (=%s) is not divisible by other (=%s)" % (self, other)) return r - def __div__(self, other): - return self / other - def __mod__(ntl_ZZ_pEX self, ntl_ZZ_pEX other): """ Given polynomials a, b in ZZ_pE[X], if p is prime and the defining modulus irreducible, diff --git a/src/sage/libs/ntl/ntl_ZZ_pX.pyx b/src/sage/libs/ntl/ntl_ZZ_pX.pyx index b2336c1c9a1..a3ac93fa81d 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pX.pyx @@ -404,9 +404,6 @@ cdef class ntl_ZZ_pX(object): raise ArithmeticError("self (=%s) is not divisible by other (=%s)" % (self, other)) return r - def __div__(self, other): - return self / other - def __mod__(ntl_ZZ_pX self, ntl_ZZ_pX other): """ Given polynomials a, b in ZZ_p[X], if p is prime, then there exist polynomials q, r diff --git a/src/sage/libs/ntl/ntl_lzz_p.pyx b/src/sage/libs/ntl/ntl_lzz_p.pyx index 7809288152f..e6c2b5a3055 100644 --- a/src/sage/libs/ntl/ntl_lzz_p.pyx +++ b/src/sage/libs/ntl/ntl_lzz_p.pyx @@ -250,9 +250,6 @@ cdef class ntl_zz_p(object): sig_off() return q - def __div__(self, other): - return self / other - def __pow__(ntl_zz_p self, long n, ignored): """ Return the n-th nonnegative power of self. diff --git a/src/sage/libs/ntl/ntl_lzz_pX.pyx b/src/sage/libs/ntl/ntl_lzz_pX.pyx index c063e30548b..72fa99ae7a6 100644 --- a/src/sage/libs/ntl/ntl_lzz_pX.pyx +++ b/src/sage/libs/ntl/ntl_lzz_pX.pyx @@ -349,9 +349,6 @@ cdef class ntl_zz_pX(object): raise ArithmeticError("self (=%s) is not divisible by other (=%s)" % (self, other)) return q - def __div__(self, other): - return self / other - def __mod__(ntl_zz_pX self, other): """ Given polynomials a, b in ZZ[X], there exist polynomials q, r diff --git a/src/sage/matrix/matrix_gfpn_dense.pyx b/src/sage/matrix/matrix_gfpn_dense.pyx index ed9c0aaf09f..97a9db20ff5 100644 --- a/src/sage/matrix/matrix_gfpn_dense.pyx +++ b/src/sage/matrix/matrix_gfpn_dense.pyx @@ -1390,7 +1390,7 @@ cdef class Matrix_gfpn_dense(Matrix_dense): sig_off() return new_mtx(mat, self) - def __div__(Matrix_gfpn_dense self, p): + def __truediv__(Matrix_gfpn_dense self, p): """ Divide a matrix by a scalar. diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index d274d9eec53..6c8e736e80b 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -3784,18 +3784,6 @@ cdef class Matroid(SageObject): """ return self.minor(contractions=X) - def __div__(self, X): - r""" - Shorthand for ``self.contract(X)``. - - EXAMPLES:: - - sage: M = matroids.CompleteGraphic(4) - sage: M.contract(1) == M / 1 # indirect doctest - True - """ - return self.contract(X) - def __truediv__(self, X): r""" Shorthand for ``self.contract(X)``. diff --git a/src/sage/misc/lazy_import.pyx b/src/sage/misc/lazy_import.pyx index 16f04bfe8d1..13453d79f1b 100644 --- a/src/sage/misc/lazy_import.pyx +++ b/src/sage/misc/lazy_import.pyx @@ -640,19 +640,6 @@ cdef class LazyImport(object): """ return obj(left) @ obj(right) - def __div__(left, right): - """ - TESTS:: - - sage: sage.all.foo = 10 - sage: lazy_import('sage.all', 'foo') - sage: type(foo) - - sage: foo / 2 - 5 - """ - return obj(left) / obj(right) - def __floordiv__(left, right): """ TESTS:: diff --git a/src/sage/modules/with_basis/indexed_element.pyx b/src/sage/modules/with_basis/indexed_element.pyx index 94d57d67e2c..ebbcf3e1d98 100644 --- a/src/sage/modules/with_basis/indexed_element.pyx +++ b/src/sage/modules/with_basis/indexed_element.pyx @@ -883,20 +883,6 @@ cdef class IndexedFreeModuleElement(ModuleElement): x_inv = B(x) ** -1 return type(self)(F, scal(x_inv, D)) - def __div__(left, right): - """ - Forward old-style division to true division. - - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, [1,2,3]) - sage: x = F._from_dict({1:2, 2:3}) - sage: x/2 - B[1] + 3/2*B[2] - """ - return left / right - - def _unpickle_element(C, d): """ Unpickle an element in ``C`` given by ``d``. diff --git a/src/sage/quivers/algebra_elements.pyx b/src/sage/quivers/algebra_elements.pyx index 25968539fcf..33f73a78208 100644 --- a/src/sage/quivers/algebra_elements.pyx +++ b/src/sage/quivers/algebra_elements.pyx @@ -1301,9 +1301,6 @@ cdef class PathAlgebraElement(RingElement): return sample._new_(homog_poly_scale((self).data, x)) raise TypeError("Don't know how to divide {} by {}".format(x, self)) - def __div__(self, x): - return self / x - ## Multiplication in the algebra cpdef _mul_(self, other): diff --git a/src/sage/rings/asymptotic/growth_group.py b/src/sage/rings/asymptotic/growth_group.py index 9514b16c4b3..ec301dd731a 100644 --- a/src/sage/rings/asymptotic/growth_group.py +++ b/src/sage/rings/asymptotic/growth_group.py @@ -623,8 +623,8 @@ def _substitute_(self, rules): ... TypeError: Cannot substitute in 1/x in . - > *previous* TypeError: unsupported operand type(s) for /: - 'sage.rings.integer.Integer' and 'str' + > *previous* TypeError: unsupported operand parent(s) for /: + 'Integer Ring' and '' sage: Variable('1/x')._substitute_({'x': 0}) Traceback (most recent call last): ... diff --git a/src/sage/rings/complex_mpc.pyx b/src/sage/rings/complex_mpc.pyx index 84546c22398..ceb052131f0 100644 --- a/src/sage/rings/complex_mpc.pyx +++ b/src/sage/rings/complex_mpc.pyx @@ -1596,7 +1596,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): mpc_norm(x.value, self.value, (x._parent).rnd) return x - def __rdiv__(self, left): + def __rtruediv__(self, left): r""" Returns the quotient of ``left`` with ``self``, that is: ``left/self`` as a complex number. @@ -1609,7 +1609,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): sage: MPC = MPComplexField() sage: a = MPC(2, 2) - sage: a.__rdiv__(MPC(1)) + sage: a.__rtruediv__(MPC(1)) 0.250000000000000 - 0.250000000000000*I sage: MPC(1)/a 0.250000000000000 - 0.250000000000000*I diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index bc87388c1c6..2b00351ffca 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -836,7 +836,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): mpfr_clear(right_nm) return x - def __rdiv__(self, left): + def __rtruediv__(self, left): r""" Returns the quotient of left with ``self``, that is: @@ -851,7 +851,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): EXAMPLES:: sage: a = ComplexNumber(2,0) - sage: a.__rdiv__(CC(1)) + sage: a.__rtruediv__(CC(1)) 0.500000000000000 sage: CC(1)/a 0.500000000000000 diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 44936e6a6a4..0cdea51d477 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -2024,7 +2024,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): mpz_mul(x.value, self.value, (right).value) return x - def __div__(left, right): + def __truediv__(left, right): r""" TESTS:: diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 3d657d1f94e..9f31b91f05b 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -2247,9 +2247,6 @@ cdef class Polynomial(CommutativeAlgebraElement): return wrapperdescr_fastcall(RingElement.__truediv__, left, (right,), NULL) - def __div__(left, right): - return PyNumber_TrueDivide(left, right) - def __pow__(left, right, modulus): """ EXAMPLES:: diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index 30b50ea4978..a4f04d6edf9 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -2435,7 +2435,7 @@ cdef class Rational(sage.structure.element.FieldElement): mpq_mul(x.value, self.value, (right).value) return x - def __div__(left, right): + def __truediv__(left, right): """ Return ``left`` divided by ``right`` diff --git a/src/sage/schemes/elliptic_curves/period_lattice_region.pyx b/src/sage/schemes/elliptic_curves/period_lattice_region.pyx index 8399d8253ba..1f8a9ce8c9e 100644 --- a/src/sage/schemes/elliptic_curves/period_lattice_region.pyx +++ b/src/sage/schemes/elliptic_curves/period_lattice_region.pyx @@ -434,9 +434,6 @@ cdef class PeriodicRegion: new_data[(a*rows+i)//n, (b*cols+j)//n] = data[i,j] return PeriodicRegion(self.w1, self.w2, new_data) - def __div__(self, other): - return self / other - def __invert__(self): """ Returns the complement of this region. diff --git a/src/sage/structure/category_object.pyx b/src/sage/structure/category_object.pyx index fdca7ec5914..ec16091b334 100644 --- a/src/sage/structure/category_object.pyx +++ b/src/sage/structure/category_object.pyx @@ -895,32 +895,6 @@ cdef class CategoryObject(SageObject): """ return dir_with_other_class(self, self.category().parent_class) - ############################################################################## - # For compatibility with Python 2 - ############################################################################## - def __div__(self, other): - """ - Implement Python 2 division as true division. - - EXAMPLES:: - - sage: V = QQ^2 - sage: V.__div__(V.span([(1,3)])) # py2 - Vector space quotient V/W of dimension 1 over Rational Field where - V: Vector space of dimension 2 over Rational Field - W: Vector space of degree 2 and dimension 1 over Rational Field - Basis matrix: - [1 3] - sage: V.__truediv__(V.span([(1,3)])) - Vector space quotient V/W of dimension 1 over Rational Field where - V: Vector space of dimension 2 over Rational Field - W: Vector space of degree 2 and dimension 1 over Rational Field - Basis matrix: - [1 3] - """ - return self / other - - cpdef normalize_names(Py_ssize_t ngens, names): r""" Return a tuple of strings of variable names of length ngens given diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index 0791c72a70c..8105a3a8abe 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -1600,77 +1600,6 @@ cdef class Element(SageObject): """ return coercion_model.bin_op(self, n, mul) - def __div__(left, right): - """ - Top-level division operator for :class:`Element` invoking - the coercion model. This is always true division. - - See :ref:`element_arithmetic`. - - EXAMPLES:: - - sage: 2 / 3 - 2/3 - sage: pi / 3 - 1/3*pi - sage: K. = NumberField(x^2+1) - sage: 2 / K.ideal(i+1) - Fractional ideal (-i + 1) - - :: - - sage: from sage.structure.element import Element - sage: class MyElement(Element): - ....: def _div_(self, other): - ....: return 42 - sage: e = MyElement(Parent()) - sage: e / e - 42 - - TESTS:: - - sage: e = Element(Parent()) - sage: e / e - Traceback (most recent call last): - ... - TypeError: unsupported operand parent(s) for /: '' and '' - sage: 1 / e - Traceback (most recent call last): - ... - TypeError: unsupported operand parent(s) for /: 'Integer Ring' and '' - sage: e / 1 - Traceback (most recent call last): - ... - TypeError: unsupported operand parent(s) for /: '' and 'Integer Ring' - sage: int(1) / e - Traceback (most recent call last): - ... - TypeError: unsupported operand type(s) for /: 'int' and 'sage.structure.element.Element' - sage: e / int(1) - Traceback (most recent call last): - ... - TypeError: unsupported operand type(s) for /: 'sage.structure.element.Element' and 'int' - sage: None / e - Traceback (most recent call last): - ... - TypeError: unsupported operand type(s) for /: 'NoneType' and 'sage.structure.element.Element' - sage: e / None - Traceback (most recent call last): - ... - TypeError: unsupported operand type(s) for /: 'sage.structure.element.Element' and 'NoneType' - """ - # See __add__ for comments - cdef int cl = classify_elements(left, right) - if HAVE_SAME_PARENT(cl): - return (left)._div_(right) - if BOTH_ARE_ELEMENT(cl): - return coercion_model.bin_op(left, right, truediv) - - try: - return coercion_model.bin_op(left, right, truediv) - except TypeError: - return NotImplemented - def __truediv__(left, right): """ Top-level true division operator for :class:`Element` invoking @@ -3419,9 +3348,6 @@ cdef class Vector(ModuleElement): cpdef _pairwise_product_(Vector left, Vector right): raise TypeError("unsupported operation for '%s' and '%s'"%(parent(left), parent(right))) - def __div__(self, other): - return self / other - def __truediv__(self, right): right = py_scalar_to_element(right) if isinstance(right, RingElement): @@ -3777,59 +3703,6 @@ cdef class Matrix(ModuleElement): return left * ~right return coercion_model.bin_op(left, right, truediv) - def __div__(left, right): - """ - Division of the matrix ``left`` by the matrix or scalar ``right``. - - EXAMPLES:: - - sage: a = matrix(ZZ, 2, range(4)) - sage: a / 5 - [ 0 1/5] - [2/5 3/5] - sage: a = matrix(ZZ, 2, range(4)) - sage: b = matrix(ZZ, 2, [1,1,0,5]) - sage: a / b - [ 0 1/5] - [ 2 1/5] - sage: c = matrix(QQ, 2, [3,2,5,7]) - sage: c / a - [-5/2 3/2] - [-1/2 5/2] - sage: a / c - [-5/11 3/11] - [-1/11 5/11] - sage: a / 7 - [ 0 1/7] - [2/7 3/7] - - Other rings work just as well:: - - sage: a = matrix(GF(3),2,2,[0,1,2,0]) - sage: b = matrix(ZZ,2,2,[4,6,1,2]) - sage: a / b - [1 2] - [2 0] - sage: c = matrix(GF(3),2,2,[1,2,1,1]) - sage: a / c - [1 2] - [1 1] - sage: a = matrix(RDF,2,2,[.1,-.4,1.2,-.6]) - sage: b = matrix(RDF,2,2,[.3,.1,-.5,1.3]) - sage: a / b # rel tol 1e-10 - [-0.15909090909090906 -0.29545454545454547] - [ 2.863636363636364 -0.6818181818181817] - sage: R. = ZZ['t'] - sage: a = matrix(R,2,2,[t^2,t+1,-t,t+2]) - sage: b = matrix(R,2,2,[t^3-1,t,-t+3,t^2]) - sage: a / b - [ (t^4 + t^2 - 2*t - 3)/(t^5 - 3*t) (t^4 - t - 1)/(t^5 - 3*t)] - [ (-t^3 + t^2 - t - 6)/(t^5 - 3*t) (t^4 + 2*t^3 + t^2 - t - 2)/(t^5 - 3*t)] - """ - if have_same_parent(left, right): - return left * ~right - return coercion_model.bin_op(left, right, truediv) - cdef _vector_times_matrix_(matrix_right, Vector vector_left): raise TypeError From aabbb247e360d3329dc71163c74d38aaf348d880 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Tue, 16 Jun 2020 08:22:26 -0700 Subject: [PATCH 051/204] use get_order instead of ordered_basis --- src/sage/combinat/root_system/weyl_characters.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index ee40a4bdc06..fce5b1617e1 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -2395,7 +2395,8 @@ def twists_matrix(self): [ 0 0 0 0 1/2 0] [ 0 0 0 0 0 3/2] """ - return diagonal_matrix(obj.twist() for obj in self.ordered_basis()) + return diagonal_matrix(self.basis()[x].twist() for x in self.get_order()) + # return diagonal_matrix(obj.twist() for obj in self.ordered_basis()) def q_dims(self): r""" @@ -2410,7 +2411,9 @@ def q_dims(self): sage: B22.q_dims() [1, 1, 2, 2, -2*zeta40^12 + 2*zeta40^8 + 1, -2*zeta40^12 + 2*zeta40^8 + 1] """ - return [x.q_dimension() for x in self.ordered_basis()] + b = self.basis() + return [b[x].q_dimension() for x in self.get_order()] + # return [x.q_dimension() for x in self.ordered_basis()] def ordered_basis(self): """ @@ -2498,7 +2501,12 @@ def s_ij(self, elt_i, elt_j): ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) + def s_matrix(self): + b = self.basis() + return matrix([[self.s_ij(b[x],b[y]) for x in self.get_order()] for y in self.get_order()]) + + def s_matrix_old(self): r""" Return the S-matrix of this FusionRing. @@ -2519,7 +2527,6 @@ def s_matrix(self): [ 1 -1 -1 1] """ ord_basis = self.ordered_basis() - fusion_mats = [x.fusion_matrix() for x in ord_basis] rng = range(len(ord_basis)) S = matrix(self.q_field(), len(ord_basis)) for i in rng: From 5bf9893ab8bde83d4a46dc3bdcdca98e2b2b6459 Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Mon, 1 Jun 2020 12:56:51 +0200 Subject: [PATCH 052/204] 29723: ring homomorphism: inverse, is_invertible, is_surjective --- src/sage/categories/rings.py | 7 +- .../rings/finite_rings/hom_finite_field.pyx | 13 + src/sage/rings/morphism.pyx | 365 ++++++++++++++++++ src/sage/rings/polynomial/flatten.py | 16 + 4 files changed, 399 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index efb906f3098..afa64f8bbbc 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -178,8 +178,11 @@ def is_injective(self): if K is self.codomain(): return True - if self.domain().cardinality() > self.codomain().cardinality(): - return False + try: + if self.domain().cardinality() > self.codomain().cardinality(): + return False + except AttributeError: + pass raise NotImplementedError diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index e0368c1fe27..a469bb5e5f9 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -684,6 +684,19 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): """ return self.__class__(self.domain(), self.power()*n) + @cached_method + def inverse(self): + """ + Return the inverse of this Frobenius endomorphism. + + EXAMPLES:: + + sage: k. = GF(7^11) + sage: f = k.frobenius_endomorphism(5) + sage: (f.inverse() * f).is_identity() + True + """ + return self.__class__(self.domain(), -self.power()) def _composition(self, right): """ diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index a8156a6590f..f8af92362d7 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -960,6 +960,8 @@ cdef class RingHomomorphism(RingMap): NotImplementedError: inverse image not implemented... sage: f.inverse_image(K.ideal(0)).is_zero() True + sage: f.inverse()(I) + Fractional ideal (-a + 1) ALGORITHM: @@ -1204,6 +1206,8 @@ cdef class RingHomomorphism(RingMap): 1.414213562373095?*x + I*y - z, 1.414213562373095?*x + (-I)*y - w) of Multivariate Polynomial Ring in x, y, z, w over Algebraic Field + sage: f.inverse()(f(z)), f.inverse()(f(w)) + (z, w) Non-trivial base maps are not supported:: @@ -1249,6 +1253,319 @@ cdef class RingHomomorphism(RingMap): raise NotImplementedError('"reduce" not implemented for %s' % Q) return graph, B_to_Q, Q_to_A + @cached_method + def inverse(self): + """ + Return the inverse of this ring homomorphism if it exists. + + Raises a ``ZeroDivisionError`` if the inverse does not exist. + + ALGORITHM: + + By default, this computes a Gröbner basis of the ideal corresponding to + the graph of the ring homomorphism. + + EXAMPLES:: + + sage: R. = QQ[] + sage: f = R.hom([2*t - 1], R) + sage: f.inverse() + Ring endomorphism of Univariate Polynomial Ring in t over Rational Field + Defn: t |--> 1/2*t + 1/2 + + The following non-linear homomorphism is not invertible, but it induces + an isomorphism on a quotient ring:: + + sage: R. = QQ[] + sage: f = R.hom([y*z, x*z, x*y], R) + sage: f.inverse() + Traceback (most recent call last): + ... + ZeroDivisionError: ring homomorphism not surjective + sage: f.is_injective() + True + sage: Q. = R.quotient(x*y*z - 1) + sage: g = Q.hom([y*z, x*z, x*y], Q) + sage: g.inverse() + Ring endomorphism of Quotient of Multivariate Polynomial Ring + in x, y, z over Rational Field by the ideal (x*y*z - 1) + Defn: x |--> y*z + y |--> x*z + z |--> x*y + + Homomorphisms over the integers are supported:: + + sage: S. = ZZ[] + sage: f = S.hom([x + 2*y, x + 3*y], S) + sage: f.inverse() + Ring endomorphism of Multivariate Polynomial Ring in x, y over Integer Ring + Defn: x |--> 3*x - 2*y + y |--> -x + y + sage: (f.inverse() * f).is_identity() + True + + The following homomorphism is invertible over the rationals, but not + over the integers:: + + sage: g = S.hom([x + y, x - y - 2], S) + sage: g.inverse() + Traceback (most recent call last): + ... + ZeroDivisionError: ring homomorphism not surjective + sage: R. = QQ[x,y] + sage: h = R.hom([x + y, x - y - 2], R) + sage: (h.inverse() * h).is_identity() + True + + This example by M. Nagata is a wild automorphism:: + + sage: R. = QQ[] + sage: sigma = R.hom([x - 2*y*(z*x+y^2) - z*(z*x+y^2)^2, + ....: y + z*(z*x+y^2), z], R) + sage: tau = sigma.inverse(); tau + Ring endomorphism of Multivariate Polynomial Ring in x, y, z over + Rational Field + Defn: x |--> -y^4*z - 2*x*y^2*z^2 - x^2*z^3 + 2*y^3 + 2*x*y*z + x + y |--> -y^2*z - x*z^2 + y + z |--> z + sage: (tau * sigma).is_identity() + True + + We compute the triangular automorphism that converts moments to + cumulants, as well as its inverse, using the moment generating + function. The choice of a term ordering can have a great impact on the + computation time of a Gröbner basis, so here we choose a weighted + ordering such that the images of the generators are homogeneous + polynomials. :: + + sage: d = 12 + sage: T = TermOrder('wdegrevlex', [1..d]) + sage: R = PolynomialRing(QQ, ['x%s' % j for j in (1..d)], order=T) + sage: S. = PowerSeriesRing(R) + sage: egf = S([0] + list(R.gens())).ogf_to_egf().exp(prec=d+1) + sage: phi = R.hom(egf.egf_to_ogf().list()[1:], R) + sage: phi.im_gens()[:5] + [x1, + x1^2 + x2, + x1^3 + 3*x1*x2 + x3, + x1^4 + 6*x1^2*x2 + 3*x2^2 + 4*x1*x3 + x4, + x1^5 + 10*x1^3*x2 + 15*x1*x2^2 + 10*x1^2*x3 + 10*x2*x3 + 5*x1*x4 + x5] + sage: all(p.is_homogeneous() for p in phi.im_gens()) + True + sage: phi.inverse().im_gens()[:5] + [x1, + -x1^2 + x2, + 2*x1^3 - 3*x1*x2 + x3, + -6*x1^4 + 12*x1^2*x2 - 3*x2^2 - 4*x1*x3 + x4, + 24*x1^5 - 60*x1^3*x2 + 30*x1*x2^2 + 20*x1^2*x3 - 10*x2*x3 - 5*x1*x4 + x5] + sage: (phi.inverse() * phi).is_identity() + True + + Automorphisms of number fields as well as Galois fields are supported:: + + sage: K. = CyclotomicField(7) + sage: c = K.hom([1/zeta7]) + sage: (c.inverse() * c).is_identity() + True + sage: F. = GF(7^3) + sage: f = F.hom(t^7, F) + sage: (f.inverse() * f).is_identity() + True + + An isomorphism between the algebraic torus and the circle over a number + field:: + + sage: K. = QuadraticField(-1) + sage: A. = K['z,w'].quotient('z*w - 1') + sage: B. = K['x,y'].quotient('x^2 + y^2 - 1') + sage: f = A.hom([x + i*y, x - i*y], B) + sage: g = f.inverse() + sage: g.morphism_from_cover().im_gens() + [1/2*z + 1/2*w, (-1/2*i)*z + (1/2*i)*w] + sage: all(g(f(z)) == z for z in A.gens()) + True + + TESTS: + + Morphisms involving quotient rings:: + + sage: R. = QQ[] + sage: S. = QQ['s,u,t'].quotient('u-t^2') + sage: f = R.hom([s, -t], S) + sage: (f.inverse() * f).is_identity() + True + sage: Q. = R.quotient(x-y^2) + sage: g = Q.hom([v, -w], Q) + sage: g.inverse()(g(v)) == v and g.inverse()(g(w)) == w + True + sage: S. = QQ[] + sage: h = Q.hom([z^2, -z], S) + sage: h.inverse()(h(v)) == v and h.inverse()(h(w)) == w + True + + Morphisms between number fields and quotient rings:: + + sage: K. = QuadraticField(2) + sage: f = K.hom([-sqrt2], K.polynomial_quotient_ring()) + sage: (f.inverse() * f).is_identity() + True + sage: g = K.polynomial_quotient_ring().hom([-sqrt2], K) + sage: (g.inverse() * g).is_identity() + True + + Morphisms involving Galois fields:: + + sage: A. = GF(7^3) + sage: R = A.polynomial_ring().quotient(A.polynomial()) + sage: g = A.hom(R.gens(), R) + sage: (g.inverse() * g).is_identity() + True + sage: B., f = A.extension(3, map=True) + sage: f.inverse() + Traceback (most recent call last): + ... + ZeroDivisionError: ring homomorphism not surjective + sage: B., f = A.extension(1, map=True) + sage: f.inverse() + Ring morphism: + From: Finite Field in T of size 7^3 + To: Finite Field in t of size 7^3 + Defn: T |--> t + + Non-injective homomorphisms:: + + sage: R. = QQ[] + sage: S. = QQ[] + sage: S.hom([x, y, 0], R).inverse() + Traceback (most recent call last): + ... + ZeroDivisionError: ring homomorphism not injective + sage: T. = QQ[] + sage: R.hom([2*z, 3*z], T).inverse() + Traceback (most recent call last): + ... + ZeroDivisionError: ring homomorphism not injective + sage: Q. = R.quotient([x^5, y^4]) + sage: R.hom([u, v], Q).inverse() + Traceback (most recent call last): + ... + ZeroDivisionError: ring homomorphism not injective + sage: Q.cover().inverse() + Traceback (most recent call last): + ... + ZeroDivisionError: ring homomorphism not injective + + Univariate quotient rings:: + + sage: R. = QQ['t'].quotient('t^5') + sage: f = R.hom([2*t], R) + sage: (f.inverse() * f).is_identity() + True + + A homomorphism over ``QQbar``:: + + sage: R. = QQbar[] + sage: f = R.hom([x + QQbar(I)*y^2, -y], R) + sage: (f.inverse() * f).is_identity() + True + + Check that results are cached:: + + sage: R. = GF(823)[] + sage: f = R.hom([x, y+x^2], R) + sage: f.inverse() is f.inverse() + True + + Some subclasses of ring homomorphisms are not supported:: + + sage: from sage.rings.morphism import FrobeniusEndomorphism_generic + sage: K. = PowerSeriesRing(GF(5)) + sage: FrobeniusEndomorphism_generic(K).inverse() + Traceback (most recent call last): + ... + NotImplementedError + + :: + + sage: R. = LaurentPolynomialRing(QQ) + sage: R.hom([y, x], R).inverse() + Traceback (most recent call last): + ... + NotImplementedError + + :: + + sage: K. = FunctionField(QQ) + sage: K.hom(1/x).inverse() + Traceback (most recent call last): + ... + NotImplementedError: inverse not implemented... + + The implementation performs several computations that require a Gröbner + basis of the graph ideal, so we check that the Gröbner basis is cached + after the first such computation:: + + sage: R. = QQ[] + sage: f = R.hom([x + 123*y^2, y], R) + sage: f._graph_ideal()[0].groebner_basis.is_in_cache() + False + sage: f.is_injective() + True + sage: f._graph_ideal()[0].groebner_basis.is_in_cache() + True + """ + if not self.is_injective(): + raise ZeroDivisionError("ring homomorphism not injective") + ys = self.codomain().gens() + try: + preimages = [self._inverse_image_element(y) for y in ys] + except ValueError: + raise ZeroDivisionError("ring homomorphism not surjective") + return self.parent().reversed()(preimages, check=False) + + def is_surjective(self): + """ + Return whether this ring homomorphism is surjective. + + EXAMPLES:: + + sage: R. = QQ[] + sage: R.hom([y*z, x*z, x*y], R).is_surjective() + False + sage: Q. = R.quotient(x*y*z - 1) + sage: R.hom([y*z, x*z, x*y], Q).is_surjective() + True + + ALGORITHM: + + By default, this requires the computation of a Gröbner basis. + """ + for y in self.codomain().gens(): + try: + self._inverse_image_element(y) + except ValueError: + return False + return True + + def is_invertible(self): + """ + Return whether this ring homomorphism is bijective. + + EXAMPLES:: + + sage: R. = QQ[] + sage: R.hom([y*z, x*z, x*y], R).is_invertible() + False + sage: Q. = R.quotient(x*y*z - 1) + sage: Q.hom([y*z, x*z, x*y], Q).is_invertible() + True + + ALGORITHM: + + By default, this requires the computation of a Gröbner basis. + """ + return self.is_injective() and self.is_surjective() + cdef class RingHomomorphism_coercion(RingHomomorphism): r""" @@ -1968,6 +2285,37 @@ cdef class RingHomomorphism_from_base(RingHomomorphism): except Exception: raise TypeError("invalid argument %s" % repr(x)) + @cached_method + def inverse(self): + """ + Return the inverse of this ring homomorphism if the underlying + homomorphism of the base ring is invertible. + + EXAMPLES:: + + sage: R. = QQ[] + sage: S. = QQ[] + sage: f = R.hom([a+b, a-b], S) + sage: PR. = R[] + sage: PS = S['t'] + sage: Pf = PR.hom(f, PS) + sage: Pf.inverse() + Ring morphism: + From: Univariate Polynomial Ring in t over Multivariate + Polynomial Ring in a, b over Rational Field + To: Univariate Polynomial Ring in t over Multivariate + Polynomial Ring in x, y over Rational Field + Defn: Induced from base ring by + Ring morphism: + From: Multivariate Polynomial Ring in a, b over Rational Field + To: Multivariate Polynomial Ring in x, y over Rational Field + Defn: a |--> 1/2*x + 1/2*y + b |--> 1/2*x - 1/2*y + sage: Pf.inverse()(Pf(x*t^2 + y*t)) + x*t^2 + y*t + """ + return self.parent().reversed()(self._underlying.inverse()) + cdef class RingHomomorphism_from_fraction_field(RingHomomorphism): r""" @@ -2066,6 +2414,23 @@ cdef class RingHomomorphism_from_fraction_field(RingHomomorphism): slots['_morphism'] = self._morphism return slots + @cached_method + def inverse(self): + """ + Return the inverse of this ring homomorphism if it exists. + + EXAMPLES:: + + sage: S. = QQ[] + sage: f = S.hom([2*x - 1]) + sage: g = f.extend_to_fraction_field() + sage: g.inverse() + Ring endomorphism of Fraction Field of Univariate Polynomial Ring + in x over Rational Field + Defn: x |--> 1/2*x + 1/2 + """ + return self.parent().reversed()(self._morphism.inverse()) + cdef class RingHomomorphism_cover(RingHomomorphism): r""" diff --git a/src/sage/rings/polynomial/flatten.py b/src/sage/rings/polynomial/flatten.py index f7168a487b6..e51eb65796f 100644 --- a/src/sage/rings/polynomial/flatten.py +++ b/src/sage/rings/polynomial/flatten.py @@ -257,6 +257,22 @@ def section(self): """ return UnflatteningMorphism(self.codomain(), self.domain()) + def inverse(self): + """ + Return the inverse of this flattening morphism. + + This is the same as calling :meth:`section`. + + EXAMPLES:: + + sage: f = QQ['x,y']['u,v'].flattening_morphism() + sage: f.inverse() + Unflattening morphism: + From: Multivariate Polynomial Ring in x, y, u, v over Rational Field + To: Multivariate Polynomial Ring in u, v over Multivariate Polynomial Ring in x, y over Rational Field + """ + return self.section() + class UnflatteningMorphism(Morphism): r""" From 85d94d28f8dd7f082cec9a749c47fc8d5a7151aa Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Tue, 16 Jun 2020 19:16:16 +0200 Subject: [PATCH 053/204] 29723: add __invert__ for ring homomorphisms --- src/sage/rings/morphism.pyx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index f8af92362d7..a73daa8b179 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -1523,6 +1523,21 @@ cdef class RingHomomorphism(RingMap): raise ZeroDivisionError("ring homomorphism not surjective") return self.parent().reversed()(preimages, check=False) + def __invert__(self): + """ + Return the inverse of this ring homomorphism if it exists. + + This simply calls :meth:`inverse`. + + EXAMPLES:: + + sage: R. = GF(17)[] + sage: f = R.hom([3*x, y + x^2 + x^3], R) + sage: (f * ~f).is_identity() + True + """ + return self.inverse() + def is_surjective(self): """ Return whether this ring homomorphism is surjective. From 72c2c44a23f4cbf317be408e7f996658c990fa58 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Tue, 16 Jun 2020 10:35:37 -0700 Subject: [PATCH 054/204] docstring edits --- .../combinat/root_system/weyl_characters.py | 120 ++---------------- 1 file changed, 12 insertions(+), 108 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index fce5b1617e1..ffb8cfb795a 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -2389,14 +2389,13 @@ def twists_matrix(self): sage: B22=FusionRing('B2',2) sage: B22.twists_matrix() [ 0 0 0 0 0 0] - [ 0 2 0 0 0 0] - [ 0 0 4/5 0 0 0] - [ 0 0 0 6/5 0 0] - [ 0 0 0 0 1/2 0] - [ 0 0 0 0 0 3/2] + [ 0 4/5 0 0 0 0] + [ 0 0 1/2 0 0 0] + [ 0 0 0 2 0 0] + [ 0 0 0 0 3/2 0] + [ 0 0 0 0 0 6/5] """ return diagonal_matrix(self.basis()[x].twist() for x in self.get_order()) - # return diagonal_matrix(obj.twist() for obj in self.ordered_basis()) def q_dims(self): r""" @@ -2409,78 +2408,10 @@ def q_dims(self): [1, -zeta80^24 + zeta80^16 + 1] sage: B22=FusionRing("B2",2) sage: B22.q_dims() - [1, 1, 2, 2, -2*zeta40^12 + 2*zeta40^8 + 1, -2*zeta40^12 + 2*zeta40^8 + 1] + [1, 2, -2*zeta40^12 + 2*zeta40^8 + 1, 1, -2*zeta40^12 + 2*zeta40^8 + 1, 2] """ b = self.basis() return [b[x].q_dimension() for x in self.get_order()] - # return [x.q_dimension() for x in self.ordered_basis()] - - def ordered_basis(self): - """ - Returns a basis of simple objects ordered according to - [NaiRow2011]_. For type A level 1, the basis is ordered by the - fundamental weights. The order is described in [Fuchs1994]_. - """ - ct = self._cartan_type[0] - k = self._k - r = self.rank() - wts = self.fundamental_weights() - ord_basis = list() - #Order bases for dimension 2 algebras - if self.dimension() == 2: - ord_basis.append(self(wts[1]*0)) - for wt in self.basis(): - wt = wt.highest_weight() - if wt.inner_product(wt) > 0: - ord_basis.append(self(wt)) - if ct == 'A' and k == 1: - ord_basis = [self(wts[1]*0)] - ord_basis += [self(wts[i+1]) for i in range(r)] - if ct == 'A' and k == 2: - #TODO: generalize to higher rank - if r == 1: - ord_basis = [self(wts[1]*0), self(wts[1]), self(wts[1]*2)] - if ct == 'B' and k == 2: - ord_basis = [self(wts[1]*0), self(2*wts[1])] - ord_basis += [self(wts[i]) for i in range(1, r)] - ord_basis += [self(2*wts[r]), self(wts[r]), self(wts[1]+wts[r])] - if ct == 'D' and k == 1: - if r % 2 == 0: - ord_basis = sorted(self.basis().values()) - else: - temp = sorted(self.basis().values(), reverse=1) - ord_basis = [temp.pop()] - ord_basis.extend(temp) - if ct == 'D' and k == 2: - ord_basis = [self(wts[1]*0), self(2*wts[1]), self(2*wts[r-1]), self(2*wts[r])] - ord_basis += [self(wts[i]) for i in range(1, r-1)] - ord_basis += [self(wts[r-1] + wts[r]), self(wts[r-1]), self(wts[r])] - ord_basis += [self(wts[1] + wts[r-1]), self(wts[1] + wts[r])] - if ct == 'E' and k == 1: - if r == 6: - ord_basis = [self(wts[1]*0), self(wts[1]), self(wts[6])] - if ct == 'E' and k == 2: - if r == 8: - ord_basis = [self(wts[1]*0), self(wts[8]), self(wts[1])] - if not ord_basis: - raise ValueError("ordered basis not yet available for this FusionRing") - return ord_basis - - def s_ij_legacy(self, elt_i, elt_j, fusion_mat_i=[]): - """ - Remove this soon - """ - dims = self.q_dims() - ord_basis = self.ordered_basis() - twists = [x.twist() for x in ord_basis] - rng = range(len(ord_basis)) - j = ord_basis.index(elt_j) - fusion_matrix = fusion_mat_i if fusion_mat_i else elt_i.fusion_matrix() - q = self.q_field().gen() - l = self.fusion_l() - s_ij = sum(fusion_matrix[k,j]*q**(2*l*twists[k])*dims[k] for k in rng) - s_ij *= q**(-2*l*(elt_i.twist() + elt_j.twist())) - return s_ij def s_ij(self, elt_i, elt_j): """ @@ -2493,7 +2424,10 @@ def s_ij(self, elt_i, elt_j): EXAMPLES:: - #TODO: update docstring using next iteration of ordered basis method + sage: G21=FusionRing("G2",1) + sage: b=G21.basis() + sage: [G21.s_ij(x,y) for x in b for y in b] + [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] """ l = self.fusion_l() K = self.q_field() @@ -2501,12 +2435,7 @@ def s_ij(self, elt_i, elt_j): ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) - def s_matrix(self): - b = self.basis() - return matrix([[self.s_ij(b[x],b[y]) for x in self.get_order()] for y in self.get_order()]) - - def s_matrix_old(self): r""" Return the S-matrix of this FusionRing. @@ -2526,13 +2455,8 @@ def s_matrix_old(self): [ 1 -1 1 -1] [ 1 -1 -1 1] """ - ord_basis = self.ordered_basis() - rng = range(len(ord_basis)) - S = matrix(self.q_field(), len(ord_basis)) - for i in rng: - for j in rng: - S[i,j] = self.s_ij(ord_basis[i], ord_basis[j]) - return S + b = self.basis() + return matrix([[self.s_ij(b[x],b[y]) for x in self.get_order()] for y in self.get_order()]) def fusion_labels(self, labels=None, key=str): """ @@ -2672,23 +2596,3 @@ def q_dimension(self): expr = expr.substitute(q=q**2)/q**(expr.degree()) zet = self.parent().q_field().gen() return expr.substitute(q=zet) - - def fusion_matrix(self): - r""" - Return a matrix containing the object's fusion coefficients. - - EXAMPLES:: - - sage: G21=FusionRing('G2',1) - sage: G21.basis() - Finite family {(0, 0, 0): G21(0,0), (1, 0, -1): G21(1,0)} - sage: G21(1,0).fusion_matrix() - [0 1] - [1 1] - """ - if not self.is_simple_object(): - raise ValueError("fusion matrix is only available for simple objects of a FusionRing") - ord_basis = self.parent().ordered_basis() - wts = [x.highest_weight() for x in ord_basis] - rows = [[wt in (self*obj).monomial_coefficients() for wt in wts] for obj in ord_basis] - return matrix(rows) From e188fa24b1857152b234551950eab526870811ca Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 17 Jun 2020 21:54:07 +0900 Subject: [PATCH 055/204] Add real_nth_root symbolic function --- src/doc/en/faq/faq-usage.rst | 19 +++-- src/sage/functions/all.py | 4 +- src/sage/functions/other.py | 131 ++++++++++++++++++++++++++++++++++- src/sage/plot/plot.py | 36 +++++----- 4 files changed, 162 insertions(+), 28 deletions(-) diff --git a/src/doc/en/faq/faq-usage.rst b/src/doc/en/faq/faq-usage.rst index 0308e1d4673..97df78e5b03 100644 --- a/src/doc/en/faq/faq-usage.rst +++ b/src/doc/en/faq/faq-usage.rst @@ -12,7 +12,7 @@ You can try out Sage without downloading anything: * **CoCalc™:** Go to https://cocalc.com and set up a free account. - If you log in, you will gain access to the latest version of Sage and to + If you log in, you will gain access to the latest version of Sage and to many other programs. Note that this website is an independent commercial service. @@ -435,7 +435,7 @@ complicated situation. To see what the preparser does:: See https://wiki.sagemath.org/sage_mathematica for more information about Mathematica vs. SageMath. - + Can I make Sage automatically execute commands on startup? """""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -684,12 +684,17 @@ How do I plot the cube root (or other odd roots) for negative input? This is one of the most frequently asked questions. There are several methods mentioned in the plot documentation, but this one is easiest:: - sage: plot(sign(x)*abs(x)^(1/3),-1,1) + sage: plot(real_nth_root(x, 3), (x, -1, 1)) Graphics object consisting of 1 graphics primitive -The *reason* this is necessary is that Sage returns complex numbers -for odd roots of negative numbers when numerically approximated, which -is a `standard convention `_. +On the other hand, note that the straightforward :: + + sage: plot(x^(1/3), (x, -1, 1)) + +produces the expected plot only for positive ``x``. The *reason* is that Sage +returns complex numbers for odd roots of negative numbers when numerically +approximated, which is a `standard convention +`_. sage: N((-1)^(1/3)) 0.500000000000000 + 0.866025403784439*I @@ -799,7 +804,7 @@ on the IPython command line with the ``??`` shortcut:: sage: plot?? # not tested Signature: plot(*args, **kwds) - Source: + Source: ... Objects that are built into Python or IPython are compiled and will diff --git a/src/sage/functions/all.py b/src/sage/functions/all.py index b84db9e7a57..0c449236e67 100644 --- a/src/sage/functions/all.py +++ b/src/sage/functions/all.py @@ -20,13 +20,13 @@ reciprocal_trig_functions = {'sec': cos, 'csc': sin, 'cot': tan, 'sech': cosh, 'csch': sinh, 'coth': tanh} -from .other import ( ceil, floor, abs_symbolic, sqrt, +from .other import ( ceil, floor, abs_symbolic, sqrt, real_nth_root, arg, real_part, real, frac, factorial, binomial, imag_part, imag, imaginary, conjugate, cases, complex_root_of) -from .log import (exp, exp_polar, log, ln, polylog, dilog, lambert_w, harmonic_number) +from .log import (exp, exp_polar, log, ln, polylog, dilog, lambert_w, harmonic_number) from .transcendental import (zeta, zetaderiv, zeta_symmetric, hurwitz_zeta, dickman_rho, stieltjes) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index e39e424c31f..e4a793a102d 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -33,6 +33,8 @@ from sage.structure.all import parent as s_parent from sage.functions.trig import arctan2 +from sage.functions.generalized import sgn + from sage.arith.all import binomial as arith_binomial one_half = SR.one() / SR(2) @@ -617,6 +619,7 @@ def _eval_(self, x): floor = Function_floor() + class Function_Order(GinacFunction): def __init__(self): r""" @@ -677,7 +680,6 @@ def _sympy_(self, arg): import sympy return sympy.O(*sympy.sympify(arg, evaluate=False)) - Order = Function_Order() @@ -751,6 +753,7 @@ def _eval_(self, x): frac = Function_frac() + def _do_sqrt(x, prec=None, extend=True, all=False): r""" Used internally to compute the square root of x. @@ -909,6 +912,129 @@ def sqrt(x, *args, **kwds): {'__call__': staticmethod(sqrt), '__setstate__': lambda x, y: None}) + +class Function_real_nth_root(BuiltinFunction): + def __init__(self): + r""" + The real `n`-th root function `x^\frac{1}{n}`. + + The function assumes positive integer `n` and real number `x`. + + EXAMPLES:: + + sage: f = real_nth_root(2, 3) + sage: f^3 + 2 + sage: f = real_nth_root(-2, 3) + sage: f^3 + -2 + + TESTS:: + + sage: f = real_nth_root(x, 3) + sage: f._sympy_() + Piecewise((Abs(x)**(1/3)*sign(x), Eq(im(x), 0)), (x**(1/3), True)) + + :: + + sage: cube_root = real_nth_root(x, 3) + sage: loads(dumps(cube_root)) + real_nth_root(x, 3) + """ + BuiltinFunction.__init__(self, "real_nth_root", nargs=2, + conversions=dict(sympy='real_root', + mathematica='Surd', + maple='surd')) + + def _evalf_(self, base, exp, parent=None): + """ + EXAMPLES:: + + sage: real_nth_root(2, 3) + real_nth_root(2, 3) + sage: real_nth_root(-2, 3) + real_nth_root(-2, 3) + sage: real_nth_root(2., 3) + 1.25992104989487 + sage: real_nth_root(-2., 3) + -1.25992104989487 + sage: real_nth_root(RBF(2), 3) + [1.259921049894873 +/- 3.92e-16] + sage: real_nth_root(-2, 4) + Traceback (most recent call last): + ... + ValueError: no real nth root of negative real number with even n + """ + negative = base < 0 + + if negative: + if exp % 2 == 0: + raise ValueError('no real nth root of negative real number with even n') + base = -base + + r = base**(1/exp) + + if negative: + return -r + else: + return r + + def _eval_(self, base, exp): + """ + EXAMPLES:: + + sage: real_nth_root(x, 1) + x + sage: real_nth_root(x, 3) + real_nth_root(x, 3) + sage: real_nth_root(8, 3) + 2 + sage: real_nth_root(-8, 3) + -2 + """ + if not isinstance(base, Expression) and not isinstance(exp, Expression): + if isinstance(base, Integer): + try: + return base.nth_root(exp) + except ValueError: + pass + self._evalf_(base, exp, parent=s_parent(base)) + + if isinstance(exp, Integer) and exp.is_one(): + return base + + def _power_(self, base, exp, power_param=None): + """ + EXAMPLES:: + + sage: v = real_nth_root(x, 3) + sage: v^5 + real_nth_root(x^5, 3) + """ + return self(base**power_param, exp) + + def _derivative_(self, base, exp, diff_param=None): + """ + EXAMPLES:: + + sage: f = real_nth_root(x, 3) + sage: f.diff() + 1/3*real_nth_root(x^(-2), 3) + sage: f = real_nth_root(-x, 3) + sage: f.diff() + -1/3*real_nth_root(x^(-2), 3) + sage: f = real_nth_root(x, 4) + sage: f.diff() + 1/4*real_nth_root(x^(-3), 4) + sage: f = real_nth_root(-x, 4) + sage: f.diff() + -1/4*real_nth_root(-1/x^3, 4) + """ + return 1/exp * self(base, exp)**(1-exp) + +real_nth_root = Function_real_nth_root() + + class Function_arg(BuiltinFunction): def __init__(self): r""" @@ -1152,6 +1278,7 @@ def __call__(self, x, **kwargs): real = real_part = Function_real_part() + class Function_imag_part(GinacFunction): def __init__(self): r""" @@ -1298,6 +1425,7 @@ def __init__(self): conjugate = Function_conjugate() + class Function_factorial(GinacFunction): def __init__(self): r""" @@ -1488,6 +1616,7 @@ def _eval_(self, x): factorial = Function_factorial() + class Function_binomial(GinacFunction): def __init__(self): r""" diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index 43d7a17cec1..8b02d6cd4fb 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -1459,17 +1459,17 @@ def h2(x): return -abs(sqrt(x**3 - 1)) .. PLOT:: - g = plot(sin(x), (x,0,10), plot_points=20, linestyle='', marker='.') + g = plot(sin(x), (x, 0, 10), plot_points=20, linestyle='', marker='.') sphinx_plot(g) The marker can be a TeX symbol as well:: - sage: plot(sin(x), (x,0,10), plot_points=20, linestyle='', marker=r'$\checkmark$') + sage: plot(sin(x), (x, 0, 10), plot_points=20, linestyle='', marker=r'$\checkmark$') Graphics object consisting of 1 graphics primitive .. PLOT:: - g = plot(sin(x), (x,0,10), plot_points=20, linestyle='', marker=r'$\checkmark$') + g = plot(sin(x), (x, 0, 10), plot_points=20, linestyle='', marker=r'$\checkmark$') sphinx_plot(g) Sage currently ignores points that cannot be evaluated @@ -1477,7 +1477,7 @@ def h2(x): return -abs(sqrt(x**3 - 1)) :: sage: set_verbose(-1) - sage: plot(-x*log(x), (x,0,1)) # this works fine since the failed endpoint is just skipped. + sage: plot(-x*log(x), (x, 0, 1)) # this works fine since the failed endpoint is just skipped. Graphics object consisting of 1 graphics primitive sage: set_verbose(0) @@ -1487,42 +1487,42 @@ def h2(x): return -abs(sqrt(x**3 - 1)) :: sage: set_verbose(-1) - sage: plot(x^(1/3), (x,-1,1)) + sage: plot(x^(1/3), (x, -1, 1)) Graphics object consisting of 1 graphics primitive sage: set_verbose(0) .. PLOT:: set_verbose(-1) - g = plot(x**(1.0/3.0), (x,-1,1)) + g = plot(x**(1.0/3.0), (x, -1, 1)) sphinx_plot(g) set_verbose(0) - Plotting the real cube root function for negative input - requires avoiding the complex numbers one would usually get. - The easiest way is to use absolute value:: + Plotting the real cube root function for negative input requires avoiding + the complex numbers one would usually get. The easiest way is to use + :class:`real_nth_root(x, n)` :: - sage: plot(sign(x)*abs(x)^(1/3), (x,-1,1)) + sage: plot(real_nth_root(x, 3), (x, -1, 1)) Graphics object consisting of 1 graphics primitive .. PLOT:: - g = plot(sign(x)*abs(x)**(1.0/3.0), (x,-1,1)) + g = plot(real_nth_root(x, 3), (x, -1, 1)) sphinx_plot(g) - We can also use the following:: + We can also get the same plot in the following way:: - sage: plot(sign(x)*(x*sign(x))^(1/3), (x,-4,4)) + sage: plot(sign(x)*abs(x)^(1/3), (x, -1, 1)) Graphics object consisting of 1 graphics primitive .. PLOT:: - g = plot(sign(x)*(x*sign(x))**(1.0/3.0), (x,-4,4)) + g = plot(sign(x)*abs(x)**(1./3.), (x, -1, 1)) sphinx_plot(g) - A way that points to how to plot other functions without - symbolic variants is using lambda functions:: + A way to plot other functions without symbolic variants is to use lambda + functions:: sage: plot(lambda x : RR(x).nth_root(3), (x,-1, 1)) Graphics object consisting of 1 graphics primitive @@ -2277,7 +2277,7 @@ def golden_rainbow(i,lightness=0.4): for x in excluded_points], []) else: initial_points = None - + # If we are a log scale plot on the x axis, do a change of variables # so we sample the range in log scale is_log_scale = ('scale' in options.keys() and @@ -2306,7 +2306,7 @@ def golden_rainbow(i,lightness=0.4): # add an exclusion point. if abs(data[i+1][0] - data[i][0]) > 2*average_distance_between_points: excluded_points.append((data[i][0] + data[i+1][0])/2) - + # If we did a change in variables, undo it now if is_log_scale: for i,(a,fa) in enumerate(data): From 05d15baa266d175bb56353450786c1417dd73bb1 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Wed, 17 Jun 2020 05:56:53 -0700 Subject: [PATCH 056/204] get_order uncached in FusionRing class only. I think this change should be made instead in free_module.py, where @cached_method should be removed from get_order. However since that change would be more global we have this temporary fix. --- src/sage/combinat/root_system/weyl_characters.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index ffb8cfb795a..7b2e5ee6999 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -2333,6 +2333,16 @@ def q_field(self): def _element_constructor(self,weight): return self._parent._element_constructor(self._parent,weight) + def get_order(self): + """ + This duplicates the functionality inherited from combinat.free_module + but it is not cached. The caching of get_order causes inconsistent + results after calling set_order. + """ + if self._order is None: + self.set_order(self.basis().keys().list()) + return self._order + def some_elements(self): """ Return some elements of ``self``. From 7cef50b2542c2b90e8a60b88beb6fc6d09d0d380 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 17 Jun 2020 23:02:07 +0900 Subject: [PATCH 057/204] Fix a typo in FAQ --- src/doc/en/faq/faq-usage.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/en/faq/faq-usage.rst b/src/doc/en/faq/faq-usage.rst index 97df78e5b03..1cbae2d1eef 100644 --- a/src/doc/en/faq/faq-usage.rst +++ b/src/doc/en/faq/faq-usage.rst @@ -691,12 +691,12 @@ On the other hand, note that the straightforward :: sage: plot(x^(1/3), (x, -1, 1)) -produces the expected plot only for positive ``x``. The *reason* is that Sage +produces the expected plot only for positive `x`. The *reason* is that Sage returns complex numbers for odd roots of negative numbers when numerically approximated, which is a `standard convention -`_. +`_. :: - sage: N((-1)^(1/3)) + sage: numerical_approx( (-1)^(1/3) ) 0.500000000000000 + 0.866025403784439*I How do I use the bitwise XOR operator in Sage? From d12ab31283e8bf8905fb1534d653df97619ba575 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Thu, 18 Jun 2020 09:11:32 +0900 Subject: [PATCH 058/204] Fix for a pyflakes warning --- src/sage/functions/other.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index e4a793a102d..cc51417f08b 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -33,7 +33,6 @@ from sage.structure.all import parent as s_parent from sage.functions.trig import arctan2 -from sage.functions.generalized import sgn from sage.arith.all import binomial as arith_binomial From 3f5b6eacdfabf83db6e23e49aacbf8300bf7ff01 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 17 Jun 2020 20:06:15 -0700 Subject: [PATCH 059/204] Move sage.misc.misc.coeff_repr, repr_lincomb to new module sage.misc.repr --- src/doc/en/reference/misc/index.rst | 1 + src/sage/algebras/free_algebra_element.py | 3 +- .../algebras/free_algebra_quotient_element.py | 2 +- .../free_algebra_element_letterplace.pyx | 2 +- .../lie_algebras/lie_algebra_element.pyx | 2 +- src/sage/combinat/combinatorial_algebra.py | 2 +- src/sage/combinat/species/series.py | 3 +- .../groups/lie_gps/nilpotent_lie_group.py | 2 +- src/sage/misc/all.py | 3 +- src/sage/misc/misc.py | 170 +----------------- src/sage/misc/repr.py | 170 ++++++++++++++++++ src/sage/modular/modsym/boundary.py | 2 +- src/sage/modular/modsym/element.py | 4 +- .../modules/with_basis/indexed_element.pyx | 6 +- src/sage/quivers/algebra_elements.pyx | 2 +- src/sage/schemes/generic/divisor.py | 4 +- .../hyperelliptic_curves/monsky_washnitzer.py | 3 +- src/sage/structure/formal_sum.py | 10 +- 18 files changed, 200 insertions(+), 191 deletions(-) create mode 100644 src/sage/misc/repr.py diff --git a/src/doc/en/reference/misc/index.rst b/src/doc/en/reference/misc/index.rst index 4e8eb49bc15..20b9285f836 100644 --- a/src/doc/en/reference/misc/index.rst +++ b/src/doc/en/reference/misc/index.rst @@ -167,6 +167,7 @@ Formatted Output sage/typeset/character_art_factory sage/typeset/ascii_art sage/typeset/unicode_art + sage/misc/repr sage/misc/sage_input sage/misc/table diff --git a/src/sage/algebras/free_algebra_element.py b/src/sage/algebras/free_algebra_element.py index 456732d6ced..da91eda2cf8 100644 --- a/src/sage/algebras/free_algebra_element.py +++ b/src/sage/algebras/free_algebra_element.py @@ -33,9 +33,8 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.monoids.free_monoid_element import FreeMonoidElement from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement from sage.structure.element import AlgebraElement diff --git a/src/sage/algebras/free_algebra_quotient_element.py b/src/sage/algebras/free_algebra_quotient_element.py index f5216b525d4..9e291010fa2 100644 --- a/src/sage/algebras/free_algebra_quotient_element.py +++ b/src/sage/algebras/free_algebra_quotient_element.py @@ -22,7 +22,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.structure.element import RingElement, AlgebraElement from sage.structure.parent_gens import localvars from sage.structure.richcmp import richcmp diff --git a/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx b/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx index ba7518818e0..ad863ea0a34 100644 --- a/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx +++ b/src/sage/algebras/letterplace/free_algebra_element_letterplace.pyx @@ -18,7 +18,7 @@ AUTHOR: # **************************************************************************** from sage.libs.singular.function import lib, singular_function -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal from cpython.object cimport PyObject_RichCompare diff --git a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx index d519d37b618..339dfe395ab 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx +++ b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx @@ -20,7 +20,7 @@ AUTHORS: from copy import copy from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.combinat.free_module import CombinatorialFreeModule from sage.structure.element cimport have_same_parent, parent from sage.structure.coerce cimport coercion_model diff --git a/src/sage/combinat/combinatorial_algebra.py b/src/sage/combinat/combinatorial_algebra.py index a473c23813e..c9e4d0675d1 100644 --- a/src/sage/combinat/combinatorial_algebra.py +++ b/src/sage/combinat/combinatorial_algebra.py @@ -60,7 +60,7 @@ #***************************************************************************** from sage.combinat.free_module import CombinatorialFreeModule -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.misc.cachefunc import cached_method from sage.categories.all import AlgebrasWithBasis from sage.structure.element import Element diff --git a/src/sage/combinat/species/series.py b/src/sage/combinat/species/series.py index 1163aebcda4..ead9daae563 100644 --- a/src/sage/combinat/species/series.py +++ b/src/sage/combinat/species/series.py @@ -37,7 +37,8 @@ from sage.rings.all import Integer from sage.misc.all import prod from functools import partial -from sage.misc.misc import repr_lincomb, is_iterator +from sage.misc.misc import is_iterator +from sage.misc.repr import repr_lincomb from sage.misc.cachefunc import cached_method from sage.algebras.algebra import Algebra diff --git a/src/sage/groups/lie_gps/nilpotent_lie_group.py b/src/sage/groups/lie_gps/nilpotent_lie_group.py index 78001d2e9be..5b6541a96fb 100644 --- a/src/sage/groups/lie_gps/nilpotent_lie_group.py +++ b/src/sage/groups/lie_gps/nilpotent_lie_group.py @@ -24,7 +24,7 @@ from sage.manifolds.structure import(DifferentialStructure, RealDifferentialStructure) from sage.misc.cachefunc import cached_method -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.modules.free_module_element import vector from sage.rings.real_mpfr import RealField_class from sage.structure.element import MultiplicativeGroupElement diff --git a/src/sage/misc/all.py b/src/sage/misc/all.py index 1eb98169cdc..61090c25111 100644 --- a/src/sage/misc/all.py +++ b/src/sage/misc/all.py @@ -8,7 +8,6 @@ union, uniq, powerset, subsets, exists, forall, is_iterator, random_sublist, walltime, - repr_lincomb, pad_zeros, SAGE_DB, SAGE_TMP, newton_method_sizes, compose, @@ -28,6 +27,8 @@ from .html import html +from .repr import repr_lincomb + from .table import table from .sage_timeit_class import timeit diff --git a/src/sage/misc/misc.py b/src/sage/misc/misc.py index 1583f4d8626..0b979bcf7de 100644 --- a/src/sage/misc/misc.py +++ b/src/sage/misc/misc.py @@ -54,6 +54,9 @@ lazy_import("sage.misc.call", ["AttrCallObject", "attrcall", "call_method"], deprecation=29869) +lazy_import("sage.misc.repr", ["coeff_repr", "repr_lincomb"], + deprecation=29892) + from sage.env import DOT_SAGE, HOSTNAME LOCAL_IDENTIFIER = '%s.%s' % (HOSTNAME, os.getpid()) @@ -775,173 +778,6 @@ def exactly_one_is_true(iterable): return any(it) and not any(it) -def coeff_repr(c, is_latex=False): - if not is_latex: - try: - return c._coeff_repr() - except AttributeError: - pass - if isinstance(c, (int, float)): - return str(c) - if is_latex and hasattr(c, '_latex_'): - s = c._latex_() - else: - s = str(c).replace(' ', '') - if s.find("+") != -1 or s.find("-") != -1: - if is_latex: - return "\\left(%s\\right)" % s - else: - return "(%s)" % s - return s - - -def repr_lincomb(terms, is_latex=False, scalar_mult="*", strip_one=False, - repr_monomial=None, latex_scalar_mult=None): - """ - Compute a string representation of a linear combination of some - formal symbols. - - INPUT: - - - ``terms`` -- list of terms, as pairs (support, coefficient) - - ``is_latex`` -- whether to produce latex (default: ``False``) - - ``scalar_mult`` -- string representing the multiplication (default:``'*'``) - - ``latex_scalar_mult`` -- latex string representing the multiplication - (default: ``''`` if ``scalar_mult`` is ``'*'``; otherwise ``scalar_mult``) - - ``coeffs`` -- for backward compatibility - - OUTPUT: - - - ``str`` - a string - - EXAMPLES:: - - sage: repr_lincomb([('a',1), ('b',-2), ('c',3)]) - 'a - 2*b + 3*c' - sage: repr_lincomb([('a',0), ('b',-2), ('c',3)]) - '-2*b + 3*c' - sage: repr_lincomb([('a',0), ('b',2), ('c',3)]) - '2*b + 3*c' - sage: repr_lincomb([('a',1), ('b',0), ('c',3)]) - 'a + 3*c' - sage: repr_lincomb([('a',-1), ('b','2+3*x'), ('c',3)]) - '-a + (2+3*x)*b + 3*c' - sage: repr_lincomb([('a', '1+x^2'), ('b', '2+3*x'), ('c', 3)]) - '(1+x^2)*a + (2+3*x)*b + 3*c' - sage: repr_lincomb([('a', '1+x^2'), ('b', '-2+3*x'), ('c', 3)]) - '(1+x^2)*a + (-2+3*x)*b + 3*c' - sage: repr_lincomb([('a', 1), ('b', -2), ('c', -3)]) - 'a - 2*b - 3*c' - sage: t = PolynomialRing(RationalField(),'t').gen() - sage: repr_lincomb([('a', -t), ('s', t - 2), ('', t^2 + 2)]) - '-t*a + (t-2)*s + (t^2+2)' - - Examples for ``scalar_mult``:: - - sage: repr_lincomb([('a',1), ('b',2), ('c',3)], scalar_mult='*') - 'a + 2*b + 3*c' - sage: repr_lincomb([('a',2), ('b',0), ('c',-3)], scalar_mult='**') - '2**a - 3**c' - sage: repr_lincomb([('a',-1), ('b',2), ('c',3)], scalar_mult='**') - '-a + 2**b + 3**c' - - Examples for ``scalar_mult`` and ``is_latex``:: - - sage: repr_lincomb([('a',-1), ('b',2), ('c',3)], is_latex=True) - '-a + 2b + 3c' - sage: repr_lincomb([('a',-1), ('b',-1), ('c',3)], is_latex=True, scalar_mult='*') - '-a - b + 3c' - sage: repr_lincomb([('a',-1), ('b',2), ('c',-3)], is_latex=True, scalar_mult='**') - '-a + 2**b - 3**c' - sage: repr_lincomb([('a',-2), ('b',-1), ('c',-3)], is_latex=True, latex_scalar_mult='*') - '-2*a - b - 3*c' - - Examples for ``strip_one``:: - - sage: repr_lincomb([ ('a',1), (1,-2), ('3',3) ]) - 'a - 2*1 + 3*3' - sage: repr_lincomb([ ('a',-1), (1,1), ('3',3) ]) - '-a + 1 + 3*3' - sage: repr_lincomb([ ('a',1), (1,-2), ('3',3) ], strip_one = True) - 'a - 2 + 3*3' - sage: repr_lincomb([ ('a',-1), (1,1), ('3',3) ], strip_one = True) - '-a + 1 + 3*3' - sage: repr_lincomb([ ('a',1), (1,-1), ('3',3) ], strip_one = True) - 'a - 1 + 3*3' - - Examples for ``repr_monomial``:: - - sage: repr_lincomb([('a',1), ('b',2), ('c',3)], repr_monomial = lambda s: s+"1") - 'a1 + 2*b1 + 3*c1' - """ - # Setting scalar_mult: symbol used for scalar multiplication - if is_latex: - if latex_scalar_mult is not None: - scalar_mult = latex_scalar_mult - elif scalar_mult == "*": - scalar_mult = "" - - if repr_monomial is None: - if is_latex: - - def repr_monomial(monomial): - return monomial._latex_() if hasattr(monomial, '_latex_') else str(monomial) - else: - repr_monomial = str - - s = "" - first = True - - if scalar_mult is None: - scalar_mult = "" if is_latex else "*" - - for (monomial, c) in terms: - if c != 0: - coeff = coeff_repr(c) - negative = False - if len(coeff) and coeff[0] == "-": - negative = True - try: - if c < 0: - negative = True - except (NotImplementedError, TypeError): - # comparisons may not be implemented for some coefficients - pass - if negative: - coeff = coeff_repr(-c, is_latex) - else: - coeff = coeff_repr(c, is_latex) - if coeff == "1": - coeff = "" - if coeff != "0": - if negative: - if first: - sign = "-" # add trailing space? - else: - sign = " - " - else: - if first: - sign = "" - else: - sign = " + " - b = repr_monomial(monomial) - if len(b): - if coeff != "": - if b == "1" and strip_one: - b = "" - else: - b = scalar_mult + b - s += "%s%s%s" % (sign, coeff, b) - first = False - if first: - return "0" - # this can happen only if are only terms with coeff_repr(c) == "0" - # elif s == "": - # return "1" # is empty string representation invalid? - else: - return s - - def strunc(s, n=60): """ Truncate at first space after position n, adding '...' if diff --git a/src/sage/misc/repr.py b/src/sage/misc/repr.py new file mode 100644 index 00000000000..ec28e9d4459 --- /dev/null +++ b/src/sage/misc/repr.py @@ -0,0 +1,170 @@ +""" +Repr formatting support +""" + + +def coeff_repr(c, is_latex=False): + if not is_latex: + try: + return c._coeff_repr() + except AttributeError: + pass + if isinstance(c, (int, float)): + return str(c) + if is_latex and hasattr(c, '_latex_'): + s = c._latex_() + else: + s = str(c).replace(' ', '') + if s.find("+") != -1 or s.find("-") != -1: + if is_latex: + return "\\left(%s\\right)" % s + else: + return "(%s)" % s + return s + + +def repr_lincomb(terms, is_latex=False, scalar_mult="*", strip_one=False, + repr_monomial=None, latex_scalar_mult=None): + """ + Compute a string representation of a linear combination of some + formal symbols. + + INPUT: + + - ``terms`` -- list of terms, as pairs (support, coefficient) + - ``is_latex`` -- whether to produce latex (default: ``False``) + - ``scalar_mult`` -- string representing the multiplication (default:``'*'``) + - ``latex_scalar_mult`` -- latex string representing the multiplication + (default: ``''`` if ``scalar_mult`` is ``'*'``; otherwise ``scalar_mult``) + - ``coeffs`` -- for backward compatibility + + OUTPUT: + + - ``str`` - a string + + EXAMPLES:: + + sage: repr_lincomb([('a',1), ('b',-2), ('c',3)]) + 'a - 2*b + 3*c' + sage: repr_lincomb([('a',0), ('b',-2), ('c',3)]) + '-2*b + 3*c' + sage: repr_lincomb([('a',0), ('b',2), ('c',3)]) + '2*b + 3*c' + sage: repr_lincomb([('a',1), ('b',0), ('c',3)]) + 'a + 3*c' + sage: repr_lincomb([('a',-1), ('b','2+3*x'), ('c',3)]) + '-a + (2+3*x)*b + 3*c' + sage: repr_lincomb([('a', '1+x^2'), ('b', '2+3*x'), ('c', 3)]) + '(1+x^2)*a + (2+3*x)*b + 3*c' + sage: repr_lincomb([('a', '1+x^2'), ('b', '-2+3*x'), ('c', 3)]) + '(1+x^2)*a + (-2+3*x)*b + 3*c' + sage: repr_lincomb([('a', 1), ('b', -2), ('c', -3)]) + 'a - 2*b - 3*c' + sage: t = PolynomialRing(RationalField(),'t').gen() + sage: repr_lincomb([('a', -t), ('s', t - 2), ('', t^2 + 2)]) + '-t*a + (t-2)*s + (t^2+2)' + + Examples for ``scalar_mult``:: + + sage: repr_lincomb([('a',1), ('b',2), ('c',3)], scalar_mult='*') + 'a + 2*b + 3*c' + sage: repr_lincomb([('a',2), ('b',0), ('c',-3)], scalar_mult='**') + '2**a - 3**c' + sage: repr_lincomb([('a',-1), ('b',2), ('c',3)], scalar_mult='**') + '-a + 2**b + 3**c' + + Examples for ``scalar_mult`` and ``is_latex``:: + + sage: repr_lincomb([('a',-1), ('b',2), ('c',3)], is_latex=True) + '-a + 2b + 3c' + sage: repr_lincomb([('a',-1), ('b',-1), ('c',3)], is_latex=True, scalar_mult='*') + '-a - b + 3c' + sage: repr_lincomb([('a',-1), ('b',2), ('c',-3)], is_latex=True, scalar_mult='**') + '-a + 2**b - 3**c' + sage: repr_lincomb([('a',-2), ('b',-1), ('c',-3)], is_latex=True, latex_scalar_mult='*') + '-2*a - b - 3*c' + + Examples for ``strip_one``:: + + sage: repr_lincomb([ ('a',1), (1,-2), ('3',3) ]) + 'a - 2*1 + 3*3' + sage: repr_lincomb([ ('a',-1), (1,1), ('3',3) ]) + '-a + 1 + 3*3' + sage: repr_lincomb([ ('a',1), (1,-2), ('3',3) ], strip_one = True) + 'a - 2 + 3*3' + sage: repr_lincomb([ ('a',-1), (1,1), ('3',3) ], strip_one = True) + '-a + 1 + 3*3' + sage: repr_lincomb([ ('a',1), (1,-1), ('3',3) ], strip_one = True) + 'a - 1 + 3*3' + + Examples for ``repr_monomial``:: + + sage: repr_lincomb([('a',1), ('b',2), ('c',3)], repr_monomial = lambda s: s+"1") + 'a1 + 2*b1 + 3*c1' + """ + # Setting scalar_mult: symbol used for scalar multiplication + if is_latex: + if latex_scalar_mult is not None: + scalar_mult = latex_scalar_mult + elif scalar_mult == "*": + scalar_mult = "" + + if repr_monomial is None: + if is_latex: + + def repr_monomial(monomial): + return monomial._latex_() if hasattr(monomial, '_latex_') else str(monomial) + else: + repr_monomial = str + + s = "" + first = True + + if scalar_mult is None: + scalar_mult = "" if is_latex else "*" + + for (monomial, c) in terms: + if c != 0: + coeff = coeff_repr(c) + negative = False + if len(coeff) and coeff[0] == "-": + negative = True + try: + if c < 0: + negative = True + except (NotImplementedError, TypeError): + # comparisons may not be implemented for some coefficients + pass + if negative: + coeff = coeff_repr(-c, is_latex) + else: + coeff = coeff_repr(c, is_latex) + if coeff == "1": + coeff = "" + if coeff != "0": + if negative: + if first: + sign = "-" # add trailing space? + else: + sign = " - " + else: + if first: + sign = "" + else: + sign = " + " + b = repr_monomial(monomial) + if len(b): + if coeff != "": + if b == "1" and strip_one: + b = "" + else: + b = scalar_mult + b + s += "%s%s%s" % (sign, coeff, b) + first = False + if first: + return "0" + # this can happen only if are only terms with coeff_repr(c) == "0" + # elif s == "": + # return "1" # is empty string representation invalid? + else: + return s diff --git a/src/sage/modular/modsym/boundary.py b/src/sage/modular/modsym/boundary.py index ca0f9b6c6eb..736d4fd0e50 100644 --- a/src/sage/modular/modsym/boundary.py +++ b/src/sage/modular/modsym/boundary.py @@ -90,7 +90,7 @@ from __future__ import absolute_import -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.structure.richcmp import richcmp_method, richcmp import sage.modules.free_module as free_module diff --git a/src/sage/modular/modsym/element.py b/src/sage/modular/modsym/element.py index fd7504a6a3b..bfeffc20ead 100644 --- a/src/sage/modular/modsym/element.py +++ b/src/sage/modular/modsym/element.py @@ -22,7 +22,7 @@ import sage.modules.free_module_element -import sage.misc.misc as misc +from sage.misc.repr import repr_lincomb import sage.structure.formal_sum as formal_sum import sage.modular.hecke.all as hecke import sage.misc.latex as latex @@ -144,7 +144,7 @@ def _repr_(self): m = self.manin_symbol_rep() elif _print_mode == "modular": m = self.modular_symbol_rep() - return misc.repr_lincomb([(t,c) for c,t in m]) + return repr_lincomb([(t,c) for c,t in m]) def _latex_(self): r""" diff --git a/src/sage/modules/with_basis/indexed_element.pyx b/src/sage/modules/with_basis/indexed_element.pyx index 94d57d67e2c..61d5fabc05b 100644 --- a/src/sage/modules/with_basis/indexed_element.pyx +++ b/src/sage/modules/with_basis/indexed_element.pyx @@ -21,7 +21,7 @@ from sage.structure.element cimport parent from sage.structure.richcmp cimport richcmp, rich_to_bool from cpython.object cimport Py_NE, Py_EQ -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.typeset.ascii_art import AsciiArt, empty_ascii_art, ascii_art @@ -307,7 +307,7 @@ cdef class IndexedFreeModuleElement(ModuleElement): sage: ascii_art(M.zero()) 0 """ - from sage.misc.misc import coeff_repr + from sage.misc.repr import coeff_repr terms = self._sorted_items_for_printing() scalar_mult = self._parent._print_options['scalar_mult'] repr_monomial = self._parent._ascii_art_term @@ -376,7 +376,7 @@ cdef class IndexedFreeModuleElement(ModuleElement): sage: unicode_art([M.zero()]) # indirect doctest [ 0 ] """ - from sage.misc.misc import coeff_repr + from sage.misc.repr import coeff_repr terms = self._sorted_items_for_printing() scalar_mult = self._parent._print_options['scalar_mult'] repr_monomial = self._parent._unicode_art_term diff --git a/src/sage/quivers/algebra_elements.pyx b/src/sage/quivers/algebra_elements.pyx index 25968539fcf..216738cadb0 100644 --- a/src/sage/quivers/algebra_elements.pyx +++ b/src/sage/quivers/algebra_elements.pyx @@ -18,7 +18,7 @@ AUTHORS: include "algebra_elements.pxi" from sage.misc.cachefunc import cached_method -from sage.misc.misc import repr_lincomb +from sage.misc.repr import repr_lincomb from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool diff --git a/src/sage/schemes/generic/divisor.py b/src/sage/schemes/generic/divisor.py index 49e07357223..c6d1494d4e8 100644 --- a/src/sage/schemes/generic/divisor.py +++ b/src/sage/schemes/generic/divisor.py @@ -41,8 +41,8 @@ # http://www.gnu.org/licenses/ #******************************************************************************* -from sage.misc.all import latex -from sage.misc.misc import repr_lincomb +from sage.misc.latex import latex +from sage.misc.repr import repr_lincomb from sage.misc.search import search from sage.rings.all import ZZ from sage.structure.formal_sum import FormalSum diff --git a/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py b/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py index 83b63b0511d..b43335492d4 100644 --- a/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +++ b/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py @@ -52,8 +52,9 @@ from sage.functions.other import floor from sage.matrix.all import matrix from sage.misc.cachefunc import cached_method -from sage.misc.misc import newton_method_sizes, repr_lincomb +from sage.misc.misc import newton_method_sizes from sage.misc.profiler import Profiler +from sage.misc.repr import repr_lincomb from sage.modules.all import vector from sage.modules.free_module import FreeModule from sage.modules.free_module_element import is_FreeModuleElement diff --git a/src/sage/structure/formal_sum.py b/src/sage/structure/formal_sum.py index 097abcacd62..f1c5dcf30cb 100644 --- a/src/sage/structure/formal_sum.py +++ b/src/sage/structure/formal_sum.py @@ -65,9 +65,8 @@ #***************************************************************************** from __future__ import print_function -import sage.misc.misc +from sage.misc.repr import repr_lincomb import operator -import sage.misc.latex from collections import OrderedDict from sage.modules.module import Module @@ -188,7 +187,7 @@ def _repr_(self): sage: a._repr_() # random '2/3 - 3*4/5 + 7*2' """ - return sage.misc.misc.repr_lincomb([t, c] for c, t in self) + return repr_lincomb([t, c] for c, t in self) def _latex_(self): r""" @@ -197,12 +196,13 @@ def _latex_(self): sage: latex(FormalSum([(1,2), (5, 8/9), (-3, 7)])) 2 + 5\cdot \frac{8}{9} - 3\cdot 7 """ + from sage.misc.latex import repr_lincomb symbols = [z[1] for z in self] coeffs = [z[0] for z in self] - return sage.misc.latex.repr_lincomb(symbols, coeffs) + return repr_lincomb(symbols, coeffs) # TODO: finish merging sage.misc.latex.repr_lincomb and # sage.misc.misc.repr_lincomb and use instead: - # return sage.misc.misc.repr_lincomb([[t,c] for c,t in self], is_latex=True) + # return repr_lincomb([[t,c] for c,t in self], is_latex=True) def _richcmp_(self, other, op): """ From 97199ddbd1a32db4685ae203ab51d76f9973b6a7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 15 Jun 2020 12:21:23 -0700 Subject: [PATCH 060/204] sage.structure.parent_gens: Remove imports --- src/sage/structure/parent_gens.pyx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/structure/parent_gens.pyx b/src/sage/structure/parent_gens.pyx index 945476a7da9..c7838996fc2 100644 --- a/src/sage/structure/parent_gens.pyx +++ b/src/sage/structure/parent_gens.pyx @@ -68,8 +68,6 @@ This example illustrates generators for a free module over `\ZZ`. # http://www.gnu.org/licenses/ #***************************************************************************** -import sage.misc.defaults -from sage.misc.latex import latex_variable_name from . import gens_py cimport sage.structure.parent as parent from sage.structure.coerce_dict cimport MonoDict From bb4c6a8cc4de60ea5224fedc1c3da13b19197f34 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 15 Jun 2020 11:51:23 -0700 Subject: [PATCH 061/204] sage.structure.sequence: Make import of sage.misc.latex local to the latex method --- src/sage/structure/sequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/structure/sequence.py b/src/sage/structure/sequence.py index eea50863c44..7aa4440e832 100644 --- a/src/sage/structure/sequence.py +++ b/src/sage/structure/sequence.py @@ -71,7 +71,6 @@ from __future__ import print_function -from sage.misc.latex import list_function as list_latex_function import sage.structure.sage_object import sage.structure.coerce @@ -683,6 +682,7 @@ def _latex_(self): sage: latex(t) \left[\sqrt{x}, e^{x}, x^{x - 1}\right] """ + from sage.misc.latex import list_function as list_latex_function return list_latex_function(self) def __str__(self): From 08202839ecc686f2519cdbf4b924c550b21889d2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 15 Jun 2020 11:50:02 -0700 Subject: [PATCH 062/204] sage.structure.factorization: Make import of sage.rings.integer.Integer local to methods --- src/sage/structure/factorization.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/structure/factorization.py b/src/sage/structure/factorization.py index b163e81a298..1d11290b0ce 100644 --- a/src/sage/structure/factorization.py +++ b/src/sage/structure/factorization.py @@ -185,7 +185,6 @@ from sage.structure.element import Element from sage.structure.sequence import Sequence from sage.structure.richcmp import richcmp_method, richcmp, richcmp_not_equal -from sage.rings.integer import Integer from sage.misc.all import prod from sage.misc.cachefunc import cached_method @@ -294,6 +293,7 @@ def __init__(self, x, unit=None, cr=False, sort=True, simplify=True): (Ambient free module of rank 2 over the principal ideal domain Integer Ring)^5 * (Ambient free module of rank 3 over the principal ideal domain Integer Ring)^2 """ + from sage.rings.integer import Integer x = [(p, Integer(e)) for (p, e) in x] try: @@ -1112,6 +1112,7 @@ def __pow__(self, n): sage: F**2 x^3 * y^2 * x^4 * y^2 * x """ + from sage.rings.integer import Integer if not isinstance(n, Integer): try: n = Integer(n) From 7991b64e470950f9fba15394e73cb54b2c9b8566 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 15 Jun 2020 12:20:43 -0700 Subject: [PATCH 063/204] sage.structure.factorization: Make import of sage.misc.misc_c.prod local to methods --- src/sage/structure/factorization.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/structure/factorization.py b/src/sage/structure/factorization.py index 1d11290b0ce..4051953333d 100644 --- a/src/sage/structure/factorization.py +++ b/src/sage/structure/factorization.py @@ -185,7 +185,6 @@ from sage.structure.element import Element from sage.structure.sequence import Sequence from sage.structure.richcmp import richcmp_method, richcmp, richcmp_not_equal -from sage.misc.all import prod from sage.misc.cachefunc import cached_method @@ -1193,6 +1192,7 @@ def value(self): sage: F.value() x^3*y^2*x """ + from sage.misc.misc_c import prod return prod([p**e for p, e in self.__x], self.__unit) # Two aliases for ``value(self)``. @@ -1348,4 +1348,5 @@ def radical_value(self): """ if not all(e > 0 for p, e in self.__x): raise ValueError("All exponents in the factorization must be positive.") + from sage.misc.misc_c import prod return prod([p for p, e in self.__x]) From 1f52255c43134cf47840466f3ca27fc79bd4e346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 17 Jun 2020 21:40:12 +0200 Subject: [PATCH 064/204] introduce minimal interface for using ZZ[x] matrices in Flint --- src/sage/libs/flint/flint_wrap.h | 1 + src/sage/libs/flint/fmpz_poly_mat.pxd | 27 +++++++++++++++++++++++++++ src/sage/libs/flint/types.pxd | 7 +++++++ 3 files changed, 35 insertions(+) create mode 100644 src/sage/libs/flint/fmpz_poly_mat.pxd diff --git a/src/sage/libs/flint/flint_wrap.h b/src/sage/libs/flint/flint_wrap.h index b68c93ce810..9c0ca4e88b1 100644 --- a/src/sage/libs/flint/flint_wrap.h +++ b/src/sage/libs/flint/flint_wrap.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/src/sage/libs/flint/fmpz_poly_mat.pxd b/src/sage/libs/flint/fmpz_poly_mat.pxd new file mode 100644 index 00000000000..d045c745de3 --- /dev/null +++ b/src/sage/libs/flint/fmpz_poly_mat.pxd @@ -0,0 +1,27 @@ +# distutils: libraries = flint +# distutils: depends = flint/fmpz_poly_mat.h + +from sage.libs.flint.types cimport fmpz_poly_mat_t, fmpz_poly_t, fmpz_t, slong, fmpz_mat_t + +# flint/fmpz_poly_mat.h +cdef extern from "flint_wrap.h": + + void fmpz_poly_mat_init(fmpz_poly_mat_t mat, slong rows, slong cols) + void fmpz_poly_mat_init_set(fmpz_poly_mat_t mat, const fmpz_poly_mat_t src) + void fmpz_poly_mat_clear(fmpz_poly_mat_t mat) + fmpz_poly_t fmpz_poly_mat_entry(fmpz_poly_mat_t mat, long i, long j) + slong fmpz_poly_mat_nrows(const fmpz_poly_mat_t mat) + slong fmpz_poly_mat_ncols(const fmpz_poly_mat_t mat) + + void fmpz_poly_mat_set(fmpz_poly_mat_t mat1, const fmpz_poly_mat_t mat2) + + void fmpz_poly_mat_swap(fmpz_poly_mat_t mat1, fmpz_poly_mat_t mat2) + + void fmpz_poly_mat_transpose(fmpz_poly_mat_t B, const fmpz_poly_mat_t A) + + void fmpz_poly_mat_evaluate_fmpz(fmpz_mat_t B, const fmpz_poly_mat_t A, + const fmpz_t x) + + void fmpz_poly_mat_trace(fmpz_poly_t trace, const fmpz_poly_mat_t mat) + + void fmpz_poly_mat_det(fmpz_poly_t det, const fmpz_poly_mat_t A) diff --git a/src/sage/libs/flint/types.pxd b/src/sage/libs/flint/types.pxd index 92173a78156..3e0d595ee9f 100644 --- a/src/sage/libs/flint/types.pxd +++ b/src/sage/libs/flint/types.pxd @@ -79,6 +79,13 @@ cdef extern from "flint_wrap.h": ctypedef fmpq_mat_struct fmpq_mat_t[1] +# flint/fmpz_poly_mat.h: +cdef extern from "flint_wrap.h": + ctypedef struct fmpz_poly_mat_struct: + pass + + ctypedef fmpz_poly_mat_struct fmpz_poly_mat_t[1] + # flint/fmpz_mod_poly.h: cdef extern from "flint_wrap.h": ctypedef struct fmpz_mod_poly_struct: From 08fedfa9b22214c9b3e8c4640c7c10fbd66f78ee Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 18 Jun 2020 10:36:04 -0700 Subject: [PATCH 065/204] src/sage/combinat/root_system/type_dual.py: Remove unused variable to fix pyflakes warning --- src/sage/combinat/root_system/type_dual.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/combinat/root_system/type_dual.py b/src/sage/combinat/root_system/type_dual.py index cdcc1601c9c..ae1d25776d9 100644 --- a/src/sage/combinat/root_system/type_dual.py +++ b/src/sage/combinat/root_system/type_dual.py @@ -596,14 +596,13 @@ def _repr_(self, compact=False): sage: CartanType(['F', 4, 1]).dual()._repr_(compact = True) 'F4~*' """ - dual_str = self.options.dual_str if self.options.notation == "Kac": if self._type.type() == 'B': if compact: return 'A%s^2'%(self.classical().rank()*2-1) return "['A', %s, 2]"%(self.classical().rank()*2-1) elif self._type.type() == 'BC': - dual_str = '+' # UNUSED ? + pass elif self._type.type() == 'C': if compact: return 'D%s^2'%(self.rank()) From 05efc119c9341ac32e19acf969438c91c5008918 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 18 Jun 2020 10:42:43 -0700 Subject: [PATCH 066/204] sage.misc.repr.coeff_repr: Add doctest, adapted from sage.misc.latex.coeff_repr --- src/sage/misc/repr.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/sage/misc/repr.py b/src/sage/misc/repr.py index ec28e9d4459..a65fd7d9269 100644 --- a/src/sage/misc/repr.py +++ b/src/sage/misc/repr.py @@ -4,6 +4,29 @@ def coeff_repr(c, is_latex=False): + r""" + String representing coefficients in a linear combination. + + INPUT: + + - ``c`` -- a coefficient (i.e., an element of a ring) + + OUTPUT: + + A string + + EXAMPLES:: + + sage: from sage.misc.repr import coeff_repr + sage: coeff_repr(QQ(1/2)) + '1/2' + sage: coeff_repr(-x^2) + '(-x^2)' + sage: coeff_repr(QQ(1/2), is_latex=True) + '\\frac{1}{2}' + sage: coeff_repr(-x^2, is_latex=True) + '\\left(-x^{2}\\right)' + """ if not is_latex: try: return c._coeff_repr() From 538323b4ed2dc5a2593acceaa5794fb4dd502f8d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 18 Jun 2020 15:15:41 -0700 Subject: [PATCH 067/204] src/sage/misc/call.py: Fix block syntax in docstring --- src/sage/misc/call.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/misc/call.py b/src/sage/misc/call.py index 25c1c451864..cf538dee9bd 100644 --- a/src/sage/misc/call.py +++ b/src/sage/misc/call.py @@ -146,11 +146,11 @@ def attrcall(name, *args, **kwds): INPUT: - - ``name`` - a string of the name of the method you - want to call + - ``name`` - a string of the name of the method you + want to call - - ``args, kwds`` - arguments and keywords to be passed - to the method + - ``args, kwds`` - arguments and keywords to be passed + to the method EXAMPLES:: From 00380b09135e8171593108059f17490a23f554ad Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Thu, 18 Jun 2020 15:01:17 +0900 Subject: [PATCH 068/204] Fixes for reviewer comments --- src/doc/en/faq/faq-usage.rst | 7 +++---- src/sage/functions/other.py | 21 +++++++++++++++++++++ src/sage/symbolic/expression.pyx | 6 +++--- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/doc/en/faq/faq-usage.rst b/src/doc/en/faq/faq-usage.rst index 1cbae2d1eef..37e877741e3 100644 --- a/src/doc/en/faq/faq-usage.rst +++ b/src/doc/en/faq/faq-usage.rst @@ -419,7 +419,7 @@ How can I wrote multiplication implicitly as in Mathematica? Sage has a function that enables this:: sage: implicit_multiplication(True) - sage: x 2 x # Not tested + sage: x 2 x # not tested 2*x^2 sage: implicit_multiplication(False) @@ -684,12 +684,11 @@ How do I plot the cube root (or other odd roots) for negative input? This is one of the most frequently asked questions. There are several methods mentioned in the plot documentation, but this one is easiest:: - sage: plot(real_nth_root(x, 3), (x, -1, 1)) - Graphics object consisting of 1 graphics primitive + sage: plot(real_nth_root(x, 3), (x, -1, 1)) # not tested On the other hand, note that the straightforward :: - sage: plot(x^(1/3), (x, -1, 1)) + sage: plot(x^(1/3), (x, -1, 1)) # not tested produces the expected plot only for positive `x`. The *reason* is that Sage returns complex numbers for odd roots of negative numbers when numerically diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index cc51417f08b..3e4a3fe7640 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -945,6 +945,17 @@ def __init__(self): mathematica='Surd', maple='surd')) + def _print_latex_(self, base, exp): + r""" + EXAMPLES:: + + sage: latex(real_nth_root(x, 3)) + x^{\frac{1}{3}} + sage: latex(real_nth_root(x^2 + x, 3)) + {\left(x^{2} + x\right)}^{\frac{1}{3}} + """ + return latex(base**(1/exp)) + def _evalf_(self, base, exp, parent=None): """ EXAMPLES:: @@ -1028,6 +1039,16 @@ def _derivative_(self, base, exp, diff_param=None): sage: f = real_nth_root(-x, 4) sage: f.diff() -1/4*real_nth_root(-1/x^3, 4) + + :: + + sage: f = real_nth_root(x,3) + sage: f.diff() + 1/3*real_nth_root(x^(-2), 3) + sage: f.integrate(x) + integrate(abs(x)^(1/3)*sgn(x), x) + sage: _.diff() + abs(x)^(1/3)*sgn(x) """ return 1/exp * self(base, exp)**(1-exp) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 19da8508d27..0a05974e406 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -126,7 +126,7 @@ Test if :trac:`9947` is fixed:: Check the fix for :trac:`25251` and :trac:`25252`:: - sage: e1 = sqrt(2)*I - sqrt(2) - 2 + sage: e1 = sqrt(2)*I - sqrt(2) - 2 sage: e2 = sqrt(2) sage: e1 * e2 sqrt(2)*((I - 1)*sqrt(2) - 2) @@ -11743,11 +11743,11 @@ cdef class Expression(CommutativeRingElement): sage: sin(x).find_root(-1,1) 0.0 - This example was fixed along with :trac:`4942` - + This example was fixed along with :trac:`4942` - there was an error in the example pi is a root for tan(x), but an asymptote to 1/tan(x) added an example to show handling of both cases:: - + sage: (tan(x)).find_root(3,3.5) 3.1415926535... sage: (1/tan(x)).find_root(3, 3.5) From 15ec29f4f1ff1bdc9f554ddb119be19c1c1b8be8 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Thu, 18 Jun 2020 21:53:14 -0700 Subject: [PATCH 069/204] trac 29897: fix database_stein_watkins_mini installation --- build/pkgs/database_stein_watkins_mini/spkg-install.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) mode change 100644 => 120000 build/pkgs/database_stein_watkins_mini/spkg-install.in diff --git a/build/pkgs/database_stein_watkins_mini/spkg-install.in b/build/pkgs/database_stein_watkins_mini/spkg-install.in deleted file mode 100644 index 4d0c0e13d25..00000000000 --- a/build/pkgs/database_stein_watkins_mini/spkg-install.in +++ /dev/null @@ -1,2 +0,0 @@ -# Use spkg-install file of database_stein_watkins -. "$SAGE_ROOT/build/pkgs/database_stein_watkins/spkg-install" diff --git a/build/pkgs/database_stein_watkins_mini/spkg-install.in b/build/pkgs/database_stein_watkins_mini/spkg-install.in new file mode 120000 index 00000000000..d7005d17fc4 --- /dev/null +++ b/build/pkgs/database_stein_watkins_mini/spkg-install.in @@ -0,0 +1 @@ +../database_stein_watkins/spkg-install.in \ No newline at end of file From 4e5a9197f025c24f2ce42d66d0b9543225a50a0a Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Fri, 19 Jun 2020 10:36:01 -0700 Subject: [PATCH 070/204] revision of fusion_labels --- .../combinat/root_system/weyl_characters.py | 102 +++++++++++------- 1 file changed, 61 insertions(+), 41 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 7b2e5ee6999..02678db2451 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -2241,30 +2241,40 @@ class FusionRing(WeylCharacterRing): weights of the basis elements, then assign new labels to them:: sage: B22 = FusionRing("B2", 2) - sage: basis = sorted(B22.basis(), key=str) - sage: basis - [B22(0,0), B22(0,1), B22(0,2), B22(1,0), B22(1,1), B22(2,0)] - sage: [x.highest_weight() for x in basis] - [(0, 0), (1/2, 1/2), (1, 1), (1, 0), (3/2, 1/2), (2, 0)] - sage: B22.fusion_labels(['1','X','Y2','Y1','Xp','Z']) - sage: relabeled_basis = sorted(B22.basis(), key=str) - sage: relabeled_basis - [1, X, Xp, Y1, Y2, Z] - sage: [(x, x.highest_weight()) for x in relabeled_basis] - [(1, (0, 0)), + sage: b = [B22(x) for x in B22.get_order()]; b + [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] + sage: [x.highest_weight() for x in b] + [(0, 0), (1, 0), (1/2, 1/2), (2, 0), (3/2, 1/2), (1, 1)] + sage: B22.fusion_labels(['I0','Y1','X','Z','Xp','Y2']) + sage: b = [B22(x) for x in B22.get_order()]; b + [I0, Y1, X, Z, Xp, Y2] + sage: [(x, x.highest_weight()) for x in b] + [(I0, (0, 0)), + (Y1, (1, 0)), (X, (1/2, 1/2)), + (Z, (2, 0)), (Xp, (3/2, 1/2)), - (Y1, (1, 0)), - (Y2, (1, 1)), - (Z, (2, 0))] + (Y2, (1, 1))] sage: X*Y1 X + Xp sage: Z*Z - 1 + I0 + + A fixed order of the basis keys is avalailable with :meth:`get_order`. + This is the order used by methods such as :meth:`s_matrix`. + You may use :meth:`set_order` to reorder the basis:: + + sage: B22.set_order([x.highest_weight() for x in [I0,Y1,Y2,X,Xp,Z]]) + sage: [B22(x) for x in B22.get_order()] + [I0, Y1, Y2, X, Xp, Z] + + To reset the labels and the order to their defaults, + you may run `fusion_labels` with no parameter: + + sage: B22.fusion_labels() + sage: [B22(x) for x in B22.get_order()] + [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] - sage: C22 = FusionRing("C2", 2) - sage: sorted(C22.basis(), key=str) - [C22(0,0), C22(0,1), C22(0,2), C22(1,0), C22(1,1), C22(2,0)] REFERENCES: @@ -2335,9 +2345,22 @@ def _element_constructor(self,weight): def get_order(self): """ - This duplicates the functionality inherited from combinat.free_module - but it is not cached. The caching of get_order causes inconsistent - results after calling set_order. + Returns the weights of the basis vectors in a fixed order. + You may change the order of the basis using :meth:`set_order` + + EXAMPLES:: + + sage: A14=FusionRing("A1",4) + sage: w = A14.get_order(); w + [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2)] + sage: A14.set_order([w[k] for k in [0,4,1,3,2]]) + sage: [A14(x) for x in A14.get_order()] + [A14(0), A14(4), A14(1), A14(3), A14(2)] + + This duplicates :meth:`get_order` from `combinat.free_module`. + However unlike the `combinat.free_module` method with the same + name this `get_order` is not cached. Caching of get_order causes + inconsistent results after calling `set_order`. """ if self._order is None: self.set_order(self.basis().keys().list()) @@ -2468,31 +2491,27 @@ def s_matrix(self): b = self.basis() return matrix([[self.s_ij(b[x],b[y]) for x in self.get_order()] for y in self.get_order()]) - def fusion_labels(self, labels=None, key=str): + def fusion_labels(self, labels=None): """ Set the labels of the basis. INPUT: - - ``labels`` -- (default: ``None``) a list of strings - - ``key`` -- (default: ``str``) key to use to sort basis + - ``labels`` -- (default: ``None``) a list of strings or string - The length of the list ``labels`` must equal the + If ``labels`` is a list, the length of the list must equal the number of basis elements. These become the names of - the basis elements. If ``labels`` is ``None``, then - this resets the labels to the default. + the basis elements. + + If ``labels`` is a string, this is treated as a prefix and a + list of names is generated. - Note that the basis is stored as unsorted data, so to obtain - consistent results, it should be sorted when applying - labels. The argument ``key`` (default ``str``) specifies how - to sort the basis. If you call this with ``key=None``, then no - sorting is done. This may lead to random results, at least - with Python 3. + If ``labels`` is ``None``, then this resets the labels to the default. EXAMPLES:: sage: A13 = FusionRing("A1", 3) - sage: A13.fusion_labels(['x0','x1','x2','x3']) + sage: A13.fusion_labels("x") sage: fb = list(A13.basis()); fb [x0, x1, x2, x3] sage: Matrix([[x*y for y in A13.basis()] for x in A13.basis()]) @@ -2508,18 +2527,19 @@ def fusion_labels(self, labels=None, key=str): [A13(0), A13(1), A13(2), A13(3)] """ if labels is None: + self._order = None self._fusion_labels = None return + elif type(labels) == str: + labels = [labels+"%d"%k for k in range(len(self.basis()))] + elif len(labels) != len(self.basis()): + raise ValueError('invalid data') d = {} - if key: - fb = sorted(self.basis(), key=key) - else: - fb = list(self.basis()) + fb = list(self.get_order()) for j, b in enumerate(fb): - wt = b.highest_weight() - t = tuple([wt.inner_product(x) for x in self.simple_coroots()]) + t = tuple([b.inner_product(x) for x in self.simple_coroots()]) d[t] = labels[j] - inject_variable(labels[j], b) + inject_variable(labels[j], self(b)) self._fusion_labels = d class Element(WeylCharacterRing.Element): From 94ec3c934e855a92b698f58ab169f0f29b8c9808 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Tue, 23 Feb 2016 23:18:31 +0100 Subject: [PATCH 071/204] Add sage --package update-latest command --- build/sage_bootstrap/app.py | 62 ++++++++++++++++++++-- build/sage_bootstrap/cmdline.py | 57 +++++++++++++++++++- build/sage_bootstrap/expand_class.py | 63 ++++++++++++++++++++++ build/sage_bootstrap/fileserver.py | 58 +++++++++++++++++++++ build/sage_bootstrap/package.py | 25 +++++++-- build/sage_bootstrap/pypi.py | 78 ++++++++++++++++++++++++++++ 6 files changed, 335 insertions(+), 8 deletions(-) create mode 100644 build/sage_bootstrap/expand_class.py create mode 100644 build/sage_bootstrap/fileserver.py create mode 100644 build/sage_bootstrap/pypi.py diff --git a/build/sage_bootstrap/app.py b/build/sage_bootstrap/app.py index 30a4d90c917..a3191b05e2b 100644 --- a/build/sage_bootstrap/app.py +++ b/build/sage_bootstrap/app.py @@ -14,6 +14,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** + import os import logging log = logging.getLogger() @@ -22,6 +23,9 @@ from sage_bootstrap.tarball import Tarball from sage_bootstrap.updater import ChecksumUpdater, PackageUpdater from sage_bootstrap.creator import PackageCreator +from sage_bootstrap.pypi import PyPiVersion, PyPiNotFound +from sage_bootstrap.fileserver import FileServer +from sage_bootstrap.expand_class import PackageClass class Application(object): @@ -39,7 +43,7 @@ def config(self): from sage_bootstrap.config import Configuration print(Configuration()) - def list(self): + def list_cls(self, package_class): """ Print a list of all available packages @@ -52,8 +56,9 @@ def list(self): zn_poly """ log.debug('Listing packages') - for pkg in Package.all(): - print(pkg.name) + pc = PackageClass(package_class) + for pkg_name in pc.names: + print(pkg_name) def name(self, tarball_filename): """ @@ -113,6 +118,32 @@ def update(self, package_name, new_version, url=None): update.download_upstream(url) update.fix_checksum() + def update_latest(self, package_name): + """ + Update a package to the latest version. This modifies the Sage sources. + """ + try: + pypi = PyPiVersion(package_name) + except PyPiNotFound: + log.debug('%s is not a pypi package', package_name) + return + else: + pypi.update() + + def update_latest_all(self): + log.debug('Attempting to update all packages') + exclude = [ + 'atlas', 'flint', 'bzip2', 'ecm', 'freetype', 'gap', 'glpk', 'graphs', + 'iconv', 'patch', 'r', 'configure', 'bliss', 'readline', 'decorator', + 'igraph', 'rw', 'planarity', 'gambit', + ] + pc = PackageClass(':standard:') + for package_name in pc.names: + if package_name in exclude: + log.debug('skipping %s because of pypi name collision', package_name) + continue + self.update_latest(package_name) + def download(self, package_name, allow_upstream=False): """ Download a package @@ -126,6 +157,31 @@ def download(self, package_name, allow_upstream=False): package.tarball.download(allow_upstream=allow_upstream) print(package.tarball.upstream_fqn) + def download_cls(self, package_name_or_class): + pc = PackageClass(package_name_or_class) + pc.apply(self.download) + + def upload(self, package_name): + """ + Upload a package to the Sage mirror network + + $ sage --package upload pari + Uploading /home/vbraun/Code/sage.git/upstream/pari-2.8-2044-g89b0f1e.tar.gz + """ + package = Package(package_name) + if not os.path.exists(package.tarball.upstream_fqn): + log.debug('Skipping %s because there is no local tarbal', package_name) + return + log.info('Uploading %s', package.tarball.upstream_fqn) + fs = FileServer() + fs.upload(package) + + def upload_cls(self, package_name_or_class): + pc = PackageClass(package_name_or_class) + pc.apply(self.upload) + fs = FileServer() + fs.publish() + def fix_all_checksums(self): """ Fix the checksum of a package diff --git a/build/sage_bootstrap/cmdline.py b/build/sage_bootstrap/cmdline.py index 48dc0d9c3fe..35ee0b6efeb 100644 --- a/build/sage_bootstrap/cmdline.py +++ b/build/sage_bootstrap/cmdline.py @@ -69,6 +69,13 @@ autotools [...] zn_poly + + $ sage --package list :standard: | sort + arb + atlas + backports_ssl_match_hostname + [...] + zn_poly """ @@ -115,6 +122,16 @@ """ +epilog_update_latest = \ +""" +Update a package to the latest version. This modifies the Sage sources. + +EXAMPLE: + + $ sage --package update-latest ipython +""" + + epilog_download = \ """ Download the tarball for a package and print the filename to stdout @@ -127,6 +144,17 @@ """ +epilog_upload = \ +""" +Upload the tarball to the Sage mirror network (requires ssh key authentication) + +EXAMPLE: + + $ sage --package upload pari + Uploading /home/vbraun/Code/sage.git/upstream/pari-2.8-2044-g89b0f1e.tar.gz +""" + + epilog_fix_checksum = \ """ Fix the checksum of a package @@ -170,6 +198,10 @@ def make_parser(): 'list', epilog=epilog_list, formatter_class=argparse.RawDescriptionHelpFormatter, help='Print a list of all available packages') + parser_list.add_argument( + 'package_class', + type=str, default=':all:', nargs='?', + help='Package class like :all: (default) or :standard:') parser_name = subparsers.add_parser( 'name', epilog=epilog_name, @@ -202,15 +234,29 @@ def make_parser(): parser_update.add_argument( '--url', type=str, default=None, help='Download URL') + parser_update_latest = subparsers.add_parser( + 'update-latest', epilog=epilog_update_latest, + formatter_class=argparse.RawDescriptionHelpFormatter, + help='Update a package to the latest version. This modifies the Sage sources.') + parser_update_latest.add_argument( + 'package_name', type=str, help='Package name (:all: for all packages)') + parser_download = subparsers.add_parser( 'download', epilog=epilog_download, formatter_class=argparse.RawDescriptionHelpFormatter, help='Download tarball') parser_download.add_argument( - 'package_name', type=str, help='Package name') + 'package_name', type=str, help='Package name or :type:') parser_download.add_argument( '--allow-upstream', action="store_true", help='Whether to fall back to downloading from the upstream URL') + + parser_upload = subparsers.add_parser( + 'upload', epilog=epilog_upload, + formatter_class=argparse.RawDescriptionHelpFormatter, + help='Upload tarball to Sage mirrors') + parser_upload.add_argument( + 'package_name', type=str, help='Package name or :type:') parser_fix_checksum = subparsers.add_parser( 'fix-checksum', epilog=epilog_fix_checksum, @@ -254,7 +300,7 @@ def run(): if args.subcommand == 'config': app.config() elif args.subcommand == 'list': - app.list() + app.list_cls(args.package_class) elif args.subcommand == 'name': app.name(args.tarball_filename) elif args.subcommand == 'tarball': @@ -263,10 +309,17 @@ def run(): app.apropos(args.incorrect_name) elif args.subcommand == 'update': app.update(args.package_name, args.new_version, url=args.url) + elif args.subcommand == 'update-latest': + if args.package_name == ':all:': + app.update_latest_all() + else: + app.update_latest(args.package_name) elif args.subcommand == 'download': app.download(args.package_name, args.allow_upstream) elif args.subcommand == 'create': app.create(args.package_name, args.version, args.tarball, args.type, args.url) + elif args.subcommand == 'upload': + app.upload_cls(args.package_name) elif args.subcommand == 'fix-checksum': if args.package_name is None: app.fix_all_checksums() diff --git a/build/sage_bootstrap/expand_class.py b/build/sage_bootstrap/expand_class.py new file mode 100644 index 00000000000..a666ec7ae93 --- /dev/null +++ b/build/sage_bootstrap/expand_class.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +""" +Utility to specify classes of packages like `:all:` +""" + + +#***************************************************************************** +# Copyright (C) 2016 Volker Braun +# +# 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/ +#***************************************************************************** + + +import os +import sys +import logging +log = logging.getLogger() + +from sage_bootstrap.package import Package + + +class PackageClass(object): + + def __init__(self, package_name_or_class): + if package_name_or_class == ':all:': + self._init_all() + elif package_name_or_class == ':standard:': + self._init_standard() + elif package_name_or_class == ':optional:': + self._init_optional() + elif package_name_or_class == ':experimental:': + self._init_experimental() + elif package_name_or_class == ':huge:': + self._init_huge() + else: + if package_name_or_class.startswith(':'): + raise ValueError('Package name cannot start with ":", got %s', package_name_or_class) + if package_name_or_class.endswith(':'): + raise ValueError('Package name cannot end with ":", got %s', package_name_or_class) + self.names = [package_name_or_class] + + def _init_all(self): + self.names = [pkg.name for pkg in Package.all()] + + def _init_standard(self): + self.names = [pkg.name for pkg in Package.all() if pkg.type == 'standard'] + + def _init_optional(self): + self.names = [pkg.name for pkg in Package.all() if pkg.type == 'optional'] + + def _init_experimental(self): + self.names = [pkg.name for pkg in Package.all() if pkg.type == 'experimental'] + + def _init_huge(self): + self.names = [pkg.name for pkg in Package.all() if pkg.type == 'huge'] + + def apply(self, function, *args, **kwds): + for package_name in self.names: + function(package_name, *args, **kwds) diff --git a/build/sage_bootstrap/fileserver.py b/build/sage_bootstrap/fileserver.py new file mode 100644 index 00000000000..df6c20ee35e --- /dev/null +++ b/build/sage_bootstrap/fileserver.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +""" +Interface to the Sage fileserver +""" + + +#***************************************************************************** +# Copyright (C) 2016 Volker Braun +# +# 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/ +#***************************************************************************** + + + +import os +import subprocess + + + +class FileServer(object): + + def __init__(self): + self.user = 'sagemath' + self.hostname = 'fileserver.sagemath.org' + + def upstream_directory(self, package): + """ + Return the directory where the tarball resides on the server + """ + return os.path.join( + '/', 'data', 'files', 'spkg', 'upstream', package.name, + ) + + def upload(self, package): + """ + Upload the current tarball of package + """ + subprocess.check_call([ + 'ssh', 'sagemath@fileserver.sagemath.org', + 'mkdir -p {0} && touch {0}/index.html'.format(self.upstream_directory(package)) + ]) + subprocess.check_call([ + 'rsync', '-av', '--checksum', '-e', 'ssh -l sagemath', + package.tarball.upstream_fqn, + 'fileserver.sagemath.org:{0}'.format(self.upstream_directory(package)) + ]) + + def publish(self): + """ + Publish the files + """ + subprocess.check_call([ + 'ssh', 'sagemath@fileserver.sagemath.org', './publish-files.sh' + ]) diff --git a/build/sage_bootstrap/package.py b/build/sage_bootstrap/package.py index 9d103db7331..bbd04fab36a 100644 --- a/build/sage_bootstrap/package.py +++ b/build/sage_bootstrap/package.py @@ -45,6 +45,7 @@ def __init__(self, package_name): self.__tarball = None self._init_checksum() self._init_version() + self._init_type() def __repr__(self): return 'Package {0}'.format(self.name) @@ -217,6 +218,13 @@ def patchlevel(self): """ return self.__patchlevel + @property + def type(self): + """ + Return the package type + """ + return self.__type + def __eq__(self, other): return self.tarball == other.tarball @@ -227,10 +235,15 @@ def all(cls): """ base = os.path.join(SAGE_ROOT, 'build', 'pkgs') for subdir in os.listdir(base): - path = os.path.join(base, subdir) + path = os.path.join(base, subdir) if not os.path.isfile(os.path.join(path, "checksums.ini")): + log.debug('%s has no checksums.ini', subdir) continue - yield cls(subdir) + try: + yield cls(subdir) + except BaseException: + log.error('Failed to open %s', subdir) + raise @property def path(self): @@ -274,4 +287,10 @@ def _init_version(self): self.__version = match.group('version') self.__patchlevel = int(match.group('patchlevel')) - + def _init_type(self): + with open(os.path.join(self.path, 'type')) as f: + package_type = f.read().strip() + assert package_type in [ + 'base', 'standard', 'optional', 'experimental', 'script', 'pip' + ] + self.__type = package_type diff --git a/build/sage_bootstrap/pypi.py b/build/sage_bootstrap/pypi.py new file mode 100644 index 00000000000..7b17df3a58e --- /dev/null +++ b/build/sage_bootstrap/pypi.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +""" +PyPi Version Information +""" + + +#***************************************************************************** +# Copyright (C) 2016 Volker Braun +# +# 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/ +#***************************************************************************** + +import logging +log = logging.getLogger() + +import json + +from sage_bootstrap.package import Package +from sage_bootstrap.updater import PackageUpdater +from sage_bootstrap.compat import urllib + + +class PyPiNotFound(Exception): + pass + + +class PyPiError(Exception): + pass + + +class PyPiVersion(object): + + def __init__(self, package_name): + self.name = package_name + self.json = self._get_json() + + def _get_json(self): + response = urllib.urlopen(self.json_url) + if response.getcode() != 200: + raise PyPiNotFound('%s not on pypi', self.name) + data = response.read() + text = data.decode('utf-8') + return json.loads(text) + + @property + def json_url(self): + return 'https://pypi.python.org/pypi/{0}/json'.format(self.name) + + @property + def version(self): + """ + Return the current version + """ + return self.json['info']['version'] + + @property + def url(self): + """ + Return the source url + """ + for download in self.json['urls']: + if download['python_version'] == 'source': + return download['url'] + raise PyPiError('No source url for %s found', self.name) + + def update(self): + package = Package(self.name) + if package.version == self.version: + log.info('%s is already at the latest version', self.name) + return + log.info('Updating %s: %s -> %s', self.name, package.version, self.version) + update = PackageUpdater(self.name, self.version) + update.download_upstream(self.url) + update.fix_checksum() From cb7a2918014f1f4634e00536b457c7c93c619130 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 19 Jun 2020 11:39:25 -0700 Subject: [PATCH 072/204] Fixup rebase --- build/sage_bootstrap/cmdline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/sage_bootstrap/cmdline.py b/build/sage_bootstrap/cmdline.py index 35ee0b6efeb..117d02c79a3 100644 --- a/build/sage_bootstrap/cmdline.py +++ b/build/sage_bootstrap/cmdline.py @@ -315,7 +315,7 @@ def run(): else: app.update_latest(args.package_name) elif args.subcommand == 'download': - app.download(args.package_name, args.allow_upstream) + app.download_cls(args.package_name, args.allow_upstream) elif args.subcommand == 'create': app.create(args.package_name, args.version, args.tarball, args.type, args.url) elif args.subcommand == 'upload': From 0bdbe49fe89ac458acdd4c3614fd1d44b7c0142f Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 19 Jun 2020 22:30:59 +0200 Subject: [PATCH 073/204] test suites for the library --- src/sage/geometry/polyhedron/library.py | 43 ++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/library.py b/src/sage/geometry/polyhedron/library.py index 7d2a1b8de20..80b9e344ece 100644 --- a/src/sage/geometry/polyhedron/library.py +++ b/src/sage/geometry/polyhedron/library.py @@ -564,6 +564,8 @@ def regular_polygon(self, n, exact=True, base_ring=None, backend=None): 8 sage: octagon.volume() # optional - pynormaliz 2*a + sage: TestSuite(octagon).run() + sage: TestSuite(polytopes.regular_polygon(5, exact=False)).run() """ n = ZZ(n) if n <= 2: @@ -628,6 +630,7 @@ def Birkhoff_polytope(self, n, backend=None): sage: b4norm = polytopes.Birkhoff_polytope(4,backend='normaliz') # optional - pynormaliz sage: TestSuite(b4norm).run() # optional - pynormaliz + sage: TestSuite(polytopes.Birkhoff_polytope(3)).run() """ from itertools import permutations verts = [] @@ -702,6 +705,7 @@ def simplex(self, dim=3, project=False, base_ring=None, backend=None): sage: s6norm = polytopes.simplex(6,backend='normaliz') # optional - pynormaliz sage: TestSuite(s6norm).run() # optional - pynormaliz + sage: TestSuite(polytopes.simplex(5)).run() """ verts = list((ZZ**(dim + 1)).basis()) if project: @@ -768,6 +772,9 @@ def icosahedron(self, exact=True, base_ring=None, backend=None): (1, 12, 30, 20, 1) sage: ico.volume() # optional - pynormaliz 5/12*sqrt5 + 5/4 + sage: TestSuite(ico).run() # optional - pynormaliz + sage: ico = polytopes.icosahedron(exact=False) + sage: TestSuite(ico).run() """ if base_ring is None and exact: @@ -833,6 +840,7 @@ def dodecahedron(self, exact=True, base_ring=None, backend=None): sage: d12 = polytopes.dodecahedron(backend='normaliz') # optional - pynormaliz sage: d12.f_vector() # optional - pynormaliz (1, 20, 30, 12, 1) + sage: TestSuite(d12).run() # optional - pynormaliz """ return self.icosahedron(exact=exact, base_ring=base_ring, backend=backend).polar() @@ -887,6 +895,7 @@ def small_rhombicuboctahedron(self, exact=True, base_ring=None, backend=None): (1, 24, 48, 26, 1) sage: sr.volume() # optional - pynormaliz 80/3*sqrt2 + 32 + sage: TestSuite(sr).run() # optional - pynormaliz """ if base_ring is None and exact: from sage.rings.number_field.number_field import QuadraticField @@ -1103,6 +1112,7 @@ def truncated_cube(self, exact=True, base_ring=None, backend=None): sage: co = polytopes.truncated_cube(backend='normaliz') # optional - pynormaliz sage: co.f_vector() # optional - pynormaliz (1, 24, 36, 14, 1) + sage: TestSuite(co).run() # optional - pynormaliz """ if base_ring is None and exact: @@ -1480,14 +1490,16 @@ def icosidodecahedron(self, exact=True, backend=None): TESTS:: - sage: polytopes.icosidodecahedron(exact=False) + sage: id = polytopes.icosidodecahedron(exact=False); id A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 30 vertices + sage: TestSuite(id).run() sage: id = polytopes.icosidodecahedron(backend='normaliz') # optional - pynormaliz sage: id.f_vector() # optional - pynormaliz (1, 30, 60, 32, 1) sage: id.base_ring() # optional - pynormaliz Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790? + sage: TestSuite(id).run() # optional - pynormaliz """ from sage.rings.number_field.number_field import QuadraticField from itertools import product @@ -1560,7 +1572,7 @@ def icosidodecahedron_V2(self, exact=True, base_ring=None, backend=None): (1, 30, 60, 32, 1) sage: id.base_ring() # optional - pynormaliz Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790? - + sage: TestSuite(id).run() # optional - pynormaliz """ if base_ring is None and exact: from sage.rings.number_field.number_field import QuadraticField @@ -2464,12 +2476,14 @@ def hypersimplex(self, dim, k, project=False, backend=None): (1, 6, 12, 8, 1) sage: h_4_2.ehrhart_polynomial() # optional - latte_int 2/3*t^3 + 2*t^2 + 7/3*t + 1 + sage: TestSuite(h_4_2).run() sage: h_7_3 = polytopes.hypersimplex(7, 3, project=True) sage: h_7_3 A 6-dimensional polyhedron in RDF^6 defined as the convex hull of 35 vertices sage: h_7_3.f_vector() (1, 35, 210, 350, 245, 84, 14, 1) + sage: TestSuite(h_7_3).run() """ verts = Permutations([0] * (dim - k) + [1] * k).list() if project: @@ -2978,7 +2992,7 @@ def one_hundred_twenty_cell(self, exact=True, backend=None, construction='coxete The ``'normaliz'`` is faster:: - sage: polytopes.one_hundred_twenty_cell(backend='normaliz') # optional - pynormaliz + sage: P = polytopes.one_hundred_twenty_cell(backend='normaliz'); P # optional - pynormaliz A 4-dimensional polyhedron in (Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^4 defined as the convex hull of 600 vertices @@ -2987,6 +3001,10 @@ def one_hundred_twenty_cell(self, exact=True, backend=None, construction='coxete sage: polytopes.one_hundred_twenty_cell(backend='normaliz',construction='as_permutahedron') # not tested - long time A 4-dimensional polyhedron in AA^4 defined as the convex hull of 600 vertices + + TESTS:: + + sage: TestSuite(P).run() # optional - pynormaliz """ if construction == 'coxeter': if not exact: @@ -3101,6 +3119,14 @@ def hypercube(self, dim, intervals=None, backend=None): sage: fc = polytopes.hypercube(4,backend='normaliz') # optional - pynormaliz sage: TestSuite(fc).run() # optional - pynormaliz + :: + + sage: set_random_seed() + sage: ls = [randint(-100,100) for _ in range(4)] + sage: intervals = [[x, x+randint(1,50)] for x in ls] + sage: P = polytopes.hypercube(4, intervals, backend='field') + sage: TestSuite(P).run() + If the dimension ``dim`` is not equal to the length of intervals, an error is raised:: @@ -3287,6 +3313,11 @@ def cross_polytope(self, dim, backend=None): sage: cp = polytopes.cross_polytope(4,backend='normaliz') # optional - pynormaliz sage: TestSuite(cp).run() # optional - pynormaliz + :: + + sage: P = polytopes.cross_polytope(6, backend='field') + sage: TestSuite(P).run() + Check that double description is set up correctly:: sage: P = polytopes.cross_polytope(6, backend='ppl') @@ -3322,10 +3353,14 @@ def parallelotope(self, generators, backend=None): sage: K = QuadraticField(2, 'sqrt2') sage: sqrt2 = K.gen() - sage: polytopes.parallelotope([ (1,sqrt2), (1,-1) ]) + sage: P = polytopes.parallelotope([ (1,sqrt2), (1,-1) ]); P A 2-dimensional polyhedron in (Number Field in sqrt2 with defining polynomial x^2 - 2 with sqrt2 = 1.414213562373095?)^2 defined as the convex hull of 4 vertices + + TESTS:: + + sage: TestSuite(P).run() """ from sage.modules.free_module_element import vector from sage.structure.sequence import Sequence From c6ea1ec460a5bea9040e8b4dae43f0e3ed2da56d Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 19 Jun 2020 22:47:15 +0200 Subject: [PATCH 074/204] added long time flags --- src/sage/geometry/polyhedron/library.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/polyhedron/library.py b/src/sage/geometry/polyhedron/library.py index d1039f2762b..0b05939f8d1 100644 --- a/src/sage/geometry/polyhedron/library.py +++ b/src/sage/geometry/polyhedron/library.py @@ -2256,7 +2256,7 @@ def six_hundred_cell(self, exact=False, backend=None): TESTS:: sage: p600 = polytopes.six_hundred_cell(exact=True, backend='normaliz') # optional - pynormaliz - sage: len(list(p600.bounded_edges())) # optional - pynormaliz + sage: len(list(p600.bounded_edges())) # optional - pynormaliz, long time 720 """ if exact: @@ -2395,7 +2395,7 @@ def Gosset_3_21(self, backend=None): TESTS:: sage: G321 = polytopes.Gosset_3_21(backend='normaliz') # optional - pynormaliz - sage: TestSuite(G321).run() # optional - pynormaliz + sage: TestSuite(G321).run() # optional - pynormaliz, long time """ from itertools import combinations verts = [] @@ -2625,8 +2625,8 @@ def generalized_permutahedron(self, coxeter_type, point=None, exact=True, regula A vertex at (0.500000000000000?, 0.866025403784439?)) sage: perm_a2_reg.is_inscribed() True - sage: perm_a3_reg = polytopes.generalized_permutahedron(['A',3],regular=True) - sage: perm_a3_reg.is_inscribed() + sage: perm_a3_reg = polytopes.generalized_permutahedron(['A',3],regular=True) # long time + sage: perm_a3_reg.is_inscribed() # long time True The same is possible with vertices in ``RDF``:: @@ -2651,7 +2651,7 @@ def generalized_permutahedron(self, coxeter_type, point=None, exact=True, regula It works also with types with non-rational coordinates:: - sage: perm_b3 = polytopes.generalized_permutahedron(['B',3]); perm_b3 + sage: perm_b3 = polytopes.generalized_permutahedron(['B',3]); perm_b3 # long time A 3-dimensional polyhedron in (Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095?)^3 defined as the convex hull of 48 vertices sage: perm_b3_reg = polytopes.generalized_permutahedron(['B',3],regular=True); perm_b3_reg # not tested - long time (12sec on 64 bits). @@ -2669,8 +2669,8 @@ def generalized_permutahedron(self, coxeter_type, point=None, exact=True, regula sage: perm_h3 = polytopes.generalized_permutahedron(['H',3],backend='normaliz') # optional - pynormaliz sage: perm_h3 # optional - pynormaliz A 3-dimensional polyhedron in (Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790?)^3 defined as the convex hull of 120 vertices - sage: perm_f4 = polytopes.generalized_permutahedron(['F',4],backend='normaliz') # optional - pynormaliz - sage: perm_f4 # optional - pynormaliz + sage: perm_f4 = polytopes.generalized_permutahedron(['F',4],backend='normaliz') # optional - pynormaliz, long time + sage: perm_f4 # optional - pynormaliz, long time A 4-dimensional polyhedron in (Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095?)^4 defined as the convex hull of 1152 vertices .. SEEALSO:: @@ -3004,7 +3004,7 @@ def one_hundred_twenty_cell(self, exact=True, backend=None, construction='coxete TESTS:: - sage: TestSuite(P).run() # optional - pynormaliz + sage: TestSuite(P).run() # optional - pynormaliz, long time """ if construction == 'coxeter': if not exact: From 5d79b2ba522c2f44aba5f5acbf3e5336b0b5f987 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Fri, 19 Jun 2020 23:40:03 +0200 Subject: [PATCH 075/204] some more testsuites --- src/sage/geometry/polyhedron/base.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 8e82e941682..6eacba5f1a5 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -113,6 +113,31 @@ class Polyhedron_base(Element): sage: p = Polyhedron() sage: TestSuite(p).run() + + :: + + sage: p = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)], base_ring=ZZ) + sage: TestSuite(p).run() + + :: + + sage: p=polytopes.flow_polytope(digraphs.DeBruijn(3,2)) + sage: TestSuite(p).run() + + :: + + sage: TestSuite(Polyhedron([[]])).run() + sage: TestSuite(Polyhedron([[0]])).run() + + :: + + sage: P = polytopes.permutahedron(3) * Polyhedron(rays=[[0,0,1],[0,1,1],[1,2,3]]) + sage: TestSuite(P).run() + + :: + + sage: P = polytopes.permutahedron(3)*Polyhedron(rays=[[0,0,1],[0,1,1]], lines=[[1,0,0]]) + sage: TestSuite(P).run() """ def __init__(self, parent, Vrep, Hrep, Vrep_minimal=None, Hrep_minimal=None, pref_rep=None, **kwds): From af2ba1f27e0bcfd1e46b4de0787d0b283c46cbac Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Sat, 20 Jun 2020 00:03:20 +0200 Subject: [PATCH 076/204] test suite for an_affine_basis --- src/sage/geometry/polyhedron/base.py | 41 ++++++++++++---------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index 6eacba5f1a5..4f74a0c3bb1 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -2078,30 +2078,6 @@ def an_affine_basis(self): Traceback (most recent call last): ... NotImplementedError: this function is not implemented for unbounded polyhedra - - TESTS: - - Checking for various inputs, that this actually works:: - - sage: def test_affine_basis(P): - ....: b = P.an_affine_basis() - ....: m = Matrix(b).transpose().stack(Matrix([[1]*len(b)])) - ....: assert m.rank() == P.dim() + 1 - ....: - sage: test_affine_basis(polytopes.permutahedron(5)) - sage: test_affine_basis(polytopes.Birkhoff_polytope(4)) - sage: test_affine_basis(polytopes.hypercube(6)) - sage: test_affine_basis(polytopes.dodecahedron()) - sage: test_affine_basis(polytopes.cross_polytope(5)) - - Small-dimensional cases: - - sage: Polyhedron([[1]]).an_affine_basis() - [A vertex at (1)] - sage: Polyhedron([[]]).an_affine_basis() - [A vertex at ()] - sage: Polyhedron().an_affine_basis() - [] """ if not self.is_compact(): raise NotImplementedError("this function is not implemented for unbounded polyhedra") @@ -2132,6 +2108,23 @@ def an_affine_basis(self): return [self.Vrepresentation()[i] for i in basis_indices] + def _test_an_affine_basis(self, tester=None, **options): + """ + Run tests on the method :meth:`.an_affine_basis` + + TESTS:: + + sage: polytopes.cross_polytope(3)._test_an_affine_basis() + """ + if tester is None: + tester = self._tester(**options) + if self.is_compact(): + b = self.an_affine_basis() + m = matrix(b).transpose().stack(matrix([[1]*len(b)])) + tester.assertEqual(m.rank(), self.dim() + 1) + for v in b: + tester.assertIn(v, self.vertices()) + def ray_generator(self): """ Return a generator for the rays of the polyhedron. From 1c2415eb958b8bf835ca29c1bf00b7f55242e420 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 19 Jun 2020 17:31:27 -0700 Subject: [PATCH 077/204] build/pkgs/tox: New --- build/pkgs/tox/requirements.txt | 1 + build/pkgs/tox/type | 1 + 2 files changed, 2 insertions(+) create mode 100644 build/pkgs/tox/requirements.txt create mode 100644 build/pkgs/tox/type diff --git a/build/pkgs/tox/requirements.txt b/build/pkgs/tox/requirements.txt new file mode 100644 index 00000000000..053148f8486 --- /dev/null +++ b/build/pkgs/tox/requirements.txt @@ -0,0 +1 @@ +tox diff --git a/build/pkgs/tox/type b/build/pkgs/tox/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/tox/type @@ -0,0 +1 @@ +optional From 21f8162c82131a6d5f10999ceb5a20fbe8e47751 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 19 Jun 2020 20:50:29 -0700 Subject: [PATCH 078/204] sage.rings.finite_rings.element_givaro: Remove dependency on NTL --- src/sage/rings/finite_rings/element_givaro.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index 47629f601ac..f8577166796 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -1,4 +1,4 @@ -# distutils: libraries = givaro ntl gmp m +# distutils: libraries = givaro gmp m # distutils: language = c++ r""" Givaro Field Elements @@ -54,7 +54,6 @@ AUTHORS: from cysignals.signals cimport sig_on, sig_off -include "sage/libs/ntl/decl.pxi" from cypari2.paridecl cimport * from sage.misc.randstate cimport randstate, current_randstate From 3f5abedf9706ff2c73872777722f327808301230 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 20 Jun 2020 10:02:08 +0200 Subject: [PATCH 079/204] trac #29605: improve alignment in graph_plot.py --- src/sage/graphs/graph_plot.py | 37 +++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/sage/graphs/graph_plot.py b/src/sage/graphs/graph_plot.py index e1b0bf9e79f..02ac6d6e35d 100644 --- a/src/sage/graphs/graph_plot.py +++ b/src/sage/graphs/graph_plot.py @@ -109,18 +109,31 @@ """ 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.', - } + '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() From 83c5003bddc679e671949559342683994a7c527e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 20 Jun 2020 12:29:16 +0200 Subject: [PATCH 080/204] add custom cython code for Moebius matrix and Coxeter matrix of poset --- src/sage/combinat/posets/hasse_cython.pyx | 126 ++++++++++++++++++++++ src/sage/combinat/posets/hasse_diagram.py | 81 +++++++++----- 2 files changed, 183 insertions(+), 24 deletions(-) create mode 100644 src/sage/combinat/posets/hasse_cython.pyx diff --git a/src/sage/combinat/posets/hasse_cython.pyx b/src/sage/combinat/posets/hasse_cython.pyx new file mode 100644 index 00000000000..258ab3d4e73 --- /dev/null +++ b/src/sage/combinat/posets/hasse_cython.pyx @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- +r""" +Some fast computations for finite posets +""" +# **************************************************************************** +# Copyright (C) 2020 Frédéric Chapoton +# +# 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 cysignals.signals cimport sig_str, sig_off + +from sage.libs.flint.fmpz cimport * +from sage.libs.flint.fmpz_mat cimport * + +from sage.rings.integer_ring import ZZ +from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense +from sage.matrix.constructor import Matrix + + +cpdef Matrix_integer_dense moebius_matrix_fast(list positions): + """ + Compute the Möbius matrix of a poset by a specific triangular inversion. + + INPUT: + + a list of sets describing the poset, as given by the + lazy attribute ``_leq_storage`` of Hasse diagrams. + + OUTPUT: + + a dense matrix + + EXAMPLES:: + + sage: from sage.combinat.posets.hasse_cython import moebius_matrix_fast + sage: D = [{0,1},{1}] + sage: moebius_matrix_fast(D) + [ 1 -1] + [ 0 1] + sage: P = posets.TamariLattice(5) + sage: H = P._hasse_diagram + sage: D = H._leq_storage + sage: moebius_matrix_fast(D) + 42 x 42 dense matrix over Integer Ring (...) + """ + cdef fmpz_mat_t M + cdef Matrix_integer_dense A + cdef Py_ssize_t n = len(positions) + cdef Py_ssize_t i + cdef int j, k + sig_str("FLINT exception") + fmpz_mat_init(M, n, n) + sig_off() + fmpz_mat_one(M) + for i in range(n - 1, -1, -1): + for j in positions[i]: + if j != i: + for k in positions[j]: + fmpz_sub(fmpz_mat_entry(M, i, k), + fmpz_mat_entry(M, i, k), + fmpz_mat_entry(M, j, k)) + A = Matrix(ZZ, n, n) + fmpz_mat_set(A._matrix, M) + fmpz_mat_clear(M) + return A + + +cpdef Matrix_integer_dense coxeter_matrix_fast(list positions): + """ + Compute the Coxeter matrix of a poset by a specific algorithm. + + INPUT: + + a list of sets describing the poset, as given by the + lazy attribute ``_leq_storage`` of Hasse diagrams. + + OUTPUT: + + a dense matrix + + EXAMPLES:: + + sage: from sage.combinat.posets.hasse_cython import coxeter_matrix_fast + sage: D = [{0,1},{1}] + sage: coxeter_matrix_fast(D) + [ 0 -1] + [ 1 -1] + sage: P = posets.TamariLattice(5) + sage: H = P._hasse_diagram + sage: D = H._leq_storage + sage: coxeter_matrix_fast(D) + 42 x 42 dense matrix over Integer Ring (...) + """ + cdef fmpz_mat_t M + cdef Matrix_integer_dense A + cdef Py_ssize_t n = len(positions) + cdef Py_ssize_t i + cdef int j, k + sig_str("FLINT exception") + fmpz_mat_init(M, n, n) + sig_off() + fmpz_mat_one(M) + for i in range(n - 1, -1, -1): + for j in positions[i]: + if j != i: + for k in positions[j]: + fmpz_sub(fmpz_mat_entry(M, k, i), + fmpz_mat_entry(M, k, i), + fmpz_mat_entry(M, k, j)) + for i in range(n): + for j in positions[i]: + if j != i: + for k in range(n): + fmpz_add(fmpz_mat_entry(M, i, k), + fmpz_mat_entry(M, i, k), + fmpz_mat_entry(M, j, k)) + fmpz_mat_scalar_mul_si(M, M, -1) + A = Matrix(ZZ, n, n) + fmpz_mat_set(A._matrix, M) + fmpz_mat_clear(M) + return A diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index 082bffcbd3b..8b3dd800451 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -24,6 +24,8 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.misc.cachefunc import cached_method from sage.misc.rest_index_of_methods import gen_rest_table_index +from sage.combinat.posets.hasse_cython import (moebius_matrix_fast, + coxeter_matrix_fast) class LatticeError(ValueError): @@ -956,31 +958,29 @@ def moebius_function(self, i, j): # dumb algorithm self._moebius_function_values[(i, j)] = -sum(self.moebius_function(i, k) for k in ci[:-1]) return self._moebius_function_values[(i, j)] - def moebius_function_matrix(self, algorithm='recursive'): + def moebius_function_matrix(self, algorithm='cython'): r""" Return the matrix of the Möbius function of this poset. - This returns the sparse matrix over `\ZZ` whose ``(x, y)`` entry + This returns the matrix over `\ZZ` whose ``(x, y)`` entry is the value of the Möbius function of ``self`` evaluated on - ``x`` and ``y``, and redefines :meth:`moebius_function` to use - it. + ``x`` and ``y``, and redefines :meth:`moebius_function` to use it. INPUT: - - ``algorithm`` -- optional, ``'recursive'`` (default) or ``'matrix'`` + - ``algorithm`` -- optional, ``'recursive'``, ``'matrix'`` + or ``'cython'`` (default) - This uses either the recursive formula or one matrix inversion. + This uses either the recursive formula, a generic matrix inversion + or a specific matrix inversion coded in Cython. - .. NOTE:: + OUTPUT: - The result is cached in :meth:`_moebius_function_matrix`. + a dense for the algorithm ``cython``, a sparse matrix otherwise - .. TODO:: + .. NOTE:: - Try to make a specific multimodular matrix inversion - algorithm for this kind of sparse triangular matrices - where the non-zero entries of the inverse are in known - positions. + The result is cached in :meth:`_moebius_function_matrix`. .. SEEALSO:: :meth:`lequal_matrix`, :meth:`coxeter_transformation` @@ -1009,12 +1009,23 @@ def moebius_function_matrix(self, algorithm='recursive'): True sage: H = posets.TamariLattice(3)._hasse_diagram - sage: H.moebius_function_matrix('matrix') + sage: M = H.moebius_function_matrix('matrix'); M [ 1 -1 -1 0 1] [ 0 1 0 0 -1] [ 0 0 1 -1 0] [ 0 0 0 1 -1] [ 0 0 0 0 1] + sage: _ = H.__dict__.pop('_moebius_function_matrix') + sage: H.moebius_function_matrix('cython') == M + True + sage: _ = H.__dict__.pop('_moebius_function_matrix') + sage: H.moebius_function_matrix('recursive') == M + True + sage: _ = H.__dict__.pop('_moebius_function_matrix') + sage: H.moebius_function_matrix('banana') + Traceback (most recent call last): + ... + ValueError: unknown algorithm """ if not hasattr(self, '_moebius_function_matrix'): if algorithm == 'recursive': @@ -1032,8 +1043,12 @@ def moebius_function_matrix(self, algorithm='recursive'): for j in available if k in greater_than[j]) M = matrix(ZZ, n, n, m, sparse=True) - else: + elif algorithm == "matrix": M = self.lequal_matrix().inverse_of_unit() + elif algorithm == "cython": + M = moebius_matrix_fast(self._leq_storage) + else: + raise ValueError("unknown algorithm") self._moebius_function_matrix = M self._moebius_function_matrix.set_immutable() self.moebius_function = self._moebius_function_from_matrix @@ -1061,30 +1076,51 @@ def _moebius_function_from_matrix(self, i, j): return self._moebius_function_matrix[i, j] @cached_method - def coxeter_transformation(self): + def coxeter_transformation(self, algorithm='cython'): r""" Return the matrix of the Auslander-Reiten translation acting on the Grothendieck group of the derived category of modules on the poset, in the basis of simple modules. + INPUT: + + - ``algorithm`` -- optional, ``'cython'`` (default) or ``'matrix'`` + + This uses either a specific matrix code in Cython, or generic matrices. + .. SEEALSO:: :meth:`lequal_matrix`, :meth:`moebius_function_matrix` EXAMPLES:: - sage: M = posets.PentagonPoset()._hasse_diagram.coxeter_transformation(); M + sage: P = posets.PentagonPoset()._hasse_diagram + sage: M = P.coxeter_transformation(); M [ 0 0 0 0 -1] [ 0 0 0 1 -1] [ 0 1 0 0 -1] [-1 1 1 0 -1] [-1 1 0 1 -1] + sage: P.__dict__['coxeter_transformation'].clear_cache() + sage: P.coxeter_transformation(algorithm="matrix") == M + True TESTS:: - sage: M = posets.PentagonPoset()._hasse_diagram.coxeter_transformation() + sage: P = posets.PentagonPoset()._hasse_diagram + sage: M = P.coxeter_transformation() sage: M**8 == 1 True + sage: P.__dict__['coxeter_transformation'].clear_cache() + sage: P.coxeter_transformation(algorithm="banana") + Traceback (most recent call last): + ... + ValueError: unknown algorithm """ - return - self.lequal_matrix() * self.moebius_function_matrix().transpose() + if algorithm == 'matrix': + return - self.lequal_matrix() * self.moebius_function_matrix().transpose() + elif algorithm == 'cython': + return coxeter_matrix_fast(self._leq_storage) + else: + raise ValueError("unknown algorithm") def order_filter(self, elements): r""" @@ -1226,12 +1262,9 @@ def _leq_matrix(self): Integer Ring """ n = self.order() - one = ZZ.one() greater_than = self._leq_storage - D = {(i, j): one for i in range(n) for j in greater_than[i]} - M = matrix(ZZ, n, n, D, sparse=True) - M.set_immutable() - return M + D = {(i, j): 1 for i in range(n) for j in greater_than[i]} + return matrix(ZZ, n, n, D, sparse=True, immutable=True) def lequal_matrix(self, boolean=False): r""" From ce8fa70e829d67d105251c325e7547f832755b4d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 20 Jun 2020 08:14:13 -0700 Subject: [PATCH 081/204] sage.structure, sage.misc: Replace import of sage.categories.all by more specific imports --- src/sage/categories/action.pyx | 4 ++-- src/sage/misc/functional.py | 4 ++-- src/sage/misc/nested_class_test.py | 8 ++++---- src/sage/structure/category_object.pyx | 2 +- src/sage/structure/element.pyx | 2 +- src/sage/structure/sage_object.pyx | 2 +- src/sage/structure/sequence.py | 8 ++++---- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/sage/categories/action.pyx b/src/sage/categories/action.pyx index fb39aaafa0a..7ec46f80a98 100644 --- a/src/sage/categories/action.pyx +++ b/src/sage/categories/action.pyx @@ -71,8 +71,8 @@ cdef inline category(x): try: return x.category() except AttributeError: - import sage.categories.all - return sage.categories.all.Objects() + from sage.categories.objects import Objects + return Objects() cdef class Action(Functor): diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index cd190ab53fd..8eb93e7c743 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -127,8 +127,8 @@ def category(x): try: return x.category() except AttributeError: - import sage.categories.all - return sage.categories.all.Objects() + from sage.categories.objects import Objects + return Objects() def characteristic_polynomial(x, var='x'): diff --git a/src/sage/misc/nested_class_test.py b/src/sage/misc/nested_class_test.py index 27273ad5c69..61e29f46d46 100644 --- a/src/sage/misc/nested_class_test.py +++ b/src/sage/misc/nested_class_test.py @@ -63,7 +63,7 @@ def __init__(self): sage: sage.misc.nested_class_test.TestParent1() """ - from sage.categories.all import Sets + from sage.categories.sets_cat import Sets Parent.__init__(self, category = Sets()) class Element(ElementWrapper): @@ -80,7 +80,7 @@ def __init__(self): ... TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases """ - from sage.categories.all import Sets + from sage.categories.sets_cat import Sets Parent.__init__(self, category = Sets()) class Element(ElementWrapper): @@ -96,7 +96,7 @@ def __init__(self): sage: sage.misc.nested_class_test.TestParent3() """ - from sage.categories.all import Sets + from sage.categories.sets_cat import Sets Parent.__init__(self, category = Sets()) class Element(ElementWrapper): @@ -111,7 +111,7 @@ def __init__(self): sage: sage.misc.nested_class_test.TestParent4() """ - from sage.categories.all import Sets + from sage.categories.sets_cat import Sets Parent.__init__(self, category=Sets()) def __eq__(self, other): diff --git a/src/sage/structure/category_object.pyx b/src/sage/structure/category_object.pyx index fdca7ec5914..e9609316994 100644 --- a/src/sage/structure/category_object.pyx +++ b/src/sage/structure/category_object.pyx @@ -647,7 +647,7 @@ cdef class CategoryObject(SageObject): return self._Hom_(codomain, cat) except (AttributeError, TypeError): pass - from sage.categories.all import Hom + from sage.categories.homset import Hom return Hom(self, codomain, cat) def latex_variable_names(self): diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index 0791c72a70c..ba491f4868d 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -653,7 +653,7 @@ cdef class Element(SageObject): return self._parent.base_ring() def category(self): - from sage.categories.all import Elements + from sage.categories.category_types import Elements return Elements(self._parent) def _test_new(self, **options): diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index 140e2defefd..865f34c5648 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -480,7 +480,7 @@ cdef class SageObject: ############################################################################# def category(self): - from sage.categories.all import Objects + from sage.categories.objects import Objects return Objects() def _test_category(self, **options): diff --git a/src/sage/structure/sequence.py b/src/sage/structure/sequence.py index eea50863c44..6d879b67640 100644 --- a/src/sage/structure/sequence.py +++ b/src/sage/structure/sequence.py @@ -232,8 +232,8 @@ def Sequence(x, universe=None, check=True, immutable=False, cr=False, cr_str=Non x = list(x) # make a copy even if x is a list, we're going to change it if len(x) == 0: - import sage.categories.all - universe = sage.categories.all.Objects() + from sage.categories.objects import Objects + universe = Objects() else: import sage.structure.element if use_sage_types: @@ -244,8 +244,8 @@ def Sequence(x, universe=None, check=True, immutable=False, cr=False, cr_str=Non try: x[i], x[i+1] = sage.structure.element.canonical_coercion(x[i],x[i+1]) except TypeError: - import sage.categories.all - universe = sage.categories.all.Objects() + from sage.categories.objects import Objects + universe = Objects() x = list(orig_x) check = False # no point break From 937d626ff74ac1b91a21a722e008315133ee8cc7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 20 Jun 2020 10:00:49 -0700 Subject: [PATCH 082/204] Move ChainComplexes from sage.categories.category_types to a separate module --- src/sage/categories/all.py | 15 ++++---- src/sage/categories/category_types.py | 37 ++----------------- src/sage/categories/chain_complexes.py | 49 ++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 43 deletions(-) create mode 100644 src/sage/categories/chain_complexes.py diff --git a/src/sage/categories/all.py b/src/sage/categories/all.py index a60c45eba95..62ff402d522 100644 --- a/src/sage/categories/all.py +++ b/src/sage/categories/all.py @@ -3,16 +3,15 @@ from .category import Category -from .category_types import( - Elements, - ChainComplexes, -) +from .category_types import Elements -from sage.categories.simplicial_complexes import SimplicialComplexes +from .chain_complexes import ChainComplexes -from sage.categories.tensor import tensor -from sage.categories.signed_tensor import tensor_signed -from sage.categories.cartesian_product import cartesian_product +from .simplicial_complexes import SimplicialComplexes + +from .tensor import tensor +from .signed_tensor import tensor_signed +from .cartesian_product import cartesian_product from .functor import (ForgetfulFunctor, IdentityFunctor) diff --git a/src/sage/categories/category_types.py b/src/sage/categories/category_types.py index ed1c7d5cce8..0ec5cce262a 100644 --- a/src/sage/categories/category_types.py +++ b/src/sage/categories/category_types.py @@ -21,6 +21,8 @@ lazy_import('sage.categories.objects', 'Objects') lazy_import('sage.misc.latex', 'latex') +lazy_import('sage.categories.chain_complexes', 'ChainComplexes', + deprecation=29917) #################################################################### # Different types of categories @@ -614,38 +616,3 @@ def __call__(self, v): if v in self: return v return self.ring().ideal(v) - -# TODO: make this into a better category -############################################################# -# ChainComplex -############################################################# -class ChainComplexes(Category_module): - """ - The category of all chain complexes over a base ring. - - EXAMPLES:: - - sage: ChainComplexes(RationalField()) - Category of chain complexes over Rational Field - - sage: ChainComplexes(Integers(9)) - Category of chain complexes over Ring of integers modulo 9 - - TESTS:: - - sage: TestSuite(ChainComplexes(RationalField())).run() - """ - - def super_categories(self): - """ - EXAMPLES:: - - sage: ChainComplexes(Integers(9)).super_categories() - [Category of modules over Ring of integers modulo 9] - """ - from sage.categories.all import Fields, Modules, VectorSpaces - base_ring = self.base_ring() - if base_ring in Fields(): - return [VectorSpaces(base_ring)] - return [Modules(base_ring)] - diff --git a/src/sage/categories/chain_complexes.py b/src/sage/categories/chain_complexes.py new file mode 100644 index 00000000000..72a4763b3b8 --- /dev/null +++ b/src/sage/categories/chain_complexes.py @@ -0,0 +1,49 @@ +""" +Category of chain complexes +""" + +#***************************************************************************** +# Copyright (C) 2007 Robert Bradshaw +# 2009 Mike Hansen +# 2013 Volker Braun +# 2013, 2015 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.categories.category_types import Category_module + +# TODO: make this into a better category +############################################################# +# ChainComplex +############################################################# +class ChainComplexes(Category_module): + """ + The category of all chain complexes over a base ring. + + EXAMPLES:: + + sage: ChainComplexes(RationalField()) + Category of chain complexes over Rational Field + + sage: ChainComplexes(Integers(9)) + Category of chain complexes over Ring of integers modulo 9 + + TESTS:: + + sage: TestSuite(ChainComplexes(RationalField())).run() + """ + + def super_categories(self): + """ + EXAMPLES:: + + sage: ChainComplexes(Integers(9)).super_categories() + [Category of modules over Ring of integers modulo 9] + """ + from sage.categories.all import Fields, Modules, VectorSpaces + base_ring = self.base_ring() + if base_ring in Fields(): + return [VectorSpaces(base_ring)] + return [Modules(base_ring)] From d7a8849a464d341ef7561b6ec47998d8ceb90089 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sat, 20 Jun 2020 16:34:29 -0700 Subject: [PATCH 083/204] FusionRing moved to a separate file fusion_ring.py --- src/doc/en/reference/combinat/module_list.rst | 1 + src/sage/combinat/root_system/__init__.py | 1 + src/sage/combinat/root_system/all.py | 4 +- src/sage/combinat/root_system/fusion_ring.py | 444 ++++++++++++++++++ .../combinat/root_system/weyl_characters.py | 417 ---------------- 5 files changed, 448 insertions(+), 419 deletions(-) create mode 100644 src/sage/combinat/root_system/fusion_ring.py diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 64e844e9b8a..86cdeafcaf5 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -278,6 +278,7 @@ Comprehensive Module list sage/combinat/root_system/weight_lattice_realizations sage/combinat/root_system/weight_space sage/combinat/root_system/weyl_characters + sage/combinat/root_system/fusion_ring sage/combinat/root_system/weyl_group sage/combinat/rooted_tree sage/combinat/rsk diff --git a/src/sage/combinat/root_system/__init__.py b/src/sage/combinat/root_system/__init__.py index fa932ab28ef..b04c503fad1 100644 --- a/src/sage/combinat/root_system/__init__.py +++ b/src/sage/combinat/root_system/__init__.py @@ -77,6 +77,7 @@ --------------------- - :ref:`sage.combinat.root_system.weyl_characters` +- :ref:`sage.combinat.root_system.fusion_ring` - :ref:`sage.combinat.root_system.integrable_representations` - :ref:`sage.combinat.root_system.branching_rules` - :ref:`sage.combinat.root_system.hecke_algebra_representation` diff --git a/src/sage/combinat/root_system/all.py b/src/sage/combinat/root_system/all.py index 8ed7e596a17..ccb40064690 100644 --- a/src/sage/combinat/root_system/all.py +++ b/src/sage/combinat/root_system/all.py @@ -19,8 +19,8 @@ 'ExtendedAffineWeylGroup') lazy_import('sage.combinat.root_system.coxeter_group', 'CoxeterGroup') lazy_import('sage.combinat.root_system.weyl_characters', ['WeylCharacterRing', - 'WeightRing', - 'FusionRing']) + 'WeightRing']) +lazy_import('sage.combinat.root_system.fusion_ring', ['FusionRing']) from .branching_rules import BranchingRule, branching_rule_from_plethysm, branching_rule lazy_import('sage.combinat.root_system.non_symmetric_macdonald_polynomials', 'NonSymmetricMacdonaldPolynomials') diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py new file mode 100644 index 00000000000..6b77d8e2ad5 --- /dev/null +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -0,0 +1,444 @@ +""" +Fusion Rings +""" +# **************************************************************************** +# Copyright (C) 2019 Daniel Bump +# Nicolas Thiery +# Guillermo Aboumrad +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.combinat.root_system.root_system import RootSystem +from sage.combinat.root_system.weyl_characters import WeylCharacterRing +from functools import reduce +import sage.combinat.root_system.branching_rules +from operator import mul +from sage.categories.all import Category, Algebras, AlgebrasWithBasis +from sage.combinat.free_module import CombinatorialFreeModule +from sage.combinat.q_analogues import q_int +from sage.combinat.root_system.cartan_type import CartanType +from sage.combinat.root_system.root_system import RootSystem +from sage.matrix.special import diagonal_matrix +from sage.matrix.constructor import matrix +from sage.misc.lazy_attribute import lazy_attribute +from sage.misc.misc import inject_variable +from sage.rings.all import ZZ, CC +from sage.rings.number_field.number_field import CyclotomicField + +class FusionRing(WeylCharacterRing): + r""" + Return the Fusion Ring (Verlinde Algebra) of level ``k``. + + INPUT: + + - ``ct`` -- the Cartan type of a simple (finite-dimensional) Lie algebra + - ``k`` -- a nonnegative integer + + This algebra has a basis indexed by the weights of level `\leq k`. + It is implemented as a variant of the :class:`WeylCharacterRing`. + + EXAMPLES:: + + sage: A22 = FusionRing("A2",2) + sage: [f1, f2] = A22.fundamental_weights() + sage: M = [A22(x) for x in [0*f1, 2*f1, 2*f2, f1+f2, f2, f1]] + sage: [M[3] * x for x in M] + [A22(1,1), + A22(0,1), + A22(1,0), + A22(0,0) + A22(1,1), + A22(0,1) + A22(2,0), + A22(1,0) + A22(0,2)] + + You may assign your own labels to the basis elements. In the next + example, we create the `SO(5)` fusion ring of level `2`, check the + weights of the basis elements, then assign new labels to them:: + + sage: B22 = FusionRing("B2", 2) + sage: b = [B22(x) for x in B22.get_order()]; b + [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] + sage: [x.highest_weight() for x in b] + [(0, 0), (1, 0), (1/2, 1/2), (2, 0), (3/2, 1/2), (1, 1)] + sage: B22.fusion_labels(['I0','Y1','X','Z','Xp','Y2']) + sage: b = [B22(x) for x in B22.get_order()]; b + [I0, Y1, X, Z, Xp, Y2] + sage: [(x, x.highest_weight()) for x in b] + [(I0, (0, 0)), + (Y1, (1, 0)), + (X, (1/2, 1/2)), + (Z, (2, 0)), + (Xp, (3/2, 1/2)), + (Y2, (1, 1))] + sage: X*Y1 + X + Xp + sage: Z*Z + I0 + + A fixed order of the basis keys is avalailable with :meth:`get_order`. + This is the order used by methods such as :meth:`s_matrix`. + You may use :meth:`set_order` to reorder the basis:: + + sage: B22.set_order([x.highest_weight() for x in [I0,Y1,Y2,X,Xp,Z]]) + sage: [B22(x) for x in B22.get_order()] + [I0, Y1, Y2, X, Xp, Z] + + To reset the labels and the order to their defaults, + you may run `fusion_labels` with no parameter: + + sage: B22.fusion_labels() + sage: [B22(x) for x in B22.get_order()] + [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] + + + REFERENCES: + + - [DFMS1996]_ Chapter 16 + - [Feingold2004]_ + - [Fuchs1994]_ + - [Walton1990]_ + """ + @staticmethod + def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False): + """ + Normalize input to ensure a unique representation. + + TESTS:: + + sage: F1 = FusionRing('B3', 2) + sage: F2 = FusionRing(CartanType('B3'), QQ(2), ZZ) + sage: F3 = FusionRing(CartanType('B3'), int(2), style="coroots") + sage: F1 is F2 and F2 is F3 + True + + sage: A23 = FusionRing('A2', 3) + sage: TestSuite(A23).run() + + sage: B22 = FusionRing('B2', 2) + sage: TestSuite(B22).run() + + sage: C31 = FusionRing('C3', 1) + sage: TestSuite(C31).run() + + sage: D41 = FusionRing('D4', 1) + sage: TestSuite(D41).run() + """ + return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, + prefix=prefix, style=style, k=k, conjugate=conjugate) + + @lazy_attribute + def _q_field(self): + """ + The cyclotomic field of 4l-th roots of unity, where + l is the fusion_l of the category (see above). Call this + lazy attribute via the method `self.q_field()`. + """ + self._K = CyclotomicField(4*self._l) + return self._K + + def q_field(self): + """ + Return the cyclotomic field of 4l-th roots of unity, where + `l` is the ``fusion_l`` of the category (see above). + + This field contains the twists, categorical dimensions, and the entries of the + S-matrix. + + EXAMPLES:: + + sage: B22=FusionRing("B2",2) + sage: B22.q_field() + Cyclotomic Field of order 40 and degree 16 + sage: A11=FusionRing('A1',1) + sage: A11.q_field() + Cyclotomic Field of order 12 and degree 4 + """ + return self._q_field + + def _element_constructor(self,weight): + return self._parent._element_constructor(self._parent,weight) + + def get_order(self): + """ + Returns the weights of the basis vectors in a fixed order. + You may change the order of the basis using :meth:`set_order` + + EXAMPLES:: + + sage: A14=FusionRing("A1",4) + sage: w = A14.get_order(); w + [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2)] + sage: A14.set_order([w[k] for k in [0,4,1,3,2]]) + sage: [A14(x) for x in A14.get_order()] + [A14(0), A14(4), A14(1), A14(3), A14(2)] + + This duplicates :meth:`get_order` from `combinat.free_module`. + However unlike the `combinat.free_module` method with the same + name this `get_order` is not cached. Caching of get_order causes + inconsistent results after calling `set_order`. + """ + if self._order is None: + self.set_order(self.basis().keys().list()) + return self._order + + def some_elements(self): + """ + Return some elements of ``self``. + + EXAMPLES:: + + sage: D41 = FusionRing('D4', 1) + sage: D41.some_elements() + [D41(1,0,0,0), D41(0,0,1,0), D41(0,0,0,1)] + """ + return [self.monomial(x) for x in self.fundamental_weights() + if self.level(x) <= self._k] + + def fusion_k(self): + r""" + Return the level of the FusionRing. + + EXAMPLES:: + + sage: B22=FusionRing('B2',2) + sage: B22.fusion_k() + 2 + """ + return self._k + + def fusion_l(self): + r""" + Returns the product `m_g(k + h^\vee)`, where `m_g` denotes the + square of the ratio of the lengths of long to short roots of + the underlying Lie algebra, `k` denotes the level of the FusionRing, + and `h^\vee` denotes the dual Coxeter number of the underlying Lie + algebra. + + This value is used to define the associated root of unity q. + + EXAMPLES:: + + sage: B22=FusionRing('B2',2) + sage: B22.fusion_l() + 10 + sage: D52=FusionRing('D5',2) + sage: D52.fusion_l() + 10 + """ + return self._l + + def twists_matrix(self): + r""" + Return a diagonal matrix describing the twist corresponding to + each simple object in the ``FusionRing``. + + EXAMPLES:: + + sage: B22=FusionRing('B2',2) + sage: B22.twists_matrix() + [ 0 0 0 0 0 0] + [ 0 4/5 0 0 0 0] + [ 0 0 1/2 0 0 0] + [ 0 0 0 2 0 0] + [ 0 0 0 0 3/2 0] + [ 0 0 0 0 0 6/5] + """ + return diagonal_matrix(self.basis()[x].twist() for x in self.get_order()) + + def q_dims(self): + r""" + Return a list of quantum dimensions of the simple objects. + + EXAMPLES:: + + sage: F41=FusionRing('F4',1,conjugate=True) + sage: F41.q_dims() + [1, -zeta80^24 + zeta80^16 + 1] + sage: B22=FusionRing("B2",2) + sage: B22.q_dims() + [1, 2, -2*zeta40^12 + 2*zeta40^8 + 1, 1, -2*zeta40^12 + 2*zeta40^8 + 1, 2] + """ + b = self.basis() + return [b[x].q_dimension() for x in self.get_order()] + + def s_ij(self, elt_i, elt_j): + """ + Return the element of the S-matrix of this FusionRing corresponding to + the given elements. + + INPUT: + + - ``elt_i``, ``elt_j`` -- elements of the fusion basis + + EXAMPLES:: + + sage: G21=FusionRing("G2",1) + sage: b=G21.basis() + sage: [G21.s_ij(x,y) for x in b for y in b] + [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] + """ + l = self.fusion_l() + K = self.q_field() + q = K.gen() + ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) + return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) + + def s_matrix(self): + r""" + Return the S-matrix of this FusionRing. + + EXAMPLES:: + + sage: D91=FusionRing('D9',1) + sage: D91.s_matrix() + [ 1 1 1 1] + [ 1 1 -1 -1] + [ 1 -1 -zeta68^17 zeta68^17] + [ 1 -1 zeta68^17 -zeta68^17] + + sage: D41=FusionRing('D4',1) + sage: D41.s_matrix() + [ 1 1 1 1] + [ 1 1 -1 -1] + [ 1 -1 1 -1] + [ 1 -1 -1 1] + """ + b = self.basis() + return matrix([[self.s_ij(b[x],b[y]) for x in self.get_order()] for y in self.get_order()]) + + def fusion_labels(self, labels=None): + """ + Set the labels of the basis. + + INPUT: + + - ``labels`` -- (default: ``None``) a list of strings or string + + If ``labels`` is a list, the length of the list must equal the + number of basis elements. These become the names of + the basis elements. + + If ``labels`` is a string, this is treated as a prefix and a + list of names is generated. + + If ``labels`` is ``None``, then this resets the labels to the default. + + EXAMPLES:: + + sage: A13 = FusionRing("A1", 3) + sage: A13.fusion_labels("x") + sage: fb = list(A13.basis()); fb + [x0, x1, x2, x3] + sage: Matrix([[x*y for y in A13.basis()] for x in A13.basis()]) + [ x0 x1 x2 x3] + [ x1 x0 + x2 x1 + x3 x2] + [ x2 x1 + x3 x0 + x2 x1] + [ x3 x2 x1 x0] + + We reset the labels to the default:: + + sage: A13.fusion_labels() + sage: fb + [A13(0), A13(1), A13(2), A13(3)] + """ + if labels is None: + self._order = None + self._fusion_labels = None + return + elif type(labels) == str: + labels = [labels+"%d"%k for k in range(len(self.basis()))] + elif len(labels) != len(self.basis()): + raise ValueError('invalid data') + d = {} + fb = list(self.get_order()) + for j, b in enumerate(fb): + t = tuple([b.inner_product(x) for x in self.simple_coroots()]) + d[t] = labels[j] + inject_variable(labels[j], self(b)) + self._fusion_labels = d + + class Element(WeylCharacterRing.Element): + """ + A class for FusionRing elements + """ + def is_simple_object(self): + """ + Determine whether element is a simple object of the FusionRing. + + EXAMPLES:: + + sage: A22=FusionRing("A2",2) + sage: x = A22(1,0); x + A22(1,0) + sage: x.is_simple_object() + True + sage: x^2 + A22(0,1) + A22(2,0) + sage: (x^2).is_simple_object() + False + """ + return self.parent()._k is not None and len(self.monomial_coefficients())==1 + + def twist(self): + r""" + Compute the object's twist. Returns a rational number `h_X` such that + `e^{(i \pi h_X)}` is the twist of `X`. + + We compute the twists following p.7 of [Row2006]_, noting that the bilinear form + is normalized so that `\langle\alpha, \alpha\rangle = 2` for SHORT roots. + + EXAMPLES:: + + sage: G21=FusionRing('G2',1) + sage: G21.basis() + Finite family {(0, 0, 0): G21(0,0), (1, 0, -1): G21(1,0)} + sage: G21(1,0).twist() + 4/5 + sage: F41=FusionRing('F4',1,conjugate=True) + sage: F41.basis() + Finite family {(0, 0, 0, 0): F41(0,0,0,0), (1, 0, 0, 0): F41(0,0,0,1)} + sage: F41(0,0,0,1).twist() + 4/5 + """ + if not self.is_simple_object(): + raise ValueError("quantum twist is only available for simple objects of a FusionRing") + rho = sum(self.parent().positive_roots())/2 + lam = self.highest_weight() + inner = lam.inner_product(lam+2*rho) + twist = self.parent()._conj*self.parent()._nf*inner/self.parent().fusion_l() + #Reduce to canonical form + while twist > 2: + twist -= 2 + while twist < 0: + twist += 2 + return twist + + def q_dimension(self): + r"""" + This returns the quantum dimension as an element of the cyclotomic + field of the `2l`-th roots of unity, where `l = m(k+h^\vee)` + with `m=1,2,3` depending on whether type is simply, doubly or + triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. + + EXAMPLE:: + + sage: B22=FusionRing("B2",2) + sage: [(b.q_dimension())^2 for b in B22.basis()] + [1, 4, 5, 1, 5, 4] + """ + if not self.is_simple_object(): + raise ValueError("quantum twist is only available for simple objects of a FusionRing") + lam = self.highest_weight() + space = self.parent().space() + rho = space.rho() + l = self.parent().fusion_l() + num = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(lam+rho)) for alpha in space.positive_roots()], 1) + den = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(rho)) for alpha in space.positive_roots()], 1) + expr = num/den + pr = expr.parent().ring() + q = pr.gen()**2 + expr = pr(expr) + expr = expr.substitute(q=q**2)/q**(expr.degree()) + zet = self.parent().q_field().gen() + return expr.substitute(q=zet) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 02678db2451..38f9b8451e8 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -2209,420 +2209,3 @@ def demazure_lusztig(self, i, v): return self.demazure_lusztig(i.reduced_word(), v) except Exception: raise ValueError("unknown index {}".format(i)) - - -class FusionRing(WeylCharacterRing): - r""" - Return the Fusion Ring (Verlinde Algebra) of level ``k``. - - INPUT: - - - ``ct`` -- the Cartan type of a simple (finite-dimensional) Lie algebra - - ``k`` -- a nonnegative integer - - This algebra has a basis indexed by the weights of level `\leq k`. - It is implemented as a variant of the :class:`WeylCharacterRing`. - - EXAMPLES:: - - sage: A22 = FusionRing("A2",2) - sage: [f1, f2] = A22.fundamental_weights() - sage: M = [A22(x) for x in [0*f1, 2*f1, 2*f2, f1+f2, f2, f1]] - sage: [M[3] * x for x in M] - [A22(1,1), - A22(0,1), - A22(1,0), - A22(0,0) + A22(1,1), - A22(0,1) + A22(2,0), - A22(1,0) + A22(0,2)] - - You may assign your own labels to the basis elements. In the next - example, we create the `SO(5)` fusion ring of level `2`, check the - weights of the basis elements, then assign new labels to them:: - - sage: B22 = FusionRing("B2", 2) - sage: b = [B22(x) for x in B22.get_order()]; b - [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] - sage: [x.highest_weight() for x in b] - [(0, 0), (1, 0), (1/2, 1/2), (2, 0), (3/2, 1/2), (1, 1)] - sage: B22.fusion_labels(['I0','Y1','X','Z','Xp','Y2']) - sage: b = [B22(x) for x in B22.get_order()]; b - [I0, Y1, X, Z, Xp, Y2] - sage: [(x, x.highest_weight()) for x in b] - [(I0, (0, 0)), - (Y1, (1, 0)), - (X, (1/2, 1/2)), - (Z, (2, 0)), - (Xp, (3/2, 1/2)), - (Y2, (1, 1))] - sage: X*Y1 - X + Xp - sage: Z*Z - I0 - - A fixed order of the basis keys is avalailable with :meth:`get_order`. - This is the order used by methods such as :meth:`s_matrix`. - You may use :meth:`set_order` to reorder the basis:: - - sage: B22.set_order([x.highest_weight() for x in [I0,Y1,Y2,X,Xp,Z]]) - sage: [B22(x) for x in B22.get_order()] - [I0, Y1, Y2, X, Xp, Z] - - To reset the labels and the order to their defaults, - you may run `fusion_labels` with no parameter: - - sage: B22.fusion_labels() - sage: [B22(x) for x in B22.get_order()] - [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] - - - REFERENCES: - - - [DFMS1996]_ Chapter 16 - - [Feingold2004]_ - - [Fuchs1994]_ - - [Walton1990]_ - """ - @staticmethod - def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False): - """ - Normalize input to ensure a unique representation. - - TESTS:: - - sage: F1 = FusionRing('B3', 2) - sage: F2 = FusionRing(CartanType('B3'), QQ(2), ZZ) - sage: F3 = FusionRing(CartanType('B3'), int(2), style="coroots") - sage: F1 is F2 and F2 is F3 - True - - sage: A23 = FusionRing('A2', 3) - sage: TestSuite(A23).run() - - sage: B22 = FusionRing('B2', 2) - sage: TestSuite(B22).run() - - sage: C31 = FusionRing('C3', 1) - sage: TestSuite(C31).run() - - sage: D41 = FusionRing('D4', 1) - sage: TestSuite(D41).run() - """ - return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, - prefix=prefix, style=style, k=k, conjugate=conjugate) - - @lazy_attribute - def _q_field(self): - """ - The cyclotomic field of 4l-th roots of unity, where - l is the fusion_l of the category (see above). Call this - lazy attribute via the method `self.q_field()`. - """ - self._K = CyclotomicField(4*self._l) - return self._K - - def q_field(self): - """ - Return the cyclotomic field of 4l-th roots of unity, where - `l` is the ``fusion_l`` of the category (see above). - - This field contains the twists, categorical dimensions, and the entries of the - S-matrix. - - EXAMPLES:: - - sage: B22=FusionRing("B2",2) - sage: B22.q_field() - Cyclotomic Field of order 40 and degree 16 - sage: A11=FusionRing('A1',1) - sage: A11.q_field() - Cyclotomic Field of order 12 and degree 4 - """ - return self._q_field - - def _element_constructor(self,weight): - return self._parent._element_constructor(self._parent,weight) - - def get_order(self): - """ - Returns the weights of the basis vectors in a fixed order. - You may change the order of the basis using :meth:`set_order` - - EXAMPLES:: - - sage: A14=FusionRing("A1",4) - sage: w = A14.get_order(); w - [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2)] - sage: A14.set_order([w[k] for k in [0,4,1,3,2]]) - sage: [A14(x) for x in A14.get_order()] - [A14(0), A14(4), A14(1), A14(3), A14(2)] - - This duplicates :meth:`get_order` from `combinat.free_module`. - However unlike the `combinat.free_module` method with the same - name this `get_order` is not cached. Caching of get_order causes - inconsistent results after calling `set_order`. - """ - if self._order is None: - self.set_order(self.basis().keys().list()) - return self._order - - def some_elements(self): - """ - Return some elements of ``self``. - - EXAMPLES:: - - sage: D41 = FusionRing('D4', 1) - sage: D41.some_elements() - [D41(1,0,0,0), D41(0,0,1,0), D41(0,0,0,1)] - """ - return [self.monomial(x) for x in self.fundamental_weights() - if self.level(x) <= self._k] - - def fusion_k(self): - r""" - Return the level of the FusionRing. - - EXAMPLES:: - - sage: B22=FusionRing('B2',2) - sage: B22.fusion_k() - 2 - """ - return self._k - - def fusion_l(self): - r""" - Returns the product `m_g(k + h^\vee)`, where `m_g` denotes the - square of the ratio of the lengths of long to short roots of - the underlying Lie algebra, `k` denotes the level of the FusionRing, - and `h^\vee` denotes the dual Coxeter number of the underlying Lie - algebra. - - This value is used to define the associated root of unity q. - - EXAMPLES:: - - sage: B22=FusionRing('B2',2) - sage: B22.fusion_l() - 10 - sage: D52=FusionRing('D5',2) - sage: D52.fusion_l() - 10 - """ - return self._l - - def twists_matrix(self): - r""" - Return a diagonal matrix describing the twist corresponding to - each simple object in the ``FusionRing``. - - EXAMPLES:: - - sage: B22=FusionRing('B2',2) - sage: B22.twists_matrix() - [ 0 0 0 0 0 0] - [ 0 4/5 0 0 0 0] - [ 0 0 1/2 0 0 0] - [ 0 0 0 2 0 0] - [ 0 0 0 0 3/2 0] - [ 0 0 0 0 0 6/5] - """ - return diagonal_matrix(self.basis()[x].twist() for x in self.get_order()) - - def q_dims(self): - r""" - Return a list of quantum dimensions of the simple objects. - - EXAMPLES:: - - sage: F41=FusionRing('F4',1,conjugate=True) - sage: F41.q_dims() - [1, -zeta80^24 + zeta80^16 + 1] - sage: B22=FusionRing("B2",2) - sage: B22.q_dims() - [1, 2, -2*zeta40^12 + 2*zeta40^8 + 1, 1, -2*zeta40^12 + 2*zeta40^8 + 1, 2] - """ - b = self.basis() - return [b[x].q_dimension() for x in self.get_order()] - - def s_ij(self, elt_i, elt_j): - """ - Return the element of the S-matrix of this FusionRing corresponding to - the given elements. - - INPUT: - - - ``elt_i``, ``elt_j`` -- elements of the fusion basis - - EXAMPLES:: - - sage: G21=FusionRing("G2",1) - sage: b=G21.basis() - sage: [G21.s_ij(x,y) for x in b for y in b] - [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] - """ - l = self.fusion_l() - K = self.q_field() - q = K.gen() - ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) - return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) - - def s_matrix(self): - r""" - Return the S-matrix of this FusionRing. - - EXAMPLES:: - - sage: D91=FusionRing('D9',1) - sage: D91.s_matrix() - [ 1 1 1 1] - [ 1 1 -1 -1] - [ 1 -1 -zeta68^17 zeta68^17] - [ 1 -1 zeta68^17 -zeta68^17] - - sage: D41=FusionRing('D4',1) - sage: D41.s_matrix() - [ 1 1 1 1] - [ 1 1 -1 -1] - [ 1 -1 1 -1] - [ 1 -1 -1 1] - """ - b = self.basis() - return matrix([[self.s_ij(b[x],b[y]) for x in self.get_order()] for y in self.get_order()]) - - def fusion_labels(self, labels=None): - """ - Set the labels of the basis. - - INPUT: - - - ``labels`` -- (default: ``None``) a list of strings or string - - If ``labels`` is a list, the length of the list must equal the - number of basis elements. These become the names of - the basis elements. - - If ``labels`` is a string, this is treated as a prefix and a - list of names is generated. - - If ``labels`` is ``None``, then this resets the labels to the default. - - EXAMPLES:: - - sage: A13 = FusionRing("A1", 3) - sage: A13.fusion_labels("x") - sage: fb = list(A13.basis()); fb - [x0, x1, x2, x3] - sage: Matrix([[x*y for y in A13.basis()] for x in A13.basis()]) - [ x0 x1 x2 x3] - [ x1 x0 + x2 x1 + x3 x2] - [ x2 x1 + x3 x0 + x2 x1] - [ x3 x2 x1 x0] - - We reset the labels to the default:: - - sage: A13.fusion_labels() - sage: fb - [A13(0), A13(1), A13(2), A13(3)] - """ - if labels is None: - self._order = None - self._fusion_labels = None - return - elif type(labels) == str: - labels = [labels+"%d"%k for k in range(len(self.basis()))] - elif len(labels) != len(self.basis()): - raise ValueError('invalid data') - d = {} - fb = list(self.get_order()) - for j, b in enumerate(fb): - t = tuple([b.inner_product(x) for x in self.simple_coroots()]) - d[t] = labels[j] - inject_variable(labels[j], self(b)) - self._fusion_labels = d - - class Element(WeylCharacterRing.Element): - """ - A class for FusionRing elements - """ - def is_simple_object(self): - """ - Determine whether element is a simple object of the FusionRing. - - EXAMPLES:: - - sage: A22=FusionRing("A2",2) - sage: x = A22(1,0); x - A22(1,0) - sage: x.is_simple_object() - True - sage: x^2 - A22(0,1) + A22(2,0) - sage: (x^2).is_simple_object() - False - """ - return self.parent()._k is not None and len(self.monomial_coefficients())==1 - - def twist(self): - r""" - Compute the object's twist. Returns a rational number `h_X` such that - `e^{(i \pi h_X)}` is the twist of `X`. - - We compute the twists following p.7 of [Row2006]_, noting that the bilinear form - is normalized so that `\langle\alpha, \alpha\rangle = 2` for SHORT roots. - - EXAMPLES:: - - sage: G21=FusionRing('G2',1) - sage: G21.basis() - Finite family {(0, 0, 0): G21(0,0), (1, 0, -1): G21(1,0)} - sage: G21(1,0).twist() - 4/5 - sage: F41=FusionRing('F4',1,conjugate=True) - sage: F41.basis() - Finite family {(0, 0, 0, 0): F41(0,0,0,0), (1, 0, 0, 0): F41(0,0,0,1)} - sage: F41(0,0,0,1).twist() - 4/5 - """ - if not self.is_simple_object(): - raise ValueError("quantum twist is only available for simple objects of a FusionRing") - rho = sum(self.parent().positive_roots())/2 - lam = self.highest_weight() - inner = lam.inner_product(lam+2*rho) - twist = self.parent()._conj*self.parent()._nf*inner/self.parent().fusion_l() - #Reduce to canonical form - while twist > 2: - twist -= 2 - while twist < 0: - twist += 2 - return twist - - def q_dimension(self): - r"""" - This returns the quantum dimension as an element of the cyclotomic - field of the `2l`-th roots of unity, where `l = m(k+h^\vee)` - with `m=1,2,3` depending on whether type is simply, doubly or - triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. - - EXAMPLE:: - - sage: B22=FusionRing("B2",2) - sage: [(b.q_dimension())^2 for b in B22.basis()] - [1, 4, 5, 1, 5, 4] - """ - if not self.is_simple_object(): - raise ValueError("quantum twist is only available for simple objects of a FusionRing") - lam = self.highest_weight() - space = self.parent().space() - rho = space.rho() - l = self.parent().fusion_l() - num = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(lam+rho)) for alpha in space.positive_roots()], 1) - den = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(rho)) for alpha in space.positive_roots()], 1) - expr = num/den - pr = expr.parent().ring() - q = pr.gen()**2 - expr = pr(expr) - expr = expr.substitute(q=q**2)/q**(expr.degree()) - zet = self.parent().q_field().gen() - return expr.substitute(q=zet) From 7c37c54a65e8e2d1942c4d1b0c95c9f9ecdd4657 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sat, 20 Jun 2020 17:28:00 -0700 Subject: [PATCH 084/204] the highest weight method for FusionRings is renamed more appropriately weight --- src/sage/combinat/root_system/fusion_ring.py | 48 +++++++++++++------ .../combinat/root_system/weyl_characters.py | 8 +--- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 6b77d8e2ad5..7512304ef1b 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -36,8 +36,18 @@ class FusionRing(WeylCharacterRing): - ``ct`` -- the Cartan type of a simple (finite-dimensional) Lie algebra - ``k`` -- a nonnegative integer - This algebra has a basis indexed by the weights of level `\leq k`. - It is implemented as a variant of the :class:`WeylCharacterRing`. + This algebra has a basis (sometimes called *primary fields*) + indexed by the weights of level `\leq k`. These arise as + the fusion algebras of WZW conformal field theories, or from + quantum groups at roots of unity. The :class:`FusionRing` class is + implemented as a variant of the :class:`WeylCharacterRing`. + + REFERENCES: + + - [DFMS1996]_ Chapter 16 + - [Feingold2004]_ + - [Fuchs1994]_ + - [Walton1990]_ EXAMPLES:: @@ -59,12 +69,12 @@ class FusionRing(WeylCharacterRing): sage: B22 = FusionRing("B2", 2) sage: b = [B22(x) for x in B22.get_order()]; b [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] - sage: [x.highest_weight() for x in b] + sage: [x.weight() for x in b] [(0, 0), (1, 0), (1/2, 1/2), (2, 0), (3/2, 1/2), (1, 1)] sage: B22.fusion_labels(['I0','Y1','X','Z','Xp','Y2']) sage: b = [B22(x) for x in B22.get_order()]; b [I0, Y1, X, Z, Xp, Y2] - sage: [(x, x.highest_weight()) for x in b] + sage: [(x, x.weight()) for x in b] [(I0, (0, 0)), (Y1, (1, 0)), (X, (1/2, 1/2)), @@ -80,7 +90,7 @@ class FusionRing(WeylCharacterRing): This is the order used by methods such as :meth:`s_matrix`. You may use :meth:`set_order` to reorder the basis:: - sage: B22.set_order([x.highest_weight() for x in [I0,Y1,Y2,X,Xp,Z]]) + sage: B22.set_order([x.weight() for x in [I0,Y1,Y2,X,Xp,Z]]) sage: [B22(x) for x in B22.get_order()] [I0, Y1, Y2, X, Xp, Z] @@ -91,13 +101,6 @@ class FusionRing(WeylCharacterRing): sage: [B22(x) for x in B22.get_order()] [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] - - REFERENCES: - - - [DFMS1996]_ Chapter 16 - - [Feingold2004]_ - - [Fuchs1994]_ - - [Walton1990]_ """ @staticmethod def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False): @@ -360,7 +363,7 @@ def fusion_labels(self, labels=None): class Element(WeylCharacterRing.Element): """ - A class for FusionRing elements + A class for FusionRing elements. """ def is_simple_object(self): """ @@ -380,6 +383,21 @@ def is_simple_object(self): """ return self.parent()._k is not None and len(self.monomial_coefficients())==1 + def weight(self): + """ + This method is only available for basis elements. Returns the + parametrizing dominant weight in the level `k` alcove. + + EXAMPLES:: + + sage: A21 = FusionRing("A2",1) + sage: sorted([x.weight() for x in A21.basis()]) + [(0, 0, 0), (1/3, 1/3, -2/3), (2/3, -1/3, -1/3)] + """ + if len(self.monomial_coefficients()) != 1: + raise ValueError("fusion weight is valid for basis elements only") + return self.leading_support() + def twist(self): r""" Compute the object's twist. Returns a rational number `h_X` such that @@ -404,7 +422,7 @@ def twist(self): if not self.is_simple_object(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") rho = sum(self.parent().positive_roots())/2 - lam = self.highest_weight() + lam = self.weight() inner = lam.inner_product(lam+2*rho) twist = self.parent()._conj*self.parent()._nf*inner/self.parent().fusion_l() #Reduce to canonical form @@ -429,7 +447,7 @@ def q_dimension(self): """ if not self.is_simple_object(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") - lam = self.highest_weight() + lam = self.weight() space = self.parent().space() rho = space.rho() l = self.parent().fusion_l() diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 38f9b8451e8..c90e2fdf0e6 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -1245,17 +1245,13 @@ def dual(self): def highest_weight(self): """ This method is only available for basis elements. Returns the - parametrizing dominant weight of an irreducible character or - simple element of a FusionRing. + parametrizing dominant weight of an irreducible character. - Examples:: + EXAMPLES:: sage: G2 = WeylCharacterRing("G2", style="coroots") sage: [x.highest_weight() for x in [G2(1,0),G2(0,1)]] [(1, 0, -1), (2, -1, -1)] - sage: A21 = FusionRing("A2",1) - sage: sorted([x.highest_weight() for x in A21.basis()]) - [(0, 0, 0), (1/3, 1/3, -2/3), (2/3, -1/3, -1/3)] """ if len(self.monomial_coefficients()) != 1: raise ValueError("fusion weight is valid for basis elements only") From 1c02c6081226c39c1ca3f485496b14302376f799 Mon Sep 17 00:00:00 2001 From: n-vi Date: Sun, 21 Jun 2020 13:58:25 +0300 Subject: [PATCH 085/204] Fix shifting issues in _polynomial_list() --- src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index fb62f7664f9..2ad90157433 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -2522,6 +2522,13 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): [1 + O(5^4), 1 + O(5^4)] sage: (1 + w + O(w^11))._polynomial_list(pad=True) [1 + O(5^4), 1 + O(5^4), O(5^3)] + sage: T. = Qp(5).extension(x^2-5) + sage: T(1/5)._polynomial_list() + [5^-1 + O(5^19)] + sage: T(a^-800)._polynomial_list() + [5^-400 + O(5^-380)] + sage: T(O(a^-2))._polynomial_list() + [] """ R = self.base_ring() if self.is_zero(): @@ -2537,9 +2544,9 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): prec = self.relprec + self.ordp e = self.parent().e() if e == 1: - return [R(c, prec) >> k for c in L] + return [R(c, prec-k) << k for c in L] else: - return [R(c, (prec - i - 1) // e + 1) >> k for i, c in enumerate(L)] + return [R(c, (((prec - i - 1) // e) + 1) - k) << k for i, c in enumerate(L)] def polynomial(self, var='x'): """ From 10572e7b4c7da18d84d55762b8cdf7b14e7db909 Mon Sep 17 00:00:00 2001 From: n-vi Date: Sun, 21 Jun 2020 19:02:53 +0300 Subject: [PATCH 086/204] Fix direction of shifting --- src/sage/rings/padics/padic_ZZ_pX_element.pyx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/padics/padic_ZZ_pX_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_element.pyx index 30b88162946..5cfe52cf362 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_element.pyx @@ -585,11 +585,14 @@ def _test_preprocess_list(R, L): sage: _test_preprocess_list(ZqCA(25,names='a',implementation="NTL"), [1/5,mod(2,625),ntl_ZZ_p(3,25)]) ([1, 10, 15], -1, NTL modulus 125) sage: _test_preprocess_list(ZqCA(25,names='a',implementation="NTL"), [1/5,mod(2,625),Zp(5)(5,3)]) - ([1, 10, 1], -1, NTL modulus 625) + ([1, 10, 25], -1, NTL modulus 625) sage: _test_preprocess_list(ZqCA(25,names='a',implementation="NTL"), [1/5,mod(2,625),Zp(5)(5,3),0]) - ([1, 10, 1, 0], -1, NTL modulus 625) + ([1, 10, 25, 0], -1, NTL modulus 625) sage: _test_preprocess_list(ZqCA(25,names='a',implementation="NTL"), [1/5,mod(2,625),Zp(5)(5,3),mod(0,3125)]) - ([1, 10, 1, 0], -1, NTL modulus 625) + ([1, 10, 25, 0], -1, NTL modulus 625) + sage: T. = Qp(5).extension(x^2-5) + sage: _test_preprocess_list(T, [5^-1 + O(5)]) + ([1], -1, NTL modulus 25) """ return preprocess_list(R(0), L) @@ -634,7 +637,7 @@ cdef preprocess_list(pAdicZZpXElement elt, L): elif isinstance(L[i], Integer) or isinstance(L[i], Rational) or isinstance(L[i], (int, long)): L[i] = ntl_ZZ_p(L[i]*pshift_m, ctx) elif isinstance(L[i], pAdicGenericElement) and L[i]._is_base_elt(elt.prime_pow.prime): - L[i] = ntl_ZZ_p((L[i] << min_val).lift(), ctx) + L[i] = ntl_ZZ_p((L[i] >> min_val).lift(), ctx) elif is_IntegerMod(L[i]): L[i] = ntl_ZZ_p(L[i].lift()*pshift_m, ctx) elif (L[i].modulus_context() is not ctx) or min_val != zero: @@ -652,7 +655,7 @@ cdef preprocess_list(pAdicZZpXElement elt, L): elif isinstance(L[i], Integer) or isinstance(L[i], Rational) or isinstance(L[i], (int, long)): L[i] = ntl_ZZ_p(L[i]//pshift_m, ctx) elif isinstance(L[i], pAdicGenericElement) and L[i]._is_base_elt(elt.prime_pow.prime): - L[i] = ntl_ZZ_p((L[i] << min_val).lift(), ctx) + L[i] = ntl_ZZ_p((L[i] >> min_val).lift(), ctx) elif is_IntegerMod(L[i]): L[i] = ntl_ZZ_p(L[i].lift()//pshift_m, ctx) elif (L[i].modulus_context() is not ctx) or min_val != zero: From 1823f188ba7cc4467bee0ea34734a6533d7ef0cb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 21 Jun 2020 11:04:44 -0700 Subject: [PATCH 087/204] Fix imports --- src/sage/homology/chain_complex.py | 2 +- src/sage/homology/chain_complex_morphism.py | 2 +- src/sage/homology/hochschild_complex.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index efc2ee6d809..234878e9d54 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -681,7 +681,7 @@ def __init__(self, grading_group, degree_of_differential, base_ring, differentia self._degree_of_differential = degree_of_differential self._diff = differentials - from sage.categories.all import ChainComplexes + from sage.categories.chain_complexes import ChainComplexes category = ChainComplexes(base_ring) super(ChainComplex_class, self).__init__(base=base_ring, category=category) diff --git a/src/sage/homology/chain_complex_morphism.py b/src/sage/homology/chain_complex_morphism.py index 885777d276e..fea32a9f6bf 100644 --- a/src/sage/homology/chain_complex_morphism.py +++ b/src/sage/homology/chain_complex_morphism.py @@ -54,7 +54,7 @@ from sage.matrix.constructor import block_diagonal_matrix, zero_matrix from sage.categories.morphism import Morphism from sage.categories.homset import Hom -from sage.categories.category_types import ChainComplexes +from sage.categories.chain_complexes import ChainComplexes def is_ChainComplexMorphism(x): diff --git a/src/sage/homology/hochschild_complex.py b/src/sage/homology/hochschild_complex.py index 29048a8ae26..78f38739dc5 100644 --- a/src/sage/homology/hochschild_complex.py +++ b/src/sage/homology/hochschild_complex.py @@ -17,7 +17,7 @@ from sage.structure.parent import Parent from sage.structure.element import ModuleElement, parent from sage.structure.richcmp import richcmp -from sage.categories.category_types import ChainComplexes +from sage.categories.chain_complexes import ChainComplexes from sage.categories.tensor import tensor from sage.combinat.free_module import CombinatorialFreeModule from sage.homology.chain_complex import ChainComplex, Chain_class From 7cdc6b011839f46f8c5a519385e443c5bbab8f52 Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Sun, 21 Jun 2020 20:51:39 +0200 Subject: [PATCH 088/204] implement action of weyl algebra on polynomials --- src/sage/algebras/weyl_algebra.py | 119 ++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/src/sage/algebras/weyl_algebra.py b/src/sage/algebras/weyl_algebra.py index 67965111f61..3ebff737431 100644 --- a/src/sage/algebras/weyl_algebra.py +++ b/src/sage/algebras/weyl_algebra.py @@ -18,10 +18,12 @@ from sage.misc.cachefunc import cached_method from sage.misc.latex import latex, LatexExpr +from sage.misc.lazy_attribute import lazy_attribute from sage.misc.misc_c import prod from sage.structure.richcmp import richcmp from sage.structure.element import AlgebraElement from sage.structure.unique_representation import UniqueRepresentation +from sage.categories.action import Action from sage.categories.rings import Rings from sage.categories.algebras_with_basis import AlgebrasWithBasis from sage.sets.family import Family @@ -618,6 +620,33 @@ def factor_differentials(self): ret[dx] += c * prod(g**e for e, g in zip(x, gens)) return ret + def diff(self, p): + """ + Apply this differential operator to a polynomial. + + INPUT: + + - ``p`` -- polynomial of the underlying polynomial ring + + OUTPUT: + + The result of the left action of the Weyl algebra on the polynomial + ring via differentiation. + + EXAMPLES:: + + sage: R. = QQ[] + sage: W = R.weyl_algebra() + sage: dx, dy = W.differentials() + sage: dx.diff(x^3) + 3*x^2 + sage: (dx*dy).diff(W(x^3*y^3)) + 9*x^2*y^2 + sage: (x*dx + dy + 1).diff(x^4*y^4 + 1) + 5*x^4*y^4 + 4*x^4*y^3 + 1 + """ + return self.parent().diff_action(self, p) + class DifferentialWeylAlgebra(Algebra, UniqueRepresentation): r""" @@ -1047,5 +1076,95 @@ def zero(self): """ return self.element_class(self, {}) + @lazy_attribute + def diff_action(self): + """ + Left action of this Weyl algebra on the underlying polynomial ring by + differentiation. + + EXAMPLES:: + + sage: R. = QQ[] + sage: W = R.weyl_algebra() + sage: dx, dy = W.differentials() + sage: W.diff_action + Left action by Differential Weyl algebra of polynomials in x, y + over Rational Field on Multivariate Polynomial Ring in x, y over + Rational Field + sage: W.diff_action(dx^2 + dy + 1, x^3*y^3) + x^3*y^3 + 3*x^3*y^2 + 6*x*y^3 + """ + return DifferentialWeylAlgebraAction(self) + Element = DifferentialWeylAlgebraElement + +class DifferentialWeylAlgebraAction(Action): + """ + Left action of a Weyl algebra on its underlying polynomial ring by + differentiation. + + EXAMPLES:: + + sage: R. = QQ[] + sage: W = R.weyl_algebra() + sage: dx, dy = W.differentials() + sage: W.diff_action + Left action by Differential Weyl algebra of polynomials in x, y + over Rational Field on Multivariate Polynomial Ring in x, y over + Rational Field + + :: + + sage: g = dx^2 + x*dy + sage: p = x^5 + x^3 + y^2*x^2 + 1 + sage: W.diff_action(g, p) + 2*x^3*y + 20*x^3 + 2*y^2 + 6*x + + The action is a left action:: + + sage: h = dx*x + x*y + sage: W.diff_action(h, W.diff_action(g, p)) == W.diff_action(h*g, p) + True + + The action endomorphism of a differential operator:: + + sage: dg = W.diff_action(g); dg + Action of dx^2 + x*dy on Multivariate Polynomial Ring in x, y over + Rational Field under Left action by Differential Weyl algebra... + sage: dg(p) == W.diff_action(g, p) == g.diff(p) + True + """ + + def __init__(self, G): + """ + INPUT: + + - ``G`` -- Weyl algebra + + EXAMPLES:: + + sage: from sage.algebras.weyl_algebra import DifferentialWeylAlgebraAction + sage: W. = DifferentialWeylAlgebra(QQ) + sage: DifferentialWeylAlgebraAction(W) + Left action by Differential Weyl algebra of polynomials in x, y + over Rational Field on Multivariate Polynomial Ring in x, y over + Rational Field + """ + super().__init__(G, G.polynomial_ring(), is_left=True) + + def _act_(self, g, x): + """ + Apply a differential operator to a polynomial. + + EXAMPLES:: + + sage: W. = DifferentialWeylAlgebra(QQ) + sage: dx, dy = W.differentials() + sage: W.diff_action(dx^3 + dx, x^3*y^3 + x*y) + 3*x^2*y^3 + 6*y^3 + y + """ + f = g * x + D = {y: c for (y, dy), c in f.monomial_coefficients(copy=False).items() + if all(dyi == 0 for dyi in dy)} + return self.right_domain()(D) From f8cb7a0429c2e696dc69db8466ecc077b0bf0f32 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Mon, 22 Jun 2020 10:46:21 +0900 Subject: [PATCH 089/204] Refactored docs --- src/doc/en/faq/faq-usage.rst | 3 +- src/sage/functions/other.py | 102 +++++++++++++++++++---------------- 2 files changed, 59 insertions(+), 46 deletions(-) diff --git a/src/doc/en/faq/faq-usage.rst b/src/doc/en/faq/faq-usage.rst index 37e877741e3..44db03050d6 100644 --- a/src/doc/en/faq/faq-usage.rst +++ b/src/doc/en/faq/faq-usage.rst @@ -684,7 +684,8 @@ How do I plot the cube root (or other odd roots) for negative input? This is one of the most frequently asked questions. There are several methods mentioned in the plot documentation, but this one is easiest:: - sage: plot(real_nth_root(x, 3), (x, -1, 1)) # not tested + sage: plot(real_nth_root(x, 3), (x, -1, 1)) + Graphics object consisting of 1 graphics primitive On the other hand, note that the straightforward :: diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 3e4a3fe7640..93dc8bd3f43 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -913,32 +913,64 @@ def sqrt(x, *args, **kwds): class Function_real_nth_root(BuiltinFunction): - def __init__(self): - r""" - The real `n`-th root function `x^\frac{1}{n}`. + r""" + Real `n`-th root function `x^\frac{1}{n}`. - The function assumes positive integer `n` and real number `x`. + The function assumes positive integer `n` and real number `x`. - EXAMPLES:: + EXAMPLES:: - sage: f = real_nth_root(2, 3) - sage: f^3 - 2 - sage: f = real_nth_root(-2, 3) - sage: f^3 - -2 + sage: v = real_nth_root(2, 3) + sage: v + real_nth_root(2, 3) + sage: v^3 + 2 + sage: v = real_nth_root(-2, 3) + sage: v + real_nth_root(-2, 3) + sage: v^3 + -2 + sage: real_nth_root(8, 3) + 2 + sage: real_nth_root(-8, 3) + -2 + + For numeric input, it gives a numerical approximation. :: + + sage: real_nth_root(2., 3) + 1.25992104989487 + sage: real_nth_root(-2., 3) + -1.25992104989487 + + Some symbolic calculus:: + + sage: f = real_nth_root(x, 5)^3 + sage: f + real_nth_root(x^3, 5) + sage: f.diff() + 3/5*x^2*real_nth_root(x^(-12), 5) + sage: f.integrate(x) + integrate((abs(x)^3)^(1/5)*sgn(x^3), x) + sage: _.diff() + (abs(x)^3)^(1/5)*sgn(x^3) + + """ + def __init__(self): + r""" + Initialize. TESTS:: + sage: cube_root = real_nth_root(x, 3) + sage: loads(dumps(cube_root)) + real_nth_root(x, 3) + + :: + sage: f = real_nth_root(x, 3) sage: f._sympy_() Piecewise((Abs(x)**(1/3)*sign(x), Eq(im(x), 0)), (x**(1/3), True)) - :: - - sage: cube_root = real_nth_root(x, 3) - sage: loads(dumps(cube_root)) - real_nth_root(x, 3) """ BuiltinFunction.__init__(self, "real_nth_root", nargs=2, conversions=dict(sympy='real_root', @@ -947,7 +979,7 @@ def __init__(self): def _print_latex_(self, base, exp): r""" - EXAMPLES:: + TESTS:: sage: latex(real_nth_root(x, 3)) x^{\frac{1}{3}} @@ -958,16 +990,10 @@ def _print_latex_(self, base, exp): def _evalf_(self, base, exp, parent=None): """ - EXAMPLES:: + TESTS:: - sage: real_nth_root(2, 3) - real_nth_root(2, 3) - sage: real_nth_root(-2, 3) - real_nth_root(-2, 3) - sage: real_nth_root(2., 3) - 1.25992104989487 - sage: real_nth_root(-2., 3) - -1.25992104989487 + sage: real_nth_root(RIF(2), 3) + 1.259921049894873? sage: real_nth_root(RBF(2), 3) [1.259921049894873 +/- 3.92e-16] sage: real_nth_root(-2, 4) @@ -991,16 +1017,12 @@ def _evalf_(self, base, exp, parent=None): def _eval_(self, base, exp): """ - EXAMPLES:: + TESTS:: sage: real_nth_root(x, 1) x sage: real_nth_root(x, 3) real_nth_root(x, 3) - sage: real_nth_root(8, 3) - 2 - sage: real_nth_root(-8, 3) - -2 """ if not isinstance(base, Expression) and not isinstance(exp, Expression): if isinstance(base, Integer): @@ -1015,17 +1037,17 @@ def _eval_(self, base, exp): def _power_(self, base, exp, power_param=None): """ - EXAMPLES:: + TESTS:: - sage: v = real_nth_root(x, 3) - sage: v^5 + sage: f = real_nth_root(x, 3) + sage: f^5 real_nth_root(x^5, 3) """ return self(base**power_param, exp) def _derivative_(self, base, exp, diff_param=None): """ - EXAMPLES:: + TESTS:: sage: f = real_nth_root(x, 3) sage: f.diff() @@ -1039,16 +1061,6 @@ def _derivative_(self, base, exp, diff_param=None): sage: f = real_nth_root(-x, 4) sage: f.diff() -1/4*real_nth_root(-1/x^3, 4) - - :: - - sage: f = real_nth_root(x,3) - sage: f.diff() - 1/3*real_nth_root(x^(-2), 3) - sage: f.integrate(x) - integrate(abs(x)^(1/3)*sgn(x), x) - sage: _.diff() - abs(x)^(1/3)*sgn(x) """ return 1/exp * self(base, exp)**(1-exp) From b5f63a5ff65e154be5b2bc1cc3bdebeba7722825 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 21 Jun 2020 23:06:42 -0700 Subject: [PATCH 090/204] src/sage/misc/call.py: Returns should be Return --- src/sage/misc/call.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/misc/call.py b/src/sage/misc/call.py index cf538dee9bd..c2b46c57b2b 100644 --- a/src/sage/misc/call.py +++ b/src/sage/misc/call.py @@ -140,7 +140,7 @@ def __hash__(self): def attrcall(name, *args, **kwds): """ - Returns a callable which takes in an object, gets the method named + Return a callable which takes in an object, gets the method named name from that object, and calls it with the specified arguments and keywords. From f143bf07c31ab887d85500f195350a8ddd590e44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 22 Jun 2020 09:58:23 +0200 Subject: [PATCH 091/204] refresh the faq-contribute file --- src/doc/en/faq/faq-contribute.rst | 76 ++++++++++++------------------- 1 file changed, 30 insertions(+), 46 deletions(-) diff --git a/src/doc/en/faq/faq-contribute.rst b/src/doc/en/faq/faq-contribute.rst index 69ea2aa400c..417c4cc5145 100644 --- a/src/doc/en/faq/faq-contribute.rst +++ b/src/doc/en/faq/faq-contribute.rst @@ -7,10 +7,9 @@ FAQ: Contributing to Sage How can I start contributing to Sage? """"""""""""""""""""""""""""""""""""" -The first step -is to use Sage and encourage your friends to use Sage. If you find -bugs or confusing documentation along the way, please report your -problems! +The first step is to use Sage and encourage your friends to use +Sage. If you find bugs or confusing documentation along the way, +please report your problems! Two popular ways to contribute to Sage are to write code and to create documentation or tutorials. Some steps in each direction @@ -20,12 +19,12 @@ I want to contribute code to Sage. How do I get started? """""""""""""""""""""""""""""""""""""""""""""""""""""""" Take a look at the -`official development guide `_ +`official development guide `_ for Sage. At a minimum, the first chapter in that guide is required reading for any Sage developer. Also pay special attention to the -`trac guidelines `_. +`trac guidelines `_. You can also join the -`sage-devel `_ +`sage-devel `_ mailing list or hang around on the ``#sage-devel`` IRC channel on `freenode `_. While you are getting to know @@ -35,7 +34,7 @@ source and familiarize yourself with the The best way to become familiar with the Sage development process is to choose a ticket from the -`trac server `_ +`trac server `_ and review the proposed changes contained in that ticket. If you want to implement something, it is a good practice to discuss your ideas on the ``sage-devel`` mailing list first, so that other developers have a @@ -43,7 +42,7 @@ chance to comment on your ideas/proposals. They are pretty open to new ideas, too, as all mathematicians should be. Sage's main programming language is -`Python `_. +`Python `_. Some parts of Sage may be written in other languages, especially the components that do the heavy number crunching, but most native functionality is done using Python, including "glue code". One of the @@ -62,11 +61,11 @@ evil." If you do not know Python, you should start learning that language. A good place to start is the -`Python Official Tutorial `_ +`Python Official Tutorial `_ and other documents in the -`Python standard documentation `_. +`Python standard documentation `_. Another good place to take a look at is -`Dive Into Python `_ +`Dive Into Python `_ by Mark Pilgrim, which may be pretty helpful on some specific topics such as test-driven development. The book `Building Skills in Python `_ @@ -82,17 +81,8 @@ programming concepts are explained more thoroughly in Python-centered resources than in Sage-centered resources; in the latter, mathematics is usually the priority. -Can I contribute to Sage using SageMathCloud? -""""""""""""""""""""""""""""""""""""""""""""" - -Absolutely! If you want to write code for Sage or update the -official documentation, -you will need your own installation of Sage on `SageMathCloud `_. -You can find more information about the details of installation in -`the SageMathCloud FAQ `_. - -I'm not a programmer. Is there another way I can help out? -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +I am not a programmer. Is there another way I can help out? +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" Yes. As with any free open source software project, there are numerous ways in which you could help out within the Sage community, and @@ -109,23 +99,20 @@ has written some which we highly recommend. For the graphic designers or the artistically creative, you can -help out with improving the design of the Sage website. Or you can -cast your critical artistic eyes over the interfaces of SageMathCloud -or the Sage notebook, and find out where they need improvement. +help out with improving the design of the Sage website. If you can speak, read, and write in another (natural) language, there are many ways in which your contribution would be very valuable to the whole Sage community. Say you know Italian. Then you can write a Sage tutorial in Italian, or help out with translating the official Sage tutorial to -Italian. +Italian. -The above is a very short -list. There are many, many more ways in which you can help out. Feel -free to send an email to the -`sage-devel `_ -mailing list to ask about possible ways in which you could help out, -or to suggest a project idea. +The above is a very short list. There are many, many more ways in +which you can help out. Feel free to send an email to the `sage-devel +`_ mailing list to ask +about possible ways in which you could help out, or to suggest a +project idea. Where can I find resources on Python or Cython? @@ -136,31 +123,26 @@ resources can be found by a web search. **General resources** -* `Cython `_ +* `Cython `_ * `pep8 `_ * `py2depgraph `_ * `pycallgraph `_ * `PyChecker `_ * `PyFlakes `_ * `Pylint `_ -* `Python `_ home page and the - `Python standard documentation `_ +* `Python `_ home page and the + `Python standard documentation `_ * `Snakefood `_ * `Sphinx `_ * `XDot `_ **Tutorials and books** -* `Building Skills in Python `_ - by Steven F. Lott * `Cython Tutorial `_ by Stefan Behnel, Robert W. Bradshaw, and Dag Sverre Seljebotn -* `Dive into Python `_ by Mark Pilgrim * `Dive Into Python 3 `_ by Mark Pilgrim * `Fast Numerical Computations with Cython `_ by Dag Sverre Seljebotn -* `How to Think Like a Computer Scientist `_ - by Jeffrey Elkner, Allen B. Downey, and Chris Meyers * `Official Python Tutorial `_ **Articles and HOWTOs** @@ -173,9 +155,6 @@ resources can be found by a web search. * `Regular Expression HOWTO `_ by A. M. Kuchling * `reStructuredText `_ -* `Static Code Analizers for Python `_ - by Doug Hellmann - Are there any coding conventions I need to follow? """""""""""""""""""""""""""""""""""""""""""""""""" @@ -183,7 +162,7 @@ Are there any coding conventions I need to follow? You should follow the standard Python conventions as documented at :pep:`8` and :pep:`257`. Also consult the Sage Developer's Guide, especially the chapter -`Conventions for Coding in Sage `_. +`Conventions for Coding in Sage `_. I submitted a bug fix to the trac server several weeks ago. Why are you ignoring my patch? @@ -268,8 +247,13 @@ necessity to import what you need. from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - You can ask Sage where to find ``PolynomialRing`` using:: + You can use ̀`import_statements`` to get the exact necessary line:: + + sage: import_statements(PolynomialRing) + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + If this fails, you can ask Sage where to find ``PolynomialRing`` using:: +x sage: PolynomialRing.__module__ 'sage.rings.polynomial.polynomial_ring_constructor' From f57e162f65b6f29c80a9e6d079cd3378cb122b85 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 22 Jun 2020 12:30:35 +0200 Subject: [PATCH 092/204] more transparent test for an_affine_basis --- 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 4f74a0c3bb1..61231b4b85a 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -2120,7 +2120,7 @@ def _test_an_affine_basis(self, tester=None, **options): tester = self._tester(**options) if self.is_compact(): b = self.an_affine_basis() - m = matrix(b).transpose().stack(matrix([[1]*len(b)])) + m = matrix([1] + list(v) for v in b) tester.assertEqual(m.rank(), self.dim() + 1) for v in b: tester.assertIn(v, self.vertices()) From 8216f329a7dca729c64b18769cfa54f5c907900b Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Mon, 22 Jun 2020 06:26:05 -0700 Subject: [PATCH 093/204] fix unused imports in fusion_ring and weyl_characters found by pyflakes --- src/sage/combinat/root_system/fusion_ring.py | 8 +------- src/sage/combinat/root_system/weyl_characters.py | 9 +-------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 7512304ef1b..84405e0e75d 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -13,18 +13,13 @@ from sage.combinat.root_system.root_system import RootSystem from sage.combinat.root_system.weyl_characters import WeylCharacterRing from functools import reduce -import sage.combinat.root_system.branching_rules from operator import mul -from sage.categories.all import Category, Algebras, AlgebrasWithBasis -from sage.combinat.free_module import CombinatorialFreeModule from sage.combinat.q_analogues import q_int -from sage.combinat.root_system.cartan_type import CartanType -from sage.combinat.root_system.root_system import RootSystem from sage.matrix.special import diagonal_matrix from sage.matrix.constructor import matrix from sage.misc.lazy_attribute import lazy_attribute from sage.misc.misc import inject_variable -from sage.rings.all import ZZ, CC +from sage.rings.all import ZZ from sage.rings.number_field.number_field import CyclotomicField class FusionRing(WeylCharacterRing): @@ -450,7 +445,6 @@ def q_dimension(self): lam = self.weight() space = self.parent().space() rho = space.rho() - l = self.parent().fusion_l() num = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(lam+rho)) for alpha in space.positive_roots()], 1) den = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(rho)) for alpha in space.positive_roots()], 1) expr = num/den diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index c90e2fdf0e6..53064302fcc 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -10,24 +10,17 @@ # **************************************************************************** from __future__ import print_function -from functools import reduce import sage.combinat.root_system.branching_rules -from operator import mul from sage.categories.all import Category, Algebras, AlgebrasWithBasis from sage.combinat.free_module import CombinatorialFreeModule -from sage.combinat.q_analogues import q_int from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.root_system.root_system import RootSystem -from sage.matrix.special import diagonal_matrix from sage.matrix.constructor import matrix from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet from sage.misc.functional import is_even -from sage.misc.misc import inject_variable -from sage.rings.all import ZZ, CC -from sage.rings.number_field.number_field import CyclotomicField - +from sage.rings.all import ZZ class WeylCharacterRing(CombinatorialFreeModule): r""" From e840d380132aed369298a464db0ae0e9e4297a5b Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Mon, 22 Jun 2020 06:43:45 -0700 Subject: [PATCH 094/204] remove misspelled (and therefore unused) method _element_constructor from fusion_ring. The (correctly spelled) _element_constructor_ method is inherited from class WeylCharacterRing so this should not be needed --- src/sage/combinat/root_system/fusion_ring.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 84405e0e75d..8e691a7176d 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -154,9 +154,6 @@ def q_field(self): """ return self._q_field - def _element_constructor(self,weight): - return self._parent._element_constructor(self._parent,weight) - def get_order(self): """ Returns the weights of the basis vectors in a fixed order. From 407c5099b3048021b2729e280cef36c2d97767be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 22 Jun 2020 15:57:33 +0200 Subject: [PATCH 095/204] fix details --- src/doc/en/faq/faq-contribute.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/faq/faq-contribute.rst b/src/doc/en/faq/faq-contribute.rst index 417c4cc5145..bc70ab6c145 100644 --- a/src/doc/en/faq/faq-contribute.rst +++ b/src/doc/en/faq/faq-contribute.rst @@ -247,13 +247,13 @@ necessity to import what you need. from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - You can use ̀`import_statements`` to get the exact necessary line:: + You can use ``import_statements`` to get the exact necessary line:: sage: import_statements(PolynomialRing) from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing If this fails, you can ask Sage where to find ``PolynomialRing`` using:: -x + sage: PolynomialRing.__module__ 'sage.rings.polynomial.polynomial_ring_constructor' From 4a7c44f17e41265f81748c69c01bbca9a2a12fd5 Mon Sep 17 00:00:00 2001 From: Jonathan Kliem Date: Mon, 22 Jun 2020 18:33:21 +0200 Subject: [PATCH 096/204] remove set_random seed --- src/sage/geometry/polyhedron/library.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/library.py b/src/sage/geometry/polyhedron/library.py index 0b05939f8d1..aede45a3844 100644 --- a/src/sage/geometry/polyhedron/library.py +++ b/src/sage/geometry/polyhedron/library.py @@ -3121,7 +3121,6 @@ def hypercube(self, dim, intervals=None, backend=None): :: - sage: set_random_seed() sage: ls = [randint(-100,100) for _ in range(4)] sage: intervals = [[x, x+randint(1,50)] for x in ls] sage: P = polytopes.hypercube(4, intervals, backend='field') @@ -3158,7 +3157,6 @@ def hypercube(self, dim, intervals=None, backend=None): Check that we set up the hypercube correctly:: - sage: set_random_seed() sage: ls = [randint(-100,100) for _ in range(4)] sage: intervals = [[x, x+randint(1,50)] for x in ls] sage: P = polytopes.hypercube(4, intervals, backend='field') From 75bb8d90ec2a8ca137f559950546a94863beaf49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 22 Jun 2020 19:14:55 +0200 Subject: [PATCH 097/204] remove some deprecated stuff in plot3d and structure --- src/sage/plot/plot3d/index_face_set.pyx | 19 ------------------- src/sage/structure/element.pyx | 12 ++---------- 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/src/sage/plot/plot3d/index_face_set.pyx b/src/sage/plot/plot3d/index_face_set.pyx index 1f6b5422240..41a47560a07 100644 --- a/src/sage/plot/plot3d/index_face_set.pyx +++ b/src/sage/plot/plot3d/index_face_set.pyx @@ -1799,25 +1799,6 @@ cdef class VertexIter: self.set.vs[self.i-1].z) -def len3d(v): - """ - Return the norm of a vector in three dimensions. - - This is deprecated since :trac:`27450` . - - EXAMPLES:: - - sage: from sage.plot.plot3d.index_face_set import len3d - sage: len3d((1,2,3)) - doctest:warning...: - DeprecationWarning: len3d is deprecated, use point_c_len instead - See https://trac.sagemath.org/27450 for details. - 3.7416573867739413 - """ - deprecation(27450, "len3d is deprecated, use point_c_len instead") - return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]) - - def sticker(face, width, hover): """ Return a sticker over the given face. diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index 0791c72a70c..2a47f412b96 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -4151,17 +4151,9 @@ cpdef canonical_coercion(x, y): """ return coercion_model.canonical_coercion(x,y) -cpdef bin_op(x, y, op): - return coercion_model.bin_op(x,y,op) - -def coerce(Parent p, x): - from sage.misc.superseded import deprecation - deprecation(25236, "sage.structure.element.coerce is deprecated") - try: - return p._coerce_c(x) - except AttributeError: - return p(x) +cpdef bin_op(x, y, op): + return coercion_model.bin_op(x, y, op) # Make coercion_model accessible as Python object From 929c13f798580ac4d4d7450ccfae4bc4194f86ee Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Mon, 22 Jun 2020 12:22:16 -0700 Subject: [PATCH 098/204] patchbot requested changes on pyflakes, blocks, coverage --- src/sage/combinat/root_system/fusion_ring.py | 18 +++++++++++------- .../combinat/root_system/weyl_characters.py | 1 - 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 8e691a7176d..f0231a1fdaf 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -10,7 +10,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.combinat.root_system.root_system import RootSystem + from sage.combinat.root_system.weyl_characters import WeylCharacterRing from functools import reduce from operator import mul @@ -131,6 +131,13 @@ def _q_field(self): The cyclotomic field of 4l-th roots of unity, where l is the fusion_l of the category (see above). Call this lazy attribute via the method `self.q_field()`. + + EXAMPLES:: + + sage: B22=FusionRing("B2",2) + sage: B22.q_field() + Cyclotomic Field of order 40 and degree 16 + """ self._K = CyclotomicField(4*self._l) return self._K @@ -145,9 +152,6 @@ def q_field(self): EXAMPLES:: - sage: B22=FusionRing("B2",2) - sage: B22.q_field() - Cyclotomic Field of order 40 and degree 16 sage: A11=FusionRing('A1',1) sage: A11.q_field() Cyclotomic Field of order 12 and degree 4 @@ -156,7 +160,7 @@ def q_field(self): def get_order(self): """ - Returns the weights of the basis vectors in a fixed order. + This returns the weights of the basis vectors in a fixed order. You may change the order of the basis using :meth:`set_order` EXAMPLES:: @@ -204,7 +208,7 @@ def fusion_k(self): def fusion_l(self): r""" - Returns the product `m_g(k + h^\vee)`, where `m_g` denotes the + This Returns the product `m_g(k + h^\vee)`, where `m_g` denotes the square of the ratio of the lengths of long to short roots of the underlying Lie algebra, `k` denotes the level of the FusionRing, and `h^\vee` denotes the dual Coxeter number of the underlying Lie @@ -431,7 +435,7 @@ def q_dimension(self): with `m=1,2,3` depending on whether type is simply, doubly or triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. - EXAMPLE:: + EXAMPLES:: sage: B22=FusionRing("B2",2) sage: [(b.q_dimension())^2 for b in B22.basis()] diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 53064302fcc..e96bea32753 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -15,7 +15,6 @@ from sage.combinat.free_module import CombinatorialFreeModule from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.root_system.root_system import RootSystem -from sage.matrix.constructor import matrix from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet From 6bec2253f4afc94fb23ff032f40f6ec348e887f2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 22 Jun 2020 13:41:41 -0700 Subject: [PATCH 099/204] sage.doctest.forker.DocTestTask._run: Remove special support for sagenb --- src/sage/doctest/forker.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index c93d1f2d995..7f28027d9de 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -2504,12 +2504,9 @@ def _run(self, runner, options, results): """ Actually run the doctests with the right set of globals """ - if self.source.basename.startswith("sagenb."): - import sage.all_notebook as sage_all - else: - # Import Jupyter globals to doctest the Jupyter - # implementation of widgets and interacts - import sage.repl.ipython_kernel.all_jupyter as sage_all + # Import Jupyter globals to doctest the Jupyter + # implementation of widgets and interacts + import sage.repl.ipython_kernel.all_jupyter as sage_all dict_all = sage_all.__dict__ # Remove '__package__' item from the globals since it is not # always in the globals in an actual Sage session. From ad00b401f49d2e6b0afbb216fad845b563cf73bc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 22 Jun 2020 14:07:48 -0700 Subject: [PATCH 100/204] sage.doctest, src/bin/sage-runtests: Remove handling of sagenb --- src/bin/sage-runtests | 3 +-- src/sage/doctest/control.py | 31 ++++--------------------------- 2 files changed, 5 insertions(+), 29 deletions(-) diff --git a/src/bin/sage-runtests b/src/bin/sage-runtests index 3248f09b0dc..ff75a49e436 100755 --- a/src/bin/sage-runtests +++ b/src/bin/sage-runtests @@ -50,7 +50,6 @@ if __name__ == "__main__": 'skipped if no limit is set (default: 3300 MB)') parser.add_option("-a", "--all", action="store_true", default=False, help="test all files in the Sage library") parser.add_option("--logfile", metavar="FILE", help="log all output to FILE") - parser.add_option("--sagenb", action="store_true", default=False, help="test all files from the Sage notebook sources") parser.add_option("-l", "--long", action="store_true", default=False, help="include lines with the phrase 'long time'") parser.add_option("-s", "--short", dest="target_walltime", default=None, action="callback", @@ -130,7 +129,7 @@ if __name__ == "__main__": options, args = parser.parse_args() - if not args and not (options.all or options.sagenb or options.new): + if not args and not (options.all or options.new): parser.print_help() sys.exit(2) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index a90b45981bb..f2dadaec131 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -94,7 +94,6 @@ def __init__(self, **kwds): self.memlimit = 0 self.all = False self.logfile = None - self.sagenb = False self.long = False self.warn_long = None self.randorder = None @@ -326,7 +325,7 @@ def __init__(self, options, args): options.timeout *= 2 if options.nthreads == 0: options.nthreads = int(os.getenv('SAGE_NUM_THREADS_PARALLEL',1)) - if options.failed and not (args or options.new or options.sagenb): + if options.failed and not (args or options.new): # If the user doesn't specify any files then we rerun all failed files. options.all = True if options.global_iterations == 0: @@ -656,7 +655,7 @@ def create_run_id(self): def add_files(self): r""" - Checks for the flags '--all', '--new' and '--sagenb'. + Checks for the flags '--all' and '--new'. For each one present, this function adds the appropriate directories and files to the todo list. @@ -679,15 +678,6 @@ def add_files(self): sage: DC = DocTestController(DD, []) sage: DC.add_files() Doctesting ... - - :: - - sage: DD = DocTestDefaults(sagenb = True) - sage: DC = DocTestController(DD, []) - sage: DC.add_files() # py2 # optional - sagenb - Doctesting the Sage notebook. - sage: DC.files[0][-6:] # py2 # optional - sagenb - 'sagenb' """ opj = os.path.join from sage.env import SAGE_SRC, SAGE_DOC_SRC, SAGE_ROOT, SAGE_ROOT_GIT @@ -706,7 +696,6 @@ def all_files(): if have_git: self.files.append(opj(SAGE_SRC, 'sage_setup')) self.files.append(SAGE_DOC_SRC) - self.options.sagenb = True if self.options.all or (self.options.new and not have_git): self.log("Doctesting entire Sage library.") @@ -732,18 +721,6 @@ def all_files(): filename.endswith(".pyx") or filename.endswith(".rst"))): self.files.append(os.path.relpath(opj(SAGE_ROOT,filename))) - if self.options.sagenb: - if not PythonModule('sagenb').is_present(): - if not self.options.all: - self.log("Skipping doctesting of the Sage notebook: " - "not installed on Python 3") - return - - if not self.options.all: - self.log("Doctesting the Sage notebook.") - from pkg_resources import Requirement, working_set - sagenb_loc = working_set.find(Requirement.parse('sagenb')).location - self.files.append(opj(sagenb_loc, 'sagenb')) def expand_files_into_sources(self): r""" @@ -1025,10 +1002,10 @@ def _assemble_cmd(self): """ cmd = "sage-runtests --serial " opt = dict_difference(self.options.__dict__, DocTestDefaults().__dict__) - for o in ("all", "sagenb"): + for o in ("all",): if o in opt: raise ValueError("You cannot run gdb/valgrind on the whole sage%s library"%("" if o == "all" else "nb")) - for o in ("all", "sagenb", "long", "force_lib", "verbose", "failed", "new"): + for o in ("all", "long", "force_lib", "verbose", "failed", "new"): if o in opt: cmd += "--%s "%o for o in ("timeout", "memlimit", "randorder", "stats_path"): From da4196661ec127749671f7ec3470dd5b5154f167 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 22 Jun 2020 16:30:58 -0700 Subject: [PATCH 101/204] src/sage/doctest/control.py: Simplify singleton loop --- src/sage/doctest/control.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index f2dadaec131..cd5ab598458 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -1002,9 +1002,8 @@ def _assemble_cmd(self): """ cmd = "sage-runtests --serial " opt = dict_difference(self.options.__dict__, DocTestDefaults().__dict__) - for o in ("all",): - if o in opt: - raise ValueError("You cannot run gdb/valgrind on the whole sage%s library"%("" if o == "all" else "nb")) + if "all" in opt: + raise ValueError("You cannot run gdb/valgrind on the whole sage library") for o in ("all", "long", "force_lib", "verbose", "failed", "new"): if o in opt: cmd += "--%s "%o From fc9f90399488962e2e09114ee34e95b19944b1fe Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Tue, 23 Jun 2020 10:08:45 -0700 Subject: [PATCH 102/204] new methods Nk_ij and N_ijk, and doc revision --- src/sage/combinat/root_system/fusion_ring.py | 72 +++++++++++++++++--- 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index f0231a1fdaf..261cd77b428 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -90,7 +90,7 @@ class FusionRing(WeylCharacterRing): [I0, Y1, Y2, X, Xp, Z] To reset the labels and the order to their defaults, - you may run `fusion_labels` with no parameter: + you may run :meth:`fusion_labels` with no parameter:: sage: B22.fusion_labels() sage: [B22(x) for x in B22.get_order()] @@ -128,9 +128,9 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug @lazy_attribute def _q_field(self): """ - The cyclotomic field of 4l-th roots of unity, where - l is the fusion_l of the category (see above). Call this - lazy attribute via the method `self.q_field()`. + The cyclotomic field of `4\ell`-th roots of unity, where + `\ell` is computed by :meth:`fusion_l`. Call this + lazy attribute via the method :meth:`q_field()`. EXAMPLES:: @@ -144,8 +144,8 @@ def _q_field(self): def q_field(self): """ - Return the cyclotomic field of 4l-th roots of unity, where - `l` is the ``fusion_l`` of the category (see above). + Return the cyclotomic field of `4\ell`-th roots of unity, where + `\ell` is computed by :meth:`fusion_l`. This field contains the twists, categorical dimensions, and the entries of the S-matrix. @@ -172,10 +172,10 @@ def get_order(self): sage: [A14(x) for x in A14.get_order()] [A14(0), A14(4), A14(1), A14(3), A14(2)] - This duplicates :meth:`get_order` from `combinat.free_module`. - However unlike the `combinat.free_module` method with the same - name this `get_order` is not cached. Caching of get_order causes - inconsistent results after calling `set_order`. + This duplicates :meth:`get_order` from :mod:`combinat.free_module`. + However unlike the :mod:`combinat.free_module` method with the same + name this :meth:`get_order` is not cached. Caching of :meth:`get_order` causes + inconsistent results after calling :meth:`set_order`. """ if self._order is None: self.set_order(self.basis().keys().list()) @@ -214,7 +214,7 @@ def fusion_l(self): and `h^\vee` denotes the dual Coxeter number of the underlying Lie algebra. - This value is used to define the associated root of unity q. + This value is used to define the associated root of unity ``q``. EXAMPLES:: @@ -261,6 +261,55 @@ def q_dims(self): b = self.basis() return [b[x].q_dimension() for x in self.get_order()] + def N_ijk(self, elt_i, elt_j, elt_k): + """ + INPUT: + + - ``elt_i``, ``elt_j``, ``elt_k`` -- elements of the fusion basis + + Returns the symmetric fusion coefficient `N_{ijk}`. This + is the same as `N_{ij}^{k\\ast}`, where $N_{ij}^k$ are the + structure coefficients of the ring (see :meth:`Nk_ij`), + and `k\\ast`` denotes the dual element. The coefficient `N_{ijk}` + is unchanged under permutations of the three basis vectors. + + EXAMPLES:: + + sage: G23=FusionRing("G2",3) + sage: G23.fusion_labels("g") + sage: b = G23.basis().list(); b + [g0, g1, g2, g3, g4, g5] + sage: [(x,y,z) for x in b for y in b for z in b if G23.N_ijk(x,y,z)>1] + [(g3, g3, g3), (g3, g3, g4), (g3, g4, g3), (g4, g3, g3)] + sage: all([G23.N_ijk(x,y,z)==G23.N_ijk(y,z,x) for x in b for y in b for z in b]) + True + sage: all([G23.N_ijk(x,y,z)==G23.N_ijk(y,x,z) for x in b for y in b for z in b]) + True + + """ + return (elt_i*elt_j).monomial_coefficients().get(elt_k.dual().weight(),0) + + def Nk_ij(self, elt_i, elt_j, elt_k): + """ + Returns the fusion coefficient `N^k_{ij}`. These are + the structure coefficients of the fusion ring, so + + .. MATH:: + + i*j = \sum_k N_{ij}^k\,k + + as the example shows. + + EXAMPLES:: + + sage: A22=FusionRing("A2",2) + sage: b = A22.basis().list() + sage: all(x*y == sum(A22.Nk_ij(x,y,k)*k for k in b) for x in b for y in b) + True + + """ + return (elt_i*elt_j).monomial_coefficients().get(elt_k.weight(),0) + def s_ij(self, elt_i, elt_j): """ Return the element of the S-matrix of this FusionRing corresponding to @@ -281,6 +330,7 @@ def s_ij(self, elt_i, elt_j): K = self.q_field() q = K.gen() ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) + return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) def s_matrix(self): From 75d6b1bbb0a99bc526413c954eff7ed3a22e7f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 23 Jun 2020 20:32:03 +0200 Subject: [PATCH 103/204] some simplifications in Cython code for Moebius + Coxeter matrices --- src/sage/combinat/posets/hasse_cython.pyx | 46 +++++++++++------------ 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/src/sage/combinat/posets/hasse_cython.pyx b/src/sage/combinat/posets/hasse_cython.pyx index 258ab3d4e73..e7a5cbf251f 100644 --- a/src/sage/combinat/posets/hasse_cython.pyx +++ b/src/sage/combinat/posets/hasse_cython.pyx @@ -12,7 +12,7 @@ Some fast computations for finite posets # https://www.gnu.org/licenses/ # **************************************************************************** -from cysignals.signals cimport sig_str, sig_off +from cysignals.signals cimport sig_str, sig_off, sig_check from sage.libs.flint.fmpz cimport * from sage.libs.flint.fmpz_mat cimport * @@ -20,6 +20,7 @@ from sage.libs.flint.fmpz_mat cimport * from sage.rings.integer_ring import ZZ from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense from sage.matrix.constructor import Matrix +from sage.matrix.matrix_space import MatrixSpace cpdef Matrix_integer_dense moebius_matrix_fast(list positions): @@ -48,25 +49,23 @@ cpdef Matrix_integer_dense moebius_matrix_fast(list positions): sage: moebius_matrix_fast(D) 42 x 42 dense matrix over Integer Ring (...) """ - cdef fmpz_mat_t M cdef Matrix_integer_dense A cdef Py_ssize_t n = len(positions) cdef Py_ssize_t i cdef int j, k + A = Matrix_integer_dense.__new__(Matrix_integer_dense, + MatrixSpace(ZZ, n, n), None, None, None) sig_str("FLINT exception") - fmpz_mat_init(M, n, n) sig_off() - fmpz_mat_one(M) + fmpz_mat_one(A._matrix) for i in range(n - 1, -1, -1): + sig_check() for j in positions[i]: if j != i: for k in positions[j]: - fmpz_sub(fmpz_mat_entry(M, i, k), - fmpz_mat_entry(M, i, k), - fmpz_mat_entry(M, j, k)) - A = Matrix(ZZ, n, n) - fmpz_mat_set(A._matrix, M) - fmpz_mat_clear(M) + fmpz_sub(fmpz_mat_entry(A._matrix, i, k), + fmpz_mat_entry(A._matrix, i, k), + fmpz_mat_entry(A._matrix, j, k)) return A @@ -96,31 +95,28 @@ cpdef Matrix_integer_dense coxeter_matrix_fast(list positions): sage: coxeter_matrix_fast(D) 42 x 42 dense matrix over Integer Ring (...) """ - cdef fmpz_mat_t M cdef Matrix_integer_dense A cdef Py_ssize_t n = len(positions) cdef Py_ssize_t i cdef int j, k - sig_str("FLINT exception") - fmpz_mat_init(M, n, n) - sig_off() - fmpz_mat_one(M) + A = Matrix_integer_dense.__new__(Matrix_integer_dense, + MatrixSpace(ZZ, n, n), None, None, None) + fmpz_mat_one(A._matrix) for i in range(n - 1, -1, -1): + sig_check() for j in positions[i]: if j != i: for k in positions[j]: - fmpz_sub(fmpz_mat_entry(M, k, i), - fmpz_mat_entry(M, k, i), - fmpz_mat_entry(M, k, j)) + fmpz_sub(fmpz_mat_entry(A._matrix, k, i), + fmpz_mat_entry(A._matrix, k, i), + fmpz_mat_entry(A._matrix, k, j)) for i in range(n): + sig_check() for j in positions[i]: if j != i: for k in range(n): - fmpz_add(fmpz_mat_entry(M, i, k), - fmpz_mat_entry(M, i, k), - fmpz_mat_entry(M, j, k)) - fmpz_mat_scalar_mul_si(M, M, -1) - A = Matrix(ZZ, n, n) - fmpz_mat_set(A._matrix, M) - fmpz_mat_clear(M) + fmpz_add(fmpz_mat_entry(A._matrix, i, k), + fmpz_mat_entry(A._matrix, i, k), + fmpz_mat_entry(A._matrix, j, k)) + fmpz_mat_scalar_mul_si(A._matrix, A._matrix, -1) return A From 729b4142d45e87f25ae2abbd6eb9ca87c06cd9d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 23 Jun 2020 20:37:21 +0200 Subject: [PATCH 104/204] some details --- src/sage/combinat/posets/hasse_cython.pyx | 1 - src/sage/combinat/posets/hasse_diagram.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/combinat/posets/hasse_cython.pyx b/src/sage/combinat/posets/hasse_cython.pyx index e7a5cbf251f..9b42430a543 100644 --- a/src/sage/combinat/posets/hasse_cython.pyx +++ b/src/sage/combinat/posets/hasse_cython.pyx @@ -19,7 +19,6 @@ from sage.libs.flint.fmpz_mat cimport * from sage.rings.integer_ring import ZZ from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense -from sage.matrix.constructor import Matrix from sage.matrix.matrix_space import MatrixSpace diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index 8b3dd800451..e2a698798c4 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -976,7 +976,7 @@ def moebius_function_matrix(self, algorithm='cython'): OUTPUT: - a dense for the algorithm ``cython``, a sparse matrix otherwise + a dense matrix for the algorithm ``cython``, a sparse matrix otherwise .. NOTE:: From 90a8d5b5877953f6489b50d241470fd33ecc5beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 23 Jun 2020 20:50:06 +0200 Subject: [PATCH 105/204] more details in trac 29914 --- src/sage/combinat/posets/hasse_cython.pyx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/posets/hasse_cython.pyx b/src/sage/combinat/posets/hasse_cython.pyx index 9b42430a543..6e327c4df86 100644 --- a/src/sage/combinat/posets/hasse_cython.pyx +++ b/src/sage/combinat/posets/hasse_cython.pyx @@ -11,14 +11,14 @@ Some fast computations for finite posets # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** - -from cysignals.signals cimport sig_str, sig_off, sig_check +from cysignals.signals cimport sig_check from sage.libs.flint.fmpz cimport * from sage.libs.flint.fmpz_mat cimport * -from sage.rings.integer_ring import ZZ from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense + +from sage.rings.integer_ring import ZZ from sage.matrix.matrix_space import MatrixSpace @@ -54,8 +54,6 @@ cpdef Matrix_integer_dense moebius_matrix_fast(list positions): cdef int j, k A = Matrix_integer_dense.__new__(Matrix_integer_dense, MatrixSpace(ZZ, n, n), None, None, None) - sig_str("FLINT exception") - sig_off() fmpz_mat_one(A._matrix) for i in range(n - 1, -1, -1): sig_check() From 18aae7f8874d6e05d925ebec99ccbe4585f18683 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 23 Jun 2020 18:29:00 -0700 Subject: [PATCH 106/204] sage_setup.command.sage_build_cython: Fix up - add list brackets --- src/sage_setup/command/sage_build_cython.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage_setup/command/sage_build_cython.py b/src/sage_setup/command/sage_build_cython.py index 5ee0d3baef2..3fc2d1cb548 100644 --- a/src/sage_setup/command/sage_build_cython.py +++ b/src/sage_setup/command/sage_build_cython.py @@ -29,11 +29,11 @@ # Set by build/bin/sage-build-env-config. Empty if the system package is used. gmp_prefix = os.environ.get("SAGE_GMP_PREFIX", "") if gmp_prefix: - lib_headers["gmp"] = os.path.join(gmp_prefix, 'include', 'gmp.h') # cf. #8664, #9896 - lib_headers["gmpxx"] = os.path.join(gmp_prefix, 'include', 'gmpxx.h') + lib_headers["gmp"] = [os.path.join(gmp_prefix, 'include', 'gmp.h')] # cf. #8664, #9896 + lib_headers["gmpxx"] = [os.path.join(gmp_prefix, 'include', 'gmpxx.h')] ntl_prefix = os.environ.get("SAGE_NTL_PREFIX", "") if ntl_prefix: - lib_headers["ntl"] = os.path.join(ntl_prefix, 'include', 'NTL', 'config.h') + lib_headers["ntl"] = [os.path.join(ntl_prefix, 'include', 'NTL', 'config.h')] # Manually add -fno-strict-aliasing, which is needed to compile Cython # and disappears from the default flags if the user has set CFLAGS. From 4c37b7430d01b01c9de3cf026163c8205a4f92dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 24 Jun 2020 10:10:46 +0200 Subject: [PATCH 107/204] simplify the Seifert matrix code for links --- src/sage/knots/link.py | 75 ++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index 52ceaf4253b..9ab236bd7c3 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -423,14 +423,14 @@ def arcs(self, presentation='pd'): elif presentation == 'gauss_code': res = [] for comp in self.gauss_code(): - if not any(i<0 for i in comp): + if not any(i < 0 for i in comp): res.append(comp) else: rescom = [] par = [] for i in comp: par.append(i) - if i<0: + if i < 0: rescom.append(copy(par)) par = [i] rescom[0] = par + rescom[0] @@ -795,7 +795,7 @@ def _directions_of_edges(self): tails[C[2]] = C a = C[2] D = C - while not a in heads: + while a not in heads: next_crossing = [x for x in pd_code if a in x and x != D] if not next_crossing: heads[a] = D @@ -819,7 +819,7 @@ def _directions_of_edges(self): if a in x: D = x break - while not a in heads: + while a not in heads: tails[a] = D for x in pd_code: if a in x and x != D: @@ -1021,12 +1021,14 @@ def _enhanced_states(self): G = Graph() for j, cr in enumerate(crossings): n = nmax + j - if not v[j]: # For negative crossings, we go from undercrossings to the left + if not v[j]: + # For negative crossings, we go from undercrossings to the left G.add_edge((cr[3], cr[0], n), cr[0]) G.add_edge((cr[3], cr[0], n), cr[3]) G.add_edge((cr[1], cr[2], n), cr[2]) G.add_edge((cr[1], cr[2], n), cr[1]) - else: # positive crossings, from undercrossing to the right + else: + # positive crossings, from undercrossing to the right G.add_edge((cr[0], cr[1], n), cr[0]) G.add_edge((cr[0], cr[1], n), cr[1]) G.add_edge((cr[2], cr[3], n), cr[2]) @@ -1037,10 +1039,10 @@ def _enhanced_states(self): jmin = writhe + iindex - len(sm) jmax = writhe + iindex + len(sm) smoothings.append((tuple(v), sm, iindex, jmin, jmax)) - states = [] # we got all the smoothings, now find all the states + states = [] # we got all the smoothings, now find all the states for sm in smoothings: for k in range(len(sm[1])+1): - for circpos in combinations(sorted(sm[1]), k): # Add each state + for circpos in combinations(sorted(sm[1]), k): # Add each state circneg = sm[1].difference(circpos) j = writhe + sm[2] + len(circpos) - len(circneg) states.append((sm[0], tuple(sorted(circneg)), tuple(circpos), sm[2], j)) @@ -1082,7 +1084,7 @@ def _khovanov_homology_cached(self, height, ring=ZZ): ncross = len(crossings) states = [(_0, set(_1), set(_2), _3, _4) for (_0, _1, _2, _3, _4) in self._enhanced_states()] - bases = {} # arrange them by (i,j) + bases = {} # arrange them by (i,j) for st in states: i, j = st[3], st[4] if j == height: @@ -1102,7 +1104,7 @@ def _khovanov_homology_cached(self, height, ring=ZZ): difs = [index for index,value in enumerate(V1[0]) if value != V20[index]] if len(difs) == 1 and not (V2[2].intersection(V1[1]) or V2[1].intersection(V1[2])): m[ii,jj] = (-1)**sum(V2[0][x] for x in range(difs[0]+1, ncross)) - #Here we have the matrix constructed, now we have to put it in the dictionary of complexes + # Here we have the matrix constructed, now we have to put it in the dictionary of complexes else: m = matrix(ring, len(bases[(i,j)]), 0) complexes[i] = m.transpose() @@ -1570,39 +1572,25 @@ def seifert_matrix(self): """ x = self._braid_word_components_vector() h = self._homology_generators() - hl = len(h) - A = matrix(ZZ, hl, hl) indices = [i for i, hi in enumerate(h) if hi] - for i in indices: + N = len(indices) + A = matrix(ZZ, N, N, 0) + for ni, i in enumerate(indices): hi = h[i] - for j in range(i, hl): - if i == j: - A[i, j] = -(x[i] + x[hi]).sign() - elif hi > h[j]: - A[i, j] = 0 - A[j, i] = 0 - elif hi < j: - A[i, j] = 0 - A[j, i] = 0 - elif hi == j: + A[ni, ni] = -(x[i] + x[hi]).sign() + for nj in range(ni + 1, N): + j = indices[nj] + if hi > h[j] or hi < j: + continue + if hi == j: if x[j] > 0: - A[i, j] = 0 - A[j, i] = 1 + A[nj, ni] = 1 else: - A[i, j] = -1 - A[j, i] = 0 - elif abs(abs(x[i]) - abs(x[j])) > 1: - A[i, j] = 0 + A[ni, nj] = -1 elif abs(x[i]) - abs(x[j]) == 1: - A[i, j] = 0 - A[j, i] = -1 + A[nj, ni] = -1 elif abs(x[j]) - abs(x[i]) == 1: - A[i, j] = 1 - A[j, i] = 0 - else: # for debugging - A[i, j] = 2 - A[j, i] = 2 - A = A.matrix_from_rows_and_columns(indices, indices) + A[ni, nj] = 1 A.set_immutable() return A @@ -1851,6 +1839,7 @@ def alexander_polynomial(self, var='t'): t = R.gen() seifert_matrix = self.seifert_matrix() f = (seifert_matrix - t * seifert_matrix.transpose()).determinant() + # could we use a charpoly here ? or faster determinant ? if f != 0: exp = f.exponents() return t ** ((-max(exp) - min(exp)) // 2) * f @@ -1998,7 +1987,7 @@ def seifert_circles(self): result = [] # detect looped segments. They must be their own seifert circles for a in available_segments: - if any(C.count(a)>1 for C in self.pd_code()): + if any(C.count(a) > 1 for C in self.pd_code()): result.append([a]) # remove the looped segments from the available for a in result: @@ -2011,7 +2000,7 @@ def seifert_circles(self): else: C = heads[a] par = [] - while not a in par: + while a not in par: par.append(a) posnext = C[(C.index(a) + 1) % 4] if tails[posnext] == C and not [posnext] in result: @@ -2099,7 +2088,7 @@ def regions(self): while available_edges: edge = available_edges.pop() region = [] - while not edge in region: + while edge not in region: region.append(edge) if edge > 0: cros = heads[edge] @@ -3054,7 +3043,7 @@ def plot(self, gap=0.1, component_gap=0.5, solver=None, rev = segments[-e][1:] rev.reverse() sig = sign(s[edges.index(-e)]) - nregion+=[[a, -sig] for a in rev] + nregion += [[a, -sig] for a in rev] nregion.append([segments[-e][0], 1]) nregions.append(nregion) N = max(segments) + 1 @@ -3182,8 +3171,8 @@ def plot(self, gap=0.1, component_gap=0.5, solver=None, y1 = y0 elif direction == 3: x1 = x0 - y1 = y0 -l - im.append(([[x0,y0],[x1,y1]], l, direction)) + y1 = y0 - l + im.append(([[x0, y0], [x1, y1]], l, direction)) direction = (direction + turn) % 4 x0 = x1 y0 = y1 From f5b983168993909a0ba713d4b72a8c33fc23dcc2 Mon Sep 17 00:00:00 2001 From: Eric Gourgoulhon Date: Wed, 24 Jun 2020 11:12:05 +0200 Subject: [PATCH 108/204] diff() acting on chart functions and differentiable forms --- src/sage/calculus/functional.py | 20 ++++++++++ src/sage/manifolds/chart_func.py | 26 +++++++++---- .../manifolds/differentiable/diff_form.py | 37 ++++++++++++------- .../manifolds/differentiable/scalarfield.py | 20 +++++----- 4 files changed, 73 insertions(+), 30 deletions(-) diff --git a/src/sage/calculus/functional.py b/src/sage/calculus/functional.py index feef8446e16..0864be59891 100644 --- a/src/sage/calculus/functional.py +++ b/src/sage/calculus/functional.py @@ -126,6 +126,26 @@ def derivative(f, *args, **kwds): 80*u^3*v^3 sage: derivative(f, [u, v, v]) 80*u^3*v^3 + + We differentiate a scalar field on a manifold:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart() + sage: f = M.scalar_field(x^2*y, name='f') + sage: derivative(f) + 1-form df on the 2-dimensional differentiable manifold M + sage: derivative(f).display() + df = 2*x*y dx + x^2 dy + + We differentiate a differentiable form, getting its exterior derivative:: + + sage: a = M.one_form(-y, x, name='a'); a.display() + a = -y dx + x dy + sage: derivative(a) + 2-form da on the 2-dimensional differentiable manifold M + sage: derivative(a).display() + da = 2 dx/\dy + """ try: return f.derivative(*args, **kwds) diff --git a/src/sage/manifolds/chart_func.py b/src/sage/manifolds/chart_func.py index 9874cbdc9ca..97eaff61f8d 100644 --- a/src/sage/manifolds/chart_func.py +++ b/src/sage/manifolds/chart_func.py @@ -912,7 +912,7 @@ def copy(self): resu._order = self._order return resu - def diff(self, coord): + def derivative(self, coord): r""" Partial derivative with respect to a coordinate. @@ -937,28 +937,38 @@ def diff(self, coord): sage: X. = M.chart(calc_method='SR') sage: f = X.function(x^2+3*y+1); f x^2 + 3*y + 1 - sage: f.diff(x) + sage: f.derivative(x) 2*x - sage: f.diff(y) + sage: f.derivative(y) 3 - Each partial derivatives is itself a chart function:: + An alias is ``diff``:: + + sage: f.diff(x) + 2*x + + Each partial derivative is itself a chart function:: sage: type(f.diff(x)) + The same result is returned by the function ``diff``:: + + sage: diff(f, x) + 2*x + An index can be used instead of the coordinate symbol:: sage: f.diff(0) 2*x - sage: f.diff(1) + sage: diff(f, 1) 3 The index range depends on the convention used on the chart's domain:: sage: M = Manifold(2, 'M', structure='topological', start_index=1) - sage: X. = M.chart(calc_method='sympy') - sage: f = X.function(x**2+3*y+1) + sage: X. = M.chart() + sage: f = X.function(x^2+3*y+1) sage: f.diff(0) Traceback (most recent call last): ... @@ -1007,6 +1017,8 @@ def diff(self, coord): else: return self._der[self._chart[:].index(coord)] + diff = derivative + def __eq__(self, other): r""" Comparison (equality) operator. diff --git a/src/sage/manifolds/differentiable/diff_form.py b/src/sage/manifolds/differentiable/diff_form.py index 56a11581cd1..417183adf62 100644 --- a/src/sage/manifolds/differentiable/diff_form.py +++ b/src/sage/manifolds/differentiable/diff_form.py @@ -163,6 +163,12 @@ class DiffForm(TensorField): sage: da.display(eV) da = -du/\dv + The exterior derivative can also be obtained by applying the function + ``diff`` to a differentiable form:: + + sage: diff(a) is a.exterior_derivative() + True + Another 1-form defined by its components in ``eU``:: sage: b = M.one_form(1+x*y, x^2, frame=eU, name='b') @@ -422,19 +428,16 @@ def exterior_derivative(self): True Instead of invoking the method :meth:`exterior_derivative`, one may - use the global function - :func:`~sage.manifolds.utilities.exterior_derivative` - or its alias :func:`~sage.manifolds.utilities.xder`:: + use the global function ``diff``:: - sage: from sage.manifolds.utilities import xder - sage: xder(a) is a.exterior_derivative() + sage: diff(a) is a.exterior_derivative() True Let us check Cartan's identity:: sage: v = M.vector_field({e_xy: [-y, x]}, name='v') sage: v.add_comp_by_continuation(e_uv, U.intersection(V), c_uv) - sage: a.lie_der(v) == v.contract(xder(a)) + xder(a(v)) # long time + sage: a.lie_der(v) == v.contract(diff(a)) + diff(a(v)) # long time True """ @@ -449,6 +452,9 @@ def exterior_derivative(self): resu._restrictions[dom] = rst.exterior_derivative() return resu + derivative = exterior_derivative # allows one to use functional notation, + # e.g. diff(a) for a.exterior_derivative() + def wedge(self, other): r""" Exterior product with another differential form. @@ -1046,7 +1052,7 @@ class DiffFormParal(FreeModuleAltForm, TensorFieldParal): no symmetry; antisymmetry: (0, 1) The exterior derivative of a differential form is obtained by means - of the :meth:`exterior_derivative`:: + of the method :meth:`exterior_derivative`:: sage: da = a.exterior_derivative() ; da 2-form dA on the 3-dimensional differentiable manifold R3 @@ -1059,6 +1065,11 @@ class DiffFormParal(FreeModuleAltForm, TensorFieldParal): sage: dab = ab.exterior_derivative() ; dab 3-form d(A/\B) on the 3-dimensional differentiable manifold R3 + or by applying the function ``diff`` to the differential form:: + + sage: diff(a) is a.exterior_derivative() + True + As a 3-form over a 3-dimensional manifold, ``d(A/\B)`` is necessarily proportional to the volume 3-form:: @@ -1319,12 +1330,9 @@ def exterior_derivative(self): True Instead of invoking the method :meth:`exterior_derivative`, one may - use the global function - :func:`~sage.manifolds.utilities.exterior_derivative` - or its alias :func:`~sage.manifolds.utilities.xder`:: + use the global function ``diff``:: - sage: from sage.manifolds.utilities import xder - sage: xder(a) is a.exterior_derivative() + sage: diff(a) is a.exterior_derivative() True The exterior derivative is nilpotent:: @@ -1339,7 +1347,7 @@ def exterior_derivative(self): Let us check Cartan's identity:: sage: v = M.vector_field(-y, x, t, z, name='v') - sage: a.lie_der(v) == v.contract(xder(a)) + xder(a(v)) # long time + sage: a.lie_der(v) == v.contract(diff(a)) + diff(a(v)) # long time True """ @@ -1399,6 +1407,9 @@ def exterior_derivative(self): resu._components[frame] = dc return resu + derivative = exterior_derivative # allows one to use functional notation, + # e.g. diff(a) for a.exterior_derivative() + def wedge(self, other): r""" Exterior product of ``self`` with another differential form. diff --git a/src/sage/manifolds/differentiable/scalarfield.py b/src/sage/manifolds/differentiable/scalarfield.py index 3116731150e..e8f7ae76a19 100644 --- a/src/sage/manifolds/differentiable/scalarfield.py +++ b/src/sage/manifolds/differentiable/scalarfield.py @@ -739,6 +739,12 @@ def differential(self): sage: f.differential() is df True + Instead of invoking the method :meth:`differential`, one may apply the + function ``diff`` to the scalar field:: + + sage: diff(f) is f.differential() + True + Since the exterior derivative of a scalar field (considered a 0-form) is nothing but its differential, ``exterior_derivative()`` is an alias of ``differential()``:: @@ -750,15 +756,6 @@ def differential(self): sage: latex(df) \mathrm{d}f - One may also use the function - :func:`~sage.manifolds.utilities.exterior_derivative` - or its alias :func:`~sage.manifolds.utilities.xder` instead - of the method ``exterior_derivative()``:: - - sage: from sage.manifolds.utilities import xder - sage: xder(f) is f.exterior_derivative() - True - Differential computed on a chart that is not the default one:: sage: c_uvw. = M.chart() @@ -808,7 +805,10 @@ def differential(self): diff_func[i, chart] = func.diff(i) return self._differential - exterior_derivative = differential + exterior_derivative = differential # a scalar field being a 0-form + derivative = differential # allows one to use functional notation, + # e.g. diff(f) for f.differential() + def lie_derivative(self, vector): r""" From 8cf91eca49d83fae744d881c6be5be3858836962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Wed, 24 Jun 2020 15:05:53 +0200 Subject: [PATCH 109/204] 29955: reduction from DLX to MILP --- src/sage/combinat/matrices/dancing_links.pyx | 113 +++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/src/sage/combinat/matrices/dancing_links.pyx b/src/sage/combinat/matrices/dancing_links.pyx index bfbc01819c9..c07d8fc06f4 100644 --- a/src/sage/combinat/matrices/dancing_links.pyx +++ b/src/sage/combinat/matrices/dancing_links.pyx @@ -1000,6 +1000,119 @@ cdef class dancing_linksWrapper: return None return [key for (key,val) in enumerate(solution, start=-1) if val] + @cached_method + def to_milp_solver(self, solver=None): + r""" + Return the mixed integer linear program (MILP) solver solving an + equivalent problem. + + See also :mod:`sage.numerical.mip.MixedIntegerLinearProgram`. + + INPUT: + + - ``solver`` -- string or ``None`` (default: ``None``), possible + values include ``'GLPK'``, ``'GLPK/exact'``, ``'Coin'``, + ``'CPLEX'``, ``'Gurobi'``, ``'CVXOPT'``, ``'PPL'``, + ``'InteractiveLP'``. + + OUTPUT: + + MixedIntegerLinearProgram instance + + EXAMPLES:: + + sage: from sage.combinat.matrices.dancing_links import dlx_solver + sage: rows = [[0,1,2], [0,2], [1], [3]] + sage: d = dlx_solver(rows) + sage: p,x = d.to_milp_solver() + sage: p + Boolean Program (no objective, 4 variables, 4 constraints) + sage: x + MIPVariable of dimension 1 + + Using some optional MILP solvers:: + + sage: d.to_milp_solver('gurobi') # optional gurobi sage_numerical_backends_gurobi + Boolean Program (no objective, 4 variables, 4 constraints) + + """ + from sage.numerical.mip import MixedIntegerLinearProgram + p = MixedIntegerLinearProgram(solver=solver) + + # x[i] == True iff i-th dlx row is in the solution + x = p.new_variable(binary=True) + + # Construction of the columns (transpose of the rows) + columns = [[] for _ in range(self.ncols())] + for i,row in enumerate(self.rows()): + for a in row: + columns[a].append(i) + + # Constraints: exactly one 1 in each column + for j,column in enumerate(columns): + S = p.sum(x[a] for a in column) + name = "one 1 in {}-th column".format(j) + p.add_constraint(S==1, name=name) + + return p,x + + def one_solution_using_milp_solver(self, solver=None): + r""" + Return a solution found using a MILP solver. + + INPUT: + + - ``solver`` -- string or ``None`` (default: ``None``), possible + values include ``'GLPK'``, ``'GLPK/exact'``, ``'Coin'``, + ``'CPLEX'``, ``'Gurobi'``, ``'CVXOPT'``, ``'PPL'``, + ``'InteractiveLP'``. + + OUTPUT: + + list of rows or ``None`` if no solution is found + + .. NOTE:: + + When comparing the time taken by method `one_solution`, have in + mind that `one_solution_using_milp_solver` first creates (and + caches) the MILP solver instance from the dancing links solver. + This copy of data may take many seconds depending on the size + of the problem. + + EXAMPLES:: + + sage: from sage.combinat.matrices.dancing_links import dlx_solver + sage: rows = [[0,1,2], [3,4,5], [0,1], [2,3,4,5], [0], [1,2,3,4,5]] + sage: d = dlx_solver(rows) + sage: solutions = [[0,1], [2,3], [4,5]] + sage: d.one_solution_using_milp_solver() in solutions + True + + Using optional solvers:: + + sage: s = d.one_solution_using_milp_solver('Gurobi') # optional -- Gurobi sage_numerical_backends_gurobi + sage: s in solutions # optional -- Gurobi sage_numerical_backends_gurobi + True + + When no solution is found:: + + sage: rows = [[0,1,2], [2,3,4,5], [0,1,2,3]] + sage: d = dlx_solver(rows) + sage: d.one_solution_using_milp_solver() is None + True + + """ + from sage.numerical.mip import MIPSolverException + p,x = self.to_milp_solver(solver) + try: + p.solve() + except MIPSolverException: + return None + else: + soln = p.get_values(x) + support = sorted(key for key in soln if soln[key]) + return support + def dlx_solver(rows): """ Internal-use wrapper for the dancing links C++ code. From b2f02481c09b7f54b9ad80aad5b9be493b16a22f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Wed, 24 Jun 2020 15:26:58 +0200 Subject: [PATCH 110/204] 29955: making the MILP variable indices correspond to dlx row indices --- src/sage/combinat/matrices/dancing_links.pyx | 23 ++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/matrices/dancing_links.pyx b/src/sage/combinat/matrices/dancing_links.pyx index c07d8fc06f4..2851e7c4512 100644 --- a/src/sage/combinat/matrices/dancing_links.pyx +++ b/src/sage/combinat/matrices/dancing_links.pyx @@ -1017,7 +1017,8 @@ cdef class dancing_linksWrapper: OUTPUT: - MixedIntegerLinearProgram instance + - MixedIntegerLinearProgram instance + - MIPVariable of dimension 1 EXAMPLES:: @@ -1030,6 +1031,24 @@ cdef class dancing_linksWrapper: sage: x MIPVariable of dimension 1 + In the reduction, the boolean variable x_i is True if and only if + the i-th row is in the solution:: + + sage: p.show() + Maximization: + + + Constraints: + one 1 in 0-th column: 1.0 <= x_0 + x_1 <= 1.0 + one 1 in 1-th column: 1.0 <= x_0 + x_2 <= 1.0 + one 1 in 2-th column: 1.0 <= x_0 + x_1 <= 1.0 + one 1 in 3-th column: 1.0 <= x_3 <= 1.0 + Variables: + x_0 is a boolean variable (min=0.0, max=1.0) + x_1 is a boolean variable (min=0.0, max=1.0) + x_2 is a boolean variable (min=0.0, max=1.0) + x_3 is a boolean variable (min=0.0, max=1.0) + Using some optional MILP solvers:: sage: d.to_milp_solver('gurobi') # optional gurobi sage_numerical_backends_gurobi @@ -1040,7 +1059,7 @@ cdef class dancing_linksWrapper: p = MixedIntegerLinearProgram(solver=solver) # x[i] == True iff i-th dlx row is in the solution - x = p.new_variable(binary=True) + x = p.new_variable(binary=True, indices=range(self.nrows())) # Construction of the columns (transpose of the rows) columns = [[] for _ in range(self.ncols())] From 9e64d1d86ab70a3653f7e8823f378fbad21f9aa7 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Wed, 24 Jun 2020 17:05:30 +0200 Subject: [PATCH 111/204] exteded bibds to allow \lambda \neq 1 --- src/sage/combinat/designs/bibd.py | 78 +++++++++++++++++-------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index b1545bd9a53..51c9350f538 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -15,7 +15,7 @@ :widths: 30, 70 :delim: | - :func:`balanced_incomplete_block_design` | Return a BIBD of parameters `v,k`. + :func:`balanced_incomplete_block_design` | Return a BIBD of parameters `v,k,\lambda`. :func:`BIBD_from_TD` | Return a BIBD through TD-based constructions. :func:`BIBD_from_difference_family` | Return the BIBD associated to the difference family ``D`` on the group ``G``. :func:`BIBD_from_PBD` | Return a `(v,k,1)`-BIBD from a `(r,K)`-PBD where `r=(v-1)/(k-1)`. @@ -61,24 +61,21 @@ from .group_divisible_designs import GroupDivisibleDesign from .designs_pyx import is_pairwise_balanced_design -def balanced_incomplete_block_design(v, k, existence=False, use_LJCR=False): +def balanced_incomplete_block_design(v, k, lambd=1, existence=False, use_LJCR=False): r""" - Return a BIBD of parameters `v,k`. + Return a BIBD of parameters `v,k, \lambda`. - A Balanced Incomplete Block Design of parameters `v,k` is a collection + A Balanced Incomplete Block Design of parameters `v,k,\lambda` is a collection `\mathcal C` of `k`-subsets of `V=\{0,\dots,v-1\}` such that for any two - distinct elements `x,y\in V` there is a unique element `S\in \mathcal C` + distinct elements `x,y\in V` there are `\lambda` elements `S\in \mathcal C` such that `x,y\in S`. - More general definitions sometimes involve a `\lambda` parameter, and we - assume here that `\lambda=1`. - For more information on BIBD, see the :wikipedia:`corresponding Wikipedia entry `. INPUT: - - ``v,k`` (integers) + - ``v,k,lambd`` (integers) - ``existence`` (boolean) -- instead of building the design, return: @@ -169,8 +166,18 @@ def balanced_incomplete_block_design(v, k, existence=False, use_LJCR=False): sage: designs.balanced_incomplete_block_design(21,6,existence=True) False + + Some BIBDs with `\lambda \neq 1`:: + + sage: designs.balanced_incomplete_block_design(176, 50, 14, existence=True) + True + sage: designs.balanced_incomplete_block_design(64,28,12) + (64,28,12)-Balanced Incomplete Block Design + sage: designs.balanced_incomplete_block_design(37,9,8) + (37,9,8)-Balanced Incomplete Block Design + sage: designs.balanced_incomplete_block_design(15,7,3) + (15,7,3)-Balanced Incomplete Block Design """ - lmbd = 1 # Trivial BIBD if v == 1: @@ -181,13 +188,13 @@ def balanced_incomplete_block_design(v, k, existence=False, use_LJCR=False): if k == v: if existence: return True - return BalancedIncompleteBlockDesign(v, [list(range(v))], check=False, copy=False) + return BalancedIncompleteBlockDesign(v, [list(range(v)) for _ in range(lambd)],lambd=lambd, check=False, copy=False) # Non-existence of BIBD if (v < k or k < 2 or - (v-1) % (k-1) != 0 or - (v*(v-1)) % (k*(k-1)) != 0 or + (lambd*(v-1)) % (k-1) != 0 or + (lambd*v*(v-1)) % (k*(k-1)) != 0 or # From the Handbook of combinatorial designs: # # With lambda>1 other exceptions are @@ -195,25 +202,24 @@ def balanced_incomplete_block_design(v, k, existence=False, use_LJCR=False): (k==6 and v in [36,46]) or (k==7 and v == 43) or # Fisher's inequality - (v*(v-1))/(k*(k-1)) < v): + (lambd*v*(v-1))/(k*(k-1)) < v): if existence: return False - raise EmptySetError("There exists no ({},{},{})-BIBD".format(v,k,lmbd)) + raise EmptySetError("There exists no ({},{},{})-BIBD".format(v,k,lambd)) if k == 2: if existence: return True - from itertools import combinations - return BalancedIncompleteBlockDesign(v, combinations(list(range(v)),2), check=False, copy=True) - if k == 3: + return BalancedIncompleteBlockDesign(v, [[x, y] for _ in range(lambd) for x in range(v) for y in range(v) if x != y], lambd=lambd, check=False, copy=True) + if k == 3 and lambd == 1: if existence: return v%6 == 1 or v%6 == 3 return steiner_triple_system(v) - if k == 4: + if k == 4 and lambd == 1: if existence: return v%12 == 1 or v%12 == 4 return BalancedIncompleteBlockDesign(v, v_4_1_BIBD(v), copy=False) - if k == 5: + if k == 5 and lambd == 1: if existence: return v%20 == 1 or v%20 == 5 return BalancedIncompleteBlockDesign(v, v_5_1_BIBD(v), copy=False) @@ -221,39 +227,39 @@ def balanced_incomplete_block_design(v, k, existence=False, use_LJCR=False): from .difference_family import difference_family from .database import BIBD_constructions - if (v,k,1) in BIBD_constructions: + if (v, k, lambd) in BIBD_constructions: if existence: return True - return BlockDesign(v,BIBD_constructions[(v,k,1)](), copy=False) - if BIBD_from_arc_in_desarguesian_projective_plane(v,k,existence=True): + return BalancedIncompleteBlockDesign(v,BIBD_constructions[(v, k, lambd)](), lambd=lambd, copy=False) + if lambd == 1 and BIBD_from_arc_in_desarguesian_projective_plane(v, k, existence=True): if existence: return True - B = BIBD_from_arc_in_desarguesian_projective_plane(v,k) + B = BIBD_from_arc_in_desarguesian_projective_plane(v, k) return BalancedIncompleteBlockDesign(v, B, copy=False) - if BIBD_from_TD(v,k,existence=True) is True: + if lambd == 1 and BIBD_from_TD(v, k, existence=True) is True: if existence: return True - return BalancedIncompleteBlockDesign(v, BIBD_from_TD(v,k), copy=False) - if v == (k-1)**2+k and is_prime_power(k-1): + return BalancedIncompleteBlockDesign(v, BIBD_from_TD(v, k), copy=False) + if lambd == 1 and v == (k-1)**2+k and is_prime_power(k-1): if existence: return True from .block_design import projective_plane return BalancedIncompleteBlockDesign(v, projective_plane(k-1),copy=False) - if difference_family(v,k,existence=True) is True: + if difference_family(v, k, l=lambd, existence=True) is True: if existence: return True - G,D = difference_family(v,k) - return BalancedIncompleteBlockDesign(v, BIBD_from_difference_family(G,D,check=False), copy=False) - if use_LJCR: + G, D = difference_family(v, k, l=lambd) + return BalancedIncompleteBlockDesign(v, BIBD_from_difference_family(G, D, check=False), lambd=lambd, copy=False) + if lambd == 1 and use_LJCR: from .covering_design import best_known_covering_design_www - B = best_known_covering_design_www(v,k,2) + B = best_known_covering_design_www(v, k, 2) # Is it a BIBD or just a good covering ? - expected_n_of_blocks = binomial(v,2)//binomial(k,2) + expected_n_of_blocks = binomial(v, 2)//binomial(k, 2) if B.low_bd() > expected_n_of_blocks: if existence: return False - raise EmptySetError("There exists no ({},{},{})-BIBD".format(v,k,lmbd)) + raise EmptySetError("There exists no ({},{},{})-BIBD".format(v,k,lambd)) B = B.incidence_structure() if B.num_blocks() == expected_n_of_blocks: if existence: @@ -448,8 +454,8 @@ def BIBD_from_TD(v,k,existence=False): # Second construction elif ((v-1)%k == 0 and - balanced_incomplete_block_design((v-1)//k+1,k,existence=True) and - transversal_design(k,(v-1)//k,existence=True)): + balanced_incomplete_block_design((v-1)//k+1,k,existence=True) is True and + transversal_design(k,(v-1)//k,existence=True)) is True: if existence: return True From 29bfb87397ca7eca8014c0808d9cda7c3567288c Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Wed, 24 Jun 2020 08:58:40 -0700 Subject: [PATCH 112/204] revision of s_ij and self._nf. Doc revision --- src/doc/en/reference/references/index.rst | 6 ++++ src/sage/combinat/root_system/fusion_ring.py | 29 ++++++++++++++----- .../combinat/root_system/weyl_characters.py | 5 ++-- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 311720794a8..b67d72feee1 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -344,6 +344,9 @@ REFERENCES: **B** +.. [BK] Bakalov and Kirillov, Lectures on tensor categories and modular functors, + AMS (2001). + .. [Ba1994] Kaushik Basu. *The Traveler's Dilemma: Paradoxes of Rationality in Game Theory*. The American Economic Review (1994): 391-395. @@ -1946,6 +1949,9 @@ REFERENCES: 1981, Pages 108-125, ISSN 0097-3165,:doi:`10.1016/0097-3165(81)90007-8`. (http://www.sciencedirect.com/science/article/pii/0097316581900078) +.. [EGNO] Pavel Etingof, Shlomo Gelaki, Dmitri Nikshych and Victor Ostrik, + "Tensor Categories", AMS Mathematical Surveys and Monographs 205 (2015). + .. [EGHLSVY] Pavel Etingof, Oleg Golberg, Sebastian Hensel, Tiankai Liu, Alex Schwendner, Dmitry Vaintrob, Elena Yudovina, "Introduction to representation theory", diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 261cd77b428..e304c464436 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -39,9 +39,12 @@ class FusionRing(WeylCharacterRing): REFERENCES: + - [BK]_ Chapter 3 - [DFMS1996]_ Chapter 16 + - [EGNO]_ Chapter 8 - [Feingold2004]_ - [Fuchs1994]_ + - [Row2006]_ - [Walton1990]_ EXAMPLES:: @@ -214,7 +217,7 @@ def fusion_l(self): and `h^\vee` denotes the dual Coxeter number of the underlying Lie algebra. - This value is used to define the associated root of unity ``q``. + This value is used to define the associated root of unity `q=e^{i\\pi/\ell}`. EXAMPLES:: @@ -296,7 +299,7 @@ def Nk_ij(self, elt_i, elt_j, elt_k): .. MATH:: - i*j = \sum_k N_{ij}^k\,k + i*j = \sum_{k} N_{ij}^k k as the example shows. @@ -313,7 +316,14 @@ def Nk_ij(self, elt_i, elt_j, elt_k): def s_ij(self, elt_i, elt_j): """ Return the element of the S-matrix of this FusionRing corresponding to - the given elements. + the given elements. This is computed using the formula + + .. MATH:: + + s_{i,j} = \\frac{1}{\\theta_i\\theta_j}\\sum_k N_{ik}^j d_k\\theta_k + + where `\\theta_k` is the twist and `d_k` is the quantum + dimension. See [Row2006]_ equation (2.2) or [EGNO]_ Proposition 8.13.8. INPUT: @@ -325,13 +335,15 @@ def s_ij(self, elt_i, elt_j): sage: b=G21.basis() sage: [G21.s_ij(x,y) for x in b for y in b] [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] + """ l = self.fusion_l() K = self.q_field() q = K.gen() ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) - - return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) + mc = (elt_i.dual()*elt_j).monomial_coefficients() + return sum(self(k).q_dimension()*self.Nk_ij(elt_i,self(k),elt_j)*q**(2*l*self(k).twist()+ijtwist) for k in mc) + # return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) def s_matrix(self): r""" @@ -446,11 +458,12 @@ def weight(self): def twist(self): r""" - Compute the object's twist. Returns a rational number `h_X` such that - `e^{(i \pi h_X)}` is the twist of `X`. + Compute the object's twist. This returns a rational number `h` such that + `\theta = e^{i \pi h}` is the twist of ``self``. This method is + only available for simple objects. We compute the twists following p.7 of [Row2006]_, noting that the bilinear form - is normalized so that `\langle\alpha, \alpha\rangle = 2` for SHORT roots. + is normalized so that `\langle\alpha, \alpha\rangle = 2` for short roots. EXAMPLES:: diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index e96bea32753..674e00d3575 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -165,12 +165,13 @@ def next_level(wt): #TODO: implement conjugate functionality if ct[0] in ['A','D','E']: self._m_g = 1 - self._nf = 1 elif ct[0] in ['B', 'C', 'F']: self._m_g = 2 - self._nf = 2 else: self._m_g = 3 + if ct[0] in ['B','F']: + self._nf = 2 + else: self._nf = 1 h_check = ct.dual_coxeter_number() self._l = self._m_g * (self._k + h_check) From b6a7f0147272177b407d518dc9628c242086e757 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Wed, 24 Jun 2020 18:11:16 +0200 Subject: [PATCH 113/204] added some whitespaces --- src/sage/combinat/designs/bibd.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index 51c9350f538..f0472ffe9bc 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -176,7 +176,7 @@ def balanced_incomplete_block_design(v, k, lambd=1, existence=False, use_LJCR=Fa sage: designs.balanced_incomplete_block_design(37,9,8) (37,9,8)-Balanced Incomplete Block Design sage: designs.balanced_incomplete_block_design(15,7,3) - (15,7,3)-Balanced Incomplete Block Design + g(15,7,3)-Balanced Incomplete Block Design """ # Trivial BIBD @@ -205,7 +205,7 @@ def balanced_incomplete_block_design(v, k, lambd=1, existence=False, use_LJCR=Fa (lambd*v*(v-1))/(k*(k-1)) < v): if existence: return False - raise EmptySetError("There exists no ({},{},{})-BIBD".format(v,k,lambd)) + raise EmptySetError("There exists no ({},{},{})-BIBD".format(v, k, lambd)) if k == 2: if existence: @@ -259,7 +259,7 @@ def balanced_incomplete_block_design(v, k, lambd=1, existence=False, use_LJCR=Fa if B.low_bd() > expected_n_of_blocks: if existence: return False - raise EmptySetError("There exists no ({},{},{})-BIBD".format(v,k,lambd)) + raise EmptySetError("There exists no ({},{},{})-BIBD".format(v, k, lambd)) B = B.incidence_structure() if B.num_blocks() == expected_n_of_blocks: if existence: @@ -270,7 +270,7 @@ def balanced_incomplete_block_design(v, k, lambd=1, existence=False, use_LJCR=Fa if existence: return Unknown else: - raise NotImplementedError("I don't know how to build a ({},{},1)-BIBD!".format(v,k)) + raise NotImplementedError("I don't know how to build a ({},{},1)-BIBD!".format(v, k)) def steiner_triple_system(n): r""" @@ -438,8 +438,8 @@ def BIBD_from_TD(v,k,existence=False): """ # First construction if (v%k == 0 and - balanced_incomplete_block_design(v//k,k,existence=True) is True and - transversal_design(k,v//k,existence=True) is True): + balanced_incomplete_block_design(v//k, k, existence=True) is True and + transversal_design(k, v//k, existence=True) is True): if existence: return True From 90c4d47cccbb4ad31dd4835ddf3842ee0d694cfe Mon Sep 17 00:00:00 2001 From: Franco Saliola Date: Wed, 24 Jun 2020 14:19:10 -0400 Subject: [PATCH 114/204] cleanup optional tags & correct doctest output --- src/sage/combinat/matrices/dancing_links.pyx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/matrices/dancing_links.pyx b/src/sage/combinat/matrices/dancing_links.pyx index 2851e7c4512..b6247cce10c 100644 --- a/src/sage/combinat/matrices/dancing_links.pyx +++ b/src/sage/combinat/matrices/dancing_links.pyx @@ -922,7 +922,7 @@ cdef class dancing_linksWrapper: Using some optional SAT solvers:: - sage: x.to_sat_solver('cryptominisat') # optional cryptominisat + sage: x.to_sat_solver('cryptominisat') # optional - cryptominisat CryptoMiniSat solver: 4 variables, 7 clauses. """ @@ -982,8 +982,8 @@ cdef class dancing_linksWrapper: Using optional solvers:: - sage: s = d.one_solution_using_sat_solver('glucose') # optional glucose - sage: s in solutions # optional glucose + sage: s = d.one_solution_using_sat_solver('glucose') # optional - glucose + sage: s in solutions # optional - glucose True When no solution is found:: @@ -1051,8 +1051,9 @@ cdef class dancing_linksWrapper: Using some optional MILP solvers:: - sage: d.to_milp_solver('gurobi') # optional gurobi sage_numerical_backends_gurobi - Boolean Program (no objective, 4 variables, 4 constraints) + sage: d.to_milp_solver('gurobi') # optional - gurobi sage_numerical_backends_gurobi + (Boolean Program (no objective, 4 variables, 4 constraints), + MIPVariable of dimension 1) """ from sage.numerical.mip import MixedIntegerLinearProgram @@ -1109,8 +1110,8 @@ cdef class dancing_linksWrapper: Using optional solvers:: - sage: s = d.one_solution_using_milp_solver('Gurobi') # optional -- Gurobi sage_numerical_backends_gurobi - sage: s in solutions # optional -- Gurobi sage_numerical_backends_gurobi + sage: s = d.one_solution_using_milp_solver('gurobi') # optional - gurobi sage_numerical_backends_gurobi + sage: s in solutions # optional - gurobi sage_numerical_backends_gurobi True When no solution is found:: From 0661926ece85d45ed1018fa9d508dfcf7bb90add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 24 Jun 2020 22:14:37 +0200 Subject: [PATCH 115/204] add some safety check for numerical integrals up to infinity --- src/sage/calculus/integration.pyx | 129 ++++++++++++++++++------------ 1 file changed, 79 insertions(+), 50 deletions(-) diff --git a/src/sage/calculus/integration.pyx b/src/sage/calculus/integration.pyx index d0a5983eb60..1571cf873d7 100644 --- a/src/sage/calculus/integration.pyx +++ b/src/sage/calculus/integration.pyx @@ -70,7 +70,7 @@ def numerical_integral(func, a, b=None, algorithm='qag', max_points=87, params=[], eps_abs=1e-6, eps_rel=1e-6, rule=6): - r""" + r""" Return the numerical integral of the function on the interval from a to b and an error bound. @@ -241,40 +241,52 @@ def numerical_integral(func, a, b=None, Traceback (most recent call last): ... TypeError: unable to simplify to float approximation - """ - cdef double abs_err # step size - cdef double result - cdef int i - cdef int j - cdef double _a, _b - cdef PyFunctionWrapper wrapper # struct to pass information into GSL C function + Check for :trac:`15496`:: - if b is None or isinstance(a, (list, tuple)): - b = a[1] - a = a[0] + sage: f = x^2/exp(-1/(x^2+1))/(x^2+1) + sage: D = integrate(f,(x,-infinity,infinity),hold=True) + sage: D.n() + Traceback (most recent call last): + ... + ValueError: integral does not converge at -infinity + """ + cdef double abs_err # step size + cdef double result + cdef int i + cdef int j + cdef double _a, _b + cdef PyFunctionWrapper wrapper # struct to pass information into GSL C function + + if b is None or isinstance(a, (list, tuple)): + b = a[1] + a = a[0] - # The integral over a point is always zero - if a == b: - return (0.0, 0.0) + # The integral over a point is always zero + if a == b: + return (0.0, 0.0) - if not callable(func): + if not callable(func): # handle the constant case return (((b - a) * func), 0.0) - cdef gsl_function F - cdef gsl_integration_workspace* W - W=NULL + cdef gsl_function F + cdef gsl_integration_workspace* W + W = NULL - if not isinstance(func, FastDoubleFunc): + if not isinstance(func, FastDoubleFunc): + from sage.rings.infinity import Infinity try: if hasattr(func, 'arguments'): vars = func.arguments() else: vars = func.variables() - if len(vars) == 0: - # handle the constant case - return (((b - a) * func), 0.0) + except (AttributeError): + pass + else: + if not vars: + # handle the constant case + return (((b - a) * func), 0.0) if len(vars) != 1: if len(params) + 1 != len(vars): raise ValueError(("The function to be integrated depends on " @@ -285,70 +297,85 @@ def numerical_integral(func, a, b=None, to_sub = dict(zip(vars[1:], params)) func = func.subs(to_sub) - func = func._fast_float_(str(vars[0])) - except (AttributeError): - pass - if isinstance(func, FastDoubleFunc): + # sanity checks for integration up to infinity + v = str(vars[0]) + if a is -Infinity: + try: + ell = func.limit(**{v: -Infinity}) + except (AttributeError, ValueError): + pass + else: + if ell.is_numeric() and not ell.is_zero(): + raise ValueError('integral does not converge at -infinity') + if b is Infinity: + try: + ell = func.limit(**{v: Infinity}) + except (AttributeError, ValueError): + pass + else: + if ell.is_numeric() and not ell.is_zero(): + raise ValueError('integral does not converge at infinity') + func = func._fast_float_(v) + + if isinstance(func, FastDoubleFunc): F.function = c_ff F.params = func - elif not isinstance(func, compiled_integrand): + elif not isinstance(func, compiled_integrand): wrapper = PyFunctionWrapper() if not func is None: wrapper.the_function = func else: raise ValueError("No integrand defined") try: - if params == [] and len(sage_getargspec(wrapper.the_function)[0]) == 1: - wrapper.the_parameters=[] - elif params == [] and len(sage_getargspec(wrapper.the_function)[0]) > 1: + if not params and len(sage_getargspec(wrapper.the_function)[0]) == 1: + wrapper.the_parameters = [] + elif not params and len(sage_getargspec(wrapper.the_function)[0]) > 1: raise ValueError("Integrand has parameters but no parameters specified") - elif params!=[]: + elif params: wrapper.the_parameters = params except TypeError: - wrapper.the_function = eval("lambda x: func(x)", {'func':func}) + wrapper.the_function = eval("lambda x: func(x)", {'func': func}) wrapper.the_parameters = [] F.function = c_f F.params = wrapper + cdef size_t n + n = max_points - cdef size_t n - n = max_points + gsl_set_error_handler_off() - gsl_set_error_handler_off() - - if algorithm == "qng": + if algorithm == "qng": _a=a _b=b sig_on() gsl_integration_qng(&F, _a, _b, eps_abs, eps_rel, &result, &abs_err, &n) sig_off() - elif algorithm == "qag": - from sage.rings.infinity import Infinity - if a is -Infinity and b is +Infinity: + elif algorithm == "qag": + if a is -Infinity and b is +Infinity: W = gsl_integration_workspace_alloc(n) sig_on() gsl_integration_qagi(&F, eps_abs, eps_rel, n, W, &result, &abs_err) sig_off() - elif a is -Infinity: + elif a is -Infinity: _b = b W = gsl_integration_workspace_alloc(n) sig_on() gsl_integration_qagil(&F, _b, eps_abs, eps_rel, n, W, &result, &abs_err) sig_off() - elif b is +Infinity: + elif b is +Infinity: _a = a W = gsl_integration_workspace_alloc(n) sig_on() gsl_integration_qagiu(&F, _a, eps_abs, eps_rel, n, W, &result, &abs_err) sig_off() - else: + else: _a = a _b = b W = gsl_integration_workspace_alloc(n) @@ -357,7 +384,7 @@ def numerical_integral(func, a, b=None, sig_off() - elif algorithm == "qags": + elif algorithm == "qags": W = gsl_integration_workspace_alloc(n) sig_on() @@ -366,13 +393,14 @@ def numerical_integral(func, a, b=None, gsl_integration_qags(&F, _a, _b, eps_abs, eps_rel, n, W, &result, &abs_err) sig_off() - else: + else: raise TypeError("invalid integration algorithm") - if W != NULL: + if W != NULL: gsl_integration_workspace_free(W) - return result, abs_err + return result, abs_err + cdef double c_monte_carlo_f(double *t, size_t dim, void *params): cdef double value @@ -393,6 +421,7 @@ cdef double c_monte_carlo_f(double *t, size_t dim, void *params): return value + cdef double c_monte_carlo_ff(double *x, size_t dim, void *params): cdef double result ( params).call_c(x, &result) @@ -598,11 +627,11 @@ def monte_carlo_integral(func, xl, xu, size_t calls, algorithm='plain', wrapper = PyFunctionWrapper() wrapper.the_function = func - if params == [] and len(sage_getargspec(wrapper.the_function)[0]) == dim: + if not params and len(sage_getargspec(wrapper.the_function)[0]) == dim: wrapper.the_parameters = [] - elif params == [] and len(sage_getargspec(wrapper.the_function)[0]) > dim: + elif not params and len(sage_getargspec(wrapper.the_function)[0]) > dim: raise ValueError("Integrand has parameters but no parameters specified") - elif params != []: + elif params: wrapper.the_parameters = params wrapper.lx = [None] * dim From 539c1828dd17c90e5baa63d814f2c84419146f90 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 25 Jun 2020 00:36:43 -0700 Subject: [PATCH 116/204] build/make/install: Do not depend on src/bin/sage-version.sh --- build/bin/write-dockerfile.sh | 1 - build/make/install | 2 -- src/doc/en/developer/portability_testing.rst | 2 -- 3 files changed, 5 deletions(-) diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh index e298620f2ea..3620cd19d29 100755 --- a/build/bin/write-dockerfile.sh +++ b/build/bin/write-dockerfile.sh @@ -152,7 +152,6 @@ ADD Makefile VERSION.txt README.md bootstrap configure.ac sage ./ ADD src/doc/bootstrap src/doc/bootstrap ADD m4 ./m4 ADD build ./build -ADD src/bin/sage-version.sh src/bin/sage-version.sh $RUN ./bootstrap FROM bootstrapped as configured diff --git a/build/make/install b/build/make/install index 81ed1e8a264..93bf676fdb3 100755 --- a/build/make/install +++ b/build/make/install @@ -19,8 +19,6 @@ export SAGE_SHARE="$SAGE_LOCAL/share" export SAGE_PKGCONFIG="$SAGE_LOCAL/lib/pkgconfig" export SAGE_LOGS="$SAGE_ROOT/logs/pkgs" export SAGE_SPKG_INST="$SAGE_LOCAL/var/lib/sage/installed" -. "$SAGE_SRC"/bin/sage-version.sh -export SAGE_VERSION if [ -z "${SAGE_ORIG_PATH_SET}" ]; then SAGE_ORIG_PATH=$PATH && export SAGE_ORIG_PATH diff --git a/src/doc/en/developer/portability_testing.rst b/src/doc/en/developer/portability_testing.rst index f550d1d3e07..d4c625c5961 100644 --- a/src/doc/en/developer/portability_testing.rst +++ b/src/doc/en/developer/portability_testing.rst @@ -346,10 +346,8 @@ Then, to bootstrap and configure...:: ADD src/doc/bootstrap src/doc/bootstrap ADD m4 ./m4 ADD build ./build - ADD src/bin/sage-version.sh src/bin/sage-version.sh RUN ./bootstrap ADD src/bin src/bin - ADD src/Makefile.in src/Makefile.in ARG EXTRA_CONFIGURE_ARGS="" RUN ./configure --enable-build-as-root ${EXTRA_CONFIGURE_ARGS} || (cat config.log; exit 1) From 9a9705cc2292fe59816b3f3c0a9c7e8a14700406 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Thu, 25 Jun 2020 11:51:15 +0200 Subject: [PATCH 117/204] fixed typos --- src/sage/combinat/designs/bibd.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index f0472ffe9bc..6112f9e322d 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -162,7 +162,7 @@ def balanced_incomplete_block_design(v, k, lambd=1, existence=False, use_LJCR=Fa sage: designs.balanced_incomplete_block_design(1171,10) (1171,10,1)-Balanced Incomplete Block Design - And we know some inexistence results:: + And we know some nonexistence results:: sage: designs.balanced_incomplete_block_design(21,6,existence=True) False @@ -176,7 +176,7 @@ def balanced_incomplete_block_design(v, k, lambd=1, existence=False, use_LJCR=Fa sage: designs.balanced_incomplete_block_design(37,9,8) (37,9,8)-Balanced Incomplete Block Design sage: designs.balanced_incomplete_block_design(15,7,3) - g(15,7,3)-Balanced Incomplete Block Design + (15,7,3)-Balanced Incomplete Block Design """ # Trivial BIBD @@ -270,7 +270,7 @@ def balanced_incomplete_block_design(v, k, lambd=1, existence=False, use_LJCR=Fa if existence: return Unknown else: - raise NotImplementedError("I don't know how to build a ({},{},1)-BIBD!".format(v, k)) + raise NotImplementedError("I don't know how to build a ({},{},{})-BIBD!".format(v, k, lambd)) def steiner_triple_system(n): r""" From 437afc8a6d12509b31fd3c76466349695a02eef2 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Thu, 25 Jun 2020 14:59:12 +0200 Subject: [PATCH 118/204] fixed bug with k=2 --- src/sage/combinat/designs/bibd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index 6112f9e322d..1289c4edba7 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -210,7 +210,7 @@ def balanced_incomplete_block_design(v, k, lambd=1, existence=False, use_LJCR=Fa if k == 2: if existence: return True - return BalancedIncompleteBlockDesign(v, [[x, y] for _ in range(lambd) for x in range(v) for y in range(v) if x != y], lambd=lambd, check=False, copy=True) + return BalancedIncompleteBlockDesign(v, [[x, y] for _ in range(lambd) for x in range(v) for y in range(x+1, v) if x != y], lambd=lambd, check=False, copy=True) if k == 3 and lambd == 1: if existence: return v%6 == 1 or v%6 == 3 From cda861d170f5b777e77581cd79b232e93dea75e8 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Thu, 25 Jun 2020 15:11:42 -0700 Subject: [PATCH 119/204] verlinde formula, larger cyclotomic field, doc revision --- src/sage/combinat/root_system/fusion_ring.py | 91 ++++++++++++------- .../combinat/root_system/weyl_characters.py | 6 ++ 2 files changed, 64 insertions(+), 33 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index e304c464436..3b5c4de48e4 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -21,6 +21,7 @@ from sage.misc.misc import inject_variable from sage.rings.all import ZZ from sage.rings.number_field.number_field import CyclotomicField +from sage.misc.cachefunc import cached_method class FusionRing(WeylCharacterRing): r""" @@ -99,6 +100,41 @@ class FusionRing(WeylCharacterRing): sage: [B22(x) for x in B22.get_order()] [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] + The fusion ring has a number of methods that reflect its role + as the Grothendieck ring of a modular tensor category. These + include a twist method :meth:twist for its elements related + to the ribbon element, and the S-matrix :meth:`s_ij`. + + Let us check the Verlinde formula. This famous identity states + that + + .. MATH:: + + N_{ijk} = \sum_l \frac{S(i,\ell)\,S(j,\ell)\,S(k,\ell)}{S(I,\ell)} + + where ``I`` is the unit object. We may define a function that corresponds + to the right-hand side:: + + sage: def V(i,j,k): + ....: R = i.parent() + ....: return sum(R.s_ij(i,l)*R.s_ij(j,l)*R.s_ij(k,l)/R.s_ij(R.one(),l) for l in R.basis()) + + This does not produce ``self.N_ijk(i,j,k)`` exactly, because our S-matrix + is omitting a normalization factor. The following code to check the + Verlinde formula takes this into account:: + + sage: def test_verlinde(R): + ....: b0 = R.one() + ....: c = V(b0, b0, b0) + ....: return all(V(i,j,k)==c*R.N_ijk(i,j,k) for i in R.basis() for j in R.basis() for k in R.basis()) + + Every fusion ring should pass this test:: + + sage: test_verlinde(FusionRing("A2",1)) + True + sage: test_verlinde(FusionRing("B4",2)) + True + """ @staticmethod def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False): @@ -128,39 +164,23 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate) - @lazy_attribute - def _q_field(self): + def field(self): """ - The cyclotomic field of `4\ell`-th roots of unity, where - `\ell` is computed by :meth:`fusion_l`. Call this - lazy attribute via the method :meth:`q_field()`. + This returns a cyclotomic field large enough to + contain the `2l`-th roots of unity, as well as + all the S-matrix entries. EXAMPLES:: - sage: B22=FusionRing("B2",2) - sage: B22.q_field() + sage: FusionRing("A2",2).field() + Cyclotomic Field of order 60 and degree 16 + sage: FusionRing("B2",2).field() Cyclotomic Field of order 40 and degree 16 """ - self._K = CyclotomicField(4*self._l) - return self._K - - def q_field(self): - """ - Return the cyclotomic field of `4\ell`-th roots of unity, where - `\ell` is computed by :meth:`fusion_l`. - - This field contains the twists, categorical dimensions, and the entries of the - S-matrix. - - EXAMPLES:: - - sage: A11=FusionRing('A1',1) - sage: A11.q_field() - Cyclotomic Field of order 12 and degree 4 - """ - return self._q_field + return CyclotomicField(4*self._fg*self._l) + def get_order(self): """ This returns the weights of the basis vectors in a fixed order. @@ -217,7 +237,8 @@ def fusion_l(self): and `h^\vee` denotes the dual Coxeter number of the underlying Lie algebra. - This value is used to define the associated root of unity `q=e^{i\\pi/\ell}`. + This value is used to define the associated root `2\ell`-th + of unity `q=e^{i\pi/\ell}`. EXAMPLES:: @@ -264,6 +285,7 @@ def q_dims(self): b = self.basis() return [b[x].q_dimension() for x in self.get_order()] + @cached_method def N_ijk(self, elt_i, elt_j, elt_k): """ INPUT: @@ -292,6 +314,7 @@ def N_ijk(self, elt_i, elt_j, elt_k): """ return (elt_i*elt_j).monomial_coefficients().get(elt_k.dual().weight(),0) + @cached_method def Nk_ij(self, elt_i, elt_j, elt_k): """ Returns the fusion coefficient `N^k_{ij}`. These are @@ -313,6 +336,7 @@ def Nk_ij(self, elt_i, elt_j, elt_k): """ return (elt_i*elt_j).monomial_coefficients().get(elt_k.weight(),0) + @cached_method def s_ij(self, elt_i, elt_j): """ Return the element of the S-matrix of this FusionRing corresponding to @@ -337,17 +361,17 @@ def s_ij(self, elt_i, elt_j): [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] """ - l = self.fusion_l() - K = self.q_field() + l = self.fusion_l()*self._fg + K = self.field() q = K.gen() ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) mc = (elt_i.dual()*elt_j).monomial_coefficients() return sum(self(k).q_dimension()*self.Nk_ij(elt_i,self(k),elt_j)*q**(2*l*self(k).twist()+ijtwist) for k in mc) - # return sum(self(k).q_dimension()*q**(2*l*self(k).twist()+ijtwist) for k in (elt_i.dual()*elt_j).monomial_coefficients()) def s_matrix(self): r""" - Return the S-matrix of this FusionRing. + Return the S-matrix of this FusionRing. This is the matrix denoted + `\widetilde{s}` in [BK]_. It is not normalized to be unitary. EXAMPLES:: @@ -491,8 +515,9 @@ def twist(self): twist += 2 return twist + @cached_method def q_dimension(self): - r"""" + r""" This returns the quantum dimension as an element of the cyclotomic field of the `2l`-th roots of unity, where `l = m(k+h^\vee)` with `m=1,2,3` depending on whether type is simply, doubly or @@ -505,7 +530,7 @@ def q_dimension(self): [1, 4, 5, 1, 5, 4] """ if not self.is_simple_object(): - raise ValueError("quantum twist is only available for simple objects of a FusionRing") + raise ValueError("quantum dimension is only available for simple objects of a FusionRing") lam = self.weight() space = self.parent().space() rho = space.rho() @@ -516,5 +541,5 @@ def q_dimension(self): q = pr.gen()**2 expr = pr(expr) expr = expr.substitute(q=q**2)/q**(expr.degree()) - zet = self.parent().q_field().gen() + zet = self.parent().field().gen()**(self.parent()._fg) return expr.substitute(q=zet) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 674e00d3575..6b49f8b932c 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -176,6 +176,12 @@ def next_level(wt): h_check = ct.dual_coxeter_number() self._l = self._m_g * (self._k + h_check) self._conj = (-1)**conjugate + if ct[0] == 'A': + self._fg = ct[1]+1 + elif ct[0] == 'E' and ct[1] == 6: + self._fg = 3 + else: + self._fg = 1 @cached_method def ambient(self): From a0650b220912d0c7acc7b9165596f4d33e663dc1 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Thu, 25 Jun 2020 16:29:30 -0700 Subject: [PATCH 120/204] total_quantum_order --- src/sage/combinat/root_system/fusion_ring.py | 37 ++++++++++++++++---- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 3b5c4de48e4..98b8bb5e467 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -105,27 +105,38 @@ class FusionRing(WeylCharacterRing): include a twist method :meth:twist for its elements related to the ribbon element, and the S-matrix :meth:`s_ij`. + There are two natural normalizations of the S-matrix. Both + are explained in Chapter 3 of [BK]_. The one that is computed + by the method :meth:`s_matrix`, or whose individual entries + are computed by :meth:`s_ij` is denoted `\\tilde{s}` in + [BK]_. It is not unitary. To make it unitary, one would + divide by the square root of `D=\\sum d_i^2` where the sum is over all simple + objects and `d_i` is the quantum dimension, computed by the + method :meth:`q_dimension`. The quantity `D` is computed by + :meth:`q_dimension`. + Let us check the Verlinde formula. This famous identity states that .. MATH:: - N_{ijk} = \sum_l \frac{S(i,\ell)\,S(j,\ell)\,S(k,\ell)}{S(I,\ell)} + N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)} - where ``I`` is the unit object. We may define a function that corresponds - to the right-hand side:: + where ``I`` is the unit object. In this formula `s` is the normalized + unitary S-matrix denoted `s` in [BK]_. We may define a function that corresponds + to the right-hand side, except using `\\tilde{s}` instead of `s`. sage: def V(i,j,k): ....: R = i.parent() ....: return sum(R.s_ij(i,l)*R.s_ij(j,l)*R.s_ij(k,l)/R.s_ij(R.one(),l) for l in R.basis()) - This does not produce ``self.N_ijk(i,j,k)`` exactly, because our S-matrix - is omitting a normalization factor. The following code to check the + This does not produce ``self.N_ijk(i,j,k)`` exactly, because of the + missing normalization factor. The following code to check the Verlinde formula takes this into account:: sage: def test_verlinde(R): ....: b0 = R.one() - ....: c = V(b0, b0, b0) + ....: c = R.total_quantum_order() ....: return all(V(i,j,k)==c*R.N_ijk(i,j,k) for i in R.basis() for j in R.basis() for k in R.basis()) Every fusion ring should pass this test:: @@ -443,6 +454,19 @@ def fusion_labels(self, labels=None): inject_variable(labels[j], self(b)) self._fusion_labels = d + def total_quantum_order(self): + """ + Return `\sum d_i^2`, where the sum is over all simple objects + and `d_i` is the quantum dimension. + + EXAMPLES:: + + sage: FusionRing("E6",1).total_quantum_order() + 3 + + """ + return sum(x.q_dimension()**2 for x in self.basis()) + class Element(WeylCharacterRing.Element): """ A class for FusionRing elements. @@ -543,3 +567,4 @@ def q_dimension(self): expr = expr.substitute(q=q**2)/q**(expr.degree()) zet = self.parent().field().gen()**(self.parent()._fg) return expr.substitute(q=zet) + From 308a0938c292d25d3fce57e9dc517c7dffe3b38e Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Thu, 25 Jun 2020 16:47:40 -0700 Subject: [PATCH 121/204] minor doc revision --- src/sage/combinat/root_system/fusion_ring.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 98b8bb5e467..02de7eb2af1 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -102,15 +102,15 @@ class FusionRing(WeylCharacterRing): The fusion ring has a number of methods that reflect its role as the Grothendieck ring of a modular tensor category. These - include a twist method :meth:twist for its elements related - to the ribbon element, and the S-matrix :meth:`s_ij`. + include a twist method :meth:`twist` for its elements related + to the ribbon structure, and the S-matrix :meth:`s_ij`. There are two natural normalizations of the S-matrix. Both are explained in Chapter 3 of [BK]_. The one that is computed by the method :meth:`s_matrix`, or whose individual entries - are computed by :meth:`s_ij` is denoted `\\tilde{s}` in + are computed by :meth:`s_ij` is denoted `\tilde{s}` in [BK]_. It is not unitary. To make it unitary, one would - divide by the square root of `D=\\sum d_i^2` where the sum is over all simple + divide by the square root of `D=\sum d_i^2` where the sum is over all simple objects and `d_i` is the quantum dimension, computed by the method :meth:`q_dimension`. The quantity `D` is computed by :meth:`q_dimension`. @@ -124,7 +124,7 @@ class FusionRing(WeylCharacterRing): where ``I`` is the unit object. In this formula `s` is the normalized unitary S-matrix denoted `s` in [BK]_. We may define a function that corresponds - to the right-hand side, except using `\\tilde{s}` instead of `s`. + to the right-hand side, except using `\tilde{s}` instead of `s`:: sage: def V(i,j,k): ....: R = i.parent() @@ -178,7 +178,7 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug def field(self): """ This returns a cyclotomic field large enough to - contain the `2l`-th roots of unity, as well as + contain the `2\ell`-th roots of unity, as well as all the S-matrix entries. EXAMPLES:: From 487966cda5bd5777247959103046d57ab417f0da Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 26 Jun 2020 13:02:13 +1000 Subject: [PATCH 122/204] Reviewer changes to fusion ring enhancements. --- src/doc/en/reference/references/index.rst | 14 +- src/sage/combinat/root_system/fusion_ring.py | 314 ++++++++++-------- .../combinat/root_system/weyl_characters.py | 10 +- 3 files changed, 193 insertions(+), 145 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 4207e521835..c70b096a4cc 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -344,8 +344,8 @@ REFERENCES: **B** -.. [BK] Bakalov and Kirillov, Lectures on tensor categories and modular functors, - AMS (2001). +.. [BK2001] Bakalov and Kirillov, *Lectures on tensor categories and modular functors*, + AMS (2001). .. [Ba1994] Kaushik Basu. *The Traveler's Dilemma: Paradoxes of Rationality in Game Theory*. The American Economic Review @@ -1956,8 +1956,8 @@ REFERENCES: 1981, Pages 108-125, ISSN 0097-3165,:doi:`10.1016/0097-3165(81)90007-8`. (http://www.sciencedirect.com/science/article/pii/0097316581900078) -.. [EGNO] Pavel Etingof, Shlomo Gelaki, Dmitri Nikshych and Victor Ostrik, - "Tensor Categories", AMS Mathematical Surveys and Monographs 205 (2015). +.. [EGNO20015] Pavel Etingof, Shlomo Gelaki, Dmitri Nikshych and Victor Ostrik, + *Tensor Categories*, AMS Mathematical Surveys and Monographs 205 (2015). .. [EGHLSVY] Pavel Etingof, Oleg Golberg, Sebastian Hensel, Tiankai Liu, Alex Schwendner, Dmitry Vaintrob, Elena Yudovina, @@ -4552,9 +4552,9 @@ REFERENCES: .. [Rot2006] Ron Roth, Introduction to Coding Theory, Cambridge University Press, 2006 -.. [Row2006] Eric Rowell, From quantum groups to unitary modular tensor categories. - In Representations of algebraic groups, quantum groups, and Lie algebras, - Contemp. Math., 413, Amer. Math. Soc., Providence, RI, 2006. +.. [Row2006] Eric Rowell, *From quantum groups to unitary modular tensor categories*. + In Representations of algebraic groups, quantum groups, and Lie algebras, + Contemp. Math., **413**, Amer. Math. Soc., Providence, RI, 2006. :arXiv:`math/0503226`. .. [RR1997] Arun Ram and Jeffrey Remmel. *Applications of the Frobenius diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 02de7eb2af1..ee54f976df3 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -10,16 +10,12 @@ # https://www.gnu.org/licenses/ # **************************************************************************** - from sage.combinat.root_system.weyl_characters import WeylCharacterRing -from functools import reduce -from operator import mul from sage.combinat.q_analogues import q_int from sage.matrix.special import diagonal_matrix from sage.matrix.constructor import matrix -from sage.misc.lazy_attribute import lazy_attribute from sage.misc.misc import inject_variable -from sage.rings.all import ZZ +from sage.rings.integer_ring import ZZ from sage.rings.number_field.number_field import CyclotomicField from sage.misc.cachefunc import cached_method @@ -40,9 +36,9 @@ class FusionRing(WeylCharacterRing): REFERENCES: - - [BK]_ Chapter 3 + - [BK2001]_ Chapter 3 - [DFMS1996]_ Chapter 16 - - [EGNO]_ Chapter 8 + - [EGNO2015]_ Chapter 8 - [Feingold2004]_ - [Fuchs1994]_ - [Row2006]_ @@ -63,14 +59,15 @@ class FusionRing(WeylCharacterRing): You may assign your own labels to the basis elements. In the next example, we create the `SO(5)` fusion ring of level `2`, check the - weights of the basis elements, then assign new labels to them:: + weights of the basis elements, then assign new labels to them while + injecting them into the global namespace:: sage: B22 = FusionRing("B2", 2) sage: b = [B22(x) for x in B22.get_order()]; b [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] sage: [x.weight() for x in b] [(0, 0), (1, 0), (1/2, 1/2), (2, 0), (3/2, 1/2), (1, 1)] - sage: B22.fusion_labels(['I0','Y1','X','Z','Xp','Y2']) + sage: B22.fusion_labels(['I0','Y1','X','Z','Xp','Y2'], inject_variables=True) sage: b = [B22(x) for x in B22.get_order()]; b [I0, Y1, X, Z, Xp, Y2] sage: [(x, x.weight()) for x in b] @@ -80,9 +77,9 @@ class FusionRing(WeylCharacterRing): (Z, (2, 0)), (Xp, (3/2, 1/2)), (Y2, (1, 1))] - sage: X*Y1 + sage: X * Y1 X + Xp - sage: Z*Z + sage: Z * Z I0 A fixed order of the basis keys is avalailable with :meth:`get_order`. @@ -93,11 +90,17 @@ class FusionRing(WeylCharacterRing): sage: [B22(x) for x in B22.get_order()] [I0, Y1, Y2, X, Xp, Z] - To reset the labels and the order to their defaults, - you may run :meth:`fusion_labels` with no parameter:: + To reset the labels, you may run :meth:`fusion_labels` with no parameter:: sage: B22.fusion_labels() sage: [B22(x) for x in B22.get_order()] + [B22(0,0), B22(1,0), B22(0,2), B22(0,1), B22(1,1), B22(2,0)] + + To reset the order to the default, simply set it to the list of basis + element keys:: + + sage: B22.set_order(B22.basis().keys().list()) + sage: [B22(x) for x in B22.get_order()] [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] The fusion ring has a number of methods that reflect its role @@ -106,29 +109,30 @@ class FusionRing(WeylCharacterRing): to the ribbon structure, and the S-matrix :meth:`s_ij`. There are two natural normalizations of the S-matrix. Both - are explained in Chapter 3 of [BK]_. The one that is computed + are explained in Chapter 3 of [BK2001]_. The one that is computed by the method :meth:`s_matrix`, or whose individual entries are computed by :meth:`s_ij` is denoted `\tilde{s}` in - [BK]_. It is not unitary. To make it unitary, one would - divide by the square root of `D=\sum d_i^2` where the sum is over all simple - objects and `d_i` is the quantum dimension, computed by the - method :meth:`q_dimension`. The quantity `D` is computed by - :meth:`q_dimension`. + [BK2001]_. It is not unitary. To make it unitary, one would + divide by the square root of `D = \sum_V d_i(V)^2` where the sum + is over all simple objects `V` and `d_i(V)` is the quantum dimension + of `V` computed by the method :meth:`q_dimension`. The quantity `D` + is computed by :meth:`q_dimension`. - Let us check the Verlinde formula. This famous identity states - that + Let us check the Verlinde formula. This famous identity states that .. MATH:: - N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)} + N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)}, where ``I`` is the unit object. In this formula `s` is the normalized - unitary S-matrix denoted `s` in [BK]_. We may define a function that corresponds - to the right-hand side, except using `\tilde{s}` instead of `s`:: + unitary S-matrix denoted `s` in [BK2001]_. We may define a function that + corresponds to the right-hand side, except using `\tilde{s}` instead + of `s`:: sage: def V(i,j,k): ....: R = i.parent() - ....: return sum(R.s_ij(i,l)*R.s_ij(j,l)*R.s_ij(k,l)/R.s_ij(R.one(),l) for l in R.basis()) + ....: return sum(R.s_ij(i,l) * R.s_ij(j,l) * R.s_ij(k,l) / R.s_ij(R.one(),l) + ....: for l in R.basis()) This does not produce ``self.N_ijk(i,j,k)`` exactly, because of the missing normalization factor. The following code to check the @@ -137,7 +141,8 @@ class FusionRing(WeylCharacterRing): sage: def test_verlinde(R): ....: b0 = R.one() ....: c = R.total_quantum_order() - ....: return all(V(i,j,k)==c*R.N_ijk(i,j,k) for i in R.basis() for j in R.basis() for k in R.basis()) + ....: return all(V(i,j,k) == c * R.N_ijk(i,j,k) for i in R.basis() + ....: for j in R.basis() for k in R.basis()) Every fusion ring should pass this test:: @@ -145,7 +150,6 @@ class FusionRing(WeylCharacterRing): True sage: test_verlinde(FusionRing("B4",2)) True - """ @staticmethod def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False): @@ -176,8 +180,8 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug prefix=prefix, style=style, k=k, conjugate=conjugate) def field(self): - """ - This returns a cyclotomic field large enough to + r""" + Return a cyclotomic field large enough to contain the `2\ell`-th roots of unity, as well as all the S-matrix entries. @@ -187,13 +191,11 @@ def field(self): Cyclotomic Field of order 60 and degree 16 sage: FusionRing("B2",2).field() Cyclotomic Field of order 40 and degree 16 - """ + return CyclotomicField(4 * self._fg * self._l) - return CyclotomicField(4*self._fg*self._l) - def get_order(self): - """ + r""" This returns the weights of the basis vectors in a fixed order. You may change the order of the basis using :meth:`set_order` @@ -206,10 +208,12 @@ def get_order(self): sage: [A14(x) for x in A14.get_order()] [A14(0), A14(4), A14(1), A14(3), A14(2)] - This duplicates :meth:`get_order` from :mod:`combinat.free_module`. - However unlike the :mod:`combinat.free_module` method with the same - name this :meth:`get_order` is not cached. Caching of :meth:`get_order` causes - inconsistent results after calling :meth:`set_order`. + .. WARNING:: + + This duplicates :meth:`get_order` from + :class:`CombinatorialFreeModule` except the result + is *not* cached. Caching of :meth:`get_order` causes + inconsistent results after calling :meth:`set_order`. """ if self._order is None: self.set_order(self.basis().keys().list()) @@ -230,11 +234,11 @@ def some_elements(self): def fusion_k(self): r""" - Return the level of the FusionRing. + Return the level of ``self``. EXAMPLES:: - sage: B22=FusionRing('B2',2) + sage: B22 = FusionRing('B2',2) sage: B22.fusion_k() 2 """ @@ -242,21 +246,21 @@ def fusion_k(self): def fusion_l(self): r""" - This Returns the product `m_g(k + h^\vee)`, where `m_g` denotes the - square of the ratio of the lengths of long to short roots of + Return the product `m_g(k + h^\vee)`, where `m_g` denotes the + square of the ratio of the lengths of long to short roots of the underlying Lie algebra, `k` denotes the level of the FusionRing, and `h^\vee` denotes the dual Coxeter number of the underlying Lie algebra. This value is used to define the associated root `2\ell`-th - of unity `q=e^{i\pi/\ell}`. + of unity `q = e^{i\pi/\ell}`. EXAMPLES:: - sage: B22=FusionRing('B2',2) + sage: B22 = FusionRing('B2',2) sage: B22.fusion_l() 10 - sage: D52=FusionRing('D5',2) + sage: D52 = FusionRing('D5',2) sage: D52.fusion_l() 10 """ @@ -269,7 +273,7 @@ def twists_matrix(self): EXAMPLES:: - sage: B22=FusionRing('B2',2) + sage: B22 = FusionRing('B2',2) sage: B22.twists_matrix() [ 0 0 0 0 0 0] [ 0 4/5 0 0 0 0] @@ -278,7 +282,8 @@ def twists_matrix(self): [ 0 0 0 0 3/2 0] [ 0 0 0 0 0 6/5] """ - return diagonal_matrix(self.basis()[x].twist() for x in self.get_order()) + B = self.basis() + return diagonal_matrix(B[x].twist() for x in self.get_order()) def q_dims(self): r""" @@ -286,10 +291,10 @@ def q_dims(self): EXAMPLES:: - sage: F41=FusionRing('F4',1,conjugate=True) + sage: F41 = FusionRing('F4',1,conjugate=True) sage: F41.q_dims() [1, -zeta80^24 + zeta80^16 + 1] - sage: B22=FusionRing("B2",2) + sage: B22 = FusionRing("B2",2) sage: B22.q_dims() [1, 2, -2*zeta40^12 + 2*zeta40^8 + 1, 1, -2*zeta40^12 + 2*zeta40^8 + 1, 2] """ @@ -298,102 +303,106 @@ def q_dims(self): @cached_method def N_ijk(self, elt_i, elt_j, elt_k): - """ + r""" + Return the symmetric fusion coefficient `N_{ijk}`. + INPUT: - ``elt_i``, ``elt_j``, ``elt_k`` -- elements of the fusion basis - Returns the symmetric fusion coefficient `N_{ijk}`. This - is the same as `N_{ij}^{k\\ast}`, where $N_{ij}^k$ are the - structure coefficients of the ring (see :meth:`Nk_ij`), - and `k\\ast`` denotes the dual element. The coefficient `N_{ijk}` + This is the same as `N_{ij}^{k\ast}`, where `N_{ij}^k` are + the structure coefficients of the ring (see :meth:`Nk_ij`), + and `k\ast`` denotes the dual element. The coefficient `N_{ijk}` is unchanged under permutations of the three basis vectors. EXAMPLES:: - sage: G23=FusionRing("G2",3) + sage: G23 = FusionRing("G2", 3) sage: G23.fusion_labels("g") sage: b = G23.basis().list(); b [g0, g1, g2, g3, g4, g5] - sage: [(x,y,z) for x in b for y in b for z in b if G23.N_ijk(x,y,z)>1] + sage: [(x,y,z) for x in b for y in b for z in b if G23.N_ijk(x,y,z) > 1] [(g3, g3, g3), (g3, g3, g4), (g3, g4, g3), (g4, g3, g3)] - sage: all([G23.N_ijk(x,y,z)==G23.N_ijk(y,z,x) for x in b for y in b for z in b]) + sage: all(G23.N_ijk(x,y,z)==G23.N_ijk(y,z,x) for x in b for y in b for z in b) True - sage: all([G23.N_ijk(x,y,z)==G23.N_ijk(y,x,z) for x in b for y in b for z in b]) + sage: all(G23.N_ijk(x,y,z)==G23.N_ijk(y,x,z) for x in b for y in b for z in b) True - """ - return (elt_i*elt_j).monomial_coefficients().get(elt_k.dual().weight(),0) + return (elt_i * elt_j).monomial_coefficients().get(elt_k.dual().weight(), 0) @cached_method def Nk_ij(self, elt_i, elt_j, elt_k): """ - Returns the fusion coefficient `N^k_{ij}`. These are - the structure coefficients of the fusion ring, so - - .. MATH:: + Return the fusion coefficient `N^k_{ij}`. - i*j = \sum_{k} N_{ij}^k k + These are the structure coefficients of the fusion ring, so - as the example shows. + .. MATH:: + + i * j = \sum_{k} N_{ij}^k k. EXAMPLES:: - sage: A22=FusionRing("A2",2) + sage: A22 = FusionRing("A2", 2) sage: b = A22.basis().list() sage: all(x*y == sum(A22.Nk_ij(x,y,k)*k for k in b) for x in b for y in b) True - """ - return (elt_i*elt_j).monomial_coefficients().get(elt_k.weight(),0) + return (elt_i * elt_j).monomial_coefficients(copy=False).get(elt_k.weight(), 0) @cached_method def s_ij(self, elt_i, elt_j): - """ - Return the element of the S-matrix of this FusionRing corresponding to + r""" + Return the element of the S-matrix of this FusionRing corresponding to the given elements. This is computed using the formula .. MATH:: - s_{i,j} = \\frac{1}{\\theta_i\\theta_j}\\sum_k N_{ik}^j d_k\\theta_k + s_{i,j} = \frac{1}{\theta_i\theta_j} \sum_k N_{ik}^j d_k \theta_k, - where `\\theta_k` is the twist and `d_k` is the quantum - dimension. See [Row2006]_ equation (2.2) or [EGNO]_ Proposition 8.13.8. + where `\theta_k` is the twist and `d_k` is the quantum + dimension. See [Row2006]_ Equation (2.2) or [EGNO2015]_ + Proposition 8.13.8. INPUT: - ``elt_i``, ``elt_j`` -- elements of the fusion basis - EXAMPLES:: + EXAMPLES:: - sage: G21=FusionRing("G2",1) - sage: b=G21.basis() - sage: [G21.s_ij(x,y) for x in b for y in b] + sage: G21 = FusionRing("G2", 1) + sage: b = G21.basis() + sage: [G21.s_ij(x, y) for x in b for y in b] [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] - """ - l = self.fusion_l()*self._fg + l = self.fusion_l() * self._fg K = self.field() q = K.gen() - ijtwist = -2*l*(elt_i.twist() + elt_j.twist()) - mc = (elt_i.dual()*elt_j).monomial_coefficients() - return sum(self(k).q_dimension()*self.Nk_ij(elt_i,self(k),elt_j)*q**(2*l*self(k).twist()+ijtwist) for k in mc) + ijtwist = -2 * l * (elt_i.twist() + elt_j.twist()) + mc = (elt_i.dual() * elt_j).monomial_coefficients(copy=False) + B = self.basis() + return sum(B[k].q_dimension() * self.Nk_ij(elt_i, B[k], elt_j) * q**(2*l*B[k].twist() + ijtwist) + for k in mc) def s_matrix(self): r""" - Return the S-matrix of this FusionRing. This is the matrix denoted - `\widetilde{s}` in [BK]_. It is not normalized to be unitary. + Return the S-matrix of this FusionRing. + + .. NOTE:: - EXAMPLES:: + This is the matrix denoted `\widetilde{s}` in [BK2001]_. + It is not normalized to be unitary. + + EXAMPLES:: - sage: D91=FusionRing('D9',1) + sage: D91 = FusionRing('D9', 1) sage: D91.s_matrix() [ 1 1 1 1] [ 1 1 -1 -1] [ 1 -1 -zeta68^17 zeta68^17] [ 1 -1 zeta68^17 -zeta68^17] - sage: D41=FusionRing('D4',1) + sage: D41 = FusionRing('D4', 1) sage: D41.s_matrix() [ 1 1 1 1] [ 1 1 -1 -1] @@ -401,19 +410,22 @@ def s_matrix(self): [ 1 -1 -1 1] """ b = self.basis() - return matrix([[self.s_ij(b[x],b[y]) for x in self.get_order()] for y in self.get_order()]) + return matrix([[self.s_ij(b[x], b[y]) for x in self.get_order()] for y in self.get_order()]) - def fusion_labels(self, labels=None): - """ + def fusion_labels(self, labels=None, inject_variables=False): + r""" Set the labels of the basis. INPUT: - ``labels`` -- (default: ``None``) a list of strings or string + - ``inject_variables`` -- (default: ``False``) if ``True``, then + inject the variable names into the global namespace; note that + this could override objects already defined If ``labels`` is a list, the length of the list must equal the number of basis elements. These become the names of - the basis elements. + the basis elements. If ``labels`` is a string, this is treated as a prefix and a list of names is generated. @@ -432,26 +444,41 @@ def fusion_labels(self, labels=None): [ x2 x1 + x3 x0 + x2 x1] [ x3 x2 x1 x0] + We give an example where the variables are injected into the + global namepsace:: + + sage: A13.fusion_labels("y", inject_variables=True) + sage: y0 + y0 + sage: y0.parent() is A13 + True + We reset the labels to the default:: sage: A13.fusion_labels() sage: fb [A13(0), A13(1), A13(2), A13(3)] + sage: y0 + A13(0) """ if labels is None: - self._order = None + # Remove the fusion labels self._fusion_labels = None return - elif type(labels) == str: - labels = [labels+"%d"%k for k in range(len(self.basis()))] - elif len(labels) != len(self.basis()): + + B = self.basis() + if isinstance(labels, str): + labels = [labels + str(k) for k in range(len(B))] + elif len(labels) != len(B): raise ValueError('invalid data') + d = {} - fb = list(self.get_order()) - for j, b in enumerate(fb): - t = tuple([b.inner_product(x) for x in self.simple_coroots()]) + ac = self.simple_coroots() + for j, b in enumerate(self.get_order()): + t = tuple([b.inner_product(x) for x in ac]) d[t] = labels[j] - inject_variable(labels[j], self(b)) + if inject_variables: + inject_variable(labels[j], B[b]) self._fusion_labels = d def total_quantum_order(self): @@ -463,7 +490,6 @@ def total_quantum_order(self): sage: FusionRing("E6",1).total_quantum_order() 3 - """ return sum(x.q_dimension()**2 for x in self.basis()) @@ -472,12 +498,12 @@ class Element(WeylCharacterRing.Element): A class for FusionRing elements. """ def is_simple_object(self): - """ - Determine whether element is a simple object of the FusionRing. + r""" + Determine whether ``self`` is a simple object of the fusion ring. EXAMPLES:: - sage: A22=FusionRing("A2",2) + sage: A22 = FusionRing("A2", 2) sage: x = A22(1,0); x A22(1,0) sage: x.is_simple_object() @@ -487,12 +513,13 @@ def is_simple_object(self): sage: (x^2).is_simple_object() False """ - return self.parent()._k is not None and len(self.monomial_coefficients())==1 + return self.parent()._k is not None and len(self._monomial_coefficients) == 1 def weight(self): - """ - This method is only available for basis elements. Returns the - parametrizing dominant weight in the level `k` alcove. + r""" + Return the parametrizing dominant weight in the level `k` alcove. + + This method is only available for basis elements. EXAMPLES:: @@ -500,27 +527,29 @@ def weight(self): sage: sorted([x.weight() for x in A21.basis()]) [(0, 0, 0), (1/3, 1/3, -2/3), (2/3, -1/3, -1/3)] """ - if len(self.monomial_coefficients()) != 1: + if len(self._monomial_coefficients) != 1: raise ValueError("fusion weight is valid for basis elements only") - return self.leading_support() + return next(iter(self._monomial_coefficients)) def twist(self): r""" - Compute the object's twist. This returns a rational number `h` such that - `\theta = e^{i \pi h}` is the twist of ``self``. This method is - only available for simple objects. + Compute the object's twist. This returns a rational number `h` + such that `\theta = e^{i \pi h}` is the twist of ``self``. + + This method is only available for simple objects. - We compute the twists following p.7 of [Row2006]_, noting that the bilinear form - is normalized so that `\langle\alpha, \alpha\rangle = 2` for short roots. + We compute the twists following p.7 of [Row2006]_, noting that + the bilinear form is normalized so that + `\langle \alpha, \alpha \rangle = 2` for short roots. EXAMPLES:: - sage: G21=FusionRing('G2',1) + sage: G21 = FusionRing('G2', 1) sage: G21.basis() Finite family {(0, 0, 0): G21(0,0), (1, 0, -1): G21(1,0)} sage: G21(1,0).twist() 4/5 - sage: F41=FusionRing('F4',1,conjugate=True) + sage: F41 = FusionRing('F4', 1, conjugate=True) sage: F41.basis() Finite family {(0, 0, 0, 0): F41(0,0,0,0), (1, 0, 0, 0): F41(0,0,0,1)} sage: F41(0,0,0,1).twist() @@ -528,11 +557,12 @@ def twist(self): """ if not self.is_simple_object(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") - rho = sum(self.parent().positive_roots())/2 + P = self.parent() + rho = P.space().rho() lam = self.weight() - inner = lam.inner_product(lam+2*rho) - twist = self.parent()._conj*self.parent()._nf*inner/self.parent().fusion_l() - #Reduce to canonical form + inner = lam.inner_product(lam + 2*rho) + twist = P._conj * P._nf * inner / P.fusion_l() + # Reduce to canonical form while twist > 2: twist -= 2 while twist < 0: @@ -542,29 +572,47 @@ def twist(self): @cached_method def q_dimension(self): r""" - This returns the quantum dimension as an element of the cyclotomic - field of the `2l`-th roots of unity, where `l = m(k+h^\vee)` + Return the quantum dimension as an element of the cyclotomic + field of the `2l`-th roots of unity, where `l = m (k+h^\vee)` with `m=1,2,3` depending on whether type is simply, doubly or - triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. + triply laced, `k` is the level and `h^\vee` is the dual + Coxeter number. EXAMPLES:: - sage: B22=FusionRing("B2",2) + sage: B22 = FusionRing("B2",2) sage: [(b.q_dimension())^2 for b in B22.basis()] [1, 4, 5, 1, 5, 4] """ if not self.is_simple_object(): raise ValueError("quantum dimension is only available for simple objects of a FusionRing") + P = self.parent() lam = self.weight() - space = self.parent().space() + space = P.space() rho = space.rho() - num = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(lam+rho)) for alpha in space.positive_roots()], 1) - den = reduce(mul, [q_int(self.parent()._nf*alpha.inner_product(rho)) for alpha in space.positive_roots()], 1) - expr = num/den - pr = expr.parent().ring() - q = pr.gen()**2 - expr = pr(expr) - expr = expr.substitute(q=q**2)/q**(expr.degree()) - zet = self.parent().field().gen()**(self.parent()._fg) + powers = {} + for alpha in space.positive_roots(): + val = alpha.inner_product(lam + rho) + if val in powers: + powers[val] += 1 + else: + powers[val] = 1 + val = alpha.inner_product(rho) + if val in powers: + powers[val] -= 1 + else: + powers[val] = -1 + R = ZZ['q'] + q = R.gen() + expr = R.fraction_field().one() + for val in powers: + exp = powers[val] + if exp > 0: + expr *= q_int(P._nf * val, q)**exp + elif exp < 0: + expr /= q_int(P._nf * val, q)**(-exp) + expr = R(expr) + expr = expr.substitute(q=q**4) / (q**(2*expr.degree())) + zet = P.field().gen() ** P._fg return expr.substitute(q=zet) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 6b49f8b932c..f145c003176 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -163,21 +163,21 @@ def next_level(wt): # Record properties of the FusionRing if k is not None: #TODO: implement conjugate functionality - if ct[0] in ['A','D','E']: + if ct[0] in ['A', 'D', 'E']: self._m_g = 1 elif ct[0] in ['B', 'C', 'F']: self._m_g = 2 - else: + else: self._m_g = 3 if ct[0] in ['B','F']: self._nf = 2 else: self._nf = 1 h_check = ct.dual_coxeter_number() - self._l = self._m_g * (self._k + h_check) - self._conj = (-1)**conjugate + self._l = self._m_g * (self._k + h_check) + self._conj = (-1) ** conjugate if ct[0] == 'A': - self._fg = ct[1]+1 + self._fg = ct[1] + 1 elif ct[0] == 'E' and ct[1] == 6: self._fg = 3 else: From f3a37b6424f4dd24b4ff17e7be3d1bf25ade4a2d Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Fri, 26 Jun 2020 16:59:56 +0900 Subject: [PATCH 123/204] Add integral curves over QQ --- src/sage/schemes/curves/affine_curve.py | 182 ++++++++++++++------ src/sage/schemes/curves/constructor.py | 23 ++- src/sage/schemes/curves/point.py | 46 +++-- src/sage/schemes/curves/projective_curve.py | 174 +++++++++++++------ 4 files changed, 310 insertions(+), 115 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index a0bb92c7b59..c2362bdcee7 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -72,6 +72,35 @@ sage: _.place() Place (x, z) +Integral affine curves over `\QQ` +--------------------------------- + +An integral curve over `\QQ` is equipped also with the function field. Unlike +over finite fields, it is not possible to enumerate closed points. + +EXAMPLES:: + + sage: A. = AffineSpace(QQ, 2) + sage: C = Curve(x^2 + y^2 -1) + sage: p = C(0,1) + sage: p + (0, 1) + sage: p.closed_point() + Point (x, y - 1) + sage: pl = _.place() + sage: C.parametric_representation(pl) + (s + ..., 1 - 1/2*s^2 - 1/8*s^4 - 1/16*s^6 + ...) + sage: sx, sy = _ + sage: sx = sx.polynomial(10); sx + s + sage: sy = sy.polynomial(10); sy + -7/256*s^10 - 5/128*s^8 - 1/16*s^6 - 1/8*s^4 - 1/2*s^2 + 1 + sage: s = var('s') + sage: P1 = parametric_plot([sx, sy], (s, -1, 1), color='red') + sage: P2 = C.plot((x, -1, 1), (y, 0, 2)) # half circle + sage: P1 + P2 + Graphics object consisting of 2 graphics primitives + AUTHORS: - William Stein (2005-11-13) @@ -125,7 +154,9 @@ from .point import (AffineCurvePoint_field, AffinePlaneCurvePoint_field, AffinePlaneCurvePoint_finite_field, + IntegralAffineCurvePoint, IntegralAffineCurvePoint_finite_field, + IntegralAffinePlaneCurvePoint, IntegralAffinePlaneCurvePoint_finite_field) from .closed_point import IntegralAffineCurveClosedPoint @@ -1883,6 +1914,7 @@ class IntegralAffineCurve(AffineCurve_field): """ Base class for integral affine curves. """ + _point = IntegralAffineCurvePoint _closed_point = IntegralAffineCurveClosedPoint def function_field(self): @@ -1891,6 +1923,13 @@ def function_field(self): EXAMPLES:: + sage: A. = AffineSpace(QQ, 2) + sage: C = Curve(x^3 - y^2 - x^4 - y^4) + sage: C.function_field() + Function field in y defined by y^4 + y^2 + x^4 - x^3 + + :: + sage: A. = AffineSpace(GF(8), 2) sage: C = Curve(x^5 + y^5 + x*y + 1) sage: C.function_field() @@ -1910,7 +1949,16 @@ def _genus(self): sage: C.genus() # indirect doctest 1 """ - return self._function_field.genus() + k = self.base_ring() + + # Singular's genus command is usually much faster than the genus method + # of function fields in Sage. But unfortunately Singular's genus + # command does not yet work over non-prime finite fields. + if k.is_finite() and k.degree() > 1: + return self._function_field.genus() + + # call Singular's genus command + return self.defining_ideal().genus() def __call__(self, *args): """ @@ -1994,27 +2042,6 @@ def coordinate_functions(self): """ return self._coordinate_functions - -class IntegralAffineCurve_finite_field(IntegralAffineCurve): - """ - Integral affine curves. - - INPUT: - - - ``A`` -- an ambient space in which the curve lives - - - ``X`` -- list of polynomials that define the curve - - EXAMPLES:: - - sage: A. = AffineSpace(GF(11), 3) - sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A); C - Affine Curve over Finite Field of size 11 defined by -y^2 + x*z, -z^2 + y, -y*z + x - sage: C.function_field() - Function field in z defined by z^3 + 10*x - """ - _point = IntegralAffineCurvePoint_finite_field - @lazy_attribute def _nonsingular_model(self): """ @@ -2323,6 +2350,13 @@ def places_at_infinity(self): EXAMPLES:: + sage: A. = AffineSpace(QQ, 2) + sage: C = Curve(x^3 - y^2 - x^4 - y^4) + sage: C.places_at_infinity() + [Place (1/x, 1/x^2*y, 1/x^3*y^2, 1/x^4*y^3)] + + :: + sage: F = GF(9) sage: A2. = AffineSpace(F, 2) sage: C = A2.curve(y^3 + y - x^4) @@ -2350,9 +2384,19 @@ def places_on(self, point): EXAMPLES:: + sage: A. = AffineSpace(QQ, 2) + sage: C = Curve(x^3 - y^2 - x^4 - y^4) + sage: C.singular_closed_points() + [Point (x, y)] + sage: p, = _ + sage: C.places_on(p) + [Place (x, y, y^2, 1/x*y^3 + 1/x*y)] + + :: + sage: k. = GF(9) sage: A. = AffineSpace(k,2) - sage: C = Curve(y^2-x^5-x^4-2*x^3-2*x-2) + sage: C = Curve(y^2 - x^5 - x^4 - 2*x^3 - 2*x - 2) sage: pts = C.closed_points() sage: pts [Point (x, y + (a + 1)), @@ -2390,6 +2434,69 @@ def places_on(self, point): places.append(p) return places + def parametric_representation(self, place, name=None): + """ + Return a power series representation of the branch of the + curve given by ``place``. + + INPUT: + + - ``place`` -- a place on the curve + + EXAMPLES:: + + sage: A. = AffineSpace(QQ, 2) + sage: C = Curve(x^2 + y^2 -1) + sage: p = C(0,1) + sage: p.closed_point() + Point (x, y - 1) + sage: p.closed_point() + Point (x, y - 1) + sage: pl = _.place() + sage: C.parametric_representation(pl) + (s + ..., 1 - 1/2*s^2 - 1/8*s^4 - 1/16*s^6 + ...) + + :: + + sage: A. = AffineSpace(GF(7^2), 2) + sage: C = Curve(x^2 - x^4 - y^4) + sage: p, = C.singular_closed_points() + sage: b1, b2 = p.places() + sage: xs, ys = C.parametric_representation(b1) + sage: f = xs^2 - xs^4 - ys^4 + sage: [f.coefficient(i) for i in range(5)] + [0, 0, 0, 0, 0] + sage: xs, ys = C.parametric_representation(b2) + sage: f = xs^2 - xs^4 - ys^4 + sage: [f.coefficient(i) for i in range(5)] + [0, 0, 0, 0, 0] + """ + F = place.function_field() + F_place = F.completion(place, prec=infinity, name=name) + + return tuple(F_place._expand_lazy(c) for c in self._coordinate_functions) + + +class IntegralAffineCurve_finite_field(IntegralAffineCurve): + """ + Integral affine curves. + + INPUT: + + - ``A`` -- an ambient space in which the curve lives + + - ``X`` -- list of polynomials that define the curve + + EXAMPLES:: + + sage: A. = AffineSpace(GF(11), 3) + sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A); C + Affine Curve over Finite Field of size 11 defined by -y^2 + x*z, -z^2 + y, -y*z + x + sage: C.function_field() + Function field in z defined by z^3 + 10*x + """ + _point = IntegralAffineCurvePoint_finite_field + def places(self, degree=1): """ Return all places on the curve of the ``degree``. @@ -2481,34 +2588,9 @@ def closed_points(self, degree=1): return points - def parametric_representation(self, place, name=None): - """ - Return a power series representation of the branch of the - curve given by ``place``. - - INPUT: - - - ``place`` -- a place on the curve - - EXAMPLES:: - sage: A. = AffineSpace(GF(7^2),2) - sage: C = Curve(x^2 - x^4 - y^4) - sage: p, = C.singular_closed_points() - sage: b1, b2 = p.places() - sage: xs, ys = C.parametric_representation(b1) - sage: f = xs^2 - xs^4 - ys^4 - sage: [f.coefficient(i) for i in range(5)] - [0, 0, 0, 0, 0] - sage: xs, ys = C.parametric_representation(b2) - sage: f = xs^2 - xs^4 - ys^4 - sage: [f.coefficient(i) for i in range(5)] - [0, 0, 0, 0, 0] - """ - F = place.function_field() - F_place = F.completion(place, prec=infinity, name=name) - - return tuple(F_place._expand_lazy(c) for c in self._coordinate_functions) +class IntegralAffinePlaneCurve(IntegralAffineCurve, AffinePlaneCurve_field): + _point = IntegralAffinePlaneCurvePoint class IntegralAffinePlaneCurve_finite_field(AffinePlaneCurve_finite_field, IntegralAffineCurve_finite_field): diff --git a/src/sage/schemes/curves/constructor.py b/src/sage/schemes/curves/constructor.py index 1d010eda1bd..8330fadea0d 100644 --- a/src/sage/schemes/curves/constructor.py +++ b/src/sage/schemes/curves/constructor.py @@ -41,6 +41,9 @@ from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing from sage.rings.finite_rings.finite_field_constructor import is_FiniteField +from sage.rings.rational_field import QQ +from sage.rings.qqbar import QQbar + from sage.structure.all import Sequence from sage.schemes.affine.affine_space import is_AffineSpace @@ -58,16 +61,20 @@ ProjectiveCurve_field, ProjectivePlaneCurve_field, ProjectivePlaneCurve_finite_field, - IntegralProjectivePlaneCurve_finite_field, - IntegralProjectiveCurve_finite_field) + IntegralProjectiveCurve, + IntegralProjectiveCurve_finite_field, + IntegralProjectivePlaneCurve, + IntegralProjectivePlaneCurve_finite_field) from .affine_curve import (AffineCurve, AffinePlaneCurve, AffineCurve_field, AffinePlaneCurve_field, AffinePlaneCurve_finite_field, - IntegralAffinePlaneCurve_finite_field, - IntegralAffineCurve_finite_field) + IntegralAffineCurve, + IntegralAffineCurve_finite_field, + IntegralAffinePlaneCurve, + IntegralAffinePlaneCurve_finite_field) from sage.schemes.plane_conics.constructor import Conic @@ -278,6 +285,8 @@ def Curve(F, A=None): if A.coordinate_ring().ideal(F).is_prime(): return IntegralAffineCurve_finite_field(A, F) if k in Fields(): + if k == QQ and A.coordinate_ring().ideal(F).is_prime(): + return IntegralAffineCurve(A, F) return AffineCurve_field(A, F) return AffineCurve(A, F) @@ -291,6 +300,8 @@ def Curve(F, A=None): return IntegralAffinePlaneCurve_finite_field(A, F) return AffinePlaneCurve_finite_field(A, F) if k in Fields(): + if k == QQ and A.coordinate_ring().ideal(F).is_prime(): + return IntegralAffinePlaneCurve(A, F) return AffinePlaneCurve_field(A, F) return AffinePlaneCurve(A, F) @@ -302,6 +313,8 @@ def Curve(F, A=None): if A.coordinate_ring().ideal(F).is_prime(): return IntegralProjectiveCurve_finite_field(A, F) if k in Fields(): + if k == QQ and A.coordinate_ring().ideal(F).is_prime(): + return IntegralProjectiveCurve(A, F) return ProjectiveCurve_field(A, F) return ProjectiveCurve(A, F) @@ -320,6 +333,8 @@ def Curve(F, A=None): return IntegralProjectivePlaneCurve_finite_field(A, F) return ProjectivePlaneCurve_finite_field(A, F) if k in Fields(): + if k == QQ and A.coordinate_ring().ideal(F).is_prime(): + return IntegralProjectivePlaneCurve(A, F) return ProjectivePlaneCurve_field(A, F) return ProjectivePlaneCurve(A, F) diff --git a/src/sage/schemes/curves/point.py b/src/sage/schemes/curves/point.py index 861c089af94..0ae3ab7e820 100644 --- a/src/sage/schemes/curves/point.py +++ b/src/sage/schemes/curves/point.py @@ -6,8 +6,6 @@ sage: P. = ProjectiveSpace(QQ, 3) sage: C = Curve([x^3 - 2*x*z^2 - y^3, z^3 - w^3 - x*y*z], P) sage: Q = C([1,1,0,0]) - sage: type(Q) - sage: Q.parent() Set of rational points of Projective Curve over Rational Field defined by x^3 - y^3 - 2*x*z^2, -x*y*z + z^3 - w^3 @@ -40,7 +38,9 @@ SchemeMorphism_point_projective_finite_field) class ProjectiveCurvePoint_field(SchemeMorphism_point_projective_field): - + """ + Point of a projective curve over a field. + """ def is_singular(self): r""" Return whether this point is a singular point of the projective curve it is on. @@ -60,7 +60,9 @@ def is_singular(self): class ProjectivePlaneCurvePoint_field(ProjectiveCurvePoint_field): - + """ + Point of a projective plane curve over a field. + """ def multiplicity(self): r""" Return the multiplicity of this point with respect to the projective @@ -161,10 +163,7 @@ class ProjectivePlaneCurvePoint_finite_field(ProjectivePlaneCurvePoint_field, pass -class IntegralProjectiveCurvePoint_finite_field(ProjectiveCurvePoint_field): - """ - Point of an integral projective curve over a finite field. - """ +class IntegralProjectiveCurvePoint(ProjectiveCurvePoint_field): def closed_point(self): """ Return the closed point corresponding to this rational point. @@ -225,6 +224,20 @@ def place(self): return self.closed_point().place() +class IntegralProjectiveCurvePoint_finite_field(IntegralProjectiveCurvePoint): + """ + Point of an integral projective curve over a finite field. + """ + pass + + +class IntegralProjectivePlaneCurvePoint(IntegralProjectiveCurvePoint, ProjectivePlaneCurvePoint_field): + """ + Point of an integral projective plane curve over a field. + """ + pass + + class IntegralProjectivePlaneCurvePoint_finite_field(ProjectivePlaneCurvePoint_finite_field, IntegralProjectiveCurvePoint_finite_field): """ Point of an integral projective plane curve over a finite field. @@ -358,9 +371,9 @@ class AffinePlaneCurvePoint_finite_field(AffinePlaneCurvePoint_field, SchemeMorp pass -class IntegralAffineCurvePoint_finite_field(AffineCurvePoint_field): +class IntegralAffineCurvePoint(AffineCurvePoint_field): """ - Point of an integral affine curve over a finite field. + Point of an integral affine curve. """ def closed_point(self): """ @@ -422,6 +435,19 @@ def place(self): return self.closed_point().place() +class IntegralAffineCurvePoint_finite_field(IntegralAffineCurvePoint): + """ + Point of an integral affine curve over a finite field. + """ + pass + + +class IntegralAffinePlaneCurvePoint(IntegralAffineCurvePoint, AffinePlaneCurvePoint_field): + """ + Point of an integral affine plane curve over a finite field. + """ + pass + class IntegralAffinePlaneCurvePoint_finite_field(AffinePlaneCurvePoint_finite_field, IntegralAffineCurvePoint_finite_field): """ Point of an integral affine plane curve over a finite field. diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index c0fd53295b5..74ffd4d33e6 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -77,6 +77,40 @@ sage: _.place() Place (y) +Integral projective curves over `\QQ` +------------------------------------- + +An integral curve over `\QQ` is also equipped with the function field. Unlike +over finite fields, it is not possible to enumerate closed points. + +EXAMPLES:: + + sage: P. = ProjectiveSpace(QQ, 2) + sage: C = Curve(x^2*z^2 - x^4 - y^4, P) + sage: C.singular_closed_points() + [Point (x, y)] + sage: p, = _ + sage: p.places() + [Place (1/y, 1/y^2*z - 1), Place (1/y, 1/y^2*z + 1)] + sage: fy = C.function(y/z) + sage: fy.divisor() + Place (1/y, 1/y^2*z - 1) + + Place (1/y, 1/y^2*z + 1) + + Place (y, z - 1) + + Place (y, z + 1) + - Place (y^4 + 1, z) + sage: supp = _.support() + sage: pl = supp[0] + sage: C.place_to_closed_point(pl) + Point (x, y) + sage: pl = supp[1] + sage: C.place_to_closed_point(pl) + Point (x, y) + sage: _.rational_point() + (0 : 0 : 1) + sage: _ in C + True + AUTHORS: - William Stein (2005-11-13) @@ -133,7 +167,9 @@ from .point import (ProjectiveCurvePoint_field, ProjectivePlaneCurvePoint_field, ProjectivePlaneCurvePoint_finite_field, + IntegralProjectiveCurvePoint, IntegralProjectiveCurvePoint_finite_field, + IntegralProjectivePlaneCurvePoint, IntegralProjectivePlaneCurvePoint_finite_field) from .closed_point import IntegralProjectiveCurveClosedPoint @@ -2120,14 +2156,45 @@ class IntegralProjectiveCurve(ProjectiveCurve_field): """ Integral projective curve. """ + _point = IntegralProjectiveCurvePoint _closed_point = IntegralProjectiveCurveClosedPoint + def __init__(self, A, f): + """ + Initialize. + + TESTS:: + + sage: P. = ProjectiveSpace(GF(5), 2) + sage: C = Curve(y^2*z^7 - x^9 - x*z^8) + sage: loads(dumps(C)) == C + True + """ + super(IntegralProjectiveCurve, self).__init__(A, f) + + ideal = self.defining_ideal() + gs = self.ambient_space().gens() + for i in range(self.ngens()): + if not gs[i] in ideal: + self._open_affine = self.affine_patch(i) + self._open_affine_index = i + break + else: + assert "no projective curve defined" + def function_field(self): """ Return the function field of this curve. EXAMPLES:: + sage: P. = ProjectiveSpace(QQ, 2) + sage: C = Curve(x^2 + y^2 + z^2, P) + sage: C.function_field() + Function field in z defined by z^2 + y^2 + 1 + + :: + sage: P. = ProjectiveSpace(GF(4), 2) sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5) sage: C.function_field() @@ -2147,7 +2214,7 @@ def _genus(self): sage: C.genus() # indirect doctest 1 """ - return self._function_field.genus() + return self._open_affine.genus() def __call__(self, *args): """ @@ -2222,55 +2289,6 @@ def coordinate_functions(self, i=None): inv = ~coords[i] return tuple([coords[j]*inv for j in range(len(coords)) if j != i]) - -class IntegralProjectiveCurve_finite_field(IntegralProjectiveCurve): - """ - Integral projective curve over a finite field. - - INPUT: - - - ``A`` -- an ambient projective space - - - ``f`` -- homogeneous polynomials defining the curve - - EXAMPLES:: - - sage: P. = ProjectiveSpace(GF(5), 2) - sage: C = Curve(y^2*z^7 - x^9 - x*z^8) - sage: C.function_field() - Function field in z defined by z^8 + 4*y^2*z^7 + 1 - sage: C.closed_points() - [Point (x, z), - Point (x, y), - Point (x - 2*z, y + 2*z), - Point (x + 2*z, y + z), - Point (x + 2*z, y - z), - Point (x - 2*z, y - 2*z)] - """ - _point = IntegralProjectiveCurvePoint_finite_field - - def __init__(self, A, f): - """ - Initialize. - - TESTS:: - - sage: P. = ProjectiveSpace(GF(5), 2) - sage: C = Curve(y^2*z^7 - x^9 - x*z^8) - sage: loads(dumps(C)) == C - True - """ - super(IntegralProjectiveCurve_finite_field, self).__init__(A, f) - - ideal = self.defining_ideal() - gs = self.ambient_space().gens() - for i in range(self.ngens()): - if not gs[i] in ideal: - break - - self._open_affine = self.affine_patch(i) - self._open_affine_index = i - @lazy_attribute def _function_field(self): """ @@ -2383,6 +2401,13 @@ def singular_closed_points(self): EXAMPLES:: + sage: P. = ProjectiveSpace(QQ, 2) + sage: C = Curve(y^2*z - x^3, P) + sage: C.singular_closed_points() + [Point (x, y)] + + :: + sage: P. = ProjectiveSpace(GF(5), 2) sage: C = Curve(y^2*z^7 - x^9 - x*z^8) sage: C.singular_closed_points() @@ -2485,6 +2510,21 @@ def places_on(self, point): EXAMPLES:: + sage: P. = ProjectiveSpace(QQ, 2) + sage: C = Curve(x*y*z^4 - x^6 - y^6) + sage: C.singular_closed_points() + [Point (x, y)] + sage: p, = _ + sage: C.places_on(p) + [Place (1/y, 1/y^2*z, 1/y^3*z^2, 1/y^4*z^3), Place (y, y*z, y*z^2, y*z^3)] + sage: pl1, pl2 =_ + sage: C.place_to_closed_point(pl1) + Point (x, y) + sage: C.place_to_closed_point(pl2) + Point (x, y) + + :: + sage: P. = ProjectiveSpace(GF(5), 2) sage: C = Curve(x^2*z - y^3) sage: [C.places_on(p) for p in C.closed_points()] @@ -2514,6 +2554,33 @@ def places_on(self, point): places.append(p) return places + +class IntegralProjectiveCurve_finite_field(IntegralProjectiveCurve): + """ + Integral projective curve over a finite field. + + INPUT: + + - ``A`` -- an ambient projective space + + - ``f`` -- homogeneous polynomials defining the curve + + EXAMPLES:: + + sage: P. = ProjectiveSpace(GF(5), 2) + sage: C = Curve(y^2*z^7 - x^9 - x*z^8) + sage: C.function_field() + Function field in z defined by z^8 + 4*y^2*z^7 + 1 + sage: C.closed_points() + [Point (x, z), + Point (x, y), + Point (x - 2*z, y + 2*z), + Point (x + 2*z, y + z), + Point (x + 2*z, y - z), + Point (x - 2*z, y - 2*z)] + """ + _point = IntegralProjectiveCurvePoint_finite_field + def places(self, degree=1): """ Return all places on the curve of the ``degree``. @@ -2668,7 +2735,12 @@ def number_of_rational_points(self, r=1): return n -class IntegralProjectivePlaneCurve_finite_field(ProjectivePlaneCurve_finite_field, IntegralProjectiveCurve_finite_field): +class IntegralProjectivePlaneCurve(IntegralProjectiveCurve, ProjectivePlaneCurve_field): + _point = IntegralProjectivePlaneCurvePoint + + +class IntegralProjectivePlaneCurve_finite_field(IntegralProjectiveCurve_finite_field, + ProjectivePlaneCurve_finite_field): """ Integral projective plane curve over a finite field. From 5f2e4b3c2a6416e62840b6c1f75b18119551516d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 26 Jun 2020 16:33:08 +0200 Subject: [PATCH 124/204] a bunch of typos --- src/sage/geometry/polyhedron/library.py | 2 +- src/sage/manifolds/differentiable/characteristic_class.py | 2 +- src/sage/manifolds/differentiable/vector_bundle.py | 2 +- src/sage/schemes/curves/closed_point.py | 6 +++--- src/sage/schemes/elliptic_curves/mod_sym_num.pyx | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/geometry/polyhedron/library.py b/src/sage/geometry/polyhedron/library.py index 7d2a1b8de20..2256fde26da 100644 --- a/src/sage/geometry/polyhedron/library.py +++ b/src/sage/geometry/polyhedron/library.py @@ -3047,7 +3047,7 @@ def hypercube(self, dim, intervals=None, backend=None): - ``intervals`` -- (default = None). It takes the following possible inputs: - - If ``None`` (the default), it returns the the `\pm 1`-cube of + - If ``None`` (the default), it returns the `\pm 1`-cube of dimension ``dim``. - ``'zero_one'`` -- (string). Return the `0/1`-cube. diff --git a/src/sage/manifolds/differentiable/characteristic_class.py b/src/sage/manifolds/differentiable/characteristic_class.py index 34f5754a8d4..da2e2299430 100644 --- a/src/sage/manifolds/differentiable/characteristic_class.py +++ b/src/sage/manifolds/differentiable/characteristic_class.py @@ -255,7 +255,7 @@ sage: g[eV,1,1], g[eV,2,2] = 4/(1+u^2+v^2)^2, 4/(1+u^2+v^2)^2 sage: nab = g.connection() -In case of the the Euler class, skew-symmetric curvature matrices are needed +In case of the Euler class, skew-symmetric curvature matrices are needed for the Pfaffian. For this, we need to define the curvature matrices by hand:: diff --git a/src/sage/manifolds/differentiable/vector_bundle.py b/src/sage/manifolds/differentiable/vector_bundle.py index a06bdfa987f..b0b83c41e21 100644 --- a/src/sage/manifolds/differentiable/vector_bundle.py +++ b/src/sage/manifolds/differentiable/vector_bundle.py @@ -1461,7 +1461,7 @@ def ambient_domain(self): def destination_map(self): r""" - Return the the destination map. + Return the destination map. OUTPUT: diff --git a/src/sage/schemes/curves/closed_point.py b/src/sage/schemes/curves/closed_point.py index ef022cbf7f5..718b8a20c76 100644 --- a/src/sage/schemes/curves/closed_point.py +++ b/src/sage/schemes/curves/closed_point.py @@ -71,7 +71,7 @@ # 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.structure.richcmp import richcmp @@ -400,7 +400,7 @@ def affine(self, i=None): sage: p3.affine(1) Traceback (most recent call last): ... - ValueError: not in the the affine patch + ValueError: not in the affine patch """ C = self.curve() P = C.ambient_space() @@ -412,7 +412,7 @@ def affine(self, i=None): break else: if P.gen(i) in ideal: - raise ValueError("not in the the affine patch") + raise ValueError("not in the affine patch") A = P.affine_patch(i) phi = A.projective_embedding(i, P) diff --git a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx index 3c2d253d63b..91ff1a9e6c9 100644 --- a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx +++ b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx @@ -1374,7 +1374,7 @@ cdef class ModularSymbolNumerical: r""" Given a point `\tau` in the upper half plane this returns a complex number that is a close - approximation to the the integral of the modular + approximation to the integral of the modular form from `i\infty` to `\tau`. INPUT: @@ -1448,7 +1448,7 @@ cdef class ModularSymbolNumerical: Given a point `\tau` in the upper half plane this returns a complex number that is a close approximation to `\lambda(tau)`, - the the integral of the modular + the integral of the modular form from `i\infty` to `\tau`. INPUT: From 22c1ae0414765bc79930f610a1441447043ec12c Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Fri, 26 Jun 2020 16:55:52 +0200 Subject: [PATCH 125/204] added construction for biplane (79,13,2); there is a bug to fix --- src/sage/combinat/designs/bibd.py | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index b1545bd9a53..d47812578ab 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -1219,6 +1219,43 @@ def BIBD_from_arc_in_desarguesian_projective_plane(n,k,existence=False): from sage.combinat.designs.block_design import DesarguesianProjectivePlaneDesign return DesarguesianProjectivePlaneDesign(q).trace(C)._blocks +def biplane_79_13_2(): + r""" + Return a `(79,13,2)`-BIBD. + + The construction follows the paper .... + """ + + # We have a correspondece between elements of G and integers + # x^a*y^b*z^c <-> 1+c+2*b+10*a + def to_int(x,y,z): + return 1 + z+ 2*y+ 10*x + + # P(i,a,b,c) represents P_i x^a*y^b*z^c + # we ensure that each set is represented uniquely + # i.e. we simplify the action of G + def P(i,x,y,z): + x = x%11 + y = y%5 + z = z%2 + if i == 1: return (to_int(0, 0, z), 0, 0, 0) + if i == 2: return (0, to_int(x, 0, 0), 0, 0) + if i == 3: return (0, 0, to_int(x, 0, 0), 0) + if i == 4: return (0, 0, 0, to_int(x, y, 0)) + + # The set of points is: + # [P(i,x,y,z) for i in range(1,5) for x in range(11) for y in range(5) for z in range(2)] + B1 = [P(1,0,0,0), P(1,0,0,1)] + [P(2,x,0,0) for x in range(11)] + B2 = [P(1,0,0,0), P(1,0,0,1)] + [P(3,x,0,0) for x in range(11)] + blocks = [B1, B2] + blocks += [([P(1,x,0,z), P(2,x,0,z), P(3,x,0,z)] + [P(4,1+x,y,z) for y in range(5)]) for x in range(11) for z in range(2)] + blocks += [[P(2,2+x,+y,0), P(2,x-2,y,0), P(3,x+5,y,0), P(3,x-5,y,0), P(4,x,y,0), + P(4,x+1,y+2,0), P(4,x-1,y+2,0), P(4,x+1,y+1,0), P(4,x-1,y+1,0), + P(4,x+5,y+1,0), P(4,x-5,y+1,0), P(4,x+5,y+4,0), P(4,x-5,y+4,0)] for x in range(11) for y in range(5)] + + return IncidenceStructure(blocks) + + class PairwiseBalancedDesign(GroupDivisibleDesign): r""" Pairwise Balanced Design (PBD) From 8c4cc0d2e7f9b0b6e26b9172817bee85ee542369 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Fri, 26 Jun 2020 09:25:53 -0700 Subject: [PATCH 126/204] [BK2001] => [BaKi2001] to avoid clash with Bruns and Koch --- src/doc/en/reference/references/index.rst | 4 ++-- src/sage/combinat/root_system/fusion_ring.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index c70b096a4cc..3d4597bc726 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -344,7 +344,7 @@ REFERENCES: **B** -.. [BK2001] Bakalov and Kirillov, *Lectures on tensor categories and modular functors*, +.. [BaKi2001] Bakalov and Kirillov, *Lectures on tensor categories and modular functors*, AMS (2001). .. [Ba1994] Kaushik Basu. *The Traveler's Dilemma: Paradoxes of @@ -1956,7 +1956,7 @@ REFERENCES: 1981, Pages 108-125, ISSN 0097-3165,:doi:`10.1016/0097-3165(81)90007-8`. (http://www.sciencedirect.com/science/article/pii/0097316581900078) -.. [EGNO20015] Pavel Etingof, Shlomo Gelaki, Dmitri Nikshych and Victor Ostrik, +.. [EGNO2015] Pavel Etingof, Shlomo Gelaki, Dmitri Nikshych and Victor Ostrik, *Tensor Categories*, AMS Mathematical Surveys and Monographs 205 (2015). .. [EGHLSVY] Pavel Etingof, Oleg Golberg, Sebastian Hensel, Tiankai diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index ee54f976df3..85910853d5e 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -36,7 +36,7 @@ class FusionRing(WeylCharacterRing): REFERENCES: - - [BK2001]_ Chapter 3 + - [BaKi2001]_ Chapter 3 - [DFMS1996]_ Chapter 16 - [EGNO2015]_ Chapter 8 - [Feingold2004]_ @@ -109,10 +109,10 @@ class FusionRing(WeylCharacterRing): to the ribbon structure, and the S-matrix :meth:`s_ij`. There are two natural normalizations of the S-matrix. Both - are explained in Chapter 3 of [BK2001]_. The one that is computed + are explained in Chapter 3 of [BaKi2001]_. The one that is computed by the method :meth:`s_matrix`, or whose individual entries are computed by :meth:`s_ij` is denoted `\tilde{s}` in - [BK2001]_. It is not unitary. To make it unitary, one would + [BaKi2001]_. It is not unitary. To make it unitary, one would divide by the square root of `D = \sum_V d_i(V)^2` where the sum is over all simple objects `V` and `d_i(V)` is the quantum dimension of `V` computed by the method :meth:`q_dimension`. The quantity `D` @@ -125,7 +125,7 @@ class FusionRing(WeylCharacterRing): N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)}, where ``I`` is the unit object. In this formula `s` is the normalized - unitary S-matrix denoted `s` in [BK2001]_. We may define a function that + unitary S-matrix denoted `s` in [BaKi2001]_. We may define a function that corresponds to the right-hand side, except using `\tilde{s}` instead of `s`:: @@ -390,7 +390,7 @@ def s_matrix(self): .. NOTE:: - This is the matrix denoted `\widetilde{s}` in [BK2001]_. + This is the matrix denoted `\widetilde{s}` in [BaKi2001]_. It is not normalized to be unitary. EXAMPLES:: From aa02d4f4a9421488d9f326adf02d587d5e6a2c7a Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Fri, 26 Jun 2020 19:56:05 +0200 Subject: [PATCH 127/204] Remove obsolete test block --- src/sage/ext/fast_callable.pyx | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/src/sage/ext/fast_callable.pyx b/src/sage/ext/fast_callable.pyx index 43dafee7421..580f115692b 100644 --- a/src/sage/ext/fast_callable.pyx +++ b/src/sage/ext/fast_callable.pyx @@ -422,39 +422,6 @@ def fast_callable(x, domain=None, vars=None, Traceback (most recent call last): ... TypeError: unable to simplify to float approximation - - Check :trac:`24805`--if a fast_callable expression involves division - on a Python object, it will always prefer Python 3 semantics (e.g. - ``x / y`` will try ``x.__truediv__`` instead of ``x.__div__``, as if - ``from __future__ import division`` is in effect). However, for - classes that implement ``__div__`` but not ``__truediv__`` it will still - fall back on ``__div__`` for backwards-compatibility, but reliance on - this functionality is deprecated:: - - sage: from sage.ext.fast_callable import ExpressionTreeBuilder - sage: etb = ExpressionTreeBuilder('x') - sage: x = etb.var('x') - sage: class One(object): - ....: def __div__(self, other): - ....: if not isinstance(other, Integer): - ....: return NotImplemented - ....: return 1 / other - sage: expr = One() / x - sage: f = fast_callable(expr, vars=[x]) - sage: f(2) # py2 - doctest:warning...: - DeprecationWarning: use of __truediv__ should be preferred over __div__ - See https://trac.sagemath.org/24805 for details. - 1/2 - sage: class ModernOne(One): - ....: def __truediv__(self, other): - ....: if not isinstance(other, Integer): - ....: return NotImplemented - ....: return 1 / other - sage: expr = ModernOne() / x - sage: f = fast_callable(expr, vars=[x]) - sage: f(2) - 1/2 """ cdef Expression et if isinstance(x, Expression): From 5a486782132739fea437c71e6e3765cf6e628d68 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Fri, 26 Jun 2020 21:56:11 -0700 Subject: [PATCH 128/204] doc revision --- src/sage/combinat/root_system/fusion_ring.py | 40 +++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 85910853d5e..a0e1978a7bf 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -28,11 +28,12 @@ class FusionRing(WeylCharacterRing): - ``ct`` -- the Cartan type of a simple (finite-dimensional) Lie algebra - ``k`` -- a nonnegative integer - This algebra has a basis (sometimes called *primary fields*) - indexed by the weights of level `\leq k`. These arise as - the fusion algebras of WZW conformal field theories, or from - quantum groups at roots of unity. The :class:`FusionRing` class is - implemented as a variant of the :class:`WeylCharacterRing`. + This algebra has a basis (sometimes called *primary fields* but here + called *simple objects*) indexed by the weights of level `\leq k`. + These arise as the fusion algebras of WZW conformal field theories, + or as Grothendieck groups of tilting modules for quantum groups at + roots of unity. The :class:`FusionRing` class is implemented as a + variant of the :class:`WeylCharacterRing`. REFERENCES: @@ -82,9 +83,9 @@ class FusionRing(WeylCharacterRing): sage: Z * Z I0 - A fixed order of the basis keys is avalailable with :meth:`get_order`. + A fixed order of the basis keys is available with :meth:`get_order`. This is the order used by methods such as :meth:`s_matrix`. - You may use :meth:`set_order` to reorder the basis:: + You may use :meth:`CombinatorialFreeModule.set_order` to reorder the basis:: sage: B22.set_order([x.weight() for x in [I0,Y1,Y2,X,Xp,Z]]) sage: [B22(x) for x in B22.get_order()] @@ -105,7 +106,7 @@ class FusionRing(WeylCharacterRing): The fusion ring has a number of methods that reflect its role as the Grothendieck ring of a modular tensor category. These - include a twist method :meth:`twist` for its elements related + include a twist method :meth:`Element.twist` for its elements related to the ribbon structure, and the S-matrix :meth:`s_ij`. There are two natural normalizations of the S-matrix. Both @@ -115,8 +116,8 @@ class FusionRing(WeylCharacterRing): [BaKi2001]_. It is not unitary. To make it unitary, one would divide by the square root of `D = \sum_V d_i(V)^2` where the sum is over all simple objects `V` and `d_i(V)` is the quantum dimension - of `V` computed by the method :meth:`q_dimension`. The quantity `D` - is computed by :meth:`q_dimension`. + of `V` computed by the method :meth:`Element.q_dimension`. The quantity `D` + is computed by :meth:`total_quantum_order`. Let us check the Verlinde formula. This famous identity states that @@ -195,9 +196,8 @@ def field(self): return CyclotomicField(4 * self._fg * self._l) def get_order(self): - r""" - This returns the weights of the basis vectors in a fixed order. - You may change the order of the basis using :meth:`set_order` + r"""This returns the weights of the basis vectors in a fixed order. + You may change the order of the basis using :meth:`CombinatorialFreeModule.set_order` EXAMPLES:: @@ -212,8 +212,10 @@ def get_order(self): This duplicates :meth:`get_order` from :class:`CombinatorialFreeModule` except the result - is *not* cached. Caching of :meth:`get_order` causes - inconsistent results after calling :meth:`set_order`. + is *not* cached. Caching of + :meth:`CombinatorialFreeModule.get_order` causes inconsistent + results after calling :meth:`CombinatorialFreeModule.set_order`. + """ if self._order is None: self.set_order(self.basis().keys().list()) @@ -246,7 +248,7 @@ def fusion_k(self): def fusion_l(self): r""" - Return the product `m_g(k + h^\vee)`, where `m_g` denotes the + Return the product `{\ell}=m_g(k + h^\vee)`, where `m_g` denotes the square of the ratio of the lengths of long to short roots of the underlying Lie algebra, `k` denotes the level of the FusionRing, and `h^\vee` denotes the dual Coxeter number of the underlying Lie @@ -391,7 +393,9 @@ def s_matrix(self): .. NOTE:: This is the matrix denoted `\widetilde{s}` in [BaKi2001]_. - It is not normalized to be unitary. + It is not normalized to be unitary. To obtain a unitary + matrix, divide by `\sqrt{D}` where `D` is computed + by :meth:`total_quantum_order`. EXAMPLES:: @@ -573,7 +577,7 @@ def twist(self): def q_dimension(self): r""" Return the quantum dimension as an element of the cyclotomic - field of the `2l`-th roots of unity, where `l = m (k+h^\vee)` + field of the `2\ell`-th roots of unity, where `l = m (k+h^\vee)` with `m=1,2,3` depending on whether type is simply, doubly or triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. From ab60c40550b9aacc101d1bbb39b4f22d5e9dfee7 Mon Sep 17 00:00:00 2001 From: Markus Wageringel Date: Sun, 21 Jun 2020 19:37:06 +0200 Subject: [PATCH 129/204] 29723: fix doctests of skew polynomials --- .../polynomial/skew_polynomial_element.pyx | 30 +++++++------------ .../rings/polynomial/skew_polynomial_ring.py | 8 ++--- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_element.pyx b/src/sage/rings/polynomial/skew_polynomial_element.pyx index db64f3ce84f..c9ad473fd5f 100644 --- a/src/sage/rings/polynomial/skew_polynomial_element.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_element.pyx @@ -195,14 +195,11 @@ cdef class SkewPolynomial(AlgebraElement): sage: r == c % b True - Left euclidean division won't work over our current `S` because Sage can't - invert the twist map:: + :: - sage: q,r = c.left_quo_rem(b) - Traceback (most recent call last): - ... - NotImplementedError: inversion of the twist map Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring - Defn: t |--> t + 1 + sage: q, r = c.left_quo_rem(b) + sage: c == b*q + r + True Here we can see the effect of the operator evaluation compared to the usual polynomial evaluation:: @@ -534,12 +531,10 @@ cdef class SkewPolynomial(AlgebraElement): able to invert the twist map:: sage: b = a.conjugate(-1) - Traceback (most recent call last): - ... - NotImplementedError: inversion of the twist map Ring endomorphism of Univariate Polynomial Ring in t over Rational Field - Defn: t |--> t + 1 + sage: a*x == x*b + True - Here is a working example:: + Here is another example:: sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() @@ -1183,19 +1178,16 @@ cdef class SkewPolynomial(AlgebraElement): sage: a == b*q + r True - In the following example, Sage does not know the inverse - of the twist map:: + :: sage: R. = ZZ[] sage: sigma = R.hom([t+1]) sage: S. = R['x',sigma] sage: a = (-2*t^2 - t + 1)*x^3 + (-t^2 + t)*x^2 + (-12*t - 2)*x - t^2 - 95*t + 1 sage: b = x^2 + (5*t - 6)*x - 4*t^2 + 4*t - 1 - sage: a.left_quo_rem(b) - Traceback (most recent call last): - ... - NotImplementedError: inversion of the twist map Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring - Defn: t |--> t + 1 + sage: q, r = a.left_quo_rem(b) + sage: a == b*q + r + True """ if not other: raise ZeroDivisionError("division by zero is not valid") diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 14e70481956..412e13366c7 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -762,13 +762,11 @@ def twist_map(self, n=1): sage: T.twist_map(-1) Frobenius endomorphism t |--> t^(5^2) on Finite Field in t of size 5^3 - Sometimes it fails, even if the twist map is actually invertible:: + :: sage: S.twist_map(-1) - Traceback (most recent call last): - ... - NotImplementedError: inversion of the twist map Ring endomorphism of Univariate Polynomial Ring in t over Rational Field - Defn: t |--> t + 1 + Ring endomorphism of Univariate Polynomial Ring in t over Rational Field + Defn: t |--> t - 1 """ try: return self._map ** n From 8a41546d3ceffe39842f7d654049043ab5424cb0 Mon Sep 17 00:00:00 2001 From: Kiran Kedlaya Date: Wed, 17 Jun 2020 21:53:28 -0700 Subject: [PATCH 130/204] Speed up hgm_coeffs using symmetry --- src/sage/modular/hypergeometric_misc.pxd | 1 + src/sage/modular/hypergeometric_misc.pyx | 79 +++++++++++++++--------- 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/src/sage/modular/hypergeometric_misc.pxd b/src/sage/modular/hypergeometric_misc.pxd index 00bf9a97e9a..d031601666f 100644 --- a/src/sage/modular/hypergeometric_misc.pxd +++ b/src/sage/modular/hypergeometric_misc.pxd @@ -1,2 +1,3 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, gtable, int gtable_prec, bint use_longs) + diff --git a/src/sage/modular/hypergeometric_misc.pyx b/src/sage/modular/hypergeometric_misc.pyx index 93e2eb34257..0a7fecafb93 100644 --- a/src/sage/modular/hypergeometric_misc.pyx +++ b/src/sage/modular/hypergeometric_misc.pyx @@ -3,6 +3,7 @@ Some utility routines for the hypergeometric motives package that benefit significantly from Cythonization. """ from cpython cimport array +from cysignals.signals cimport sig_on, sig_off cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, gtable, int gtable_prec, bint use_longs): @@ -36,7 +37,7 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, gl = len(gamma) cdef array.array gamma_array1 = array.array('i', gamma.keys()) cdef array.array gamma_array2 = array.array('i', gamma.values()) - cdef array.array r_array = array.array('i', [0]) * gl + r_array = [0] * gl cdef array.array digit_count = array.array('i', [0]) * q1 cdef array.array gtab2 @@ -62,13 +63,18 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, else: for r in range(q1): r1 = r - digit_count[r] = 0 + w = 0 for i in range(f): - digit_count[r] += r1 % p + w += r1 % p r1 //= p + digit_count[r] = w Rz = R.zero() + ans = [None] * q1 for r in range(q1): - # First determine whether this term is forced to be zero + # Skip this coefficient if we already have it by symmetry. + if ans[r] is not None: + continue + # Determine whether this term is forced to be zero # for divisibility reasons. If so, skip the p-adic arithmetic. i = 0 for k in range(gl): @@ -80,34 +86,47 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, i //= (p - 1) l = i + f * (D + m[0] - m[r]) if l >= prec: - ans.append(Rz) - continue - # Keep numerator and denominator separate for efficiency. - if use_longs: - w = 1 - w1 = 1 + ans[r] = Rz else: - u = R.one() - u1 = R.one() - for k in range(gl): - gv = gamma_array2[k] - r1 = r_array[k] - if flip: - gv = -gv + # Keep numerator and denominator separate for efficiency. if use_longs: - w2 = gtab2[r1] # cast to long long to avoid overflow - if gv > 0: - for j in range(gv): w = w * w2 % q2 - else: - for j in range(-gv): w1 = w1 * w2 % q2 + w = 1 + w1 = 1 else: - if gv > 0: - for j in range(gv): u *= gtable[r1] + u = R.one() + u1 = R.one() + for k in range(gl): + gv = gamma_array2[k] + r1 = r_array[k] + if flip: + gv = -gv + if use_longs: + w2 = gtab2[r1] # cast to long long to avoid overflow + if gv > 0: + for j in range(gv): + w = w * w2 % q2 + else: + for j in range(-gv): + w1 = w1 * w2 % q2 else: - for j in range(-gv): u1 *= gtable[r1] - if use_longs: - u = R(w) - u1 = R(w1) - if i % 2: u = -u - ans.append((u / u1) << l) + w2 = gtable[r1] + if gv > 0: + for j in range(gv): + u *= w2 + else: + for j in range(-gv): + u1 *= w2 + if use_longs: + u = R(w) + u1 = R(w1) + if i % 2: u = -u + ans[r] = (u / u1) << l + if f > 1: + r1 = r + for j in range(f-1): + r1 = r1 * p % q1 + if ans[r1] is not None: + break + ans[r1] = ans[r] return ans + From 2ca6e9953ecf3223b4815508d435c3fc845ce255 Mon Sep 17 00:00:00 2001 From: Kiran Kedlaya Date: Thu, 18 Jun 2020 06:28:00 -0700 Subject: [PATCH 131/204] Fix inline if --- src/sage/modular/hypergeometric_misc.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/modular/hypergeometric_misc.pyx b/src/sage/modular/hypergeometric_misc.pyx index 0a7fecafb93..b587960d425 100644 --- a/src/sage/modular/hypergeometric_misc.pyx +++ b/src/sage/modular/hypergeometric_misc.pyx @@ -119,7 +119,8 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, if use_longs: u = R(w) u1 = R(w1) - if i % 2: u = -u + if i % 2: + u = -u ans[r] = (u / u1) << l if f > 1: r1 = r From a7565d2a456d1f0bb5a112b739b1d31a0eb56d8f Mon Sep 17 00:00:00 2001 From: Kiran Kedlaya Date: Thu, 18 Jun 2020 06:40:44 -0700 Subject: [PATCH 132/204] Implement keyboard interrupt in hgm_coeffs --- src/sage/modular/hypergeometric_misc.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/modular/hypergeometric_misc.pyx b/src/sage/modular/hypergeometric_misc.pyx index b587960d425..b74045acb66 100644 --- a/src/sage/modular/hypergeometric_misc.pyx +++ b/src/sage/modular/hypergeometric_misc.pyx @@ -3,7 +3,7 @@ Some utility routines for the hypergeometric motives package that benefit significantly from Cythonization. """ from cpython cimport array -from cysignals.signals cimport sig_on, sig_off +from cysignals.signals cimport sig_check cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, gtable, int gtable_prec, bint use_longs): @@ -71,6 +71,7 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, Rz = R.zero() ans = [None] * q1 for r in range(q1): + sig_check() # Skip this coefficient if we already have it by symmetry. if ans[r] is not None: continue From fcbb2bee76873ae431a8fdf58d29864aa98c58a9 Mon Sep 17 00:00:00 2001 From: Kiran Kedlaya Date: Thu, 18 Jun 2020 20:44:09 -0700 Subject: [PATCH 133/204] Consolidate terms in hgm_coeffs --- src/sage/modular/hypergeometric_misc.pyx | 13 +++++++++++-- src/sage/modular/hypergeometric_motive.py | 4 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/sage/modular/hypergeometric_misc.pyx b/src/sage/modular/hypergeometric_misc.pyx index b74045acb66..4db9f801cc1 100644 --- a/src/sage/modular/hypergeometric_misc.pyx +++ b/src/sage/modular/hypergeometric_misc.pyx @@ -130,5 +130,14 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, if ans[r1] is not None: break ans[r1] = ans[r] - return ans - + if f == 1: + return ans + # Consolidate down to p-1 terms. + ans2 = [Rz] * (p-1) + r1 = 0 + for r in range(q1): + ans2[r1] += ans[r] + r1 += 1 + if r1 == p-1: + r1 = 0 + return ans2 diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index b15d149cb42..2bca0d85f87 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -1291,10 +1291,10 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False): else: gtab = gauss_table(p, f, prec, use_longs) trcoeffs = hgm_coeffs(p, f, prec, gamma, m, D, gtab, prec, use_longs) - sigma = trcoeffs[q-2] + sigma = trcoeffs[p-2] p_ring = sigma.parent() teich = p_ring.teichmuller(M/t) - for i in range(q-3, -1, -1): + for i in range(p-3, -1, -1): sigma = sigma * teich + trcoeffs[i] resu = ZZ(-1) ** m[0] * sigma / (1 - q) return IntegerModRing(p**prec)(resu).lift_centered() From 058dc4d2a74580a7299941887f4873b0c6f584fd Mon Sep 17 00:00:00 2001 From: Kiran Kedlaya Date: Fri, 19 Jun 2020 06:57:03 -0700 Subject: [PATCH 134/204] Fix deprecated inline ifs --- src/sage/modular/hypergeometric_motive.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 2bca0d85f87..787ccb08644 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -1270,7 +1270,11 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False): m = defaultdict(int) for b in beta: u = b * (q - 1) +<<<<<<< HEAD if u.is_integer(): +======= + if u.is_integer(): +>>>>>>> Fix deprecated inline ifs m[u] += 1 M = self.M_value() D = -min(self.zigzag(x, flip_beta=True) for x in alpha + beta) From ad2888b6047c8180a787e16832770315bfa11728 Mon Sep 17 00:00:00 2001 From: Kiran Kedlaya Date: Sat, 27 Jun 2020 08:34:53 -0700 Subject: [PATCH 135/204] Update doctest in hgm_coeffs, simplify final loop --- src/sage/modular/hypergeometric_misc.pyx | 13 ++++--------- src/sage/modular/hypergeometric_motive.py | 4 ---- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/sage/modular/hypergeometric_misc.pyx b/src/sage/modular/hypergeometric_misc.pyx index 4db9f801cc1..0faa55e3701 100644 --- a/src/sage/modular/hypergeometric_misc.pyx +++ b/src/sage/modular/hypergeometric_misc.pyx @@ -15,16 +15,14 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, TESTS:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp - sage: import array sage: from sage.modular.hypergeometric_misc import hgm_coeffs sage: H = Hyp(cyclotomic=([3],[4])) sage: H.euler_factor(2, 7, cache_p=True) 7*T^2 - 3*T + 1 sage: gamma = H.gamma_array() sage: prec, gtable = H.gauss_table(7, 1, 2) - sage: m = array.array('i', [0]*6) sage: D = 1 - sage: hgm_coeffs(7, 1, 2, gamma, m, D, gtable, prec, False) + sage: hgm_coeffs(7, 1, 2, gamma, [0]*6, D, gtable, prec, False) [7, 2*7, 6*7, 7, 6, 4*7] """ from sage.rings.padics.factory import Zp @@ -134,10 +132,7 @@ cpdef hgm_coeffs(long long p, int f, int prec, gamma, m, int D, return ans # Consolidate down to p-1 terms. ans2 = [Rz] * (p-1) - r1 = 0 - for r in range(q1): - ans2[r1] += ans[r] - r1 += 1 - if r1 == p-1: - r1 = 0 + for r1 in range(p-1): + for r in range(r1, q1, p-1): + ans2[r1] += ans[r] return ans2 diff --git a/src/sage/modular/hypergeometric_motive.py b/src/sage/modular/hypergeometric_motive.py index 787ccb08644..2bca0d85f87 100644 --- a/src/sage/modular/hypergeometric_motive.py +++ b/src/sage/modular/hypergeometric_motive.py @@ -1270,11 +1270,7 @@ def padic_H_value(self, p, f, t, prec=None, cache_p=False): m = defaultdict(int) for b in beta: u = b * (q - 1) -<<<<<<< HEAD if u.is_integer(): -======= - if u.is_integer(): ->>>>>>> Fix deprecated inline ifs m[u] += 1 M = self.M_value() D = -min(self.zigzag(x, flip_beta=True) for x in alpha + beta) From 504472f2010ba746db0e3ec12f3e1b87228b6152 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sat, 27 Jun 2020 08:58:15 -0700 Subject: [PATCH 136/204] add Ising MTC as an example --- src/doc/en/reference/references/index.rst | 3 ++ src/sage/combinat/root_system/fusion_ring.py | 32 ++++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 3d4597bc726..2f12e4f60e8 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -4557,6 +4557,9 @@ REFERENCES: Contemp. Math., **413**, Amer. Math. Soc., Providence, RI, 2006. :arXiv:`math/0503226`. +.. [RoStWa2009] Eric Rowell, Richard Stong and Zhenghan Wang, *On classification + of modular tensor categories*, Comm. Math. Phys. 292, 343--389, 2009. + .. [RR1997] Arun Ram and Jeffrey Remmel. *Applications of the Frobenius formulas and the characters of the symmetric group and the Hecke algebras of type A*. J. Algebraic Combin. diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index a0e1978a7bf..77246df6cbe 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -20,8 +20,7 @@ from sage.misc.cachefunc import cached_method class FusionRing(WeylCharacterRing): - r""" - Return the Fusion Ring (Verlinde Algebra) of level ``k``. + r"""Return the Fusion Ring (Verlinde Algebra) of level ``k``. INPUT: @@ -151,6 +150,33 @@ class FusionRing(WeylCharacterRing): True sage: test_verlinde(FusionRing("B4",2)) True + + As an exercise, the reader may verify the examples in + Section 5.3 of [RoStWa2009]_. Here we check the example + of the Ising modular tensor product, which is related + to the BPZ minimal model `M(4,3)` or to an `E_8` coset + model. See [DFMS1996]_ sections 7.4.2 and + 18.4.1. [RoStWa2009]_ Example 5.3.4 tells us how to + construct it as the conjugate of the `E_8` level 2 + :class:`FusionRing`:: + + sage: I = FusionRing("E8",2,conjugate=True) + sage: I.fusion_labels(["i0","p","s"],inject_variables=True) + sage: b = I.basis().list(); b + [i0, p, s] + sage: [[x*y for x in b] for y in b] + [[i0, p, s], [p, i0, s], [s, s, i0 + p]] + sage: [x.q_dimension()^2 for x in b] + [1, 1, 2] + sage: I.s_matrix() + [ 1 1 -zeta128^48 + zeta128^16] + [ 1 1 zeta128^48 - zeta128^16] + [-zeta128^48 + zeta128^16 zeta128^48 - zeta128^16 0] + sage: I.s_matrix().apply_map(lambda x:x^2) + [1 1 2] + [1 1 2] + [2 2 0] + """ @staticmethod def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False): @@ -196,7 +222,7 @@ def field(self): return CyclotomicField(4 * self._fg * self._l) def get_order(self): - r"""This returns the weights of the basis vectors in a fixed order. + r"""Return the weights of the basis vectors in a fixed order. You may change the order of the basis using :meth:`CombinatorialFreeModule.set_order` EXAMPLES:: From 336e6c01e814e060023f7d3f50164862d8c622b5 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sat, 27 Jun 2020 09:13:17 -0700 Subject: [PATCH 137/204] typo --- src/sage/combinat/root_system/fusion_ring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 77246df6cbe..043e9bee0b6 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -153,7 +153,7 @@ class FusionRing(WeylCharacterRing): As an exercise, the reader may verify the examples in Section 5.3 of [RoStWa2009]_. Here we check the example - of the Ising modular tensor product, which is related + of the Ising modular tensor category, which is related to the BPZ minimal model `M(4,3)` or to an `E_8` coset model. See [DFMS1996]_ sections 7.4.2 and 18.4.1. [RoStWa2009]_ Example 5.3.4 tells us how to From c8d94ca553339d6357d9e5b2f52f0400a04e9a64 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sat, 27 Jun 2020 09:19:46 -0700 Subject: [PATCH 138/204] add twists to Ising example --- src/sage/combinat/root_system/fusion_ring.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 043e9bee0b6..4d6219d5302 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -166,6 +166,8 @@ class FusionRing(WeylCharacterRing): [i0, p, s] sage: [[x*y for x in b] for y in b] [[i0, p, s], [p, i0, s], [s, s, i0 + p]] + sage: [x.twist() for x in b] + [0, 1, 1/8] sage: [x.q_dimension()^2 for x in b] [1, 1, 2] sage: I.s_matrix() From 91990d0e1a3b6d934725cb9dfd4500aefebf7da9 Mon Sep 17 00:00:00 2001 From: David Fifield Date: Sat, 2 May 2020 10:09:48 -0600 Subject: [PATCH 139/204] Fix variable name in StandardPermutations_n.rank exception message --- src/sage/combinat/permutation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index bcefcbc24a8..67866f91508 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -6409,7 +6409,7 @@ def rank(self, p=None): return self.n - 1 if p in self: return Permutation(p).rank() - raise ValueError("x not in self") + raise ValueError("p not in self") def random_element(self): """ From f424a248c31e96a2dfcb9f8694cb7d9f5f1d3613 Mon Sep 17 00:00:00 2001 From: David Fifield Date: Sat, 2 May 2020 10:08:49 -0600 Subject: [PATCH 140/204] Typo fixes in src/sage/combinat/gray_codes.py. --- src/sage/combinat/gray_codes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/gray_codes.py b/src/sage/combinat/gray_codes.py index d9c6da78127..90924caf5f3 100644 --- a/src/sage/combinat/gray_codes.py +++ b/src/sage/combinat/gray_codes.py @@ -3,10 +3,10 @@ REFERENCES: -.. [Knuth-TAOCP2A] \D. Knuth "The art of computer programming", fascicules 2A, +.. [Knuth-TAOCP2A] \D. Knuth "The art of computer programming", fascicle 2A, "generating all n-tuples" -.. [Knuth-TAOCP3A] \D. Knuth "The art of computer programming", fascicule 3A +.. [Knuth-TAOCP3A] \D. Knuth "The art of computer programming", fascicle 3A "generating all combinations" Functions @@ -229,7 +229,7 @@ def _revolving_door_odd(n,t): sage: sum(1 for _ in _revolving_door_odd(10,5)) == binomial(10,5) - 1 True """ - # note: the numerotation of the steps below follows Kunth TAOCP + # note: the numbering of the steps below follows Knuth TAOCP c = list(range(t)) + [n] # the combination (ordered list of numbers of length t+1) while True: @@ -274,7 +274,7 @@ def _revolving_door_even(n,t): sage: sum(1 for _ in _revolving_door_even(12,6)) == binomial(12,6) - 1 True """ - # note: the numerotation of the setps below follows Kunth TAOCP + # note: the numbering of the steps below follows Knuth TAOCP c = list(range(t)) + [n] # the combination (ordered list of numbers of length t+1) From 13486dd490c6cff3ea162d12e79882eab5b04f71 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Sat, 27 Jun 2020 22:25:09 +0200 Subject: [PATCH 141/204] added biplanes (56,11,2) and (79,13,2) to database --- src/sage/combinat/designs/bibd.py | 37 ---------- src/sage/combinat/designs/database.py | 101 ++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 37 deletions(-) diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index d47812578ab..b1545bd9a53 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -1219,43 +1219,6 @@ def BIBD_from_arc_in_desarguesian_projective_plane(n,k,existence=False): from sage.combinat.designs.block_design import DesarguesianProjectivePlaneDesign return DesarguesianProjectivePlaneDesign(q).trace(C)._blocks -def biplane_79_13_2(): - r""" - Return a `(79,13,2)`-BIBD. - - The construction follows the paper .... - """ - - # We have a correspondece between elements of G and integers - # x^a*y^b*z^c <-> 1+c+2*b+10*a - def to_int(x,y,z): - return 1 + z+ 2*y+ 10*x - - # P(i,a,b,c) represents P_i x^a*y^b*z^c - # we ensure that each set is represented uniquely - # i.e. we simplify the action of G - def P(i,x,y,z): - x = x%11 - y = y%5 - z = z%2 - if i == 1: return (to_int(0, 0, z), 0, 0, 0) - if i == 2: return (0, to_int(x, 0, 0), 0, 0) - if i == 3: return (0, 0, to_int(x, 0, 0), 0) - if i == 4: return (0, 0, 0, to_int(x, y, 0)) - - # The set of points is: - # [P(i,x,y,z) for i in range(1,5) for x in range(11) for y in range(5) for z in range(2)] - B1 = [P(1,0,0,0), P(1,0,0,1)] + [P(2,x,0,0) for x in range(11)] - B2 = [P(1,0,0,0), P(1,0,0,1)] + [P(3,x,0,0) for x in range(11)] - blocks = [B1, B2] - blocks += [([P(1,x,0,z), P(2,x,0,z), P(3,x,0,z)] + [P(4,1+x,y,z) for y in range(5)]) for x in range(11) for z in range(2)] - blocks += [[P(2,2+x,+y,0), P(2,x-2,y,0), P(3,x+5,y,0), P(3,x-5,y,0), P(4,x,y,0), - P(4,x+1,y+2,0), P(4,x-1,y+2,0), P(4,x+1,y+1,0), P(4,x-1,y+1,0), - P(4,x+5,y+1,0), P(4,x-5,y+1,0), P(4,x+5,y+4,0), P(4,x-5,y+4,0)] for x in range(11) for y in range(5)] - - return IncidenceStructure(blocks) - - class PairwiseBalancedDesign(GroupDivisibleDesign): r""" Pairwise Balanced Design (PBD) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 1866fdc6b3c..01a61e7ff1e 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -4565,6 +4565,105 @@ def BIBD_201_6_1(): bibd = RecursivelyEnumeratedSet([frozenset(e) for e in bibd], successors=gens) return IncidenceStructure(bibd)._blocks +def biplane_79_13_2(): + r""" + Return a `(79,13,2)`-BIBD. + + EXAMPLES: + + sage: from sage.combinat.designs.database import biplane_79_13_2 + sage: D = IncidenceStructure(biplane_79_13_2()) + sage: D.is_t_design(t=2, v=79, k=13, l=2) + True + """ + from .incidence_structures import IncidenceStructure + Gelems = [(x,y,z) for x in range(11) for y in range(5) for z in range(2)] + + def Gop(a,b,c,x,y,z): + # First normalise the input + a = a%11 + b = b%5 + c = c%2 + x = x%11 + y = y%5 + z = z%2 + # We know: x y = y x^4 + # y x = x^3 y + # z x = x^10 z + # x z = z x^-1 + # z y = y z + return ((a + (3**b) * (10**c) * x)%11, (b+y)%5, (z+c)%2) + + # P(i,a,b,c) represents P_i x^a*y^b*z^c + # i.e. we compute the action of x^a*y^b*z^c on P_i + def P(i,x,y,z): + x = x%11 + y = y%5 + z = z%2 + if i == 1: return (1, (0, 0, z)) + if i == 2: return (2, (((4**y)*(1-2*z)*x)%11, 0, 0)) + if i == 3: return (3, (((4**y)*(1-2*z)*x)%11, 0, 0)) + if i == 4: return (4, (((1-2*z)*x)%11, y, 0)) + + points = {P(i,x,y,z) for i in range(1,5) for x in range(11) for y in range(5) for z in range(2)} + Gaction = {(i,(a,b,c)): {(x,y,z): P(i,*Gop(a,b,c,x,y,z)) for x,y,z in Gelems} for a,b,c in Gelems for i in [1,2,3,4]} + + B1 = [P(1,0,0,0), P(1,0,0,1)] + [P(2,x,0,0) for x in range(11)] + B2 = [P(1,0,0,0), P(1,0,0,1)] + [P(3,x,0,0) for x in range(11)] + B3 = [P(1,0,0,0), P(2,0,0,0), P(3,0,0,0)] + [P(4,1,y,0) for y in range(5)] + [P(4,4,y,0) for y in range(5)] + B4 = [P(2,2,0,0), P(2,-2,0,0), P(3,5,0,0), P(3,-5,0,0), P(4,0,0,0), P(4,1,2,0), P(4,-1,2,0), P(4,1,1,0), P(4,-1,1,0), P(4,5,1,0), P(4,-5,1,0), P(4,5,4,0), P(4,-5,4,0)] + + B3Orbit = set() + for g in Gelems: + B3g = frozenset([Gaction[p][g] for p in B3]) + B3Orbit.add(B3g) + + B4Orbit = set() + for g in Gelems: + B4g = frozenset([Gaction[p][g] for p in B4]) + B4Orbit.add(B4g) + + blocks = [B1,B2] + list(B3Orbit) + list(B4Orbit) + + D = IncidenceStructure(blocks) + return D._blocks + +def biplane_56_11_2(): + r""" + Return a `(56,11,2)`-BIBD. + + EXAMPLES: + + sage: from sage.combinat.designs.database import biplane_56_11_2 + sage: D = IncidenceStructure(biplane_56_11_2()) + sage: D.is_t_design(t=2, v=56, k=11, l=2) + True + """ + from sage.libs.gap.libgap import libgap + from .incidence_structures import IncidenceStructure + + a = list(range(2,57)) + [50] + a[6] = 1 + a[13] = 8 + a[20] = 15 + a[27] = 22 + a[34] = 29 + a[41] = 36 + a[48] = 43 + + b = [1,8,27,36,20,14,42,41,29,52,24,30,55,22,26,21,10,40,23,53, + 56,6,49,46,50,32,28,3,34,48,4,15,13,9,18,31,51,39,43,35, + 2,54,38,25,45,11,37,12,19,44,47,17,5,7,33,16] + + a = libgap.PermList(a) + b = libgap.PermList(b) + G = libgap.Group(a,b) + + B = libgap.Set([1,12,19,23,30,37,45,47,48,49,51]) + + D = IncidenceStructure(libgap.Orbit(G, B, libgap.OnSets)) + return D._blocks + # Index of the BIBD constructions # # Associates to triple (v,k,lambda) a function that return a @@ -4574,8 +4673,10 @@ def BIBD_201_6_1(): BIBD_constructions = { ( 45,9,8): BIBD_45_9_8, + (56,11,2): biplane_56_11_2, ( 66,6,1): BIBD_66_6_1, ( 76,6,1): BIBD_76_6_1, + (79,13,2): biplane_79_13_2, ( 96,6,1): BIBD_96_6_1, (120,8,1): RBIBD_120_8_1, (106,6,1): BIBD_106_6_1, From b45d5fb82ba163f5df09223cfc27327dd0b58f44 Mon Sep 17 00:00:00 2001 From: David Fifield Date: Sat, 2 May 2020 10:38:41 -0600 Subject: [PATCH 142/204] Update references in src/sage/combinat/gray_codes.py. These chapters have now appeared in book form. The references to "fascicle 2A" and "fascicle 3A" were probably erroneous; they should have been "Volume 4A Fascicle 2" and "Volume 4A Fascicle 3". See https://www-cs-faculty.stanford.edu/~knuth/taocp.html#vol4. --- src/sage/combinat/gray_codes.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/gray_codes.py b/src/sage/combinat/gray_codes.py index 90924caf5f3..87282c08a8e 100644 --- a/src/sage/combinat/gray_codes.py +++ b/src/sage/combinat/gray_codes.py @@ -3,11 +3,8 @@ REFERENCES: -.. [Knuth-TAOCP2A] \D. Knuth "The art of computer programming", fascicle 2A, - "generating all n-tuples" - -.. [Knuth-TAOCP3A] \D. Knuth "The art of computer programming", fascicle 3A - "generating all combinations" +.. [Knuth-TAOCP4A] \D. Knuth *The Art of Computer Programming. Volume 4A. + Combinatorial Algorithms, Part 1.* Functions --------- @@ -25,8 +22,8 @@ def product(m): apply the increment ``i`` at the position ``p``. By construction, the increment is either ``+1`` or ``-1``. - This is algorithm H in [Knuth-TAOCP2A]_: loopless reflected mixed-radix Gray - generation. + This is algorithm H in [Knuth-TAOCP4A]_ Section 7.2.1.1, "Generating All + `n`-Tuples": loopless reflected mixed-radix Gray generation. INPUT: @@ -123,7 +120,7 @@ def combinations(n,t): The ground set is always `\{0, 1, ..., n-1\}`. Note that ``n`` can be infinity in that algorithm. - See [Knuth-TAOCP3A]_. + See [Knuth-TAOCP4A]_ Section 7.2.1.3, "Generating All Combinations". INPUT: From 04cefcf9dca68aff2bef03c2d4778cc6df7ab796 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sat, 27 Jun 2020 18:10:24 -0700 Subject: [PATCH 143/204] renamed total_quantum_order => global_q_dimension --- src/sage/combinat/root_system/fusion_ring.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 4d6219d5302..263a4cb2ea7 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -116,7 +116,7 @@ class FusionRing(WeylCharacterRing): divide by the square root of `D = \sum_V d_i(V)^2` where the sum is over all simple objects `V` and `d_i(V)` is the quantum dimension of `V` computed by the method :meth:`Element.q_dimension`. The quantity `D` - is computed by :meth:`total_quantum_order`. + is computed by :meth:`global_q_dimension`. Let us check the Verlinde formula. This famous identity states that @@ -140,7 +140,7 @@ class FusionRing(WeylCharacterRing): sage: def test_verlinde(R): ....: b0 = R.one() - ....: c = R.total_quantum_order() + ....: c = R.global_q_dimension() ....: return all(V(i,j,k) == c * R.N_ijk(i,j,k) for i in R.basis() ....: for j in R.basis() for k in R.basis()) @@ -168,6 +168,8 @@ class FusionRing(WeylCharacterRing): [[i0, p, s], [p, i0, s], [s, s, i0 + p]] sage: [x.twist() for x in b] [0, 1, 1/8] + sage: I.global_q_dimension() + 4 sage: [x.q_dimension()^2 for x in b] [1, 1, 2] sage: I.s_matrix() @@ -423,7 +425,7 @@ def s_matrix(self): This is the matrix denoted `\widetilde{s}` in [BaKi2001]_. It is not normalized to be unitary. To obtain a unitary matrix, divide by `\sqrt{D}` where `D` is computed - by :meth:`total_quantum_order`. + by :meth:`global_q_dimension`. EXAMPLES:: @@ -513,14 +515,14 @@ def fusion_labels(self, labels=None, inject_variables=False): inject_variable(labels[j], B[b]) self._fusion_labels = d - def total_quantum_order(self): + def global_q_dimension(self): """ Return `\sum d_i^2`, where the sum is over all simple objects and `d_i` is the quantum dimension. EXAMPLES:: - sage: FusionRing("E6",1).total_quantum_order() + sage: FusionRing("E6",1).global_q_dimension() 3 """ return sum(x.q_dimension()**2 for x in self.basis()) From c1338b932e8e42a19ddba27b0915fb7487ede207 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sat, 27 Jun 2020 21:54:55 -0700 Subject: [PATCH 144/204] doc cleanup --- src/sage/combinat/root_system/fusion_ring.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 263a4cb2ea7..a26b40aac70 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -20,7 +20,8 @@ from sage.misc.cachefunc import cached_method class FusionRing(WeylCharacterRing): - r"""Return the Fusion Ring (Verlinde Algebra) of level ``k``. + r""" + Return the Fusion Ring (Verlinde Algebra) of level ``k``. INPUT: @@ -120,9 +121,9 @@ class FusionRing(WeylCharacterRing): Let us check the Verlinde formula. This famous identity states that - .. MATH:: + .. MATH:: - N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)}, + N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)}, where ``I`` is the unit object. In this formula `s` is the normalized unitary S-matrix denoted `s` in [BaKi2001]_. We may define a function that @@ -226,7 +227,9 @@ def field(self): return CyclotomicField(4 * self._fg * self._l) def get_order(self): - r"""Return the weights of the basis vectors in a fixed order. + r""" + Return the weights of the basis vectors in a fixed order. + You may change the order of the basis using :meth:`CombinatorialFreeModule.set_order` EXAMPLES:: @@ -364,7 +367,7 @@ def N_ijk(self, elt_i, elt_j, elt_k): @cached_method def Nk_ij(self, elt_i, elt_j, elt_k): - """ + r""" Return the fusion coefficient `N^k_{ij}`. These are the structure coefficients of the fusion ring, so @@ -386,7 +389,9 @@ def Nk_ij(self, elt_i, elt_j, elt_k): def s_ij(self, elt_i, elt_j): r""" Return the element of the S-matrix of this FusionRing corresponding to - the given elements. This is computed using the formula + the given elements. + + This is computed using the formula .. MATH:: @@ -516,7 +521,7 @@ def fusion_labels(self, labels=None, inject_variables=False): self._fusion_labels = d def global_q_dimension(self): - """ + r""" Return `\sum d_i^2`, where the sum is over all simple objects and `d_i` is the quantum dimension. From cde265448f3281c6703485e4a9284450bf5fce82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 28 Jun 2020 09:55:23 +0200 Subject: [PATCH 145/204] implement multiplicative_order for elements of AA --- src/sage/rings/qqbar.py | 129 ++++++++++++++++++++++++++-------------- 1 file changed, 85 insertions(+), 44 deletions(-) diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index b1ba7de7e70..8b92f4ddb56 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -581,13 +581,14 @@ def default_interval_prec(self): sage: AA.default_interval_prec() 64 """ - return 64 def characteristic(self): r""" - Return the characteristic of this field. Since this class is only used - for fields of characteristic 0, always returns 0. + Return the characteristic of this field. + + Since this class is only used + for fields of characteristic 0, this always returns 0. EXAMPLES:: @@ -598,7 +599,9 @@ def characteristic(self): def order(self): r""" - Return the cardinality of self. Since this class is only used for + Return the cardinality of ``self``. + + Since this class is only used for fields of characteristic 0, always returns Infinity. EXAMPLES:: @@ -612,7 +615,7 @@ def common_polynomial(self, poly): """ Given a polynomial with algebraic coefficients, returns a wrapper that caches high-precision calculations and - factorizations. This wrapper can be passed to polynomial_root + factorizations. This wrapper can be passed to ``polynomial_root`` in place of the polynomial. Using ``common_polynomial`` makes no semantic difference, but will @@ -667,7 +670,7 @@ def _get_action_(self, G, op, self_on_left): return AlgebraicNumberPowQQAction(G, self) def _factor_multivariate_polynomial(self, f, proof=True): - """ + r""" Factor the multivariate polynomial ``f``. INPUT: @@ -682,9 +685,9 @@ def _factor_multivariate_polynomial(self, f, proof=True): ALGORITHM: - For rings over QQ, uses Singular's ``absfact`` library. + For rings over `\QQ`, uses Singular's ``absfact`` library. - For rings over number fields, we reduce to the QQ case by factoring + For rings over number fields, we reduce to the `\QQ` case by factoring the norm of the polynomial. .. NOTE:: @@ -1030,7 +1033,7 @@ def __init__(self): EXAMPLES: - This function calls functions in superclasses which set the category, so we check that. + This function calls functions in superclasses which set the category, so we check that. :: sage: QQbar.category() # indirect doctest Category of infinite fields @@ -1044,18 +1047,18 @@ def _element_constructor_(self, x): EXAMPLES:: - sage: QQbar(sqrt(2)) in AA # indirect doctest + sage: QQbar(sqrt(2)) in AA # indirect doctest True sage: QQbar(I) in AA False sage: AA in AA False - The following should both return True (this is a bug). :: + The following should both return ``True`` (this is a bug). :: - sage: sqrt(2) in AA # not tested + sage: sqrt(2) in AA # known bug False - sage: K. = CyclotomicField(5); z + 1/z in AA # not tested + sage: K. = CyclotomicField(5); z + 1/z in AA # known bug False """ if isinstance(x, AlgebraicReal): @@ -1071,7 +1074,7 @@ def _element_constructor_(self, x): def _repr_(self): r""" - String representation of self. + String representation of ``self``. EXAMPLES:: @@ -1098,7 +1101,7 @@ def _repr_option(self, key): # Is there a standard representation for this? def _latex_(self): r""" - Latex representation of self. + Latex representation of ``self``. EXAMPLES:: @@ -1144,12 +1147,13 @@ def _coerce_map_from_(self, from_par): def completion(self, p, prec, extras={}): r""" - Return the completion of self at the place `p`. Only implemented for `p - = \infty` at present. + Return the completion of ``self`` at the place `p`. + + Only implemented for `p = \infty` at present. INPUT: - - ``p`` -- either a prime (not implemented at present) or Infinity + - ``p`` -- either a prime (not implemented at present) or ``Infinity`` - ``prec`` -- precision of approximate field to return - ``extras`` -- (optional) a dict of extra keyword arguments for the ``RealField`` constructor @@ -1187,8 +1191,10 @@ def algebraic_closure(self): def _is_valid_homomorphism_(self, codomain, im_gens, base_map=False): r""" - Attempt to construct a homomorphism from self to codomain sending the - generators to ``im_gens``. Since this field is not finitely generated, + Attempt to construct a homomorphism from ``self`` to ``codomain`` sending the + generators to ``im_gens``. + + Since this field is not finitely generated, this cannot be implemented in a mathematically sensible way, and we just test that there exists a canonical coercion. @@ -1206,7 +1212,9 @@ def _is_valid_homomorphism_(self, codomain, im_gens, base_map=False): def gens(self): r""" - Return a set of generators for this field. As this field is not + Return a set of generators for this field. + + As this field is not finitely generated, we opt for just returning 1. EXAMPLES:: @@ -1501,7 +1509,7 @@ def _repr_(self): def _latex_(self): r""" - Latex representation of self. + Latex representation of ``self``. EXAMPLES:: @@ -1545,12 +1553,13 @@ def _coerce_map_from_(self, from_par): def completion(self, p, prec, extras={}): r""" - Return the completion of self at the place `p`. Only implemented for `p - = \infty` at present. + Return the completion of ``self`` at the place `p`. + + Only implemented for `p = \infty` at present. INPUT: - - ``p`` -- either a prime (not implemented at present) or Infinity + - ``p`` -- either a prime (not implemented at present) or ``Infinity`` - ``prec`` -- precision of approximate field to return - ``extras`` -- (optional) a dict of extra keyword arguments for the ``RealField`` constructor @@ -1576,8 +1585,9 @@ def completion(self, p, prec, extras={}): def algebraic_closure(self): """ - Return the algebraic closure of this field. As this field is already - algebraically closed, just returns self. + Return the algebraic closure of this field. + + As this field is already algebraically closed, just returns ``self``. EXAMPLES:: @@ -1588,7 +1598,7 @@ def algebraic_closure(self): def construction(self): """ - Return a functor that constructs self (used by the coercion machinery). + Return a functor that constructs ``self`` (used by the coercion machinery). EXAMPLES:: @@ -1601,7 +1611,9 @@ def construction(self): def gens(self): r""" - Return a set of generators for this field. As this field is not + Return a set of generators for this field. + + As this field is not finitely generated over its prime field, we opt for just returning I. EXAMPLES:: @@ -1861,7 +1873,6 @@ def _is_irreducible_univariate_polynomial(self, f): ValueError: polynomial must not be constant sage: R(1).is_irreducible() False - """ if f.degree() < 1: # this case is handled by the caller (PolynomialElement.is_irreducible()) @@ -2000,8 +2011,9 @@ def tail_prec_seq(): def rational_exact_root(r, d): r""" - Checks whether the rational `r` is an exact `d`'th power. If so, returns - the `d`'th root of `r`; otherwise, returns None. + Check whether the rational `r` is an exact `d`'th power. + + If so, this returns the `d`'th root of `r`; otherwise, this returns ``None``. EXAMPLES:: @@ -2836,7 +2848,7 @@ def __richcmp__(self, other, op): def is_complex(self): r""" - Return True if this is a generator for a non-real number field. + Return ``True`` if this is a generator for a non-real number field. EXAMPLES:: @@ -3873,8 +3885,10 @@ def is_square(self): OUTPUT: - (boolean) True in all cases for elements of QQbar; True for - non-negative elements of AA, otherwise False. + (boolean) + ``True`` in all cases for elements of ``QQbar``; + ``True`` for non-negative elements of ``AA``; + otherwise ``False`` EXAMPLES:: @@ -3894,7 +3908,7 @@ def is_square(self): def is_integer(self): """ - Return True if this number is a integer + Return ``True`` if this number is a integer. EXAMPLES:: @@ -3962,8 +3976,6 @@ def sqrt(self, all=False, extend=True): Traceback (most recent call last): ... ValueError: -2 is not a square in AA, being negative. Use extend = True for a square root in QQbar. - - """ # deal with 0 first: @@ -4982,8 +4994,9 @@ def complex_exact(self, field): def multiplicative_order(self): r""" - Compute the multiplicative order of this algebraic real - number. That is, find the smallest positive integer `n` such + Compute the multiplicative order of this algebraic number. + + That is, find the smallest positive integer `n` such that `x^n = 1`. If there is no such `n`, returns ``+Infinity``. We first check that ``abs(x)`` is very close to 1. If so, we compute @@ -5437,6 +5450,34 @@ def conjugate(self): """ return self + def multiplicative_order(self): + r""" + Compute the multiplicative order of this real algebraic number. + + That is, find the smallest positive integer `n` such + that `x^n = 1`. If there is no such `n`, returns ``+Infinity``. + + We first check that ``abs(x)`` is very close to 1. If so, we compute + `x` exactly and compare it to 1 and -1. + + EXAMPLES:: + + sage: AA(1).multiplicative_order() + 1 + sage: AA(-1).multiplicative_order() + 2 + sage: AA(5).sqrt().multiplicative_order() + +Infinity + """ + if not (1 in RIF(self).abs()): + return infinity.infinity + if self == 1: + return 1 + elif self == -1: + return 2 + else: + return infinity.infinity + def sign(self): """ Compute the sign of this algebraic number (return -1 if negative, @@ -5528,7 +5569,7 @@ def sign(self): # Sigh... self.exactify() return self.sign() - + def _interval_fast(self, prec): r""" Compute an approximation to this ``AlgebraicReal`` object in a real interval field of precision prec. @@ -6422,7 +6463,7 @@ def poly(self): def is_complex(self): r""" - Return True if the coefficients of this polynomial are non-real. + Return ``True`` if the coefficients of this polynomial are non-real. EXAMPLES:: @@ -6437,7 +6478,7 @@ def is_complex(self): def complex_roots(self, prec, multiplicity): """ - Find the roots of self in the complex field to precision prec. + Find the roots of ``self`` in the complex field to precision ``prec``. EXAMPLES:: @@ -6445,7 +6486,7 @@ def complex_roots(self, prec, multiplicity): sage: cp = AA.common_polynomial(x^4 - 2) Note that the precision is not guaranteed to find the tightest - possible interval since complex_roots() depends on the + possible interval since ``complex_roots()`` depends on the underlying BLAS implementation. :: sage: cp.complex_roots(30, 1) From 8b6562b901a3e56a41e5e6555e4bdafc8a185ec3 Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Sun, 28 Jun 2020 06:55:23 -0300 Subject: [PATCH 146/204] Implemented vector regular partition tuples --- src/sage/combinat/partition_tuple.py | 288 ++++++++++++++++++++++++++- 1 file changed, 285 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/partition_tuple.py b/src/sage/combinat/partition_tuple.py index 45b75b582e6..aa874194cf1 100644 --- a/src/sage/combinat/partition_tuple.py +++ b/src/sage/combinat/partition_tuple.py @@ -1771,7 +1771,7 @@ def defect(self, e, multicharge): class PartitionTuples(UniqueRepresentation, Parent): - """ + r""" Class of all partition tuples. For more information about partition tuples, see :class:`PartitionTuple`. @@ -1785,8 +1785,13 @@ class PartitionTuples(UniqueRepresentation, Parent): - ``size`` -- the total number of cells - - ``regular`` -- the highest multiplicity an entry may have in a - component plus `1` + - ``regular`` -- a positive integer or a tuple of non-negative + integers; If an integer, the highest multiplicity an entry may + have in a component plus `1`. If a level `k` is specified and + ``regular`` is a tuple of integers `l_1,..,l_k` then this + specifies partition tuples `\mu` such that + `mu_i` is `l_i`-regular. The special case `l_i = 0` is treated + as `\infty`-regular, that is, partitions without restrictions. TESTS:: @@ -1841,19 +1846,27 @@ def __classcall_private__(klass, level=None, size=None, regular=None): if size is None: if regular is None: return _Partitions + if isinstance(regular, tuple): + return VectorRegularPartitionTuples_level(1,regular) return RegularPartitions_all(regular) if regular is None: return Partitions_n(size) + if isinstance(regular, tuple): + return VectorRegularPartitionTuples_level_size(1,size,regular) return RegularPartitions_n(size, regular) # Higher level if size is None: if regular is None: return PartitionTuples_level(level) + if isinstance(regular, tuple): + return VectorRegularPartitionTuples_level(level,regular) return RegularPartitionTuples_level(level, regular) if regular is None: return PartitionTuples_level_size(level, size) + if isinstance(regular, tuple): + return VectorRegularPartitionTuples_level_size(level,size,regular) return RegularPartitionTuples_level_size(level, size, regular) Element = PartitionTuple @@ -2886,3 +2899,272 @@ def _an_element_(self): mu[0] = [1] mu[-1] = [self._size-1] return self.element_class(self, mu) + + +class VectorRegularPartitionTuples_level(PartitionTuples_level): + """ + Class of vector-regular partition tuples with a fixed level. + """ + def __init__(self, level, regular): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: RPT = PartitionTuples(level=4, regular=(2,3,0,2)) + sage: RPT[:24] + [([], [], [], []), + ([1], [], [], []), + ([], [1], [], []), + ([], [], [1], []), + ([], [], [], [1]), + ([2], [], [], []), + ([1], [1], [], []), + ([1], [], [1], []), + ([1], [], [], [1]), + ([], [2], [], []), + ([], [1, 1], [], []), + ([], [1], [1], []), + ([], [1], [], [1]), + ([], [], [2], []), + ([], [], [1, 1], []), + ([], [], [1], [1]), + ([], [], [], [2]), + ([3], [], [], []), + ([2, 1], [], [], []), + ([2], [1], [], []), + ([2], [], [1], []), + ([2], [], [], [1]), + ([1], [2], [], []), + ([1], [1, 1], [], [])] + sage: [[1,1],[3],[5,5,5],[7,2]] in RPT + False + sage: [[3,1],[3],[5,5,5],[7,2]] in RPT + True + sage: [[3,1],[3],[5,5,5]] in RPT + False + """ + if level not in ZZ or level <= 0: + raise ValueError('level must be a positive integer') + if len(regular) != level: + raise ValueError("regular must be a tuple with length {}".\ + format(level)) + if any (i not in NN for i in regular): + raise ValueError('regular must be a tuple of non-negative integers') + PartitionTuples_level.__init__(self, level) + self._ell = regular + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: RPT = PartitionTuples(level=4, regular=(2,3,0,2)) + sage: RPT + (2, 3, 0, 2)-Regular partition tuples of level 4 + """ + return '{}-Regular partition tuples of level {}'.format(self._ell, + self._level) + + def __contains__(self, mu): + """ + Return ``True`` if ``mu`` is in ``self``. + + TESTS:: + + sage: RPT = PartitionTuples(level=3, regular=(2,1,4)) + sage: [[4],[2],[5]] in RPT + False + sage: [[4],[],[5]] in RPT + True + sage: [[4,3],[],[5]] in RPT + True + sage: [[4,4],[],[5]] in RPT + False + sage: [[4,3],[5]] in RPT + False + sage: [5,4,3] in RPT + False + sage: RPT = PartitionTuples(level=1,regular=(3,)); RPT + (3,)-Regular partition tuples of level 1 + sage: [[2,2]] in RPT + True + sage: [[2,2,2]] in RPT + False + """ + if self._level != len(mu): + return False + if mu not in PartitionTuples_level(self._level): + return False + if isinstance(mu, Partition): + if self._ell[0] == 0: + return True + return max(mu.to_exp() + [0]) < self._ell[0] + if isinstance(mu, PartitionTuple): + return all(max(mu[i].to_exp() + [0]) < self._ell[i] for + i in range(len(mu)) if self._ell[i] > 0) + if len(mu) == 0: + return True + return all(mu[i] in RegularPartitions_all(self._ell[i]) for i in + range(len(mu)) if self._ell[i] > 0) + + def __iter__(self): + """ + Iterate through the class of vector `\ell`-regular partition + tuples of a fixed level. + + EXAMPLES:: + + sage: PartitionTuples(level=3, regular=(2,1,4))[:24] + [([], [], []), + ([1], [], []), + ([], [], [1]), + ([2], [], []), + ([1], [], [1]), + ([], [], [2]), + ([], [], [1, 1]), + ([3], [], []), + ([2, 1], [], []), + ([2], [], [1]), + ([1], [], [2]), + ([1], [], [1, 1]), + ([], [], [3]), + ([], [], [2, 1]), + ([], [], [1, 1, 1]), + ([4], [], []), + ([3, 1], [], []), + ([3], [], [1]), + ([2, 1], [], [1]), + ([2], [], [2]), + ([2], [], [1, 1]), + ([1], [], [3]), + ([1], [], [2, 1]), + ([1], [], [1, 1, 1])] + """ + for size in NN: + for mu in VectorRegularPartitionTuples_level_size(self._level, + size, self._ell): + yield self.element_class(self, list(mu)) + +class VectorRegularPartitionTuples_level_size(PartitionTuples_level_size): + """ + Class of vector `\ell`-regular partition tuples with a fixed level + and a fixed size. + """ + def __init__(self, level, size, regular): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: PartitionTuples(level=3, size=7, regular=(2,1,3))[0:24] + [([7], [], []), + ([6, 1], [], []), + ([5, 2], [], []), + ([4, 3], [], []), + ([4, 2, 1], [], []), + ([6], [], [1]), + ([5, 1], [], [1]), + ([4, 2], [], [1]), + ([3, 2, 1], [], [1]), + ([5], [], [2]), + ([5], [], [1, 1]), + ([4, 1], [], [2]), + ([4, 1], [], [1, 1]), + ([3, 2], [], [2]), + ([3, 2], [], [1, 1]), + ([4], [], [3]), + ([4], [], [2, 1]), + ([3, 1], [], [3]), + ([3, 1], [], [2, 1]), + ([3], [], [4]), + ([3], [], [3, 1]), + ([3], [], [2, 2]), + ([3], [], [2, 1, 1]), + ([2, 1], [], [4])] + """ + if size not in NN: + raise ValueError('size must be a non-negative integer') + if not (level in ZZ and level > 0): + raise ValueError('level must be a positive integer') + PartitionTuples_level_size.__init__(self, level, size) + if len(regular) != level: + raise ValueError('regular must be a list with length'\ + ' {}'.format(level)) + if any (i not in NN for i in regular): + raise ValueError('regular must be a list of non-negative integers') + self._ell = regular + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: RPT = PartitionTuples(level=3, size=7, regular=(2,1,4)) + sage: RPT + (2, 1, 4)-Regular partition tuples of level 3 and size 7 + """ + return '{}-Regular partition tuples of level {} and size {}'.\ + format(self._ell, self._level, self._size) + + def __contains__(self, mu): + """ + Return ``True`` if `\mu` is in ``self``. + + TESTS:: + + sage: RPT = PartitionTuples(level=3, size=7, regular=(2,1,4)) + sage: RPT + (2, 1, 4)-Regular partition tuples of level 3 and size 7 + sage: [[3,1],[],[3]] in RPT + True + sage: [[3],[1],[3]] in RPT + False + sage: [[3,2],[],[3]] in RPT + False + sage: [[3,3],[],[1]] in RPT + False + """ + if mu not in PartitionTuples_level_size(self._level, self._size): + return False + if mu not in VectorRegularPartitionTuples_level(self._level, self._ell): + return False + return True + + def __iter__(self): + """ + Iterate through the finite class of vector `\ell`-regular + partition tuples of a fixed level and a fixed size. + + EXAMPLES:: + + sage: list(PartitionTuples(level=3,size=3,regular=(2,1,2))) + [([3], [], []), + ([2, 1], [], []), + ([2], [], [1]), + ([1], [], [2]), + ([], [], [3]), + ([], [], [2, 1])] + """ + for iv in IntegerVectors(self._size, self._level): + p = [RegularPartitions_n(iv[j], self._ell[j]) if self._ell[j] else + Partitions_n(iv[j]) for j in range(self._level)] + for cp in itertools.product(*[p[i] for i in range(self._level)]): + yield self._element_constructor_(cp) + + def _an_element_(self): + """ + Return a generic element. + + EXAMPLES:: + + sage: PartitionTuples(level=4, size=4, regular=3).an_element() + ([1], [], [], [3]) + """ + if self._size == 0: + return self.element_class(self,([],)*self._level) + return PartitionTuples(level=self._level, size=self._size, + regular=self._ell)[1] + From e959d56f9b61b9370257aab62c3a3ad3c577e7a5 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Sun, 28 Jun 2020 13:21:38 +0100 Subject: [PATCH 147/204] workaround to allow non-EN locales for docbuild Since Sphinx 3.0, messages/warnings it emits are localised and out warnings scraping out fails. --- src/bin/sage | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bin/sage b/src/bin/sage index d7c009fb402..ea4d6634555 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -1040,6 +1040,7 @@ if [ "$1" = "-docbuild" -o "$1" = "--docbuild" ]; then # tends to ask interactive questions if something goes wrong. These # cause the build to hang. If stdin is /dev/null, TeX just aborts. shift + export LANG=C # to ensure it is possible to scrape out non-EN locale warnings exec sage-python -m sage_setup.docbuild "$@" Date: Sun, 28 Jun 2020 13:45:12 +0100 Subject: [PATCH 148/204] get rid of old data added by commit 0f0517c0c90 it can't be just reverted for some obscure git reason --- src/sage/ext_data/images/corner.png | Bin 94 -> 0 bytes src/sage/ext_data/images/evaluate.png | Bin 222 -> 0 bytes src/sage/ext_data/images/evaluate_over.png | Bin 224 -> 0 bytes src/sage/ext_data/images/favicon.ico | Bin 1406 -> 0 bytes src/sage/ext_data/images/sagelogo.png | Bin 3245 -> 0 bytes 5 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/sage/ext_data/images/corner.png delete mode 100644 src/sage/ext_data/images/evaluate.png delete mode 100644 src/sage/ext_data/images/evaluate_over.png delete mode 100644 src/sage/ext_data/images/favicon.ico delete mode 100644 src/sage/ext_data/images/sagelogo.png diff --git a/src/sage/ext_data/images/corner.png b/src/sage/ext_data/images/corner.png deleted file mode 100644 index f4b96d28f6ff6ae42a7df426fd33fefefa3746c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 94 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4sd~CNhDcma{?RXMVz7XDkw{`- rN37s1r#oE}L>`5+gnC3=6kufV<_OsQwCC{`paup{S3j3^P6f4u_bxCyDx`7I;J! zGca%qgD@k*tT_@uLG}_)Usv{PJY1r3viu=gSwJD#64!_l=ltB<)VvY~=c3falGGH1 z^30M9g^-L?1$R&1fcGh?c|e6uo-U3d5|@|uJ9057@*Gk7_y7Dyl{KuZHxx@{aL7(t z!L(wMvzy#!ewG@uu!<{>#c%wIx*Hklkz}6a84!KB)TcI`sf|mSxADmbX`pEgp00i_ I>zopr0Kn@)X#fBK diff --git a/src/sage/ext_data/images/evaluate_over.png b/src/sage/ext_data/images/evaluate_over.png deleted file mode 100644 index 46fc67204d7c000fb8d363311a6ea31a308c5e27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4u_bxCyDx`7I;J! zGca%qgD@k*tT_@uLG}_)Usv{PJY1sk2Eh~9^g4 L)z4*}Q$iB}8?r`& diff --git a/src/sage/ext_data/images/favicon.ico b/src/sage/ext_data/images/favicon.ico deleted file mode 100644 index 3cefb468d16b982d27ac140f3fd22f9a3fd18bed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1406 zcmeH^yK9Y@&ii|xhjVxjppX^`Xa#ljS$~Q~EI~vtn z-c_mjd|3;8tR}DSYBdM4;c)LWA5gh-sCaq!^-O&V$?FeS-Y5yk Yr{AQ;$%*XI9)Hm+Pk(`o|MkQF00r))g#Z8m diff --git a/src/sage/ext_data/images/sagelogo.png b/src/sage/ext_data/images/sagelogo.png deleted file mode 100644 index 5b9fad9a6578ac88d3fdffebb70f7569d2b59626..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3245 zcmV;e3{vxnP)Eqo}RI>v2Zw?N~HurKoA5)Q4~cn z3`0>AK@jkyBuM}d#0H+halE3UV&A@fEiEk+MIi{{_xq0@KRz)rkxV8F z!g0L3yj-i*YPH%OJ9cc_whhBDNLn6#{q@(gXV2cec{7zt0VXjF1B?Np08kXgaa^O( z=yW=QAOI(7wfe;uUv#-#klgh2^f%vpbN%}DU@(|WCUaDm!xH?O^8rB+>>f}QRbF0R zRaMp5*=aNyF$^m#hEyC59Xdo1M2YGs2nd2GmCAz$5B~MnU-5W6lgXSvf1Y8ORpErH z#4t>!(|!8sr-48KD!mwpqWI;PUm6UCY-cKkN;WyW-G1%bwQx8rilQV*Z@>NaIu%lo zD93SU&YYQ>n=2+1UhI8Ik|ri50CT|CNRnh(mZoWvBms69J(+s|Jq7wHNs%2;fB&MG1nSX_}^KilU%4bF`4jWO$wrhr_X0EEEbIJ9aFQNbKFax4gWZ zAc$hjcXxMx^wCGrXcWhBnx>UXCGb-i^f}K9g23~9DwRs7)6mQd3k#PnU1Aufy1JSq zNw3$N$z&u+LQ#~WD3)a@iXur;KC-j_l*wcSK@bEX9*>8^VW5h1Iz2Qr^!VeCvn-3_ zcs{D4M#^L|$z&4dE`!1F)KgD2Ha1$VR-H}2z8YMTJ5^5CkBGAPA1*TCEmX z2tkm$ckcpQqA04>YTa(P!{M-6twy6!rBW4(_e>@e4u{X3J9p{QrD!yI>eMN{UcYPC zuClT+9LMuD-qX`_bi_vHVJQO3r^ZeZ0-1pyqfBpJ(Ac!c6 zKA&%Sd07wykOqN)DT-=sZEb35s;Q~b>-EqBN&(;yiA3VWi4!9uBLLy@@^Um96$GK+ zD|jh*aU6f_vB%om+uPgQtE#GKn$9jBIhhkiF?1qW!xRcdJRS$nU>Ihz+1lFLIyyS+ zc00?m@^Xrz*w)r|;J|?k7cPWCp%W)gl$Dh|_uO-Oy*}r~zP`S$uC7QV!Z6G;&phLH zyPKPvYin!OYIVLvbNElE(2x|Z8cpdIf`l@e z%*BfrM@L66hG9T8+NiRLVVF{> zEWH$?DEi1FkDNSta?_?wk|a${O&vde{N~M@i9`ap;q>(MzJ2?AJ|B+bb#-;Et*tJX ztFEqYRnBfO7>Yd%(-G{@)~pQ#L0Fb8(cMcm5)AfrLcu0`Rj)t!*$%H#3yJioZOxOeZ~$;n9sK`a(adwaXn>1=Fl%+H7NkY8@|jkE^N3;Jk@s*uw= z%d&u7(CTbR^sL{~IF9eywQK+W{S-y}}ili6&R2@9D>D#j!<5}Y^IQ=6x! zry&w~Ysq;ToI(UaxN_x6I2^96tz{Sn$dHY?%cT}NfJR2Hk+-vxfwLvrI+RWFqN4dVe;pB zUJ!(zfByOW`SWY51#n!+pWjzuq}{uB-?(vOe0)3{4yV)UQkr#96w~Q+GMP*y5@4Jb zMR9a=bZTmf<2ZsKRy$J2XHJ{VW-^&P9uJ(o>~_1|ZePRfvSLEcu0xU}B@&5LDz$a% z)?_jn2n2$`ASeg~RJu>uc`&}BTj4mq+Adb1P;eZ#Vggx@!G?#2Km72+SS&`-FL|?r=D^Zr!?b=g#i#?j1XJpePy$1dbj(`rB{6EiW&xA@Ai+@f+@8{}dx-HyU7n zT(ESlAwR_9@q-5s{_)2j2!fc+=55=yH8wWZ*Vot7)SNzj+U0T~2r@G>bNKM#k&%%| zB(nb5uApHy-1AB@Qg)laHpp&OCnhGc7w<6PX7_=&Zrut50zA)`s7FbX=H})O960dP zPd|yG$Z_2E?c3|?>l+#x3o9C)b^Y?tKgUhA^S&yAfnOe`1m+f zJsADqNeo2m^?JLyy1xDPTa88o3U!XMvY8PCVPRomc6N4od07-ik|eim+0xL^;BYuL zZQ2CSA-8PV($mxP`s=TIz24t{|NWI$USU~QrBZ>zD%cd{GmjuhU0q#wcejj@VD#`j z|HBVIT)A>ZtJT8XSn#|YR4j_3Fbo6IfdLCD2a*E~SGI;x6m4l~`Q(#N@VU9U%a<>2 z$i}c(egHfH5=NtuAc#~d1^jVwak0O@f3-Mq9Jg33ZnxX%bXu)enx>&%FbsS8>8DSh zKHb^bIX^!Sy8jZ<`u+Z)p&`57UQin-iaH#Q;o)I$bel*dZr{FrzZ2ck($Whrynxr& z*YDrIe`aQ8X=$n8Axgo+=7L`fPH-ei+U<65;D#VbdwctP@4YuVItnxh@1nrC$%*5f zz{N0(APAOaT`pHkOG`sTL$)u&4eayJKi}Ki``vfnO-xLHF>}RKhU2)&Wb*s{X0y3i z7x(eUA6HdX4Gs?a{eE~6rIeLH!3V&2C*NlpjmBHIZXu#5Mx#-m&j-rGhU|B9Iwg%p zQ(0N5*Xs#_kR&OUN-Zrd&CkzAA`!6iDm^H{wH}J1)M~ZSXw+yl#fBkK6eE#HAP`tu zT1q4m@^cv0rCIgVo(#%i_RCz9wrfXnrYiVBm-w6?wrUc!1RP07|v3Eg1%49NNodMfxFhPQ; fXtn Date: Sun, 28 Jun 2020 15:56:18 +0200 Subject: [PATCH 149/204] more places where on can use coprime_range --- .../endPN_automorphism_group.py | 29 +++++++++---------- src/sage/modular/modsym/ambient.py | 12 ++------ src/sage/rings/universal_cyclotomic_field.py | 6 ++-- 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py index 5f855cd915f..275a7b37cd1 100644 --- a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +++ b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py @@ -523,21 +523,20 @@ def valid_automorphisms(automorphisms_CRT, rational_function, ht_bound, M, # multiply lift by appropriate scalar matrices and adjust (mod M) # to find an element of minimal height. These will have # coefficients in [-M/2, M/2) - for scalar in range(1, M): - if gcd(scalar, M) == 1: - new_lift = [scalar*x - (scalar*x/M).round()*M - for x in init_lift] - g = gcd(new_lift) - new_lift = [x // g for x in new_lift] - if all(abs(x) <= ht_bound for x in new_lift): - a, b, c, d = new_lift - f = (a*z + b) / (c*z + d) - if rational_function(f(z)) == f(rational_function(z)): - if return_functions: - valid_auto.append(f) - else: - valid_auto.append(matrix(ZZ,2,2,new_lift)) - break + for scalar in M.coprime_integers(M): + new_lift = [scalar*x - (scalar*x/M).round()*M + for x in init_lift] + g = gcd(new_lift) + new_lift = [x // g for x in new_lift] + if all(abs(x) <= ht_bound for x in new_lift): + a, b, c, d = new_lift + f = (a*z + b) / (c*z + d) + if rational_function(f(z)) == f(rational_function(z)): + if return_functions: + valid_auto.append(f) + else: + valid_auto.append(matrix(ZZ,2,2,new_lift)) + break return valid_auto diff --git a/src/sage/modular/modsym/ambient.py b/src/sage/modular/modsym/ambient.py index ef0db033ab6..adeea328a32 100644 --- a/src/sage/modular/modsym/ambient.py +++ b/src/sage/modular/modsym/ambient.py @@ -2181,21 +2181,15 @@ def twisted_winding_element(self, i, eps): sage: M = ModularSymbols(37,2,0,K) sage: M.twisted_winding_element(0,eps) 2*(1,23) - 2*(1,32) + 2*(1,34) - """ - if not dirichlet.is_DirichletCharacter(eps): raise TypeError("eps must be a Dirichlet character.") - if (i < 0) or (i > self.weight()-2): + if (i < 0) or (i > self.weight() - 2): raise ValueError("i must be between 0 and k-2.") m = eps.modulus() - s = self(0) - - for a in ([ x for x in range(1,m) if gcd(x, m) == 1 ]): - s += eps(a) * self.modular_symbol([i, Cusp(0), Cusp(a/m)]) - - return s + return self.sum(eps(a) * self.modular_symbol([i, Cusp(0), Cusp(a / m)]) + for a in m.coprime_integers(m)) ###################################################################### # Z-module of integral modular symbols. diff --git a/src/sage/rings/universal_cyclotomic_field.py b/src/sage/rings/universal_cyclotomic_field.py index 2a69af743fe..fd078cc278d 100644 --- a/src/sage/rings/universal_cyclotomic_field.py +++ b/src/sage/rings/universal_cyclotomic_field.py @@ -1681,13 +1681,11 @@ def _factor_univariate_polynomial(self, f): m = p.is_cyclotomic(certificate=True) if not m: raise NotImplementedError('no known factorization for this polynomial') - for i in range(1, m): - if gcd(m, i) == 1: - factors.append((x - UCF.zeta(m, i), e)) + for i in m.coprime_integers(m): + factors.append((x - UCF.zeta(m, i), e)) return Factorization(factors, unit) - def degree(self): r""" Return the *degree* of ``self`` as a field extension over the Rationals. From 55b2e6be8c143481dd885f694d9beafe27aa4d68 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sun, 28 Jun 2020 07:03:14 -0700 Subject: [PATCH 150/204] added _test_verlinde method to check Verlinde formula --- src/sage/combinat/root_system/fusion_ring.py | 37 +++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index a26b40aac70..b4608505563 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -149,7 +149,7 @@ class FusionRing(WeylCharacterRing): sage: test_verlinde(FusionRing("A2",1)) True - sage: test_verlinde(FusionRing("B4",2)) + sage: test_verlinde(FusionRing("B4",2)) # long time (.56s) True As an exercise, the reader may verify the examples in @@ -207,10 +207,45 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug sage: D41 = FusionRing('D4', 1) sage: TestSuite(D41).run() + + sage: G22 = FusionRing('G2', 2) + sage: TestSuite(G22).run() + + sage: F41 = FusionRing('F4', 1) + sage: TestSuite(F41).run() + + sage: E61 = FusionRing('E6', 1) + sage: TestSuite(E61).run() + + sage: E71 = FusionRing('E7', 1) + sage: TestSuite(E71).run() + + sage: E81 = FusionRing('E8', 1) + sage: TestSuite(E81).run() + """ return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate) + def _test_verlinde(self, **options): + """ + Check the Verlinde formula for this :class:`FusionRing` instance. + + EXAMPLES:: + + sage: G22=FusionRing("G2",2) + sage: G22._test_verlinde() + + """ + tester = self._tester(**options) + c = self.global_q_dimension() + i0 = self.one() + for x in self.basis(): + for y in self.basis(): + for z in self.basis(): + v = sum(self.s_ij(x,w)*self.s_ij(y,w)*self.s_ij(z,w)/self.s_ij(i0,w) for w in self.basis()) + tester.assertEqual(v,c*self.N_ijk(x,y,z)) + def field(self): r""" Return a cyclotomic field large enough to From 9c89799b96640180f3fffce0e94d0aa9d81f75f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 27 Jun 2020 22:25:37 +0200 Subject: [PATCH 151/204] some details in Kloosterman sums for Dirichlet characters --- src/sage/modular/dirichlet.py | 56 ++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py index 1204e393221..648c2aeadf9 100644 --- a/src/sage/modular/dirichlet.py +++ b/src/sage/modular/dirichlet.py @@ -1238,34 +1238,50 @@ def kloosterman_sum(self, a=1, b=0): sage: e.kloosterman_sum(3,5) -2*zeta6 + 1 sage: G = DirichletGroup(20) - sage: e = G([1 for u in G.unit_gens()]) + sage: e = G([1 for u in G.unit_gens()]) sage: e.kloosterman_sum(7,17) -2*zeta20^6 + 2*zeta20^4 + 4 + TESTS:: + + sage: G = DirichletGroup(20, UniversalCyclotomicField()) + sage: e = G([1 for u in G.unit_gens()]) + sage: e.kloosterman_sum(7,17) + -2*E(5) - 4*E(5)^2 - 4*E(5)^3 - 2*E(5)^4 + + sage: G = DirichletGroup(12, QQbar) + sage: e = G.gens()[0] + sage: e.kloosterman_sum(5,11) + Traceback (most recent call last): + ... + NotImplementedError: Kloosterman sums not implemented over this ring """ G = self.parent() K = G.base_ring() - if not (number_field.is_CyclotomicField(K) or is_RationalField(K)): - raise NotImplementedError("Kloosterman sums only currently implemented when the base ring is a cyclotomic field or QQ.") - g = 0 + zo = G.zeta_order() m = G.modulus() - L = rings.CyclotomicField(lcm(m,G.zeta_order())) + g = 0 + L = rings.CyclotomicField(m.lcm(zo)) zeta = L.gen(0) + try: + self(1) * zeta**(a+b) + except TypeError: + raise NotImplementedError('Kloosterman sums not implemented ' + 'over this ring') n = zeta.multiplicative_order() - zeta = zeta ** (n // m) - for c in range(1,m): - if gcd(c,m)==1: - e = rings.Mod(c,m) - z = zeta ** int(a*e + b*(e**(-1))) - g += self(c)*z + zeta = zeta**(n // m) + for c in m.coprime_integers(m): + e = rings.Mod(c, m) + g += self(c) * zeta**int(a*e + b*e**(-1)) return g - def kloosterman_sum_numerical(self, prec=53, a=1,b=0): + def kloosterman_sum_numerical(self, prec=53, a=1, b=0): r""" Return the Kloosterman sum associated to this Dirichlet character as - an approximate complex number with prec bits of precision. See also - :meth:`.kloosterman_sum`, which calculates the sum exactly (which is - generally slower). + an approximate complex number with prec bits of precision. + + See also :meth:`.kloosterman_sum`, which calculates the sum + exactly (which is generally slower). INPUT: @@ -1299,12 +1315,10 @@ def kloosterman_sum_numerical(self, prec=53, a=1,b=0): g = 0 m = G.modulus() zeta = CC.zeta(m) - - for c in range(1,m): - if gcd(c,m)==1: - e = rings.Mod(c,m) - z = zeta ** int(a*e + b*(e**(-1))) - g += phi(self(c))*z + for c in m.coprime_integers(m): + e = rings.Mod(c, m) + z = zeta ** int(a*e + b*(e**(-1))) + g += phi(self(c))*z return g @cached_method From 9591e109b5bee37dcf1f5497d59f3bcf0b25515f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 28 Jun 2020 20:38:29 +0200 Subject: [PATCH 152/204] some cleanup (pep + pyflakes) in Universal Cyclotomic Field --- src/sage/rings/universal_cyclotomic_field.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/universal_cyclotomic_field.py b/src/sage/rings/universal_cyclotomic_field.py index fd078cc278d..52948e23156 100644 --- a/src/sage/rings/universal_cyclotomic_field.py +++ b/src/sage/rings/universal_cyclotomic_field.py @@ -205,6 +205,7 @@ def late_import(): GapElement_Cyclotomic) from sage.interfaces import (gap, gap3) + def UCF_sqrt_int(N, UCF): r""" Return the square root of the integer ``N``. @@ -234,9 +235,9 @@ def UCF_sqrt_int(N, UCF): return UCF.zero() res = UCF.one() if N > 0 else UCF.zeta(4) - for p,e in N.factor(): + for p, e in N.factor(): if p == 2: - res *= (UCF.zeta(8) + UCF.zeta(8,7))**e + res *= (UCF.zeta(8) + UCF.zeta(8, 7))**e else: res *= UCF.sum(UCF.zeta(p, n**2) for n in range(p))**e if p % 4 == 3: @@ -244,6 +245,7 @@ def UCF_sqrt_int(N, UCF): return res + class UCFtoQQbar(Morphism): r""" Conversion to ``QQbar``. @@ -1114,10 +1116,10 @@ def sqrt(self, extend=True, all=False): k = self._obj.Conductor() coeffs = self._obj.CoeffsCyc(k).sage() if sum(bool(x) for x in coeffs) == 1: - for i,x in enumerate(coeffs): + for i, x in enumerate(coeffs): if x: break - return UCF(x).sqrt() * UCF.zeta(2*k, i) + return UCF(x).sqrt() * UCF.zeta(2 * k, i) # no method to construct square roots yet... if extend: @@ -1639,7 +1641,6 @@ def _factor_univariate_polynomial(self, f): sage: p(r[0][0]) 0 """ - from sage.arith.all import gcd from sage.structure.factorization import Factorization UCF = self @@ -1663,7 +1664,7 @@ def _factor_univariate_polynomial(self, f): f = f.change_ring(QQ) factors = [] - for p,e in f.factor(): + for p, e in f.factor(): if p.degree() == 1: factors.append((x + p[0], e)) @@ -1671,9 +1672,9 @@ def _factor_univariate_polynomial(self, f): c = p[0] b = p[1] a = p[2] - D = UCF(b**2 - 4*a*c).sqrt() - r1 = (-b - D) / (2*a) - r2 = (-b + D) / (2*a) + D = UCF(b**2 - 4 * a * c).sqrt() + r1 = (-b - D) / (2 * a) + r2 = (-b + D) / (2 * a) factors.append((x - r1, e)) factors.append((x - r2, e)) From da23ad1d5973bea8e7df69800ca2dd0a4c647ea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Sun, 28 Jun 2020 21:30:42 +0200 Subject: [PATCH 153/204] 29955:to_milp_solver -> to_milp --- src/sage/combinat/matrices/dancing_links.pyx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/matrices/dancing_links.pyx b/src/sage/combinat/matrices/dancing_links.pyx index b6247cce10c..c62780c5d74 100644 --- a/src/sage/combinat/matrices/dancing_links.pyx +++ b/src/sage/combinat/matrices/dancing_links.pyx @@ -1001,9 +1001,9 @@ cdef class dancing_linksWrapper: return [key for (key,val) in enumerate(solution, start=-1) if val] @cached_method - def to_milp_solver(self, solver=None): + def to_milp(self, solver=None): r""" - Return the mixed integer linear program (MILP) solver solving an + Return the mixed integer linear program (MILP) representing an equivalent problem. See also :mod:`sage.numerical.mip.MixedIntegerLinearProgram`. @@ -1025,7 +1025,7 @@ cdef class dancing_linksWrapper: sage: from sage.combinat.matrices.dancing_links import dlx_solver sage: rows = [[0,1,2], [0,2], [1], [3]] sage: d = dlx_solver(rows) - sage: p,x = d.to_milp_solver() + sage: p,x = d.to_milp() sage: p Boolean Program (no objective, 4 variables, 4 constraints) sage: x @@ -1051,7 +1051,7 @@ cdef class dancing_linksWrapper: Using some optional MILP solvers:: - sage: d.to_milp_solver('gurobi') # optional - gurobi sage_numerical_backends_gurobi + sage: d.to_milp('gurobi') # optional - gurobi sage_numerical_backends_gurobi (Boolean Program (no objective, 4 variables, 4 constraints), MIPVariable of dimension 1) @@ -1123,7 +1123,7 @@ cdef class dancing_linksWrapper: """ from sage.numerical.mip import MIPSolverException - p,x = self.to_milp_solver(solver) + p,x = self.to_milp(solver) try: p.solve() except MIPSolverException: From d9a05b78dd6419a4d57daabcc4ee4670edb7bd7f Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Mon, 29 Jun 2020 10:05:16 +0900 Subject: [PATCH 154/204] Fixes for pyflakes, coverage, and reviewer comments --- src/sage/schemes/curves/affine_curve.py | 4 +-- src/sage/schemes/curves/constructor.py | 30 ++++++++++++++------- src/sage/schemes/curves/projective_curve.py | 2 +- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index c2362bdcee7..33f69326728 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1,4 +1,4 @@ -""" +r""" Affine curves Affine curves in Sage are curves in an affine space or an affine plane. @@ -2450,8 +2450,6 @@ def parametric_representation(self, place, name=None): sage: p = C(0,1) sage: p.closed_point() Point (x, y - 1) - sage: p.closed_point() - Point (x, y - 1) sage: pl = _.place() sage: C.parametric_representation(pl) (s + ..., 1 - 1/2*s^2 - 1/8*s^4 - 1/16*s^6 + ...) diff --git a/src/sage/schemes/curves/constructor.py b/src/sage/schemes/curves/constructor.py index 8330fadea0d..ca9b570d6ac 100644 --- a/src/sage/schemes/curves/constructor.py +++ b/src/sage/schemes/curves/constructor.py @@ -42,7 +42,6 @@ from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.rings.rational_field import QQ -from sage.rings.qqbar import QQbar from sage.structure.all import Sequence @@ -79,6 +78,21 @@ from sage.schemes.plane_conics.constructor import Conic +def _is_irreducible_and_reduced(F): + """ + Check if the polynomial F is irreducible and reduced. + + TESTS:: + + sage: R. = QQ[] + sage: F = x^2 + y^2 + sage: from sage.schemes.curves.constructor import _is_irreducible_and_reduced + sage: _is_irreducible_and_reduced(F) + True + """ + factors = F.factor() + return len(factors) == 1 and factors[0][1] == 1 + def Curve(F, A=None): """ Return the plane or space curve defined by ``F``, where ``F`` can be either @@ -295,12 +309,11 @@ def Curve(F, A=None): F = F[0] if is_FiniteField(k): - factors = F.factor() - if len(factors) == 1 and factors[0][1] == 1: # irreducible + if _is_irreducible_and_reduced(F): return IntegralAffinePlaneCurve_finite_field(A, F) return AffinePlaneCurve_finite_field(A, F) if k in Fields(): - if k == QQ and A.coordinate_ring().ideal(F).is_prime(): + if k == QQ and _is_irreducible_and_reduced(F): return IntegralAffinePlaneCurve(A, F) return AffinePlaneCurve_field(A, F) return AffinePlaneCurve(A, F) @@ -318,8 +331,8 @@ def Curve(F, A=None): return ProjectiveCurve_field(A, F) return ProjectiveCurve(A, F) - # there is no dimension check when initializing a plane curve, so check - # here that F consists of a single nonconstant polynomial + # There is no dimension check when initializing a plane curve, so check + # here that F consists of a single nonconstant polynomial. if not (len(F) == 1 and F[0] != 0 and F[0].degree() > 0): raise TypeError("need a single nonconstant polynomial to define a plane curve") @@ -328,12 +341,11 @@ def Curve(F, A=None): raise TypeError("{} is not a homogeneous polynomial".format(F)) if is_FiniteField(k): - factors = F.factor() - if len(factors) == 1 and factors[0][1] == 1: # irreducible + if _is_irreducible_and_reduced(F): return IntegralProjectivePlaneCurve_finite_field(A, F) return ProjectivePlaneCurve_finite_field(A, F) if k in Fields(): - if k == QQ and A.coordinate_ring().ideal(F).is_prime(): + if k == QQ and _is_irreducible_and_reduced(F): return IntegralProjectivePlaneCurve(A, F) return ProjectivePlaneCurve_field(A, F) return ProjectivePlaneCurve(A, F) diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 74ffd4d33e6..3e5e35f2e51 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -1,4 +1,4 @@ -""" +r""" Projective curves Projective curves in Sage are curves in a projective space or a projective plane. From 0974b086cdb6994313c5e004a55b9499df0a8a6f Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sun, 28 Jun 2020 19:36:33 -0700 Subject: [PATCH 155/204] explain about conjugate in Verlinde formula --- src/sage/combinat/root_system/fusion_ring.py | 30 +++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index b4608505563..d4767a6e632 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -20,8 +20,7 @@ from sage.misc.cachefunc import cached_method class FusionRing(WeylCharacterRing): - r""" - Return the Fusion Ring (Verlinde Algebra) of level ``k``. + r"""Return the Fusion Ring (Verlinde Algebra) of level ``k``. INPUT: @@ -119,16 +118,33 @@ class FusionRing(WeylCharacterRing): of `V` computed by the method :meth:`Element.q_dimension`. The quantity `D` is computed by :meth:`global_q_dimension`. - Let us check the Verlinde formula. This famous identity states that + Let us check the Verlinde formula, which is [DFMS1996]_ (16.3). This + famous identity states that + + .. MATH:: + + N^k_{ij} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,\overline{s(k,\ell)}}{s(I,\ell)}, + + where `N^k_{ij}` are the fusion coefficients, i.e. the structure + constants of the fusion ring, and ``I`` is the unit object. + The S-matrix has the property that if `i*` denotes the dual + object of `i`, implemented in Sage as ``i.dual()``, then + + .. MATH:: + + s(i*,j) = s(i,j*) = \overline{s(i,j)}. + + This is equation (16.5) in [DFMS1996]_. Thus with `N_{ijk}=N^{k*}_{ij}` + the Verlinde formula is equivalent to .. MATH:: N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)}, - where ``I`` is the unit object. In this formula `s` is the normalized - unitary S-matrix denoted `s` in [BaKi2001]_. We may define a function that - corresponds to the right-hand side, except using `\tilde{s}` instead - of `s`:: + In this formula `s` is the normalized unitary S-matrix + denoted `s` in [BaKi2001]_. We may define a function that + corresponds to the right-hand side, except using + `\tilde{s}` instead of `s`:: sage: def V(i,j,k): ....: R = i.parent() From e9de384fb376b67faa18d530bbdc8193a9fdbc03 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sun, 28 Jun 2020 21:25:28 -0700 Subject: [PATCH 156/204] revision of twists docstring --- src/sage/combinat/root_system/fusion_ring.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index d4767a6e632..ad8147b82bc 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -622,15 +622,16 @@ def weight(self): return next(iter(self._monomial_coefficients)) def twist(self): - r""" - Compute the object's twist. This returns a rational number `h` + r"""Compute the object's twist. This returns a rational number `h` such that `\theta = e^{i \pi h}` is the twist of ``self``. - This method is only available for simple objects. - - We compute the twists following p.7 of [Row2006]_, noting that - the bilinear form is normalized so that - `\langle \alpha, \alpha \rangle = 2` for short roots. + This method is only available for simple objects. If + `\lambda` is the weight of the object, then + `h = \langle\lambda,\lambda+2\rho\rangle` where + `\rho` is half the sum of the positive roots. + As in [Row2006]_, this requires normalizing + the invariant bilinear form so that `\langle \alpha, \alpha + \rangle = 2` for short roots. EXAMPLES:: @@ -644,6 +645,7 @@ def twist(self): Finite family {(0, 0, 0, 0): F41(0,0,0,0), (1, 0, 0, 0): F41(0,0,0,1)} sage: F41(0,0,0,1).twist() 4/5 + """ if not self.is_simple_object(): raise ValueError("quantum twist is only available for simple objects of a FusionRing") From 0e97174cbc8b8d000ab8f160d2cf0a167ce65b3d Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Sun, 28 Jun 2020 21:56:20 -0700 Subject: [PATCH 157/204] fix newline before docstring --- src/sage/combinat/root_system/fusion_ring.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index ad8147b82bc..4fb51fd12b7 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -622,8 +622,9 @@ def weight(self): return next(iter(self._monomial_coefficients)) def twist(self): - r"""Compute the object's twist. This returns a rational number `h` - such that `\theta = e^{i \pi h}` is the twist of ``self``. + r""" + Return a rational number `h` such that `\theta = e^{i \pi h}` + is the twist of ``self``. This method is only available for simple objects. If `\lambda` is the weight of the object, then From 642e80f1afbd6456bfb697fca3b72f8ed4218114 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 29 Jun 2020 15:43:10 +1000 Subject: [PATCH 158/204] Final reviewer changes. --- src/sage/combinat/root_system/fusion_ring.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 4fb51fd12b7..dfb5a66f293 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -20,7 +20,8 @@ from sage.misc.cachefunc import cached_method class FusionRing(WeylCharacterRing): - r"""Return the Fusion Ring (Verlinde Algebra) of level ``k``. + r""" + Return the Fusion Ring (Verlinde Algebra) of level ``k``. INPUT: @@ -238,7 +239,6 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug sage: E81 = FusionRing('E8', 1) sage: TestSuite(E81).run() - """ return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate) @@ -249,18 +249,18 @@ def _test_verlinde(self, **options): EXAMPLES:: - sage: G22=FusionRing("G2",2) + sage: G22 = FusionRing("G2",2) sage: G22._test_verlinde() - """ tester = self._tester(**options) c = self.global_q_dimension() i0 = self.one() - for x in self.basis(): - for y in self.basis(): - for z in self.basis(): - v = sum(self.s_ij(x,w)*self.s_ij(y,w)*self.s_ij(z,w)/self.s_ij(i0,w) for w in self.basis()) - tester.assertEqual(v,c*self.N_ijk(x,y,z)) + S = tester.some_elements() + from sage.misc.misc import some_tuples + B = self.basis() + for x,y,z in some_tuples(B, 3, tester._max_runs): + v = sum(self.s_ij(x,w) * self.s_ij(y,w) * self.s_ij(z,w) / self.s_ij(i0,w) for w in B) + tester.assertEqual(v, c * self.N_ijk(x,y,z)) def field(self): r""" From 46cdf0ae2ab045f8744b2291e79faa53933309ec Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Mon, 29 Jun 2020 12:33:04 +0530 Subject: [PATCH 159/204] exposed in graph.py --- src/sage/graphs/graph.py | 51 +++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index f5c1f50d714..f5ee36b9a55 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -5223,14 +5223,21 @@ def eccentricity(self, v=None, by_weight=False, algorithm=None, - ``'BFS'`` - the computation is done through a BFS centered on each vertex successively. Works only if ``by_weight==False``. + - ``'DHV'`` - the computation is done using the algorithm proposed in + [Dragan2018]_. Works only if ``self`` has non-negative edge weights + and ``v is None`` or ``v`` should contain all vertices of ``self``. + For more information see method + :func:`sage.graphs.distances_all_pairs.eccentricity` and + :func:`sage.graphs.base.boost_graph.eccentricity_DHV`. + - ``'Floyd-Warshall-Cython'`` - a Cython implementation of the Floyd-Warshall algorithm. Works only if ``by_weight==False`` and - ``v is None``. + ``v is None`` or ``v==list(self)``. - ``'Floyd-Warshall-Python'`` - a Python implementation of the Floyd-Warshall algorithm. Works also with weighted graphs, even with negative weights (but no negative cycle is allowed). However, ``v`` - must be ``None``. + must be ``None`` or ``v==list(self)``. - ``'Dijkstra_NetworkX'`` - the Dijkstra algorithm, implemented in NetworkX. It works with weighted graphs, but no negative weight is @@ -5241,7 +5248,7 @@ def eccentricity(self, v=None, by_weight=False, algorithm=None, - ``'Johnson_Boost'`` - the Johnson algorithm, implemented in Boost (works also with negative weights, if there is no negative - cycle). + cycle). Works only if ``v is None`` or ``v == list(self)`` . - ``'From_Dictionary'`` - uses the (already computed) distances, that are provided by input variable ``dist_dict``. @@ -5303,6 +5310,10 @@ def eccentricity(self, v=None, by_weight=False, algorithm=None, [2, 1, 2] sage: G.eccentricity(dist_dict = G.shortest_path_all_pairs(by_weight = True)[0]) [2, 1, 2] + sage: G.eccentricity(by_weight = False, algorithm = 'DHV') + [1, 1, 1] + sage: G.eccentricity(by_weight = True, algorithm = 'DHV') + [2.0, 1.0, 2.0] TESTS: @@ -5339,6 +5350,10 @@ def eccentricity(self, v=None, by_weight=False, algorithm=None, Traceback (most recent call last): ... ValueError: algorithm 'Johnson_Boost' works only if all eccentricities are needed + sage: G.eccentricity(0, algorithm = 'DHV') + Traceback (most recent call last): + ... + ValueError: algorithm 'DHV' works only if all eccentricities are needed """ if weight_function is not None: by_weight = True @@ -5367,6 +5382,8 @@ def weight_function(e): v = [v] if v is None or all(u in v for u in self): + if v is None: + v = list(self) # If we want to use BFS, we use the Cython routine if algorithm == 'BFS': if by_weight: @@ -5374,10 +5391,28 @@ def weight_function(e): from sage.graphs.distances_all_pairs import eccentricity algo = 'bounds' if with_labels: - vertex_list = list(self) - return dict(zip(vertex_list, eccentricity(self, algorithm=algo, vertex_list=vertex_list))) + return dict(zip(v, eccentricity(self, algorithm=algo, vertex_list=v))) else: - return eccentricity(self, algorithm=algo) + return eccentricity(self, algorithm=algo,vertex_list=v) + + if algorithm == 'DHV': + if by_weight: + from sage.graphs.base.boost_graph import eccentricity_DHV + if with_labels: + return dict(zip(v, eccentricity_DHV(self, vertex_list=v, + weight_function=weight_function, + check_weight=check_weight))) + else: + return eccentricity_DHV(self, vertex_list=v, + weight_function=weight_function, + check_weight=check_weight) + else: + from sage.graphs.distances_all_pairs import eccentricity + if with_labels: + return dict(zip(v, eccentricity(self, algorithm=algorithm, + vertex_list=v))) + else: + return eccentricity(self, algorithm=algorithm, vertex_list=v) if algorithm in ['Floyd-Warshall-Python', 'Floyd-Warshall-Cython', 'Johnson_Boost']: dist_dict = self.shortest_path_all_pairs(by_weight, algorithm, @@ -5385,9 +5420,7 @@ def weight_function(e): check_weight)[0] algorithm = 'From_Dictionary' - v = self.vertices() - - elif algorithm in ['Floyd-Warshall-Python', 'Floyd-Warshall-Cython', 'Johnson_Boost']: + elif algorithm in ['Floyd-Warshall-Python', 'Floyd-Warshall-Cython', 'Johnson_Boost','DHV']: raise ValueError("algorithm '" + algorithm + "' works only if all" + " eccentricities are needed") From 690b7573b92930b018773a96cf8d90281e9094c4 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Mon, 29 Jun 2020 11:03:07 +0200 Subject: [PATCH 160/204] added references and changed names to BIBD --- src/sage/combinat/designs/database.py | 35 ++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 01a61e7ff1e..56dd198ea8d 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Database of small combinatorial designs @@ -43,6 +44,15 @@ Chapman & Hall/CRC 2012 +.. [Aschbacher71] \M. Aschbacher, + On collineation groups of symmetric block designs. + J. Combinatorial Theory Ser. A 11 (1971), pp. 272–281. + +.. [Hall71] \M. Hall, Jr., + Combinatorial designs and groups. + Actes du Congrès International des Mathématiciens (Nice, 1970), + v.3, pp. 217–222. Gauthier-Villars, Paris, 1971. + Functions --------- """ @@ -4565,9 +4575,16 @@ def BIBD_201_6_1(): bibd = RecursivelyEnumeratedSet([frozenset(e) for e in bibd], successors=gens) return IncidenceStructure(bibd)._blocks -def biplane_79_13_2(): +def BIBD_79_13_2(): r""" - Return a `(79,13,2)`-BIBD. + Return a symmetric `(79,13,2)`-BIBD. + + The construction implemented is the one described in [Aschbacher71]_. + A typo in that paper was corrected in [Hall71]_. + + .. NOTE:: + + A symmetric `(v,k,\lambda)` BIBD is a `(v,k,\lambda)` BIBD with `v` blocks. EXAMPLES: @@ -4628,9 +4645,15 @@ def P(i,x,y,z): D = IncidenceStructure(blocks) return D._blocks -def biplane_56_11_2(): +def BIBD_56_11_2(): r""" - Return a `(56,11,2)`-BIBD. + Return a symmetric `(56,11,2)`-BIBD. + + The construction implemented is given in [Hall71]_. + + .. NOTE:: + + A symmetric `(v,k,\lambda)` BIBD is a `(v,k,\lambda)` BIBD with `v` blocks. EXAMPLES: @@ -4673,10 +4696,10 @@ def biplane_56_11_2(): BIBD_constructions = { ( 45,9,8): BIBD_45_9_8, - (56,11,2): biplane_56_11_2, + (56,11,2): BIBD_56_11_2, ( 66,6,1): BIBD_66_6_1, ( 76,6,1): BIBD_76_6_1, - (79,13,2): biplane_79_13_2, + (79,13,2): BIBD_79_13_2, ( 96,6,1): BIBD_96_6_1, (120,8,1): RBIBD_120_8_1, (106,6,1): BIBD_106_6_1, From f016797af059aefdf081866da3b3d2d99c9c64dc Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Mon, 29 Jun 2020 11:08:33 +0200 Subject: [PATCH 161/204] fixed typo in docstring --- src/sage/combinat/designs/database.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 56dd198ea8d..30d6b3b5ed2 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -4588,8 +4588,8 @@ def BIBD_79_13_2(): EXAMPLES: - sage: from sage.combinat.designs.database import biplane_79_13_2 - sage: D = IncidenceStructure(biplane_79_13_2()) + sage: from sage.combinat.designs.database import BIBD_79_13_2 + sage: D = IncidenceStructure(BIBD_79_13_2()) sage: D.is_t_design(t=2, v=79, k=13, l=2) True """ @@ -4657,8 +4657,8 @@ def BIBD_56_11_2(): EXAMPLES: - sage: from sage.combinat.designs.database import biplane_56_11_2 - sage: D = IncidenceStructure(biplane_56_11_2()) + sage: from sage.combinat.designs.database import BIBD_56_11_2 + sage: D = IncidenceStructure(BIBD_56_11_2()) sage: D.is_t_design(t=2, v=56, k=11, l=2) True """ From 5186f8f9f3f4b3c8f33d6bfb229595e2e94d17bc Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Mon, 29 Jun 2020 12:17:52 +0200 Subject: [PATCH 162/204] small code formatting --- src/sage/combinat/designs/bibd.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index 1289c4edba7..e9ccc83e532 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -569,7 +569,8 @@ def BIBD_from_difference_family(G, D, lambd=None, check=True): GG = Gset.copy() while GG: g = GG.pop() - if S: GG.difference_update(mul(s,g) for s in S) + if S: + GG.difference_update(mul(s,g) for s in S) bibd.append([p_to_i[mul(i,g)] for i in b]) if check: From 5119f82dde825622597aa2075351a1ca4cf68c80 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 29 Jun 2020 11:31:25 +0100 Subject: [PATCH 163/204] correct (sic) version for freetype 2.4 or better --- build/pkgs/freetype/spkg-configure.m4 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/pkgs/freetype/spkg-configure.m4 b/build/pkgs/freetype/spkg-configure.m4 index 2f2b9445f99..91618333b41 100644 --- a/build/pkgs/freetype/spkg-configure.m4 +++ b/build/pkgs/freetype/spkg-configure.m4 @@ -6,7 +6,8 @@ SAGE_SPKG_CONFIGURE([freetype], [ sage_spkg_install_freetype=yes else AC_MSG_RESULT([no]) - PKG_CHECK_MODULES([FREETYPE], [freetype2 >= 2.4], [], [sage_spkg_install_freetype=yes]) + dnl freetype versions are libtool's ones, cf trac #30014 + PKG_CHECK_MODULES([FREETYPE], [freetype2 >= 16.1], [], [sage_spkg_install_freetype=yes]) fi if test x$sage_spkg_install_freetype = xyes; then AC_SUBST(SAGE_FREETYPE_PREFIX, ['$SAGE_LOCAL']) From d2d340e040d05aa8be6326913576bdf7cda44d23 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Mon, 29 Jun 2020 12:34:58 +0200 Subject: [PATCH 164/204] forgot to delete old import --- src/sage/combinat/designs/bibd.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index e9ccc83e532..11ccde64cea 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -56,7 +56,6 @@ from sage.categories.sets_cat import EmptySetError from sage.misc.unknown import Unknown from .design_catalog import transversal_design -from .block_design import BlockDesign from sage.arith.all import binomial, is_prime_power from .group_divisible_designs import GroupDivisibleDesign from .designs_pyx import is_pairwise_balanced_design From 168de8b8935b1d5f95a07e57d589fc3a053982b5 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 29 Jun 2020 11:42:13 +0100 Subject: [PATCH 165/204] use macro for deps, unconditionally do AC_SUBSTs AC_SUBSTs should be done in the correct block, even if the main body of the macro is skipped. --- build/pkgs/freetype/spkg-configure.m4 | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/build/pkgs/freetype/spkg-configure.m4 b/build/pkgs/freetype/spkg-configure.m4 index 91618333b41..846bcb368ce 100644 --- a/build/pkgs/freetype/spkg-configure.m4 +++ b/build/pkgs/freetype/spkg-configure.m4 @@ -1,14 +1,9 @@ SAGE_SPKG_CONFIGURE([freetype], [ - AC_REQUIRE([SAGE_SPKG_CONFIGURE_LIBPNG]) - AC_MSG_CHECKING([Installing libpng? ]) - if test x$sage_spkg_install_libpng = xyes; then - AC_MSG_RESULT([yes; install freetype as well]) - sage_spkg_install_freetype=yes - else - AC_MSG_RESULT([no]) + SAGE_SPKG_DEPCHECK([libpng], [ dnl freetype versions are libtool's ones, cf trac #30014 PKG_CHECK_MODULES([FREETYPE], [freetype2 >= 16.1], [], [sage_spkg_install_freetype=yes]) - fi + ]) +], [], [], [ if test x$sage_spkg_install_freetype = xyes; then AC_SUBST(SAGE_FREETYPE_PREFIX, ['$SAGE_LOCAL']) else From 46c89ac2f44ad8e400aab740613971093fd9a1bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 29 Jun 2020 13:10:32 +0200 Subject: [PATCH 166/204] remove unused import --- src/sage/modular/dirichlet.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py index 648c2aeadf9..c3139807b3c 100644 --- a/src/sage/modular/dirichlet.py +++ b/src/sage/modular/dirichlet.py @@ -1257,7 +1257,6 @@ def kloosterman_sum(self, a=1, b=0): NotImplementedError: Kloosterman sums not implemented over this ring """ G = self.parent() - K = G.base_ring() zo = G.zeta_order() m = G.modulus() g = 0 From 7425416d2c8474b9dc0cc5ee77d6259f8616eda0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 29 Jun 2020 13:13:29 +0200 Subject: [PATCH 167/204] remove unused import (found by pyflakes) --- src/sage/modular/modsym/ambient.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/modular/modsym/ambient.py b/src/sage/modular/modsym/ambient.py index adeea328a32..aad77cd7f73 100644 --- a/src/sage/modular/modsym/ambient.py +++ b/src/sage/modular/modsym/ambient.py @@ -70,7 +70,7 @@ class ``ModularSymbolsAmbient``, derived from # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ################################################################################ # Sage packages import sage.misc.latex as latex @@ -84,7 +84,7 @@ class ``ModularSymbolsAmbient``, derived from import sage.modular.dirichlet as dirichlet import sage.modular.hecke.all as hecke from sage.rings.all import Integer, QQ, ZZ, Ring -from sage.arith.all import is_prime, gcd, divisors, number_of_divisors, crt +from sage.arith.all import is_prime, divisors, number_of_divisors, crt import sage.rings.polynomial.multi_polynomial_element import sage.structure.formal_sum as formal_sum import sage.categories.all as cat From 8918e84803d65fa44b65b7fbffc6a7316b3a02c6 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Mon, 29 Jun 2020 07:43:25 -0700 Subject: [PATCH 168/204] remove S = tester.some_elements() --- src/sage/combinat/root_system/fusion_ring.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index dfb5a66f293..ed76e05bbee 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -255,7 +255,6 @@ def _test_verlinde(self, **options): tester = self._tester(**options) c = self.global_q_dimension() i0 = self.one() - S = tester.some_elements() from sage.misc.misc import some_tuples B = self.basis() for x,y,z in some_tuples(B, 3, tester._max_runs): From d56f0122e37a34a45e9d85e3d475df2edd767897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 29 Jun 2020 17:56:03 +0200 Subject: [PATCH 169/204] fix oeis doctest --- src/sage/combinat/quickref.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/quickref.py b/src/sage/combinat/quickref.py index f37950b34c6..a62944138ad 100644 --- a/src/sage/combinat/quickref.py +++ b/src/sage/combinat/quickref.py @@ -7,7 +7,10 @@ sage: s = oeis([1,3,19,211]); s # optional - internet 0: A000275: Coefficients of a Bessel function (reciprocal of J_0(z)); also pairs of permutations with rise/rise forbidden. sage: s[0].programs() # optional - internet - 0: (PARI) {a(n) = if( n<0, 0, n!^2 * 4^n * polcoeff( 1 / besselj(0, x + x * O(x^(2*n))), 2*n))}; /* _Michael Somos_, May 17 2004 */ + [('maple', ...), + ('mathematica', ...), + ('pari', + 0: {a(n) = if( n<0, 0, n!^2 * 4^n * polcoeff( 1 / besselj(0, x + x * O(x^(2*n))), 2*n))}; /* _Michael Somos_, May 17 2004 */)] Combinatorial objects:: From 242c55239c18a11921d24ed55749b56389c61e3a Mon Sep 17 00:00:00 2001 From: Martin Albrecht Date: Mon, 29 Jun 2020 17:27:47 +0100 Subject: [PATCH 170/204] update FPLLL to 5.3.3 --- build/pkgs/fplll/checksums.ini | 6 +++--- build/pkgs/fplll/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/fplll/checksums.ini b/build/pkgs/fplll/checksums.ini index 371a327766d..e1eec5f9ba2 100644 --- a/build/pkgs/fplll/checksums.ini +++ b/build/pkgs/fplll/checksums.ini @@ -1,4 +1,4 @@ tarball=fplll-VERSION.tar.gz -sha1=67b70f4dcbb835025abce879b9acb4500e2f1d2c -md5=e72082af9084c5b2d427977c9b79a602 -cksum=65319984 +sha1=3ab5e269fdf73b6f933b24db656e5c55d83aa690 +md5=31f0f5d2045802d0beb8945dd0fa07f0 +cksum=2692708834 diff --git a/build/pkgs/fplll/package-version.txt b/build/pkgs/fplll/package-version.txt index 84197c89467..74664af7400 100644 --- a/build/pkgs/fplll/package-version.txt +++ b/build/pkgs/fplll/package-version.txt @@ -1 +1 @@ -5.3.2 +5.3.3 From 58e75539da7d258a90d66e74ccb49babb8b241b7 Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Mon, 29 Jun 2020 14:21:21 -0300 Subject: [PATCH 171/204] PartitionTuples allow componentwise regular value * Implemented component-wise regular value on RegularPartitionTuples --- src/sage/combinat/partition_tuple.py | 693 ++++++++++++--------------- 1 file changed, 296 insertions(+), 397 deletions(-) diff --git a/src/sage/combinat/partition_tuple.py b/src/sage/combinat/partition_tuple.py index aa874194cf1..425e3b820e0 100644 --- a/src/sage/combinat/partition_tuple.py +++ b/src/sage/combinat/partition_tuple.py @@ -244,7 +244,7 @@ class of modules for the algebras, which are generalisations of the Specht """ -# **************************************************************************** +#***************************************************************************** # Copyright (C) 2012 Andrew Mathas # # This program is free software: you can redistribute it and/or modify @@ -252,9 +252,11 @@ class of modules for the algebras, which are generalisations of the Specht # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # https://www.gnu.org/licenses/ -# **************************************************************************** +#***************************************************************************** from __future__ import print_function, absolute_import +from six.moves import range + import itertools from .combinat import CombinatorialElement @@ -264,7 +266,7 @@ class of modules for the algebras, which are generalisations of the Specht from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.groups.perm_gps.permgroup import PermutationGroup -from sage.libs.pari.all import pari +from sage.interfaces.all import gp from sage.misc.cachefunc import cached_method from sage.rings.all import NN, ZZ, IntegerModRing from sage.rings.integer import Integer @@ -272,11 +274,10 @@ class of modules for the algebras, which are generalisations of the Specht from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -# ------------------------------------------------- -# Partition tuple - element class -# ------------------------------------------------- - +#-------------------------------------------------- +# Partition tuple - element class +#-------------------------------------------------- class PartitionTuple(CombinatorialElement): r""" A tuple of :class:`Partition`. @@ -555,7 +556,7 @@ def _repr_list(self): sage: PartitionTuple(([2,1],[3,2],[1,1,1]))._repr_list() '([2, 1], [3, 2], [1, 1, 1])' """ - return '(' + ', '.join(nu._repr_() for nu in self) + ')' + return '('+', '.join(nu._repr_() for nu in self)+')' def _repr_exp_low(self): """ @@ -614,11 +615,12 @@ def _repr_compact_high(self): return '%s' % '|'.join(mu._repr_compact_high() for mu in self) # override default string representation which is str(self._list) - __str__ = lambda self: self._repr_() + __str__=lambda self: self._repr_() + def _latex_(self): r""" - Return a LaTeX version of ``self``. + Returns a LaTeX version of ``self``. For more on the latex options, see :meth:`Partitions.option`. @@ -686,8 +688,7 @@ def _latex_young_diagram(self): sage: mu = PartitionTuple([[2, 1],[1,1,1]])._latex_young_diagram() """ from sage.combinat.output import tex_from_array_tuple - return tex_from_array_tuple([[["\\phantom{x}"] * row for row in mu] - for mu in self._list]) + return tex_from_array_tuple([ [["\\phantom{x}"]*row for row in mu] for mu in self._list ]) def _latex_diagram(self): """ @@ -699,8 +700,7 @@ def _latex_diagram(self): """ entry = self.parent().options("latex_diagram_str") from sage.combinat.output import tex_from_array_tuple - return tex_from_array_tuple([[[entry] * row for row in mu] - for mu in self._list], with_lines=False) + return tex_from_array_tuple([ [[entry]*row for row in mu] for mu in self._list ], with_lines=False) def _latex_list(self): """ @@ -720,10 +720,8 @@ def _latex_exp_low(self): sage: mu = PartitionTuple([[2, 1],[1,1,1,1,1,1,1,1,1,1]])._latex_exp_low() """ - txt = '|'.join(','.join('%s%s' % (a + 1, '' if e == 1 else '^{%s}' % e) - for a, e in enumerate(mu)) - for mu in self.to_exp()) - return '(' + txt + ')' + return '(%s)' % '|'.join(','.join('%s%s'%(a+1,'' if e==1 else '^{%s}'%e) + for (a,e) in enumerate(mu)) for mu in self.to_exp()) def _latex_exp_high(self): """ @@ -733,10 +731,9 @@ def _latex_exp_high(self): sage: mu = PartitionTuple([[2, 1],[1,1,1,1,1,1,1,1,1,1]])._latex_exp_high() """ - txt = '|'.join(','.join(['%s%s' % (a + 1, '' if e == 1 else '^{%s}' % e) - for a, e in enumerate(mu)][::-1]) - for mu in self.to_exp()) - return '(' + txt + ')' + return '(%s)' % '|'.join(','.join(['%s%s'%(a+1,'' if e==1 else '^{%s}'%e) + for (a,e) in enumerate(mu)][::-1]) for mu in self.to_exp()) + def components(self): r""" @@ -763,7 +760,8 @@ def components(self): *** ** """ - return [t for t in self] + return [ t for t in self ] + def diagram(self): r""" @@ -788,7 +786,7 @@ def diagram(self): *** ** - * sage: PartitionTuples.options._reset() """ - col_len = [mu and mu[0] or 1 for mu in self] # columns per component + col_len = [len(mu)>0 and mu[0] or 1 for mu in self] # columns per component row_max = max(len(mu) for mu in self) # maximum row length # There should be a fancier list compression for this but I couldn't get # one to work in the cases where a component was the empty partition @@ -811,6 +809,7 @@ def diagram(self): ferrers_diagram = diagram + def pp(self): r""" Pretty prints this partition tuple. See :meth:`diagram`. @@ -825,6 +824,7 @@ def pp(self): """ print(self.diagram()) + def size(self): """ Return the size of a partition tuple. @@ -945,7 +945,7 @@ def cells(self): def content(self, k,r,c, multicharge): r""" - Return the content of the cell. + Returns the content of the cell. Let `m_k =` ``multicharge[k]``, then the content of a cell is `m_k + c - r`. @@ -1004,10 +1004,8 @@ def content_tableau(self,multicharge): 2 """ from sage.combinat.tableau_tuple import TableauTuple - return TableauTuple([[[multicharge[k] - r + c - for c in range(self[k][r])] - for r in range(len(self[k]))] - for k in range(len(self))]) + return TableauTuple([[[multicharge[k]-r+c for c in range(self[k][r])] + for r in range(len(self[k]))] for k in range(len(self))]) def conjugate(self): """ @@ -1020,6 +1018,7 @@ def conjugate(self): sage: PartitionTuple([[2,1],[1],[1,1,1]]).conjugate() ([3], [1], [2, 1]) + """ return PartitionTuple([nu.conjugate() for nu in self[::-1]]) @@ -1057,25 +1056,22 @@ def dominates(self, mu): except ValueError: raise ValueError('%s must be a PartitionTuple' % mu) - if mu == self: - return True - level = 0 - ssum = 0 # sum of successive rows in self - musum = 0 # sum of successive rows in self + if mu==self: return True + level=0 + ssum=0 # sum of successive rows in self + musum=0 # sum of successive rows in self while levelssum: - return False + if musum>ssum: return False row+=1 if rowssum: - return False + if musum>ssum: return False level+=1 return True @@ -1251,14 +1247,12 @@ def top_garnir_tableau(self,e,cell): g=self.garnir_tableau(cell) - if e==0: - return # no more dominant tableau of the same residue + if e==0: return # no more dominant tableau of the same residue a=e*int((self[comp][row]-col)/e) # number of cells in the e-bricks in row `row` b=e*int((col+1)/e) # number of cells in the e-bricks in row `row+1` - if a==0 or b==0: - return self.garnir_tableau(cell) + if a==0 or b==0: return self.garnir_tableau(cell) t=g.to_list() m=t[comp][row+1][0] # smallest number of 0-Garnir belt @@ -1333,7 +1327,7 @@ def leg_length(self, k,r,c): def contains(self, mu): r""" - Return ``True`` if this partition tuple contains `\mu`. + Returns ``True`` if this partition tuple contains `\mu`. If `\lambda=(\lambda^{(1)}, \ldots, \lambda^{(l)})` and `\mu=(\mu^{(1)}, \ldots, \mu^{(m)})` are two partition tuples then @@ -1385,7 +1379,7 @@ def to_exp(self, k=0): def removable_cells(self): """ - Return a list of the removable cells of this partition tuple. + Returns a list of the removable cells of this partition tuple. All indices are of the form ``(k, r, c)``, where ``r`` is the row-index, ``c`` is the column index and ``k`` is the component. @@ -1421,7 +1415,7 @@ def addable_cells(self): outside_corners = addable_cells # for compatibility with partitions - def add_cell(self, k, r, c): + def add_cell(self, k,r,c): r""" Return the partition tuple obtained by adding a cell in row ``r``, column ``c``, and component ``k``. @@ -1432,18 +1426,17 @@ def add_cell(self, k, r, c): sage: PartitionTuple([[1,1],[4,3],[2,1,1]]).add_cell(0,0,1) ([2, 1], [4, 3], [2, 1, 1]) + """ - if (k, r, c) in self.addable_cells(): # an addable cell - mu = self.to_list() - if c == 0: - mu[k].append(1) - else: - mu[k][r] += 1 + if (k,r,c) in self.addable_cells(): # an addable cell + mu=self.to_list() + if c==0: mu[k].append(1) + else: mu[k][r]+=1 return PartitionTuple(mu) else: - raise ValueError("%s is not an addable cell" % ((k, r, c),)) + raise ValueError("%s is not an addable cell"%((k,r,c),)) - def remove_cell(self, k, r, c): + def remove_cell(self, k,r,c): """ Return the partition tuple obtained by removing a cell in row ``r``, column ``c``, and component ``k``. @@ -1454,13 +1447,14 @@ def remove_cell(self, k, r, c): sage: PartitionTuple([[1,1],[4,3],[2,1,1]]).remove_cell(0,1,0) ([1], [4, 3], [2, 1, 1]) + """ - if (k, r, c) in self.removable_cells(): # a removable cell - mu = self.to_list() - mu[k][r] -= 1 + if (k,r,c) in self.removable_cells(): # a removable cell + mu=self.to_list() + mu[k][r]-=1 return PartitionTuple(mu) else: - raise ValueError("%s is not a removable cell" % ((k, r, c),)) + raise ValueError("%s is not a removable cell"%((k,r,c),)) def to_list(self): r""" @@ -1476,7 +1470,7 @@ def to_list(self): sage: all(mu==PartitionTuple(mu.to_list()) for mu in PartitionTuples(4,4)) True """ - return [mu.to_list() for mu in self] + return [ mu.to_list() for mu in self] def young_subgroup(self): """ @@ -1488,14 +1482,14 @@ def young_subgroup(self): sage: PartitionTuple([[2,1],[4,2],[1]]).young_subgroup() Permutation Group with generators [(), (8,9), (6,7), (5,6), (4,5), (1,2)] """ - gens = [] - m = 0 + gens=[] + m=0 for comp in self: for row in comp: - gens.extend([(c, c+1) for c in range(m+1, m+row)]) - m += row - gens.append(list(range(1, self.size()+1))) # to ensure we get a subgroup of Sym_n - return PermutationGroup(gens) + gens.extend([(c,c+1) for c in range(m+1,m+row)]) + m+=row + gens.append(list(range(1,self.size()+1))) # to ensure we get a subgroup of Sym_n + return PermutationGroup( gens ) def young_subgroup_generators(self): """ @@ -1507,12 +1501,12 @@ def young_subgroup_generators(self): sage: PartitionTuple([[2,1],[4,2],[1]]).young_subgroup_generators() [1, 4, 5, 6, 8] """ - gens = [] - m = 0 + gens=[] + m=0 for comp in self: for row in comp: - gens.extend([c for c in range(m + 1, m + row)]) - m += row + gens.extend([c for c in range(m+1,m+row)]) + m+=row return gens @cached_method @@ -1765,11 +1759,9 @@ def defect(self, e, multicharge): return (sum(beta.get(r, 0) for r in multicharge) - sum(beta[r]**2 - beta[r] * beta.get(Ie(r+1), 0) for r in beta)) -# ------------------------------------------------- +#-------------------------------------------------- # Partition tuples - parent classes -# ------------------------------------------------- - - +#-------------------------------------------------- class PartitionTuples(UniqueRepresentation, Parent): r""" Class of all partition tuples. @@ -1786,12 +1778,12 @@ class PartitionTuples(UniqueRepresentation, Parent): - ``size`` -- the total number of cells - ``regular`` -- a positive integer or a tuple of non-negative - integers; If an integer, the highest multiplicity an entry may - have in a component plus `1`. If a level `k` is specified and - ``regular`` is a tuple of integers `l_1,..,l_k` then this - specifies partition tuples `\mu` such that - `mu_i` is `l_i`-regular. The special case `l_i = 0` is treated - as `\infty`-regular, that is, partitions without restrictions. + integers; if an integer, the highest multiplicity an entry may + have in a component plus `1`. If a level `k` is specified and + ``regular`` is a tuple of integers `l_1,..,l_k` then this + specifies partition tuples `\mu` such that + `\mu_i` is `l_i`-regular. The special case `l_i = 0` is treated + as `\infty`-regular, that is, partitions without restrictions. TESTS:: @@ -1823,6 +1815,10 @@ def __classcall_private__(klass, level=None, size=None, regular=None): Partition tuples of size 3 sage: PartitionTuples(3,8) Partition tuples of level 3 and size 8 + sage: PartitionTuples(level=3, regular=(0,2,4)) + (0, 2, 4)-Regular partition tuples of level 3 + sage: PartitionTuples(level=1,regular=(4,)) == PartitionTuples(level=1,regular=4) + True """ # sanity testing @@ -1832,6 +1828,13 @@ def __classcall_private__(klass, level=None, size=None, regular=None): if size is not None and (not isinstance(size,(int,Integer)) or size<0): raise ValueError('the size must be a non-negative integer') + if isinstance(regular, (list,tuple)): + if level is None: + raise ValueError("When no level is specified, regular must be"\ + "a positive integer") + if len(regular) != level: + raise ValueError("regular must be a list of length {}, got "\ + "{}".format(level,regular)) if level is None: if size is None: if regular is None: @@ -1843,30 +1846,26 @@ def __classcall_private__(klass, level=None, size=None, regular=None): return RegularPartitionTuples_size(size, regular) elif level == 1: + if isinstance(regular, (list,tuple)): + regular = regular[0] if size is None: if regular is None: return _Partitions - if isinstance(regular, tuple): - return VectorRegularPartitionTuples_level(1,regular) return RegularPartitions_all(regular) if regular is None: return Partitions_n(size) - if isinstance(regular, tuple): - return VectorRegularPartitionTuples_level_size(1,size,regular) return RegularPartitions_n(size, regular) # Higher level + if regular is not None and not isinstance(regular, (list,tuple)): + regular = (regular,)*level if size is None: if regular is None: return PartitionTuples_level(level) - if isinstance(regular, tuple): - return VectorRegularPartitionTuples_level(level,regular) return RegularPartitionTuples_level(level, regular) if regular is None: return PartitionTuples_level_size(level, size) - if isinstance(regular, tuple): - return VectorRegularPartitionTuples_level_size(level,size,regular) return RegularPartitionTuples_level_size(level, size, regular) Element = PartitionTuple @@ -2040,7 +2039,7 @@ def _an_element_(self): sage: PartitionTuples().an_element() ([1, 1, 1, 1], [2, 1, 1], [3, 1], [4]) """ - return PartitionTuple(([1, 1, 1, 1], [2, 1, 1], [3, 1], [4])) + return PartitionTuple( ([1,1,1,1],[2,1,1],[3,1],[4]) ) class PartitionTuples_all(PartitionTuples): @@ -2121,7 +2120,7 @@ class PartitionTuples_level(PartitionTuples): integer. """ - def __init__(self, level): + def __init__(self, level, **kwds): r""" Initializes this class. @@ -2133,9 +2132,10 @@ def __init__(self, level): Partition tuples of level 6 sage: TestSuite( PartitionTuples(level=4) ).run() """ - if level not in NN: + if not level in NN: raise ValueError('level must be a non-negative integer') - super(PartitionTuples_level, self).__init__(category=InfiniteEnumeratedSets()) + category = kwds.pop('category', InfiniteEnumeratedSets()) + super(PartitionTuples_level, self).__init__(category=category) self._level=level def _repr_(self): @@ -2216,7 +2216,7 @@ def _an_element_(self): sage: PartitionTuples(level=4).an_element() ([], [1], [2], [3]) """ - return self.element_class(self, tuple([l] for l in range(self.level()))) + return self.element_class(self, tuple([l] for l in range(self.level()) )) class PartitionTuples_size(PartitionTuples): @@ -2225,7 +2225,7 @@ class PartitionTuples_size(PartitionTuples): """ def __init__(self, size): r""" - Initialize this class. + Initializes this class. EXAMPLES:: @@ -2236,7 +2236,7 @@ def __init__(self, size): sage: TestSuite( PartitionTuples(size=6) ).run() """ - if size not in NN: + if not size in NN: raise ValueError('size must be a non-negative integer') super(PartitionTuples_size, self).__init__(category=InfiniteEnumeratedSets()) self._size=size @@ -2321,7 +2321,7 @@ def _an_element_(self): sage: PartitionTuples(size=4).an_element() ([1], [1], [1], [1]) """ - return self.element_class(self, tuple([1] for l in range(self._size))) + return self.element_class(self, tuple([1] for l in range(self._size) )) class PartitionTuples_level_size(PartitionTuples): @@ -2414,6 +2414,7 @@ def __iter__(self): for cp in itertools.product(*[p[i] for i in iv]): yield self._element_constructor_(cp) + def _an_element_(self): """ Return a generic element. @@ -2423,10 +2424,9 @@ def _an_element_(self): sage: PartitionTuples(level=4,size=4).an_element() ([1], [], [], [3]) """ - mu = [[] for l in range(self._level)] + mu=[[] for l in range(self._level)] if self._size > 0: - if self._level == 1: - mu=[self._size-1,1] + if self._level == 1: mu=[self._size-1,1] else: mu[0]=[1] mu[-1]=[self._size-1] @@ -2434,9 +2434,9 @@ def _an_element_(self): def cardinality(self): r""" - Return the number of ``level``-tuples of partitions of size ``n``. + Returns the number of ``level``-tuples of partitions of size ``n``. - Wraps a pari function call using :pari:`eta`. + Wraps a pari function call. EXAMPLES:: @@ -2461,8 +2461,7 @@ def cardinality(self): These answers were checked against Gap4 (the last of which takes an awful long time for gap to compute). """ - eta = pari(f'Ser(x,x,{self.size()})').eta() - return ZZ((1 / eta**self.level()).polcoef(self.size(), pari('x'))) + return ZZ(gp.eval('polcoeff((1/eta(x+O(x^%s)))^%s, %s, x)'%(self.size()+1,self.level(), self.size()))) def __setstate__(self, state): r""" @@ -2485,8 +2484,7 @@ def __setstate__(self, state): super(PartitionTuples, self).__setstate__(state) ############################################################################### -# Regular partition tuples - +## Regular partition tuples class RegularPartitionTuples(PartitionTuples): r""" @@ -2562,7 +2560,6 @@ def _an_element_(self): elt = RegularPartitionTuples_level_size(lvl, size, self._ell).an_element() return self.element_class(self, list(elt)) - class RegularPartitionTuples_all(RegularPartitionTuples): r""" Class of `\ell`-regular partition tuples. @@ -2623,27 +2620,77 @@ def __iter__(self): yield self.element_class(self, list(mu)) -class RegularPartitionTuples_level(RegularPartitionTuples): - r""" - Class of `\ell`-regular partition tuples with a fixed level. - """ +class RegularPartitionTuples_level(PartitionTuples_level): def __init__(self, level, regular): r""" - Initialize ``self``. + Regular Partition tuples of a fixed level. + + INPUT: + + - ``level`` -- a non-negative Integer; the level + - ``regular`` -- a tuple of non-negative integers with length + ``level`` or a non-negative integer; if it is a non-negative + integer it is converted into a tuple by repeating this + integer ``level`` times. the ``i``-th entry of this tuple, + if non-zero, is the maximum number that a part can appear in + the ``i``-th component plus one. EXAMPLES:: + sage: RPT = PartitionTuples(level=4, regular=(2,3,0,2)) + sage: RPT[:24] + [([], [], [], []), + ([1], [], [], []), + ([], [1], [], []), + ([], [], [1], []), + ([], [], [], [1]), + ([2], [], [], []), + ([1], [1], [], []), + ([1], [], [1], []), + ([1], [], [], [1]), + ([], [2], [], []), + ([], [1, 1], [], []), + ([], [1], [1], []), + ([], [1], [], [1]), + ([], [], [2], []), + ([], [], [1, 1], []), + ([], [], [1], [1]), + ([], [], [], [2]), + ([3], [], [], []), + ([2, 1], [], [], []), + ([2], [1], [], []), + ([2], [], [1], []), + ([2], [], [], [1]), + ([1], [2], [], []), + ([1], [1, 1], [], [])] + sage: [[1,1],[3],[5,5,5],[7,2]] in RPT + False + sage: [[3,1],[3],[5,5,5],[7,2]] in RPT + True + sage: [[3,1],[3],[5,5,5]] in RPT + False + + TESTS:: + sage: RPT = PartitionTuples(level=4, regular=3) sage: TestSuite(RPT).run() """ - if level not in ZZ or level <= 0: - raise ValueError('level must be a positive integer') - if regular > 1: + if not level in NN: + raise ValueError('level must be a non-negative integer') + if not isinstance(regular, tuple): + #This should not happen if called from RegularPartitionTuples + regular = (regular,)*level + if any (r > 1 for r in regular): category = InfiniteEnumeratedSets() else: category = FiniteEnumeratedSets() - RegularPartitionTuples.__init__(self, regular, category=category) - self._level = level + if any (r not in NN for r in regular): + raise ValueError('regular must be a tuple of non-negative integers') + if len(regular) != level: + raise ValueError("regular must be a tuple with length {}".\ + format(level)) + PartitionTuples_level.__init__(self, level, category=category) + self._ell = regular def _repr_(self): """ @@ -2653,8 +2700,14 @@ def _repr_(self): sage: PartitionTuples(level=4, regular=3) 3-Regular partition tuples of level 4 + sage: PartitionTuples(level=4, regular=(2,3,0,2)) + (2, 3, 0, 2)-Regular partition tuples of level 4 """ - return '{}-Regular partition tuples of level {}'.format(self._ell, self._level) + if self._ell[1:] == self._ell[:-1]: + return '{}-Regular partition tuples of level {}'.format(self._ell[0], + self._level) + return '{}-Regular partition tuples of level {}'.format(self._ell, + self._level) def __contains__(self, mu): r""" @@ -2671,11 +2724,42 @@ def __contains__(self, mu): False sage: [4, 3, 2] in RPT False + sage: RPT = PartitionTuples(level=3, regular=(2,1,4)) + sage: [[4],[2],[5]] in RPT + False + sage: [[4],[],[5]] in RPT + True + sage: [[4,3],[],[5]] in RPT + True + sage: [[4,4],[],[5]] in RPT + False + sage: [[4,3],[5]] in RPT + False + sage: [5,4,3] in RPT + False + sage: from sage.combinat.partition_tuple import RegularPartitionTuples_level + sage: RPT = RegularPartitionTuples_level(1,(3,)); RPT + 3-Regular partition tuples of level 1 + sage: [[2,2]] in RPT + True + sage: [[2,2,2]] in RPT + False """ - if self._level == 1: - if mu[0] in ZZ: - return mu in RegularPartitions_all(self._ell) - return RegularPartitionTuples.__contains__(self, mu) and self._level == len(mu) + if self._level != len(mu): + return False + if mu not in PartitionTuples_level(self._level): + return False + if isinstance(mu, Partition): + if self._ell[0] == 0: + return True + return mu in RegularPartitions_all(self._ell[0]) + if isinstance(mu, PartitionTuple): + return all(max(p.to_exp() + [0]) < ell for p,ell in \ + zip(mu,self._ell) if ell > 0) + if len(mu) == 0: #level = 0 + return True + return all(p in RegularPartitions_all(ell) for p,ell in zip(mu, + self._ell) if ell > 0) def __iter__(self): r""" @@ -2684,6 +2768,31 @@ def __iter__(self): EXAMPLES:: + sage: PartitionTuples(level=3, regular=(2,1,4))[:24] + [([], [], []), + ([1], [], []), + ([], [], [1]), + ([2], [], []), + ([1], [], [1]), + ([], [], [2]), + ([], [], [1, 1]), + ([3], [], []), + ([2, 1], [], []), + ([2], [], [1]), + ([1], [], [2]), + ([1], [], [1, 1]), + ([], [], [3]), + ([], [], [2, 1]), + ([], [], [1, 1, 1]), + ([4], [], []), + ([3, 1], [], []), + ([3], [], [1]), + ([2, 1], [], [1]), + ([2], [], [2]), + ([2], [], [1, 1]), + ([1], [], [3]), + ([1], [], [2, 1]), + ([1], [], [1, 1, 1])] sage: PartitionTuples(level=4, regular=2)[:20] [([], [], [], []), ([1], [], [], []), @@ -2710,7 +2819,6 @@ def __iter__(self): for mu in RegularPartitionTuples_level_size(self._level, size, self._ell): yield self.element_class(self, list(mu)) - class RegularPartitionTuples_size(RegularPartitionTuples): r""" Class of `\ell`-regular partition tuples with a fixed size. @@ -2768,10 +2876,9 @@ def __contains__(self, mu): sage: [4, 3, 2] in RPT True """ - return ((mu in RegularPartitions_all(self._ell) - and self._size == sum(mu)) - or (RegularPartitionTuples.__contains__(self, mu) - and self._size == sum(map(sum, mu)))) + return ( (mu in RegularPartitions_all(self._ell) and self._size == sum(mu)) + or (RegularPartitionTuples.__contains__(self, mu) and self._size == sum(map(sum,mu))) + ) def __iter__(self): r""" @@ -2797,266 +2904,24 @@ def __iter__(self): yield self.element_class(self, list(mu)) -class RegularPartitionTuples_level_size(RegularPartitionTuples): - r""" - Class of `\ell`-regular partition tuples with a fixed level and a fixed - size. - """ +class RegularPartitionTuples_level_size(PartitionTuples_level_size): def __init__(self, level, size, regular): r""" - Initialize ``self``. - - EXAMPLES:: - - sage: RPT = PartitionTuples(4,2,3) - sage: TestSuite(RPT).run() - """ - if size not in NN: - raise ValueError('size must be a non-negative integer') - if not (level in ZZ and level > 0): - raise ValueError('level must be a positive integer') - RegularPartitionTuples.__init__(self, regular, category=FiniteEnumeratedSets()) - self._level = level - self._size = size - - def _repr_(self): - """ - Return a string representation of ``self``. - - EXAMPLES:: - - sage: PartitionTuples(4,2,3) - 3-Regular partition tuples of level 4 and size 2 - sage: PartitionTuples(size=2,level=4,regular=3) - 3-Regular partition tuples of level 4 and size 2 - """ - return '{}-Regular partition tuples of level {} and size {}'.format(self._ell, self._level, self._size) - - def __contains__(self, mu): - r""" - Return ``True`` if `\mu` is in ``self``. - - TESTS:: - - sage: RPT = PartitionTuples(4,3,2) - sage: [[], [], [2], [1]] in RPT - True - sage: [[1], [1], [], [1]] in RPT - True - sage: [[1,1,1], [], [], []] in RPT - False - sage: RPT = PartitionTuples(9, 3, 2) - sage: [4, 3, 2] in RPT - False - """ - if self._level == 1 and mu[0] in ZZ: - return mu in RegularPartitions_all(self._ell) and self._size == sum(mu) - return (RegularPartitionTuples.__contains__(self, mu) - and self._level == len(mu) - and self._size == sum(map(sum,mu))) - - def __iter__(self): - r""" - Iterate through the finite class of `\ell`-regular partition tuples - of a fixed level and a fixed size. - - EXAMPLES:: - - sage: list(PartitionTuples(3,3,2)) - [([3], [], []), - ([2, 1], [], []), - ([2], [1], []), - ([2], [], [1]), - ([1], [2], []), - ([1], [1], [1]), - ([1], [], [2]), - ([], [3], []), - ([], [2, 1], []), - ([], [2], [1]), - ([], [1], [2]), - ([], [], [3]), - ([], [], [2, 1])] - """ - p = [RegularPartitions_n(i, self._ell) for i in range(self._size+1)] - for iv in IntegerVectors(self._size, self._level): - for cp in itertools.product(*[p[i] for i in iv]): - yield self._element_constructor_(cp) - - def _an_element_(self): - """ - Return a generic element. - - EXAMPLES:: - - sage: PartitionTuples(level=4, size=4, regular=3).an_element() - ([1], [], [], [3]) - """ - mu = [[] for l in range(self._level)] - if self._size > 0: - if self._level == 1: - mu = [self._size-1,1] - else: - mu[0] = [1] - mu[-1] = [self._size-1] - return self.element_class(self, mu) - + Class of `\ell`-regular partition tuples with a fixed level and + a fixed size. -class VectorRegularPartitionTuples_level(PartitionTuples_level): - """ - Class of vector-regular partition tuples with a fixed level. - """ - def __init__(self, level, regular): - """ - Initialize ``self``. - - EXAMPLES:: - - sage: RPT = PartitionTuples(level=4, regular=(2,3,0,2)) - sage: RPT[:24] - [([], [], [], []), - ([1], [], [], []), - ([], [1], [], []), - ([], [], [1], []), - ([], [], [], [1]), - ([2], [], [], []), - ([1], [1], [], []), - ([1], [], [1], []), - ([1], [], [], [1]), - ([], [2], [], []), - ([], [1, 1], [], []), - ([], [1], [1], []), - ([], [1], [], [1]), - ([], [], [2], []), - ([], [], [1, 1], []), - ([], [], [1], [1]), - ([], [], [], [2]), - ([3], [], [], []), - ([2, 1], [], [], []), - ([2], [1], [], []), - ([2], [], [1], []), - ([2], [], [], [1]), - ([1], [2], [], []), - ([1], [1, 1], [], [])] - sage: [[1,1],[3],[5,5,5],[7,2]] in RPT - False - sage: [[3,1],[3],[5,5,5],[7,2]] in RPT - True - sage: [[3,1],[3],[5,5,5]] in RPT - False - """ - if level not in ZZ or level <= 0: - raise ValueError('level must be a positive integer') - if len(regular) != level: - raise ValueError("regular must be a tuple with length {}".\ - format(level)) - if any (i not in NN for i in regular): - raise ValueError('regular must be a tuple of non-negative integers') - PartitionTuples_level.__init__(self, level) - self._ell = regular - - def _repr_(self): - """ - Return a string representation of ``self``. - - EXAMPLES:: - - sage: RPT = PartitionTuples(level=4, regular=(2,3,0,2)) - sage: RPT - (2, 3, 0, 2)-Regular partition tuples of level 4 - """ - return '{}-Regular partition tuples of level {}'.format(self._ell, - self._level) - - def __contains__(self, mu): - """ - Return ``True`` if ``mu`` is in ``self``. - - TESTS:: - - sage: RPT = PartitionTuples(level=3, regular=(2,1,4)) - sage: [[4],[2],[5]] in RPT - False - sage: [[4],[],[5]] in RPT - True - sage: [[4,3],[],[5]] in RPT - True - sage: [[4,4],[],[5]] in RPT - False - sage: [[4,3],[5]] in RPT - False - sage: [5,4,3] in RPT - False - sage: RPT = PartitionTuples(level=1,regular=(3,)); RPT - (3,)-Regular partition tuples of level 1 - sage: [[2,2]] in RPT - True - sage: [[2,2,2]] in RPT - False - """ - if self._level != len(mu): - return False - if mu not in PartitionTuples_level(self._level): - return False - if isinstance(mu, Partition): - if self._ell[0] == 0: - return True - return max(mu.to_exp() + [0]) < self._ell[0] - if isinstance(mu, PartitionTuple): - return all(max(mu[i].to_exp() + [0]) < self._ell[i] for - i in range(len(mu)) if self._ell[i] > 0) - if len(mu) == 0: - return True - return all(mu[i] in RegularPartitions_all(self._ell[i]) for i in - range(len(mu)) if self._ell[i] > 0) - - def __iter__(self): - """ - Iterate through the class of vector `\ell`-regular partition - tuples of a fixed level. - - EXAMPLES:: - - sage: PartitionTuples(level=3, regular=(2,1,4))[:24] - [([], [], []), - ([1], [], []), - ([], [], [1]), - ([2], [], []), - ([1], [], [1]), - ([], [], [2]), - ([], [], [1, 1]), - ([3], [], []), - ([2, 1], [], []), - ([2], [], [1]), - ([1], [], [2]), - ([1], [], [1, 1]), - ([], [], [3]), - ([], [], [2, 1]), - ([], [], [1, 1, 1]), - ([4], [], []), - ([3, 1], [], []), - ([3], [], [1]), - ([2, 1], [], [1]), - ([2], [], [2]), - ([2], [], [1, 1]), - ([1], [], [3]), - ([1], [], [2, 1]), - ([1], [], [1, 1, 1])] - """ - for size in NN: - for mu in VectorRegularPartitionTuples_level_size(self._level, - size, self._ell): - yield self.element_class(self, list(mu)) + INPUT: -class VectorRegularPartitionTuples_level_size(PartitionTuples_level_size): - """ - Class of vector `\ell`-regular partition tuples with a fixed level - and a fixed size. - """ - def __init__(self, level, size, regular): - """ - Initialize ``self``. + - ``level`` -- a non-negative Integer; the level + - ``size`` -- a non-negative Integer; the size + - ``regular`` -- a tuple of non-negative integers with length + ``level`` or a non-negative integer; if it is a non-negative + integer it is converted into a tuple by repeating this + integer ``level`` times. the ``i``-th entry of this tuple, + if non-zero, is the maximum number that a part can appear in + the ``i``-th component plus one. - EXAMPLES:: + EXAMPLES:: sage: PartitionTuples(level=3, size=7, regular=(2,1,3))[0:24] [([7], [], []), @@ -3083,17 +2948,25 @@ def __init__(self, level, size, regular): ([3], [], [2, 2]), ([3], [], [2, 1, 1]), ([2, 1], [], [4])] + + TESTS:: + + sage: RPT = PartitionTuples(4,2,3) + sage: TestSuite(RPT).run() """ if size not in NN: raise ValueError('size must be a non-negative integer') if not (level in ZZ and level > 0): raise ValueError('level must be a positive integer') - PartitionTuples_level_size.__init__(self, level, size) + if not isinstance(regular, tuple): + #This should not happen if called from RegularPartitionTuples + regular = (regular,)*level if len(regular) != level: raise ValueError('regular must be a list with length'\ ' {}'.format(level)) if any (i not in NN for i in regular): raise ValueError('regular must be a list of non-negative integers') + PartitionTuples_level_size.__init__(self, level, size) self._ell = regular def _repr_(self): @@ -3102,15 +2975,21 @@ def _repr_(self): EXAMPLES:: - sage: RPT = PartitionTuples(level=3, size=7, regular=(2,1,4)) - sage: RPT + sage: PartitionTuples(level=3, size=7, regular=(2,1,4)) (2, 1, 4)-Regular partition tuples of level 3 and size 7 + sage: PartitionTuples(4,2,3) + 3-Regular partition tuples of level 4 and size 2 + sage: PartitionTuples(size=2,level=4,regular=3) + 3-Regular partition tuples of level 4 and size 2 """ - return '{}-Regular partition tuples of level {} and size {}'.\ - format(self._ell, self._level, self._size) + if self._ell[1:] == self._ell[:-1]: + return '{}-Regular partition tuples of level {} and size {}'.format( + self._ell[0], self._level, self._size) + return '{}-Regular partition tuples of level {} and size {}'.format( + self._ell, self._level, self._size) def __contains__(self, mu): - """ + r""" Return ``True`` if `\mu` is in ``self``. TESTS:: @@ -3126,31 +3005,48 @@ def __contains__(self, mu): False sage: [[3,3],[],[1]] in RPT False + sage: RPT = PartitionTuples(4,3,2) + sage: [[], [], [2], [1]] in RPT + True + sage: [[1], [1], [], [1]] in RPT + True + sage: [[1,1,1], [], [], []] in RPT + False + sage: RPT = PartitionTuples(9, 3, 2) + sage: [4, 3, 2] in RPT + False """ if mu not in PartitionTuples_level_size(self._level, self._size): return False - if mu not in VectorRegularPartitionTuples_level(self._level, self._ell): + if mu not in RegularPartitionTuples_level(self._level, self._ell): return False return True def __iter__(self): - """ - Iterate through the finite class of vector `\ell`-regular - partition tuples of a fixed level and a fixed size. + r""" + Iterate through the finite class of `\ell`-regular partition tuples + of a fixed level and a fixed size. EXAMPLES:: - sage: list(PartitionTuples(level=3,size=3,regular=(2,1,2))) + sage: list(PartitionTuples(3,3,2)) [([3], [], []), ([2, 1], [], []), + ([2], [1], []), ([2], [], [1]), + ([1], [2], []), + ([1], [1], [1]), ([1], [], [2]), + ([], [3], []), + ([], [2, 1], []), + ([], [2], [1]), + ([], [1], [2]), ([], [], [3]), ([], [], [2, 1])] """ for iv in IntegerVectors(self._size, self._level): - p = [RegularPartitions_n(iv[j], self._ell[j]) if self._ell[j] else - Partitions_n(iv[j]) for j in range(self._level)] + p = [RegularPartitions_n(v, ell) if ell > 0 else Partitions_n(v)\ + for v,ell in zip(iv,self._ell)] for cp in itertools.product(*[p[i] for i in range(self._level)]): yield self._element_constructor_(cp) @@ -3163,8 +3059,11 @@ def _an_element_(self): sage: PartitionTuples(level=4, size=4, regular=3).an_element() ([1], [], [], [3]) """ - if self._size == 0: - return self.element_class(self,([],)*self._level) - return PartitionTuples(level=self._level, size=self._size, - regular=self._ell)[1] - + mu = [[] for l in range(self._level)] + if self._size > 0: + if self._level == 1: + mu = [[self._size-1,1]] + else: + mu[0] = [1] + mu[-1] = [self._size-1] + return self.element_class(self, mu) From 192b9ec0727fea27f9c0c41e30d6a78cdb67c332 Mon Sep 17 00:00:00 2001 From: Reimundo Heluani Date: Mon, 29 Jun 2020 15:02:00 -0300 Subject: [PATCH 172/204] rebase #30007 --- src/sage/combinat/partition_tuple.py | 173 ++++++++++++++------------- 1 file changed, 93 insertions(+), 80 deletions(-) diff --git a/src/sage/combinat/partition_tuple.py b/src/sage/combinat/partition_tuple.py index 425e3b820e0..9d6c757816e 100644 --- a/src/sage/combinat/partition_tuple.py +++ b/src/sage/combinat/partition_tuple.py @@ -244,7 +244,7 @@ class of modules for the algebras, which are generalisations of the Specht """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2012 Andrew Mathas # # This program is free software: you can redistribute it and/or modify @@ -252,11 +252,9 @@ class of modules for the algebras, which are generalisations of the Specht # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # https://www.gnu.org/licenses/ -#***************************************************************************** +# **************************************************************************** from __future__ import print_function, absolute_import -from six.moves import range - import itertools from .combinat import CombinatorialElement @@ -266,7 +264,7 @@ class of modules for the algebras, which are generalisations of the Specht from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.groups.perm_gps.permgroup import PermutationGroup -from sage.interfaces.all import gp +from sage.libs.pari.all import pari from sage.misc.cachefunc import cached_method from sage.rings.all import NN, ZZ, IntegerModRing from sage.rings.integer import Integer @@ -274,10 +272,11 @@ class of modules for the algebras, which are generalisations of the Specht from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation - -#-------------------------------------------------- +# ------------------------------------------------- # Partition tuple - element class -#-------------------------------------------------- +# ------------------------------------------------- + + class PartitionTuple(CombinatorialElement): r""" A tuple of :class:`Partition`. @@ -556,7 +555,7 @@ def _repr_list(self): sage: PartitionTuple(([2,1],[3,2],[1,1,1]))._repr_list() '([2, 1], [3, 2], [1, 1, 1])' """ - return '('+', '.join(nu._repr_() for nu in self)+')' + return '(' + ', '.join(nu._repr_() for nu in self) + ')' def _repr_exp_low(self): """ @@ -615,12 +614,11 @@ def _repr_compact_high(self): return '%s' % '|'.join(mu._repr_compact_high() for mu in self) # override default string representation which is str(self._list) - __str__=lambda self: self._repr_() - + __str__ = lambda self: self._repr_() def _latex_(self): r""" - Returns a LaTeX version of ``self``. + Return a LaTeX version of ``self``. For more on the latex options, see :meth:`Partitions.option`. @@ -688,7 +686,8 @@ def _latex_young_diagram(self): sage: mu = PartitionTuple([[2, 1],[1,1,1]])._latex_young_diagram() """ from sage.combinat.output import tex_from_array_tuple - return tex_from_array_tuple([ [["\\phantom{x}"]*row for row in mu] for mu in self._list ]) + return tex_from_array_tuple([[["\\phantom{x}"] * row for row in mu] + for mu in self._list]) def _latex_diagram(self): """ @@ -700,7 +699,8 @@ def _latex_diagram(self): """ entry = self.parent().options("latex_diagram_str") from sage.combinat.output import tex_from_array_tuple - return tex_from_array_tuple([ [[entry]*row for row in mu] for mu in self._list ], with_lines=False) + return tex_from_array_tuple([[[entry] * row for row in mu] + for mu in self._list], with_lines=False) def _latex_list(self): """ @@ -720,8 +720,10 @@ def _latex_exp_low(self): sage: mu = PartitionTuple([[2, 1],[1,1,1,1,1,1,1,1,1,1]])._latex_exp_low() """ - return '(%s)' % '|'.join(','.join('%s%s'%(a+1,'' if e==1 else '^{%s}'%e) - for (a,e) in enumerate(mu)) for mu in self.to_exp()) + txt = '|'.join(','.join('%s%s' % (a + 1, '' if e == 1 else '^{%s}' % e) + for a, e in enumerate(mu)) + for mu in self.to_exp()) + return '(' + txt + ')' def _latex_exp_high(self): """ @@ -731,9 +733,10 @@ def _latex_exp_high(self): sage: mu = PartitionTuple([[2, 1],[1,1,1,1,1,1,1,1,1,1]])._latex_exp_high() """ - return '(%s)' % '|'.join(','.join(['%s%s'%(a+1,'' if e==1 else '^{%s}'%e) - for (a,e) in enumerate(mu)][::-1]) for mu in self.to_exp()) - + txt = '|'.join(','.join(['%s%s' % (a + 1, '' if e == 1 else '^{%s}' % e) + for a, e in enumerate(mu)][::-1]) + for mu in self.to_exp()) + return '(' + txt + ')' def components(self): r""" @@ -760,8 +763,7 @@ def components(self): *** ** """ - return [ t for t in self ] - + return [t for t in self] def diagram(self): r""" @@ -786,7 +788,7 @@ def diagram(self): *** ** - * sage: PartitionTuples.options._reset() """ - col_len = [len(mu)>0 and mu[0] or 1 for mu in self] # columns per component + col_len = [mu and mu[0] or 1 for mu in self] # columns per component row_max = max(len(mu) for mu in self) # maximum row length # There should be a fancier list compression for this but I couldn't get # one to work in the cases where a component was the empty partition @@ -809,7 +811,6 @@ def diagram(self): ferrers_diagram = diagram - def pp(self): r""" Pretty prints this partition tuple. See :meth:`diagram`. @@ -824,7 +825,6 @@ def pp(self): """ print(self.diagram()) - def size(self): """ Return the size of a partition tuple. @@ -945,7 +945,7 @@ def cells(self): def content(self, k,r,c, multicharge): r""" - Returns the content of the cell. + Return the content of the cell. Let `m_k =` ``multicharge[k]``, then the content of a cell is `m_k + c - r`. @@ -1004,8 +1004,10 @@ def content_tableau(self,multicharge): 2 """ from sage.combinat.tableau_tuple import TableauTuple - return TableauTuple([[[multicharge[k]-r+c for c in range(self[k][r])] - for r in range(len(self[k]))] for k in range(len(self))]) + return TableauTuple([[[multicharge[k] - r + c + for c in range(self[k][r])] + for r in range(len(self[k]))] + for k in range(len(self))]) def conjugate(self): """ @@ -1018,7 +1020,6 @@ def conjugate(self): sage: PartitionTuple([[2,1],[1],[1,1,1]]).conjugate() ([3], [1], [2, 1]) - """ return PartitionTuple([nu.conjugate() for nu in self[::-1]]) @@ -1056,22 +1057,25 @@ def dominates(self, mu): except ValueError: raise ValueError('%s must be a PartitionTuple' % mu) - if mu==self: return True - level=0 - ssum=0 # sum of successive rows in self - musum=0 # sum of successive rows in self + if mu == self: + return True + level = 0 + ssum = 0 # sum of successive rows in self + musum = 0 # sum of successive rows in self while levelssum: return False + if musum>ssum: + return False row+=1 if rowssum: return False + if musum>ssum: + return False level+=1 return True @@ -1247,12 +1251,14 @@ def top_garnir_tableau(self,e,cell): g=self.garnir_tableau(cell) - if e==0: return # no more dominant tableau of the same residue + if e==0: + return # no more dominant tableau of the same residue a=e*int((self[comp][row]-col)/e) # number of cells in the e-bricks in row `row` b=e*int((col+1)/e) # number of cells in the e-bricks in row `row+1` - if a==0 or b==0: return self.garnir_tableau(cell) + if a==0 or b==0: + return self.garnir_tableau(cell) t=g.to_list() m=t[comp][row+1][0] # smallest number of 0-Garnir belt @@ -1327,7 +1333,7 @@ def leg_length(self, k,r,c): def contains(self, mu): r""" - Returns ``True`` if this partition tuple contains `\mu`. + Return ``True`` if this partition tuple contains `\mu`. If `\lambda=(\lambda^{(1)}, \ldots, \lambda^{(l)})` and `\mu=(\mu^{(1)}, \ldots, \mu^{(m)})` are two partition tuples then @@ -1379,7 +1385,7 @@ def to_exp(self, k=0): def removable_cells(self): """ - Returns a list of the removable cells of this partition tuple. + Return a list of the removable cells of this partition tuple. All indices are of the form ``(k, r, c)``, where ``r`` is the row-index, ``c`` is the column index and ``k`` is the component. @@ -1415,7 +1421,7 @@ def addable_cells(self): outside_corners = addable_cells # for compatibility with partitions - def add_cell(self, k,r,c): + def add_cell(self, k, r, c): r""" Return the partition tuple obtained by adding a cell in row ``r``, column ``c``, and component ``k``. @@ -1426,17 +1432,18 @@ def add_cell(self, k,r,c): sage: PartitionTuple([[1,1],[4,3],[2,1,1]]).add_cell(0,0,1) ([2, 1], [4, 3], [2, 1, 1]) - """ - if (k,r,c) in self.addable_cells(): # an addable cell - mu=self.to_list() - if c==0: mu[k].append(1) - else: mu[k][r]+=1 + if (k, r, c) in self.addable_cells(): # an addable cell + mu = self.to_list() + if c == 0: + mu[k].append(1) + else: + mu[k][r] += 1 return PartitionTuple(mu) else: - raise ValueError("%s is not an addable cell"%((k,r,c),)) + raise ValueError("%s is not an addable cell" % ((k, r, c),)) - def remove_cell(self, k,r,c): + def remove_cell(self, k, r, c): """ Return the partition tuple obtained by removing a cell in row ``r``, column ``c``, and component ``k``. @@ -1447,14 +1454,13 @@ def remove_cell(self, k,r,c): sage: PartitionTuple([[1,1],[4,3],[2,1,1]]).remove_cell(0,1,0) ([1], [4, 3], [2, 1, 1]) - """ - if (k,r,c) in self.removable_cells(): # a removable cell - mu=self.to_list() - mu[k][r]-=1 + if (k, r, c) in self.removable_cells(): # a removable cell + mu = self.to_list() + mu[k][r] -= 1 return PartitionTuple(mu) else: - raise ValueError("%s is not a removable cell"%((k,r,c),)) + raise ValueError("%s is not a removable cell" % ((k, r, c),)) def to_list(self): r""" @@ -1470,7 +1476,7 @@ def to_list(self): sage: all(mu==PartitionTuple(mu.to_list()) for mu in PartitionTuples(4,4)) True """ - return [ mu.to_list() for mu in self] + return [mu.to_list() for mu in self] def young_subgroup(self): """ @@ -1482,14 +1488,14 @@ def young_subgroup(self): sage: PartitionTuple([[2,1],[4,2],[1]]).young_subgroup() Permutation Group with generators [(), (8,9), (6,7), (5,6), (4,5), (1,2)] """ - gens=[] - m=0 + gens = [] + m = 0 for comp in self: for row in comp: - gens.extend([(c,c+1) for c in range(m+1,m+row)]) - m+=row - gens.append(list(range(1,self.size()+1))) # to ensure we get a subgroup of Sym_n - return PermutationGroup( gens ) + gens.extend([(c, c+1) for c in range(m+1, m+row)]) + m += row + gens.append(list(range(1, self.size()+1))) # to ensure we get a subgroup of Sym_n + return PermutationGroup(gens) def young_subgroup_generators(self): """ @@ -1501,12 +1507,12 @@ def young_subgroup_generators(self): sage: PartitionTuple([[2,1],[4,2],[1]]).young_subgroup_generators() [1, 4, 5, 6, 8] """ - gens=[] - m=0 + gens = [] + m = 0 for comp in self: for row in comp: - gens.extend([c for c in range(m+1,m+row)]) - m+=row + gens.extend([c for c in range(m + 1, m + row)]) + m += row return gens @cached_method @@ -1759,9 +1765,11 @@ def defect(self, e, multicharge): return (sum(beta.get(r, 0) for r in multicharge) - sum(beta[r]**2 - beta[r] * beta.get(Ie(r+1), 0) for r in beta)) -#-------------------------------------------------- +# ------------------------------------------------- # Partition tuples - parent classes -#-------------------------------------------------- +# ------------------------------------------------- + + class PartitionTuples(UniqueRepresentation, Parent): r""" Class of all partition tuples. @@ -2039,7 +2047,7 @@ def _an_element_(self): sage: PartitionTuples().an_element() ([1, 1, 1, 1], [2, 1, 1], [3, 1], [4]) """ - return PartitionTuple( ([1,1,1,1],[2,1,1],[3,1],[4]) ) + return PartitionTuple(([1, 1, 1, 1], [2, 1, 1], [3, 1], [4])) class PartitionTuples_all(PartitionTuples): @@ -2132,7 +2140,7 @@ def __init__(self, level, **kwds): Partition tuples of level 6 sage: TestSuite( PartitionTuples(level=4) ).run() """ - if not level in NN: + if level not in NN: raise ValueError('level must be a non-negative integer') category = kwds.pop('category', InfiniteEnumeratedSets()) super(PartitionTuples_level, self).__init__(category=category) @@ -2216,7 +2224,7 @@ def _an_element_(self): sage: PartitionTuples(level=4).an_element() ([], [1], [2], [3]) """ - return self.element_class(self, tuple([l] for l in range(self.level()) )) + return self.element_class(self, tuple([l] for l in range(self.level()))) class PartitionTuples_size(PartitionTuples): @@ -2225,7 +2233,7 @@ class PartitionTuples_size(PartitionTuples): """ def __init__(self, size): r""" - Initializes this class. + Initialize this class. EXAMPLES:: @@ -2236,7 +2244,7 @@ def __init__(self, size): sage: TestSuite( PartitionTuples(size=6) ).run() """ - if not size in NN: + if size not in NN: raise ValueError('size must be a non-negative integer') super(PartitionTuples_size, self).__init__(category=InfiniteEnumeratedSets()) self._size=size @@ -2321,7 +2329,7 @@ def _an_element_(self): sage: PartitionTuples(size=4).an_element() ([1], [1], [1], [1]) """ - return self.element_class(self, tuple([1] for l in range(self._size) )) + return self.element_class(self, tuple([1] for l in range(self._size))) class PartitionTuples_level_size(PartitionTuples): @@ -2414,7 +2422,6 @@ def __iter__(self): for cp in itertools.product(*[p[i] for i in iv]): yield self._element_constructor_(cp) - def _an_element_(self): """ Return a generic element. @@ -2424,9 +2431,10 @@ def _an_element_(self): sage: PartitionTuples(level=4,size=4).an_element() ([1], [], [], [3]) """ - mu=[[] for l in range(self._level)] + mu = [[] for l in range(self._level)] if self._size > 0: - if self._level == 1: mu=[self._size-1,1] + if self._level == 1: + mu=[self._size-1,1] else: mu[0]=[1] mu[-1]=[self._size-1] @@ -2434,9 +2442,9 @@ def _an_element_(self): def cardinality(self): r""" - Returns the number of ``level``-tuples of partitions of size ``n``. + Return the number of ``level``-tuples of partitions of size ``n``. - Wraps a pari function call. + Wraps a pari function call using :pari:`eta`. EXAMPLES:: @@ -2461,7 +2469,8 @@ def cardinality(self): These answers were checked against Gap4 (the last of which takes an awful long time for gap to compute). """ - return ZZ(gp.eval('polcoeff((1/eta(x+O(x^%s)))^%s, %s, x)'%(self.size()+1,self.level(), self.size()))) + eta = pari(f'Ser(x,x,{self.size()})').eta() + return ZZ((1 / eta**self.level()).polcoef(self.size(), pari('x'))) def __setstate__(self, state): r""" @@ -2484,7 +2493,8 @@ def __setstate__(self, state): super(PartitionTuples, self).__setstate__(state) ############################################################################### -## Regular partition tuples +# Regular partition tuples + class RegularPartitionTuples(PartitionTuples): r""" @@ -2560,6 +2570,7 @@ def _an_element_(self): elt = RegularPartitionTuples_level_size(lvl, size, self._ell).an_element() return self.element_class(self, list(elt)) + class RegularPartitionTuples_all(RegularPartitionTuples): r""" Class of `\ell`-regular partition tuples. @@ -2819,6 +2830,7 @@ def __iter__(self): for mu in RegularPartitionTuples_level_size(self._level, size, self._ell): yield self.element_class(self, list(mu)) + class RegularPartitionTuples_size(RegularPartitionTuples): r""" Class of `\ell`-regular partition tuples with a fixed size. @@ -2876,9 +2888,10 @@ def __contains__(self, mu): sage: [4, 3, 2] in RPT True """ - return ( (mu in RegularPartitions_all(self._ell) and self._size == sum(mu)) - or (RegularPartitionTuples.__contains__(self, mu) and self._size == sum(map(sum,mu))) - ) + return ((mu in RegularPartitions_all(self._ell) + and self._size == sum(mu)) + or (RegularPartitionTuples.__contains__(self, mu) + and self._size == sum(map(sum, mu)))) def __iter__(self): r""" From 500f76786c1deb56199f568ff3c82ee33cdb1ce9 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Mon, 29 Jun 2020 11:48:55 -0700 Subject: [PATCH 173/204] new method to_field, rewriting s_ij --- src/sage/combinat/root_system/fusion_ring.py | 39 ++++++++++++------- .../combinat/root_system/weyl_characters.py | 21 +++++++--- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index ed76e05bbee..c33a7173723 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -201,7 +201,7 @@ class FusionRing(WeylCharacterRing): """ @staticmethod - def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False): + def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False, cyclotomic_order=None): """ Normalize input to ensure a unique representation. @@ -241,7 +241,7 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug sage: TestSuite(E81).run() """ return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, - prefix=prefix, style=style, k=k, conjugate=conjugate) + prefix=prefix, style=style, k=k, conjugate=conjugate, cyclotomic_order=cyclotomic_order) def _test_verlinde(self, **options): """ @@ -274,7 +274,22 @@ def field(self): sage: FusionRing("B2",2).field() Cyclotomic Field of order 40 and degree 16 """ - return CyclotomicField(4 * self._fg * self._l) + return CyclotomicField(4 * self._cyclotomic_order) + + def to_field(self, r): + """ + Returns `e^{i\pi r}` as an element of ``self.field()`` if possible. + + INPUT: + + = ``i`` -- a rational number + """ + + n = 2 * r * self._cyclotomic_order + if n in ZZ: + return self.field().gen()**n + else: + return None def get_order(self): r""" @@ -462,14 +477,10 @@ def s_ij(self, elt_i, elt_j): sage: [G21.s_ij(x, y) for x in b for y in b] [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] """ - l = self.fusion_l() * self._fg - K = self.field() - q = K.gen() - ijtwist = -2 * l * (elt_i.twist() + elt_j.twist()) mc = (elt_i.dual() * elt_j).monomial_coefficients(copy=False) - B = self.basis() - return sum(B[k].q_dimension() * self.Nk_ij(elt_i, B[k], elt_j) * q**(2*l*B[k].twist() + ijtwist) - for k in mc) + b = self.basis() + ijtwist = elt_i.twist() + elt_j.twist() + return sum(k.q_dimension() * self.Nk_ij(elt_i, k, elt_j) * self.to_field(k.twist() - ijtwist) for k in self.basis()) def s_matrix(self): r""" @@ -486,10 +497,10 @@ def s_matrix(self): sage: D91 = FusionRing('D9', 1) sage: D91.s_matrix() - [ 1 1 1 1] - [ 1 1 -1 -1] - [ 1 -1 -zeta68^17 zeta68^17] - [ 1 -1 zeta68^17 -zeta68^17] + [ 1 1 1 1] + [ 1 1 -1 -1] + [ 1 -1 -zeta136^34 zeta136^34] + [ 1 -1 zeta136^34 -zeta136^34] sage: D41 = FusionRing('D4', 1) sage: D41.s_matrix() diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index f145c003176..63f906802f2 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -90,7 +90,7 @@ class WeylCharacterRing(CombinatorialFreeModule): https://doc.sagemath.org/html/en/thematic_tutorials/lie.html """ @staticmethod - def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False): + def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False, cyclotomic_order=None): """ TESTS:: @@ -106,9 +106,9 @@ def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, c prefix = ct[0]+str(ct[1]) else: prefix = repr(ct) - return super(WeylCharacterRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate) + return super(WeylCharacterRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate, cyclotomic_order=cyclotomic_order) - def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False): + def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False, cyclotomic_order=None): """ EXAMPLES:: @@ -173,15 +173,24 @@ def next_level(wt): self._nf = 2 else: self._nf = 1 - h_check = ct.dual_coxeter_number() - self._l = self._m_g * (self._k + h_check) - self._conj = (-1) ** conjugate + self._h_check = ct.dual_coxeter_number() + self._l = self._m_g * (self._k + self._h_check) + if conjugate: + self._conj = -1 + else: + self._conj = 1 if ct[0] == 'A': self._fg = ct[1] + 1 elif ct[0] == 'E' and ct[1] == 6: self._fg = 3 + elif ct[0] == 'D': + self._fg = 2 else: self._fg = 1 + if cyclotomic_order is None: + self._cyclotomic_order = self._fg * self._l + else: + self._cyclotomic_order = cyclotomic_order @cached_method def ambient(self): From b6ac057c9b58f57e00cd089ee9082325506c79d2 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Mon, 29 Jun 2020 20:56:11 +0200 Subject: [PATCH 174/204] changed function to use GAP --- src/sage/combinat/designs/database.py | 118 +++++++++++++++----------- 1 file changed, 68 insertions(+), 50 deletions(-) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 30d6b3b5ed2..d6a7b269f04 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -4593,57 +4593,75 @@ def BIBD_79_13_2(): sage: D.is_t_design(t=2, v=79, k=13, l=2) True """ + from sage.libs.gap.libgap import libgap from .incidence_structures import IncidenceStructure - Gelems = [(x,y,z) for x in range(11) for y in range(5) for z in range(2)] - - def Gop(a,b,c,x,y,z): - # First normalise the input - a = a%11 - b = b%5 - c = c%2 - x = x%11 - y = y%5 - z = z%2 - # We know: x y = y x^4 - # y x = x^3 y - # z x = x^10 z - # x z = z x^-1 - # z y = y z - return ((a + (3**b) * (10**c) * x)%11, (b+y)%5, (z+c)%2) - - # P(i,a,b,c) represents P_i x^a*y^b*z^c - # i.e. we compute the action of x^a*y^b*z^c on P_i - def P(i,x,y,z): - x = x%11 - y = y%5 - z = z%2 - if i == 1: return (1, (0, 0, z)) - if i == 2: return (2, (((4**y)*(1-2*z)*x)%11, 0, 0)) - if i == 3: return (3, (((4**y)*(1-2*z)*x)%11, 0, 0)) - if i == 4: return (4, (((1-2*z)*x)%11, y, 0)) - - points = {P(i,x,y,z) for i in range(1,5) for x in range(11) for y in range(5) for z in range(2)} - Gaction = {(i,(a,b,c)): {(x,y,z): P(i,*Gop(a,b,c,x,y,z)) for x,y,z in Gelems} for a,b,c in Gelems for i in [1,2,3,4]} - - B1 = [P(1,0,0,0), P(1,0,0,1)] + [P(2,x,0,0) for x in range(11)] - B2 = [P(1,0,0,0), P(1,0,0,1)] + [P(3,x,0,0) for x in range(11)] - B3 = [P(1,0,0,0), P(2,0,0,0), P(3,0,0,0)] + [P(4,1,y,0) for y in range(5)] + [P(4,4,y,0) for y in range(5)] - B4 = [P(2,2,0,0), P(2,-2,0,0), P(3,5,0,0), P(3,-5,0,0), P(4,0,0,0), P(4,1,2,0), P(4,-1,2,0), P(4,1,1,0), P(4,-1,1,0), P(4,5,1,0), P(4,-5,1,0), P(4,5,4,0), P(4,-5,4,0)] - - B3Orbit = set() - for g in Gelems: - B3g = frozenset([Gaction[p][g] for p in B3]) - B3Orbit.add(B3g) - - B4Orbit = set() - for g in Gelems: - B4g = frozenset([Gaction[p][g] for p in B4]) - B4Orbit.add(B4g) - - blocks = [B1,B2] + list(B3Orbit) + list(B4Orbit) - - D = IncidenceStructure(blocks) - return D._blocks + + g11 = libgap.Z(11) # generator for GF(11) + one = g11**0 + zero = 0*g11 + + X = libgap([[one, one], [zero, one]]) + Y = libgap([[5*one, zero], [zero, 9*one]]) + Z = libgap([[-one, zero], [zero, one]]) + I = libgap([[one, zero], [zero, one]]) + + G = libgap.Group(X, Y, Z) + H1 = libgap.Group(X, Y) + H23 = libgap.Group(Y, Z) + H4 = libgap.Group(Z) + + P1Action = G.FactorCosetAction(H1) + P23Action = G.FactorCosetAction(H23) + P4Action = G.FactorCosetAction(H4) + + libgap.set_global("p1Act", P1Action) + libgap.set_global("p23Act", P23Action) + libgap.set_global("p4Act", P4Action) + + get_action = libgap.eval("""ChooseMyAction := function(i) + if i = 1 then + return p1Act; + elif i = 4 then + return p4Act; + else + return p23Act; + fi; + end;""") + + action = libgap.eval("""MyAction := function(pair, g) + local i, C, hom; + i := pair[1]; + C := pair[2]; + hom := ChooseMyAction(i); + return [i, C^(ImageElm(hom,g))]; + end;""") + + p1 = (1,1) + p2 = (2,1) + p3 = (3,1) + p4 = (4,1) + + B1 = libgap.Set([p1, action(p1, Z)] + [action(p2, X**i) for i in range(11)]) + B2 = libgap.Set([p1, action(p1, Z)] + [action(p3, X**i) for i in range(11)]) + B3 = libgap.Set([p1, p2, p3] + [action(p4, X * Y**i) for i in range(5)] + [action(p4, X**4 * Y**i) for i in range(5)]) + B4 = libgap.Set([action(p2, X**2), action(p2, X**-2), action(p3, X**5), action(p3, X**-5), p4, + action(p4, X * Y**2), action(p4, X**-1 * Y**2), action(p4, X*Y), action(p4, X**-1 * Y), + action(p4, X**5 * Y), action(p4, X**-5 * Y), action(p4, X**5 * Y**4), action(p4, X**-5 * Y**4)]) + + actionOnSet = libgap.eval("""MyActionOnSets := function(block, g) + local set, p, q; + set := Set([]); + for p in block do + q := MyAction(p, g); + AddSet(set, q); + od; + return set; + end;""") + + B3Orbit = libgap.Orbit(G, B3, actionOnSet) + B4Orbit = libgap.Orbit(G, B4, actionOnSet) + blocks = [B1, B2] + list(B3Orbit) + list(B4Orbit) + return blocks def BIBD_56_11_2(): r""" From 6640ce657a939971ea2f9b9e86037d96f89c2509 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Mon, 29 Jun 2020 21:48:18 -0700 Subject: [PATCH 175/204] D_plus and D_minus --- src/sage/combinat/root_system/fusion_ring.py | 161 ++++++++++-------- .../combinat/root_system/weyl_characters.py | 4 +- 2 files changed, 94 insertions(+), 71 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index c33a7173723..e74337d3b6c 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -261,6 +261,75 @@ def _test_verlinde(self, **options): v = sum(self.s_ij(x,w) * self.s_ij(y,w) * self.s_ij(z,w) / self.s_ij(i0,w) for w in B) tester.assertEqual(v, c * self.N_ijk(x,y,z)) + def fusion_labels(self, labels=None, inject_variables=False): + r""" + Set the labels of the basis. + + INPUT: + + - ``labels`` -- (default: ``None``) a list of strings or string + - ``inject_variables`` -- (default: ``False``) if ``True``, then + inject the variable names into the global namespace; note that + this could override objects already defined + + If ``labels`` is a list, the length of the list must equal the + number of basis elements. These become the names of + the basis elements. + + If ``labels`` is a string, this is treated as a prefix and a + list of names is generated. + + If ``labels`` is ``None``, then this resets the labels to the default. + + EXAMPLES:: + + sage: A13 = FusionRing("A1", 3) + sage: A13.fusion_labels("x") + sage: fb = list(A13.basis()); fb + [x0, x1, x2, x3] + sage: Matrix([[x*y for y in A13.basis()] for x in A13.basis()]) + [ x0 x1 x2 x3] + [ x1 x0 + x2 x1 + x3 x2] + [ x2 x1 + x3 x0 + x2 x1] + [ x3 x2 x1 x0] + + We give an example where the variables are injected into the + global namepsace:: + + sage: A13.fusion_labels("y", inject_variables=True) + sage: y0 + y0 + sage: y0.parent() is A13 + True + + We reset the labels to the default:: + + sage: A13.fusion_labels() + sage: fb + [A13(0), A13(1), A13(2), A13(3)] + sage: y0 + A13(0) + """ + if labels is None: + # Remove the fusion labels + self._fusion_labels = None + return + + B = self.basis() + if isinstance(labels, str): + labels = [labels + str(k) for k in range(len(B))] + elif len(labels) != len(B): + raise ValueError('invalid data') + + d = {} + ac = self.simple_coroots() + for j, b in enumerate(self.get_order()): + t = tuple([b.inner_product(x) for x in ac]) + d[t] = labels[j] + if inject_variables: + inject_variable(labels[j], B[b]) + self._fusion_labels = d + def field(self): r""" Return a cyclotomic field large enough to @@ -383,7 +452,14 @@ def twists_matrix(self): [ 0 0 0 0 0 6/5] """ B = self.basis() - return diagonal_matrix(B[x].twist() for x in self.get_order()) + return diagonal_matrix(self.to_field(B[x].twist()) for x in self.get_order()) + + def cc_matrix(self): + r""" + Return the conjugation matrix, the permutation matrix + for the conjugation operation on basis elements. + """ + return matrix([[i==j.dual() for i in self.get_order()] for j in self.get_order()]) def q_dims(self): r""" @@ -512,75 +588,6 @@ def s_matrix(self): b = self.basis() return matrix([[self.s_ij(b[x], b[y]) for x in self.get_order()] for y in self.get_order()]) - def fusion_labels(self, labels=None, inject_variables=False): - r""" - Set the labels of the basis. - - INPUT: - - - ``labels`` -- (default: ``None``) a list of strings or string - - ``inject_variables`` -- (default: ``False``) if ``True``, then - inject the variable names into the global namespace; note that - this could override objects already defined - - If ``labels`` is a list, the length of the list must equal the - number of basis elements. These become the names of - the basis elements. - - If ``labels`` is a string, this is treated as a prefix and a - list of names is generated. - - If ``labels`` is ``None``, then this resets the labels to the default. - - EXAMPLES:: - - sage: A13 = FusionRing("A1", 3) - sage: A13.fusion_labels("x") - sage: fb = list(A13.basis()); fb - [x0, x1, x2, x3] - sage: Matrix([[x*y for y in A13.basis()] for x in A13.basis()]) - [ x0 x1 x2 x3] - [ x1 x0 + x2 x1 + x3 x2] - [ x2 x1 + x3 x0 + x2 x1] - [ x3 x2 x1 x0] - - We give an example where the variables are injected into the - global namepsace:: - - sage: A13.fusion_labels("y", inject_variables=True) - sage: y0 - y0 - sage: y0.parent() is A13 - True - - We reset the labels to the default:: - - sage: A13.fusion_labels() - sage: fb - [A13(0), A13(1), A13(2), A13(3)] - sage: y0 - A13(0) - """ - if labels is None: - # Remove the fusion labels - self._fusion_labels = None - return - - B = self.basis() - if isinstance(labels, str): - labels = [labels + str(k) for k in range(len(B))] - elif len(labels) != len(B): - raise ValueError('invalid data') - - d = {} - ac = self.simple_coroots() - for j, b in enumerate(self.get_order()): - t = tuple([b.inner_product(x) for x in ac]) - d[t] = labels[j] - if inject_variables: - inject_variable(labels[j], B[b]) - self._fusion_labels = d - def global_q_dimension(self): r""" Return `\sum d_i^2`, where the sum is over all simple objects @@ -593,6 +600,20 @@ def global_q_dimension(self): """ return sum(x.q_dimension()**2 for x in self.basis()) + def D_plus(self): + r""" + Return `\sum d_i^2\theta_i` where `i` runs through the simple objects and + `theta_i` is the twist. + """ + return sum((x.q_dimension())**2*self.to_field(x.twist()) for x in self.basis()) + + def D_minus(self): + r""" + Return `\sum d_i^2\theta_i^{-1}` where `i` runs through the simple objects and + `theta_i` is the twist. + """ + return sum((x.q_dimension())**2*self.to_field(-x.twist()) for x in self.basis()) + class Element(WeylCharacterRing.Element): """ A class for FusionRing elements. diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 63f906802f2..b0e66a18b8a 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -161,8 +161,10 @@ def next_level(wt): self.register_conversion(self.retract) # Record properties of the FusionRing + # mg = square of long to short root lengths + # nf = normalizing factor for the inner product + # fg = order of the fundamental group (except for Type B) if k is not None: - #TODO: implement conjugate functionality if ct[0] in ['A', 'D', 'E']: self._m_g = 1 elif ct[0] in ['B', 'C', 'F']: From f9c6e5354f0153c0a27db95d171ea7db1afee36e Mon Sep 17 00:00:00 2001 From: vipul79321 Date: Tue, 30 Jun 2020 11:38:20 +0530 Subject: [PATCH 176/204] minor edit in documentation --- src/sage/graphs/graph.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index f5ee36b9a55..0ff9f5fb8fa 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -5232,12 +5232,12 @@ def eccentricity(self, v=None, by_weight=False, algorithm=None, - ``'Floyd-Warshall-Cython'`` - a Cython implementation of the Floyd-Warshall algorithm. Works only if ``by_weight==False`` and - ``v is None`` or ``v==list(self)``. + ``v is None`` or ``v`` should contain all vertices of ``self``. - ``'Floyd-Warshall-Python'`` - a Python implementation of the Floyd-Warshall algorithm. Works also with weighted graphs, even with negative weights (but no negative cycle is allowed). However, ``v`` - must be ``None`` or ``v==list(self)``. + must be ``None`` or ``v`` should contain all vertices of ``self``. - ``'Dijkstra_NetworkX'`` - the Dijkstra algorithm, implemented in NetworkX. It works with weighted graphs, but no negative weight is @@ -5248,7 +5248,8 @@ def eccentricity(self, v=None, by_weight=False, algorithm=None, - ``'Johnson_Boost'`` - the Johnson algorithm, implemented in Boost (works also with negative weights, if there is no negative - cycle). Works only if ``v is None`` or ``v == list(self)`` . + cycle). Works only if ``v is None`` or ``v`` should contain all + vertices of ``self``. - ``'From_Dictionary'`` - uses the (already computed) distances, that are provided by input variable ``dist_dict``. From 9fd36fd32c9addbf3fdfdeb84999f748d8a2be50 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 30 Jun 2020 10:29:29 +0200 Subject: [PATCH 177/204] used function_factory; rewrote initial blocks; clear GAP global var --- src/sage/combinat/designs/database.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index d6a7b269f04..74407454d08 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -4618,7 +4618,7 @@ def BIBD_79_13_2(): libgap.set_global("p23Act", P23Action) libgap.set_global("p4Act", P4Action) - get_action = libgap.eval("""ChooseMyAction := function(i) + get_action = libgap.function_factory("""ChooseMyAction := function(i) if i = 1 then return p1Act; elif i = 4 then @@ -4628,7 +4628,7 @@ def BIBD_79_13_2(): fi; end;""") - action = libgap.eval("""MyAction := function(pair, g) + action = libgap.function_factory("""MyAction := function(pair, g) local i, C, hom; i := pair[1]; C := pair[2]; @@ -4641,14 +4641,18 @@ def BIBD_79_13_2(): p3 = (3,1) p4 = (4,1) - B1 = libgap.Set([p1, action(p1, Z)] + [action(p2, X**i) for i in range(11)]) - B2 = libgap.Set([p1, action(p1, Z)] + [action(p3, X**i) for i in range(11)]) - B3 = libgap.Set([p1, p2, p3] + [action(p4, X * Y**i) for i in range(5)] + [action(p4, X**4 * Y**i) for i in range(5)]) + B1 = list(libgap.Orbit(H4, p1, action)) + list(libgap.Orbit(G, p2, action)) + B2 = list(libgap.Orbit(H4, p1, action)) + list(libgap.Orbit(G, p3, action)) + B3 = list(libgap([p1, p2, p3])) + list(libgap.Orbit(libgap.Group(Y), action(p4, X), action)) + list(libgap.Orbit(libgap.Group(Y), action(p4, X**4), action)) + B1 = libgap.Set(B1) + B2 = libgap.Set(B2) + B3 = libgap.Set(B3) + g B4 = libgap.Set([action(p2, X**2), action(p2, X**-2), action(p3, X**5), action(p3, X**-5), p4, action(p4, X * Y**2), action(p4, X**-1 * Y**2), action(p4, X*Y), action(p4, X**-1 * Y), action(p4, X**5 * Y), action(p4, X**-5 * Y), action(p4, X**5 * Y**4), action(p4, X**-5 * Y**4)]) - actionOnSet = libgap.eval("""MyActionOnSets := function(block, g) + actionOnSet = libgap.function_factory("""MyActionOnSets := function(block, g) local set, p, q; set := Set([]); for p in block do @@ -4661,6 +4665,14 @@ def BIBD_79_13_2(): B3Orbit = libgap.Orbit(G, B3, actionOnSet) B4Orbit = libgap.Orbit(G, B4, actionOnSet) blocks = [B1, B2] + list(B3Orbit) + list(B4Orbit) + + #clear gap variables + libgap.unset_global("p1Act") + libgap.unset_global("p23Act") + libgap.unset_global("p4Act") + libgap.unset_global("ChooseMyAction") + libgap.unset_global("MyAction") + libgap.unset_global("MyActionOnSets") return blocks def BIBD_56_11_2(): From 6c60867bffe003146cbb4560e7d767df83c32625 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 30 Jun 2020 10:30:15 +0200 Subject: [PATCH 178/204] removed floating g --- src/sage/combinat/designs/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 74407454d08..0fb34910a5f 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -4647,7 +4647,7 @@ def BIBD_79_13_2(): B1 = libgap.Set(B1) B2 = libgap.Set(B2) B3 = libgap.Set(B3) - g + B4 = libgap.Set([action(p2, X**2), action(p2, X**-2), action(p3, X**5), action(p3, X**-5), p4, action(p4, X * Y**2), action(p4, X**-1 * Y**2), action(p4, X*Y), action(p4, X**-1 * Y), action(p4, X**5 * Y), action(p4, X**-5 * Y), action(p4, X**5 * Y**4), action(p4, X**-5 * Y**4)]) From 3b15980fcbabd983d3a7718a2789137172dc0ecc Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 30 Jun 2020 13:21:37 +0200 Subject: [PATCH 179/204] pyflakes errors --- src/sage/combinat/designs/database.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 0fb34910a5f..6e0b6f269d6 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -59,9 +59,7 @@ from __future__ import print_function, absolute_import from sage.combinat.designs.orthogonal_arrays import (OA_from_quasi_difference_matrix, - OA_from_Vmt, QDM_from_Vmt, - OA_from_wider_OA, OA_from_PBD, OA_n_times_2_pow_c_from_matrix, orthogonal_array) @@ -781,7 +779,6 @@ def OA_9_120(): sage: designs.orthogonal_arrays.is_available(9,120) True """ - from .incidence_structures import IncidenceStructure RBIBD_120 = RBIBD_120_8_1() equiv = [RBIBD_120[i*15:(i+1)*15] for i in range(17)] @@ -3161,7 +3158,6 @@ def DM_12_6_1(): :doi:`10.1016/0012-365X(75)90040-0`, Discrete Mathematics, Volume 11, Issue 3, 1975, Pages 255-369. """ - from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup from sage.rings.finite_rings.integer_mod_ring import IntegerModRing as AdditiveCyclic G = AdditiveCyclic(2).cartesian_product(AdditiveCyclic(6)) M = [[(0,0),(0,0),(0,0),(0,0),(0,0),(0,0)], @@ -3699,7 +3695,6 @@ def DM_52_6_1(): sage: _ = designs.difference_matrix(52,6) """ - from sage.rings.finite_rings.integer_mod_ring import IntegerModRing as AdditiveCyclic from sage.rings.finite_rings.finite_field_constructor import FiniteField F4 = FiniteField(4,'z') G13 = FiniteField(13) @@ -3731,7 +3726,6 @@ def DM_52_6_1(): Mb=[[(0,0)]*6] from itertools import product - p = lambda x,y : G(tuple([x*yy for yy in G(y)])) def t1(i,R): if i > 1: @@ -4594,7 +4588,6 @@ def BIBD_79_13_2(): True """ from sage.libs.gap.libgap import libgap - from .incidence_structures import IncidenceStructure g11 = libgap.Z(11) # generator for GF(11) one = g11**0 @@ -4603,7 +4596,6 @@ def BIBD_79_13_2(): X = libgap([[one, one], [zero, one]]) Y = libgap([[5*one, zero], [zero, 9*one]]) Z = libgap([[-one, zero], [zero, one]]) - I = libgap([[one, zero], [zero, one]]) G = libgap.Group(X, Y, Z) H1 = libgap.Group(X, Y) @@ -4618,7 +4610,7 @@ def BIBD_79_13_2(): libgap.set_global("p23Act", P23Action) libgap.set_global("p4Act", P4Action) - get_action = libgap.function_factory("""ChooseMyAction := function(i) + libgap.function_factory("""ChooseMyAction := function(i) if i = 1 then return p1Act; elif i = 4 then @@ -4666,7 +4658,7 @@ def BIBD_79_13_2(): B4Orbit = libgap.Orbit(G, B4, actionOnSet) blocks = [B1, B2] + list(B3Orbit) + list(B4Orbit) - #clear gap variables + # clear gap variables libgap.unset_global("p1Act") libgap.unset_global("p23Act") libgap.unset_global("p4Act") From fdf88ac58c5cf4e84b3c6ec258c5abd3980bbea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 30 Jun 2020 14:17:38 +0200 Subject: [PATCH 180/204] remove some traces of __cmp__ (quickref for coding and primes doc) --- src/doc/en/developer/coding_in_python.rst | 36 ++++++++++++----------- src/sage/sets/primes.py | 15 +++------- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/src/doc/en/developer/coding_in_python.rst b/src/doc/en/developer/coding_in_python.rst index d2850268b2c..7036ac8d69d 100644 --- a/src/doc/en/developer/coding_in_python.rst +++ b/src/doc/en/developer/coding_in_python.rst @@ -310,23 +310,25 @@ The __hash__ Special Method Here is the definition of ``__hash__`` from the Python reference manual: - Called by built-in function ``hash()`` and for operations on members of - hashed collections including set, frozenset, and dict. ``__hash__()`` - should return an integer. The only required property is that objects which - compare equal have the same hash value; it is advised to somehow mix - together (e.g. using exclusive or) the hash values for the components of - the object that also play a part in comparison of objects. If a class does - not define a - ``__cmp__()`` method it should not define a - ``__hash__()`` operation either; if it defines - ``__cmp__()`` or ``__eq__()`` but not - ``__hash__()``, its instances will not be usable as - dictionary keys. If a class defines mutable objects and implements - a ``__cmp__()`` or ``__eq__()`` method, it - should not implement ``__hash__()``, since the dictionary - implementation requires that a key's hash value is immutable (if - the object's hash value changes, it will be in the wrong hash - bucket). + Called by built-in function ``hash()`` and for operations on members + of hashed collections including ``set``, ``frozenset``, and + ``dict``. ``__hash__()`` should return an integer. The only required + property is that objects which compare equal have the same hash + value; it is advised to mix together the hash values of the + components of the object that also play a part in comparison of + objects by packing them into a tuple and hashing the tuple. + + If a class does not define an ``__eq__()`` method it should not define + a ``__hash__()`` operation either; if it defines ``__eq__()`` but not + ``__hash__()``, its instances will not be usable as items in hashable + collections. If a class defines mutable objects and implements an + ``__eq__()`` method, it should not implement ``__hash__()``, since the + implementation of hashable collections requires that a key’s hash + value is immutable (if the object’s hash value changes, it will be + in the wrong hash bucket). + +See https://docs.python.org/3/reference/datamodel.html#object.__hash__ for more +information on the subject. Notice the phrase, "The only required property is that objects which compare equal have the same hash value." This is an assumption made by diff --git a/src/sage/sets/primes.py b/src/sage/sets/primes.py index 8681c615e24..0b8f99a3642 100644 --- a/src/sage/sets/primes.py +++ b/src/sage/sets/primes.py @@ -4,23 +4,16 @@ AUTHORS: - William Stein (2005): original version - - Florent Hivert (2009-11): adapted to the category framework. The following - methods were removed: - - - cardinality, __len__, __iter__: provided by EnumeratedSets - - __cmp__(self, other): __eq__ is provided by UniqueRepresentation - and seems to do as good a job (all test pass) + - Florent Hivert (2009-11): adapted to the category framework. """ -from __future__ import absolute_import -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 William Stein # 2009 Florent Hivert # # Distributed under the terms of the GNU General Public License (GPL) # -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.rings.all import ZZ from .set import Set_generic from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets From a6059a31edd9bb9255104de3ee9ab4fdaea4f226 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 30 Jun 2020 14:29:58 +0200 Subject: [PATCH 181/204] changed to use only 1 permutation action --- src/sage/combinat/designs/database.py | 53 +++++++++------------------ 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 6e0b6f269d6..ecc17faa543 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -4610,22 +4610,12 @@ def BIBD_79_13_2(): libgap.set_global("p23Act", P23Action) libgap.set_global("p4Act", P4Action) - libgap.function_factory("""ChooseMyAction := function(i) - if i = 1 then - return p1Act; - elif i = 4 then - return p4Act; - else - return p23Act; - fi; - end;""") - - action = libgap.function_factory("""MyAction := function(pair, g) - local i, C, hom; + action = libgap.function_factory("""function(pair, g) + local i, C, homs; i := pair[1]; C := pair[2]; - hom := ChooseMyAction(i); - return [i, C^(ImageElm(hom,g))]; + homs := [p1Act, p23Act, p23Act, p4Act]; + return [i, C^(ImageElm(homs[i],g))]; end;""") p1 = (1,1) @@ -4636,35 +4626,26 @@ def BIBD_79_13_2(): B1 = list(libgap.Orbit(H4, p1, action)) + list(libgap.Orbit(G, p2, action)) B2 = list(libgap.Orbit(H4, p1, action)) + list(libgap.Orbit(G, p3, action)) B3 = list(libgap([p1, p2, p3])) + list(libgap.Orbit(libgap.Group(Y), action(p4, X), action)) + list(libgap.Orbit(libgap.Group(Y), action(p4, X**4), action)) - B1 = libgap.Set(B1) - B2 = libgap.Set(B2) - B3 = libgap.Set(B3) - - B4 = libgap.Set([action(p2, X**2), action(p2, X**-2), action(p3, X**5), action(p3, X**-5), p4, + B4 = [action(p2, X**2), action(p2, X**-2), action(p3, X**5), action(p3, X**-5), p4, action(p4, X * Y**2), action(p4, X**-1 * Y**2), action(p4, X*Y), action(p4, X**-1 * Y), - action(p4, X**5 * Y), action(p4, X**-5 * Y), action(p4, X**5 * Y**4), action(p4, X**-5 * Y**4)]) - - actionOnSet = libgap.function_factory("""MyActionOnSets := function(block, g) - local set, p, q; - set := Set([]); - for p in block do - q := MyAction(p, g); - AddSet(set, q); - od; - return set; - end;""") + action(p4, X**5 * Y), action(p4, X**-5 * Y), action(p4, X**5 * Y**4), action(p4, X**-5 * Y**4)] + + points = [] + for i in range(1,5): + points += list(libgap.Orbit(G, (i,1), action)) + + permAction = libgap.Action(G, points, action) + + baseBlocks = [libgap.Set(list(map(lambda p: libgap.Position(points, p), B))) for B in [B1, B2, B3, B4]] - B3Orbit = libgap.Orbit(G, B3, actionOnSet) - B4Orbit = libgap.Orbit(G, B4, actionOnSet) - blocks = [B1, B2] + list(B3Orbit) + list(B4Orbit) + B3Orbit = libgap.Orbit(permAction, baseBlocks[2], libgap.OnSets) + B4Orbit = libgap.Orbit(permAction, baseBlocks[3], libgap.OnSets) + blocks = baseBlocks[0:2] + list(B3Orbit) + list(B4Orbit) # clear gap variables libgap.unset_global("p1Act") libgap.unset_global("p23Act") libgap.unset_global("p4Act") - libgap.unset_global("ChooseMyAction") - libgap.unset_global("MyAction") - libgap.unset_global("MyActionOnSets") return blocks def BIBD_56_11_2(): From db2eaa80b65734f3c4921ed75389a3c15a2675aa Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Tue, 30 Jun 2020 07:26:59 -0700 Subject: [PATCH 182/204] work on central charge --- src/sage/combinat/root_system/fusion_ring.py | 71 +++++++++++++++---- .../combinat/root_system/weyl_characters.py | 2 + 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index e74337d3b6c..e04a643709b 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -20,8 +20,7 @@ from sage.misc.cachefunc import cached_method class FusionRing(WeylCharacterRing): - r""" - Return the Fusion Ring (Verlinde Algebra) of level ``k``. + r"""Return the Fusion Ring (Verlinde Algebra) of level ``k``. INPUT: @@ -30,10 +29,10 @@ class FusionRing(WeylCharacterRing): This algebra has a basis (sometimes called *primary fields* but here called *simple objects*) indexed by the weights of level `\leq k`. - These arise as the fusion algebras of WZW conformal field theories, - or as Grothendieck groups of tilting modules for quantum groups at - roots of unity. The :class:`FusionRing` class is implemented as a - variant of the :class:`WeylCharacterRing`. + These arise as the fusion algebras of Wess-Zumino-Witten (WZW) conformal + field theories, or as Grothendieck groups of tilting modules for quantum + groups at roots of unity. The :class:`FusionRing` class is implemented as + a variant of the :class:`WeylCharacterRing`. REFERENCES: @@ -261,6 +260,7 @@ def _test_verlinde(self, **options): v = sum(self.s_ij(x,w) * self.s_ij(y,w) * self.s_ij(z,w) / self.s_ij(i0,w) for w in B) tester.assertEqual(v, c * self.N_ijk(x,y,z)) + def fusion_labels(self, labels=None, inject_variables=False): r""" Set the labels of the basis. @@ -435,6 +435,44 @@ def fusion_l(self): """ return self._l + def virasoro_central_charge(self): + r""" + Return the Virasoro central charge of the WZW conformal + field theory associated with the Fusion Ring. If `\mathfrak{g}` + is the corresponding semisimple Lie algebra, this is + + .. MATH:: + + \frac{k\dim\mathfrak{g}}{k+h^\vee} + + where `k` is the level and `h^\vee` is the dual Coxeter number. + By Proposition 2.3 in [RoStWa2009]_ there exists + a rational number c such that `D_+/\sqrt{D}=e^{i\pi c/4}` + where `D_+=\sum d_i^2\theta_i` is computed in + :meth:`D_plus` and `D=\sum d_i^2>0` is computed + by :meth:`global_q_dimension`. Squaring this identity + and remembering that `D_+D_-=D` gives + + .. MATH:: + + D_+/D_- = e^{i\pi c/2}. + + EXAMPLES:: + + sage: R = FusionRing("A1",2) + sage: c = R.virasoro_central_charge(); c + 3/2 + sage: Dp = R.D_plus(); Dp + 2*zeta32^6 + sage: Dm = R.D_minus(); Dm + -2*zeta32^10 + sage: Dp/Dm == R.to_field(c/2) + True + + """ + dim_g = len(self.space().roots())+self.cartan_type()[1] + return self._conj*self._k*dim_g/(self._k+self._h_check) + def twists_matrix(self): r""" Return a diagonal matrix describing the twist corresponding to @@ -442,14 +480,16 @@ def twists_matrix(self): EXAMPLES:: - sage: B22 = FusionRing('B2',2) - sage: B22.twists_matrix() - [ 0 0 0 0 0 0] - [ 0 4/5 0 0 0 0] - [ 0 0 1/2 0 0 0] - [ 0 0 0 2 0 0] - [ 0 0 0 0 3/2 0] - [ 0 0 0 0 0 6/5] + sage: B21=FusionRing("B2",1) + sage: [x.twist() for x in B21.basis().list()] + [0, 1, 5/8] + sage: [B21.to_field(x.twist()) for x in B21.basis().list()] + [1, -1, zeta32^10] + sage: B21.twists_matrix() + [ 1 0 0] + [ 0 -1 0] + [ 0 0 zeta32^10] + """ B = self.basis() return diagonal_matrix(self.to_field(B[x].twist()) for x in self.get_order()) @@ -459,7 +499,8 @@ def cc_matrix(self): Return the conjugation matrix, the permutation matrix for the conjugation operation on basis elements. """ - return matrix([[i==j.dual() for i in self.get_order()] for j in self.get_order()]) + b = self.basis().list() + return matrix([[i==j.dual() for i in b] for j in b]) def q_dims(self): r""" diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index b0e66a18b8a..a07b06f9e43 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -185,6 +185,8 @@ def next_level(wt): self._fg = ct[1] + 1 elif ct[0] == 'E' and ct[1] == 6: self._fg = 3 + elif ct[0] == 'E' and ct[1] == 7: + self._fg = 2 elif ct[0] == 'D': self._fg = 2 else: From b99b1eb2c8c73d0aa921b11abd4736bb02f5acd8 Mon Sep 17 00:00:00 2001 From: Ivo Maffei Date: Tue, 30 Jun 2020 18:26:44 +0200 Subject: [PATCH 183/204] change gap int to int --- src/sage/combinat/designs/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index ecc17faa543..2437c0362ed 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -4646,7 +4646,7 @@ def BIBD_79_13_2(): libgap.unset_global("p1Act") libgap.unset_global("p23Act") libgap.unset_global("p4Act") - return blocks + return [[int(t)-1 for t in y] for y in blocks] def BIBD_56_11_2(): r""" From bdd7bf697fe8b67441cfaf489666a7cee12d3220 Mon Sep 17 00:00:00 2001 From: Joshua Campbell Date: Tue, 30 Jun 2020 10:23:13 -0700 Subject: [PATCH 184/204] Prevent JSmol from phoning home. --- src/sage/repl/display/jsmol_iframe.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/repl/display/jsmol_iframe.py b/src/sage/repl/display/jsmol_iframe.py index 82b897fc11c..d8de42f7bb7 100644 --- a/src/sage/repl/display/jsmol_iframe.py +++ b/src/sage/repl/display/jsmol_iframe.py @@ -47,6 +47,7 @@