コンテンツにスキップ

うまく解けないとき

直接法は初期推定から出発して少しずつ解を改善する方法なので、問題の書き方によっては収束しなかったり、 意図しない解に落ち着いたりします。よくある原因と対処を、確認しやすい順に並べます。

result.success が False のときは、result.message に理由が入っています。

メッセージ意味と対処
Iteration limit reached反復回数の上限で止まった。maxiter を増やす、または Problem(..., maxIterator=n) で自動の解き直し回数を増やす。
Positive directional derivative for linesearchこれ以上改善する方向が見つからない。制約が互いに矛盾している、スケーリングが悪い、初期推定が遠い、のいずれかが多い。
Inequality constraints incompatible制約を同時に満たす点が近くにない。境界条件と上下限の矛盾を確認する。

success が True でも、それは「離散化した問題の局所的な解が見つかった」という意味です。 グラフを描いて物理的におかしくないか、ノード数を変えても結果が変わらないかを確認してください。

最も効果が大きいのは初期推定です。

  • 境界条件を満たす形にする(始点と終点の値を合わせた Guess.linear や Guess.cubic)。
  • 終了時刻の初期推定を、だいたいの所要時間に合わせる。
  • 推力のように切り替わる制御は、区間ごとに「最大」「ゼロ」など予想される値で始める。
  • 簡単な問題(制約を外した問題、ノードの少ない問題)を先に解き、その解を初期推定に使う。 problem.refine(nodes) は、解いた問題から補間した初期推定を持つ新しい問題を作ります。

変数の桁が大きく違うと収束しにくくなります。set_unit_states・set_unit_controls・set_unit_time で、 各変数が 1 前後になる単位を指定してください(問題の書き方)。 条件の残差も Condition の unit= で同じ程度の大きさにそろえます。

  • 状態や制御の単純な上下限は、inequality ではなく set_states_bounds・set_controls_bounds・set_time_final_bounds で与える方が安定します。
  • 境界条件で値を固定したノードに、同じ値の不等式制約を重ねないでください。等式と不等式が同じ点で接すると、ソルバーが混乱します。 たとえば初期質量を等式で固定したら、質量の下限・上限は m[1:] のように最初のノードを除いて課します。
  • 終了時刻には set_time_final_bounds で下限を与えると、所要時間がゼロに縮む解を防げます。

推力の急な切り替えや段分離を1本の多項式で表すと、前後で振動が出ます。切り替えが起きそうな時刻で区間を分けてください (例題08)。区間の境目の時刻も最適化の変数になるので、正確な時刻を知っている必要はありません。

  • 少なすぎると精度が出ません。滑らかな解なら 10〜30 程度から始め、増やして結果が変わらないことを確認します。
  • 多すぎると計算が遅くなり、かえって収束しにくくなることもあります。1区間の点数を増やすより、区間を分ける方が効果的な場合が多いです。

既定では目的関数と制約の微分を差分近似で計算します。運動方程式を DynamicsModel で書くと、 solve(..., derivatives="structured") で微分の構造を使った計算ができ、大幅に速くなることがあります。 大規模な問題では solver="trust-constr" も試してください。