~ubuntu-branches/ubuntu/vivid/ffc/vivid

« back to all changes in this revision

Viewing changes to sandbox/swig/new_symbol.py

  • Committer: Package Import Robot
  • Author(s): Johannes Ring
  • Date: 2014-01-10 13:56:45 UTC
  • mfrom: (1.1.14)
  • Revision ID: package-import@ubuntu.com-20140110135645-4ozcd71y1oggj44z
Tags: 1.3.0-1
* New upstream release.
* debian/watch: Update URL for move to Bitbucket.
* debian/docs: README -> README.rst and remove TODO.
* debian/control:
  - Add python-numpy to Build-Depends.
  - Replace python-all with python-all-dev in Build-Depends.
  - Add ${shlibs:Depends} to Depends.
  - Change to Architecture: any.
  - Bump Standards-Version to 3.9.5 (no changes needed).
* debian/rules: Call dh_numpy in override_dh_python2.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
"This file contains functions to optimise the code generated for quadrature representation."
2
 
 
3
 
# Copyright (C) 2009-2010 Kristian B. Oelgaard
4
 
#
5
 
# This file is part of FFC.
6
 
#
7
 
# FFC is free software: you can redistribute it and/or modify
8
 
# it under the terms of the GNU Lesser General Public License as published by
9
 
# the Free Software Foundation, either version 3 of the License, or
10
 
# (at your option) any later version.
11
 
#
12
 
# FFC is distributed in the hope that it will be useful,
13
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
 
# GNU Lesser General Public License for more details.
16
 
#
17
 
# You should have received a copy of the GNU Lesser General Public License
18
 
# along with FFC. If not, see <http://www.gnu.org/licenses/>.
19
 
#
20
 
# First added:  2009-07-12
21
 
# Last changed: 2010-01-21
22
 
 
23
 
# FFC common modules
24
 
from ffc.common.log import debug, error
25
 
 
26
 
#import psyco
27
 
#psyco.full()
28
 
 
29
 
# TODO: Use proper errors, not just RuntimeError.
30
 
# TODO: Change all if value == 0.0 to something more safe.
31
 
 
32
 
# Some basic variables.
33
 
BASIS = 0
34
 
IP  = 1
35
 
GEO = 2
36
 
CONST = 3
37
 
type_to_string = {BASIS:"BASIS", IP:"IP",GEO:"GEO", CONST:"CONST"}
38
 
format = None
39
 
 
40
 
# Functions and dictionaries for cache implementation.
41
 
# Increases speed and should also reduce memory consumption.
42
 
_float_cache = {}
43
 
def create_float(val):
44
 
    if val in _float_cache:
45
 
#        print "found %f in cache" %val
46
 
        return _float_cache[val]
47
 
    float_val = FloatValue(val)
48
 
    _float_cache[val] = float_val
49
 
    return float_val
50
 
 
51
 
_symbol_cache = {}
52
 
def create_symbol(variable, symbol_type, base_expr=None, base_op=0):
53
 
    key = (variable, symbol_type, base_expr, base_op)
54
 
    if key in _symbol_cache:
55
 
#        print "found %s in cache" %variable
56
 
        return _symbol_cache[key]
57
 
    symbol = Symbol(variable, symbol_type, base_expr, base_op)
58
 
    _symbol_cache[key] = symbol
59
 
    return symbol
60
 
 
61
 
_product_cache = {}
62
 
def create_product(variables):
63
 
    # NOTE: If I switch on the sorted line, it might be possible to find more
64
 
    # variables in the cache, but it adds some overhead so I don't think it
65
 
    # pays off. The member variables are also sorted in the classes
66
 
    # (Product and Sum) so the list 'variables' is probably already sorted.
67
 
#    key = tuple(sorted(variables))
68
 
    key = tuple(variables)
69
 
    if key in _product_cache:
70
 
#        print "found %s in cache" %str(key)
71
 
        return _product_cache[key]
72
 
    product = Product(key)
73
 
    _product_cache[key] = product
74
 
    return product
75
 
 
76
 
_sum_cache = {}
77
 
def create_sum(variables):
78
 
    # NOTE: If I switch on the sorted line, it might be possible to find more
79
 
    # variables in the cache, but it adds some overhead so I don't think it
80
 
    # pays off. The member variables are also sorted in the classes
81
 
    # (Product and Sum) so the list 'variables' is probably already sorted.
82
 
