Tableaux et matrices#

La définition de tableaux i.e. vecteurs, matrices, hypermatrices est un élément essentiel de Julia.

Julia ne possède qu’un seul type de tableau : Array on peut définir son nombre d’entrées (1 entrée= 1 dimension …) et son contenu de façon assez générale. Un tableau peut contenir des matrices à chaque élément.

Une particularité est que les indices de tableaux commencent à 1, et l’accès aux éléments se fera à l’aide de [ ] et non ( ) qui est réservé aux fonctions.

Avant de rentrer dans la construction et manipulation de tableau regardons une autre classe

Iterateur#

Julia possède un Type particulier fait à l’aide du “:”

a = 1:5
1:5
a .+ 1
2:6
typeof(a)
UnitRange{Int64}
b = 0:0.5:2
0.0:0.5:2.0
typeof(b)
StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}

Ce type “formel” permet d’avoir une définition et une méthode associée sans stocker l’ensemble des valeurs. Attention celui-ci peut être vide :

d=1:0 # itérateur formel mais correspond à un ensemble vide de valeurs
1:0
collect(1:10)
10-element Vector{Int64}:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
d  = 1:10  |> collect |> sum
55

Tableau#

On vient de voir que l’on peut transformer l’itérateur précédent en tableau à l’aide de la commande “collect”

a = 1:5
aa = collect(complex.(a)) .+ 1im
5-element Vector{Complex{Int64}}:
 1 + 1im
 2 + 1im
 3 + 1im
 4 + 1im
 5 + 1im
