sin(y) results in "can't convert expression to float" error - python

I created a very simple MWE to illustrate my problem. When I type y**(2), the program works. But when I type sin(y) or cos(y), it results in the error TypeError: can't convert expression to float. I discuss attempts to fix this error below.
from vpython import *
from scipy.optimize import fsolve
import math
import numpy as np
import sympy as sp
from sympy import Eq, Symbol, solve
import matplotlib.pyplot as plt
y = Symbol('y')
i = input()
i = ''.join(i).split(',')
for x in range(0, len(i)):
i[x] = i[x].strip()
userMediums = i
def refIndexSize(medium):
def refractiveProfile(y):
return eval(medium, {'y': y, 'np': np})
lowerProfile = Eq(eval(medium), 1)
upperProfile = Eq(eval(medium), 1.6)
bounds = [abs(round(float(solve(lowerProfile)[0]),5)),
abs(round(float(solve(upperProfile)[0]),5))]
lowerBound = np.amin(bounds)
upperBound = np.amax(bounds)
return lowerProfile
refIndexSize(userMediums[0])
Error:
sin(y)+1
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/tmp/ipykernel_48/825631216.py in <module>
29 return lowerProfile
30
---> 31 refIndexSize(userMediums[0])
/tmp/ipykernel_48/825631216.py in refIndexSize(medium)
20 return eval(medium, {'y': y, 'np': np})
21
---> 22 lowerProfile = eval(medium)
23 upperProfile = Eq(eval(medium), 1.6)
24 bounds = [abs(round(float(solve(lowerProfile)[0]),5)),
<string> in <module>
/srv/conda/envs/notebook/lib/python3.7/site-packages/sympy/core/expr.py in __float__(self)
357 if result.is_number and result.as_real_imag()[1]:
358 raise TypeError("can't convert complex to float")
--> 359 raise TypeError("can't convert expression to float")
360
361 def __complex__(self):
TypeError: can't convert expression to float
I've looked at other questions regarding TypeError: can't convert expression to float, such as this and this. As a result, I have tried changing the order of my imports, although I can do nothing to change the wildcard from vpython import *, as it is the only way to import vpython (to my understanding), but this did not work. I've also tried inputting sp.sin(y) after looking at a different SO answer, but this did not help either. Once again, any tips or assistance is appreciated.

Your problem lies in this line:
bounds = [abs(round(float(solve(lowerProfile)[0]),5)),
abs(round(float(solve(upperProfile)[0]),5))]
Specifically this part:
abs(round(float(solve(upperProfile)[0]),5))
Here the solve() function returns complex solutions in a list.
See this:
[1.5707963267949 - 1.04696791500319*I, 1.5707963267949 + 1.04696791500319*I]
So, when you pick the 0 index it will be a complex result like this one:
1.5707963267949 - 1.04696791500319*I
So, you are trying to cast float() to this solution which results in error. Instead you can remove the bounds for solutions having complex results by using try-except block like this:
try:
bounds = [abs(round(float(solve(lowerProfile)[0]),5)),
abs(round(float(solve(upperProfile)[0]),5))]
lowerBound = np.amin(bounds)
upperBound = np.amax(bounds)
except:
print("The solutions are complex. Cant find a result")
Also import like this:
from vpython import *
from scipy.optimize import fsolve
import math
import numpy as np
import sympy as sp
from sympy import *
import matplotlib.pyplot as plt

In an ipython session, with the most relevant imports:
In [1]: import numpy as np
...: import sympy as sp
...: from sympy import Eq, Symbol, solve
Modify your function to return the bounds as well.
In [2]: def refIndexSize(medium):
...:
...: def refractiveProfile(y):
...: return eval(medium, {'y': y, 'np': np})
...:
...: lowerProfile = Eq(eval(medium), 1)
...: upperProfile = Eq(eval(medium), 1.6)
...: bounds = [abs(round(float(solve(lowerProfile)[0]),5)),
...: abs(round(float(solve(upperProfile)[0]),5))]
...: lowerBound = np.amin(bounds)
...: upperBound = np.amax(bounds)
...:
...: return lowerProfile, bounds
...:
Define the symbol, and call the function with a string. In the interactive session I don't need to go through the input complications.
In [3]: y = sp.Symbol('y')
y**2 gives the bounds that you claim in a comment:
In [4]: refIndexSize("y**(2)")
Out[4]: (Eq(y**2, 1), [1.0, 1.26491])
Errors due to sin definition
Using a sin expression gives a NameError. sin hasn't been imported or defined.
In [5]: refIndexSize("sin(y)+1")
Traceback (most recent call last):
File "<ipython-input-5-30c99485bce7>", line 1, in <module>
refIndexSize("sin(y)+1")
File "<ipython-input-2-6fea36c332b7>", line 6, in refIndexSize
lowerProfile = Eq(eval(medium), 1)
File "<string>", line 1, in <module>
NameError: name 'sin' is not defined
Import sin from math gives your error:
In [6]: from math import sin
In [7]: refIndexSize("sin(y)+1")
Traceback (most recent call last):
File "<ipython-input-7-30c99485bce7>", line 1, in <module>
refIndexSize("sin(y)+1")
File "<ipython-input-2-6fea36c332b7>", line 6, in refIndexSize
lowerProfile = Eq(eval(medium), 1)
File "<string>", line 1, in <module>
File "/usr/local/lib/python3.8/dist-packages/sympy/core/expr.py", line 359, in __float__
raise TypeError("can't convert expression to float")
TypeError: can't convert expression to float
math.sin expects a float value, so does not work with symbol y.
But import sin from sympy, and it works:
In [8]: from sympy import sin
In [9]: refIndexSize("sin(y)+1")
Out[9]: (Eq(sin(y) + 1, 1), [0.0, 0.6435])
Errors due to complex values
Originally your question showed the use of sin(y), which gives the complex error that #Prakash discusses
In [10]: refIndexSize("sin(y)")
Traceback (most recent call last):
File "<ipython-input-10-d470e7448a68>", line 1, in <module>
refIndexSize("sin(y)")
File "<ipython-input-2-6fea36c332b7>", line 9, in refIndexSize
abs(round(float(solve(upperProfile)[0]),5))]
File "/usr/local/lib/python3.8/dist-packages/sympy/core/expr.py", line 358, in __float__
raise TypeError("can't convert complex to float")
TypeError: can't convert complex to float
Let's simplify your function to get rid float call that seems to be giving problems
In [11]: def refIndexSize(medium):
...: lowerProfile = Eq(eval(medium), 1)
...: upperProfile = Eq(eval(medium), 1.6)
...: bounds = [solve(lowerProfile),
...: solve(upperProfile)]
...: return lowerProfile, bounds
...:
Run on sin(y)+1), we get the [0.0, 0.6435] values as before:
In [12]: refIndexSize("sin(y)+1")
Out[12]: (Eq(sin(y) + 1, 1), [[0, pi], [0.643501108793284, 2.49809154479651]])
Run on sin(y), we see that the 'raw' bounds includes complex values:
In [13]: refIndexSize("sin(y)")
Out[13]:
(Eq(sin(y), 1),
[[pi/2],
[1.5707963267949 - 1.04696791500319*I,
1.5707963267949 + 1.04696791500319*I]])
If you really need a rounded float from such an answer, you need to either extract the real part first, or use abs first:
In [15]: bounds = _[1][1]
In [17]: abs(bounds[0])
Out[17]: 1.88773486361789

