2

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.

K450
  • 691
  • 5
  • 17
rb3652
  • 425
  • 2
  • 12
  • `sin(y)` is taken as expression which wont be converted to float – Prakash Dahal Jan 03 '22 at 05:02
  • Hi @PrakashDahal -- how can I evaluate sin(y)? Do you have any suggestions? – rb3652 Jan 03 '22 at 05:06
  • @PrakashDahal Is the problem with SymPy, SciPy, eval(), or a combination of these? – rb3652 Jan 03 '22 at 05:18
  • More like a problem of `eval()`. What should be the value of `lowerProfile()` and `upperProfile()` – Prakash Dahal Jan 03 '22 at 05:19
  • @PrakashDahal This code is solving a very simple problem. Given a function, say, y^2, for what value of y is y^2 = 1 and y^2 = 1.6? So, for y^2, the answer should be y=1 and 1.26. It works perfectly fine for things like `y**(2)` or `y**(-1/2)` or whatnot. But with `sin(y)`, there's some weird problem that I just can't figure out. – rb3652 Jan 03 '22 at 05:21
  • I got your problem. It results in getting complex solution as result. So, you are trying to solve complex system to float using `float()` which is invalid. The `solve()` gives complex results which you are type casting to float – Prakash Dahal Jan 03 '22 at 05:40
  • 1
    `math.sin(y)` produces this error, since `math.sin` expects a float argument, not a symbol like `y`. But I don't follow the "medium" stuff, so don't see how a plain `sin` is evaluated as `math.sin`. `np.sin(y)` gives a different error, but for the same reason. `sp.sin(y)` works, What is `sin(y)` supposed to be doing? – hpaulj Jan 03 '22 at 05:58
  • Please don't post images of code or console output. Format the text version of the stack trace as code and get rid of the picture – Mad Physicist Jan 03 '22 at 05:59
  • @MadPhysicist OK, thanks for the tip. I've reformatted the question accordingly. I'm currently trying out Prakash's solution. – rb3652 Jan 03 '22 at 06:00
  • @hpaulj It says "sp.sin(y)+1: NameError: name 'sp' is not defined" – rb3652 Jan 03 '22 at 06:05
  • I thought the `input` was `sin(y)` when I last commented. I just noticed the `ipykernel` in the traceback. That means you are running this as jupyter-notebook. I wonder if you have unstated imports in an earlier cell. The only `*` import you show is `vpython`; I don't know what that does. If I do a `from sympy import sin`, `lowerProfile` becomes `Eq(sin(y) + 1, 1)` – hpaulj Jan 03 '22 at 07:22
  • @rb3652 is your problem solved? – Prakash Dahal Jan 03 '22 at 07:33
  • @rb3652 check my updated answer and reply there – Prakash Dahal Jan 03 '22 at 07:39
  • 1
    Hi @PrakashDahal -- just accepted your answer. Worked marvelously. – rb3652 Jan 04 '22 at 03:31

2 Answers2

2

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
Prakash Dahal
  • 4,388
  • 2
  • 11
  • 25
  • the traceback points to a previous line, the one that creates `lowerProfile` – hpaulj Jan 03 '22 at 16:05
  • While his question original used `sin(y)`, the current one uses `sin(y)+1` which does not hit this 'complex' value issue. – hpaulj Jan 03 '22 at 19:15
  • 1
    This is really remarkable! Even though this answer is short, it cracked the blackbox mystery: I was trying to "float" a complex value all along! Genius. In the future, before I ask on SO, I think I'll try to peel back the onion layers and see what the native result is and take it from there. Upvoted and accepted your answer. – rb3652 Jan 04 '22 at 03:28
1

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
hpaulj
  • 221,503
  • 14
  • 230
  • 353
  • Wow, this is really in-depth! I'm the creator, and you went even deeper than me -- thank you. And yes, the `sin(y)+1` avoids the problem that `sin(y)` encounters. Wish I could accept two answers, but I still upvoted this one -- thank you for the insights. – rb3652 Jan 04 '22 at 03:29