Skip to content

Commit

Permalink
Bring Nanobind. Fix object leak in from_numpy (#2545)
Browse files Browse the repository at this point in the history
  • Loading branch information
ferdonline authored Jun 26, 2024
1 parent 5f65be0 commit 8ffa1fa
Show file tree
Hide file tree
Showing 20 changed files with 325 additions and 244 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@
[submodule "external/eigen"]
path = external/eigen
url = https://gitlab.com/libeigen/eigen.git
[submodule "external/nanobind"]
path = external/nanobind
url = https://github.com/wjakob/nanobind
11 changes: 7 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,9 @@ endif()
if(NRN_ENABLE_PYTHON)
# Make sure the USE_PYTHON macro is defined in the C++ code
list(APPEND NRN_COMPILE_DEFS USE_PYTHON)
# Ensure nanobind is there, but dont import, we don't want its CMake
nrn_add_external_project(nanobind DISABLE_ADD RECURSIVE SHALLOW)
include(NanoBindMinimal)
endif()

# =============================================================================
Expand Down Expand Up @@ -572,6 +575,10 @@ endif()
# =============================================================================
add_subdirectory(src/sparse13)
add_subdirectory(src/gnu)
if(NRN_ENABLE_PYTHON)
add_subdirectory(src/nrnpython)
endif()

add_subdirectory(src/nrniv)

# Collect the environment variables that are needed to execute NEURON from the build directory. This
Expand Down Expand Up @@ -603,10 +610,6 @@ if(NRN_ENABLE_PYTHON)
endif()
add_subdirectory(bin)

if(NRN_ENABLE_PYTHON)
add_subdirectory(src/nrnpython)
endif()

if(NRN_MACOS_BUILD)
add_subdirectory(src/mac)
endif()
Expand Down
30 changes: 21 additions & 9 deletions cmake/ExternalProjectHelper.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,22 @@ endfunction(nrn_submodule_file_not_found)

# initialize submodule with given path
function(nrn_initialize_submodule path)
cmake_parse_arguments(PARSE_ARGV 1 opt "RECURSIVE;SHALLOW" "" "")
set(UPDATE_OPTIONS "")
if(opt_RECURSIVE)
list(APPEND UPDATE_OPTIONS --recursive)
endif()
if(opt_SHALLOW)
list(APPEND UPDATE_OPTIONS --depth 1)
endif()
if(NOT ${GIT_FOUND})
message(
FATAL_ERROR "git not found and ${path} sub-module not cloned (use git clone --recursive)")
endif()
message(STATUS "Sub-module : missing ${path} : running git submodule update --init")
message(
STATUS "Sub-module : missing ${path} : running git submodule update ${UPDATE_OPTIONS} --init")
execute_process(
COMMAND ${GIT_EXECUTABLE} submodule update --init -- ${path}
COMMAND ${GIT_EXECUTABLE} submodule update ${UPDATE_OPTIONS} --init -- ${path}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
RESULT_VARIABLE ret)
if(NOT ret EQUAL 0)
Expand All @@ -39,23 +48,26 @@ endfunction()

# check for external project and initialize submodule if it is missing
function(nrn_add_external_project name)
cmake_parse_arguments(PARSE_ARGV 1 opt "RECURSIVE;SHALLOW;DISABLE_ADD" "" "")
find_path(
${name}_PATH
NAMES CMakeLists.txt
PATHS "${THIRD_PARTY_DIRECTORY}/${name}"
NO_DEFAULT_PATH)
if(NOT EXISTS ${${name}_PATH})
nrn_submodule_file_not_found("${THIRD_PARTY_DIRECTORY}/${name}")
nrn_initialize_submodule("${THIRD_PARTY_DIRECTORY}/${name}")
set(OPTIONS "")
if(opt_RECURSIVE)
list(APPEND OPTIONS "RECURSIVE")
endif()
if(opt_SHALLOW)
list(APPEND OPTIONS "SHALLOW")
endif()
nrn_initialize_submodule("${THIRD_PARTY_DIRECTORY}/${name}" ${OPTIONS})
else()
message(STATUS "Sub-project : using ${name} from from ${THIRD_PARTY_DIRECTORY}/${name}")
endif()
# if second argument is passed and if it's OFF then skip add_subdirectory
if(${ARGC} GREATER 1)
if(${ARGV2})
add_subdirectory("${THIRD_PARTY_DIRECTORY}/${name}")
endif()
else()
if(NOT opt_DISABLE_ADD)
add_subdirectory("${THIRD_PARTY_DIRECTORY}/${name}")
endif()
endfunction()
21 changes: 21 additions & 0 deletions cmake/NanoBindMinimal.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# =============================================================================
# minimal nanobind interface
# =============================================================================

set(NB_DIR ${PROJECT_SOURCE_DIR}/external/nanobind)

function(make_nanobind_target TARGET_NAME PYINC)
add_library(${TARGET_NAME} STATIC ${NB_DIR}/src/nb_combined.cpp)
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${NB_DIR}/include)
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${NB_DIR}/ext/robin_map/include ${PYINC})
if(MSVC)
# Do not complain about vsnprintf
target_compile_definitions(${TARGET_NAME} PRIVATE -D_CRT_SECURE_NO_WARNINGS)
else()
# Generally needed to handle type punning in Python code
target_compile_options(${TARGET_NAME} PRIVATE -fno-strict-aliasing)
endif()
target_compile_features(${TARGET_NAME} PUBLIC cxx_std_17)
set_property(TARGET ${TARGET_NAME} PROPERTY POSITION_INDEPENDENT_CODE True)
set_property(TARGET ${TARGET_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
endfunction()
1 change: 1 addition & 0 deletions external/nanobind
Submodule nanobind added at 8d7f1e
2 changes: 1 addition & 1 deletion src/coreneuron/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ else()
set(NMODL_ENABLE_PYTHON_BINDINGS
OFF
CACHE BOOL "Enable NMODL python bindings")
nrn_add_external_project(nmodl OFF)
nrn_add_external_project(nmodl DISABLE_ADD)
add_subdirectory(${PROJECT_SOURCE_DIR}/external/nmodl ${CMAKE_BINARY_DIR}/external/nmodl)
set(CORENRN_NMODL_BINARY ${CMAKE_BINARY_DIR}/bin/nmodl${CMAKE_EXECUTABLE_SUFFIX})
set(CORENRN_NMODL_INCLUDE ${CMAKE_BINARY_DIR}/include)
Expand Down
9 changes: 7 additions & 2 deletions src/nrnpython/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,14 @@ if(NRN_ENABLE_PYTHON_DYNAMIC)
list(GET NRN_PYTHON_VERSIONS ${val} pyver)
list(GET NRN_PYTHON_INCLUDES ${val} pyinc)
list(GET NRN_PYTHON_LIBRARIES ${val} pylib)
set(nanobind_target "nanobind_py${pyver}")
make_nanobind_target(${nanobind_target} ${pyinc})
add_library(nrnpython${pyver} SHARED ${NRNPYTHON_FILES_LIST})
target_include_directories(nrnpython${pyver} BEFORE PUBLIC ${pyinc} ${INCLUDE_DIRS})
target_link_libraries(nrnpython${pyver} nrniv_lib ${Readline_LIBRARY})
target_link_libraries(nrnpython${pyver} PUBLIC nrniv_lib)
target_link_libraries(nrnpython${pyver} PRIVATE ${Readline_LIBRARY} ${nanobind_target})
if(NRN_LINK_AGAINST_PYTHON)
target_link_libraries(nrnpython${pyver} ${pylib})
target_link_libraries(nrnpython${pyver} PUBLIC ${pylib})
endif()
add_dependencies(nrnpython${pyver} nrniv_lib)
list(APPEND nrnpython_lib_list nrnpython${pyver})
Expand All @@ -68,6 +71,8 @@ else()
target_link_libraries(nrnpython ${NRN_DEFAULT_PYTHON_LIBRARIES})
target_include_directories(nrnpython PUBLIC ${PROJECT_SOURCE_DIR}/${NRN_3RDPARTY_DIR}/eigen)
target_include_directories(nrnpython PUBLIC ${PROJECT_BINARY_DIR}/src/nrniv/oc_generated)
make_nanobind_target(nanobind ${NRN_DEFAULT_PYTHON_INCLUDES})
target_link_libraries(nrnpython nanobind)
endif()

configure_file(_config_params.py.in "${PROJECT_BINARY_DIR}/lib/python/neuron/_config_params.py"
Expand Down
142 changes: 73 additions & 69 deletions src/nrnpython/grids.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ extern double* _rxd_induced_currents_ecs;
extern double* _rxd_induced_currents_scale;

// Set dt, t pointers
extern "C" void make_time_ptr(PyHocObject* my_dt_ptr, PyHocObject* my_t_ptr) {
extern "C" NRN_EXPORT void make_time_ptr(PyHocObject* my_dt_ptr, PyHocObject* my_t_ptr) {
dt_ptr = static_cast<double*>(my_dt_ptr->u.px_);
t_ptr = static_cast<double*>(my_t_ptr->u.px_);
}
Expand Down Expand Up @@ -194,22 +194,22 @@ ECS_Grid_node::ECS_Grid_node(PyHocObject* my_states,
// Insert a Grid_node "new_Grid" into the list located at grid_list_index in Parallel_grids
/* returns the grid number
TODO: change this to returning the pointer */
extern "C" int ECS_insert(int grid_list_index,
PyHocObject* my_states,
int my_num_states_x,
int my_num_states_y,
int my_num_states_z,
double my_dc_x,
double my_dc_y,
double my_dc_z,
double my_dx,
double my_dy,
double my_dz,
PyHocObject* my_alpha,
PyHocObject* my_permeability,
int bc,
double bc_value,
double atolscale) {
extern "C" NRN_EXPORT int ECS_insert(int grid_list_index,
PyHocObject* my_states,
int my_num_states_x,
int my_num_states_y,
int my_num_states_z,
double my_dc_x,
double my_dc_y,
double my_dc_z,
double my_dx,
double my_dy,
double my_dz,
PyHocObject* my_alpha,
PyHocObject* my_permeability,
int bc,
double bc_value,
double atolscale) {
ECS_Grid_node* new_Grid = new ECS_Grid_node(my_states,
my_num_states_x,
my_num_states_y,
Expand Down Expand Up @@ -381,21 +381,21 @@ ICS_Grid_node::ICS_Grid_node(PyHocObject* my_states,
// Insert a Grid_node "new_Grid" into the list located at grid_list_index in Parallel_grids
/* returns the grid number
TODO: change this to returning the pointer */
extern "C" int ICS_insert(int grid_list_index,
PyHocObject* my_states,
long num_nodes,
long* neighbors,
long* x_line_defs,
long x_lines_length,
long* y_line_defs,
long y_lines_length,
long* z_line_defs,
long z_lines_length,
double* dcs,
double dx,
bool is_diffusable,
double atolscale,
double* ics_alphas) {
extern "C" NRN_EXPORT int ICS_insert(int grid_list_index,
PyHocObject* my_states,
long num_nodes,
long* neighbors,
long* x_line_defs,
long x_lines_length,
long* y_line_defs,
long y_lines_length,
long* z_line_defs,
long z_lines_length,
double* dcs,
double dx,
bool is_diffusable,
double atolscale,
double* ics_alphas) {
ICS_Grid_node* new_Grid = new ICS_Grid_node(my_states,
num_nodes,
neighbors,
Expand All @@ -415,21 +415,21 @@ extern "C" int ICS_insert(int grid_list_index,
return new_Grid->insert(grid_list_index);
}

int ICS_insert_inhom(int grid_list_index,
PyHocObject* my_states,
long num_nodes,
long* neighbors,
long* x_line_defs,
long x_lines_length,
long* y_line_defs,
long y_lines_length,
long* z_line_defs,
long z_lines_length,
double* dcs,
double dx,
bool is_diffusable,
double atolscale,
double* ics_alphas) {
extern "C" NRN_EXPORT int ICS_insert_inhom(int grid_list_index,
PyHocObject* my_states,
long num_nodes,
long* neighbors,
long* x_line_defs,
long x_lines_length,
long* y_line_defs,
long y_lines_length,
long* z_line_defs,
long z_lines_length,
double* dcs,
double dx,
bool is_diffusable,
double atolscale,
double* ics_alphas) {
ICS_Grid_node* new_Grid = new ICS_Grid_node(my_states,
num_nodes,
neighbors,
Expand All @@ -449,7 +449,7 @@ int ICS_insert_inhom(int grid_list_index,
}


extern "C" int set_diffusion(int grid_list_index, int grid_id, double* dc, int length) {
extern "C" NRN_EXPORT int set_diffusion(int grid_list_index, int grid_id, double* dc, int length) {
int id = 0;
Grid_node* node = Parallel_grids[grid_list_index];
while (id < grid_id) {
Expand All @@ -462,7 +462,9 @@ extern "C" int set_diffusion(int grid_list_index, int grid_id, double* dc, int l
return 0;
}

extern "C" int set_tortuosity(int grid_list_index, int grid_id, PyHocObject* my_permeability) {
extern "C" NRN_EXPORT int set_tortuosity(int grid_list_index,
int grid_id,
PyHocObject* my_permeability) {
int id = 0;
Grid_node* node = Parallel_grids[grid_list_index];
while (id < grid_id) {
Expand Down Expand Up @@ -513,7 +515,9 @@ void ECS_Grid_node::set_tortuosity(PyHocObject* my_permeability) {
}
}

extern "C" int set_volume_fraction(int grid_list_index, int grid_id, PyHocObject* my_alpha) {
extern "C" NRN_EXPORT int set_volume_fraction(int grid_list_index,
int grid_id,
PyHocObject* my_alpha) {
int id = 0;
Grid_node* node = Parallel_grids[grid_list_index];
while (id < grid_id) {
Expand Down Expand Up @@ -585,11 +589,11 @@ void ECS_Grid_node::set_diffusion(double* dc, int) {
}


extern "C" void ics_set_grid_concentrations(int grid_list_index,
int index_in_list,
int64_t* nodes_per_seg,
int64_t* nodes_per_seg_start_indices,
PyObject* neuron_pointers) {
extern "C" NRN_EXPORT void ics_set_grid_concentrations(int grid_list_index,
int index_in_list,
int64_t* nodes_per_seg,
int64_t* nodes_per_seg_start_indices,
PyObject* neuron_pointers) {
Grid_node* g;
ssize_t i;
ssize_t n = (ssize_t) PyList_Size(neuron_pointers); // number of segments.
Expand All @@ -610,10 +614,10 @@ extern "C" void ics_set_grid_concentrations(int grid_list_index,
}
}

extern "C" void ics_set_grid_currents(int grid_list_index,
int index_in_list,
PyObject* neuron_pointers,
double* scale_factors) {
extern "C" NRN_EXPORT void ics_set_grid_currents(int grid_list_index,
int index_in_list,
PyObject* neuron_pointers,
double* scale_factors) {
Grid_node* g;
ssize_t i;
ssize_t n = (ssize_t) PyList_Size(neuron_pointers);
Expand All @@ -634,10 +638,10 @@ extern "C" void ics_set_grid_currents(int grid_list_index,


/* TODO: make this work with Grid_node ptrs instead of pairs of list indices */
extern "C" void set_grid_concentrations(int grid_list_index,
int index_in_list,
PyObject* grid_indices,
PyObject* neuron_pointers) {
extern "C" NRN_EXPORT void set_grid_concentrations(int grid_list_index,
int index_in_list,
PyObject* grid_indices,
PyObject* neuron_pointers) {
/*
Preconditions:
Expand Down Expand Up @@ -675,11 +679,11 @@ extern "C" void set_grid_concentrations(int grid_list_index,
}

/* TODO: make this work with Grid_node ptrs instead of pairs of list indices */
extern "C" void set_grid_currents(int grid_list_index,
int index_in_list,
PyObject* grid_indices,
PyObject* neuron_pointers,
PyObject* scale_factors) {
extern "C" NRN_EXPORT void set_grid_currents(int grid_list_index,
int index_in_list,
PyObject* grid_indices,
PyObject* neuron_pointers,
PyObject* scale_factors) {
/*
Preconditions:
Expand Down Expand Up @@ -780,7 +784,7 @@ int remove(Grid_node** head, Grid_node* find) {
return 1;
}

extern "C" void delete_by_id(int id) {
extern "C" NRN_EXPORT void delete_by_id(int id) {
Grid_node* grid;
int k;
for (k = 0, grid = Parallel_grids[0]; grid != NULL; grid = grid->next, k++) {
Expand Down
Loading

0 comments on commit 8ffa1fa

Please sign in to comment.