当约束在 OpenMDAO 中计算为无穷大时,最好的处理方法是什么?
What's the best way to handle when a constraint evaluates to infinity in OpenMDAO?
如问题所述,我想知道当约束或函数通常计算为无穷大时的最佳实践。
例如,假设我们限制零件的安全系数,其中该系数计算为
safety_factor = np.divide(allowable_force, applied_force)
在施加的力为 0 的情况下,safety_factor 计算为无穷大。由于当施加的力接近 0 时安全系数的极限是无穷大,因此从数学上来说这是可以检验的。假设安全系数被约束在2以上,无穷大大于2,因此应该满足约束。然而在实践中,这会导致设计变量在下一次迭代中转向 NaN。
这可以在下面的简单示例代码中看到
import openmdao.api as om
import numpy as np
class A(om.ExplicitComponent):
def setup(self):
self.add_input('x')
self.add_output('y')
self.add_output('y2')
def compute(self, inputs, outputs):
outputs['y'] = inputs['x']**2
outputs['y2']= np.inf
if __name__ == '__main__':
prob = om.Problem()
model = prob.model
idv = model.add_subsystem('idv', om.IndepVarComp(), promotes=['*'])
idv.add_output('x')
model.add_subsystem('A', A(), promotes=['*'])
model.add_design_var('x')
model.add_constraint('x', lower=2)
model.add_constraint('y2', lower=2)
model.add_objective('y')
# Setup Optimization
prob.driver = om.ScipyOptimizeDriver()
prob.driver.options['optimizer'] = 'SLSQP'
prob.driver.options['maxiter'] = 5
prob.driver.options['debug_print'] = ['desvars', 'nl_cons', 'objs', 'totals']
model.approx_totals()
prob.setup()
prob.run_driver()
print('---')
print(prob['y'])
尽管 y2 始终大于 2,但设计变量 x 的计算结果仍为 NaN。我做了一个快速修复,我只是检查约束是否无限并将其替换为通用的非常大的浮点数(例如 999999999),但这看起来不是很 pythonic,因此我很好奇最佳实践。
我认为最好的处理方法是通过重新排列方程式来避免它。而不是
con1: (allowable_force / applied_force) > 2
尝试:
con1: applied_force < 0.5 * allowable_force
那里没有除法,所以你没有除以零。
您一定要避免 inf 或 NaN 预计会经常出现在您的变量中的情况,因为您的求解器和优化器可能无法收敛或找到正确的搜索方向。
但是,对于无法避免的组件输出计算,有时我们不得不在输出太接近于零时将其替换为较小的值。这也会在导数中引入不连续性,这也可能是一个问题。
如问题所述,我想知道当约束或函数通常计算为无穷大时的最佳实践。
例如,假设我们限制零件的安全系数,其中该系数计算为
safety_factor = np.divide(allowable_force, applied_force)
在施加的力为 0 的情况下,safety_factor 计算为无穷大。由于当施加的力接近 0 时安全系数的极限是无穷大,因此从数学上来说这是可以检验的。假设安全系数被约束在2以上,无穷大大于2,因此应该满足约束。然而在实践中,这会导致设计变量在下一次迭代中转向 NaN。
这可以在下面的简单示例代码中看到
import openmdao.api as om
import numpy as np
class A(om.ExplicitComponent):
def setup(self):
self.add_input('x')
self.add_output('y')
self.add_output('y2')
def compute(self, inputs, outputs):
outputs['y'] = inputs['x']**2
outputs['y2']= np.inf
if __name__ == '__main__':
prob = om.Problem()
model = prob.model
idv = model.add_subsystem('idv', om.IndepVarComp(), promotes=['*'])
idv.add_output('x')
model.add_subsystem('A', A(), promotes=['*'])
model.add_design_var('x')
model.add_constraint('x', lower=2)
model.add_constraint('y2', lower=2)
model.add_objective('y')
# Setup Optimization
prob.driver = om.ScipyOptimizeDriver()
prob.driver.options['optimizer'] = 'SLSQP'
prob.driver.options['maxiter'] = 5
prob.driver.options['debug_print'] = ['desvars', 'nl_cons', 'objs', 'totals']
model.approx_totals()
prob.setup()
prob.run_driver()
print('---')
print(prob['y'])
尽管 y2 始终大于 2,但设计变量 x 的计算结果仍为 NaN。我做了一个快速修复,我只是检查约束是否无限并将其替换为通用的非常大的浮点数(例如 999999999),但这看起来不是很 pythonic,因此我很好奇最佳实践。
我认为最好的处理方法是通过重新排列方程式来避免它。而不是
con1: (allowable_force / applied_force) > 2
尝试:
con1: applied_force < 0.5 * allowable_force
那里没有除法,所以你没有除以零。
您一定要避免 inf 或 NaN 预计会经常出现在您的变量中的情况,因为您的求解器和优化器可能无法收敛或找到正确的搜索方向。
但是,对于无法避免的组件输出计算,有时我们不得不在输出太接近于零时将其替换为较小的值。这也会在导数中引入不连续性,这也可能是一个问题。