Coverage for /usr/lib/python3/dist-packages/sympy/core/symbol.py: 47%
330 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
1from __future__ import annotations
3from .assumptions import StdFactKB, _assume_defined
4from .basic import Basic, Atom
5from .cache import cacheit
6from .containers import Tuple
7from .expr import Expr, AtomicExpr
8from .function import AppliedUndef, FunctionClass
9from .kind import NumberKind, UndefinedKind
10from .logic import fuzzy_bool
11from .singleton import S
12from .sorting import ordered
13from .sympify import sympify
14from sympy.logic.boolalg import Boolean
15from sympy.utilities.iterables import sift, is_sequence
16from sympy.utilities.misc import filldedent
18import string
19import re as _re
20import random
21from itertools import product
22from typing import Any
25class Str(Atom):
26 """
27 Represents string in SymPy.
29 Explanation
30 ===========
32 Previously, ``Symbol`` was used where string is needed in ``args`` of SymPy
33 objects, e.g. denoting the name of the instance. However, since ``Symbol``
34 represents mathematical scalar, this class should be used instead.
36 """
37 __slots__ = ('name',)
39 def __new__(cls, name, **kwargs):
40 if not isinstance(name, str):
41 raise TypeError("name should be a string, not %s" % repr(type(name)))
42 obj = Expr.__new__(cls, **kwargs)
43 obj.name = name
44 return obj
46 def __getnewargs__(self):
47 return (self.name,)
49 def _hashable_content(self):
50 return (self.name,)
53def _filter_assumptions(kwargs):
54 """Split the given dict into assumptions and non-assumptions.
55 Keys are taken as assumptions if they correspond to an
56 entry in ``_assume_defined``.
57 """
58 assumptions, nonassumptions = map(dict, sift(kwargs.items(),
59 lambda i: i[0] in _assume_defined,
60 binary=True))
61 Symbol._sanitize(assumptions)
62 return assumptions, nonassumptions
64def _symbol(s, matching_symbol=None, **assumptions):
65 """Return s if s is a Symbol, else if s is a string, return either
66 the matching_symbol if the names are the same or else a new symbol
67 with the same assumptions as the matching symbol (or the
68 assumptions as provided).
70 Examples
71 ========
73 >>> from sympy import Symbol
74 >>> from sympy.core.symbol import _symbol
75 >>> _symbol('y')
76 y
77 >>> _.is_real is None
78 True
79 >>> _symbol('y', real=True).is_real
80 True
82 >>> x = Symbol('x')
83 >>> _symbol(x, real=True)
84 x
85 >>> _.is_real is None # ignore attribute if s is a Symbol
86 True
88 Below, the variable sym has the name 'foo':
90 >>> sym = Symbol('foo', real=True)
92 Since 'x' is not the same as sym's name, a new symbol is created:
94 >>> _symbol('x', sym).name
95 'x'
97 It will acquire any assumptions give:
99 >>> _symbol('x', sym, real=False).is_real
100 False
102 Since 'foo' is the same as sym's name, sym is returned
104 >>> _symbol('foo', sym)
105 foo
107 Any assumptions given are ignored:
109 >>> _symbol('foo', sym, real=False).is_real
110 True
112 NB: the symbol here may not be the same as a symbol with the same
113 name defined elsewhere as a result of different assumptions.
115 See Also
116 ========
118 sympy.core.symbol.Symbol
120 """
121 if isinstance(s, str):
122 if matching_symbol and matching_symbol.name == s:
123 return matching_symbol
124 return Symbol(s, **assumptions)
125 elif isinstance(s, Symbol):
126 return s
127 else:
128 raise ValueError('symbol must be string for symbol name or Symbol')
130def uniquely_named_symbol(xname, exprs=(), compare=str, modify=None, **assumptions):
131 """
132 Return a symbol whose name is derivated from *xname* but is unique
133 from any other symbols in *exprs*.
135 *xname* and symbol names in *exprs* are passed to *compare* to be
136 converted to comparable forms. If ``compare(xname)`` is not unique,
137 it is recursively passed to *modify* until unique name is acquired.
139 Parameters
140 ==========
142 xname : str or Symbol
143 Base name for the new symbol.
145 exprs : Expr or iterable of Expr
146 Expressions whose symbols are compared to *xname*.
148 compare : function
149 Unary function which transforms *xname* and symbol names from
150 *exprs* to comparable form.
152 modify : function
153 Unary function which modifies the string. Default is appending
154 the number, or increasing the number if exists.
156 Examples
157 ========
159 By default, a number is appended to *xname* to generate unique name.
160 If the number already exists, it is recursively increased.
162 >>> from sympy.core.symbol import uniquely_named_symbol, Symbol
163 >>> uniquely_named_symbol('x', Symbol('x'))
164 x0
165 >>> uniquely_named_symbol('x', (Symbol('x'), Symbol('x0')))
166 x1
167 >>> uniquely_named_symbol('x0', (Symbol('x1'), Symbol('x0')))
168 x2
170 Name generation can be controlled by passing *modify* parameter.
172 >>> from sympy.abc import x
173 >>> uniquely_named_symbol('x', x, modify=lambda s: 2*s)
174 xx
176 """
177 def numbered_string_incr(s, start=0):
178 if not s:
179 return str(start)
180 i = len(s) - 1
181 while i != -1:
182 if not s[i].isdigit():
183 break
184 i -= 1
185 n = str(int(s[i + 1:] or start - 1) + 1)
186 return s[:i + 1] + n
188 default = None
189 if is_sequence(xname):
190 xname, default = xname
191 x = compare(xname)
192 if not exprs:
193 return _symbol(x, default, **assumptions)
194 if not is_sequence(exprs):
195 exprs = [exprs]
196 names = set().union(
197 [i.name for e in exprs for i in e.atoms(Symbol)] +
198 [i.func.name for e in exprs for i in e.atoms(AppliedUndef)])
199 if modify is None:
200 modify = numbered_string_incr
201 while any(x == compare(s) for s in names):
202 x = modify(x)
203 return _symbol(x, default, **assumptions)
204_uniquely_named_symbol = uniquely_named_symbol
206class Symbol(AtomicExpr, Boolean):
207 """
208 Assumptions:
209 commutative = True
211 You can override the default assumptions in the constructor.
213 Examples
214 ========
216 >>> from sympy import symbols
217 >>> A,B = symbols('A,B', commutative = False)
218 >>> bool(A*B != B*A)
219 True
220 >>> bool(A*B*2 == 2*A*B) == True # multiplication by scalars is commutative
221 True
223 """
225 is_comparable = False
227 __slots__ = ('name', '_assumptions_orig', '_assumptions0')
229 name: str
231 is_Symbol = True
232 is_symbol = True
234 @property
235 def kind(self):
236 if self.is_commutative:
237 return NumberKind
238 return UndefinedKind
240 @property
241 def _diff_wrt(self):
242 """Allow derivatives wrt Symbols.
244 Examples
245 ========
247 >>> from sympy import Symbol
248 >>> x = Symbol('x')
249 >>> x._diff_wrt
250 True
251 """
252 return True
254 @staticmethod
255 def _sanitize(assumptions, obj=None):
256 """Remove None, convert values to bool, check commutativity *in place*.
257 """
259 # be strict about commutativity: cannot be None
260 is_commutative = fuzzy_bool(assumptions.get('commutative', True))
261 if is_commutative is None:
262 whose = '%s ' % obj.__name__ if obj else ''
263 raise ValueError(
264 '%scommutativity must be True or False.' % whose)
266 # sanitize other assumptions so 1 -> True and 0 -> False
267 for key in list(assumptions.keys()):
268 v = assumptions[key]
269 if v is None:
270 assumptions.pop(key)
271 continue
272 assumptions[key] = bool(v)
274 def _merge(self, assumptions):
275 base = self.assumptions0
276 for k in set(assumptions) & set(base):
277 if assumptions[k] != base[k]:
278 raise ValueError(filldedent('''
279 non-matching assumptions for %s: existing value
280 is %s and new value is %s''' % (
281 k, base[k], assumptions[k])))
282 base.update(assumptions)
283 return base
285 def __new__(cls, name, **assumptions):
286 """Symbols are identified by name and assumptions::
288 >>> from sympy import Symbol
289 >>> Symbol("x") == Symbol("x")
290 True
291 >>> Symbol("x", real=True) == Symbol("x", real=False)
292 False
294 """
295 cls._sanitize(assumptions, cls)
296 return Symbol.__xnew_cached_(cls, name, **assumptions)
298 @staticmethod
299 def __xnew__(cls, name, **assumptions): # never cached (e.g. dummy)
300 if not isinstance(name, str):
301 raise TypeError("name should be a string, not %s" % repr(type(name)))
303 # This is retained purely so that srepr can include commutative=True if
304 # that was explicitly specified but not if it was not. Ideally srepr
305 # should not distinguish these cases because the symbols otherwise
306 # compare equal and are considered equivalent.
307 #
308 # See https://github.com/sympy/sympy/issues/8873
309 #
310 assumptions_orig = assumptions.copy()
312 # The only assumption that is assumed by default is comutative=True:
313 assumptions.setdefault('commutative', True)
315 assumptions_kb = StdFactKB(assumptions)
316 assumptions0 = dict(assumptions_kb)
318 obj = Expr.__new__(cls)
319 obj.name = name
321 obj._assumptions = assumptions_kb
322 obj._assumptions_orig = assumptions_orig
323 obj._assumptions0 = assumptions0
325 # The three assumptions dicts are all a little different:
326 #
327 # >>> from sympy import Symbol
328 # >>> x = Symbol('x', finite=True)
329 # >>> x.is_positive # query an assumption
330 # >>> x._assumptions
331 # {'finite': True, 'infinite': False, 'commutative': True, 'positive': None}
332 # >>> x._assumptions0
333 # {'finite': True, 'infinite': False, 'commutative': True}
334 # >>> x._assumptions_orig
335 # {'finite': True}
336 #
337 # Two symbols with the same name are equal if their _assumptions0 are
338 # the same. Arguably it should be _assumptions_orig that is being
339 # compared because that is more transparent to the user (it is
340 # what was passed to the constructor modulo changes made by _sanitize).
342 return obj
344 @staticmethod
345 @cacheit
346 def __xnew_cached_(cls, name, **assumptions): # symbols are always cached
347 return Symbol.__xnew__(cls, name, **assumptions)
349 def __getnewargs_ex__(self):
350 return ((self.name,), self._assumptions_orig)
352 # NOTE: __setstate__ is not needed for pickles created by __getnewargs_ex__
353 # but was used before Symbol was changed to use __getnewargs_ex__ in v1.9.
354 # Pickles created in previous SymPy versions will still need __setstate__
355 # so that they can be unpickled in SymPy > v1.9.
357 def __setstate__(self, state):
358 for name, value in state.items():
359 setattr(self, name, value)
361 def _hashable_content(self):
362 # Note: user-specified assumptions not hashed, just derived ones
363 return (self.name,) + tuple(sorted(self.assumptions0.items()))
365 def _eval_subs(self, old, new):
366 if old.is_Pow:
367 from sympy.core.power import Pow
368 return Pow(self, S.One, evaluate=False)._eval_subs(old, new)
370 def _eval_refine(self, assumptions):
371 return self
373 @property
374 def assumptions0(self):
375 return self._assumptions0.copy()
377 @cacheit
378 def sort_key(self, order=None):
379 return self.class_key(), (1, (self.name,)), S.One.sort_key(), S.One
381 def as_dummy(self):
382 # only put commutativity in explicitly if it is False
383 return Dummy(self.name) if self.is_commutative is not False \
384 else Dummy(self.name, commutative=self.is_commutative)
386 def as_real_imag(self, deep=True, **hints):
387 if hints.get('ignore') == self:
388 return None
389 else:
390 from sympy.functions.elementary.complexes import im, re
391 return (re(self), im(self))
393 def is_constant(self, *wrt, **flags):
394 if not wrt:
395 return False
396 return self not in wrt
398 @property
399 def free_symbols(self):
400 return {self}
402 binary_symbols = free_symbols # in this case, not always
404 def as_set(self):
405 return S.UniversalSet
408class Dummy(Symbol):
409 """Dummy symbols are each unique, even if they have the same name:
411 Examples
412 ========
414 >>> from sympy import Dummy
415 >>> Dummy("x") == Dummy("x")
416 False
418 If a name is not supplied then a string value of an internal count will be
419 used. This is useful when a temporary variable is needed and the name
420 of the variable used in the expression is not important.
422 >>> Dummy() #doctest: +SKIP
423 _Dummy_10
425 """
427 # In the rare event that a Dummy object needs to be recreated, both the
428 # `name` and `dummy_index` should be passed. This is used by `srepr` for
429 # example:
430 # >>> d1 = Dummy()
431 # >>> d2 = eval(srepr(d1))
432 # >>> d2 == d1
433 # True
434 #
435 # If a new session is started between `srepr` and `eval`, there is a very
436 # small chance that `d2` will be equal to a previously-created Dummy.
438 _count = 0
439 _prng = random.Random()
440 _base_dummy_index = _prng.randint(10**6, 9*10**6)
442 __slots__ = ('dummy_index',)
444 is_Dummy = True
446 def __new__(cls, name=None, dummy_index=None, **assumptions):
447 if dummy_index is not None:
448 assert name is not None, "If you specify a dummy_index, you must also provide a name"
450 if name is None:
451 name = "Dummy_" + str(Dummy._count)
453 if dummy_index is None:
454 dummy_index = Dummy._base_dummy_index + Dummy._count
455 Dummy._count += 1
457 cls._sanitize(assumptions, cls)
458 obj = Symbol.__xnew__(cls, name, **assumptions)
460 obj.dummy_index = dummy_index
462 return obj
464 def __getnewargs_ex__(self):
465 return ((self.name, self.dummy_index), self._assumptions_orig)
467 @cacheit
468 def sort_key(self, order=None):
469 return self.class_key(), (
470 2, (self.name, self.dummy_index)), S.One.sort_key(), S.One
472 def _hashable_content(self):
473 return Symbol._hashable_content(self) + (self.dummy_index,)
476class Wild(Symbol):
477 """
478 A Wild symbol matches anything, or anything
479 without whatever is explicitly excluded.
481 Parameters
482 ==========
484 name : str
485 Name of the Wild instance.
487 exclude : iterable, optional
488 Instances in ``exclude`` will not be matched.
490 properties : iterable of functions, optional
491 Functions, each taking an expressions as input
492 and returns a ``bool``. All functions in ``properties``
493 need to return ``True`` in order for the Wild instance
494 to match the expression.
496 Examples
497 ========
499 >>> from sympy import Wild, WildFunction, cos, pi
500 >>> from sympy.abc import x, y, z
501 >>> a = Wild('a')
502 >>> x.match(a)
503 {a_: x}
504 >>> pi.match(a)
505 {a_: pi}
506 >>> (3*x**2).match(a*x)
507 {a_: 3*x}
508 >>> cos(x).match(a)
509 {a_: cos(x)}
510 >>> b = Wild('b', exclude=[x])
511 >>> (3*x**2).match(b*x)
512 >>> b.match(a)
513 {a_: b_}
514 >>> A = WildFunction('A')
515 >>> A.match(a)
516 {a_: A_}
518 Tips
519 ====
521 When using Wild, be sure to use the exclude
522 keyword to make the pattern more precise.
523 Without the exclude pattern, you may get matches
524 that are technically correct, but not what you
525 wanted. For example, using the above without
526 exclude:
528 >>> from sympy import symbols
529 >>> a, b = symbols('a b', cls=Wild)
530 >>> (2 + 3*y).match(a*x + b*y)
531 {a_: 2/x, b_: 3}
533 This is technically correct, because
534 (2/x)*x + 3*y == 2 + 3*y, but you probably
535 wanted it to not match at all. The issue is that
536 you really did not want a and b to include x and y,
537 and the exclude parameter lets you specify exactly
538 this. With the exclude parameter, the pattern will
539 not match.
541 >>> a = Wild('a', exclude=[x, y])
542 >>> b = Wild('b', exclude=[x, y])
543 >>> (2 + 3*y).match(a*x + b*y)
545 Exclude also helps remove ambiguity from matches.
547 >>> E = 2*x**3*y*z
548 >>> a, b = symbols('a b', cls=Wild)
549 >>> E.match(a*b)
550 {a_: 2*y*z, b_: x**3}
551 >>> a = Wild('a', exclude=[x, y])
552 >>> E.match(a*b)
553 {a_: z, b_: 2*x**3*y}
554 >>> a = Wild('a', exclude=[x, y, z])
555 >>> E.match(a*b)
556 {a_: 2, b_: x**3*y*z}
558 Wild also accepts a ``properties`` parameter:
560 >>> a = Wild('a', properties=[lambda k: k.is_Integer])
561 >>> E.match(a*b)
562 {a_: 2, b_: x**3*y*z}
564 """
565 is_Wild = True
567 __slots__ = ('exclude', 'properties')
569 def __new__(cls, name, exclude=(), properties=(), **assumptions):
570 exclude = tuple([sympify(x) for x in exclude])
571 properties = tuple(properties)
572 cls._sanitize(assumptions, cls)
573 return Wild.__xnew__(cls, name, exclude, properties, **assumptions)
575 def __getnewargs__(self):
576 return (self.name, self.exclude, self.properties)
578 @staticmethod
579 @cacheit
580 def __xnew__(cls, name, exclude, properties, **assumptions):
581 obj = Symbol.__xnew__(cls, name, **assumptions)
582 obj.exclude = exclude
583 obj.properties = properties
584 return obj
586 def _hashable_content(self):
587 return super()._hashable_content() + (self.exclude, self.properties)
589 # TODO add check against another Wild
590 def matches(self, expr, repl_dict=None, old=False):
591 if any(expr.has(x) for x in self.exclude):
592 return None
593 if not all(f(expr) for f in self.properties):
594 return None
595 if repl_dict is None:
596 repl_dict = {}
597 else:
598 repl_dict = repl_dict.copy()
599 repl_dict[self] = expr
600 return repl_dict
603_range = _re.compile('([0-9]*:[0-9]+|[a-zA-Z]?:[a-zA-Z])')
606def symbols(names, *, cls=Symbol, **args) -> Any:
607 r"""
608 Transform strings into instances of :class:`Symbol` class.
610 :func:`symbols` function returns a sequence of symbols with names taken
611 from ``names`` argument, which can be a comma or whitespace delimited
612 string, or a sequence of strings::
614 >>> from sympy import symbols, Function
616 >>> x, y, z = symbols('x,y,z')
617 >>> a, b, c = symbols('a b c')
619 The type of output is dependent on the properties of input arguments::
621 >>> symbols('x')
622 x
623 >>> symbols('x,')
624 (x,)
625 >>> symbols('x,y')
626 (x, y)
627 >>> symbols(('a', 'b', 'c'))
628 (a, b, c)
629 >>> symbols(['a', 'b', 'c'])
630 [a, b, c]
631 >>> symbols({'a', 'b', 'c'})
632 {a, b, c}
634 If an iterable container is needed for a single symbol, set the ``seq``
635 argument to ``True`` or terminate the symbol name with a comma::
637 >>> symbols('x', seq=True)
638 (x,)
640 To reduce typing, range syntax is supported to create indexed symbols.
641 Ranges are indicated by a colon and the type of range is determined by
642 the character to the right of the colon. If the character is a digit
643 then all contiguous digits to the left are taken as the nonnegative
644 starting value (or 0 if there is no digit left of the colon) and all
645 contiguous digits to the right are taken as 1 greater than the ending
646 value::
648 >>> symbols('x:10')
649 (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9)
651 >>> symbols('x5:10')
652 (x5, x6, x7, x8, x9)
653 >>> symbols('x5(:2)')
654 (x50, x51)
656 >>> symbols('x5:10,y:5')
657 (x5, x6, x7, x8, x9, y0, y1, y2, y3, y4)
659 >>> symbols(('x5:10', 'y:5'))
660 ((x5, x6, x7, x8, x9), (y0, y1, y2, y3, y4))
662 If the character to the right of the colon is a letter, then the single
663 letter to the left (or 'a' if there is none) is taken as the start
664 and all characters in the lexicographic range *through* the letter to
665 the right are used as the range::
667 >>> symbols('x:z')
668 (x, y, z)
669 >>> symbols('x:c') # null range
670 ()
671 >>> symbols('x(:c)')
672 (xa, xb, xc)
674 >>> symbols(':c')
675 (a, b, c)
677 >>> symbols('a:d, x:z')
678 (a, b, c, d, x, y, z)
680 >>> symbols(('a:d', 'x:z'))
681 ((a, b, c, d), (x, y, z))
683 Multiple ranges are supported; contiguous numerical ranges should be
684 separated by parentheses to disambiguate the ending number of one
685 range from the starting number of the next::
687 >>> symbols('x:2(1:3)')
688 (x01, x02, x11, x12)
689 >>> symbols(':3:2') # parsing is from left to right
690 (00, 01, 10, 11, 20, 21)
692 Only one pair of parentheses surrounding ranges are removed, so to
693 include parentheses around ranges, double them. And to include spaces,
694 commas, or colons, escape them with a backslash::
696 >>> symbols('x((a:b))')
697 (x(a), x(b))
698 >>> symbols(r'x(:1\,:2)') # or r'x((:1)\,(:2))'
699 (x(0,0), x(0,1))
701 All newly created symbols have assumptions set according to ``args``::
703 >>> a = symbols('a', integer=True)
704 >>> a.is_integer
705 True
707 >>> x, y, z = symbols('x,y,z', real=True)
708 >>> x.is_real and y.is_real and z.is_real
709 True
711 Despite its name, :func:`symbols` can create symbol-like objects like
712 instances of Function or Wild classes. To achieve this, set ``cls``
713 keyword argument to the desired type::
715 >>> symbols('f,g,h', cls=Function)
716 (f, g, h)
718 >>> type(_[0])
719 <class 'sympy.core.function.UndefinedFunction'>
721 """
722 result = []
724 if isinstance(names, str):
725 marker = 0
726 splitters = r'\,', r'\:', r'\ '
727 literals: list[tuple[str, str]] = []
728 for splitter in splitters:
729 if splitter in names:
730 while chr(marker) in names:
731 marker += 1
732 lit_char = chr(marker)
733 marker += 1
734 names = names.replace(splitter, lit_char)
735 literals.append((lit_char, splitter[1:]))
736 def literal(s):
737 if literals:
738 for c, l in literals:
739 s = s.replace(c, l)
740 return s
742 names = names.strip()
743 as_seq = names.endswith(',')
744 if as_seq:
745 names = names[:-1].rstrip()
746 if not names:
747 raise ValueError('no symbols given')
749 # split on commas
750 names = [n.strip() for n in names.split(',')]
751 if not all(n for n in names):
752 raise ValueError('missing symbol between commas')
753 # split on spaces
754 for i in range(len(names) - 1, -1, -1):
755 names[i: i + 1] = names[i].split()
757 seq = args.pop('seq', as_seq)
759 for name in names:
760 if not name:
761 raise ValueError('missing symbol')
763 if ':' not in name:
764 symbol = cls(literal(name), **args)
765 result.append(symbol)
766 continue
768 split: list[str] = _range.split(name)
769 split_list: list[list[str]] = []
770 # remove 1 layer of bounding parentheses around ranges
771 for i in range(len(split) - 1):
772 if i and ':' in split[i] and split[i] != ':' and \
773 split[i - 1].endswith('(') and \
774 split[i + 1].startswith(')'):
775 split[i - 1] = split[i - 1][:-1]
776 split[i + 1] = split[i + 1][1:]
777 for s in split:
778 if ':' in s:
779 if s.endswith(':'):
780 raise ValueError('missing end range')
781 a, b = s.split(':')
782 if b[-1] in string.digits:
783 a_i = 0 if not a else int(a)
784 b_i = int(b)
785 split_list.append([str(c) for c in range(a_i, b_i)])
786 else:
787 a = a or 'a'
788 split_list.append([string.ascii_letters[c] for c in range(
789 string.ascii_letters.index(a),
790 string.ascii_letters.index(b) + 1)]) # inclusive
791 if not split_list[-1]:
792 break
793 else:
794 split_list.append([s])
795 else:
796 seq = True
797 if len(split_list) == 1:
798 names = split_list[0]
799 else:
800 names = [''.join(s) for s in product(*split_list)]
801 if literals:
802 result.extend([cls(literal(s), **args) for s in names])
803 else:
804 result.extend([cls(s, **args) for s in names])
806 if not seq and len(result) <= 1:
807 if not result:
808 return ()
809 return result[0]
811 return tuple(result)
812 else:
813 for name in names:
814 result.append(symbols(name, cls=cls, **args))
816 return type(names)(result)
819def var(names, **args):
820 """
821 Create symbols and inject them into the global namespace.
823 Explanation
824 ===========
826 This calls :func:`symbols` with the same arguments and puts the results
827 into the *global* namespace. It's recommended not to use :func:`var` in
828 library code, where :func:`symbols` has to be used::
830 Examples
831 ========
833 >>> from sympy import var
835 >>> var('x')
836 x
837 >>> x # noqa: F821
838 x
840 >>> var('a,ab,abc')
841 (a, ab, abc)
842 >>> abc # noqa: F821
843 abc
845 >>> var('x,y', real=True)
846 (x, y)
847 >>> x.is_real and y.is_real # noqa: F821
848 True
850 See :func:`symbols` documentation for more details on what kinds of
851 arguments can be passed to :func:`var`.
853 """
854 def traverse(symbols, frame):
855 """Recursively inject symbols to the global namespace. """
856 for symbol in symbols:
857 if isinstance(symbol, Basic):
858 frame.f_globals[symbol.name] = symbol
859 elif isinstance(symbol, FunctionClass):
860 frame.f_globals[symbol.__name__] = symbol
861 else:
862 traverse(symbol, frame)
864 from inspect import currentframe
865 frame = currentframe().f_back
867 try:
868 syms = symbols(names, **args)
870 if syms is not None:
871 if isinstance(syms, Basic):
872 frame.f_globals[syms.name] = syms
873 elif isinstance(syms, FunctionClass):
874 frame.f_globals[syms.__name__] = syms
875 else:
876 traverse(syms, frame)
877 finally:
878 del frame # break cyclic dependencies as stated in inspect docs
880 return syms
882def disambiguate(*iter):
883 """
884 Return a Tuple containing the passed expressions with symbols
885 that appear the same when printed replaced with numerically
886 subscripted symbols, and all Dummy symbols replaced with Symbols.
888 Parameters
889 ==========
891 iter: list of symbols or expressions.
893 Examples
894 ========
896 >>> from sympy.core.symbol import disambiguate
897 >>> from sympy import Dummy, Symbol, Tuple
898 >>> from sympy.abc import y
900 >>> tup = Symbol('_x'), Dummy('x'), Dummy('x')
901 >>> disambiguate(*tup)
902 (x_2, x, x_1)
904 >>> eqs = Tuple(Symbol('x')/y, Dummy('x')/y)
905 >>> disambiguate(*eqs)
906 (x_1/y, x/y)
908 >>> ix = Symbol('x', integer=True)
909 >>> vx = Symbol('x')
910 >>> disambiguate(vx + ix)
911 (x + x_1,)
913 To make your own mapping of symbols to use, pass only the free symbols
914 of the expressions and create a dictionary:
916 >>> free = eqs.free_symbols
917 >>> mapping = dict(zip(free, disambiguate(*free)))
918 >>> eqs.xreplace(mapping)
919 (x_1/y, x/y)
921 """
922 new_iter = Tuple(*iter)
923 key = lambda x:tuple(sorted(x.assumptions0.items()))
924 syms = ordered(new_iter.free_symbols, keys=key)
925 mapping = {}
926 for s in syms:
927 mapping.setdefault(str(s).lstrip('_'), []).append(s)
928 reps = {}
929 for k in mapping:
930 # the first or only symbol doesn't get subscripted but make
931 # sure that it's a Symbol, not a Dummy
932 mapk0 = Symbol("%s" % (k), **mapping[k][0].assumptions0)
933 if mapping[k][0] != mapk0:
934 reps[mapping[k][0]] = mapk0
935 # the others get subscripts (and are made into Symbols)
936 skip = 0
937 for i in range(1, len(mapping[k])):
938 while True:
939 name = "%s_%i" % (k, i + skip)
940 if name not in mapping:
941 break
942 skip += 1
943 ki = mapping[k][i]
944 reps[ki] = Symbol(name, **ki.assumptions0)
945 return new_iter.xreplace(reps)