~ubuntu-branches/ubuntu/natty/ffc/natty

« back to all changes in this revision

Viewing changes to ffc/tensor/monomialextraction.py

  • Committer: Bazaar Package Importer
  • Author(s): Johannes Ring
  • Date: 2010-02-03 20:22:35 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20100203202235-fe8d0kajuvgy2sqn
Tags: 0.9.0-1
* New upstream release.
* debian/control: Bump Standards-Version (no changes needed).
* Update debian/copyright and debian/copyright_hints.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"Extraction of monomial representations of UFL forms."
 
2
 
 
3
__author__ = "Anders Logg (logg@simula.no)"
 
4
__date__ = "2008-08-01"
 
5
__copyright__ = "Copyright (C) 2008-2009 Anders Logg"
 
6
__license__  = "GNU GPL version 3 or any later version"
 
7
 
 
8
# Modified by Martin Alnes, 2008
 
9
# Modified by Kristian B. Oelgaard
 
10
# Last changed: 2010-01-25
 
11
 
 
12
# UFL modules
 
13
from ufl.classes import Form, Argument, Coefficient, ScalarValue, IntValue
 
14
from ufl.algorithms import purge_list_tensors, apply_transformer, ReuseTransformer
 
15
 
 
16
# FFC modules
 
17
from ffc.log import info, debug, ffc_assert
 
18
 
 
19
# Cache for computed integrand representations
 
20
_cache = {}
 
21
 
 
22
def extract_monomial_form(integrals):
 
23
    """
 
24
    Extract monomial representation of form (if possible). When
 
25
    successful, the form is represented as a sum of products of scalar
 
26
    components of basis functions or derivatives of basis functions.
 
27
    If unsuccessful, MonomialException is raised.
 
28
    """
 
29
 
 
30
    info("Extracting monomial form representation from UFL form")
 
31
 
 
32
    # Iterate over all integrals
 
33
    monomial_form = MonomialForm()
 
34
    for integral in integrals:
 
35
 
 
36
        # Get measure and integrand
 
37
        measure = integral.measure()
 
38
        integrand = integral.integrand()
 
39
 
 
40
        # Extract monomial representation if possible
 
41
        integrand = extract_monomial_integrand(integrand)
 
42
        monomial_form.append(integrand, measure)
 
43
 
 
44
    return monomial_form
 
45
 
 
46
def extract_monomial_integrand(integrand):
 
47
    "Extract monomial integrand (if possible)."
 
48
 
 
49
    # Check cache
 
50
    if integrand in _cache:
 
51
        debug("Reusing monomial integrand from cache")
 
52
        return _cache[integrand]
 
53
 
 
54
    # Purge list tensors
 
55
    integrand = purge_list_tensors(integrand)
 
56
 
 
57
    # Apply monomial transformer
 
58
    monomial_integrand = apply_transformer(integrand, MonomialTransformer())
 
59
 
 
60
    # Store in cache
 
61
    _cache[integrand] = monomial_integrand
 
62
 
 
63
    return monomial_integrand
 
64
 
 
65
class MonomialException(Exception):
 
66
    "Exception raised when monomial extraction fails."
 
67
    def __init__(self, *args, **kwargs):
 
68
        Exception.__init__(self, *args, **kwargs)
 
69
 
 
70
class MonomialFactor:
 
71
    """
 
72
    This class represents a monomial factor, that is, a derivative of
 
73
    a scalar component of a basis function.
 
74
    """
 
75
 
 
76
    def __init__(self, arg=None):
 
77
        if isinstance(arg, MonomialFactor):
 
78
            self.function = arg.function
 
79
            self.components = arg.components
 
80
            self.derivatives = arg.derivatives
 
81
            self.restriction = arg.restriction
 
82
        elif isinstance(arg, (Argument, Coefficient)):
 
83
            self.function = arg
 
84
            self.components = []
 
85
            self.derivatives = []
 
86
            self.restriction = None
 
87
        elif arg is None:
 
88
            self.function = None
 
89
            self.components = []
 
90
            self.derivatives = []
 
91
            self.restriction = None
 
92
        else:
 
93
            raise MonomialException, ("Unable to create monomial from expression: " + str(arg))
 
94
 
 
95
    def element(self):
 
96
        return self.function.element()
 
97
 
 
98
    def count(self):
 
99
        return self.function.count()
 
100
 
 
101
    def apply_derivative(self, indices):
 
102
        self.derivatives += indices
 
103
 
 
104
    def apply_restriction(self, restriction):
 
105
        self.restriction = restriction
 
106
 
 
107
    def replace_indices(self, old_indices, new_indices):
 
108
        if old_indices is None:
 
109
            self.components = new_indices
 
110
        else:
 
111
            _replace_indices(self.components, old_indices, new_indices)
 
