diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 69f8450da00..b7b10ddfa83 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -169,12 +169,12 @@ jobs: 2-optional*) export TARGETS_PRE="build/make/Makefile" TARGETS="build/make/Makefile" targets_pattern="${{ matrix.stage }}" targets_pattern="${targets_pattern#2-optional-}" - export TARGETS_OPTIONAL=$( echo $(export PATH=build/bin:$PATH && (for a in spkg-install.in spkg-install requirements.txt; do sage-package list :optional: --has-file $a --no-file huge --no-file has_nonfree_dependencies; done) | grep -v ^_ | grep -v sagemath_doc | grep '^[${{ matrix.targets_pattern }}]' ) ) + export TARGETS_OPTIONAL=$( echo $(export PATH=build/bin:$PATH && (for a in spkg-install.in spkg-install requirements.txt; do sage-package list :optional: --has-file $a --no-file huge --no-file has_nonfree_dependencies; done) | grep -v ^_ | grep -v sagemath_doc | grep "^[$targets_pattern]" ) ) ;; 2-experimental*) export TARGETS_PRE="build/make/Makefile" TARGETS="build/make/Makefile" targets_pattern="${{ matrix.stage }}" targets_pattern="${targets_pattern#2-experimental-}" - export TARGETS_OPTIONAL=$( echo $(export PATH=build/bin:$PATH && (for a in spkg-install.in spkg-install requirements.txt; do sage-package list :experimental: --has-file $a --no-file huge --no-file has_nonfree_dependencies; done) | grep -v ^_ | grep -v sagemath_doc | grep '^[${{ matrix.targets_pattern }}]' ) ) + export TARGETS_OPTIONAL=$( echo $(export PATH=build/bin:$PATH && (for a in spkg-install.in spkg-install requirements.txt; do sage-package list :experimental: --has-file $a --no-file huge --no-file has_nonfree_dependencies; done) | grep -v ^_ | grep -v sagemath_doc | grep "^[$targets_pattern]" ) ) ;; esac MAKE="make -j12" tox -e $TOX_ENV -- SAGE_NUM_THREADS=4 $TARGETS diff --git a/.vscode/launch.json b/.vscode/launch.json index fcf2e9c45d8..15913b0085c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,6 +8,18 @@ "program": "${file}", "console": "integratedTerminal", "justMyCode": false + }, + { + "name": "Sage: Test", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/src/bin/sage-runtests", + "args": [ + "--verbose", + "${file}" + ], + "console": "integratedTerminal", + "justMyCode": false } ], } diff --git a/.zenodo.json b/.zenodo.json index d2346f2f37f..89887360497 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -1,10 +1,10 @@ { "description": "Mirror of the Sage https://sagemath.org/ source tree", "license": "other-open", - "title": "sagemath/sage: 9.6.rc3", - "version": "9.6.rc3", + "title": "sagemath/sage: 9.6.rc4", + "version": "9.6.rc4", "upload_type": "software", - "publication_date": "2022-04-29", + "publication_date": "2022-05-12", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.6.rc3", + "identifier": "https://github.com/sagemath/sage/tree/9.6.rc4", "relation": "isSupplementTo" }, { diff --git a/VERSION.txt b/VERSION.txt index 161f2afb9d7..dc3ae1ae0ce 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.6.rc3, Release Date: 2022-04-29 +SageMath version 9.6.rc4, Release Date: 2022-05-12 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 6b2485946dc..6d491dc9d63 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=625fcd35850a91e736c0e618ace82b8e09c8e605 -md5=db697a2f6460f5328f64cfaafdb58703 -cksum=2241741028 +sha1=ebad99b2c419469d2b491a012dc3a4d53a0fc4c0 +md5=f0696455b8ba7dc50b7d23031f52ed19 +cksum=143579459 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 7880a6f9cca..6d234b474a0 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -8e8d7b448d7dfe9b1c223a245e6d164507ddfe15 +feb23e71747e42520fe984fa377c7ce5b5af025e diff --git a/build/pkgs/sagelib/package-version.txt b/build/pkgs/sagelib/package-version.txt index 24562793a28..82b75159501 100644 --- a/build/pkgs/sagelib/package-version.txt +++ b/build/pkgs/sagelib/package-version.txt @@ -1 +1 @@ -9.6.rc3 +9.6.rc4 diff --git a/src/VERSION.txt b/src/VERSION.txt index 24562793a28..82b75159501 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -9.6.rc3 +9.6.rc4 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index f34be5ac786..edc4ff086d2 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,5 +1,5 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='9.6.rc3' -SAGE_RELEASE_DATE='2022-04-29' -SAGE_VERSION_BANNER='SageMath version 9.6.rc3, Release Date: 2022-04-29' +SAGE_VERSION='9.6.rc4' +SAGE_RELEASE_DATE='2022-05-12' +SAGE_VERSION_BANNER='SageMath version 9.6.rc4, Release Date: 2022-05-12' diff --git a/src/doc/en/reference/calculus/index.rst b/src/doc/en/reference/calculus/index.rst index c49c4bd8dd4..abbb408f6e9 100644 --- a/src/doc/en/reference/calculus/index.rst +++ b/src/doc/en/reference/calculus/index.rst @@ -25,7 +25,6 @@ Using calculus - :doc:`Main operations on symbolic expressions ` - :doc:`Assumptions about symbols and functions ` - :doc:`sage/symbolic/relation` -- :doc:`sage/symbolic/series` - :doc:`sage/symbolic/integration/integral` - :doc:`sage/calculus/desolvers` - :doc:`sage/calculus/ode` @@ -49,7 +48,6 @@ Internal functionality supporting calculus - :doc:`sage/symbolic/ring` - :doc:`sage/symbolic/subring` - :doc:`sage/symbolic/operators` -- :doc:`sage/symbolic/getitem` - :doc:`sage/symbolic/function` - :doc:`sage/calculus/functional` - :doc:`sage/symbolic/function_factory` @@ -58,7 +56,6 @@ Internal functionality supporting calculus - :doc:`sage/symbolic/substitution_map` - :doc:`sage/symbolic/benchmark` - :doc:`sage/symbolic/random_tests` -- :doc:`sage/libs/pynac/pynac` - :doc:`sage/symbolic/maxima_wrapper` - :doc:`External integrators ` - :doc:`External interpolators ` @@ -78,7 +75,6 @@ Internal functionality supporting calculus sage/symbolic/function sage/symbolic/function_factory sage/calculus/functional - sage/symbolic/series sage/symbolic/integration/integral sage/symbolic/integration/external sage/calculus/test_sympy @@ -97,12 +93,10 @@ Internal functionality supporting calculus sage/calculus/interpolators sage/calculus/functions sage/calculus/var - sage/symbolic/getitem sage/symbolic/maxima_wrapper sage/symbolic/operators sage/symbolic/substitution_map sage/symbolic/benchmark sage/symbolic/random_tests - sage/libs/pynac/pynac .. include:: ../footer.txt diff --git a/src/doc/en/reference/constants/index.rst b/src/doc/en/reference/constants/index.rst index a10ee041555..96ae5e256ae 100644 --- a/src/doc/en/reference/constants/index.rst +++ b/src/doc/en/reference/constants/index.rst @@ -5,6 +5,5 @@ Constants :maxdepth: 2 sage/symbolic/constants - sage/symbolic/constants_c .. include:: ../footer.txt diff --git a/src/doc/en/reference/finance/index.rst b/src/doc/en/reference/finance/index.rst index 28bddb84cd8..bba570a13ae 100644 --- a/src/doc/en/reference/finance/index.rst +++ b/src/doc/en/reference/finance/index.rst @@ -4,7 +4,6 @@ Quantitative Finance .. toctree:: :maxdepth: 2 - sage/finance/time_series sage/finance/stock sage/finance/option sage/finance/fractal diff --git a/src/sage/features/phitigra.py b/src/sage/features/phitigra.py new file mode 100644 index 00000000000..f792d7b47cd --- /dev/null +++ b/src/sage/features/phitigra.py @@ -0,0 +1,31 @@ +r""" +Check for phitigra +""" +from . import PythonModule + + +class Phitigra(PythonModule): + r""" + A :class:`sage.features.Feature` describing the presence of phitigra. + + Phitigra is provided by an optional package in the Sage distribution. + + EXAMPLES:: + + sage: from sage.features.phitigra import Phitigra + sage: Phitigra().is_present() # optional - phitigra + FeatureTestResult('phitigra', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.phitigra import Phitigra + sage: isinstance(Phitigra(), Phitigra) + True + """ + PythonModule.__init__(self, 'phitigra', spkg='phitigra') + + +def all_features(): + return [Phitigra()] diff --git a/src/sage/finance/time_series.py b/src/sage/finance/time_series.py index 977a13b5e65..ab2d836d1fa 100644 --- a/src/sage/finance/time_series.py +++ b/src/sage/finance/time_series.py @@ -1,3 +1,10 @@ -from sage.misc.lazy_import import lazy_import +r""" +Time Series (deprecated module) + +This module consists only of deprecated lazy imports from +:mod:`sage.stats.time_series`. +""" + +from sage.misc.lazy_import import lazy_import lazy_import('sage.stats.time_series', ('TimeSeries', 'autoregressive_fit'), deprecation=32427) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index a57e74c632a..112f57457c5 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -5116,6 +5116,15 @@ def is_planar(self, on_embedding=None, kuratowski=False, set_embedding=False, se sage: posets.BooleanLattice(3).cover_relations_graph().is_planar() True + :trac:`33759`:: + + sage: G = Graph([(1, 2)]) + sage: for set_embedding, set_pos in ((True,True), (True,False), (False, True), (False, False)): + ....: G = Graph([(1, 2)]) + ....: assert G.is_planar(set_embedding=set_embedding, set_pos=set_pos) + ....: assert (hasattr(G, '_embedding') and G._embedding is not None) == set_embedding, (set_embedding, set_pos) + ....: assert (hasattr(G, '_pos') and G._pos is not None) == set_pos, (set_embedding, set_pos) + Corner cases:: sage: graphs.EmptyGraph().is_planar() @@ -5239,13 +5248,13 @@ def is_circular_planar(self, on_embedding=None, kuratowski=False, sage: g439.is_circular_planar(kuratowski=True, boundary=[1, 2, 3]) (True, None) sage: g439.get_embedding() - {1: [7, 5], - 2: [5, 6], - 3: [6, 7], - 4: [7, 6, 5], - 5: [1, 4, 2], - 6: [2, 4, 3], - 7: [3, 4, 1]} + {1: [5, 7], + 2: [6, 5], + 3: [7, 6], + 4: [5, 6, 7], + 5: [2, 4, 1], + 6: [3, 4, 2], + 7: [1, 4, 3]} Order matters:: @@ -5262,6 +5271,22 @@ def is_circular_planar(self, on_embedding=None, kuratowski=False, TESTS: + Non-simple graphs:: + + sage: Graph([(1, 1)], loops=True).is_circular_planar(set_embedding=True) + Traceback (most recent call last): + ... + NotImplementedError: cannot compute with embeddings of multiple-edged or looped graphs + + Argument saving:: + + sage: G = Graph([(1, 2)]) + sage: for set_embedding, set_pos in ((True,True), (True,False), (False, True), (False, False)): + ....: G = Graph([(1, 2)]) + ....: assert G.is_circular_planar(set_embedding=set_embedding, set_pos=set_pos) + ....: assert (hasattr(G, '_embedding') and G._embedding is not None) == set_embedding, (set_embedding, set_pos) + ....: assert (hasattr(G, '_pos') and G._pos is not None) == set_pos, (set_embedding, set_pos) + Corner cases:: sage: graphs.EmptyGraph().is_circular_planar() @@ -5279,6 +5304,12 @@ def is_circular_planar(self, on_embedding=None, kuratowski=False, if self.order() > 3 and self.size() > 2 * self.order() - 3: return False + if self.has_multiple_edges() or self.has_loops(): + if set_embedding or (on_embedding is not None) or set_pos: + raise NotImplementedError("cannot compute with embeddings of multiple-edged or looped graphs") + else: + return self.to_simple().is_circular_planar(kuratowski=kuratowski, boundary=boundary, ordered=ordered) + if boundary is None: boundary = self @@ -5287,7 +5318,7 @@ def is_circular_planar(self, on_embedding=None, kuratowski=False, from sage.graphs.planarity import is_planar graph = Graph(self) if hasattr(graph, '_embedding'): - del(graph._embedding) + del graph._embedding # Adds a new vertex to the graph and connects it to all vertices of the # boundary @@ -5308,7 +5339,7 @@ def is_circular_planar(self, on_embedding=None, kuratowski=False, graph.add_edges(extra_edges) - result = is_planar(graph, kuratowski=kuratowski, set_embedding=set_embedding, circular=True) + result = is_planar(graph, kuratowski=kuratowski, set_embedding=set_embedding, set_pos=set_pos) if kuratowski: bool_result = result[0] @@ -5316,24 +5347,21 @@ def is_circular_planar(self, on_embedding=None, kuratowski=False, bool_result = result if bool_result: - graph.delete_vertex(extra) - graph.delete_edges(extra_edges) - - if hasattr(graph,'_embedding'): + if set_embedding: # strip the embedding to fit original graph - for u,v in extra_edges: - graph._embedding[u].pop(graph._embedding[u].index(v)) - graph._embedding[v].pop(graph._embedding[v].index(u)) - for w in boundary: - graph._embedding[w].pop(graph._embedding[w].index(extra)) - - if set_embedding: - self._embedding = graph._embedding.copy() + del graph._embedding[extra] + for u, v in extra_edges: + graph._embedding[u].remove(v) + graph._embedding[v].remove(u) + for v in boundary: + graph._embedding[v].remove(extra) + self._embedding = graph._embedding + + if set_pos: + # strip the position + del graph._pos[extra] + self._pos = graph._pos - if (set_pos and set_embedding): - self.layout(layout="planar", save_pos=True, test=False) - - del graph return result def layout_planar(self, set_embedding=False, on_embedding=None, @@ -5538,7 +5566,7 @@ def layout_planar(self, set_embedding=False, on_embedding=None, embedding_copy = {v: neighbors[:] for v, neighbors in G._embedding.items()} else: if on_embedding is not None: - G._check_embedding_validity(on_embedding,boolean=False) + G._check_embedding_validity(on_embedding, boolean=False) if not G.is_planar(on_embedding=on_embedding): raise ValueError('provided embedding is not a planar embedding for %s'%self ) G.set_embedding(on_embedding) @@ -10257,18 +10285,39 @@ def delete_vertex(self, vertex, in_order=False): {0: 'no delete', 2: None, 3: None, 4: None} sage: G.get_pos() {0: (0, 0), 2: (2, 0), 3: (3, 0), 4: (4, 0)} + + TESTS: + + Test that :trac:`33759` is fixed:: + + sage: G = Graph([(1, 4), (2, 3)]) + sage: G.is_planar(set_embedding=True) + True + sage: G.delete_vertex(3) + sage: G.is_planar() + True """ if in_order: vertex = self.vertices()[vertex] if vertex not in self: raise ValueError("vertex (%s) not in the graph"%str(vertex)) - self._backend.del_vertex(vertex) - attributes_to_update = ('_pos', '_assoc', '_embedding') + attributes_to_update = ('_pos', '_assoc') for attr in attributes_to_update: if hasattr(self, attr) and getattr(self, attr) is not None: getattr(self, attr).pop(vertex, None) + if hasattr(self, '_embedding'): + embedding = self._embedding + if embedding is not None: + neighbors = set(self.neighbor_iterator(vertex)) + neighbors.discard(vertex) + for w in neighbors: + embedding[w] = [x for x in embedding[w] if x != vertex] + embedding.pop(vertex, None) + + self._backend.del_vertex(vertex) + def delete_vertices(self, vertices): """ Delete vertices from the (di)graph taken from an iterable container of @@ -10290,19 +10339,39 @@ def delete_vertices(self, vertices): ... ValueError: vertex (1) not in the graph + TESTS: + + Test that :trac:`33759` is fixed:: + + sage: G = Graph([(1, 4), (2, 3)]) + sage: G.is_planar(set_embedding=True) + True + sage: G.delete_vertices([3]) + sage: G.is_planar() + True """ vertices = list(vertices) - for vertex in vertices: - if vertex not in self: - raise ValueError("vertex (%s) not in the graph"%str(vertex)) + for v in vertices: + if v not in self: + raise ValueError("vertex (%s) not in the graph"%str(v)) - self._backend.del_vertices(vertices) - attributes_to_update = ('_pos', '_assoc', '_embedding') - for attr in attributes_to_update: + for attr in ('_pos', '_assoc'): if hasattr(self, attr) and getattr(self, attr) is not None: attr_dict = getattr(self, attr) - for vertex in vertices: - attr_dict.pop(vertex, None) + for v in vertices: + attr_dict.pop(v, None) + + if hasattr(self, '_embedding'): + embedding = self._embedding + if embedding is not None: + neighbors = set().union(*[self.neighbor_iterator(v) for v in vertices]) + neighbors.difference_update(vertices) + for w in neighbors: + embedding[w] = [x for x in embedding[w] if x not in vertices] + for v in vertices: + embedding.pop(v, None) + + self._backend.del_vertices(vertices) def has_vertex(self, vertex): """ @@ -11429,6 +11498,7 @@ def delete_edge(self, u, v=None, label=None): except Exception: u, v = u label = None + self._backend.del_edge(u, v, label, self._directed) def delete_edges(self, edges): @@ -19024,6 +19094,11 @@ def layout(self, layout=None, pos=None, dim=2, save_pos=False, **options): ('1', 1): [2.26..., -0.87...]} sage: g.layout(layout="acyclic_dummy", save_pos=True) + {('0', 0): [0.3..., 0], + ('0', 1): [0.3..., 1], + ('1', 0): [0.6..., 0], + ('1', 1): [0.6..., 1]} + sage: g.get_pos() {('0', 0): [0.3..., 0], ('0', 1): [0.3..., 1], ('1', 0): [0.6..., 0], @@ -19085,6 +19160,13 @@ def layout(self, layout=None, pos=None, dim=2, save_pos=False, **options): use this feature for all the predefined graphs classes (like for the Petersen graph, ...), rather than systematically building the layout at construction time. + + TESTS:: + + sage: for style in ('spring', 'planar', 'circular', 'forest'): + ....: for G in [Graph([(1, 2)]), Graph([(1, 2), (2, 3), (3, 4)])]: + ....: pos = G.layout(style, save_pos=True) + ....: assert G._pos is not None """ if layout is None: if pos is None: diff --git a/src/sage/graphs/graph_editor.py b/src/sage/graphs/graph_editor.py index e56c443b8f5..d4d8722369f 100644 --- a/src/sage/graphs/graph_editor.py +++ b/src/sage/graphs/graph_editor.py @@ -1,168 +1,81 @@ r""" -Graph editor +Graph editor widget + +This module adds an interface to ``phitigra``, a graph editor widget for +Jupyter and JupyterLab. The ``phitigra`` optional package should be installed +on your Sage installation. + +AUTHORS: + +- Radoslav Kirov (2009): initial editor for use with the old sage notebook + +- Jean-Florent Raymond (2022-04-12): replacement with the ``phitigra`` package """ + # **************************************************************************** -# Copyright (C) 2009 Radoslav Kirov -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# The full text of the GPL is available at: +# Copyright (C) 2022 Jean-Florent Raymond # +# 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/ # **************************************************************************** -import sys -from .graph_generators import graphs -from sage.misc.html import html +from sage.misc.lazy_import import lazy_import +from sage.features.phitigra import Phitigra +lazy_import('phitigra', 'GraphEditor', feature=Phitigra()) -def graph_to_js(g): +def graph_editor(graph=None, **display_options): """ - Returns a string representation of a :class:`Graph` instance - usable by the :func:`graph_editor`. The encoded information is - the number of vertices, their 2D positions, and a list of edges. + Return a graph editor widget. - INPUT: + The graph editor widget can be displayed with Jupyter or JupyterLab. + It is provided by the ``phitigra`` optional package, see + https://github.com/jfraymond/phitigra for details about the + possible options (changing the width/height of the canvas, the + default size and color of vertices, etc.). - - ``g`` - a :class:`Graph` instance + INPUT: - OUTPUT: + - ``graph`` -- a graph to edit (default: ``None``) - - a string + - ``display_options`` -- options for the widget EXAMPLES:: - sage: from sage.graphs.graph_editor import graph_to_js - sage: G = graphs.CompleteGraph(4) - sage: graph_to_js(G) - 'num_vertices=4;edges=[[0,1],[0,2],[0,3],[1,2],[1,3],[2,3]];pos=[[0.5,0.0],[0.0,0.5],[0.5,1.0],[1.0,0.5]];' - sage: graph_to_js(graphs.StarGraph(2)) - 'num_vertices=3;edges=[[0,1],[0,2]];pos=[[0.0,0.5],[0.0,0.0],[0.0,1.0]];' - """ - string = '' - vertex_list = list(g.get_vertices()) - string += 'num_vertices=' + str(len(vertex_list)) + ';' - string += 'edges=[' - for i, e in enumerate(g.edges()): - if i: - string += ',' - string += '[' + str(vertex_list.index(e[0])) + ',' + str(vertex_list.index(e[1])) + ']' - string += '];' - string += 'pos=[' - pos = g.get_pos() - max_x = max([i[0] for i in pos.values()]) - max_y = max([i[1] for i in pos.values()]) - min_x = min([i[0] for i in pos.values()]) - min_y = min([i[1] for i in pos.values()]) - if max_x == 0: - max_x = 1 - if max_y == 0: - max_y = 1 - for i, v in enumerate(vertex_list): - if i: - string += ',' - new_pos = [float(pos[v][0] - min_x) / (max_x - min_x), - 1.0 - float(pos[v][1] - min_y) / (max_y - min_y)] - string += str(new_pos) - string += '];' - string = string.replace(' ', '') - return string - - -def graph_editor(graph=None, graph_name=None, - replace_input=True, **layout_options): - """ - Opens a graph editor in the Sage notebook. + sage: e = graph_editor() # optional - phitigra + sage: e.show() # not tested - INPUT: + Opening an existing graph:: - - ``graph`` - a :class:`Graph` instance (default: - graphs.CompleteGraph(2)); the graph to edit + sage: G = graphs.RandomGNP(10, 0.5) + sage: e = graph_editor(G) # optional - phitigra + sage: e.show() # not tested - - ``graph_name`` - a string (default: None); the variable name to - use for the updated instance; by default, this function attempts - to determine the name automatically + Retrieving a copy of the drawn graph:: - - ``replace_input`` - a boolean (default: True); whether to - replace the text in the input cell with the updated graph data - when "Save" is clicked; if this is False, the data is **still** - evaluated as if it had been entered in the cell + sage: G = graphs.RandomGNP(10, 0.5) + sage: e = graph_editor(G) # optional - phitigra + sage: H = e.get_graph() # optional - phitigra + sage: H == G and not H is G # optional - phitigra + True - EXAMPLES:: + Using different display options:: - sage: g = graphs.CompleteGraph(3) - sage: graph_editor(g) # not tested - sage: graph_editor(graphs.HouseGraph()) # not tested - sage: graph_editor(graph_name='my_graph') # not tested - sage: h = graphs.StarGraph(6) - sage: graph_editor(h, replace_input=False) # not tested + sage: e = graph_editor(graphs.PetersenGraph(), width=300, height=300, # optional - phitigra + ....: default_radius=12, default_vertex_color='orange', # optional - phitigra + ....: default_edge_color='#666', show_vertex_labels=False) # optional - phitigra + sage: e.show() # not tested + + .. NOTE:: + + The editor does not support multigraphs. """ - import sagenb.notebook.interact + from .graph import Graph + if graph is None: - graph = graphs.CompleteGraph(2) - - return "This graph editor only runs in the deprecated Sage notebook." - - graph.layout(save_pos=True, **layout_options) - - if graph_name is None: - graph_name = '' - locs = sys._getframe(1).f_locals - for var in locs: - if id(locs[var]) == id(graph): - graph_name = var - - cell_id = sagenb.notebook.interact.SAGE_CELL_ID - - # TODO: Put reasonable checks for large graphs, before disaster - # occurs (i.e., breaks browser). - - close_button = r"""""" % locals() - - if replace_input: - eval_strategy = r""" - f += ' graph_editor(' + g[2] + ');' - \$('#cell_input_%(cell_id)s').val(f); - cell_input_resize(%(cell_id)s); - evaluate_cell(%(cell_id)s, false); -""" % locals() - else: - eval_strategy = r""" - saved_input = \$('#cell_input_%(cell_id)s').val(); - \$('#cell_input_%(cell_id)s').val(f); - evaluate_cell(%(cell_id)s, false); - \$('#cell_input_%(cell_id)s').val(saved_input); - send_cell_input(%(cell_id)s); - cell_input_resize(%(cell_id)s); -""" % locals() - - update_button = r"""""" % locals() - - graph_js = graph_to_js(graph) - data_fields = """""" % locals() - - return html(r"""
- - -
%(data_fields)s
%(update_button)s%(close_button)s
""" % locals()) - -# This is commented out because the mouse_out call raises an error in -# Firebug's console when the event fires but the function itself has -# not yet been loaded. - -# %(data_fields)s + graph = Graph(0) + + return GraphEditor(graph, **display_options) diff --git a/src/sage/graphs/planarity.pyx b/src/sage/graphs/planarity.pyx index f761281c0ea..4b14aa25796 100644 --- a/src/sage/graphs/planarity.pyx +++ b/src/sage/graphs/planarity.pyx @@ -29,7 +29,7 @@ cdef extern from "planarity/graph.h": cdef int gp_Embed(graphP theGraph, int embedFlags) cdef int gp_SortVertices(graphP theGraph) -def is_planar(g, kuratowski=False, set_pos=False, set_embedding=False, circular=False): +def is_planar(g, kuratowski=False, set_pos=False, set_embedding=False, circular=None): r""" Check whether ``g`` is planar using Boyer's planarity algorithm. @@ -54,8 +54,7 @@ def is_planar(g, kuratowski=False, set_pos=False, set_embedding=False, circular= combinatorial embedding returned (see :meth:`~sage.graphs.generic_graph.GenericGraph.get_embedding`) - - ``circular`` -- boolean (default: ``False``); whether to test for circular - planarity + - ``circular`` -- deprecated argument EXAMPLES:: @@ -86,7 +85,21 @@ def is_planar(g, kuratowski=False, set_pos=False, set_embedding=False, circular= ....: if (not g.is_connected() or i == 0): ....: continue ....: _ = g.is_planar(set_embedding=True, set_pos=True) + + Argument saving:: + + sage: G = Graph([(1, 2)]) + sage: for set_embedding, set_pos in ((True,True), (True,False), (False, True), (False, False)): + ....: G = Graph([(1, 2)]) + ....: assert is_planar(G, set_embedding=set_embedding, set_pos=set_pos) + ....: assert (hasattr(G, '_embedding') and G._embedding is not None) == set_embedding, (set_embedding, set_pos) + ....: assert (hasattr(G, '_pos') and G._pos is not None) == set_pos, (set_embedding, set_pos) + """ + if circular is not None: + from sage.misc.superseded import deprecation + deprecation(33759, 'the circular argument of is_planar is deprecated and has no effect') + if set_pos and not g.is_connected(): raise ValueError("is_planar() cannot set vertex positions for a disconnected graph") @@ -158,41 +171,23 @@ def is_planar(g, kuratowski=False, set_pos=False, set_embedding=False, circular= else: return False else: - if not circular: + if set_pos or set_embedding: + emb_dict = {} + #for i in range(theGraph.N): + for i in range(1, theGraph.N + 1): + linked_list = [] + j = theGraph.V[i].link[1] + while j: + linked_list.append(to[theGraph.E[j].neighbor]) + j = theGraph.E[j].link[1] + emb_dict[to[i]] = linked_list if set_embedding: - emb_dict = {} - #for i in range(theGraph.N): - for i in range(1, theGraph.N + 1): - linked_list = [] - j = theGraph.V[i].link[1] - while j: - linked_list.append(to[theGraph.E[j].neighbor]) - j = theGraph.E[j].link[1] - emb_dict[to[i]] = linked_list g._embedding = emb_dict if set_pos: - g.layout(layout='planar', save_pos=True) - else: - if set_embedding: - # Take counter-clockwise embedding if circular planar test - # Also, pos must be set after removing extra vertex and edges - - # This is separated out here for now because in the circular case, - # setting positions would have to come into play while the extra - # "wheel" or "star" is still part of the graph. - - emb_dict = {} - #for i in range(theGraph.N): - for i in range(1, theGraph.N + 1): - linked_list = [] - j = theGraph.V[i].link[0] - while j: - linked_list.append(to[theGraph.E[j].neighbor]) - j = theGraph.E[j].link[0] - emb_dict[to[i]] = linked_list - g._embedding = emb_dict + g.layout(layout='planar', save_pos=True, on_embedding=emb_dict) + gp_Free(&theGraph) if kuratowski: - return (True,None) + return (True, None) else: return True diff --git a/src/sage/libs/pynac/pynac.py b/src/sage/libs/pynac/pynac.py index 5ea76961bc6..ae39f63c4c2 100644 --- a/src/sage/libs/pynac/pynac.py +++ b/src/sage/libs/pynac/pynac.py @@ -1,3 +1,11 @@ +r""" +Interface to the pynac library (deprecated module) + +This module consists only of deprecated lazy imports from +:mod:`sage.symbolic.expression`. +""" + + from sage.misc.lazy_import import lazy_import lazy_import('sage.symbolic.expression', diff --git a/src/sage/manifolds/differentiable/diff_form.py b/src/sage/manifolds/differentiable/diff_form.py index cc1ad54a75d..ec6a8d353f3 100644 --- a/src/sage/manifolds/differentiable/diff_form.py +++ b/src/sage/manifolds/differentiable/diff_form.py @@ -1631,6 +1631,15 @@ def interior_product(self, qvect): sage: s == a.contract(0,1,b,0,1) True + TESTS: + + Check that :trac:`33780` is fixed:: + + sage: v = X.frame()[1] # vector field d/dx + sage: f = X.coframe()[2] # 1-form dy + sage: f.interior_product(v) + Scalar field zero on the 3-dimensional differentiable manifold M + """ if self._domain.is_subset(qvect._domain): if not self._ambient_domain.is_subset(qvect._ambient_domain): diff --git a/src/sage/manifolds/differentiable/multivectorfield.py b/src/sage/manifolds/differentiable/multivectorfield.py index c93c833946d..307e35495f5 100644 --- a/src/sage/manifolds/differentiable/multivectorfield.py +++ b/src/sage/manifolds/differentiable/multivectorfield.py @@ -1171,6 +1171,15 @@ def interior_product(self, form): sage: s == d.contract(0, 1, c, 0, 1) True + TESTS: + + Check that :trac:`33780` is fixed:: + + sage: v = X.frame()[0] # vector field d/dt + sage: f = X.coframe()[1] # 1-form dx + sage: v.interior_product(f) + Scalar field zero on the 4-dimensional differentiable manifold M + """ if self._domain.is_subset(form._domain): if not self._ambient_domain.is_subset(form._ambient_domain): diff --git a/src/sage/matrix/matrix_integer_dense_hnf.py b/src/sage/matrix/matrix_integer_dense_hnf.py index e1ce64d5605..735e7016eda 100644 --- a/src/sage/matrix/matrix_integer_dense_hnf.py +++ b/src/sage/matrix/matrix_integer_dense_hnf.py @@ -1103,13 +1103,18 @@ def hnf(A, include_zero_rows=True, proof=True): return H.matrix_from_rows(range(len(pivots))), pivots while True: - H, pivots = probable_hnf(A, include_zero_rows=include_zero_rows, + try: + H, pivots = probable_hnf(A, include_zero_rows=include_zero_rows, proof=True) + except ValueError: + verbose("The attempt failed since the pivots must have been wrong. We try again.") + continue + if is_in_hnf_form(H, pivots): if not include_zero_rows and len(pivots) > H.nrows(): H = H.matrix_from_rows(range(len(pivots))) return H, pivots - verbose("After attempt the return matrix is not in HNF form since pivots must have been wrong. We try again.") + verbose("After attempt the return matrix is not in HNF form since pivots must have been wrong. We try again.") def hnf_with_transformation(A, proof=True): diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 93689cddf23..13b1b8b5736 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -2569,10 +2569,19 @@ cdef class FreeModuleElement(Vector): # abstract base class sage: v = vector(ZZ, []) sage: v.dot_product(v) 0 + + TESTS: + + Check for :trac:`33814`:: + + sage: for R in [ZZ, QQ, RDF, RR, GF(2), GF(3), GF(4), ZZ['x']]: + ....: _ = (R**0)().dot_product((R**0)()) """ cdef FreeModuleElement r = right if self._parent is r._parent: # If the parents are equal, the degree is also equal + if self._degree == 0: + return self._parent.coordinate_ring().zero() return self._dot_product_(r) if self._degree != r._degree: raise ArithmeticError("degrees (%s and %s) must be the same"%(self.degree(), right.degree())) diff --git a/src/sage/symbolic/comparison.py b/src/sage/symbolic/comparison.py index ea0737146b6..38c23b97d6c 100644 --- a/src/sage/symbolic/comparison.py +++ b/src/sage/symbolic/comparison.py @@ -1,3 +1,11 @@ +r""" +Comparison of Symbolic Expressions (deprecated module) + +This module consists only of deprecated lazy imports from +:mod:`sage.symbolic.expression`. +""" + + from sage.misc.lazy_import import lazy_import lazy_import('sage.symbolic.expression', ['print_order', '_print_key', 'print_sorted', '_math_key', diff --git a/src/sage/symbolic/constant.py b/src/sage/symbolic/constant.py index d8774c858f2..53571b8beb0 100644 --- a/src/sage/symbolic/constant.py +++ b/src/sage/symbolic/constant.py @@ -1,2 +1,10 @@ +r""" +Symbolic constants (deprecated module) + +This module consists only of deprecated lazy imports from +:mod:`sage.symbolic.expression`. +""" + + from sage.misc.lazy_import import lazy_import lazy_import('sage.symbolic.expression', 'PynacConstant', deprecation=32386) diff --git a/src/sage/symbolic/constants_c.py b/src/sage/symbolic/constants_c.py index 553f35f4227..b18d0d0eb2f 100644 --- a/src/sage/symbolic/constants_c.py +++ b/src/sage/symbolic/constants_c.py @@ -1,2 +1,10 @@ +r""" +The constant `e` (deprecated module) + +This module consists only of deprecated lazy imports from +:mod:`sage.symbolic.expression`. +""" + + from sage.misc.lazy_import import lazy_import lazy_import('sage.symbolic.expression', 'E', deprecation=32386) diff --git a/src/sage/symbolic/getitem.py b/src/sage/symbolic/getitem.py index 390a87853cd..3ff5e400fba 100644 --- a/src/sage/symbolic/getitem.py +++ b/src/sage/symbolic/getitem.py @@ -1,3 +1,11 @@ +r""" +Operands (deprecated module) + +This module consists only of deprecated lazy imports from +:mod:`sage.symbolic.expression`. +""" + + from sage.misc.lazy_import import lazy_import lazy_import('sage.symbolic.expression', ['normalize_index_for_doctests', 'OperandsWrapper', 'restore_op_wrapper'], diff --git a/src/sage/symbolic/series.py b/src/sage/symbolic/series.py index c562d6bb9ae..943631c52f2 100644 --- a/src/sage/symbolic/series.py +++ b/src/sage/symbolic/series.py @@ -1,2 +1,10 @@ +r""" +Symbolic Series + +This module consists only of deprecated lazy imports from +:mod:`sage.symbolic.expression`. +""" + + from sage.misc.lazy_import import lazy_import lazy_import('sage.symbolic.expression', 'SymbolicSeries', deprecation=32386) diff --git a/src/sage/tensor/modules/alternating_contr_tensor.py b/src/sage/tensor/modules/alternating_contr_tensor.py index eed734b037f..8af7be2363d 100644 --- a/src/sage/tensor/modules/alternating_contr_tensor.py +++ b/src/sage/tensor/modules/alternating_contr_tensor.py @@ -761,13 +761,10 @@ def interior_product(self, form): if not is_atomic(olname): olname = r'\left(' + olname + r'\right)' res_latex_name = r'\iota_{' + slname + '} ' + olname - if p_res == 0: - if res_name: - try: # there is no guarantee that base ring elements have - # set_name - res.set_name(res_name, latex_name=res_latex_name) - except (AttributeError, TypeError): - pass - else: - res.set_name(res_name, latex_name=res_latex_name) + if res_name: + try: # there is no guarantee that the result has set_name + # and is mutable + res.set_name(res_name, latex_name=res_latex_name) + except (AttributeError, TypeError, ValueError): + pass return res diff --git a/src/sage/tensor/modules/free_module_alt_form.py b/src/sage/tensor/modules/free_module_alt_form.py index aae1ae990c3..2e8abc33249 100644 --- a/src/sage/tensor/modules/free_module_alt_form.py +++ b/src/sage/tensor/modules/free_module_alt_form.py @@ -856,13 +856,10 @@ def interior_product(self, alt_tensor): if not is_atomic(olname): olname = r'\left(' + olname + r'\right)' res_latex_name = r'\iota_{' + slname + '} ' + olname - if p_res == 0: - if res_name: - try: # there is no guarantee that base ring elements have - # set_name - res.set_name(res_name, latex_name=res_latex_name) - except (AttributeError, TypeError): - pass - else: - res.set_name(res_name, latex_name=res_latex_name) + if res_name: + try: # there is no guarantee that the result has set_name + # and is mutable + res.set_name(res_name, latex_name=res_latex_name) + except (AttributeError, TypeError, ValueError): + pass return res diff --git a/src/sage/version.py b/src/sage/version.py index 5d921980573..062dc77315b 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '9.6.rc3' -date = '2022-04-29' -banner = 'SageMath version 9.6.rc3, Release Date: 2022-04-29' +version = '9.6.rc4' +date = '2022-05-12' +banner = 'SageMath version 9.6.rc4, Release Date: 2022-05-12'