Skip to content

Commit 3285e44

Browse files
authored
Merge pull request #29 from JohanMabille/pytensor
Pytensor
2 parents 01d1a07 + 9d2289e commit 3285e44

24 files changed

+1919
-870
lines changed

.gitignore

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,6 @@
3030
# Vim tmp files
3131
*.swp
3232

33-
# Test build artefacts
34-
test/test_xtensor
35-
test/CMakeCache.txt
36-
test/Makefile
37-
test/CMakeFiles/
38-
test/cmake_install.cmake
39-
4033
# Documentation build artefacts
4134
docs/CMakeCache.txt
4235
docs/xml/
@@ -48,3 +41,5 @@ docs/build/
4841
# Python
4942
*.py[cod]
5043
__pycache__
44+
build
45+
*.egg-info

CMakeLists.txt

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
############################################################################
2+
# Copyright (c) 2016, Johan Mabille and Sylvain Corlay #
3+
# #
4+
# Distributed under the terms of the BSD 3-Clause License. #
5+
# #
6+
# The full license is in the file LICENSE, distributed with this software. #
7+
############################################################################
8+
9+
cmake_minimum_required(VERSION 3.1)
10+
project(xtensor-python)
11+
12+
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
13+
set(XTENSOR_PYTHON_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
14+
15+
# Versionning
16+
# ===========
17+
18+
set(XTENSOR_PYTHON_CONFIG_FILE
19+
"${XTENSOR_PYTHON_INCLUDE_DIR}/xtensor-python/xtensor_python_config.hpp")
20+
file(STRINGS ${XTENSOR_PYTHON_CONFIG_FILE} xtensor_python_version_defines
21+
REGEX "#define XTENSOR_PYTHON_VERSION_(MAJOR|MINOR|PATCH)")
22+
foreach(ver ${xtensor_python_version_defines})
23+
if(ver MATCHES "#define XTENSOR_PYTHON_VERSION_(MAJOR|MINOR|PATCH) +([^ ]+)$")
24+
set(XTENSOR_PYTHON_VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}" CACHE INTERNAL "")
25+
endif()
26+
endforeach()
27+
set(${PROJECT_NAME}_VERSION
28+
${XTENSOR_PYTHON_VERSION_MAJOR}.${XTENSOR_PYTHON_VERSION_MINOR}.${XTENSOR_PYTHON_VERSION_PATCH})
29+
message(STATUS "xtensor-python v${${PROJECT_NAME}_VERSION}")
30+
31+
# Build
32+
# =====
33+
34+
OPTION(BUILD_TESTS "xtensor test suite" ON)
35+
36+
set(XTENSOR_PYTHON_HEADERS
37+
${XTENSOR_PYTHON_INCLUDE_DIR}/xtensor-python/pyarray.hpp
38+
${XTENSOR_PYTHON_INCLUDE_DIR}/xtensor-python/pybuffer_adaptor.hpp
39+
${XTENSOR_PYTHON_INCLUDE_DIR}/xtensor-python/pycontainer.hpp
40+
${XTENSOR_PYTHON_INCLUDE_DIR}/xtensor-python/pytensor.hpp
41+
${XTENSOR_PYTHON_INCLUDE_DIR}/xtensor-python/pyvectorize.hpp
42+
${XTENSOR_PYTHON_INCLUDE_DIR}/xtensor-python/xtensor_python_config.hpp
43+
)
44+
45+
if(BUILD_TESTS)
46+
47+
include_directories(${XTENSOR_PYTHON_INCLUDE_DIR})
48+
find_package(xtensor REQUIRED)
49+
include_directories(${xtensor_INCLUDE_DIR})
50+
find_package(NumPy REQUIRED)
51+
include_directories(${NUMPY_INCLUDE_DIRS})
52+
53+
#TODO replace this with a find_package(pybind11 REQUIRED)
54+
# in parent CMakeLists when pybind11 CMakeLists has been fixed.
55+
find_package(PythonLibs REQUIRED)
56+
include_directories(${PYTHON_INCLUDE_DIRS})
57+
58+
if(MSVC)
59+
set(PYTHON_MODULE_EXTENSION ".pyd")
60+
else()
61+
set(PYTHON_MODULE_EXTENSION ".so")
62+
endif()
63+
64+
#add_subdirectory(test)
65+
add_subdirectory(benchmark)
66+
endif()
67+
68+
# Installation
69+
# ============
70+
71+
include(GNUInstallDirs)
72+
include(CMakePackageConfigHelpers)
73+
74+
install(FILES ${XTENSOR_PYTHON_HEADERS}
75+
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/xtensor-python)
76+
77+
# GNUInstallDirs "DATADIR" wrong here; CMake search path wants "share".
78+
set(XTENSOR_PYTHON_CMAKECONFIG_INSTALL_DIR "share/cmake/${PROJECT_NAME}" CACHE STRING "install path for xtensor-pythonConfig.cmake")
79+
80+
configure_package_config_file(${PROJECT_NAME}Config.cmake.in
81+
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
82+
INSTALL_DESTINATION ${XTENSOR_PYTHON_CMAKECONFIG_INSTALL_DIR})
83+
write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
84+
VERSION ${${PROJECT_NAME}_VERSION}
85+
COMPATIBILITY AnyNewerVersion)
86+
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
87+
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
88+
DESTINATION ${XTENSOR_PYTHON_CMAKECONFIG_INSTALL_DIR})
89+

