読者です 読者をやめる 読者になる 読者になる

いものやま。

雑多な知識の寄せ集め

Swiftでの行列計算について調べてみた。(その2)

技術 Swift

昨日はAccelerateフレームワークの概要と、行列の生成と内容の取得について説明した。

今日はベクトルとSplatについて。

ベクトルの生成

ベクトルといっても、実際には1行もしくは1列の行列。
ただ、扱いやすいように、いろいろマクロや関数が用意されている。

配列から生成する

配列からベクトルを生成できるようにするために、次のマクロが用意されている:

// Float/Doubleのバッファ(配列)から列ベクトルを生成する。
// (バッファの内容はコピーされる)
#define la_vector_from_float_buffer(buffer, vector_length, buffer_stride, attributes) \
    la_matrix_from_float_buffer(buffer, vector_length, 1, buffer_stride, LA_NO_HINT, attributes)
#define la_vector_from_double_buffer(buffer, vector_length, buffer_stride, attributes) \
    la_matrix_from_double_buffer(buffer, vector_length, 1, buffer_stride, LA_NO_HINT, attributes)

ただ、Swiftだと使えないみたい。

もし関数で使いたい場合、自分で次のように定義するといいかもしれない:
(もっといい方法は後述)

func la_vector_from_float_buffer(buffer: [Float],
                                 _ vector_length: la_count_t,
                                 _ buffer_stride: la_count_t,
                                 _ attributes: la_attribute_t) -> la_object_t {
  return la_matrix_from_float_buffer(buffer,
                                     vector_length, la_count_t(1),
                                     buffer_stride,
                                     la_hint_t(LA_NO_HINT),
                                     attributes)
}

func la_vector_from_double_buffer(buffer: [Double],
                                  _ vector_length: la_count_t,
                                  _ buffer_stride: la_count_t,
                                  _ attributes: la_attribute_t) -> la_object_t {
  return la_matrix_from_double_buffer(buffer,
                                      vector_length, la_count_t(1),
                                      buffer_stride,
                                      la_hint_t(LA_NO_HINT),
                                      attributes)
}

部分的なベクトルを生成する

行列と同様に、すでにあるベクトルから成分を部分的にピックアップして新しいベクトルを生成するために、次の関数が用意されている:

// 部分的なベクトルを生成して返す。
/a_object_t
la_vector_slice(
    la_object_t vector,        // 元のベクトル
    la_index_t vector_first,   // ピックアップする最初のインデックス
    la_index_t vector_stride,  // ピックアップしていくときにズラす幅
    la_count_t slice_length);  // ピックアップする要素数

逆順のベクトルを生成する

すでにあるベクトルの順序を逆順にしたベクトルを生成するマクロが用意されている:

// 逆順にしたベクトルを生成して返す。
#define la_vector_reverse(vector) \
    la_vector_slice(vector, la_vector_length(vector)-1, -1, la_vector_length(vector))

ただ、マクロなので、やはりSwiftでは使えない。
必要なら、自分で関数を定義する必要がある。

ベクトルのサイズ

ベクトルのサイズは次の関数で取得できる:

// ベクトルのサイズを返す。
// (引数には行ベクトルも列ベクトルも指定できる)
la_count_t
la_vector_length(la_object_t vector);

ベクトルの内容の取得

ベクトルの内容を見るには、行列と同様に、Floatの配列、もしくはDoubleの配列をバッファとして用意して、そこにベクトルの内容を書き込む必要がある。

もちろん、配列に行列の内容を書き込む関数を使うのでもいいんだけど、次の関数も用意されている:

// バッファとして用意した配列に行列の内容を書き込む。
// 配列は十分なサイズを用意し、inout引数になるので&をつけて渡す。
la_status_t
la_vector_to_float_buffer(
    float *buffer,             // バッファとなるFloatの配列
    la_index_t buffer_stride,  // バッファの各要素(2つ目以降)のオフセット
    la_object_t vector);       // ベクトル
la_status_t
la_vector_to_double_buffer(
    double *buffer,            // バッファとなるDoubleの配列
    la_index_t buffer_stride,  // バッファの各要素(2つ目以降)のオフセット
    la_object_t vector);       // ベクトル

buffer_strideは行列版のbuffer_row_strideとほぼ同じで、ベクトルが列ベクトルの場合、実際同じ。
ただ、この関数では、行ベクトルの場合もうまく働くようになっている。

Splatの生成

Splatはすべての要素の値が等しい行列(もしくはベクトル)。
ただ、サイズは決まっていなくて、演算のもう一方のオブジェクトのサイズに合わせて自動的に調整される。
例えば、2x3の行列と足し算をするときには2x3の行列として振舞うし、5x1のベクトルと内積を取るときには5x1のベクトルとして振舞う。

Splatを生成するための関数として、以下のものが用意されている:

// 指定されたFloat/Doubleの値でSplatを生成する。
la_object_t
la_splat_from_float(
    float scalar_value,          // Floatの値
    la_attribute_t attributes);  // 属性
la_object_t
la_splat_from_double(
    double scalar_value,         // Doubleの値
    la_attribute_t attributes);  // 属性

また、ベクトルや行列の要素の値からSplatを生成することも出来る:

// 指定されたベクトルのインデックスにある値でSplatを生成する。
la_object_t
la_splat_from_vector_element(
    la_object_t vector,        // ベクトル
    la_index_t vector_index);  // ベクトルのインデックス

// 指定された行列のインデックスにある値でSplatを生成する。
la_object_t
la_splat_from_matrix_element(
    la_object_t matrix,      // 行列
    la_index_t matrix_row,   // 行列の行
    la_index_t matrix_col);  // 行列の列

Splatからベクトル、行列を生成する

Splatからサイズを指定してベクトル、行列を生成することも出来る:

// 指定されたサイズの列ベクトルを生成する。
la_object_t
la_vector_from_splat(
    la_object_t splat,          // Splat
    la_count_t vector_length);  // 生成するベクトルのサイズ

// 指定されたサイズの行列を生成する。
la_object_t la_matrix_from_splat(
    la_object_t splat,        // Splat
    la_count_t matrix_rows,   // 生成する行列の行数
    la_count_t matrix_cols);  // 生成する行列の列数

実際の演算では、いろんな演算の一方のサイズがいろいろ変わるということはあまり考えられないので、これらの関数を使ってSplatを(固定サイズの)行列やベクトルに変換してしまった方がいいと思う。

今日はここまで!