Skip to content

Commit

Permalink
Merge pull request #29 from pradal/traversal
Browse files Browse the repository at this point in the history
Add traversal algo on graph from container
  • Loading branch information
pradal authored Jan 22, 2024
2 parents 59828e2 + 58104a7 commit 894baa4
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/openalea/core/graph/traversal/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .tree import pre_order, post_order, level_order
from .graph import topological_sort, breadth_first_search
75 changes: 75 additions & 0 deletions src/openalea/core/graph/traversal/graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
# -*- python -*-
#
# OpenAlea.Container
#
# Copyright 2008-2009 INRIA - CIRAD - INRA
#
# File author(s): Christophe Pradal <christophe.pradal.at.cirad.fr>
#
# Distributed under the Cecill-C License.
# See accompanying file LICENSE.txt or copy at
# http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html
#
# OpenAlea WebSite : http://openalea.gforge.inria.fr
#
###############################################################################

"""
This module provides several implementation of traversal on a directed graph.
"""

__license__= "Cecill-C"
__revision__=" $Id$ "

from collections import deque

def topological_sort(graph, vtx_id, visited = None):
'''
Topolofgical sort of a directed graph implementing the
:class:`openalea.container.interface.graph.IGraph` interface.
Return an iterator on the vertices.
:Parameters:
- `graph`: a directed graph
- vtx_id: a vertex_identifier
.. note :: This is a non recursive implementation.
'''
if visited is None:
visited = {}

yield vtx_id
visited[vtx_id] = True
for vid in graph.out_neighbors(vtx_id):
if vid in visited:
continue
for node in topological_sort(graph, vid, visited):
yield node


def breadth_first_search(graph, vtx_id):
'''
Breadth first search of a graph implementing the
:class:`openalea.container.interface.graph.IGraph` interface.
Return an iterator on the vertices.
:Parameters:
- `graph`: a directed graph
- vtx_id: a vertex_identifier
.. note :: This is a non recursive implementation.
'''
visited = {}

queue = deque()
queue.append(vtx_id)

while queue:
vid = queue.popleft()
if vid in visited:
continue
yield vid
visited[vid] = True
queue.extend(graph.out_neighbors(vid))

124 changes: 124 additions & 0 deletions src/openalea/core/graph/traversal/tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
# -*- python -*-
#
# OpenAlea.Container
#
# Copyright 2008-2009 INRIA - CIRAD - INRA
#
# File author(s): Christophe Pradal <christophe.pradal.at.cirad.fr>
#
# Distributed under the Cecill-C License.
# See accompanying file LICENSE.txt or copy at
# http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html
#
# OpenAlea WebSite : http://openalea.gforge.inria.fr
#
###############################################################################

'''
This module provides different traversal for tree data structure
implemented :class:`openalea.container.interface.tree` interface.
'''

__docformat__ = "restructuredtext"
__license__ = "Cecill-C"
__revision__ = " $Id$ "

from collections import deque

def pre_order(tree, vtx_id, edge_type_property = None):
'''
Traverse a tree in a prefix way.
(root then children)
Return an iterator on vertices.
If an edge_type_property is given, visit first branches rather than successor.
This is a non recursive implementation.
'''

if edge_type_property is None:
yield vtx_id
for vid in tree.children(vtx_id):
for node in pre_order(tree, vid):
yield node
else:

# 1. select first '+' edges
successor = []
yield vtx_id
for vid in tree.children(vtx_id):
if edge_type_property.get(vid) == '<':
successor.append(vid)
continue

for node in pre_order(tree, vid):
yield node

# 2. select then '<' edges
for vid in successor:
for node in pre_order(tree, vid):
yield node


def post_order(tree, vtx_id):
'''
Traverse a tree in a postfix way.
(from leaves to root)
'''
for vid in tree.children(vtx_id):
for node in post_order(tree, vid):
yield node
yield vtx_id

def level_order(tree, vtx_id):
''' Traverse the vertices in a level order.
Traverse the root node, then its children and so on.
'''
queue = deque()
queue.append(vtx_id)

while queue:
vid = queue.popleft()
yield vid
queue.extend(tree.children(vid))


def depth_order(tree, vtx_id):
'''Traverse all the leaves first.
Then their parent until the root.
.. todo:: To implement
'''
raise NotImplementedError


def traverse_tree(tree, vtx_id, visitor):
'''
Traverse a tree in a prefix or postfix way.
We call a visitor for each vertex.
This is usefull for printing, cmputing or storing vertices
in a specific order.
See boost.graph.
'''

yield visitor.pre_order(vtx_id)

for v in tree.children(vtx_id):
for res in traverse_tree(tree, v, visitor):
yield res

yield visitor.post_order(vtx_id)


class Visitor(object):
''' Used during a tree traversal. '''

def pre_order(self, vtx_id):
pass

def post_order(self, vtx_id):
pass

0 comments on commit 894baa4

Please sign in to comment.