Эффективный способ решения линейного программирования

Я занимаюсь исследованием линейного программирования, и мне нужно решить сложную (сотни переменных и ограничений) задачу. Есть много способов решить LP (это не проблема) в самостоятельном решателе. Однако мне нужно решить это из приложения С#. Я изо всех сил старался найти способ решить LP внутри кода C #, но единственное, что я нашел (и что можно было использовать), - это CPLEX и его библиотека .NET Concert. Выглядит это вполне неплохо (и на самом деле я использую это прямо сейчас, и это хорошо работает), но постановка какой-то большой сложной задачи — это настоящий кошмар! То, что на AMPL можно написать в 10 строк и понятно любому, на C# нужно порядка 200 строк.

Знаете ли вы какую-нибудь библиотеку для C#, которая позволила бы предоставить определение модели задачи эффективным (дружественным) способом? (CPLEX принимает формат LP, но он может вырасти до сотен мегабайт, когда пытаешься решить задачу с большим количеством переменных, и тогда алгоритм работает целую вечность) Или я слышал о возможности конвертировать AMPL в формат LP, а затем решать его библиотекой CPLEX C#, но это не звучит эффективно :-)

Короче говоря, у меня есть проект С#. Мне нужно решить проблему с ЛП. Мне нужно решить это очень эффективно... Все это достаточно простым способом (а не сотнями хаотичных строк, добавляющих переменные и ограничения в for-циклы и т.д.).


person Marek    schedule 20.03.2013    source источник
comment
Microsoft предоставляет Solver Foundation. Я никогда не использовал его, поэтому я не могу сказать, решает ли он ваши проблемы. Может все же стоит посмотреть.   -  person Daniel Hilgarth    schedule 21.03.2013
comment
Возможно, не подходит для SO: вопрос, основанный на API поиска меня, который мне нравится, не может быть решен другими людьми. Использование SO-вопроса в качестве поисковой системы для библиотеки также не рекомендуется.   -  person Alexei Levenkov    schedule 21.03.2013
comment
Возможно, здесь есть повторяющийся (или похожий) вопрос: stackoverflow.com/questions/3363143/   -  person Matthew Watson    schedule 21.03.2013
comment
Эта библиотека кажется интересной: mathnetnumerics.codeplex.com   -  person Matthew Watson    schedule 21.03.2013


Ответы (2)


Возможно, это не тот ответ, который вы ищете, но Concert для .NET — отличный способ моделирования линейных программ. Я думаю, что вы просто преувеличиваете разницу между AMPL и Concert для .NET. Вот SteelT.mod в AMPL из примеров книги AMPL, за которым следует часть steel.cs, включенная в CPLEX. Это примерно соответствует тому же коду. Не хватает нескольких вещей, таких как чтение данных и вызов решателя, но это должно быть написано в отдельном файле на AMPL. Это 122 строки.

Да, это сложнее, чем AMPL, потому что это API для языка программирования общего назначения. Если вам нужно использовать C# и у вас есть доступ к CPLEX, этого, вероятно, будет достаточно. Это также большие возможности, чем AMPL, такие как доступ к ветвям и связанному дереву в целочисленных программах.

Есть много очень хороших примеров Concert, включенных в сам CPLEX.

set PROD;     # products
param T > 0;  # number of weeks

param rate {PROD} > 0;          # tons per hour produced
param inv0 {PROD} >= 0;         # initial inventory
param avail {1..T} >= 0;        # hours available in week
param market {PROD,1..T} >= 0;  # limit on tons sold in week

param prodcost {PROD} >= 0;     # cost per ton produced
param invcost {PROD} >= 0;      # carrying cost/ton of inventory
param revenue {PROD,1..T} >= 0; # revenue per ton sold

var Make {PROD,1..T} >= 0;      # tons produced
var Inv {PROD,0..T} >= 0;       # tons inventoried
var Sell {p in PROD, t in 1..T} >= 0, <= market[p,t]; # tons sold

maximize Total_Profit:
  sum {p in PROD, t in 1..T} (revenue[p,t]*Sell[p,t] -
    prodcost[p]*Make[p,t] - invcost[p]*Inv[p,t]);

subject to Time {t in 1..T}:
  sum {p in PROD} (1/rate[p]) * Make[p,t] <= avail[t];

subject to Init_Inv {p in PROD}:  Inv[p,0] = inv0[p];

