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.
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.
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
.
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.
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.
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
.