|
12 | 12 | :widths: 30, 70 |
13 | 13 | :delim: | |
14 | 14 |
|
| 15 | + :meth:`orient` | Return an oriented version of `G` according the input function `f`. |
| 16 | + :meth:`acyclic_orientations` | Return an iterator over all acyclic orientations of an undirected graph `G`. |
15 | 17 | :meth:`strong_orientations_iterator` | Return an iterator over all strong orientations of a graph `G` |
16 | 18 | :meth:`random_orientation` | Return a random orientation of a graph `G` |
17 | 19 |
|
|
28 | 30 | # **************************************************************************** |
29 | 31 | # Copyright (C) 2017 Kolja Knauer <[email protected]> |
30 | 32 | # 2017 Petru Valicov <[email protected]> |
31 | | -# 2017-2023 David Coudert <[email protected]> |
| 33 | +# 2017-2024 David Coudert <[email protected]> |
32 | 34 | # |
33 | 35 | # This program is free software: you can redistribute it and/or modify |
34 | 36 | # it under the terms of the GNU General Public License as published by |
|
41 | 43 | from sage.graphs.digraph import DiGraph |
42 | 44 |
|
43 | 45 |
|
| 46 | +def orient(G, f, weighted=None, data_structure=None, sparse=None, |
| 47 | + immutable=None, hash_labels=None): |
| 48 | + r""" |
| 49 | + Return an oriented version of `G` according the input function `f`. |
| 50 | +
|
| 51 | + INPUT: |
| 52 | +
|
| 53 | + - ``G`` -- an undirected graph |
| 54 | +
|
| 55 | + - ``f`` -- a function that inputs an edge and outputs an orientation of this |
| 56 | + edge |
| 57 | +
|
| 58 | + - ``weighted`` -- boolean (default: ``None``); weightedness for the oriented |
| 59 | + digraph. By default (``None``), the graph and its orientation will behave |
| 60 | + the same. |
| 61 | +
|
| 62 | + - ``sparse`` -- boolean (default: ``None``); ``sparse=True`` is an alias for |
| 63 | + ``data_structure="sparse"``, and ``sparse=False`` is an alias for |
| 64 | + ``data_structure="dense"``. Only used when ``data_structure=None``. |
| 65 | +
|
| 66 | + - ``data_structure`` -- string (default: ``None``); one of ``'sparse'``, |
| 67 | + ``'static_sparse'``, or ``'dense'``. See the documentation of |
| 68 | + :class:`DiGraph`. |
| 69 | +
|
| 70 | + - ``immutable`` -- boolean (default: ``None``); whether to create a |
| 71 | + mutable/immutable digraph. Only used when ``data_structure=None``. |
| 72 | +
|
| 73 | + * ``immutable=None`` (default) means that the graph and its orientation |
| 74 | + will behave the same way. |
| 75 | +
|
| 76 | + * ``immutable=True`` is a shortcut for ``data_structure='static_sparse'`` |
| 77 | +
|
| 78 | + * ``immutable=False`` means that the created digraph is mutable. When used |
| 79 | + to orient an immutable graph, the data structure used is ``'sparse'`` |
| 80 | + unless anything else is specified. |
| 81 | +
|
| 82 | + - ``hash_labels`` -- boolean (default: ``None``); whether to include edge |
| 83 | + labels during hashing of the oriented digraph. This parameter defaults to |
| 84 | + ``True`` if the graph is weighted. This parameter is ignored when |
| 85 | + parameter ``immutable`` is not ``True``. Beware that trying to hash |
| 86 | + unhashable labels will raise an error. |
| 87 | +
|
| 88 | + OUTPUT: a :class:`DiGraph` object |
| 89 | +
|
| 90 | + .. NOTE:: |
| 91 | +
|
| 92 | + This method behaves similarly to method |
| 93 | + :meth:`~sage.graphs.generic_graph.GenericGraph.copy`. That is, the |
| 94 | + returned digraph uses the same data structure by default, unless the |
| 95 | + user asks to use another data structure, and the attributes of the input |
| 96 | + graph are copied. |
| 97 | +
|
| 98 | + EXAMPLES:: |
| 99 | +
|
| 100 | + sage: G = graphs.CycleGraph(4); G |
| 101 | + Cycle graph: Graph on 4 vertices |
| 102 | + sage: D = G.orient(lambda e:e if e[0] < e[1] else (e[1], e[0], e[2])); D |
| 103 | + Orientation of Cycle graph: Digraph on 4 vertices |
| 104 | + sage: sorted(D.edges(labels=False)) |
| 105 | + [(0, 1), (0, 3), (1, 2), (2, 3)] |
| 106 | +
|
| 107 | + TESTS: |
| 108 | +
|
| 109 | + We make sure that one can get an immutable orientation by providing the |
| 110 | + ``data_structure`` optional argument:: |
| 111 | +
|
| 112 | + sage: def foo(e): |
| 113 | + ....: return e if e[0] < e[1] else (e[1], e[0], e[2]) |
| 114 | + sage: G = graphs.CycleGraph(4) |
| 115 | + sage: D = G.orient(foo, data_structure='static_sparse') |
| 116 | + sage: D.is_immutable() |
| 117 | + True |
| 118 | + sage: D = G.orient(foo, immutable=True) |
| 119 | + sage: D.is_immutable() |
| 120 | + True |
| 121 | +
|
| 122 | + Bad input:: |
| 123 | +
|
| 124 | + sage: G.orient(foo, data_structure='sparse', sparse=False) |
| 125 | + Traceback (most recent call last): |
| 126 | + ... |
| 127 | + ValueError: you cannot define 'immutable' or 'sparse' when 'data_structure' has a value |
| 128 | + sage: G.orient(foo, data_structure='sparse', immutable=True) |
| 129 | + Traceback (most recent call last): |
| 130 | + ... |
| 131 | + ValueError: you cannot define 'immutable' or 'sparse' when 'data_structure' has a value |
| 132 | + sage: G.orient(foo, immutable=True, sparse=False) |
| 133 | + Traceback (most recent call last): |
| 134 | + ... |
| 135 | + ValueError: there is no dense immutable backend at the moment |
| 136 | +
|
| 137 | + Which backend? :: |
| 138 | +
|
| 139 | + sage: G.orient(foo, data_structure='sparse')._backend |
| 140 | + <sage.graphs.base.sparse_graph.SparseGraphBackend object at ...> |
| 141 | + sage: G.orient(foo, data_structure='dense')._backend |
| 142 | + <sage.graphs.base.dense_graph.DenseGraphBackend object at ...> |
| 143 | + sage: G.orient(foo, data_structure='static_sparse')._backend |
| 144 | + <sage.graphs.base.static_sparse_backend.StaticSparseBackend object at ...> |
| 145 | + sage: G.orient(foo, immutable=True)._backend |
| 146 | + <sage.graphs.base.static_sparse_backend.StaticSparseBackend object at ...> |
| 147 | + sage: G.orient(foo, immutable=True, sparse=True)._backend |
| 148 | + <sage.graphs.base.static_sparse_backend.StaticSparseBackend object at ...> |
| 149 | + sage: G.orient(foo, immutable=False, sparse=True)._backend |
| 150 | + <sage.graphs.base.sparse_graph.SparseGraphBackend object at ...> |
| 151 | + sage: G.orient(foo, immutable=False, sparse=False)._backend |
| 152 | + <sage.graphs.base.sparse_graph.SparseGraphBackend object at ...> |
| 153 | + sage: G.orient(foo, data_structure=None, immutable=None, sparse=True)._backend |
| 154 | + <sage.graphs.base.sparse_graph.SparseGraphBackend object at ...> |
| 155 | + sage: G.orient(foo, data_structure=None, immutable=None, sparse=False)._backend |
| 156 | + <sage.graphs.base.dense_graph.DenseGraphBackend object at ...> |
| 157 | + sage: G.orient(foo, data_structure=None, immutable=None, sparse=None)._backend |
| 158 | + <sage.graphs.base.sparse_graph.SparseGraphBackend object at ...> |
| 159 | + sage: H = Graph(data_structure='dense') |
| 160 | + sage: H.orient(foo, data_structure=None, immutable=None, sparse=None)._backend |
| 161 | + <sage.graphs.base.dense_graph.DenseGraphBackend object at ...> |
| 162 | + """ |
| 163 | + # Which data structure should be used ? |
| 164 | + if data_structure is not None: |
| 165 | + # data_structure is already defined so there is nothing left to do |
| 166 | + # here. Did the user try to define too much ? |
| 167 | + if immutable is not None or sparse is not None: |
| 168 | + raise ValueError("you cannot define 'immutable' or 'sparse' " |
| 169 | + "when 'data_structure' has a value") |
| 170 | + # At this point, data_structure is None. |
| 171 | + elif immutable is True: |
| 172 | + data_structure = 'static_sparse' |
| 173 | + if sparse is False: |
| 174 | + raise ValueError("there is no dense immutable backend at the moment") |
| 175 | + elif immutable is False: |
| 176 | + # If the user requests a mutable digraph and input is immutable, we |
| 177 | + # choose the 'sparse' cgraph backend. Unless the user explicitly |
| 178 | + # asked for something different. |
| 179 | + if G.is_immutable(): |
| 180 | + data_structure = 'dense' if sparse is False else 'sparse' |
| 181 | + elif sparse is True: |
| 182 | + data_structure = "sparse" |
| 183 | + elif sparse is False: |
| 184 | + data_structure = "dense" |
| 185 | + |
| 186 | + if data_structure is None: |
| 187 | + from sage.graphs.base.dense_graph import DenseGraphBackend |
| 188 | + if isinstance(G._backend, DenseGraphBackend): |
| 189 | + data_structure = "dense" |
| 190 | + else: |
| 191 | + data_structure = "sparse" |
| 192 | + |
| 193 | + if weighted is None: |
| 194 | + weighted = G.weighted() |
| 195 | + |
| 196 | + edges = (f(e) for e in G.edge_iterator()) |
| 197 | + D = DiGraph([G, edges], format='vertices_and_edges', |
| 198 | + data_structure=data_structure, |
| 199 | + loops=G.allows_loops(), |
| 200 | + multiedges=G.allows_multiple_edges(), |
| 201 | + name=f"Orientation of {G.name()}", |
| 202 | + pos=copy(G._pos), weighted=weighted, |
| 203 | + hash_labels=hash_labels) |
| 204 | + |
| 205 | + attributes_to_copy = ('_assoc', '_embedding') |
| 206 | + for attr in attributes_to_copy: |
| 207 | + if hasattr(G, attr): |
| 208 | + copy_attr = {} |
| 209 | + old_attr = getattr(G, attr) |
| 210 | + if isinstance(old_attr, dict): |
| 211 | + for v, value in old_attr.items(): |
| 212 | + try: |
| 213 | + copy_attr[v] = value.copy() |
| 214 | + except AttributeError: |
| 215 | + copy_attr[v] = copy(value) |
| 216 | + setattr(D, attr, copy_attr) |
| 217 | + else: |
| 218 | + setattr(D, attr, copy(old_attr)) |
| 219 | + |
| 220 | + return D |
| 221 | + |
| 222 | + |
44 | 223 | def acyclic_orientations(G): |
45 | 224 | r""" |
46 | 225 | Return an iterator over all acyclic orientations of an undirected graph `G`. |
|
0 commit comments