# Hyperbolic tangents (of matrix) numerically inaccurate?

**URL:** <https://discourse.julialang.org/t/hyperbolic-tangents-of-matrix-numerically-inaccurate/63558>\
**Category:** General Usage\
**Created:** [June 25, 2021, 3:25pm UTC](https://discourse.julialang.org/t/hyperbolic-tangents-of-matrix-numerically-inaccurate/63558 "2021-06-25T15:25:51Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![judober](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/judober/32/6676_2.png) [@judober](https://discourse.julialang.org/u/judober)\
**Post date:** [June 25, 2021, 3:25pm UTC](https://discourse.julialang.org/t/hyperbolic-tangents-of-matrix-numerically-inaccurate/63558/1 "2021-06-25T15:25:51Z")

</div>

Hello everyone,

I need to calculate the tanh of a complex square matrix. The function needs to work for different matrixes and the numbers can get relative big (well, values around 20 don’t seem big but apparently they are big enough to cause trouble). E.g.

```julia
C = ComplexF64[20.532918596935836 + 13.825923408965787im 18.707372202499215 - 13.100775315493328im; 21.669638013670312 - 15.16565944942833im 19.776899867610187 + 14.368130886743469im]

```

The standard version gives me an error `ERROR: SingularException(2)`.

Looking at the [https://github.com/JuliaLang/julia/blob/ed4c44fbafbbace523ebdc9440bf1f542157048a/stdlib/LinearAlgebra/src/dense.jl#L1057](https://Implementation) I figured this is essentially

```julia
tanh2(x) = (exp(x) - exp(-x)) / (exp(x) + exp(-x))

```

which results in the same error.  
This lead me to two alternatives:

```julia
tanh3(x) = (exp(2x) - I) / (exp(2x) + I)
tanh4(x) = (I - exp(-2x)) / (I + exp(-2x))

```

which work both but give (as expected) very different results for C.  
I expected Version 4 to be right in this case as C has a quite big positive real part. For the verification, I used

```julia
tanhdf(x) = tanh(ComplexDF64.(x))

```

with [DoubleFloats](https://github.com/JuliaMath/DoubleFloats.jl) (and GenericLinearAlgebra that seems to be a missing dependency in the latest version) for verification and got similar results.

My questions:

- Has someone already implemented a better version of tanh somewhere that has higher accuracy so that I can simply use this version?
- I’m not sure of when to choose which version (tanh2,3,4). I would have chosen based on the common factor of real(C) (In this case approx. 20) bigger some threshhold → 4, negative threshold → 3 original version otherwise.

Thanks

---

<div class="post-metadata">

**Author:** ![mstewart](https://avatars.discourse-cdn.com/v4/letter/m/b5a626/32.png) [@mstewart](https://discourse.julialang.org/u/mstewart)\
**Post date:** [June 26, 2021, 10:38am UTC](https://discourse.julialang.org/t/hyperbolic-tangents-of-matrix-numerically-inaccurate/63558/2 "2021-06-26T10:38:47Z")

</div>

I don’t think this is an entirely solved problem in which you can expect to find existing code that is flawlessly reliable in all cases. With that said, using the naive exponential formulas directly to produce intermediate results with large norm does look like it’s asking for trouble. If I needed to compute this, I would try one of two things:

- You might consider a Schur-Parlett approach. There is a Julia implementation with blocking at [Funm.jl](https://github.com/YingboMa/Funm.jl).

- A quick web search turned up this [paper](https://www.mdpi.com/2227-7390/9/11/1219). One of the considered methods is to scale the matrix first, compute the exponential for a scaled matrix with relatively small norm (which should be easy) and then apply some identities to recover the hyperbolic tangent of the original matrix. It is analogous to scaling and squaring for the matrix exponential, which is a standard approach, but it also leverages existing code for computing matrix exponentials. If you want to stick to using exponentials, it would probably improve things. (The authors do report better results with scaling and direct Taylor approximation of the hyperbolic tangent, however. But that looks like a little more work to try.)

---

<div class="post-metadata">

**Author:** ![judober](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/judober/32/6676_2.png) [@judober](https://discourse.julialang.org/u/judober)\
**Post date:** [June 28, 2021, 8:18am UTC](https://discourse.julialang.org/t/hyperbolic-tangents-of-matrix-numerically-inaccurate/63558/3 "2021-06-28T08:18:02Z")

</div>

Thanks for your input.  
The scaling and squaring looks interesting, I will have a look into this.

---

<div class="post-metadata">

**Author:** ![mstewart](https://avatars.discourse-cdn.com/v4/letter/m/b5a626/32.png) [@mstewart](https://discourse.julialang.org/u/mstewart)\
**Post date:** [June 28, 2021, 10:56am UTC](https://discourse.julialang.org/t/hyperbolic-tangents-of-matrix-numerically-inaccurate/63558/4 "2021-06-28T10:56:37Z")

</div>

That sounds like the way to go. I didn’t look carefully enough at the Schur-Parlett code, which looks like a stalled work in progress implementation of the algorithm from this [paper](https://epubs.siam.org/doi/abs/10.1137/S0895479802410815). It did look fairly complete, but the author didn’t release it and I didn’t see a license or tests. That does seem to be a gap in Julia support for numerical linear algebra. That algorithm should probably be the default choice for matrix functions of non-normal matrices for functions for which there are not well established alternative algorithms.
