Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Topic/cython #2

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ endif()

setup_project()

option(PYTHON_BINDING "Generate python binding." ON)
option(PYTHON_BINDING_USER_INSTALL "Install the Python bindings in user space" OFF)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")

if(UNIX)
Expand All @@ -50,6 +53,10 @@ add_definitions("-DEIGEN_QUADPROG_EXPORT")
add_subdirectory(src)
add_subdirectory(tests)

if(${PYTHON_BINDING})
add_subdirectory(binding/python)
endif()

# Add dependency towards the library in the pkg-config file.
pkg_config_append_libs(${PROJECT_NAME})

Expand Down
63 changes: 63 additions & 0 deletions binding/python/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Copyright 2012-2017 CNRS-UM LIRMM, CNRS-AIST JRL
#
# This file is part of eigen-quadprog.
#
# eigen-quadprog is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# eigen-quadprog is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with eigen-quadprog. If not, see <http://www.gnu.org/licenses/>.

macro(GET_EQUADPROG_PROPERTY PROPERTY)
get_target_property(EQUADPROG_${PROPERTY} eigen-quadprog ${PROPERTY})
if(NOT EQUADPROG_${PROPERTY})
set(EQUADPROG_${PROPERTY} "")
endif()
endmacro()
GET_EQUADPROG_PROPERTY(COMPILE_FLAGS)
GET_EQUADPROG_PROPERTY(INCLUDE_DIRECTORIES)
LIST(APPEND EQUADPROG_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/../../src)
GET_EQUADPROG_PROPERTY(LINK_FLAGS)
GET_EQUADPROG_PROPERTY(LINK_LIBRARIES)
GET_EQUADPROG_PROPERTY(LOCATION)
configure_file(setup.in.py ${CMAKE_CURRENT_SOURCE_DIR}/setup.py)

# Build the bindings locally at build time for test purposes
add_custom_target(eigen-quadprog-python-bindings ALL
COMMAND python setup.py build_ext --inplace
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Generating local eigen-quadprog Python bindings"
DEPENDS eigen_quadprog/c_eigen_quadprog.pxd eigen_quadprog/eigen_quadprog.pxd eigen_quadprog/eigen_quadprog.pyx
)

