行列の積で見る2次元配列の例(Julia, Go, Rust)

# はじめに

別の言語の例を移植すると意外と考えさせられる。

環境

* The Go Playground (2021-12-23)
* Julia 1.6 (Julia Playground)
* Rust 1.5.7 (Rust Playground; rustc)

Rust は手元に環境があるのだが、サンプル程度でプロジェクトを作ったりのが面倒なときは、Playground を利用する。

## Julia

計算科学用途な言語だけあって、こういうところは強い。
この宣言の時点で行列の型だと決めつけができる。

```julia
A = [1 2 1; 0 1 1]
B = [1 0; 0 1; 1 1]
println(A*B)
```

```
[2 3; 1 2]
```

## Go

* 2次元配列
  関数も含めてサイズを事前に決めておく必要があり、その結果、任意のサイズの行列の積を行う関数が作れない

    ```golang
    package main

    import "fmt"

    func mul(a [2][3]int, b [3][2]int) [2][2]int {
        var i, j, k int
        var c [len(a)][len(b[0])]int
        for i = 0; i < len(a); i++ {
            for k = 0; k < len(b[0]); k++ {
                for j = 0; j < len(a[0]); j++ {
                    c[i][k] += a[i][j] * b[j][k]
                }
            }
        }
        return c
    }

    func main() {
        var a = [2][3]int{
            {1, 2, 1},
            {0, 1, 1},
        }
        var b = [3][2]int{
            {1, 0},
            {0, 1},
            {1, 1},
        }

        var c = mul(a, b)
        fmt.Println(c)
        fmt.Printf("%d %d\n", c[0][0], c[0][1])
        fmt.Printf("%d %d\n", c[1][0], c[1][1])
    }
    ```

    **実行結果**

    ```
    [[2 3] [1 2]]
    2 3
    1 2
    ```


* 2次元スライス
  スライスであればサイズを指定しない宣言ができため、任意のサイズの行列の積を行う関数を実現できる。

    ```golang
    package main

    import "fmt"

    func mul(a [][]int, b [][]int) [][]int {
        var j, k int
        c := make([][]int, len(a))
        for i := range a {
            c[i] = make([]int, len(b[0]))
            for k = 0; k < len(b[0]); k++ {
                for j = 0; j < len(a[0]); j++ {
                    c[i][k] += a[i][j] * b[j][k]
                }
            }
        }
        return c
    }

    func main() {
        var a = make([][]int, 2)
        a[0] = []int{1, 2, 1}
        a[1] = []int{0, 1, 1}
        var b = make([][]int, 3)
        b[0] = []int{1, 0}
        b[1] = []int{0, 1}
        b[2] = []int{1, 1}

        var c = mul(a, b)
        fmt.Println(c)
        fmt.Printf("%d %d\n", c[0][0], c[0][1])
        fmt.Printf("%d %d\n", c[1][0], c[1][1])
    }
    ```

    **実行結果**

    ```
    [[2 3] [1 2]]
    2 3
    1 2
    ```

## Rust

C/C++辺りで実装すると必要な malloc/free, new/delete が不要なのは楽。

* Vec を使った例
  Rust の制約として初期化が必要(この辺り、非効率に思えるんだが最適化次第な気がする。)

    ```rust
    fn mul(a: &Vec<Vec<i32>>, b: &Vec<Vec<i32>>) -> Vec<Vec<i32>> {
        let mut result = vec![vec![0; a.len()]; b[0].len()];

        for i in 0..a.len() {
            for k in 0..b[0].len() {
                for j in 0..a[0].len() {
                    result[i][k] += a[i][j] * b[j][k];
                }
            }
        }
        result
    }

    fn main() {
        let a = vec![
            vec![1, 2, 1],
            vec![0, 1, 1]
        ];
        let b = vec![
            vec![1, 0],
            vec![0, 1],
            vec![1, 1]
        ];
        let c = mul(&a, &b);
        println!("c={:?}", c);
    }
    ```

    **実行結果**

    ```
    c=[[2, 3], [1, 2]]
    ```
* Generics
  0 で初期化する部分は、`T::zero()` を実装している必要がある。
  Copy がないと `a[i][j]` と `b[j][k]` で所有権の移動が行われて処理が成立しない。
  あとは、 `*` と `+=` のTraitを実装している必要がある。

    ```rust
    fn mul<T>(a: &Vec<Vec<T>>, b: &Vec<Vec<T>>) -> Vec<Vec<T>>
    where T: num::traits::Zero, T: Copy,
          T: std::ops::Mul<Output = T>, T: std::ops::AddAssign<T>
    {
        let mut result = vec![vec![T::zero(); a.len()]; b[0].len()];

        for i in 0..a.len() {
            for k in 0..b[0].len() {
                for j in 0..a[0].len() {
                    result[i][k] += a[i][j] * b[j][k];
                }
            }
        }
        result
    }

    fn main() {
        let a = vec![
            vec![1, 2, 1],
            vec![0, 1, 1]
        ];
        let b = vec![
            vec![1, 0],
            vec![0, 1],
            vec![1, 1]
        ];
        let c = mul::<u32>(&a, &b);
        println!("c={:?}", c);
    }
    ```
    **実行結果**

    ```
    c=[[2, 3], [1, 2]]
    ```

* Nalgebra
  `SMatrix::<u32, 2, 3>` は `Matrix2x3` でもよい。

    ```rust
    extern crate nalgebra as na;

    fn main() {
        let a = na::SMatrix::<u32, 2, 3>::new(
            1, 2, 1,
            0, 1, 1
        );
        let b = na::SMatrix::<u32, 3, 2>::new(
            1, 0,
            0, 1,
            1, 1
        );
        let c = a * b;
        println!("{:?}", c);
        println!(
            "{:?} {:?}\n{:?} {:?}",
            c[(0, 0)], c[(0, 1)], c[(1, 0)], c[(1, 1)]
        );
    }
    ```

    **実行結果**

    ```
    Matrix { data: [[2, 1], [3, 2]] }
    2 3
    1 2
    ```