解析ソルバー HiGHS C++ソースにおいて、高速化技術、C++の高速化・アルゴリズムの高速化、その他すべての高速化について、100個、あげて下さい。
引用 Gemini
HiGHSは、現在世界で最も高速なオープンソースの線形計画法(LP)および混合整数計画法(MIP)ソルバーの一つです。その高速化の秘密は、教科書的なアルゴリズムの実装にとどまらず、**「計算幾何学的なデータ構造の最適化」と「徹底したC++の低レイヤ最適化」**の両面にあります。
100個のポイントを、アルゴリズム、実装技術、データ構造などのカテゴリに分けて解説します。
1. 改良単体法(Simplex Method)の高速化
単体法の核心は「いかに少ない反復回数で、いかに速く1ステップを回すか」にあります。
主対対偶単体法(Dual Simplex)の優先利用: LPでは一般的に対偶法の方が高速で安定しています。
Steepest Edge Pricing: 勾配が最も急な方向を選ぶことで、反復回数を劇的に削減します。
Devex Pricing: Steepest Edgeの近似版。計算負荷を抑えつつ効率的なパスを選択します。
Btran/Ftranの疎性(Sparsity)活用: ベクトルの非ゼロ要素のみを計算対象にします。
Hyper-sparsityの利用: 解のベクトルが極めて疎な場合、専用の高速ルーチンへ切り替えます。
LU分解のMarkowitzピボッティング: フィルイン(非ゼロ要素の増加)を最小限に抑えます。
Forrest-Tomlinアップデート: 基底行列の更新を効率的に行い、再分解の頻度を下げます。
Harrisの許容誤差選択: 数値的不安定性を回避し、無駄な反復を防ぎます。
Crash手続き: 最初に「良さそうな」基底を高速に見つけ、反復のスタートラインを上げます。
定期的な再分解(Refactorization): 誤差の蓄積を防ぎ、計算効率をリセットします。
Primal-Dual遷移: 必要に応じて主問題と対偶問題を切り替えて解きます。
ピボット選択のタイブレーク戦略: 数値的安定性と疎性のバランスをとります。
劣決定系の高速処理: 変数が多い問題特有の最適化。
過決定系の高速処理: 制約が多い問題特有の最適化。
スラック変数の効率的表現: メモリと計算の両面でスラックを特別扱いします。
2. 内点法(Interior Point Method / IPM)
大規模で疎な行列に対して威力を発揮します。
メカニズムとしての予測子・修正子法(Predictor-Corrector): 収束を早めます。
KKTシステムの正則化: 数値的な安定性を確保し、反復の質を上げます。
Cholesky分解の順序付け(Ordering): AMDやMETISを用いたフィルイン抑制。
疎なコラムの分離処理: 密な列が計算を遅延させるのを防ぐため、シャーマン・モリソンの公式などで別処理します。
Iterative Refinement: 線形システムの解の精度を高速に向上させます。
クロスオーバー・アルゴリズム: 内点法の解から単体法の基底解へ高速に変換します。
3. 混合整数計画法(MIP)の高速化
離散的な決定が必要なMIPは、探索空間の削減が命です。
強分岐(Strong Branching): 実際に分岐してみて、どの変数が最も目的関数に影響するかを事前に評価します。
信頼性分岐(Reliability Branching): 過去の強分岐の結果を統計的に再利用します。
擬似コスト(Pseudo-costs): 分岐による目的関数の変化を予測します。
ドメイン伝播(Domain Propagation): 一つの変数の値が決まった際、他の変数の範囲を即座に更新します。
衝突分析(Conflict Analysis): 不可能な組み合わせから新しい制約(カット)を生成します。
Gomory Mixed Integer Cuts: 数学的な切り口を追加して探索空間を削ります。
MIR Cuts (Mixed Integer Rounding): 丸めを利用した強力なカット。
Clique Cuts: 変数間の相互排他関係を利用。
Flow Cover Cuts: ネットワークフロー構造を抽出してカットを生成。
Feasibility Pump: 素早く実行可能解(暫定解)を見つけるための強力なヒューリスティック。
Local Branching: 暫定解の近傍を集中攻撃します。
RINS (Relaxation Induced Neighborhood Search): 連続緩和解と暫定解の共通点を利用。
Symmetry Breaking: 問題の対称性を検知し、重複した探索を排除します。
Node Selection (Best Bound First): 期待値の高いノードから探索。
Node Selection (Depth First): メモリ節約と暫定解発見を優先。
Lazy Constraints: 必要になるまで制約を追加しない仕組み。
User Heuristicsのフック: ユーザー定義の知識を高速化に組み込む口。
4. 前処理(Presolve)とスケーリング
問題を解く前に、小さく、解きやすくします。
Row/Column Singletonの除去: 変数や制約が一つしかない場合の即時解決。
冗長な制約の検出: 他の制約に内包される式を削除。
変数の固定(Fixing variables): 範囲制約から値が確定する変数を排除。
Bound Strengthening: 制約を組み合わせて変数の上下限を絞り込みます。
Coefficient Tightening: 係数をより厳しいものに書き換えます。
Dual Redundancy: 対偶問題の視点から不要な変数を削除。
Aggregator: 変数を別の変数の式で置き換え、次元を削減します。
Equilibration Scaling: 行列の要素の桁数を揃え、数値安定性を向上。
Geometric Mean Scaling: スケーリング係数の決定手法。
Ruiz Scaling: 繰り返し計算による高度なスケーリング。
5. C++ 実装レベルの高速化
HiGHSが「モダン」と言われる所以です。
キャッシュ効率の良いデータレイアウト: 疎行列をCSC(Compressed Sparse Column)形式で保持。
メモリプールの活用: 頻繁な new/delete を避け、フラグメンテーションを防止。
std::vector::reserve() の徹底: 再確保によるコピーコストを排除。
仮想関数の抑制: ホットパス(頻繁に呼ばれる箇所)での動的ディスパッチを回避。
インライン化(Inlining): 小さな関数をコンパイル時に展開。
テンプレートによる特殊化: 型に応じた最適コードの生成。
SIMDの活用: 並列命令を用いたベクトル演算の加速。
ムーブセマンティクスの活用: 大きな行列データの所有権移動を高速化。
アライメントの最適化: データのメモリ境界を揃えてロードを高速化。
const 伝播の徹底: コンパイラの最適化ヒントを最大化。
例外処理の最小化: パフォーマンス低下を防ぐため、正常系での例外利用を避ける。
分岐予測の最適化: if 文の順序を統計的な確率に基づいて配置。
ループ展開(Loop Unrolling): ループのオーバーヘッドを削減。
ポインタ・エイリアシングの排除: __restrict 的なアプローチでメモリアクセスを最適化。
カスタム・アロケータ: 特定のデータ構造に最適化されたメモリ管理。
6. 並列化・マルチスレッド(Parallelism)
OpenMPによる並列計算: 行列演算やヒューリスティックの並列実行。
並列MIP探索: 複数のCPUコアで異なるサブツリーを同時に探索。
決定論的並列化(Deterministic Parallelism): 実行のたびに結果が変わらないよう制御。
スレッドセーフなロギング: ログ出力がボトルネックにならないよう設計。
タスク並列: 独立した前処理タスクを同時に実行。
アトミック演算の利用: ミューテックスを避け、軽量な同期を実現。
7. 疎行列・線形代数ライブラリの最適化
Sparse Accumulator (SPA): 疎なベクトル加算を効率化するデータ構造。
リンクリストによる非ゼロ要素管理: 行列の更新時に要素移動を最小化。
ビットセットによるフラグ管理: 大量の変数状態を高速にスキャン。
高速な絶対値・比較演算: CPU命令を直接叩く工夫。
再利用可能な作業配列: 関数の呼び出しごとにメモリを確保せず、ワークスペースを使い回す。
行列の転置の高速化: キャッシュを意識した転置アルゴリズム。
8. ソフトウェア設計とアーキテクチャ
モジュール化されたソルバー構造: 必要最小限の機能だけを呼び出す。
遅延評価(Lazy Evaluation): 必要になるまで計算しない。
ロギングレベルの動的制御: 不要な文字列操作をスキップ。
プロファイリングに基づいたホットスポット修正: ボトルネックの継続的な排除。
回帰テストによるパフォーマンス監視: 修正による速度低下を即座に検知。
9. 特殊な問題構造への対応
SOS (Special Ordered Sets) の処理: 特定の変数集合に対する専用ロジック。
区分線形関数の高速処理: 近似を用いた最適化。
ネットワーク構造の自動抽出: グラフ理論的アプローチで高速化。
10. 数値的安定性と効率のトレードオフ(残り17個)
動的ピボット耐性(Pivot Tolerance): 状況に応じて精度と速度を調整。
スラック変数の再利用: 基底変更時の計算量削減。
整数性の許容誤差の自動調整: 無駄なノード探索を防止。
高速なInfinity表現: 計算のオーバーフローを防ぎつつ比較を単純化。
行列のスパース・シンボリック分解の再利用: 数値だけ変わる場合の再計算抑制。
列生成(Column Generation)の口: 大規模問題への拡張性。
ヒストグラムを用いた分岐変数選択: 統計的データの蓄積。
メモリ使用量の動的監視: スワップ発生を防ぎ、速度低下を回避。
SIMD化されたドット積計算: 疎ベクトルと密ベクトルの積を加速。
プリフェッチ命令の挿入: 次に使うデータを事前にキャッシュへ。
関数コールのインライン深度の調整: バイナリサイズと速度のバランス。
std::unordered_map ではなく独自のハッシュテーブル: 高速なルックアップ。
行列要素のソート維持: 二分探索による高速アクセスを可能に。
数値不安定時の自動再スタート: 泥沼の計算を避け、クリーンな状態で再開。
実行時CPU機能検知: AVX2などの命令を動的に使い分け。
ビット操作による状態フラグの圧縮: キャッシュラインに多くの情報を詰め込む。
ソースコードの「ミニマリズム」: 複雑さを排除し、コンパイラの最適化を促す設計。
まとめ
HiGHSが速いのは、単に「Simplexがすごい」からではなく、**「メモリの1バイト、CPUの1サイクルを惜しむC++の執念」と「数理最適化の高度な理論」**が、密に統合されているからです。ソースコードを読む際は、特に src/util/HighsSparseMatrix.cpp や src/lp/HEkk.cpp (Simplexの核心部)にこれらのテクニックが凝縮されています