うまく解けないとき
直接法は初期推定から出発して少しずつ解を改善する方法なので、問題の書き方によっては収束しなかったり、 意図しない解に落ち着いたりします。よくある原因と対処を、確認しやすい順に並べます。
まず結果を確かめる
Section titled “まず結果を確かめる”result.success が False のときは、result.message に理由が入っています。
| メッセージ | 意味と対処 |
|---|---|
Iteration limit reached | 反復回数の上限で止まった。maxiter を増やす、または Problem(..., maxIterator=n) で自動の解き直し回数を増やす。 |
Positive directional derivative for linesearch | これ以上改善する方向が見つからない。制約が互いに矛盾している、スケーリングが悪い、初期推定が遠い、のいずれかが多い。 |
Inequality constraints incompatible | 制約を同時に満たす点が近くにない。境界条件と上下限の矛盾を確認する。 |
success が True でも、それは「離散化した問題の局所的な解が見つかった」という意味です。
グラフを描いて物理的におかしくないか、ノード数を変えても結果が変わらないかを確認してください。
初期推定を見直す
Section titled “初期推定を見直す”最も効果が大きいのは初期推定です。
- 境界条件を満たす形にする(始点と終点の値を合わせた
Guess.linearやGuess.cubic)。 - 終了時刻の初期推定を、だいたいの所要時間に合わせる。
- 推力のように切り替わる制御は、区間ごとに「最大」「ゼロ」など予想される値で始める。
- 簡単な問題(制約を外した問題、ノードの少ない問題)を先に解き、その解を初期推定に使う。
problem.refine(nodes)は、解いた問題から補間した初期推定を持つ新しい問題を作ります。
スケーリングをそろえる
Section titled “スケーリングをそろえる”変数の桁が大きく違うと収束しにくくなります。set_unit_states・set_unit_controls・set_unit_time で、
各変数が 1 前後になる単位を指定してください(問題の書き方)。
条件の残差も Condition の unit= で同じ程度の大きさにそろえます。
制約の書き方
Section titled “制約の書き方”- 状態や制御の単純な上下限は、
inequalityではなくset_states_bounds・set_controls_bounds・set_time_final_boundsで与える方が安定します。 - 境界条件で値を固定したノードに、同じ値の不等式制約を重ねないでください。等式と不等式が同じ点で接すると、ソルバーが混乱します。
たとえば初期質量を等式で固定したら、質量の下限・上限は
m[1:]のように最初のノードを除いて課します。 - 終了時刻には
set_time_final_boundsで下限を与えると、所要時間がゼロに縮む解を防げます。
不連続がある
Section titled “不連続がある”推力の急な切り替えや段分離を1本の多項式で表すと、前後で振動が出ます。切り替えが起きそうな時刻で区間を分けてください (例題08)。区間の境目の時刻も最適化の変数になるので、正確な時刻を知っている必要はありません。
- 少なすぎると精度が出ません。滑らかな解なら 10〜30 程度から始め、増やして結果が変わらないことを確認します。
- 多すぎると計算が遅くなり、かえって収束しにくくなることもあります。1区間の点数を増やすより、区間を分ける方が効果的な場合が多いです。
既定では目的関数と制約の微分を差分近似で計算します。運動方程式を DynamicsModel で書くと、
solve(..., derivatives="structured") で微分の構造を使った計算ができ、大幅に速くなることがあります。
大規模な問題では solver="trust-constr" も試してください。