跳到主要内容

求解接口

该章节介绍OPTIMake的求解接口. 求解接口接收problem, option, workspace作为输入, 返回solve_status与output. 下面为求解接口solve的伪代码:

[solve_status, output] = solve(problem, option, workspace)
注意

在第一次求解时, 需要调用初始化函数将problem, option以及workspace初始化. 在连续求解时, 也只需要在第一次求解前进行初始化. 该初始化函数会完成以下工作:

  • 将problem中用户定义的数据初始化, 如插值表
  • 将option设置为默认值
  • 将workspace中需要清零的部分清零

problem设置

在模型中定义的stage-independent parameter与stage-dependent parameter (详见建模接口 > parameter定义) 可通过problem结构体设置.

下面为设置parameter的示例代码:

/* mass */
problem.param[VEHICLE_PARAM_MASS] = 2500.0;
/* length */
problem.param[VEHICLE_PARAM_LENGTH] = 2.2;
for (i = 0; i < VEHICLE_DIM_N; i++) {
/* xLowerBound */
problem.param_stage[i][VEHICLE_PARAM_STAGE_XLOWERBOUND] = i;
}
信息

VEHICLE_PARAM_MASS, VEHICLE_PARAM_LENGTH等enum变量会在代码生成时自动生成在prob.h的头文件中.

同时, 各优化变量index的enum值及各种维度信息也会被包含在该头文件中.

使用时需要包含该头文件 (比如vehicle模型需要包含vehicle_prob.h).

option设置

求解器参数, 可分为通用option与求解算法相关option.

通用option

print_level: 打印信息的等级, int类型

  • 0: 不打印
  • 1: 打印错误信息与最终求解状态
  • 2: 在1的基础上, 打印迭代过程
  • 3: 在2的基础上, 打印更多求解信息

max_num_iter: 最大迭代次数, int类型, 非负

max_stepsize: 最大迭代步长, double类型, 0~1

auto scaling

obj_scaling_method: objective的scaling方法, int类型

  • 0: 无scaling
  • 1: 用户指定scaling factor, 其值通过option中的obj_scaling_factor输入
  • 2: 自动根据Hessian值进行scaling

obj_scaling_factor: objective的scaling factor, double类型, 非负

regularization

reg_hessian: Hessian的regularization, double类型, 非负

reg_eq: 等式约束拉格朗日乘子的regularization, double类型, 非负

reg_ineq: 不等式约束拉格朗日乘子的regularization, double类型, 非负

line_search_method: 线搜索策略

  • 0: 无线搜索, 即每次迭代都全步长接收
  • 1: filter策略

line_search_max_num_iter: 最大线搜索次数, int类型, 非负

infeasibility_relaxation_weight: 在线搜索打开且达到最大线搜索次数时, OPTIMake会将所有的等式硬约束与不等式硬约束 (不包含variable的hard bound约束)进行l1松弛, 该值为松弛的权重, double类型, 非负, 默认值为1e4

tolerance

tol_eq: 等式约束的tolerance, double类型, 非负

tol_ineq: 不等式约束的tolerance, double类型, 非负

tol_stat: stationarity condition的tolerance, double类型, 非负

tol_comp: complementarity condition的tolerance, double类型, 非负

tol_step: 优化变量的变化的tolerance, double类型, 非负

其他option

bfgs_max_num_skipping: bfgs更新的最大skip次数, 当大于该次数时, Hessian会被初始化. int类型, 非负, 默认值为2

pdipm option

barrier

barrier_strategy: barrier策略

  • 0: monotone策略
  • 1: loqo adaptive策略
  • 2: 结合monotone策略与loqo adaptive策略

barrier_init: barrier parameter的初始值, double类型, 非负

barrier_target: barrier parameter的最终值, double类型, 非负且不大于mu_start

try_warm_start_barrier: 是否尝试从barrier_target开始warm start求解 (当hessian_approximation设置为'bfgs'时, Hessian也会被设置为上一次求解的Hessian值), 如果warm start成功, 将有效降低迭代次数, 适合需要连续求解且barrier_target不至于过小的优化问题.

  • 0: 否 (默认)
  • 1: 是

如果warm start尝试失败, barrier parameter会被设置为 barrier_init 开始迭代, 且对偶变量, slack变量, Hessian均会被初始化. 此时相对于try_warm_start_barrier为0的设置会多一次迭代.

workspace设置