Related

Lambidify throws TypeError: can't multiply sequence by non-int of type 'float' when I try to plot through Matplotlib

Whenever I try to run this:
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
import numpy as np
from numpy import *
import sympy
from sympy.abc import x,y
from sympy import symbols
from sympy.plotting import plot
def Plot_2D_RHS(xdata,ydata,RHS):
x = symbols('x')
RHS=sympy.sympify(RHS)
RHS=sympy.lambdify([x], RHS)
plt.figure(figsize=(6, 4))
plt.scatter(xdata, ydata, label='Data')
plt.plot(xdata, RHS(xdata), label='Fit')
plt.legend(loc='best')
plt.show()
xdata=[1, 4 , 6, 8 ,9,10]
ydata=[2, 5, 7, 8,11,12]
RHS='9.8+3.7*x'
Plot_2D_RHS(xdata,ydata,RHS)
I get
Traceback (most recent call last):
File "C:\Users\Essam\Source\Repos\Curve-Fitting\NetwonWillRemember\Trail1\Trail1.py", line 182, in <module>
Plot_2D_RHS(xdata,ydata,RHS)
File "C:\Users\Essam\Source\Repos\Curve-Fitting\NetwonWillRemember\Trail1\Trail1.py", line 16, in Plot_2D_RHS
plt.plot(xdata, RHS(xdata), label='Fit')
File "<lambdifygenerated-1>", line 2, in _lambdifygenerated
TypeError: can't multiply sequence by non-int of type 'float'
Press any key to continue . . .
and no plot is shown, however for some reason if we change the x in the RHS to something like 'cos(x)' it works with no problems, how can I solve this issue without using Sympy's plot since it doesn't offer scatter plots.
In [23]: sympify('9.8+3.7*x')
Out[23]: 3.7⋅x + 9.8
In [24]: f=lambdify([x],sympify('9.8+3.7*x'))
In [25]: print(f.__doc__)
Created with lambdify. Signature:
func(x)
Expression:
3.7*x + 9.8
Source code:
def _lambdifygenerated(x):
return (3.7*x + 9.8)
Imported modules:
It works fine with an array argument, but not with a list:
In [26]: f(np.arange(3))
Out[26]: array([ 9.8, 13.5, 17.2])
In [27]: f([1,2,3])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-27-816cce84b257> in <module>
----> 1 f([1,2,3])
<lambdifygenerated-2> in _lambdifygenerated(x)
1 def _lambdifygenerated(x):
----> 2 return (3.7*x + 9.8)
TypeError: can't multiply sequence by non-int of type 'float'

