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

Feature/pypi #196

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 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
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ project(teaserpp VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 14)

set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/" ${CMAKE_MODULE_PATH})
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just curious; is compile_commands.json needed to support pip installation?

Copy link
Author

@EmDash00 EmDash00 Dec 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No this is not. The compile flags just are convenient for development. The C++ linters and LSPs can use them to locate included libraries like pybind and Eigen so I don't get a billion errors everywhere and can get completion from the included libraries. I use clangd as my LSP so it's just helpful for me. I'm not sure if VSCode or other IDEs have more automatic detection. I'm just using a barebones setup with Neovim and their built-in LSP/linting support.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your kind answer. I thought compile_commands.json was needed pip3. But isn't can be produced by -DCMAKE_EXPORT_COMPILE_COMMANDS=1?

Copy link
Author

@EmDash00 EmDash00 Dec 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it can yeah. I think when building with scikit-build it was easier just to put it in the file since I had to figure out a way to pass it through in the pyproject file. We could probably put a switch there where they aren't made during release builds.


# Check build types
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
Expand All @@ -12,6 +13,10 @@ if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
STRING "Choose the type of build." FORCE)
endif ()

if (DEFINED SKBUILD)
message(STATUS "Building with Scikit-Build")
endif ()

# Options
option(BUILD_TESTS "Build tests" ON)
option(BUILD_TEASER_FPFH "Build TEASER++ wrappers for PCL FPFH estimation." OFF)
Expand Down
4 changes: 2 additions & 2 deletions cmake/pybind11.CMakeLists.txt.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ project(pybind11-download NONE)
include(ExternalProject)
ExternalProject_Add(pmc
GIT_REPOSITORY https://github.com/pybind/pybind11.git
GIT_TAG v2.11.1
GIT_TAG v2.13.6
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/pybind11-src"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/pybind11-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
)
58 changes: 58 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
[build-system]
requires = ["scikit_build_core", "pybind11"]
build-backend = "scikit_build_core.build"

[project]
name = "teaserpp_python"
version = "1.1.0"
description = "Python binding for TEASER++"
readme = "README.md"
authors = [
{ name = "Jingnan Shi", email = "[email protected]" },
]
keywords = [
"Point cloud",
"Registration",
"Non-minimal solver",
"Solver",
]
classifiers = [
"Intended Audience :: Developers",
"Intended Audience :: Education",
"Intended Audience :: Other Audience",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Operating System :: MacOS",
"Operating System :: Microsoft :: Windows",
"Operating System :: Unix",
"Programming Language :: C++",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
dependencies = [
"numpy",
]

[project.urls]
Homepage = "https://github.com/MIT-SPARK/TEASER-plusplus"

[tool.scikit-build]
build-dir = "build/{wheel_tag}"
build.verbose = false
cmake.version = ">=3.16"
wheel.install-dir = "teaserpp_python.libs"

[tool.cibuildwheel]
archs = ["auto64"]
skip = ["*-musllinux*", "pp*", "cp36-*"]

[tool.cibuildwheel.macos]
environment = "MACOSX_DEPLOYMENT_TARGET=10.14"
archs = ["auto64", "arm64"]
21 changes: 17 additions & 4 deletions python/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.10)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same question) I'm just curious; is compile_commands.json needed to support pip installation?


project(teaser_python_bindings)

Expand All @@ -24,10 +25,12 @@ endif ()

# make sure to output the build file to teaserpp_python folder
SET_TARGET_PROPERTIES(teaserpp_python
PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/teaserpp_python"
)
PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/teaserpp_python"
INSTALL_RPATH "$ORIGIN/../teaserpp_python.libs/lib;$ORIGIN/../../teaser/"
BUILD_WITH_INSTALL_RPATH TRUE
)

# copy package __init__.py file
configure_file(teaserpp_python/__init__.py
Expand All @@ -43,3 +46,13 @@ file(COPY .
DESTINATION .
FILES_MATCHING
PATTERN *.py)

if (DEFINED SKBUILD)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/teaserpp/
DESTINATION "../teaserpp"
FILES_MATCHING PATTERN "*.py"
PATTERN "*.pyi"
PATTERN "*.so"
)
install(TARGETS teaserpp_python DESTINATION "../teaserpp_python")
endif ()
61 changes: 57 additions & 4 deletions python/teaserpp_python/teaserpp_python.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* See LICENSE for the license information
*/