initial guess

优化变量的初始值 (initial guess)通过workspace设定.

下面为设置初始解的示例代码:

for (i = 0; i < VEHICLE_DIM_N; i++) {
/* initial guess */
workspace.primal.var[i][VEHICLE_VAR_X] = 2.0; /* x */
workspace.primal.var[i][VEHICLE_VAR_Y] = 5.0; /* y */
workspace.primal.var[i][VEHICLE_VAR_PHI] = 1.57; /* phi */
}

同时, workspace中也包含了以下额外信息:

  • 求解器的对偶变量, 对偶变量的初始值会被求解器自动设置. 用户可以在求解结束后通过workspace.dual访问对偶变量
  • Hessian, 用户可以在求解结束后通过workspace.hessian访问Lagrangian的Hessian. 每个stage的Hessian按照列优先存储, 且只存储下三角 (包含对角线)

solve_status

solve_status为求解状态:

  • 1: 求解成功: 找到tol_eqtol_ineqtol_stattol_comp均满足的解
  • 2: 找到可行解: 找到满足tol_eqtol_ineq, 但tol_stattol_comp不满足的解
  • 3: 问题不可行, 即问题进行l1松弛后求解成功, 但该解对原问题不可行
  • 4: 达到最大迭代次数, 未找到可行解
  • 5: 优化变量的变化小于设定的tolerance (tol_step), 未找到可行解
  • 6: 迭代过程中产生inf/nan非法值, 未找到可行解
  • 7: 求解前未初始化
  • 8: 求解问题无效
  • 100: license检查失败
  • 101: 初始解检查失败 (包含inf/nan非法值)
  • 102: 参数检查失败 (包含inf/nan非法值)

output

output中包含了以下信息:

  • iter: 迭代次数
  • obj: 目标函数值
  • solve_time: 求解时间, 单位为秒 (代码生成中enable_timing 为True时有效, 否则为0)
  • res_eq: 等式约束的残差
  • res_ineq: 不等式约束的残差
  • res_stat: stationarity condition的残差
  • res_comp: complementarity condition的残差
  • primal: primal变量, 存储了优化变量, 约束软化的slack变量等
信息

primal中各变量的详细信息如下:

  • ii个stage的第jj个优化变量可通过output.primal.var[i][j]获取
  • output.primal.p_foutput.primal.n_f为等式约束ff软化引入的slack变量ppnn, 其他等式约束同理
  • output.primal.p_g为不等式约束gg软化引入的slack变量pp, 其他不等式约束同理

示例

注意

当问题的维度较大时, prob, workspace, output的局部变量会占用较大的栈空间, 可能会导致栈溢出.

可以通过动态内存分配或增加栈的大小解决该问题.

以下为一个介绍了如何设置problem, option, initial guess的完整的demo (使用C/C++直接调用):

vehicle_demo.cpp
#include "vehicle_solver.h"
#include "vehicle_prob.h"
#include <stdio.h>

int main(void)
{
size_t i = 0;
size_t j = 0;
int status;
Vehicle_Problem prob;
Vehicle_Option option;
Vehicle_WorkSpace ws;
Vehicle_Output output;

/* must be initialized before the very first solve */
vehicle_init(&prob, &option, ws);

/* prob */
prob.param[VEHICLE_PARAM_X0] = 0; /* x */
prob.param[VEHICLE_PARAM_Y0] = 0; /* y */
prob.param[VEHICLE_PARAM_PHI0] = 0; /* phi */
prob.param[VEHICLE_PARAM_LENGTH] = 1.0; /* L */
for (i = 0; i < VEHICLE_DIM_N; i++) {
prob.param_stage[i][VEHICLE_PARAM_STAGE_XREF] = i;
prob.param_stage[i][VEHICLE_PARAM_STAGE_YREF] = i;
}

/* option */
option.print_level = 2;

/* workspace, initial guess */
for (i = 0; i < VEHICLE_DIM_N; i++) {
ws.primal.var[i][VEHICLE_VAR_X] = i;
ws.primal.var[i][VEHICLE_VAR_Y] = i;
}

status = vehicle_solve(&prob, &option, &ws, &output);

for (i = 0; i < VEHICLE_DIM_N; i++) {
for (j = 0; j < VEHICLE_DIM_VAR; j++) {
printf("%f\t", output.primal.var[i][j]);
}
printf("\n");
}
printf("\n");

return 0;
}