sympy division by zero error when computing laplacian

Question:

I’m using sympy to get laplacian of a function f(x,y)=r^(2/3)(1-x^2)(1-y^2)sin(2/3*theta), where r and theta are polar coordinates of x,y with 0<theta<2*pi. The function is smooth enough around (-1,0).

x,y = sympy.symbols('x y')
r = sympy.sqrt(x**2+y**2)
A1 = sympy.acos(x/r)
A2 = 2*sympy.pi-sympy.acos(x/r)  
theta = sympy.Piecewise((A1,y>=0),(A2,y<0)) #compute polar coordinate with range [0,2pi)

expr = r**(2/3)*(1-x**2)*(1-y**2)*sympy.sin((2/3)*theta)
uxx = sympy.diff(expr,x,x)
uyy = sympy.diff(expr,y,y)
lapl=-(uxx+uyy)
fxy = sympy.lambdify([x,y],lapl,'math')
u_ = sympy.lambdify([x,y],expr,'math')

I want to compute fxy(-1,0)but yields ‘division by 0’ error:

Observe that

print(fxy(-1,-0.01))
print(fxy(-1,-0.001))
print(fxy(-1,-0.0001))
print(fxy(-1,-0.00001))
print(fxy(-1,-0.000001))
print(fxy(-1,-0.0000001)) 

yields

4.038793489121227
4.0412252999447835
4.04142961848259
4.041449661668847
4.04145166209092
4.041451863851409

which definitely is converging, but print(fxy(-1,-0.00000001)) yields:

    ZeroDivisionError                         Traceback (most recent call last)
~AppDataLocalTemp/ipykernel_40368/1586132126.py in <module>
----> 1 print(fxy(-1,-0.00000001))

<lambdifygenerated-5> in _lambdifygenerated(x, y)
    1 def _lambdifygenerated(x, y):
