# HG changeset patch
# User Nathann Cohen
# Date 1354362247 3600
# Node ID 76c0c1b4aee6a503ca4569e9b3de9b7cc468cb3f
# Parent 654bc12b640b8bc0fa5da75f62f4ecfda1a351ac
Move methods from GenericGraph to Graph
diff git a/sage/graphs/generic_graph.py b/sage/graphs/generic_graph.py
 a/sage/graphs/generic_graph.py
+++ b/sage/graphs/generic_graph.py
@@ 297,19 +297,6 @@
:meth:`~GenericGraph.genus`  Returns the minimal genus of the graph.
:meth:`~GenericGraph.trace_faces`  A helper function for finding the genus of a graph.
**Graph stuff that should not be in this file:**

.. csvtable::
 :class: contentstable
 :widths: 30, 70
 :delim: 

 :meth:`~GenericGraph.minimum_outdegree_orientation`  Returns an orientation of ``self`` with the smallest possible maximum outdegree
 :meth:`~GenericGraph.matching`  Returns a maximum weighted matching of the graph
 :meth:`~GenericGraph.maximum_average_degree`  Returns the Maximum Average Degree (MAD) of the current graph.
 :meth:`~GenericGraph.cores`  Returns the core number for each vertex in an ordered list.


Methods

