I've tried to call some c++ functions from Python. I am using python from windows in anaconda.
I tried it with types, but it appears an error when loading the shared library (both .dll and .so)
DO you know any simple way to call these functions from Python?
Here you have the c++ functions:
#include <vector>
#include <cmath>
#include <ctime>
#include <stdio.h>
#include <string>
using namespace std;
typedef vector<double> VE;
typedef vector<VE> VVE;
double sq_norm(VE x, VE y, double s_v, double hat_s_v) {
double sq_norm = 0;
int size = x.size();
for (int i = 0; i < size; ++i){
sq_norm += pow((hat_s_v/s_v)*x[i]-y[i],2);
}
return sq_norm;
}
VE gradient_rho(VE y, VVE eta, double s_v, double hat_s_v){
int nu = eta[0].size();
int N = eta.size();
VE gradient(nu,0);
for (int j = 0; j < N; ++j) {
VVE add = VVE(nu, VE(1, 0));
double exponential = exp(-0.5*sq_norm(eta[j], y, s_v, hat_s_v)/(pow(hat_s_v,2)));
for (int i = 0; i < nu; ++i) {
gradient[i] += ((hat_s_v/s_v)*eta[j][i]-y[i])*exponential/(N*(pow(hat_s_v,2)));
}
}
return gradient;
}
double rho(VE y, VVE eta, double s_v, double hat_s_v){
int nu = eta[0].size();
int N = eta.size();
double rho_ = 0;
for (int j = 0; j < N; ++j) {
VVE add = VVE(nu, VE(1, 0));
double exponential = exp(-0.5*sq_norm(eta[j], y, s_v, hat_s_v)/(pow(hat_s_v,2)));
rho_ += exponential/N;
}
return rho_;
}
The command to generate the .ddl file is:
cpp -fPIC -shared -o library.dll library.cc
The python script where I try to load the library is:
from ctypes import *
import ctypes
import pathlib
so_file = "C://Users//asus2//Box Sync//Box Sync//PLOM With Constraints//Code//library.dll"
c_lib = ctypes.windll.LoadLibrary(so_file)
Related
I tried to call this C++ file (myfunc.cpp) from Python. I decided to use ctypes module, since it seems to work pretty well for both C and C++ code.
I followed several tutorial (e.g., Modern/2020 way to call C++ code from Python), which suggests to add 'extern C' on the top of the C++ function to be called in Python.
#include <iostream>
#include <cmath>
#include <vector>
#include <cstdio>
using namespace std;
int from_xy(int x, int y, int nside) {
return x + (nside * y);
}
std::vector<int> to_xy(int k, int nside) {
int x = k%nside;
int y = floor(k / nside);
vector<int> res(x, y);
return res;
}
int modNegOperator(int k, int n){
return ((k %= n) < 0) ? k+n : k;
}
extern "C"
double** create2Darray(unsigned nside, double mx, double my) {
int n_matrix = nside * nside;
double **array2D = 0;
array2D = new double *[n_matrix];
for (int h = 0; h < n_matrix; h++) {
array2D[h] = new double[n_matrix];
for (int w = 0; w < n_matrix; w++) {
// fill in some initial values
// (filling in zeros would be more logic, but this is just for the example)
array2D[h][w] = 0;
}
}
for (int h = 0; h < n_matrix; h++){
std::vector<int> xy_vec = to_xy(h, nside);
int modneg1 = modNegOperator(xy_vec[0] + 1,nside);
int modneg2 = modNegOperator(xy_vec[0] - 1,nside);
int modneg3 = modNegOperator(xy_vec[1] + 1,nside);
int modneg4 = modNegOperator(xy_vec[1] - 1,nside);
int pos1 = from_xy(modneg1, xy_vec[1], nside);
int pos2 = from_xy(modneg2, xy_vec[1], nside);
int pos3 = from_xy(xy_vec[0], modneg3, nside);
int pos4 = from_xy(xy_vec[0], modneg4, nside);
double half_mx = mx / 2;
double half_my = my / 2;
array2D[h][h] = 0;
array2D[h][pos1] = half_mx;
array2D[h][pos2] = half_mx;
array2D[h][pos3] = half_my;
array2D[h][pos4] = half_my;
}
return array2D;
}
I have then created a shared library:
g++ -fPIC -shared -o libTest.so myfunc.cpp
Created a myLib.py file including:
import ctypes
import sys
import os
dir_path = os.path.dirname(os.path.realpath(__file__))
handle = ctypes.CDLL(dir_path + "/libTest.so")
handle.create2Darray.argtypes = [ctypes.c_int, ctypes.c_double, ctypes.c_double]
def create2Darray(nside, mx, my):
return handle.create2Darray(nside, mx, my)
However, when I then try to run my function in python:
from myLib import *
create2Darray(4, 0.01,0.01)
I get 'Segmentation fault'.
Do you know what am I doing wrong? Do you have any suggestion? Or perhaps suggest another approach with which I can import my C++ function in python. The alternative ofcourse would be to re-write the code in C.
As mentioned in the chat we had, the code posted in the question segfaults because the following code returns a zero-length vector when first called:
std::vector<int> to_xy(int k, int nside) {
int x = k%nside;
int y = floor(k / nside);
vector<int> res(x, y); // if k==0, this is length 0
return res;
}
then this code faults here:
for (int h = 0; h < n_matrix; h++){
std::vector<int> xy_vec = to_xy(h, nside); // length 0
int modneg1 = modNegOperator(xy_vec[0] + 1,nside); // xy_vec[0] faults.
The code we discussed in chat didn't fail because:
vector<int> res(x, y); // if k==0, this is length 0
had been changed to:
vector<int> res{x, y}; // curly braces, length 2
After resolving that, the Python code just needed .restype defined, and technically c_uint for the first parameter:
handle.create2Darray.argtypes = ctypes.c_uint, ctypes.c_double, ctypes.c_double
handle.create2Darray.restype = ctypes.POINTER(ctypes.POINTER(ctypes.c_double))
Then the code would return the double** correctly.
In python language, we can use A[A!=0]=-10 to trans all the non-zero value in A to -10. How can I implement this function in C++ language or is here any similar function in 3rd party?
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat A(3, 3, CV_16SC1);
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
A.at<short>(i, j) = i + j;
}
}
for (auto& value : A) if (value != 2) value = -10;
}
There is std::ranges::replace in C++20:
std::vector<int> values = {1,2,3,1,2,5,3,165};
std::ranges::replace(values, 3, 999);
And, similarly, std::ranges::replace_if in C++20:
std::vector<int> values = {1,2,0,1,0,5,3,165};
std::ranges::replace_if(values, [](int a) { return a != 0;}, -10);
You can see it in
compiler explorer
It is also possible to use std::not_equal_to with std::bind_front instead of the lambda, if you prefer it that way, like this
Edit: here is the bind_front code (from the above link):
std::ranges::replace_if(values, std::bind_front(std::not_equal_to{}, 0), -20);
Range-based for loops will not work since the cv::Mat::begin() is a member function template. You'll have to use begin<mat_type>() and end<mat_type>().
Example:
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <algorithm>
#include <iostream>
#include <iterator>
int main() {
cv::Mat A(3, 3, CV_16SC1);
using mtype = short; // convenience typedef
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
A.at<mtype>(i, j) = i + j; // using mtype
}
}
// using mtype:
for(auto it = A.begin<mtype>(); it != A.end<mtype>(); ++it) {
if(*it != 2) *it = -10;
}
// print result:
std::copy(A.begin<mtype>(), A.end<mtype>(),
std::ostream_iterator<mtype>(std::cout, "\n"));
}
Or using the std::replace_if algorithm to replace all non 2's with -10:
std::replace_if(A.begin<mtype>(), A.end<mtype>(), // using mtype
[](auto& value) { return value != 2; }, -10);
Output:
-10
-10
2
-10
2
-10
2
-10
-10
In STL container there is a function that has similar behavior. For example:
#include <vector>
#include <algorithm>
int main() {
using namespace std;
vector<int> example_vector{0,1,4,1,5};
for_each(example_vector.begin(), example_vector.end(), [](auto& a) { if (a != 0)a = -10; });
}
Although the syntax seems not simple like python, it is optimized, and may be parallelized by execution policy.
I'm working with Python and C++ integration, and I'm on MacOS.
When I use all of the PyObject methods they all seem to work but none of the PyModule methods seem to be working.
This is my code:
#include <iostream>
#include <cmath>
#include <vector>
#if PY_MAJOR_VERSION >= 3
#define PY3K
#endif
#ifdef __APPLE__
#include <Python/Python.h>
#else
#include <Python.h>
#endif
#define space " "
#define epoch int(1000)
using namespace std;
double hypothesis(const std::vector<double>& b_val, double x){
return b_val[0] + b_val[1] * x;
}
std::vector<double> regression(const std::vector<double>& x, const std::vector<double>& y,
int epochs, double learning_rate){
if(!epochs) epochs = epoch;
double _m = 0;
double _b = 0;
std::vector<double> _values(2);
int N = x.size();
for(int i = 0; i < epochs; i++){
_values[0] = _b, _values[1] = _m;
double dm = 0;
double db = 0;
double cost = 0;
for(int j = 0; j < N; j++){
double p = hypothesis(_values, x[j]);
cost += pow(y[j] - p, 2);
dm += (-2.0 / N) * (x[j] * (y[j] - p));
db += (-2.0 / N) * (y[j] - p);
}
cost /= N;
_m = _m - (learning_rate * dm);
_b = _b - (learning_rate * db);
if ((i + 1) % 100 == 0)
std::cout << "Epoch: " << (i + 1) << " Cost: " << cost << std::endl;
}
std::vector<double> result(2);
result[0] = _m, result[1] = _b;
return result;
}
static PyObject * fit(PyObject * self, PyObject * args){
PyObject *x;
PyObject *y;
double learning_rate;
int epochs;
int N;
if(!PyArg_ParseTuple(args, "00di0i", &x, &y, &epochs, &learning_rate, &N)){
return nullptr;
}
std::vector<double> _x(N), _y(N);
for(int i = 0; i < N; i++){
_x[i] = PyFloat_AsDouble(PyList_GetItem(x, (Py_ssize_t)i));
_y[i] = PyFloat_AsDouble(PyList_GetItem(y, (Py_ssize_t)i));
}
std::vector<double> _result = regression(_x, _y, epochs, learning_rate);
PyObject *result = PyTuple_New(2);
for(int i = 0; i < 2; i++){
PyTuple_SetItem(result, i, PyFloat_FromDouble(_result[i]));
}
return Py_BuildValue("s", result);
}
static PyMethodDef linreg_methods[] = {
{"fit", (PyCFunction)fit, METH_VARARGS, "Linear Regression"},
{nullptr}
};
static PyModuleDef linear_regression = {
PyModuleDef_HEAD_INIT,
"linear_regression"
"Linear Regression",
-1,
linreg_methods
};
PyMODINIT_FUNC PyInit_linear_regression(void){
return PyModule_Create(&linear_regression);
}
The output I get is error: unknown type name 'PyModuleDef'.
I can't seem to understand what the problem is.
You will need to make sure the included Python.h is from the Python3.x build, and that you link to the corresponding library, for example, on Linux that would be:
g++ my_module.cpp -I/usr/include/python3.8/ -lpython3.8
But on MacOS this file would be dependent on where/how you installed Python3. You should be able to use locate Python.h and find the Python3 directory.
I have the following c++ and header file, which I would like to compile in Cython:
c++ code:
\\ CA.cpp c++ code
#include "CA.h"
#include<iostream>
#include <vector>
Cell::Cell() {}
// construct cell: cell is defined with position (x,y,z coordinates) and neighborhood
Cell::Cell(int x, int y, int z) {
this->x = x;
this->y = y;
this->z = z;
std::vector<int> ngh;
}
// Return the neighbors of the cell
void Cell::neighbors( ) {
std::vector<int> a{ -1, 0, 1 };
std::vector<int> ranges(gridSize, 0);
for (int i = 0; i < ranges.size(); i++) {
ranges[i] = i;
}
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
for (int k = -1; k <= 1; k++) {
int x_ngh = ranges[Cell::x + i];
int y_ngh = ranges[Cell::y + j];
int z_ngh = ranges[Cell::z + k];
int id_ngh = x_ngh + (y_ngh - 1) * gridSize + (z_ngh - 1) * gridSize * gridSize;
Cell:ngh.push_back(id_ngh);
}
}
}
}
and header file:
\\CA.h
#ifndef CA_H
#define CA_H
#include <vector>
const int gridSize = 400;
class Cell {
public:
int x, y, z;
std::vector<int> ngh;
Cell();
Cell(int x, int y, int z);
void neighbors();
};
#endif
In order to compile the code using cython I created CA.pxd file:
cdef extern from "CA.cpp":
pass
# Declare the class with cdef
cdef extern from "CA.h":
cdef cppclass Cell:
Cell() except +
Cell(int, int, int) except +
int x,y,z
neighbors()
and CA.pyx
# distutils: language = c++
from CA cimport *
from libcpp.vector cimport vector
cdef class PyCell:
cdef Cell c_cell
def __cinit__(self, int x, int y, int z):
self.c_cell = Cell(x,y,z)
def neighbors(self):
return self.c_cell.neighbors()
Finally, I created setup.py file:
from Cython.Build import cythonize
setup(ext_modules=cythonize("CA.pyx"))
When I run the command:
python setup.py build_ext --inplace
I get an error LINK : error LNK2001: unresolved external symbol PyInit_CA. I don't know why. Any help greatly appreciated.
I created a *.so file for use in Python using SWIG, but when I import it I get this:
/_analyzer.so: undefined symbol: autocorellation
I did almost everything according to this instruction: https://scipy.github.io/old-wiki/pages/Cookbook/SWIG_NumPy_examples.html
my code is following:
analyzer.h:
void autocorellation(double *in, double *out, long long n);
analyzer.cpp:
#include "analyzer.h"
#include <math.h>
#include <stdlib.h>
#define PI 3.14159265358979323846
typedef struct {
double real;
double im;
} Complex;
void complex_multiply(Complex a,Complex b,Complex* c){
c->real = a.real * b.real - a.im * b.im;
c->im = a.real * b.im + a.im * b.real;
}
void complex_multiply_int(int a, Complex b,Complex* c){
c->real = a * b.real;
c->im = a * b.im;
}
void complex_sum(Complex a,Complex b,Complex* c){
c->real = a.real + b.real;
c->im = a.im + b.im;
}
void complex_conjugate(Complex* a,Complex* b,long long n){
for(int i = 0; i < n; ++i){
b[i].real = a[i].real;
b[i].im = -1 * a[i].im;
}
}
long long rev (long long num, long long lg_n) {
long long res = 0;
for (long long i=0; i < lg_n; ++i)
if (num & (1 << i))
res |= 1 << (lg_n-1-i);
return res;
}
void fft (Complex* a, long long n,bool invert) {
long long lg_n = 0;
while ((1 << lg_n) < n)
++lg_n;
for (long long i=0; i<n; ++i){
long long r= rev(i,lg_n);
if (i < r){
a[i].real = a[i].real + a[r].real;
a[r].real = a[i].real - a[r].real;
a[i].real = a[i].real - a[r].real;
a[i].im = a[i].im + a[r].im;
a[r].im = a[i].im - a[r].im;
a[i].im = a[i].im - a[r].im;
}
}
for (long long len=2; len<=n; len <<= 1) {
double ang = 2*PI/len * (invert ? -1 : 1);
Complex wn;
wn.real = cos(ang);
wn.im = sin(ang);
for (long long i=0; i<n; i+=len) {
Complex w;
w.real = 1;
w.im = 0;
long long ll = (long long)(len * 0.5);
for (long long j=0; j< ll; ++j) {
Complex u = a[i+j],v;
complex_multiply(a[i+j+ll],w,&v);
complex_sum(u,v,&a[i+j]);
complex_multiply_int(-1,v,&v);
complex_sum(u,v,&a[i+j+ll]);
complex_multiply(w,wn,&w);
}
}
}
if (invert)
for (long long i=0; i<n; ++i){
a[i].real /= n;
a[i].im /= n;
}
}
void autocorellation(double *in, double *out, long long n){
long long le = 1;
while(n > le)
le *= 2;
double m = 0;
for(int i = 0; i < n; ++i)
m+=in[i];
m /= n;
for(int i = 0; i < n; ++i)
in[i] -= m;
Complex* a = (Complex*) malloc(le*sizeof(Complex));
Complex* b = (Complex*) malloc(le*sizeof(Complex));
for(long long i = 0; i < n; ++i){
a[i].im = 0;
a[i].real = in[i];
}
for(long long i = n; i < le; ++i){
a[i].im = 0;
a[i].real = 0;
}
fft(a,le,false);
complex_conjugate(a,b,le);
Complex* c = (Complex*) malloc(le*sizeof(Complex));
for(long long i = 0; i < le; ++i)
complex_multiply(b[i],a[i],&c[i]);
fft(c,le,true);
for(long long i = 0; i < n; ++i)
out[i] = (c[i].real/c[0].real);
free(a);
free(b);
free(c);
}
analyzer.i:
%module analyzer
%{
#define SWIG_FILE_WITH_INIT
#include "analyzer.h"
%}
%include "numpy.i"
%init %{
import_array();
%}
%apply (double* IN_ARRAY1,int DIM1) {(double *in, long long n)}
%apply (double* ARGOUT_ARRAY1,int DIM1) {(double *out, long long n)}
%include "analyzer.h"
setup.py:
#! /usr/bin/env python
# System imports
from distutils.core import *
from distutils import sysconfig
# Third-party modules - we depend on numpy for everything
import numpy
# Obtain the numpy include directory. This logic works across numpy versions.
try:
numpy_include = numpy.get_include()
except AttributeError:
numpy_include = numpy.get_numpy_include()
# ezrange extension module
_analyzer = Extension("_analyzer",
["analyzer.i","analyzer.cpp"],
include_dirs = [numpy_include],
)
# ezrange setup
setup( name = "range function",
description = "Autocorellation function evaluation",
author = "Bodya",
version = "1.0",
ext_modules = [_analyzer]
)
The difference between your code and the cookbook examples is that your code is C++. Therefore, you need to pass the -c++ option to SWIG. In the construction of Extension(...) in setup.py, simply add swig_opts=['-c++'],.
Note that distutils will still invoke the C compiler on the generated wrapper file, but this will have a .cpp extension, so it should be compiled correctly if the compiler is gcc or clang.
My experience using distutils or setuptools for C++ SWIG extensions that are even slightly beyond trivial has been poor, so I invoke SWIG to generate a wrapper outside of distutils (I do it via a Makefile) and only use distutils to compile the extension from the wrapper file.