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

« back to all changes in this revision

Viewing changes to sandbox/swig/symbol_obj.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 implements a class to represent a symbol."
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 error
25
 
 
26
 
from new_symbol import type_to_string, create_float, create_product, create_fraction
27
 
from expr import Expr
28
 
 
29
 
# FFC common modules
30
 
from ffc.common.log import error
31
 
 
32
 
#import psyco
33
 
#psyco.full()
34
 
 
35
 
class Symbol(Expr):
36
 
    __slots__ = ("v", "base_expr", "base_op")
37
 
    def __init__(self, variable, symbol_type, base_expr=None, base_op=0):
38
 
        """Initialise a Symbols object, it derives from Expr and contains
39
 
        the additional variables:
40
 
 
41
 
        v         - string, variable name
42
 
        base_expr - Other expression type like 'x*y + z'
43
 
        base_op   - number of operations for the symbol itself if it's a math
44
 
                    operation like std::cos(.) -> base_op = 1.
45
 
        NOTE: self._prec = 1."""
46
 
 
47
 
        # Dummy value, a symbol is always one.
48
 
        self.val = 1.0
49
 
 
50
 
        # Initialise variable, type and class.
51
 
        self.v = variable
52
 
        self.t = symbol_type
53
 
        self._prec = 1
54
 
 
55
 
        # Needed for symbols like std::cos(x*y + z),
56
 
        # where base_expr = x*y + z.
57
 
        # ops = base_expr.ops() + base_ops = 2 + 1 = 3
58
 
        self.base_expr = base_expr
59
 
        self.base_op = base_op
60
 
 
61
 
        # If type of the base_expr is lower than the given symbol_type change type.
62
 
        # TODO: Should we raise an error here? Or simply require that one
63
 
        # initalise the symbol by Symbol('std::cos(x*y)', (x*y).t, x*y, 1).
64
 
        if base_expr and base_expr.t < self.t:
65
 
            self.t = base_expr.t
66
 
 
67
 
        # Compute the representation now, such that we can use it directly
68
 
        # in the __eq__ and __ne__ methods (improves performance a bit, but
69
 
        # only when objects are cached).
70
 
        if self.base_expr:
71
 
            self._repr = "Symbol('%s', %s, %s, %d)" % (self.v, type_to_string[self.t], self.base_expr._repr, self.base_op)
72
 
        else:
73
 
            self._repr = "Symbol('%s', %s)" % (self.v, type_to_string[self.t])
74
 
 
75
 
        # Use repr as hash value.
76
 
        self._hash = hash(self._repr)
77
 
 
78
 
    # Print functions.
79
 
    def __str__(self):
80
 
        "Simple string representation which will appear in the generated code."
81
 
        return self.v
82
 
 
83
 
    # Binary operators.
84
 
    def __add__(self, other):
85
 
        "Addition by other objects."
86
 
        # NOTE: We expect expanded objects and we only expect to add equal
87
 
        # symbols, if other is a product, try to let product handle the addition.
88
 
        # TODO: Should we also support addition by other objects for generality?
89
 
        # Returns x + x -> 2*x, x + 2*x -> 3*x.
90
 
        if self._repr == other._repr:
91
 
            return create_product([create_float(2), self])
92
 
        elif other._prec == 2: # prod
93
 
            return other.__add__(self)
94
 
        error("Not implemented.")
95
 
 
96
 
    def __mul__(self, other):
97
 
        "Multiplication by other objects."
98
 
        # NOTE: We assume expanded objects.
99
 
        # If product will be zero.
100
 
        if self.val == 0.0 or other.val == 0.0:
101
 
            return create_float(0)
102
 
 
103
 
        # If other is Sum or Fraction let them handle the multiply.
104
 
        if other._prec in (3, 4): # sum or frac
105
 
            return other.__mul__(self)
106
 
 
107
 
        # If other is a float or symbol, create simple product.
108
 
        if other._prec in (0, 1): # float or sym
109
 
            return create_product([self, other])
110
 
 
111
 
        # Else add variables from product.
112
 
        return create_product([self] + other.vrs)
113
 
 
114
 
    def __div__(self, other):
115
 
        "Division by other objects."