Medium numbers in Broyden1

I used broyden1 in Python resolver. The question was answered here, I need to use a bit larger numbers, but not newton_krylov. If I use numbers over 100, then it starts throwing errors.
The code is here:
import numpy as np
import scipy.optimize
from scipy.optimize import fsolve
from functools import partial
from itertools import repeat
small_data=[100,220,350,480]
def G(small_data, x):
return np.cos(x) +x[::-1] - small_data
G_partial = partial(G, small_data)
approximate=list(repeat(1,period))
y = scipy.optimize.broyden1(G_partial, approximate, f_tol=1e-14)
print(y)
The error is:
Warning (from warnings module):
File "C:\Python\Python38\lib\site-packages\scipy\optimize\nonlin.py", line 1004
d = v / vdot(df, v)
RuntimeWarning: invalid value encountered in true_divide
Traceback (most recent call last):
File "read_data.py", line 176, in <module>
y = scipy.optimize.broyden1(G_partial, approximate, f_tol=1e-14)
File "<string>", line 6, in broyden1
File "C:\Python\Python38\lib\site-
packages\scipy\optimize\nonlin.py", line 350, in nonlin_solve
raise NoConvergence(_array_like(x, x0))
scipy.optimize.nonlin.NoConvergence: [ 99.49247662 219.22593164 350.14354166 480.95722345]
I found that the best method is changing the equation in Boryden1 to :
y = scipy.optimize.broyden1(G_partial, approximate, f_tol=5000e-14)
instead of:
f_tot=1e-14
so larger values will be accepted with a good accuracy

Is there a method to plot and numerically integrate a sympy function with parameters?

