求解接口
该章节介绍OPTIMake的求解接口. 求解接口接收problem, option, workspace作为输入, 返回solve_status与output. 下面为求解接口solve的伪代码:
[solve_status, output] = solve(problem, option, workspace)
在第一次求解时, 需要调用初始化函数将problem, option以及workspace初始化. 在连续求解时, 也只需要在第一次求解前进行初始化. 该初始化函数会完成以下工作:
- 将problem中的参数 (包括stage-independent与stage-dependent parameter) 设置为-0.0
- 将problem中用户定义的数据初始化, 如插值表
- 将problem中的有效stage数设置为最大stage数N
- 将workspace中的primal变量的initial guess设置为-0.0; 并且将其他求解器需要清零的部分清零
- 将option设置为默认值
可通过参数与initial guess是否为-0.0, 判断参数或initial guess是否已被用户显式地设置
problem设置
通过problem结构体, 可以设置以下内容:
- 在模型中定义的stage-independent parameter与stage-dependent parameter (详见建模接口 > parameter定义), 分别通过
param
与param_stage
设置 - 有效stage数目, 通过
valid_dim_N
设置
parameter设置
下面为设置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).
有效stage数目
下面为设置有效stage数目的示例代码:
/* set valid stage number */
problem.valid_dim_N = 5;
- 终点等式约束或终点处额外的objective 作用在最后一个有效stage而非最大stage上
- 当优化问题具有等式约束或等式约束时, 需要保证有效stage数目大于等于2
- 有效stage数目必须小于等于最大stage数目N
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
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_eq
、tol_ineq
、tol_stat
、tol_comp
均满足的解 - 2: 找到可行解: 找到满足
tol_eq
与tol_ineq
, 但tol_stat
或tol_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中各变量的详细信息如下:
- 第个stage的第个优化变量可通过
output.primal.var[i][j]
获取, 只有有效stage数目内的变量才有效, 即stage的index从0到valid_dim_N - 1
output.primal.p_f
与output.primal.n_f
为转移等式约束软化引入的slack变量与, 其他等式约束同理output.primal.p_g
为不等式约束软化引入的slack变量, 其他不等式约束同理
示例
当问题的维度较大时, prob, workspace, output的局部变量会占用较大的栈空间, 可能会导致栈溢出.
可以通过动态内存分配或增加栈的大小解决该问题.
以下为一个介绍了如何设置problem, option, initial guess的完整的demo (使用C/C++直接调用):
#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;
}