Skip to content

代码生成与部署

当完成问题定义后, 需通过OPTIMake的代码生成函数生成问题定义与求解器的C/C++代码. 该章节主要介绍了如何配置代码生成以及调用生成的代码或库.

OPTIMake的代码生成流程如下:

Overview of code generation
代码生成流程

其中, 代码生成分为问题生成与求解器生成两个步骤:

  • 问题生成:生成问题相关的函数, 如约束的函数值及其梯度. 该过程在用户本地进行.
  • 求解器生成:生成问题对应的求解器. 该过程在OPTIMake服务器进行 (该过程需要联网). 当调用生成的求解器时, 不需要联网.

在本地完成问题生成后, 客户端会将问题脱敏后发送至OPTIMake服务器进行求解器生成, 即用户的具体问题定义及数据不会被发送至OPTIMake服务器. 所有上传的数据均对用户透明 (客户端上传部分的代码开源) . 以下为具体发送至服务器的数据:

  • 脱敏后的问题 (masked_multi_stage_problem):
    • 维度
    • 稀疏性: 为0/1的矩阵, 用于稀疏求解加速运算
    • 性质 (如是否线性): 用于选择更好的求解策略
  • 用户信息 (user_info), 用于license check:
    • mac地址
    • 计算机名
    • OS
    • CPU
  • 代码生成option (codegen_option): 定义了需要生成的求解器类型, 硬件平台等信息

代码生成

下面首先介绍代码生成的option, 主要可被分类为生成配置相关, 求解算法相关与性能相关.

生成配置相关

codegen_path: 代码生成路径

overwrite: 是否overwrite已生成的代码

verbose: 是否打印代码生成相关的信息 (默认False)

enable_printing: 是否开启打印功能

  • True: 生成代码中包含printf函数, 在求解器print_level设置为非0时, 求解器可打印求解状态 (默认)
  • False: 生成代码中不包含printf函数, 即使在求解器print_level设置为非0时, 求解器亦不可以可打印求解状态

enable_timing: 是否在生成代码中包含计时功能 (默认True)

enable_serialization: 是否开启序列化功能 (可将求解设定存储为二进制文件并读取复现, 默认False). 详见Tools > 序列化工具

platform

  • 'linux-x86_64-gcc' (默认)
  • 'windows-x86_64-mingw'
  • 'linux-arm64-gcc'
  • 'linux-armv7-gcc'

lib_type

  • 'static': 生成求解器的静态链接库 (默认)
  • 'shared': 生成求解器的动态链接库

求解算法相关

solver

  • 'pdipm': primal-dual interior-point method (默认)
  • 'sqp': sequential quadratic programming method (当前暂未支持)

hessian_approximation

  • 'exact' (默认)
  • 'gauss-newton'
  • 'bfgs'

Note

  1. 当objective为least square类型且残差较小, 或问题的非线性较低时, 选择'gauss-newton'. 当非上述情况, 需要计算Hessian才能收敛时, 选择'exact'或'bfgs'.
  2. 当Hessian很容易得到 (比如问题通过符号定义, OPTIMake会自动生成exact Hessian), 选择'exact'一般比'bfgs'更高效且鲁棒
  3. 如下的情况OPTIMake不能自动生成exact Hessian:
    • 外部函数, 此时Hessian需要用户自己提供. (见建模接口 > 外部C/C++函数支持了解OPTIMake中的外部函数)
    • differential_equation且discretization_method为'irk2'或'irk4'

bfgs_init_hessian: bfgs的Hessian初始值, 可以为常数或关于参数p的维度为[nv, nv]的对称矩阵 (通过list传入, list的元素为nv维的list);其默认值单位矩阵

Note

求解过程中计算的Hessian值被存储在workspace中, 可以求解优化问题后访问该值来优化bfgs_init_hessian的选取.

性能相关

optimization_level: 编译优化等级

  • 0,1,2,3 (默认3)

common_subexpression_elimination: 提取公共表达式, 减少运算量

  • 'on'
  • 'off'
  • 'auto' (默认)

会导致计算顺序变化, 当问题对数值敏感时, 可以尝试不同的选项求解.

simplify_expression: 简化表达式, 可能会导致本地问题时间过长

  • True
  • False (默认)

default_tolerance_level: 默认的求解精度 (设置后求解精度亦可手动调整)

  • 'low'
  • 'medium' (默认)
  • 'high'

下面为代码生成的例子:

option = codegen_option()
option.solver = 'pdipm' # primal-dual interior-point method
option.enable_printing = True
option.optimization_level = 3
# 'windows-x86_64-mingw', 'linux-x86_64-gcc', 'linux-arm64-gcc', 'linux-armv7-gcc'
option.platform = 'linux-x86_64-gcc'

codegen = code_generator()
codegen.codegen(prob, option)

假设问题的名称为 'vehicle', 生成platform为 'linux-x86_64-gcc', OPTIMake会生成以下文件:

  • vehicle_prob.h:问题的头文件, 定义了维度, 优化变量与参数index的enum值等
  • vehicle_prob.c:问题的源码文件, 定义了各函数, 如objective及其梯度的实现
  • vehicle_prob_ext.c.template:只当问题定义中出现外部函数时会生成该文件, 包含了外部函数的模板. 用户完成外部函数的实现后需要去掉.template后缀编译
  • vehicle_solver.h:求解代码的接口头文件
  • libvehicle_solver_shared.so:求解代码的动态链接库 (platform为windows时后缀为.dll)
  • libvehicle_solver_static.a:求解代码的静态链接库 (platform为windows时后缀为.dll.a)

Note

每一次代码生成都会有唯一的session uuid. 当前代码生成失败时, 请提供该id以及返回状态至OPTIMake以获取技术支持.

代码部署

包含生成的源码文件以及链接动态库或静态库即可调用生成代码. 调用生成代码时需注意:

  • 调用平台及编译器需与代码生成中的option.platform保持一致
  • 当调用的平台为Linux/Mac时, 需要链接math库 (-lm) , 以使用sin, cos, tan, sqrt等函数.
  • 当调用平台为Windows时, 需要iphlpapi链接 (-liphlpapi) , 以获取mac地址.

以下为使用静态链接库编译一个demo (vehicle_demo.cpp)的CMake示例:

# ${VEHICLE_CODEGEN_DIR} is the dir of the generated code
set(VEHICLE_CODEGEN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vehicle) 
# ${LIBS} is the required libs (math or iphlpapi)
set(LIBS m) 
add_executable(vehicle_demo vehicle_demo.cpp ${VEHICLE_CODEGEN_DIR}/vehicle_prob.c)
target_include_directories(vehicle_demo PRIVATE ${VEHICLE_CODEGEN_DIR})
target_link_libraries(vehicle_demo ${VEHICLE_CODEGEN_DIR}/libvehicle_solver_shared.a ${LIBS})