Reputation: 25083
I want to visualize the roots of tan(xi) = tanh(xi), xi>0
and my plot
plot(tan(pi*xi), tanh(pi*xi), (xi, 0, 4), ylim=(-1, 2))
comes out like this
where one sees the actual roots, xi_i \approx pi*(n+1/4), n=1, ...
but also
fake roots at pi*(n+1/2)
, the reason why being sympy
plotting algorithm that draws a vertical line between plus and minus infinity.
I tried to avoid the adaptive sampling and using a low sampling rate to no avail. Other programs, eg gnuplot
, give me a more reasonable plot, at least in view of my concerns, that is...
Eventually my question is, is it possible to avoid those vertical lines in sympy
's plot()
function?
Upvotes: 2
Views: 578
Reputation: 7524
When a curve has singularities, it can plotted by segments, excluding the singularities. A tiny interval around the each singularity is built with math.nextafter(a,b)
which returns a
plus the smallest possible increment for a float, in direction of b
.
from sympy import init_printing, symbols, plot
from sympy import singularities, Interval, tan, pi
from math import nextafter
init_printing()
x = symbols('x')
# The function to plot
y = tan(pi*x)
# Split x range at x singularities
min_x = next_x = 0
max_x = 4 # 4*pi
segments = []
undefs = singularities(y, x, domain=Interval(min_x, max_x))
for u in undefs:
# Add a subrange up to singularity for singularities within x range
if (u >= min_x) and (u <= max_x):
segments.append((x, next_x, nextafter(u, u-1)))
next_x = nextafter(u, u+1)
# Add last segment
if u <= max_x: segments.append((x, next_x, max_x))
# Plot all segments
plots = plot(*[(y, segment) for segment in segments], ylim=(-2,2), show=False)
plots.aspect_ratio = (1,1)
plots.show()
Of course you can use the same color for each curve segment.
Upvotes: 0
Reputation:
Sympy uses matplotlib as a backend for plotting; the root cause is that matplotlib connects the dots even around a singularity. If one plots with numpy, the direct access to y-values being plotted allows one to replace overly large numbers with nan or infinity. If staying within sympy, such tight control over numerics does not appear to be available. The best I could do is to split the range into a list of smaller ranges that do not include singularities, using the knowledge of the specific function tan(pi*x):
import math
from sympy import *
xi = symbols('xi')
xmin = 0
xmax = 4
ranges = [(xi, n-0.499, n+0.499) for n in range(math.ceil(xmin+0.5), math.floor(xmax+0.5))]
ranges.insert(0, (xi, 0, 0.499))
ranges.append((xi, math.floor(xmax+0.5) - 0.499, xmax))
plot((tanh(pi*xi), (xi, xmin, xmax)), *[(tan(pi*xi), ran) for ran in ranges], ylim=(-1, 2))
Output:
Upvotes: 2