使用相同的输入对外部代码组件进行多次评估? OpenMDAO
Multiple Evaluations of the External Code Component With the same input? OpenMDAO
我正在使用 SLSQP、外部代码、显式 comp 和 FD 微分设置包含三个组件的优化例程。
- Comp1:IndepVarComp 输出 ='x'
- Comp2 : 外部代码输入 ='x' / 输出 = 'f','g' - 导数
='fd'
- Comp3:显式 Comp 输入 ='f','g' /
输出 = 'obj' - 解析
衍生品声明
更具体地说,Comp2 类似于抛物面示例,而 Comp3 只是一个线性函数。没有限制。
如果我在 Comp2 上使用中央 FD,它会在更改之前使用相同的输入值 (x) 评估外部代码 4 次,而前向 FD 是两次。我模糊地理解优化器试图做什么但是
重复相同的计算只是一种计算负担。
我的补救方法是用相应的输入名称保存输出文件,并在第二次迭代中检查此文件是否存在,如果存在则跳过计算并从文件中读入。这很好用。
我的问题是:
- --- 我想知道是否应该有一种嵌入式方法来避免 re-evaluating 相同的功能?
- --- 尽管我声称我有点了解优化器的作用,但尚不清楚为什么它会尝试在不步进的情况下找到梯度。
重要说明:
如果 Comp2 有两个输入,一个输出,则不会发生这种情况。
如果 Comp2 有两个输入,两个输出,它会再次发生
下面是前几次迭代的示例输出。
ExtInX = [10.], , ExtOutF = 79.0, ExtOutG = 373.0
OBJECTIVE 589362.0
----------------------------------------------------------------------
ExtInX = [10.], , ExtOutF = 79.0, ExtOutG = 373.0
OBJECTIVE 589362.0
----------------------------------------------------------------------
ExtInX = [10.], , ExtOutF = 79.0, ExtOutG = 373.0
ExtInX = [10.], , ExtOutF = 79.0, ExtOutG = 373.0
ExtInX = [10.], , ExtOutF = 79.0, ExtOutG = 373.0
ExtInX = [10.], , ExtOutF = 79.0, ExtOutG = 373.0
ExtInX = [10.000001], , ExtOutF = 79.00001700000098, ExtOutG = 373.0001500000209
ExtInX = [9.999999], , ExtOutF = 78.99998300000101, ExtOutG = 372.9998500000211
ExtInX = [8.07102609], , ExtOutF = 49.928382692675996, ExtOutG = 154.61605669448198
OBJECTIVE 250797.01369955452
----------------------------------------------------------------------
**ExtInX = [8.07102609], , ExtOutF = 49.928382692675996, ExtOutG = 154.61605669448198
ExtInX = [8.07102609], , ExtOutF = 49.928382692675996, ExtOutG = 154.61605669448198
ExtInX = [8.07102609], , ExtOutF = 49.928382692675996, ExtOutG = 154.61605669448198
ExtInX = [8.07102609], , ExtOutF = 49.928382692675996, ExtOutG = 154.61605669448198**
ExtInX = [8.07102709], , ExtOutF = 49.92839583472901, ExtOutG = 154.61613684041134
ExtInX = [8.07102509], , ExtOutF = 49.92836955062501, ExtOutG = 154.61597654858318
ExtInX = [4.88062761], , ExtOutF = 18.178645674383997, ExtOutG = 21.29321703417343
OBJECTIVE 38811.35361617729
第一个答案之后
感谢您的回答。
据我了解,在 2.2 中,缓存必须由用户完成,并且
人们可以用不同的方式做到这一点。这可以。
而你在 2.3 中提到的改进似乎解决了一件事。
开始时的单个额外调用。我更关心的是
在迭代期间使用相同输入进行多次调用。
我正在添加下面的四个代码补丁。应该能够 运行 优化器
只要其他人在同一个文件夹中。
在这种情况下,外部代码是纯粹的 python。但同时我是
开发一个使用 'actual' 外部代码的优化器。又是
虽然包裹在 python 中。所以一些系统调用被发送到可执行文件
python 处理来自外部代码的输出,python 做一些 post
处理并输出由 OpenMDAO 外部代码读取的文本文件
成分。所以不确定那个是否算作纯 python。
关于约束:在下面的代码中有 none。在其他代码中
我已经解释过汽车部件有一些使用寿命限制
(例如 xx,较低=20)。
代码:
Optimizer.py : Driver
和其他 3 个 .py 文件
https://gist.github.com/anonymous/2c9d5d182cbb24a2334c97b57a954802
在 OpenMDAO v2.2 中没有内置处理结果缓存的方法。所以你必须在那里推出你自己的解决方案。您可以在内存中进行缓存并仅存储最后已知的输入值,而不是保存完整的输出文件。如果它们没有改变,您可以将输出值保留在原处。
在 OpenMDAO V2.2 中,驱动程序被编码为在开始优化之前进行一个额外的函数调用。这是因为 OpenMDAO 处理线性约束的方式。它将 pre-compute 与线性约束相关联的导数并存储它们。
在 OpenMDAO V2.3 中,我们修改了驱动程序,因此它们仅在如果您的问题中有线性约束时才进行此额外调用。这样,至少可以解释您的一个额外函数调用。
在检查您的测试问题后,OpenMDAO 是 finite-differencing 组件的方式似乎确实存在错误。即使您使用转发模式,问题也会出现,并且它似乎与您声明部分的方式有关。
您声明它们的方式没有任何问题,但是使用另一种声明方法我能够使其正常运行:
from openmdao.api import ExternalCode
import numpy as np
class ParaboloidExternalCode(ExternalCode):
def setup(self):
self.add_input('x', val=0.0)
self.add_input('y', val=0.0)
self.add_output('f_xy', val=0.0)
self.add_output('g_xy', val=0.0)
self.input_file = 'paraboloid_input.dat'
self.output_file = 'paraboloid_output.dat'
# providing these is optional; the component will verify that any input
# files exist before execution and that the output files exist after.
self.options['external_input_files'] = [self.input_file,]
self.options['external_output_files'] = [self.output_file,]
self.options['command'] = [
'python', 'extcode_paraboloid.py', self.input_file, self.output_file
]
# self.declare_partials(of='*', wrt='*', method='fd', form='central', step=1e-6)
self.declare_partials(of='f_xy', wrt=['x','y'], method='fd', form='central', step=1e-6)
self.declare_partials(of='g_xy', wrt=['x','y'], method='fd', form='central', step=1e-6)
def compute(self, inputs, outputs):
x = inputs['x']
y = inputs['y']
# generate the input file for the paraboloid external code
with open(self.input_file, 'w') as input_file:
input_file.write('%f\n%f\n' % (x,y))
# the parent compute function actually runs the external code
super(ParaboloidExternalCode, self).compute(inputs, outputs)
file_contents = np.loadtxt(self.output_file)
outputs['f_xy'] = file_contents [0]
outputs['g_xy'] = file_contents [1]
print('ExtInX = {}, ExtInY = {},, ExtOutF_XY = {}, ExtOutG_XY = {} '.format(x,y,file_contents [0],file_contents[1]) )
我摆脱了 of='*', wrt='*'
并将其替换为两个单独的调用,这些调用明确命名了变量。我还能够在完全没有外部代码的情况下使用 in-memory 抛物面重现此问题。我已将该错误输入到开发积压列表中,我们会解决它。同时,此解决方法解决了问题。
请注意,OpenMDAO V2.2 中存在一个导致额外评估的错误。它已通过 OpenMDAO PR # 553
修复
我正在使用 SLSQP、外部代码、显式 comp 和 FD 微分设置包含三个组件的优化例程。
- Comp1:IndepVarComp 输出 ='x'
- Comp2 : 外部代码输入 ='x' / 输出 = 'f','g' - 导数 ='fd'
- Comp3:显式 Comp 输入 ='f','g' / 输出 = 'obj' - 解析 衍生品声明
更具体地说,Comp2 类似于抛物面示例,而 Comp3 只是一个线性函数。没有限制。
如果我在 Comp2 上使用中央 FD,它会在更改之前使用相同的输入值 (x) 评估外部代码 4 次,而前向 FD 是两次。我模糊地理解优化器试图做什么但是 重复相同的计算只是一种计算负担。
我的补救方法是用相应的输入名称保存输出文件,并在第二次迭代中检查此文件是否存在,如果存在则跳过计算并从文件中读入。这很好用。
我的问题是:
- --- 我想知道是否应该有一种嵌入式方法来避免 re-evaluating 相同的功能?
- --- 尽管我声称我有点了解优化器的作用,但尚不清楚为什么它会尝试在不步进的情况下找到梯度。
重要说明: 如果 Comp2 有两个输入,一个输出,则不会发生这种情况。
如果 Comp2 有两个输入,两个输出,它会再次发生
下面是前几次迭代的示例输出。
ExtInX = [10.], , ExtOutF = 79.0, ExtOutG = 373.0
OBJECTIVE 589362.0
----------------------------------------------------------------------
ExtInX = [10.], , ExtOutF = 79.0, ExtOutG = 373.0
OBJECTIVE 589362.0
----------------------------------------------------------------------
ExtInX = [10.], , ExtOutF = 79.0, ExtOutG = 373.0
ExtInX = [10.], , ExtOutF = 79.0, ExtOutG = 373.0
ExtInX = [10.], , ExtOutF = 79.0, ExtOutG = 373.0
ExtInX = [10.], , ExtOutF = 79.0, ExtOutG = 373.0
ExtInX = [10.000001], , ExtOutF = 79.00001700000098, ExtOutG = 373.0001500000209
ExtInX = [9.999999], , ExtOutF = 78.99998300000101, ExtOutG = 372.9998500000211
ExtInX = [8.07102609], , ExtOutF = 49.928382692675996, ExtOutG = 154.61605669448198
OBJECTIVE 250797.01369955452
----------------------------------------------------------------------
**ExtInX = [8.07102609], , ExtOutF = 49.928382692675996, ExtOutG = 154.61605669448198
ExtInX = [8.07102609], , ExtOutF = 49.928382692675996, ExtOutG = 154.61605669448198
ExtInX = [8.07102609], , ExtOutF = 49.928382692675996, ExtOutG = 154.61605669448198
ExtInX = [8.07102609], , ExtOutF = 49.928382692675996, ExtOutG = 154.61605669448198**
ExtInX = [8.07102709], , ExtOutF = 49.92839583472901, ExtOutG = 154.61613684041134
ExtInX = [8.07102509], , ExtOutF = 49.92836955062501, ExtOutG = 154.61597654858318
ExtInX = [4.88062761], , ExtOutF = 18.178645674383997, ExtOutG = 21.29321703417343
OBJECTIVE 38811.35361617729
第一个答案之后
感谢您的回答。 据我了解,在 2.2 中,缓存必须由用户完成,并且 人们可以用不同的方式做到这一点。这可以。
而你在 2.3 中提到的改进似乎解决了一件事。 开始时的单个额外调用。我更关心的是 在迭代期间使用相同输入进行多次调用。
我正在添加下面的四个代码补丁。应该能够 运行 优化器 只要其他人在同一个文件夹中。
在这种情况下,外部代码是纯粹的 python。但同时我是 开发一个使用 'actual' 外部代码的优化器。又是 虽然包裹在 python 中。所以一些系统调用被发送到可执行文件 python 处理来自外部代码的输出,python 做一些 post 处理并输出由 OpenMDAO 外部代码读取的文本文件 成分。所以不确定那个是否算作纯 python。
关于约束:在下面的代码中有 none。在其他代码中 我已经解释过汽车部件有一些使用寿命限制 (例如 xx,较低=20)。
代码: Optimizer.py : Driver 和其他 3 个 .py 文件 https://gist.github.com/anonymous/2c9d5d182cbb24a2334c97b57a954802
在 OpenMDAO v2.2 中没有内置处理结果缓存的方法。所以你必须在那里推出你自己的解决方案。您可以在内存中进行缓存并仅存储最后已知的输入值,而不是保存完整的输出文件。如果它们没有改变,您可以将输出值保留在原处。
在 OpenMDAO V2.2 中,驱动程序被编码为在开始优化之前进行一个额外的函数调用。这是因为 OpenMDAO 处理线性约束的方式。它将 pre-compute 与线性约束相关联的导数并存储它们。
在 OpenMDAO V2.3 中,我们修改了驱动程序,因此它们仅在如果您的问题中有线性约束时才进行此额外调用。这样,至少可以解释您的一个额外函数调用。
在检查您的测试问题后,OpenMDAO 是 finite-differencing 组件的方式似乎确实存在错误。即使您使用转发模式,问题也会出现,并且它似乎与您声明部分的方式有关。 您声明它们的方式没有任何问题,但是使用另一种声明方法我能够使其正常运行:
from openmdao.api import ExternalCode
import numpy as np
class ParaboloidExternalCode(ExternalCode):
def setup(self):
self.add_input('x', val=0.0)
self.add_input('y', val=0.0)
self.add_output('f_xy', val=0.0)
self.add_output('g_xy', val=0.0)
self.input_file = 'paraboloid_input.dat'
self.output_file = 'paraboloid_output.dat'
# providing these is optional; the component will verify that any input
# files exist before execution and that the output files exist after.
self.options['external_input_files'] = [self.input_file,]
self.options['external_output_files'] = [self.output_file,]
self.options['command'] = [
'python', 'extcode_paraboloid.py', self.input_file, self.output_file
]
# self.declare_partials(of='*', wrt='*', method='fd', form='central', step=1e-6)
self.declare_partials(of='f_xy', wrt=['x','y'], method='fd', form='central', step=1e-6)
self.declare_partials(of='g_xy', wrt=['x','y'], method='fd', form='central', step=1e-6)
def compute(self, inputs, outputs):
x = inputs['x']
y = inputs['y']
# generate the input file for the paraboloid external code
with open(self.input_file, 'w') as input_file:
input_file.write('%f\n%f\n' % (x,y))
# the parent compute function actually runs the external code
super(ParaboloidExternalCode, self).compute(inputs, outputs)
file_contents = np.loadtxt(self.output_file)
outputs['f_xy'] = file_contents [0]
outputs['g_xy'] = file_contents [1]
print('ExtInX = {}, ExtInY = {},, ExtOutF_XY = {}, ExtOutG_XY = {} '.format(x,y,file_contents [0],file_contents[1]) )
我摆脱了 of='*', wrt='*'
并将其替换为两个单独的调用,这些调用明确命名了变量。我还能够在完全没有外部代码的情况下使用 in-memory 抛物面重现此问题。我已将该错误输入到开发积压列表中,我们会解决它。同时,此解决方法解决了问题。
请注意,OpenMDAO V2.2 中存在一个导致额外评估的错误。它已通过 OpenMDAO PR # 553
修复