Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
maximilianbehr committed Mar 20, 2024
1 parent 8e28dcf commit 8c38f0d
Show file tree
Hide file tree
Showing 14 changed files with 1,357 additions and 364 deletions.
3 changes: 3 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
BasedOnStyle: Google
IndentWidth: 4
ColumnLimit: 0
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
build
*bin
22 changes: 12 additions & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
# SOFTWARE.

cmake_minimum_required(VERSION 3.23)
project(cunmf LANGUAGES CXX CUDA VERSION 2.0.0)
project(cunmf LANGUAGES CXX CUDA VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CUDA_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
Expand All @@ -35,20 +35,22 @@ else()
message(FATAL_ERROR "CUDA version ${CMAKE_CUDA_COMPILER_TOOLKIT_VERSION} is not supported. Please install CUDA version 11.4.2 or higher.")
endif()


# cuexpm library
# cunmf library
add_library(cunmf SHARED cunmf.cu cunmf_info.cpp cunmf_options.cpp)
set_property(TARGET cunmf PROPERTY CUDA_ARCHITECTURES all)
set_target_properties(cunmf PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
set_target_properties(cunmf PROPERTIES CUDA_RESOLVE_DEVICE_SYMBOLS ON)
target_link_libraries(cunmf PUBLIC cublas cusolver)
target_compile_options(cunmf PUBLIC $<$<COMPILE_LANGUAGE:CUDA>:-Xcompiler=-Wall,-Wextra>)
target_link_libraries(cunmf PUBLIC cublas)
target_compile_options(cunmf PUBLIC $<$<COMPILE_LANGUAGE:CUDA>:-Xcompiler=-Wall,-Wextra --threads 0>)
install(TARGETS cunmf DESTINATION lib)
install(FILES cunmf.h DESTINATION include)

# copy binary files to build directory
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/X_10000_500.bin DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/W0_10000_40.bin DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/H0_40_500.bin DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

# examples
#foreach(x s d c z)
# add_executable(example_cunmf${x} example_cuexpm${x}.cu)
# target_link_libraries(example_cuexpm${x} PUBLIC cuexpm)
# set_property(TARGET example_cuexpm${x} PROPERTY CUDA_ARCHITECTURES all)
#endforeach()
add_executable(example_cunmf_MUbeta example_cunmf_MUbeta.cu)
target_link_libraries(example_cunmf_MUbeta PUBLIC cunmf)
set_property(TARGET example_cunmf_MUbeta PROPERTY CUDA_ARCHITECTURES all)
94 changes: 94 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# cuNMF - Nonnegative Matrix Factorization using CUDA

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
![GitHub Release](https://img.shields.io/github/v/release/maximilianbehr/cuNMF?display_name=release&style=flat)

**Version:** 1.0.0

**Copyright:** Maximilian Behr

**License:** The software is licensed under under MIT. See [`LICENSE`](LICENSE) for details.

`cuNMF` is a `CUDA` library implementing multiplicative update rules for the nonnegative matrix factorization $X\approx WH$, using the $\beta$-divergence as an error measure.

`cuNMF` supports real single and double precision matrices.

## Available Functions

### General Functions
```C
int cunmf_info_create(cunmf_info* info);
int cunmf_info_destroy(cunmf_info info);
```
### Single Precision Functions
```C
int cunmf_options_screate(cunmf_options* opt);
int cunmf_options_sdestroy(cunmf_options opt);
int cunmf_sMUbeta_buffersize(int m, int n, int k, double beta, size_t* bufferSize);
int cunmf_sMUbeta(int m, int n, int k, double beta, const float* X, void* buffer, const cunmf_options opt, float* W, float* H, cunmf_info info);
```

### Double Precision Functions
```C
int cunmf_options_dcreate(cunmf_options* opt);
int cunmf_options_ddestroy(cunmf_options opt);

int cunmf_dMUbeta_buffersize(int m, int n, int k, double beta, size_t* bufferSize);
int cunmf_dMUbeta(int m, int n, int k, double beta, const float* X, void* buffer, const cunmf_options opt, float* W, float* H, cunmf_info info);
```
## Algorithm
`cuNMF` implements the multiplicative update rules to minimize the $\beta$-divergence for the nonnegative matrix factorization $X\approx W H$, where
$X$ is of size $m\times n$, $W$ is if size $m\times k$ and $H$ is of size $k\times n$.
In more details, we consider the optimization problem
```math
\min\limits_{W \geq \epsilon, H \geq \epsilon} D_{\beta}(X || WH)=\sum_{i=1}^{m}\sum_{j=1}^{n} d_{\beta}(X_{i,j},(WH)_{i,j})
```
where $\epsilon$ is a small nonegative constant and the $\beta$-divergence $d_{\beta}(x,y)$ is given by
```math
d_{\beta}(x,y)=\left\{
\begin{array}{ll}
\frac{x}{y} -\log(\frac{x}{y}) -1 & \beta = 0, \\
x\log(\frac{x}{y}) -x + y & \beta = 1, \\
\frac{1}{\beta(\beta-1)}(x^{\beta} + (\beta-1)y^{\beta}-\beta xy^{\beta-1}) & \textrm{otherwise}.
\end{array}
\right.
```
The case $\beta=0$ gives the Itakura–Saito divergence, $\beta = 1$ gives the Kullback–Leibler divergence, and $\beta=2$ gives the Frobenius norm distance $\frac{1}{2}||\cdot||_F^2$.
For more details on the multiplicative update rule see Theorem 8.8 and Theorem 8.9 in

> Gillis, Nicolas. _Nonnegative matrix factorization_. Society for Industrial and Applied Mathematics, 2020.

## Installation

Prerequisites:
* `CMake >= 3.23`
* `CUDA >= 11.4.2`

```shell
mkdir build && cd build
cmake ..
make
make install
```

## Usage and Examples

The multiplicate update algorithm is an iterative ones. The initial iterates $W_0$ and $H_0$ must be nonnegative.
The parameter $k$ (number of columns of $W$ / rows of $H$) must be specified by the user.
The user can also specifiy stopping criteria based on the

* number of iterations (`maxiter`)
* computational time (`maxtime`)
* relative change of the iterates $W$ and $H$ (`tol_relchange_WH`)
* relative change of the objective $D_{\beta}$ (`tol_relchange_objective`).

See [`example_cunmf_MUbeta.cu`](example_cunmf_MUbeta.cu) for an example using double precision data.


56 changes: 30 additions & 26 deletions checkcuda.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Expand All @@ -23,34 +23,38 @@

#pragma once

#include <cublas_v2.h>

#include <cstdio>

#include "cunmf_error.h"

#define CHECK_CUNMF(err) \
do { \
int error_code = (err); \
if (error_code) { \
fprintf(stderr, "cunmf error %d. In file '%s' on line %d\n", error_code, __FILE__, __LINE__); \
fflush(stderr); \
return CUNMF_INTERNAL_ERRROR; \
} \
#define CHECK_CUNMF(err) \
do { \
int error_code = (err); \
if (error_code) { \
fprintf(stderr, "cunmf error %d. %s:%d\n", error_code, __FILE__, __LINE__); \
fflush(stderr); \
return CUNMF_INTERNAL_ERROR; \
} \
} while (false)

#define CHECK_CUDA(err) \
do { \
cudaError_t error_code = (err); \
if (error_code != cudaSuccess) { \
fprintf(stderr, "CUDA Error %d: %s. In file '%s' on line %d\n", error_code, cudaGetErrorString(error_code), __FILE__, __LINE__); \
fflush(stderr); \
return CUNMF_CUDA_ERROR; \
} \
#define CHECK_CUDA(err) \
do { \
cudaError_t error_code = (err); \
if (error_code != cudaSuccess) { \
fprintf(stderr, "CUDA Error %d: %s. %s:%d\n", error_code, cudaGetErrorString(error_code), __FILE__, __LINE__); \
fflush(stderr); \
return CUNMF_CUDA_ERROR; \
} \
} while (false)

#define CHECK_CUBLAS(err) \
do { \
cublasStatus_t error_code = (err); \
if (error_code != CUBLAS_STATUS_SUCCESS) { \
fprintf(stderr, "CUBLAS Error %d - %s. In file '%s' on line %d\n", error_code, cublasGetStatusString(error_code), __FILE__, __LINE__); \
fflush(stderr); \
return CUNMF_CUBLAS_ERROR; \
} \
#define CHECK_CUBLAS(err) \
do { \
cublasStatus_t error_code = (err); \
if (error_code != CUBLAS_STATUS_SUCCESS) { \
fprintf(stderr, "CUBLAS Error %d - %s. %s:%d\n", error_code, cublasGetStatusString(error_code), __FILE__, __LINE__); \
fflush(stderr); \
return CUNMF_CUBLAS_ERROR; \
} \
} while (false)
Loading

0 comments on commit 8c38f0d

Please sign in to comment.