123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945 |
- from __future__ import annotations
- from .assumptions import StdFactKB, _assume_defined
- from .basic import Basic, Atom
- from .cache import cacheit
- from .containers import Tuple
- from .expr import Expr, AtomicExpr
- from .function import AppliedUndef, FunctionClass
- from .kind import NumberKind, UndefinedKind
- from .logic import fuzzy_bool
- from .singleton import S
- from .sorting import ordered
- from .sympify import sympify
- from sympy.logic.boolalg import Boolean
- from sympy.utilities.iterables import sift, is_sequence
- from sympy.utilities.misc import filldedent
- import string
- import re as _re
- import random
- from itertools import product
- from typing import Any
- class Str(Atom):
- """
- Represents string in SymPy.
- Explanation
- ===========
- Previously, ``Symbol`` was used where string is needed in ``args`` of SymPy
- objects, e.g. denoting the name of the instance. However, since ``Symbol``
- represents mathematical scalar, this class should be used instead.
- """
- __slots__ = ('name',)
- def __new__(cls, name, **kwargs):
- if not isinstance(name, str):
- raise TypeError("name should be a string, not %s" % repr(type(name)))
- obj = Expr.__new__(cls, **kwargs)
- obj.name = name
- return obj
- def __getnewargs__(self):
- return (self.name,)
- def _hashable_content(self):
- return (self.name,)
- def _filter_assumptions(kwargs):
- """Split the given dict into assumptions and non-assumptions.
- Keys are taken as assumptions if they correspond to an
- entry in ``_assume_defined``.
- """
- assumptions, nonassumptions = map(dict, sift(kwargs.items(),
- lambda i: i[0] in _assume_defined,
- binary=True))
- Symbol._sanitize(assumptions)
- return assumptions, nonassumptions
- def _symbol(s, matching_symbol=None, **assumptions):
- """Return s if s is a Symbol, else if s is a string, return either
- the matching_symbol if the names are the same or else a new symbol
- with the same assumptions as the matching symbol (or the
- assumptions as provided).
- Examples
- ========
- >>> from sympy import Symbol
- >>> from sympy.core.symbol import _symbol
- >>> _symbol('y')
- y
- >>> _.is_real is None
- True
- >>> _symbol('y', real=True).is_real
- True
- >>> x = Symbol('x')
- >>> _symbol(x, real=True)
- x
- >>> _.is_real is None # ignore attribute if s is a Symbol
- True
- Below, the variable sym has the name 'foo':
- >>> sym = Symbol('foo', real=True)
- Since 'x' is not the same as sym's name, a new symbol is created:
- >>> _symbol('x', sym).name
- 'x'
- It will acquire any assumptions give:
- >>> _symbol('x', sym, real=False).is_real
- False
- Since 'foo' is the same as sym's name, sym is returned
- >>> _symbol('foo', sym)
- foo
- Any assumptions given are ignored:
- >>> _symbol('foo', sym, real=False).is_real
- True
- NB: the symbol here may not be the same as a symbol with the same
- name defined elsewhere as a result of different assumptions.
- See Also
- ========
- sympy.core.symbol.Symbol
- """
- if isinstance(s, str):
- if matching_symbol and matching_symbol.name == s:
- return matching_symbol
- return Symbol(s, **assumptions)
- elif isinstance(s, Symbol):
- return s
- else:
- raise ValueError('symbol must be string for symbol name or Symbol')
- def uniquely_named_symbol(xname, exprs=(), compare=str, modify=None, **assumptions):
- """
- Return a symbol whose name is derivated from *xname* but is unique
- from any other symbols in *exprs*.
- *xname* and symbol names in *exprs* are passed to *compare* to be
- converted to comparable forms. If ``compare(xname)`` is not unique,
- it is recursively passed to *modify* until unique name is acquired.
- Parameters
- ==========
- xname : str or Symbol
- Base name for the new symbol.
- exprs : Expr or iterable of Expr
- Expressions whose symbols are compared to *xname*.
- compare : function
- Unary function which transforms *xname* and symbol names from
- *exprs* to comparable form.
- modify : function
- Unary function which modifies the string. Default is appending
- the number, or increasing the number if exists.
- Examples
- ========
- By default, a number is appended to *xname* to generate unique name.
- If the number already exists, it is recursively increased.
- >>> from sympy.core.symbol import uniquely_named_symbol, Symbol
- >>> uniquely_named_symbol('x', Symbol('x'))
- x0
- >>> uniquely_named_symbol('x', (Symbol('x'), Symbol('x0')))
- x1
- >>> uniquely_named_symbol('x0', (Symbol('x1'), Symbol('x0')))
- x2
- Name generation can be controlled by passing *modify* parameter.
- >>> from sympy.abc import x
- >>> uniquely_named_symbol('x', x, modify=lambda s: 2*s)
- xx
- """
- def numbered_string_incr(s, start=0):
- if not s:
- return str(start)
- i = len(s) - 1
- while i != -1:
- if not s[i].isdigit():
- break
- i -= 1
- n = str(int(s[i + 1:] or start - 1) + 1)
- return s[:i + 1] + n
- default = None
- if is_sequence(xname):
- xname, default = xname
- x = compare(xname)
- if not exprs:
- return _symbol(x, default, **assumptions)
- if not is_sequence(exprs):
- exprs = [exprs]
- names = set().union(
- [i.name for e in exprs for i in e.atoms(Symbol)] +
- [i.func.name for e in exprs for i in e.atoms(AppliedUndef)])
- if modify is None:
- modify = numbered_string_incr
- while any(x == compare(s) for s in names):
- x = modify(x)
- return _symbol(x, default, **assumptions)
- _uniquely_named_symbol = uniquely_named_symbol
- class Symbol(AtomicExpr, Boolean):
- """
- Assumptions:
- commutative = True
- You can override the default assumptions in the constructor.
- Examples
- ========
- >>> from sympy import symbols
- >>> A,B = symbols('A,B', commutative = False)
- >>> bool(A*B != B*A)
- True
- >>> bool(A*B*2 == 2*A*B) == True # multiplication by scalars is commutative
- True
- """
- is_comparable = False
- __slots__ = ('name', '_assumptions_orig', '_assumptions0')
- name: str
- is_Symbol = True
- is_symbol = True
- @property
- def kind(self):
- if self.is_commutative:
- return NumberKind
- return UndefinedKind
- @property
- def _diff_wrt(self):
- """Allow derivatives wrt Symbols.
- Examples
- ========
- >>> from sympy import Symbol
- >>> x = Symbol('x')
- >>> x._diff_wrt
- True
- """
- return True
- @staticmethod
- def _sanitize(assumptions, obj=None):
- """Remove None, convert values to bool, check commutativity *in place*.
- """
- # be strict about commutativity: cannot be None
- is_commutative = fuzzy_bool(assumptions.get('commutative', True))
- if is_commutative is None:
- whose = '%s ' % obj.__name__ if obj else ''
- raise ValueError(
- '%scommutativity must be True or False.' % whose)
- # sanitize other assumptions so 1 -> True and 0 -> False
- for key in list(assumptions.keys()):
- v = assumptions[key]
- if v is None:
- assumptions.pop(key)
- continue
- assumptions[key] = bool(v)
- def _merge(self, assumptions):
- base = self.assumptions0
- for k in set(assumptions) & set(base):
- if assumptions[k] != base[k]:
- raise ValueError(filldedent('''
- non-matching assumptions for %s: existing value
- is %s and new value is %s''' % (
- k, base[k], assumptions[k])))
- base.update(assumptions)
- return base
- def __new__(cls, name, **assumptions):
- """Symbols are identified by name and assumptions::
- >>> from sympy import Symbol
- >>> Symbol("x") == Symbol("x")
- True
- >>> Symbol("x", real=True) == Symbol("x", real=False)
- False
- """
- cls._sanitize(assumptions, cls)
- return Symbol.__xnew_cached_(cls, name, **assumptions)
- @staticmethod
- def __xnew__(cls, name, **assumptions): # never cached (e.g. dummy)
- if not isinstance(name, str):
- raise TypeError("name should be a string, not %s" % repr(type(name)))
- # This is retained purely so that srepr can include commutative=True if
- # that was explicitly specified but not if it was not. Ideally srepr
- # should not distinguish these cases because the symbols otherwise
- # compare equal and are considered equivalent.
- #
- # See https://github.com/sympy/sympy/issues/8873
- #
- assumptions_orig = assumptions.copy()
- # The only assumption that is assumed by default is comutative=True:
- assumptions.setdefault('commutative', True)
- assumptions_kb = StdFactKB(assumptions)
- assumptions0 = dict(assumptions_kb)
- obj = Expr.__new__(cls)
- obj.name = name
- obj._assumptions = assumptions_kb
- obj._assumptions_orig = assumptions_orig
- obj._assumptions0 = assumptions0
- # The three assumptions dicts are all a little different:
- #
- # >>> from sympy import Symbol
- # >>> x = Symbol('x', finite=True)
- # >>> x.is_positive # query an assumption
- # >>> x._assumptions
- # {'finite': True, 'infinite': False, 'commutative': True, 'positive': None}
- # >>> x._assumptions0
- # {'finite': True, 'infinite': False, 'commutative': True}
- # >>> x._assumptions_orig
- # {'finite': True}
- #
- # Two symbols with the same name are equal if their _assumptions0 are
- # the same. Arguably it should be _assumptions_orig that is being
- # compared because that is more transparent to the user (it is
- # what was passed to the constructor modulo changes made by _sanitize).
- return obj
- @staticmethod
- @cacheit
- def __xnew_cached_(cls, name, **assumptions): # symbols are always cached
- return Symbol.__xnew__(cls, name, **assumptions)
- def __getnewargs_ex__(self):
- return ((self.name,), self._assumptions_orig)
- # NOTE: __setstate__ is not needed for pickles created by __getnewargs_ex__
- # but was used before Symbol was changed to use __getnewargs_ex__ in v1.9.
- # Pickles created in previous SymPy versions will still need __setstate__
- # so that they can be unpickled in SymPy > v1.9.
- def __setstate__(self, state):
- for name, value in state.items():
- setattr(self, name, value)
- def _hashable_content(self):
- # Note: user-specified assumptions not hashed, just derived ones
- return (self.name,) + tuple(sorted(self.assumptions0.items()))
- def _eval_subs(self, old, new):
- if old.is_Pow:
- from sympy.core.power import Pow
- return Pow(self, S.One, evaluate=False)._eval_subs(old, new)
- def _eval_refine(self, assumptions):
- return self
- @property
- def assumptions0(self):
- return self._assumptions0.copy()
- @cacheit
- def sort_key(self, order=None):
- return self.class_key(), (1, (self.name,)), S.One.sort_key(), S.One
- def as_dummy(self):
- # only put commutativity in explicitly if it is False
- return Dummy(self.name) if self.is_commutative is not False \
- else Dummy(self.name, commutative=self.is_commutative)
- def as_real_imag(self, deep=True, **hints):
- if hints.get('ignore') == self:
- return None
- else:
- from sympy.functions.elementary.complexes import im, re
- return (re(self), im(self))
- def is_constant(self, *wrt, **flags):
- if not wrt:
- return False
- return self not in wrt
- @property
- def free_symbols(self):
- return {self}
- binary_symbols = free_symbols # in this case, not always
- def as_set(self):
- return S.UniversalSet
- class Dummy(Symbol):
- """Dummy symbols are each unique, even if they have the same name:
- Examples
- ========
- >>> from sympy import Dummy
- >>> Dummy("x") == Dummy("x")
- False
- If a name is not supplied then a string value of an internal count will be
- used. This is useful when a temporary variable is needed and the name
- of the variable used in the expression is not important.
- >>> Dummy() #doctest: +SKIP
- _Dummy_10
- """
- # In the rare event that a Dummy object needs to be recreated, both the
- # `name` and `dummy_index` should be passed. This is used by `srepr` for
- # example:
- # >>> d1 = Dummy()
- # >>> d2 = eval(srepr(d1))
- # >>> d2 == d1
- # True
- #
- # If a new session is started between `srepr` and `eval`, there is a very
- # small chance that `d2` will be equal to a previously-created Dummy.
- _count = 0
- _prng = random.Random()
- _base_dummy_index = _prng.randint(10**6, 9*10**6)
- __slots__ = ('dummy_index',)
- is_Dummy = True
- def __new__(cls, name=None, dummy_index=None, **assumptions):
- if dummy_index is not None:
- assert name is not None, "If you specify a dummy_index, you must also provide a name"
- if name is None:
- name = "Dummy_" + str(Dummy._count)
- if dummy_index is None:
- dummy_index = Dummy._base_dummy_index + Dummy._count
- Dummy._count += 1
- cls._sanitize(assumptions, cls)
- obj = Symbol.__xnew__(cls, name, **assumptions)
- obj.dummy_index = dummy_index
- return obj
- def __getnewargs_ex__(self):
- return ((self.name, self.dummy_index), self._assumptions_orig)
- @cacheit
- def sort_key(self, order=None):
- return self.class_key(), (
- 2, (self.name, self.dummy_index)), S.One.sort_key(), S.One
- def _hashable_content(self):
- return Symbol._hashable_content(self) + (self.dummy_index,)
- class Wild(Symbol):
- """
- A Wild symbol matches anything, or anything
- without whatever is explicitly excluded.
- Parameters
- ==========
- name : str
- Name of the Wild instance.
- exclude : iterable, optional
- Instances in ``exclude`` will not be matched.
- properties : iterable of functions, optional
- Functions, each taking an expressions as input
- and returns a ``bool``. All functions in ``properties``
- need to return ``True`` in order for the Wild instance
- to match the expression.
- Examples
- ========
- >>> from sympy import Wild, WildFunction, cos, pi
- >>> from sympy.abc import x, y, z
- >>> a = Wild('a')
- >>> x.match(a)
- {a_: x}
- >>> pi.match(a)
- {a_: pi}
- >>> (3*x**2).match(a*x)
- {a_: 3*x}
- >>> cos(x).match(a)
- {a_: cos(x)}
- >>> b = Wild('b', exclude=[x])
- >>> (3*x**2).match(b*x)
- >>> b.match(a)
- {a_: b_}
- >>> A = WildFunction('A')
- >>> A.match(a)
- {a_: A_}
- Tips
- ====
- When using Wild, be sure to use the exclude
- keyword to make the pattern more precise.
- Without the exclude pattern, you may get matches
- that are technically correct, but not what you
- wanted. For example, using the above without
- exclude:
- >>> from sympy import symbols
- >>> a, b = symbols('a b', cls=Wild)
- >>> (2 + 3*y).match(a*x + b*y)
- {a_: 2/x, b_: 3}
- This is technically correct, because
- (2/x)*x + 3*y == 2 + 3*y, but you probably
- wanted it to not match at all. The issue is that
- you really did not want a and b to include x and y,
- and the exclude parameter lets you specify exactly
- this. With the exclude parameter, the pattern will
- not match.
- >>> a = Wild('a', exclude=[x, y])
- >>> b = Wild('b', exclude=[x, y])
- >>> (2 + 3*y).match(a*x + b*y)
- Exclude also helps remove ambiguity from matches.
- >>> E = 2*x**3*y*z
- >>> a, b = symbols('a b', cls=Wild)
- >>> E.match(a*b)
- {a_: 2*y*z, b_: x**3}
- >>> a = Wild('a', exclude=[x, y])
- >>> E.match(a*b)
- {a_: z, b_: 2*x**3*y}
- >>> a = Wild('a', exclude=[x, y, z])
- >>> E.match(a*b)
- {a_: 2, b_: x**3*y*z}
- Wild also accepts a ``properties`` parameter:
- >>> a = Wild('a', properties=[lambda k: k.is_Integer])
- >>> E.match(a*b)
- {a_: 2, b_: x**3*y*z}
- """
- is_Wild = True
- __slots__ = ('exclude', 'properties')
- def __new__(cls, name, exclude=(), properties=(), **assumptions):
- exclude = tuple([sympify(x) for x in exclude])
- properties = tuple(properties)
- cls._sanitize(assumptions, cls)
- return Wild.__xnew__(cls, name, exclude, properties, **assumptions)
- def __getnewargs__(self):
- return (self.name, self.exclude, self.properties)
- @staticmethod
- @cacheit
- def __xnew__(cls, name, exclude, properties, **assumptions):
- obj = Symbol.__xnew__(cls, name, **assumptions)
- obj.exclude = exclude
- obj.properties = properties
- return obj
- def _hashable_content(self):
- return super()._hashable_content() + (self.exclude, self.properties)
- # TODO add check against another Wild
- def matches(self, expr, repl_dict=None, old=False):
- if any(expr.has(x) for x in self.exclude):
- return None
- if not all(f(expr) for f in self.properties):
- return None
- if repl_dict is None:
- repl_dict = {}
- else:
- repl_dict = repl_dict.copy()
- repl_dict[self] = expr
- return repl_dict
- _range = _re.compile('([0-9]*:[0-9]+|[a-zA-Z]?:[a-zA-Z])')
- def symbols(names, *, cls=Symbol, **args) -> Any:
- r"""
- Transform strings into instances of :class:`Symbol` class.
- :func:`symbols` function returns a sequence of symbols with names taken
- from ``names`` argument, which can be a comma or whitespace delimited
- string, or a sequence of strings::
- >>> from sympy import symbols, Function
- >>> x, y, z = symbols('x,y,z')
- >>> a, b, c = symbols('a b c')
- The type of output is dependent on the properties of input arguments::
- >>> symbols('x')
- x
- >>> symbols('x,')
- (x,)
- >>> symbols('x,y')
- (x, y)
- >>> symbols(('a', 'b', 'c'))
- (a, b, c)
- >>> symbols(['a', 'b', 'c'])
- [a, b, c]
- >>> symbols({'a', 'b', 'c'})
- {a, b, c}
- If an iterable container is needed for a single symbol, set the ``seq``
- argument to ``True`` or terminate the symbol name with a comma::
- >>> symbols('x', seq=True)
- (x,)
- To reduce typing, range syntax is supported to create indexed symbols.
- Ranges are indicated by a colon and the type of range is determined by
- the character to the right of the colon. If the character is a digit
- then all contiguous digits to the left are taken as the nonnegative
- starting value (or 0 if there is no digit left of the colon) and all
- contiguous digits to the right are taken as 1 greater than the ending
- value::
- >>> symbols('x:10')
- (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9)
- >>> symbols('x5:10')
- (x5, x6, x7, x8, x9)
- >>> symbols('x5(:2)')
- (x50, x51)
- >>> symbols('x5:10,y:5')
- (x5, x6, x7, x8, x9, y0, y1, y2, y3, y4)
- >>> symbols(('x5:10', 'y:5'))
- ((x5, x6, x7, x8, x9), (y0, y1, y2, y3, y4))
- If the character to the right of the colon is a letter, then the single
- letter to the left (or 'a' if there is none) is taken as the start
- and all characters in the lexicographic range *through* the letter to
- the right are used as the range::
- >>> symbols('x:z')
- (x, y, z)
- >>> symbols('x:c') # null range
- ()
- >>> symbols('x(:c)')
- (xa, xb, xc)
- >>> symbols(':c')
- (a, b, c)
- >>> symbols('a:d, x:z')
- (a, b, c, d, x, y, z)
- >>> symbols(('a:d', 'x:z'))
- ((a, b, c, d), (x, y, z))
- Multiple ranges are supported; contiguous numerical ranges should be
- separated by parentheses to disambiguate the ending number of one
- range from the starting number of the next::
- >>> symbols('x:2(1:3)')
- (x01, x02, x11, x12)
- >>> symbols(':3:2') # parsing is from left to right
- (00, 01, 10, 11, 20, 21)
- Only one pair of parentheses surrounding ranges are removed, so to
- include parentheses around ranges, double them. And to include spaces,
- commas, or colons, escape them with a backslash::
- >>> symbols('x((a:b))')
- (x(a), x(b))
- >>> symbols(r'x(:1\,:2)') # or r'x((:1)\,(:2))'
- (x(0,0), x(0,1))
- All newly created symbols have assumptions set according to ``args``::
- >>> a = symbols('a', integer=True)
- >>> a.is_integer
- True
- >>> x, y, z = symbols('x,y,z', real=True)
- >>> x.is_real and y.is_real and z.is_real
- True
- Despite its name, :func:`symbols` can create symbol-like objects like
- instances of Function or Wild classes. To achieve this, set ``cls``
- keyword argument to the desired type::
- >>> symbols('f,g,h', cls=Function)
- (f, g, h)
- >>> type(_[0])
- <class 'sympy.core.function.UndefinedFunction'>
- """
- result = []
- if isinstance(names, str):
- marker = 0
- splitters = r'\,', r'\:', r'\ '
- literals: list[tuple[str, str]] = []
- for splitter in splitters:
- if splitter in names:
- while chr(marker) in names:
- marker += 1
- lit_char = chr(marker)
- marker += 1
- names = names.replace(splitter, lit_char)
- literals.append((lit_char, splitter[1:]))
- def literal(s):
- if literals:
- for c, l in literals:
- s = s.replace(c, l)
- return s
- names = names.strip()
- as_seq = names.endswith(',')
- if as_seq:
- names = names[:-1].rstrip()
- if not names:
- raise ValueError('no symbols given')
- # split on commas
- names = [n.strip() for n in names.split(',')]
- if not all(n for n in names):
- raise ValueError('missing symbol between commas')
- # split on spaces
- for i in range(len(names) - 1, -1, -1):
- names[i: i + 1] = names[i].split()
- seq = args.pop('seq', as_seq)
- for name in names:
- if not name:
- raise ValueError('missing symbol')
- if ':' not in name:
- symbol = cls(literal(name), **args)
- result.append(symbol)
- continue
- split: list[str] = _range.split(name)
- split_list: list[list[str]] = []
- # remove 1 layer of bounding parentheses around ranges
- for i in range(len(split) - 1):
- if i and ':' in split[i] and split[i] != ':' and \
- split[i - 1].endswith('(') and \
- split[i + 1].startswith(')'):
- split[i - 1] = split[i - 1][:-1]
- split[i + 1] = split[i + 1][1:]
- for s in split:
- if ':' in s:
- if s.endswith(':'):
- raise ValueError('missing end range')
- a, b = s.split(':')
- if b[-1] in string.digits:
- a_i = 0 if not a else int(a)
- b_i = int(b)
- split_list.append([str(c) for c in range(a_i, b_i)])
- else:
- a = a or 'a'
- split_list.append([string.ascii_letters[c] for c in range(
- string.ascii_letters.index(a),
- string.ascii_letters.index(b) + 1)]) # inclusive
- if not split_list[-1]:
- break
- else:
- split_list.append([s])
- else:
- seq = True
- if len(split_list) == 1:
- names = split_list[0]
- else:
- names = [''.join(s) for s in product(*split_list)]
- if literals:
- result.extend([cls(literal(s), **args) for s in names])
- else:
- result.extend([cls(s, **args) for s in names])
- if not seq and len(result) <= 1:
- if not result:
- return ()
- return result[0]
- return tuple(result)
- else:
- for name in names:
- result.append(symbols(name, cls=cls, **args))
- return type(names)(result)
- def var(names, **args):
- """
- Create symbols and inject them into the global namespace.
- Explanation
- ===========
- This calls :func:`symbols` with the same arguments and puts the results
- into the *global* namespace. It's recommended not to use :func:`var` in
- library code, where :func:`symbols` has to be used::
- Examples
- ========
- >>> from sympy import var
- >>> var('x')
- x
- >>> x # noqa: F821
- x
- >>> var('a,ab,abc')
- (a, ab, abc)
- >>> abc # noqa: F821
- abc
- >>> var('x,y', real=True)
- (x, y)
- >>> x.is_real and y.is_real # noqa: F821
- True
- See :func:`symbols` documentation for more details on what kinds of
- arguments can be passed to :func:`var`.
- """
- def traverse(symbols, frame):
- """Recursively inject symbols to the global namespace. """
- for symbol in symbols:
- if isinstance(symbol, Basic):
- frame.f_globals[symbol.name] = symbol
- elif isinstance(symbol, FunctionClass):
- frame.f_globals[symbol.__name__] = symbol
- else:
- traverse(symbol, frame)
- from inspect import currentframe
- frame = currentframe().f_back
- try:
- syms = symbols(names, **args)
- if syms is not None:
- if isinstance(syms, Basic):
- frame.f_globals[syms.name] = syms
- elif isinstance(syms, FunctionClass):
- frame.f_globals[syms.__name__] = syms
- else:
- traverse(syms, frame)
- finally:
- del frame # break cyclic dependencies as stated in inspect docs
- return syms
- def disambiguate(*iter):
- """
- Return a Tuple containing the passed expressions with symbols
- that appear the same when printed replaced with numerically
- subscripted symbols, and all Dummy symbols replaced with Symbols.
- Parameters
- ==========
- iter: list of symbols or expressions.
- Examples
- ========
- >>> from sympy.core.symbol import disambiguate
- >>> from sympy import Dummy, Symbol, Tuple
- >>> from sympy.abc import y
- >>> tup = Symbol('_x'), Dummy('x'), Dummy('x')
- >>> disambiguate(*tup)
- (x_2, x, x_1)
- >>> eqs = Tuple(Symbol('x')/y, Dummy('x')/y)
- >>> disambiguate(*eqs)
- (x_1/y, x/y)
- >>> ix = Symbol('x', integer=True)
- >>> vx = Symbol('x')
- >>> disambiguate(vx + ix)
- (x + x_1,)
- To make your own mapping of symbols to use, pass only the free symbols
- of the expressions and create a dictionary:
- >>> free = eqs.free_symbols
- >>> mapping = dict(zip(free, disambiguate(*free)))
- >>> eqs.xreplace(mapping)
- (x_1/y, x/y)
- """
- new_iter = Tuple(*iter)
- key = lambda x:tuple(sorted(x.assumptions0.items()))
- syms = ordered(new_iter.free_symbols, keys=key)
- mapping = {}
- for s in syms:
- mapping.setdefault(str(s).lstrip('_'), []).append(s)
- reps = {}
- for k in mapping:
- # the first or only symbol doesn't get subscripted but make
- # sure that it's a Symbol, not a Dummy
- mapk0 = Symbol("%s" % (k), **mapping[k][0].assumptions0)
- if mapping[k][0] != mapk0:
- reps[mapping[k][0]] = mapk0
- # the others get subscripts (and are made into Symbols)
- skip = 0
- for i in range(1, len(mapping[k])):
- while True:
- name = "%s_%i" % (k, i + skip)
- if name not in mapping:
- break
- skip += 1
- ki = mapping[k][i]
- reps[ki] = Symbol(name, **ki.assumptions0)
- return new_iter.xreplace(reps)
|