1 """ Provides BasicAlgebra class.
2 """
3
4 __docformat__ = "restructuredtext"
5 __all__ = ['Algebra', 'SymbolicEquality']
6
7 from ..core import classes, Expr
8 from ..utils import LT, GT, LE, GE, NE, EQ
9
10 symbolic_comparison_map = dict(
11 equality = dict(__eq__=EQ, __ne__=NE),
12 inequality = dict(__lt__=LT,__le__=LE, __gt__=GT,__ge__=GE)
13 )
14
16 """ Contex for logical operations.
17
18 In the ``SymbolicEquality(<Algebra class>)`` context relational
19 operations return ``Logic`` instances instead of computing
20 lexicographic value of relational operations.
21
22 For example,
23
24 >>> x = Calculus('x')
25 >>> with SymbolicEquality(Calculus):
26 >>> print x==x
27 ...
28 ...
29 x==x
30 >>> print x==x
31 True
32
33 Add ``from __future__ import with_statement`` to the header of
34 python file when using Python version 2.5.
35
36 """
37
40
44
49
50
52 """ Represents an element of an algebraic structure.
53
54 This class collects implementation specific methods of algebra
55 classes.
56
57 For implemented algebras, see:
58
59 Verbatim
60 CollectingField
61
62 New algebras may need to redefine the following methods::
63
64 __new__(cls, ...)
65 convert(cls, obj, typeerror=True)
66 convert_coefficient(cls, obj, typeerror=True)
67 convert_exponent(cls, obj, typeerror=True)
68 as_verbatim(self)
69 as_algebra(self, cls)
70
71 and the following properties::
72
73 args(self)
74 func(self)
75
76 """
77
78
79 _str_value = None
80
81 coefftypes = (int, long)
82 exptypes = (int, long)
83
89
91 return '%s(%r)' % (self.__class__.__name__, str(self))
92
94 return not not self.data
95
96 @classmethod
98 """ Enable returning Logic instances from relational methods.
99 """
100 logic_map = symbolic_comparison_map.get(name)
101 if logic_map is None:
102 raise ValueError('Unknown symbolic comparison name: %r. Valid names are %s'\
103 % (name, ', '.join([repr(k) for k in symbolic_comparison_map])))
104 logic_map_name = '_%s_map' % (name)
105 d = getattr(cls, logic_map_name, None)
106 if d is None:
107 d = {}
108 setattr(cls, logic_map_name, d)
109 for mthname, head in logic_map.items():
110 d[mthname] = getattr(cls, mthname, None)
111 setattr(cls, mthname, lambda self, other, head=head: classes.Logic(head, (self, other)))
112
113 @classmethod
115 """ Disable returning Logic instances from relational methods.
116 """
117 logic_map = symbolic_comparison_map.get(name)
118 if logic_map is None:
119 raise ValueError('Unknown symbolic comparison name: %r. Valid names are %s'\
120 % (name, ', '.join([repr(k) for k in symbolic_comparison_map])))
121 logic_map_name = '_%s_map' % (name)
122 d = getattr(cls, logic_map_name, None)
123 if d is None:
124 return
125 for mthname, mth in d.items():
126 if mth is None:
127 delattr(cls, mthname)
128 else:
129 setattr(cls, mthname, mth)
130 delattr(cls, logic_map_name)
131
132 - def as_tree(self, tab='', level=0):
134
135 @classmethod
136 - def convert(cls, obj, typeerror=True):
137 """ Convert obj to algebra element.
138
139 Set typeerror=False when calling from operation methods like __add__,
140 __mul__, etc.
141 """
142
143 if isinstance(obj, cls):
144 return obj
145
146
147 if isinstance(obj, (str, unicode, Verbatim)):
148 return Verbatim.convert(obj).as_algebra(cls)
149
150
151 r = cls.convert_coefficient(obj, typeerror=False)
152 if r is not NotImplemented:
153 return cls.Number(r)
154
155
156 if isinstance(obj, Algebra):
157 return obj.as_algebra(cls)
158
159 if typeerror:
160 raise TypeError('%s.convert: failed to convert %s instance'\
161 ' to algebra'\
162 % (cls.__name__, obj.__class__.__name__))
163 else:
164 return NotImplemented
165
166 @classmethod
168 """ Convert obj to exponent algebra.
169 """
170 if isinstance(obj, cls.exptypes) or isinstance(obj, cls):
171 return obj
172 if typeerror:
173 raise TypeError('%s.convert_exponent: failed to convert %s instance'\
174 ' to exponent algebra, expected int|long object'\
175 % (cls.__name__, obj.__class__.__name__))
176 else:
177 return NotImplemented
178
179 @classmethod
181 """ Convert obj to coefficient algebra.
182 """
183 if isinstance(obj, cls.coefftypes):
184 return obj
185 if typeerror:
186 raise TypeError('%s.convert_coefficient: failed to convert %s instance'\
187 ' to coefficient algebra, expected int|long object'\
188 % (cls.__name__, obj.__class__.__name__))
189 else:
190 return NotImplemented
191
193 raise NotImplementedError('%s must define as_verbatim method'
194 % (self.__class__.__name__))
195
197 """ Convert algebra to another algebra.
198
199 This method uses default conversation via verbatim algebra that
200 might not be the most efficient. For efficiency, algebras should
201 redefine this method to implement direct conversation.
202 """
203
204 if cls is classes.Verbatim:
205 return self.as_verbatim()
206 return self.as_verbatim().as_algebra(cls)
207
208 @classmethod
211
212 @property
214 """ Returns a callable such that ``self.func(*self.args) == self``.
215 """
216 raise NotImplementedError('%s must define property func'
217 % (cls.__name__))
218
219 @property
221 """ Returns a sequence such that ``self.func(*self.args) == self``.
222 """
223 raise NotImplementedError('%s must define property args'
224 % (cls.__name__))
225
226 @classmethod
228 """ Construct algebra symbol directly from obj.
229 """
230 raise NotImplementedError('%s must define classmethod Symbol'
231 % (cls.__name__))
232
233 @classmethod
234 - def Number(cls, num, denom=None):
235 """ Construct algebra number directly from obj.
236 """
237 raise NotImplementedError('%s must define classmethod Number'
238 % (cls.__name__))
239
240 @property
255
256 - def has(self, obj):
261
262 - def match(self, pattern, *wildcards):
263 """
264 Pattern matching.
265
266 Return None when expression (self) does not match with
267 pattern. Otherwise return a dictionary such that
268
269 ::
270
271 pattern.subs_dict(self.match(pattern, *wildcards)) == self
272
273 Don't redefine this method, redefine ``matches(..)`` method instead.
274
275 :See:
276 http://wiki.sympy.org/wiki/Generic_interface#Pattern_matching
277 """
278 pattern = self.convert(pattern)
279 wild_expressions = []
280 wild_predicates = []
281 for w in wildcards:
282 if type(w) in [list, tuple]:
283 assert len(w)==2,`w`
284 s, func = w
285 else:
286 s, func = w, True
287 s = self.convert(s)
288 wild_expressions.append(s)
289 wild_predicates.append(func)
290 if wild_expressions:
291 wild_info = (wild_expressions, wild_predicates)
292 else:
293 wild_info = None
294 return pattern.matches(self, {}, wild_info)
295
296 - def matches(pattern, expr, repl_dict={}, wild_info=None):
297
298
299 v = repl_dict.get(pattern)
300 if v is not None:
301 if v==expr:
302 return repl_dict
303 return
304
305 if pattern==expr:
306 if wild_info and expr in wild_info[0]:
307 return
308 return repl_dict
309 if wild_info:
310 wild_expressions, wild_predicates = wild_info
311
312 if pattern in wild_expressions:
313 if expr in wild_expressions:
314
315 return
316 if expr.symbols.intersection(pattern.symbols):
317
318 return
319
320 predicate = wild_predicates[wild_expressions.index(pattern)]
321 if (isinstance(predicate, bool) and predicate) or predicate(expr):
322 repl_dict = repl_dict.copy()
323 repl_dict[pattern] = expr
324 return repl_dict
325 pattern_symbols = pattern.symbols
326 for w in wild_expressions:
327 if w in pattern_symbols:
328
329
330
331 return pattern._matches(expr, repl_dict, wild_info)
332 return
333
334 @classmethod
335 - def _matches_seq(cls, pattern_seq, expr_seq, repl_dict, wild_info):
336 n = len(pattern_seq)
337 m = len(expr_seq)
338 if n!=m:
339 return
340 elif n==0:
341 return repl_dict
342 elif n==1:
343 p = pattern_seq[0]
344 if isinstance(p, cls):
345 return p.matches(expr_seq[0], repl_dict, wild_info)
346 if p==expr_seq[0]:
347 return repl_dict
348 return
349 def matches(pattern, expr):
350 if isinstance(pattern, cls):
351 return pattern.matches(expr, repl_dict, wild_info)
352 if pattern==expr:
353 return repl_dict
354 return
355 def subs(expr, l):
356 if isinstance(expr, cls):
357 return expr.subs(l)
358 return expr
359 for i in xrange(n):
360 p = pattern_seq[i]
361 e = expr_seq[i]
362 r = matches(p, e)
363 if r is not None:
364 new_pattern_seq = [subs(pattern_seq[j],r.items()) for j in xrange(n) if j!=i]
365 new_expr_seq = [expr_seq[j] for j in xrange(n) if j!=i]
366 r1 = cls._matches_seq(new_pattern_seq, new_expr_seq, r, wild_info)
367 if r1 is not None:
368 return r1
369 return
370
371 - def _matches(pattern, expr, repl_dict, wild_info):
375
376 - def subs(self, subexpr, newexpr=None):
377 """ Substitute a sub-expression with new expression.
378
379 There are two usage forms::
380
381 obj.subs(subexpr, newexpr)
382 obj.subs([(subexpr1, newexpr1), (subexpr2, newexpr2), ..])
383
384 """
385 convert = self.convert
386 if newexpr is None:
387 r = self
388 for s,n in subexpr:
389 r = r._subs(convert(s), convert(n))
390 return r
391 return self._subs(convert(subexpr), convert(newexpr))
392
393 - def _subs(self, subexpr, newexpr):
394 raise NotImplementedError('%s must define _subs method'
395 % (self.__class__.__name__))
396
397
398 from .verbatim import Verbatim
399