1 #from timeit import default_timer as clock
12 Hash of a sequence, that *depends* on the order of elements.
14 # make this more robust:
17 m
= hash(m
+ 1001 ^
hash(x
))
22 def __new__(cls
, type, args
):
23 obj
= object.__new
__(cls
)
25 obj
._args
= tuple(args
)
33 if self
.mhash
is None:
34 h
= hash_seq(self
.args
)
44 def as_coeff_rest(self
):
45 return (Integer(1), self
)
47 def as_base_exp(self
):
48 return (self
, Integer(1))
72 return Mul((x
, Pow((y
, Integer(-1)))))
75 return Mul((y
, Pow((x
, Integer(-1)))))
84 return Mul((Integer(-1), x
))
90 return not self
.__eq
__(x
)
94 if o
.type == self
.type:
95 return self
.args
== o
.args
100 class Integer(Basic
):
103 obj
= Basic
.__new
__(cls
, INTEGER
, [])
108 if self
.mhash
is None:
117 if o
.type == INTEGER
:
125 def __add__(self
, o
):
127 if o
.type == INTEGER
:
128 return Integer(self
.i
+o
.i
)
129 return Basic
.__add
__(self
, o
)
131 def __mul__(self
, o
):
133 if o
.type == INTEGER
:
134 return Integer(self
.i
*o
.i
)
135 return Basic
.__mul
__(self
, o
)
140 def __new__(cls
, name
):
141 obj
= Basic
.__new
__(cls
, SYMBOL
, [])
146 if self
.mhash
is None:
156 return self
.name
== o
.name
165 def __new__(cls
, args
, canonicalize
=True):
166 if canonicalize
== False:
167 obj
= Basic
.__new
__(cls
, ADD
, args
)
170 args
= [sympify(x
) for x
in args
]
171 return Add
.canonicalize(args
)
174 def canonicalize(cls
, args
):
176 #print "Add.canon-start"
179 from csympy
import HashTable
185 if a
.type == INTEGER
:
189 if b
.type == INTEGER
:
192 coeff
, key
= b
.as_coeff_rest()
198 coeff
, key
= a
.as_coeff_rest()
206 #print "Add.canon-end:", len(d)
209 for a
, b
in d
.iteritems():
210 args
.append(Mul((a
, b
)))
218 return Add(args
, False)
220 def freeze_args(self
):
221 #print "add is freezing"
222 if self
._args
_set
is None:
223 self
._args
_set
= frozenset(self
.args
)
231 return self
._args
_set
== o
._args
_set
236 s
= str(self
.args
[0])
237 if self
.args
[0].type == ADD
:
239 for x
in self
.args
[1:]:
240 s
= "%s + %s" % (s
, str(x
))
246 if self
.mhash
is None:
247 # XXX: it is surprising, but this is *not* faster:
249 #h = hash(self._args_set)
252 a
= list(self
.args
[:])
262 for term
in self
.args
:
263 r
.append( term
.expand() )
268 def __new__(cls
, args
, canonicalize
=True):
269 if canonicalize
== False:
270 obj
= Basic
.__new
__(cls
, MUL
, args
)
273 args
= [sympify(x
) for x
in args
]
274 return Mul
.canonicalize(args
)
277 def canonicalize(cls
, args
):
280 from csympy
import HashTable
284 if len(args
) == 2 and args
[0].type == MUL
and args
[1].type == INTEGER
:
287 assert b
.type == INTEGER
292 if a
.args
[0].type == INTEGER
:
294 args
= (b
,) + a
.args
[1:]
296 args
= (b
*a
.args
[0],) + a
.args
[1:]
299 return Mul(args
, False)
303 if a
.type == INTEGER
:
307 if b
.type == INTEGER
:
310 key
, coeff
= b
.as_base_exp()
316 key
, coeff
= a
.as_base_exp()
321 if num
.i
== 0 or len(d
)==0:
324 for a
, b
in d
.iteritems():
325 args
.append(Pow((a
, b
)))
331 return Mul(args
, False)
334 if self
.mhash
is None:
335 # in contrast to Add, here it is faster:
337 h
= hash(self
._args
_set
)
339 #a = list(self.args[:])
347 def freeze_args(self
):
348 #print "mul is freezing"
349 if self
._args
_set
is None:
350 self
._args
_set
= frozenset(self
.args
)
358 return self
._args
_set
== o
._args
_set
363 def as_coeff_rest(self
):
364 if self
.args
[0].type == INTEGER
:
365 return self
.as_two_terms()
366 return (Integer(1), self
)
368 def as_two_terms(self
):
375 return (a0
, Mul(args
[1:], False))
379 s
= str(self
.args
[0])
380 if self
.args
[0].type in [ADD
, MUL
]:
382 for x
in self
.args
[1:]:
383 if x
.type in [ADD
, MUL
]:
384 s
= "%s * (%s)" % (s
, str(x
))
386 s
= "%s*%s" % (s
, str(x
))
390 def expand_two(self
, a
, b
):
392 Both a and b are assumed to be expanded.
394 if a
.type == ADD
and b
.type == ADD
:
413 a
, b
= self
.as_two_terms()
414 r
= Mul
.expand_two(a
, b
)
418 return Mul
.expand_two(a
, b
)
424 def __new__(cls
, args
, canonicalize
=True, do_sympify
=True):
425 if canonicalize
== False:
426 obj
= Basic
.__new
__(cls
, POW
, args
)
429 args
= [sympify(x
) for x
in args
]
430 return Pow
.canonicalize(args
)
433 def canonicalize(cls
, args
):
435 if base
.type == INTEGER
:
440 if exp
.type == INTEGER
:
446 return Pow((base
.args
[0], base
.args
[1]*exp
))
447 return Pow(args
, False)
450 s
= str(self
.args
[0])
451 if self
.args
[0].type == ADD
:
453 if self
.args
[1].type == ADD
:
454 s
= "%s^(%s)" % (s
, str(self
.args
[1]))
456 s
= "%s^%s" % (s
, str(self
.args
[1]))
459 def as_base_exp(self
):
463 base
, exp
= self
.args
464 if base
.type == ADD
and exp
.type == INTEGER
:
468 d
= multinomial_coefficients(m
, n
)
471 for powers
, coeff
in d
.iteritems():
476 for x
, p
in zip(base
.args
, powers
):
481 tt
= Pow((x
, Integer(p
)), do_sympify
=False)
490 #time2 = clock()-time
491 #print "done", time2, t2
496 if isinstance(x
, int):
502 Create a symbolic variable with the name *s*.
505 s -- a string, either a single variable name, or
506 a space separated list of variable names, or
507 a list of variable names.
509 NOTE: The new variable is both returned and automatically injected into
510 the parent's *global* namespace. It's recommended not to use "var" in
511 library code, it is better to use symbols() instead.
514 We define some symbolic variables:
517 >>> var('n xx yy zz')
525 frame
= inspect
.currentframe().f_back
528 if not isinstance(s
, list):
529 s
= re
.split('\s|,', s
)
538 frame
.f_globals
[t
] = sym
542 if len(res
) == 0: # var('')
544 elif len(res
) == 1: # var('x')
546 # otherwise var('a b ...')
550 # we should explicitly break cyclic dependencies as stated in inspect
554 def binomial_coefficients(n
):
555 """Return a dictionary containing pairs {(k1,k2) : C_kn} where
556 C_kn are binomial coefficients and n=k1+k2."""
557 d
= {(0, n
):1, (n
, 0):1}
559 for k
in xrange(1, n
//2+1):
561 d
[k
, n
-k
] = d
[n
-k
, k
] = a
564 def binomial_coefficients_list(n
):
565 """ Return a list of binomial coefficients as rows of the Pascal's
570 for k
in xrange(1, n
//2+1):
575 def multinomial_coefficients(m
, n
, _tuple
=tuple, _zip
=zip):
576 """Return a dictionary containing pairs ``{(k1,k2,..,km) : C_kn}``
577 where ``C_kn`` are multinomial coefficients such that
582 >>> print multinomial_coefficients(2,5)
583 {(3, 2): 10, (1, 4): 5, (2, 3): 10, (5, 0): 1, (0, 5): 1, (4, 1): 5}
585 The algorithm is based on the following result:
587 Consider a polynomial and it's ``m``-th exponent::
589 P(x) = sum_{i=0}^m p_i x^k
590 P(x)^n = sum_{k=0}^{m n} a(n,k) x^k
592 The coefficients ``a(n,k)`` can be computed using the
593 J.C.P. Miller Pure Recurrence [see D.E.Knuth, Seminumerical
594 Algorithms, The art of Computer Programming v.2, Addison
595 Wesley, Reading, 1981;]::
597 a(n,k) = 1/(k p_0) sum_{i=1}^m p_i ((n+1)i-k) a(n,k-i),
599 where ``a(n,0) = p_0^n``.
603 return binomial_coefficients(n
)
604 symbols
= [(0,)*i
+ (1,) + (0,)*(m
-i
-1) for i
in range(m
)]
606 p0
= [_tuple(aa
-bb
for aa
,bb
in _zip(s
,s0
)) for s
in symbols
]
607 r
= {_tuple(aa
*n
for aa
in s0
):1}
610 l
= [0] * (n
*(m
-1)+1)
612 for k
in xrange(1, n
*(m
-1)+1):
615 for i
in xrange(1, min(m
,k
+1)):
620 for t2
, c2
in l
[k
-i
]:
621 tt
= _tuple([aa
+bb
for aa
,bb
in _zip(t2
,t
)])
632 r1
= [(t
, c
//k
) for (t
, c
) in d
.iteritems()]