本教程內容大部分來自Cplex官方文檔掖桦,若涉及侵權請聯系我刪除
介紹教程
.NET框架中的CPLEX支持創(chuàng)建模型冕香,使用數據填充模型儒喊,解決問題以及顯示解決方案的結果镣奋。
本教程通過.NET框架中的Concert Technology介紹CPLEX。它概述了典型應用程序并強調了以下過程:
創(chuàng)建模型
- 使用數據(按行怀愧,按列或非零)填充模型
- 求解這個模型
- 求解后顯示結果
本章重點介紹使用C#.NET的示例侨颈。您還可以在你的Cplex文件夾中隨CPLEX 一起提供的VB.NET(.NET框架中的Visual Basic)示例\例子\ SRC \ VB。由于它們的.NET框架芯义,這些VB.NET示例與某些CPLEX用戶可能已經熟悉的傳統Visual Basic示例不同哈垢。
你將要做什么
本教程將指導您在C#.NET中構建和解決一個小規(guī)模的線性規(guī)劃模型。
CPLEX可以與Concert Technology for .NET用戶一起使用毕贼,這是一個.NET庫温赔,允許您獨立于用于解決問題的算法來建模優(yōu)化問題。它提供了一個可擴展的建模層鬼癣,適用于現成的各種算法陶贼。此建模層使您可以更改模型,而無需完全重寫應用程序待秃。
要通過CPLEX with Concert Technology for .NET用戶找到問題的解決方案拜秧,可以使用三階段方法:describe,model和solve章郁。
第一階段是用自然語言描述問題枉氮。
第二階段是使用Concert Technology for .NET用戶的類和接口來模擬問題。該模型由數據暖庄,決策變量和約束組成聊替。決策變量是問題中的未知信息。每個決策變量都有一個可能值的域培廓。約束是對這些決策變量的值組合的限制或限制惹悄。該模型還可以包含目標,可以最大化或最小化的表達肩钠。
第三階段是使用Concert Technology for .NET用戶的類來解決問題泣港。解決問題包括為每個決策變量找到一個值,同時滿足約束條件价匠,并最大化或最小化一個目標(如果一個目標包含在模型中)当纱。
描述
第一步是用自然語言描述問題并回答有關問題的基本問題。
這個問題中的已知信息是什么踩窖?也就是說坡氯,有哪些數據?
這個問題中的未知信息是什么?也就是說箫柳,決策變量是什么颓遏?
這個問題有什么限制?也就是說滞时,決策變量的約束是什么?
解決這個問題的目的是什么滤灯?也就是說坪稽,目標函數是什么?
注意:
雖然這個過程的描述步驟在像這樣的簡單問題中看似微不足道鳞骤,但您會發(fā)現花時間充分描述更復雜的問題對于創(chuàng)建成功的應用程序至關重要窒百。如果您花時間描述模型,隔離決策變量豫尽,約束和目標篙梢,您將能夠更快速有效地編寫應用程序代碼。
模型
第二階段是讓您使用Concert Technology for .NET用戶的類來構建問題模型美旧。該模型由決策變量和對這些變量的約束組成渤滞。這個問題的模型也包含一個目標。
解決
第三階段是您使用CPLEX類的實例尋找解決方案并解決問題榴嗅。解決問題包括為每個變量找到一個值妄呕,同時滿足約束并最小化目標。
描述
提出這些問題來充分描述應用程序開發(fā)的優(yōu)化問題嗽测。
本教程的目的是查看構建模型的三種不同方法:按行绪励,按列或按非零。在以這些方式之一構建問題模型之后唠粥,應用程序優(yōu)化問題并顯示解決方案疏魏。
第一步:描述問題
寫出問題的自然語言描述并回答以下問題:
對這個問題了解多少?
這個問題中有哪些未知的信息(決策變量)晤愧?
決策變量有哪些限制(約束)大莫?
解決這個問題的目的(目標)是什么?
在C#中構建一個小LP問題
以下是該示例優(yōu)化的問題的傳統公式:
Max x1 + 2x2 + 3x3
Subject to.
-x1 + x2 + x3 ≤ 20
x1 - 3x2 + x3 ≤ 30
0 ≤ x1 ≤ 40
0 ≤ x2 ≤ ∞
0 ≤ x3 ≤ ∞
這個問題的決策變量是什么养涮?
x1葵硕,x2,x3有什么限制贯吓?
-x1 + x2 + x3 ≤ 20
x1 - 3x2 + x3 ≤ 30
0 ≤ x1 ≤ 40
0 ≤ x2 ≤ ∞
0 ≤ x3 ≤ ∞-
目標是什么懈凹?
Max x1 + 2x2 + 3x3
模型
使用Concert Technology for .NET用戶類來構建問題的模型。
在編寫問題描述之后悄谐,可以使用Concert Technology for .NET用戶和CPLEX類來構建模型介评。
第2步:打開文件
打開yourCPLEXhome文件 \實例的\ src \教程\ LPex1lesson.cs 在您的集成開發(fā)環(huán)境中,例如Microsoft Visual Studio。
第3步:創(chuàng)建模型對象
轉到該文件中的注釋步驟3们陆,并添加此語句以創(chuàng)建CPLEX 適合您的應用的模型寒瓦。
Cplex cplex = new Cplex();
該語句創(chuàng)建了一個類的空實例 CPLEX。在接下來的步驟中坪仇,您將添加一些方法杂腰,使您的應用程序可以使用行,按列或非零數據為數據填充數據椅文。
第4步:按行填充模型
現在轉到該文件中的注釋步驟4喂很,并添加這些行以創(chuàng)建一個方法,用行按數據填充空模型皆刺。
internal static void PopulateByRow(IMPModeler model,
INumVar[][] var,
IRange[][] rng) {
double[] lb = {0.0, 0.0, 0.0};
double[] ub = {40.0,
System.Double.MaxValue,
System.Double.MaxValue};
INumVar[] x = model.NumVarArray(3, lb, ub);
var[0] = x;
double[] objvals = {1.0, 2.0, 3.0};
model.AddMaximize(model.ScalProd(x, objvals));
rng[0] = new IRange[2];
rng[0][0] = model.AddLe(model.Sum(model.Prod(-1.0, x[0]),
model.Prod( 1.0, x[1]),
model.Prod( 1.0, x[2])), 20.0);
rng[0][1] = model.AddLe(model.Sum(model.Prod( 1.0, x[0]),
model.Prod(-3.0, x[1]),
model.Prod( 1.0, x[2])), 30.0);
}
這些行使用特定于此特定示例的數據填充模型少辣。但是,您可以從它的使用界面中看到IMPModeler 如何將遠程約束 s 添加到模型中羡蛾。IMPModeler 是Concert Technology界面漓帅,通常用于構建數學編程(MP)矩陣模型。您將在步驟5和步驟6中再次看到它的用途痴怨。
第5步:按列填充模型
轉到文件中的注釋步驟5忙干,并添加這些行以創(chuàng)建一個方法,用列按數據填充空模型腿箩。
internal static void PopulateByColumn(IMPModeler model,
INumVar[][] var,
IRange[][] rng) {
IObjective obj = model.AddMaximize();
rng[0] = new IRange[2];
rng[0][0] = model.AddRange(-System.Double.MaxValue, 20.0);
rng[0][1] = model.AddRange(-System.Double.MaxValue, 30.0);
IRange r0 = rng[0][0];
IRange r1 = rng[0][1];
var[0] = new INumVar[3];
var[0][0] = model.NumVar(model.Column(obj, 1.0).And(
model.Column(r0, -1.0).And(
model.Column(r1, 1.0))),
0.0, 40.0);
var[0][1] = model.NumVar(model.Column(obj, 2.0).And(
model.Column(r0, 1.0).And(
model.Column(r1, -3.0))),
0.0, System.Double.MaxValue);
var[0][2] = model.NumVar(model.Column(obj, 3.0).And(
model.Column(r0, 1.0).And(
model.Column(r1, 1.0))),
0.0, System.Double.MaxValue);
}
同樣豪直,這些行使用特定于此問題的數據填充模型。從中他們可以看到如何使用界面IMPModeler 將列添加到空模型珠移。
雖然對于許多示例而言弓乙,按行顯示可能看起來最簡單和自然,但是有些模型按行列出是更自然或更有效的實現方法钧惧。例如暇韧,網絡結構問題通常很適合按列建模。熟悉矩陣代數的讀者可以查看該方法populateByColumn 作為的轉置 populateByRow 浓瞪。
在此方法中懈玻,創(chuàng)建范圍對象以按列建模,僅具有其下限和上限乾颁。沒有給出變量的表達式涂乌,因為在這一點上構建它們是不可能的,因為還沒有創(chuàng)建變量英岭。類似地湾盒,目標函數僅在其預期的優(yōu)化意義下創(chuàng)建,并且沒有任何表達诅妹。
接下來罚勾,創(chuàng)建變量并將其安裝在現有范圍和目標中毅人。這些新創(chuàng)建的變量通過列對象引入范圍和目標,列對象在類中實現IColumn尖殃。使用這些方法創(chuàng)建此類的對象Cplex.Column丈莺,可以與方法鏈接在一起 IColumn.And 形成聚合 IColumn 對象。
一個 IColumn 用該方法創(chuàng)建的對象 ICplex.Column包含有關如何使用此列將新變量引入現有建模對象的信息送丰。例如缔俄,如果OBJ 是一個 IObjective 賓語, cplex.Column(obj器躏,2.0) 創(chuàng)造一個 IColumn 包含在表達式中安裝新變量的信息的對象 IObjective 賓語 OBJ 線性系數為 2.0牵现。同樣,對于IRange 約束 RNG 邀桑,方法調用 cplex.Column(rng,-1.0) 創(chuàng)造一個 IColumn 包含要在表達式中安裝新變量的信息的對象 RNG 科乎,作為系數的線性項 -1.0 壁畸。
簡而言之,當您使用逐列建模方法時茅茂,會創(chuàng)建新列并將其作為變量安裝在需要它們的所有現有建模對象中捏萍。要使用Concert Technology執(zhí)行此操作,您需要創(chuàng)建一個IColumn 您要在其中安裝新變量的每個建模對象的對象空闲,并使用該方法將它們鏈接在一起 IColumn.And 令杈。
第6步:使用非零填充模型
轉到文件中的注釋步驟6,并添加這些行以創(chuàng)建一個方法碴倾,用非零的數據填充空模型逗噩。
internal static void PopulateByNonzero(IMPModeler model,
INumVar[][] var,
IRange[][] rng) {
double[] lb = {0.0, 0.0, 0.0};
double[] ub = {40.0, System.Double.MaxValue, System.Double.MaxValue};
INumVar[] x = model.NumVarArray(3, lb, ub);
var[0] = x;
double[] objvals = {1.0, 2.0, 3.0};
model.Add(model.Maximize(model.ScalProd(x, objvals)));
rng[0] = new IRange[2];
rng[0][0] = model.AddRange(-System.Double.MaxValue, 20.0);
rng[0][1] = model.AddRange(-System.Double.MaxValue, 30.0);
rng[0][0].Expr = model.Sum(model.Prod(-1.0, x[0]),
model.Prod( 1.0, x[1]),
model.Prod( 1.0, x[2]));
rng[0][1].Expr = model.Sum(model.Prod( 1.0, x[0]),
model.Prod(-3.0, x[1]),
model.Prod( 1.0, x[2]));
}
在這些行中,您可以看到如何使用指示約束矩陣的非零的數據填充空模型跌榔。這些行首先為目標和沒有表達式的范圍創(chuàng)建對象异雁。他們還創(chuàng)建沒有列的變量; 也就是說,只有邊界的變量僧须。然后纲刀,這些行在目標,范圍和變量上創(chuàng)建表達式担平,并將表達式添加到模型中示绊。
第7步:添加界面
轉到文件中的注釋步驟7,并添加這些行以創(chuàng)建一個方法暂论,告訴用戶如何調用此應用程序面褐。
internal static void Usage() {
System.Console.WriteLine(“usage: LPex1 <option>”);
System.Console.WriteLine(“options: -r build model row by row”);
System.Console.WriteLine(“options: -c build model column by column”);
System.Console.WriteLine(“options: -n build model nonzero by nonzero”);
}
步驟8:添加命令評估程序
轉到文件中的注釋步驟8,并添加這些行以創(chuàng)建一個switch語句空另,用于評估應用程序用戶可能輸入的命令盆耽。
switch ( args[0].ToCharArray()[1] ) {
case ‘r’: PopulateByRow(cplex, var, rng);
break;
case ‘c’: PopulateByColumn(cplex, var, rng);
break;
case ‘n’: PopulateByNonzero(cplex, var, rng);
break;
default: Usage();
return;
}
求解
添加解決問題的應用程序部分。
在聲明決策變量并將約束和目標函數添加到模型后,您的應用程序就可以搜索解決方案了摄杂。
第9步:搜索解決方案
轉到文件中的步驟9坝咐,然后添加此行以使應用程序搜索解決方案。
if ( cplex.Solve() ) {
第10步:顯示解決方案
轉到文件中的注釋步驟10析恢,并添加這些行以使您的應用程序能夠顯示在步驟9中找到的任何解決方案墨坚。
double[] x = cplex.GetValues(var[0]);
double[] dj = cplex.GetReducedCosts(var[0]);
double[] pi = cplex.GetDuals(rng[0]);
double[] slack = cplex.GetSlacks(rng[0]);
cplex.Output().WriteLine(“Solution status = “
+ cplex.GetStatus());
cplex.Output().WriteLine(“Solution value = “
+ cplex.ObjValue);
int nvars = x.Length;
for (int j = 0; j < nvars; ++j) {
cplex.Output().WriteLine(“Variable :”
+ j
+” Value = “
+ x[j]
+” Reduced cost = “
+ dj[j]);
}
int ncons = slack.Length;
for (int i = 0; i < ncons; ++i) {
cplex.Output().WriteLine(“Constraint:”
+ i
+” Slack = “
+ slack[i]
+” Pi = “
+ pi[i]);
}
}
步驟11:將模型保存到文件中
如果要將模型保存為LP格式的文件,請轉到應用程序文件中的注釋步驟11映挂,然后添加此行泽篮。
cplex.ExportModel(“l(fā)pex1.lp”);
如果您以交互方式執(zhí)行本教程中的步驟,那么現在您可以編譯并執(zhí)行完整的應用程序柑船。
完整程序
// --------------------------------------------------------------------------
// File: LPex1.cs
// Version 12.9.0
// --------------------------------------------------------------------------
// Licensed Materials - Property of IBM
// 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55 5655-Y21
// Copyright IBM Corporation 2003, 2019. All Rights Reserved.
//
// US Government Users Restricted Rights - Use, duplication or
// disclosure restricted by GSA ADP Schedule Contract with
// IBM Corp.
// --------------------------------------------------------------------------
//
// LPex1.cs - Entering and optimizing an LP problem
//
// Demonstrates different methods for creating a problem. The user has to
// choose the method on the command line:
//
// LPex1 -r generates the problem by adding constraints
// LPex1 -c generates the problem by adding variables
// LPex1 -n generates the problem by adding expressions
//
using ILOG.Concert;
using ILOG.CPLEX;
public class LPex1 {
internal static void Usage() {
System.Console.WriteLine("usage: LPex1 <option>");
System.Console.WriteLine("options: -r build model row by row");
System.Console.WriteLine("options: -c build model column by column");
System.Console.WriteLine("options: -n build model nonzero by nonzero");
}
public static void Main(string[] args) {
if ( args.Length != 1 || args[0].ToCharArray()[0] != '-' ) {
Usage();
return;
}
try {
// Create the modeler/solver object
Cplex cplex = new Cplex();
INumVar[][] var = new INumVar[1][];
IRange[][] rng = new IRange[1][];
// Evaluate command line option and call appropriate populate method.
// The created ranges and variables are returned as element 0 of arrays
// var and rng.
switch ( args[0].ToCharArray()[1] ) {
case 'r': PopulateByRow(cplex, var, rng);
break;
case 'c': PopulateByColumn(cplex, var, rng);
break;
case 'n': PopulateByNonzero(cplex, var, rng);
break;
default: Usage();
return;
}
// write model to file
cplex.ExportModel("lpex1.lp");
// solve the model and display the solution if one was found
if ( cplex.Solve() ) {
double[] x = cplex.GetValues(var[0]);
double[] dj = cplex.GetReducedCosts(var[0]);
double[] pi = cplex.GetDuals(rng[0]);
double[] slack = cplex.GetSlacks(rng[0]);
cplex.Output().WriteLine("Solution status = " + cplex.GetStatus());
cplex.Output().WriteLine("Solution value = " + cplex.ObjValue);
int nvars = x.Length;
for (int j = 0; j < nvars; ++j) {
cplex.Output().WriteLine("Variable " + j +
": Value = " + x[j] +
" Reduced cost = " + dj[j]);
}
int ncons = slack.Length;
for (int i = 0; i < ncons; ++i) {
cplex.Output().WriteLine("Constraint " + i +
": Slack = " + slack[i] +
" Pi = " + pi[i]);
}
}
cplex.End();
}
catch (ILOG.Concert.Exception e) {
System.Console.WriteLine("Concert exception '" + e + "' caught");
}
}
// The following methods all populate the problem with data for the following
// linear program:
//
// Maximize
// x1 + 2 x2 + 3 x3
// Subject To
// - x1 + x2 + x3 <= 20
// x1 - 3 x2 + x3 <= 30
// Bounds
// 0 <= x1 <= 40
// End
//
// using the IMPModeler API
internal static void PopulateByRow(IMPModeler model,
INumVar[][] var,
IRange[][] rng) {
double[] lb = {0.0, 0.0, 0.0};
double[] ub = {40.0, double.MaxValue, double.MaxValue};
string[] varname = {"x1", "x2", "x3"};
INumVar[] x = model.NumVarArray(3, lb, ub, varname);
var[0] = x;
double[] objvals = {1.0, 2.0, 3.0};
model.AddMaximize(model.ScalProd(x, objvals));
rng[0] = new IRange[2];
rng[0][0] = model.AddLe(model.Sum(model.Prod(-1.0, x[0]),
model.Prod( 1.0, x[1]),
model.Prod( 1.0, x[2])), 20.0, "c1");
rng[0][1] = model.AddLe(model.Sum(model.Prod( 1.0, x[0]),
model.Prod(-3.0, x[1]),
model.Prod( 1.0, x[2])), 30.0, "c2");
}
internal static void PopulateByColumn(IMPModeler model,
INumVar[][] var,
IRange[][] rng) {
IObjective obj = model.AddMaximize();
rng[0] = new IRange[2];
rng[0][0] = model.AddRange(-double.MaxValue, 20.0, "c1");
rng[0][1] = model.AddRange(-double.MaxValue, 30.0, "c2");
IRange r0 = rng[0][0];
IRange r1 = rng[0][1];
var[0] = new INumVar[3];
var[0][0] = model.NumVar(model.Column(obj, 1.0).And(
model.Column(r0, -1.0).And(
model.Column(r1, 1.0))),
0.0, 40.0, "x1");
var[0][1] = model.NumVar(model.Column(obj, 2.0).And(
model.Column(r0, 1.0).And(
model.Column(r1, -3.0))),
0.0, double.MaxValue, "x2");
var[0][2] = model.NumVar(model.Column(obj, 3.0).And(
model.Column(r0, 1.0).And(
model.Column(r1, 1.0))),
0.0, double.MaxValue, "x3");
}
internal static void PopulateByNonzero(IMPModeler model,
INumVar[][] var,
IRange[][] rng) {
double[] lb = {0.0, 0.0, 0.0};
double[] ub = {40.0, double.MaxValue, double.MaxValue};
INumVar[] x = model.NumVarArray(3, lb, ub);
var[0] = x;
double[] objvals = {1.0, 2.0, 3.0};
model.Add(model.Maximize(model.ScalProd(x, objvals)));
rng[0] = new IRange[2];
rng[0][0] = model.AddRange(-double.MaxValue, 20.0);
rng[0][1] = model.AddRange(-double.MaxValue, 30.0);
rng[0][0].Expr = model.Sum(model.Prod(-1.0, x[0]),
model.Prod( 1.0, x[1]),
model.Prod( 1.0, x[2]));
rng[0][1].Expr = model.Sum(model.Prod( 1.0, x[0]),
model.Prod(-3.0, x[1]),
model.Prod( 1.0, x[2]));
x[0].Name = "x1";
x[1].Name = "x2";
x[2].Name = "x3";
rng[0][0].Name = "c1";
rng[0][0].Name = "c2";
}
}