Сообщений 0 Оценка 0 Оценить |
В статье представлен обзор пакета 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 позволяет разработчику не акцентировать внимание на техническую реализацию методов оптимизации, а больше времени уделить построению модели и формализации задачи.
Сообщений 0 Оценка 0 Оценить |