【问题标题】:Performing gradient-based optimization with intermediate discrete variables in OpenMDAO在 OpenMDAO 中使用中间离散变量执行基于梯度的优化
【发布时间】:2021-12-17 21:44:55
【问题描述】:

我们有一位用户想要使用基于梯度的方法解决具有中间离散变量的优化问题。他们遇到了this error。我知道我们可以重组问题以不使用离散变量,但鉴于离散变量不会改变,我是否也可以将此错误视为警告?或者是否存在衍生品无法正确传播的根本原因。为了清楚起见,我们在模型级别使用了 approx_totals。

这是一个展示这一点的小测试用例:

import openmdao.api as om
import numpy as np


class DiscreteComp(om.ExplicitComponent):
    def setup(self):
        self.add_input('a', 2.)
        self.add_discrete_output('b', val=0)

    def compute(self, inputs, outputs, discrete_inputs, discrete_outputs):
        discrete_outputs['b'] = 2 * inputs['a']
        
class DummyComp(om.ExplicitComponent):
    def setup(self):
        self.add_input('a', 2.)
        self.add_discrete_input('b', 1)
        self.add_output('c')

    def compute(self, inputs, outputs, discrete_inputs, discrete_outputs):
        b = discrete_inputs['b']
        outputs['c'] = inputs['a']**2 * b

prob = om.Problem()

prob.model.add_subsystem('discrete_comp', DiscreteComp(), promotes=['*'])
prob.model.add_subsystem('dummy_comp', DummyComp(), promotes=['*'])

prob.driver = om.pyOptSparseDriver()
prob.driver.options['optimizer'] = 'SLSQP'

prob.model.add_design_var('a', lower=-10, upper=10)
prob.model.add_objective('c')

prob.model.approx_totals()

prob.setup()

# run the optimization
prob.run_driver()

给出这个输出:

Traceback (most recent call last):
  File "tmp.py", line 39, in <module>
    prob.run_driver()
  File "/home/john/anaconda3/envs/weis/lib/python3.8/site-packages/openmdao/core/problem.py", line 685, in run_driver
    return self.driver.run()
  File "/home/john/anaconda3/envs/weis/lib/python3.8/site-packages/openmdao/drivers/pyoptsparse_driver.py", line 480, in run
    raise self._exc_info
  File "/home/john/anaconda3/envs/weis/lib/python3.8/site-packages/openmdao/drivers/pyoptsparse_driver.py", line 643, in _gradfunc
    sens_dict = self._compute_totals(of=self._quantities,
  File "/home/john/anaconda3/envs/weis/lib/python3.8/site-packages/openmdao/core/driver.py", line 892, in _compute_totals
    total_jac = _TotalJacInfo(problem, of, wrt, use_abs_names,
  File "/home/john/anaconda3/envs/weis/lib/python3.8/site-packages/openmdao/core/total_jac.py", line 209, in __init__
    raise RuntimeError("Total derivative %s '%s' depends upon "
RuntimeError: Total derivative with respect to '_auto_ivc.v0' depends upon discrete output variables ['discrete_comp.b'].

【问题讨论】:

    标签: openmdao


    【解决方案1】:

    根据您的示例,OpenMDAO 引发此错误是有原因的。离散输出不通过与连续输出相同的数据系统。只有连续输出才能访问导数系统。虽然可以在这里使用这个中间离散变量,并对整个事物进行有限差分,但您只能侥幸成功,因为您确实在那里进行了连续计算。

    如果输出确实是离散的,那么在计算上进行导数或进行有限差分逼近在数学上是无效的。这就是 OpenMDAO 引发错误的原因。

    诚然,在某些极端情况下,您可能会争辩说 OpenMDAO 应该让您这样做。例如,如果您的输出 c 计算为: outputs['c'] = floor(inputs['a']) 并且您知道您会将 a 的值限制在 1 和 2 之间。您知道 c 将始终恰好为 1。因此您可以说区分它是有效的,因为离散变量的值永远不会改变因此函数在这些范围内是可微的。

    如果您想使用 OpenMDAO 进行有限差分,您有两种选择:

    1. 尽管它是一个离散值,但还是将其列为连续值。这适用于 OpenMDAO 的有效性检查,但完全由您来确保这些值不会随着您所采用的导数范围而改变。如果您决定实现解析导数,请不要为离散输出声明任何关于任何事物的导数。
    2. 将离散计算移至setup 阶段并将信息作为选项传递。这迫使您绝对尊重“永不改变”的范式,因为设置只发生一次。它确实需要重新设计。
    3. 如果您不喜欢 OpenMDAO 的保姆,您可以随时将错误注释掉:) 显然,这样做需要您自担风险……但这就是开源软件的价值之一!

    我个人的建议是选项 #2。这确实是最好的整体设计,并迫使您确保离散计算在选择期间永远不会改变。

    【讨论】:

    • 谢谢,贾斯汀!这是关于可能解决方案的非常有用的演练。
    猜你喜欢
    • 2021-12-11
    • 1970-01-01
    • 1970-01-01
    • 2022-08-06
    • 2019-02-04
    • 2013-05-26
    • 1970-01-01
    • 1970-01-01
    • 2015-09-28
    相关资源
    最近更新 更多