"""
@@ 2848,116 +2835,6 @@
import networkx
return networkx.cycle_basis(self.networkx_graph(copy=False))
 def minimum_outdegree_orientation(self, use_edge_labels=False, solver=None, verbose=0):
 r"""
 Returns an orientation of ``self`` with the smallest possible maximum
 outdegree.

 Given a Graph `G`, is is polynomial to compute an orientation
 `D` of the edges of `G` such that the maximum outdegree in
 `D` is minimized. This problem, though, is NPcomplete in the
 weighted case [AMOZ06]_.

 INPUT:

  ``use_edge_labels``  boolean (default: ``False``)

  When set to ``True``, uses edge labels as weights to
 compute the orientation and assumes a weight of `1`
 when there is no value available for a given edge.

  When set to ``False`` (default), gives a weight of 1
 to all the edges.

  ``solver``  (default: ``None``) Specify a Linear Program (LP)
 solver to be used. If set to ``None``, the default one is used. For
 more information on LP solvers and which default solver is used, see
 the method
 :meth:`solve `
 of the class
 :class:`MixedIntegerLinearProgram `.

  ``verbose``  integer (default: ``0``). Sets the level of
 verbosity. Set to 0 by default, which means quiet.

 EXAMPLE:

 Given a complete bipartite graph `K_{n,m}`, the maximum outdegree
 of an optimal orientation is `\left\lceil \frac {nm} {n+m}\right\rceil`::

 sage: g = graphs.CompleteBipartiteGraph(3,4)
 sage: o = g.minimum_outdegree_orientation()
 sage: max(o.out_degree()) == ceil((4*3)/(3+4))
 True

 REFERENCES:

 .. [AMOZ06] Asahiro, Y. and Miyano, E. and Ono, H. and Zenmyo, K.
 Graph orientation algorithms to minimize the maximum outdegree
 Proceedings of the 12th Computing: The Australasian Theory Symposium
 Volume 51, page 20
 Australian Computer Society, Inc. 2006
 """

 if self.is_directed():
 raise ValueError("Cannot compute an orientation of a DiGraph. "+\
 "Please convert it to a Graph if you really mean it.")

 if use_edge_labels:
 from sage.rings.real_mpfr import RR
 weight = lambda u,v : self.edge_label(u,v) if self.edge_label(u,v) in RR else 1
 else:
 weight = lambda u,v : 1

 from sage.numerical.mip import MixedIntegerLinearProgram

 p = MixedIntegerLinearProgram(maximization=False, solver=solver)

 # The orientation of an edge is boolean
 # and indicates whether the edge uv
 # with uv else variable

 for u in self:
 p.add_constraint(p.sum([weight(u,v)*outgoing(u,v,orientation[min(u,v)][max(u,v)]) for v in self.neighbors(u)])degree['max'],max=0)

 p.set_objective(degree['max'])

 p.set_binary(orientation)

 p.solve(log=verbose)

 orientation = p.get_values(orientation)

 # All the edges from self are doubled in O
 # ( one in each direction )
 from sage.graphs.digraph import DiGraph
 O = DiGraph(self)

 # Builds the list of edges that should be removed
 edges=[]

 for u,v in self.edge_iterator(labels=None):
 # assumes uv:
 u,v=v,u

 if orientation[min(u,v)][max(u,v)] == 1:
 edges.append((max(u,v),min(u,v)))
 else:
 edges.append((min(u,v),max(u,v)))

 O.delete_edges(edges)

 return O

### Planarity
def is_planar(self, on_embedding=None, kuratowski=False, set_embedding=False, set_pos=False):
@@ 6937,141 +6814,6 @@
return paths
 def matching(self, value_only=False, algorithm="Edmonds", use_edge_labels=True, solver=None, verbose=0):
 r"""
 Returns a maximum weighted matching of the graph
 represented by the list of its edges. For more information, see the
 `Wikipedia article on matchings
 `_.

 Given a graph `G` such that each edge `e` has a weight `w_e`,
 a maximum matching is a subset `S` of the edges of `G` of
 maximum weight such that no two edges of `S` are incident
 with each other.

 As an optimization problem, it can be expressed as:

 .. math::

 \mbox{Maximize : }&\sum_{e\in G.edges()} w_e b_e\\
 \mbox{Such that : }&\forall v \in G, \sum_{(u,v)\in G.edges()} b_{(u,v)}\leq 1\\
 &\forall x\in G, b_x\mbox{ is a binary variable}

 INPUT:

  ``value_only``  boolean (default: ``False``). When set to
 ``True``, only the cardinal (or the weight) of the matching is
 returned.

  ``algorithm``  string (default: ``"Edmonds"``)

  ``"Edmonds"`` selects Edmonds' algorithm as implemented in NetworkX

  ``"LP"`` uses a Linear Program formulation of the matching problem

  ``use_edge_labels``  boolean (default: ``False``)

  When set to ``True``, computes a weighted matching where each edge
 is weighted by its label. (If an edge has no label, `1` is assumed.)

  When set to ``False``, each edge has weight `1`.

  ``solver``  (default: ``None``) Specify a Linear Program (LP)
 solver to be used. If set to ``None``, the default one is used. For
 more information on LP solvers and which default solver is used, see
 the method
 :meth:`solve `
 of the class
 :class:`MixedIntegerLinearProgram `.

  ``verbose``  integer (default: ``0``). Sets the level of
 verbosity. Set to 0 by default, which means quiet.
 Only useful when ``algorithm == "LP"``.

 ALGORITHM:

 The problem is solved using Edmond's algorithm implemented in
 NetworkX, or using Linear Programming depending on the value of
 ``algorithm``.

 EXAMPLES:

 Maximum matching in a Pappus Graph::

 sage: g = graphs.PappusGraph()
 sage: g.matching(value_only=True)
 9.0

 Same test with the Linear Program formulation::

 sage: g = graphs.PappusGraph()
 sage: g.matching(algorithm="LP", value_only=True)
 9.0

 TESTS:

 If ``algorithm`` is set to anything different from ``"Edmonds"`` or
 ``"LP"``, an exception is raised::

 sage: g = graphs.PappusGraph()
 sage: g.matching(algorithm="somethingdifferent")
 Traceback (most recent call last):
 ...
 ValueError: algorithm must be set to either "Edmonds" or "LP"
 """
 from sage.rings.real_mpfr import RR
 weight = lambda x: x if x in RR else 1

 if algorithm == "Edmonds":
 import networkx
 if use_edge_labels:
 g = networkx.Graph()
 for u, v, l in self.edges():
 g.add_edge(u, v, attr_dict={"weight": weight(l)})
 else:
 g = self.networkx_graph(copy=False)
 d = networkx.max_weight_matching(g)
 if value_only:
 if use_edge_labels:
 return sum([weight(self.edge_label(u, v))
 for u, v in d.iteritems()]) * 0.5
 else:
 return Integer(len(d)/2)
 else:
 return [(u, v, self.edge_label(u, v))
 for u, v in d.iteritems() if u < v]

 elif algorithm == "LP":
 from sage.numerical.mip import MixedIntegerLinearProgram
 g = self
 # returns the weight of an edge considering it may not be
 # weighted ...
 p = MixedIntegerLinearProgram(maximization=True, solver=solver)
 b = p.new_variable(dim=2)
 p.set_objective(
 p.sum([weight(w) * b[min(u, v)][max(u, v)]
 for u, v, w in g.edges()]))
 # for any vertex v, there is at most one edge incident to v in
 # the maximum matching
 for v in g.vertex_iterator():
 p.add_constraint(
 p.sum([b[min(u, v)][max(u, v)]
 for u in g.neighbors(v)]), max=1)
 p.set_binary(b)
 if value_only:
 if use_edge_labels:
 return p.solve(objective_only=True, log=verbose)
 else:
 return Integer(round(p.solve(objective_only=True, log=verbose)))
 else:
 p.solve(log=verbose)
 b = p.get_values(b)
 return [(u, v, w) for u, v, w in g.edges()
 if b[min(u, v)][max(u, v)] == 1]

 else:
 raise ValueError('algorithm must be set to either "Edmonds" or "LP"')

def dominating_set(self, independent=False, value_only=False, solver=None, verbose=0):
r"""
Returns a minimum dominating set of the graph
@@ 9406,111 +9148,6 @@
return 2*Integer(self.size())/Integer(self.order())
 def maximum_average_degree(self, value_only=True, solver = None, verbose = 0):
 r"""
 Returns the Maximum Average Degree (MAD) of the current graph.

 The Maximum Average Degree (MAD) of a graph is defined as
 the average degree of its densest subgraph. More formally,
 ``Mad(G) = \max_{H\subseteq G} Ad(H)``, where `Ad(G)` denotes
 the average degree of `G`.

 This can be computed in polynomial time.

 INPUT:

  ``value_only`` (boolean)  ``True`` by default

  If ``value_only=True``, only the numerical
 value of the `MAD` is returned.

  Else, the subgraph of `G` realizing the `MAD`
 is returned.

  ``solver``  (default: ``None``) Specify a Linear Program (LP)
 solver to be used. If set to ``None``, the default one is used. For
 more information on LP solvers and which default solver is used, see
 the method
 :meth:`solve `
 of the class
 :class:`MixedIntegerLinearProgram `.

  ``verbose``  integer (default: ``0``). Sets the level of
 verbosity. Set to 0 by default, which means quiet.

 EXAMPLES:

 In any graph, the `Mad` is always larger than the average
 degree::

 sage: g = graphs.RandomGNP(20,.3)
 sage: mad_g = g.maximum_average_degree()
 sage: g.average_degree() <= mad_g
 True

 Unlike the average degree, the `Mad` of the disjoint
 union of two graphs is the maximum of the `Mad` of each
 graphs::

 sage: h = graphs.RandomGNP(20,.3)
 sage: mad_h = h.maximum_average_degree()
 sage: (g+h).maximum_average_degree() == max(mad_g, mad_h)
 True

 The subgraph of a regular graph realizing the maximum
 average degree is always the whole graph ::

 sage: g = graphs.CompleteGraph(5)
 sage: mad_g = g.maximum_average_degree(value_only=False)
 sage: g.is_isomorphic(mad_g)
 True

 This also works for complete bipartite graphs ::

 sage: g = graphs.CompleteBipartiteGraph(3,4)
 sage: mad_g = g.maximum_average_degree(value_only=False)
 sage: g.is_isomorphic(mad_g)
 True
 """

 g = self
 from sage.numerical.mip import MixedIntegerLinearProgram

 p = MixedIntegerLinearProgram(maximization=True, solver = solver)

 d = p.new_variable()
 one = p.new_variable()

 # Reorders u and v so that uv and vu are not considered
 # to be different edges
 reorder = lambda u,v : (min(u,v),max(u,v))

 for u,v in g.edge_iterator(labels=False):
 p.add_constraint( one[ reorder(u,v) ]  2*d[u] , max = 0 )
 p.add_constraint( one[ reorder(u,v) ]  2*d[v] , max = 0 )

 p.add_constraint( p.sum([d[v] for v in g]), max = 1)

 p.set_objective( p.sum([ one[reorder(u,v)] for u,v in g.edge_iterator(labels=False)]) )

 obj = p.solve(log = verbose)

 # Paying attention to numerical error :
 # The zero values could be something like 0.000000000001
 # so I can not write l > 0
 # And the nonzero, though they should be equal to
 # 1/(order of the optimal subgraph) may be a bit lower

 # setting the minimum to 1/(10 * size of the whole graph )
 # should be safe :)
 m = 1/(10 *Integer(g.order()))
 g_mad = g.subgraph([v for v,l in p.get_values(d).iteritems() if l>m ])

 if value_only:
 return g_mad.average_degree()
 else:
 return g_mad

