阿里达摩院MindOpt求解器V1.1新增C#接口

简介: 阿里达摩院MindOpt求解器发布最新版本V1.1,增加了C#相关API和文档。优化求解器产品是求解优化问题的专业计算软件,可广泛各个行业。阿里达摩院从2019年投入自研MindOpt优化求解器,截止目前经历27个版本的迭代,取得了多项国内和国际第一的成绩。就在上个月,2023年12月,在工信部产业发展促进中心等单位主办的首届能源电子产业创新大赛上,MindOpt获得电力用国产求解器第一名。本文将为C#开发者讲述如何下载安装MindOpt和C#案例源代码。

MindOpt V1.1 新增C#接口

C#的开发者好消息:阿里达摩院MindOpt求解器最新版本V1.1,增加了C#相关API和文档。

image.png


什么是优化求解器

优化求解器产品是求解优化问题的专业计算软件。可广泛应用于云计算、电力能源、工业制造、交通物流、零售、金融等领域,能帮助做方案设计、生产方案优化、资源合理分配、辅助决策等,是深埋于智能决策场景底层的“降本增效”的好工具,工业设计软件之芯。


优化求解器作为底层计算软件,因其技术壁垒高、研发难度大,几十年来,商用求解器一直被少数国外厂商垄断。阿里达摩院从2019年投入自研MindOpt优化求解器,截止目前经历27个版本的迭代,取得了多项国内和国际第一的成绩。就在上个月,2023年12月,在工信部产业发展促进中心等单位主办的首届能源电子产业创新大赛上,MindOpt获得电力用国产求解器第一名