I'm doing a number of symbolic calculations in a Jupyter notebook and would like to plot the functions and calculate numeric integrals before continuing with other symbolic calculations.
I have tried to lambdify the function, but I can only get plots and integrals if I actually retype the function definition. Is there some way to do these operations directly with the symbolic form? If not, is there a way to correctly lambdify the function with fixed input parameters a and b?
from sympy import *
from sympy.utilities.lambdify import lambdify
import matplotlib.pyplot as plt
import numpy as np
import scipy.integrate as int
x,a,b = symbols('x a b')
u = log(exp(-b*x**2/2) + 1/a)
t = np.linspace(-5,5,100)
# Attempt to lambdify u:
f = (lambda x,a=1,b=3: lambdify((x,a,b),u, modules = ['numpy','sympy']))
y = [f(t[i]) for i in range(len(t))]
plt.plot(t,y)
int.quad(f,-5,5)
# Gives error:
Traceback (most recent call last):
File "C:\Users\...\AppData\Local\Continuum\anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 3296, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-86-75a94a575357>", line 2, in <module>
y = [f(t[i]) for i in range(len(t))]
File "<ipython-input-86-75a94a575357>", line 2, in <listcomp>
y = [f(t[i]) for i in range(len(t))]
File "<ipython-input-86-75a94a575357>", line 1, in <lambda>
f = (lambda x,a=1,b=3: lambdify((x,a,b),u, modules = ['numpy','sympy']))
File "C:\Users\...\AppData\Local\Continuum\anaconda3\lib\site-packages\sympy\utilities\lambdify.py", line 476, in lambdify
c = compile(funcstr, filename, 'exec')
File "<lambdifygenerated-10>", line 1
def _lambdifygenerated(-5.0, 1, 3):
^
SyntaxError: invalid syntax
# But explicitly typing u works:
f = (lambda x, a=1, b=3: log(a + exp(b*x**2/2)))
y = [f(t[i]) for i in range(len(t))]
plt.plot(t,y)
int.quad(f,-5,5)
(126.10732269388095,1.0767576643095748𝑒−07)
Lambdify creates a function: you call it once to get the function then you call that function many times. You can do it like this:
In [11]: a = Symbol('a')
In [12]: b = Symbol('b')
In [13]: u = log(exp(-b*x**2/2) + 1/a)
In [14]: flambda = lambdify((x,a,b),u, modules = ['numpy','sympy'])
In [16]: flambda(1, 1, 3)
Out[16]: 0.20141327798275246
In [17]: f = lambda x,a=1,b=3: flambda(x, a, b)
In [18]: f(1)
Out[18]: 0.20141327798275246

Python matplotlib error: Supplied function does not return a valid float