if(NOT ${DISABLE_TESTS})
add_custom_target(test-eigen-quadprog-python-bindings
COMMAND nosetests
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Test eigen-quadprog Python bindings"
DEPENDS tests/test_eigen-quadprog_pickle.py
)
add_test(NAME PythonBindingsTest
COMMAND nosetests
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endif()

set(PIP_EXTRA_OPTIONS "")
if(${PYTHON_BINDING_USER_INSTALL})
set(PIP_EXTRA_OPTIONS "--user")
endif()
# Install the bindings
add_custom_target(install-eigen-quadprog-python-bindings
COMMAND pip install . ${PIP_EXTRA_OPTIONS}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Install eigen-quadprog Python bindings"
)
install(CODE "execute_process(COMMAND ${CMAKE_MAKE_PROGRAM} install-eigen-quadprog-python-bindings)")
1 change: 1 addition & 0 deletions binding/python/eigen_quadprog/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .eigen_quadprog import *
35 changes: 35 additions & 0 deletions binding/python/eigen_quadprog/c_eigen_quadprog.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright 2012-2017 CNRS-UM LIRMM, CNRS-AIST JRL
#
# This file is part of eigen-lssol.
#
# eigen-lssol is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# eigen-lssol is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with eigen-lssol. If not, see <http://www.gnu.org/licenses/>.

cimport eigen.c_eigen as c_eigen
from libcpp cimport bool

cdef extern from "<eigen-quadprog/QuadProg.h>" namespace "Eigen":
cdef cppclass QuadProgDense:
QuadProgDense()
QuadProgDense(int, int, int)

void problem(int, int, int)

const c_eigen.VectorXd& result()

double cost()

bool solve(const c_eigen.MatrixXd&, const c_eigen.VectorXd&,
const c_eigen.MatrixXd&,
const c_eigen.VectorXd&, const c_eigen.MatrixXd&,
const c_eigen.VectorXd&, bool)
21 changes: 21 additions & 0 deletions binding/python/eigen_quadprog/eigen_quadprog.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2012-2017 CNRS-UM LIRMM, CNRS-AIST JRL
#
# This file is part of eigen-quadprog.
#
# eigen-lssol is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# eigen-lssol is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with eigen-lssol. If not, see <http://www.gnu.org/licenses/>.

cimport c_eigen_quadprog

cdef class QuadProgDense(object):
cdef c_eigen_quadprog.QuadProgDense * impl
50 changes: 50 additions & 0 deletions binding/python/eigen_quadprog/eigen_quadprog.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# distutils: language = c++

# Copyright 2012-2017 CNRS-UM LIRMM, CNRS-AIST JRL
#
# This file is part of eigen-quadprog.
#
# eigen-lssol is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# eigen-lssol is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with eigen-lssol. If not, see <http://www.gnu.org/licenses/>.

cdef extern from "private_typedefs.h":
pass

cimport c_eigen_quadprog

cimport eigen.eigen as eigen

cdef class QuadProgDense(object):
def __dealloc__(self):
if type(self) is QuadProgDense:
del self.impl
def __cinit__(self, *args):
if type(self) is not QuadProgDense:
return
if len(args) == 0:
self.impl = new c_eigen_quadprog.QuadProgDense()
elif len(args) == 3:
self.impl = new c_eigen_quadprog.QuadProgDense(args[0], args[1], args[2])
else:
raise TypeError("Wrong arguments passed to LSSOL ctor")
def problem(self, nrvar, nreq, nrineq):
self.impl.problem(nrvar, nreq, nrineq)
def result(self):
return eigen.VectorXdFromC(self.impl.result())
def cost(self):
return self.impl.cost()
def solve(self, eigen.MatrixXd Q, eigen.VectorXd C,
eigen.MatrixXd Aeq, eigen.VectorXd Beq,
eigen.MatrixXd Aineq, eigen.VectorXd Bineq,
isDecomp = False):
return self.impl.solve(Q.impl, C.impl, Aeq.impl, Beq.impl, Aineq.impl, Bineq.impl,isDecomp)
7 changes: 7 additions & 0 deletions binding/python/eigen_quadprog/private_typedefs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include <Eigen/Core>

namespace Eigen
{
typedef Matrix<double, 6, 1> Vector6d;
typedef Matrix<double, 6, 6> Matrix6d;
}
119 changes: 119 additions & 0 deletions binding/python/setup.in.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Copyright 2012-2017 CNRS-UM LIRMM, CNRS-AIST JRL
#
# This file is part of eigen-quadprog.
#
# eigen-quadprog is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# eigen-quadprog is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with eigen-quadprog. If not, see <http://www.gnu.org/licenses/>.

from __future__ import print_function
try:
from setuptools import setup
from setuptools import Extension
except ImportError:
from distutils.core import setup
from distutils.extension import Extension

from Cython.Build import cythonize

import hashlib
import os
import subprocess

from numpy import get_include as numpy_get_include

win32_build = os.name == 'nt'

this_path = os.path.dirname(os.path.realpath(__file__))
with open(this_path + '/eigen_quadprog/__init__.py', 'w') as fd:
fd.write('from .eigen_quadprog import *\n')

sha512 = hashlib.sha512()
src_files = ['eigen_quadprog/eigen_quadprog.pyx', 'eigen_quadprog/c_eigen_quadprog.pxd', 'eigen_quadprog/eigen_quadprog.pxd']
src_files = [ '{}/{}'.format(this_path, f) for f in src_files ]
for f in src_files:
chunk = 2**16
with open(f, 'r') as fd:
while True:
data = fd.read(chunk)
if data:
sha512.update(data.encode('ascii'))
else:
break
version_hash = sha512.hexdigest()[:7]

class pkg_config(object):
def __init__(self):
self.compile_args = []
self.include_dirs = [ x for x in '@EQUADPROG_INCLUDE_DIRECTORIES@'.split(';') if len(x) ]
self.library_dirs = [ x for x in '@EQUADPROG_LINK_FLAGS@'.split(';') if len(x) ]
self.libraries = ['eigen-quadprog']
equadprog_location = '@EQUADPROG_LOCATION@'
self.library_dirs.append(os.path.dirname(equadprog_location))
self.found = True

python_libs = []
python_lib_dirs = []
python_others = []
if not win32_build:
tokens = subprocess.check_output(['python-config', '--ldflags']).split()
tokens = [ token.decode('ascii') for token in tokens ]
for token in tokens:
flag = token[:2]
value = token[2:]
if flag == '-l':
python_libs.append(value)
elif flag == '-L':
python_lib_dirs.append(value)
elif token[:1] == '-':
python_others.append(token)

config = pkg_config()

config.compile_args.append('-std=c++11')
for o in python_others:
config.compile_args.append(o)
config.include_dirs.append(os.getcwd() + "/include")
if not win32_build:
config.library_dirs.extend(python_lib_dirs)
config.libraries.extend(python_libs)
else:
config.compile_args.append("-DWIN32")

def GenExtension(name, pkg, ):
pyx_src = name.replace('.', '/')
cpp_src = pyx_src + '.cpp'
pyx_src = pyx_src + '.pyx'
ext_src = pyx_src
if pkg.found:
return Extension(name, [ext_src], extra_compile_args = pkg.compile_args, include_dirs = pkg.include_dirs + [numpy_get_include()], library_dirs = pkg.library_dirs, libraries = pkg.libraries)
else:
print("Failed to find {}".format(pkg.name))
return None

extensions = [
GenExtension('eigen_quadprog.eigen_quadprog', config)
]

extensions = [ x for x in extensions if x is not None ]
packages = ['eigen_quadprog']
data = ['__init__.py', 'c_eigen_quadprog.pxd', 'eigen_quadprog.pxd']

extensions = cythonize(extensions)

setup(
name = 'eigen_quadprog',
version='1.0.0-{}'.format(version_hash),
ext_modules = extensions,
packages = packages,
package_data = { 'eigen_quadprog': data }
)
Loading