collect(aa')
1×5 Matrix{Complex{Int64}}:
 1-1im  2-1im  3-1im  4-1im  5-1im
transpose(aa)
1×5 transpose(::Vector{Complex{Int64}}) with eltype Complex{Int64}:
 1+1im  2+1im  3+1im  4+1im  5+1im
a = collect(1:5)
5-element Vector{Int64}:
 1
 2
 3
 4
 5
a' * a
55
typeof(aa)
Vector{Complex{Int64}} (alias for Array{Complex{Int64}, 1})

La réponse est de la forme Array{Type,dim} un tableau de Type à dim entrées (1 pour vecteur, 2 pour matrices …)

A remarquer :

  • l’indexation des tableaux commence à 1.

  • un tableau à une entrée est vu comme un vecteur colonne par défaut.

  • le crochet [ ] sert à extraire ou affecter une valeur ou un bloc de valeur. Attention le crochet [ ] sert également de “concatenateur” et constructeur de tableau (voir suite)

  • Il est possible de faire des tableaux de n’importe quoi (fonction, tableau, …).

aa[1]
1 + 1im
aa[begin] # pour accèder au premier élément
1 + 1im
aa[end] # pour accèder au dernier élément
5 + 1im
first(a), last(a)
(1, 5)
aa[end-2:end] .= 1 ; println(aa)
Complex{Int64}[1 + 1im, 2 + 1im, 1 + 0im, 1 + 0im, 1 + 0im]

Les crochets permettent la construction explicite de tableaux (ou leur concaténation)

A = [1 2 ; 3 4] # {espace} = séparateur de colonne
2×2 Matrix{Int64}:
 1  2
 3  4
AA=[A  A] # concaténation par bloc
2×4 Matrix{Int64}:
 1  2  1  2
 3  4  3  4
hcat(A, A) # commande équivalent à la précédente [A  A]
2×4 Matrix{Int64}:
 1  2  1  2
 3  4  3  4
AA = [A ; A]
4×2 Matrix{Int64}:
 1  2
 3  4
 1  2
 3  4
vcat(A,A) # commande équivalent à la précédente [A  ; A]
4×2 Matrix{Int64}:
 1  2
 3  4
 1  2
 3  4

On peut accéder à tout ou partie d’un tableau à l’aide de 2 indices

A[2,1]
3
A[2,:]
2-element Vector{Int64}:
 3
 4
A[:,2]
2-element Vector{Int64}:
 2
 4
A[end,end]
4
AA
4×2 Matrix{Int64}:
 1  2
 3  4
 1  2
 3  4
sum(AA, dims=2)
4×1 Matrix{Int64}:
 3
 7
 3
 7
B = [1 2 3 4]
1×4 Matrix{Int64}:
 1  2  3  4
B = [1;2;3;4]
4-element Vector{Int64}:
 1
 2
 3
 4
for i in eachindex(AA)
    println(i)
end
1
2
3
4
5
6
7
8
for row  eachrow(AA)
    println("$row")
end
[1, 2]
[3, 4]
[1, 2]
[3, 4]

A noter que l’on peut faire des tableaux de tout type voir de les mélanger (Any)

a=["un";"deux"]
2-element Vector{String}:
 "un"
 "deux"
b=[1>2,true,false]
3-element Vector{Bool}:
 0
 1
 0
c=["un"; 2 ; true]
3-element Vector{Any}:
     "un"
    2
 true

Le crochet [ ] permet également la construction rapide de matrice ou tableau comme le montre l’exemple ci-dessous pour construire une matrice de VanderMonde

\[ V_{i,j}=x_i^{j-1}\]
x = 0:0.2:1
V = [ x[i]^(j-1) for i=1:6, j=1:6] # ligne et colonne
6×6 Matrix{Float64}:
 1.0  0.0  0.0   0.0    0.0     0.0
 1.0  0.2  0.04  0.008  0.0016  0.00032
 1.0  0.4  0.16  0.064  0.0256  0.01024
 1.0  0.6  0.36  0.216  0.1296  0.07776
 1.0  0.8  0.64  0.512  0.4096  0.32768
 1.0  1.0  1.0   1.0    1.0     1.0
V = zeros(Float64, 6,6)
for j in 1:6, i in 1:6
    V[i,j] = x[i]^(j-1)
end
V
6×6 Matrix{Float64}:
 1.0  0.0  0.0   0.0    0.0     0.0
 1.0  0.2  0.04  0.008  0.0016  0.00032
 1.0  0.4  0.16  0.064  0.0256  0.01024
 1.0  0.6  0.36  0.216  0.1296  0.07776
 1.0  0.8  0.64  0.512  0.4096  0.32768
 1.0  1.0  1.0   1.0    1.0     1.0
D=[ u*v for u=1:0.5:3, v=1:0.5:4]
5×7 Matrix{Float64}:
 1.0  1.5   2.0  2.5   3.0   3.5    4.0
 1.5  2.25  3.0  3.75  4.5   5.25   6.0
 2.0  3.0   4.0  5.0   6.0   7.0    8.0
 2.5  3.75  5.0  6.25  7.5   8.75  10.0
 3.0  4.5   6.0  7.5   9.0  10.5   12.0

On peux évidemment faire des tableaux à 3,4… entrèes.

Manipulation de Tableau#

push!#

Le fonction push permet d’ajouter à un tableau une valeur supplémentaire

a = Int[]
push!(a,1)     # => [1]
push!(a,2)     # => [1,2]
push!(a,4)     # => [1,2,4]
push!(a,6)     # => [1,2,4,6]
4-element Vector{Int64}:
 1
 2
 4
 6

append!#

Cette fonction permet de mettre bout à bout 2 tableaux

append!(a,a)
8-element Vector{Int64}:
 1
 2
 4
 6
 1
 2
 4
 6

A noté le Array{Any,1} !

a = ComplexF64[]
push!(a,1)     # => [1]
push!(a,2)     # => [1,2]
push!(a,4)     # => [1,2,4]
push!(a,6)  
4-element Vector{ComplexF64}:
 1.0 + 0.0im
 2.0 + 0.0im
 4.0 + 0.0im
 6.0 + 0.0im
a = zeros(0)
Float64[]
a = zeros(Int32,1)
1-element Vector{Int32}:
 0
ones(3)
3-element Vector{Float64}:
 1.0
 1.0
 1.0
fill(5, (6,6))
6×6 Matrix{Int64}:
 5  5  5  5  5  5
 5  5  5  5  5  5
 5  5  5  5  5  5
 5  5  5  5  5  5
 5  5  5  5  5  5
 5  5  5  5  5  5

Attention sur la conversion de type !#

a = collect(1:5)
5-element Vector{Int64}:
 1
 2
 3
 4
 5
a = fill(0.,3,2)
3×2 Matrix{Float64}:
 0.0  0.0
 0.0  0.0
 0.0  0.0
a[2] = sqrt(2)
1.4142135623730951
a = map( x -> 2x+1 , a )
3×2 Matrix{Float64}:
 1.0      1.0
 3.82843  1.0
 1.0      1.0
a = ComplexF64.(a)
3×2 Matrix{ComplexF64}:
     1.0+0.0im  1.0+0.0im
 3.82843+0.0im  1.0+0.0im
     1.0+0.0im  1.0+0.0im

Algèbre linéaire#

On retrouve beaucoup (toutes) de fonctions usuelles de l’algèbre linéaire

using LinearAlgebra
A = SymTridiagonal(rand(5), rand(4))
5×5 SymTridiagonal{Float64, Vector{Float64}}:
 0.847034  0.307827    ⋅          ⋅         ⋅ 
 0.307827  0.898      0.0554185   ⋅         ⋅ 
  ⋅        0.0554185  0.825485   0.063353   ⋅ 
  ⋅         ⋅         0.063353   0.763637  0.134202
  ⋅         ⋅          ⋅         0.134202  0.927218
det(A)
0.3750274106551066
tr(A)
4.261373765642171
eigvals(A)
5-element Vector{Float64}:
 0.5577547428227645
 0.6695570133399208
 0.839919198728379
 1.007956036262465
 1.1861867744886425
b = collect(1:5); #résolution du système Ax=b
x = A \ b 
5-element Vector{Float64}:
 0.5058104234190263
 1.8567601832194116
 3.1925664157211275
 4.130619194095755
 4.794623364454379

Fonctions scientifiques et opérations#

L’usage des fonction scientifiques se fait termes à termes pour l’ensemble des valeurs du tableau (sauf pour les fonctions matricielles comme exp, log …). L’usage des opérations +,-,*,^,/ et \(résolution) est disponible à condition de respecter les contraintes de dimension (multiplication matricielle par exemple). Sont ajouté des opérations termes à termes .*,.^,./ et . toujours avec une contrainte de dimensions compatibles.

A=[1 2;3 4]
exp(A)
2×2 Matrix{Float64}:
  51.969   74.7366
 112.105  164.074
exp.(A) # exponentielle matricielle
2×2 Matrix{Float64}:
  2.71828   7.38906
 20.0855   54.5982

De plus les tableaux possèdes des opérations de multiplication, division, puissance termes à termes

A^2 #Multiplication Matricielle
2×2 Matrix{Int64}:
  7  10
 15  22
A.^2 #Multiplication terme à terme
2×2 Matrix{Int64}:
 1   4
 9  16
A ./ [2 3 ; 4 5] #Division terme à terme 
2×2 Matrix{Float64}:
 0.5   0.666667
 0.75  0.8
A./2
2×2 Matrix{Float64}:
 0.5  1.0
 1.5  2.0

Opérateurs booléens sur les tableaux#

A = collect(1:5)
A .> 2
5-element BitVector:
 0
 0
 1
 1
 1
collect(1:5) .> [0;2;3;4;5]
5-element BitVector:
 1
 0
 0
 0
 0
collect(1:5).>[0 2 3 4 5]
5×5 BitMatrix:
 1  0  0  0  0
 1  0  0  0  0
 1  1  0  0  0
 1  1  1  0  0
 1  1  1  1  0

Particularité max, maximum, min, minimum#

max(2,3)
3
max(1,2,3)
3
max(1:5...)
5
?max
search: max maximum maximum! maxintfloat argmax eigmax RowMaximum typemax
max(x, y, ...)

Return the maximum of the arguments (with respect to isless). See also the maximum function to take the maximum element from a collection.

Examples

julia> max(2, 5, 1)
5
max.(1:5,2:6)
5-element Vector{Int64}:
 2
 3
 4
 5
 6

Constructeurs#

Enfin il est possible de construire rapidement certaines matrices

a = fill(0.,2,3) 
2×3 Matrix{Float64}:
 0.0  0.0  0.0
 0.0  0.0  0.0
a=fill(2,2)
2-element Vector{Int64}:
 2
 2
b = range(0, stop=2pi, length=10)
0.0:0.6981317007977318:6.283185307179586
c = LinRange(0, 2π, 10)
10-element LinRange{Float64, Int64}:
 0.0, 0.698132, 1.39626, 2.0944, …, 4.18879, 4.88692, 5.58505, 6.28319
collect(b)
10-element Vector{Float64}:
 0.0
 0.6981317007977318
 1.3962634015954636
 2.0943951023931953
 2.792526803190927
 3.490658503988659
 4.1887902047863905
 4.886921905584122
 5.585053606381854
 6.283185307179586
A=ones(3)
3-element Vector{Float64}:
 1.0
 1.0
 1.0
A=zeros(3)
3-element Vector{Float64}:
 0.0
 0.0
 0.0
using Random

rng = Xoshiro(111)

B = randn(rng, 5) # loi normale centrée de variance 1
5-element Vector{Float64}:
 -1.2141570148792402
  0.8728141561713048
 -0.4473225480452905
  0.14532188192398446
 -0.8369753246696554
minimum(B), maximum(B)
(-1.2141570148792402, 0.8728141561713048)
extrema(B)
(-1.2141570148792402, 0.8728141561713048)
B = [ones(3,2) zeros(3,2)] # concaténation de tableaux
3×4 Matrix{Float64}:
 1.0  1.0  0.0  0.0
 1.0  1.0  0.0  0.0
 1.0  1.0  0.0  0.0
hcat(ones(3,2), zeros(3,2))
3×4 Matrix{Float64}:
 1.0  1.0  0.0  0.0
 1.0  1.0  0.0  0.0
 1.0  1.0  0.0  0.0
B=[ones(3,2); zeros(3,2)] # , ou ; jouent le rôle de retour à la ligne
6×2 Matrix{Float64}:
 1.0  1.0
 1.0  1.0
 1.0  1.0
 0.0  0.0
 0.0  0.0
 0.0  0.0
vcat(ones(3,2), zeros(3,2))
6×2 Matrix{Float64}:
 1.0  1.0
 1.0  1.0
 1.0  1.0
 0.0  0.0
 0.0  0.0
 0.0  0.0
C = Diagonal(ones(3,3))
3×3 Diagonal{Float64, Vector{Float64}}:
 1.0   ⋅    ⋅ 
  ⋅   1.0   ⋅ 
  ⋅    ⋅   1.0
A = rand(5,5)

U, D, Vt = svd(A'A)
SVD{Float64, Float64, Matrix{Float64}, Vector{Float64}}
U factor:
5×5 Matrix{Float64}:
 -0.463543  -0.293881   0.0810887   0.470752   -0.685987
 -0.37719   -0.31532    0.0182981  -0.849448   -0.190799
 -0.46022   -0.101653  -0.792201    0.144199    0.359845
 -0.463886   0.869596   0.0914905  -0.0901132  -0.110102
 -0.46461   -0.218351   0.597609    0.167082    0.592795
singular values:
5-element Vector{Float64}:
 5.039741452325593
 0.95604231508676
 0.47342133951040216
 0.09810077368527008
 0.01592566168359706
Vt factor:
5×5 Matrix{Float64}:
 -0.463543   -0.37719    -0.46022   -0.463886   -0.46461
 -0.293881   -0.31532    -0.101653   0.869596   -0.218351
  0.0810887   0.0182981  -0.792201   0.0914905   0.597609
  0.470752   -0.849448    0.144199  -0.0901132   0.167082
 -0.685987   -0.190799    0.359845  -0.110102    0.592795
A'A
5×5 Matrix{Float64}:
 1.19782   0.933319  1.07601   0.839931  1.17092
 0.933319  0.883594  0.885522  0.628307  0.938472
 1.07601   0.885522  1.37852   0.955203  0.880463
 0.839931  0.628307  0.955203  1.81241   0.928034
 1.17092   0.938472  0.880463  0.928034  1.31089
U * Diagonal(D) * Vt 
5×5 Matrix{Float64}:
 1.15486  0.813783  -0.227291  -0.86153   1.65842
 1.03096  0.581025  -0.175963  -0.630546  1.37203
 1.27517  0.761444   0.111975  -1.0637    1.4765
 0.7551   0.4132    -0.210532  -1.80001   1.46064
 1.02194  0.737384  -0.410679  -0.884052  1.75168
diag(C) # extraction d'une diagonale
3-element Vector{Float64}:
 1.0
 1.0
 1.0
diagm(ones(3))
3×3 Matrix{Float64}:
 1.0  0.0  0.0
 0.0  1.0  0.0
 0.0  0.0  1.0
Matrix(I, 3, 3)
3×3 Matrix{Bool}:
 1  0  0
 0  1  0
 0  0  1

type sparse#

Julia possède un type sparse i.e. des matrices creuses, ces dernières ayant un comportement identique aux matrices elles ne diffèrent que dans leur définition (et leur stockage).

using SparseArrays
A = spzeros(3,3)
3×3 SparseMatrixCSC{Float64, Int64} with 0 stored entries:
  ⋅    ⋅    ⋅ 
  ⋅    ⋅    ⋅ 
  ⋅    ⋅    ⋅ 
A = spdiagm(0 => 1:3)
3×3 SparseMatrixCSC{Int64, Int64} with 3 stored entries:
 1  ⋅  ⋅
 ⋅  2  ⋅
 ⋅  ⋅  3
A = A + spdiagm(1 => 1:2)
3×3 SparseMatrixCSC{Int64, Int64} with 5 stored entries:
 1  1  ⋅
 ⋅  2  2
 ⋅  ⋅  3
nonzeros(A), nnz(A)
([1, 1, 2, 2, 3], 5)
sparse([0 1 2; 2 0 0]) # pour rendre sparse une matrice "full"
2×3 SparseMatrixCSC{Int64, Int64} with 3 stored entries:
 ⋅  1  2
 2  ⋅  ⋅
det(A)
6.0

Affectation et copie#

Attention julia à un mode de passage de valeur qui fonctionne différemment suivant une variable type ou un tableau.

Pour une variable scalaire

a = 1
b = a
b += 1
2
a
1
b
2
a
1

Par contre maintenant si la variable est un tableau

A = collect(1:5)
B = A
B .+= 1
5-element Vector{Int64}:
 2
 3
 4
 5
 6
A
5-element Vector{Int64}:
 2
 3
 4
 5
 6
B
5-element Vector{Int64}:
 2
 3
 4
 5
 6

Pour avoir un comportement il faut copier le tableau A

A = collect(1:5)
B = copy(A)
B .= B .+ 1
5-element Vector{Int64}:
 2
 3
 4
 5
 6
A
5-element Vector{Int64}:
 1
 2
 3
 4
 5
B
5-element Vector{Int64}:
 2
 3
 4
 5
 6

Il faut faire attention à ce comportement lorsque l’on utilise des vecteurs de vecteurs

vecs = [A for i in 1:3]
3-element Vector{Vector{Int64}}:
 [1, 2, 3, 4, 5]
 [1, 2, 3, 4, 5]
 [1, 2, 3, 4, 5]
A .+= 1
vecs
3-element Vector{Vector{Int64}}:
 [2, 3, 4, 5, 6]
 [2, 3, 4, 5, 6]
 [2, 3, 4, 5, 6]

Dictionnaire#

d = Dict(:key1 => "val1", :key2 => "val2")
Dict{Symbol, String} with 2 entries:
  :key2 => "val2"
  :key1 => "val1"
keys(d) # Toutes clés (itérateur)
KeySet for a Dict{Symbol, String} with 2 entries. Keys:
  :key2
  :key1
values(d) # Toutes valeurs (itérateur)
ValueIterator for a Dict{Symbol, String} with 2 entries. Values:
  "val2"
  "val1"
for (k,v) in d # Itérer par paire clé-valeur
    println("key: $k, value: $v")
end
key: key2, value: val2
key: key1, value: val1
haskey(d, :k) # Vérifier la présence de la clé :k
false

Les dictionnaires sont muables; quand des symboles sont utilisés comme clés, les clés sont immuables.