对优化求解器有更多好奇心的初学者,可查阅小编之前的文章《什么是优化技术?给算法小白同学的快速讲解和上手文》(或公众号精排版


MindOpt C#接口-使用说明


本次MindOpt更新的V1.1版本,除了在性能上进一步提升,还增加了C#的接口。


加上之前已经支持的,当前MindOpt支持

  • 主流操作系统:Windows、MacOS、Linux的X86和ARM芯片版本,
  • 调用方式支持:
  • 命令行运行;
  • 通用编程语言 C、C++、C#、Java、Python,部分Julia;
  • 优化领域的建模语言 AMPL、Pyomo、PuLP、GAMS以及达摩院自研的MAPL(MindOpt APL)。


步骤1:下载MindOpt和配置License


自2021年起,MindOpt坚持做软件包开放下载,通过阿里云平台,方便用户自助获取商用级别的优化求解器软件,长期有免费额度,企业用户和教育用户均可自助0元购买后使用。


下载安装包https://opt.aliyun.com/portal/docs/htmldoc/solverDownLoad

此地址索引了阿里云的文档窗口,方便用户记住地址 opt.aliyun.com 和快速找到MindOpt的其他文档和资源。也可以点击右边的“打开原始文档”,在原阿里云网站中打开。

image.png


安装软件:


  • Windows双击安装包
  • Linux和macOS命令行运行bash mindopt-install-xxx-1.1.0.sh指令安装

安装后即可在命令行中运行mindopt来检查是否安装成功。


如果有在云计算环境运行的,如docker、maxCompute、Flink等环境,也可把本软件当作一个lib库来用,从安装后目录里面拿对应的文件。


配置License:


当前MindOpt供用户自助获取的License是联网云鉴权License,只传鉴权信息(阿里云账号和LIcenseKey码等),不传递用户的建模等数据,可放心使用。


导航栏上点击 产品-优化求解器,将进入阿里云的商品购买介绍页,根据指引点购买进入控制台来购买。可参考文档:https://help.aliyun.com/zh/optimization-solver/getting-started/activate-and-use-the-service

image.png

购买后,在已购服务列表中会看到LicenseKey,然后配置fl_client.ini文件,放置在mindopt文件夹,如下:

image.png

配置了后,可命令行执行mindopt -c,会遍历所有的License方式,最后看求解任务成功执行,命令行中如果有如下信息,即可认为此fl_client.ini文件配置成功。

[INFO ] Using floating license configuration in '/Users/xxxx/mindopt'

[INFO ] Completed validation with floating license configuration.


步骤2:试运行MindOpt安装目录的C# examples

进入到安装目录examples文件夹,有提供各种编程语言、建模语言和数据的实例,可参考运行。

image.png

Example.csproj是一个示例项目,配置中如下方式引用了MindOpt库(不同操作系统地址不同):

<ItemGroup><ReferenceInclude="mindoptcs.dll"><HintPath>$(MINDOPT_HOME)/osx64-aarch/lib/mindoptcs.dll</HintPath></Reference></ItemGroup>


.NET 编译和运行:


这里以命令行运行为例讲解如何运行,如在Linux或mac机器上,安装了.NET SDK 8.x ,将.cs代码文件拷贝入csproject文件夹中,运行如下指令编译和运行:

  • 示例1:拷贝不需要依赖外部数据的ExampleDiet.cs进入csproject文件夹
cd csproject
cp ../ExampleDiet.cs .
dotnet build
dotnet run  --framework netcoreapp8.0


如下图示意,会执行求解和输出

image.png


  • 示例2:拷贝需要依赖外部数据的ReadMps.cs进入csproject文件夹。其中第四行引用的数据地址也是MindOpt的安装目录examples文件夹中提供的示例数据。
cd csproject
cp ../ReadMps.cs .
dotnet build
dotnet run ../../data/afiro.mps --framework netcoreapp8.0


C#源代码:如何使用MindOpt解决建模和求解优化问题


建模数学原理的讲解,可参考MindOpt平台上的资源-案例广场https://opt.aliyun.com/platform/case。以下仅给出代码供参考如何调用。


1. 定制食谱

usingMindopt;
namespaceExample{
publicclassExampleDiet    {
publicstaticvoidMain(string[] args)
        {
MDOEnvenv=newMDOEnv();
MDOModelmodel=newMDOModel(env);
model.Set(MDO.StringAttr.ModelName, "diet");
Dictionary<string, double[]>requirements=new()
            {
                {"Calories",    newdouble[]{ 2000, MDO.INFINITY }},
                {"Carbohydrates",         newdouble[]{ 350, 375 }},
                {"Protein",       newdouble[]{ 55, MDO.INFINITY }},
                {"VitA",         newdouble[]{ 100, MDO.INFINITY }},
                {"VitC",         newdouble[]{ 100, MDO.INFINITY }},
                {"Calcium",      newdouble[]{ 100, MDO.INFINITY }},
                {"Iron",         newdouble[]{ 100, MDO.INFINITY }},
                {"Volume",        newdouble[]{ -MDO.INFINITY,75 }}
            };
Dictionary<string, double[]>foods=new()
            {
                {"Cheeseburger",    newdouble[]{ 0, MDO.INFINITY, 1.84 }},
                {"HamSandwich",     newdouble[]{ 0, MDO.INFINITY, 2.19 }},
                {"Hamburger",       newdouble[]{ 0, MDO.INFINITY, 1.84 }},
                {"FishSandwich",    newdouble[]{ 0, MDO.INFINITY, 1.44 }},
                {"ChickenSandwich", newdouble[]{ 0, MDO.INFINITY, 2.29 }},
                {"Fries",           newdouble[]{ 0, MDO.INFINITY, 0.77 }},
                {"SausageBiscuit",  newdouble[]{ 0, MDO.INFINITY, 1.29 }},
                {"LowfatMilk",      newdouble[]{ 0, MDO.INFINITY, 0.60 }},
                {"OrangeJuice",     newdouble[]{ 0, MDO.INFINITY, 0.72 }}
            };
double[,] values=newdouble[,]
            {
                { 510.0, 34.0, 28.0, 15.0,   6.0, 30.0, 20.0,  4.0 },
                { 370.0, 35.0, 24.0, 15.0,  10.0, 20.0, 20.0,  7.5 },
                { 500.0, 42.0, 25.0,  6.0,   2.0, 25.0, 20.0,  3.5 },
                { 370.0, 38.0, 14.0,  2.0,   0.0, 15.0, 10.0,  5.0 },
                { 400.0, 42.0, 31.0,  8.0,  15.0, 15.0,  8.0,  7.3 },
                { 220.0, 26.0,  3.0,  0.0,  15.0,  0.0,  2.0,  2.6 },
                { 345.0, 27.0, 15.0,  4.0,   0.0, 20.0, 15.0,  4.1 },
                { 110.0, 12.0,  9.0, 10.0, 120.0, 30.0,  0.0,  8.0 },
                {  80.0, 20.0,  1.0,  2.0,   4.0,  2.0,  2.0, 12.0 }
            };
string[] nutritionNames=requirements.Keys.Cast<string>().ToArray();
intnumNutrition=nutritionNames.Length;
string[] foodNames=foods.Keys.Cast<string>().ToArray();
intnumFood=foodNames.Length;
Dictionary<string, Dictionary<string, double>>reqValues=new();
for (inti=0; i<foodNames.Length; i++)
            {
reqValues[foodNames[i]] =newDictionary<string, double>();
for (intj=0; j<nutritionNames.Length; j++)
reqValues[foodNames[i]][nutritionNames[j]] =values[i, j];
            }
try            {
MDOVar[] foodVars=newMDOVar[numFood];
for (inti=0; i<numFood; ++i)
                {
double[] foodData=foods[foodNames[i]];
foodVars[i] =model.AddVar(foodData[0], foodData[1], 0, MDO.CONTINUOUS, foodNames[i]);
                }
// 添加约束for (inti=0; i<numNutrition; i++)
                {
MDOLinExprlin=newMDOLinExpr();
stringnutri=nutritionNames[i];
for (intj=0; j<numFood; j++)
                    {
stringfood=foodNames[j];
lin.AddTerm(reqValues[food][nutri], foodVars[j]);
                    }
model.AddRange(lin, requirements[nutritionNames[i]][0], requirements[nutritionNames[i]][1], nutritionNames[i]);
                }
// 添加目标函数MDOLinExprlinExpr=newMDOLinExpr();
for (inti=0; i<numFood; i++)
linExpr.AddTerm(foods[foodNames[i]][2], foodVars[i]);
model.SetObjective(linExpr, MDO.MINIMIZE);
model.Optimize();
model.Write("ExmapleDiet.mps");
// 打印结果foreach (MDOVarfoodVarinfoodVars)
Console.WriteLine($"You should buy {foodVar.Get(MDO.DoubleAttr.X)} unit of {foodVar.Get(MDO.StringAttr.VarName)}");
            }
catch (MDOExceptione)
            {
Console.WriteLine(e.Message);
            }
finally            {
model.Dispose();
env.Dispose();
            }
        }
    }
}

2. 设施选址

usingMindopt;
namespaceExample{
publicclassExampleFacility    {
publicstaticvoidMain(string[] args)
        {
MDOEnvenv=newMDOEnv();
MDOModelmodel=newMDOModel(env);
model.Set(MDO.StringAttr.ModelName, "Facility");
Dictionary<Tuple<double, double>, int>marketInfo=new()
            {
                {Tuple.Create(0.0, 1.7), 100},
                {Tuple.Create(1.4, 2.9), 200},
            };
Dictionary<Tuple<int, int>, double>facilitiesInfo=new()
            {
                {Tuple.Create(0, 1), 3.0},
                {Tuple.Create(0, 2), 1.0},
                {Tuple.Create(1, 0), 1.5},
                {Tuple.Create(1, 1), 1.3},
                {Tuple.Create(1, 2), 1.8},
                {Tuple.Create(2, 0), 1.6},
                {Tuple.Create(2, 1), 1.1},
                {Tuple.Create(2, 2), 1.9}
            };
doubletransportFeePerM=1.23;
try            {
inti=0;
intj=0;
MDOVar[] xVars=model.AddVars(facilitiesInfo.Count, MDO.BINARY);
MDOVar[,] yVars=newMDOVar[marketInfo.Count, facilitiesInfo.Count];
for (i=0; i<marketInfo.Count; i++)
                {
for (j=0; j<facilitiesInfo.Count; j++)
yVars[i, j] =model.AddVar(0, MDO.INFINITY, 0, MDO.CONTINUOUS, $"{i}{j}");
                }
i=0;
foreach (KeyValuePair<Tuple<double, double>, int>marketPairinmarketInfo)
                {
MDOLinExprlinExpr=newMDOLinExpr();
for (j=0; j<facilitiesInfo.Count; j++)
                    {
linExpr.AddTerm(1, yVars[i, j]);
MDOLinExprlhe=newMDOLinExpr();
lhe.AddTerm(1.0/marketPair.Value, yVars[i, j]);
model.AddConstr(lhe, MDO.LESS_EQUAL, xVars[j], $"is_built[{i}, {j}]");
                    }
model.AddConstr(linExpr, MDO.EQUAL, marketPair.Value, $"is_satisify[{i}, {j}]");
i++;
                }
MDOLinExprobjective=newMDOLinExpr();
j=0;
foreach (KeyValuePair<Tuple<int, int>, double>facPairinfacilitiesInfo)
                {
objective.AddTerm(facPair.Value, xVars[j]);
i=0;
foreach (Tuple<double, double>marketPosinmarketInfo.Keys)
                    {
objective.AddTerm(calcTransFee(marketPos, facPair.Key, transportFeePerM), xVars[j]);
i++;
                    }
j++;
                }
model.SetObjective(objective, MDO.MINIMIZE);
model.Optimize();
model.Write("ExampleFacility.mps");
i=0;
foreach (Tuple<int, int>posinfacilitiesInfo.Keys)
                {
MDOVarx=xVars[i];
if (x.Get(MDO.DoubleAttr.X) ==1) {
Console.Write($"The No.{i} warehouse should be built at ");
Console.WriteLine($"({pos.Item1}, {pos.Item2})");
                    }
i++;
                }
Console.WriteLine($"Trnasport fee is: {objective.Value} ");
            }
catch (MDOExceptione)
            {
Console.WriteLine(e.Message);
            }
finally            {
model.Dispose();
env.Dispose();
            }
        }
privatestaticdoublecalcTransFee(Tuple<double, double>pos1, Tuple<int, int>pos2, doubleunitPrice)
        {
doublex1=pos1.Item1-pos2.Item1;
doublex2=pos1.Item2-pos2.Item2;
return (x1*x1+x2*x2) *unitPrice;
        }
    }
}

3. 人力分配

usingMindopt;
namespaceExample{
publicclassExampleWorkforce    {
publicstaticvoidMain(string[] args)
        {
// 创建MindOpt模型MDOEnvenv=newMDOEnv();
MDOModelmodel=newMDOModel(env);
// 每天需要的工日数目string[] dayName= { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
int[] workersPerDay= {  3, 1, 4, 2, 1, 3, 3 };
// 对应工人的工资Dictionary<string, int>workers=new()
            {
                {"Xiaoming", 13},
                {"Huahua",   10},
                {"HongHong", 11},
                {"Dahua",     8},
                {"Lihua",     9},
                {"Niuniu",   14},
                {"Gouzi",    14}
            };
List<string>workers_name=newList<string>(workers.Keys);
// 定义可用集合List<Tuple<string, string>>availability=new()
            {
Tuple.Create( "Xiaoming",     "Tuesday" ),
Tuple.Create( "Xiaoming",   "Wednesday" ),
Tuple.Create( "Xiaoming",      "Friday" ),
Tuple.Create( "Xiaoming",      "Sunday" ),
Tuple.Create( "Huahua",        "Monday" ),
Tuple.Create( "Huahua",       "Tuesday" ),
Tuple.Create( "Huahua",        "Friday" ),
Tuple.Create( "Huahua",      "Saturday" ),
Tuple.Create( "HongHong",   "Wednesday" ),
Tuple.Create( "HongHong",    "Thursday" ),
Tuple.Create( "HongHong",      "Friday" ),
Tuple.Create( "HongHong",      "Sunday" ),
Tuple.Create( "Dahua",        "Tuesday" ),
Tuple.Create( "Dahua",      "Wednesday" ),
Tuple.Create( "Dahua",         "Friday" ),
Tuple.Create( "Dahua",       "Saturday" ),
Tuple.Create( "Lihua",         "Monday" ),
Tuple.Create( "Lihua",        "Tuesday" ),
Tuple.Create( "Lihua",      "Wednesday" ),
Tuple.Create( "Lihua",       "Thursday" ),
Tuple.Create( "Lihua",         "Friday" ),
Tuple.Create( "Lihua",         "Sunday" ),
Tuple.Create( "Niuniu",        "Monday" ),
Tuple.Create( "Niuniu",       "Tuesday" ),
Tuple.Create( "Niuniu",     "Wednesday" ),
Tuple.Create( "Niuniu",      "Saturday" ),
Tuple.Create( "Gouzi",         "Monday" ),
Tuple.Create( "Gouzi",        "Tuesday" ),
Tuple.Create( "Gouzi",      "Wednesday" ),
Tuple.Create( "Gouzi",         "Friday" ),
Tuple.Create( "Gouzi",       "Saturday" ),
Tuple.Create( "Gouzi",         "Sunday" )
            };
try            {
MDOVar[] x=newMDOVar[availability.Count];
for (inti=0; i<x.Length; i++)
                {
Tuple<string, string>workerDay=availability[i];
x[i] =model.AddVar(0, 1, 0, MDO.BINARY, $"sched({workerDay.Item1}, {workerDay.Item2})");
                }
Dictionary<string, MDOLinExpr>dayCount=new();
foreach (stringdayindayName) dayCount[day] =newMDOLinExpr();
for (inti=0; i<availability.Count; i++)
                {
MDOLinExprexpr=newMDOLinExpr();
Tuple<string, string>workerDay=availability[i];
dayCount[workerDay.Item2].AddTerm(1, x[i]);
                }
for (inti=0; i<dayName.Length; i++)
                {
MDOLinExprexpr=dayCount[dayName[i]];
model.AddConstr(expr, MDO.EQUAL, workersPerDay[i], "c1_"+availability[i].Item2);
                }
MDOLinExprobj=newMDOLinExpr();
for (inti=0; i<availability.Count; i++)
obj.AddTerm(workers[availability[i].Item1], x[i]);
model.SetObjective(obj, MDO.MINIMIZE);
model.Optimize();
for (inti=0; i<availability.Count; i++)
                {
if (x[i].Get(MDO.DoubleAttr.X) >0)
Console.WriteLine($"{availability[i].Item1} should work at {availability[i].Item2}");
                }
Console.WriteLine($"The total cost is { + model.Get(MDO.DoubleAttr.ObjVal)}");
            }
catch (MDOExceptione)
            {
Console.WriteLine(e.Message);
            }
finally            {
model.Dispose();
env.Dispose();
            }
        }
    }
}
目录
相关文章
|
1月前
|
C#
C# 接口(Interface)
接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同 "是什么" 部分,派生类定义了语法合同 "怎么做" 部分。 接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。 接口使得实现接口的类或结构在形式上保持一致。 抽象类在某种程度上与接口类似,但是,它们大多只是用在当只有少数方法由基类声明由派生类实现时。 接口本身并不实现任何功能,它只是和声明实现该接口的对象订立一个必须实现哪些行为的契约。 抽象类不能直接实例化,但允许派生出具体的,具有实际功能的类。
46 9
|
2月前
|
C# 索引
C# 一分钟浅谈:接口与抽象类的区别及使用
【9月更文挑战第2天】本文详细对比了面向对象编程中接口与抽象类的概念及区别。接口定义了行为规范,强制实现类提供具体实现;抽象类则既能定义抽象方法也能提供具体实现。文章通过具体示例介绍了如何使用接口和抽象类,并探讨了其实现方式、继承限制及实例化差异。最后总结了选择接口或抽象类应基于具体设计需求。掌握这两者有助于编写高质量的面向对象程序。
114 5
|
3月前
|
达摩院 供应链 安全
光储荷经济性调度问题【数学规划的应用(含代码)】阿里达摩院MindOpt
本文介绍使用MindOpt工具优化光储荷经济性调度的数学规划问题。光储荷经济性调度技术旨在最大化能源利用率和经济效益,应用场景包括分布式光伏微网、家庭能源管理系统、商业及工业用电、电力市场参与者等。文章详细阐述了如何通过数学规划方法解决虚拟电厂中的不确定性与多目标优化难题,并借助MindOpt云建模平台、MindOpt APL建模语言及MindOpt优化求解器实现问题建模与求解。最终案例展示了如何通过合理充放电策略减少37%的电费支出,实现经济与环保双重效益。读者可通过提供的链接获取完整源代码。
|
3月前
|
达摩院 BI 索引
切割问题【数学规划的应用(含代码)】阿里达摩院MindOpt
本文主要讲述了使用MindOpt工具对切割问题进行优化的过程与实践。切割问题是指从一维原材料(如木材、钢材等)中切割出特定长度的零件以满足不同需求,同时尽可能减少浪费的成本。文章通过实例详细介绍了如何使用MindOpt云上建模求解平台及其配套的MindOpt APL建模语言来解决此类问题,包括数学建模、代码实现、求解过程及结果分析等内容。此外,还讨论了一维切割问题的应用场景,并对其进行了扩展,探讨了更复杂的二维和三维切割问题。通过本文的学习,读者能够掌握利用MindOpt工具解决实际切割问题的方法和技术。
|
3月前
|
达摩院 算法 安全
智慧楼宇多目标调度问题【数学规划的应用(含代码)】阿里达摩院MindOpt
本文探讨了使用MindOpt工具优化智慧楼宇的多目标调度问题,特别是在虚拟电厂场景下的应用。智慧楼宇通过智能化技术综合考虑能耗、舒适度等多目标,实现楼宇设备的有效管理和调度。虚拟电厂作为多能源聚合体,能够参与电力市场,提供调峰、调频等辅助服务。文章介绍了如何使用MindOpt云上建模求解平台及MindOpt APL建模语言对楼宇多目标调度问题进行数学建模和求解,旨在通过优化储能设备的充放电操作来最小化用电成本、碳排放成本和功率变化成本,从而实现经济、环保和电网稳定的综合目标。最终结果显示,在使用储能设备的情况下,相比不使用储能设备的情形,成本节约达到了约48%。
|
3月前
|
C#
C# 面向对象编程(三)——接口/枚举类型/泛型
C# 面向对象编程(三)——接口/枚举类型/泛型
32 0
|
6月前
|
达摩院 开发者 容器
「达摩院MindOpt」优化形状切割问题(MILP)
在制造业,高效地利用材料不仅是节约成本的重要环节,也是可持续发展的关键因素。无论是在金属加工、家具制造还是纺织品生产中,原材料的有效利用都直接影响了整体效率和环境影响。
「达摩院MindOpt」优化形状切割问题(MILP)
|
6月前
|
人工智能 自然语言处理 达摩院
MindOpt 云上建模求解平台:多求解器协同优化
数学规划是一种数学优化方法,主要是寻找变量的取值在特定的约束情况下,使我们的决策目标得到一个最大或者最小值的决策。
|
1月前
|
机器学习/深度学习 算法 数据可视化
如果你的PyTorch优化器效果欠佳,试试这4种深度学习中的高级优化技术吧
在深度学习领域,优化器的选择对模型性能至关重要。尽管PyTorch中的标准优化器如SGD、Adam和AdamW被广泛应用,但在某些复杂优化问题中,这些方法未必是最优选择。本文介绍了四种高级优化技术:序列最小二乘规划(SLSQP)、粒子群优化(PSO)、协方差矩阵自适应进化策略(CMA-ES)和模拟退火(SA)。这些方法具备无梯度优化、仅需前向传播及全局优化能力等优点,尤其适合非可微操作和参数数量较少的情况。通过实验对比发现,对于特定问题,非传统优化方法可能比标准梯度下降算法表现更好。文章详细描述了这些优化技术的实现过程及结果分析,并提出了未来的研究方向。
26 1
|
4月前
|
人工智能 算法 调度
优化问题之如何选择合适的优化求解器
优化问题之如何选择合适的优化求解器