1、C++调用Python
1.1、 背景
最近实验室有一个项目要用遗传算法和粒子群算法解决问题,但是没有找到在windows上合适的C++库,但是Python的库很多(例如说pymoo: Multi-objective Optimization in Python),所以决定采用在C++中嵌入python解释器的方法,使用C++调用Python的接口。
1.2、环境安装
安装Python到指定文件夹
在Windows内离线部署python:
https://www.python.org/ftp/python
这里选择的是3.8.9版本:
https://www.python.org/ftp/python/3.8.9/python-3.8.9-amd64.exe
然后选择安装到指定文件夹的Python38目录下。
安装Python第三方库
pip安装环境依赖
根据pymoo的指引进行安装,因为github访问不稳定,可以使用proxychains4代理。
git clone https://github.com/anyoptimization/pymoo
现在目录格式像这样:
.
├── Python38
│ ├── DLLs
│ ├── Doc
│ ├── LICENSE.txt
│ ├── Lib
│ ├── NEWS.txt
│ ├── Scripts
│ ├── Tools
│ ├── include
│ ├── libs
│ ├── python.exe
│ ├── python3.dll
│ ├── python38.dll
│ ├── pythonw.exe
│ ├── share
│ ├── tcl
│ ├── vcruntime140.dll
│ └── vcruntime140_1.dll
└── pymoo
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.rst
├── data
├── pymoo
├── setup.py
└── tests
执行以下命令安装环境
cd pymoo
..\Python38\python.exe -m pip install .
调试环境
如果需要在Debug方式下运行,需要将Python38目录下libs目录下的python38.lib copy一份,重命名为python38_d.lib,即可以使用。
编译设置
将Python38/include目录设置为包含目录。
将Python38/libs设置为库目录。
将Python38.dll拷贝一份到执行文件的目录下。
1.3、编程开发
Python的相关接口
'''
Problem defination
Problem link: https://www.pymoo.org/getting_started/part_2.html
\begin{align}
\label{eq:getting_started_pymoo}
\begin{split}
\min \;\; & f_1(x) = 100 \, (x_1^2 + x_2^2) \\
\min \;\; & f_2(x) = (x_1-1)^2 + x_2^2 \\[1mm]
\text{s.t.} \;\; & g_1(x) = 2 \, (x_1 - 0.1) \, (x_1 - 0.9) \, / \, 0.18 \leq 0\\
& g_2(x) = - 20 \, (x_1 - 0.4) \, (x_1 - 0.6) \, / \, 4.8 \leq 0\\[1mm]
& -2 \leq x_1 \leq 2 \\
& -2 \leq x_2 \leq 2\\[1mm]
& x \in \mathbb{R}
\end{split}
\end{align}
'''
import numpy as np
from pymoo.core.problem import ElementwiseProblem
class MyProblem(ElementwiseProblem):
def __init__(self):
super().__init__(n_var=2,
n_obj=2,
n_constr=2,
xl=np.array([-2,-2]),
xu=np.array([2,2]))
def _evaluate(self, x, out, *args, **kwargs):
f1 = 100 * (x[0]**2 + x[1]**2)
f2 = (x[0]-1)**2 + x[1]**2
g1 = 2*(x[0]-0.1) * (x[0]-0.9) / 0.18
g2 = - 20*(x[0]-0.4) * (x[0]-0.6) / 4.8
out["F"] = [f1, f2]
out["G"] = [g1, g2]
# Define a Algorithm
from pymoo.algorithms.moo.nsga2 import NSGA2
from pymoo.factory import get_sampling, get_crossover, get_mutation
# Define a Termination Criterion
from pymoo.factory import get_termination
#Optimize
from pymoo.optimize import minimize
def run():
problem = MyProblem()
algorithm = NSGA2(
pop_size=40,
n_offsprings=10,
sampling=get_sampling("real_random"),
crossover=get_crossover("real_sbx", prob=0.9, eta=15),
mutation=get_mutation("real_pm", eta=20),
eliminate_duplicates=True
)
termination = get_termination("n_gen", 40)
res = minimize(problem,
algorithm,
termination,
seed=1,
save_history=True,
verbose=True)
X = res.X.tolist()
F = res.F.tolist()
# print(res.X)
# print(res.F)
return X, F
C++调用Python和相关的类型转换
#include <Python.h>
#include <iostream>
#include <vector>
using namespace std;
vector<vector<double>> list2DToVector2D(PyObject* pReturn) {
vector<vector<double>> ret;
if (PyList_Check(pReturn)) {
int SizeOfList = PyList_Size(pReturn);
for (int i = 0; i < SizeOfList; i++) {
PyObject* ListItem = PyList_GetItem(pReturn, i);
int NumOfItems = PyList_Size(ListItem);
vector<double> temp;
for (int j = 0; j < NumOfItems; j++) {
PyObject* Item = PyList_GetItem(ListItem, j);
double result;
PyArg_Parse(Item, "d", &result);
temp.push_back(result);
}
ret.push_back(temp);
}
}
else {
cout << "parse_two_dimension_list error" << endl;
}
return ret;
}
void showVector2D(vector<vector<double>> v) {
for (int i = 0; i < v.size(); i++) {
auto& vv = v[i];
for (int j = 0; j < vv.size(); j++) {
cout << vv[j] << " ";
}
cout << "\n";
}
}
void do_multi_objective_optimization()
{
vector<vector<double>> X;
vector<vector<double>> Y;
PyObject* pModule = NULL;
PyObject* pFunc = NULL;
PyObject* pDict = NULL;
PyObject* pReturn = NULL;
if (0 == Py_IsInitialized())
{
cout << "Initialize error\n";
return;
}
pModule = PyImport_ImportModule("multi-objective-optimization");
if (!pModule) {
cout << "load error\n";
return;
}
pDict = PyModule_GetDict(pModule);
pFunc = PyDict_GetItemString(pDict, "run");
pReturn = PyEval_CallObject(pFunc, NULL);
if (PyTuple_Check(pReturn)) {
int sizeOfTuple = PyTuple_Size(pReturn);
if (sizeOfTuple == 2) {
PyObject* Item;
Item = PyTuple_GetItem(pReturn, 0);
X = list2DToVector2D(Item);
Item = PyTuple_GetItem(pReturn, 1);
Y = list2DToVector2D(Item);
}
}
else {
cout << "Not a Tuple" << endl;
}
Py_DECREF(pReturn);
Py_DECREF(pModule);
showVector2D(X);
showVector2D(Y);
}
int main()
{
Py_SetPythonHome(L"D:\\Code\\cpp_py_interpreter\\Python38");
Py_Initialize();
do_multi_objective_optimization();
Py_Finalize();
}