spacepaste

  1.  
  2. # Python 3. May also work in previous Python versions.
  3. # Inspired by https://bbs.archlinux.org/viewtopic.php?id=165527
  4. def interpret(tree):
  5. '''
  6. Expand a tree containing a recursively defined mathematical expression.
  7. Essentially the problem is turning the following expression into a sum of
  8. simple terms:
  9. -u - v*w
  10. where u, v and w may each be subexpressions of the same form, or simple
  11. variables.
  12. This function expects such an expression as a 3-element list, of which
  13. each element may be another list (recursively, hence the tree) or a
  14. 2-tuple. It returns an iterator which yields each term of the expanded
  15. expression one by one. A "term" here is a dictionary with two entries:
  16. "coef", the numeric coefficient, and "vars", the list of variables
  17. multiplied together to give the term.
  18. Like terms are not combined; in other words, if the input contains a
  19. subexpression like (A + AB)(C + BC), the result will contain (AC + ABC +
  20. ABC + ABBC), not (AC + 2ABC + ABBC). Multiplication is not treated as
  21. commutative, so variables are not rearranged within a term.
  22. Example usage:
  23. >>> expr = [(1, 0), (2, 1), [(0, 1), (1, 2), (2, 0)]]
  24. >>> for term in interpret(expr):
  25. ... print('{coef}*{vars}'.format(**term))
  26. ...
  27. -1*[(1, 0)] # -1*a_1s
  28. 1*[(2, 1), (0, 1)] # 1*a_21*a_s1
  29. 1*[(2, 1), (1, 2), (2, 0)] # 1*a_21*a_12*a_2s
  30. '''
  31. # The basic notion here is that every call to interpret() yields an
  32. # iterator over a sequence of terms. Therefore, recursive calls to
  33. # interpret() can be folded into the "result so far" by multiplying each
  34. # term in the first subexpression by -1, and multiplying each term in the
  35. # second subexpression by each term in the third subexpression (i.e.
  36. # applying the distributive property) and the result by -1.
  37. if len(tree) == 2:
  38. # Base case is easy: just yield one term containing the only variable
  39. # with a coefficient of 1.
  40. var = (tree[0], tree[1])
  41. yield dict(coef=1, vars=[var])
  42. elif len(tree) == 3:
  43. # tree[0] is our first subexpression. interpret(tree[0]) is a bunch of
  44. # terms (implicitly, a sum). Multiplying this subexpression by -1 is
  45. # the same as multiplying each term individually by -1 and summing
  46. # them together afterwards.
  47. for t in interpret(tree[0]):
  48. yield dict(coef=-1*t['coef'], vars=t['vars'])
  49. # tree[1] and tree[2] have to be multiplied together to give simple
  50. # terms that we can add to the result. Since interpret(tree[1]) and
  51. # interpret(tree[2]) are both iterable, we can do this in a nested
  52. # loop.
  53. for s in interpret(tree[1]):
  54. for t in interpret(tree[2]):
  55. # Two terms multiplied together is the same as one term with
  56. # all of their combined variables (in order) and the product
  57. # of their coefficients
  58. new_vars = s['vars'] + t['vars']
  59. new_coef = -1*s['coef']*t['coef']
  60. yield dict(coef=new_coef, vars=new_vars)
  61. else:
  62. raise ValueError("interpret(): malformed argument list")
  63.