OpenMDAO v0.10.3.2: проблемы с использованием драйвера итератора case в рабочем процессе драйвера оптимизатора

Из соображений совместимости я использую OpenMDAO v0.10.3.2.

Я пытаюсь настроить проблему оптимизации в OpenMDAO, которая требует использования драйвера итератора case в рабочем процессе оптимизатора. Для иллюстрации я создал простой пример, показанный ниже. Кажется, что код работает, но оптимизатор не изменяет параметры и завершает работу после двух запусков своего рабочего процесса.

Я ищу предложения по настройке такого типа проблемы и понимание того, что может пойти не так с моей текущей формулировкой.

from openmdao.main.api import Assembly, Component
from openmdao.lib.datatypes.api import Float, Array, List
from openmdao.lib.drivers.api import DOEdriver, SLSQPdriver, CaseIteratorDriver

import numpy as np


class component1(Component):

    x = Float(iotype='in')
    term1 = Float(iotype='out')
    a = Float(iotype='in', default_value=1)
    def execute(self):
        x = self.x
        a = self.a

        term1 = a*x**2
        self.term1 = term1


class component2(Component):

    x = Float(iotype='in')
    y = Float(iotype='in')
    term1 = Float(iotype='in')
    f = Float(iotype='out')

    def execute(self):

        y = self.y
        x = self.x
        term1 = self.term1

        f = term1 + x + y**2

        self.f = f


class summer(Component):

    fs = Array(iotype='in', desc='f values from all cases')
    total = Float(iotype='out', desc='sum of all f values')

    def execute(self):
        self.total = sum(self.fs)
        print 'In summer, fs = %s and total = %s' % (self.fs, self.total)


class assembly(Assembly):

    cases_a = Array(iotype='in', desc='list of cases')
    x = Float(iotype='in')
    y = Float(iotype='in')
    f = Float(iotype='out')
    total = Float(iotype='out', default_value=100)

    def configure(self):

        # create instances of components
        self.add('component1', component1())
        self.add('component2', component2())
        self.add('summer', summer())

        # set up main driver (optimizer)
        self.add('driver', SLSQPdriver())
        self.driver.iprint = 1
        self.driver.maxiter = 100
        self.driver.accuracy = 1.0e-6
        self.driver.add('summer', summer())
        self.driver.add_parameter('x', low=-5., high=5.)
        self.driver.add_parameter('y', low=-5., high=5.)
        self.driver.add_objective('summer.total')

        # set up case iterator driver
        self.add('case_driver', CaseIteratorDriver())
        self.case_driver.workflow.add(['component1', 'component2'])
        self.case_driver.add_parameter('component1.a')
        self.case_driver.add_response('component2.f')

        # Set up connections
        self.connect('x', 'component1.x')
        self.connect('y', 'component2.y')
        self.connect('component1.x', 'component2.x')
        self.connect('component1.term1', 'component2.term1')
        self.connect('component2.f', 'f')
        self.connect('cases_a', 'case_driver.case_inputs.component1.a')
        self.connect('case_driver.case_outputs.component2.f', 'summer.fs')
        self.connect('summer.total', 'total')

        # establish main workflow
        self.driver.workflow.add(['case_driver', 'summer'])

if __name__ == "__main__":
    """ the result should be -1 at (x, y) = (-0.5, 0) """

    import time

    test = assembly()
    values = [1, 1, 1, 1]
    test.cases_a = np.array(values)
    test.x = 4
    test.y = 4

    tt = time.time()
    test.run()

    print "Elapsed time: ", time.time()-tt, "seconds"

    print 'result = ', test.total
    print '(x, y) = (%s, %s)' % (test.x, test.y)

person jthomas    schedule 17.07.2015    source источник
comment
Я решил эту проблему, используя ALPSO, NSGA2, MIDACO, COBYLA и ALHSO, в то время как SLSQP, CONMIN, KSOPT и SOLVOPT терпят неудачу. Это означает, что проблема может быть решена с помощью методов безградиентной оптимизации, но не может быть решена с помощью методов на основе градиента. Однако основная проблема должна быть легко решена с использованием алгоритмов на основе градиента. Это заставляет меня думать, что проблема связана с использованием итератора case.   -  person jthomas    schedule 18.07.2015


Ответы (1)


Есть много проблем, связанных с распространением производных через драйвер CID, и мы никогда не добивались того, чтобы он работал так, как мы хотели. Поэтому вместо этого я предлагаю альтернативный подход, при котором вы создаете отдельный экземпляр для каждого случая, который хотите запустить. Это будет работать намного лучше, особенно если вы планируете использовать аналитические производные в какой-то момент.

