Skip to content

Commit

Permalink
Merge 1276ee8 into b1f6d14
Browse files Browse the repository at this point in the history
  • Loading branch information
chebee7i committed May 9, 2014
2 parents b1f6d14 + 1276ee8 commit 9e2e6c8
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 55 deletions.
3 changes: 1 addition & 2 deletions networkx/algorithms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
from networkx.algorithms.simple_paths import *
from networkx.algorithms.tree import *


import networkx.algorithms.assortativity
import networkx.algorithms.bipartite
import networkx.algorithms.centrality
Expand All @@ -49,7 +48,7 @@
import networkx.algorithms.traversal
import networkx.algorithms.chordal
import networkx.algorithms.operators
import networkx.algorithms.tree
import networkx.algorithms.tree

from networkx.algorithms.bipartite import projected_graph,project,is_bipartite
from networkx.algorithms.isomorphism import (is_isomorphic,could_be_isomorphic,
Expand Down
153 changes: 127 additions & 26 deletions networkx/algorithms/tree/recognition.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,151 @@
#-*- coding: utf-8 -*-
"""
We use the following definitions:
A forest is an (undirected) graph with no cycles.
A tree is a connected forest.
A directed forest is a directed graph whose underlying graph is a forest.
A directed tree is a (weakly) connected, directed forest.
Equivalently: It is a directed graph whose underlying graph is a tree.
Note: Some take the term directed tree to be synonymous with an
arborescence. We do not follow that convention here.
Note: Since the underlying graph is a tree, any orientation defines a DAG
So all directed trees are DAGs. Thus, the definition we use here is,
in fact, equivalent to a polytree.
A DAG is a directed graph with no directed cycles.
Example: A -> B, A -> C, B -> C is a DAG that is not a directed tree.
A polyforest is a DAG that is also a directed forest.
A polytree is a weakly connected polyforest.
Equivalently, a polytree is a DAG whose underlying graph is a tree.
A branching is a polyforest with each edge directed to a different node.
So the maximum in-degree is, at most, one. The maximum number of edges any
branching can have is n-1. In this case, the branching spans the graph, and
we have an arborescence. It is in this sense that the min/max spanning tree
problem is analogous to the min/max arborescence problem.
An arborescence is a (weakly) connected branching. That is, if you look
at the underlying graph, it is a spanning tree. Additionally, all edges
are directed away from a unique root node, for if you had two nodes with
in-degree zero, then weak connectivity would force some other node
to have in-degree of at least 2 (which is not allowed in branchings).
"""

import networkx as nx
from networkx.utils import not_implemented_for
__author__ = """\n""".join(['Ferdinando Papale <ferdinando.papale@gmail.com>'])
__all__ = ['is_tree', 'is_forest']

__author__ = """\n""".join([
'Ferdinando Papale <ferdinando.papale@gmail.com>',
'chebee7i <chebee7i@gmail.com>',
])

@not_implemented_for('directed')
def is_tree(G):
"""Return True if the input graph is a tree

__all__ = ['is_arborescence', 'is_branching', 'is_forest', 'is_tree']

@nx.utils.not_implemented_for('undirected')
def is_arborescence(G):
"""
Returns `True` if `G` is an arborescence.
"""
if not is_tree(G):
return False

if max(G.in_degree().values()) > 1:
return False

return True

@nx.utils.not_implemented_for('undirected')
def is_branching(G):
"""
Returns `True` if `G` is a branching.
A branching is a directed forest with maximum in-degree equal to 1.
Parameters
----------
G : NetworkX Graph
An undirected graph.
G : directed graph
The directed graph to test.
Returns
-------
True if the input graph is a tree
b : bool
A boolean that is `True` if `G` is a branching.
Notes
-----
For undirected graphs only.
"""
n = len(G)
if n == 0:
raise nx.NetworkXPointlessConcept
return nx.number_of_edges(G) == n - 1
if not is_forest(G):
return False

if max(G.in_degree().values()) > 1:
return False

return True

@not_implemented_for('directed')
def is_forest(G):
"""Return True if the input graph is a forest
"""
Returns `True` if G is a forest.
For directed graphs, the direction of edges is ignored, and the graph `G`
is considered to be a directed forest if the underlying graph is a forest.
"""
n = G.number_of_nodes()
if n == 0:
raise nx.exception.NetworkXPointlessConcept('G has no nodes.')

if G.is_directed():
components = nx.weakly_connected_component_subgraphs
else:
components = nx.connected_component_subgraphs

for component in components(G):
# Make sure the component is a tree.
if component.number_of_edges() != component.number_of_nodes() - 1:
return False

return True

def is_tree(G):
"""
Returns `True` if `G` is a tree.
A tree is a simple, connected graph with no cycles.
For directed graphs, the direction of edges is ignored, and the graph `G`
is considered to be a directed tree if the underlying graph is a tree.
Parameters
----------
G : NetworkX Graph
An undirected graph.
G : graph
The graph to test.
Returns
-------
True if the input graph is a forest
b : bool
A boolean that is `True` if `G` is a tree.
Notes
-----
For undirected graphs only.
Directed trees are also known as polytrees. Sometimes, "directed tree"
is defined more restrictively to mean "arboresence" instead.
"""
for graph in nx.connected_component_subgraphs(G):
if not nx.is_tree(graph):
return False
return True
n = G.number_of_nodes()
if n == 0:
raise nx.exception.NetworkXPointlessConcept('G has no nodes.')

if G.is_directed():
is_connected = nx.is_weakly_connected
else:
is_connected = nx.is_connected

# A simple, connected graph with no cycles has n-1 edges.

if G.number_of_edges() != n - 1:
return False

return is_connected(G)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env python

from nose.tools import *
import networkx as nx

Expand All @@ -25,13 +25,7 @@ def setUp(self):
self.T6.add_nodes_from([6,7])
self.T6.add_edge(6,7)

self.F1 = nx.compose(self.T6,self.T3)



self.N1 = nx.DiGraph()

self.N3 = nx.MultiDiGraph()
self.F1 = nx.compose(self.T6, self.T3)

self.N4 = nx.Graph()
self.N4.add_node(1)
Expand All @@ -55,23 +49,6 @@ def test_null(self):
def test_null(self):
nx.is_tree(nx.MultiGraph())


@raises(nx.NetworkXNotImplemented)
def test_digraph(self):
assert_false(nx.is_tree(self.N1))

@raises(nx.NetworkXNotImplemented)
def test_multidigraph(self):
assert_false(nx.is_tree(self.N3))

@raises(nx.NetworkXNotImplemented)
def test_digraph_forest(self):
assert_false(nx.is_forest(self.N1))

@raises(nx.NetworkXNotImplemented)
def test_multidigraph_forest(self):
assert_false(nx.is_forest(self.N3))

def test_is_tree(self):
assert_true(nx.is_tree(self.T2))
assert_true(nx.is_tree(self.T3))
Expand All @@ -94,7 +71,11 @@ def test_is_not_forest(self):
assert_false(nx.is_forest(self.N6))
assert_false(nx.is_forest(self.NF1))


def test_disconnected_graph():
# https://github.com/networkx/networkx/issues/1144
G = nx.Graph()
G.add_edges_from([(0, 1), (1, 2), (2, 0), (3, 4)])
assert_false(nx.is_tree(G))




0 comments on commit 9e2e6c8

Please sign in to comment.