# Inconsistent/incorrect value of limit involving tan and tanh

Out of sheer curiosity I tried to compute $\lim_{x \rightarrow \infty} \frac{d}{dx} \log \tan (\frac \pi 2 \tanh x)$, and found that the maxima engine disagrees on how to take the limit. I am not particularly great at math but I thought it would be interesting to "compare" the horizontal and vertical asymptotes of tanh and tan like this.

It returns something different for the original expression within the limit versus simplify_full, reduce_trig, and exponentialize. Other algorithms variously either fail or return 2, but maxima will confidently report 0 if the expression is not simplified. Presumably it is tempted by the division-by-infinity and multiplication-by-zero, but isn't that the point of taking limits?

sage: fun = diff(log(tan(pi/2 * tanh(x))), x)
sage: simplifications = [lambda x: x, attrcall('simplify_full'), attrcall('reduce_trig'), attrcall('exponentialize')]
sage: algorithms = ['maxima', 'giac', 'fricas', 'mathematica_free']
sage: for simplification in simplifications:
....:     simplified = simplification(fun)
....:     print("Simplified:", simplified)
....:     for algorithm in algorithms:
....:         print(algorithm, limit(simplified, x=infinity, algorithm=algorithm))
....:     print()
....:
Simplified: -1/2*pi*(tan(1/2*pi*tanh(x))^2 + 1)*(tanh(x)^2 - 1)/tan(1/2*pi*tanh(x))
maxima 0
giac 2
fricas failed
mathematica_free 2

Simplified: 1/2*pi/(cos(1/2*pi*sinh(x)/cosh(x))*cosh(x)^2*sin(1/2*pi*sinh(x)/cosh(x)))
maxima 1/2*pi*limit(1/(cos(1/2*pi*sinh(x)/cosh(x))*cosh(x)^2*sin(1/2*pi*sinh(x)/cosh(x))), x, +Infinity)
giac 2
fricas failed
mathematica_free 2

Simplified: 1/2*pi*cot(1/2*pi*tanh(x))*sec(1/2*pi*tanh(x))^2*sech(x)^2
maxima und
giac 2
fricas failed
mathematica_free 2

Simplified: -1/2*pi*((e^(-x) - e^x)^2/(e^(-x) + e^x)^2 - 1)*((I*e^(1/2*I*pi*e^(-x)/(e^(-x) + e^x) - 1/2*I*pi*e^x/(e^(-x) + e^x)) - I*e^(-1/2*I*pi*e^(-x)/(e^(-x) + e^x) + 1/2*I*pi*e^x/(e^(-x) + e^x)))^2/(e^(1/2*I*pi*e^(-x)/(e^(-x) + e^x) - 1/2*I*pi*e^x/(e^(-x) + e^x)) + e^(-1/2*I*pi*e^(-x)/(e^(-x) + e^x) + 1/2*I*pi*e^x/(e^(-x) + e^x)))^2 + 1)*(e^(1/2*I*pi*e^(-x)/(e^(-x) + e^x) - 1/2*I*pi*e^x/(e^(-x) + e^x)) + e^(-1/2*I*pi*e^(-x)/(e^(-x) + e^x) + 1/2*I*pi*e^x/(e^(-x) + e^x)))/(I*e^(1/2*I*pi*e^(-x)/(e^(-x) + e^x) - 1/2*I*pi*e^x/(e^(-x) + e^x)) - I*e^(-1/2*I*pi*e^(-x)/(e^(-x) + e^x) + 1/2*I*pi*e^x/(e^(-x) + e^x)))
maxima 2
giac 2
fricas failed
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In [105], line 5
3 print("Simplified:", simplified)
4 for algorithm in algorithms:
----> 5     print(algorithm, limit(simplified, x=infinity, algorithm=algorithm))
6 print()

File /usr/lib/python3.10/site-packages/sage/calculus/calculus.py:1461, in limit(ex, dir, taylor, algorithm, **argv)
1459         l = libgiac.limit(ex, v, a, -1).sage()
1460 elif algorithm == 'mathematica_free':
-> 1461     return mma_free_limit(ex, v, a, dir)
1462 else:
1463     raise ValueError("Unknown algorithm: %s" % algorithm)

File /usr/lib/python3.10/site-packages/sage/calculus/calculus.py:1510, in mma_free_limit(expression, v, a, dir)
1508     raise ValueError('wrong input for limit')
1509 json_page_data = request_wolfram_alpha(input)
-> 1510 all_outputs = parse_moutput_from_json(json_page_data)
1511 if not all_outputs:
1512     raise ValueError("no outputs found in the answer from Wolfram Alpha")

File /usr/lib/python3.10/site-packages/sage/interfaces/mathematica.py:1284, in parse_moutput_from_json(page_data, verbose)
1282 queryresult = page_data['queryresult']
1283 if not queryresult['success']:
-> 1284     raise ValueError('asking wolframalpha.com was not successful')
1285 if 'pods' not in queryresult:
1286     raise ValueError('json object contains no pods')

ValueError: asking wolframalpha.com was not successful


The sympy algorithm was excluded from the above because it seems to be much worse behaved than the others. For the same suite of simplifications above, it respectively returns [hangs forever], 2, "conversion to SageMath is not implemented", "maximum recursion depth exceeded".

Is the maxima behaviour here a bug, and if so, where should I report it? (And if anyone off the top of their head knows any interesting identities around this expression, that would be cool too ๐)

