matten: `Tensor` コア

@nabbisen

Series


これは matten を紹介する全 4 回シリーズの第 2 回です。第 1 回では背景を説明しました。今回はライブラリの実際の使い方を紹介します。


はじめ方

# Cargo.toml
[dependencies]
matten = "0.28"

デフォルトの features には serdejsoncsv が含まれています。依存関係をできるだけ小さくしたい場合は無効にします:

matten = { version = "0.28", default-features = false }

テンソルをつくる

use matten::Tensor; をインポートするだけです。ジェネリクスの型パラメータも、ライフタイムアノテーションも不要です。

use matten::Tensor;

// データとシェイプを明示してつくる
let a = Tensor::new(vec![1.0, 2.0, 3.0, 4.0], &[2, 2]);
assert_eq!(a.shape(), &[2, 2]);
assert_eq!(a.ndim(), 2);

// 便利なコンストラクタ
let z = Tensor::zeros(&[3, 3]);
let o = Tensor::ones(&[3, 3]);
let f = Tensor::full(&[2, 4], 5.0);

境界スタイルのコンストラクタを使うと、シェイプの不一致はパニックではなくエラーとして返ります:

use matten::{MattenError, Tensor};

let result = Tensor::try_new(vec![1.0, 2.0, 3.0], &[2, 2]);
assert!(matches!(result, Err(MattenError::Shape { .. })));

算術演算とブロードキャスト

演算子は参照を受け取ります。そのため、もとの値の所有権を保持したまま使えます。シェイプのブロードキャストは NumPy 方式の右揃えルールに従います。

use matten::Tensor;

let a = Tensor::new(vec![1.0, 2.0, 3.0, 4.0], &[2, 2]);
let b = Tensor::ones(&[2, 2]);

let c = &a + &b;          // [2.0, 3.0, 4.0, 5.0]
let d = &a * 2.0;         // スカラーブロードキャスト: [2.0, 4.0, 6.0, 8.0]

// 行ベクトルを行列全体にブロードキャスト
let row = Tensor::new(vec![1.0, 2.0], &[1, 2]);
let mat = Tensor::ones(&[3, 2]);
let result = &mat + &row; // シェイプ [3, 2]

シェイプ操作

use matten::Tensor;

let t = Tensor::new(vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0], &[2, 3]);

let flat = t.flatten();           // シェイプ [6]
let reshaped = t.reshape(&[3, 2])?;
let transposed = t.transpose()?;  // シェイプ [3, 2]

// リダクション
let s = t.sum();          // スカラー
let m = t.mean()?;
let col_sums = t.sum_axis(0)?;    // シェイプ [3]

JSON と CSV

どちらもデフォルトで有効です。境界部分では Result を返すので、不正な入力はパニックではなくエラーとして扱われます。

use matten::Tensor;

// JSON — 受け付け可能な形式
let t = Tensor::from_json(r#"{"shape":[2,2],"data":[1.0,2.0,3.0,4.0]}"#)?;
let t = Tensor::from_json("[[1.0, 2.0], [3.0, 4.0]]")?;

// ファイルから読み込む
let t = Tensor::load_json("data/tensor.json")?;

// CSV
let t = Tensor::from_csv("1.0,2.0,3.0\n4.0,5.0,6.0\n")?;
let t = Tensor::load_csv("data/matrix.csv")?;

serde 経由のシリアライズも使えます。json または serde feature が有効なとき、serde_json::to_string(&t)serde_json::from_str(&json_str) で正しくラウンドトリップします。

エラーハンドリング

matten には、意図的に 2 つのエラー種別があります。

  • シェイプ操作(new での構築、reshape、スライス)はパニックし、わかりやすいメッセージを出します — 素早いプロトタイプ開発中に問題をすぐ見つけられるように。
  • 外部境界の操作(from_jsonfrom_csvload_*)は常に Result<Tensor, MattenError> を返し、通常の不正入力ではパニックしません。実際のデータは常にクリーンとは限らないので。
    • MattenError#[non_exhaustive] です。気になるバリアントにマッチし、残りはワイルドカードで受けてください:
use matten::{MattenError, Tensor};

match Tensor::from_csv("1.0,not_a_number\n") {
    Ok(t) => println!("shape {:?}", t.shape()),
    Err(MattenError::Parse { .. }) => println!("不正な入力"),
    Err(e) => println!("その他のエラー: {e:?}"),
}

日常的な数値計算のコア部分は以上です。次の記事では少し異なる状況を扱います — 入力データがクリーンな f64 の行列ではないとき、つまり型が混在していたり、欠損値があったり、整数と浮動小数点が混在している場合です。

リンク: crates.io · docs.rs · mdBook · リポジトリ


Comments or feedbacks are welcomed and appreciated.