subject to Balance {p in PROD, t in 1..T}:
  Make[p,t] + Inv[p,t-1] = Sell[p,t] + Inv[p,t];


    public class Steel {
       internal static int _nProd;
       internal static int _nTime;

       internal static double[] _avail;
       internal static double[] _rate;
       internal static double[] _inv0;
       internal static double[] _prodCost;
       internal static double[] _invCost;

       internal static double[][] _revenue;
       internal static double[][] _market;

       internal static void ReadData(string fileName) {
          InputDataReader reader = new InputDataReader(fileName);

          _avail    = reader.ReadDoubleArray();
          _rate     = reader.ReadDoubleArray();
          _inv0     = reader.ReadDoubleArray();
          _prodCost = reader.ReadDoubleArray();
          _invCost  = reader.ReadDoubleArray();
          _revenue  = reader.ReadDoubleArrayArray();
          _market   = reader.ReadDoubleArrayArray();

          _nProd = _rate.Length;
          _nTime = _avail.Length;
       }

       public static void Main(string[] args) {
          try {
             string filename = "../../../../examples/data/steel.dat";
             if ( args.Length > 0 )
                filename = args[0];
             ReadData(filename);

             Cplex cplex = new Cplex();

             // VARIABLES
             INumVar[][] Make = new INumVar[_nProd][];
             for (int p = 0; p < _nProd; p++) {
                Make[p] = cplex.NumVarArray(_nTime, 0.0, System.Double.MaxValue);
             }

             INumVar[][] Inv = new INumVar[_nProd][];
             for (int p = 0; p < _nProd; p++) {
                Inv[p] = cplex.NumVarArray(_nTime, 0.0, System.Double.MaxValue);
             }

             INumVar[][] Sell = new INumVar[_nProd][];
             for (int p = 0; p < _nProd; p++) {
                 Sell[p] = new INumVar[_nTime];
                for (int t = 0; t < _nTime; t++) {
                   Sell[p][t] = cplex.NumVar(0.0, _market[p][t]);
                }
             }

             // OBJECTIVE
             ILinearNumExpr TotalRevenue  = cplex.LinearNumExpr();
             ILinearNumExpr TotalProdCost = cplex.LinearNumExpr();
             ILinearNumExpr TotalInvCost  = cplex.LinearNumExpr();

             for (int p = 0; p < _nProd; p++) {
                for (int t = 1; t < _nTime; t++) {
                   TotalRevenue.AddTerm (_revenue[p][t], Sell[p][t]);
                   TotalProdCost.AddTerm(_prodCost[p], Make[p][t]);
                   TotalInvCost.AddTerm (_invCost[p], Inv[p][t]);
                }
             }

             cplex.AddMaximize(cplex.Diff(TotalRevenue, 
                                          cplex.Sum(TotalProdCost, TotalInvCost)));

             // TIME AVAILABILITY CONSTRAINTS

             for (int t = 0; t < _nTime; t++) {
                ILinearNumExpr availExpr = cplex.LinearNumExpr();
                for (int p = 0; p < _nProd; p++) {
                   availExpr.AddTerm(1.0/_rate[p], Make[p][t]);
                }
                cplex.AddLe(availExpr, _avail[t]);
             }

             // MATERIAL BALANCE CONSTRAINTS

             for (int p = 0; p < _nProd; p++) {
                cplex.AddEq(cplex.Sum(Make[p][0], _inv0[p]), 
                            cplex.Sum(Sell[p][0], Inv[p][0]));
                for (int t = 1; t < _nTime; t++) {
                   cplex.AddEq(cplex.Sum(Make[p][t], Inv[p][t-1]), 
                               cplex.Sum(Sell[p][t], Inv[p][t]));
                }
             }

             cplex.ExportModel("steel.lp");

             if ( cplex.Solve() ) {
                System.Console.WriteLine();
                System.Console.WriteLine("Total Profit = " + cplex.ObjValue);

                System.Console.WriteLine();
                System.Console.WriteLine("\tp\tt\tMake\tInv\tSell");

                for (int p = 0; p < _nProd; p++) {
                   for (int t = 0; t < _nTime; t++) {
                      System.Console.WriteLine("\t" + p +"\t" + t +"\t" + cplex.GetValue(Make[p][t]) +
                                               "\t" + cplex.GetValue(Inv[p][t]) +"\t" + cplex.GetValue(Sell[p][t]));
                   }
                }
             }
             cplex.End();
          }
          catch (ILOG.Concert.Exception exc) {
             System.Console.WriteLine("Concert exception '" + exc + "' caught");
          }
          catch (System.IO.IOException exc) {
             System.Console.WriteLine("Error reading file " + args[0] + ": " + exc);
          }
          catch (InputDataReader.InputDataReaderException exc) {
             System.Console.WriteLine(exc);
          }
       }
    }
person user327301    schedule 21.03.2013
comment
Вы более-менее правы. Я просто нахожу формулировку модели Concert трудной для чтения или изменения. А так как формулировка не совсем естественная (с математической точки зрения), то очень вероятно, что можно очень легко сделать ошибку (циклы, индексы и т.д.) по сравнению с AMPL, который можно проверить за несколько секунд и найти ошибка почти сразу. Но я согласен, что Concert пока лучшее решение, которое я нашел. - person Marek; 21.03.2013

Если вы использовали C или C++, я мог бы порекомендовать GLPK. Это очень чистый код, и он поставляется с реализацией определенного подмножества AMPL и решателем, которого вполне достаточно для моделей того размера, с которым вы работаете. Таким образом, вы можете написать свою модель в GNU Mathprog (его диалект AMPL) и напрямую передать ее в решатель LP.

person tmyklebu    schedule 21.03.2013
comment
Я думаю, я должен добавить, что GLPK также имеет оболочку C #. Я думаю, что пакет называется GLPK#. Я никогда не пробовал его, поэтому я не могу ручаться за его качество. - person tmyklebu; 21.03.2013