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.
CodePudding user response:
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
CodePudding user response:
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