Skip to content

Commit 9eeeb70

Browse files
author
Andrew McRae
committed
Merge branch 'fenics_master'
2 parents f23e92b + 34f8129 commit 9eeeb70

File tree

4 files changed

+74
-9
lines changed

4 files changed

+74
-9
lines changed

ufl/core/compute_expr_hash.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""Non-recursive traversal based hash computation algorithm.
2+
3+
Fast iteration over nodes in an Expr DAG to compute
4+
memoized hashes for all unique nodes.
5+
"""
6+
7+
# Copyright (C) 2015 Martin Sandve Alnes
8+
#
9+
# This file is part of UFL.
10+
#
11+
# UFL is free software: you can redistribute it and/or modify
12+
# it under the terms of the GNU Lesser General Public License as published by
13+
# the Free Software Foundation, either version 3 of the License, or
14+
# (at your option) any later version.
15+
#
16+
# UFL is distributed in the hope that it will be useful,
17+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
# GNU Lesser General Public License for more details.
20+
#
21+
# You should have received a copy of the GNU Lesser General Public License
22+
# along with UFL. If not, see <http://www.gnu.org/licenses/>.
23+
24+
25+
# This limits the _depth_ of expression trees
26+
_recursion_limit_ = 6400 # should be enough for everyone
27+
28+
29+
def compute_expr_hash(expr):
30+
"""Compute hashes of expr and all its nodes efficiently without using Python recursion."""
31+
if expr._hash is not None:
32+
return expr._hash
33+
34+
stack = [None]*_recursion_limit_
35+
stacksize = 0
36+
37+
ops = expr.ufl_operands
38+
stack[stacksize] = [expr, ops, len(ops)]
39+
stacksize += 1
40+
41+
while stacksize > 0:
42+
entry = stack[stacksize - 1]
43+
e = entry[0]
44+
if e._hash is not None:
45+
# cutoff: don't need to visit children when hash has previously been computed
46+
stacksize -= 1
47+
elif entry[2] == 0:
48+
# all children consumed: trigger memoized hash computation
49+
e._hash = e._ufl_compute_hash_()
50+
stacksize -= 1
51+
else:
52+
# add children to stack to hash them first
53+
entry[2] -= 1
54+
o = entry[1][entry[2]]
55+
oops = o.ufl_operands
56+
stack[stacksize] = [o, oops, len(oops)]
57+
stacksize += 1
58+
59+
return expr._hash

ufl/core/expr.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,11 @@ def __init__(self):
116116
def __del__(self):
117117
pass
118118

119-
def __hash__(self):
120-
if self._hash is None:
121-
self._hash = self._ufl_compute_hash_()
122-
return self._hash
123-
119+
# This shows the principal behaviour of the hash function attached in ufl_type:
120+
#def __hash__(self):
121+
# if self._hash is None:
122+
# self._hash = self._ufl_compute_hash_()
123+
# return self._hash
124124

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

ufl/core/ufl_type.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22

33
from ufl.core.expr import Expr
4+
from ufl.core.compute_expr_hash import compute_expr_hash
45

56
from ufl.common import camel2underscore, EmptyDict
67

@@ -216,6 +217,7 @@ def ufl_type(is_abstract=False,
216217
is_restriction=False,
217218
is_evaluation=False,
218219
is_differential=None,
220+
use_default_hash=True,
219221
num_ops=None,
220222
inherit_shape_from_operand=None,
221223
inherit_indices_from_operand=None,
@@ -311,9 +313,10 @@ def _ufl_expr_rbinop_(self, other):
311313

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

318321
# NB! This function conditionally adds some methods to the class!
319322
# This approach significantly reduces the amount of small functions to

ufl/corealg/traversal.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
# You should have received a copy of the GNU Lesser General Public License
2222
# along with UFL. If not, see <http://www.gnu.org/licenses/>.
2323

24-
_recursion_limit_ = 2000
24+
25+
# This limits the _depth_ of expression trees
26+
_recursion_limit_ = 6400 # should be enough for everyone
27+
2528

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

0 commit comments

Comments
 (0)