(edu-practice-execution-02)= # 02. 縮約:二乗和と最大値 前の課題の設定を使い、`reduce` で配列の二乗和と最大値を求めます。 {ref}`第 7 章 `の総和の例を、二乗和と最大値の 2 つに広げます。 ## 実装する内容 長さ `N` の配列 `x` を `for_each` で `x[i] = util::sin(0.001 * i)` と初期化し、次の 2 つを求めます。 - 二乗和 `Σ x[i]²` - 最大値 `max x[i]` MISO の `reduce` は 1 回の呼び出しで 1 つの値を返すので、`reduce` を 2 回呼びます。 それぞれについて、次の 3 つを決めて書きます。 | | 二乗和 | 最大値 | |---|---|---| | 各点の値を返すラムダ式(`map`) | `x[i] * x[i]` | `x[i]` | | 2 つの値をまとめるラムダ式(`op`) | 和 | 大きい方 | | 初期値(`init`) | `0.0` | `std::numeric_limits::lowest()` | 初期値は、まとめても結果を変えない値(単位元)にします。 `std::numeric_limits` には `` を読み込みます。 大きい方を選ぶ処理には `util::fmax(a, b)` を使えます。 `util::max2` は `double` を `float` に丸めるので(MISO #189)、ここでは使いません。 まず `N = 1000`、次に `N = 1000000` で確かめてください。 ## 結果の確認 `x` を `copy_from` でホスト側の配列へ写し、その同じ配列から通常の `for` 文で期待値を求めます。 最大値は比べて選ぶだけなので、この配列に NaN(数として表せない値)がなければ完全一致で確かめられます。 二乗和は、足す順序によって末尾の桁が変わります。 ホスト側の期待値は、各要素を `long double` に変換してから二乗し、`long double` で足します。 `long double` が `double` より多い桁を持つかは環境によります。 今回の入力と上の `N` では、`1e-12 * max(1, |期待値|)` 以下の差を許容する比較を使い、実際の差も表示してください。 別の入力や大きさへ変えるときは、同じ許容誤差を無条件に使わず、足す回数と丸め誤差を考えて決めます。 CPU で確認できたら、{ref}`1 次元移流 `へ進めます。 ## GPU で実行する段階 {ref}`第 9 章 `を読んだ後、CUDA でも同じ課題を実行します。 GPU で作った配列をホスト側へ写し、その配列から求めた期待値と比べます。 CPU と GPU で二乗和の値がどれだけ違うかも記録します。 初期化に使う数学関数の実装と、`reduce` の足す順序は、どちらも差の原因になり得ます。