#    key = tuple(sorted(variables))
83
 
    key = tuple(variables)
84
 
    if key in _sum_cache:
85
 
#        print "found %s in cache" %str(key)
86
 
        return _sum_cache[key]
87
 
    s = Sum(key)
88
 
    _sum_cache[key] = s
89
 
    return s
90
 
 
91
 
_fraction_cache = {}
92
 
def create_fraction(num, denom):
93
 
    key = (num, denom)
94
 
    if key in _fraction_cache:
95
 
#        print "found %s in cache" %str(key)
96
 
        return _fraction_cache[key]
97
 
    fraction = Fraction(num, denom)
98
 
    _fraction_cache[key] = fraction
99
 
    return fraction
100
 
 
101
 
# Function to set global format to avoid passing around the dictionary 'format'.
102
 
def set_format(_format):
103
 
    global format
104
 
    format = _format
105
 
    set_format_float(format)
106
 
    set_format_prod(format)
107
 
    set_format_sum(format)
108
 
    set_format_frac(format)
109
 
    global format_comment
110
 
    format_comment = format["comment"]
111
 
 
112
 
def get_format():
113
 
    return format
114
 
 
115
 
# NOTE: We use commented print for debug, since debug will make the code run slower.
116
 
def generate_aux_constants(constant_decl, name, var_type, print_ops=False):
117
 
    "A helper tool to generate code for constant declarations."
118
 
    code = []
119
 
    append = code.append
120
 
    ops = 0
121
 
    for s in sorted([(v, k) for k, v in constant_decl.iteritems()]):
122
 
        c = s[1]
123
 
#        debug("c orig: " + str(c))
124
 
#        prit "c orig: " + str(c)
125
 
        c = c.expand().reduce_ops()
126
 
#        debug("c opt:  " + str(c))
127
 
#        print "c opt:  " + str(c)
128
 
        if print_ops:
129
 
            op = c.ops()
130
 
            ops += op
131
 
            append(format_comment("Number of operations: %d" %op))
132
 
            append((var_type + name + str(s[0]), str(c)))
133
 
            append("")
134
 
        else:
135
 
            ops += c.ops()
136
 
            append((var_type + name + str(s[0]), str(c)))
137
 
 
138
 
    return (ops, code)
139
 
 
140
 
def optimise_code(expr, ip_consts, geo_consts, trans_set):
141
 
    """Optimise a given expression with respect to, basis functions,
142
 
    integration points variables and geometric constants.
143
 
    The function will update the dictionaries ip_const and geo_consts with new
144
 
    declarations and update the trans_set (used transformations)."""
145
 
 
146
 
    format_G  = format["geometry tensor"]
147
 
    format_ip = format["integration points"]
148
 
    trans_set_update = trans_set.update
149
 
 
150
 
    # Return constant symbol if value is zero.
151
 
    if expr.val == 0.0:
152
 
        return create_float(0)
153
 
 
154
 
    # Reduce expression with respect to basis function variable.
155
 
#    debug("\n\nexpr before exp: " + repr(expr))
156
 
#    print "\n\nexpr before exp: " + repr(expr)
157
 
    basis_expressions = expr.expand().reduce_vartype(BASIS)
158
 
 
159
 
    # If we had a product instance we'll get a tuple back so embed in list.
160
 
    if not isinstance(basis_expressions, list):
161
 
        basis_expressions = [basis_expressions]
162
 
 
163
 
    basis_vals = []
164
 
    # Process each instance of basis functions.
165
 
    for basis, ip_expr in basis_expressions:
166
 
        # Get the basis and the ip expression.
167
 
#        debug("\nbasis\n" + str(basis))
168
 
#        debug("ip_epxr\n" + str(ip_expr))
169
 
#        print "\nbasis\n" + str(basis)
170
 
#        print "ip_epxr\n" + str(ip_expr)
171
 
 
172
 
        # If we have no basis (like functionals) create a const.
173
 
        if not basis:
174
 
            basis = create_float(1)
175
 
        # NOTE: Useful for debugging to check that terms where properly reduced.
176
 
#        if Product([basis, ip_expr]).expand() != expr.expand():
177
 
#            prod = Product([basis, ip_expr]).expand()
178
 
#            print "prod == sum: ", isinstance(prod, Sum)
179
 
#            print "expr == sum: ", isinstance(expr, Sum)
180
 
 
181
 
#            print "prod.vrs: ", prod.vrs
182
 
