3

I do a research on Linear Programming and I need to solve a complicated (hundreds of variables and constraints) problem. There are a lot of ways how to solve LP (that's not the problem) in a self-standing solver. However, I need to solve it from C# application. I tried my best to find any way how to solve LP inside C# code but the only thing I found (and was usable) was CPLEX and it's .NET Concert library. This looks quite well (and actually I use it right now and it works good) but the formulation of some large complicated problem is a real nightmare! What can be written in AMPL in 10 rows and is understandable for anyone, needs about 200 rows in C#.

Do you know any library for C# that would allow providing the problem model definition in some efficient (friendly) way? (CPLEX accepts LP format, but it can grow to hundreds of megabytes when you try to solve a problem with a lot of variables and then the algorithm runs for ages) Or I heard about a possibility to convert AMPL to LP format and then solve it by CPLEX C# library but it doesn't sound efficient :-)

In short, I have C# project. I need to solve LP problem. I need to solve it very efficiently... All this in some reasonably simple way (not hundreds of chaotic lines adding variables and constraints in for-cycles etc).

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
Marek
  • 815
  • 8
  • 19
  • 3
    Microsoft provides the [Solver Foundation](http://msdn.microsoft.com/en-us/devlabs/hh145003.aspx). I never used it, so I can't say if it solves your problems. It still might be worth a look. – Daniel Hilgarth Mar 20 '13 at 22:24
  • Possibly not a good fit for SO: question that is based on "find me API I like" can't be answered by other people. Using SO question as search engine for "a library" is not recommended either. – Alexei Levenkov Mar 20 '13 at 22:36
  • There's possibly a duplicate (or similar) question here: http://stackoverflow.com/questions/3363143/good-linear-programming-library-for-c – Matthew Watson Mar 20 '13 at 23:21
  • This library seems interesting: http://mathnetnumerics.codeplex.com/ – Matthew Watson Mar 20 '13 at 23:23

2 Answers2

1

Maybe this isn't the answer that you're looking for, but Concert for .NET is a great way to model linear programs. I think that you're simply exaggerating the difference between AMPL and Concert for .NET. Here is SteelT.mod in AMPL from the AMPL book examples, followed by part of steel.cs, included with CPLEX. This is approximately corresponding to the same code. There are a few things missing, like data reading and calling the solver, but that has to be written in a separate file in AMPL. It's 122 lines.

Yes, it's more difficult than AMPL because it's an API for a general purpose programming language. If you need to use C# and you have access to CPLEX this will likely be good enough. It also greater capabilities than AMPL, like access to the branch and bound tree in integer programs.

There are a lot of very good Concert examples included with CPLEX itself.

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);
          }
       }
    }
user327301
  • 2,641
  • 2
  • 26
  • 33
  • You are more or less right. I just find Concert model formulation hard to read or modify. And as the formulation is not really natural (from math point of view), it's very likely that a mistake can be very easily made (cycles, indices etc.) in comparison to AMPL which can be checked in a few seconds and you can find a mistake almost immediately. But I agree that Concert is so far the best solution I found. – Marek Mar 21 '13 at 09:47
0

If you were using C or C++, I might recommend GLPK. It's very clean code and it comes with an implementation of a certain subset of AMPL and a solver that's entirely sufficient for models of the size you're working with. So you could write your model in GNU Mathprog (its AMPL dialect) and feed it into the LP solver directly.

tmyklebu
  • 13,915
  • 3
  • 28
  • 57
  • I guess I should add that GLPK also has a C# wrapper. I think the package is called "GLPK#." I've never tried it, though, so I can't really vouch for its quality. – tmyklebu Mar 21 '13 at 08:54