Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
trac #15603: "immutable=True" for Graph/Digraph __init__ and copy()
Browse files Browse the repository at this point in the history
  • Loading branch information
nathanncohen committed Dec 29, 2013
1 parent 2525d22 commit 7860f39
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 35 deletions.
17 changes: 16 additions & 1 deletion src/sage/graphs/digraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,11 @@ class DiGraph(GenericGraph):
*Only available when* ``implementation == 'c_graph'``
- ``immutable`` (boolean) -- whether to create a immutable digraph. Note
that ``immutable=True`` is actually a shortcut for
``data_structure='static_sparse'``. Set to ``False`` by default, only
available when ``implementation='c_graph'``
- ``vertex_labels`` - only for implementation == 'c_graph'.
Whether to allow any object as a vertex (slower), or
only the integers 0, ..., n-1, where n is the number of vertices.
Expand Down Expand Up @@ -439,7 +444,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None,
boundary=None, weighted=None, implementation='c_graph',
data_structure="sparse", vertex_labels=True, name=None,
multiedges=None, convert_empty_dict_labels_to_None=None,
sparse=True):
sparse=True, immutable=False):
"""
TESTS::
Expand Down Expand Up @@ -505,6 +510,13 @@ def __init__(self, data=None, pos=None, loops=None, format=None,
sage: grafo4 = DiGraph(matad,format = "adjacency_matrix", weighted=True)
sage: grafo4.shortest_path(0,6,by_weight=True)
[0, 1, 2, 5, 4, 6]
Building a DiGraph with ``immutable=False`` returns a mutable graph::
sage: g = graphs.PetersenGraph()
sage: g = DiGraph(g.edges(),immutable=False)
sage: g.add_edge("Hey", "Heyyyyyyy")
"""
msg = ''
GenericGraph.__init__(self)
Expand Down Expand Up @@ -844,6 +856,9 @@ def __init__(self, data=None, pos=None, loops=None, format=None,
if data_structure == "dense":
raise RuntimeError("Multiedge and weighted c_graphs must be sparse.")

if immutable:
data_structure = 'static_sparse'

# If the data structure is static_sparse, we first build a graph
# using the sparse data structure, then reencode the resulting graph
# as a static sparse graph.
Expand Down
136 changes: 109 additions & 27 deletions src/sage/graphs/generic_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

:meth:`~GenericGraph.networkx_graph` | Creates a new NetworkX graph from the Sage graph
:meth:`~GenericGraph.to_dictionary` | Creates a dictionary encoding the graph.
:meth:`~GenericGraph.copy` | Return a copy of the graph.
:meth:`~GenericGraph.adjacency_matrix` | Returns the adjacency matrix of the (di)graph.
:meth:`~GenericGraph.incidence_matrix` | Returns an incidence matrix of the (di)graph
:meth:`~GenericGraph.distance_matrix` | Returns the distance matrix of the (strongly) connected (di)graph
Expand Down Expand Up @@ -711,29 +712,44 @@ def _repr_(self):
### Formats

def __copy__(self, implementation='c_graph', data_structure=None,
sparse=None):
sparse=None, immutable=None):
"""
Creates a copy of the graph.

NOTE:

If the graph uses :class:`~sage.graphs.base.static_sparse_backend.StaticSparseBackend`
and uses the _immutable flag, then ``self`` is returned, rather
than a copy, unless one of the optional arguments is used.
Return a copy of the graph.

INPUT:

- ``implementation`` - string (default: 'networkx') the
implementation goes here. Current options are only
'networkx' or 'c_graph'.
- ``implementation`` - string (default: 'c_graph') the implementation
goes here. Current options are only 'networkx' or 'c_graph'.

- ``sparse`` (boolean) -- ``sparse=True`` is an alias for
``data_structure="sparse"``, and ``sparse=False`` is an alias for
``data_structure="dense"``.
``data_structure="dense"``. Only used when
``implementation='c_graph'`` and ``data_structure=None``.

- ``data_structure`` -- one of ``"sparse"``, ``"static_sparse"``, or
``"dense"``. See the documentation of :class:`Graph` or
:class:`DiGraph`.
:class:`DiGraph`. Only used when ``implementation='c_graph'``.

- ``immutable`` (boolean) -- whether to create a mutable/immutable
copy. Only used when ``implementation='c_graph'`` and
``data_structure=None``.

* ``immutable=None`` (default) means that the graph and its copy will
behave the same way.

* ``immutable=True`` is a shortcut for
``data_structure='static_sparse'`` and ``implementation='c_graph'``

* ``immutable=False`` sets ``implementation`` to ``'c_graph'``. When
``immutable=False`` is used to copy an immutable graph, the data
structure used is ``"sparse"`` unless anything else is specified.

.. NOTE::

If the graph uses
:class:`~sage.graphs.base.static_sparse_backend.StaticSparseBackend`
and the ``_immutable`` flag, then ``self`` is returned rather than a
copy (unless one of the optional arguments is used).

OUTPUT:

Expand All @@ -743,7 +759,7 @@ def __copy__(self, implementation='c_graph', data_structure=None,

Please use this method only if you need to copy but change the
underlying implementation. Otherwise simply do ``copy(g)``
instead of doing ``g.copy()``.
instead of ``g.copy()``.

EXAMPLES::

Expand Down Expand Up @@ -786,8 +802,7 @@ def __copy__(self, implementation='c_graph', data_structure=None,

TESTS:

We make copies of the _pos and _boundary attributes.
::
We make copies of the ``_pos`` and ``_boundary`` attributes::

sage: g = graphs.PathGraph(3)
sage: h = copy(g)
Expand Down Expand Up @@ -828,27 +843,94 @@ def __copy__(self, implementation='c_graph', data_structure=None,
sage: copy(H) is H
False

"""
if sparse != None:
if data_structure != None:
raise ValueError("The 'sparse' argument is an alias for "
"'data_structure'. Please do not define both.")
data_structure = "sparse" if sparse else "dense"
TESTS:

