Coverage for /usr/lib/python3/dist-packages/sympy/strategies/rl.py: 30%
54 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1""" Generic Rules for SymPy
3This file assumes knowledge of Basic and little else.
4"""
5from sympy.utilities.iterables import sift
6from .util import new
9# Functions that create rules
10def rm_id(isid, new=new):
11 """ Create a rule to remove identities.
13 isid - fn :: x -> Bool --- whether or not this element is an identity.
15 Examples
16 ========
18 >>> from sympy.strategies import rm_id
19 >>> from sympy import Basic, S
20 >>> remove_zeros = rm_id(lambda x: x==0)
21 >>> remove_zeros(Basic(S(1), S(0), S(2)))
22 Basic(1, 2)
23 >>> remove_zeros(Basic(S(0), S(0))) # If only identites then we keep one
24 Basic(0)
26 See Also:
27 unpack
28 """
29 def ident_remove(expr):
30 """ Remove identities """
31 ids = list(map(isid, expr.args))
32 if sum(ids) == 0: # No identities. Common case
33 return expr
34 elif sum(ids) != len(ids): # there is at least one non-identity
35 return new(expr.__class__,
36 *[arg for arg, x in zip(expr.args, ids) if not x])
37 else:
38 return new(expr.__class__, expr.args[0])
40 return ident_remove
43def glom(key, count, combine):
44 """ Create a rule to conglomerate identical args.
46 Examples
47 ========
49 >>> from sympy.strategies import glom
50 >>> from sympy import Add
51 >>> from sympy.abc import x
53 >>> key = lambda x: x.as_coeff_Mul()[1]
54 >>> count = lambda x: x.as_coeff_Mul()[0]
55 >>> combine = lambda cnt, arg: cnt * arg
56 >>> rl = glom(key, count, combine)
58 >>> rl(Add(x, -x, 3*x, 2, 3, evaluate=False))
59 3*x + 5
61 Wait, how are key, count and combine supposed to work?
63 >>> key(2*x)
64 x
65 >>> count(2*x)
66 2
67 >>> combine(2, x)
68 2*x
69 """
70 def conglomerate(expr):
71 """ Conglomerate together identical args x + x -> 2x """
72 groups = sift(expr.args, key)
73 counts = {k: sum(map(count, args)) for k, args in groups.items()}
74 newargs = [combine(cnt, mat) for mat, cnt in counts.items()]
75 if set(newargs) != set(expr.args):
76 return new(type(expr), *newargs)
77 else:
78 return expr
80 return conglomerate
83def sort(key, new=new):
84 """ Create a rule to sort by a key function.
86 Examples
87 ========
89 >>> from sympy.strategies import sort
90 >>> from sympy import Basic, S
91 >>> sort_rl = sort(str)
92 >>> sort_rl(Basic(S(3), S(1), S(2)))
93 Basic(1, 2, 3)
94 """
96 def sort_rl(expr):
97 return new(expr.__class__, *sorted(expr.args, key=key))
98 return sort_rl
101def distribute(A, B):
102 """ Turns an A containing Bs into a B of As
104 where A, B are container types
106 >>> from sympy.strategies import distribute
107 >>> from sympy import Add, Mul, symbols
108 >>> x, y = symbols('x,y')
109 >>> dist = distribute(Mul, Add)
110 >>> expr = Mul(2, x+y, evaluate=False)
111 >>> expr
112 2*(x + y)
113 >>> dist(expr)
114 2*x + 2*y
115 """
117 def distribute_rl(expr):
118 for i, arg in enumerate(expr.args):
119 if isinstance(arg, B):
120 first, b, tail = expr.args[:i], expr.args[i], expr.args[i + 1:]
121 return B(*[A(*(first + (arg,) + tail)) for arg in b.args])
122 return expr
123 return distribute_rl
126def subs(a, b):
127 """ Replace expressions exactly """
128 def subs_rl(expr):
129 if expr == a:
130 return b
131 else:
132 return expr
133 return subs_rl
136# Functions that are rules
137def unpack(expr):
138 """ Rule to unpack singleton args
140 >>> from sympy.strategies import unpack
141 >>> from sympy import Basic, S
142 >>> unpack(Basic(S(2)))
143 2
144 """
145 if len(expr.args) == 1:
146 return expr.args[0]
147 else:
148 return expr
151def flatten(expr, new=new):
152 """ Flatten T(a, b, T(c, d), T2(e)) to T(a, b, c, d, T2(e)) """
153 cls = expr.__class__
154 args = []
155 for arg in expr.args:
156 if arg.__class__ == cls:
157 args.extend(arg.args)
158 else:
159 args.append(arg)
160 return new(expr.__class__, *args)
163def rebuild(expr):
164 """ Rebuild a SymPy tree.
166 Explanation
167 ===========
169 This function recursively calls constructors in the expression tree.
170 This forces canonicalization and removes ugliness introduced by the use of
171 Basic.__new__
172 """
173 if expr.is_Atom:
174 return expr
175 else:
176 return expr.func(*list(map(rebuild, expr.args)))