Skip to content

Commit

Permalink
Merge pull request #1 from sympy/master
Browse files Browse the repository at this point in the history
Sympy updates
  • Loading branch information
Smit-create committed Dec 30, 2019
2 parents 378cb9c + 1ad8d16 commit 8db290e
Show file tree
Hide file tree
Showing 58 changed files with 1,772 additions and 476 deletions.
148 changes: 98 additions & 50 deletions doc/src/guide.rst
Expand Up @@ -34,7 +34,7 @@ why I call it that way)::

e = x + y + x

print e
print(e)

And I try if it works

Expand Down Expand Up @@ -147,10 +147,12 @@ and ``__rdiv__`` is replaced by ``__rtruediv__``.)

So, you can write normal expressions using python arithmetics like this::

a = Symbol("a")
b = Symbol("b")
e = (a + b)**2
print e
>>> from sympy import Symbol
>>> a = Symbol("a")
>>> b = Symbol("b")
>>> e = (a + b)**2
>>> e
(a + b)**2

but from the SymPy point of view, we just need the classes ``Add``, ``Mul``,
``Pow``, ``Rational``, ``Integer``.
Expand Down Expand Up @@ -238,8 +240,11 @@ need.

You can define the ``cos`` class like this::

class cos(Function):
pass
>>> from sympy import Function
>>> class cos(Function):
... pass
>>> 1 + cos(x)
cos(x) + 1

and use it like ``1 + cos(x)``, but if you don't implement the ``fdiff()`` method,
you will not be able to call ``(1 + cos(x)).series()``.
Expand Down Expand Up @@ -279,52 +284,87 @@ Functions

How to create a new function with one variable::

class sign(Function):

nargs = 1
>>> from sympy import Mul, S
>>> class sign(Function):
... nargs = 1
...
... @classmethod
... def eval(cls, arg):
... if arg is S.NaN:
... return S.NaN
... if arg is S.Zero:
... return S.Zero
... if arg.is_extended_positive:
... return S.One
... if arg.is_extended_negative:
... return S.NegativeOne
... if isinstance(arg, Mul):
... coeff, terms = arg.as_coeff_mul()
... if coeff is not S.One:
... return cls(coeff) * cls(Mul(*terms))
...
... def _eval_is_finite(self):
... return True
...
... def _eval_conjugate(self):
... return self

@classmethod
def eval(cls, arg):
if isinstance(arg, Basic.NaN):
return S.NaN
if isinstance(arg, Basic.Zero):
return S.Zero
if arg.is_positive:
return S.One
if arg.is_negative:
return S.NegativeOne
if isinstance(arg, Basic.Mul):
coeff, terms = arg.as_coeff_mul()
if not isinstance(coeff, Basic.One):
return cls(coeff) * cls(Basic.Mul(*terms))
The applied function ``sign(x)`` is constructed using::

is_finite = True
>>> from sympy.abc import x
>>> sign(x)
sign(x)

def _eval_conjugate(self):
return self
both inside and outside of SymPy. Unapplied functions ``sign`` is just the class
itself::

def _eval_is_zero(self):
return isinstance(self[0], Basic.Zero)
>>> sign
sign

and that's it. The ``_eval_*`` functions are called when something is needed.
The ``eval`` is called when the class is about to be instantiated and it
should return either some simplified instance of some other class or if the
class should be unmodified, return ``None`` (see ``core/function.py`` in
``Function.__new__`` for implementation details). See also tests in
`sympy/functions/elementary/tests/test_interface.py
<https://github.com/sympy/sympy/blob/master/sympy/functions/elementary/tests/test_interface.py>`_ that test this interface. You can use them to create your own new functions.
should return either an another SymPy object or ``None``.
If it returns ``None``, it becomes an unevaluated function.
(see ``core/function.py`` in ``Function.__new__`` for implementation details)

Here are some examples that how ``eval`` works::

The applied function ``sign(x)`` is constructed using
::
>>> sign(S.NaN)
nan
>>> sign(S.Zero)
0

>>> x = Symbol('x')
>>> sign(2*x)
sign(x)

both inside and outside of SymPy. Unapplied functions ``sign`` is just the class
itself::
>>> x = Symbol('x', positive=True)
>>> sign(x)
1

sign
>>> x = Symbol('x', negative=True)
>>> sign(x)
-1

The ``_eval_*`` functions are automatically called when something is
needed.
For example, ``conjugate`` or ``is_finite`` will automatically call
``_eval_is_finite``, ``_eval_conjugate`` if you have these defined::