#include <omp.h>
#include <string>
#include <sstream>

Expand All @@ -21,7 +22,7 @@ namespace py = pybind11;
/**
* Python interface with pybind11
*/
PYBIND11_MODULE(teaserpp_python, m) {
PYBIND11_MODULE(_teaserpp, m) {
m.doc() = "Python binding for TEASER++";

// Python bound for teaser::RegistrationSolution
Expand All @@ -41,14 +42,66 @@ PYBIND11_MODULE(teaserpp_python, m) {
return print_string.str();
});

m.attr("OMP_MAX_THREADS") = omp_get_max_threads();

// Python bound for teaser::RobustRegistrationSolver::ROTATION_ESTIMATE_ALGORITHM
py::enum_<teaser::RobustRegistrationSolver::ROTATION_ESTIMATION_ALGORITHM>(
m, "RotationEstimationAlgorithm"
)
.value("GNC_TLS", teaser::RobustRegistrationSolver::ROTATION_ESTIMATION_ALGORITHM::GNC_TLS)
.value("FGR", teaser::RobustRegistrationSolver::ROTATION_ESTIMATION_ALGORITHM::FGR)
.value("QUATRO", teaser::RobustRegistrationSolver::ROTATION_ESTIMATION_ALGORITHM::QUATRO);

// Python bound for teaser::RobustRegistrationSolver::INLIER_GRAPH_FORMULATION
py::enum_<teaser::RobustRegistrationSolver::INLIER_GRAPH_FORMULATION>(
m, "InlierGraphFormulation"
)
.value("CHAIN", teaser::RobustRegistrationSolver::INLIER_GRAPH_FORMULATION::CHAIN)
.value("COMPLETE", teaser::RobustRegistrationSolver::INLIER_GRAPH_FORMULATION::COMPLETE);

// Python bound for teaser::RobustRegistrationSolver::INLIER_SELECTION_MODE
py::enum_<teaser::RobustRegistrationSolver::INLIER_SELECTION_MODE>(
m, "InlierSelectionMode"
)
.value("PMC_EXACT", teaser::RobustRegistrationSolver::INLIER_SELECTION_MODE::PMC_EXACT)
.value("PMC_HEU", teaser::RobustRegistrationSolver::INLIER_SELECTION_MODE::PMC_HEU)
.value("KCORE_HEU", teaser::RobustRegistrationSolver::INLIER_SELECTION_MODE::KCORE_HEU)
.value("NONE", teaser::RobustRegistrationSolver::INLIER_SELECTION_MODE::NONE);

// Python bound for DRSCertifier::EIG_SOLVER_TYPE
py::enum_<teaser::DRSCertifier::EIG_SOLVER_TYPE>(m, "EigSolverType")
.value("EIGEN", teaser::DRSCertifier::EIG_SOLVER_TYPE::EIGEN)
.value("SPECTRA", teaser::DRSCertifier::EIG_SOLVER_TYPE::SPECTRA);

// Python bound for teaser::RobustRegistraionSolver
py::class_<teaser::RobustRegistrationSolver> solver(m, "RobustRegistrationSolver");

// Python bound for teaser::RobustRegistrationSolver functions
solver.def(py::init<>())
.def(py::init<const teaser::RobustRegistrationSolver::Params&>())
solver.def(py::init<double, double, bool,
teaser::RobustRegistrationSolver::ROTATION_ESTIMATION_ALGORITHM,
double, size_t, double,
teaser::RobustRegistrationSolver::INLIER_GRAPH_FORMULATION,
teaser::RobustRegistrationSolver::INLIER_SELECTION_MODE,
double, bool, bool,
double, int>(),
py::arg("noise_bound") = 0.01,
py::arg("cbar2") = 1,
py::arg("estimate_scaling") = true,
py::arg("rotation_estimation_algorithm") =
teaser::RobustRegistrationSolver::ROTATION_ESTIMATION_ALGORITHM::GNC_TLS,
py::arg("rotation_gnc_factor") = 1.4,
py::arg("rotation_max_iterations") = 100,
py::arg("rotation_cost_threshold") = 1e-6,
py::arg("rotation_tim_graph") =
teaser::RobustRegistrationSolver::INLIER_GRAPH_FORMULATION::CHAIN,
py::arg("inlier_selection_mode") =
teaser::RobustRegistrationSolver::INLIER_SELECTION_MODE::PMC_EXACT,
py::arg("kcore_heuristic_threshold") = 0.5,
py::arg("use_max_clique") = true,
py::arg("max_clique_exact_solution") = true,
py::arg("max_clique_time_limit") = 3000,
py::arg("max_clique_num_threads") = omp_get_max_threads())
.def("getParams", &teaser::RobustRegistrationSolver::getParams)
.def("reset", &teaser::RobustRegistrationSolver::reset)
.def("solve", py::overload_cast<const Eigen::Matrix<double, 3, Eigen::Dynamic>&,
const Eigen::Matrix<double, 3, Eigen::Dynamic>&>(
&teaser::RobustRegistrationSolver::solve))
Expand Down
27 changes: 24 additions & 3 deletions teaser/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
project(teaser_source)
include(GNUInstallDirs)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

if (DEFINED SKBUILD)
set(CMAKE_INSTALL_RPATH "$ORIGIN")
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
endif ()

# teaser_io library
add_library(teaser_io SHARED src/ply_io.cc)
Expand All @@ -9,6 +15,13 @@ target_include_directories(teaser_io PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)


SET_TARGET_PROPERTIES(teaser_io
PROPERTIES
INSTALL_RPATH "$ORIGIN;$ORIGIN/../tinyply-build"
BUILD_WITH_INSTALL_RPATH TRUE
)

install(TARGETS teaser_io
EXPORT teaserpp-export
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
Expand All @@ -23,6 +36,12 @@ add_library(teaser_registration SHARED
src/registration.cc
src/graph.cc
)

SET_TARGET_PROPERTIES(teaser_registration
PROPERTIES
INSTALL_RPATH "$ORIGIN;$ORIGIN/../pmc-build"
BUILD_WITH_INSTALL_RPATH TRUE
)
target_link_libraries(teaser_registration
PUBLIC Eigen3::Eigen
PRIVATE pmc ${TEASERPP_BLAS_LAPACK_LIBS}
Expand Down Expand Up @@ -88,11 +107,13 @@ endif ()
set(TEASERPP_EXPORTED_TARGETS "${TEASERPP_EXPORTED_TARGETS}" PARENT_SCOPE)

# installation
install(EXPORT teaserpp-export
install(EXPORT teaserpp-export
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/teaserpp
NAMESPACE teaserpp::
FILE teaserppTargets.cmake
)

install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(DIRECTORY ${SPECTRA_INCLUDE_DIRS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
if (NOT DEFINED SKBUILD)
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(DIRECTORY ${SPECTRA_INCLUDE_DIRS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
endif ()
4 changes: 2 additions & 2 deletions teaser/include/teaser/linalg.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Eigen::Matrix<double, 3, 3> hatmap(const Eigen::Matrix<double, 3, 1>& u) {
template <typename NumT, int N, int M>
void vectorKron(const Eigen::Matrix<NumT, N, 1>& v1, const Eigen::Matrix<NumT, M, 1>& v2,
Eigen::Matrix<NumT, N * M, 1>* output) {
#pragma omp parallel for collapse(2) shared(v1, v2, output) default(none)
#pragma omp parallel for collapse(2) shared(v1, v2, output)
for (size_t i = 0; i < N; ++i) {
for (size_t j = 0; j < M; ++j) {
(*output)[i * M + j] = v1[i] * v2[j];
Expand All @@ -62,7 +62,7 @@ template <typename NumT, int N, int M>
Eigen::Matrix<NumT, Eigen::Dynamic, 1> vectorKron(const Eigen::Matrix<NumT, N, 1>& v1,
const Eigen::Matrix<NumT, M, 1>& v2) {
Eigen::Matrix<double, Eigen::Dynamic, 1> output(v1.rows() * v2.rows(), 1);
#pragma omp parallel for collapse(2) shared(v1, v2, output) default(none)
#pragma omp parallel for collapse(2) shared(v1, v2, output)
for (size_t i = 0; i < v1.rows(); ++i) {
for (size_t j = 0; j < v2.rows(); ++j) {
output[i * v2.rows() + j] = v1[i] * v2[j];
Expand Down
Loading