Juliaにおける基本的な行列操作

# はじめに
Juliaにおける基本的な操作を書き残します.
間違い等ありましたら, ぜひご連絡ください.

ちなみにJulia(ver1.1.0)における環境で動作チェックしています.

それと行列操作に関する逆引き的なものを用意しようと思っているのでそちらもよろしくお願いします.
(作成中...)

# 行列の定義
## 単純な書き方
行の区切りは`改行`もしくは`;`で表現し, 列の区切りは` `(半角スペース)で表現する.
つまり, 以下のようにすれば作成できる.

```julia
julia> mat = [1 2 3; 4 5 6]
2×3 Array{Int64,2}:
 1  2  3
 4  5  6

julia> mat = [
       1 2 3
       4 5 6
       ]
2×3 Array{Int64,2}:
 1  2  3
 4  5  6
```

## 内包表記で生成する
内包表記というものを利用しても, 行列は作成できる.

```julia
# 内包表記
julia> mat = [ x for x in 1:4]
4-element Array{Int64,1}:
 1
 2
 3
 4

# 内包表記を二重で利用する
julia> mat = [ 10a + b for a in 0:2, b in 0:3]
3×4 Array{Int64,2}:
  0   1   2   3
 10  11  12  13
 20  21  22  23
```

### 後置ifを利用した内包表記


```julia
julia> mat = [ i for i in 0:10 if (i%2)==0 ]
6-element Array{Int64,1}:
  0
  2
  4
  6
  8
 10
```

また2次元行列を作成する時に後置ifを利用すると, ベクトルになって返ってくる.
(実際後置ifを利用すると次元数がバラバラになる可能性があるので, 行列が作れてしまうといろいろと問題があるけど.)

```julia
julia> mat = [ a + b for a in 1:2, b in 1:2 ]
2×2 Array{Int64,2}:
 2  3
 3  4

julia> mat = [ a + b for a in 1:2, b in 1:2 if true ]
4-element Array{Int64,1}:
 2
 3
 3
 4
```

そのため, 2次元以上で後置ifを利用する場合は, ３項演算子等を利用して違う数字を埋めるようにすれば大丈夫.

```julia
julia> mat = [ ( (b%2==a%2) ? 10a + b : 0 )  for a in 0:1, b in 0:9 ]
2×10 Array{Int64,2}:
 0   0  2   0  4   0  6   0  8   0
 0  11  0  13  0  15  0  17  0  19
```

# 関数を使った行列の生成
## 零行列を作成する
組み込みの関数である`zeros([T=Float64,] dims...)`を利用していきます.

| 引数 | 概要 |
| :-: | :-: | 
| `T` | 型を指定します. 省略可. | 
| `dims...` | 次元数を列挙します. |

```bash
julia> zeros(ComplexF64, 2, 2)
2×2 Array{Complex{Float64},2}:
 0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im

julia> zeros(Int8, 2, 2, 2)
2×2×2 Array{Int8,3}:
[:, :, 1] =
 0  0
 0  0

[:, :, 2] =
 0  0
 0  0

julia> zeros(2, 3)
2×3 Array{Float64,2}:
 0.0  0.0  0.0
 0.0  0.0  0.0
```

## すべての要素が1の行列
組み込みの関数である`ones([T=Float64,] dims...)`を利用していきます.
利用の仕方は上記の`zeros`と同じなので省略します.

## 単位行列
単位行列の生成方法はバージョンによって結構変わってきます.
(`eye`だったり,`I`を利用したり...)
ver1.0以降(自分の場合は`ver1.1.0`)においては`LinearAlgebra`にある`I`を利用して単位行列を作成します.

構文としては`Matrix{T}(I, row, col}`です.
ちなみに正則行列でなくても作成できます.