>>> sign(x).is_finite
True

>>> from sympy.functions import conjugate
>>> x = Symbol('x')
>>> conjugate(sign(x))
sign(x)

If you want to check out some other examples of implementing custom
SymPy functions, see the tests in
`sympy/functions/elementary/tests/test_interface.py
<https://github.com/sympy/sympy/blob/master/sympy/functions/elementary/tests/test_interface.py>`_
that test this interface. You can use them to create your own new functions.

both inside and outside of SymPy. This is the current structure of classes in
Both inside and outside of SymPy. This is the current structure of classes in
SymPy::

class BasicType(type):
Expand All @@ -347,15 +387,23 @@ can be changed in the future.

This is how to create a function with two variables::

class chebyshevt_root(Function):
nargs = 2

@classmethod
def eval(cls, n, k):
if not 0 <= k < n:
raise ValueError("must have 0 <= k < n")
return cos(S.Pi*(2*k + 1)/(2*n))

>>> class chebyshevt_root(Function):
... nargs = 2
...
... @classmethod
... def eval(cls, n, k):
... if not (0 <= k) & (k < n):
... raise ValueError("must have 0 <= k < n")
... return cos(S.Pi*(2*k + 1)/(2*n))

>>> from sympy import symbols
>>> n, k = symbols('n k')
>>> chebyshevt_root(n, k)
cos(pi*(2*k + 1)/(2*n))
>>> chebyshevt_root(2, 3)
Traceback (most recent call last):
...
ValueError: must have 0 <= k < n

.. note:: the first argument of a @classmethod should be ``cls`` (i.e. not
``self``).
Expand Down
4 changes: 4 additions & 0 deletions doc/src/modules/ntheory.rst
Expand Up @@ -56,8 +56,12 @@ Ntheory Functions Reference

.. autofunction:: divisors

.. autofunction:: proper_divisors

.. autofunction:: divisor_count

.. autofunction:: proper_divisor_count

.. autofunction:: udivisors

.. autofunction:: udivisor_count
Expand Down
2 changes: 1 addition & 1 deletion doc/src/modules/polys/basics.rst
Expand Up @@ -69,7 +69,7 @@ keyword parameter. By default, it is determined by the coefficients
of the polynomial arguments.

Polynomial expressions can be transformed into polynomials by the
method :obj:`sympy.core.basic.Basic.as_poly`::
method :obj:`sympy.core.expr.Expr.as_poly`::

>>> e = (x + y)*(y - 2*z)
>>> e.as_poly()
Expand Down
75 changes: 75 additions & 0 deletions doc/src/tutorial/matrices.rst
Expand Up @@ -412,6 +412,81 @@ expensive to calculate.
Possible Issues
===============

Controlling matrix expression blowup
------------------------------------

Normally a matrix ``*`` multiplication or ``**`` power will not simplify
intermediate terms as they are calcualted to give the best general performance.
Unfortunately this can lead to an explosion in the size and complexity of matrix
element symbolic expressions (including complex number expressions), and even
impair the ability of the calculation to finish, though in the majority of small
matrices it is much faster. This can be a problem however for larger matrices
and can even cause multiplication to never end or may be impossible to simplify
afterwards.

For this reason two separate functions - ``multiply`` and ``pow`` for
multiplication and exponentiation have been added with a flag which allows you
to specify that an optimized intermediate simplification step is to be performed
at each step of calculation. The matrix ``exp`` function has also been modified
to take this flag. By default these functions have the flag ``dotprodsimp`` set
to None which means that intermediate simplification is not done, set this to
``True`` to use the simplification. This will leave the matrix in a relatively
simplified state after the operation and permit calculations on some large
matrices which were not possible or extremely slow previously.

This flag has also been added to the majority of matrix functions to enable
this intermedate simplification in order to speed up calculation in many
instances and return a simplified result. NOTE: It does not work in all cases,
but so far testing has shown that it helps with the majority.

