Ask Your Question

Inconsistent/incorrect value of limit involving tan and tanh

asked 2022-11-09 17:37:56 +0100

score gravatar image

updated 2022-11-27 10:50:12 +0100

tmonteil gravatar image

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/, 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/, 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/, in parse_moutput_from_json(page_data, verbose)
   1282 queryresult = page_data['queryresult']
   1283 if not queryresult['success']:
-> 1284     raise ValueError('asking was not successful')
   1285 if 'pods' not in queryresult:
   1286     raise ValueError('json object contains no pods')

ValueError: asking 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 flag offensive close merge delete



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})

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 ?

Emmanuel Charpentier gravatar imageEmmanuel Charpentier ( 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...

Emmanuel Charpentier gravatar imageEmmanuel Charpentier ( 2022-11-12 11:27:38 +0100 )edit

2 Answers

Sort by ยป oldest newest most voted

answered 2022-11-15 00:07:37 +0100

Juanjo gravatar image

updated 2022-11-15 00:21:40 +0100

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)                                         

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} $$

edit flag offensive delete link more



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)

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

Emmanuel Charpentier gravatar imageEmmanuel Charpentier ( 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)

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)

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)
Emmanuel Charpentier gravatar imageEmmanuel Charpentier ( 2022-11-16 23:07:13 +0100 )edit

answered 2022-11-18 20:48:27 +0100

Emmanuel Charpentier gravatar image

updated 2022-11-19 23:33:11 +0100

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.


EDIT : The problem of numerical computation is reformulated here.

edit flag offensive delete link more

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

1 follower


Asked: 2022-11-09 17:37:56 +0100

Seen: 98 times

Last updated: Nov 19