from openmdao.main.api import Assembly, Component
from openmdao.lib.datatypes.api import Float, Array, List
from openmdao.lib.drivers.api import DOEdriver, SLSQPdriver, COBYLAdriver, CaseIteratorDriver

import numpy as np


class component1(Component):

    x = Float(iotype='in')
    term1 = Float(iotype='out')
    a = Float(iotype='in', default_value=1)
    def execute(self):
        x = self.x
        a = self.a

        term1 = a*x**2
        self.term1 = term1

        print "In comp1", self.name, self.a, self.x, self.term1


class component2(Component):

    x = Float(iotype='in')
    y = Float(iotype='in')
    term1 = Float(iotype='in')
    f = Float(iotype='out')

    def execute(self):

        y = self.y
        x = self.x
        term1 = self.term1

        f = term1 + x + y**2

        self.f = f
        print "In comp2", self.name, self.x, self.y, self.term1, self.f



class summer(Component):


    total = Float(iotype='out', desc='sum of all f values')

    def __init__(self, size):
        super(summer, self).__init__()
        self.size = size

        self.add('fs', Array(np.zeros(size), iotype='in', desc='f values from all cases'))

    def execute(self):
        self.total = sum(self.fs)
        print 'In summer, fs = %s and total = %s' % (self.fs, self.total)


class assembly(Assembly):

    x = Float(iotype='in')
    y = Float(iotype='in')
    total = Float(iotype='out', default_value=100)

    def __init__(self, a_vals=[1, 1, 1, 1]):
        self.a_vals = a_vals

        super(assembly, self).__init__()



    def configure(self):

        #add the driver first, so I don't overwrite the workflow later on
        self.add('driver', SLSQPdriver())


        #create this first, so we can connect to it
        self.add('summer', summer(size=len(self.a_vals)))
        self.connect('summer.total', 'total')

        # create instances of components
        for i, a in enumerate(self.a_vals):
            c1 = self.add('comp1_%d'%i, component1())
            c1.a = a
            c2 = self.add('comp2_%d'%i, component2())

            self.connect('x', ['comp1_%d.x'%i,'comp2_%d.x'%i])
            self.connect('y', 'comp2_%d.y'%i)
            self.connect( 'comp1_%d.term1'%i, 'comp2_%d.term1'%i)

            self.connect('comp2_%d.f'%i, 'summer.fs[%d]'%i)

            self.driver.workflow.add(['comp1_%d'%i, 'comp2_%d'%i])

        # establish main workflow


        # set up main driver (optimizer)
        self.driver.iprint = 1
        self.driver.maxiter = 100
        self.driver.accuracy = 1.0e-6
        self.driver.add_parameter('x', low=-5., high=5.)
        self.driver.add_parameter('y', low=-5., high=5.)
        self.driver.add_objective('summer.total')


if __name__ == "__main__":
    """ the result should be -1 at (x, y) = (-0.5, 0) """

    import time

    test = assembly([1, 1, 1, 1])

    test.x = 2
    test.y = 4

    tt = time.time()
    test.run()

    print "Elapsed time: ", time.time()-tt, "seconds"

    print 'result = ', test.total
    print '(x, y) = (%s, %s)' % (test.x, test.y)
person Justin Gray    schedule 19.07.2015
comment
Благодарю за ваш ответ. Это выглядит как хорошее решение, однако, когда я запускаю ваш код, я получаю сообщение об ошибке AttributeError: объект не имеет атрибута 'a_vals', относящегося к строке 77. Похоже, конфигурация не распознает атрибут, добавленный методом инициализации. У вас есть идеи, почему это не сработает? Я мало использовал super() и не смог понять, что не так. - person jthomas; 20.07.2015
comment
Судя по результатам печати, кажется, что метод configure запускается раньше, чем метод init. - person jthomas; 20.07.2015
comment
В более старых версиях OpenMDAO (0.12.0 и старше) базовая реализация init из сборки вызывала configure для всех своих дочерних элементов. Это изменилось в 0.13.0, но чтобы мой скрипт работал в более старых версиях, я отредактировал его, создав атрибут a_vals перед вызовом Super (init родительского класса). Я отредактировал свой ответ, включив это небольшое исправление. - person Justin Gray; 20.07.2015
comment
Даже после внесения этого исправления я не смог заставить это работать в OpenMDAO 0.10.3.2, но оно работает в 0.9.7. Я буду продолжать и использовать эту версию на данный момент. Спасибо. - person jthomas; 20.07.2015
comment
Я подозреваю, что в 0.10.3.2 есть ошибка. Он должен работать в более поздних версиях (также 0.12 и 0.13) - person Justin Gray; 22.07.2015