112
            _replace_indices(self.derivatives, old_indices, new_indices)
 
113
 
 
114
    def __str__(self):
 
115
        if len(self.components) == 0:
 
116
            c = ""
 
117
        else:
 
118
            c = "[%s]" % ", ".join(str(c) for c in self.components)
 
119
        if len(self.derivatives) == 0:
 
120
            d0 = ""
 
121
            d1 = ""
 
122
        else:
 
123
            d0 = "(" + " ".join("d/dx_%s" % str(d) for d in self.derivatives) + " "
 
124
            d1 = ")"
 
125
        if self.restriction is None:
 
126
            r = ""
 
127
        else:
 
128
            r = "(%s)" % str(self.restriction)
 
129
        return d0 + str(self.function) + r + c + d1
 
130
 
 
131
class Monomial:
 
132
    "This class represents a product of monomial factors."
 
133
 
 
134
    def __init__(self, arg=None):
 
135
        if isinstance(arg, Monomial):
 
136
            self.float_value = arg.float_value
 
137
            self.factors = [MonomialFactor(v) for v in arg.factors]
 
138
            self.index_slots = arg.index_slots
 
139
        elif isinstance(arg, (MonomialFactor, Argument, Coefficient)):
 
140
            self.float_value = 1.0
 
141
            self.factors = [MonomialFactor(arg)]
 
142
            self.index_slots = None
 
143
        elif isinstance(arg, ScalarValue):
 
144
            self.float_value = float(arg)
 
145
            self.factors = []
 
146
            self.index_slots = None
 
147
        elif arg is None:
 
148
            self.float_value = 1.0
 
149
            self.factors = []
 
150
            self.index_slots = None
 
151
        else:
 
152
            raise MonomialException, ("Unable to create monomial from expression: " + str(arg))
 
153
 
 
154
    def apply_derivative(self, indices):
 
155
        if not len(self.factors) == 1:
 
156
            raise MonomialException, "Expecting a single factor."
 
157
        self.factors[0].apply_derivative(indices)
 
158
 
 
159
    def apply_tensor(self, indices):
 
160
        if not self.index_slots is None:
 
161
            raise MonomialException, "Expecting scalar-valued expression."
 
162
        self.index_slots = indices
 
163
 
 
164
    def apply_indices(self, indices):
 
165
        for v in self.factors:
 
166
            v.replace_indices(self.index_slots, indices)
 
167
        self.index_slots = None
 
168
 
 
169
    def apply_restriction(self, restriction):
 
170
        for v in self.factors:
 
171
            v.apply_restriction(restriction)
 
172
 
 
173
    def __mul__(self, other):
 
174
        m = Monomial()
 
175
        m.float_value = self.float_value * other.float_value
 
176
        m.factors = self.factors + other.factors
 
177
        return m
 
178
 
 
179
    def __str__(self):
 
180
        if self.float_value == 1.0:
 
181
            float_value = ""
 
182
        else:
 
183
            float_value = "%g * " % self.float_value
 
184
        return float_value + " * ".join(str(v) for v in self.factors)
 
185
 
 
186
class MonomialSum:
 
187
    "This class represents a sum of monomials."
 
188
 
 
189
    def __init__(self, arg=None):
 
190
        if isinstance(arg, MonomialSum):
 
191
            self.monomials = [Monomial(m) for m in arg.monomials]
 
192
        elif arg is None:
 
193
            self.monomials = []
 
194
        else:
 
195
            self.monomials = [Monomial(arg)]
 
196
 
 
197
    def apply_derivative(self, indices):
 
198
        for m in self.monomials:
 
199
            m.apply_derivative(indices)
 
200
 
 
201
    def apply_tensor(self, indices):
 
202
        for m in self.monomials:
 
203
            m.apply_tensor(indices)
 
204
 
 
205
    def apply_indices(self, indices):
 
206
        for m in self.monomials:
 
207
            m.apply_indices(indices)
 
208
 
 
209
    def apply_restriction(self, restriction):
 
210
        for m in self.monomials:
 
211
            m.apply_restriction(restriction)
 
212
 
 
213
    def __add__(self, other):
 
214
        m0 = [Monomial(m) for m in self.monomials]
 
215
        m1 = [Monomial(m) for m in other.monomials]
 
216
        sum = MonomialSum()
 
217
        sum.monomials = m0 + m1
 
218
        return sum
 
219
 
 
220
    def __mul__(self, other):
 
221
        sum = MonomialSum()
 
222
        for m0 in self.monomials:
 
223
            for m1 in other.monomials:
 
224
                sum.monomials.append(m0 * m1)
 
225
        return sum
 
226
 
 
227
    def __str__(self):
 
228
        return " + ".join(str(m) for m in self.monomials)
 
229
 
 
230
class MonomialForm:
 