benchmark/CMakeLists.txt

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
############################################################################
2+
# Copyright (c) 2016, Johan Mabille and Sylvain Corlay #
3+
# #
4+
# Distributed under the terms of the BSD 3-Clause License. #
5+
# #
6+
# The full license is in the file LICENSE, distributed with this software. #
7+
############################################################################
8+
9+
message(STATUS "Forcing tests build type to Release")
10+
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
11+
12+
include(CheckCXXCompilerFlag)
13+
14+
string(TOUPPER "${CMAKE_BUILD_TYPE}" U_CMAKE_BUILD_TYPE)
15+
16+
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Intel")
17+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -Wunused-parameter -Wextra -Wreorder -Wconversion")
18+
CHECK_CXX_COMPILER_FLAG("-std=c++14" HAS_CPP14_FLAG)
19+
20+
if (HAS_CPP14_FLAG)
21+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
22+
else()
23+
message(FATAL_ERROR "Unsupported compiler -- xtensor requires C++14 support!")
24+
endif()
25+
26+
# Enable link time optimization and set the default symbol
27+
# visibility to hidden (very important to obtain small binaries)
28+
if (NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG)
29+
# Default symbol visibility
30+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
31+
32+
# Check for Link Time Optimization support
33+
# (GCC/Clang)
34+
CHECK_CXX_COMPILER_FLAG("-flto" HAS_LTO_FLAG)
35+
if (HAS_LTO_FLAG)
36+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto")
37+
endif()
38+
39+
# Intel equivalent to LTO is called IPO
40+
if (CMAKE_CXX_COMPILER_ID MATCHES "Intel")
41+
CHECK_CXX_COMPILER_FLAG("-ipo" HAS_IPO_FLAG)
42+
if (HAS_IPO_FLAG)
43+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ipo")
44+
endif()
45+
endif()
46+
endif()
47+
endif()
48+
49+
if(MSVC)
50+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /MP /bigobj")
51+
set(CMAKE_EXE_LINKER_FLAGS /MANIFEST:NO)
52+
foreach(flag_var
53+
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
54+
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
55+
string(REPLACE "/MD" "-MT" ${flag_var} "${${flag_var}}")
56+
endforeach()
57+
endif()
58+
59+
set(XTENSOR_PYTHON_BENCHMARK
60+
main.cpp
61+
)
62+
63+
set(XTENSOR_PYTHON_BENCHMARK_TARGET benchmark_xtensor_python)
64+
add_library(${XTENSOR_PYTHON_BENCHMARK_TARGET} MODULE EXCLUDE_FROM_ALL
65+
${XTENSOR_PYTHON_BENCHMARK} ${XTENSOR_PYTHON_HEADERS})
66+
67+
set_target_properties(${XTENSOR_PYTHON_BENCHMARK_TARGET} PROPERTIES PREFIX "")
68+
set_target_properties(${XTENSOR_PYTHON_BENCHMARK_TARGET} PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}")
69+
70+
if (APPLE)
71+
target_link_libraries(${XTENSOR_PYTHON_BENCHMARK_TARGET} PRIVATE "-undefined dynamic_lookup")
72+
elseif (MSVC)
73+
target_link_libraries(${XTENSOR_PYTHON_BENCHMARK_TARGET} ${PYTHON_LIBRARIES})
74+
else ()
75+
target_link_libraries(${XTENSOR_PYTHON_BENCHMARK_TARGET} "-shared")
76+
endif()
77+
78+
configure_file(benchmark_pyarray.py benchmark_pyarray.py COPYONLY)
79+
configure_file(benchmark_pytensor.py benchmark_pytensor.py COPYONLY)
80+
configure_file(benchmark_pybind_array.py benchmark_pybind_array.py COPYONLY)
81+
82+
add_custom_target(xbenchmark DEPENDS ${XTENSOR_PYTHON_BENCHMARK_TARGET})
83+

benchmark/benchmark_pyarray.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from xtensor_python_benchmark import sum_array
1+
from benchmark_xtensor_python import sum_array
22
import numpy as np
33

44
u = np.ones(1000000, dtype=float)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from benchmark_xtensor_python import pybind_sum_array
2+
import numpy as np
3+
4+
u = np.ones(1000000, dtype=float)
5+
from timeit import timeit
6+
print (timeit ('pybind_sum_array(u)', setup='from __main__ import u, pybind_sum_array', number=1000))

benchmark/benchmark_pytensor.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from benchmark_xtensor_python import sum_tensor
2+
import numpy as np
3+
4+
u = np.ones(1000000, dtype=float)
5+
#print(sum_tensor(u))
6+
from timeit import timeit
7+
print (timeit ('sum_tensor(u)', setup='from __main__ import u, sum_tensor', number=1000))

benchmark/main.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
11
#include "pybind11/pybind11.h"
2+
#include "pybind11/numpy.h"
3+
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
4+
#include "numpy/arrayobject.h"
25
#include "xtensor/xtensor.hpp"
36
#include "xtensor/xarray.hpp"
47
#include "xtensor-python/pyarray.hpp"
8+
#include "xtensor-python/pytensor.hpp"
59

610
#include <complex>
711

812
namespace py = pybind11;
913

10-
PYBIND11_PLUGIN(xtensor_python_benchmark)
14+
PYBIND11_PLUGIN(benchmark_xtensor_python)
1115
{
12-
py::module m("xtensor_python_benchmark", "Benchmark module for xtensor python bindings");
16+
if(_import_array() < 0)
17+
{
18+
PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import");
19+
return nullptr;
20+
}
21+
22+
py::module m("benchmark_xtensor_python", "Benchmark module for xtensor python bindings");
1323

1424
m.def("sum_array", [](xt::pyarray<double> const& x) {
1525
double sum = 0;
@@ -19,5 +29,23 @@ PYBIND11_PLUGIN(xtensor_python_benchmark)
1929
}
2030
);
2131

32+
m.def("sum_tensor", [](xt::pytensor<double, 1> const& x) {
33+
double sum = 0;
34+
for(auto e : x)
35+
sum += e;
36+
return sum;
37+
}
38+
);
39+
40+
m.def("pybind_sum_array", [](py::array_t<double> const& x) {
41+
double sum = 0;
42+
size_t size = x.size();
43+
const double* data = x.data(0);
44+
for(size_t i = 0; i < size; ++i)
45+
sum += data[i];
46+
return sum;
47+
}
48+
);
49+
2250
return m.ptr();
2351
}

benchmark/setup.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,27 @@ def __str__(self):
2121
import pybind11
2222
return pybind11.get_include(self.user)
2323

24+
class get_numpy_include(object):
25+
"""Helper class to determine the numpy include path
26+
27+
The purpose of this class is to postpone importing numpy
28+
until it is actually installed, so that the ``get_include()``
29+
method can be invoked. """
30+
31+
def __str__(self):
32+
import numpy
33+
return numpy.get_include()
2434

2535
ext_modules = [
2636
Extension(
27-
'xtensor_python_benchmark',
37+
'benchmark_xtensor_python',
2838
['main.cpp'],
2939
include_dirs=[
3040
# Path to pybind11 headers
3141
get_pybind_include(),
3242
get_pybind_include(user=True),
43+
# Path to numpy headers
44+
get_numpy_include(),
3345
os.path.join(sys.prefix, 'include'),
3446
os.path.join(sys.prefix, 'Library', 'include')
3547
],
@@ -87,7 +99,7 @@ def build_extensions(self):
8799
build_ext.build_extensions(self)
88100

89101
setup(
90-
name='xtensor_python_benchmark',
102+
name='benchmark_xtensor_python',
91103
version=__version__,
92104
author='Sylvain Corlay',
93105
author_email='sylvain.corlay@gmail.com',

cmake/FindNumPy.cmake

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# - Find the NumPy libraries
2+
# This module finds if NumPy is installed, and sets the following variables
3+
# indicating where it is.
4+
#
5+
# TODO: Update to provide the libraries and paths for linking npymath lib.
6+
#
7+
# NUMPY_FOUND - was NumPy found
8+
# NUMPY_VERSION - the version of NumPy found as a string
9+
# NUMPY_VERSION_MAJOR - the major version number of NumPy
10+
# NUMPY_VERSION_MINOR - the minor version number of NumPy
11+
# NUMPY_VERSION_PATCH - the patch version number of NumPy
12+
# NUMPY_VERSION_DECIMAL - e.g. version 1.6.1 is 10601
13+
# NUMPY_INCLUDE_DIRS - path to the NumPy include files
14+
15+
#============================================================================
16+
# Copyright 2012 Continuum Analytics, Inc.
17+
#
18+
# MIT License
19+
#
20+
# Permission is hereby granted, free of charge, to any person obtaining
21+
# a copy of this software and associated documentation files
22+
# (the "Software"), to deal in the Software without restriction, including
23+
# without limitation the rights to use, copy, modify, merge, publish,
24+
# distribute, sublicense, and/or sell copies of the Software, and to permit
25+
# persons to whom the Software is furnished to do so, subject to
26+
# the following conditions:
27+
#
28+
# The above copyright notice and this permission notice shall be included
29+
# in all copies or substantial portions of the Software.
30+
#
31+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
32+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
33+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
34+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
35+
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
36+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
37+
# OTHER DEALINGS IN THE SOFTWARE.
38+
#
39+
#============================================================================
40+
41+
# Finding NumPy involves calling the Python interpreter
42+
if(NumPy_FIND_REQUIRED)
43+
find_package(PythonInterp REQUIRED)
44+
else()
45+
find_package(PythonInterp)
46+
endif()
47+
48+
if(NOT PYTHONINTERP_FOUND)
49+
set(NUMPY_FOUND FALSE)
50+
endif()
51+
52+
execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c"
53+
"import numpy as n; print(n.__version__); print(n.get_include());"
54+
RESULT_VARIABLE _NUMPY_SEARCH_SUCCESS
55+
OUTPUT_VARIABLE _NUMPY_VALUES
56+
ERROR_VARIABLE _NUMPY_ERROR_VALUE
57+
OUTPUT_STRIP_TRAILING_WHITESPACE)
58+
59+
if(NOT _NUMPY_SEARCH_SUCCESS MATCHES 0)
60+
if(NumPy_FIND_REQUIRED)
61+
message(FATAL_ERROR
62+
"NumPy import failure:\n${_NUMPY_ERROR_VALUE}")
63+
endif()
64+
set(NUMPY_FOUND FALSE)
65+
endif()
66+
67+
# Convert the process output into a list
68+
string(REGEX REPLACE ";" "\\\\;" _NUMPY_VALUES ${_NUMPY_VALUES})
69+
string(REGEX REPLACE "\n" ";" _NUMPY_VALUES ${_NUMPY_VALUES})
70+
list(GET _NUMPY_VALUES 0 NUMPY_VERSION)
71+
list(GET _NUMPY_VALUES 1 NUMPY_INCLUDE_DIRS)
72+
73+
# Make sure all directory separators are '/'
74+
string(REGEX REPLACE "\\\\" "/" NUMPY_INCLUDE_DIRS ${NUMPY_INCLUDE_DIRS})
75+
76+
# Get the major and minor version numbers
77+
string(REGEX REPLACE "\\." ";" _NUMPY_VERSION_LIST ${NUMPY_VERSION})
78+
list(GET _NUMPY_VERSION_LIST 0 NUMPY_VERSION_MAJOR)
79+
list(GET _NUMPY_VERSION_LIST 1 NUMPY_VERSION_MINOR)
80+
list(GET _NUMPY_VERSION_LIST 2 NUMPY_VERSION_PATCH)
81+
string(REGEX MATCH "[0-9]*" NUMPY_VERSION_PATCH ${NUMPY_VERSION_PATCH})
82+
math(EXPR NUMPY_VERSION_DECIMAL
83+
"(${NUMPY_VERSION_MAJOR} * 10000) + (${NUMPY_VERSION_MINOR} * 100) + ${NUMPY_VERSION_PATCH}")
84+
85+
find_package_message(NUMPY
86+
"Found NumPy: version \"${NUMPY_VERSION}\" ${NUMPY_INCLUDE_DIRS}"
87+
"${NUMPY_INCLUDE_DIRS}${NUMPY_VERSION}")
88+
89+
set(NUMPY_FOUND TRUE)

0 commit comments

Comments
 (0)