Skip to content

Commit

Permalink
Merge branch 'fenics_master'
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew McRae committed Jul 16, 2015
2 parents f23e92b + 34f8129 commit 9eeeb70
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 9 deletions.
59 changes: 59 additions & 0 deletions ufl/core/compute_expr_hash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""Non-recursive traversal based hash computation algorithm.
Fast iteration over nodes in an Expr DAG to compute
memoized hashes for all unique nodes.
"""

# Copyright (C) 2015 Martin Sandve Alnes
#
# This file is part of UFL.
#
# UFL is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# UFL is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with UFL. If not, see <http://www.gnu.org/licenses/>.


# This limits the _depth_ of expression trees
_recursion_limit_ = 6400 # should be enough for everyone


def compute_expr_hash(expr):
"""Compute hashes of expr and all its nodes efficiently without using Python recursion."""
if expr._hash is not None:
return expr._hash

stack = [None]*_recursion_limit_
stacksize = 0

ops = expr.ufl_operands
stack[stacksize] = [expr, ops, len(ops)]
stacksize += 1

while stacksize > 0:
entry = stack[stacksize - 1]
e = entry[0]
if e._hash is not None:
# cutoff: don't need to visit children when hash has previously been computed
stacksize -= 1
elif entry[2] == 0:
# all children consumed: trigger memoized hash computation
e._hash = e._ufl_compute_hash_()
stacksize -= 1
else:
# add children to stack to hash them first
entry[2] -= 1
o = entry[1][entry[2]]
oops = o.ufl_operands
stack[stacksize] = [o, oops, len(oops)]
stacksize += 1

return expr._hash
10 changes: 5 additions & 5 deletions ufl/core/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,11 @@ def __init__(self):
def __del__(self):
pass

def __hash__(self):
if self._hash is None:
self._hash = self._ufl_compute_hash_()
return self._hash

# This shows the principal behaviour of the hash function attached in ufl_type:
#def __hash__(self):
# if self._hash is None:
# self._hash = self._ufl_compute_hash_()
# return self._hash

# --- Type traits are added to subclasses by the ufl_type class decorator ---

Expand Down
9 changes: 6 additions & 3 deletions ufl/core/ufl_type.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@


from ufl.core.expr import Expr
from ufl.core.compute_expr_hash import compute_expr_hash

from ufl.common import camel2underscore, EmptyDict

Expand Down Expand Up @@ -216,6 +217,7 @@ def ufl_type(is_abstract=False,
is_restriction=False,
is_evaluation=False,
is_differential=None,
use_default_hash=True,
num_ops=None,
inherit_shape_from_operand=None,
inherit_indices_from_operand=None,
Expand Down Expand Up @@ -311,9 +313,10 @@ def _ufl_expr_rbinop_(self, other):

# Make sure every non-abstract class has its own __hash__ and __eq__.
# Python 3 will set __hash__ to None if cls has __eq__, but we've
# implemented it in Expr and want to inherit it.
if cls.__hash__ is None:
cls.__hash__ = Expr.__hash__
# implemented it in a separate function and want to inherit/use that
# for all types. Allow overriding by setting use_default_hash=False.
if use_default_hash:
cls.__hash__ = compute_expr_hash

# NB! This function conditionally adds some methods to the class!
# This approach significantly reduces the amount of small functions to
Expand Down
5 changes: 4 additions & 1 deletion ufl/corealg/traversal.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
# You should have received a copy of the GNU Lesser General Public License
# along with UFL. If not, see <http://www.gnu.org/licenses/>.

_recursion_limit_ = 2000

# This limits the _depth_ of expression trees
_recursion_limit_ = 6400 # should be enough for everyone


def pre_traversal(expr):
"""Yields o for each tree node o in expr, parent before child."""
Expand Down

0 comments on commit 9eeeb70

Please sign in to comment.