edit retag close merge delete

Interesting...

Trying to plot this function from, say, 0 to 30 shows decreasing values quickly close to 2, then numerical instability between about 16 to about 22, oscillating between 0 and 2, then stays at 0.This is also true fopr the various simplifications you tried. Interestingly :

sage: mathematica.FullSimplify(f(x))._sage_(locals={("Csc", 1):csc, ("Tanh", 1):tanh, ("Sech", 1):sech, ("Sec", 1):sec})
1/2*pi*csc(1/2*pi*tanh(x))*sec(1/2*pi*tanh(x))*sech(x)^2


Firing up Mathematica shows the same numerically unstable behaviour, even after various transformations.

My guess is that function deserves more advanced approaches. Is there an analyst in the room ?

( 2022-11-11 23:12:25 +0100 )edit

A bt more exploration forcng highre precision shows that the (default) algorithms for computing graphical representations are misleading for x superior to about 16. This is probably the result of an ill-conditioned problem, qhere the sought values of very large values of opposed sign.

This numerical exploration hints that $\lim_{x\rightarrow\infty}f(x)=2$ is probably right. Proving it "by hand" with my rusty calculus isn't easy...

( 2022-11-12 11:27:38 +0100 )edit

Sort by ยป oldest newest most voted

Let $$f(x)=\log(\tan g(x)),$$ with $$g(x)=\frac{\pi}{2}\tanh x.$$ It can be seen that $f$ is a differentiable function in $(0,+\infty)$, so it makes sense to try to compute $$\lim_{x\to+\infty}f'(x).$$ To this end, we first obtain $f'$ by applying the chain rule: $$f'(x)=\frac{1}{\tan g(x)} \frac{1}{\cos^2 g(x)}g'(x),$$ where $$g'(x)=\frac{\pi}{2}\frac{1}{\cosh^2 x}.$$ By using some trigonometric relations, we get $$\tan g(x)\,\cos^2 g(x)=\sin g(x)\cos g(x)=\frac{1}{2}\sin 2g(x).$$ Hence, $$f'(x)=\frac{2g'(x)}{\sin2g(x)}.$$ SageMath can handle the limit of the above expression of $f'$:

sage: g(x) = (pi/2) * tanh(x)
sage: gp = g.derivative()
sage: limit(2*gp(x)/sin(2*g(x)), x=+oo)
2


Consequently, $$\lim_{x\to+\infty}f'(x)=2,$$ as shown in part of the results reported in the opening post or conjectured by @Emmanuel Charpentier in his comment.

It is not hard to finish the computation by hand. Since $$\lim_{x\to+\infty}2g(x)=\pi \lim_{x\to+\infty}\tanh x=\pi,$$ we have the following equivalence as $x\to+\infty$: $$\sin 2g(x)=\sin(\pi-2g(x))=\sin\pi(1-\tanh x)\sim \pi(1-\tanh x).$$ Therefore, \begin{aligned} \lim_{x\to+\infty}f'(x) &=\lim_{x\to+\infty}\frac{2g'(x)}{\sin2g(x)} \\ &=\lim_{x\to+\infty}\frac{\pi/\cosh^2x}{\pi(1-\tanh x)} \\ &=\lim_{x\to+\infty}\frac{1}{(\cosh x-\sinh x)\cosh x} \\ &=\lim_{x\to+\infty}\frac{2}{e^{-x}(e^x+e^{-x})} \\ &=\lim_{x\to+\infty}\frac{2}{1+e^{-2x}} =2. \end{aligned}

more

1

Clap, clap, clap, clap, clap....

The following abomination gives a one-liner for this computation (I note F(x) the antiderivative of f(x)) :

sage: g=function("g")
sage: F(x).subs(F(x).operands()[0].operands()[0]==g(x)).diff(x).trig_reduce().trig_simplify().trig_reduce().subs(csc(w0)==1/sin(w0))._sympy_().subs(g(x)._sympy_(), (pi/2*tanh(x))._sympy_())._sage_().limit(x=oo)
2


One notes that the fact that Sage's subs method does not dives in subexpressions makes us use Sympy's method, which does...

One also notes, not for the first time, the need for the sequence ...trig_reduce().trig_simplify().trig_reduce()...

( 2022-11-15 19:47:20 +0100 )edit

A slightly shorter one-liner:

sage: F(x).subs(pi/2*tanh(x)==g(x)).diff(x).trig_reduce().trig_simplify().trig_reduce().substitute_function(g,(pi/2*tanh(x).function(x))).limit(x=oo)
2


An even shorter one, which dispenses with isolating pi/2*tanh(x) :

sage: F(x).diff(x).trig_reduce().trig_simplify().trig_reduce().limit(x=oo)
2


The key seems to be the .trig_reduce().trig_simplify().trig_reduce() sequence... Alternative :

sage: F(x).diff(x)._sympy_().simplify()._sage_().limit(x=oo)
2

( 2022-11-16 23:07:13 +0100 )edit

As proved by @Juanjo , the correct response can be obtained by transforming the original form of f(x) in f(x).trig_reduce().trig_simplify().trig_reduce(). This is now Trac#34757

A different but similar problem with Maxima has been identified and reported.

The fact that algorithm="sympy"never returns has been explored, shown to be a genuine Sympy problem and reported.

HTH,

EDIT : The problem of numerical computation is reformulated here.

more