```julia
julia> using LinearAlgebra

julia> Matrix{Int8}(I, 3, 3)
3×3 Array{Int8,2}:
 1  0  0
 0  1  0
 0  0  1

julia> Matrix{ComplexF64}(I, 3, 3)
3×3 Array{Complex{Float64},2}:
 1.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  1.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  1.0+0.0im

julia> Matrix{Int8}(I, 4, 3)
4×3 Array{Int8,2}:
 1  0  0
 0  1  0
 0  0  1
 0  0  0
```


## ランダムな値を持つ行列を生成
組み込み関数である`rand([rng=GLOBAL_RNG], [S], [dims...])`を利用していきます.

| 引数 | 概要 | 省略 | 注意 |
| :-: | :-: | :-: | :-: |
| `rng` | わからないです<br>(たぶんシード値?) | 可 | わかる方は教えてください |
| `S` | 生成するデータの指定<br>(指定方法は後述) | 可 | tupleで指定できるのは`v1.1`以降から |
| `dims` | 各次元数 | 可 |

`S`の指定方法は結構柔軟で, 以下に示すような方法で指定できます.

| 指定方法 | データの選ばれ方 |
| :-: | :-: |
| indexable collection<br>(`1:9`, `(1, 2, 3, 100)` など) | そのタプルに属する値のうち1つ |  |
| AbstractDict, AbstractSet | 〃 |  |
| string | 文字列を文字単位に分解したうちの1つ. |  |
| type | `typemin(S)`〜`typemax(S)`のうちの1つ |  |

以下にそれぞれの実行例を載せておきます.

```julia
julia> rand((1, 2, 3, -99), 2, 9)
2×9 Array{Int64,2}:
 2    3  1    2  -99    3  2    2    3
 2  -99  3  -99    2  -99  2  -99  -99
```

```julia
julia> rand(Dict("bird"=>3, "mammal"=>345, "reptiles"=>32), 2, 4)
2×4 Array{Pair{String,Int64},2}:
   "mammal"=>345  "mammal"=>345  "reptiles"=>32      "bird"=>3 
 "reptiles"=>32     "bird"=>3        "bird"=>3   "reptiles"=>32
```

```julia
julia> rand("hoge", 2, 9)
2×9 Array{Char,2}:
 'e'  'o'  'h'  'g'  'o'  'o'  'o'  'e'  'e'
 'o'  'h'  'h'  'e'  'e'  'h'  'e'  'e'  'h'

julia> rand("aaaaaaaaaaag", 2, 9)
2×9 Array{Char,2}:
 'a'  'a'  'a'  'a'  'a'  'a'  'g'  'a'  'a'
 'a'  'a'  'a'  'a'  'a'  'a'  'a'  'g'  'a'
```

```julia
julia> rand(Char, 2, 6)
2×6 Array{Char,2}:
 '\Ub8de6'  '𗀋'  '骊'        '\U63ebc'  '\Ue15e4'  '\U640aa'
 '\U39ad9'  '㵑'  '\U4d6b0'  '뛐'        '\U6302d'  '\U8d876'

julia> rand(ComplexF64, 3, 3)
3×3 Array{Complex{Float64},2}:
 0.833554+0.844678im  0.0205492+0.966243im  0.665147+0.809684im
 0.237675+0.252522im    0.30283+0.414608im  0.139303+0.571629im
 0.386357+0.949509im    0.56883+0.772397im  0.984182+0.255435im
```

## 標準正規分布に従う乱数を持つ行列を生成
`randn([rng=GLOBAL_RNG], [T=Float64], [dims...])`を利用します.
使い方は`rand`とほぼ同じなので, 省略します.

また`randexp`も, もちろんあります.


# 添字
Juliaの添字は1から始まるので注意が必要です.
添字の選択方法に関しては以下の二通ります(たぶん).

## 各要素を一様の数字で選択する
こんな感じ. 