def degree_histogram(self):
"""
Returns a list, whose ith entry is the frequency of degree i.
@@ 11275,158 +10912,6 @@
"""
import networkx
return networkx.transitivity(self.networkx_graph(copy=False))

 ### Cores

 def cores(self, k = None, with_labels=False):
 """
 Returns the core number for each vertex in an ordered list.


 **DEFINITIONS**

 * *Kcores* in graph theory were introduced by Seidman in 1983 and by
 Bollobas in 1984 as a method of (destructively) simplifying graph
 topology to aid in analysis and visualization. They have been more
 recently defined as the following by Batagelj et al:

 *Given a graph `G` with vertices set `V` and edges set `E`, the
 `k`core of `G` is the graph obtained from `G` by recursively removing
 the vertices with degree less than `k`, for as long as there are any.*

 This operation can be useful to filter or to study some properties of
 the graphs. For instance, when you compute the 2core of graph G, you
 are cutting all the vertices which are in a tree part of graph. (A
 tree is a graph with no loops). [WPkcore]_

 [PSW1996]_ defines a `k`core of `G` as the largest subgraph (it is
 unique) of `G` with minimum degree at least `k`.

 * Core number of a vertex

 The core number of a vertex `v` is the largest integer `k` such that
 `v` belongs to the `k`core of `G`.

 * Degeneracy

 The *degeneracy* of a graph `G`, usually denoted `\delta^*(G)`, is the
 smallest integer `k` such that the graph `G` can be reduced to the
 empty graph by iteratively removing vertices of degree `\leq
 k`. Equivalently, `\delta^*(G)=k` if `k` is the smallest integer such
 that the `k`core of `G` is empty.

 **IMPLEMENTATION**

 This implementation is based on the NetworkX implementation of
 the algorithm described in [BZ]_.

 **INPUT**

  ``k`` (integer)

 * If ``k = None`` (default), returns the core number for each vertex.

 * If ``k`` is an integer, returns a pair ``(ordering, core)``, where
 ``core`` is the list of vertices in the `k`core of ``self``, and
 ``ordering`` is an elimination order for the other vertices such
 that each vertex is of degree strictly less than `k` when it is to
 be eliminated from the graph.

  ``with_labels`` (boolean)

 * When set to ``False``, and ``k = None``, the method returns a list
 whose `i` th element is the core number of the `i` th vertex. When
 set to ``True``, the method returns a dictionary whose keys are
 vertices, and whose values are the corresponding core numbers.

 By default, ``with_labels = False``.

 REFERENCE:

 .. [WPkcore] Kcore. Wikipedia. (2007). [Online] Available:
 http://en.wikipedia.org/wiki/Kcore

 .. [PSW1996] Boris Pittel, Joel Spencer and Nicholas Wormald. Sudden
 Emergence of a Giant kCore in a Random
 Graph. (1996). J. Combinatorial Theory. Ser B 67. pages
 111151. [Online] Available:
 http://cs.nyu.edu/cs/faculty/spencer/papers/kcore.pdf

 .. [BZ] Vladimir Batagelj and Matjaz Zaversnik. An `O(m)`
 Algorithm for Cores Decomposition of
 Networks. arXiv:cs/0310049v1. [Online] Available:
 http://arxiv.org/abs/cs/0310049

 EXAMPLES::

 sage: (graphs.FruchtGraph()).cores()
 [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
 sage: (graphs.FruchtGraph()).cores(with_labels=True)
 {0: 3, 1: 3, 2: 3, 3: 3, 4: 3, 5: 3, 6: 3, 7: 3, 8: 3, 9: 3, 10: 3, 11: 3}
 sage: a=random_matrix(ZZ,20,x=2,sparse=True, density=.1)
 sage: b=DiGraph(20)
 sage: b.add_edges(a.nonzero_positions())
 sage: cores=b.cores(with_labels=True); cores
 {0: 3, 1: 3, 2: 3, 3: 3, 4: 2, 5: 2, 6: 3, 7: 1, 8: 3, 9: 3, 10: 3, 11: 3, 12: 3, 13: 3, 14: 2, 15: 3, 16: 3, 17: 3, 18: 3, 19: 3}
 sage: [v for v,c in cores.items() if c>=2] # the vertices in the 2core
 [0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

 Checking the 2core of a random lobster is indeed the empty set::

 sage: g = graphs.RandomLobster(20,.5,.5)
 sage: ordering, core = g.cores(2)
 sage: len(core) == 0
 True
 """
 # compute the degrees of each vertex
 degrees=self.degree(labels=True)

 # sort vertices by degree. Store in a list and keep track of
 # where a specific degree starts (effectively, the list is
 # sorted by bins).
 verts= sorted( degrees.keys(), key=lambda x: degrees[x])
 bin_boundaries=[0]
 curr_degree=0
 for i,v in enumerate(verts):
 if degrees[v]>curr_degree:
 bin_boundaries.extend([i]*(degrees[v]curr_degree))
 curr_degree=degrees[v]
 vert_pos = dict((v,pos) for pos,v in enumerate(verts))
 # Set up initial guesses for core and lists of neighbors.
 core= degrees
 nbrs=dict((v,set(self.neighbors(v))) for v in self)
 # form vertex core building up from smallest
 for v in verts:

 # If all the vertices have a degree larger than k, we can
 # return our answer if k != None
 if k is not None and core[v] >= k:
 return verts[:vert_pos[v]], verts[vert_pos[v]:]

 for u in nbrs[v]:
 if core[u] > core[v]:
 nbrs[u].remove(v)

 # cleverly move u to the end of the next smallest
 # bin (i.e., subtract one from the degree of u).
 # We do this by swapping u with the first vertex
 # in the bin that contains u, then incrementing
 # the bin boundary for the bin that contains u.
 pos=vert_pos[u]
 bin_start=bin_boundaries[core[u]]
 vert_pos[u]=bin_start
 vert_pos[verts[bin_start]]=pos
 verts[bin_start],verts[pos]=verts[pos],verts[bin_start]
 bin_boundaries[core[u]]+=1
 core[u] = 1

 if k is not None:
 return verts, []

 if with_labels:
 return core
 else:
 return core.values()
