Сообщений 0    Оценка 0        Оценить  
Система Orphus

Применение Xpress Optimizer для решения задач моделирования и оптимизации.

Автор: Перцовский Александр Константинович
Опубликовано: 21.03.2012
Исправлено: 10.12.2016
Версия текста: 1.1

В статье представлен обзор пакета Xpress Optimizer, предназначенного для решения задач математического моделирования и оптимизации. Данный программный продукт в течение нескольких лет разрабатывается компанией Dash Optimization. Его сокращенная версия доступна для свободной загрузки на сайте компании.

С помощью Xpress Optimizer можно реализовывать математические модели различной сложности и проводить их оптимизацию по всевозможным параметрам. Гибкий язык описания математических моделей позволяет учитывать широкий спектр ограничений, а введенные в него элементы функционального программирования позволяют избежать многих сложностей с формализацией модели.

В основе пакета лежит ядро математических функций, реализованных на C++. При этом к математическому ядру поставляется Xpress IVE – графическая среда разработки на специализированном языке программирования Mosel.

Программа на Mosel называется моделью. Она компилируется в промежуточный формат, а затем запускается на ядре оптимизатора, представляющем собой набор реализованных численных оптимизационных методов. При этом скомпилированную модель можно запустить на консольном варианте Optimizer, установленном, например, на кластере или суперкомпьютере. Стоит отметить, что консольная версия Optimizer поддерживает компиляцию моделей и из языка Mosel из командной строки.

Язык программирования Mosel является процедурным языком, поддерживающим условные, а также циклические операторы. Графическая среда разработки IVE поддерживает несколько мастеров автогенерации кода, при помощи которых можно в режиме помощника реализовать и оптимизировать математическую модель, не содержащую сложных условий. Также язык программирования поддерживает реализацию функций. В целом, как будет видно из последующих примеров, синтаксис mosel сильно напоминает синтаксис языка программирования Pascal.

Далее рассмотрим простую программу на Mosel, демонстрирующую работу с множествами:

      model
      
      ModelName
      uses
       "
      mmxprs
      "; 
      !gain access to the Xpress-Optimizer solver
      declarations 
      
      a, b
      : set of integer
      end-declarations
      a:= {1, 2, 3}
      b:= {2, 4, 5}
      writeln
      (a+b)
      writeln
      (a*b)
      writeln
      (a-b)
      writeln
      (b-a)
      end-model
    

Первая строка программы содержит ключевое слово model и название модели. Далее следует раздел, расписывающий подключаемые модули ядра оптимизации (в данном случае программой по умолчанию подключен модуль линейной оптимизации). Далее следует раздел declarations, в котором происходит объявление переменных и параметров. В приведенном примере в нем объявлены множества целых чисел a и b.

После объявления переменных проинициализируем множества некоторыми значениями. Следующие три строки программы последовательно выводят в консоль объединение, пересечение и левую и правую симметрическую разность множеств a и b.

Далее рассмотрим применение Xpress Optimizer для решения простейшей оптимизационной задачи.

Данная задача представляет собой модель управления доставками. В качестве исходных данных программе предоставляются множества производителей и потребителей, производственные мощности поставщиков и спрос потребителей, а также издержки на доставку единицы продукции от производителя к потребителю. Целью оптимизации является минимизация транспортных издержек при условии удовлетворения спроса всех потребителей. В качестве переменных задачи принимаются объемы доставки от производителя потребителю. Все исходные данные загружаются из файлов. Результат работы программы выводится в консоль оптимизатора и в файл. Также программа строит столбчатую диаграмму, показывающую загрузки производителей. Диаграмма отрисовывается в соответствующем окне Xpress Optimizer.