116
 
        # NOTE: We assume expanded objects.
117
 
        # If division is illegal (this should definitely not happen).
118
 
        if other.val == 0.0:
119
 
            error("Division by zero.")
120
 
 
121
 
        # Return 1 if the two symbols are equal.
122
 
        if self._repr == other._repr:
123
 
            return create_float(1)
124
 
 
125
 
        # If other is a Sum we can only return a fraction.
126
 
        # TODO: Refine this later such that x / (x + x*y) -> 1 / (1 + y)?
127
 
        if other._prec == 3: # sum
128
 
            return create_fraction(self, other)
129
 
 
130
 
        # Handle division by FloatValue, Symbol, Product and Fraction.
131
 
        # Create numerator and list for denominator.
132
 
        num = [self]
133
 
        denom = []
134
 
 
135
 
        # Add floatvalue, symbol and products to the list of denominators.
136
 
        if other._prec in (0, 1): # float or sym
137
 
            denom = [other]
138
 
        elif other._prec == 2: # prod
139
 
            # Need copies, so can't just do denom = other.vrs.
140
 
            denom += other.vrs
141
 
        # fraction.
142
 
        else:
143
 
            # TODO: Should we also support division by fraction for generality?
144
 
            # It should not be needed by this module.
145
 
            error("Did not expected to divide by fraction.")
146
 
 
147
 
        # Remove one instance of self in numerator and denominator if
148
 
        # present in denominator i.e., x/(x*y) --> 1/y.
149
 
        if self in denom:
150
 
            denom.remove(self)
151
 
            num.remove(self)
152
 
 
153
 
        # Loop entries in denominator and move float value to numerator.
154
 
        for d in denom:
155
 
            # Add the inverse of a float to the numerator, remove it from
156
 
            # the denominator and continue.
157
 
            if d._prec == 0: # float
158
 
                num.append(create_float(1.0/other.val))
159
 
                denom.remove(d)
160
 
                continue
161
 
 
162
 
        # Create appropriate return value depending on remaining data.
163
 
        # Can only be for x / (2*y*z) -> 0.5*x / (y*z).
164
 
        if len(num) > 1:
165
 
            num = create_product(num)
166
 
        # x / (y*z) -> x/(y*z),
167
 
        elif num:
168
 
            num = num[0]
169
 
        # else x / (x*y) -> 1/y.
170
 
        else:
171
 
            num = create_float(1)
172
 
 
173
 
        # If we have a long denominator, create product and fraction.
174
 
        if len(denom) > 1:
175
 
            return create_fraction(num, create_product(denom))
176
 
        # If we do have a denominator, but only one variable don't create a
177
 
        # product, just return a fraction using the variable as denominator.
178
 
        elif denom:
179
 
            return create_fraction(num, denom[0])
180
 
        # If we don't have any donominator left, return the numerator.
181
 
        # x / 2.0 -> 0.5*x.
182
 
        return num
183
 
 
184
 
    # Public functions.
185
 
    def get_unique_vars(self, var_type):
186
 
        "Get unique variables (Symbols) as a set."
187
 
        # Return self if type matches, also return base expression variables.
188
 
        s = set()
189
 
        if self.t == var_type:
190
 
            s.add(self)
191
 
        if self.base_expr:
192
 
            s.update(self.base_expr.get_unique_vars(var_type))
193
 
        return s
194
 
 
195
 
    def get_var_occurrences(self):
196
 
        """Determine the number of times all variables occurs in the expression.
197
 
        Returns a dictionary of variables and the number of times they occur."""
198
 
        # There is only one symbol.
199
 
        return {self:1}
200
 
 
201
 
    def ops(self):
202
 
        "Returning the number of floating point operation for symbol."
203
 
        # Get base ops, typically 1 for sin() and then add the operations
204
 
        # for the base (sin(2*x + 1)) --> 2 + 1.
205
 
        if self.base_expr:
206
 
            return self.base_op + self.base_expr.ops()
207
 
        return self.base_op
208
 
 
209
 
from floatvalue_obj import FloatValue
210
 
from product_obj    import Product
211
 
from sum_obj        import Sum
212
 
from fraction_obj   import Fraction
213