lp_solve线性规划工具的android移植

lpsolve-android

github原文: https://github.com/leonardodalinky/lpsolve-android
lp_solve线性规划工具从c和java移植到android安卓平台上。
lp_solve版本: 5.5.2.5

背景

几天前, 当我正在为《公主连接》游戏写基于线性规划的装备计算器时,我发现常用的纯Java实现的org.apache.commons:commons-math3库,在装备项数和约束方程很大的情况下,表现得非常差。

通过在谷歌之后,发现lp_solve这个开源工具包有着更好的表现,但是由于其并未直接提供Android版本的库文件,因而使我萌生了移植代码的念头。

经过lp_solveorg.apache.commons:commons-math3库进行比较后,发现在大规模线性规划问题下,前者有着更好的准确度更快的速度,可见lp_solve更加适合安卓平台线性规划工具的开发。

移植遇到的问题

有人可能会问,为什么我们不直接使用他提供的Jar包?Android不是可以直接读取Java的.Class文件吗?

对于安卓平台而言,由于cpu可能采用不同的架构,例如arm等cpu架构,因此不能直接使用已有的jar包,因为这些jar包内部也是通过动态链接库加上JNI所实现的。

所以我们要做的,就是查清楚其MakeFile的过程,并在Android Studio上复现,并且去除平台不兼容所带来的bug。

破寻原有的Build Up过程

首先,我们先得明确:lp_solve内核是由C语言实现,Java只是为其提供了JNI的接口。

$c -fpic $INCL -c $SRC_DIR/lpsolve5j.cpp
$c -shared -Wl,-soname,liblpsolve55j.so -o $PLATFORM/liblpsolve55j.so lpsolve5j.o -L../../../lpsolve55/bin/$PLATFORM -lc -llpsolve55

lp_solve的JNI Wrapper的生成中(上述代码),我们可以发现其调用了其C语言源码库.../lpsolve55文件夹中的binary文件,可知其C语言的核心先在该文件夹中编译为静态库。

之后,我们在.../lpsolve55文件夹中查阅其静态库的生成方式:

  src='../lp_MDO.c ../shared/commonlib.c ../shared/mmio.c ../shared/myblas.c ../ini.c ../fortify.c ../colamd/colamd.c ../lp_rlp.c ../lp_crash.c ../bfp/bfp_LUSOL/lp_LUSOL.c ../bfp/bfp_LUSOL/LUSOL/lusol.c ../lp_Hash.c ../lp_lib.c ../lp_wlp.c ../lp_matrix.c ../lp_mipbb.c ../lp_MPS.c ../lp_params.c ../lp_presolve.c ../lp_price.c ../lp_pricePSE.c ../lp_report.c ../lp_scale.c ../lp_simplex.c ../lp_SOS.c ../lp_utils.c ../yacc_read.c'
	......
  $c -fpic -s -c -I.. -I../shared -I../bfp -I../bfp/bfp_LUSOL -I../bfp/bfp_LUSOL/LUSOL -I../colamd -I. $opts $NOISNAN -DYY_NEVER_INTERACTIVE -DPARSER_LP -DINVERSE_ACTIVE=INVERSE_LUSOL -DRoleIsExternalInvEngine $src
  $c -shared -Wl,-Bsymbolic -Wl,-soname,liblpsolve55.so -o bin/$PLATFORM/liblpsolve55.so `echo $src|sed s/[.]c/.o/g|sed 's/[^ ]*\///g'` -lc -lm -ldl

通过上面的代码,我们能够确定其产生静态库的重要文件。

改写CMake

由于Android Studio使用NDK需要自己配置CMakeLists.txt,因此在调整文件结构后,根据上面的重要信息,写出CMakeLists.txt

#/**
#* @ FileName: CMakeLists.txt
#* @ Function: The CMakeList to build up the dynamic library for lp_solve
#* @ Author: AyajiLin & YesunHuang
#* @ Mail: 493987054@qq.com
#* @ Github: https://github.com/leonardodalinky
#* @ Data: May 10, 2020
#*
#*/

# 确定CMake最小版本
cmake_minimum_required(VERSION 3.4.1)

# 包含头文件
include_directories(java_src)
include_directories(c_src)
include_directories(c_src/colamd)
include_directories(c_src/shared)
include_directories(c_src/bfp)
include_directories(c_src/bfp/bfp_LUSOL)
include_directories(c_src/bfp/bfp_LUSOL/LUSOL)

# 增加JNI中的源码
aux_source_directory(java_src DIR_JAVA_SRCS)