231
    """
 
232
    This class represents a monomial form, that is, a sum of
 
233
    integrals, each represented as a MonomialSum.
 
234
    """
 
235
 
 
236
    def __init__(self):
 
237
        self.integrals = []
 
238
 
 
239
    def append(self, integral, measure):
 
240
        self.integrals.append((integral, measure))
 
241
 
 
242
    def __len__(self):
 
243
        return len(self.integrals)
 
244
 
 
245
    def __getitem__(self, i):
 
246
        return self.integrals[i]
 
247
 
 
248
    def __iter__(self):
 
249
        return iter(self.integrals)
 
250
 
 
251
    def __str__(self):
 
252
        if len(self.integrals) == 0:
 
253
            return "<Empty form>"
 
254
        s  = "Monomial form of %d integral(s)\n" % len(self.integrals)
 
255
        s += len(s) * "-" + "\n"
 
256
        for (integrand, measure) in self.integrals:
 
257
            s += "Integrand: " + str(integrand) + "\n"
 
258
            s += "Measure:   " + str(measure) + "\n"
 
259
        return s
 
260
 
 
261
class MonomialTransformer(ReuseTransformer):
 
262
    """
 
263
    This class defines the transformation rules for extraction of a
 
264
    monomial form represented as a MonomialSum from a UFL integral.
 
265
    """
 
266
 
 
267
    def __init__(self):
 
268
        ReuseTransformer.__init__(self)
 
269
 
 
270
    def expr(self, o, *ops):
 
271
        raise MonomialException, ("No handler defined for expression %s." % o._uflclass.__name__)
 
272
 
 
273
    def terminal(self, o):
 
274
        raise MonomialException, ("No handler defined for terminal %s." % o._uflclass.__name__)
 
275
 
 
276
    def variable(self, o):
 
277
        return self.visit(o.expression())
 
278
 
 
279
    #--- Operator handles ---
 
280
 
 
281
    def sum(self, o, s0, s1):
 
282
        s = s0 + s1
 
283
        return s
 
284
 
 
285
    def product(self, o, s0, s1):
 
286
        s = s0 * s1
 
287
        return s
 
288
 
 
289
    def index_sum(self, o, s, index):
 
290
        return s
 
291
 
 
292
    def indexed(self, o, s, indices):
 
293
        s = MonomialSum(s)
 
294
        s.apply_indices(indices)
 
295
        return s
 
296
 
 
297
    def component_tensor(self, o, s, indices):
 
298
        s = MonomialSum(s)
 
299
        s.apply_tensor(indices)
 
300
        return s
 
301
 
 
302
    def spatial_derivative(self, o, s, indices):
 
303
        s = MonomialSum(s)
 
304
        s.apply_derivative(indices)
 
305
        return s
 
306
 
 
307
    def positive_restricted(self, o, s):
 
308
        s.apply_restriction("+")
 
309
        return s
 
310
 
 
311
    def negative_restricted(self, o, s):
 
312
        s.apply_restriction("-")
 
313
        return s
 
314
 
 
315
    def power(self, o, s, ignored_exponent_expressed_as_sum):
 
316
        (expr, exponent) = o.operands()
 
317
        if not isinstance(exponent, IntValue):
 
318
            raise MonomialException, "Cannot handle non-integer exponents."
 
319
        p = MonomialSum(Monomial())
 
320
        for i in range(int(exponent)):
 
321
            p = p * s
 
322
        return p
 
323
 
 
324
    #--- Terminal handlers ---
 
325
 
 
326
    def multi_index(self, multi_index):
 
327
        indices = [index for index in multi_index]
 
328
        return indices
 
329
 
 
330
    def index(self, o):
 
331
        raise MonomialException, "Not expecting to see an Index terminal."
 
332
 
 
333
    def argument(self, v):
 
334
        s = MonomialSum(v)
 
335
        return s
 
336
 
 
337
    def coefficient(self, v):
 
338
        s = MonomialSum(v)
 
339
        return s
 
340
 
 
341
    def scalar_value(self, x):
 
342
        s = MonomialSum(x)
 
343
        return s
 
344
 
 
345
def _replace_indices(indices, old_indices, new_indices):
 
346
    "Handle replacement of subsets of multi indices."
 
347
 
 
348
    # Old and new indices must match
 
349
    if not len(old_indices) == len(new_indices):
 
350
        raise MonomialException, "Unable to replace indices, mismatching index dimensions."
 
351
 
 
352
    # Build index map
 
353
    index_map = {}
 
354
    for (i, index) in enumerate(old_indices):
 
355
        index_map[index] = new_indices[i]
 
356
 
 
357
    # Check all indices and replace
 
358
    for (i, index) in enumerate(indices):
 
359
        if index in old_indices:
 
360
            indices[i] = index_map[index]