OpenMDAO: как обрабатывать не сходящиеся точки в ExplicitComponent.compute?
Я пытался обрабатывать не сходящиеся точки в compute
метод моего ExplicitComponent
подняв AnalysisError
Как указано в разделе Как лучше всего сообщить драйверу или решателю openMDAO, что в какой-то момент невозможно оценить модель? (Первоначально я хотел оставить комментарий в этой теме, но мне не разрешили из-за моей низкой оценки репутации переполнения стека). Однако, похоже, это не решает мою проблему. Я ожидал, что ошибка будет обнаружена, точка проектирования будет пропущена, и оптимизатор продолжит оценивать другие точки, чтобы найти решение. Правильно, что ошибка обнаружена, но по какой-то причине ошибка затем пересматривается в ScipyOptimizeDriver.run
, Какова цель этого?
Это пример сценария для воспроизведения поведения:
import numpy as np
from openmdao.api import Problem, Group, IndepVarComp, ExplicitComponent, ScipyOptimizeDriver, ScipyKrylov, AnalysisError
class Test1Comp(ExplicitComponent):
def setup(self):
self.add_input('design_x', 1.0)
self.add_input('design_y', 1.0)
self.add_input('design_z', 0.5)
self.add_output('y', val=0.1)
self.add_output('z', val=0.1)
self.add_output('obj', val=0.0)
self.declare_partials(of='*', wrt='*', method='fd', form='central', step=1.0e-4)
def compute(self, inputs, outputs):
design_z = inputs['design_z']
design_x = inputs['design_x']
design_y = inputs['design_y']
# Let's assume we have a model that has problems converging around design_x = 5.0
if 0.49999 < design_x < 0.500001:
raise AnalysisError()
z = 4/(design_z + 1)
y = - design_z - 2*z
obj = (y/5.833333 - design_x)**2 + z/2.666667*100*(design_y - design_x**2)**2
outputs["z"] = z
outputs["y"] = y
outputs['obj'] = obj
if __name__ == "__main__":
prob = Problem()
model = prob.model = Group()
model.add_subsystem('d1', IndepVarComp('design_x', 1.0))
model.add_subsystem('d2', IndepVarComp('design_y', 1.0))
model.add_subsystem('d3', IndepVarComp('design_z', 0.5))
model.add_subsystem('comp', Test1Comp())
model.connect('d1.design_x', 'comp.design_x')
model.connect('d2.design_y', 'comp.design_y')
model.connect('d3.design_z', 'comp.design_z')
prob.driver = ScipyOptimizeDriver()
prob.driver.options["optimizer"] = 'SLSQP'
prob.driver.options['tol'] = 1e-8
model.add_design_var("d1.design_x", lower=0.5, upper=1.5)
model.add_design_var("d2.design_y", lower=0.5, upper=1.5)
model.add_design_var("d3.design_z", lower=0.0, upper=1.0)
model.add_objective('comp.obj')
model.linear_solver = ScipyKrylov()
model.linear_solver.options['maxiter'] = int(1200)
model.linear_solver.options['restart'] = int(20)
# prob.model.approx_totals()
prob.setup()
prob.run_driver()
print(prob['comp.y'])
print(prob['comp.z'])
Кроме того, глядя на ExplicitComponent._solve_nonlinear
, который вызывает метод ExplicitComponent.compute
мне кажется, что естественным способом сообщения OpenMDAO о том, что точка не сходится, было бы иметь ExplicitComponent.compute
вернуть True
, Посмотрите исходный код для метода:
def _solve_nonlinear(self):
"""
Compute outputs. The model is assumed to be in a scaled state.
Returns
-------
boolean
Failure flag; True if failed to converge, False is successful.
float
absolute error.
float
relative error.
"""
super(ExplicitComponent, self)._solve_nonlinear()
with Recording(self.pathname + '._solve_nonlinear', self.iter_count, self):
with self._unscaled_context(
outputs=[self._outputs], residuals=[self._residuals]):
self._residuals.set_const(0.0)
failed = self.compute(self._inputs, self._outputs)
return bool(failed), 0., 0.
Таким образом, кто-то может уточнить, каков рекомендуемый способ обработки не сходящихся вычислений в ExplicitComponent.compute
?
1 ответ
Я посмотрел на ваш код, и вы все правильно указали, как сказать оптимизатору, что компонент не может оценить проектную точку. Проблема в том, что scipy.minimize (который является оптимизатором подScipyOptimizeDriver
) не знает, что делать, когда он достигает точки сбоя (кроме создания исключения), и не имеет возможности сообщить об ошибке обратно (по крайней мере, насколько мне известно).
Тем не мение, pyOptSparseDriver
может сделать что-то, когда точка не удалась: попытка продвинуться снова в направлении градиента, но с меньшим размером шага. Я взял твой код и обменял ScipyOptimizeDriver
с pyOptSparseDriver
Я использовал оптимизатор "SLSQP" в этом пакете, и он прекрасно справился с этой проблемной точкой, достигнув, как мне кажется, хорошего оптимума:
[0.69651727]
[-5.]
[2.]
Мой код опции драйвера выглядит так:
prob.driver = pyOptSparseDriver()
prob.driver.options["optimizer"] = 'SLSQP'
prob.driver.opt_settings['ACC'] = 1e-8
Если у вас еще нет pyoptsparse, который является отдельным пакетом, который не поддерживается нами, вы можете получить его по https://github.com/mdolab/pyoptsparse - и вы можете собрать и установить его с помощью:
python setup.py build
python setup.py install
По вашему другому вопросу, я просмотрел наш код и обнаружил, что флаг сбоя возвращается _solve_nonlinear
никогда не используется ни для чего. Таким образом, единственный способ сообщить о не сходящемся статусе драйверу или решателю более высокого уровня - это вызвать AnalysisError. Решатели могут создать АЕ, когда они не сходятся, если для параметра "err_on_maxiter" в его параметрах установлено значение "Истина" (по умолчанию установлено значение "Ложь").
В заключение, я думаю, что пока используются наши механизмы обработки ошибок, мы не думаем обо всем и всегда открыты для предложений по улучшению.