Thursday, September 26, 2019
This is the standard import we will use in the first portion of the class. It imports the core SymPy tools and the special tools from the mechanics package. The init_printing()
function is optional but it will increase the number of outputs that print as symbolic math.
import sympy as sm
import sympy.physics.mechanics as me
sm.init_printing()
This creates three symbols and shows how to create expressions with them.
a, b, c = sm.symbols('a, b, c')
a + b**2
expr = a + b**2
expr * expr
SymPy will not simplify or expand expressions by default. You have to use various functions to do so. This shouws how to expand the expression we created above using the expand
function.
new_expr = expr*expr
new_expr
sm.expand(new_expr)
If you know the name of the function you want to use you can open it's help documentation by appending the ?
symbol. This only works in the notebook or IPython terminal, not the basic Python command prompt.
sm.expand?
Signature: sm.expand(e, deep=True, modulus=None, power_base=True, power_exp=True, mul=True, log=True, multinomial=True, basic=True, **hints) Docstring: Expand an expression using methods given as hints. Hints evaluated unless explicitly set to False are: ``basic``, ``log``, ``multinomial``, ``mul``, ``power_base``, and ``power_exp`` The following hints are supported but not applied unless set to True: ``complex``, ``func``, and ``trig``. In addition, the following meta-hints are supported by some or all of the other hints: ``frac``, ``numer``, ``denom``, ``modulus``, and ``force``. ``deep`` is supported by all hints. Additionally, subclasses of Expr may define their own hints or meta-hints. The ``basic`` hint is used for any special rewriting of an object that should be done automatically (along with the other hints like ``mul``) when expand is called. This is a catch-all hint to handle any sort of expansion that may not be described by the existing hint names. To use this hint an object should override the ``_eval_expand_basic`` method. Objects may also define their own expand methods, which are not run by default. See the API section below. If ``deep`` is set to ``True`` (the default), things like arguments of functions are recursively expanded. Use ``deep=False`` to only expand on the top level. If the ``force`` hint is used, assumptions about variables will be ignored in making the expansion. Hints ===== These hints are run by default mul --- Distributes multiplication over addition: >>> from sympy import cos, exp, sin >>> from sympy.abc import x, y, z >>> (y*(x + z)).expand(mul=True) x*y + y*z multinomial ----------- Expand (x + y + ...)**n where n is a positive integer. >>> ((x + y + z)**2).expand(multinomial=True) x**2 + 2*x*y + 2*x*z + y**2 + 2*y*z + z**2 power_exp --------- Expand addition in exponents into multiplied bases. >>> exp(x + y).expand(power_exp=True) exp(x)*exp(y) >>> (2**(x + y)).expand(power_exp=True) 2**x*2**y power_base ---------- Split powers of multiplied bases. This only happens by default if assumptions allow, or if the ``force`` meta-hint is used: >>> ((x*y)**z).expand(power_base=True) (x*y)**z >>> ((x*y)**z).expand(power_base=True, force=True) x**z*y**z >>> ((2*y)**z).expand(power_base=True) 2**z*y**z Note that in some cases where this expansion always holds, SymPy performs it automatically: >>> (x*y)**2 x**2*y**2 log --- Pull out power of an argument as a coefficient and split logs products into sums of logs. Note that these only work if the arguments of the log function have the proper assumptions--the arguments must be positive and the exponents must be real--or else the ``force`` hint must be True: >>> from sympy import log, symbols >>> log(x**2*y).expand(log=True) log(x**2*y) >>> log(x**2*y).expand(log=True, force=True) 2*log(x) + log(y) >>> x, y = symbols('x,y', positive=True) >>> log(x**2*y).expand(log=True) 2*log(x) + log(y) basic ----- This hint is intended primarily as a way for custom subclasses to enable expansion by default. These hints are not run by default: complex ------- Split an expression into real and imaginary parts. >>> x, y = symbols('x,y') >>> (x + y).expand(complex=True) re(x) + re(y) + I*im(x) + I*im(y) >>> cos(x).expand(complex=True) -I*sin(re(x))*sinh(im(x)) + cos(re(x))*cosh(im(x)) Note that this is just a wrapper around ``as_real_imag()``. Most objects that wish to redefine ``_eval_expand_complex()`` should consider redefining ``as_real_imag()`` instead. func ---- Expand other functions. >>> from sympy import gamma >>> gamma(x + 1).expand(func=True) x*gamma(x) trig ---- Do trigonometric expansions. >>> cos(x + y).expand(trig=True) -sin(x)*sin(y) + cos(x)*cos(y) >>> sin(2*x).expand(trig=True) 2*sin(x)*cos(x) Note that the forms of ``sin(n*x)`` and ``cos(n*x)`` in terms of ``sin(x)`` and ``cos(x)`` are not unique, due to the identity `\sin^2(x) + \cos^2(x) = 1`. The current implementation uses the form obtained from Chebyshev polynomials, but this may change. See `this MathWorld article <http://mathworld.wolfram.com/Multiple-AngleFormulas.html>`_ for more information. Notes ===== - You can shut off unwanted methods:: >>> (exp(x + y)*(x + y)).expand() x*exp(x)*exp(y) + y*exp(x)*exp(y) >>> (exp(x + y)*(x + y)).expand(power_exp=False) x*exp(x + y) + y*exp(x + y) >>> (exp(x + y)*(x + y)).expand(mul=False) (x + y)*exp(x)*exp(y) - Use deep=False to only expand on the top level:: >>> exp(x + exp(x + y)).expand() exp(x)*exp(exp(x)*exp(y)) >>> exp(x + exp(x + y)).expand(deep=False) exp(x)*exp(exp(x + y)) - Hints are applied in an arbitrary, but consistent order (in the current implementation, they are applied in alphabetical order, except multinomial comes before mul, but this may change). Because of this, some hints may prevent expansion by other hints if they are applied first. For example, ``mul`` may distribute multiplications and prevent ``log`` and ``power_base`` from expanding them. Also, if ``mul`` is applied before ``multinomial`, the expression might not be fully distributed. The solution is to use the various ``expand_hint`` helper functions or to use ``hint=False`` to this function to finely control which hints are applied. Here are some examples:: >>> from sympy import expand, expand_mul, expand_power_base >>> x, y, z = symbols('x,y,z', positive=True) >>> expand(log(x*(y + z))) log(x) + log(y + z) Here, we see that ``log`` was applied before ``mul``. To get the mul expanded form, either of the following will work:: >>> expand_mul(log(x*(y + z))) log(x*y + x*z) >>> expand(log(x*(y + z)), log=False) log(x*y + x*z) A similar thing can happen with the ``power_base`` hint:: >>> expand((x*(y + z))**x) (x*y + x*z)**x To get the ``power_base`` expanded form, either of the following will work:: >>> expand((x*(y + z))**x, mul=False) x**x*(y + z)**x >>> expand_power_base((x*(y + z))**x) x**x*(y + z)**x >>> expand((x + y)*y/x) y + y**2/x The parts of a rational expression can be targeted:: >>> expand((x + y)*y/x/(x + 1), frac=True) (x*y + y**2)/(x**2 + x) >>> expand((x + y)*y/x/(x + 1), numer=True) (x*y + y**2)/(x*(x + 1)) >>> expand((x + y)*y/x/(x + 1), denom=True) y*(x + y)/(x**2 + x) - The ``modulus`` meta-hint can be used to reduce the coefficients of an expression post-expansion:: >>> expand((3*x + 1)**2) 9*x**2 + 6*x + 1 >>> expand((3*x + 1)**2, modulus=5) 4*x**2 + x + 1 - Either ``expand()`` the function or ``.expand()`` the method can be used. Both are equivalent:: >>> expand((x + 1)**2) x**2 + 2*x + 1 >>> ((x + 1)**2).expand() x**2 + 2*x + 1 API === Objects can define their own expand hints by defining ``_eval_expand_hint()``. The function should take the form:: def _eval_expand_hint(self, **hints): # Only apply the method to the top-level expression ... See also the example below. Objects should define ``_eval_expand_hint()`` methods only if ``hint`` applies to that specific object. The generic ``_eval_expand_hint()`` method defined in Expr will handle the no-op case. Each hint should be responsible for expanding that hint only. Furthermore, the expansion should be applied to the top-level expression only. ``expand()`` takes care of the recursion that happens when ``deep=True``. You should only call ``_eval_expand_hint()`` methods directly if you are 100% sure that the object has the method, as otherwise you are liable to get unexpected ``AttributeError``s. Note, again, that you do not need to recursively apply the hint to args of your object: this is handled automatically by ``expand()``. ``_eval_expand_hint()`` should generally not be used at all outside of an ``_eval_expand_hint()`` method. If you want to apply a specific expansion from within another method, use the public ``expand()`` function, method, or ``expand_hint()`` functions. In order for expand to work, objects must be rebuildable by their args, i.e., ``obj.func(*obj.args) == obj`` must hold. Expand methods are passed ``**hints`` so that expand hints may use 'metahints'--hints that control how different expand methods are applied. For example, the ``force=True`` hint described above that causes ``expand(log=True)`` to ignore assumptions is such a metahint. The ``deep`` meta-hint is handled exclusively by ``expand()`` and is not passed to ``_eval_expand_hint()`` methods. Note that expansion hints should generally be methods that perform some kind of 'expansion'. For hints that simply rewrite an expression, use the .rewrite() API. Examples ======== >>> from sympy import Expr, sympify >>> class MyClass(Expr): ... def __new__(cls, *args): ... args = sympify(args) ... return Expr.__new__(cls, *args) ... ... def _eval_expand_double(self, **hints): ... ''' ... Doubles the args of MyClass. ... ... If there more than four args, doubling is not performed, ... unless force=True is also used (False by default). ... ''' ... force = hints.pop('force', False) ... if not force and len(self.args) > 4: ... return self ... return self.func(*(self.args + self.args)) ... >>> a = MyClass(1, 2, MyClass(3, 4)) >>> a MyClass(1, 2, MyClass(3, 4)) >>> a.expand(double=True) MyClass(1, 2, MyClass(3, 4, 3, 4), 1, 2, MyClass(3, 4, 3, 4)) >>> a.expand(double=True, deep=False) MyClass(1, 2, MyClass(3, 4), 1, 2, MyClass(3, 4)) >>> b = MyClass(1, 2, 3, 4, 5) >>> b.expand(double=True) MyClass(1, 2, 3, 4, 5) >>> b.expand(double=True, force=True) MyClass(1, 2, 3, 4, 5, 1, 2, 3, 4, 5) See Also ======== expand_log, expand_mul, expand_multinomial, expand_complex, expand_trig, expand_power_base, expand_power_exp, expand_func, hyperexpand File: /opt/conda/lib/python3.6/site-packages/sympy/core/function.py Type: function
Greek symbols will render nicely too.
omega = sm.symbols('omega')
omega
You can create arbitrary functions of variables. In this case we make a function of $t$ to represent time.
t = sm.symbols('t')
f = sm.Function('F')
SymPy knows how to differentiate these mathematical functions:
f(t)
sm.diff(f(t), t)
The mechanics package has a convience function for creating functions of time.
f_of_t = me.dynamicsymbols('f')
f_of_t
SymPy is a powerful tool. It can solve many integrals that are not that fun to solve by hand. For example:
sm.integrate(sm.cos(omega)*(omega**2)/sm.exp(omega), omega)
This stores the prior result in the variable long_expr
.
long_expr = _
long_expr
SymPy has a powerful printing system. Here it prints a latex version of the above expression.
print(sm.latex(long_expr))
\frac{\omega^{2} e^{- \omega} \sin{\left(\omega \right)}}{2} - \frac{\omega^{2} e^{- \omega} \cos{\left(\omega \right)}}{2} + \omega e^{- \omega} \sin{\left(\omega \right)} + \frac{e^{- \omega} \sin{\left(\omega \right)}}{2} + \frac{e^{- \omega} \cos{\left(\omega \right)}}{2}
You can print output for many othter programming languages too:
sm.ccode(long_expr)
'(1.0/2.0)*pow(omega, 2)*exp(-omega)*sin(omega) - 1.0/2.0*pow(omega, 2)*exp(-omega)*cos(omega) + omega*exp(-omega)*sin(omega) + (1.0/2.0)*exp(-omega)*sin(omega) + (1.0/2.0)*exp(-omega)*cos(omega)'
sm.rust_code(long_expr)
'(1_f64/2.0)*omega.powi(2)*(-omega).exp()*omega.sin() - 1_f64/2.0*omega.powi(2)*(-omega).exp()*omega.cos() + omega*(-omega).exp()*omega.sin() + (1_f64/2.0)*(-omega).exp()*omega.sin() + (1_f64/2.0)*(-omega).exp()*omega.cos()'
The expressions can be evaluated numerically to arbitrary precision with evalf()
.
long_expr.evalf(subs={omega: 10.0}, n=1000)
If you want to evaluate the expressions using floating point values, it is best to use lambdify
which generates a Python function that can be used to evaluated the expression.
num_func = sm.lambdify(omega, long_expr)
help(num_func)
Help on function _lambdifygenerated: _lambdifygenerated(omega) Created with lambdify. Signature: func(omega) Expression: omega**2*exp(-omega)*sin(omega)/2 - omega**2*exp(-omega)*cos(omega)/2 +... Source code: def _lambdifygenerated(omega): return ((1/2)*omega**2*exp(-omega)*sin(omega) - 1/2*omega**2*exp(-omega)*cos(omega) + omega*exp(-omega)*sin(omega) + (1/2)*exp(-omega)*sin(omega) + (1/2)*exp(-omega)*cos(omega)) Imported modules:
num_func(10.0)
num_func(13.3434)