diff --git a/.figures/classification_example.png b/.figures/classification_example.png
new file mode 100644
index 000000000..4343b7b88
Binary files /dev/null and b/.figures/classification_example.png differ
diff --git a/.figures/plssvm_bokeh.gif b/.figures/plssvm_bokeh.gif
new file mode 100644
index 000000000..8cad0eaf4
Binary files /dev/null and b/.figures/plssvm_bokeh.gif differ
diff --git a/.figures/regression_example.png b/.figures/regression_example.png
new file mode 100644
index 000000000..b6cab4ab1
Binary files /dev/null and b/.figures/regression_example.png differ
diff --git a/.figures/sklearn_examples/classifier_comparison.png b/.figures/sklearn_examples/classifier_comparison.png
new file mode 100644
index 000000000..9fcdacc85
Binary files /dev/null and b/.figures/sklearn_examples/classifier_comparison.png differ
diff --git a/.figures/sklearn_examples/decision_boundaries_via_coef_and_intercept.png b/.figures/sklearn_examples/decision_boundaries_via_coef_and_intercept.png
new file mode 100644
index 000000000..b3e0d2f2d
Binary files /dev/null and b/.figures/sklearn_examples/decision_boundaries_via_coef_and_intercept.png differ
diff --git a/.figures/sklearn_examples/decision_boundary_confidence.png b/.figures/sklearn_examples/decision_boundary_confidence.png
new file mode 100644
index 000000000..93eb03a6a
Binary files /dev/null and b/.figures/sklearn_examples/decision_boundary_confidence.png differ
diff --git a/.figures/sklearn_examples/different_classifiers.png b/.figures/sklearn_examples/different_classifiers.png
new file mode 100644
index 000000000..e47aea6e4
Binary files /dev/null and b/.figures/sklearn_examples/different_classifiers.png differ
diff --git a/.figures/sklearn_examples/digit_classification_1.png b/.figures/sklearn_examples/digit_classification_1.png
new file mode 100644
index 000000000..f291df815
Binary files /dev/null and b/.figures/sklearn_examples/digit_classification_1.png differ
diff --git a/.figures/sklearn_examples/digit_classification_2.png b/.figures/sklearn_examples/digit_classification_2.png
new file mode 100644
index 000000000..32e78979e
Binary files /dev/null and b/.figures/sklearn_examples/digit_classification_2.png differ
diff --git a/.figures/sklearn_examples/digit_classification_confusion_matrix.png b/.figures/sklearn_examples/digit_classification_confusion_matrix.png
new file mode 100644
index 000000000..d34e72afc
Binary files /dev/null and b/.figures/sklearn_examples/digit_classification_confusion_matrix.png differ
diff --git a/.figures/sklearn_examples/face_recognition.png b/.figures/sklearn_examples/face_recognition.png
new file mode 100644
index 000000000..3e61c9155
Binary files /dev/null and b/.figures/sklearn_examples/face_recognition.png differ
diff --git a/.figures/sklearn_examples/face_recognition_confusion_matrix.png b/.figures/sklearn_examples/face_recognition_confusion_matrix.png
new file mode 100644
index 000000000..3bcb33fc8
Binary files /dev/null and b/.figures/sklearn_examples/face_recognition_confusion_matrix.png differ
diff --git a/.figures/sklearn_examples/face_recognition_eigenfaces.png b/.figures/sklearn_examples/face_recognition_eigenfaces.png
new file mode 100644
index 000000000..2553e7f97
Binary files /dev/null and b/.figures/sklearn_examples/face_recognition_eigenfaces.png differ
diff --git a/.figures/sklearn_examples/feature_discretization.png b/.figures/sklearn_examples/feature_discretization.png
new file mode 100644
index 000000000..79e0d59b4
Binary files /dev/null and b/.figures/sklearn_examples/feature_discretization.png differ
diff --git a/.figures/sklearn_examples/rbf_parameters.png b/.figures/sklearn_examples/rbf_parameters.png
new file mode 100644
index 000000000..6acae61a1
Binary files /dev/null and b/.figures/sklearn_examples/rbf_parameters.png differ
diff --git a/.figures/sklearn_examples/rbf_parameters_3_classes.png b/.figures/sklearn_examples/rbf_parameters_3_classes.png
new file mode 100644
index 000000000..e946f6335
Binary files /dev/null and b/.figures/sklearn_examples/rbf_parameters_3_classes.png differ
diff --git a/.figures/sklearn_examples/rbf_parameters_accuracy.png b/.figures/sklearn_examples/rbf_parameters_accuracy.png
new file mode 100644
index 000000000..da5add793
Binary files /dev/null and b/.figures/sklearn_examples/rbf_parameters_accuracy.png differ
diff --git a/.figures/sklearn_examples/rbf_parameters_accuracy_3_classes.png b/.figures/sklearn_examples/rbf_parameters_accuracy_3_classes.png
new file mode 100644
index 000000000..5b0610e2e
Binary files /dev/null and b/.figures/sklearn_examples/rbf_parameters_accuracy_3_classes.png differ
diff --git a/.figures/sklearn_examples/real_world/california_housing.png b/.figures/sklearn_examples/real_world/california_housing.png
new file mode 100644
index 000000000..c009e1bed
Binary files /dev/null and b/.figures/sklearn_examples/real_world/california_housing.png differ
diff --git a/.figures/sklearn_examples/real_world/fashion_mnist.png b/.figures/sklearn_examples/real_world/fashion_mnist.png
new file mode 100644
index 000000000..1f308444e
Binary files /dev/null and b/.figures/sklearn_examples/real_world/fashion_mnist.png differ
diff --git a/.figures/sklearn_examples/real_world/fashion_mnist_confusion_matrix.png b/.figures/sklearn_examples/real_world/fashion_mnist_confusion_matrix.png
new file mode 100644
index 000000000..ee6ef26db
Binary files /dev/null and b/.figures/sklearn_examples/real_world/fashion_mnist_confusion_matrix.png differ
diff --git a/.figures/sklearn_examples/real_world/svhn.png b/.figures/sklearn_examples/real_world/svhn.png
new file mode 100644
index 000000000..61bc78ecf
Binary files /dev/null and b/.figures/sklearn_examples/real_world/svhn.png differ
diff --git a/.figures/sklearn_examples/real_world/svhn_confusion_matrix.png b/.figures/sklearn_examples/real_world/svhn_confusion_matrix.png
new file mode 100644
index 000000000..e7506cee2
Binary files /dev/null and b/.figures/sklearn_examples/real_world/svhn_confusion_matrix.png differ
diff --git a/.figures/sklearn_examples/separating_hyperplane.png b/.figures/sklearn_examples/separating_hyperplane.png
new file mode 100644
index 000000000..53b7a350f
Binary files /dev/null and b/.figures/sklearn_examples/separating_hyperplane.png differ
diff --git a/.figures/sklearn_examples/svm_anova.png b/.figures/sklearn_examples/svm_anova.png
new file mode 100644
index 000000000..3e0fe41c5
Binary files /dev/null and b/.figures/sklearn_examples/svm_anova.png differ
diff --git a/.figures/sklearn_examples/svm_kernels_data.png b/.figures/sklearn_examples/svm_kernels_data.png
new file mode 100644
index 000000000..e7d776358
Binary files /dev/null and b/.figures/sklearn_examples/svm_kernels_data.png differ
diff --git a/.figures/sklearn_examples/svm_kernels_laplacian.png b/.figures/sklearn_examples/svm_kernels_laplacian.png
new file mode 100644
index 000000000..cfd64a135
Binary files /dev/null and b/.figures/sklearn_examples/svm_kernels_laplacian.png differ
diff --git a/.figures/sklearn_examples/svm_kernels_linear.png b/.figures/sklearn_examples/svm_kernels_linear.png
new file mode 100644
index 000000000..9cc493483
Binary files /dev/null and b/.figures/sklearn_examples/svm_kernels_linear.png differ
diff --git a/.figures/sklearn_examples/svm_kernels_poly.png b/.figures/sklearn_examples/svm_kernels_poly.png
new file mode 100644
index 000000000..7a3486bb9
Binary files /dev/null and b/.figures/sklearn_examples/svm_kernels_poly.png differ
diff --git a/.figures/sklearn_examples/svm_kernels_rbf.png b/.figures/sklearn_examples/svm_kernels_rbf.png
new file mode 100644
index 000000000..b6201c766
Binary files /dev/null and b/.figures/sklearn_examples/svm_kernels_rbf.png differ
diff --git a/.figures/sklearn_examples/svm_kernels_sigmoid.png b/.figures/sklearn_examples/svm_kernels_sigmoid.png
new file mode 100644
index 000000000..ce15ef6a8
Binary files /dev/null and b/.figures/sklearn_examples/svm_kernels_sigmoid.png differ
diff --git a/.figures/sklearn_examples/svm_kernels_xor.png b/.figures/sklearn_examples/svm_kernels_xor.png
new file mode 100644
index 000000000..3fa8b491e
Binary files /dev/null and b/.figures/sklearn_examples/svm_kernels_xor.png differ
diff --git a/.figures/sklearn_examples/svm_margin.png b/.figures/sklearn_examples/svm_margin.png
new file mode 100644
index 000000000..754b6df6c
Binary files /dev/null and b/.figures/sklearn_examples/svm_margin.png differ
diff --git a/.figures/sklearn_examples/svm_regression.png b/.figures/sklearn_examples/svm_regression.png
new file mode 100644
index 000000000..aa0dbfc3b
Binary files /dev/null and b/.figures/sklearn_examples/svm_regression.png differ
diff --git a/.github/workflows/clang_gcc_linux.yml b/.github/workflows/clang_gcc_linux.yml
new file mode 100644
index 000000000..1fa8867a8
--- /dev/null
+++ b/.github/workflows/clang_gcc_linux.yml
@@ -0,0 +1,46 @@
+name: Linux (Ubuntu) CPU
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ workflow_dispatch:
+jobs:
+ Linux-Test:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ build_type: [Debug, Release]
+ compiler: [g++, clang++]
+ steps:
+ - name: "Update system"
+ run: |
+ sudo apt update
+ sudo apt upgrade
+ sudo apt autoremove
+ - name: "Install Compiler"
+ run: |
+ sudo apt install g++ clang libomp-dev
+ - name: "Install cmake 3.31.0"
+ uses: lukka/get-cmake@v3.31.0
+ - name: "Clone the PLSSVM repository into PLSSVM/"
+ uses: actions/checkout@v4.1.1
+ with:
+ path: PLSSVM
+ - name: "Install Python dependencies"
+ run: |
+ pip install --upgrade pip
+ pip install -r PLSSVM/install/python_requirements.txt
+ - name: "Configure PLSSVM using CMake"
+ run: |
+ cd PLSSVM
+ cmake --preset openmp_test -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DPLSSVM_TARGET_PLATFORMS="cpu" -DPLSSVM_ENABLE_LANGUAGE_BINDINGS=ON -DPLSSVM_ENABLE_PERFORMANCE_TRACKING=ON -DPLSSVM_TEST_FILE_NUM_DATA_POINTS=50 -DPLSSVM_TEST_FILE_NUM_FEATURES=20 -DPLSSVM_ENABLE_LTO=OFF
+ - name: "Build PLSSVM"
+ run: |
+ cd PLSSVM
+ cmake --build --preset openmp_test --config ${{ matrix.build_type }}
+ echo "${GITHUB_WORKSPACE}/PLSSVM/build" >> $GITHUB_PATH
+ - name: "Run tests"
+ run: |
+ cd PLSSVM
+ ctest --preset openmp_test -C ${{ matrix.build_type }} --parallel 2
\ No newline at end of file
diff --git a/.github/workflows/clang_macos.yml b/.github/workflows/clang_macos.yml
index f83f0caa5..052bdd2a7 100644
--- a/.github/workflows/clang_macos.yml
+++ b/.github/workflows/clang_macos.yml
@@ -1,7 +1,10 @@
name: macOS CPU
on:
- workflow_dispatch:
push:
+ branches:
+ - main
+ pull_request:
+ workflow_dispatch:
jobs:
macOS-Test:
runs-on: macos-13
@@ -22,7 +25,12 @@ jobs:
brew install libomp
- name: "Install Python dependencies"
run: |
- python3 -m pip install argparse scikit-learn humanize --break-system-packages
+ python3 -m pip install -U pip
+ pip install argparse scikit-learn humanize --break-system-packages
+ - name: "Set PYTHONPATH"
+ run: |
+ export PYTHONPATH=$(python3 -c "import site; print(site.getsitepackages()[0])")
+ echo "PYTHONPATH=$PYTHONPATH" >> $GITHUB_ENV
- name: "Clone the PLSSVM repository into PLSSVM/"
uses: actions/checkout@v4.1.1
with:
@@ -32,7 +40,7 @@ jobs:
cd PLSSVM
export LDFLAGS="-L/opt/homebrew/opt/libomp/lib"
export CPPFLAGS="-I/opt/homebrew/opt/libomp/include"
- cmake --preset openmp_test -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DPLSSVM_TARGET_PLATFORMS="cpu" -DPLSSVM_ENABLE_LANGUAGE_BINDINGS=ON -DPLSSVM_ENABLE_PERFORMANCE_TRACKING=ON -DPLSSVM_GENERATE_TEST_FILE=OFF -DPLSSVM_ENABLE_LTO=OFF
+ cmake --preset openmp_test -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DPLSSVM_TARGET_PLATFORMS="cpu" -DPLSSVM_ENABLE_LANGUAGE_BINDINGS=ON -DPLSSVM_ENABLE_PERFORMANCE_TRACKING=ON -DPLSSVM_TEST_FILE_NUM_DATA_POINTS=50 -DPLSSVM_TEST_FILE_NUM_FEATURES=20 -DPLSSVM_ENABLE_LTO=OFF
- name: "Build PLSSVM"
shell: bash
run: |
diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml
index 20552191d..8632723be 100644
--- a/.github/workflows/format.yml
+++ b/.github/workflows/format.yml
@@ -31,6 +31,7 @@ jobs:
cmake --preset all -DPLSSVM_TARGET_PLATFORMS="cpu" -DPLSSVM_ENABLE_FORMATTING=ON
# check source file formatting
- name: "Check source file formatting via clang-format"
+ if: always()
run: |
set +e
cd PLSSVM
@@ -53,6 +54,7 @@ jobs:
if-no-files-found: ignore
# check CMake formatting
- name: "Check CMake formatting via cmake-format"
+ if: always()
run: |
set +e
cd PLSSVM
diff --git a/.github/workflows/msvc_windows.yml b/.github/workflows/msvc_windows.yml
index d31b5df7f..f833ad83a 100644
--- a/.github/workflows/msvc_windows.yml
+++ b/.github/workflows/msvc_windows.yml
@@ -1,7 +1,10 @@
name: Windows MSVC CPU
on:
- workflow_dispatch:
push:
+ branches:
+ - main
+ pull_request:
+ workflow_dispatch:
jobs:
MSVC-Test:
runs-on: windows-latest
diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml
new file mode 100644
index 000000000..cf8e9d991
--- /dev/null
+++ b/.github/workflows/pip.yml
@@ -0,0 +1,66 @@
+name: pip install
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ workflow_dispatch:
+jobs:
+ pip-Test:
+ runs-on: ubuntu-latest
+ steps:
+ - name: "Update system"
+ run: |
+ sudo apt update
+ sudo apt upgrade
+ sudo apt autoremove
+ - name: "Install new g++"
+ run: |
+ sudo apt install g++
+ - name: "Clone the PLSSVM repository into PLSSVM/"
+ uses: actions/checkout@v4.1.1
+ with:
+ path: PLSSVM
+ - name: "Set up Python"
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.11"
+ - name: "Create and activate virtual environment"
+ run: |
+ python -m venv venv
+ source venv/bin/activate
+ echo "VIRTUAL_ENV=$VIRTUAL_ENV" >> $GITHUB_ENV
+ - name: "Install Python dependencies"
+ run: |
+ source $VIRTUAL_ENV/bin/activate
+ pip install --upgrade pip
+ pip install -r PLSSVM/install/python_requirements.txt
+ - name: "Build PLSSVM locally via pip"
+ run: |
+ source $VIRTUAL_ENV/bin/activate
+ cd PLSSVM
+ pip install .
+ - name: "Run examples"
+ env:
+ MPLBACKEND: Agg
+ run: |
+ source $VIRTUAL_ENV/bin/activate
+ set -e
+ cd PLSSVM/examples/python
+ python sklearn_like_svc.py
+ python sklearn_like_svr.py
+ cd sklearn
+ python plot_classifier_comparison.py
+ python plot_decision_boundaries_via_coef_and_intercept.py
+ python plot_decision_boundary_confidence.py
+ python plot_different_classifiers.py
+ python plot_digits_classification.py
+ python plot_face_recognition.py
+ python plot_feature_discretization.py
+ python plot_rbf_parameters.py
+ python plot_rbf_parameters_3_classes.py
+ python plot_separating_hyperplane.py
+ python plot_svm_anova.py
+ python plot_svm_kernels.py
+ python plot_svm_margin.py
+ python plot_svm_regression.py
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 84e730eec..7ef583bd8 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -93,20 +93,22 @@ set(PLSSVM_BASE_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/detail/string_utility.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/detail/utility.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/exceptions/exceptions.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/svm/csvm.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/version/version.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/version/git_metadata/git_metadata.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/backend_types.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/classification_report.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/classification_types.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/csvm.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/file_format_types.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/gamma.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/kernel_function_types.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/matrix.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/parameter.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/regression_report.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/shape.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/solver_types.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/target_platforms.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/svm_types.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/plssvm/verbosity_levels.cpp
)
@@ -166,7 +168,9 @@ target_compile_options(
/wd4005
/wd4702
/wd4849
- /wd4127>
+ /wd4127
+ /wd4250 # disable multiple-inheritance dominance warning
+ /wd4589> # disable constructor inheritance warning
# /wd4849: ignore "OpenMP 'reduction' clause ignored in 'simd' directive" -> no SIMD clause currently effective in MSVC /wd4127: ignore "conditional
# expression is constant" from {fmt} ranges.h header
)
@@ -211,11 +215,11 @@ endif ()
########################################################################################################################
# check for OpenMP (not for the backend!) #
########################################################################################################################
-find_package(OpenMP 4.0 QUIET)
+find_package(OpenMP QUIET)
if (OpenMP_FOUND)
message(STATUS "Found OpenMP ${OpenMP_CXX_VERSION} to speed-up library utilities (like file parsing).")
set(PLSSVM_FOUND_OPENMP_FOR_UTILITY ON)
- if (${CMAKE_CXX_COMPILER_ID} MATCHES "MSVC")
+ if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_options(${PLSSVM_BASE_LIBRARY_NAME} PUBLIC -openmp:llvm -openmp:experimental)
else ()
target_link_libraries(${PLSSVM_BASE_LIBRARY_NAME} PUBLIC OpenMP::OpenMP_CXX)
@@ -326,17 +330,6 @@ else ()
)
endif ()
- include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/check_python_libs.cmake)
- set(PLSSVM_TARGET_PLATFORMS_PYTHON_SCRIPT_REQUIRED_LIBS cpuinfo GPUtil pyamdgpuinfo pylspci)
- message(
- STATUS
- "Checking required Python3 libraries (${PLSSVM_TARGET_PLATFORMS_PYTHON_SCRIPT_REQUIRED_LIBS}) to automatically determine the PLSSVM_TARGET_PLATFORMS."
- )
- set(PLSSVM_TARGET_PLATFORMS_PYTHON_SCRIPT_REQUIRED_LIBS_ERROR_MESSAGE
- "or manually define PLSSVM_TARGET_PLATFORMS (e.g. -DPLSSVM_TARGET_PLATFORMS=\"cpu;nvidia:sm_70,sm_86;amd:gfx906;intel:skl\"!"
- )
- check_python_libs(${PLSSVM_TARGET_PLATFORMS_PYTHON_SCRIPT_REQUIRED_LIBS} ${PLSSVM_TARGET_PLATFORMS_PYTHON_SCRIPT_REQUIRED_LIBS_ERROR_MESSAGE})
-
# run our `plssvm_target_platforms.py` script to determine the PLSSVM_TARGET_PLATFORMS string
execute_process(
COMMAND ${Python3_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/utility_scripts/plssvm_target_platforms.py" "--quiet"
diff --git a/README.md b/README.md
index 7325d3e00..b367cb9cf 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-](docs/resources/logo_245x150.png)
+
# PLSSVM - Parallel Least Squares Support Vector Machine
@@ -12,6 +12,7 @@
- [Building PLSSVM](#building-plssvm)
- [Running the Tests](#running-the-tests)
- [Generating Test Coverage Results](#generating-test-coverage-results)
+ - [Automatic Source File Formatting](#automatic-source-file-formatting)
- [Creating the Documentation](#creating-the-documentation)
- [Installing](#installing)
- [Usage](#usage)
@@ -20,7 +21,7 @@
- [Predicting using `plssvm-predict`](#predicting-using-plssvm-predict)
- [Data Scaling using `plssvm-scale`](#data-scaling-using-plssvm-scale)
- [Example Code for PLSSVM Used as a Library](#example-code-for-plssvm-used-as-a-library)
- - [Example Using the Python Bindings Available For PLSSVM](#example-using-the-python-bindings-available-for-plssvm)
+ - [Example Using the `sklearn` Python Bindings Available For PLSSVM](#example-using-the-sklearn-like-python-bindings-available-for-plssvm)
- [Citing PLSSVM](#citing-plssvm)
- [License](#license)
@@ -34,7 +35,7 @@ To predict to which class a new, unseen data point belongs, the SVM simply has t
This is very efficient since it only involves a single scalar product of the size corresponding to the numer of features of the data set.
-
+
However, normal SVMs suffer in their potential parallelizability.
@@ -79,9 +80,12 @@ The main highlights of our SVM implementations are:
5. Multi-class classification available via one vs. all (also one vs. rest or OAA) and one vs. one (also OAO):
- OAA: one huge classification task where our CG algorithm solves a system of linear equations with multiple right-hand sides. The resulting model file is **not** compatible with LIBSVM.
- OAO: constructs many but smaller binary classifications. The resulting model file is **fully** compatible with LIBSVM.
-6. Multi-GPU support for **all** kernel functions and GPU backends for `fit` as well as `predict/score` (**note**: no multi-GPU support for the stdpar backend even if run on a GPU!).
-7. Python bindings as drop-in replacement for `sklearn.SVC` (some features currently not implemented).
+6. Also, support for the regression task.
+7. Multi-GPU support for **all** kernel functions and GPU backends for `fit` as well as `predict/score` (**note**: no multi-GPU support for the stdpar backend even if run on a GPU!).
+8. Python bindings as drop-in replacement for `sklearn.SVC` and `sklearn.SVR` (some features currently not implemented).
+To see the full power of Support Vector Machines, have a look at our live visualization examples in
+[examples/python/interactive](examples/python/interactive/README.md).
## Getting Started
@@ -144,7 +148,7 @@ Additional dependencies for the stdpar backend:
- `AdaptiveCpp`: Intel's [TBB](https://github.com/wjakob/tbb) library
- `GNU GCC`: [Boost ≥ 1.73.0](https://www.boost.org/) with the `atomic` library enabled and Intel's [TBB](https://github.com/wjakob/tbb) library
-Additional dependencies if `PLSSVM_ENABLE_TESTING` and `PLSSVM_GENERATE_TEST_FILE` are both set to `ON`:
+Additional dependencies if `PLSSVM_ENABLE_TESTING` and `PLSSVM_GENERATE_TEST_FILES` are both set to `ON`:
- [Python3](https://www.python.org/) with the [`argparse`](https://docs.python.org/3/library/argparse.html), [`timeit`](https://docs.python.org/3/library/timeit.html), [`sklearn`](https://scikit-learn.org/stable/), and [`humanize`](https://pypi.org/project/humanize/) modules
@@ -186,13 +190,9 @@ Valid targets are:
- `intel`: compile for Intel GPUs; **at least one** architectural specification is necessary, e.g., `intel:skl`
At least one of the above targets must be present. If the option `PLSSVM_TARGET_PLATFORMS` is not present, the targets
-are automatically determined using the Python3 `utility_scripts/plssvm_target_platforms.py` script (required Python3 dependencies:
-[`argparse`](https://docs.python.org/3/library/argparse.html), [`py-cpuinfo`](https://pypi.org/project/py-cpuinfo/),
-[`GPUtil`](https://pypi.org/project/GPUtil/), [`pyamdgpuinfo`](https://pypi.org/project/pyamdgpuinfo/), and
-[`pylspci`](https://pypi.org/project/pylspci/)).
+are automatically determined using the Python3 `utility_scripts/plssvm_target_platforms.py` script.
-Note that when using DPC++/icpx only a single architectural specification for `cpu`, `nvidia` or `amd` is allowed and that
-automatically retrieving AMD GPU information on Windows is currently not supported due to `pyamdgpuinfo` limitations.
+Note that when using DPC++/icpx only a single architectural specification for `cpu`, `nvidia` or `amd` is allowed.
```bash
@@ -202,8 +202,9 @@ python3 utility_scripts/plssvm_target_platforms.py --help
usage: plssvm_target_platforms.py [-h] [--quiet]
optional arguments:
- -h, --help show this help message and exit
- --quiet only output the final PLSSVM_TARGET_PLATFORMS string
+ -h, --help show this help message and exit
+ --quiet only output the final PLSSVM_TARGET_PLATFORMS string
+ --gpus_only only output gpu architectures to the final PLSSVM_TARGET_PLATFORMS string
```
Example invocation:
@@ -212,10 +213,9 @@ Example invocation:
python3 utility_scripts/plssvm_target_platforms.py
```
```
-Intel(R) Core(TM) i9-10980XE CPU @ 3.00GHz: {'avx512': True, 'avx2': True, 'avx': True, 'sse4_2': True}
+supported CPU SIMD flags: {'avx512': True, 'avx2': True, 'avx': True, 'sse4_2': True}
-Found 1 NVIDIA GPU(s):
- 1x NVIDIA GeForce RTX 3080: sm_86
+Found 1 NVIDIA GPU(s): [sm_86]
Possible -DPLSSVM_TARGET_PLATFORMS entries:
cpu:avx512;nvidia:sm_86
@@ -229,13 +229,11 @@ or with the `--quiet` flag provided:
python3 utility_scripts/plssvm_target_platforms.py --quiet
```
```
-cpu:avx512;intel:dg1
+cpu:avx512;nvidia:sm_86
```
If the architectural information for the requested GPU could not be retrieved, one option would be to have a look at:
-- for NVIDIA GPUs: [Your GPU Compute Capability](https://developer.nvidia.com/cuda-gpus)
-- for AMD GPUs: [clang AMDGPU backend usage](https://llvm.org/docs/AMDGPUUsage.html)
- for Intel GPUs and CPUs: [Ahead of Time Compilation](https://www.intel.com/content/www/us/en/develop/documentation/oneapi-dpcpp-cpp-compiler-dev-guide-and-reference/top/compilation/ahead-of-time-compilation.html) and [Intel graphics processor table](https://dgpu-docs.intel.com/devices/hardware-table.html)
@@ -301,9 +299,9 @@ The `[optional_options]` can be one or multiple of:
If `PLSSVM_ENABLE_TESTING` is set to `ON`, the following option can also be set:
-- `PLSSVM_GENERATE_TEST_FILE=ON|OFF` (default: `ON`): automatically generate test files
+- `PLSSVM_GENERATE_TEST_FILES=ON|OFF` (default: `ON`): automatically generate test files
-If `PLSSVM_GENERATE_TEST_FILE` is set to `ON`, the following options can also be set:
+If `PLSSVM_GENERATE_TEST_FILES` is set to `ON`, the following options can also be set:
- `PLSSVM_TEST_FILE_NUM_DATA_POINTS` (default: `5000`): the number of data points in the test file
- `PLSSVM_TEST_FILE_NUM_FEATURES` (default: `2000`): the number of features per data point in the test file
@@ -322,10 +320,6 @@ If `PLSSVM_ENABLE_LANGUAGE_BINDINGS` is set to `ON`, the following option can al
- `PLSSVM_ENABLE_PYTHON_BINDINGS=ON|OFF` (default: `PLSSVM_ENABLE_LANGUAGE_BINDINGS`): enable Python bindings using Pybind11; **note:** `PLSSVM_ENABLE_LANGUAGE_BINDINGS` must be set that this option has any effect
-If `PLSSVM_ENABLE_PYTHON_BINDINGS` is set to `ON`, the following options can also be set:
-
-- `PLSSVM_PYTHON_BINDINGS_PREFERRED_LABEL_TYPE` (default: `std::string`): the default `label_type` used if the generic `plssvm.Model` and `plssvm.DataSet` Python classes are used
-
If the OpenCL backend is available and NVIDIA GPUs should be targeted, an additional option can be set.
- `PLSSVM_OPENCL_BACKEND_ENABLE_PTX_INLINE_ASSEMBLY=ON|OFF` (default: `ON`): enable PTX inline assembly to speed up the FP32/FP64 atomicAdd implementations on NVIDIA GPUs. **Note:** requires `sm_60` or newer!
@@ -500,6 +494,8 @@ The documentation of the current state of the main branch can be found [here](ht
### Installing
+#### Install via CMake
+
The library supports the `install` target:
```bash
@@ -517,6 +513,48 @@ export LD_LIBRARY_PATH=${CMAKE_INSTALL_PREFIX}/lib:${CMAKE_INSTALL_PREFIX}/lib64
export CPLUS_INCLUDE_PATH=${CMAKE_INSTALL_PREFIX}/include:${CPLUS_INCLUDE_PATH}
```
+If our library was built with the Python bindings enabled, the `PYTHONPATH` must additionally be set:
+
+```bash
+export PYTHONPATH=${CMAKE_INSTALL_PREFIX}/lib:${CMAKE_INSTALL_PREFIX}/lib64:${PYTHONPATH}
+```
+
+#### Install via pip
+
+We also support a pip packages that can be used to install our library:
+
+```bash
+pip install plssvm
+```
+
+This pip install behaves **as if** the CMake `all_python` preset is used.
+This means that the `PLSSVM_TARGET_PLATFORMS` are automatically determined and PLSSVM is build with all supported
+backends that available on the target machine at the point of the `pip install plssvm` invocation.
+To check the installation, including, e.g., the installed backends, we provide the `plssvm-install-check` command after
+PLSSVM has been installed via pip.
+An example output of this command can look like:
+
+```text
+PLSSVM - Parallel Least Squares Support Vector Machine (3.0.0)
+
+Copyright(C) 2018-today The PLSSVM project - All Rights Reserved
+This is free software distributed under the MIT license.
+
+Available target platforms: TargetPlatform.AUTOMATIC, TargetPlatform.GPU_NVIDIA, TargetPlatform.CPU
+Default target platform: TargetPlatform.GPU_NVIDIA
+
+Available backends: BackendType.AUTOMATIC, BackendType.OPENMP, BackendType.CUDA, BackendType.OPENCL, BackendType.SYCL
+Default backend for target platform TargetPlatform.GPU_NVIDIA: BackendType.CUDA
+Default backend for target platform TargetPlatform.CPU: BackendType.SYCL
+
+Available SYCL implementations: ImplementationType.AUTOMATIC, ImplementationType.ADAPTIVECPP
+
+
+Repository: https://github.com/SC-SGS/PLSSVM.git
+Documentation: https://sc-sgs.github.io/PLSSVM
+Issues: https://github.com/SC-SGS/PLSSVM/issues
+```
+
## Usage
PLSSVM provides three executables: `plssvm-train`, `plssvm-predict`, and `plssvm-scale`.
@@ -525,7 +563,7 @@ For more information, see the respective `man` pages which are installed via `cm
### Generating Artificial Data
-The repository comes with a Python3 script (in the `utility_scripts/` directory) to simply generate arbitrarily large data sets.
+The repository comes with a Python3 script (in the `utility_scripts/` directory) to simply generate arbitrarily large classification and regression data sets.
In order to use all functionality, the following Python3 modules must be installed:
[`argparse`](https://docs.python.org/3/library/argparse.html), [`timeit`](https://docs.python.org/3/library/timeit.html),
@@ -535,26 +573,58 @@ In order to use all functionality, the following Python3 modules must be install
and [`humanize`](https://pypi.org/project/humanize/).
```
-usage: generate_data.py [-h] [--output OUTPUT] [--format FORMAT] [--problem PROBLEM] --samples SAMPLES [--test_samples TEST_SAMPLES] --features FEATURES [--classes CLASSES] [--plot]
+usage: generate_data.py [-h] [--output OUTPUT] [--format FORMAT] --samples SAMPLES [--test_samples TEST_SAMPLES] --features FEATURES [--scale SCALE SCALE] [--plot] {classification,regression} ...
-options:
- -h, --help show this help message and exit
+positional arguments:
+ {classification,regression}
+ classification create a classification data set
+ regression create regression data set
+
+optional arguments:
+ -h, -?, --help show this help message and exit
--output OUTPUT the output file to write the samples to (without extension)
--format FORMAT the file format; either arff, libsvm, or csv
- --problem PROBLEM the problem to solve; one of: blobs, blobs_merged, planes, ball
--samples SAMPLES the number of training samples to generate
--test_samples TEST_SAMPLES
the number of test samples to generate; default: 0
--features FEATURES the number of features per data point
- --classes CLASSES the number of classes to generate; default: 2
+ --scale SCALE SCALE scale the features to the provided range
--plot plot training samples; only possible if 0 < samples <= 2000 and 1 < features <= 3
+
+
+classification specific arguments:
+
+usage: generate_data.py classification [-h] [--problem {blobs,blobs_merged,planes,ball}] [--classes CLASSES]
+
+optional arguments:
+ -h, --help show this help message and exit
+ --problem {blobs,blobs_merged,planes,ball}
+ the problem to solve
+ --classes CLASSES the number of classes to generate; default: 2
+
+
+regression specific arguments:
+
+usage: generate_data.py regression [-h] [--problem {linear,linear_noisy,friedman1}]
+
+optional arguments:
+ -h, --help show this help message and exit
+ --problem {linear,linear_noisy,friedman1}
+ the problem to solve
+
```
-An example invocation generating a data set consisting of blobs with 1000 data points with 200 features each and
+An example invocation generating a classification data set consisting of blobs with 1000 data points with 200 features each and
4 classes could look like:
```bash
-python3 generate_data.py --output data_file --format libsvm --problem blobs --samples 1000 --features 200 --classes 4
+python3 generate_data.py --output data_file --format libsvm --problem blobs --samples 1000 --features 200 classification --classes 4
+```
+
+An example invocation generating a linear regression data set consisting of 1000 data points with 200 features each could look like:
+
+```bash
+python3 generate_data.py --output data_file --format libsvm --problem linear --samples 1000 --features 200 regression
```
### Training using `plssvm-train`
@@ -567,6 +637,9 @@ LS-SVM with multiple (GPU-)backends
Usage:
./plssvm-train [OPTION...] training_set_file [model_file]
+ -s, --svm_type arg set type of SVM
+ 0 -- C-SVC
+ 1 -- C-SVR (default: 0)
-t, --kernel_type arg set type of kernel function.
0 -- linear: u'*v
1 -- polynomial: (gamma*u'*v+coef0)^degree
@@ -575,10 +648,10 @@ Usage:
4 -- laplacian: exp(-gamma*|u-v|_1)
5 -- chi_squared: exp(-gamma*sum_i((x[i]-y[i])^2/(x[i]+y[i]))) (default: 2)
-d, --degree arg set degree in kernel function (default: 3)
- -g, --gamma arg set gamma in kernel function (default: automatic)
+ -g, --gamma arg set gamma in kernel function (default: "1 / num_features")
-r, --coef0 arg set coef0 in kernel function (default: 0)
-c, --cost arg set the parameter C (default: 1)
- -e, --epsilon arg set the tolerance of termination criterion (default: 0.001)
+ -e, --epsilon arg set the tolerance of termination criterion (default: 1e-10)
-i, --max_iter arg set the maximum number of CG iterations (default: num_features)
-l, --solver arg choose the solver: automatic|cg_explicit|cg_implicit (default: automatic)
-a, --classification arg the classification strategy to use for multi-class classification: oaa|oao (default: oaa)
@@ -651,7 +724,36 @@ If the `--kokkos_execution_space` is `automatic`, uses the best fitting executio
### Predicting using `plssvm-predict`
Our predict utility is fully conform to LIBSVM's model files.
-This means that our `plssvm-predict` can be used on model files learned with, e.g., LIBSVM's `svm-train`.
+This means that our `plssvm-predict` can be used on model files learned with, e.g., LIBSVM's `svm-train`.
+Note: this is not the case for the regression task since the `svm_type` filed mismatch between LIBSVM (`epsilon_svr`)
+and PLSSVM (`c_svr`). To automatically convert between the two, simply use the `convert_model.py` script
+(in the `utility_scripts/` directory) which simply replaces these fields with the respectively expected one
+(note that for large files doing that manually may be faster):
+
+```bash
+usage: convert_model.py [-h] [-o OUTPUT] [--to_plssvm] [--to_libsvm] model_file
+
+positional arguments:
+ model_file the regression model file to convert
+
+options:
+ -h, --help show this help message and exit
+ -o OUTPUT, --output OUTPUT
+ output the regression model to the new file, otherwise the regression model us updated inplace
+ --to_plssvm convert the regression model to a PLSSVM conform model file
+ --to_libsvm convert the regression model to a LIBSVM conform model file
+```
+
+An example invocation could look like:
+
+```bash
+python3 convert_model.py --to_libsvm -o 5x4_libsvm.libsvm.model 5x4.libsvm.model
+```
+```
+Converting a PLSSVM model file to a LIBSVM model file.
+```
+
+After a correct model file exists, predict works as follows:
```bash
./plssvm-predict --help
@@ -734,7 +836,7 @@ An example invocation to scale a train and test file in the same way looks like:
### Example Code for PLSSVM Used as a Library
-A simple C++ program (`main.cpp`) using PLSSVM as library could look like:
+A simple C++ program (`main_classification.cpp`) using PLSSVM as library for classification could look like:
```cpp
#include "plssvm/core.hpp"
@@ -748,26 +850,26 @@ int main() {
plssvm::environment::scope_guard environment_guard{};
try {
- // create a new C-SVM parameter set, explicitly overriding the default kernel function
+ // create a new C-SVC parameter set, explicitly overriding the default kernel function
const plssvm::parameter params{ plssvm::kernel_type = plssvm::kernel_function_type::polynomial };
// create two data sets: one with the training data scaled to [-1, 1]
// and one with the test data scaled like the training data
- const plssvm::data_set train_data{ "train_file.libsvm", { -1.0, 1.0 } };
- const plssvm::data_set test_data{ "test_file.libsvm", train_data.scaling_factors()->get() };
+ const plssvm::classification_data_set train_data{ "train_file.libsvm", { -1.0, 1.0 } };
+ const plssvm::classification_data_set test_data{ "test_file.libsvm", train_data.scaling_factors()->get() };
- // create C-SVM using the default backend and the previously defined parameter
- const auto svm = plssvm::make_csvm(params);
+ // create C-SVC using the default backend and the previously defined parameter
+ const auto svc = plssvm::make_csvc(params);
// fit using the training data, (optionally) set the termination criterion
- const plssvm::model model = svm->fit(train_data, plssvm::epsilon = 10e-6);
+ const plssvm::classification_model model = svc->fit(train_data, plssvm::epsilon = 1e-6);
// get accuracy of the trained model
- const double model_accuracy = svm->score(model);
+ const double model_accuracy = svc->score(model);
std::cout << "model accuracy: " << model_accuracy << std::endl;
// predict the labels
- const std::vector predicted_label = svm->predict(model, test_data);
+ const std::vector predicted_label = svc->predict(model, test_data);
// output a more complete classification report
const std::vector &correct_label = test_data.labels().value();
std::cout << plssvm::classification_report{ correct_label, predicted_label } << std::endl;
@@ -784,10 +886,60 @@ int main() {
}
```
+A simple C++ program (`main_regression.cpp`) using PLSSVM as library for regression could look like:
+
+```cpp
+#include "plssvm/core.hpp"
+
+#include
+#include
+#include
+
+int main() {
+ // correctly initialize and finalize environments
+ plssvm::environment::scope_guard environment_guard{};
+
+ try {
+ // create a new C-SVR parameter set, explicitly overriding the default kernel function
+ const plssvm::parameter params{ plssvm::kernel_type = plssvm::kernel_function_type::polynomial };
+
+ // create two data sets: one with the training data scaled to [-1, 1]
+ // and one with the test data scaled like the training data
+ const plssvm::regression_data_set train_data{ "train_file.libsvm", { -1.0, 1.0 } };
+ const plssvm::regression_data_set test_data{ "test_file.libsvm", train_data.scaling_factors()->get() };
+
+ // create C-SVR using the default backend and the previously defined parameter
+ const auto svr = plssvm::make_csvr(params);
+
+ // fit using the training data, (optionally) set the termination criterion
+ const plssvm::regression_model model = svr->fit(train_data, plssvm::epsilon = 1e-6);
+
+ // get accuracy of the trained model
+ const double model_accuracy = svr->score(model);
+ std::cout << "model accuracy: " << model_accuracy << std::endl;
+
+ // predict the labels
+ const std::vector predicted_values = svc->predict(model, test_data);
+ // output a more complete regression report
+ const std::vector &correct_values = test_data.labels().value();
+ std::cout << plssvm::regression_report{ correct_label, predicted_label } << std::endl;
+
+ // write model file to disk
+ model.save("model_file.libsvm");
+ } catch (const plssvm::exception &e) {
+ std::cerr << e.what_with_loc() << std::endl;
+ } catch (const std::exception &e) {
+ std::cerr << e.what() << std::endl;
+ }
+
+ return 0;
+}
+```
+
With a corresponding minimal CMake file:
```cmake
-cmake_minimum_required(VERSION 3.16)
+cmake_minimum_required(VERSION 3.25)
project(LibraryUsageExample
LANGUAGES CXX)
@@ -796,7 +948,8 @@ find_package(plssvm REQUIRED)
# CMake's COMPONENTS mechanism can also be used if a specific library component is required, e.g.:
# find_package(plssvm REQUIRED COMPONENTS CUDA)
-add_executable(prog main.cpp)
+add_executable(classification main_classification.cpp)
+add_executable(regression main_regression.cpp)
target_compile_features(prog PUBLIC cxx_std_17)
target_link_libraries(prog PUBLIC plssvm::all)
@@ -804,58 +957,157 @@ target_link_libraries(prog PUBLIC plssvm::all)
# target_link_libraries(prog PUBLIC plssvm::cuda)
```
-### Example Using the Python Bindings Available For PLSSVM
+### Example Using the `sklearn` like Python Bindings Available For PLSSVM
-Roughly the same can be achieved using our Python bindings with the following Python script (note: needs [`sklearn`](https://scikit-learn.org/stable/)):
+A classification example using PLSSVM's `SVC` Python binding and sklearn's breast cancer data set:
```python
-import plssvm
-from sklearn.metrics import classification_report
-
-try:
- # create a new C-SVM parameter set, explicitly overriding the default kernel function
- params = plssvm.Parameter(kernel_type=plssvm.KernelFunctionType.POLYNOMIAL)
-
- # create two data sets: one with the training data scaled to [-1, 1]
- # and one with the test data scaled like the training data
- train_data = plssvm.DataSet("train_data.libsvm", scaling=(-1.0, 1.0))
- test_data = plssvm.DataSet("test_data.libsvm", scaling=train_data.scaling_factors())
-
- # create C-SVM using the default backend and the previously defined parameter
- svm = plssvm.CSVM(params)
-
- # fit using the training data, (optionally) set the termination criterion
- model = svm.fit(train_data, epsilon=10e-6)
-
- # get accuracy of the trained model
- model_accuracy = svm.score(model)
- print("model accuracy: {}".format(model_accuracy))
-
- # predict labels
- predicted_label = svm.predict(model, test_data)
- # output a more complete classification report
- correct_label = test_data.labels()
- print(classification_report(correct_label, predicted_label))
-
- # write model file to disk
- model.save("model_file.libsvm")
-except plssvm.PLSSVMError as e:
- print(e)
-except RuntimeError as e:
- print(e)
-```
-
-**Note:** it may be necessary to set the environment variable `PYTHONPATH` to the `lib` folder in the PLSSVM install path.
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+########################################################################################################################
+# Authors: Alexander Van Craen, Marcel Breyer #
+# Copyright (C): 2018-today The PLSSVM project - All Rights Reserved #
+# License: This file is part of the PLSSVM project which is released under the MIT license. #
+# See the LICENSE.md file in the project root for full license information. #
+########################################################################################################################
+
+import matplotlib.pyplot as plt
+import sklearn.datasets
+import sklearn.metrics
+import sklearn.inspection
+import numpy as np
+from plssvm import SVC # identical to from sklearn.svm import SVC
+
+# load the breast cancer datasets
+cancer = sklearn.datasets.load_breast_cancer()
+X = cancer.data[:, :2]
+y = cancer.target
+y_label = cancer.target_names
+
+# build the SVC model
+svm = SVC(kernel="rbf", gamma=0.5, C=1.0).fit(X, y)
+
+# score the model
+print(sklearn.metrics.classification_report(y, svm.predict(X)))
+print("Score: {:.2f}%".format(svm.score(X, y) * 100))
+
+# plot the decision boundary
+sklearn.inspection.DecisionBoundaryDisplay.from_estimator(
+ svm,
+ X,
+ response_method="predict",
+ cmap=plt.cm.Spectral,
+ alpha=0.8,
+ xlabel=cancer.feature_names[0],
+ ylabel=cancer.feature_names[1],
+)
+
+# scatter plot the decision boundary
+viridis = plt.cm.get_cmap('viridis', len(np.unique(y)))
+plt.scatter(X[:, 0], X[:, 1],
+ cmap=viridis,
+ c=y,
+ s=20, edgecolors="k")
+
+# generate legend handles and add handle
+legend_handles = [plt.scatter([], [], color=viridis(color), label=f'{label}')
+ for label, color in zip(y_label, np.unique(y))]
+plt.legend(handles=legend_handles)
+
+plt.title("SVC classifier on breast cancer dataset")
+plt.show()
+```
+with an example output:
+```text
+ precision recall f1-score support
-```bash
-export PYTHONPATH=${CMAKE_INSTALL_PREFIX}/lib:${CMAKE_INSTALL_PREFIX}/lib64:${PYTHONPATH}
+ 0 0.91 0.85 0.88 212
+ 1 0.91 0.95 0.93 357
+
+ accuracy 0.91 569
+ macro avg 0.91 0.90 0.91 569
+weighted avg 0.91 0.91 0.91 569
+
+Score: 91.39%
+```
+
+
+
+
+A regression example comparing PLSSVM's `SVR` Python binding and `sklearn.SVR` using a sine curve:
+
+```python
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+########################################################################################################################
+# Authors: Alexander Van Craen, Marcel Breyer #
+# Copyright (C): 2018-today The PLSSVM project - All Rights Reserved #
+# License: This file is part of the PLSSVM project which is released under the MIT license. #
+# See the LICENSE.md file in the project root for full license information. #
+########################################################################################################################
+
+import numpy as np
+import matplotlib.pyplot as plt
+
+# generate sample data (sine curve with noise)
+X = np.sort(5 * np.random.rand(40, 1), axis=0)
+y = np.sin(X).ravel()
+
+# add noise to targets
+y[::5] += 3 * (0.5 - np.random.rand(8))
+
+plt.scatter(X, y, color='darkorange', label='data')
+
+# fit the sklearn regression model
+from sklearn.svm import SVR
+
+sklearn_svr_lin = SVR(kernel='linear', C=100, epsilon=0.1)
+y_lin_sklearn = sklearn_svr_lin.fit(X, y).predict(X)
+plt.plot(X, y_lin_sklearn, lw=2, linestyle='dashed', label='Linear model sklearn')
+
+sklearn_svr_poly = SVR(kernel='poly', C=100, degree=3, epsilon=0.1, coef0=1)
+y_poly_sklearn = sklearn_svr_poly.fit(X, y).predict(X)
+plt.plot(X, y_poly_sklearn, lw=2, linestyle='dashed', label='Polynomial model sklearn')
+
+sklearn_svr_rbf = SVR(kernel='rbf', C=100, gamma=0.1, epsilon=0.1)
+y_rbf_sklearn = sklearn_svr_rbf.fit(X, y).predict(X)
+plt.plot(X, y_rbf_sklearn, lw=2, linestyle='dashed', label='RBF model sklearn')
+
+# fit the PLSSVM regression model
+from plssvm import SVR
+
+plssvm_svr_lin = SVR(kernel='linear', C=100)
+y_lin_plssvm = plssvm_svr_lin.fit(X, y).predict(X)
+plt.plot(X, y_lin_plssvm, lw=2, label='Linear model plssvm')
+
+plssvm_svr_poly = SVR(kernel='poly', C=100, degree=3, coef0=1)
+y_poly_plssvm = plssvm_svr_poly.fit(X, y).predict(X)
+plt.plot(X, y_poly_plssvm, lw=2, label='Polynomial model plssvm')
+
+plssvm_svr_rbf = SVR(kernel='rbf', C=100, gamma=0.1)
+y_rbf_plssvm = plssvm_svr_rbf.fit(X, y).predict(X)
+plt.plot(X, y_rbf_plssvm, lw=2, label='RBF model plssvm')
+
+# show the result plots
+plt.xlabel('data')
+plt.ylabel('target')
+plt.title('Support Vector Regression')
+plt.legend()
+plt.show()
```
+with an example output:
+
+
+
-We also provide Python bindings for a `plssvm.SVC` class that offers the same interface as the [`sklearn.svm.SVC`](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html) class.
-Note that currently not all functionality has been implemented in PLSSVM.
+Note that currently not all sklearn `SVC` and `SVR` functionality has been implemented in PLSSVM.
The respective functions will throw a Python `AttributeError` if called.
For a detailed overview of the functions that are currently implemented, see [our API documentation](bindings/Python/README.md).
+There are more examples located in the `examples/python/sklearn` directory that are copied from the sklearn repository and slightly changed for PLSSVM.
+
## Citing PLSSVM
If you use PLSSVM in your research, we kindly request you to cite:
diff --git a/bindings/Python/CMakeLists.txt b/bindings/Python/CMakeLists.txt
index fea4a0998..54f967f2a 100644
--- a/bindings/Python/CMakeLists.txt
+++ b/bindings/Python/CMakeLists.txt
@@ -35,18 +35,26 @@ set(PLSSVM_PYTHON_BINDINGS_SOURCES
${CMAKE_CURRENT_LIST_DIR}/version/version.cpp
${CMAKE_CURRENT_LIST_DIR}/backend_types.cpp
${CMAKE_CURRENT_LIST_DIR}/classification_types.cpp
- ${CMAKE_CURRENT_LIST_DIR}/csvm.cpp
- ${CMAKE_CURRENT_LIST_DIR}/data_set.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/svm/csvm.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/svm/csvc.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/svm/csvr.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/data_set/classification_data_set.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/data_set/min_max_scaler.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/data_set/regression_data_set.cpp
${CMAKE_CURRENT_LIST_DIR}/file_format_types.cpp
${CMAKE_CURRENT_LIST_DIR}/gamma.cpp
${CMAKE_CURRENT_LIST_DIR}/kernel_function_types.cpp
${CMAKE_CURRENT_LIST_DIR}/kernel_functions.cpp
- ${CMAKE_CURRENT_LIST_DIR}/model.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/model/classification_model.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/model/regression_model.cpp
${CMAKE_CURRENT_LIST_DIR}/parameter.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/regression_report.cpp
${CMAKE_CURRENT_LIST_DIR}/solver_types.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/svm_types.cpp
${CMAKE_CURRENT_LIST_DIR}/target_platforms.cpp
${CMAKE_CURRENT_LIST_DIR}/verbosity_levels.cpp
- ${CMAKE_CURRENT_LIST_DIR}/sklearn.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/sklearn_svc.cpp
+ ${CMAKE_CURRENT_LIST_DIR}/sklearn_svr.cpp
${CMAKE_CURRENT_LIST_DIR}/main.cpp
)
@@ -102,28 +110,6 @@ endif ()
set(PLSSVM_PYTHON_BINDINGS_LIBRARY_NAME plssvm)
pybind11_add_module(${PLSSVM_PYTHON_BINDINGS_LIBRARY_NAME} ${PLSSVM_PYTHON_BINDINGS_SOURCES})
-# set default label type
-set(PLSSVM_PYTHON_BINDINGS_POSSIBLE_LABEL_TYPE
- "bool;char;signed char;unsigned char;short;unsigned short;int;unsigned int;long;unsigned long;long long;unsigned long long;float;double;long double;std::string"
-)
-set(PLSSVM_PYTHON_BINDINGS_PREFERRED_LABEL_TYPE "std::string" CACHE STRING "The preferred type of the labels for the Python bindings.")
-set_property(CACHE PLSSVM_PYTHON_BINDINGS_PREFERRED_LABEL_TYPE PROPERTY STRINGS ${PLSSVM_PYTHON_BINDINGS_POSSIBLE_LABEL_TYPE})
-if (NOT "${PLSSVM_PYTHON_BINDINGS_PREFERRED_LABEL_TYPE}" IN_LIST PLSSVM_PYTHON_BINDINGS_POSSIBLE_LABEL_TYPE)
- message(
- FATAL_ERROR
- "The provided label_type \"${PLSSVM_PYTHON_BINDINGS_PREFERRED_LABEL_TYPE}\" is not one of the allowed values: \"${PLSSVM_PYTHON_BINDINGS_POSSIBLE_LABEL_TYPE}\""
- )
-endif ()
-message(STATUS "The preferred label_type for the Python bindings is \"${PLSSVM_PYTHON_BINDINGS_PREFERRED_LABEL_TYPE}\".")
-
-# add necessary compile definitions for the default real_type and label_type
-target_compile_definitions(
- ${PLSSVM_PYTHON_BINDINGS_LIBRARY_NAME} PRIVATE PLSSVM_PYTHON_BINDINGS_PREFERRED_LABEL_TYPE=${PLSSVM_PYTHON_BINDINGS_PREFERRED_LABEL_TYPE}
-)
-if (PLSSVM_PYTHON_BINDINGS_PREFERRED_LABEL_TYPE STREQUAL "std::string")
- target_compile_definitions(${PLSSVM_PYTHON_BINDINGS_LIBRARY_NAME} PRIVATE PLSSVM_PYTHON_BINDINGS_LABEL_TYPE_IS_STRING)
-endif ()
-
# add necessary compile options
target_include_directories(${PLSSVM_PYTHON_BINDINGS_LIBRARY_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/../..)
target_link_libraries(${PLSSVM_PYTHON_BINDINGS_LIBRARY_NAME} PRIVATE ${PLSSVM_ALL_LIBRARY_NAME})
@@ -135,3 +121,13 @@ target_compile_options(${PLSSVM_BASE_LIBRARY_NAME} PUBLIC -fPIC)
# append pybind11 bindings library to installed targets
append_local_and_parent(PLSSVM_TARGETS_TO_INSTALL ${PLSSVM_PYTHON_BINDINGS_LIBRARY_NAME})
+
+# install necessary Python files to make pip install plssvm work correctly:
+#
+# - __init__.py: PLSSVM is correctly recognized as Python package
+# - __cli__.py: PLSSVM's executables are correctly usable
+# - __install_check__.py: custom script outputting some PLSSVM build information
+include(GNUInstallDirs)
+install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/__init__.py" "${CMAKE_CURRENT_SOURCE_DIR}/__cli__.py" "${CMAKE_CURRENT_SOURCE_DIR}/__install_check__.py"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+)
diff --git a/bindings/Python/README.md b/bindings/Python/README.md
index 04d0cee14..fdb812fbd 100644
--- a/bindings/Python/README.md
+++ b/bindings/Python/README.md
@@ -1,6 +1,10 @@
# The Python3 Bindings
-- [Sklearn like API](#sklearn-like-api)
+- [Sklearn like API for sklearn.svm.SVC](#sklearn-like-api-for-sklearnsvmsvc)
+ - [Parameters](#parameters)
+ - [Attributes](#attributes)
+ - [Methods](#methods)
+- [Sklearn like API for sklearn.svm.SVR](#sklearn-like-api-for-sklearnsvmsvr)
- [Parameters](#parameters)
- [Attributes](#attributes)
- [Methods](#methods)
@@ -8,10 +12,11 @@
- [Enumerations](#enumerations)
- [Classes and submodules](#classes-and-submodules)
- [plssvm.Parameter](#plssvmparameter)
- - [plssvm.DataSet](#plssvmdataset)
- - [plssvm.CSVM](#plssvmcsvm)
- - [plssvm.openmp.CSVM, plssvm.hpx.CSVM, plssvm.stdpar.CSVM, plssvm.cuda.CSVM, plssvm.hip.CSVM, plssvm.opencl.CSVM, plssvm.sycl.CSVM, plssvm.dpcpp.CSVM, plssvm.adaptivecpp.CSVM, plssvm.kokkos.CSVM](#plssvmopenmpcsvm-plssvmhpxcsvm-plssvmcudacsvm-plssvmhipcsvm-plssvmopenclcsvm-plssvmsyclcsvm-plssvmdpcppcsvm-plssvmadaptivecppcsvm-plssvmkokkoscsvm)
- - [plssvm.Model](#plssvmmodel)
+ - [plssvm.ClassificationDataSet and plssvm.RegressionDataSet](#plssvmclassificationdataset-and-plssvmregressiondataset)
+ - [plssvm.MinMaxScaler](#plssvmminmaxscaler)
+ - [plssvm.CSVC and plssvm.CSVR](#plssvmcsvc-and-plssvmcsvr)
+ - [The backend C-SVCs and C-SVRs](#the-backend-c-svcs-and-c-svrs)
+ - [plssvm.ClassificationModel and plssvm.RegressionModel](#plssvmclassificationmodel-and-plssvmregressionmodel)
- [plssvm.Version](#plssvmversion)
- [plssvm.detail.tracking.PerformanceTracker](#plssvmdetailtrackingperformancetracker)
- [plssvm.detail.tracking.Events](#plssvmdetailtrackingevent-plssvmdetailtrackingevents)
@@ -19,13 +24,14 @@
- [Exceptions](#exceptions)
We currently support two kinds of Python3 bindings, one reflecting the API
-of [`sklearn.svm.SVC`](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html) and one extremely closely
+of [`sklearn.svm.SVC`](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html) and [
+`sklearn.svm.SVR`](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVR.html) and one extremely closely
to our C++ API.
**Note**: this page is solely meant as an API reference and overview. For examples see the
top-level [`../../examples/`](/examples) folder.
-## Sklearn like API
+## Sklearn like API for `sklearn.svm.SVC`
The following tables show the API provided
by [`sklearn.svm.SVC`](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html) and whether we currently
@@ -46,9 +52,9 @@ new `SVC`:
| :white_check_mark: | `degree : int, default=3` | Degree of the polynomial kernel function (‘poly’). Must be non-negative. Ignored by all other kernels. |
| :white_check_mark: | `gamma : {'scale', 'auto'} or real_type, default='scale'` | Kernel coefficient for various kernel functions. **Note**: the default in PLSSVM is 'auto'. |
| :white_check_mark: | `coef0 : real_type, default=0.0` | Independent term in kernel function. It is only significant in 'poly' or 'sigmoid'. |
-| :x: | `shrinking : bool, default=False` | Whether to use the shrinking heuristic. **Note**: not supported, therefore, the default is set to `False` |
+| :x: | `shrinking : bool, default=False` | Whether to use the shrinking heuristic. **Note**: not supported and makes no sense for a LS-SVM, therefore, the default is set to `False`. |
| :x: | `probability : bool, default=False` | Whether to enable probability estimates. |
-| :white_check_mark: | `tol : real_type, default=1e-3` | Tolerance for stopping criterion. **Note**: in PLSSVM, this is equal to the (relative) epsilon used in the CG algorithm and, therefore, other values may be necessary than for `sklearn.SVC` SVM implementation. |
+| :white_check_mark: | `tol : real_type, default=1e-10` | Tolerance for stopping criterion. **Note**: in PLSSVM, this is equal to the (relative) epsilon used in the CG algorithm and, therefore, other values may be necessary than for `sklearn.SVC` SVM implementation. |
| :x: | `cache_size : real_type, default=0` | Specify the size of the kernel cache (in MB). **Note**: not applicable in PLSSVM. |
| :x: | `class_weight : dict or 'balanced, default=None` | Set the parameter C of class i to class_weight[i]*C for SVC. If not given, all classes are supposed to have weight one. |
| :white_check_mark: | `verbose : bool, default=False` | Enable verbose output. **Note**: if set to True, more information will be displayed than it would be the case with LIBSVM (and, therefore, `sklearn.svm.SVC`). |
@@ -65,23 +71,23 @@ they were made available during PLSSVM's build step.
The following attributes are supported
by [`sklearn.svm.SVC`](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html):
-| implementation status | attribute | sklearn description |
-|:---------------------:|-----------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| :white_check_mark: | `class_weight_ : ndarray of shape (n_classes,)` | Multipliers of parameter C for each class. Computed based on the `class_weight` parameter. **Note**: returns all `1.0` since the `class_weight` parameter is currently not supported. |
-| :white_check_mark: | `classes_ : ndarray of shape (n_classes,)` | The classes labels. |
-| :x: | `coef_ : ndarray of shape (n_classes * (n_classes - 1) / 2, n_features)` | Weights assigned to the features when `kernel="linear"`. |
-| :x: | `dual_coef_ : ndarray of shape (n_classes -1, n_SV)` | Dual coefficients of the support vector in the decision function, multiplied by their targets. |
-| :white_check_mark: | `fit_status_ : int` | 0 if correctly fitted, 1 otherwise (will raise warning). |
-| :x: | `intercept_ : ndarray of shape (n_classes * (n_classes - 1) / 2,)` | Constants in decision function. |
-| :white_check_mark: | `n_features_in_ : int` | Number of features seen during `fit`. |
-| :x: | `feature_names_in_ : ndarray of shape (n_features_in_,)` | Names of features seen during `fit`. |
-| :white_check_mark: | `n_iter_ : ndarray of shape (n_classes * (n_classes - 1) / 2,)` for 'ovo' and ndarray of shape (n_classes,) for 'ovr' | Number of iterations run by the optimization routine to fit the model. The shape of this attribute depends on the number of models optimized which in turn depends on the number of classes and decision function. **Note**: for 'ovr' the values correspond to the number of CG iterations necessary for each right-hand side (i.e., class) to converge. |
-| :white_check_mark: | `support_ : ndarray of shape (n_SV)` | Indices of support vectors. |
-| :white_check_mark: | `support_vectors_ : ndarray of shape (n_SV, n_features)` | Support vectors. |
-| :white_check_mark: | `n_support_ : ndarray of shape (n_classes,), dtype=int32` | Number of support vectors for each class. |
-| :x: | `probA_ : ndarray of shape (n_classes * (n_classes - 1) / 2)` | Parameter learned in Platt scaling when `probability=True`. |
-| :x: | `probB_ : ndarray of shape (n_classes * (n_classes - 1) / 2)` | Parameter learned in Platt scaling when `probability=True`. |
-| :white_check_mark: | `shape_fit_ : tuple of int of shape (n_dimensions_of_X,)` | Array dimensions of training vector `X`. |
+| implementation status | attribute | sklearn description |
+|:---------------------:|------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| :white_check_mark: | `class_weight_ : ndarray of shape (n_classes,)` | Multipliers of parameter C for each class. Computed based on the `class_weight` parameter. **Note**: returns all `1.0` since the `class_weight` parameter is currently not supported. |
+| :white_check_mark: | `classes_ : ndarray of shape (n_classes,)` | The classes labels. |
+| :white_check_mark: | `coef_ : ndarray of shape (n_classes * (n_classes - 1) / 2, n_features) for ovo, ndarray of shape (n_classes, n_features) for ovr` | Weights assigned to the features when `kernel="linear"`. |
+| :x: | `dual_coef_ : ndarray of shape (n_classes -1, n_SV)` | Dual coefficients of the support vector in the decision function, multiplied by their targets. |
+| :white_check_mark: | `fit_status_ : int` | 0 if correctly fitted, 1 otherwise (will raise warning). |
+| :white_check_mark: | `intercept_ : ndarray of shape (n_classes * (n_classes - 1) / 2,) for ovo, ndarray of shape (n_classes,) for ovr` | Constants in decision function. |
+| :white_check_mark: | `n_features_in_ : int` | Number of features seen during `fit`. |
+| :white_check_mark: | `feature_names_in_ : ndarray of shape (n_features_in_,)` | Names of features seen during `fit`. Only available of the data for `fit` is provided via a Pandas DataFrame and the column names are set. |
+| :white_check_mark: | `n_iter_ : ndarray of shape (n_classes * (n_classes - 1) / 2,)` for 'ovo' and ndarray of shape (n_classes,) for 'ovr' | Number of iterations run by the optimization routine to fit the model. The shape of this attribute depends on the number of models optimized which in turn depends on the number of classes and decision function. **Note**: for 'ovr' the values correspond to the number of CG iterations necessary for each right-hand side (i.e., class) to converge. |
+| :white_check_mark: | `support_ : ndarray of shape (n_SV)` | Indices of support vectors. |
+| :white_check_mark: | `support_vectors_ : ndarray of shape (n_SV, n_features)` | Support vectors. |
+| :white_check_mark: | `n_support_ : ndarray of shape (n_classes,), dtype=int32` | Number of support vectors for each class. |
+| :x: | `probA_ : ndarray of shape (n_classes * (n_classes - 1) / 2)` | Parameter learned in Platt scaling when `probability=True`. |
+| :x: | `probB_ : ndarray of shape (n_classes * (n_classes - 1) / 2)` | Parameter learned in Platt scaling when `probability=True`. |
+| :white_check_mark: | `shape_fit_ : tuple of int of shape (n_dimensions_of_X,)` | Array dimensions of training vector `X`. |
### Methods
@@ -90,7 +96,7 @@ by [`sklearn.svm.SVC`](https://scikit-learn.org/stable/modules/generated/sklearn
| implementation status | method | sklearn description |
|:---------------------:|-----------------------------------------|------------------------------------------------------------------------------------------------|
-| :x: | `decision_function(X)` | Evaluate the decision function for the samples in X. |
+| :white_check_mark: | `decision_function(X)` | Evaluate the decision function for the samples in X. |
| :white_check_mark: | `fit(X, y[, sample_weight])` | Fit the SVM model according to the given training data. **Note**: without `sample_weight`. |
| :x: | `get_metadata_routing()` | Get metadata routing of this object. |
| :white_check_mark: | `get_params([deep])` | Get parameters for this estimator. |
@@ -113,8 +119,8 @@ More detailed description of the class methods:
- `fit(X, y[, sample_weight])`: Fit the SVM model according to the given training data.
- Parameters:
- - `X : array_like of shape (n_samples, n_features) or (n_samples, n_samples)`: Training vectors,
- where `n_samples` is the number of samples and `n_features` is the number of features.
+ - `X : array_like of shape (n_samples, n_features)`: Training vectors, where `n_samples` is the number of
+ samples and `n_features` is the number of features.
- `y : array-like of shape (n_samples,)`: Target values (class labels).
- `sample_weight : array-like of shape (n_samples,), default=None`: Per-sample weights. Rescale C per sample.
Higher weights force the classifier to put more emphasis on these points. **Note**: not supported
@@ -181,22 +187,148 @@ More detailed description of the class methods:
- Returns:
- `self : object`: The updated object.
+## Sklearn like API for `sklearn.svm.SVR`
+
+The following tables show the API provided
+by [`sklearn.svm.SVR`](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVR.html) and whether we currently
+support the respective constructor parameter, class attribute, or method.
+Note that the documentation is a verbose copy from the sklearn SVR page with some additional information added if our
+implementation differs from the sklearn implementation.
+
+### Parameters
+
+The following parameters are supported
+by [`sklearn.svm.SVR`](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVR.html) when construction a
+new `SVR`:
+
+| implementation status | parameter | sklearn description |
+|:---------------------:|--------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| :white_check_mark: | `C : real_type, default=1.0` | Regularization parameter. The strength of the regularization is inversely proportional to C. Must be strictly positive. The penalty is a squared l2 penalty. |
+| :white_check_mark: | `kernel : {'linear', 'poly', 'rbf', 'sigmoid', 'laplacian', 'chi_squared'}, default='rbf'` | Specifies the kernel type to be used in the algorithm. If none is given, 'rbf' will be used. **Note**: 'precomputed' is not supported, but 'laplacian' and 'chi_squared' are supported in addition. |
+| :white_check_mark: | `degree : int, default=3` | Degree of the polynomial kernel function (‘poly’). Must be non-negative. Ignored by all other kernels. |
+| :white_check_mark: | `gamma : {'scale', 'auto'} or real_type, default='scale'` | Kernel coefficient for various kernel functions. **Note**: the default in PLSSVM is 'auto'. |
+| :white_check_mark: | `coef0 : real_type, default=0.0` | Independent term in kernel function. It is only significant in 'poly' or 'sigmoid'. |
+| :x: | `shrinking : bool, default=False` | Whether to use the shrinking heuristic. **Note**: not supported, therefore, the default is set to `False` |
+| :white_check_mark: | `tol : real_type, default=1e-10` | Tolerance for stopping criterion. **Note**: in PLSSVM, this is equal to the (relative) epsilon used in the CG algorithm and, therefore, other values may be necessary than for `sklearn.SVC` SVM implementation. |
+| :x: | `cache_size : real_type, default=0` | Specify the size of the kernel cache (in MB). **Note**: not applicable in PLSSVM. |
+| :white_check_mark: | `verbose : bool, default=False` | Enable verbose output. **Note**: if set to True, more information will be displayed than it would be the case with LIBSVM (and, therefore, `sklearn.svm.SVC`). |
+| :white_check_mark: | `max_iter : int, default=-1` | Hard limit on iterations within solver, or -1 for no limit. **Note**: if -1 is provided, at most `#data_points - 1` many CG iterations are performed. |
+| :x: | `epsilon : real_type, default=0.1` | The epsilon-tube within which no penalty is associated in the training loss function. **Note**: not applicable to PLSSVM's regression notation. |
+
+**Note**: the `plssvm.SVR` automatically uses the optimal (in the sense of performance) backend and target platform, as
+they were made available during PLSSVM's build step.
+
+### Attributes
+
+The following attributes are supported
+by [`sklearn.svm.SVR`](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVR.html):
+
+| implementation status | attribute | sklearn description |
+|:---------------------:|-----------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| :x: | `coef_ : ndarray of shape (n_classes * (n_classes - 1) / 2, n_features)` | Weights assigned to the features when `kernel="linear"`. |
+| :x: | `dual_coef_ : ndarray of shape (n_classes -1, n_SV)` | Dual coefficients of the support vector in the decision function, multiplied by their targets. |
+| :white_check_mark: | `fit_status_ : int` | 0 if correctly fitted, 1 otherwise (will raise warning). |
+| :x: | `intercept_ : ndarray of shape (n_classes * (n_classes - 1) / 2,)` | Constants in decision function. |
+| :white_check_mark: | `n_features_in_ : int` | Number of features seen during `fit`. |
+| :x: | `feature_names_in_ : ndarray of shape (n_features_in_,)` | Names of features seen during `fit`. |
+| :white_check_mark: | `n_iter_ : ndarray of shape (n_classes * (n_classes - 1) / 2,)` for 'ovo' and ndarray of shape (n_classes,) for 'ovr' | Number of iterations run by the optimization routine to fit the model. The shape of this attribute depends on the number of models optimized which in turn depends on the number of classes and decision function. **Note**: for 'ovr' the values correspond to the number of CG iterations necessary for each right-hand side (i.e., class) to converge. |
+| :white_check_mark: | `support_ : ndarray of shape (n_SV)` | Indices of support vectors. |
+| :white_check_mark: | `support_vectors_ : ndarray of shape (n_SV, n_features)` | Support vectors. |
+| :white_check_mark: | `n_support_ : ndarray of shape (n_classes,), dtype=int32` | Number of support vectors for each class. |
+| :x: | `probA_ : ndarray of shape (n_classes * (n_classes - 1) / 2)` | Parameter learned in Platt scaling when `probability=True`. |
+| :x: | `probB_ : ndarray of shape (n_classes * (n_classes - 1) / 2)` | Parameter learned in Platt scaling when `probability=True`. |
+
+### Methods
+
+The following methods are supported
+by [`sklearn.svm.SVR`](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVR.html):
+
+| implementation status | method | sklearn description |
+|:---------------------:|-----------------------------------------|------------------------------------------------------------------------------------------------|
+| :white_check_mark: | `fit(X, y[, sample_weight])` | Fit the SVM model according to the given training data. **Note**: without `sample_weight`. |
+| :x: | `get_metadata_routing()` | Get metadata routing of this object. |
+| :white_check_mark: | `get_params([deep])` | Get parameters for this estimator. |
+| :white_check_mark: | `predict(X)` | Perform classification on samples in X. |
+| :white_check_mark: | `score(X, y[, sample_weight])` | Return the mean accuracy on the given test data and labels. **Note**: without `sample_weight`. |
+| :x: | `set_fit_request(*[, sample_weight])` | Request metadata passed to the `fit` method. |
+| :white_check_mark: | `set_params(**params)` | Set the parameters of this estimator. |
+| :x: | `set_score_request(*[, sample_weight])` | Request metadata passed to the `score` method. |
+
+More detailed description of the class methods:
+
+- `fit(X, y[, sample_weight])`: Fit the SVM model according to the given training data.
+ - Parameters:
+ - `X : array_like of shape (n_samples, n_features) or (n_samples, n_samples)`: Training vectors,
+ where `n_samples` is the number of samples and `n_features` is the number of features.
+ - `y : array-like of shape (n_samples,)`: Target values (class labels).
+ - `sample_weight : array-like of shape (n_samples,), default=None`: Per-sample weights. Rescale C per sample.
+ Higher weights force the classifier to put more emphasis on these points. **Note**: not supported
+ - Returns:
+ - `self : object`: Fitted estimator.
+
+- `get_metadata_routing()`: Get metadata routing of this object.
+ - Returns:
+ - `routing : MetadataRequest`: A MetadataRequest encapsulating routing information.
+
+- `get_params(deep=True)`: Get parameters for this estimator.
+ - Parameters:
+ - `deep : bool, default=True`: If True, will return the parameters for this estimator and contained sub-objects
+ that are estimators. **Note**: not applicable, therefore, ignored.
+ - Returns:
+ - `params : dict`: Parameter names mapped to their values.
+
+- `predict(X)`: Perform classification on samples in X.
+ - Parameters:
+ - `X : array-like of shape (n_samples, n_features)`
+ - Returns:
+ - `y_pred : ndarray of shape (n_samples,)`: Class labels for samples in X.
+
+- `score(X, y, sample_weight=None)`: Return the mean accuracy on the given test data and labels.
+ - Parameters:
+ - `X : array-like of shape (n_samples, n_features)`: Test samples.
+ - `y : array-like of shape (n_samples,) or (n_samples, n_outputs)`: True labels for X.
+ - `sample_weightarray-like of shape (n_samples,), default=None`: Sample weights.
+ - Returns:
+ - `score : float`: Mean accuracy of `self.predict(X)` w.r.t. `y`.
+
+- `set_fit_request(*, sample_weight: bool | None | str = "$UNCHANGED$") → SVC`: Request metadata passed to the fit method.
+ - Parameters:
+ - `sample_weight : str, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED`: Metadata
+ routing for `sample_weight` parameter in `fit`.
+ - Returns:
+ - `self : object`: The updated object.
+
+- `set_params(**params)`: Set the parameters of this estimator.
+ - Parameters:
+ - `**params : dict`: Estimator parameters.
+ - Returns:
+ - `self : object`: Estimator instance.
+
+- `set_score_request(*, sample_weight: bool | None | str = "$UNCHANGED$") → SVC`: Request metadata passed to the score
+ method.
+ - Parameters:
+ - `sample_weightstr, True, False, or None, default=sklearn.utils.metadata_routing.UNCHANGED`: Metadata routing
+ for `sample_weight` parameter in `score`.
+ - Returns:
+ - `self : object`: The updated object.
+
## Bindings close to our C++ API
### Enumerations
The following table lists all PLSSVM enumerations exposed on the Python side:
-| enumeration | values | description |
-|------------------------|-------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `TargetPlatform` | `AUTOMATIC`, `CPU`, `GPU_NVIDIA`, `GPU_AMD`, `GPU_INTEL` | The different supported target platforms (default: `AUTOMATIC`). If `AUTOMATIC` is provided, checks for available devices in the following order: NVIDIA GPUs -> AMD GPUs -> Intel GPUs -> CPUs. |
-| `SolverType` | `AUTOMATIC`, `CG_EXPLICIT`, `CG_IMPLICIT` | The different supported solver types (default: `AUTOMATIC`). If `AUTOMATIC` is provided, the used solver types depends on the available device and system memory. |
-| `KernelFunctionType` | `LINEAR`, `POLYNOMIAL`, `RBF`, `SIGMOID`, `LAPLACIAN`, `CHI_SQUARED` | The different supported kernel functions (default: `LINEAR`). |
-| `FileFormatType` | `LIBSVM`, `ARFF` | The different supported file format types (default: `LIBSVM`). |
-| `GammaCoefficientType` | `AUTOMATIC`, `SCALE` | The different modes for the dynamic gamma calculation (default: `AUTOMATIC`). |
-| `ClassificationType` | `OAA`, `OAO` | The different supported multi-class classification strategies (default: `LIBSVM`). |
-| `BackendType` | `AUTOMATIC`, `OPENMP`, `HPX`, `CUDA`, `HIP`, `OPENCL`, `SYCL`, `KOKKOS` | The different supported backends (default: `AUTOMATIC`). If `AUTOMATIC` is provided, the selected backend depends on the used target platform. |
-| `VerbosityLevel` | `QUIET`, `LIBSVM`, `TIMING`, `FULL` | The different supported log levels (default: `FULL`). `QUIET` means no output, `LIBSVM` output that is as conformant as possible with LIBSVM's output, `TIMING` all timing related outputs, and `FULL` everything. Can be combined via bit-wise operations. |
+| enumeration | values | description |
+|------------------------|----------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `TargetPlatform` | `AUTOMATIC`, `CPU`, `GPU_NVIDIA`, `GPU_AMD`, `GPU_INTEL` | The different supported target platforms (default: `AUTOMATIC`). If `AUTOMATIC` is provided, checks for available devices in the following order: NVIDIA GPUs -> AMD GPUs -> Intel GPUs -> CPUs. |
+| `SolverType` | `AUTOMATIC`, `CG_EXPLICIT`, `CG_IMPLICIT` | The different supported solver types (default: `AUTOMATIC`). If `AUTOMATIC` is provided, the used solver types depends on the available device and system memory. |
+| `KernelFunctionType` | `LINEAR`, `POLYNOMIAL`, `RBF`, `SIGMOID`, `LAPLACIAN`, `CHI_SQUARED` | The different supported kernel functions (default: `RBF`). |
+| `FileFormatType` | `LIBSVM`, `ARFF` | The different supported file format types (default: `LIBSVM`). |
+| `GammaCoefficientType` | `AUTOMATIC`, `SCALE` | The different modes for the dynamic gamma calculation (default: `AUTOMATIC`). |
+| `ClassificationType` | `OAA`, `OAO` | The different supported multi-class classification strategies (default: `LIBSVM`). |
+| `BackendType` | `AUTOMATIC`, `OPENMP`, `HPX`, `STDPAR` `CUDA`, `HIP`, `OPENCL`, `SYCL`, `KOKKOS` | The different supported backends (default: `AUTOMATIC`). If `AUTOMATIC` is provided, the selected backend depends on the used target platform. |
+| `VerbosityLevel` | `QUIET`, `LIBSVM`, `TIMING`, `FULL` | The different supported log levels (default: `FULL`). `QUIET` means no output, `LIBSVM` output that is as conformant as possible with LIBSVM's output, `TIMING` all timing related outputs, and `FULL` everything. Can be combined via bit-wise operations. |
+| `SVMType` | `CSVC`, `CSVR`, | The different supported C-SVM types. |
If a SYCL implementation is available, additional enumerations are available:
@@ -225,15 +357,15 @@ The following tables list all PLSSVM classes exposed on the Python side:
The parameter class encapsulates all necessary hyperparameters needed to fit an SVM.
-| constructors | description |
-|---------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
-| `Parameter()` | Default construct a parameter object. |
-| `Parameter(kernel_type, degree, gamma, coef0, cost)` | Construct a parameter object by explicitly providing each hyper-parameter value. |
-| `Parameter([kernel_type=KernelFunctionType.LINEAR, degree=3, gamma=*1/#features*, coef=0.0, cost=1.0])` | Construct a parameter object with the provided named parameters. |
+| constructors | description |
+|------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
+| `Parameter()` | Default construct a parameter object. |
+| `Parameter(kernel_type, degree, gamma, coef0, cost)` | Construct a parameter object by explicitly providing each hyper-parameter value. |
+| `Parameter([kernel_type=KernelFunctionType.RBF, degree=3, gamma=*1/#features*, coef=0.0, cost=1.0])` | Construct a parameter object with the provided named parameters. |
| attributes | description |
|------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `kernel_type : KernelFunctionType` | The used kernel function type (default: `LINEAR`). |
+| `kernel_type : KernelFunctionType` | The used kernel function type (default: `RBF`). |
| `degree : int` | The used degree in the polynomial kernel function (default: `3`). |
| `gamma : gamma_type` | The used gamma in the different kernel functions (default: `AUTOMATIC`). The `gamma_type` is a `std::variant 0 |
| `rbf_kernel_function(x, y, gamma)` | Calculate the radial basis function kernel function of two vectors: exp(-gamma*\|x-y\|^2), with gamma > 0 |
| `sigmoid_kernel_function(x, y, gamma, coef0)` | Calculate the sigmoid kernel function of two vectors: tanh(gamma*x'*y), with gamma > 0 |
-| `laplacian_kernel_function(x, y, gamma)` | Calculate the sigmoid kernel function of two vectors: exp(-gamma*\|x-y\|_1), with gamma > 0 |
-| `chi_squared_kernel_function(x, y, gamma)` | Calculate the sigmoid kernel function of two vectors: exp(-gamma*sum_i((x[i] - y[i])^2) / (x[i] + y[i])), with gamma > 0 |
+| `laplacian_kernel_function(x, y, gamma)` | Calculate the laplacian kernel function of two vectors: exp(-gamma*\|x-y\|_1), with gamma > 0 |
+| `chi_squared_kernel_function(x, y, gamma)` | Calculate the chi-squared kernel function of two vectors: exp(-gamma*sum_i((x[i] - y[i])^2) / (x[i] + y[i])), with gamma > 0 |
| `kernel_function(x, y, params)` | Calculate the kernel function provided in params with the additional parameters also provided in params. |
| `classification_type_to_full_string(classification)` | Returns the full string of the provided classification type, i.e., "one vs. all" and "one vs. one" instead of only "oaa" or "oao". |
| `calculate_number_of_classifiers(classification, num_classes)` | Return the number of necessary classifiers in a multi-class setting with the provided classification strategy and number of different classes. |
@@ -520,6 +660,10 @@ The following table lists all free functions in PLSSVM directly callable via `pl
| `equivalent(params1, params2)` | Check whether the two parameter classes are equivalent, i.e., the parameters for **the current kernel function** are identical. E.g., for the rbf kernel function the gamma values must be identical, but the degree values can be different, since degree isn't used in the rbf kernel function. |
| `get_gamma_string(gamma)` | Returns the gamma string based on the active member in the `gamma_type` `std::variant`. |
| `calculate_gamma_value(gamma, matrix)` | Calculate the value of gamma based on the active member in the `gamma_type` `std::variant`. |
+| `list_available_svm_types()` | List all available SVM types (C-SVC or C-SVR). |
+| `svm_type_to_task_name(svm_type)` | Returns the task name (classification or regression) associated with the provided SVM type. |
+| `svm_type_from_model_file(model_file)` | Returns the SVM type used to create the provided model file. |
+| `regression_report(y_true, y_pred, [force_finite, output_dict])` | Returns a regression report similar to sklearn's [`metrics.classification_report`](https://scikit-learn.org/0.15/modules/generated/sklearn.metrics.classification_report.html) for the regression task. If `output_dict` is , returns a Python dictionary, otherwise directly returns a string. |
If a SYCL implementation is available, additional free functions are available:
@@ -537,19 +681,22 @@ If a stdpar implementation is available, additional free functions are available
The PLSSVM Python3 bindings define a few new exception types:
-| exception | description |
-|------------------------------|------------------------------------------------------------------------------------------------------------------------|
-| `PLSSVMError` | Base class of all other PLSSVM specific exceptions. |
-| `InvalidParameterError` | If an invalid hyper-parameter has been provided in the `plssvm.Parameter` class. |
-| `FileReaderError` | If something went wrong while reading the requested file (possibly using memory mapped IO.) |
-| `DataSetError` | If something related to the `plssvm.DataSet` class(es) went wrong, e.g., wrong arguments provided to the constructors. |
-| `FileNotFoundError` | If the requested data or model file couldn't be found. |
-| `InvalidFileFormatError` | If the requested data or model file are invalid, e.g., wrong LIBSVM model header. |
-| `UnsupportedBackendError` | If an unsupported backend has been requested. |
-| `UnsupportedKernelTypeError` | If an unsupported target platform has been requested. |
-| `GPUDevicePtrError` | If something went wrong in one of the backend's GPU device pointers. **Note**: shouldn't occur in user code. |
-| `MatrixError` | If something went wrong in the internal matrix class. **Note**: shouldn't occur in user code. |
-| `KernelLaunchResourcesError` | If something went wrong during a kernel launch due to insufficient ressources. |
-| `ClassificationReportError` | If something in the classification report went wrong. **Note**: shouldn't occur in user code. |
+| exception | description |
+|------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `PLSSVMError` | Base class of all other PLSSVM specific exceptions. |
+| `InvalidParameterError` | If an invalid hyper-parameter has been provided in the `plssvm.Parameter` class. |
+| `FileReaderError` | If something went wrong while reading the requested file (possibly using memory mapped IO.) |
+| `DataSetError` | If something related to the `plssvm.ClassificationDataSet`/`plssvm.RegressionDataSet` class(es) went wrong, e.g., wrong arguments provided to the constructors. |
+| `MinMaxScalerError` | If something related to the `plssvm.MinMaxScaler` went wrong, e.g., scaling wasn't successfully. |
+| `FileNotFoundError` | If the requested data or model file couldn't be found. |
+| `InvalidFileFormatError` | If the requested data or model file are invalid, e.g., wrong LIBSVM model header. |
+| `UnsupportedBackendError` | If an unsupported backend has been requested. |
+| `UnsupportedKernelTypeError` | If an unsupported target platform has been requested. |
+| `GPUDevicePtrError` | If something went wrong in one of the backend's GPU device pointers. **Note**: shouldn't occur in user code. |
+| `MatrixError` | If something went wrong in the internal matrix class. **Note**: shouldn't occur in user code. |
+| `KernelLaunchResourcesError` | If something went wrong during a kernel launch due to insufficient ressources. |
+| `ClassificationReportError` | If something in the classification report went wrong. **Note**: shouldn't occur in user code. |
+| `RegressionReportError` | If something in the regression report went wrong. **Note**: shouldn't occur in user code. |
+| `EnvironmentError` | If something during the special environment initialization or finalization went wrong. |
Depending on the available backends, additional `BackendError`s are also available (e.g., `plssvm.cuda.BackendError`).
diff --git a/bindings/Python/__cli__.py b/bindings/Python/__cli__.py
new file mode 100644
index 000000000..f64ec44d3
--- /dev/null
+++ b/bindings/Python/__cli__.py
@@ -0,0 +1,21 @@
+import sys
+import subprocess
+from pathlib import Path
+
+
+# support for plssvm-train including command line arguments
+def train():
+ exe_path = Path(__file__).parent / "plssvm-train"
+ subprocess.run([str(exe_path)] + sys.argv[1:])
+
+
+# support for plssvm-predict including command line arguments
+def predict():
+ exe_path = Path(__file__).parent / "plssvm-predict"
+ subprocess.run([str(exe_path)] + sys.argv[1:])
+
+
+# support for plssvm-scale including command line arguments
+def scale():
+ exe_path = Path(__file__).parent / "plssvm-scale"
+ subprocess.run([str(exe_path)] + sys.argv[1:])
diff --git a/bindings/Python/__init__.py b/bindings/Python/__init__.py
new file mode 100644
index 000000000..5976a4f2a
--- /dev/null
+++ b/bindings/Python/__init__.py
@@ -0,0 +1,8 @@
+# import the plssvm module explicitly
+from . import plssvm
+# export everything
+from .plssvm import * # noqa: F405
+
+# explicitly set the module level attributes
+__doc__ = plssvm.__doc__
+__version__ = plssvm.__version__
diff --git a/bindings/Python/__install_check__.py b/bindings/Python/__install_check__.py
new file mode 100644
index 000000000..eedfd897f
--- /dev/null
+++ b/bindings/Python/__install_check__.py
@@ -0,0 +1,33 @@
+import plssvm
+
+# print information regarding the current installation after an installation via pip
+def check():
+ print("{} ({})".format(plssvm.__doc__, plssvm.__version__))
+ print()
+
+ print("Copyright(C) 2018-today The PLSSVM project - All Rights Reserved")
+ print("This is free software distributed under the MIT license.")
+ print()
+
+ print("Available target platforms: {}".format(', '.join(str(target) for target in plssvm.list_available_target_platforms())))
+ print("Default target platform: {}\n".format(str(plssvm.determine_default_target_platform())))
+
+ print("Available backends: {}".format(', '.join(str(backend) for backend in plssvm.list_available_backends())))
+ for target in plssvm.list_available_target_platforms():
+ if target == plssvm.TargetPlatform.AUTOMATIC:
+ continue
+ try:
+ backend = plssvm.determine_default_backend(available_target_platforms=[target])
+ print("Default backend for target platform {}: {}".format(str(target), str(backend)))
+ except Exception:
+ pass
+ print()
+
+ if plssvm.BackendType.SYCL in plssvm.list_available_backends():
+ print("Available SYCL implementations: {}".format(', '.join(str(impl) for impl in plssvm.sycl.list_available_sycl_implementations())))
+ print()
+
+ print()
+ print("Repository: https://github.com/SC-SGS/PLSSVM.git")
+ print("Documentation: https://sc-sgs.github.io/PLSSVM/")
+ print("Issues: https://github.com/SC-SGS/PLSSVM/issues")
\ No newline at end of file
diff --git a/bindings/Python/backend_types.cpp b/bindings/Python/backend_types.cpp
index 5664cf360..7997991b9 100644
--- a/bindings/Python/backend_types.cpp
+++ b/bindings/Python/backend_types.cpp
@@ -18,7 +18,7 @@ namespace py = pybind11;
void init_backend_types(py::module_ &m) {
// bind enum class
- py::enum_(m, "BackendType")
+ py::enum_(m, "BackendType", "Enum class for all possible backend types, all different SYCL implementations have the same backend type \"sycl\".")
.value("AUTOMATIC", plssvm::backend_type::automatic, "the default backend; depends on the specified target platform")
.value("OPENMP", plssvm::backend_type::openmp, "OpenMP to target CPUs only (currently no OpenMP target offloading support)")
.value("HPX", plssvm::backend_type::hpx, "HPX to target CPUs only (currently no GPU executor support)")
diff --git a/bindings/Python/backends/adaptivecpp_csvm.cpp b/bindings/Python/backends/adaptivecpp_csvm.cpp
index bf81b11ae..a02dd0b65 100644
--- a/bindings/Python/backends/adaptivecpp_csvm.cpp
+++ b/bindings/Python/backends/adaptivecpp_csvm.cpp
@@ -6,59 +6,104 @@
* See the LICENSE.md file in the project root for full license information.
*/
+#include "plssvm/backend_types.hpp" // plssvm::adaptivecpp::backend_csvm_type_t
#include "plssvm/backends/SYCL/AdaptiveCpp/csvm.hpp" // plssvm::adaptivecpp::csvm
#include "plssvm/backends/SYCL/exceptions.hpp" // plssvm::adaptivecpp::backend_exception
#include "plssvm/backends/SYCL/kernel_invocation_types.hpp" // plssvm::sycl::kernel_invocation_type
-#include "plssvm/csvm.hpp" // plssvm::csvm
#include "plssvm/exceptions/exceptions.hpp" // plssvm::exception
-#include "plssvm/parameter.hpp" // plssvm::parameter, plssvm::sycl_kernel_invocation_type
+#include "plssvm/parameter.hpp" // plssvm::parameter
+#include "plssvm/svm/csvc.hpp" // plssvm::csvc
+#include "plssvm/svm/csvm.hpp" // plssvm::csvm
+#include "plssvm/svm/csvr.hpp" // plssvm::csvr
#include "plssvm/target_platforms.hpp" // plssvm::target_platform
-#include "bindings/Python/utility.hpp" // check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception
+#include "bindings/Python/utility.hpp" // plssvm::bindings::python::util::{check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception}
-#include "pybind11/pybind11.h" // py::module_, py::class_, py::init
-#include "pybind11/stl.h" // support for STL types
+#include "fmt/format.h" // fmt::format
+#include "pybind11/pybind11.h" // py::module_, py::class_, py::init, py::exception
+#include "pybind11/pytypes.h" // py::kwargs
#include // std::make_unique
+#include // std::string
namespace py = pybind11;
-py::module_ init_adaptivecpp_csvm(py::module_ &m, const py::exception &base_exception) {
- // use its own submodule for the AdaptiveCpp CSVM bindings
- py::module_ adaptivecpp_module = m.def_submodule("adaptivecpp", "a module containing all AdaptiveCpp SYCL backend specific functionality");
+namespace {
+
+template
+void bind_adaptivecpp_csvms(py::module_ &m, const std::string &csvm_name) {
+ using backend_csvm_type = plssvm::adaptivecpp::backend_csvm_type_t;
- // bind the CSVM using the AdaptiveCpp backend
- py::class_(adaptivecpp_module, "CSVM")
- .def(py::init<>(), "create an SVM with the automatic target platform and default parameter object")
- .def(py::init(), "create an SVM with the automatic target platform and provided parameter object")
- .def(py::init(), "create an SVM with the provided target platform and default parameter object")
- .def(py::init(), "create an SVM with the provided target platform and parameter object")
+ // assemble docstrings
+ const std::string class_docstring{ fmt::format("A {} using the AdaptiveCpp SYCL backend.", csvm_name) };
+ const std::string param_docstring{ fmt::format("create an AdaptiveCpp SYCL {} with the provided parameters and optional SYCL specific keyword arguments", csvm_name) };
+ const std::string target_param_docstring{ fmt::format("create an AdaptiveCpp SYCL {} with the provided target platform, parameters, and optional SYCL specific keyword arguments", csvm_name) };
+ const std::string kwargs_docstring{ fmt::format("create an AdaptiveCpp SYCL {} with the provided keyword arguments (including optional SYCL specific keyword arguments)", csvm_name) };
+ const std::string target_kwargs_docstring{ fmt::format("create an AdaptiveCpp SYCL {} with the provided target platform and keyword arguments (including optional SYCL specific keyword arguments)", csvm_name) };
+
+ py::class_(m, csvm_name.c_str(), class_docstring.c_str())
+ .def(py::init([](const plssvm::parameter params, const py::kwargs &args) {
+ // check for valid keys
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "sycl_kernel_invocation_type" });
+ // set SYCL kernel invocation type
+ const plssvm::sycl::kernel_invocation_type invocation = args.contains("sycl_kernel_invocation_type") ? args["sycl_kernel_invocation_type"].cast() : plssvm::sycl::kernel_invocation_type::automatic;
+ // create C-SVM with the default target platform
+ return std::make_unique(params, plssvm::sycl_kernel_invocation_type = invocation);
+ }),
+ param_docstring.c_str())
+ .def(py::init([](const plssvm::target_platform target, const plssvm::parameter params, const py::kwargs &args) {
+ // check for valid keys
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "sycl_kernel_invocation_type" });
+ // set SYCL kernel invocation type
+ const plssvm::sycl::kernel_invocation_type invocation = args.contains("sycl_kernel_invocation_type") ? args["sycl_kernel_invocation_type"].cast() : plssvm::sycl::kernel_invocation_type::automatic;
+ // create C-SVM with the default target platform
+ return std::make_unique(target, params, plssvm::sycl_kernel_invocation_type = invocation);
+ }),
+ target_param_docstring.c_str())
.def(py::init([](const py::kwargs &args) {
// check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost", "sycl_kernel_invocation_type" });
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost", "sycl_kernel_invocation_type" });
// if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
// set SYCL kernel invocation type
- const plssvm::sycl::kernel_invocation_type invoc = args.contains("sycl_kernel_invocation_type") ? args["sycl_kernel_invocation_type"].cast() : plssvm::sycl::kernel_invocation_type::automatic;
- // create CSVM with the default target platform
- return std::make_unique(params, plssvm::sycl_kernel_invocation_type = invoc);
+ const plssvm::sycl::kernel_invocation_type invocation = args.contains("sycl_kernel_invocation_type") ? args["sycl_kernel_invocation_type"].cast() : plssvm::sycl::kernel_invocation_type::automatic;
+ // create C-SVM with the default target platform
+ return std::make_unique(params, plssvm::sycl_kernel_invocation_type = invocation);
}),
- "create an SVM with the default target platform and keyword arguments")
+ kwargs_docstring.c_str())
.def(py::init([](const plssvm::target_platform target, const py::kwargs &args) {
// check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost", "sycl_kernel_invocation_type" });
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost", "sycl_kernel_invocation_type" });
// if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
// set SYCL kernel invocation type
- const plssvm::sycl::kernel_invocation_type invoc = args.contains("sycl_kernel_invocation_type") ? args["sycl_kernel_invocation_type"].cast() : plssvm::sycl::kernel_invocation_type::automatic;
- // create CSVM with the default target platform
- return std::make_unique(target, params, plssvm::sycl_kernel_invocation_type = invoc);
+ const plssvm::sycl::kernel_invocation_type invocation = args.contains("sycl_kernel_invocation_type") ? args["sycl_kernel_invocation_type"].cast() : plssvm::sycl::kernel_invocation_type::automatic;
+ // create C-SVM with the provided target platform
+ return std::make_unique(target, params, plssvm::sycl_kernel_invocation_type = invocation);
}),
- "create an SVM with the provided target platform and keyword arguments")
- .def("get_kernel_invocation_type", &plssvm::adaptivecpp::csvm::get_kernel_invocation_type, "get the kernel invocation type used in this SYCL SVM");
+ target_kwargs_docstring.c_str())
+ .def("get_kernel_invocation_type", &plssvm::adaptivecpp::csvm::get_kernel_invocation_type, "get the kernel invocation type used in this SYCL C-SVM")
+ .def("__repr__", [csvm_name](const backend_csvm_type &self) {
+ return fmt::format("", csvm_name, self.num_available_devices(), self.get_kernel_invocation_type());
+ });
+}
+
+} // namespace
+
+py::module_ init_adaptivecpp_csvm(py::module_ &m, const py::exception &base_exception) {
+ // use its own submodule for the AdaptiveCpp C-SVM bindings
+ py::module_ adaptivecpp_module = m.def_submodule("adaptivecpp", "a module containing all AdaptiveCpp backend specific functionality");
+ const py::module_ adaptivecpp_pure_virtual_module = adaptivecpp_module.def_submodule("__pure_virtual", "a module containing all pure-virtual AdaptiveCpp backend specific functionality");
+
+ // bind the pure-virtual base AdaptiveCpp C-SVM
+ [[maybe_unused]] const py::class_ virtual_base_adaptivecpp_csvm(adaptivecpp_pure_virtual_module, "__pure_virtual_adaptivecpp_base_CSVM");
+
+ // bind the specific AdaptiveCpp C-SVC and C-SVR classes
+ bind_adaptivecpp_csvms(adaptivecpp_module, "CSVC");
+ bind_adaptivecpp_csvms(adaptivecpp_module, "CSVR");
// register AdaptiveCpp backend specific exceptions
- register_py_exception(adaptivecpp_module, "BackendError", base_exception);
+ plssvm::bindings::python::util::register_py_exception(adaptivecpp_module, "BackendError", base_exception);
return adaptivecpp_module;
}
diff --git a/bindings/Python/backends/cuda_csvm.cpp b/bindings/Python/backends/cuda_csvm.cpp
index adc9ce306..cb7867d20 100644
--- a/bindings/Python/backends/cuda_csvm.cpp
+++ b/bindings/Python/backends/cuda_csvm.cpp
@@ -6,51 +6,80 @@
* See the LICENSE.md file in the project root for full license information.
*/
+#include "plssvm/backend_types.hpp" // plssvm::cuda::backend_csvm_type_t
#include "plssvm/backends/CUDA/csvm.hpp" // plssvm::cuda::csvm
#include "plssvm/backends/CUDA/exceptions.hpp" // plssvm::cuda::backend_exception
-#include "plssvm/csvm.hpp" // plssvm::csvm
#include "plssvm/exceptions/exceptions.hpp" // plssvm::exception
#include "plssvm/parameter.hpp" // plssvm::parameter
+#include "plssvm/svm/csvc.hpp" // plssvm::csvc
+#include "plssvm/svm/csvm.hpp" // plssvm::csvm
+#include "plssvm/svm/csvr.hpp" // plssvm::csvr
#include "plssvm/target_platforms.hpp" // plssvm::target_platform
-#include "bindings/Python/utility.hpp" // check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception
+#include "bindings/Python/utility.hpp" // plssvm::bindings::python::util::{check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception}
-#include "pybind11/pybind11.h" // py::module_, py::class_, py::init
-#include "pybind11/stl.h" // support for STL types
+#include "fmt/format.h" // fmt::format
+#include "pybind11/pybind11.h" // py::module_, py::class_, py::init, py::exception
+#include "pybind11/pytypes.h" // py::kwargs
#include // std::make_unique
+#include // std::string
namespace py = pybind11;
-void init_cuda_csvm(py::module_ &m, const py::exception &base_exception) {
- // use its own submodule for the CUDA CSVM bindings
- py::module_ cuda_module = m.def_submodule("cuda", "a module containing all CUDA backend specific functionality");
+namespace {
+
+template
+void bind_cuda_csvms(py::module_ &m, const std::string &csvm_name) {
+ using backend_csvm_type = plssvm::cuda::backend_csvm_type_t;
- // bind the CSVM using the CUDA backend
- py::class_(cuda_module, "CSVM")
- .def(py::init<>(), "create an SVM with the automatic target platform and default parameter object")
- .def(py::init(), "create an SVM with the automatic target platform and provided parameter object")
- .def(py::init(), "create an SVM with the provided target platform and default parameter object")
- .def(py::init(), "create an SVM with the provided target platform and parameter object")
+ // assemble docstrings
+ const std::string class_docstring{ fmt::format("A {} using the CUDA backend.", csvm_name) };
+ const std::string param_docstring{ fmt::format("create a CUDA {} with the provided parameters", csvm_name) };
+ const std::string target_param_docstring{ fmt::format("create a CUDA {} with the provided target platform and parameters", csvm_name) };
+ const std::string kwargs_docstring{ fmt::format("create a CUDA {} with the provided keyword arguments", csvm_name) };
+ const std::string target_kwargs_docstring{ fmt::format("create a CUDA {} with the provided target platform and keyword arguments", csvm_name) };
+
+ py::class_(m, csvm_name.c_str(), class_docstring.c_str())
+ .def(py::init(), param_docstring.c_str())
+ .def(py::init(), target_param_docstring.c_str())
.def(py::init([](const py::kwargs &args) {
// check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
// if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
- // create CSVM with the default target platform
- return std::make_unique(params);
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
+ // create C-SVM with the default target platform
+ return std::make_unique(params);
}),
- "create an SVM with the default target platform and keyword arguments")
+ kwargs_docstring.c_str())
.def(py::init([](const plssvm::target_platform target, const py::kwargs &args) {
// check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
// if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
- // create CSVM with the provided target platform
- return std::make_unique(target, params);
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
+ // create C-SVM with the provided target platform
+ return std::make_unique(target, params);
}),
- "create an SVM with the provided target platform and keyword arguments");
+ target_kwargs_docstring.c_str())
+ .def("__repr__", [csvm_name](const backend_csvm_type &self) {
+ return fmt::format("", csvm_name, self.num_available_devices());
+ });
+}
+
+} // namespace
+
+void init_cuda_csvm(py::module_ &m, const py::exception &base_exception) {
+ // use its own submodule for the CUDA C-SVM bindings
+ py::module_ cuda_module = m.def_submodule("cuda", "a module containing all CUDA backend specific functionality");
+ const py::module_ cuda_pure_virtual_module = cuda_module.def_submodule("__pure_virtual", "a module containing all pure-virtual CUDA backend specific functionality");
+
+ // bind the pure-virtual base CUDA C-SVM
+ [[maybe_unused]] const py::class_ virtual_base_cuda_csvm(cuda_pure_virtual_module, "__pure_virtual_cuda_base_CSVM");
+
+ // bind the specific CUDA C-SVC and C-SVR classes
+ bind_cuda_csvms(cuda_module, "CSVC");
+ bind_cuda_csvms(cuda_module, "CSVR");
// register CUDA backend specific exceptions
- register_py_exception(cuda_module, "BackendError", base_exception);
+ plssvm::bindings::python::util::register_py_exception(cuda_module, "BackendError", base_exception);
}
diff --git a/bindings/Python/backends/dpcpp_csvm.cpp b/bindings/Python/backends/dpcpp_csvm.cpp
index 906cb5979..b895bdeef 100644
--- a/bindings/Python/backends/dpcpp_csvm.cpp
+++ b/bindings/Python/backends/dpcpp_csvm.cpp
@@ -6,59 +6,104 @@
* See the LICENSE.md file in the project root for full license information.
*/
+#include "plssvm/backend_types.hpp" // plssvm::dpcpp::backend_csvm_type_t
#include "plssvm/backends/SYCL/DPCPP/csvm.hpp" // plssvm::dpcpp::csvm
#include "plssvm/backends/SYCL/exceptions.hpp" // plssvm::dpcpp::backend_exception
#include "plssvm/backends/SYCL/kernel_invocation_types.hpp" // plssvm::sycl::kernel_invocation_type
-#include "plssvm/csvm.hpp" // plssvm::csvm
#include "plssvm/exceptions/exceptions.hpp" // plssvm::exception
-#include "plssvm/parameter.hpp" // plssvm::parameter, plssvm::sycl_kernel_invocation_type
+#include "plssvm/parameter.hpp" // plssvm::parameter
+#include "plssvm/svm/csvc.hpp" // plssvm::csvc
+#include "plssvm/svm/csvm.hpp" // plssvm::csvm
+#include "plssvm/svm/csvr.hpp" // plssvm::csvr
#include "plssvm/target_platforms.hpp" // plssvm::target_platform
-#include "bindings/Python/utility.hpp" // check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception
+#include "bindings/Python/utility.hpp" // plssvm::bindings::python::util::{check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception}
-#include "pybind11/pybind11.h" // py::module_, py::class_, py::init
-#include "pybind11/stl.h" // support for STL types
+#include "fmt/format.h" // fmt::format
+#include "pybind11/pybind11.h" // py::module_, py::class_, py::init, py::exception
+#include "pybind11/pytypes.h" // py::kwargs
#include // std::make_unique
+#include // std::string
namespace py = pybind11;
-py::module_ init_dpcpp_csvm(py::module_ &m, const py::exception &base_exception) {
- // use its own submodule for the DPCPP CSVM bindings
- py::module_ dpcpp_module = m.def_submodule("dpcpp", "a module containing all DPC++ SYCL backend specific functionality");
+namespace {
+
+template
+void bind_dpcpp_csvms(py::module_ &m, const std::string &csvm_name) {
+ using backend_csvm_type = plssvm::dpcpp::backend_csvm_type_t;
- // bind the CSVM using the DPCPP backend
- py::class_(dpcpp_module, "CSVM")
- .def(py::init<>(), "create an SVM with the automatic target platform and default parameter object")
- .def(py::init(), "create an SVM with the automatic target platform and provided parameter object")
- .def(py::init(), "create an SVM with the provided target platform and default parameter object")
- .def(py::init(), "create an SVM with the provided target platform and parameter object")
+ // assemble docstrings
+ const std::string class_docstring{ fmt::format("A {} using the DPC++ SYCL backend.", csvm_name) };
+ const std::string param_docstring{ fmt::format("create a DPC++ SYCL {} with the provided parameters and optional SYCL specific keyword arguments", csvm_name) };
+ const std::string target_param_docstring{ fmt::format("create a DPC++ SYCL {} with the provided target platform, parameters, and optional SYCL specific keyword arguments", csvm_name) };
+ const std::string kwargs_docstring{ fmt::format("create a DPC++ SYCL {} with the provided keyword arguments (including optional SYCL specific keyword arguments)", csvm_name) };
+ const std::string target_kwargs_docstring{ fmt::format("create a DPC++ SYCL {} with the provided target platform and keyword arguments (including optional SYCL specific keyword arguments)", csvm_name) };
+
+ py::class_(m, csvm_name.c_str(), class_docstring.c_str())
+ .def(py::init([](const plssvm::parameter params, const py::kwargs &args) {
+ // check for valid keys
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "sycl_kernel_invocation_type" });
+ // set SYCL kernel invocation type
+ const plssvm::sycl::kernel_invocation_type invocation = args.contains("sycl_kernel_invocation_type") ? args["sycl_kernel_invocation_type"].cast() : plssvm::sycl::kernel_invocation_type::automatic;
+ // create C-SVM with the default target platform
+ return std::make_unique(params, plssvm::sycl_kernel_invocation_type = invocation);
+ }),
+ param_docstring.c_str())
+ .def(py::init([](const plssvm::target_platform target, const plssvm::parameter params, const py::kwargs &args) {
+ // check for valid keys
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "sycl_kernel_invocation_type" });
+ // set SYCL kernel invocation type
+ const plssvm::sycl::kernel_invocation_type invocation = args.contains("sycl_kernel_invocation_type") ? args["sycl_kernel_invocation_type"].cast() : plssvm::sycl::kernel_invocation_type::automatic;
+ // create C-SVM with the default target platform
+ return std::make_unique(target, params, plssvm::sycl_kernel_invocation_type = invocation);
+ }),
+ target_param_docstring.c_str())
.def(py::init([](const py::kwargs &args) {
// check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost", "sycl_kernel_invocation_type" });
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost", "sycl_kernel_invocation_type" });
// if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
// set SYCL kernel invocation type
- const plssvm::sycl::kernel_invocation_type invoc = args.contains("sycl_kernel_invocation_type") ? args["sycl_kernel_invocation_type"].cast() : plssvm::sycl::kernel_invocation_type::automatic;
- // create CSVM with the default target platform
- return std::make_unique(params, plssvm::sycl_kernel_invocation_type = invoc);
+ const plssvm::sycl::kernel_invocation_type invocation = args.contains("sycl_kernel_invocation_type") ? args["sycl_kernel_invocation_type"].cast() : plssvm::sycl::kernel_invocation_type::automatic;
+ // create C-SVM with the default target platform
+ return std::make_unique(params, plssvm::sycl_kernel_invocation_type = invocation);
}),
- "create an SVM with the default target platform and keyword arguments")
+ kwargs_docstring.c_str())
.def(py::init([](const plssvm::target_platform target, const py::kwargs &args) {
// check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost", "sycl_kernel_invocation_type" });
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost", "sycl_kernel_invocation_type" });
// if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
// set SYCL kernel invocation type
- const plssvm::sycl::kernel_invocation_type invoc = args.contains("sycl_kernel_invocation_type") ? args["sycl_kernel_invocation_type"].cast() : plssvm::sycl::kernel_invocation_type::automatic;
- // create CSVM with the default target platform
- return std::make_unique(target, params, plssvm::sycl_kernel_invocation_type = invoc);
+ const plssvm::sycl::kernel_invocation_type invocation = args.contains("sycl_kernel_invocation_type") ? args["sycl_kernel_invocation_type"].cast() : plssvm::sycl::kernel_invocation_type::automatic;
+ // create C-SVM with the provided target platform
+ return std::make_unique(target, params, plssvm::sycl_kernel_invocation_type = invocation);
}),
- "create an SVM with the provided target platform and keyword arguments")
- .def("get_kernel_invocation_type", &plssvm::dpcpp::csvm::get_kernel_invocation_type, "get the kernel invocation type used in this SYCL SVM");
+ target_kwargs_docstring.c_str())
+ .def("get_kernel_invocation_type", &plssvm::dpcpp::csvm::get_kernel_invocation_type, "get the kernel invocation type used in this SYCL C-SVM")
+ .def("__repr__", [csvm_name](const backend_csvm_type &self) {
+ return fmt::format("", csvm_name, self.num_available_devices(), self.get_kernel_invocation_type());
+ });
+}
+
+} // namespace
+
+py::module_ init_dpcpp_csvm(py::module_ &m, const py::exception &base_exception) {
+ // use its own submodule for the DPC++ C-SVM bindings
+ py::module_ dpcpp_module = m.def_submodule("dpcpp", "a module containing all DPC++ backend specific functionality");
+ const py::module_ dpcpp_pure_virtual_module = dpcpp_module.def_submodule("__pure_virtual", "a module containing all pure-virtual DPC++ backend specific functionality");
+
+ // bind the pure-virtual base DPC++ C-SVM
+ [[maybe_unused]] const py::class_ virtual_base_dpcpp_csvm(dpcpp_pure_virtual_module, "__pure_virtual_dpcpp_base_CSVM");
+
+ // bind the specific DPC++ C-SVC and C-SVR classes
+ bind_dpcpp_csvms(dpcpp_module, "CSVC");
+ bind_dpcpp_csvms(dpcpp_module, "CSVR");
- // register DPCPP backend specific exceptions
- register_py_exception(dpcpp_module, "BackendError", base_exception);
+ // register DPC++ backend specific exceptions
+ plssvm::bindings::python::util::register_py_exception(dpcpp_module, "BackendError", base_exception);
return dpcpp_module;
}
diff --git a/bindings/Python/backends/hip_csvm.cpp b/bindings/Python/backends/hip_csvm.cpp
index 9647f8517..fc05c13d4 100644
--- a/bindings/Python/backends/hip_csvm.cpp
+++ b/bindings/Python/backends/hip_csvm.cpp
@@ -6,51 +6,80 @@
* See the LICENSE.md file in the project root for full license information.
*/
+#include "plssvm/backend_types.hpp" // plssvm::hip::backend_csvm_type_t
#include "plssvm/backends/HIP/csvm.hpp" // plssvm::hip::csvm
#include "plssvm/backends/HIP/exceptions.hpp" // plssvm::hip::backend_exception
-#include "plssvm/csvm.hpp" // plssvm::csvm
#include "plssvm/exceptions/exceptions.hpp" // plssvm::exception
#include "plssvm/parameter.hpp" // plssvm::parameter
+#include "plssvm/svm/csvc.hpp" // plssvm::csvc
+#include "plssvm/svm/csvm.hpp" // plssvm::csvm
+#include "plssvm/svm/csvr.hpp" // plssvm::csvr
#include "plssvm/target_platforms.hpp" // plssvm::target_platform
-#include "bindings/Python/utility.hpp" // check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception
+#include "bindings/Python/utility.hpp" // plssvm::bindings::python::util::{check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception}
-#include "pybind11/pybind11.h" // py::module_, py::class_, py::init
-#include "pybind11/stl.h" // support for STL types
+#include "fmt/format.h" // fmt::format
+#include "pybind11/pybind11.h" // py::module_, py::class_, py::init, py::exception
+#include "pybind11/pytypes.h" // py::kwargs
#include // std::make_unique
+#include // std::string
namespace py = pybind11;
-void init_hip_csvm(py::module_ &m, const py::exception &base_exception) {
- // use its own submodule for the HIP CSVM bindings
- py::module_ hip_module = m.def_submodule("hip", "a module containing all HIP backend specific functionality");
+namespace {
+
+template
+void bind_hip_csvms(py::module_ &m, const std::string &csvm_name) {
+ using backend_csvm_type = plssvm::hip::backend_csvm_type_t;
- // bind the CSVM using the HIP backend
- py::class_(hip_module, "CSVM")
- .def(py::init<>(), "create an SVM with the automatic target platform and default parameter object")
- .def(py::init(), "create an SVM with the automatic target platform and provided parameter object")
- .def(py::init(), "create an SVM with the provided target platform and default parameter object")
- .def(py::init(), "create an SVM with the provided target platform and parameter object")
+ // assemble docstrings
+ const std::string class_docstring{ fmt::format("A {} using the HIP backend.", csvm_name) };
+ const std::string param_docstring{ fmt::format("create a HIP {} with the provided parameters", csvm_name) };
+ const std::string target_param_docstring{ fmt::format("create a HIP {} with the provided target platform and parameters", csvm_name) };
+ const std::string kwargs_docstring{ fmt::format("create a HIP {} with the provided keyword arguments", csvm_name) };
+ const std::string target_kwargs_docstring{ fmt::format("create a HIP {} with the provided target platform and keyword arguments", csvm_name) };
+
+ py::class_(m, csvm_name.c_str(), class_docstring.c_str())
+ .def(py::init(), param_docstring.c_str())
+ .def(py::init(), target_param_docstring.c_str())
.def(py::init([](const py::kwargs &args) {
// check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
// if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
- // create CSVM with the default target platform
- return std::make_unique(params);
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
+ // create C-SVM with the default target platform
+ return std::make_unique(params);
}),
- "create an SVM with the default target platform and keyword arguments")
+ kwargs_docstring.c_str())
.def(py::init([](const plssvm::target_platform target, const py::kwargs &args) {
// check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
// if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
- // create CSVM with the provided target platform
- return std::make_unique(target, params);
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
+ // create C-SVM with the provided target platform
+ return std::make_unique(target, params);
}),
- "create an SVM with the provided target platform and keyword arguments");
+ target_kwargs_docstring.c_str())
+ .def("__repr__", [csvm_name](const backend_csvm_type &self) {
+ return fmt::format("", csvm_name, self.num_available_devices());
+ });
+}
+
+} // namespace
+
+void init_hip_csvm(py::module_ &m, const py::exception &base_exception) {
+ // use its own submodule for the HIP C-SVM bindings
+ py::module_ hip_module = m.def_submodule("hip", "a module containing all HIP backend specific functionality");
+ const py::module_ hip_pure_virtual_module = hip_module.def_submodule("__pure_virtual", "a module containing all pure-virtual HIP backend specific functionality");
+
+ // bind the pure-virtual base HIP C-SVM
+ [[maybe_unused]] const py::class_ virtual_base_hip_csvm(hip_pure_virtual_module, "__pure_virtual_hip_base_CSVM");
+
+ // bind the specific HIP C-SVC and C-SVR classes
+ bind_hip_csvms(hip_module, "CSVC");
+ bind_hip_csvms(hip_module, "CSVR");
// register HIP backend specific exceptions
- register_py_exception(hip_module, "BackendError", base_exception);
+ plssvm::bindings::python::util::register_py_exception(hip_module, "BackendError", base_exception);
}
diff --git a/bindings/Python/backends/hpx_csvm.cpp b/bindings/Python/backends/hpx_csvm.cpp
index 92b4fef10..48710e4c5 100644
--- a/bindings/Python/backends/hpx_csvm.cpp
+++ b/bindings/Python/backends/hpx_csvm.cpp
@@ -7,51 +7,80 @@
* See the LICENSE.md file in the project root for full license information.
*/
+#include "plssvm/backend_types.hpp" // plssvm::hpx::backend_csvm_type_t
#include "plssvm/backends/HPX/csvm.hpp" // plssvm::hpx::csvm
#include "plssvm/backends/HPX/exceptions.hpp" // plssvm::hpx::backend_exception
-#include "plssvm/csvm.hpp" // plssvm::csvm
#include "plssvm/exceptions/exceptions.hpp" // plssvm::exception
#include "plssvm/parameter.hpp" // plssvm::parameter
+#include "plssvm/svm/csvc.hpp" // plssvm::csvc
+#include "plssvm/svm/csvm.hpp" // plssvm::csvm
+#include "plssvm/svm/csvr.hpp" // plssvm::csvr
#include "plssvm/target_platforms.hpp" // plssvm::target_platform
-#include "bindings/Python/utility.hpp" // check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception
+#include "bindings/Python/utility.hpp" // plssvm::bindings::python::util::{check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception}
-#include "pybind11/pybind11.h" // py::module_, py::class_, py::init
-#include "pybind11/stl.h" // support for STL types
+#include "fmt/format.h" // fmt::format
+#include "pybind11/pybind11.h" // py::module_, py::class_, py::init, py::exception
+#include "pybind11/pytypes.h" // py::kwargs
#include // std::make_unique
+#include // std::string
namespace py = pybind11;
-void init_hpx_csvm(py::module_ &m, const py::exception &base_exception) {
- // use its own submodule for the HPX CSVM bindings
- py::module_ hpx_module = m.def_submodule("hpx", "a module containing all HPX backend specific functionality");
+namespace {
+
+template
+void bind_hpx_csvms(py::module_ &m, const std::string &csvm_name) {
+ using backend_csvm_type = plssvm::hpx::backend_csvm_type_t;
- // bind the CSVM using the HPX backend
- py::class_(hpx_module, "CSVM")
- .def(py::init<>(), "create an SVM with the automatic target platform and default parameter object")
- .def(py::init(), "create an SVM with the automatic target platform and provided parameter object")
- .def(py::init(), "create an SVM with the provided target platform and default parameter object")
- .def(py::init(), "create an SVM with the provided target platform and parameter object")
+ // assemble docstrings
+ const std::string class_docstring{ fmt::format("A {} using the HPX backend.", csvm_name) };
+ const std::string param_docstring{ fmt::format("create an HPX {} with the provided parameters", csvm_name) };
+ const std::string target_param_docstring{ fmt::format("create an HPX {} with the provided target platform and parameters", csvm_name) };
+ const std::string kwargs_docstring{ fmt::format("create an HPX {} with the provided keyword arguments", csvm_name) };
+ const std::string target_kwargs_docstring{ fmt::format("create an HPX {} with the provided target platform and keyword arguments", csvm_name) };
+
+ py::class_(m, csvm_name.c_str(), class_docstring.c_str())
+ .def(py::init(), param_docstring.c_str())
+ .def(py::init(), target_param_docstring.c_str())
.def(py::init([](const py::kwargs &args) {
// check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
// if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
- // create CSVM with the default target platform
- return std::make_unique(params);
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
+ // create C-SVM with the default target platform
+ return std::make_unique(params);
}),
- "create an SVM with the default target platform and keyword arguments")
+ kwargs_docstring.c_str())
.def(py::init([](const plssvm::target_platform target, const py::kwargs &args) {
// check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
// if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
- // create CSVM with the provided target platform
- return std::make_unique(target, params);
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
+ // create C-SVM with the provided target platform
+ return std::make_unique(target, params);
}),
- "create an SVM with the provided target platform and keyword arguments");
+ target_kwargs_docstring.c_str())
+ .def("__repr__", [csvm_name](const backend_csvm_type &self) {
+ return fmt::format("", csvm_name, self.num_available_devices());
+ });
+}
+
+} // namespace
+
+void init_hpx_csvm(py::module_ &m, const py::exception &base_exception) {
+ // use its own submodule for the HPX C-SVM bindings
+ py::module_ hpx_module = m.def_submodule("hpx", "a module containing all HPX backend specific functionality");
+ const py::module_ hpx_pure_virtual_module = hpx_module.def_submodule("__pure_virtual", "a module containing all pure-virtual HPX backend specific functionality");
+
+ // bind the pure-virtual base HPX C-SVM
+ [[maybe_unused]] const py::class_ virtual_base_hpx_csvm(hpx_pure_virtual_module, "__pure_virtual_hpx_base_CSVM");
+
+ // bind the specific HPX C-SVC and C-SVR classes
+ bind_hpx_csvms(hpx_module, "CSVC");
+ bind_hpx_csvms(hpx_module, "CSVR");
// register HPX backend specific exceptions
- register_py_exception(hpx_module, "BackendError", base_exception);
+ plssvm::bindings::python::util::register_py_exception(hpx_module, "BackendError", base_exception);
}
diff --git a/bindings/Python/backends/kokkos_csvm.cpp b/bindings/Python/backends/kokkos_csvm.cpp
index ea6c4af80..884e8a68f 100644
--- a/bindings/Python/backends/kokkos_csvm.cpp
+++ b/bindings/Python/backends/kokkos_csvm.cpp
@@ -6,72 +6,102 @@
* See the LICENSE.md file in the project root for full license information.
*/
+#include "plssvm/backend_types.hpp" // plssvm::kokkos::backend_csvm_type_t
#include "plssvm/backends/Kokkos/csvm.hpp" // plssvm::kokkos::csvm
#include "plssvm/backends/Kokkos/exceptions.hpp" // plssvm::kokkos::backend_exception
#include "plssvm/backends/Kokkos/execution_space.hpp" // plssvm::kokkos::execution_space
-#include "plssvm/csvm.hpp" // plssvm::csvm
#include "plssvm/exceptions/exceptions.hpp" // plssvm::exception
-#include "plssvm/parameter.hpp" // plssvm::parameter, plssvm::kokkos_execution_space
+#include "plssvm/parameter.hpp" // plssvm::parameter
+#include "plssvm/svm/csvc.hpp" // plssvm::csvc
+#include "plssvm/svm/csvm.hpp" // plssvm::csvm
+#include "plssvm/svm/csvr.hpp" // plssvm::csvr
#include "plssvm/target_platforms.hpp" // plssvm::target_platform
-#include "bindings/Python/utility.hpp" // check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception
+#include "bindings/Python/utility.hpp" // plssvm::bindings::python::util::{check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception}
-#include "pybind11/pybind11.h" // py::module_, py::class_, py::init
-#include "pybind11/stl.h" // support for STL types
+#include "fmt/format.h" // fmt::format
+#include "pybind11/pybind11.h" // py::module_, py::class_, py::init, py::exception
+#include "pybind11/pytypes.h" // py::kwargs
#include // std::make_unique
+#include // std::string
namespace py = pybind11;
-void init_kokkos_csvm(py::module_ &m, const py::exception &base_exception) {
- // use its own submodule for the Kokkos CSVM bindings
- py::module_ kokkos_module = m.def_submodule("kokkos", "a module containing all Kokkos backend specific functionality");
+namespace {
+
+template
+void bind_kokkos_csvms(py::module_ &m, const std::string &csvm_name) {
+ using backend_csvm_type = plssvm::kokkos::backend_csvm_type_t;
- // bind the CSVM using the Kokkos backend
- py::class_(kokkos_module, "CSVM")
- .def(py::init<>(), "create an SVM with the automatic target platform and default parameter object")
- .def(py::init(), "create an SVM with the automatic target platform and provided parameter object")
- .def(py::init(), "create an SVM with the provided target platform and default parameter object")
- .def(py::init(), "create an SVM with the provided target platform and parameter object")
+ // assemble docstrings
+ const std::string class_docstring{ fmt::format("A {} using the Kokkos backend.", csvm_name) };
+ const std::string param_docstring{ fmt::format("create a Kokkos {} with the provided parameters and optional Kokkos specific keyword arguments", csvm_name) };
+ const std::string target_param_docstring{ fmt::format("create a Kokkos {} with the provided target platform, parameters, and optional Kokkos specific keyword arguments", csvm_name) };
+ const std::string kwargs_docstring{ fmt::format("create a Kokkos {} with the provided keyword arguments (including optional Kokkos specific keyword arguments)", csvm_name) };
+ const std::string target_kwargs_docstring{ fmt::format("create a Kokkos {} with the provided target platform and keyword arguments (including optional Kokkos specific keyword arguments)", csvm_name) };
+
+ py::class_(m, csvm_name.c_str(), class_docstring.c_str())
+ .def(py::init([](const plssvm::parameter params, const py::kwargs &args) {
+ // check for valid keys
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kokkos_execution_space" });
+ // set Kokkos execution space
+ const plssvm::kokkos::execution_space space = args.contains("kokkos_execution_space") ? args["kokkos_execution_space"].cast() : plssvm::kokkos::execution_space::automatic;
+ // create C-SVM with the default target platform
+ return std::make_unique(params, plssvm::kokkos_execution_space = space);
+ }),
+ param_docstring.c_str())
+ .def(py::init([](const plssvm::target_platform target, const plssvm::parameter params, const py::kwargs &args) {
+ // check for valid keys
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kokkos_execution_space" });
+ // set Kokkos execution space
+ const plssvm::kokkos::execution_space space = args.contains("kokkos_execution_space") ? args["kokkos_execution_space"].cast() : plssvm::kokkos::execution_space::automatic;
+ // create C-SVM with the default target platform
+ return std::make_unique(target, params, plssvm::kokkos_execution_space = space);
+ }),
+ target_param_docstring.c_str())
.def(py::init([](const py::kwargs &args) {
// check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost", "kokkos_execution_space" });
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost", "kokkos_execution_space" });
// if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
// set Kokkos execution space
const plssvm::kokkos::execution_space space = args.contains("kokkos_execution_space") ? args["kokkos_execution_space"].cast() : plssvm::kokkos::execution_space::automatic;
- // create CSVM with the default target platform
- return std::make_unique(params, plssvm::kokkos_execution_space = space);
+ // create C-SVM with the default target platform
+ return std::make_unique(params, plssvm::kokkos_execution_space = space);
}),
- "create an SVM with the default target platform and keyword arguments")
+ kwargs_docstring.c_str())
.def(py::init([](const plssvm::target_platform target, const py::kwargs &args) {
// check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost", "kokkos_execution_space" });
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost", "kokkos_execution_space" });
// if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
// set Kokkos execution space
const plssvm::kokkos::execution_space space = args.contains("kokkos_execution_space") ? args["kokkos_execution_space"].cast() : plssvm::kokkos::execution_space::automatic;
- // create CSVM with the provided target platform
- return std::make_unique(target, params, plssvm::kokkos_execution_space = space);
+ // create C-SVM with the provided target platform
+ return std::make_unique(target, params, plssvm::kokkos_execution_space = space);
}),
- "create an SVM with the provided target platform and keyword arguments")
- .def("get_execution_space", &plssvm::kokkos::csvm::get_execution_space, "get the Kokkos execution space used in this Kokkos SVM");
+ target_kwargs_docstring.c_str())
+ .def("get_execution_space", &plssvm::kokkos::csvm::get_execution_space, "get the Kokkos execution space used in this Kokkos C-SVM")
+ .def("__repr__", [csvm_name](const backend_csvm_type &self) {
+ return fmt::format("", csvm_name, self.num_available_devices(), self.get_execution_space());
+ });
+}
- // register Kokkos backend specific exceptions
- register_py_exception(kokkos_module, "BackendError", base_exception);
+} // namespace
- // bind the execution space enum classes
- py::enum_(kokkos_module, "ExecutionSpace")
- .value("AUTOMATIC", plssvm::kokkos::execution_space::cuda, "automatically determine the used Kokkos execution space (note: this does not necessarily correspond to Kokkos::DefaultExecutionSpace)")
- .value("CUDA", plssvm::kokkos::execution_space::cuda, "execution space representing execution on a CUDA device")
- .value("HIP", plssvm::kokkos::execution_space::hip, "execution space representing execution on a device supported by HIP")
- .value("SYCL", plssvm::kokkos::execution_space::sycl, "execution space representing execution on a device supported by SYCL")
- .value("HPX", plssvm::kokkos::execution_space::hpx, "execution space representing execution with the HPX runtime system")
- .value("OPENMP", plssvm::kokkos::execution_space::openmp, "execution space representing execution with the OpenMP runtime system")
- .value("OPENMPTARGET", plssvm::kokkos::execution_space::openmp_target, "execution space representing execution using the target offloading feature of the OpenMP runtime system")
- .value("OPENACC", plssvm::kokkos::execution_space::openacc, "execution space representing execution with the OpenACC runtime system")
- .value("THREADS", plssvm::kokkos::execution_space::threads, "execution space representing parallel execution with std::threads")
- .value("SERIAL", plssvm::kokkos::execution_space::serial, "execution space representing serial execution on the CPU; should always be available");
+void init_kokkos_csvm(py::module_ &m, const py::exception &base_exception) {
+ // use its own submodule for the Kokkos C-SVM bindings
+ py::module_ kokkos_module = m.def_submodule("kokkos", "a module containing all Kokkos backend specific functionality");
+ const py::module_ kokkos_pure_virtual_module = kokkos_module.def_submodule("__pure_virtual", "a module containing all pure-virtual Kokkos backend specific functionality");
+
+ // bind the pure-virtual base Kokkos C-SVM
+ [[maybe_unused]] const py::class_ virtual_base_kokkos_csvm(kokkos_pure_virtual_module, "__pure_virtual_kokkos_base_CSVM");
- kokkos_module.def("list_available_execution_spaces", &plssvm::kokkos::list_available_execution_spaces, "list all available Kokkos execution spaces");
+ // bind the specific Kokkos C-SVC and C-SVR classes
+ bind_kokkos_csvms(kokkos_module, "CSVC");
+ bind_kokkos_csvms(kokkos_module, "CSVR");
+
+ // register Kokkos backend specific exceptions
+ plssvm::bindings::python::util::register_py_exception(kokkos_module, "BackendError", base_exception);
}
diff --git a/bindings/Python/backends/opencl_csvm.cpp b/bindings/Python/backends/opencl_csvm.cpp
index d9c74f4c2..77ae0f2d8 100644
--- a/bindings/Python/backends/opencl_csvm.cpp
+++ b/bindings/Python/backends/opencl_csvm.cpp
@@ -6,51 +6,80 @@
* See the LICENSE.md file in the project root for full license information.
*/
+#include "plssvm/backend_types.hpp" // plssvm::opencl::backend_csvm_type_t
#include "plssvm/backends/OpenCL/csvm.hpp" // plssvm::opencl::csvm
#include "plssvm/backends/OpenCL/exceptions.hpp" // plssvm::opencl::backend_exception
-#include "plssvm/csvm.hpp" // plssvm::csvm
#include "plssvm/exceptions/exceptions.hpp" // plssvm::exception
#include "plssvm/parameter.hpp" // plssvm::parameter
+#include "plssvm/svm/csvc.hpp" // plssvm::csvc
+#include "plssvm/svm/csvm.hpp" // plssvm::csvm
+#include "plssvm/svm/csvr.hpp" // plssvm::csvr
#include "plssvm/target_platforms.hpp" // plssvm::target_platform
-#include "bindings/Python/utility.hpp" // check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception
+#include "bindings/Python/utility.hpp" // plssvm::bindings::python::util::{check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception}
-#include "pybind11/pybind11.h" // py::module_, py::class_, py::init
-#include "pybind11/stl.h" // support for STL types
+#include "fmt/format.h" // fmt::format
+#include "pybind11/pybind11.h" // py::module_, py::class_, py::init, py::exception
+#include "pybind11/pytypes.h" // py::kwargs
#include // std::make_unique
+#include // std::string
namespace py = pybind11;
-void init_opencl_csvm(py::module_ &m, const py::exception &base_exception) {
- // use its own submodule for the OpenCL CSVM bindings
- py::module_ opencl_module = m.def_submodule("opencl", "a module containing all OpenCL backend specific functionality");
+namespace {
+
+template
+void bind_opencl_csvms(py::module_ &m, const std::string &csvm_name) {
+ using backend_csvm_type = plssvm::opencl::backend_csvm_type_t;
- // bind the CSVM using the OpenCL backend
- py::class_(opencl_module, "CSVM")
- .def(py::init<>(), "create an SVM with the automatic target platform and default parameter object")
- .def(py::init(), "create an SVM with the automatic target platform and provided parameter object")
- .def(py::init(), "create an SVM with the provided target platform and default parameter object")
- .def(py::init(), "create an SVM with the provided target platform and parameter object")
+ // assemble docstrings
+ const std::string class_docstring{ fmt::format("A {} using the OpenCL backend.", csvm_name) };
+ const std::string param_docstring{ fmt::format("create an OpenCL {} with provided parameters", csvm_name) };
+ const std::string target_param_docstring{ fmt::format("create an OpenCL {} with the provided target platform and parameters", csvm_name) };
+ const std::string kwargs_docstring{ fmt::format("create an OpenCL {} with the provided keyword arguments", csvm_name) };
+ const std::string target_kwargs_docstring{ fmt::format("create an OpenCL {} with the provided target platform and keyword arguments", csvm_name) };
+
+ py::class_(m, csvm_name.c_str(), class_docstring.c_str())
+ .def(py::init(), param_docstring.c_str())
+ .def(py::init(), target_param_docstring.c_str())
.def(py::init([](const py::kwargs &args) {
// check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
// if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
- // create CSVM with the default target platform
- return std::make_unique(params);
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
+ // create C-SVM with the default target platform
+ return std::make_unique(params);
}),
- "create an SVM with the default target platform and keyword arguments")
+ kwargs_docstring.c_str())
.def(py::init([](const plssvm::target_platform target, const py::kwargs &args) {
// check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
// if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
- // create CSVM with the provided target platform
- return std::make_unique(target, params);
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
+ // create C-SVM with the provided target platform
+ return std::make_unique(target, params);
}),
- "create an SVM with the provided target platform and keyword arguments");
+ target_kwargs_docstring.c_str())
+ .def("__repr__", [csvm_name](const backend_csvm_type &self) {
+ return fmt::format("", csvm_name, self.num_available_devices());
+ });
+}
+
+} // namespace
+
+void init_opencl_csvm(py::module_ &m, const py::exception &base_exception) {
+ // use its own submodule for the OpenCL C-SVM bindings
+ py::module_ opencl_module = m.def_submodule("opencl", "a module containing all OpenCL backend specific functionality");
+ const py::module_ opencl_pure_virtual_module = opencl_module.def_submodule("__pure_virtual", "a module containing all pure-virtual OpenCL backend specific functionality");
+
+ // bind the pure-virtual base OpenCL C-SVM
+ [[maybe_unused]] const py::class_ virtual_base_opencl_csvm(opencl_pure_virtual_module, "__pure_virtual_opencl_base_CSVM");
+
+ // bind the specific OpenCL C-SVC and C-SVR classes
+ bind_opencl_csvms(opencl_module, "CSVC");
+ bind_opencl_csvms(opencl_module, "CSVR");
// register OpenCL backend specific exceptions
- register_py_exception(opencl_module, "BackendError", base_exception);
+ plssvm::bindings::python::util::register_py_exception(opencl_module, "BackendError", base_exception);
}
diff --git a/bindings/Python/backends/openmp_csvm.cpp b/bindings/Python/backends/openmp_csvm.cpp
index 099e73e98..376329474 100644
--- a/bindings/Python/backends/openmp_csvm.cpp
+++ b/bindings/Python/backends/openmp_csvm.cpp
@@ -6,51 +6,80 @@
* See the LICENSE.md file in the project root for full license information.
*/
+#include "plssvm/backend_types.hpp" // plssvm::openmp::backend_csvm_type_t
#include "plssvm/backends/OpenMP/csvm.hpp" // plssvm::openmp::csvm
#include "plssvm/backends/OpenMP/exceptions.hpp" // plssvm::openmp::backend_exception
-#include "plssvm/csvm.hpp" // plssvm::csvm
#include "plssvm/exceptions/exceptions.hpp" // plssvm::exception
#include "plssvm/parameter.hpp" // plssvm::parameter
+#include "plssvm/svm/csvc.hpp" // plssvm::csvc
+#include "plssvm/svm/csvm.hpp" // plssvm::csvm
+#include "plssvm/svm/csvr.hpp" // plssvm::csvr
#include "plssvm/target_platforms.hpp" // plssvm::target_platform
-#include "bindings/Python/utility.hpp" // check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception
+#include "bindings/Python/utility.hpp" // plssvm::bindings::python::util::{check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception}
-#include "pybind11/pybind11.h" // py::module_, py::class_, py::init
-#include "pybind11/stl.h" // support for STL types
+#include "fmt/format.h" // fmt::format
+#include "pybind11/pybind11.h" // py::module_, py::class_, py::init, py::exception
+#include "pybind11/pytypes.h" // py::kwargs
#include // std::make_unique
+#include // std::string
namespace py = pybind11;
-void init_openmp_csvm(py::module_ &m, const py::exception &base_exception) {
- // use its own submodule for the OpenMP CSVM bindings
- py::module_ openmp_module = m.def_submodule("openmp", "a module containing all OpenMP backend specific functionality");
+namespace {
+
+template
+void bind_openmp_csvms(py::module_ &m, const std::string &csvm_name) {
+ using backend_csvm_type = plssvm::openmp::backend_csvm_type_t;
- // bind the CSVM using the OpenMP backend
- py::class_(openmp_module, "CSVM")
- .def(py::init<>(), "create an SVM with the automatic target platform and default parameter object")
- .def(py::init(), "create an SVM with the automatic target platform and provided parameter object")
- .def(py::init(), "create an SVM with the provided target platform and default parameter object")
- .def(py::init(), "create an SVM with the provided target platform and parameter object")
+ // assemble docstrings
+ const std::string class_docstring{ fmt::format("A {} using the OpenMP backend.", csvm_name) };
+ const std::string param_docstring{ fmt::format("create an OpenMP {} with the provided parameters", csvm_name) };
+ const std::string target_param_docstring{ fmt::format("create an OpenMP {} with the provided target platform and parameters", csvm_name) };
+ const std::string kwargs_docstring{ fmt::format("create an OpenMP {} with the provided keyword arguments", csvm_name) };
+ const std::string target_kwargs_docstring{ fmt::format("create an OpenMP {} with the provided target platform and keyword arguments", csvm_name) };
+
+ py::class_(m, csvm_name.c_str(), class_docstring.c_str())
+ .def(py::init(), param_docstring.c_str())
+ .def(py::init(), target_param_docstring.c_str())
.def(py::init([](const py::kwargs &args) {
// check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
// if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
- // create CSVM with the default target platform
- return std::make_unique(params);
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
+ // create C-SVM with the default target platform
+ return std::make_unique(params);
}),
- "create an SVM with the default target platform and keyword arguments")
+ kwargs_docstring.c_str())
.def(py::init([](const plssvm::target_platform target, const py::kwargs &args) {
// check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
// if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
- // create CSVM with the provided target platform
- return std::make_unique(target, params);
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
+ // create C-SVM with the provided target platform
+ return std::make_unique(target, params);
}),
- "create an SVM with the provided target platform and keyword arguments");
+ target_kwargs_docstring.c_str())
+ .def("__repr__", [csvm_name](const backend_csvm_type &self) {
+ return fmt::format("", csvm_name, self.num_available_devices());
+ });
+}
+
+} // namespace
+
+void init_openmp_csvm(py::module_ &m, const py::exception &base_exception) {
+ // use its own submodule for the OpenMP C-SVM bindings
+ py::module_ openmp_module = m.def_submodule("openmp", "a module containing all OpenMP backend specific functionality");
+ const py::module_ openmp_pure_virtual_module = openmp_module.def_submodule("__pure_virtual", "a module containing all pure-virtual OpenMP backend specific functionality");
+
+ // bind the pure-virtual base OpenMP C-SVM
+ [[maybe_unused]] const py::class_ virtual_base_openmp_csvm(openmp_pure_virtual_module, "__pure_virtual_openmp_base_CSVM");
+
+ // bind the specific OpenMP C-SVC and C-SVR classes
+ bind_openmp_csvms(openmp_module, "CSVC");
+ bind_openmp_csvms(openmp_module, "CSVR");
// register OpenMP backend specific exceptions
- register_py_exception(openmp_module, "BackendError", base_exception);
+ plssvm::bindings::python::util::register_py_exception(openmp_module, "BackendError", base_exception);
}
diff --git a/bindings/Python/backends/stdpar_csvm.cpp b/bindings/Python/backends/stdpar_csvm.cpp
index 945dd104f..f1dadad50 100644
--- a/bindings/Python/backends/stdpar_csvm.cpp
+++ b/bindings/Python/backends/stdpar_csvm.cpp
@@ -6,29 +6,77 @@
* See the LICENSE.md file in the project root for full license information.
*/
+#include "plssvm/backend_types.hpp" // plssvm::stdpar::backend_csvm_type_t
#include "plssvm/backends/stdpar/csvm.hpp" // plssvm::stdpar::csvm
#include "plssvm/backends/stdpar/exceptions.hpp" // plssvm::stdpar::backend_exception
#include "plssvm/backends/stdpar/implementation_types.hpp" // plssvm::stdpar::implementation_type
-#include "plssvm/csvm.hpp" // plssvm::csvm
#include "plssvm/exceptions/exceptions.hpp" // plssvm::exception
#include "plssvm/parameter.hpp" // plssvm::parameter
+#include "plssvm/svm/csvc.hpp" // plssvm::csvc
+#include "plssvm/svm/csvm.hpp" // plssvm::csvm
+#include "plssvm/svm/csvr.hpp" // plssvm::csvr
#include "plssvm/target_platforms.hpp" // plssvm::target_platform
-#include "bindings/Python/utility.hpp" // check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception
+#include "bindings/Python/utility.hpp" // plssvm::bindings::python::util::{check_kwargs_for_correctness, convert_kwargs_to_parameter, register_py_exception}
-#include "pybind11/pybind11.h" // py::module_, py::class_, py::init
+#include "fmt/format.h" // fmt::format
+#include "pybind11/pybind11.h" // py::module_, py::class_, py::init, py::exception
#include "pybind11/pytypes.h" // py::kwargs
#include // std::make_unique
+#include // std::string
namespace py = pybind11;
+namespace {
+
+template
+void bind_stdpar_csvms(py::module_ &m, const std::string &csvm_name) {
+ using backend_csvm_type = plssvm::stdpar::backend_csvm_type_t;
+
+ // assemble docstrings
+ const std::string class_docstring{ fmt::format("A {} using the stdpar backend.", csvm_name) };
+ const std::string param_docstring{ fmt::format("create an stdpar {} with the provided parameters", csvm_name) };
+ const std::string target_param_docstring{ fmt::format("create an stdpar {} with the provided target platform and parameters", csvm_name) };
+ const std::string kwargs_docstring{ fmt::format("create an stdpar {} with the provided keyword arguments", csvm_name) };
+ const std::string target_kwargs_docstring{ fmt::format("create an stdpar {} with the provided target platform and keyword arguments", csvm_name) };
+
+ py::class_(m, csvm_name.c_str())
+ .def(py::init(), param_docstring.c_str())
+ .def(py::init(), target_param_docstring.c_str())
+ .def(py::init([](const py::kwargs &args) {
+ // check for valid keys
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
+ // if one of the value keyword parameter is provided, set the respective value
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
+ // create C-SVM with the default target platform
+ return std::make_unique(params);
+ }),
+ kwargs_docstring.c_str())
+ .def(py::init([](const plssvm::target_platform target, const py::kwargs &args) {
+ // check for valid keys
+ plssvm::bindings::python::util::check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
+ // if one of the value keyword parameter is provided, set the respective value
+ const plssvm::parameter params = plssvm::bindings::python::util::convert_kwargs_to_parameter(args);
+ // create C-SVM with the provided target platform
+ return std::make_unique(target, params);
+ }),
+ target_kwargs_docstring.c_str())
+ .def("get_implementation_type", &plssvm::stdpar::csvm::get_implementation_type, "get the stdpar implementation used in this stdpar C-SVM")
+ .def("__repr__", [csvm_name](const backend_csvm_type &self) {
+ return fmt::format("", csvm_name, self.num_available_devices(), self.get_implementation_type());
+ });
+}
+
+} // namespace
+
void init_stdpar_csvm(py::module_ &m, const py::exception &base_exception) {
- // use its own submodule for the stdpar CSVM bindings
+ // use its own submodule for the stdpar C-SVM bindings
py::module_ stdpar_module = m.def_submodule("stdpar", "a module containing all stdpar backend specific functionality");
+ const py::module_ stdpar_pure_virtual_module = stdpar_module.def_submodule("__pure_virtual", "a module containing all pure-virtual stdpar backend specific functionality");
// bind the enum class
- py::enum_(stdpar_module, "ImplementationType")
+ py::enum_(stdpar_module, "ImplementationType", "Enum class for all supported stdpar implementations in PLSSVM.")
.value("NVHPC", plssvm::stdpar::implementation_type::nvhpc, "use NVIDIA's HPC SDK (NVHPC) compiler nvc++")
.value("ROC_STDPAR", plssvm::stdpar::implementation_type::roc_stdpar, "use AMD's roc-stdpar compiler (patched LLVM)")
.value("INTEL_LLVM", plssvm::stdpar::implementation_type::intel_llvm, "use Intel's LLVM compiler icpx")
@@ -37,32 +85,13 @@ void init_stdpar_csvm(py::module_ &m, const py::exception &ba
stdpar_module.def("list_available_stdpar_implementations", &plssvm::stdpar::list_available_stdpar_implementations, "list all available stdpar implementations");
- // bind the CSVM using the stdpar backend
- py::class_(stdpar_module, "CSVM")
- .def(py::init<>(), "create an SVM with the automatic target platform and default parameter object")
- .def(py::init(), "create an SVM with the automatic target platform and provided parameter object")
- .def(py::init(), "create an SVM with the provided target platform and default parameter object")
- .def(py::init(), "create an SVM with the provided target platform and parameter object")
- .def(py::init([](const py::kwargs &args) {
- // check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
- // if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
- // create CSVM with the default target platform
- return std::make_unique(params);
- }),
- "create an SVM with the default target platform and keyword arguments")
- .def(py::init([](const plssvm::target_platform target, const py::kwargs &args) {
- // check for valid keys
- check_kwargs_for_correctness(args, { "kernel_type", "degree", "gamma", "coef0", "cost" });
- // if one of the value keyword parameter is provided, set the respective value
- const plssvm::parameter params = convert_kwargs_to_parameter(args);
- // create CSVM with the provided target platform
- return std::make_unique(target, params);
- }),
- "create an SVM with the provided target platform and keyword arguments")
- .def("get_implementation_type", &plssvm::stdpar::csvm::get_implementation_type, "get the stdpar implementation type used in this stdpar SVM");
+ // bind the pure-virtual base stdpar C-SVM
+ [[maybe_unused]] const py::class_ virtual_base_stdpar_csvm(stdpar_pure_virtual_module, "__pure_virtual_stdpar_base_CSVM");
+
+ // bind the specific stdpar C-SVC and C-SVR classes
+ bind_stdpar_csvms(stdpar_module, "CSVC");
+ bind_stdpar_csvms(stdpar_module, "CSVR");
// register stdpar backend specific exceptions
- register_py_exception(stdpar_module, "BackendError", base_exception);
+ plssvm::bindings::python::util::register_py_exception(stdpar_module, "BackendError", base_exception);
}
diff --git a/bindings/Python/backends/sycl.cpp b/bindings/Python/backends/sycl.cpp
index 891cb9898..0421fc308 100644
--- a/bindings/Python/backends/sycl.cpp
+++ b/bindings/Python/backends/sycl.cpp
@@ -11,7 +11,7 @@
#include "plssvm/backends/SYCL/kernel_invocation_types.hpp" // plssvm::sycl::kernel_invocation_type
#include "plssvm/exceptions/exceptions.hpp" // plssvm::exception
-#include "bindings/Python/utility.hpp" // register_py_exception
+#include "bindings/Python/utility.hpp" // plssvm::bindings::python::util::register_py_exception
#include "pybind11/pybind11.h" // py::module_, py::enum_, py::exception
#include "pybind11/stl.h" // support for STL types: std:vector
@@ -31,17 +31,17 @@ void init_sycl(py::module_ &m, const py::exception &base_exce
py::module_ sycl_module = m.def_submodule("sycl", "a module containing all SYCL backend specific functionality");
// register SYCL backend specific exceptions
- register_py_exception(sycl_module, "BackendError", base_exception);
+ plssvm::bindings::python::util::register_py_exception(sycl_module, "BackendError", base_exception);
// bind the two enum classes
- py::enum_(sycl_module, "ImplementationType")
+ py::enum_(sycl_module, "ImplementationType", "Enum class for all supported SYCL implementation in PLSSVM.")
.value("AUTOMATIC", plssvm::sycl::implementation_type::automatic, "use the available SYCL implementation; if more than one implementation is available, the macro PLSSVM_SYCL_BACKEND_PREFERRED_IMPLEMENTATION must be defined during the CMake configuration")
.value("DPCPP", plssvm::sycl::implementation_type::dpcpp, "use DPC++ as SYCL implementation")
.value("ADAPTIVECPP", plssvm::sycl::implementation_type::adaptivecpp, "use AdaptiveCpp (formerly known as hipSYCL) as SYCL implementation");
sycl_module.def("list_available_sycl_implementations", &plssvm::sycl::list_available_sycl_implementations, "list all available SYCL implementations");
- py::enum_(sycl_module, "KernelInvocationType")
+ py::enum_(sycl_module, "KernelInvocationType", "Enum class for all possible SYCL kernel invocation types supported in PLSSVM.")
.value("AUTOMATIC", plssvm::sycl::kernel_invocation_type::automatic, "use the best kernel invocation type for the current SYCL implementation and target hardware platform")
.value("ND_RANGE", plssvm::sycl::kernel_invocation_type::nd_range, "use the nd_range kernel invocation type");
@@ -53,6 +53,7 @@ void init_sycl(py::module_ &m, const py::exception &base_exce
const py::module_ dpcpp_module = init_dpcpp_csvm(m, base_exception);
#endif
- // "alias" one of the DPC++ or AdaptiveCpp CSVMs to be the default SYCL CSVM
- sycl_module.attr("CSVM") = PLSSVM_CONCATENATE(PLSSVM_SYCL_BACKEND_PREFERRED_IMPLEMENTATION, _module).attr("CSVM");
+ // "alias" one of the DPC++ or AdaptiveCpp C-SVCs and C-SVRs to be the respective default SYCL C-SVC and C-SVR
+ sycl_module.attr("CSVC") = PLSSVM_CONCATENATE(PLSSVM_SYCL_BACKEND_PREFERRED_IMPLEMENTATION, _module).attr("CSVC");
+ sycl_module.attr("CSVR") = PLSSVM_CONCATENATE(PLSSVM_SYCL_BACKEND_PREFERRED_IMPLEMENTATION, _module).attr("CSVR");
}
diff --git a/bindings/Python/classification_types.cpp b/bindings/Python/classification_types.cpp
index a66cf7b3f..c429973c7 100644
--- a/bindings/Python/classification_types.cpp
+++ b/bindings/Python/classification_types.cpp
@@ -16,7 +16,7 @@ namespace py = pybind11;
void init_classification_types(py::module_ &m) {
// bind enum class
- py::enum_(m, "ClassificationType")
+ py::enum_(m, "ClassificationType", "Enum class for all implemented multiclass classification strategies.")
.value("OAA", plssvm::classification_type::oaa, "use the one vs. all classification strategy (default)")
.value("OAO", plssvm::classification_type::oao, "use the one vs. one classification strategy");
diff --git a/bindings/Python/csvm.cpp b/bindings/Python/csvm.cpp
deleted file mode 100644
index 35dff1c54..000000000
--- a/bindings/Python/csvm.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-/**
- * @author Alexander Van Craen
- * @author Marcel Breyer
- * @copyright 2018-today The PLSSVM project - All Rights Reserved
- * @license This file is part of the PLSSVM project which is released under the MIT license.
- * See the LICENSE.md file in the project root for full license information.
- */
-
-#include "plssvm/csvm.hpp" // plssvm::csvm
-
-#include "plssvm/backend_types.hpp" // plssvm::backend_type, plssvm::determine_default_backend, plssvm::list_available_backends
-#include "plssvm/backends/SYCL/implementation_types.hpp" // plssvm::sycl::implementation_type
-#include "plssvm/backends/SYCL/kernel_invocation_types.hpp" // plssvm::sycl::kernel_invocation_type
-#include "plssvm/classification_types.hpp" // plssvm::classification_type
-#include "plssvm/constants.hpp" // plssvm::real_type
-#include "plssvm/csvm_factory.hpp" // plssvm::make_csvm
-#include "plssvm/data_set.hpp" // plssvm::data_set
-#include "plssvm/detail/type_list.hpp" // plssvm::detail::supported_label_types
-#include "plssvm/model.hpp" // plssvm::model
-#include "plssvm/parameter.hpp" // plssvm::parameter, named parameters
-#include "plssvm/solver_types.hpp" // plssvm::solver_type
-#include "plssvm/target_platforms.hpp" // plssvm::target_platform, plssvm::determine_default_target_platform, plssvm::list_available_target_platforms
-
-#include "bindings/Python/utility.hpp" // check_kwargs_for_correctness, convert_kwargs_to_parameter
-
-#include "fmt/format.h" // fmt::format
-#include "fmt/ranges.h" // fmt::join
-#include "pybind11/pybind11.h" // py::module_, py::class_, py::kwargs, py::overload_cast, py::const_
-
-#include // std::size_t
-#include // std::unique_ptr
-#include // std::istringstream
-#include // std::string
-#include // std::tuple_element_t, std::tuple_size_v
-#include // std::is_same_v
-#include