Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

線形代数

行列の加算

ndarray-badge cat-science-badge

ndarray::arr2 を使用して 2 つの 2 次元行列を作成し、要素ごとに加算します。

和は let sum = &a + &b として計算されることに注意してください。& 演算子は ab を消費しないようにするために使用され、後で表示できるようになります。それらの和を含む新しい配列が作成されます。


use ndarray::arr2;

fn main() {
    let a = arr2(&[[1, 2, 3],
                   [4, 5, 6]]);

    let b = arr2(&[[6, 5, 4],
                   [3, 2, 1]]);

    let sum = &a + &b;

    println!("{}", a);
    println!("+");
    println!("{}", b);
    println!("=");
    println!("{}", sum);
}

行列の乗算

ndarray-badge cat-science-badge

ndarray::arr2 で 2 つの行列を作成し、ndarray::ArrayBase::dot でそれらの行列乗算を実行します。


use ndarray::arr2;

fn main() {
    let a = arr2(&[[1, 2, 3],
                   [4, 5, 6]]);

    let b = arr2(&[[6, 3],
                   [5, 2],
                   [4, 1]]);

    println!("{}", a.dot(&b));
}

スカラーをベクトルに掛け、さらに行列を掛ける

ndarray-badge cat-science-badge

ndarray::arr1 で 1 次元配列(ベクトル)を作成し、ndarray::arr2 で 2 次元配列(行列)を作成します。

まず、スカラーをベクトルに掛けて別のベクトルを得ます。次に、その新しいベクトルに 対して行列を ndarray::Array2::dot で掛けます。(行列の乗算は dot を使用して行われます。一方、 * 演算子は要素ごとの乗算を行います。)

ndarray では、1 次元配列は文脈に応じて行ベクトルまたは列ベクトルの いずれとしても解釈できます。ベクトルの向きを表すことが重要な場合は、 代わりに 1 行または 1 列の 2 次元配列を使用する必要があります。この例では、 ベクトルは右辺の 1 次元配列であるため、dot はそれを列ベクトルとして 扱います。


use ndarray::{arr1, arr2, Array1};

fn main() {
    let scalar = 4;

    let vector = arr1(&[1, 2, 3]);

    let matrix = arr2(&[[4, 5, 6],
                        [7, 8, 9]]);

    let new_vector: Array1<_> = scalar * vector;
    println!("{}", new_vector);

    let new_matrix = matrix.dot(&new_vector);
    println!("{}", new_matrix);
}

ベクトルの比較

ndarray-badge

ndarray クレートは配列を作成するためのさまざまな方法をサポートしています。このレシピでは、from を使って std::Vec から ndarray::Array を作成します。次に、それらの配列を要素ごとに加算します。

このレシピには、2 つの浮動小数点ベクトルを要素ごとに比較する例も含まれています。 単純なケースでは、厳密な等価比較に assert_eq! を使用できます。より 複雑な浮動小数点比較で精度の問題を扱う必要がある場合は、Cargo.tomlndarray 依存関係で approx 機能を有効にしたうえで approx クレートを使用できます。たとえば、 ndarray = { version = "0.13", features = ["approx"] } のようにします。

このレシピには、所有権に関する追加の例も含まれています。ここでは、let z = a + b によって ab が消費され、a が結果で更新された後、その所有権が z にムーブされます。別の方法として、 let w = &c + &dcd を消費せずに新しいベクトルを作成するため、 後からそれらを変更できます。詳細については Binary Operators With Two Arrays を参照してください。


use ndarray::Array;

fn main() {
  let a = Array::from(vec![1., 2., 3., 4., 5.]);
  let b = Array::from(vec![5., 4., 3., 2., 1.]);
  let mut c = Array::from(vec![1., 2., 3., 4., 5.]);
  let mut d = Array::from(vec![5., 4., 3., 2., 1.]);

  let z = a + b;
  let w =  &c + &d;

  assert_eq!(z, Array::from(vec![6., 6., 6., 6., 6.]));

  println!("c = {}", c);
  c[0] = 10.;
  d[1] = 10.;

  assert_eq!(w, Array::from(vec![6., 6., 6., 6., 6.]));

}