#            print "expr.vrs: ", expr.vrs
183
 
#            print "expr.vrs = prod.vrs: ", expr.vrs == prod.vrs
184
 
 
185
 
#            print "equal: ", prod == expr
186
 
 
187
 
#            print "\nprod:    ", prod
188
 
#            print "\nexpr:    ", expr
189
 
#            print "\nbasis:   ", basis
190
 
#            print "\nip_expr: ", ip_expr
191
 
#            error("Not equal")
192
 
 
193
 
        # If the ip expression doesn't contain any operations skip remainder.
194
 
        if not ip_expr:
195
 
            basis_vals.append(basis)
196
 
            continue
197
 
        if not ip_expr.ops() > 0:
198
 
            basis_vals.append(create_product([basis, ip_expr]))
199
 
            continue
200
 
 
201
 
        # Reduce the ip expressions with respect to IP variables.
202
 
        ip_expressions = ip_expr.expand().reduce_vartype(IP)
203
 
 
204
 
        # If we had a product instance we'll get a tuple back so embed in list.
205
 
        if not isinstance(ip_expressions, list):
206
 
            ip_expressions = [ip_expressions]
207
 
 
208
 
        ip_vals = []
209
 
        # Loop ip expressions.
210
 
        for ip in ip_expressions:
211
 
            ip_dec, geo = ip
212
 
#            debug("\nip_dec: " + str(ip_dec))
213
 
#            debug("\ngeo: " + str(geo))
214
 
#            print "\nip_dec: " + str(ip_dec)
215
 
#            print "\ngeo: " + str(geo)
216
 
            # Update transformation set with those values that might be embedded in IP terms.
217
 
            if ip_dec:
218
 
                trans_set_update(map(lambda x: str(x), ip_dec.get_unique_vars(GEO)))
219
 
 
220
 
            # Append and continue if we did not have any geo values.
221
 
            if not geo:
222
 
                ip_vals.append(ip_dec)
223
 
                continue
224
 
 
225
 
            # Update the transformation set with the variables in the geo term.
226
 
            trans_set_update(map(lambda x: str(x), geo.get_unique_vars(GEO)))
227
 
 
228
 
            # Only declare auxiliary geo terms if we can save operations.
229
 
            if geo.ops() > 0:
230
 
#                debug("geo: " + str(geo))
231
 
#                print "geo: " + str(geo)
232
 
                # If the geo term is not in the dictionary append it.
233
 
                if not geo_consts.has_key(geo):
234
 
                    geo_consts[geo] = len(geo_consts)
235
 
 
236
 
                # Substitute geometry expression.
237
 
                geo = create_symbol(format_G + str(geo_consts[geo]), GEO)
238
 
 
239
 
            # If we did not have any ip_declarations use geo, else create a
240
 
            # product and append to the list of ip_values.
241
 
            if not ip_dec:
242
 
                ip_dec = geo
243
 
            else:
244
 
                ip_dec = create_product([ip_dec, geo])
245
 
            ip_vals.append(ip_dec)
246
 
 
247
 
        # Create sum of ip expressions to multiply by basis.
248
 
        if len(ip_vals) > 1:
249
 
            ip_expr = create_sum(ip_vals)
250
 
        elif ip_vals:
251
 
            ip_expr = ip_vals.pop()
252
 
 
253
 
        # If we can save operations by declaring it as a constant do so, if it
254
 
        # is not in IP dictionary, add it and use new name.
255
 
        if ip_expr.ops() > 0:
256
 
            if not ip_expr in ip_consts:
257
 
                ip_consts[ip_expr] = len(ip_consts)
258
 
 
259
 
            # Substitute ip expression.
260
 
            ip_expr = create_symbol(format_G + format_ip + str(ip_consts[ip_expr]), IP)
261
 
 
262
 
        # Multiply by basis and append to basis vals.
263
 
        basis_vals.append(create_product([basis, ip_expr]).expand())
264
 
 
265
 
    # Return sum of basis values.
266
 
    return create_sum(basis_vals)
267
 
 
268
 
from floatvalue_obj import FloatValue, set_format as set_format_float
269
 
from symbol_obj     import Symbol
270
 
from product_obj    import Product, set_format as set_format_prod
271
 
from sum_obj        import Sum, group_fractions, set_format as set_format_sum
272
 
from fraction_obj   import Fraction, set_format as set_format_frac
273