### Distance
diff git a/sage/graphs/graph.py b/sage/graphs/graph.py
 a/sage/graphs/graph.py
+++ b/sage/graphs/graph.py
@@ 62,11 +62,11 @@
:delim: 
:meth:`~Graph.gomory_hu_tree`  Returns a GomoryHu tree of self.
+ :meth:`~Graph.minimum_outdegree_orientation`  Returns an orientation of ``self`` with the smallest possible maximum outdegree
:meth:`~Graph.bounded_outdegree_orientation`  Computes an orientation of ``self`` such that every vertex `v` has outdegree less than `b(v)`
:meth:`~Graph.strong_orientation`  Returns a strongly connected orientation of the current graph.
:meth:`~Graph.degree_constrained_subgraph`  Returns a degreeconstrained subgraph.

**Cliquerelated methods:**
.. csvtable::
@@ 115,8 +115,11 @@
:widths: 30, 70
:delim: 
+ :meth:`~Graph.cores`  Returns the core number for each vertex in an ordered list.
+ :meth:`~Graph.matching`  Returns a maximum weighted matching of the graph
:meth:`~Graph.fractional_chromatic_index`  Computes the fractional chromatic index of ``self``
:meth:`~Graph.modular_decomposition`  Returns the modular decomposition of the current graph.
+ :meth:`~Graph.maximum_average_degree`  Returns the Maximum Average Degree (MAD) of the current graph.
:meth:`~Graph.two_factor_petersen`  Returns a decomposition of the graph into 2factors.
@@ 2446,9 +2449,119 @@
else:
d.add_edge(e)
tmp = (e[0],e[1])

