多次元配列#

Rustでの多次元配列の扱いを学ぶ

参考#

// 配列処理
:dep ndarray = { version = "0.15.6" }
use ndarray::{Array, array};

配列の生成#

Array::from_vec(vec![
    vec![1, 2, 3],
    vec![4, 5, 6],
])
[[1, 2, 3], [4, 5, 6]], shape=[2], strides=[1], layout=CFcf (0xf), const ndim=1
// 不揃いでも良いっぽい?
Array::from_vec(vec![
    vec![1, 2, 3],
    vec![4, 5]
])
[[1, 2, 3], [4, 5]], shape=[2], strides=[1], layout=CFcf (0xf), const ndim=1
// array! マクロでも可
array![
    [3, 1, 4],
    [1, 5, 9],
    [2, 6, 5],
]
[[3, 1, 4],
 [1, 5, 9],
 [2, 6, 5]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2

Array::from_shape_vec で1次元配列をshape形式に変形して生成できる.(結果はResult型)

Array::from_shape_vec((1,), vec![1])
Ok([1], shape=[1], strides=[1], layout=CFcf (0xf), const ndim=1)
Array::from_shape_vec((2, 3), vec![1, 2, 3, 4, 5, 6])?
[[1, 2, 3],
 [4, 5, 6]], shape=[2, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2

関数からも生成できるらしい

Array::from_shape_fn((4, 5), |(i, j)| 4 * i + j)
[[0, 1, 2, 3, 4],
 [4, 5, 6, 7, 8],
 [8, 9, 10, 11, 12],
 [12, 13, 14, 15, 16]], shape=[4, 5], strides=[5, 1], layout=Cc (0x5), const ndim=2

スライス#

use ndarray::s;
let A = array![
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
];

&A
[[1, 2, 3],
 [4, 5, 6],
 [7, 8, 9]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2
// インデックス
A.slice(s![1, 1])
5, shape=[], strides=[], layout=CFcf (0xf), const ndim=0
// 複数指定
A.slice(s![.., 1..])
[[2, 3],
 [5, 6],
 [8, 9]], shape=[3, 2], strides=[3, 1], layout=c (0x4), const ndim=2
// ステップ
A.slice(s![..;-1, ..])
[[7, 8, 9],
 [4, 5, 6],
 [1, 2, 3]], shape=[3, 3], strides=[-3, 1], layout=c (0x4), const ndim=2

演算#

let W = array![[1, 2, 3], [4, 5, 6]];
let X = array![[0, 1, 2], [3, 4, 5]];
// 要素ごとの和
&W + &X
[[1, 3, 5],
 [7, 9, 11]], shape=[2, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2
// 要素ごとの差
&W - &X
[[1, 1, 1],
 [1, 1, 1]], shape=[2, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2
// 要素ごとの積
&W * &X
[[0, 2, 6],
 [12, 20, 30]], shape=[2, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2
// 転置
W.t()
[[1, 4],
 [2, 5],
 [3, 6]], shape=[3, 2], strides=[1, 3], layout=Ff (0xa), const ndim=2
// ドット積
W.t().dot(&X)
[[12, 17, 22],
 [15, 22, 29],
 [18, 27, 36]], shape=[3, 3], strides=[3, 1], layout=Cc (0x5), const ndim=2
// 形状が一致しない場合
W.dot(&X)
thread '<unnamed>' panicked at /home/powell/.cargo/registry/src/index.crates.io-6f17d22bba15001f/ndarray-0.15.6/src/linalg/impl_linalg.rs:299:5:
ndarray: inputs 2 × 3 and 2 × 3 are not compatible for matrix multiplication
stack backtrace:
   0: rust_begin_unwind
             at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/std/src/panicking.rs:647:5
   1: core::panicking::panic_fmt
             at /rustc/25ef9e3d85d934b27d9dada2f9dd52b1dc63bb04/library/core/src/panicking.rs:72:14
   2: ndarray::linalg::impl_linalg::dot_shape_error
   3: ndarray::linalg::impl_linalg::<impl ndarray::ArrayBase<S,ndarray::dimension::dim::Dim<[usize; 2]>>>::dot
   4: std::panicking::try
   5: run_user_code_15
   6: evcxr::runtime::Runtime::run_loop
   7: evcxr::runtime::runtime_hook
   8: evcxr_jupyter::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

線形代数#

:dep ndarray-linalg = { version = "0.16.0", features = ["openblas"] }
use ndarray_linalg::{Determinant, Inverse};

行列式#

let A = array![[3.0, 4.0], [5.0, 6.0]];

A.det()
Ok(-2.0)

逆行列#

A.inv()
Ok([[-3.0, 2.0],
 [2.5, -1.5]], shape=[2, 2], strides=[2, 1], layout=Cc (0x5), const ndim=2)