from __future__ import division
import functools
import warnings
import numpy as np
import scipy as sp
from scipy import integrate
from numpy import exp, pi
import matplotlib.pyplot as plt
warnings.simplefilter("ignore", np.ComplexWarning)
def legendrePoly(a,n):
def integrand(t):
return ( ((a + exp(2j*pi*t))**2 - 1)/(2*exp(2j*pi*t)) )**n
return sp.integrate.quad(integrand,0,1)[0]
basisDim = 6
legendreBasis = [functools.partial(legendrePoly, n=i) for i in range(basisDim)]
integrand = [lambda x,i=i: exp(x) * legendreBasis[i](x) for i in range(basisDim)]
normalizingConst = [lambda x,i=i: legendreBasis[i](x)**2 for i in range(basisDim)]
basisCoeff = [sp.integrate.quad(integrand[i],-1,1)[0]
/sp.integrate.quad(normalizingConst[i],-1,1)[0] for i in range(basisDim)]
approxPoly = lambda x: sum(basisCoeff[i]*legendreBasis[i](x) for i in range(basisDim))
t = np.arange(-1, 1, 1e-3)
plt.plot(t,exp(t),'b')
plt.plot(t,approxPoly(t),'r')
plt.show()
I'm using Legendre polynomials as a basis for a polynomial approximation to the exponential function. I'm also using Cauchy's Integral Formula to evaluate them, rather than importing them directly from numpy.
Everything runs fine up to and including defining approxPoly, and approxPoly returns the expected value for any input I enter. But for some reason when I try to plot approxPoly(t), it returns the error: Supplied function does not return a valid float.
This error seems to suggest that when my functions in legendreBasis call to scipy.integrate.quad, that something goes wrong there, but if that was the case then approxPoly wouldn't work, yet if you evaluate it at 2000 points between -1 and 1 manually, and plot these points, everything works fine, but is that not exactly what plt.plot doing when it tries to graph my function?
Traceback:
Traceback (most recent call last):
File "/private/var/folders/mb/yyp8v3_95l538z3g7jsttq540000gn/T/Cleanup At Startup/Exercise-413072597.643.py", line 34, in <module>
plt.plot(t,approxPoly(t),'r')
File "/private/var/folders/mb/yyp8v3_95l538z3g7jsttq540000gn/T/Cleanup At Startup/Exercise-413072597.643.py", line 29, in <lambda>
approxPoly = lambda x: sum(basisCoeff[i]*legendreBasis[i](x) for i in range(basisDim))
File "/private/var/folders/mb/yyp8v3_95l538z3g7jsttq540000gn/T/Cleanup At Startup/Exercise-413072597.643.py", line 29, in <genexpr>
approxPoly = lambda x: sum(basisCoeff[i]*legendreBasis[i](x) for i in range(basisDim))
File "/private/var/folders/mb/yyp8v3_95l538z3g7jsttq540000gn/T/Cleanup At Startup/Exercise-413072597.643.py", line 18, in legendrePoly
return sp.integrate.quad(integrand,0,1)[0]
File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/scipy/integrate/quadpack.py", line 247, in quad
retval = _quad(func,a,b,args,full_output,epsabs,epsrel,limit,points)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/scipy/integrate/quadpack.py", line 312, in _quad
return _quadpack._qagse(func,a,b,args,full_output,epsabs,epsrel,limit)
quadpack.error: Supplied function does not return a valid float.
logout
scipy.integrate.quad doesn't integrate functions that return arrays. That means when you try to call approxPoly(t), t gets passed around until it ends up in this function:
def legendrePoly(a,n):
def integrand(t):
return ( ((a + exp(2j*pi*t))**2 - 1)/(2*exp(2j*pi*t)) )**n
return sp.integrate.quad(integrand,0,1)[0]
integrand returns an array, and sp.integrate.quad chokes. The same problem occurs a number of times in your code. Everything seems to be written assuming scalar arguments.
You can probably fix this by calling vectorize on approxPoly:
plt.plot(t,np.vectorize(approxPoly)(t),'r')
NumPy will then call approxPoly on each element of t separately.

Python and Scipy programming

I'm getting this error message:
Traceback (most recent call last):
File "C:/Python27/test", line 14, in <module>
tck = interpolate.bisplrep(X,Y,Z)
File "C:\Python27\lib\site-packages\scipy\interpolate\fitpack.py", line 850, in bisplrep
raise TypeError('m >= (kx+1)(ky+1) must hold')
TypeError: m >= (kx+1)(ky+1) must hold
The error says that len(X) = m is <=(kx+1)(ky+1). How can I solve this? Here's my program:
import scipy
import math
import numpy
from scipy import interpolate
x= [1000,2000,3000,4000,5000,6000]
y= [1000]
Y = numpy.array([[i]*len(x) for i in y])
X = numpy.array([x for i in y])
Z = numpy.array([[21284473.74,2574509.71,453334.97,95761.64,30580.45,25580.60]])
tck = interpolate.bisplrep(x,y,Z)
print interpolate.bisplev(3500,1000,tck)
Have you read the documentation?
If you don't specify kx and ky, default values will be 3:
scipy.interpolate.bisplrep(x, y, z, w=None, xb=None, xe=None, yb=None, ye=None,
kx=3, ky=3, task=0, s=None, eps=1e-16, tx=None, ty=None,
full_output=0, nxest=None, nyest=None, quiet=1)
And of course, len(X) = 6 < 16 = (3+1)(3+1).
Even if you give kx=1 and ky=1 explicitly while calling, you have another problem. Your (x,y) values form a line, and you can not define a surface from a line. Therefore it gives you ValueError: Invalid inputs.. First, you should fix your data. If this is your data, as you have no variation in Y, skip it and do a spline in 2D with X and Z.

Categories