```julia
julia> mat = [ 3a + b for a in 0:2, b in 1:3]
3×3 Array{Int64,2}:
 1  2  3
 4  5  6
 7  8  9

julia> print(mat[1], " ", mat[4], " ", mat[7], "\n",
             mat[2], " ", mat[5], " ", mat[8], "\n",
             mat[3], " ", mat[6], " ", mat[9], "\n")
1 2 3
4 5 6
7 8 9
```

## mat[行, 列]形式で選択する
こんな感じ.

```julia
julia> mat = [ 3a + b for a in 0:2, b in 1:3]
3×3 Array{Int64,2}:
 1  2  3
 4  5  6
 7  8  9

julia> print(mat[1, 1], " ", mat[1, 2], " ", mat[1, 3], "\n",
             mat[2, 1], " ", mat[2, 2], " ", mat[2, 3], "\n",
             mat[3, 1], " ", mat[3, 2], " ", mat[3, 3], "\n")
1 2 3
4 5 6
7 8 9
```

## 行/列の切り出し
Juliaでは次元数を保持せずに特定行/列を取ってくることができます.

```julia
# 1行目をとる
julia> mat[1, :]
3-element Array{Int64,1}:
 1
 2
 3

# 1列目をとる
julia> mat[:, 1]
3-element Array{Int64,1}:
 1
 4
 7
```

## 複数行/複数列の切り出し

```julia
julia> mat[1:2, :]
2×3 Array{Int64,2}:
 1  2  3
 4  5  6

julia> mat[:, 1:2]
3×2 Array{Int64,2}:
 1  2
 4  5
 7  8
```

## 部分切り出し

```julia
julia> mat[2:3, 2]
2-element Array{Int64,1}:
 5
 8

julia> mat[2:3, 1:2]
2×2 Array{Int64,2}:
 4  5
 7  8
```


# 基本演算
行列演算と要素毎の演算のそれぞれについて触れていきます.

## 要素毎の演算
`.`で各関数を各要素に適用できます.

```julia
julia> a = [1 2 3; 4 5 6; 7 8 9]
3×3 Array{Int64,2}:
 1  2  3
 4  5  6
 7  8  9



julia> b = [-1 -2 -3; 4 5 6; -7 -8 -9]
3×3 Array{Int64,2}:
 -1  -2  -3
  4   5   6
 -7  -8  -9

# 算術演算子
julia> a .* b
3×3 Array{Int64,2}:
  -1   -4   -9
  16   25   36
 -49  -64  -81

julia> a .*= b
3×3 Array{Int64,2}:
  -1   -4   -9
  16   25   36
 -49  -64  -81
```

算術演算子以外にも利用できます. また, 自作関数にも適用することができます.

```julia
# 関数
julia> a = [1 2 3; 4 5 6; 7 8 9]
3×3 Array{Int64,2}:
 1  2  3
 4  5  6
 7  8  9

julia> sin.(a)
3×3 Array{Float64,2}:
  0.841471   0.909297   0.14112 
 -0.756802  -0.958924  -0.279415
  0.656987   0.989358   0.412118

julia> myfunc = function(x) 2x -5 end
#5 (generic function with 1 method)

julia> myfunc.(a)
3×3 Array{Int64,2}:
 -3  -1   1
  3   5   7
  9  11  13
```

一応, `map`関数を利用してでも似たようなことが実現できます.

```julia
a = map(sin, a)
a = map(myfunc, a)
```

## 要素毎の代入
`=`を用いた通常の代入ではなく, `.=`を用いることで要素毎に代入することができます.

```julia
julia> mat = [ 3a + b for a in 0:2, b in 1:3]
3×3 Array{Int64,2}:
 1  2  3
 4  5  6
 7  8  9

julia> mat .= -1
3×3 Array{Int64,2}:
 -1  -1  -1
 -1  -1  -1
 -1  -1  -1
```

## 和算, 減算
演算子`+`, `-`を利用していきます.

