Emile59
Emile59

Reputation: 85

Error when instantiating bessel functions with Sympy lambdify function

I am currently developping a python program to tranform a symbolic expression computed by sympy into a numpy array containing all the numerical values. I instantiate the symbolic expression with the sympy.lambdify function.

Some of the symbolic expressions contain Bessel functions, and I pass scipy.special.jv/jy etc. as parameters in the lambdify function. Here is the single code (appart from the program) :

m = Symbol('m')
expr= -1.06048319593874*(-(3.14159265358979*m*besselj(27.9937791601866*m, 4.46681007624482*sqrt(-I)) - 0.501286290793831*sqrt(-I)*besselj(27.9937791601866*m + 1, 4.46681007624482*sqrt(-I)))*bessely(27.9937791601866*m, 6.81776274795262*sqrt(-I))/(3.14159265358979*m*bessely(27.9937791601866*m, 4.46681007624482*sqrt(-I)) - 0.501286290793831*sqrt(-I)*bessely(27.9937791601866*m + 1, 4.46681007624482*sqrt(-I))) + besselj(27.9937791601866*m, 6.81776274795262*sqrt(-I)))*sin(0.942966379693359*m)*cos(1.5707963267949*m)/m
nm = 5
vm = arange(nm) +1 
bessel = {'besselj':jv,'besselk':kv,'besseli':iv,'bessely':yv}
libraries = [bessel, "numpy"]     
result = lambdify(m, expr, modules=libraries)(vm)

In[1] : result
Out[1]: 
array([ -7.51212638e-030 -3.22606326e-030j,
         4.81143544e-046 +1.04405860e-046j,
         1.97977798e-097 +3.02047228e-098j,
         3.84986092e-124 +4.73598141e-125j,
         1.12934434e-181 +1.21145535e-182j])

The result is as I expected : a 5 rows 1-d array with each integer value of the symbol m.

The problem is when I try to implement it in the program. Here is the implementation :

expr = list_symbolic_expr[index]
vcm = arange(Dim_col[0]) +1   #Dim_col = list of integer range values to instantiate                     
m = Symbol(str(Var_col[0])) #Var_col = list of symbolic integer parameters
print expr, m
smat = lambdify(m, expr, modules=libraries)(vcm)

Here is the error when using scipy.special bessel functions :

In [2]: run Get_System_Num
Out [2]:
-1.06048319593874*(-(3.14159265358979*m*besselj(27.9937791601866*m, 4.46681007624482*sqrt(-I)) - 0.501286290793831*sqrt(-I)*besselj(27.9937791601866*m + 1, 4.46681007624482*sqrt(-I)))*bessely(27.9937791601866*m, 6.81776274795262*sqrt(-I))/(3.14159265358979*m*bessely(27.9937791601866*m, 4.46681007624482*sqrt(-I)) - 0.501286290793831*sqrt(-I)*bessely(27.9937791601866*m + 1, 4.46681007624482*sqrt(-I))) + besselj(27.9937791601866*m, 6.81776274795262*sqrt(-I)))*sin(0.942966379693359*m)*cos(1.5707963267949*m)/m m
  File "Numeric\Get_System_Num.py", line 183, in get_Mat
    smat = lambdify(m, expr, modules=libraries)(vcm)

  File "<string>", line 1, in <lambda>

TypeError: ufunc 'jv' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''

Here is the error when using sympy.special bessel functions :

  File "Numeric\Get_System_Num.py", line 183, in get_Mat
    smat = lambdify(m, expr, modules=libraries)(vcm)

  File "<string>", line 1, in <lambda>

  File "C:\Users\Emile\Anaconda2\lib\site-packages\sympy\core\function.py", line 375, in __new__
    result = super(Function, cls).__new__(cls, *args, **options)

  File "C:\Users\Emile\Anaconda2\lib\site-packages\sympy\core\function.py", line 199, in __new__
    evaluated = cls.eval(*args)

  File "C:\Users\Emile\Anaconda2\lib\site-packages\sympy\functions\special\bessel.py", line 161, in eval
    if nu.is_integer:

AttributeError: 'numpy.ndarray' object has no attribute 'is_integer'

It seems that lambdify cannot interprete properly the input value in the bessel function in the program code (wether it is with scipy or sympy bessel functions), as the input value seems to be an array object, while it is not a problem in the single code. Yet I do not see the difference between them.

Thank you for reading this, and I hope someone may help me on this topic. Please tell me if more information are needed to illustrate this problem.

--Edit 1--

I have kept on searching the problem, and even when I try to lambdify this simple expression : 17469169.5935065*sin(0.942966379693359*m)*cos(1.5707963267949*m)/m, with "numpy" as module argument, it gives : 'float' object has no attribute 'sin'

