Commit 11479616 authored by Katter, Janike Yvonne's avatar Katter, Janike Yvonne
Browse files

Swig wrapper for python bindings

parent 3fb945e3
......@@ -22,6 +22,7 @@ if(NOT DEFINED LIB_MAN)
endif()
option(PEDANTIC "Compile with pedantic warnings" ON)
option(WERROR "Treat warnings as errors" OFF)
option(PYLMFIT "Build python bindings with Swig" ON)
## Compiler settings.
......@@ -54,6 +55,9 @@ configure_file("lmfit.pc.in" "lmfit.pc" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/lmfit.pc"
DESTINATION "${destination}/lib/pkgconfig/")
if(PYLMFIT)
include(CheckDependencies)
endif()
include(CTest)
add_subdirectory(lib)
......@@ -62,6 +66,9 @@ add_subdirectory(test)
if(LMFIT_CPPTEST)
add_subdirectory(democpp)
endif()
if (PYLMFIT)
add_subdirectory(swig)
endif()
if (LIB_MAN)
add_subdirectory(man)
endif()
......
###### Find Python
# python-dev and interpreter
set(Python_ADDITIONAL_VERSIONS 3.7 3.6 3.5 3.4)
find_package(PythonInterp 3)
find_package(PythonLibs 3)
execute_process (
COMMAND ${PYTHON_EXECUTABLE} -c "from __future__ import print_function; import numpy; print(numpy.get_include())"
ERROR_VARIABLE NUMPY_FIND_ERROR
RESULT_VARIABLE NUMPY_FIND_RESULT
OUTPUT_VARIABLE NUMPY_FIND_OUTPUT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
## process the output from the execution of the command
if(NOT NUMPY_FIND_RESULT)
set (NUMPY_INCLUDES ${NUMPY_FIND_OUTPUT})
message(STATUS "numpy includes ${NUMPY_INCLUDES}")
include_directories(SYSTEM ${NUMPY_INCLUDES})
else()
message(FATAL_ERROR "Could NOT find numpy headers")
endif()
# Python packages dir
execute_process(COMMAND ${PYTHON_EXECUTABLE} -c
"from __future__ import print_function; from distutils import sysconfig as sc; print(sc.get_python_lib(prefix='', plat_specific=True))"
RESULT_VARIABLE PYTHON_SITE_RESULT
OUTPUT_VARIABLE PYTHON_SITE
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT PYTHON_SITE_RESULT)
message(STATUS "python package destination is ${PYTHON_SITE}")
else()
message(FATAL_ERROR "could NOT determine python package directory")
endif()
# swig
find_package(SWIG REQUIRED)
include(UseSWIG)
if(POLICY CMP0071)
cmake_policy(SET CMP0071 NEW)
endif()
if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR ( CMAKE_CXX_COMPILER_ID MATCHES "CLANG"))
# suppress warning from swig::container_owner<T>::back_reference(PyObject*, PyObject*)
add_compile_options(-Wno-unused-parameter;-Wno-deprecated-declarations;-Wno-sometimes-uninitialized;-Wno-missing-field-initializers)
endif()
set(CMAKE_SWIG_OUTDIR ${CMAKE_CURRENT_BINARY_DIR})
include_directories(${CMAKE_SOURCE_DIR} )
include_directories(${PYTHON_INCLUDE_DIRS})
set_property(SOURCE lmfit.i PROPERTY CPLUSPLUS ON)
set_property(SOURCE lmfit.i PROPERTY SWIG_MODULE_NAME pylmfit)
# Set the core headers path at the swig level
get_directory_property(PYLMFIT_INCLUDE_DIRECTORIES INCLUDE_DIRECTORIES)
list(REMOVE_DUPLICATES PYLMFIT_INCLUDE_DIRECTORIES)
set(PYLMFIT_SWIG_INCLUDE_DIRECTORIES)
foreach(it ${PYLMFIT_INCLUDE_DIRECTORIES})
set(PYLMFIT_SWIG_INCLUDE_DIRECTORIES ${PYLMFIT_SWIG_INCLUDE_DIRECTORIES} "-I${it}")
endforeach()
set_property(SOURCE lmfit.i PROPERTY SWIG_FLAGS ${PYLMFIT_SWIG_INCLUDE_DIRECTORIES})
swig_add_library(pylmfit TYPE MODULE LANGUAGE python SOURCES lmfit.i)
swig_link_libraries(pylmfit ${PYTHON_LIBRARIES} lmfit)
%module "pylmfit"
%include "std_vector.i"
namespace std {
%template(VectorDouble) vector<double>;
};
%{
#include <Python.h>
#include <iostream>
#include "lib/lmdecls.h"
#include "lib/lmstruct.h"
#include "lib/lmfit.hpp"
/* Variables to be used in function_g and function_eval */
static PyObject *callback;
static int len_par = 1;
/* Wrappers for the python callback function */
double function_g(const double t, const double *p);
void function_eval(const double *const par, const int m_dat,
const void *const data, double *const fvec, int *const userbreak);
%}
%include "lib/lmdecls.h"
%include "lib/lmstruct.h"
%include "lib/lmfit.hpp"
/* Declaring function_g and function_eval as callback functions.
Cannot be used otherwise */
%constant double function_g(const double t, const double *p);
%constant void function_eval(const double *const par, const int m_dat,
const void *const data, double *const fvec, int *const userbreak);
/* Extra methods for setting python callback, number of parameters,
and printing the lm infomessages */
%inline %{
void set_callback(PyObject *callable)
{
callback = callable;
std::cout << "Callback was set." << '\n';
}
void set_parlen(int parlen)
{
len_par = parlen;
std::cout << "Length of parameter array was set." << '\n';
}
int parlen() { return len_par; }
void print_infmsg(int outcome)
{
std::cout << lm_infmsg[outcome] << '\n';
}
struct Data_Wrap {
PyObject *content;
Data_Wrap(PyObject* o): content(o) {}
};
void* to_void(Data_Wrap *data)
{
void *d = (void*)data;
return d;
}
%}
/* Implementation of function_g and function_eval */
%{
double function_g(const double t, const double *p)
{
if (!callback) {
std::cerr << "Callback function not set correctly!" << '\n';
}
else
{
PyObject *args = PyTuple_New(2);
PyTuple_SetItem(args, 0, PyFloat_FromDouble(t));
PyObject *list = PyList_New(len_par);
int i;
for (i = 0; i < len_par; ++i)
{
PyList_SetItem(list, i, PyFloat_FromDouble(p[i]));
}
PyTuple_SetItem(args, 1, list);
PyObject *result = PyObject_CallObject(callback, args);
if (PyErr_Occurred()) {
PyErr_PrintEx(0);
PyErr_Clear();
}
Py_XDECREF(list);
Py_XDECREF(args);
if (!PyFloat_Check(result)) {
Py_XDECREF(result);
std::cerr << "The callback did not return a python float!" << '\n';
}
else
{
return PyFloat_AsDouble(result);
}
}
return -1.;
}
void function_eval(const double *const par, const int m_dat,
const void *const data, double *const fvec, int *const userbreak)
{
if (!callback) {
std::cerr << "Callback function not set correctly!" << '\n';
}
else
{
PyObject *args;
if (data) {
args = PyTuple_New(2);
Data_Wrap *D = (Data_Wrap*)data;
PyTuple_SetItem(args, 1, D->content);
} else {
args = PyTuple_New(1);
}
PyObject *pars = PyList_New(len_par);
int i;
for (i = 0; i < len_par; ++i) {
PyList_SetItem(pars, i, PyFloat_FromDouble(par[i]));
}
PyTuple_SetItem(args, 0, pars);
PyObject *result = PyObject_CallObject(callback, args);
if (PyErr_Occurred()) {
PyErr_PrintEx(0);
PyErr_Clear();
}
if (!PyTuple_Check(result)) {
Py_XDECREF(result);
std::cerr << "Callback should return a tupel, but does not!!!";
} else {
PyObject *vec = PyTuple_GetItem(result, 0);
PyObject *ub = PyTuple_GetItem(result, 1);
*userbreak = (int) PyLong_AsLong(ub);
for (i = 0; i < m_dat; ++i) {
fvec[i] = PyFloat_AsDouble(PyList_GetItem(vec, i));
}
Py_XDECREF(result);
}
}
}
%}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment