1
2
3
4
5 """Provides CollectingField class.
6 """
7 __docformat__ = "restructuredtext"
8
9 __all__ = ['CollectingField']
10
11
12 from ..core import classes
13 from ..utils import str_SUM, str_PRODUCT, str_POWER, str_APPLY, str_SYMBOL, str_NUMBER
14 from ..utils import TERMS, FACTORS, SYMBOL, NUMBER, APPLY, POW, TUPLE, head_to_string
15
16 from .algebra import Algebra
17 from .ring import CommutativeRing
18 from .verbatim import Verbatim
19 from ..arithmetic.numbers import mpq, realtypes
20
21 from .pairs_ops import (add_method, sub_method, rsub_method, neg_method,
22 mul_method, div_method, rdiv_method, pow_method)
23
24 from .pairs_iops import (inplace_add, inplace_add2, inplace_sub,
25 return_terms, return_factors,
26 inplace_mul, inplace_mul2)
27
28 from .pairs_expand import expand
29
31 """ Implementation of a commutative ring where sums and products
32 are represented as dictionaries of pairs.
33 """
34
35
36 one_c = 1
37 one_e = 1
38 zero_c = 0
39 zero_e = 0
40
41 _symbols = None
42 _symbols_data = None
43
44 coefftypes = (int, long, mpq)
45 exptypes = (int, long, mpq)
46
47 _coeff_terms = (1, None)
48 __neg__ = neg_method
49 __add__ = __radd__ = add_method
50 __sub__ = sub_method
51 __rsub__ = rsub_method
52 __mul__ = __rmul__ = mul_method
53 __div__ = div_method
54 __rdiv__ = rdiv_method
55 __pow__ = pow_method
56 expand = expand
57
58 __repr__ = CommutativeRing.__repr__
59
62
70
71
72
73
74 - def as_tree(self, tab='', level=0):
93
94 @classmethod
96 tobj = type(obj)
97 if tobj is int or tobj is long:
98 if obj < 0:
99 return str_SUM, str(obj)
100 return str_NUMBER, str(obj)
101 if hasattr(obj, 'to_str_data'):
102 return obj.to_str_data(sort)
103 raise NotImplementedError('%s.coefficient_to_str_data(%r)' % (cls.__name__, obj))
104
105 @classmethod
107 tobj = type(obj)
108 if tobj is int or tobj is long:
109 if obj < 0:
110 return str_SUM, str(obj)
111 return str_NUMBER, str(obj)
112 if hasattr(obj, 'to_str_data'):
113 return obj.to_str_data(sort)
114 raise NotImplementedError('%s.exponent_to_str_data(%r)' % (cls.__name__, obj))
115
116 @classmethod
123
126
128 head = self.head
129 one = self.one
130 cls = type(self)
131 if head is NUMBER:
132 return cls.coefficient_to_str_data(self.data, sort)
133 if head is TERMS:
134 pos_dict = {}
135 neg_dict = {}
136 for t, c in self.data.iteritems():
137 if isinstance(c, realtypes) and c<0:
138 d = neg_dict
139 c = -c
140 else:
141 d = pos_dict
142 h1, s1 = cls.coefficient_to_str_data(c, sort)
143 h2, s2 = t.to_str_data(sort)
144 if t is one:
145 h, s = h1, s1
146 elif c==1:
147 h, s = h2, s2
148 else:
149 if h1 > str_PRODUCT:
150 s1 = '(%s)' % (s1)
151 if h2 > str_PRODUCT:
152 s2 = '(%s)' % (s2)
153 s = '%s*%s' % (s1,s2)
154 h = str_PRODUCT
155 l = d.get(h)
156 if l is None:
157 l = d[h] = []
158 l.append(s)
159 if sort:
160 r1 = []
161 for k in sorted(pos_dict):
162 r1 += sorted(pos_dict[k])
163 r2 = []
164 for k in sorted(neg_dict):
165 r2 += sorted(neg_dict[k])
166 else:
167 r1 = reduce(lambda x,y:x+y, pos_dict.values(),[])
168 r2 = reduce(lambda x,y:x+y, neg_dict.values(),[])
169 if r1:
170 if r2:
171 return str_SUM, (' + '.join(r1)) + ' - ' + (' - '.join(r2))
172 if len(r1)>1:
173 return str_SUM, (' + '.join(r1))
174 h,l = pos_dict.items()[0]
175 return h, l[0]
176 return str_SUM, '-' + (' - '.join(r2))
177 if head is FACTORS:
178 d = {}
179 for t, c in self.data.iteritems():
180 h1, s1 = t.to_str_data(sort)
181 h2, s2 = cls.exponent_to_str_data(c, sort)
182 if c==1:
183 if h1 > str_POWER:
184 s1 = '(%s)' % (s1)
185 h, s = h1, s1
186 else:
187 if h1 >= str_POWER:
188 s1 = '(%s)' % (s1)
189 if h2 >= str_POWER:
190 s2 = '(%s)' % (s2)
191 s = '%s**%s' % (s1, s2)
192 h = str_POWER
193 l = d.get(h)
194 if l is None:
195 l = d[h] = []
196 l.append(s)
197 if sort:
198 r1 = []
199 for k in sorted(d):
200 r1 += sorted(d[k])
201 else:
202 r1 = reduce(lambda x,y:x+y, d.values(),[])
203 if len(r1)>1:
204 return str_PRODUCT, '*'.join(r1)
205 h, l = d.items()[0]
206 return h, l[0]
207 if callable(head):
208 h1, s1 = cls.callable_to_str_data(head, sort)
209 if h1 > str_APPLY:
210 s1 = '(%s)' % (s1)
211 args = self.data
212 if type(args) is not tuple:
213 args = args,
214 s2 = ', '.join([a.to_str_data(sort)[1] for a in args])
215 return str_APPLY, '%s(%s)' % (s1, s2)
216 return str_SYMBOL, str(self.data)
217
218 @property
220 """ Return callable func such that ``self.func(**self.args) == self``
221 """
222 head = self.head
223 data = self.data
224 if head is SYMBOL or head is NUMBER:
225 return lambda : self
226 elif head is TERMS:
227 if len(self.data)==1:
228 return self.Mul
229 return self.Add
230 elif head is FACTORS:
231 if len(data)>1:
232 return self.Mul
233 return self.Pow
234 elif callable(head):
235 return head
236 raise NotImplementedError(`self, head`)
237
238 @property
260
261 @property
265
266 @property
271
272 @property
276
277 @property
280
281 @property
284
286 head, data = self.pair
287 if head is TERMS:
288 if len(data)==1:
289 return [self]
290 return [t * c for t,c in data.iteritems()]
291 return [self]
292
309
317
325
331
333 """ Convert algebra element to a verbatim algebra element.
334 """
335 head, data = self.pair
336 func = self.func
337 args = self.args
338 if func==self.Add:
339 r = Verbatim(TERMS,tuple([a.as_verbatim() for a in args]))
340 r.commutative_add = True
341 elif func==self.Mul:
342 h, d = args[0].pair
343 if h is NUMBER and d < 0:
344 if d==-1:
345 args = [a.as_verbatim() for a in args[1:]]
346 if len(args)==1:
347 return -args[0]
348 else:
349 args = [(-args[0]).as_verbatim()] + [a.as_verbatim() for a in args[1:]]
350 r = Verbatim(FACTORS, tuple(args))
351 r.commutative_mul = True
352 r = -r
353 else:
354 r = Verbatim(FACTORS, tuple([a.as_verbatim() for a in args]))
355 r.commutative_mul = True
356 elif func==self.Pow:
357 r = Verbatim(POW, tuple(map(Verbatim.convert, args)))
358 elif head is NUMBER:
359 value = data
360 if hasattr(value, 'as_verbatim'):
361 r = value.as_verbatim()
362 elif isinstance(value, (int, long, float)) and value<0:
363 r = -Verbatim(NUMBER, -value)
364 else:
365 r = Verbatim(NUMBER, value)
366 elif head is SYMBOL:
367 if hasattr(data, 'as_verbatim'):
368 r = data.as_verbatim()
369 else:
370 r = Verbatim(SYMBOL, data)
371 elif callable(head):
372 if hasattr(data, 'as_verbatim'):
373 args = data.as_verbatim(),
374 elif isinstance(data, tuple):
375 args = tuple([a.as_verbatim() for a in args])
376 else:
377 args = data.as_verbatim()
378 r = Verbatim(APPLY, (head,)+args)
379 else:
380 if hasattr(data, 'as_verbatim'):
381 r = data.as_verbatim()
382 else:
383 r = Verbatim(SYMBOL, (head, data))
384 return r
385
386 @classmethod
388 """ Construct new symbol instance as an algebra element.
389 """
390 assert isinstance(obj, str),`obj`
391 return cls(SYMBOL, obj)
392
393 @classmethod
395 """ Construct new number instance as an algebra number.
396 """
397 return cls(NUMBER, obj)
398
399 @classmethod
400 - def Apply(cls, func, args):
402
403 @classmethod
404 - def Add(cls, *seq):
405 """ Return canonized sum as an algebra element.
406 """
407 d = {}
408 d_get = d.get
409 one = cls.one
410 for t in seq:
411 inplace_add(cls, t, d, d_get, one)
412 return return_terms(cls, d)
413
414 @classmethod
415 - def Sub(cls, *seq):
424
425 @classmethod
433
435 head, data = self.pair
436 cls = type(self)
437 if self.is_writable and head is TERMS:
438 if type(other) is not cls:
439 t = cls.convert(other)
440 else:
441 t = other
442 h, d = t.pair
443 if h is TERMS and len(d)==1:
444 t, c = d.items()[0]
445 elif h is NUMBER:
446 c = d
447 t = cls.one
448 else:
449 c = 1
450 inplace_add2(cls, t, c, data, data.get, cls.one)
451 return self
452 return self + other
453
455 head, data = self.pair
456 cls = type(self)
457 if self.is_writable and head is FACTORS:
458 if type(other) is not cls:
459 t = cls.convert(other)
460 else:
461 t = other
462 h, d = t.pair
463 if h is FACTORS and len(d)==1:
464 t, c = d.items()[0]
465 elif h is NUMBER:
466 return self * t
467 elif h is TERMS and len(d)==1:
468 return self * t
469 else:
470 c = 1
471 inplace_mul2(cls, t, c, data, data.get, cls.one)
472 return self
473 return self * other
474
475 @classmethod
476 - def Mul(cls, *seq):
477 """ Return canonized product as an algebra element.
478 """
479 d = {}
480 d_get = d.get
481 number = 1
482 for t in seq:
483 n = inplace_mul(cls, t, d, d_get)
484 if n is not 1:
485 number = number * n
486 r = return_factors(cls, d)
487 if number is 1:
488 return r
489 return r * number
490
491 @classmethod
493 d = {}
494 d_get = d.get
495 number = 1
496 for t, c in seq:
497 n = inplace_mul2(cls, t, c, d, d_get)
498 if n is not 1:
499 number = number * n
500 r = return_factors(cls, d)
501 if number is 1:
502 return r
503 return r * number
504
505 @classmethod
506 - def Pow(cls, base, exp):
508
513
518
523
526
528 if type(other) is type(self):
529 return self.data < other.data
530 return self.data < other
532 if type(other) is type(self):
533 return self.data <= other.data
534 return self.data <= other
536 if type(other) is type(self):
537 return self.data > other.data
538 return self.data > other
540 if type(other) is type(self):
541 return self.data >= other.data
542 return self.data >= other
544 return not (self == other)
545
571
572 @property
582
587
588 - def has(self, subexpr):
589 """ Check if self contains sub-expression.
590 """
591 subexpr = self.convert(subexpr)
592 head, data = subexpr.pair
593 if head is SYMBOL:
594 return data in self._get_symbols_data()
595 raise NotImplementedError('%s.has(%r)' % (type(self).__name__, subexpr))
596
597 - def _subs(self, subexpr, newexpr):
598 head, data = self.pair
599
600 if head is subexpr.head:
601 if data == subexpr.data:
602 return newexpr
603
604 if head is SYMBOL or head is NUMBER:
605 return self
606
607 cls = type(self)
608
609 if head is TERMS:
610 d = {}
611 d_get = d.get
612 one = cls.one
613 for t,c in data.iteritems():
614 r = t._subs(subexpr, newexpr)
615 if c is 1:
616 inplace_add(cls, r, d, d_get, one)
617 else:
618 inplace_add2(cls, r, c, d, d_get, one)
619 return return_terms(cls, d)
620
621 elif head is FACTORS:
622 d = {}
623 d_get = d.get
624 num = 1
625 for t,c in data.iteritems():
626 r = t._subs(subexpr, newexpr)
627 if type(c) is cls:
628 c = c._subs(subexpr, newexpr)
629 if c is 1:
630 n = inplace_mul(cls, r, d, d_get)
631 else:
632 n = inplace_mul2(cls, r, c, d, d_get)
633 if n is not 1:
634 num = num * n
635 r = return_factors(cls, d)
636 if num is 1:
637 return r
638 return r * num
639
640 elif callable(head):
641 args = data
642 if type(args) is tuple:
643 args = [a._subs(subexpr, newexpr) for a in args]
644 return head(*args)
645 else:
646 return head(args._subs(subexpr, newexpr))
647
648 raise NotImplementedError(`self`)
649
651 """ Return ``(term, coeff)`` such that self=term*coeff and
652 coeff is integer.
653 """
654 head, data = self.pair
655 if head is NUMBER:
656 td = type(data)
657 if td is int or td is long:
658 return self.one, data
659 if td is FractionTyple:
660 n,p = data
661 return self.Number(mpq((1, p))), n
662 elif head is TERMS:
663 if len(data)==1:
664 t, c = data.items()[0]
665 td = type(c)
666 if td is int or td is long:
667 return t, c
668 if td is mpq:
669 n,p = c
670 return t * FractionTyple((1,p)), n
671 return self, 1
672
674 """ Convert pairs representation to ``{exponents: coeff},
675 variables_list`` representation.
676
677 For example::
678
679 x**2 + 3*sin(x) -> {(2,0):1, (0,1):3}, [x,sin(x)]
680 3*x*(x+y)**2 -> {(1,2):3}, [x, x+y]
681
682 Notes:
683
684 * In general, ``exponent``-s are tuples of integers. An
685 expection is when ``len(variable_list)==1``: ``exponent``-s
686 will be integers.
687
688 * When ``fixed`` is True then one must provide ``variables` list
689 that will be equal to returned ``variable_list``. Symbols
690 not in a variables list are treated as coefficients.
691
692 """
693 head, data = self.pair
694 cls = type(self)
695 if variables is None:
696 assert not fixed
697 variables = []
698 else:
699 variables = list(variables)
700 if head is NUMBER:
701 l = len(variables)
702 if l==1:
703 return {0 : data}, variables
704 return {(0,)*l : data}, variables
705 if head is TERMS:
706 exps_append_methods = []
707 exponents = []
708 coeffs = []
709 for t, c in data.iteritems():
710 if fixed:
711 rest = 1
712 h, d = t.pair
713 exps = [0] * len(variables)
714 exps_append_methods.append(exps.append)
715 if h is FACTORS:
716 for f, e in d.iteritems():
717 if isinstance(e, CommutativeRing):
718 r, e = e.as_term_intcoeff()
719 f = f**r
720 else:
721 te = type(e)
722 if te is mpq:
723 n,p = e
724 f = f ** mpq((1,p))
725 e = n
726 elif te is int or te is long:
727 pass
728 else:
729 f = cls.Factors((f,e))
730 e = 1
731 try:
732 i = variables.index(f)
733 except ValueError:
734 if fixed:
735
736 rest *= f**e
737 else:
738 i = len(variables)
739 variables.append(f)
740 [mth(0) for mth in exps_append_methods]
741 exps[i] += e
742 else:
743 exps[i] += e
744 elif h is NUMBER:
745 assert d==1,`t`
746 else:
747 try:
748 i = variables.index(t)
749 except ValueError:
750 if fixed:
751 rest *= t
752 else:
753 i = len(variables)
754 variables.append(t)
755 [mth(0) for mth in exps_append_methods]
756 exps[i] += 1
757 else:
758 exps[i] += 1
759 exponents.append(exps)
760 if fixed and rest is not 1:
761 c = c * rest
762 coeffs.append(c)
763 l = len(variables)
764 d = {}
765 if l==1:
766 for exps, coeff in zip(exponents, coeffs):
767 d[exps[0]] = coeff
768 else:
769 for exps, coeff in zip(exponents, coeffs):
770 d[tuple(exps)] = coeff
771 return d, variables
772 elif head is FACTORS:
773 exps = [0] * len(variables)
774 coeff = 1
775 for f, e in data.iteritems():
776 if isinstance(e, CommutativeRing):
777 r, e = e.as_term_intcoeff()
778 f = f**r
779 else:
780 te = type(e)
781 if te is mpq:
782 n,p = e
783 f = f ** mpq((1,p))
784 e = n
785 elif te is int or te is long:
786 pass
787 else:
788 f = cls.Factors((f,e))
789 e = 1
790 try:
791 i = variables.index(f)
792 except ValueError:
793 if fixed:
794 coeff *= f**e
795 continue
796 i = len(variables)
797 variables.append(f)
798 exps.append(0)
799 exps[i] += e
800 l = len(variables)
801 if l==1:
802 return {exps[0]: coeff}, variables
803 return {tuple(exps): coeff}, variables
804 else:
805 coeff = 1
806 l = len(variables)
807 exps = [0]*l
808 try:
809 i = variables.index(self)
810 except ValueError:
811 if fixed:
812 coeff = self
813 exps = [0]*l
814 else:
815 i = len(variables)
816 variables.append(self)
817 exps.append(1)
818 l += 1
819 else:
820 exps[i] = 1
821 if l==1:
822 return {exps[0]: coeff}, variables
823 return {tuple(exps): coeff}, variables
824
825 classes.CollectingField = CollectingField
826
827
828 CollectingField.one = CollectingField.Number(1)
829 CollectingField.zero = CollectingField.Number(0)
830