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).
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
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.
Provided that the matrix is diagonalizable, of course.
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.