Ниже приведен листинг разработанной программы на Mosel.

      model
      
      ProbSup
      uses
       "
      mmxprs
      ";
      uses
       "
      mmive
      "; 
      declarations
      
      
      !клиенты и производители
      
      Customers
      : 
      set of string
      
      Developers
      : 
      set of string
      
      
      !цены доставки, производства и потребности
      
      Cost
      : array (
      Customers, Developers
      ) of real
      
      Cap
      : array (
      Developers
      ) of real
      
      Dem
      : array (
      Customers
      ) of real
      
      
      Rasp
      : array (
      Developers
      ) of integer
      
      i
      :integer
      
      graph
      : integer
      
      k
      :integer
      
      
      x
      : array(
      Customers
      , 
      Developers
      ) of mpvar
      
      outX
      : array (
      Customers
      , 
      Developers
      ) of real  
      end-declarations
      writeln("
      Begin running model
      ")
      initializations from "
      Developers.dat
      "
      
      Developers Cap
      end-initializations
      writeln("
      Developers initialized
      ")
      initializations from "
      Customers.dat
      "
      
      Customers Dem
      end-initializations
      writeln("
      Customers initialized
      ")
      initializations from "
      Cost.dat
      "
      
      Cost
      end-initializations
      writeln("
      Cost initialized
      ")
      forall (
      c
       in 
      Customers
      , 
      d
       in 
      Developers
      | exists(Cost(
      c
      ,
      d
      ))) do
        create(
      x(c,d))
      
      x(c,d)>=0
      end-do
      TotalCost
      :=sum(
      c
       in 
      Customers
      , 
      d
       in 
      Developers
       | exists(
      Cost(c,d)
      ))(
      Cost(c ,d)*x(c, d)
      )
      TotCap
      :=sum(
      d
       in 
      Developers
      )
      Cap(d)
      TotDem
      :=sum(
      c
       in 
      Customers
      )
      Dem(c)
      if 
      TotCap<TotDem
       then
        writeln("
      Can't satisfy all customers
      ")
      end-if
      forall (
      d
       in 
      Developers
      ) sum(
      c
       in 
      Customers
      )
      x(c,d)<=Cap(d)
      forall (
      c
       in 
      Customers
      ) sum(
      d
       in 
      Developers
      )x(c,d)>=Dem(c)
      minimize(
      TotalCost
      )
      if (getprobstat=
      2
      ) then
        writeln("
      The resuls:
       ")
        forall (
      c
       in 
      Customers
      , 
      d
       in 
      Developers
      |(exists(
      x(c,d)
      ) and getsol(
      x(c,d))>0
      )) do 
          writeln("
      To
       ",
      c
      ," we deliver
       ",getsol(
      x(c,d)
      )," 
      from
       ",
      d
      )
      
      outX(c,d)
      :=getsol(
      x(c,d)
      )
        end-do
        writeln("
      Total cost is
       ",getobjval)
      else
        writeln("
      Problem infeasible
      ")
        exit(
      0
      )  
      end-if
      writeln("
      Building graf
      ")
      forall(
      d
       in 
      Developers
      )
      Rasp(d)
      :=round(sum(
      c
       in 
      Customers
      | exists(
      x(c, d)
      ))(getsol(
      x(c,d)
      )))
      graph
      :=IVEaddplot("
      Загрузка заводов
      ",IVE_BLUE)
      i:=1
      forall(
      d
       in 
      Developers
      ) do
        IVEdrawrectangle(
      graph,i-0.9,0,i-0.1,Rasp(d)
      )
        IVEdrawlabel(
      graph,i-0.5,0,d
      )
      
      i:=i+1
      end-do
      writeln("
      Creating outputfile
      ")
      initializations to "
      output.txt
      "
      
      outX
      end-initializations
      writeln("
      End running model
      ")
      end-model
    

В отличие от предыдущей программы, в данном примере было подключено два модуля: линейной оптимизации и модуль, ответственный за отрисовку графиков (mmive). В разделе declarations помимо множеств string объявляются массивы, индексируемые по данным множествам, а также скалярные переменные. Следует отдельно отметить тип mpvar – это тип «переменные задачи». По ним ядро оптимизации будет вести работу. Инициализация данных проводится из файлов при помощи команды initializations from, далее следует имя файла. Данные в файле должны быть ровно в том порядке, в котором они приводятся внутри команды initializations. Пример такого файла для первой инициализации приведем ниже:

Developers.dat

Developers: ['Plant1', 'Plant2', 'Plant3']
Cap: [2 3 4]

После инициализации динамически проинициализируем переменные задач. Они создаются только для тех пар клиент-поставщик, для которых заданы транспортные издержки. Такой прием позволяет сэкономить системную память в случае, если количество поставщиков и потребителей будет значительным, а информация об затратах на доставку будет задана не для всех возможных их пар. Переменные задачи создаются следующим кодом:

forall (c in Customers, d in Developers| exists(Cost(c,d))) do
  create(x(c,d))
  x(c,d)>=0
end-do

Функция exisits проверяет наличие объекта в памяти. Инициализация переменных задачи производится в цикле, итерирующем по подмножеству декартова произведения множеств Customers и Developers, для каждого из которых выполняется условие, записанное после символа |.

Далее объявляется целевая функция TotalCost – функция линейного программирования. Затем задаются ограничения по производственным мощностям и удовлетворению спроса.

После задания ограничений командой minimize проведем решение задачи линейного программирования.

В следующем коде проводится анализ результатов решения задачи:

if (getprobstat=2) then
  writeln("The resuls: ")
  forall (c in Customers, d in Developers|(exists(x(c,d)) and getsol(x(c,d))>0)) do 
    writeln("To ",c," we deliver ",getsol(x(c,d))," from ",d)
    outX(c,d):=getsol(x(c,d))
  end-do
  writeln("Total cost is ",getobjval)
else
  writeln("Problem infeasible")
  exit(0)  
end-if

Функция getprobstat получает статус задачи (решена, неограничена или несовместна). Константа 2 означает, что задача решена. В таком случае производится получение значений переменных при помощи команды getsol. Проверка getsol(x(c,d))>0 необходима, так как задача решается численно, и некоторые нулевые значения переменных определяются с небольшой погрешностью.

Далее происходит построение столбчатой диаграммы, показывающей загрузки производителей.

Затем при помощи команды initializations to происходит запись результатов в текстовый файл.

В данном примере рассмотрена задача линейного программирования. Однако ядро Xpress Optimizer предоставляет функции для решения задач квадратичного программирования (в том числе целочисленных и смешанных). При этом язык mosel позволяет разработчику указывать метод решения – симплекс или эвристические алгоритмы в качестве параметра функции minimize.

Помимо минимизации доступна также максимизация (функция maximize).

В целом, язык mosel по своему синтаксису напоминает Pascal. В нем доступна реализация процедур, функций, дополнительных модулей, в том числе на C++.

В качестве заключения можно отметить, что использование Mosel и Xpress Optimizer позволяет разработчику не акцентировать внимание на техническую реализацию методов оптимизации, а больше времени уделить построению модели и формализации задачи.

Список литературы

  1. Введение в исследование операций. Таха Х. Издательство «Вильямс», 2007.
  2. Xpress optimizer user manual, http://brblog.typepad.com/files/optimizer-1.pdf


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 0    Оценка 0        Оценить