2 from basic
import Basic
, S
, C
, sympify
3 from operations
import AssocOp
4 from cache
import cacheit
6 from logic
import fuzzy_not
8 from symbol
import Symbol
, Wild
9 # from function import FunctionClass, WildFunction /cyclic/
10 # from numbers import Number, Integer, Real /cyclic/
11 # from add import Add /cyclic/
12 # from power import Pow /cyclic/
14 import sympy
.mpmath
as mpmath
23 def flatten(cls
, seq
):
24 # apply associativity, separate commutative part of seq
31 coeff
= S
.One
# standalone term
34 c_powers
= {} # base -> exp z
35 # e.g. (x+y) -> z for ... * (x+y) * ...
37 exp_dict
= {} # num-base -> exp y
38 # e.g. 3 -> y for ... * 3 * ...
40 inv_exp_dict
= {} # exp -> Mul(num-bases) x x
41 # e.g. x -> 6 for ... * 2 * 3 * ...
46 while c_seq
or nc_seq
:
50 # first process commutative objects
55 o
, order_symbols
= o
.as_expr_symbols(order_symbols
)
60 c_seq
= list(o
.args
[:]) + c_seq
71 base
, exponent
= o
.as_base_exp()
77 # let's collect factors with numeric base
79 exp_dict
[base
] += exponent
81 exp_dict
[base
] = exponent
86 # exp(x) / exp(y) -> exp(x-y)
92 b
, e
= o
.as_base_exp()
98 # (-3 + y) -> (-1) * (3 - y)
99 if b
.is_Add
and e
.is_Number
:
100 #found factor (x+y)**number; split off initial coefficient
101 c
, t
= b
.as_coeff_terms()
102 #last time I checked, Add.as_coeff_terms returns One or NegativeOne
103 #but this might change
104 if c
.is_negative
and not e
.is_integer
:
105 # extracting root from negative number: ignore sign
106 if c
is not S
.NegativeOne
:
107 # make c positive (probably never occurs)
111 #else: ignoring sign from NegativeOne: nothing to do!
116 #else: c is One, so pass
118 # let's collect factors with the same base, so e.g.
130 if isinstance(o
, WildFunction
):
133 o
, order_symbols
= o
.as_expr_symbols(order_symbols
)
137 # separate commutative symbols
142 if o
.__class
__ is cls
:
144 nc_seq
= list(o
.args
) + nc_seq
151 # try to combine last terms: a * a -> a
153 b1
,e1
= o1
.as_base_exp()
154 b2
,e2
= o
.as_base_exp()
156 nc_seq
.insert(0, b1
** (e1
+ e2
))
162 # ................................
169 for b
, e
in c_powers
.items():
178 elif e
.is_Integer
and b
.is_Number
:
181 c_part
.append(Pow(b
, e
))
185 for b
,e
in exp_dict
.items():
186 if e
in inv_exp_dict
:
191 for e
,b
in inv_exp_dict
.items():
200 elif e
.is_Integer
and b
.is_Number
:
211 # (oo|nan|zero) * ...
212 if (coeff
is S
.Infinity
) or (coeff
is S
.NegativeInfinity
):
229 new_nc_part
.append(t
)
230 nc_part
= new_nc_part
231 c_part
.insert(0, coeff
)
232 elif (coeff
is S
.Zero
) or (coeff
is S
.NaN
):
233 c_part
, nc_part
= [coeff
], []
236 c_part
, nc_part
= [coeff
], []
237 elif coeff
!= Real(1):
238 c_part
.insert(0, coeff
)
239 elif coeff
is not S
.One
:
240 c_part
.insert(0, coeff
)
242 # order commutative part canonically
243 c_part
.sort(Basic
.compare
)
246 if len(c_part
)==2 and c_part
[0].is_Number
and c_part
[1].is_Add
:
247 # 2*(1+a) -> 2 + 2 * a
249 c_part
= [Add(*[coeff
*f
for f
in c_part
[1].args
])]
251 return c_part
, nc_part
, order_symbols
254 def _eval_power(b
, e
):
258 # (a*b)**2 -> a**2 * b**2
259 return Mul(*[s
**e
for s
in b
.args
])
262 coeff
, rest
= b
.as_coeff_terms()
266 return (-coeff
)**e
* Mul(*((S
.NegativeOne
,) +rest
))**e
268 return coeff
**e
* Mul(*[s
**e
for s
in rest
])
271 coeff
, rest
= b
.as_coeff_terms()
272 if coeff
is not S
.One
:
273 # (2*a)**3 -> 2**3 * a**3
274 return coeff
**e
* Mul(*[s
**e
for s
in rest
])
276 coeff
, rest
= b
.as_coeff_terms()
277 l
= [s
**e
for s
in rest
]
280 return coeff
**e
* Mul(*l
)
282 c
,t
= b
.as_coeff_terms()
283 if e
.is_even
and c
.is_Number
and c
< 0:
284 return (-c
* Mul(*t
)) ** e
287 # return Mul(*[t**e for t in b])
289 def _eval_evalf(self
, prec
):
290 return AssocOp
._eval
_evalf
(self
, prec
).expand()
293 def as_two_terms(self
):
302 return args
[0], self
._new
_rawargs
(*args
[1:])
305 def as_coeff_terms(self
, x
=None):
314 return Mul(*l1
), tuple(l2
)
317 return coeff
, self
.args
[1:]
318 return S
.One
, self
.args
321 def _expandsums(sums
):
326 left
= Mul
._expandsums
(sums
[:L
//2])
327 right
= Mul
._expandsums
(sums
[L
//2:])
328 if isinstance(right
, Basic
):
330 if isinstance(left
, Basic
):
333 if len(left
) == 1 and len(right
) == 1:
334 # no expansion needed, bail out now to avoid infinite recursion
335 return [Mul(left
[0], right
[0])]
340 terms
.append(Mul(a
,b
).expand())
343 terms
= list(added
.args
)
348 def _eval_expand_basic(self
):
349 plain
, sums
, rewrite
= [], [], False
351 for factor
in self
.args
:
352 terms
= factor
._eval
_expand
_basic
()
354 if terms
is not None:
361 if factor
.is_commutative
:
364 sums
.append([factor
])
366 if terms
is not None:
373 terms
= Mul
._expandsums
(sums
)
375 if isinstance(terms
, Basic
):
380 return Add(*(Mul(plain
, term
) for term
in terms
), **self
.assumptions0
)
382 return Mul(*plain
, **self
.assumptions0
)
384 def _eval_derivative(self
, s
):
385 terms
= list(self
.args
)
387 for i
in xrange(len(terms
)):
391 factors
.append(Mul(*(terms
[:i
]+[t
]+terms
[i
+1:])))
394 def _matches_simple(pattern
, expr
, repl_dict
):
395 # handle (w*3).matches('x*5') -> {w: x*5/3}
396 coeff
, terms
= pattern
.as_coeff_terms()
398 return terms
[0].matches(expr
/ coeff
, repl_dict
)
401 def matches(pattern
, expr
, repl_dict
={}, evaluate
=False):
403 if pattern
.is_commutative
and expr
.is_commutative
:
404 return AssocOp
._matches
_commutative
(pattern
, expr
, repl_dict
, evaluate
)
405 # todo for commutative parts, until then use the default matches method for non-commutative products
406 return Basic
.matches(pattern
, expr
, repl_dict
, evaluate
)
409 def _combine_inverse(lhs
, rhs
):
414 def as_powers_dict(self
):
415 return dict([ term
.as_base_exp() for term
in self
])
417 def as_numer_denom(self
):
418 numers
, denoms
= [],[]
420 n
,d
= t
.as_numer_denom()
423 return Mul(*numers
), Mul(*denoms
)
426 def count_ops(self
, symbolic
=True):
428 return Add(*[t
.count_ops(symbolic
) for t
in self
[:]]) + Symbol('MUL') * (len(self
[:])-1)
429 return Add(*[t
.count_ops(symbolic
) for t
in self
.args
[:]]) + (len(self
.args
)-1)
431 def _eval_is_polynomial(self
, syms
):
432 for term
in self
.args
:
433 if not term
._eval
_is
_polynomial
(syms
):
437 _eval_is_bounded
= lambda self
: self
._eval
_template
_is
_attr
('is_bounded')
438 _eval_is_commutative
= lambda self
: self
._eval
_template
_is
_attr
('is_commutative')
439 _eval_is_integer
= lambda self
: self
._eval
_template
_is
_attr
('is_integer')
440 _eval_is_comparable
= lambda self
: self
._eval
_template
_is
_attr
('is_comparable')
443 # I*I -> R, I*I*I -> -I
445 def _eval_is_real(self
):
458 elif fuzzy_not(t_real
):
467 return (im_count
% 2 == 0)
470 def _eval_is_imaginary(self
):
484 return (im_count
% 2 == 1)
488 def _eval_is_irrational(self
):
495 def _eval_is_positive(self
):
496 terms
= [t
for t
in self
.args
if not t
.is_positive
]
505 if c
.is_negative
and r
.is_negative
:
507 if r
.is_negative
and c
.is_negative
:
509 # check for nonpositivity, <=0
510 if c
.is_negative
and r
.is_nonnegative
:
512 if r
.is_negative
and c
.is_nonnegative
:
514 if c
.is_nonnegative
and r
.is_nonpositive
:
516 if r
.is_nonnegative
and c
.is_nonpositive
:
520 def _eval_is_negative(self
):
521 terms
= [t
for t
in self
.args
if not t
.is_positive
]
523 # all terms are either positive -- 2*Symbol('n', positive=T)
524 # or unknown -- 2*Symbol('x')
533 # check for nonnegativity, >=0
534 if c
.is_negative
and r
.is_nonpositive
:
536 if r
.is_negative
and c
.is_nonpositive
:
538 if c
.is_nonpositive
and r
.is_nonpositive
:
540 if c
.is_nonnegative
and r
.is_nonnegative
:
543 def _eval_is_odd(self
):
544 is_integer
= self
.is_integer
556 elif is_integer
== False:
560 def _eval_is_even(self
):
561 is_integer
= self
.is_integer
564 return fuzzy_not(self
._eval
_is
_odd
())
566 elif is_integer
== False:
569 def _eval_subs(self
, old
, new
):
572 if isinstance(old
, FunctionClass
):
573 return self
.__class
__(*[s
.subs(old
, new
) for s
in self
.args
])
574 coeff1
,terms1
= self
.as_coeff_terms()
575 coeff2
,terms2
= old
.as_coeff_terms()
576 if terms1
==terms2
: # (2*a).subs(3*a,y) -> 2/3*y
577 return new
* coeff1
/coeff2
578 l1
,l2
= len(terms1
),len(terms2
)
580 # if old is just a number, go through the self.args one by one
581 return Mul(*[x
.subs(old
, new
) for x
in self
.args
])
583 # old is some something more complex, like:
584 # (a*b*c*d).subs(b*c,x) -> a*x*d
585 # then we need to search where in self.args the "old" is, and then
586 # correctly substitute both terms and coefficients.
587 for i
in xrange(l1
-l2
+1):
588 if terms2
==terms1
[i
:i
+l2
]:
589 m1
= Mul(*terms1
[:i
]).subs(old
,new
)
590 m2
= Mul(*terms1
[i
+l2
:]).subs(old
,new
)
591 return Mul(*([coeff1
/coeff2
,m1
,new
,m2
]))
592 return self
.__class
__(*[s
.subs(old
, new
) for s
in self
.args
])
594 def _eval_oseries(self
, order
):
599 #separate terms containing "x" (r) and the rest (l)
605 #if r is empty or just one term, it's easy:
607 if order
.contains(1,x
): return S
.Zero
610 return Mul(*(l
+ [r
[0].oseries(order
)]))
611 #otherwise, we need to calculate how many orders we need to calculate
612 #in each term. Currently this is done using as_leading_term, but this
613 #is fragile and slow, because this involves limits. Let's find some
614 #more clever approach.
615 lt
= [t
.as_leading_term(x
) for t
in r
]
616 for i
in xrange(len(r
)):
617 m
= Mul(*(lt
[:i
]+lt
[i
+1:]))
618 #calculate how many orders we want
620 #expand each term and multiply things together
621 l
.append(r
[i
].oseries(o
))
622 #shouldn't we rather expand everything? This seems to me to leave
623 #things as (x+x**2+...)*(x-x**2+...) etc.:
626 def nseries(self
, x
, x0
, n
):
627 terms
= [t
.nseries(x
, x0
, n
) for t
in self
.args
]
628 return Mul(*terms
).expand()
631 def _eval_as_leading_term(self
, x
):
632 return Mul(*[t
.as_leading_term(x
) for t
in self
.args
])
634 def _eval_conjugate(self
):
635 return Mul(*[t
.conjugate() for t
in self
.args
])
661 import operations
as _