+
return d
+ def minimum_outdegree_orientation(self, use_edge_labels=False, solver=None, verbose=0):
+ r"""
+ Returns an orientation of ``self`` with the smallest possible maximum
+ outdegree.
+
+ Given a Graph `G`, is is polynomial to compute an orientation
+ `D` of the edges of `G` such that the maximum outdegree in
+ `D` is minimized. This problem, though, is NPcomplete in the
+ weighted case [AMOZ06]_.
+
+ INPUT:
+
+  ``use_edge_labels``  boolean (default: ``False``)
+
+  When set to ``True``, uses edge labels as weights to
+ compute the orientation and assumes a weight of `1`
+ when there is no value available for a given edge.
+
+  When set to ``False`` (default), gives a weight of 1
+ to all the edges.
+
+  ``solver``  (default: ``None``) Specify a Linear Program (LP)
+ solver to be used. If set to ``None``, the default one is used. For
+ more information on LP solvers and which default solver is used, see
+ the method
+ :meth:`solve `
+ of the class
+ :class:`MixedIntegerLinearProgram `.
+
+  ``verbose``  integer (default: ``0``). Sets the level of
+ verbosity. Set to 0 by default, which means quiet.
+
+ EXAMPLE:
+
+ Given a complete bipartite graph `K_{n,m}`, the maximum outdegree
+ of an optimal orientation is `\left\lceil \frac {nm} {n+m}\right\rceil`::
+
+ sage: g = graphs.CompleteBipartiteGraph(3,4)
+ sage: o = g.minimum_outdegree_orientation()
+ sage: max(o.out_degree()) == ceil((4*3)/(3+4))
+ True
+
+ REFERENCES:
+
+ .. [AMOZ06] Asahiro, Y. and Miyano, E. and Ono, H. and Zenmyo, K.
+ Graph orientation algorithms to minimize the maximum outdegree
+ Proceedings of the 12th Computing: The Australasian Theory Symposium
+ Volume 51, page 20
+ Australian Computer Society, Inc. 2006
+ """
+
+ if self.is_directed():
+ raise ValueError("Cannot compute an orientation of a DiGraph. "+\
+ "Please convert it to a Graph if you really mean it.")
+
+ if use_edge_labels:
+ from sage.rings.real_mpfr import RR
+ weight = lambda u,v : self.edge_label(u,v) if self.edge_label(u,v) in RR else 1
+ else:
+ weight = lambda u,v : 1
+
+ from sage.numerical.mip import MixedIntegerLinearProgram
+
+ p = MixedIntegerLinearProgram(maximization=False, solver=solver)
+
+ # The orientation of an edge is boolean
+ # and indicates whether the edge uv
+ # with uv else variable
+
+ for u in self:
+ p.add_constraint(p.sum([weight(u,v)*outgoing(u,v,orientation[min(u,v)][max(u,v)]) for v in self.neighbors(u)])degree['max'],max=0)
+
+ p.set_objective(degree['max'])
+
+ p.set_binary(orientation)
+
+ p.solve(log=verbose)
+
+ orientation = p.get_values(orientation)
+
+ # All the edges from self are doubled in O
+ # ( one in each direction )
+ from sage.graphs.digraph import DiGraph
+ O = DiGraph(self)
+
+ # Builds the list of edges that should be removed
+ edges=[]
+
+ for u,v in self.edge_iterator(labels=None):
+ # assumes uv:
+ u,v=v,u
+
+ if orientation[min(u,v)][max(u,v)] == 1:
+ edges.append((max(u,v),min(u,v)))
+ else:
+ edges.append((min(u,v),max(u,v)))
+
+ O.delete_edges(edges)
+
+ return O
+
def bounded_outdegree_orientation(self, bound):
r"""
Computes an orientation of ``self`` such that every vertex `v`
@@ 2790,6 +2903,141 @@
else:
raise ValueError("The 'algorithm' keyword must be set to either 'DLX' or 'MILP'.")
+ def matching(self, value_only=False, algorithm="Edmonds", use_edge_labels=True, solver=None, verbose=0):
+ r"""
+ Returns a maximum weighted matching of the graph
+ represented by the list of its edges. For more information, see the
+ `Wikipedia article on matchings
+ `_.
+
+ Given a graph `G` such that each edge `e` has a weight `w_e`,
+ a maximum matching is a subset `S` of the edges of `G` of
+ maximum weight such that no two edges of `S` are incident
+ with each other.
+
+ As an optimization problem, it can be expressed as:
+
+ .. math::
+
+ \mbox{Maximize : }&\sum_{e\in G.edges()} w_e b_e\\
+ \mbox{Such that : }&\forall v \in G, \sum_{(u,v)\in G.edges()} b_{(u,v)}\leq 1\\
+ &\forall x\in G, b_x\mbox{ is a binary variable}
+
+ INPUT:
+
+  ``value_only``  boolean (default: ``False``). When set to
+ ``True``, only the cardinal (or the weight) of the matching is
+ returned.
+
+  ``algorithm``  string (default: ``"Edmonds"``)
+
+  ``"Edmonds"`` selects Edmonds' algorithm as implemented in NetworkX
+
+  ``"LP"`` uses a Linear Program formulation of the matching problem
+
+  ``use_edge_labels``  boolean (default: ``False``)
+
+  When set to ``True``, computes a weighted matching where each edge
+ is weighted by its label. (If an edge has no label, `1` is assumed.)
+
+  When set to ``False``, each edge has weight `1`.
+
+  ``solver``  (default: ``None``) Specify a Linear Program (LP)
+ solver to be used. If set to ``None``, the default one is used. For
+ more information on LP solvers and which default solver is used, see
+ the method
+ :meth:`solve `
+ of the class
+ :class:`MixedIntegerLinearProgram `.
+
+  ``verbose``  integer (default: ``0``). Sets the level of
+ verbosity. Set to 0 by default, which means quiet.
+ Only useful when ``algorithm == "LP"``.
+
+ ALGORITHM:
+
+ The problem is solved using Edmond's algorithm implemented in
+ NetworkX, or using Linear Programming depending on the value of
+ ``algorithm``.
+
+ EXAMPLES:
+
+ Maximum matching in a Pappus Graph::
+
+ sage: g = graphs.PappusGraph()
+ sage: g.matching(value_only=True)
+ 9.0
+
+ Same test with the Linear Program formulation::
+
+ sage: g = graphs.PappusGraph()
+ sage: g.matching(algorithm="LP", value_only=True)
+ 9.0
+
+ TESTS:
+
+ If ``algorithm`` is set to anything different from ``"Edmonds"`` or
+ ``"LP"``, an exception is raised::
+
+ sage: g = graphs.PappusGraph()
+ sage: g.matching(algorithm="somethingdifferent")
+ Traceback (most recent call last):
+ ...
+ ValueError: algorithm must be set to either "Edmonds" or "LP"
+ """
+ from sage.rings.real_mpfr import RR
+ weight = lambda x: x if x in RR else 1
+
+ if algorithm == "Edmonds":
+ import networkx
+ if use_edge_labels:
+ g = networkx.Graph()
+ for u, v, l in self.edges():
+ g.add_edge(u, v, attr_dict={"weight": weight(l)})
+ else:
+ g = self.networkx_graph(copy=False)
+ d = networkx.max_weight_matching(g)
+ if value_only:
+ if use_edge_labels:
+ return sum([weight(self.edge_label(u, v))
+ for u, v in d.iteritems()]) * 0.5
+ else:
+ return Integer(len(d)/2)
+ else:
+ return [(u, v, self.edge_label(u, v))
+ for u, v in d.iteritems() if u < v]
+
+ elif algorithm == "LP":
+ from sage.numerical.mip import MixedIntegerLinearProgram
+ g = self
+ # returns the weight of an edge considering it may not be
+ # weighted ...
+ p = MixedIntegerLinearProgram(maximization=True, solver=solver)
+ b = p.new_variable(dim=2)
+ p.set_objective(
+ p.sum([weight(w) * b[min(u, v)][max(u, v)]
+ for u, v, w in g.edges()]))
+ # for any vertex v, there is at most one edge incident to v in
+ # the maximum matching
+ for v in g.vertex_iterator():
+ p.add_constraint(
+ p.sum([b[min(u, v)][max(u, v)]
+ for u in g.neighbors(v)]), max=1)
+ p.set_binary(b)
+ if value_only:
+ if use_edge_labels:
+ return p.solve(objective_only=True, log=verbose)
+ else:
+ return Integer(round(p.solve(objective_only=True, log=verbose)))
+ else:
+ p.solve(log=verbose)
+ b = p.get_values(b)
+ return [(u, v, w) for u, v, w in g.edges()
+ if b[min(u, v)][max(u, v)] == 1]
+
+ else:
+ raise ValueError('algorithm must be set to either "Edmonds" or "LP"')
+
def fractional_chromatic_index(self, verbose_constraints = 0, verbose = 0):
r"""
Computes the fractional chromatic index of ``self``
@@ 2814,19 +3062,19 @@
The fractional chromatic index is computed through Linear Programming
through its dual. The LP solved by sage is actually:
 .. MATH::