ベクトルノルム

ndarray-badge

このレシピでは、与えられたベクトルの l1 ノルムと l2 ノルムを計算する際の Array1 型、ArrayView1 型、 fold メソッド、および dot メソッドの使い方を示します。

  • l2_norm 関数は 2 つのうち単純な方であり、ベクトルとそれ自身との ドット積の平方根を計算します。
  • l1_norm 関数は、要素の絶対値を合計する fold 演算によって計算されます。(これは x.mapv(f64::abs).scalar_sum() でも実行できますが、その場合 mapv の結果を保持するための新しい 配列が確保されます。)

l1_norml2_norm はどちらも ArrayView1 型を受け取ることに注意してください。このレシピでは ベクトルノルムを扱うため、ノルム関数は一次元の ビューだけを受け取れれば十分です(したがって ArrayView1)。 これらの関数は代わりに &Array1<f64> 型の パラメータを取ることもできますが、その場合、呼び出し側は所有された配列への 参照を持っている必要があり、単にビューへアクセスできる場合よりも制約が強くなります (ビューは所有された配列だけでなく、任意の配列またはビューから作成できるためです)。

ArrayArrayView はどちらも ArrayBase の型エイリアスです。したがって、呼び出し側にとって 最も汎用的な引数型は &ArrayBase<S, Ix1> where S: Data になります。これは、呼び出し側が x.view() の代わりに &array または &view を 使えるようになるためです。 関数が公開 API の一部である場合、ユーザーの利便性のためにそれがより良い選択である 可能性があります。内部関数では、より簡潔な ArrayView1<f64> の方が望ましいかもしれません。


use ndarray::{array, Array1, ArrayView1};

fn l1_norm(x: ArrayView1<f64>) -> f64 {
    x.fold(0., |acc, elem| acc + elem.abs())
}

fn l2_norm(x: ArrayView1<f64>) -> f64 {
    x.dot(&x).sqrt()
}

fn normalize(mut x: Array1<f64>) -> Array1<f64> {
    let norm = l2_norm(x.view());
    x.mapv_inplace(|e| e/norm);
    x
}

fn main() {
    let x = array![1., 2., 3., 4., 5.];
    println!("||x||_2 = {}", l2_norm(x.view()));
    println!("||x||_1 = {}", l1_norm(x.view()));
    println!("Normalizing x yields {:?}", normalize(x));
}

行列を反転

nalgebra-badge cat-science-badge

nalgebra::Matrix3 で 3x3 行列を作成し、可能であればそれを反転します。


use nalgebra::Matrix3;

fn main() {
    let m1 = Matrix3::new(2.0, 1.0, 1.0, 3.0, 2.0, 1.0, 2.0, 1.0, 2.0);
    println!("m1 = {}", m1);
    match m1.try_inverse() {
        Some(inv) => {
            println!("The inverse of m1 is: {}", inv);
        }
        None => {
            println!("m1 is not invertible!");
        }
    }
}

行列をシリアライズ/デシリアライズする

ndarray-badge cat-science-badge

行列を JSON との間でシリアライズおよびデシリアライズします。シリアライズは serde_json::to_string が行い、serde_json::from_str がデシリアライズを行います。

シリアライズの後にデシリアライズすると、元の行列がそのまま返されることに注意してください。


use nalgebra::DMatrix;

fn main() -> Result<(), std::io::Error> {
    let row_slice: Vec<i32> = (1..5001).collect();
    let matrix = DMatrix::from_row_slice(50, 100, &row_slice);

    // 行列をシリアライズする
    let serialized_matrix = serde_json::to_string(&matrix)?;

    // 行列をデシリアライズする
    let deserialized_matrix: DMatrix<i32> = serde_json::from_str(&serialized_matrix)?;

    // `deserialized_matrix` が `matrix` と等しいことを検証する
    assert!(deserialized_matrix == matrix);

    Ok(())
}