Hello,
I am pritty new to Julia and try to find my first steps. I am a bit confused now.
In Python I had this (simplyfied) piece of code:
import numpy as np
a = [[1,2], [3,4]]
M = np.array(a)
np.tanh(M)
It brings me the expected result:
array([[0.76159416, 0.96402758],
[0.99505475, 0.9993293 ]])
Now I try my luck with Julia:
M = [1 2
3 4]
tanh(M)
Output:
2×2 Array{Float64,2}:
-0.0320733 0.472079
0.708118 0.676045
My question, what is calculated here?
However if I do:
tanh.(M)
I get the expected result again. I think I understand concept of “.” as the elementwise operator. But I thought tanh() and similar are always elementwise. If I am wrong, what is tanh(M) without “.” calculating?
Thanks
Floriano
In julia, any operation on a matrix will not be element-wise unless you have a .
. It turns out that for square matrices, you can take a hyperbolic tangent of a matrix. That’s the result you are seeing when you type tanh(M)
.
9 Likes
julia> tanh(1+2im)
1.1667362572409197 - 0.24345820118572525im
julia> tanh([1 -2; 2 1])
2×2 Array{Float64,2}:
1.16674 0.243458
-0.243458 1.16674
4 Likes
As Oscar_Smith said, the component-wise tanh
and (diagonalizable, square) matrix tanh
are different in general. The matrix tanh
is derived from a power series expansion of the scalar tanh, where the scalar powers are replaced with matrix powers. It can be computed by taking the eigenvalue decomposition U*diagm(evals)*U'
of the matrix, taking the component-wise tanh
on the eigenvalues, and then reconstructing:
julia> A = [1 -2; 2 1]
2×2 Array{Int64,2}:
1 -2
2 1
julia> using LinearAlgebra
julia> evals, U = eigen(A)
Eigen{Complex{Float64},Complex{Float64},Array{Complex{Float64},2},Array{Complex{Float64},1}}
eigenvalues:
2-element Array{Complex{Float64},1}:
1.0 - 2.0000000000000004im
1.0 + 2.0000000000000004im
eigenvectors:
2×2 Array{Complex{Float64},2}:
0.0+0.707107im 0.0-0.707107im
-0.707107-0.0im -0.707107+0.0im
julia> U*diagm(evals)*U'-A
2×2 Array{Complex{Float64},2}:
-2.22045e-16+0.0im -4.44089e-16+0.0im
4.44089e-16+0.0im 2.22045e-16+0.0im
julia> U*diagm(tanh.(evals))*U'-tanh(A)
2×2 Array{Complex{Float64},2}:
-4.44089e-16+0.0im 2.22045e-16+0.0im
-2.498e-16+0.0im 0.0+0.0im
julia> tanh.(A)-tanh(A)
2×2 Array{Float64,2}:
-0.405142 -1.20749
1.20749 -0.405142
The last line gives an example where the component-wise and matrix versions of tanh
differ.
1 Like
Provided that the matrix is diagonalizable, of course.
1 Like
Yes, thanks for reminding us that the power series derivation of tanh(A)
requires diagonalizability of A
. But my demo used the decomposition A=U*diagm(evals)*U'
, so we need to further assume that A
is unitarily diagonalizable for that to work, i.e., the transpose U'
is also the inverse of U
, which occurs when A
is normal (A*A' == A'*A
). It’s sufficient that A
is (real) symmetric for normality to hold.