+ .. MATH::
+
\mbox{Maximize : }&\sum_{e\in E(G)} r_{e}\\
 \mbox{Such that : }&\\
 &\forall M\text{ matching }\subseteq G, \sum_{e\in M}r_{v}\leq 1\\

+ \mbox{Such that : }&\\
+ &\forall M\text{ matching }\subseteq G, \sum_{e\in M}r_{v}\leq 1\\
+
INPUT:
 ``verbose_constraints``  whether to display which constraints are
being generated.

+
 ``verbose``  level of verbosity required from the LP solver

+
.. NOTE::
This implementation can be improved by computing matchings through a
@@ 2838,23 +3086,23 @@
EXAMPLE:

+
The fractional chromatic index of a `C_5` is `5/2`::

+
sage: g = graphs.CycleGraph(5)
sage: g.fractional_chromatic_index()
2.5
"""
from sage.numerical.mip import MixedIntegerLinearProgram

+
g = self.copy()
p = MixedIntegerLinearProgram(constraint_generation = True)
# One variable per edge
r = p.new_variable(dim = 2)
R = lambda x,y : r[x][y] if x`
+ of the class
+ :class:`MixedIntegerLinearProgram `.
+
+  ``verbose``  integer (default: ``0``). Sets the level of
+ verbosity. Set to 0 by default, which means quiet.
+
+ EXAMPLES:
+
+ In any graph, the `Mad` is always larger than the average
+ degree::
+
+ sage: g = graphs.RandomGNP(20,.3)
+ sage: mad_g = g.maximum_average_degree()
+ sage: g.average_degree() <= mad_g
+ True
+
+ Unlike the average degree, the `Mad` of the disjoint
+ union of two graphs is the maximum of the `Mad` of each
+ graphs::
+
+ sage: h = graphs.RandomGNP(20,.3)
+ sage: mad_h = h.maximum_average_degree()
+ sage: (g+h).maximum_average_degree() == max(mad_g, mad_h)
+ True
+
+ The subgraph of a regular graph realizing the maximum
+ average degree is always the whole graph ::
+
+ sage: g = graphs.CompleteGraph(5)
+ sage: mad_g = g.maximum_average_degree(value_only=False)
+ sage: g.is_isomorphic(mad_g)
+ True
+
+ This also works for complete bipartite graphs ::
+
+ sage: g = graphs.CompleteBipartiteGraph(3,4)
+ sage: mad_g = g.maximum_average_degree(value_only=False)
+ sage: g.is_isomorphic(mad_g)
+ True
+ """
+
+ g = self
+ from sage.numerical.mip import MixedIntegerLinearProgram
+
+ p = MixedIntegerLinearProgram(maximization=True, solver = solver)
+
+ d = p.new_variable()
+ one = p.new_variable()
+
+ # Reorders u and v so that uv and vu are not considered
+ # to be different edges
+ reorder = lambda u,v : (min(u,v),max(u,v))
+
+ for u,v in g.edge_iterator(labels=False):
+ p.add_constraint( one[ reorder(u,v) ]  2*d[u] , max = 0 )
+ p.add_constraint( one[ reorder(u,v) ]  2*d[v] , max = 0 )
+
+ p.add_constraint( p.sum([d[v] for v in g]), max = 1)
+
+ p.set_objective( p.sum([ one[reorder(u,v)] for u,v in g.edge_iterator(labels=False)]) )
+
+ obj = p.solve(log = verbose)
+
+ # Paying attention to numerical error :
+ # The zero values could be something like 0.000000000001
+ # so I can not write l > 0
+ # And the nonzero, though they should be equal to
+ # 1/(order of the optimal subgraph) may be a bit lower
+
+ # setting the minimum to 1/(10 * size of the whole graph )
+ # should be safe :)
+ m = 1/(10 *Integer(g.order()))
+ g_mad = g.subgraph([v for v,l in p.get_values(d).iteritems() if l>m ])
+
+ if value_only:
+ return g_mad.average_degree()
+ else:
+ return g_mad
+
def independent_set_of_representatives(self, family, solver=None, verbose=0):
r"""
Returns an independent set of representatives.
@@ 4460,6 +4813,156 @@
### Miscellaneous
+ def cores(self, k = None, with_labels=False):
+ """
+ Returns the core number for each vertex in an ordered list.
+
+
+ **DEFINITIONS**
+
+ * *Kcores* in graph theory were introduced by Seidman in 1983 and by
+ Bollobas in 1984 as a method of (destructively) simplifying graph
+ topology to aid in analysis and visualization. They have been more
+ recently defined as the following by Batagelj et al:
+
+ *Given a graph `G` with vertices set `V` and edges set `E`, the
+ `k`core of `G` is the graph obtained from `G` by recursively removing
+ the vertices with degree less than `k`, for as long as there are any.*
+
+ This operation can be useful to filter or to study some properties of
+ the graphs. For instance, when you compute the 2core of graph G, you
+ are cutting all the vertices which are in a tree part of graph. (A
+ tree is a graph with no loops). [WPkcore]_
+
+ [PSW1996]_ defines a `k`core of `G` as the largest subgraph (it is
+ unique) of `G` with minimum degree at least `k`.
+
+ * Core number of a vertex
+
+ The core number of a vertex `v` is the largest integer `k` such that
+ `v` belongs to the `k`core of `G`.
+
+ * Degeneracy
+
+ The *degeneracy* of a graph `G`, usually denoted `\delta^*(G)`, is the
+ smallest integer `k` such that the graph `G` can be reduced to the
+ empty graph by iteratively removing vertices of degree `\leq
+ k`. Equivalently, `\delta^*(G)=k` if `k` is the smallest integer such
+ that the `k`core of `G` is empty.
+
+ **IMPLEMENTATION**
+
+ This implementation is based on the NetworkX implementation of
+ the algorithm described in [BZ]_.
+
+ **INPUT**
+
+  ``k`` (integer)
+
+ * If ``k = None`` (default), returns the core number for each vertex.
+
+ * If ``k`` is an integer, returns a pair ``(ordering, core)``, where
+ ``core`` is the list of vertices in the `k`core of ``self``, and
+ ``ordering`` is an elimination order for the other vertices such
+ that each vertex is of degree strictly less than `k` when it is to
+ be eliminated from the graph.
+
+  ``with_labels`` (boolean)
+
+ * When set to ``False``, and ``k = None``, the method returns a list
+ whose `i` th element is the core number of the `i` th vertex. When
+ set to ``True``, the method returns a dictionary whose keys are
+ vertices, and whose values are the corresponding core numbers.
+
+ By default, ``with_labels = False``.
+
+ REFERENCE:
+
+ .. [WPkcore] Kcore. Wikipedia. (2007). [Online] Available:
+ http://en.wikipedia.org/wiki/Kcore
+
+ .. [PSW1996] Boris Pittel, Joel Spencer and Nicholas Wormald. Sudden
+ Emergence of a Giant kCore in a Random
+ Graph. (1996). J. Combinatorial Theory. Ser B 67. pages
+ 111151. [Online] Available:
+ http://cs.nyu.edu/cs/faculty/spencer/papers/kcore.pdf
+
+ .. [BZ] Vladimir Batagelj and Matjaz Zaversnik. An `O(m)`
+ Algorithm for Cores Decomposition of
+ Networks. arXiv:cs/0310049v1. [Online] Available:
+ http://arxiv.org/abs/cs/0310049
+
+ EXAMPLES::
+
+ sage: (graphs.FruchtGraph()).cores()
+ [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
+ sage: (graphs.FruchtGraph()).cores(with_labels=True)
+ {0: 3, 1: 3, 2: 3, 3: 3, 4: 3, 5: 3, 6: 3, 7: 3, 8: 3, 9: 3, 10: 3, 11: 3}
+ sage: a=random_matrix(ZZ,20,x=2,sparse=True, density=.1)
+ sage: b=DiGraph(20)
+ sage: b.add_edges(a.nonzero_positions())
+ sage: cores=b.cores(with_labels=True); cores
+ {0: 3, 1: 3, 2: 3, 3: 3, 4: 2, 5: 2, 6: 3, 7: 1, 8: 3, 9: 3, 10: 3, 11: 3, 12: 3, 13: 3, 14: 2, 15: 3, 16: 3, 17: 3, 18: 3, 19: 3}
+ sage: [v for v,c in cores.items() if c>=2] # the vertices in the 2core
+ [0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
+
+ Checking the 2core of a random lobster is indeed the empty set::
+
+ sage: g = graphs.RandomLobster(20,.5,.5)
+ sage: ordering, core = g.cores(2)
+ sage: len(core) == 0
+ True
+ """
+ # compute the degrees of each vertex
+ degrees=self.degree(labels=True)
+
+ # sort vertices by degree. Store in a list and keep track of
+ # where a specific degree starts (effectively, the list is
+ # sorted by bins).
+ verts= sorted( degrees.keys(), key=lambda x: degrees[x])
+ bin_boundaries=[0]
+ curr_degree=0
+ for i,v in enumerate(verts):
+ if degrees[v]>curr_degree:
+ bin_boundaries.extend([i]*(degrees[v]curr_degree))
+ curr_degree=degrees[v]
+ vert_pos = dict((v,pos) for pos,v in enumerate(verts))
+ # Set up initial guesses for core and lists of neighbors.
+ core= degrees
+ nbrs=dict((v,set(self.neighbors(v))) for v in self)
+ # form vertex core building up from smallest
+ for v in verts:
+
+ # If all the vertices have a degree larger than k, we can
+ # return our answer if k != None
+ if k is not None and core[v] >= k:
+ return verts[:vert_pos[v]], verts[vert_pos[v]:]
+
+ for u in nbrs[v]:
+ if core[u] > core[v]:
+ nbrs[u].remove(v)
+
+ # cleverly move u to the end of the next smallest
+ # bin (i.e., subtract one from the degree of u).
+ # We do this by swapping u with the first vertex
+ # in the bin that contains u, then incrementing
+ # the bin boundary for the bin that contains u.
+ pos=vert_pos[u]
+ bin_start=bin_boundaries[core[u]]
+ vert_pos[u]=bin_start
+ vert_pos[verts[bin_start]]=pos
+ verts[bin_start],verts[pos]=verts[pos],verts[bin_start]
+ bin_boundaries[core[u]]+=1
+ core[u] = 1
+
+ if k is not None:
+ return verts, []
+
+ if with_labels:
+ return core
+ else:
+ return core.values()
+
def modular_decomposition(self):
r"""
Returns the modular decomposition of the current graph.