1
2 """
3 This module implements extension type Expr that holds two Python
4 objects, head and data, in a pair attribute.
5
6 When adding new features to Expr class, make sure that these are
7 added also to extension type Expr in src/expr_ext.c.
8
9 C Expr:
10
11 >>> from sympycore.expr_ext import *
12 >>> %timeit Expr(1,2)
13 10000000 loops, best of 3: 179 ns per loop
14 >>> e=Expr(1,2)
15 >>> %timeit h = e.head
16 10000000 loops, best of 3: 113 ns per loop
17 >>> %timeit h,d = e.pair
18 10000000 loops, best of 3: 141 ns per loop
19
20 Python Expr:
21
22 >>> from sympycore.expr import *
23 >>> %timeit Expr(1,2)
24 1000000 loops, best of 3: 988 ns per loop
25 >>> e=Expr(1,2)
26 >>> %timeit h = e.head
27 10000000 loops, best of 3: 119 ns per loop
28 >>> %timeit h,d = e.pair
29 10000000 loops, best of 3: 139 ns per loop
30 """
31
32
33
34 __all__ = ['Expr']
35
36 rc_switch = []
37
39 """Represents an symbolic expression in a pair form: (head, data)
40
41 The pair (head, data) is saved in an attribute ``pair``. The parts of
42 a pair, head and data, can be also accessed via ``head`` and ``data``
43 attributes, respectively. All three attributes are read-only.
44
45 The head part is assumed to be an immutable object. The data part can
46 be either an immutable object or Python dictionary. In the former
47 case, the hash value of Expr instance is defined as::
48
49 hash((<Expr>.head, <Expr>.
50
51 Otherwise, if ``data`` contains a Python dictionary, then the hash
52 value is defined as::
53
54 hash((<Expr>.head, frozenset(<Expr>.data.items())
55
56 WARNING: the hash value of an Expr instance is computed (and cached)
57 when it is used as a key to Python dictionary. This means that the
58 instance content MUST NOT be changed after the hash is computed. To
59 check if it is safe to change the ``data`` dictionary, use
60 ``is_writable`` attribute that returns True when the hash value has
61 not been computed::
62
63 <Expr>.is_writable -> True or False
64
65 There are two ways to access the parts of a Expr instance from
66 Python::
67
68 a = Expr(<head>, <data>)
69 head, data = a.head, a.data - for backward compatibility
70 head, data = a.pair - fastest way
71
72 When Expr constructor is called with one argument, say ``x``, then
73 ``<Expr subclass>.convert(x)`` will be returned.
74
75 This is Python version of Expr type.
76 """
77
78 __slots__ = ['head', 'data', 'pair', '_hash']
79
88
90 return '%s%r' % (type(self).__name__, self.pair)
91
93 """ Compute hash value.
94
95 Different from expr_ext.Expr, an exception is raised when data
96 dictionary values contain dictionaries.
97 """
98 h = self._hash
99 if not h:
100 pair = self.pair
101 obj = self.as_lowlevel()
102 if obj is not pair:
103 h = hash(obj)
104 else:
105 head, data = pair = self.pair
106 if type(data) is dict:
107 h = hash((head, frozenset(data.iteritems())))
108 else:
109 h = hash(pair)
110 self._hash = h
111 return h
112
113 @property
115 return not self._hash
116
117 @property
120
121 @property
124
125
127 """ Set hash value for the object.
128
129 If hashvalue==-1, then the hash value will be reset.
130
131 Used by pickle support in sympycore.core._reconstruct. DO NOT
132 use this method directly.
133 """
134 if hashvalue==-1:
135 self._hash = None
136 else:
137 self._hash = hashvalue
138
140 version = 1
141 from sympycore.core import _reconstruct
142 if version==1:
143 hashvalue = self._hash
144 if hashvalue is None:
145 hashvalue = -1
146 state = (type(self), self.pair, hashvalue)
147 else:
148 raise NotImplementedError('pickle state version %s' % (version))
149 return _reconstruct, (version, state)
150
151
153 """ Return self as low-level object instance that will be
154 used in comparison and in hash computation.
155
156 By default. as_lowlevel returns ``data`` part if the ``head``
157 part is ``SYMBOL`` or ``NUMBER``. Otherwise it returns
158 ``pair`` tuple. Note that returning a copy of the tuple will
159 disable using ``frozenset`` hash algorithm for dictionaries.
160 """
161 head, data = pair = self.pair
162 if head is NUMBER or head is SYMBOL:
163 return data
164 return pair
165
167 return not not self.data
168
170 pair = self.pair
171 tother = type(other)
172 if tother is not type(self):
173 obj = self.as_lowlevel()
174 if obj is not pair or type(obj) is tother:
175 return obj == other
176 return False
177 return pair==other.pair
178
180 return not (self==other)
181
183 pair = self.pair
184 tother = type(other)
185 if tother is not type(self):
186 obj = self.as_lowlevel()
187 if obj is not pair or type(obj) is tother:
188 return obj < other
189 return NotImplemented
190 return pair < other.pair
191
193 pair = self.pair
194 tother = type(other)
195 if tother is not type(self):
196 obj = self.as_lowlevel()
197 if obj is not pair or type(obj) is tother:
198 return obj <= other
199 return NotImplemented
200 return pair <= other.pair
201
203 pair = self.pair
204 tother = type(other)
205 if tother is not type(self):
206 obj = self.as_lowlevel()
207 if obj is not pair or type(obj) is tother:
208 return obj > other
209 return NotImplemented
210 return pair > other.pair
211
213 pair = self.pair
214 tother = type(other)
215 if tother is not type(self):
216 obj = self.as_lowlevel()
217 if obj is not pair or type(obj) is tother:
218 return obj >= other
219 return NotImplemented
220 return pair >= other.pair
221
222
223 from .utils import NUMBER, SYMBOL
224