# Source code for sympy.core.symbol

from __future__ import print_function, division

from sympy.core.assumptions import StdFactKB
from sympy.core.compatibility import string_types
from .basic import Basic
from .core import C
from .sympify import sympify
from .singleton import S
from .expr import Expr, AtomicExpr
from .cache import cacheit
from .function import FunctionClass
from sympy.core.logic import fuzzy_bool
from sympy.logic.boolalg import Boolean
from sympy.utilities.iterables import cartes

import string
import re as _re

[docs]class Symbol(AtomicExpr, Boolean):
"""
Assumptions:
commutative = True

You can override the default assumptions in the constructor:

>>> 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']

is_Symbol = True

@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, covert values to bool, check commutativity *in place*.
"""

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)
assumptions['commutative'] = is_commutative

# sanitize other assumptions so 1 -> True and 0 -> False
for key in list(assumptions.keys()):
from collections import defaultdict
from sympy.utilities.exceptions import SymPyDeprecationWarning
keymap = defaultdict(lambda: None)
keymap.update({'bounded': 'finite', 'unbounded': 'infinite', 'infinitesimal': 'zero'})
if keymap[key]:
SymPyDeprecationWarning(
feature="%s assumption" % key,
issue=8071,
deprecated_since_version="0.7.6").warn()
assumptions[keymap[key]] = assumptions[key]
assumptions.pop(key)
key = keymap[key]

v = assumptions[key]
if v is None:
assumptions.pop(key)
continue
assumptions[key] = bool(v)

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)

def __new_stage2__(cls, name, **assumptions):
if not isinstance(name, string_types):
raise TypeError("name should be a string, not %s" % repr(type(name)))

obj = Expr.__new__(cls)
obj.name = name
obj._assumptions = StdFactKB(assumptions)
return obj

__xnew__ = staticmethod(
__new_stage2__)            # never cached (e.g. dummy)
__xnew_cached_ = staticmethod(
cacheit(__new_stage2__))   # symbols are always cached

def __getnewargs__(self):
return (self.name,)

def __getstate__(self):
return {'_assumptions': self._assumptions}

def _hashable_content(self):
return (self.name,) + tuple(sorted(self.assumptions0.items()))

@property
def assumptions0(self):
return dict((key, value) for key, value
in self._assumptions.items() if value is not None)

@cacheit
def sort_key(self, order=None):
return self.class_key(), (1, (str(self),)), S.One.sort_key(), S.One

[docs]    def as_dummy(self):
"""Return a Dummy having the same name and same assumptions as self."""
return Dummy(self.name, **self.assumptions0)

def __call__(self, *args):
from .function import Function
return Function(self.name)(*args)

def as_real_imag(self, deep=True, **hints):
if hints.get('ignore') == self:
return None
else:
return (C.re(self), C.im(self))

def _sage_(self):
import sage.all as sage
return sage.var(self.name)

def is_constant(self, *wrt, **flags):
if not wrt:
return False
return not self in wrt

@property
def free_symbols(self):
return set([self])

[docs]class Dummy(Symbol):
"""Dummy symbols are each unique, identified by an internal count index:

>>> from sympy import Dummy
>>> bool(Dummy("x") == Dummy("x")) == True
False

If a name is not supplied then a string value of the count index 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

"""

_count = 0

__slots__ = ['dummy_index']

is_Dummy = True

def __new__(cls, name=None, **assumptions):
if name is None:
name = "Dummy_" + str(Dummy._count)

cls._sanitize(assumptions, cls)
obj = Symbol.__xnew__(cls, name, **assumptions)

Dummy._count += 1
obj.dummy_index = Dummy._count
return obj

def __getstate__(self):
return {'_assumptions': self._assumptions, 'dummy_index': self.dummy_index}

@cacheit
def sort_key(self, order=None):
return self.class_key(), (
2, (str(self), self.dummy_index)), S.One.sort_key(), S.One

def _hashable_content(self):
return Symbol._hashable_content(self) + (self.dummy_index,)

[docs]class Wild(Symbol):
"""
A Wild symbol matches anything, or anything
without whatever is explicitly excluded.

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 didn't 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}

"""
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(Wild, self)._hashable_content() + (self.exclude, self.properties)

# TODO add check against another Wild
def matches(self, expr, repl_dict={}, old=False):
if any(expr.has(x) for x in self.exclude):
return None
if any(not f(expr) for f in self.properties):
return None
repl_dict = repl_dict.copy()
repl_dict[self] = expr
return repl_dict

def __call__(self, *args, **kwargs):
raise TypeError("'%s' object is not callable" % type(self).__name__)

_range = _re.compile('([0-9]*:[0-9]+|[a-zA-Z]?:[a-zA-Z])')

[docs]def symbols(names, **args):
"""
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(set(['a', 'b', 'c']))
set([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('x(:1\,:2)')  # or '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, string_types):
marker = 0
literals = ['\,', '\:', '\ ']
for i in range(len(literals)):
lit = literals.pop(0)
if lit in names:
while chr(marker) in names:
marker += 1
lit_char = chr(marker)
marker += 1
names = names.replace(lit, lit_char)
literals.append((lit_char, lit[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()

cls = args.pop('cls', Symbol)
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 = _range.split(name)
# 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 i, s in enumerate(split):
if ':' in s:
if s[-1].endswith(':'):
raise ValueError('missing end range')
a, b = s.split(':')
if b[-1] in string.digits:
a = 0 if not a else int(a)
b = int(b)
split[i] = [str(c) for c in range(a, b)]
else:
a = a or 'a'
split[i] = [string.ascii_letters[c] for c in range(
string.ascii_letters.index(a),
string.ascii_letters.index(b) + 1)]  # inclusive
if not split[i]:
break
else:
split[i] = [s]
else:
seq = True
if len(split) == 1:
names = split[0]
else:
names = [''.join(s) for s in cartes(*split)]
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, **args))

return type(names)(result)

[docs]def var(names, **args):
"""
Create symbols and inject them into the global namespace.

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
x

>>> var('a,ab,abc')
(a, ab, abc)
>>> abc
abc

>>> var('x,y', real=True)
(x, y)
>>> x.is_real and y.is_real
True

See :func:symbol 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