>>> x = Symbol('x')
>>> M = Matrix([[1+x, 1-x], [1-x, 1+x]])
>>> M*M
⎡ 2 2 ⎤
⎢(1 - x) + (x + 1) 2⋅(1 - x)⋅(x + 1) ⎥
⎢ ⎥
⎢ 2 2⎥
⎣ 2⋅(1 - x)⋅(x + 1) (1 - x) + (x + 1) ⎦
>>> M.multiply(M, dotprodsimp=True)
⎡ 2 2⎤
⎢2⋅x + 2 2 - 2⋅x ⎥
⎢ ⎥
⎢ 2 2 ⎥
⎣2 - 2⋅x 2⋅x + 2⎦
>>> M**2
⎡ 2 2 ⎤
⎢(1 - x) + (x + 1) 2⋅(1 - x)⋅(x + 1) ⎥
⎢ ⎥
⎢ 2 2⎥
⎣ 2⋅(1 - x)⋅(x + 1) (1 - x) + (x + 1) ⎦
>>> M.pow(8, dotprodsimp=True)
⎡ 8 8⎤
⎢128⋅x + 128 128 - 128⋅x ⎥
⎢ ⎥
⎢ 8 8 ⎥
⎣128 - 128⋅x 128⋅x + 128⎦
>>> M.exp()
⎡ ⎛ 1 - x ⎞ 2 ⎤
⎢ 2 2⋅x (1 - x)⋅⎜───────── + 1⎟⋅ℯ 2⋅x⎥
⎢ (1 - x)⋅ℯ ℯ ⎝2⋅(x - 1) ⎠ (1 - x)⋅ℯ ⎥
⎢- ────────── + ──── - ────────────────────────── + ────────────⎥
⎢ 2⋅(x - 1) 2 x - 1 2⋅(x - 1) ⎥
⎢ ⎥
⎢ 2⋅x 2 2⋅x ⎥
⎢ ℯ ℯ (1 - x)⋅ℯ ⎛ 1 - x ⎞ 2 ⎥
⎢ - ──── + ── - ──────────── + ⎜───────── + 1⎟⋅ℯ ⎥
⎣ 2 2 2⋅(x - 1) ⎝2⋅(x - 1) ⎠ ⎦
>>> M.exp(dotprodsimp=True)
⎡ 2⋅x 2 2⋅x 2⎤
⎢ ℯ ℯ ℯ ℯ ⎥
⎢ ──── + ── - ──── + ──⎥
⎢ 2 2 2 2 ⎥
⎢ ⎥
⎢ 2⋅x 2 2⋅x 2 ⎥
⎢ ℯ ℯ ℯ ℯ ⎥
⎢- ──── + ── ──── + ── ⎥
⎣ 2 2 2 2 ⎦

Zero Testing
------------

Expand Down
29 changes: 0 additions & 29 deletions sympy/core/basic.py
Expand Up @@ -765,35 +765,6 @@ def _sorted_args(self):
"""
return self.args


def as_poly(self, *gens, **args):
"""Converts ``self`` to a polynomial or returns ``None``.
>>> from sympy import sin
>>> from sympy.abc import x, y
>>> print((x**2 + x*y).as_poly())
Poly(x**2 + x*y, x, y, domain='ZZ')
>>> print((x**2 + x*y).as_poly(x, y))
Poly(x**2 + x*y, x, y, domain='ZZ')
>>> print((x**2 + sin(y)).as_poly(x, y))
None
"""
from sympy.polys import Poly, PolynomialError

try:
poly = Poly(self, *gens, **args)

if not poly.is_Poly:
return None
else:
return poly
except PolynomialError:
return None

def as_content_primitive(self, radical=False, clear=True):
"""A stub to allow Basic args (like Tuple) to be skipped when computing
the content and primitive components of an expression.
Expand Down
28 changes: 28 additions & 0 deletions sympy/core/expr.py
Expand Up @@ -1076,6 +1076,34 @@ def as_ordered_factors(self, order=None):
"""Return list of ordered factors (if Mul) else [self]."""
return [self]

def as_poly(self, *gens, **args):
"""Converts ``self`` to a polynomial or returns ``None``.
>>> from sympy import sin
>>> from sympy.abc import x, y
>>> print((x**2 + x*y).as_poly())
Poly(x**2 + x*y, x, y, domain='ZZ')
>>> print((x**2 + x*y).as_poly(x, y))
Poly(x**2 + x*y, x, y, domain='ZZ')
>>> print((x**2 + sin(y)).as_poly(x, y))
None
"""
from sympy.polys import Poly, PolynomialError

try:
poly = Poly(self, *gens, **args)

if not poly.is_Poly:
return None
else:
return poly
except PolynomialError:
return None

def as_ordered_terms(self, order=None, data=False):
"""
Transform an expression to an ordered list of terms.
Expand Down

0 comments on commit 8db290e

Please sign in to comment.