Bad input::

sage: G.copy(data_structure="sparse", sparse=False)
Traceback (most recent call last):
...
ValueError: You cannot define 'immutable' or 'sparse' when 'data_structure' has a value.
sage: G.copy(data_structure="sparse", immutable=True)
Traceback (most recent call last):
...
ValueError: You cannot define 'immutable' or 'sparse' when 'data_structure' has a value.
sage: G.copy(immutable=True, sparse=False)
Traceback (most recent call last):
...
ValueError: There is no dense immutable backend at the moment.

Which backend ?::

sage: G.copy(data_structure="sparse")._backend
<class 'sage.graphs.base.sparse_graph.SparseGraphBackend'>
sage: G.copy(data_structure="dense")._backend
<class 'sage.graphs.base.dense_graph.DenseGraphBackend'>
sage: G.copy(data_structure="static_sparse")._backend
<class 'sage.graphs.base.static_sparse_backend.StaticSparseBackend'>
sage: G.copy(immutable=True)._backend
<class 'sage.graphs.base.static_sparse_backend.StaticSparseBackend'>
sage: G.copy(immutable=True, sparse=True)._backend
<class 'sage.graphs.base.static_sparse_backend.StaticSparseBackend'>
sage: G.copy(immutable=False, sparse=True)._backend
<class 'sage.graphs.base.sparse_graph.SparseGraphBackend'>
sage: G.copy(immutable=False, sparse=False)._backend
<class 'sage.graphs.base.sparse_graph.SparseGraphBackend'>
sage: Graph(implementation="networkx").copy(implementation='c_graph')._backend
<class 'sage.graphs.base.sparse_graph.SparseGraphBackend'>

Fake immutable graphs::