```julia
julia> a = [1 2 3; 4 5 6; 7 8 9]
3×3 Array{Int64,2}:
 1  2  3
 4  5  6
 7  8  9

julia> b = [-1 -2 -3; 4 5 6; -7 -8 -9]
3×3 Array{Int64,2}:
 -1  -2  -3
  4   5   6
 -7  -8  -9

julia> a + b
3×3 Array{Int64,2}:
 0   0   0
 8  10  12
 0   0   0

julia> a - b
3×3 Array{Int64,2}:
  2   4   6
  0   0   0
 14  16  18
```

## 積
行列の積は`*`, 要素毎の掛け算は`.*`となります.s

```julia
julia> a * b
3×3 Array{Int64,2}:
 -14  -16  -18
 -26  -31  -36
 -38  -46  -54

julia> a .* b
3×3 Array{Int64,2}:
  -1   -4   -9
  16   25   36
 -49  -64  -81
```

## 割り算, 逆行列の掛け算
要素毎の割り算は`./`を使います.

```julia
julia> a ./ b
3×3 Array{Float64,2}:
 -1.0  -1.0  -1.0
  1.0   1.0   1.0
 -1.0  -1.0  -1.0
```

演算子`/`は少し特殊で, `a / b`は`a * inv(b)`を表します.

```julia
julia> x = randn(Float64, 3, 3)
3×3 Array{Float64,2}:
  0.774262  -0.385865  -1.1918  
  0.402656  -1.53214    1.95856 
 -0.622096   0.619093  -0.787436

julia> y = randn(Float64, 3, 3)
3×3 Array{Float64,2}:
  0.330775  -0.95705   0.993989 
 -1.37908   -0.312028  0.0130837
  0.882019   0.93005   0.816249 
```
```julia
julia> x / y
3×3 Array{Float64,2}:
 -0.254909  -1.34409   -1.12813  
  1.75524    0.293593   0.257316 
 -0.763695   0.243228  -0.0386082

julia> x * inv(y)
3×3 Array{Float64,2}:
 -0.254909  -1.34409   -1.12813  
  1.75524    0.293593   0.257316 
 -0.763695   0.243228  -0.0386082
```

また`\`では, `a \ b`は`inv(a) * b`を表します.

```julia
julia> x \ y
3×3 Array{Float64,2}:
 -0.712879  -1.7516    -1.79956
 -0.165542  -0.484339  -2.15161
 -0.687072  -0.178097  -1.30651

julia> inv(x) * y
3×3 Array{Float64,2}:
 -0.712879  -1.7516    -1.79956
 -0.165542  -0.484339  -2.15161
 -0.687072  -0.178097  -1.30651
```

## 逆行列
`inv(x)`を利用します.

## 転置行列
`'`を利用して`x'`のように書きます.
ちなみに複素数に`x'`を利用するとエルミート行列になってしまうため, `transpose()`を使いましょう.

```julia
julia> mat = [1 2 3; 4 5 6; 7 8 9]
3×3 Array{Int64,2}:
 1  2  3
 4  5  6
 7  8  9

julia> mat'
3×3 LinearAlgebra.Adjoint{Int64,Array{Int64,2}}:
 1  4  7
 2  5  8
 3  6  9
```

```julia
julia> mat = rand(Complex{Int8}, 2, 2)
2×2 Array{Complex{Int8},2}:
 13+19im   7+92im
 82-10im  91+53im

# エルミート行列になっちゃう
julia> mat'
2×2 LinearAlgebra.Adjoint{Complex{Int8},Array{Complex{Int8},2}}:
 13-19im  82+10im
  7-92im  91-53im

# 複素数の転置はこちらを使う
julia> transpose(mat)
2×2 LinearAlgebra.Transpose{Complex{Int8},Array{Complex{Int8},2}}:
 13+19im  82-10im
  7+92im  91+53im
```

## エルミート行列(共役転置行列, 自己随伴行列)
前節で説明したように, 複素数に対して`'`を利用するとエルミート行列が得られます.

