@@ -923,8 +923,8 @@ def is_directed(self):
923
923
# Properties
924
924
925
925
def is_directed_acyclic (self , certificate = False ):
926
- """
927
- Return whether the digraph is acyclic or not.
926
+ r """
927
+ Check whether the digraph is acyclic or not.
928
928
929
929
A directed graph is acyclic if for any vertex `v`, there is no directed
930
930
path that starts and ends at `v`. Every directed acyclic graph (DAG)
@@ -943,8 +943,8 @@ def is_directed_acyclic(self, certificate=False):
943
943
* When ``certificate=True``:
944
944
945
945
* If the graph is acyclic, returns a pair ``(True, ordering)`` where
946
- ``ordering`` is a list of the vertices such that ``u` ` appears
947
- before ``v`` in ``ordering`` if ``u, v` ` is an edge.
946
+ ``ordering`` is a list of the vertices such that `u ` appears
947
+ before `v` in ``ordering`` if `uv ` is an edge.
948
948
949
949
* Else, returns a pair ``(False, cycle)`` where ``cycle`` is a list of
950
950
vertices representing a circuit in the graph.
@@ -1272,8 +1272,7 @@ def in_degree(self, vertices=None, labels=False):
1272
1272
return self ._backend .in_degree (vertices )
1273
1273
elif labels :
1274
1274
return {v : d for v , d in self .in_degree_iterator (vertices , labels = labels )}
1275
- else :
1276
- return list (self .in_degree_iterator (vertices , labels = labels ))
1275
+ return list (self .in_degree_iterator (vertices , labels = labels ))
1277
1276
1278
1277
def in_degree_iterator (self , vertices = None , labels = False ):
1279
1278
"""
@@ -1343,8 +1342,7 @@ def out_degree(self, vertices=None, labels=False):
1343
1342
return self ._backend .out_degree (vertices )
1344
1343
elif labels :
1345
1344
return {v : d for v , d in self .out_degree_iterator (vertices , labels = labels )}
1346
- else :
1347
- return list (self .out_degree_iterator (vertices , labels = labels ))
1345
+ return list (self .out_degree_iterator (vertices , labels = labels ))
1348
1346
1349
1347
def out_degree_iterator (self , vertices = None , labels = False ):
1350
1348
"""
@@ -1631,14 +1629,16 @@ def feedback_edge_set(self, constraint_generation=True, value_only=False,
1631
1629
if self .has_loops ():
1632
1630
# We solve the problem on a copy without loops of the digraph
1633
1631
D = DiGraph (self .edges (sort = False ), multiedges = self .allows_multiple_edges (), loops = True )
1634
- D .allow_loops (False )
1632
+ loops = D .loops (labels = None )
1633
+ D .delete_edges (loops )
1634
+ D .allow_loops (False , check = False )
1635
1635
FAS = D .feedback_edge_set (constraint_generation = constraint_generation ,
1636
1636
value_only = value_only , solver = solver , verbose = verbose ,
1637
1637
integrality_tolerance = integrality_tolerance )
1638
1638
if value_only :
1639
- return FAS + self . number_of_loops ( )
1639
+ return FAS + len ( loops )
1640
1640
else :
1641
- return FAS + self . loops ( labels = None )
1641
+ return FAS + loops
1642
1642
1643
1643
if not self .is_strongly_connected ():
1644
1644
# If the digraph is not strongly connected, we solve the problem on
@@ -1647,6 +1647,8 @@ def feedback_edge_set(self, constraint_generation=True, value_only=False,
1647
1647
FAS = 0 if value_only else []
1648
1648
1649
1649
for h in self .strongly_connected_components_subgraphs ():
1650
+ if not h .size ():
1651
+ continue
1650
1652
if value_only :
1651
1653
FAS += h .feedback_edge_set (constraint_generation = constraint_generation ,
1652
1654
value_only = True , solver = solver , verbose = verbose ,
@@ -1691,9 +1693,8 @@ def feedback_edge_set(self, constraint_generation=True, value_only=False,
1691
1693
if isok :
1692
1694
if value_only :
1693
1695
return sum (1 for e in self .edge_iterator (labels = False ) if val [e ])
1694
- else :
1695
- # listing the edges contained in the MFAS
1696
- return [e for e in self .edge_iterator (labels = False ) if val [e ]]
1696
+ # listing the edges contained in the MFAS
1697
+ return [e for e in self .edge_iterator (labels = False ) if val [e ]]
1697
1698
1698
1699
# There is a circuit left. Let's add the corresponding
1699
1700
# constraint !
@@ -1736,28 +1737,92 @@ def feedback_edge_set(self, constraint_generation=True, value_only=False,
1736
1737
1737
1738
if value_only :
1738
1739
return sum (1 for e in self .edge_iterator (labels = False ) if b_sol [e ])
1739
- else :
1740
- return [e for e in self .edge_iterator (labels = False ) if b_sol [e ]]
1740
+ return [e for e in self .edge_iterator (labels = False ) if b_sol [e ]]
1741
1741
1742
1742
# Construction
1743
1743
1744
- def reverse (self ):
1744
+ def reverse (self , immutable = None ):
1745
1745
"""
1746
1746
Return a copy of digraph with edges reversed in direction.
1747
1747
1748
+ INPUT:
1749
+
1750
+ - ``immutable`` -- boolean (default: ``None``); whether to return an
1751
+ immutable digraph or not. By default (``None``), the returned digraph
1752
+ has the same setting than ``self``. That is, if ``self`` is immutable,
1753
+ the returned digraph also is.
1754
+
1748
1755
EXAMPLES::
1749
1756
1750
- sage: D = DiGraph({0: [1,2,3], 1: [0,2], 2: [3], 3: [4], 4: [0,5], 5: [1]})
1751
- sage: D.reverse()
1757
+ sage: adj = {0: [1,2,3], 1: [0,2], 2: [3], 3: [4], 4: [0,5], 5: [1]}
1758
+ sage: D = DiGraph(adj)
1759
+ sage: R = D.reverse(); R
1752
1760
Reverse of (): Digraph on 6 vertices
1761
+ sage: H = R.reverse()
1762
+ sage: adj == H.to_dictionary()
1763
+ True
1764
+
1765
+ TESTS::
1766
+
1767
+ sage: adj = {0: [1, 1], 1: [1]}
1768
+ sage: D = DiGraph(adj, immutable=True, multiedges=True, loops=True)
1769
+ sage: R = D.reverse()
1770
+ sage: R.is_immutable() and R.allows_loops() and R.allows_multiple_edges()
1771
+ True
1772
+ sage: adj == R.reverse().to_dictionary(multiple_edges=True)
1773
+ True
1774
+
1775
+ Check the behavior of parameter ``immutable``::
1776
+
1777
+ sage: D = DiGraph([(0, 1)], immutable=False)
1778
+ sage: R = D.reverse()
1779
+ sage: R.is_immutable()
1780
+ False
1781
+ sage: R = D.reverse(immutable=True)
1782
+ sage: R.is_immutable()
1783
+ True
1784
+ sage: H = R.reverse()
1785
+ sage: H.is_immutable()
1786
+ True
1787
+ sage: H = R.reverse(immutable=False)
1788
+ sage: H.is_immutable()
1789
+ False
1753
1790
"""
1754
- H = DiGraph (multiedges = self .allows_multiple_edges (), loops = self .allows_loops ())
1791
+ from sage .graphs .base .dense_graph import DenseGraphBackend
1792
+ if isinstance (self ._backend , DenseGraphBackend ):
1793
+ data_structure = "dense"
1794
+ else :
1795
+ data_structure = "sparse"
1796
+
1797
+ H = DiGraph (data_structure = data_structure ,
1798
+ multiedges = self .allows_multiple_edges (), loops = self .allows_loops (),
1799
+ pos = copy (self ._pos ), weighted = self .weighted (),
1800
+ hash_labels = self ._hash_labels )
1755
1801
H .add_vertices (self )
1756
1802
H .add_edges ((v , u , d ) for u , v , d in self .edge_iterator ())
1757
1803
name = self .name ()
1758
1804
if name is None :
1759
1805
name = ''
1760
1806
H .name ("Reverse of (%s)" % name )
1807
+
1808
+ attributes_to_copy = ('_assoc' , '_embedding' )
1809
+ for attr in attributes_to_copy :
1810
+ if hasattr (self , attr ):
1811
+ copy_attr = {}
1812
+ old_attr = getattr (self , attr )
1813
+ if isinstance (old_attr , dict ):
1814
+ for v , value in old_attr .items ():
1815
+ try :
1816
+ copy_attr [v ] = value .copy ()
1817
+ except AttributeError :
1818
+ copy_attr [v ] = copy (value )
1819
+ setattr (H , attr , copy_attr )
1820
+ else :
1821
+ setattr (H , attr , copy (old_attr ))
1822
+
1823
+ if immutable or (immutable is None and self .is_immutable ()):
1824
+ return H .copy (immutable = True )
1825
+
1761
1826
return H
1762
1827
1763
1828
def reverse_edge (self , u , v = None , label = None , inplace = True , multiedges = None ):
0 commit comments