Package gmisclib :: Module parse_tree_number
[frames] | no frames]

Source Code for Module gmisclib.parse_tree_number

  1  import sets 
  2  import math 
  3  import operator as OP 
  4   
  5  """This module lets you write mathematical expressions and 
  6  build a parse tree.   You can include variables and floats in 
  7  the expressions, along with elements of arrays. 
  8   
  9  You can then walk the parse tree and find out which variables 
 10  are used, or which elements of a specified array. 
 11  """ 
 12   
13 -class Array(object):
14 - def __init__(self, name):
15 """This creates an array. Arrays are basic 16 elements of expressions.""" 17 self.name = name
18
19 - def __getitem__(self, key):
20 return elementof(self, key)
21
22 - def __str__(self, env={}):
23 if self.name in env: 24 return env[self.name].__str__(env) 25 return self.name
26 27 __repr__ = __str__ 28
29 - def indices(self, env={}):
30 raise RuntimeError, 'It is an array'
31
32 - def eval(self, env={}):
33 raise RuntimeError, 'It is an array'
34 35
36 - def variables(self, env={}):
37 if self.name in env: 38 return env[self.name].variables(env) 39 return sets.Set( ( self.name,) )
40
41 - def debug(self):
42 return '<Array %s>' % self.name
43
44 - def _complexity(self):
45 return 2
46 47
48 -class output_mixin(object):
49 - def __init__(self):
50 pass
51
52 - def eval(self, env={}):
53 """This returns an Expression. 54 """ 55 raise RuntimeError, 'Virtual!'
56
57 - def variables(self, env={}):
58 """Any definitions in env are used to resolve otherwise 59 undefined names. This returns a Set containing 60 all undefined variable names. 61 """ 62 raise RuntimeError, 'Virtual!'
63
64 - def indices(self, variable, env={}):
65 raise RuntimeError, 'Virtual!'
66
67 - def debug(self):
68 return '<Output_mixin: virtual!>'
69
70 - def __str__(self, env={}):
71 """Env is optional. Anything defined in env is used 72 to resolve references, but the outcome may still contain 73 undefined variables, which are printed as names. 74 """ 75 raise RuntimeError, 'Virtual!'
76
77 - def _complexity(self):
78 raise RuntimeError, "Virtual!"
79 80
81 -def _is_opNg1(x, op):
82 assert op in _operator.Priority 83 return isinstance(x, operatorNg1) and x.op==op
84
85 -def _is_op1(x, op):
86 assert op in _operator.Priority 87 return isinstance(x, operator1) and x.op==op
88
89 -def _IMO_float(a, b, indent):
90 if a is b: 91 return Float(1.0) 92 try: 93 fb = float(b) 94 except TypeError: 95 pass 96 else: 97 # b is a Float 98 if fb == 0.0: 99 return None 100 # b is a nonzero float 101 try: 102 fa = float(a) 103 except TypeError: 104 # a is an expression 105 # This is the degenerate case expression/float, which is useless 106 # and leads to infinite loops. 107 return None 108 else: 109 # Both a and b are floats. 110 return Float(fa/fb) 111 return None
112 113
114 -def _IMO_Name(a, b, indent):
115 if isinstance(a, Name) and isinstance(b, Name) and a.name==b.name: 116 return Float(1.0) 117 return None
118
119 -def _IMO_elementof(a, b, indent):
120 if isinstance(a, elementof) and isinstance(b, elementof): 121 # Need to think about the following test a bit... 122 # print indent+'is_mul_of element element', a.array, a.index, b.array, b.index 123 if str(a.array)==str(b.array) and str(a.index)==str(b.index): 124 return Float(1.0) 125 return None
126 127
128 -def _IMO_products(a, b, indent):
129 if _is_opNg1(a, '*'): 130 atmp = list(a.operands) 131 else: 132 atmp = [ a ] 133 if _is_opNg1(b, '*'): 134 btmp = list(b.operands) 135 else: 136 btmp = [ b ] 137 # print 'atmp=', atmp 138 # print 'btmp=', btmp 139 if len(atmp) + len(btmp) > 2: 140 rtmp = [] 141 while atmp: 142 ai = atmp.pop() 143 t = None 144 for j in range(len(btmp)): 145 # print indent+'Checking is_mul_of', ai, btmp[j] 146 t = is_mul_of(ai, btmp[j], indent+'#') 147 if t is not None: 148 tmp = coerce_into(t) 149 if tmp._complexity() < ai._complexity()+btmp[j]._complexity(): 150 break 151 if t is not None: 152 # print 'Cancellation', j, ai, btmp[j], '->', t 153 atmp.append(t) 154 btmp.pop(j) 155 else: 156 rtmp.append( ai ) 157 if len(btmp) == 0: 158 rv = rtmp[0] 159 for rt in rtmp[1:]: 160 rv *= rt 161 return rv 162 # print indent+'IMO: leftovers:', rtmp, atmp, btmp 163 return None
164 165
166 -def _IMO_neg(a, b, indent):
167 if _is_op1(a, 'U-') and _is_op1(b, 'U-'): 168 return is_mul_of(a.operand, b.operand, indent+'#') 169 if _is_op1(a, 'U-'): 170 t = is_mul_of(a.operand, b, indent+'#') 171 if t is not None: 172 return -t 173 if _is_op1(b, 'U-'): 174 t = is_mul_of(a, b.operand, indent+'#') 175 if t is not None: 176 return -t 177 return None
178 179
180 -def _IMO_exp(a, b, indent):
181 if _is_op1(a, 'exp') and _is_op1(b, 'exp'): 182 return operator1('exp', a.operand-b.operand) 183 return None
184 185
186 -def _IMO_pow(a, b, indent):
187 if _is_opNg1(a, '**') and len(a.operands)==2: 188 if a.operands[1] >= 1.0: 189 t = is_mul_of(a.operands[0], b, indent+'#') 190 # (tx)^n / x = t^n x^(n-1) 191 if t is not None: 192 return operatorN('*', operatorN('**', [ t, a.operands[1] ] ), 193 operatorN('**', [ b, operatorN('-', a.operands[1], 1) ] ) 194 ) 195 if _is_opNg1(a, '**') and len(a.operands)==2 and _is_opNg1(b, '**') and len(b.operands)==2: 196 if a.operands[1] >= b.operands[1]: 197 t = is_mul_of(a.operands[0], b.operands[0], indent+'#') 198 # (tx)^n / x^m = t^n x^(n-m) 199 if t is not None: 200 return operatorN('*', operatorN('**', t, a.operands[1]), 201 operatorN('**', b, 202 operatorN('-', [ a.operands[1], 203 b.operands[1] ] 204 ) 205 ) 206 ) 207 return None
208 209
210 -def is_mul_of(a, b, indent=''):
211 """Returns the ratio a/b if a contains b as a factor, 212 returns None otherwise. 213 The routine does not promise to find all possible factors. 214 """ 215 # print indent+'PTN.is_mul_of', a, b 216 for test in [ 217 # _IMO_float, 218 _IMO_Name, _IMO_elementof, 219 _IMO_neg, 220 _IMO_products, 221 _IMO_exp, _IMO_pow 222 ]: 223 tmp = test(a, b, indent) 224 if tmp is not None: 225 # print "IMO: a=", a, 'b=', b 226 # print 'type(tmp)=', type(tmp), 'test=', tmp 227 if tmp._complexity() < a._complexity()+b._complexity(): 228 return tmp 229 return None
230 231 232 233 234
235 -class abstract_number(object):
236 - def __init__(self):
237 pass
238
239 - def __add__(self, other):
240 # print '__add__', str(self), str(other) 241 try: 242 fs = float(self) 243 except TypeError: 244 fs = None 245 try: 246 fo = float(other) 247 except TypeError: 248 fo = None 249 if fo is not None and fs is not None: 250 return Float(fo + fs) 251 if fs == 0.0: 252 return other 253 if fo == 0.0: 254 return self 255 256 other = coerce_into(other) 257 258 if _is_opNg1(self, '+'): 259 a = list(self.operands) 260 else: 261 a = [ self ] 262 if _is_opNg1(other, '+'): 263 b = list( other.operands ) 264 else: 265 b = [ other ] 266 267 # print '__add__ a=', a 268 # print '__add__ b=', b 269 ao = [] 270 while a: 271 ai = a.pop() 272 # print 'A.pop =', ai 273 for j in range(len(b)): 274 # print '__add__IMO ab', j, ai, b[j] 275 t1 = is_mul_of(ai, b[j]) 276 if t1 is not None: 277 tmp = coerce_into(t1+1.0)*b[j] 278 # print 'tmp=', tmp 279 if tmp._complexity() < ai._complexity()+b[j]._complexity()+1: 280 a.append(tmp) 281 # print 'a.append', tmp 282 b.pop(j) 283 ai = None 284 break 285 else: 286 # print "\ttoo complex:", tmp, tmp._complexity(), ai._complexity(), b[j]._complexity() 287 pass 288 # print '__add__IMO ba', j, b[j], ai 289 t2 = is_mul_of(b[j], ai) 290 if t2 is not None: 291 tmp = coerce_into(t2+1.0)*ai 292 # print 'tmp=', tmp 293 if tmp._complexity() < ai._complexity()+b[j]._complexity()+1: 294 a.append(tmp) 295 ai = None 296 # print 'a.append', tmp 297 b.pop(j) 298 break 299 else: 300 # print "\t too complex:", tmp, tmp._complexity(), ai._complexity(), b[j]._complexity() 301 pass 302 if ai: 303 ao.append(ai) 304 # print '__add__ ao =', ao 305 # print '__add__ btuple=', b 306 return operatorN('+', tuple(ao) + tuple(b))
307 308 __radd__ = __add__ 309
310 - def __sub__(self, other):
311 try: 312 fs = float(self) 313 except TypeError: 314 fs = None 315 try: 316 fo = float(other) 317 except TypeError: 318 fo = None 319 if fo is not None and fs is not None: 320 return Float(fs - fo) 321 if fs == 0.0: 322 return operator1('U-', other) 323 if fo == 0.0: 324 return self 325 other = coerce_into(other) 326 if _is_op1(other, 'U-'): 327 return operatorN('+', (self, other)) 328 if _is_op1(self, 'U-'): 329 return operator1('U-', operatorN('+', (self, other))) 330 return operatorN('-', (self, other))
331
332 - def __rsub__(self, other):
333 return other.__sub__(self)
334
335 - def __pow__(self, other):
336 try: 337 fs = float(self) 338 except TypeError: 339 fs = None 340 try: 341 fo = float(other) 342 except TypeError: 343 fo = None 344 if fo is not None and fs is not None: 345 return Float(fs ** fo) 346 other = coerce_into(other) 347 if _is_float(self, 1.0): 348 return Float(1.0) 349 if _is_float(other, 1.0): 350 return self 351 if _is_float(other, 0.5): 352 return operator1('sqrt', self) 353 return operatorN('**', (self, other))
354
355 - def __rpow__(self, other):
356 return other.__pow__(self)
357
358 - def __mul__(self, other):
359 # print 'PTN __mul__', str(self), str(other) 360 try: 361 fs = float(self) 362 except TypeError: 363 fs = None 364 try: 365 fo = float(other) 366 except TypeError: 367 fo = None 368 if fo is not None and fs is not None: 369 return Float(fo * fs) 370 if fs == 1.0: 371 return other 372 if fo == 1.0: 373 return self 374 if fs == -1.0: 375 return operator1('U-', other) 376 if fo == -1.0: 377 return operator1('U-', self) 378 379 other = coerce_into(other) 380 381 if _is_op1(self, 'U-') and _is_op1(other, 'U-'): 382 self = self.operand 383 other = other.operand 384 385 if _is_opNg1(self, '*'): 386 if _is_opNg1(other, '*'): 387 # print '\tmore', other.operands 388 return self.more(other.operands) 389 else: 390 # print '\tMore', str(other) 391 return self.more( (other,) ) 392 # print 'PTN_mul_out', self, other 393 return operatorN('*', (self, other))
394 395 __rmul__ = __mul__ 396
397 - def __div__(self, other):
398 # print '__div__', self, other 399 try: 400 fs = float(self) 401 except TypeError: 402 fs = None 403 try: 404 fo = float(other) 405 except TypeError: 406 fo = None 407 if fo is not None and fs is not None: 408 return Float(fs / fo) 409 if fo == 1.0: 410 return self 411 if fo == -1.0: 412 return operator1('U-', self) 413 if fo is not None and _is_opNg1(self, '*'): 414 modified = False 415 atmp = list(self.operands) 416 for (i, op) in enumerate(atmp): 417 try: 418 f = float(op) 419 except TypeError: 420 pass 421 else: 422 atmp[i] = Float(f/fo) 423 modified = True 424 if modified: 425 rv = atmp[0] 426 for rt in atmp[1:]: 427 rv *= rt 428 return rv 429 430 other = coerce_into(other) 431 432 t = is_mul_of(self, other) 433 if t is not None: 434 tmp = coerce_into(t) 435 if tmp._complexity() < self._complexity + other._complexity(): 436 return tmp 437 438 if _is_op1(self, 'U-') and _is_op1(other, 'U-'): 439 self = self.operand 440 441 return operatorN('/', (self, other))
442
443 - def __rdiv__(self, other):
444 return other.__div__(self)
445 446 __truediv__ = __div__ 447 __rtruediv__ = __rdiv__ 448
449 - def __neg__(self):
450 try: 451 fs = float(self) 452 except TypeError: 453 pass 454 else: 455 return Float(-fs) 456 457 if _is_op1(self, 'U-'): 458 # -(-x) = x 459 return self.operand 460 if _is_opNg1(self, '-') and len(self.operands)==2: 461 return operatorN('-', (self.operands[1], self.operands[0])) 462 return operator1('U-', self)
463
464 - def __pos__(self):
465 return self
466
467 - def __abs__(self):
468 if _is_op1(self, 'U-'): 469 return operator1('abs', self.operand) 470 if _is_op1(self, 'exp') or _is_op1(self, 'abs'): 471 return self 472 return operator1('abs', self)
473 474 475 476 _Opfcn = {'-': OP.neg, 'abs': abs, 477 'sin': math.sin, 'cos': math.cos, 'exp': math.exp, 478 'log': math.log 479 }
480 -def _do_math(x, name):
481 try: 482 f = float(x) 483 except TypeError: 484 # print 'Name=', name 485 return operator1(name, x) 486 else: 487 return _Opfcn[name](f)
488 489
490 -def abs(x):
491 if _is_op1(x, 'abs'): # abs(abs(x)) = x 492 return x 493 _do_math(x, 'abs')
494
495 -def cos(x):
496 if _is_op1(x, 'abs'): # cos(abs(x)) = cos(x) 497 return _do_math(x.operand, 'cos') 498 return _do_math(x, 'cos')
499
500 -def sin(x):
501 return _do_math(x, 'sin')
502
503 -def exp(x):
504 return _do_math(x, 'exp')
505 506
507 -def log(x):
508 if _is_op1(x, 'exp'): # log(exp(x)) = x 509 return x 510 return _do_math(x, 'log')
511 512
513 -def sqrt(x):
514 if _is_opNg1(x, '**') and len(x.operands)==2 and _is_float(x.operands[1], 2.0): 515 # sqrt(x**2) = abs(x) 516 return operator1('abs', x.operands[0]) 517 return _do_math(x, 'sqrt')
518 519
520 -class Expression(abstract_number,output_mixin):
521 - def __init__(self, value):
522 abstract_number.__init__(self) 523 output_mixin.__init__(self) 524 self.x = coerce_into(value) 525 self._check()
526
527 - def _check(self):
528 assert isinstance(self.x, Float) or isinstance(self.x, abstract_number), 'Bad type=%s' % str(type(self.x))
529
530 - def __iadd__(self, other):
531 self._check() 532 ci = coerce_into(other) 533 # print 'PTN __iadd', type(self.x), self.x.debug(), type(ci), ci.debug() 534 self.x = self.x + ci 535 # print 'PTNa __iadd', type(self.x), self.x.debug() 536 self._check() 537 return self
538
539 - def __isub__(self, other):
540 self._check() 541 self.x = self.x - coerce_into(other) 542 self._check() 543 return self
544
545 - def __imul__(self, other):
546 self._check() 547 self.x = self.x * coerce_into(other) 548 self._check() 549 return self
550
551 - def __idiv__(self, other):
552 self._check() 553 self.x = self.x / coerce_into(other) 554 self._check() 555 return self
556
557 - def eval(self, env={}):
558 return self.x.eval(env)
559
560 - def variables(self, env={}):
561 return self.x.variables(env)
562
563 - def indices(self, variable, env={}):
564 return self.x.indices(variable, env)
565
566 - def __str__(self, env={}):
567 # print 'self.x=', self.x, self.x.debug() 568 return self.x.__str__(env)
569 570 __repr__ = __str__ 571
572 - def debug(self):
573 return 'E%s' % self.x.debug()
574
575 - def __float__(self):
576 # print 'PTN float(expression)', self.x 577 return float(self.x)
578
579 - def priority(self):
580 return self.x.priority()
581
582 - def _complexity(self):
583 return self.x.complexity()
584
585 -class Name(abstract_number,output_mixin):
586 - def __init__(self, name):
587 """This creates a named variable. Names are 588 basic parts of expressions.""" 589 abstract_number.__init__(self) 590 output_mixin.__init__(self) 591 self.name = name
592
593 - def __str__(self, env={}):
594 if self.name in env: 595 return str(env[self.name]) 596 return self.name
597 598 __repr__ = __str__ 599
600 - def eval(self, env):
601 return env.get(self.name, self.name)
602
603 - def variables(self, env={}):
604 if self.name in env: 605 return env[self.name].variables(env) 606 else: 607 return sets.Set( ( self.name,) )
608
609 - def indices(self, variable, env={}):
610 if self.name in env: 611 return env[self.name].indices(variable, env) 612 else: 613 return sets.Set()
614
615 - def debug(self):
616 return '<Name %s>' % self.name
617
618 - def _complexity(self):
619 return 2
620 621
622 -class Float(abstract_number,output_mixin):
623 - def __init__(self, v):
624 self.v = float(v)
625
626 - def eval(self, env={}):
627 return self.v
628
629 - def variables(self, env={}):
630 return sets.Set()
631
632 - def indices(self, variable, env={}):
633 return sets.Set()
634
635 - def debug(self, env={}):
636 return '<Float %g>' % self.v
637
638 - def __str__(self, env={}):
639 return str(self.v)
640
641 - def __float__(self):
642 # print 'Float -> float', self.v 643 return self.v
644
645 - def _complexity(self):
646 return 1
647 648
649 -def _is_float(x, v):
650 # print 'PTN, _is_float, type', type(x), x, str(x) 651 try: 652 f = float(x) 653 except TypeError: 654 return None 655 return f == v
656 657
658 -class _operator(abstract_number, output_mixin):
659 Priority = {'U-': 5, 'abs': 5, 'sin':5, 'cos':5, 'exp':5, 660 'sqrt':5, 'log': 5, 661 '+': 2, '-': 2, '*': 3, '/': 3, 662 '**': 4, 663 '[]': 6 664 } 665
666 - def __init__(self, operator):
667 abstract_number.__init__(self) 668 output_mixin.__init__(self) 669 self.op = operator
670
671 - def priority(self):
672 return self.Priority[self.op]
673 674
675 -def coerce_into(x):
676 if isinstance(x, Expression): 677 x = x.x 678 679 try: 680 f = float(x) 681 except TypeError: 682 # print 'coerce_type=', type(x) 683 assert isinstance(x, abstract_number) 684 assert isinstance(x, output_mixin) 685 # print 'Type=', str(type(x)) 686 return x 687 else: 688 return Float(x)
689 690
691 -class operator1(_operator):
692 Printable = {'U-': '-', 'abs': 'abs', 693 'sin': 'math.sin', 'cos': 'math.cos', 694 'exp': 'math.exp', 'log': 'math.log', 695 'sqrt': 'math.sqrt' 696 }
697 - def __init__(self, operator, operand):
698 # print 'Operator1: operator=', operator 699 _operator.__init__(self, operator) 700 self.operand = operand 701 assert self.op
702
703 - def __str__(self, env={}):
704 return '%s(%s)' % (self.Printable[self.op], str(self.operand))
705 __repr__ = __str__ 706
707 - def eval(self, env={}):
708 _do_math( self.op, self.operand.eval(env) )
709
710 - def variables(self, env={}):
711 return self.operand.variables(env)
712
713 - def indices(self, variable, env={}):
714 return self.operand.indices(variable, env)
715
716 - def debug(self):
717 return '<op1:%s %s>' % (self.op, self.operand.debug())
718
719 - def _complexity(self):
720 return 1 + len(self.operand)
721 722
723 -class elementof(_operator):
724 - def __init__(self, array, index):
725 _operator.__init__(self, '[]') 726 self.array = array 727 self.index = index
728
729 - def __str__(self, env = {}):
730 index = repr(env.get(self.index, self.index)) 731 array = str(env.get(self.array, self.array)) 732 return '%s[%s]' % (array, index)
733 __repr__ = __str__ 734 735
736 - def variables(self, env={}):
737 tmp = sets.Set() 738 tmp.update( self.array.variables(env) ) 739 try: 740 v = self.index.variables(env) 741 tmp.update( v ) 742 except AttributeError: 743 pass 744 return tmp
745
746 - def indices(self, variable, env={}):
747 tmp = sets.Set() 748 if isinstance(self.array, Array) and self.array.name==variable: 749 if isinstance(self.index, abstract_number): 750 tmp.add( self.index.eval(env) ) 751 else: 752 tmp.add(self.index) 753 return tmp
754
755 - def debug(self):
756 if isinstance(self.array, Array): 757 a = self.array.debug() 758 else: 759 a = str(self.array) 760 if isinstance(self.index, abstract_number): 761 i = self.index.debug() 762 else: 763 i = str(self.index) 764 return '%s[%s]' % (a, i)
765 766 # def provably_eq(self, other): 767 # return self.array.provably_equal(other.array) 768 769
770 - def _complexity(self):
771 if isinstance(self.array, Array): 772 a = self.array._complexity() 773 else: 774 a = 0 775 if isinstance(self.index, abstract_number): 776 a += self.index._complexity() 777 return a
778 779 780
781 -def operatorN(operator, operands):
782 if len(operands) == 1: 783 return operands[0] 784 nf = 0 785 for o in operands: 786 try: 787 float(o) 788 nf += 1 789 except TypeError: 790 pass 791 assert nf<len(operands), "All floats!" 792 return operatorNg1(operator, operands)
793 794
795 -class operatorNg1(_operator):
796 - def __init__(self, operator, operands):
797 _operator.__init__(self, operator) 798 assert type(operands) == type( () ) 799 assert len(operands) > 1 800 self.operands = operands
801
802 - def more(self, items):
803 assert type(items) == type( () ) 804 return operatorNg1(self.op, self.operands + items)
805
806 - def __str__(self, env={}):
807 tmp = [] 808 p = self.priority() 809 # print 'OP=', self.op, p 810 # print 'self=', self.debug() 811 for o in self.operands: 812 # print 'o=', o.debug(), 'OPp=', p 813 try: 814 if o.priority() <= p: 815 # print '() pri=', o.priority() 816 tmp.append( '(%s)' % o.__str__(env) ) 817 else: 818 # print 'NO()', type(o), o, o.priority() 819 tmp.append( o.__str__(env) ) 820 except AttributeError, x: 821 # print 'ATTRERR', x 822 so = str(o) 823 if so.startswith('-'): 824 tmp.append( '(%s)' % so) 825 else: 826 tmp.append( so ) 827 return self.op.join( tmp )
828 __repr__ = __str__ 829
830 - def variables(self, env={}):
831 tmp = sets.Set() 832 for o in self.operands: 833 tmp.update( o.variables(env) ) 834 return tmp
835
836 - def indices(self, variable, env={}):
837 tmp = sets.Set() 838 for o in self.operands: 839 tmp.update( o.indices(variable, env) ) 840 return tmp
841
842 - def debug(self):
843 dl = [ x.debug() for x in self.operands ] 844 if len(dl) > 5: 845 dl = dl[:4] + ['...'] 846 return '<opNg1:%s %s>' % (self.op, ' '.join(dl))
847 848
849 - def _complexity(self):
850 c = 1 851 for op in self.operands: 852 c += op._complexity() 853 return c
854 855 856 if __name__ == '__main__': 857 x = 3.0 858 z = 3.0 859 y = Name('q') 860 w = x + y + y 861 print 'w=', w 862 print 'wz*wz*wz=', (w+z)*(w+z)*(w+z) 863 print 'w.variables()=', w.variables() 864 print 'w*w.variables()=', (w*w).variables() 865 print '---------- arrays --' 866 a = Array('p') 867 print "a['x']=", a['x'] 868 print (336.0*a[5])/336.0 - (a[5]*336.0)/336.0 869 print a[1]+a[3]+a[1] 870 print a[1] 871 print a[1]+a[1] 872 print (a[1]+a[1]+a[1]+a[1])/4 873 print a[1]+y+x 874 print (a[1]+y+x)/336 875 print (a[1]+a[2]+a[3])/336 876 print a[1].variables() 877 print a[1].indices('p') 878 print (a[1]+a[3]+a[Name('q')]).indices('p') 879 print a[1].indices('b') 880 print '--------Expressions-----' 881 q = Expression(0.0) 882 print 'Float(Expression)=', float(q) 883 q += w 884 print q 885 print q.variables() 886