Tuesday, September 2, 2014

(Linear) Optimization with C# and Gurobi

Today I want to write about the Gurobi Optimizer, with which optimization problems can be solved, more specifically linear and quadratical programs.
In principle Gurobi does cost anything, however members of an academical institution (like students of a university) can acquire a free license, for all others there are trial licenses. For many different programming languages, for example the .Net languages, interfaces are offered.
First Gurobi has to be downloaded, which can be done here. Attention: For .Net the 64 Bit version does not seem to work, but the 32 Bit version runs on all systems - see the next post.
After that a Gurobi license has to be aquired. For that you have to select the desired license under Downloads - Licenses on the Gurobi website and click "Request License". On the new page you find a command in the form "grbgetkey e7300a11-...", which has to be executed via Start - Run. Then the Gurobi server is contacted, the license aquired and saved on the computer. Now the solver is ready to go.
Now I want to give an example on the usage of it, for which I will use the same example as in the Gurobi Quickstart Guide.
We want to optimize the following model:

Maximize x + y + 2z
s.t. x + 2y + 3z <= 4
x + y >= 1
x, y ∈ {0, 1}

To be able to use Gurobi in .Net, we first have to add a reference to the library Gurobi56.NET.dll to our project. In my case this is located in the folder C:\gurobi563\win32\bin.
The C# code for a console application, which models the above example, is then:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Gurobi;

namespace GurobiConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                GRBEnv env = new GRBEnv("mip1.log");
                GRBModel model = new GRBModel(env);

                // Create variables

                GRBVar x = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "x");
                GRBVar y = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "y");
                GRBVar z = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, "z");

                // Integrate new variables

                model.Update();

                // Set objective: maximize x + y + 2 z

                model.SetObjective(x + y + 2 * z, GRB.MAXIMIZE);

                // Add constraint: x + 2 y + 3 z <= 4

                model.AddConstr(x + 2 * y + 3 * z <= 4.0, "c0");

                // Add constraint: x + y >= 1

                model.AddConstr(x + y >= 1.0, "c1");

                // Optimize model

                model.Optimize();

                Console.WriteLine(x.Get(GRB.StringAttr.VarName)
                                   + " " + x.Get(GRB.DoubleAttr.X));
                Console.WriteLine(y.Get(GRB.StringAttr.VarName)
                                   + " " + y.Get(GRB.DoubleAttr.X));
                Console.WriteLine(z.Get(GRB.StringAttr.VarName)
                                   + " " + z.Get(GRB.DoubleAttr.X));

                Console.WriteLine("Obj: " + model.Get(GRB.DoubleAttr.ObjVal));

                // Dispose of model and env

                model.Dispose();
                env.Dispose();

                string Dummy = Console.ReadLine();

            }
            catch (GRBException ex)
            {
                Console.WriteLine("Error code: " + ex.ErrorCode + ". " + ex.Message);
            }
        }
    }
}

Let's walk this through step by step. First we create a Gurobi environment and a model, as a parameter we pass the path to the log file, in which outputs of the program are printed. Then we create the 3 needed variables x, y and z. The first two parameters are upper and lower bounds for them, the third describes their coefficient in the target funtion. We here pass 0, because we define the objective later. GRB.BINARY says, that the variable is binary. Other possible values are GRB.INTEGER and GRB.CONTINOUS. The following commands should be self-explanatory, via SetObjective() we define the objective, via AddConstr() we add constraints. The found solution is eventually outputted in the console.
If the LP has no solution or is unbound, an error is thrown when trying to obtain the variable values, namely "Error at GRBVar.Get".

2 comments:

  1. Thanks for your nice article. I have found a fine article also. I want to share it

    We should follow standard naming convention.
    The variables and methods/functions name should be relevant and small as far as possible.
    Use X++ instead of X=X+1. Both returns the same result but X++ use fewer characters.

    for more....


    http://cybarlab.com/c-sharp-code-optimization-tips



    Hope it will help us.

    ReplyDelete
  2. Thanks, for your interest and feedback!

    ReplyDelete