# -*- coding: iso-8859-1 -*-
# Copyright 2009-2017 Francesco Biscani (bluescarni@gmail.com)
#
# This file is part of the Piranha library.
#
# The Piranha library is free software; you can redistribute it and/or modify
# it under the terms of either:
#
# * the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 3 of the License, or (at your
# option) any later version.
#
# or
#
# * the GNU General Public License as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any
# later version.
#
# or both in parallel, as here.
#
# The Piranha library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received copies of the GNU General Public License and the
# GNU Lesser General Public License along with the Piranha library. If not,
# see https://www.gnu.org/licenses/.
# Use absolute imports to avoid issues with the main math module.
from __future__ import absolute_import as _ai
from ._common import _cpp_type_catcher, __check_eval_subs_dict
def __check_names_argument(names):
# This is used in a few functions below.
if not names is None and (not isinstance(names, list) or not all([isinstance(_, str) for _ in names])):
raise TypeError(
'the optional \'names\' argument must be a list of strings')
[docs]def cos(arg):
"""Cosine.
The supported types are ``int``, ``float``, ``Fraction``, ``mpf`` and any symbolic type that supports
the operation.
:param arg: cosine argument
:type arg: ``int``, ``float``, ``Fraction``, ``mpf``, or a supported symbolic type.
:returns: cosine of *arg*
:raises: :exc:`TypeError` if the type of *arg* is not supported, or any other exception raised by the invoked
low-level function
>>> cos(0)
1
>>> cos(2) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
ValueError: cannot compute the cosine of a non-zero integer
>>> cos(2.) # doctest: +ELLIPSIS
-0.4161468...
>>> from pyranha.types import poisson_series, polynomial, rational, int16, monomial
>>> t = poisson_series[polynomial[rational,monomial[int16]]]()
>>> cos(2 * t('x'))
cos(2*x)
>>> cos('hello') # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _cos
return _cpp_type_catcher(_cos, arg)
[docs]def sin(arg):
"""Sine.
The supported types are ``int``, ``float``, ``Fraction``, ``mpf`` and any symbolic type that supports
the operation.
:param arg: sine argument
:type arg: ``int``, ``float``, ``Fraction``, ``mpf``, or a supported symbolic type.
:returns: sine of *arg*
:raises: :exc:`TypeError` if the type of *arg* is not supported, or any other exception raised by the invoked
low-level function
>>> sin(0)
0
>>> sin(2) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
ValueError: cannot compute the cosine of a non-zero integer
>>> sin(2.) # doctest: +ELLIPSIS
0.9092974...
>>> from pyranha.types import poisson_series, polynomial, rational, int16, monomial
>>> t = poisson_series[polynomial[rational,monomial[int16]]]()
>>> sin(2 * t('x'))
sin(2*x)
>>> sin('hello') # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _sin
return _cpp_type_catcher(_sin, arg)
[docs]def binomial(x, y):
"""Binomial coefficient.
This function is a wrapper around a lower level function. It will calculate the generalised binomial coefficient :math:`{x \choose y}`,
supporting various combinations of integral, rational, floating-point and arbitrary-precision floating-point types
as input.
:param x: top argument for the binomial coefficient
:type x: ``int``, ``float``, ``Fraction``, ``mpf``
:param y: bottom argument for the binomial coefficient
:type y: ``int``, ``float``, ``Fraction``, ``mpf``
:returns: *x* choose *y*
:raises: :exc:`TypeError` if the types of *x* and/or *y* are not supported
:raises: any exception raised by the invoked low-level function
>>> binomial(3,2)
3
>>> binomial(-6,2)
21
>>> from fractions import Fraction as F
>>> binomial(F(-4,5),2)
Fraction(18, 25)
>>> binomial(10,2.4) # doctest: +ELLIPSIS
70.3995282...
>>> binomial('hello world',2.4) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _binomial
return _cpp_type_catcher(_binomial, x, y)
[docs]def gcd(x, y):
"""Greatest common divisor.
This function is a wrapper around a lower level function. It will calculate the GCD of *x* and *y*,
supporting ``int`` as argument type.
:param x: first argument
:type x: ``int``
:param y: second argument
:type y: ``int``
:returns: the GCD of *x* and *y*
:raises: :exc:`TypeError` if the types of *x* and/or *y* are not supported
:raises: any exception raised by the invoked low-level function
>>> gcd(12,9)
3
>>> from pyranha.types import polynomial, integer, k_monomial
>>> pt = polynomial[integer,k_monomial]()
>>> x,y = pt('x'), pt('y')
>>> gcd(x**-1,y) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _gcd
return _cpp_type_catcher(_gcd, x, y)
[docs]def partial(arg, name):
"""Partial derivative.
Compute the partial derivative of *arg* with respect to the variable *name*. *arg* must be a symbolic type and
*name* a string.
:param arg: argument for the partial derivative
:type arg: a symbolic type
:param name: name of the variable with respect to which the derivative will be calculated
:type name: string
:returns: partial derivative of *arg* with respect to *name*
:raises: :exc:`TypeError` if the types of *arg* and/or *name* are not supported, or any other exception raised by the invoked
low-level function
>>> from pyranha.types import polynomial, integer, int16, monomial
>>> pt = polynomial[integer,monomial[int16]]()
>>> x,y = pt('x'), pt('y')
>>> partial(x + 2*x*y,'y')
2*x
>>> partial(x + 2*x*y,1) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _partial
return _cpp_type_catcher(_partial, arg, name)
[docs]def integrate(arg, name):
"""Integration.
Compute the antiderivative of *arg* with respect to the variable *name*. *arg* must be a symbolic type and
*name* a string. The success of the operation is not guaranteed, and depends both on the type and value of
*arg*.
:param arg: argument for the integration
:type arg: a symbolic type
:param name: name of the variable with respect to which the integration will be calculated
:type name: string
:returns: antiderivative of *arg* with respect to *name*
:raises: :exc:`TypeError` if the types of *arg* and/or *name* are not supported, or any other exception raised by the invoked
low-level function
>>> from pyranha.types import polynomial, rational, k_monomial
>>> pt = polynomial[rational,k_monomial]()
>>> x,y = pt('x'), pt('y')
>>> integrate(x + 2*x*y,'x') == x**2/2 + x**2*y
True
>>> integrate(x**-1,'x') # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
ValueError: negative unitary exponent
>>> integrate(x**-1,1) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _integrate
return _cpp_type_catcher(_integrate, arg, name)
[docs]def factorial(n):
"""Factorial.
Will compute the factorial of *n*, which must be a non-negative instance of ``int``.
:param n: argument for the factorial
:type n: ``int``
:returns: factorial of *n*
:raises: :exc:`TypeError` if *n* is not an ``int``
:raises: :exc:`ValueError` if *n* is negative or too large
>>> factorial(0)
1
>>> factorial(6)
720
>>> factorial(-1)
Traceback (most recent call last):
...
ValueError: invalid argument value
>>> factorial(1.5)
Traceback (most recent call last):
...
TypeError: factorial argument must be an integer
"""
from ._core import _factorial
if not isinstance(n, int):
raise TypeError('factorial argument must be an integer')
try:
return _factorial(n)
except ValueError:
raise ValueError('invalid argument value')
[docs]def pbracket(f, g, p_list, q_list):
"""Poisson bracket.
Compute the Poisson bracket of *f* and *g* with respect to the momenta with names in *p_list*
and coordinates with names in *q_list*. *f* and *g* must be symbolic objects of the same type, and
*p_list* and *q_list* lists of strings with the same size and no duplicate entries.
:param f: first argument
:type f: a symbolic type
:param g: second argument
:type g: a symbolic type
:param p_list: list of momenta names
:type p_list: list of strings
:param q_list: list of coordinates names
:type q_list: list of strings
:returns: Poisson bracket of *f* and *g* with respect to momenta *p_list* and coordinates *q_list*
:raises: :exc:`ValueError` if *p_list* and *q_list* have different sizes or duplicate entries
:raises: :exc:`TypeError` if the types of the arguments are invalid
:raises: any exception raised by the invoked low-level function
>>> from pyranha.types import polynomial, rational, int16, monomial
>>> pt = polynomial[rational,monomial[int16]]()
>>> x,v = pt('x'), pt('v')
>>> pbracket(x+v,x+v,['v'],['x']) == 0
True
>>> pbracket(x+v,x+v,[],['x']) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
ValueError: the number of coordinates is different from the number of momenta
>>> pbracket(x+v,x+v,['v','v'],['x']) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
ValueError: the list of momenta contains duplicate entries
>>> pbracket(v,x,['v'],[1]) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _pbracket
return _cpp_type_catcher(_pbracket, f, g, p_list, q_list)
[docs]def truncate_degree(arg, max_degree, names=None):
"""Degree-based truncation.
This function will eliminate from *arg* parts whose degree is greater than *max_degree*. The truncation
operates on symbolic types both by eliminating entire terms and by truncating recursively the coefficients,
if possible.
If *names* is ``None``, then the total degree is considered for the truncation. Otherwise, *names* must be
a list of strings enumerating the variables to be considered for the computation of the degree.
:param arg: argument for the truncation
:type arg: a symbolic type
:param max_degree: maximum degree that will be kept in *arg*
:type max_degree: a type comparable to the type representing the degree of *arg*
:param names: list of the names of the variables to be considered in the computation of the degree
:type names: ``None`` or a list of strings
:returns: the truncated counterpart of *arg*
:raises: :exc:`TypeError` if *names* is neither ``None`` nor a list of strings
:raises: any exception raised by the invoked low-level function
>>> from pyranha.types import polynomial, rational, k_monomial, poisson_series
>>> pt = polynomial[rational,k_monomial]()
>>> x,y,z = pt('x'), pt('y'), pt('z')
>>> truncate_degree(x**2*y+x*y+z,2) # doctest: +SKIP
x*y+z
>>> truncate_degree(x**2*y+x*y+z,0,['x'])
z
>>> pt = poisson_series[polynomial[rational,k_monomial]]()
>>> x,y,z,a,b = pt('x'), pt('y'), pt('z'), pt('a'), pt('b')
>>> truncate_degree((x+y*x+x**2*z)*cos(a+b)+(y-y*z+x**4)*sin(2*a+b),3) # doctest: +SKIP
(x+x*y+x**2*z)*cos(a+b)+(-y*z+y)*sin(2*a+b)
>>> truncate_degree((x+y*x+x**2*z)*cos(a+b)+(y-y*z+x**4)*sin(2*a+b),1,['x','y']) # doctest: +SKIP
x*cos(a+b)+(-y*z+y)*sin(2*a+b)
>>> truncate_degree((x+y*x+x**2*z)*cos(a+b)+(y-y*z+x**4)*sin(2*a+b),1,'x') # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: the optional 'names' argument must be a list of strings
>>> truncate_degree((x+y*x+x**2*z)*cos(a+b)+(y-y*z+x**4)*sin(2*a+b),1,['x',1]) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: the optional 'names' argument must be a list of strings
"""
from ._core import _truncate_degree
__check_names_argument(names)
if names is None:
return _cpp_type_catcher(_truncate_degree, arg, max_degree)
else:
return _cpp_type_catcher(_truncate_degree, arg, max_degree, names)
[docs]def evaluate(arg, eval_dict):
"""Evaluation.
This function will evaluate *arg* according to the evaluation dictionary *eval_dict*. Evaluation
is the replacement of all symbolic quantities with numerical values.
*eval_dict* must be a dictionary of ``(name,value)`` pairs, where ``name`` is a string referring to the symbol
to be replaced and ``value`` is the value with which ``name`` will be replaced. All values must be of the same type,
and this type needs to support the operations needed to compute the evaluation.
:param arg: argument for the evaluation
:type arg: a symbolic type
:param eval_dict: evaluation dictionary
:type eval_dict: a dictionary mapping strings to values, with all values of the same type
:returns: the evaluation of *arg* according to *eval_dict*
:raises: :exc:`TypeError` if *eval_dict* does not satisfy the requirements outlined above
:raises: :exc:`ValueError` if *eval_dict* is empty
:raises: any exception raised by the invoked low-level function
>>> from pyranha.types import polynomial, rational, k_monomial
>>> pt = polynomial[rational,k_monomial]()
>>> x,y,z = pt('x'), pt('y'), pt('z')
>>> evaluate(x*y+4*(y/4)**2*z,{'x':3,'y':-3,'z':5})
Fraction(9, 4)
>>> evaluate(x*y+4*(y/4)**2*z,{'x':3.01,'y':-3.32,'z':5.34}) # doctest: +ELLIPSIS
4.7217...
>>> evaluate(x*y+4*(y/4)**2*z,{'x':3.1,'y':-3.2,'z':5}) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: all values in the evaluation dictionary must be of the same type
>>> evaluate(x*y+4*(y/4)**2*z,{}) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
ValueError: evaluation dictionary cannot be empty
>>> evaluate(x*y+4*(y/4)**2*z,{'x':3,'y':-3,5:5}) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: all keys in the evaluation dictionary must be string objects
>>> evaluate(x*y+4*(y/4)**2*z,[1,2,3]) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: evaluation dictionary must be a dict object
>>> evaluate(x*y+4*(y/4)**2*z,{'x':3,'y':-3}) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
ValueError: invalid positions map for evaluation
>>> evaluate(x*y+4*(y/4)**2*z,{'x':'a','y':'b', 'z':'c'}) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _evaluate
# Check input dict.
__check_eval_subs_dict(eval_dict)
return _cpp_type_catcher(_evaluate, arg, eval_dict, eval_dict[list(eval_dict.keys())[0]])
[docs]def subs(arg, subs_dict):
"""Substitution.
This function will replace, in *arg*, the symbols listed in the dictionary *subs_dict*
with their mapped values. Internally, the functionality is implemented via a call to a
lower-level C++ routine that supports various combinations of input argument types.
*subs_dict* must be a dictionary of ``(name,value)`` pairs, where ``name`` is a string referring to the symbol
to be substituted and ``value`` is the value with which ``name`` will be replaced. All values must be
of the same type, and this type needs to support the operations needed to compute the
substitution.
:param arg: the argument for the substitution
:type arg: a symbolic type
:param subs_dict: the substitution dictionary
:type subs_dict: a dictionary mapping strings to values, with all values of the same type
:returns: *arg* after the substitution of the symbols in *subs_dict* with the mapped values
:raises: :exc:`TypeError` if *subs_dict* does not satisfy the requirements outlined above
:raises: :exc:`ValueError` if *subs_dict* is empty
:raises: any exception raised by the invoked low-level function
>>> from pyranha.types import polynomial, rational, k_monomial
>>> pt = polynomial[rational,k_monomial]()
>>> x,y,z = pt('x'), pt('y'), pt('z')
>>> subs(x*y+4*(y/4)**2*z,{'x':3,'y':-3,'z':2})
-9/2
>>> subs(x*y+4*(y/4)**2*z,{'x':3.01,'y':-3.32,'z':5.34}) # doctest: +ELLIPSIS
4.7217...
>>> subs(x*y+4*(y/4)**2*z,{'x':3.1,'y':-3.2,'z':5}) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: all values in an evaluation/substitution dictionary must be of the same type
>>> subs(x*y+4*(y/4)**2*z,{}) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
ValueError: an evaluation/substitution dictionary cannot be empty
>>> subs(x*y+4*(y/4)**2*z,{'x':3,'y':-3,5:5}) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: all keys in an evaluation/substitution dictionary must be string objects
>>> subs(x*y+4*(y/4)**2*z,[1,2,3]) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: an evaluation/substitution dictionary must be a dict object
>>> subs(x*y+4*(y/4)**2*z,{'x':'a','y':'b', 'z':'c'}) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _subs
__check_eval_subs_dict(subs_dict)
return _cpp_type_catcher(_subs, arg, subs_dict, subs_dict[list(subs_dict.keys())[0]])
[docs]def t_subs(arg, name, x, y):
"""Trigonometric substitution.
This function will replace, in *arg*, the cosine and sine of the symbol called *name* with
the generic objects *x* and *y*. Internally, the functionality is implemented via a call to
a lower-level C++ routine that supports various combinations for the types of *arg*, *x* and *y*.
:param arg: argument for the substitution
:type arg: a symbolic type
:param name: name of the symbol whose cosine and sine will be substituted
:type name: a string
:param x: the quantity that will be substituted for the cosine of *name*
:type x: any type supported by the low-level C++ routine
:param y: the quantity that will be substituted for the sine of *name*
:type y: any type supported by the low-level C++ routine
:returns: *arg* after the substitution of the cosine and sine of the symbol called *name* with *x* and *y*
:raises: :exc:`TypeError` in case the input types are not supported or invalid
:raises: any exception raised by the invoked low-level function
>>> from pyranha.types import poisson_series, rational, polynomial, int16, monomial
>>> pt = poisson_series[polynomial[rational,monomial[int16]]]()
>>> x,y = pt('x'), pt('y')
>>> t_subs(cos(x+y),'x',1,0)
cos(y)
>>> t_subs(sin(2*x+y),'x',0,1)
-sin(y)
>>> t_subs(sin(2*x+y),0,0,1) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _t_subs
return _cpp_type_catcher(_t_subs, arg, name, x, y)
[docs]def ipow_subs(arg, name, n, x):
"""Integral power substitution.
This function will replace, in *arg*, the *n*-th power of the symbol called *name* with
the generic object *x*. Internally, the functionality is implemented via a call to
a lower-level C++ routine that supports various combinations for the types of *arg* and *x*.
:param arg: argument for the substitution
:type arg: a symbolic type
:param name: name of the symbol whose power will be substituted
:type name: a string
:param n: power of *name* that will be substituted
:type n: an integer
:param x: the quantity that will be substituted for the power of *name*
:type x: any type supported by the low-level C++ routine
:returns: *arg* after the substitution of *n*-th power of the symbol called *name* with *x*
:raises: :exc:`TypeError` in case the input types are not supported or invalid
:raises: any exception raised by the invoked low-level function
>>> from pyranha.types import rational, polynomial, int16, monomial
>>> pt = polynomial[rational,monomial[int16]]()
>>> x,y,z = pt('x'), pt('y'), pt('z')
>>> ipow_subs(x**5*y,'x',2,z)
x*y*z**2
>>> ipow_subs(x**6*y,'x',2,z**-1)
y*z**-3
>>> ipow_subs(x**6*y,0,2,z**-1) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
>>> ipow_subs(x**6*y,'x',2.1,z**-1) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _ipow_subs
return _cpp_type_catcher(_ipow_subs, arg, name, n, x)
[docs]def invert(arg):
"""Inverse.
Compute the multiplicative inverse of the argument.
The supported types are ``int``, ``float``, ``Fraction``, ``mpf`` and any symbolic type that supports
the operation.
:param arg: inversion argument
:type arg: ``int``, ``float``, ``Fraction``, ``mpf``, or a supported symbolic type.
:returns: inverse of *arg*
:raises: :exc:`TypeError` if the type of *arg* is not supported, or any other exception raised by the invoked
low-level function
>>> from fractions import Fraction as F
>>> invert(F(1,2))
Fraction(2, 1)
>>> invert(1.23) # doctest: +ELLIPSIS
0.8130081...
>>> invert(0) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
>>> from pyranha.types import polynomial, rational, int16, monomial, divisor, divisor_series
>>> t = polynomial[rational,monomial[int16]]()
>>> invert(t('x'))
x**-1
>>> t = divisor_series[polynomial[rational,monomial[int16]],divisor[int16]]()
>>> invert(-2*t('x')+8*t('y'))
-1/2*1/[(x-4*y)]
>>> invert(t('x')+1) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
ValueError: invalid argument for series exponentiation: negative integral value
>>> invert('hello') # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _invert
return _cpp_type_catcher(_invert, arg)
[docs]def degree(arg, names=None):
"""Degree.
This function will return the degree of the input argument. Various symbolic types are supported as input
(e.g., polynomials, Poisson series, etc.). If *names* is ``None``, then the total degree is
returned. Otherwise, *names* must be a list of strings enumerating the variables to be considered for the
computation of the partial degree.
:param arg: argument whose degree will be returned
:type arg: a supported symbolic type.
:param names: list of the names of the variables to be considered in the computation of the degree
:type names: ``None`` or a list of strings
:returns: the degree of *arg*
:raises: :exc:`TypeError` if the type of *arg* is not supported, or any other exception raised by the invoked
low-level function
>>> from pyranha.types import polynomial, rational, k_monomial
>>> t = polynomial[rational,k_monomial]()
>>> x,y,z = t('x'),t('y'),t('z')
>>> degree(x**3+y*z) == 3
True
>>> degree(x**3+y*z,['z']) == 1
True
>>> from fractions import Fraction as F
>>> degree(F(1,2)) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _degree
__check_names_argument(names)
if names is None:
return _cpp_type_catcher(_degree, arg)
else:
return _cpp_type_catcher(_degree, arg, names)
[docs]def ldegree(arg, names=None):
"""Low degree.
This function will return the low degree of the input argument. Various symbolic types are supported as input
(e.g., polynomials, Poisson series, etc.). If *names* is ``None``, then the total low degree is
returned. Otherwise, *names* must be a list of strings enumerating the variables to be considered for the
computation of the partial low degree.
:param arg: argument whose low degree will be returned
:type arg: a supported symbolic type.
:param names: list of the names of the variables to be considered in the computation of the low degree
:type names: ``None`` or a list of strings
:returns: the low degree of *arg*
:raises: :exc:`TypeError` if the type of *arg* is not supported, or any other exception raised by the invoked
low-level function
>>> from pyranha.types import polynomial, rational, k_monomial
>>> t = polynomial[rational,k_monomial]()
>>> x,y,z = t('x'),t('y'),t('z')
>>> ldegree(x**3+y*z) == 2
True
>>> ldegree(x**3+y*x,['x']) == 1
True
>>> from fractions import Fraction as F
>>> ldegree(F(1,2)) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _ldegree
__check_names_argument(names)
if names is None:
return _cpp_type_catcher(_ldegree, arg)
else:
return _cpp_type_catcher(_ldegree, arg, names)
[docs]def t_degree(arg, names=None):
"""Trigonometric degree.
This function will return the trigonometric degree of the input argument. Various symbolic types are supported as
input. If *names* is ``None``, then the total degree is returned. Otherwise, *names* must be a list of strings
enumerating the variables to be considered for the computation of the partial degree.
:param arg: argument whose trigonometric degree will be returned
:type arg: a supported symbolic type.
:param names: list of the names of the variables to be considered in the computation of the degree
:type names: ``None`` or a list of strings
:returns: the trigonometric degree of *arg*
:raises: :exc:`TypeError` if the type of *arg* is not supported, or any other exception raised by the invoked
low-level function
>>> from pyranha.types import poisson_series, polynomial, rational, k_monomial
>>> from pyranha.math import cos
>>> t = poisson_series[polynomial[rational,k_monomial]]()
>>> x,y,z = t('x'),t('y'),t('z')
>>> t_degree(cos(3*x+y-z)+cos(2*x)) == 3
True
>>> t_degree(cos(3*x+y+z)+cos(x),['z']) == 1
True
>>> from fractions import Fraction as F
>>> t_degree(F(1,2)) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _t_degree
__check_names_argument(names)
if names is None:
return _cpp_type_catcher(_t_degree, arg)
else:
return _cpp_type_catcher(_t_degree, arg, names)
[docs]def t_ldegree(arg, names=None):
"""Trigonometric low degree.
This function will return the trigonometric low degree of the input argument. Various symbolic types are supported as
input. If *names* is ``None``, then the total low degree is returned. Otherwise, *names* must be a list of strings
enumerating the variables to be considered for the computation of the partial low degree.
:param arg: argument whose trigonometric low degree will be returned
:type arg: a supported symbolic type.
:param names: list of the names of the variables to be considered in the computation of the low degree
:type names: ``None`` or a list of strings
:returns: the trigonometric low degree of *arg*
:raises: :exc:`TypeError` if the type of *arg* is not supported, or any other exception raised by the invoked
low-level function
>>> from pyranha.types import poisson_series, polynomial, rational, k_monomial
>>> from pyranha.math import cos
>>> t = poisson_series[polynomial[rational,k_monomial]]()
>>> x,y,z = t('x'),t('y'),t('z')
>>> t_ldegree(cos(3*x+y-z)+cos(2*x)) == 2
True
>>> t_ldegree(cos(3*x+y-z)+cos(2*y+z),['y']) == 1
True
>>> from fractions import Fraction as F
>>> t_ldegree(F(1,2)) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _t_ldegree
__check_names_argument(names)
if names is None:
return _cpp_type_catcher(_t_ldegree, arg)
else:
return _cpp_type_catcher(_t_ldegree, arg, names)
[docs]def t_order(arg, names=None):
"""Trigonometric order.
This function will return the trigonometric order of the input argument. Various symbolic types are supported as
input. If *names* is ``None``, then the total order is returned. Otherwise, *names* must be a list of strings
enumerating the variables to be considered for the computation of the partial order.
:param arg: argument whose trigonometric order will be returned
:type arg: a supported symbolic type.
:param names: list of the names of the variables to be considered in the computation of the order
:type names: ``None`` or a list of strings
:returns: the order of *arg*
:raises: :exc:`TypeError` if the type of *arg* is not supported, or any other exception raised by the invoked
low-level function
>>> from pyranha.types import poisson_series, polynomial, rational, k_monomial
>>> from pyranha.math import cos
>>> t = poisson_series[polynomial[rational,k_monomial]]()
>>> x,y,z = t('x'),t('y'),t('z')
>>> t_order(cos(3*x+y-z)+cos(x)) == 5
True
>>> t_order(cos(3*x+y-z)-sin(y),['z']) == 1
True
>>> from fractions import Fraction as F
>>> t_order(F(1,2)) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _t_order
__check_names_argument(names)
if names is None:
return _cpp_type_catcher(_t_order, arg)
else:
return _cpp_type_catcher(_t_order, arg, names)
[docs]def t_lorder(arg, names=None):
"""Trigonometric low order.
This function will return the trigonometric low order of the input argument. Various symbolic types are supported as
input. If *names* is ``None``, then the total low order is returned. Otherwise, *names* must be a list of strings
enumerating the variables to be considered for the computation of the partial low order.
:param arg: argument whose trigonometric low order will be returned
:type arg: a supported symbolic type.
:param names: list of the names of the variables to be considered in the computation of the low order
:type names: ``None`` or a list of strings
:returns: the trigonometric low order of *arg*
:raises: :exc:`TypeError` if the type of *arg* is not supported, or any other exception raised by the invoked
low-level function
>>> from pyranha.types import poisson_series, polynomial, rational, k_monomial
>>> from pyranha.math import cos
>>> t = poisson_series[polynomial[rational,k_monomial]]()
>>> x,y,z = t('x'),t('y'),t('z')
>>> t_lorder(cos(4*x+y-z)+cos(2*x)) == 2
True
>>> t_lorder(cos(3*x+y-z)+cos(2*y+z),['y']) == 1
True
>>> from fractions import Fraction as F
>>> t_lorder(F(1,2)) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: invalid argument type(s)
"""
from ._core import _t_lorder
__check_names_argument(names)
if names is None:
return _cpp_type_catcher(_t_lorder, arg)
else:
return _cpp_type_catcher(_t_lorder, arg, names)
[docs]def lambdify(t, x, names, extra_map={}):
"""Turn a symbolic object into a function.
This function is a wrapper around :func:`~pyranha.math.evaluate()` which returns a callable object that can be used
to evaluate the input argument *x*. The call operator of the returned object takes as input a collection of length
``len(names)`` of objects of type *t* which, for the purpose of evaluation, are associated to the list of
symbols *names* at the corresponding positions.
The optional argument *extra_map* is a dictionary that maps symbol names to callables, and it used to specify
what values to assign to specific symbols based on the values of the symbols in *names*. For instance, suppose
that :math:`z` depends on :math:`x` and :math:`y` via :math:`z\\left(x,y\\right)=\\sqrt{3x+y}`. We can then evaluate
the expression :math:`x+y+z` as follows:
>>> from pyranha.types import polynomial, rational, k_monomial
>>> pt = polynomial[rational,k_monomial]()
>>> x,y,z = x,y,z = pt('x'),pt('y'),pt('z')
>>> l = lambdify(float,x+y+z,['x','y'],{'z': lambda a: (3.*a[0] + a[1])**.5})
>>> l([1.,2.]) # doctest: +ELLIPSIS
5.236067977...
The output value is :math:`1+2+\\sqrt{5}`.
:param t: the type that will be used for the evaluation of *x*
:type t: a supported evaluation type
:param x: symbolic object that will be evaluated
:type x: a supported symbolic type
:param names: the symbols that will be used for evaluation
:type names: a list of strings
:param extra_map: a dictionary mapping symbol names to custom evaluation functions
:type extra_map: a dictionary in which the keys are strings and the values are callables
:returns: a callable object that can be used to evaluate *x* with objects of type *t*
:rtype: the type returned by the invoked low-level function
:raises: :exc:`TypeError` if *t* is not a type, or if *names* is not a list of strings, or if *extra_map* is not
a dictionary of the specified type
:raises: :exc:`ValueError` if *names* contains duplicates or if *extra_map* contains strings already present in *names*
:raises: :exc:`unspecified` any exception raised by the invoked low-level function or by the invoked
callables in *extra_map*
>>> from pyranha.types import polynomial, rational, k_monomial
>>> pt = polynomial[rational,k_monomial]()
>>> x,y,z = x,y,z = pt('x'),pt('y'),pt('z')
>>> l = lambdify(int,2*x-y+3*z,['z','y','x'])
>>> l([1,2,3])
Fraction(7, 1)
>>> l([1,2,-3])
Fraction(-5, 1)
>>> l = lambdify(float,x+y+z,['x','y'],{'z': lambda a: (3.*a[0] + a[1])**.5})
>>> l([1.,2.]) # doctest: +ELLIPSIS
5.236067977...
>>> l([1]) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
ValueError: invalid size of the evaluation vector
>>> l = lambdify(3,2*x-y+3*z,['z','y','x']) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: the 't' argument must be a type
>>> l = lambdify(int,2*x-y+3*z,[1,2,3]) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
TypeError: the 'names' argument must be a list of strings
>>> l = lambdify(int,2*x-y+3*z,['z','y','x','z']) # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
ValueError: the 'names' argument contains duplicates
>>> l = lambdify(float,x+y+z,['x','y'],{'z': 123})
Traceback (most recent call last):
...
TypeError: all the values in the 'extra_map' argument must be callables
"""
from ._core import _lambdify
if not isinstance(t, type):
raise TypeError('the \'t\' argument must be a type')
if not isinstance(names, list) or not all([isinstance(_, str) for _ in names]):
raise TypeError('the \'names\' argument must be a list of strings')
if not all([isinstance(_, str) for _ in extra_map]):
raise TypeError(
'the \'extra_map\' argument must be a dictionary in which the keys are strings')
if not all([callable(extra_map[_]) for _ in extra_map]):
raise TypeError(
'all the values in the \'extra_map\' argument must be callables')
return _cpp_type_catcher(_lambdify, x, names, extra_map, t())