----> 2     return -(x**2 - 1)*(2.66666666666667*y**2*(x**2 + y**2)**
...(very long formula omitted)
ZeroDivisionError: float division by zero

This makes me very confused.

Asked By: ssjk667

||

Answers:

The very long division is this:

----> 2     return -(x**2 - 1)*(2.66666666666667*y**2*(x**2 + y**2)**(-0.666666666666667)*sin(0.666666666666667*((acos(x/sqrt(x**2 + y**2))) if (y >= 0) else (-acos(x/sqrt(x**2 + y**2)) + 2*pi))) + 0.888888888888889*y*(x**2 + y**2)**(-0.666666666666667)*(y**2 - 1)*((x*y/((x**2 + y**2)**(3/2)*sqrt(-x**2/(x**2 + y**2) + 1))) if (y >= 0) else (-x*y/((x**2 + y**2)**(3/2)*sqrt(-x**2/(x**2 + y**2) + 1))))*cos(0.666666666666667*((acos(x/sqrt(x**2 + y**2))) if (y >= 0) else (-acos(x/sqrt(x**2 + y**2)) + 2*pi))) + 2.66666666666667*y*(x**2 + y**2)**0.333333333333333*((x*y/((x**2 + y**2)**(3/2)*sqrt(-x**2/(x**2 + y**2) + 1))) if (y >= 0) else (-x*y/((x**2 + y**2)**(3/2)*sqrt(-x**2/(x**2 + y**2) + 1))))*cos(0.666666666666667*((acos(x/sqrt(x**2 + y**2))) if (y >= 0) else (-acos(x/sqrt(x**2 + y**2)) + 2*pi))) + (x**2 + y**2)**0.333333333333333*(y**2 - 1)*(0.444444444444444*x**2*y**2*sin(0.666666666666667*((acos(x/sqrt(x**2 + y**2))) if (y >= 0) else (-acos(x/sqrt(x**2 + y**2)) + 2*pi)))/((x**2 + y**2)**3*(x**2/(x**2 + y**2) - 1)) + 0.666666666666667*((x*(x**2*y**2/((x**2 + y**2)**2*(x**2/(x**2 + y**2) - 1)) - 3*y**2/(x**2 + y**2) + 1)/((x**2 + y**2)**(3/2)*sqrt(-x**2/(x**2 + y**2) + 1))) if (y >= 0) else (-x*(x**2*y**2/((x**2 + y**2)**2*(x**2/(x**2 + y**2) - 1)) - 3*y**2/(x**2 + y**2) + 1)/((x**2 + y**2)**(3/2)*sqrt(-x**2/(x**2 + y**2) + 1))))*cos(0.666666666666667*((acos(x/sqrt(x**2 + y**2))) if (y >= 0) else (-acos(x/sqrt(x**2 + y**2)) + 2*pi)))) + 2*(x**2 + y**2)**0.333333333333333*sin(0.666666666666667*((acos(x/sqrt(x**2 + y**2))) if (y >= 0) else (-acos(x/sqrt(x**2 + y**2)) + 2*pi))) - (y**2 - 1)*(0.888888888888889*y**2*(x**2 + y**2)**(-1.66666666666667) - 0.666666666666667*(x**2 + y**2)**(-0.666666666666667))*sin(0.666666666666667*((acos(x/sqrt(x**2 + y**2))) if (y >= 0) else (-acos(x/sqrt(x**2 + y**2)) + 2*pi)))) - (y**2 - 1)*(2.66666666666667*x**2*(x**2 + y**2)**(-0.666666666666667)*sin(0.666666666666667*((acos(x/sqrt(x**2 + y**2))) if (y >= 0) else (-acos(x/sqrt(x**2 + y**2)) + 2*pi))) + 0.888888888888889*x*(x**2 - 1)*(x**2 + y**2)**(-0.666666666666667)*(((x**2/(x**2 + y**2) - 1)/(sqrt(x**2 + y**2)*sqrt(-x**2/(x**2 + y**2) + 1))) if (y >= 0) else (-(x**2/(x**2 + y**2) - 1)/(sqrt(x**2 + y**2)*sqrt(-x**2/(x**2 + y**2) + 1))))*cos(0.666666666666667*((acos(x/sqrt(x**2 + y**2))) if (y >= 0) else (-acos(x/sqrt(x**2 + y**2)) + 2*pi))) + 2.66666666666667*x*(x**2 + y**2)**0.333333333333333*(((x**2/(x**2 + y**2) - 1)/(sqrt(x**2 + y**2)*sqrt(-x**2/(x**2 + y**2) + 1))) if (y >= 0) else (-(x**2/(x**2 + y**2) - 1)/(sqrt(x**2 + y**2)*sqrt(-x**2/(x**2 + y**2) + 1))))*cos(0.666666666666667*((acos(x/sqrt(x**2 + y**2))) if (y >= 0) else (-acos(x/sqrt(x**2 + y**2)) + 2*pi))) + (x**2 - 1)*(x**2 + y**2)**0.333333333333333*(0.666666666666667*((-2*x*(x**2/(x**2 + y**2) - 1)/((x**2 + y**2)**(3/2)*sqrt(-x**2/(x**2 + y**2) + 1))) if (y >= 0) else (2*x*(x**2/(x**2 + y**2) - 1)/((x**2 + y**2)**(3/2)*sqrt(-x**2/(x**2 + y**2) + 1))))*cos(0.666666666666667*((acos(x/sqrt(x**2 + y**2))) if (y >= 0) else (-acos(x/sqrt(x**2 + y**2)) + 2*pi))) + 0.444444444444444*(x**2/(x**2 + y**2) - 1)*sin(0.666666666666667*((acos(x/sqrt(x**2 + y**2))) if (y >= 0) else (-acos(x/sqrt(x**2 + y**2)) + 2*pi)))/(x**2 + y**2)) - (x**2 - 1)*(0.888888888888889*x**2*(x**2 + y**2)**(-1.66666666666667) - 0.666666666666667*(x**2 + y**2)**(-0.666666666666667))*sin(0.666666666666667*((acos(x/sqrt(x**2 + y**2))) if (y >= 0) else (-acos(x/sqrt(x**2 + y**2)) + 2*pi))) + 2*(x**2 + y**2)**0.333333333333333*sin(0.666666666666667*((acos(x/sqrt(x**2 + y**2))) if (y >= 0) else (-acos(x/sqrt(x**2 + y**2)) + 2*pi))))

This is huge and contains a number of divisions, at least one of which, wouldhave the issues pointed out in the comments. ie. suspiciously small number that is zero in the denominator.

Answered By: D.L

You can extract the powers from this expression when x is equal to -1 like this:

In [17]: lapl.subs(x, -1).atoms(Pow)
Out[17]: 
⎧                                       -0.666666666666667                       0.333333333333333⎫
⎪ 2          1            1     ⎛ 2    ⎞                         1       ⎛ 2    ⎞                 ⎪
⎪y , ─────────────────, ──────, ⎝y  + 1⎠                  , ───────────, ⎝y  + 1⎠                 ⎪
⎪         ____________   2                                     ________                           ⎪
⎨        ╱       1      y  + 1                                ╱  2                                ⎬
⎪       ╱  1 - ──────                                       ╲╱  y  + 1                            ⎪
⎪      ╱        2                                                                                 ⎪
⎪    ╲╱        y  + 1                                                                             ⎪
⎩                                                                                                 ⎭

Looking at the second of these you can see how you get a zero in the denominator:

In [18]: y = -0.00000001

In [19]: 1 - (1 / (y**2 + 1))
Out[19]: 0.0

That’s because y**2 is too small to be added to 1 in ordinary floating point. Note that SymPy can calculate this if you use exact numbers and don’t use lambdify:

In [30]: lapl.subs({x:-1, y:Rational('-0.00000001')}).n()
Out[30]: 4.04145188210516

You can also rearrange the formula to make it more suitable for this case:

lapl.replace(Pow, lambda b, e: ratsimp(b)**e)

That should make it able to handle small floating point values of y.

Answered By: Oscar Benjamin
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.