# 高速化のための Tips :::{container} prog-cpp このページでは、効率的な Hi-QUBO プログラムを書くための一般的な注意点とベストプラクティスを紹介します。Hi-QUBO は求解の前に式を記号的に構築するため、式の構築方法がパフォーマンスに大きく影響します。 ::: :::{container} prog-python このページでは、効率的な pyQBPP プログラムを書くための一般的な注意点とベストプラクティスを紹介します。pyQBPP は求解の前に式を記号的に構築するため、式の構築方法がパフォーマンスに大きく影響します。 ::: ## Tip 1: ループで式を構築する際は `= +` ではなく `+=` を使う ループで項を累積する場合は、常に複合代入演算子 `+=` を使用してください: :::{container} prog-cpp ```{include} ../../programFiles/markDown/advanced/performance-tips-program.md :start-after: :end-before: ``` ::: :::{container} prog-python ```{include} ../../programFiles/markDown/advanced/performance-tips-program.md :start-after: :end-before: ``` ::: > 理由: `+` 演算子は、結果が別の変数に代入される可能性があるため(例:`g = f + x[i]`)、 新しい `Expr` オブジェクトを作成する必要があります。 そのため `f = f + x[i]` は毎回既存の全項をコピーし、合計で $O(N^2)$ のコストになります。 一方 `f += x[i]` はコピーなしで既存の式に直接追加します。 `-=` と `*=` についても同様です。 ## Tip 2: `f * f` ではなく `sqr()` を使う :::{container} prog-cpp ```{include} ../../programFiles/markDown/advanced/performance-tips-program.md :start-after: :end-before: ``` ::: :::{container} prog-python ```{include} ../../programFiles/markDown/advanced/performance-tips-program.md :start-after: :end-before: ``` ::: > 理由: `sqr()` は必要なメモリ量を事前に正確に確保する専用の展開アルゴリズムを使用し、 中間的な再割り当てを一切行いません。 ## Tip 3: `+=` の累積ではなく `sum()` を使う :::{container} prog-cpp ```{include} ../../programFiles/markDown/advanced/performance-tips-program.md :start-after: :end-before: ``` ::: :::{container} prog-python ```{include} ../../programFiles/markDown/advanced/performance-tips-program.md :start-after: :end-before: ``` ::: > 理由: `sum()` は共有ライブラリ内部で全要素を一括処理するため、 繰り返しの境界越え呼び出しのオーバーヘッドを回避できます。 さらに、サイズが大きい配列の場合、`sum()` は共有ライブラリ内部でマルチスレッド並列処理を 自動的に行うため、逐次ループでは得られない高速化が可能です。 ## Tip 4: 要素ごとのループではなく Array 演算を使う :::{container} prog-cpp Hi-QUBO は Array 同士および Array とスカラーの演算をサポートしています。 要素ごとの明示的な `for` ループの代わりにこれらを使用してください: ::: :::{container} prog-python pyQBPP は Array 同士および Array とスカラーの演算をサポートしています。 要素ごとの明示的な `for` ループの代わりにこれらを使用してください: ::: :::{container} prog-cpp ```{include} ../../programFiles/markDown/advanced/performance-tips-program.md :start-after: :end-before: ``` ::: :::{container} prog-python ```{include} ../../programFiles/markDown/advanced/performance-tips-program.md :start-after: :end-before: ``` ::: > 理由: Array 演算は共有ライブラリ内部で一括処理されるため、 要素ごとの境界越え呼び出しのオーバーヘッドを排除できます。 さらに、サイズが大きい配列の場合、これらの演算はマルチスレッド並列処理を 自動的に行うため、逐次ループに比べて大幅な高速化が可能です。 ## Tip 5: `replace()` には全マッピングをまとめて渡す :::{container} prog-cpp ```{include} ../../programFiles/markDown/advanced/performance-tips-program.md :start-after: :end-before: ``` ::: :::{container} prog-python ```{include} ../../programFiles/markDown/advanced/performance-tips-program.md :start-after: :end-before: ``` ::: > 理由: `replace()` の各呼び出しは式の全項を走査し、新しい式を生成します。 1つのマッピングで $N$ 回呼び出すと式が $N$ 回走査され、さらに変数→式の置換後に式が膨張するため、 後の反復ほど処理が重くなります。 全マッピングをまとめて渡すと、ハッシュマップを使って1回の走査で全変数を同時に置換できます。 ## Tip 6: `simplify()` は式の構築が完了してから呼ぶ :::{container} prog-cpp ```{include} ../../programFiles/markDown/advanced/performance-tips-program.md :start-after: :end-before: ``` ::: :::{container} prog-python ```{include} ../../programFiles/markDown/advanced/performance-tips-program.md :start-after: :end-before: ``` ::: > 理由: `simplify()` は全項をソートして同類項をマージするため、$O(N \log N)$ のコストがかかります。ループ内で呼び出すと合計で $O(N² \log N)$ の処理量になります。`simplify` は通常、ソルバーに式を渡す直前に1回だけ必要です。 > 例外: `sqr()` や `*` のような重い演算が後に続く場合、 事前に `simplify` して項数を削減すると、全体の計算が劇的に速くなることがあります: :::{container} prog-cpp ```{include} ../../programFiles/markDown/advanced/performance-tips-program.md :start-after: :end-before: ``` ::: :::{container} prog-python ```{include} ../../programFiles/markDown/advanced/performance-tips-program.md :start-after: :end-before: ``` ::: 一般に、項数に依存するコストの高い演算(`sqr()`、`*`、`replace()` など)の前に `simplify()` を呼ぶことで、同類項が多い場合に大幅な高速化が得られます。