C语言调用MindOpt对二次规划问题建模优化

简介: MindOpt是达摩院决策智能实验室研究的一款优化求解器,能帮助做方案设计、生产方案优化、资源合理分配、辅助决策等。可以支持命令行、c、c++、java和python调用,目前求解算法实现了线性规划、混合整数线性规划、二次规划。

下文我们将讲述小编对线性规划的理解以及展示两个算例,和使用 MindOpt C 语言的 API 来建模以及求解 二次规划示例 中的问题。

(二次规划定义、算数例题都是与前文python语言的二次规划问题是一致的,已经阅读了前文可以忽略,直接点击目录进阶算例阅读建模优化代码。)


MindOpt Python、C、C++语言求解LP、MILP、QP问题系列


下载安装

用户可以点这里下载安装MindOpt优化求解器,免费的。找不到安装步骤点这里

(官网https://opt.aliyun.com有更多信息等着您哟!)


二次规划

在前文线性规划问题示例中,讲述到线性规划在我个人认为是在线性的目标和约束中,找出一个最优解。而本文的二次规划,是非线性规划中的一类。具体地说,是一个线性约束的、二次规划问题,就是优化(最小化或最大化)二次函数目标的问题。


关于优化的类别,有很多,比如MindOpt的案例广场的标签里面提到的问题标签,就列出了常见的数学规划的类型。其中关于变量、约束、目标这建模三要素,进行罗列:

  • 关于变量:取值有连续的,有整数的,还有更特殊的二进制(0或1)的,
  • 关于约束和目标:一般用变量的函数变换来表达,其中约束再增加它函数的取值范围。
  • 当函数是变量的线性关系时,比如x的1次方相加,我们称呼为线性约束、线性的目标。(如果变量也是连续的,这个就是线性规划问题啦。)
  • 当函数是变量的是二次关系的时候,比如函数中有 x的2次方项。我们称呼为二次约束,或二次目标。
  • 函数还会有凸函数和非凸函数,数学里面都代表不同的特性,大家可以再多去查阅材料。

image.png

本文主要讲 凸二次规划,Convex Quadratic Programming。

数学形式下的二次规划问题:

image.png

公式参考自MindOpt文档:https://solver.damo.alibaba.com/doc/html/model/qp/quadratic%20problem.html


案例

讲一个简单的例子,使用二次规划方法优化汽车轨迹,自动化驾驶车辆行驶在道路比较狭窄的路径上,还有其他障碍物阻碍的情况下,如果需要快速通过的话,我们需要暂时借用相邻车道通过,这个情况需要考虑自身车辆的情况、交通规则、保障远离障碍物距离的信息,然后找出一条通道。那么这个例子的解决办法是先考虑自身车辆的位置和周围障碍物,精确处理前一步可用车道,得到路径的边界,然后对路径边界进行优化(比如把车辆和障碍物之间的距离最大化,以允许车辆安全通过间隙)。

image.png

再比如,二次规划在机器人中应用,解决机械臂控制的问题。对于机器人来说,常常具备冗余关节,并且存在关节角度、角速度等限制条件,非常适合用二次规划方法来进行优化求解

image.png


数学算例

接下来我们举一个简单的数学算例,和如何用MindOpt优化求解器进行求解。

二次规划问题示例:

image.png


C语言+MindOpt代码实现

核心使用的几个APIs是:

MDO_CHECK_CALL(Mdo_createMdl(&model))

MDO_CHECK_CALL(Mdo_setIntAttr(model, MDO_INT_ATTR_MIN_SENSE, MDO_YES))

MDO_CHECK_CALL(Mdo_addRow(model, 1.0, MDO_INFINITY, 4, row1_idx, row1_val, "c0"));
MDO_CHECK_CALL(Mdo_addRow(model, 1.0, 1.0,          3, row2_idx, row2_val, "c1"));

MDO_CHECK_CALL(Mdo_solveProb(model));
Mdo_displayResults(model);

#关于更多API的详细使用方式,请参考 C 接口函数

下面是完整的例子,可复制存为MdoQoEx1.c文件。

#include <stdio.h>
/*引入头文件*/
#include "Mindopt.h"

/* 检查返回码的宏 */
#define MDO_CHECK_CALL(MDO_CALL)                                    \
code = MDO_CALL;                                                \
if (code != MDO_OKAY)                                           \
{                                                               \
Mdo_explainResult(model, code, str);                        \
Mdo_freeMdl(&model);                                        \
fprintf(stderr, "===================================\n");   \
fprintf(stderr, "Error   : code <%d>\n", code);             \
fprintf(stderr, "Reason  : %s\n", str);                     \
fprintf(stderr, "===================================\n");   \
return (int)code;                                           \
}

int main(void)
{
    /* 变量 */
    char str[1024] = { "\0" };
    MdoMdl * model = NULL;
    MdoResult code = MDO_OKAY;
    MdoStatus status = MDO_UNKNOWN;

    const int    row1_idx[] = { 0,   1,   2,   3   };
    const double row1_val[] = { 1.0, 1.0, 2.0, 3.0 };
    const int    row2_idx[] = { 0,    2,   3   };
    const double row2_val[] = { 1.0, -1.0, 6.0 };

    const int qo_col1[] = 
    {
        0, 
        1,   1,
                  2,
                       3  
    };
    const int qo_col2[] =
    {
        0,
        0,   1,
                  2,
                       3
    };
    const double qo_values[] =
    {
        1.0,
        0.5, 1.0,
                  1.0, 
                       1.0
    };

    /*------------------------------------------------------------------*/
    /* Step 1. 创建模型并更改参数。               */
    /*------------------------------------------------------------------*/
    /* 创建一个空模型。 */
    MDO_CHECK_CALL(Mdo_createMdl(&model));

    /*------------------------------------------------------------------*/
    /* Step 2. 输入模型。                                             */
    /*------------------------------------------------------------------*/
    /* 改成最小化问题。 */
    /*通过 Mdo_setIntAttr() 将目标函数设置为 最小化 */
    MDO_CHECK_CALL(Mdo_setIntAttr(model, MDO_INT_ATTR_MIN_SENSE, MDO_YES));

    /* 添加变量。 */
    /*调用 Mdo_addCol() 来添加四个优化变量,定义其下界、上界、名称和类型*/
    MDO_CHECK_CALL(Mdo_addCol(model, 0.0, 10.0,         1.0, 0, NULL, NULL, "x0", MDO_YES));
    MDO_CHECK_CALL(Mdo_addCol(model, 0.0, MDO_INFINITY, 1.0, 0, NULL, NULL, "x1", MDO_YES));
    MDO_CHECK_CALL(Mdo_addCol(model, 0.0, MDO_INFINITY, 1.0, 0, NULL, NULL, "x2", MDO_YES));
    MDO_CHECK_CALL(Mdo_addCol(model, 0.0, MDO_INFINITY, 1.0, 0, NULL, NULL, "x3", MDO_NO));

    /* 添加约束。
     * 请注意,这里的非零元素是按行顺序输入的。
     *调用 Mdo_addRow() 来输入约束
*/
    MDO_CHECK_CALL(Mdo_addRow(model, 1.0, MDO_INFINITY, 4, row1_idx, row1_val, "c0"));
    MDO_CHECK_CALL(Mdo_addRow(model, 1.0, 1.0,          3, row2_idx, row2_val, "c1"));

    /* 添加二次目标项。*/ 
    MDO_CHECK_CALL(Mdo_setQuadraticElements(模型, 5, qo_col1, qo_col2, qo_values)); 
    
    /*------------------------------------------------------------------*/
    /* Step 3. 解决问题并填充结果。               */
    /*------------------------------------------------------------------*/
    /* 调用 Mdo_solveProb() 求解优化问题,并通过 Mdo_displayResults() 查看优化结果 */
    MDO_CHECK_CALL(Mdo_solveProb(model));
    Mdo_displayResults(model);

    /*------------------------------------------------------------------*/
    /* Step 4. 释放模型。                                          */
    /*------------------------------------------------------------------*/
    /* 调用 Mdo_freeMdl() 来释放内存 */
    Mdo_freeMdl(&model);

    return (int)code;
}

MindOpt求解的结果

如何编译及运行MdoQoEx1.c文件

#运行方式与前文线性规划一致,只需要修改文件就好;把MdoQoEx1.c换成MdoQoEx1.c

windows系统本例是在Visual Studio上运行,版本为2019

c语言 windows系统运行mindopt.gif

/*linux和mac系统直接在命令行输入*/

cd <MDOHOME>/<VERSION>/examples/C
make -f Makefile all
./MdoQoEx1

然后运行MdoQoEx1.c 文件后,得到求解的结果如下所示,/**/号里面是我添加的注释。

Model summary.           /*模型摘要*/
 - Num. variables     : 4
 - Num. constraints   : 2
 - Num. nonzeros      : 7
 - Bound range        : [1.0e+00,1.0e+01]
 - Objective range    : [1.0e+00,1.0e+00]
 - Quad. obj. range   : [5.0e-01,1.0e+00]
 - Matrix range       : [1.0e+00,6.0e+00]

Presolver started.
Presolver terminated. Time : 0.000s

Interior point method started.   /*内点法*/
 Iter         PrimObj         DualObj PrimFea DualFea  GapFea      Mu   Time
    0 +5.21966603e+01 -5.98089411e+01 7.3e-01 1.3e+00 2.1e+00 1.5e+01   0.00s
    1 +6.67007650e+00 -3.65314339e+00 1.8e-02 3.2e-02 2.8e+00 1.7e+00   0.00s
    2 +1.36507017e+00 +2.11936527e-02 4.6e-04 8.1e-04 1.3e+00 2.2e-01   0.00s
    3 +5.85640966e-01 +3.97564854e-01 2.6e-05 3.7e-03 1.9e-01 3.1e-02   0.00s
    4 +4.53810895e-01 +4.38464927e-01 6.4e-07 9.3e-05 1.5e-02 2.6e-03   0.00s
    5 +4.40393096e-01 +4.39961443e-01 1.6e-08 2.3e-06 4.3e-04 7.2e-05   0.00s
    6 +4.40009842e-01 +4.39999040e-01 4.0e-10 5.9e-08 1.1e-05 1.8e-06   0.00s
    7 +4.40000247e-01 +4.39999976e-01 1.0e-11 1.5e-09 2.7e-07 4.5e-08   0.00s
    8 +4.40000006e-01 +4.39999999e-01 2.5e-13 3.7e-11 6.8e-09 1.1e-09   0.01s
Terminated.
 - Method             : Interior point method.
 - Primal objective   : 4.3999999423925E-01
 - Dual objective     : 4.3999999955250E-01
 - Num. threads       : 2
 - Num. iterations    : 8
 - Solver details     : Solver terminated with a primal/dual optimal status.

Interior point method terminated. Time : 0.005s

Optimizer summary.          /*求解器最终选择的优化方法以及求解消耗的时间*/
 - Optimizer used     : Interior point method   /*内点法*/
 - Optimizer status   : OPTIMAL
 - Total time         : 0.006s

Solution summary.       Primal solution
 - Objective          : 4.3999999424e-01   /*目标函数最优解*/

联系我们

钉钉:damodi

邮箱地址:solver.damo@list.alibaba-inc.com

相关文章
|
7月前
|
存储 缓存 安全
C语言中的内存管理与优化技巧
C语言中的内存管理与优化技巧
204 0
|
7月前
|
缓存 算法 Java
C语言中的内存优化及碎片优化
C语言中的内存优化及碎片优化
|
算法 C语言
使用指针来优化C语言程序性能
在C语言中,指针是一种强大且重要的概念。合理地使用指针可以提高程序的性能,减少内存的开销,并使代码更加简洁和易于维护。本文将介绍一些使用指针来优化C语言程序性能的技术。
286 0
|
28天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
62 1
|
2月前
|
算法 搜索推荐 C语言
【C语言】冒泡排序+优化版
【C语言】冒泡排序+优化版
|
6月前
|
机器学习/深度学习 搜索推荐 程序员
C语言实现个人通讯录(功能优化)-2
C语言实现个人通讯录(功能优化)
|
6月前
|
存储 C语言 索引
C语言实现个人通讯录(功能优化)-1
C语言实现个人通讯录(功能优化)
C语言实现个人通讯录(功能优化)-1
|
5月前
|
存储 编译器 定位技术
结构体数组在C语言中的应用与优化策略
结构体数组在C语言中的应用与优化策略
|
5月前
|
存储 编译器 数据库
结构体数组在C语言中的应用与优化技巧
结构体数组在C语言中的应用与优化技巧
|
7月前
|
并行计算 算法 测试技术
【C 言专栏】优化 C 语言程序性能的策略
【5月更文挑战第2天】本文探讨了优化C语言程序性能的策略,包括算法优化(选择合适的时间和空间复杂度)、代码结构优化(减少函数调用,合理使用循环)、内存管理优化(合理分配和及时释放内存)、编译器优化(选择优化级别,内联函数,循环展开)、数据结构优化(根据需求选择数组、哈希表或堆)、并行计算优化(多线程、多进程和MPI编程)以及性能测试与分析(使用性能分析工具、基准测试和分析执行路径)。通过这些方法,可以提升C语言程序的效率和运行速度。
233 1