I have also tried the packaged module 'numexpr' without success, as it stops on this other expression : 0.058**(46.6321243523316*k), saying :

  File "C:\Users\Emile\Anaconda2\lib\site-packages\numexpr\necompiler.py", line 756, in evaluate
    signature = [(name, getType(arg)) for (name, arg) in zip(names, arguments)]

  File "C:\Users\Emile\Anaconda2\lib\site-packages\numexpr\necompiler.py", line 654, in getType
    raise ValueError("unknown type %s" % a.dtype.name)

ValueError: unknown type object 

So I really don't know the nature of this problem, and I can't even track it as the exception raised comes from inside sympy and I don't know how to put flags in sympy functions.

--Edit 2--

Here is the body of the Get_System_Num class : first the imports, then I also import the data from the symbolic project, meaning : each symbolic coefficient that I want to instantiate (listed in Mat_Num) ; the symbols to instantiate (listed in "list_Var') for each coefficient and depending on the rows and columns ; the size and the position of the instantiated matrix in the real big matrix (listed in list_Dim). As those lists are symbolic at the beginning I need to substitute symbols with real values that I put in Pr_Num <=> numerical project, and I do it in the sub_system_num method.

from numpy import diag, zeros, concatenate, arange, meshgrid, array
from scipy.special import jv,kv,iv,yv
from sympy import srepr, Matrix, lambdify, Symbol
from Numeric.Sub_System_Num import Sub_System_Num

class Get_System_Num :

    #Calling constructor
    def __init__(self, Pr_Num) :

        #Assigning inputs values to local values
        self.Pr_Num = Pr_Num
        self.Pr_Symb = Pr_Num.Pr_Symb

        #Load data from Pr_Symb              
        self.Mat_Num_Index = self.Pr_Symb.Mat_Num_Index        

        #Subs symbols with numeric values
        print "Substitute symbols with real values..."
        self.Sub_System_Num = Sub_System_Num(self)

        #Gathering the results        
        self.Mat_Num = self.Sub_System_Num.Mat_Num
        self.Mat_Size = self.Sub_System_Num.Mat_Size
        self.list_Index = self.Sub_System_Num.list_Index        
        self.list_Var_row = self.Sub_System_Num.list_Var.col(0)  
        self.list_Dim_row = self.Sub_System_Num.list_Dim.col(0)
        self.list_Var_col = self.Sub_System_Num.list_Var.col(1)  
        self.list_Dim_col = self.Sub_System_Num.list_Dim.col(1)

        self.count = 0

        print "Compute numerical matrix..."
        self.Mat = self.get_Mat()

Here is the method to fill the numerical Matrix. I only put the case where there is only one variable to instantiate for both row and column. It's one case among 9 (3²) possible, as each row and columns have either 0,1 or 2 variables to instantiate. About the if srepr(coeff).__contains__(srepr(Var_row[0])) : this checks if the variable is really contained in the expr, and if not I duplicate manually the coeff. As expressions are previousvly computed symbolically, sometimes sympy may simplify them so that the variable is not here anymore.

def get_Mat(self) : 

    bessel = {'besselj':jv,'besselk':kv,'besseli':iv,'bessely':yv}
    libraries = [bessel, 'numpy']

    Mat = zeros((self.Mat_Size[0], self.Mat_Size[0]), dtype=complex)

    for index in range(0, self.Mat_Num_Index.__len__()) :

        Nb_row = self.list_Index[self.Mat_Num_Index[index][0], 0]
        Nb_col = self.list_Index[self.Mat_Num_Index[index][1], 1]

        Pos_row = self.list_Index[self.Mat_Num_Index[index][0], 2]
        Pos_col = self.list_Index[self.Mat_Num_Index[index][1], 3]

        Var_row = self.list_Var_row[self.Mat_Num_Index[index][0]]
        Var_col = self.list_Var_col[self.Mat_Num_Index[index][1]]

        Dim_row = self.list_Dim_row[self.Mat_Num_Index[index][0]]
        Dim_col = self.list_Dim_col[self.Mat_Num_Index[index][1]]

        coeff = self.Mat_Num[index]           

        #M(K or Z, M or Z)
        if Var_row.__len__() == 1 :    
            if Var_col.__len__() == 1 :                    
                if Var_row[0] == Var_col[0] :  #M(K,M=K) or M(Z,Z)  
                    vrk = arange(Dim_row[0]) +1  
                    k = Symbol(str(Var_row[0]))
                    smat = lambdify(k, coeff, modules=libraries)(vrk)                                           
                    if srepr(coeff).__contains__(srepr(Var_row[0])) :
                        smat = diag(smat)
                        print 'coeff n°', index, ' Case 1/1 M(K,M=K) or M(Z,Z) : i dependent '
                        print coeff, Var_col, Var_row

                    else :                           
                        smat = diag([smat]*Dim_row[0])                            
                        print 'coeff n°', index, ' Case 1/1 M(K,M=K) or M(Z,Z) : i non dependent'
                        print coeff, Var_col, Var_row                                  

                else :
                    if srepr(coeff).__contains__(srepr(Var_col[0]) and srepr(Var_row[0])) :  #M(K,Z) or M(Z,M) or M(K, M) 
                        print 'coeff n°', index, ' Case 1/1 M(K,Z) or M(Z,M) : i dependent ' 
                        print coeff, Var_col, Var_row, index                                                    
                        vrk = arange(Dim_row[0]) +1
                        vcm = arange(Dim_col[0]) +1
                        mcm, mrk = meshgrid(vcm, vrk)
                        k = Symbol(str(Var_row[0]))
                        i = Symbol(str(Var_col[0]))
                        smat = lambdify((k, i), coeff, modules=libraries)(mrk, mcm)

                    elif not(srepr(coeff).__contains__(srepr(Var_row[0]))) : #M(Z,M)
                        print 'coeff n°', index, ' Case 1/1 M(Z,M) : i non dependent '
                        print coeff, Var_col, Var_row, index                        
                        vcm = arange(Dim_col[0]) +1
                        m = Symbol(str(Var_col[0]))
                        smat = lambdify(m, coeff, modules=libraries)(vcm)
                        smat = [smat]*Dim_row[0]
                        smat = concatenate(list(smat), axis=0)

                    elif not(srepr(coeff).__contains__(srepr(Var_col[0]))) : #M(K,Z)
                        print 'coeff n°', index, ' Case 1/1 M(K,Z) : i non dependent'
                        print coeff, Var_col, Var_row, index                        
                        vrk = arange(Dim_row[0]) +1
                        k = Symbol(str(Var_row[0]))
                        smat = lambdify(k, coeff, modules=libraries)(vrk)
                        smat = [smat]*Dim_col[0]
                        smat = concatenate(list(smat), axis=1)                            

        self.count = self.count +1
        Mat[Pos_row:Pos_row+Nb_row, Pos_col:Pos_col+Nb_col] = smat

    return Mat

In the end I fill the matrix with the lambdified coeff, which is in this case a smaller matrix of size (Nb_row, Nb_col).

I will keep searching on my own, do not hesitate to ask for more details !

--Edit 3 --

I have found that if I do this :

m = Symbol(str(Var_col[0]))
coeff = lambdify(m, coeff, modules=libraries)
for nm in range(0, Dim_col[0]) :
     smat.append(coeff(nm+1)) 

It works but it's far more time consuming (though far less than using subs and evalf). The error appears when I call the lambda function created by lambdify with an array object (whatever it is numpy or sympy array type).

Upvotes: 1

Views: 1135

Answers (1)

Mike M&#252;ller
Mike M&#252;ller

Reputation: 85512

I can reproduce your error. Based on these inputs:

m = Symbol('m')
expr = -1.06048319593874*(-(3.14159265358979*m*besselj(27.9937791601866*m, 4.46681007624482*sqrt(-I)) - 0.501286290793831*sqrt(-I)*besselj(27.9937791601866*m + 1, 4.46681007624482*sqrt(-I)))*bessely(27.9937791601866*m, 6.81776274795262*sqrt(-I))/(3.14159265358979*m*bessely(27.9937791601866*m, 4.46681007624482*sqrt(-I)) - 0.501286290793831*sqrt(-I)*bessely(27.9937791601866*m + 1, 4.46681007624482*sqrt(-I))) + besselj(27.9937791601866*m, 6.81776274795262*sqrt(-I)))*sin(0.942966379693359*m)*cos(1.5707963267949*m)/m
nm = 2
bessel = {'besselj': jv,'besselk':kv,'besseli':iv,'bessely':yv}
libraries = [bessel, "numpy"]     

It works integers in vm:

vm = np.arange(nm, dtype=np.int) +1 
result = lambdify(m, expr, modules=libraries)(vm)

But complex numbers produce the same error message as you got:

vm = np.arange(nm, dtype=np.complex) +1 
result = lambdify(m, expr, modules=libraries)(vm)

The error message:

TypeError                                 Traceback (most recent call last)
<ipython-input-30-f0e31009275a> in <module>()
      1 vm = np.arange(nm, dtype=np.complex) +1
----> 2 result = lambdify(m, expr, modules=libraries)(vm)

/Users/mike/anaconda/envs/py34/lib/python3.4/site-packages/numpy/__init__.py in <lambda>(_Dummy_27)

TypeError: ufunc 'jv' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''

So, check what data type you got in vcm, i.e. what arange(Dim_col[0]) +1 returns.

You can take only the real part of complex numbers with the attribute real. For example, vm.real gives you the real part of vm. If this is what you want, you should get your result with:

result = lambdify(m, expr, modules=libraries).(vm.real)

Upvotes: 1

Related Questions