```julia
julia> mat = rand(Complex{Int8}, 2, 2)
2×2 Array{Complex{Int8},2}:
 13+19im   7+92im
 82-10im  91+53im

# エルミート行列
julia> mat'
2×2 LinearAlgebra.Adjoint{Complex{Int8},Array{Complex{Int8},2}}:
 13-19im  82+10im
  7-92im  91-53im

# 複素数の通常の転置はこちらを使う
julia> transpose(mat)
2×2 LinearAlgebra.Transpose{Complex{Int8},Array{Complex{Int8},2}}:
 13+19im  82-10im
  7+92im  91+53im
```

## 固有値, 固有ベクトル

`LinearAlgebra`の`eigvals`, `eigvecs`を利用していきます.
それぞれ固有値, 固有ベクトルを得ることができます.

```julia
julia> using LinearAlgebra

julia> a = [1 2; 3 4]
2×2 Array{Int64,2}:
 1  2
 3  4

julia> eigvals(a)
2-element Array{Float64,1}:
 -0.3722813232690143
  5.372281323269014 

julia> eigvecs(a)
2×2 Array{Float64,2}:
 -0.824565  -0.415974
  0.565767  -0.909377
```

# 次元数1の箇所を削る

`dropdims`を利用していきます.
削れるのは次元数が1の箇所のみです.

```julia
julia> a = rand(1, 1, 1, 3)
1×1×1×3 Array{Float64,4}:
[:, :, 1, 1] =
 0.4319242309225346

[:, :, 1, 2] =
 0.22722643863542324

[:, :, 1, 3] =
 0.7460652772993

julia> dropdims(a, dims=(2, 3))
1×3 Array{Float64,2}:
 0.431924  0.227226  0.746065

julia> dropdims(a, dims=(1, 2, 3))
3-element Array{Float64,1}:
 0.4319242309225346 
 0.22722643863542324
 0.7460652772993
```

# 配列の複製
他の言語でもそうですがふつーに`b = a`をしてbにaをコピーすると

```julia
julia> a = [1 2 3; 4 5 6; 7 8 9]
3×3 Array{Int64,2}:
 1  2  3
 4  5  6
 7  8  9

julia> b = a
3×3 Array{Int64,2}:
 1  2  3
 4  5  6
 7  8  9

julia> b .= 0
3×3 Array{Int64,2}:
 0  0  0
 0  0  0
 0  0  0

julia> a
3×3 Array{Int64,2}:
 0  0  0
 0  0  0
 0  0  0
```
のように, 値だけでなく参照先まで同じになってしまう.(浅いコピー)

そこで, 値だけを得たい場合は`copy()`を利用する.(深いコピー)

```julia
julia> a = [1 2 3; 4 5 6; 7 8 9]
3×3 Array{Int64,2}:
 1  2  3
 4  5  6
 7  8  9

julia> b = copy(a)
3×3 Array{Int64,2}:
 1  2  3
 4  5  6
 7  8  9

julia> b .= 0
3×3 Array{Int64,2}:
 0  0  0
 0  0  0
 0  0  0

julia> a
3×3 Array{Int64,2}:
 1  2  3
 4  5  6
 7  8  9
```


# 複素数の演算

## 実部をとる

ビルトイン関数の`real()`を使っていきます.

```julia
julia> real(1.2 + 3.4im)
1.2

julia> real(-2.5 + 2.1im)
-2.5
```

## 虚部をとる

ビルトイン関数の`imag()`を使っていきます.

```julia
julia> imag(1.2 + 3.4im)
3.4

julia> imag(2.1 - 3.2im)
-3.2
```

## 複素共役をとる

ビルトイン関数の`conj()`を使っていきます.

```julia
julia> conj(1.2 + 3.4im)
1.2 - 3.4im

julia> conj(1.2 - 3.4im)
1.2 + 3.4im
```


# さいごに
その他についても思いつき次第追加していきたいと思います.