# 设置编译器参数(由上一节中顺带得到)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -lc -lm -ldl -DYY_NEVER_INTERACTIVE -DPARSER_LP -DINVERSE_ACTIVE=INVERSE_LUSOL -DRoleIsExternalInvEngine -DNOLONGLONG -DCLOCKTIME -dy")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -frtti -fexceptions -O3 -lc -lm -ldl -DYY_NEVER_INTERACTIVE -DPARSER_LP -DINVERSE_ACTIVE=INVERSE_LUSOL -DRoleIsExternalInvEngine -DNOLONGLONG -DCLOCKTIME -dy")

# 定义所需的C语言源码
SET(C_SRCS c_src/lp_MDO.c c_src/shared/commonlib.c c_src/shared/mmio.c c_src/shared/myblas.c c_src/ini.c c_src/fortify.c c_src/colamd/colamd.c c_src/lp_rlp.c c_src/lp_crash.c c_src/bfp/bfp_LUSOL/lp_LUSOL.c c_src/bfp/bfp_LUSOL/LUSOL/lusol.c c_src/lp_Hash.c c_src/lp_lib.c c_src/lp_wlp.c c_src/lp_matrix.c c_src/lp_mipbb.c c_src/lp_MPS.c c_src/lp_params.c c_src/lp_presolve.c c_src/lp_price.c c_src/lp_pricePSE.c c_src/lp_report.c c_src/lp_scale.c c_src/lp_simplex.c c_src/lp_SOS.c c_src/lp_utils.c c_src/yacc_read.c)

# 选择编译为动态链接库
add_library( # Sets the name of the library.
            lpsolve55j

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
            ${C_SRCS} ${DIR_JAVA_SRCS}
        )
...后略

部署过程的问题

在部署过程中,由于自身对CMake的不熟悉,导致花费了很多时间。

除此以外,还曾经遇到cannot initialize a parameter of type 'JNIEnv **' (aka '_JNIEnv **') with an rvalue of type 'void **'的错误。经过排查,发现其源码中存在着不恰当的类型转换,将JNIEnv **类型强制转换为void **类型,而传给一个需要JNIEnv **类型变量作为参数的函数,因此被JNI规范发现并报错。

修改其源码后,部署成功。

以下为Android Studio上的部署教程,其他IDE流程未尝试过,待补充。英文水平较渣,就不带翻译了。此处为github原文

How to Use

Before we go on, we shall check some prerequisites:

  1. Android Studio
  2. CMake

After that, download the latest lpsolve_android_release.zip in release page and unzip it. The directory structure is listed below

app
  - build.gradle
  - src
    - main
      - cpp
        - CMakeLists.txt
        - ...More
      - java
        - lpsolve
        - ...More
lpsolve_bin
  - lib
    - arm64-v8a
    - armeabi-v7a
    - x86
    - x86_64

The directories inside lpsolve_bin/lib contain the .so libraries for each platforms.

There are 2 different ways to lay out the lp_solve libraries.

Way 1: Build A New NDK Project

First, build a NDK project in AS.

The file structure in directory app correspond to the real structure in ndk project of Android Studio. Then:

  1. Put the files in directory app/src in the corresponding location of the ndk project. Make sure the lpsolve package is the subdirectory of app/src/main/java and the cpp dir is the subdirectory of app/src/main, or it may cause errors.
  2. Modify the build.gradle in app as below:
android{
	...
	defaultConfig{
		...
		# Here define the parameters which control the cmake
        	externalNativeBuild {
            		cmake {
                		cFlags ""
                		cppFlags ""
            		}
        	}
	}
	...
	# Here is the relative path of the CMakeList.txt
    	externalNativeBuild {
        	cmake  {
			path "src/main/cpp/CMakeLists.txt"
       		}
    	}
}

Way 2: Add the lp_solve libraries to an existing project

  1. Start an existing android project with AS.

  2. Put the files in directory app/src in the corresponding location of the NDK project. Make sure the lpsolve package is the subdirectory of app/src/main/java and the cpp directory is the subdirectory of app/src/main, or it may cause errors.

  3. Open the Project pane from the left side of the IDE and select the Android view.

  4. Right-click on the module you would like to link to your native library, such as the app module, and select Link C++ Project with Gradle from the menu.

  5. From the drop-down menu, select CMake. Then, use the field next to Project Path to specify the src/main/cpp/CMakeLists.txt script file for your external CMake project.

  6. Click OK.

Above steps could be referred to Link Gradle to your native library on the website of Android Developers.

When everything is done, you could import the lpsolve package the same as what we do with Java.

And every time the .apk file is compiled and built, the lpsolve would be packed in it.

API Reference

lp_solve reference guide

License

The MIT License

Credits

The project relies on:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林地宁宁

谢谢你给宁宁打米哦!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值