1 """ Provides BasicAlgebra class.
2 """
3
4 __docformat__ = "restructuredtext"
5
6 from ..core import Basic, classes
7
9 """ Represents an element of an algebraic structure.
10
11 This class collects implementation specific methods of algebra
12 classes.
13
14 For implemented algebras, see:
15
16 PrimitiveAlgebra
17 CommutativeRingWithPairs
18
19 New algebras may need to redefine the following methods::
20
21 __new__(cls, ...)
22 convert(cls, obj, typeerror=True)
23 convert_coefficient(cls, obj, typeerror=True)
24 convert_exponent(cls, obj, typeerror=True)
25 as_primitive(self)
26 as_algebra(self, cls)
27
28 and the following properties::
29
30 args(self)
31 func(self)
32
33 """
34 __slots__ = ['_str_value']
35
36 _str_value = None
37
43
45 return '%s(%r)' % (self.__class__.__name__, str(self))
46
47 - def as_tree(self, tab='', level=0):
49
50 @classmethod
51 - def convert(cls, obj, typeerror=True):
52 """ Convert obj to algebra element.
53
54 Set typeerror=False when calling from operation methods like __add__,
55 __mul__, etc.
56 """
57
58 if isinstance(obj, cls):
59 return obj
60
61
62 if isinstance(obj, (str, unicode, PrimitiveAlgebra)):
63 return PrimitiveAlgebra(obj).as_algebra(cls)
64
65
66 r = cls.convert_coefficient(obj, typeerror=False)
67 if r is not NotImplemented:
68 return cls.Number(r)
69
70
71 if isinstance(obj, BasicAlgebra):
72 return obj.as_algebra(cls)
73
74 if typeerror:
75 raise TypeError('%s.convert: failed to convert %s instance'\
76 ' to algebra'\
77 % (cls.__name__, obj.__class__.__name__))
78 else:
79 return NotImplemented
80
81 @classmethod
83 """ Convert obj to exponent algebra.
84 """
85 if isinstance(obj, (int, long, cls)):
86 return obj
87 if typeerror:
88 raise TypeError('%s.convert_exponent: failed to convert %s instance'\
89 ' to exponent algebra, expected int|long object'\
90 % (cls.__name__, obj.__class__.__name__))
91 else:
92 return NotImplemented
93
94 @classmethod
96 """ Convert obj to coefficient algebra.
97 """
98 if isinstance(obj, (int, long)):
99 return obj
100 if typeerror:
101 raise TypeError('%s.convert_coefficient: failed to convert %s instance'\
102 ' to coefficient algebra, expected int|long object'\
103 % (cls.__name__, obj.__class__.__name__))
104 else:
105 return NotImplemented
106
108 raise NotImplementedError('%s must define as_primitive method'
109 % (self.__class__.__name__))
110
112 """ Convert algebra to another algebra.
113
114 This method uses default conversation via primitive algebra that
115 might not be the most efficient. For efficiency, algebras should
116 redefine this method to implement direct conversation.
117 """
118
119 if cls is classes.PrimitiveAlgebra:
120 return self.as_primitive()
121 return self.as_primitive().as_algebra(cls)
122
123 @classmethod
126
127 @property
129 """ Returns a callable such that ``self.func(*self.args) == self``.
130 """
131 raise NotImplementedError('%s must define property func'
132 % (cls.__name__))
133
134 @property
136 """ Returns a sequence such that ``self.func(*self.args) == self``.
137 """
138 raise NotImplementedError('%s must define property args'
139 % (cls.__name__))
140
141 @classmethod
143 """ Construct algebra symbol directly from obj.
144 """
145 raise NotImplementedError('%s must define classmethod Symbol'
146 % (cls.__name__))
147
148 @classmethod
149 - def Number(cls, num, denom=None):
150 """ Construct algebra number directly from obj.
151 """
152 raise NotImplementedError('%s must define classmethod Number'
153 % (cls.__name__))
154
155 @property
170
171 - def has(self, obj):
176
177 - def match(self, pattern, *wildcards):
178 """
179 Pattern matching.
180
181 Return None when expression (self) does not match with
182 pattern. Otherwise return a dictionary such that
183
184 ::
185
186 pattern.subs_dict(self.match(pattern, *wildcards)) == self
187
188 Don't redefine this method, redefine ``matches(..)`` method instead.
189
190 :See:
191 http://wiki.sympy.org/wiki/Generic_interface#Pattern_matching
192 """
193 pattern = self.convert(pattern)
194 wild_expressions = []
195 wild_predicates = []
196 for w in wildcards:
197 if type(w) in [list, tuple]:
198 assert len(w)==2,`w`
199 s, func = w
200 else:
201 s, func = w, True
202 s = self.convert(s)
203 wild_expressions.append(s)
204 wild_predicates.append(func)
205 if wild_expressions:
206 wild_info = (wild_expressions, wild_predicates)
207 else:
208 wild_info = None
209 return pattern.matches(self, {}, wild_info)
210
211 - def matches(pattern, expr, repl_dict={}, wild_info=None):
212
213
214 v = repl_dict.get(pattern)
215 if v is not None:
216 if v==expr:
217 return repl_dict
218 return
219
220 if pattern==expr:
221 if wild_info and expr in wild_info[0]:
222 return
223 return repl_dict
224 if wild_info:
225 wild_expressions, wild_predicates = wild_info
226
227 if pattern in wild_expressions:
228 if expr in wild_expressions:
229
230 return
231
232 predicate = wild_predicates[wild_expressions.index(pattern)]
233 if (isinstance(predicate, bool) and predicate) or predicate(expr):
234 repl_dict = repl_dict.copy()
235 repl_dict[pattern] = expr
236 return repl_dict
237 pattern_symbols = pattern.symbols
238 for w in wild_expressions:
239 if w in pattern_symbols:
240
241
242
243 return pattern._matches(expr, repl_dict, wild_info)
244 return
245
246 @classmethod
247 - def _matches_seq(cls, pattern_seq, expr_seq, repl_dict, wild_info):
248 n = len(pattern_seq)
249 m = len(expr_seq)
250 if n!=m:
251 return
252 elif n==0:
253 return repl_dict
254 elif n==1:
255 p = pattern_seq[0]
256 if isinstance(p, cls):
257 return p.matches(expr_seq[0], repl_dict, wild_info)
258 if p==expr_seq[0]:
259 return repl_dict
260 return
261 def matches(pattern, expr):
262 if isinstance(pattern, cls):
263 return pattern.matches(expr, repl_dict, wild_info)
264 if pattern==expr:
265 return repl_dict
266 return
267 def subs(expr, l):
268 if isinstance(expr, cls):
269 return expr.subs(l)
270 return expr
271 for i in xrange(n):
272 p = pattern_seq[i]
273 e = expr_seq[i]
274 r = matches(p, e)
275 if r is not None:
276 new_pattern_seq = [subs(pattern_seq[j],r.items()) for j in xrange(n) if j!=i]
277 new_expr_seq = [expr_seq[j] for j in xrange(n) if j!=i]
278 r1 = cls._matches_seq(new_pattern_seq, new_expr_seq, r, wild_info)
279 if r1 is not None:
280 return r1
281 return
282
283 - def _matches(pattern, expr, repl_dict, wild_info):
287
288 - def subs(self, subexpr, newexpr=None):
289 """ Substitute a sub-expression with new expression.
290
291 There are two usage forms::
292
293 obj.subs(subexpr, newexpr)
294 obj.subs([(subexpr1, newexpr1), (subexpr2, newexpr2), ..])
295
296 """
297 convert = self.convert
298 if newexpr is None:
299 r = self
300 for s,n in subexpr:
301 r = r._subs(convert(s), convert(n))
302 return r
303 return self._subs(convert(subexpr), convert(newexpr))
304
305 - def _subs(self, subexpr, newexpr):
306 raise NotImplementedError('%s must define _subs method'
307 % (self.__class__.__name__))
308
309
310 from .primitive import PrimitiveAlgebra
311