sage: G._immutable = True
sage: G.copy()._backend
<class 'sage.graphs.base.sparse_graph.SparseGraphBackend'>
"""
# Which data structure should be used ?
if implementation != 'c_graph':
# We do not care about the value of data_structure. But let's check
# the user did not define too much.
if data_structure != None or immutable != None or sparse != None:
raise ValueError("'data_structure' 'immutable' and 'sparse' can"
" only be defined when 'implementation'='c_graph'")
elif data_structure != None:
# data_structure is already defined so there is nothing left to do
# here ! Did the user try to define too much ?
if immutable != None or sparse != None:
raise ValueError("You cannot define 'immutable' or 'sparse' "
"when 'data_structure' has a value.")
# At this point :
# - implementation is 'c_graph'
# - data_structure is None.
elif immutable is True:
data_structure = 'static_sparse'
if sparse is False:
raise ValueError("There is no dense immutable backend at the moment.")
elif immutable is False:
# If the users requests a mutable graph and input is immutable, we
# chose the 'sparse' cgraph backend. Unless the user explicitly
# asked for something different.
if getattr(self, '_immutable', False):
data_structure = 'dense' if sparse is False else 'sparse'
elif sparse is True:
data_structure = "sparse"
elif sparse is False:
data_structure = "dense"

# Immutable copy of an immutable graph ? return self !
if getattr(self, '_immutable', False):
from sage.graphs.base.static_sparse_backend import StaticSparseBackend
if isinstance(self._backend, StaticSparseBackend) and implementation=='c_graph' and (data_structure=='static_sparse' or data_structure is None):
if (isinstance(self._backend, StaticSparseBackend) and
implementation=='c_graph' and
(data_structure=='static_sparse' or data_structure is None)):
return self

if data_structure is None:
from sage.graphs.base.dense_graph import DenseGraphBackend
from sage.graphs.base.sparse_graph import SparseGraphBackend
if isinstance(self._backend, DenseGraphBackend):
data_structure = "dense"
elif isinstance(self._backend, SparseGraphBackend):
data_structure = "sparse"
else:
data_structure = "static_sparse"
data_structure = "sparse"

from copy import copy
G = self.__class__(self, name=self.name(), pos=copy(self._pos), boundary=copy(self._boundary), implementation=implementation, data_structure=data_structure)

Expand Down
26 changes: 19 additions & 7 deletions src/sage/graphs/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,8 +503,7 @@
sage: {G:1}[G]
Traceback (most recent call last):
...
TypeError: This graph is mutable, and thus not hashable. Create an
immutable copy by `g.copy(data_structure='static_sparse')`
TypeError: This graph is mutable, and thus not hashable. Create an immutable copy by `g.copy(data_structure='static_sparse')`
sage: G_immutable = Graph(G, data_structure="static_sparse")
sage: G_immutable == G
True
Expand Down Expand Up @@ -700,6 +699,11 @@ class Graph(GenericGraph):
*Only available when* ``implementation == 'c_graph'``
- ``immutable`` (boolean) -- whether to create a immutable graph. Note that
``immutable=True`` is actually a shortcut for
``data_structure='static_sparse'``. Set to ``False`` by default, only
available when ``implementation='c_graph'``
- ``vertex_labels`` - only for implementation == 'c_graph'.
Whether to allow any object as a vertex (slower), or
only the integers 0, ..., n-1, where n is the number of vertices.
Expand Down Expand Up @@ -961,15 +965,14 @@ class Graph(GenericGraph):
sage: {G:1}[H]
Traceback (most recent call last):
...
TypeError: This graph is mutable, and thus not hashable. Create
an immutable copy by `g.copy(data_structure='static_sparse')`
TypeError: This graph is mutable, and thus not hashable. Create an immutable copy by `g.copy(data_structure='static_sparse')`
If the ``data_structure`` is equal to ``"static_sparse"``, then an
immutable graph results. Note that this does not use the NetworkX data
structure::
sage: G_imm = Graph(g, data_structure="static_sparse")
sage: H_imm = Graph(g, data_structure="static_sparse")
sage: G_imm = Graph(g, immutable=True)
sage: H_imm = Graph(g, immutable=True)
sage: G_imm == H_imm == G == H
True
sage: hasattr(G_imm._backend, "_nxg")
Expand All @@ -984,7 +987,7 @@ def __init__(self, data=None, pos=None, loops=None, format=None,
boundary=None, weighted=None, implementation='c_graph',
data_structure="sparse", vertex_labels=True, name=None,
multiedges=None, convert_empty_dict_labels_to_None=None,
sparse = True):
sparse=True, immutable=False):
"""
TESTS::
Expand Down Expand Up @@ -1078,6 +1081,12 @@ def __init__(self, data=None, pos=None, loops=None, format=None,
sage: G = Graph(boundary=None)
sage: G._boundary
[]
Graphs returned when setting ``immutable=False`` are mutable::
sage: g = graphs.PetersenGraph()
sage: g = Graph(g.edges(),immutable=False)
sage: g.add_edge("Hey", "Heyyyyyyy")
"""
GenericGraph.__init__(self)
msg = ''
Expand Down Expand Up @@ -1493,6 +1502,9 @@ def __init__(self, data=None, pos=None, loops=None, format=None,
if data_structure == "dense":
raise RuntimeError("Multiedge and weighted c_graphs must be sparse.")

if immutable:
data_structure = 'static_sparse'

# If the data structure is static_sparse, we first build a graph
# using the sparse data structure, then reencode the resulting graph
# as a static sparse graph.
Expand Down

0 comments on commit 7860f39

Please sign in to comment.