Coverage for /usr/lib/python3/dist-packages/sympy/core/containers.py: 59%
170 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"""Module for SymPy containers
3 (SymPy objects that store other SymPy objects)
5 The containers implemented in this module are subclassed to Basic.
6 They are supposed to work seamlessly within the SymPy framework.
7"""
9from collections import OrderedDict
10from collections.abc import MutableSet
11from typing import Any, Callable
13from .basic import Basic
14from .sorting import default_sort_key, ordered
15from .sympify import _sympify, sympify, _sympy_converter, SympifyError
16from sympy.core.kind import Kind
17from sympy.utilities.iterables import iterable
18from sympy.utilities.misc import as_int
21class Tuple(Basic):
22 """
23 Wrapper around the builtin tuple object.
25 Explanation
26 ===========
28 The Tuple is a subclass of Basic, so that it works well in the
29 SymPy framework. The wrapped tuple is available as self.args, but
30 you can also access elements or slices with [:] syntax.
32 Parameters
33 ==========
35 sympify : bool
36 If ``False``, ``sympify`` is not called on ``args``. This
37 can be used for speedups for very large tuples where the
38 elements are known to already be SymPy objects.
40 Examples
41 ========
43 >>> from sympy import Tuple, symbols
44 >>> a, b, c, d = symbols('a b c d')
45 >>> Tuple(a, b, c)[1:]
46 (b, c)
47 >>> Tuple(a, b, c).subs(a, d)
48 (d, b, c)
50 """
52 def __new__(cls, *args, **kwargs):
53 if kwargs.get('sympify', True):
54 args = (sympify(arg) for arg in args)
55 obj = Basic.__new__(cls, *args)
56 return obj
58 def __getitem__(self, i):
59 if isinstance(i, slice):
60 indices = i.indices(len(self))
61 return Tuple(*(self.args[j] for j in range(*indices)))
62 return self.args[i]
64 def __len__(self):
65 return len(self.args)
67 def __contains__(self, item):
68 return item in self.args
70 def __iter__(self):
71 return iter(self.args)
73 def __add__(self, other):
74 if isinstance(other, Tuple):
75 return Tuple(*(self.args + other.args))
76 elif isinstance(other, tuple):
77 return Tuple(*(self.args + other))
78 else:
79 return NotImplemented
81 def __radd__(self, other):
82 if isinstance(other, Tuple):
83 return Tuple(*(other.args + self.args))
84 elif isinstance(other, tuple):
85 return Tuple(*(other + self.args))
86 else:
87 return NotImplemented
89 def __mul__(self, other):
90 try:
91 n = as_int(other)
92 except ValueError:
93 raise TypeError("Can't multiply sequence by non-integer of type '%s'" % type(other))
94 return self.func(*(self.args*n))
96 __rmul__ = __mul__
98 def __eq__(self, other):
99 if isinstance(other, Basic):
100 return super().__eq__(other)
101 return self.args == other
103 def __ne__(self, other):
104 if isinstance(other, Basic):
105 return super().__ne__(other)
106 return self.args != other
108 def __hash__(self):
109 return hash(self.args)
111 def _to_mpmath(self, prec):
112 return tuple(a._to_mpmath(prec) for a in self.args)
114 def __lt__(self, other):
115 return _sympify(self.args < other.args)
117 def __le__(self, other):
118 return _sympify(self.args <= other.args)
120 # XXX: Basic defines count() as something different, so we can't
121 # redefine it here. Originally this lead to cse() test failure.
122 def tuple_count(self, value) -> int:
123 """Return number of occurrences of value."""
124 return self.args.count(value)
126 def index(self, value, start=None, stop=None):
127 """Searches and returns the first index of the value."""
128 # XXX: One would expect:
129 #
130 # return self.args.index(value, start, stop)
131 #
132 # here. Any trouble with that? Yes:
133 #
134 # >>> (1,).index(1, None, None)
135 # Traceback (most recent call last):
136 # File "<stdin>", line 1, in <module>
137 # TypeError: slice indices must be integers or None or have an __index__ method
138 #
139 # See: http://bugs.python.org/issue13340
141 if start is None and stop is None:
142 return self.args.index(value)
143 elif stop is None:
144 return self.args.index(value, start)
145 else:
146 return self.args.index(value, start, stop)
148 @property
149 def kind(self):
150 """
151 The kind of a Tuple instance.
153 The kind of a Tuple is always of :class:`TupleKind` but
154 parametrised by the number of elements and the kind of each element.
156 Examples
157 ========
159 >>> from sympy import Tuple, Matrix
160 >>> Tuple(1, 2).kind
161 TupleKind(NumberKind, NumberKind)
162 >>> Tuple(Matrix([1, 2]), 1).kind
163 TupleKind(MatrixKind(NumberKind), NumberKind)
164 >>> Tuple(1, 2).kind.element_kind
165 (NumberKind, NumberKind)
167 See Also
168 ========
170 sympy.matrices.common.MatrixKind
171 sympy.core.kind.NumberKind
172 """
173 return TupleKind(*(i.kind for i in self.args))
175_sympy_converter[tuple] = lambda tup: Tuple(*tup)
181def tuple_wrapper(method):
182 """
183 Decorator that converts any tuple in the function arguments into a Tuple.
185 Explanation
186 ===========
188 The motivation for this is to provide simple user interfaces. The user can
189 call a function with regular tuples in the argument, and the wrapper will
190 convert them to Tuples before handing them to the function.
192 Explanation
193 ===========
195 >>> from sympy.core.containers import tuple_wrapper
196 >>> def f(*args):
197 ... return args
198 >>> g = tuple_wrapper(f)
200 The decorated function g sees only the Tuple argument:
202 >>> g(0, (1, 2), 3)
203 (0, (1, 2), 3)
205 """
206 def wrap_tuples(*args, **kw_args):
207 newargs = []
208 for arg in args:
209 if isinstance(arg, tuple):
210 newargs.append(Tuple(*arg))
211 else:
212 newargs.append(arg)
213 return method(*newargs, **kw_args)
214 return wrap_tuples
217class Dict(Basic):
218 """
219 Wrapper around the builtin dict object.
221 Explanation
222 ===========
224 The Dict is a subclass of Basic, so that it works well in the
225 SymPy framework. Because it is immutable, it may be included
226 in sets, but its values must all be given at instantiation and
227 cannot be changed afterwards. Otherwise it behaves identically
228 to the Python dict.
230 Examples
231 ========
233 >>> from sympy import Dict, Symbol
235 >>> D = Dict({1: 'one', 2: 'two'})
236 >>> for key in D:
237 ... if key == 1:
238 ... print('%s %s' % (key, D[key]))
239 1 one
241 The args are sympified so the 1 and 2 are Integers and the values
242 are Symbols. Queries automatically sympify args so the following work:
244 >>> 1 in D
245 True
246 >>> D.has(Symbol('one')) # searches keys and values
247 True
248 >>> 'one' in D # not in the keys
249 False
250 >>> D[1]
251 one
253 """
255 def __new__(cls, *args):
256 if len(args) == 1 and isinstance(args[0], (dict, Dict)):
257 items = [Tuple(k, v) for k, v in args[0].items()]
258 elif iterable(args) and all(len(arg) == 2 for arg in args):
259 items = [Tuple(k, v) for k, v in args]
260 else:
261 raise TypeError('Pass Dict args as Dict((k1, v1), ...) or Dict({k1: v1, ...})')
262 elements = frozenset(items)
263 obj = Basic.__new__(cls, *ordered(items))
264 obj.elements = elements
265 obj._dict = dict(items) # In case Tuple decides it wants to sympify
266 return obj
268 def __getitem__(self, key):
269 """x.__getitem__(y) <==> x[y]"""
270 try:
271 key = _sympify(key)
272 except SympifyError:
273 raise KeyError(key)
275 return self._dict[key]
277 def __setitem__(self, key, value):
278 raise NotImplementedError("SymPy Dicts are Immutable")
280 def items(self):
281 '''Returns a set-like object providing a view on dict's items.
282 '''
283 return self._dict.items()
285 def keys(self):
286 '''Returns the list of the dict's keys.'''
287 return self._dict.keys()
289 def values(self):
290 '''Returns the list of the dict's values.'''
291 return self._dict.values()
293 def __iter__(self):
294 '''x.__iter__() <==> iter(x)'''
295 return iter(self._dict)
297 def __len__(self):
298 '''x.__len__() <==> len(x)'''
299 return self._dict.__len__()
301 def get(self, key, default=None):
302 '''Returns the value for key if the key is in the dictionary.'''
303 try:
304 key = _sympify(key)
305 except SympifyError:
306 return default
307 return self._dict.get(key, default)
309 def __contains__(self, key):
310 '''D.__contains__(k) -> True if D has a key k, else False'''
311 try:
312 key = _sympify(key)
313 except SympifyError:
314 return False
315 return key in self._dict
317 def __lt__(self, other):
318 return _sympify(self.args < other.args)
320 @property
321 def _sorted_args(self):
322 return tuple(sorted(self.args, key=default_sort_key))
324 def __eq__(self, other):
325 if isinstance(other, dict):
326 return self == Dict(other)
327 return super().__eq__(other)
329 __hash__ : Callable[[Basic], Any] = Basic.__hash__
331# this handles dict, defaultdict, OrderedDict
332_sympy_converter[dict] = lambda d: Dict(*d.items())
334class OrderedSet(MutableSet):
335 def __init__(self, iterable=None):
336 if iterable:
337 self.map = OrderedDict((item, None) for item in iterable)
338 else:
339 self.map = OrderedDict()
341 def __len__(self):
342 return len(self.map)
344 def __contains__(self, key):
345 return key in self.map
347 def add(self, key):
348 self.map[key] = None
350 def discard(self, key):
351 self.map.pop(key)
353 def pop(self, last=True):
354 return self.map.popitem(last=last)[0]
356 def __iter__(self):
357 yield from self.map.keys()
359 def __repr__(self):
360 if not self.map:
361 return '%s()' % (self.__class__.__name__,)
362 return '%s(%r)' % (self.__class__.__name__, list(self.map.keys()))
364 def intersection(self, other):
365 return self.__class__([val for val in self if val in other])
367 def difference(self, other):
368 return self.__class__([val for val in self if val not in other])
370 def update(self, iterable):
371 for val in iterable:
372 self.add(val)
374class TupleKind(Kind):
375 """
376 TupleKind is a subclass of Kind, which is used to define Kind of ``Tuple``.
378 Parameters of TupleKind will be kinds of all the arguments in Tuples, for
379 example
381 Parameters
382 ==========
384 args : tuple(element_kind)
385 element_kind is kind of element.
386 args is tuple of kinds of element
388 Examples
389 ========
391 >>> from sympy import Tuple
392 >>> Tuple(1, 2).kind
393 TupleKind(NumberKind, NumberKind)
394 >>> Tuple(1, 2).kind.element_kind
395 (NumberKind, NumberKind)
397 See Also
398 ========
400 sympy.core.kind.NumberKind
401 MatrixKind
402 sympy.sets.sets.SetKind
403 """
404 def __new__(cls, *args):
405 obj = super().__new__(cls, *args)
406 obj.element_kind = args
407 return obj
409 def __repr__(self):
410 return "TupleKind{}".format(self.element_kind)