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

Java bindings #2100

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f46f48f
Initial commit in Java bindings
Vasniktel Nov 1, 2019
837a47c
Make Java binding for cli util compile
Vasniktel Nov 16, 2019
7803774
Removed *.class files
Vasniktel Nov 16, 2019
ab85f8c
Updated .gitignore
Vasniktel Nov 16, 2019
18f829a
Remove VSCode related files
Vasniktel Nov 16, 2019
8c6ed19
Fix javacpp generation bug
Vasniktel Nov 17, 2019
9cb524c
Add missing includes
Vasniktel Nov 17, 2019
a86f37c
Add more utility functions; fix bugs
Vasniktel Nov 17, 2019
06bf060
Add unittests
Vasniktel Nov 17, 2019
09ada4d
Add more bindings; use nd4j 1.0.0-beta5
Vasniktel Nov 23, 2019
19ac81f
Update tests
Vasniktel Nov 23, 2019
2a3b08c
Add support for model parameters
Vasniktel Nov 26, 2019
6f565d7
Add support for categorical matrices
Vasniktel Nov 26, 2019
5aa8ebc
Add support for binding generation
Vasniktel Nov 27, 2019
a042098
Implement ProgramCall
Vasniktel Nov 28, 2019
c01572d
Fix minor bugs
Vasniktel Nov 28, 2019
2962965
Add bindings for methods; fix compilation bugs
Vasniktel Nov 28, 2019
c63c599
Introduce JAVA_VERSION cmake variable
Vasniktel Nov 28, 2019
4567b12
Fix style issues
Vasniktel Nov 30, 2019
ba7dd79
Fix bugs for row and col bindings
Vasniktel Dec 1, 2019
5afe1d7
Merge remote-tracking branch 'upstream/master' into java-bindings
Vasniktel Jan 10, 2020
ffdd622
Fix cmake issues
Vasniktel Jan 10, 2020
6574dc8
Remove unused includes
Vasniktel Jan 10, 2020
e3aef86
Remove needless templates
Vasniktel Feb 21, 2020
77d28a5
Fix indentation
Vasniktel Feb 21, 2020
ba37472
Rename utility methods to be similar to java types
Vasniktel Feb 21, 2020
b40716e
Add support for custom prefix to mlpack::util::HyphenateString
Vasniktel Feb 21, 2020
81ab8ee
Improve generation process
Vasniktel Feb 21, 2020
876a580
Fix incorrect arguments
Vasniktel Feb 21, 2020
e495358
Don't use auto
Vasniktel Feb 21, 2020
1741959
Improve docs
Vasniktel Feb 21, 2020
3b89355
Make target specification optional
Vasniktel Feb 22, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ xcode*
src/mlpack/core/util/gitversion.hpp
src/mlpack/core/util/arma_config.hpp
.idea
.vscode/*
cmake-build-*
*.pyc
Testing/
File renamed without changes.
9 changes: 9 additions & 0 deletions CMake/GenerateBinding.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# GenerateBinding.cmake: a CMake script that actually runs the given program to
# generate an mlpack binding file.
#
# This script depends on the following arguments:
#
# GENERATE_BINDING_PROGRAM: the program to run to generate the binding file.
# BINDING_OUTPUT_FILE: the file to store the output in.
execute_process(COMMAND ${GENERATE_BINDING_PROGRAM}
OUTPUT_FILE ${BINDING_OUTPUT_FILE})
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ else ()
"Compile shared libraries (if OFF, static libraries are compiled)." ON)
endif()

option(BUILD_JAVA_BINDINGS "Build Java bindings" OFF)

# Build Markdown bindings for documentation. This is used as part of website
# generation.
option(BUILD_MARKDOWN_BINDINGS "Build Markdown bindings for website documentation." OFF)
Expand Down
1 change: 1 addition & 0 deletions src/mlpack/bindings/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# All we have to do is recurse into the subdirectories.
set(DIRS
cli
java
markdown
python
tests
Expand Down
4 changes: 4 additions & 0 deletions src/mlpack/bindings/java/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
maven/.classpath
maven/.project
maven/target/*
maven/.settings/*
227 changes: 227 additions & 0 deletions src/mlpack/bindings/java/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
macro (not_found_return message)
message(STATUS "${message}")
macro (add_java_binding name java_name)
# Do nothing.
endmacro ()

return()
endmacro ()

# If we are not supposed to make Java bindings, define the macro so it does
# nothing and leave this file.
if (NOT BUILD_JAVA_BINDINGS)
not_found_return("Not building Java bindings.")
endif ()

set(JAVA_VERSION 1.7)
find_package(Java ${JAVA_VERSION} REQUIRED COMPONENTS Development)
if (Java_FOUND)
include(UseJava)
else()
not_found_return("Java components weren't found")
endif()

find_package(JNI REQUIRED)
if (NOT JNI_FOUND)
not_found_return("JNI wasn't found")
endif()

set(JNI_INCLUDE ${JNI_INCLUDE_DIRS} CACHE INTERNAL "")

if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
find_program(MAVEN_BIN mvn.bat REQUIRED)
else()
find_program(MAVEN_BIN mvn REQUIRED)
endif()

if (MAVEN_BIN)
message(STATUS "Found maven: ${MAVEN}")
Vasniktel marked this conversation as resolved.
Show resolved Hide resolved
else()
not_found_return("Maven not found")
Vasniktel marked this conversation as resolved.
Show resolved Hide resolved
endif()

set(MAVEN ${MAVEN_BIN} CACHE INTERNAL "")
set(JAVA ${Java_JAVA_EXECUTABLE} CACHE INTERNAL "")

add_custom_target(java_configure ALL)
add_custom_target(java ALL DEPENDS java_configure)

# Create directory structure for maven project
set(MAVEN_BUILD_DIR ${CMAKE_BINARY_DIR}/src/mlpack/bindings/java/maven CACHE INTERNAL "")
Vasniktel marked this conversation as resolved.
Show resolved Hide resolved
add_custom_command(TARGET java_configure PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${MAVEN_BUILD_DIR}/src/main/java/org/mlpack)

# Create directory to output native libraries to
set(NATIVE_OUT_DIR ${MAVEN_BUILD_DIR}/native CACHE INTERNAL "")
add_custom_command(TARGET java_configure PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${NATIVE_OUT_DIR})

# Create directory to generate JNI files to
set(JNI_GEN_DIR ${CMAKE_BINARY_DIR}/src/mlpack/bindings/java/build/gen CACHE INTERNAL "")
add_custom_command(TARGET java_configure PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${JNI_GEN_DIR})

set(BUILD_SRC_DIR ${CMAKE_BINARY_DIR}/src/mlpack/bindings/java/build/src CACHE INTERNAL "")
add_custom_command(TARGET java_configure PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${BUILD_SRC_DIR})

# Copy maven pom.xml
set(JAVACPP_VERSION 1.5.1)

file(READ "${CMAKE_SOURCE_DIR}/src/mlpack/core/util/version.hpp"
VERSION_HPP_CONTENTS)
string(REGEX REPLACE ".*#define MLPACK_VERSION_MAJOR ([0-9]+).*" "\\1"
MLPACK_VERSION_MAJOR "${VERSION_HPP_CONTENTS}")
string(REGEX REPLACE ".*#define MLPACK_VERSION_MINOR ([0-9]+).*" "\\1"
MLPACK_VERSION_MINOR "${VERSION_HPP_CONTENTS}")
string(REGEX REPLACE ".*#define MLPACK_VERSION_PATCH [\"]?([0-9x]+)[\"]?.*"
"\\1" MLPACK_VERSION_PATCH "${VERSION_HPP_CONTENTS}")
set(PACKAGE_VERSION
"${MLPACK_VERSION_MAJOR}.${MLPACK_VERSION_MINOR}.${MLPACK_VERSION_PATCH}")

configure_file(maven/pom.xml.in ${MAVEN_BUILD_DIR}/pom.xml @ONLY)

# Make maven resolve dependencies into lib directory (see pom.xml)
add_custom_command(TARGET java_configure PRE_BUILD
COMMAND ${MAVEN} dependency:copy-dependencies
WORKING_DIRECTORY ${MAVEN_BUILD_DIR})

# Javacpp was pulled from maven
set(JAVACPP ${MAVEN_BUILD_DIR}/lib/javacpp-${JAVACPP_VERSION}.jar CACHE INTERNAL "")

# Compile cli_util library for java
add_custom_target(jniCLI_configure ALL
Vasniktel marked this conversation as resolved.
Show resolved Hide resolved
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/maven/CLI.java
${MAVEN_BUILD_DIR}/src/main/java/org/mlpack/CLI.java
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/maven/MatrixWithInfo.java
${MAVEN_BUILD_DIR}/src/main/java/org/mlpack/MatrixWithInfo.java
DEPENDS java_configure)

add_custom_command(
OUTPUT
${BUILD_SRC_DIR}/cli_util.hpp
${BUILD_SRC_DIR}/deleter.hpp
${BUILD_SRC_DIR}/cli_util.cpp
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/cli_util.hpp
${BUILD_SRC_DIR}/cli_util.hpp
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/deleter.hpp
${BUILD_SRC_DIR}/deleter.hpp
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/cli_util.cpp
${BUILD_SRC_DIR}/cli_util.cpp)

# Compile java sources with maven
add_custom_command(TARGET jniCLI_configure PRE_BUILD
COMMAND ${MAVEN} compile -Dchanged.classes=org/mlpack/CLI.java
WORKING_DIRECTORY ${MAVEN_BUILD_DIR})

# Generate JNI wrappers from compiled java bytecode
add_custom_command(OUTPUT ${JNI_GEN_DIR}/jnijavacpp.cpp ${JNI_GEN_DIR}/jniCLI.cpp
COMMAND ${Java_JAVA_EXECUTABLE} -jar ${JAVACPP} -classpath .:${MAVEN_BUILD_DIR}/lib/* -nocompile -d ${JNI_GEN_DIR} org/mlpack/CLI.class
WORKING_DIRECTORY ${MAVEN_BUILD_DIR}/target/classes
DEPENDS jniCLI_configure)

add_library(jnijavacpp STATIC
${JNI_GEN_DIR}/jnijavacpp.cpp)

target_include_directories(jnijavacpp PUBLIC ${JNI_INCLUDE})
set_target_properties(jnijavacpp PROPERTIES
COMPILE_FLAGS -fPIC)

# Compile shared object
add_library(jniCLI SHARED
${JNI_GEN_DIR}/jniCLI.cpp
${BUILD_SRC_DIR}/cli_util.hpp
${BUILD_SRC_DIR}/cli_util.cpp
${BUILD_SRC_DIR}/deleter.hpp)

target_include_directories(jniCLI PUBLIC ${BUILD_SRC_DIR} ${JNI_GEN_DIR} ${JNI_INCLUDE})
target_link_libraries(jniCLI mlpack jnijavacpp)
set_target_properties(jniCLI PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${NATIVE_OUT_DIR}")
add_dependencies(jniCLI jniCLI_configure)
add_dependencies(java jniCLI)

add_library(java_generate_parts STATIC
${CMAKE_CURRENT_SOURCE_DIR}/strip_type.hpp
${CMAKE_CURRENT_SOURCE_DIR}/print_param_defn.hpp
${CMAKE_CURRENT_SOURCE_DIR}/print_output_param.hpp
${CMAKE_CURRENT_SOURCE_DIR}/print_java.cpp
${CMAKE_CURRENT_SOURCE_DIR}/print_java.hpp
${CMAKE_CURRENT_SOURCE_DIR}/print_input_param.hpp
${CMAKE_CURRENT_SOURCE_DIR}/print_doc_functions.hpp
${CMAKE_CURRENT_SOURCE_DIR}/java_option.hpp
${CMAKE_CURRENT_SOURCE_DIR}/get_java_type.hpp)
Vasniktel marked this conversation as resolved.
Show resolved Hide resolved

macro(add_java_binding file_name class_name)
Vasniktel marked this conversation as resolved.
Show resolved Hide resolved
foreach(model_name ${ARGN})
configure_file(
${CMAKE_SOURCE_DIR}/src/mlpack/bindings/java/model.java.in
${MAVEN_BUILD_DIR}/src/main/java/org/mlpack/${model_name}Type.java)
endforeach()

add_custom_command(OUTPUT ${BUILD_SRC_DIR}/generate_java_${class_name}.cpp
COMMAND ${CMAKE_COMMAND}
-DFILE_NAME=${file_name}
-DCLASS_NAME=${class_name}
-DGENERATE_CPP_IN=${CMAKE_SOURCE_DIR}/src/mlpack/bindings/java/generate_java.cpp.in
-DGENERATE_CPP_OUT=${BUILD_SRC_DIR}/generate_java_${class_name}.cpp
-DPROGRAM_MAIN_FILE=${CMAKE_CURRENT_SOURCE_DIR}/${file_name}_main.cpp
-P ${CMAKE_SOURCE_DIR}/CMake/ConfigureGenerate.cmake
DEPENDS ${CMAKE_SOURCE_DIR}/src/mlpack/bindings/java/generate_java.cpp.in
${CMAKE_SOURCE_DIR}/CMake/ConfigureGenerate.cmake)

add_executable(generate_java_${class_name}
${BUILD_SRC_DIR}/generate_java_${class_name}.cpp)

target_link_libraries(generate_java_${class_name}
mlpack
java_generate_parts
${MLPACK_LIBRARIES})

set_target_properties(generate_java_${class_name} PROPERTIES
COMPILE_FLAGS -DBINDING_TYPE=BINDING_TYPE_JAVA
RUNTIME_OUTPUT_DIRECTORY "${BUILD_SRC_DIR}")

add_custom_command(TARGET generate_java_${class_name} POST_BUILD
COMMAND ${CMAKE_COMMAND}
-DGENERATE_BINDING_PROGRAM="${BUILD_SRC_DIR}/generate_java_${class_name}"
-DBINDING_OUTPUT_FILE=${MAVEN_BUILD_DIR}/src/main/java/org/mlpack/${class_name}.java
-P ${CMAKE_SOURCE_DIR}/CMake/GenerateBinding.cmake)

add_custom_target(jni${class_name}_configure ALL
COMMAND ${MAVEN} compile -Dchanged.classes=org/mlpack/${class_name}.java
WORKING_DIRECTORY ${MAVEN_BUILD_DIR}
DEPENDS generate_java_${class_name} jniCLI)

add_custom_command(OUTPUT ${JNI_GEN_DIR}/jni${class_name}.cpp
COMMAND ${JAVA} -jar ${JAVACPP} -classpath .:${MAVEN_BUILD_DIR}/lib/* -nocompile -d ${JNI_GEN_DIR} org/mlpack/${class_name}.class
WORKING_DIRECTORY ${MAVEN_BUILD_DIR}/target/classes)

add_library(jni${class_name} SHARED
${JNI_GEN_DIR}/jni${class_name}.cpp)

target_include_directories(jni${class_name} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${BUILD_SRC_DIR}
${JNI_GEN_DIR}
${JNI_INCLUDE}
${CMAKE_SOURCE_DIR}/src/mlpack/bindings/java)

target_link_libraries(jni${class_name} mlpack ${MLPACK_LIBRARIES} jnijavacpp)

set_target_properties(jni${class_name} PROPERTIES
COMPILE_FLAGS -DBINDING_TYPE=BINDING_TYPE_JAVA
LIBRARY_OUTPUT_DIRECTORY "${NATIVE_OUT_DIR}")

add_dependencies(jni${class_name} jni${class_name}_configure)
add_dependencies(java jni${class_name})
endmacro()

if (BUILD_TESTS)
add_subdirectory(tests)
endif()
94 changes: 94 additions & 0 deletions src/mlpack/bindings/java/cli_util.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#include "cli_util.hpp"
Vasniktel marked this conversation as resolved.
Show resolved Hide resolved

namespace mlpack {
namespace util {

void SetPassed(const char* name)
{
CLI::SetPassed(name);
}

void RestoreSettings(const char* name)
{
CLI::RestoreSettings(name);
}

bool* GetMatWithInfoParamInfo(const char* name)
{
const auto& info = std::get<0>(
CLI::GetParam<std::tuple<data::DatasetInfo, arma::mat>>(name));

size_t n = info.Dimensionality();
bool* result = new bool[n];

for (size_t i = 0; i < n; ++i)
{
result[i] = info.Type(i) == data::Datatype::categorical;
}

return result;
}

size_t GetMatWithInfoParamLength(const char* name)
{
return std::get<1>(
CLI::GetParam<std::tuple<data::DatasetInfo, arma::mat>>(name)).n_elem;
}

double* GetMatWithInfoParamData(const char* name)
{
auto& param = std::get<1>(
CLI::GetParam<std::tuple<data::DatasetInfo, arma::mat>>(name));

if (param.mem && param.n_elem <= arma::arma_config::mat_prealloc)
{
double* result = new double[param.n_elem];
arma::arrayops::copy(result, param.mem, param.n_elem);
return result;
}

arma::access::rw(param.mem_state) = 1;
return param.memptr();
}

size_t GetMatWithInfoParamCols(const char* name)
{
return std::get<1>(
CLI::GetParam<std::tuple<data::DatasetInfo, arma::mat>>(name)).n_cols;
}

size_t GetMatWithInfoParamRows(const char* name)
{
return std::get<1>(
CLI::GetParam<std::tuple<data::DatasetInfo, arma::mat>>(name)).n_rows;
}

void SetMatWithInfoParam(
const char* name, double* data, bool* info, size_t rows, size_t columns)
{
data::DatasetInfo d(rows);
for (size_t i = 0; i < d.Dimensionality(); ++i)
{
d.Type(i) = (info[i]) ? data::Datatype::categorical :
data::Datatype::numeric;
}

arma::mat m(data, rows, columns, false, true);
std::get<0>(CLI::GetParam<std::tuple<data::DatasetInfo, arma::mat>>(
name)) = std::move(d);
std::get<1>(CLI::GetParam<std::tuple<data::DatasetInfo, arma::mat>>(
name)) = std::move(m);
}

void EnableVerbose()
{
Log::Info.ignoreInput = false;
}

void DisableVerbose()
{
Log::Info.ignoreInput = true;
}

} // namespace util
} // namespace mlpack