Skip to content

Commit 9549d6d

Browse files
authored
refactor c++ code: (#55)
* refactor c++ code: move more functions to header files, change file naming * use clang-format * stronger SCD tolerance as NNLS it might have issues with convergence * don't format auto-generated RcppExports
1 parent 2191210 commit 9549d6d

31 files changed

+1807
-1911
lines changed

.Rbuildignore

+3
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ docs/
99
^cran-comments\.md$
1010
.github/
1111
^\.github$
12+
^.clang-format$
13+
^Makefile$
14+
extradata/

.clang-format

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
BasedOnStyle: Google
2+
DerivePointerAlignment: false
3+
ColumnLimit: 90
4+
IncludeBlocks: Preserve

.gitignore

+1-4
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ src/*.o
66
src/*.so
77
src/*.dll
88
.DS_Store
9-
demo/
10-
data/lastfm.rds
11-
docs/papers/
12-
R/draft/
9+
extradata/
1310

1411
*.status
1512
config.log

Makefile

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
clang_format=`which clang-format`
2+
3+
format: $(shell find . -name *.hpp) $(shell find . -type f \( -iname "*.cpp" ! -iname "RcppExports.cpp" \))
4+
@${clang_format} -i $?

R/RcppExports.R

+52-52
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,6 @@
11
# Generated by using Rcpp::compileAttributes() -> do not edit by hand
22
# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
33

4-
check_is_seq <- function(indices) {
5-
.Call(`_rsparse_check_is_seq`, indices)
6-
}
7-
8-
copy_csr_rows <- function(indptr, indices, values, rows_take) {
9-
.Call(`_rsparse_copy_csr_rows`, indptr, indices, values, rows_take)
10-
}
11-
12-
copy_csr_rows_col_seq <- function(indptr, indices, values, rows_take, cols_take) {
13-
.Call(`_rsparse_copy_csr_rows_col_seq`, indptr, indices, values, rows_take, cols_take)
14-
}
15-
16-
copy_csr_arbitrary <- function(indptr, indices, values, rows_take, cols_take) {
17-
.Call(`_rsparse_copy_csr_arbitrary`, indptr, indices, values, rows_take, cols_take)
18-
}
19-
20-
get_ftrl_weights <- function(R_model) {
21-
.Call(`_rsparse_get_ftrl_weights`, R_model)
22-
}
23-
24-
ftrl_partial_fit <- function(m, y, R_model, weights, do_update = 1L, n_threads = 1L) {
25-
.Call(`_rsparse_ftrl_partial_fit`, m, y, R_model, weights, do_update, n_threads)
26-
}
27-
284
fm_create_param <- function(learning_rate_w, learning_rate_v, rank, lambda_w, lambda_v, w0_R, w_R, v_R, grad_w2_R, grad_v2_R, task, intercept) {
295
.Call(`_rsparse_fm_create_param`, learning_rate_w, learning_rate_v, rank, lambda_w, lambda_v, w0_R, w_R, v_R, grad_w2_R, grad_v2_R, task, intercept)
306
}
@@ -57,6 +33,14 @@ is_invalid_ptr <- function(sexp_ptr) {
5733
.Call(`_rsparse_is_invalid_ptr`, sexp_ptr)
5834
}
5935

36+
get_ftrl_weights <- function(R_model) {
37+
.Call(`_rsparse_get_ftrl_weights`, R_model)
38+
}
39+
40+
ftrl_partial_fit <- function(m, y, R_model, weights, do_update = 1L, n_threads = 1L) {
41+
.Call(`_rsparse_ftrl_partial_fit`, m, y, R_model, weights, do_update, n_threads)
42+
}
43+
6044
cpp_glove_create <- function(params) {
6145
.Call(`_rsparse_cpp_glove_create`, params)
6246
}
@@ -65,36 +49,40 @@ cpp_glove_partial_fit <- function(ptr, x_irow, x_icol, x_val, iter_order, n_thre
6549
.Call(`_rsparse_cpp_glove_partial_fit`, ptr, x_irow, x_icol, x_val, iter_order, n_threads)
6650
}
6751

68-
csr_dense_tcrossprod <- function(x_csr_r, y_transposed, num_threads = 1L) {
69-
.Call(`_rsparse_csr_dense_tcrossprod`, x_csr_r, y_transposed, num_threads)
52+
arma_kmeans <- function(x, k, seed_mode, n_iter, verbose, result) {
53+
.Call(`_rsparse_arma_kmeans`, x, k, seed_mode, n_iter, verbose, result)
7054
}
7155

72-
dense_csc_prod <- function(x_r, y_csc_r, num_threads = 1L) {
73-
.Call(`_rsparse_dense_csc_prod`, x_r, y_csc_r, num_threads)
56+
check_is_seq <- function(indices) {
57+
.Call(`_rsparse_check_is_seq`, indices)
7458
}
7559

76-
als_implicit_double <- function(m_csc_r, X, Y, XtX, lambda, n_threads, solver, cg_steps, with_biases, is_x_bias_last_row) {
77-
.Call(`_rsparse_als_implicit_double`, m_csc_r, X, Y, XtX, lambda, n_threads, solver, cg_steps, with_biases, is_x_bias_last_row)
60+
copy_csr_rows <- function(indptr, indices, values, rows_take) {
61+
.Call(`_rsparse_copy_csr_rows`, indptr, indices, values, rows_take)
7862
}
7963

80-
als_implicit_float <- function(m_csc_r, X_, Y_, XtX_, lambda, n_threads, solver, cg_steps, with_biases, is_x_bias_last_row) {
81-
.Call(`_rsparse_als_implicit_float`, m_csc_r, X_, Y_, XtX_, lambda, n_threads, solver, cg_steps, with_biases, is_x_bias_last_row)
64+
copy_csr_rows_col_seq <- function(indptr, indices, values, rows_take, cols_take) {
65+
.Call(`_rsparse_copy_csr_rows_col_seq`, indptr, indices, values, rows_take, cols_take)
8266
}
8367

84-
als_explicit_double <- function(m_csc_r, X, Y, cnt_X, lambda, n_threads, solver, cg_steps, dynamic_lambda, with_biases, is_x_bias_last_row) {
85-
.Call(`_rsparse_als_explicit_double`, m_csc_r, X, Y, cnt_X, lambda, n_threads, solver, cg_steps, dynamic_lambda, with_biases, is_x_bias_last_row)
68+
copy_csr_arbitrary <- function(indptr, indices, values, rows_take, cols_take) {
69+
.Call(`_rsparse_copy_csr_arbitrary`, indptr, indices, values, rows_take, cols_take)
8670
}
8771

88-
als_explicit_float <- function(m_csc_r, X_, Y_, cnt_X_, lambda, n_threads, solver, cg_steps, dynamic_lambda, with_biases, is_x_bias_last_row) {
89-
.Call(`_rsparse_als_explicit_float`, m_csc_r, X_, Y_, cnt_X_, lambda, n_threads, solver, cg_steps, dynamic_lambda, with_biases, is_x_bias_last_row)
72+
csr_dense_tcrossprod <- function(x_csr_r, y_transposed, num_threads = 1L) {
73+
.Call(`_rsparse_csr_dense_tcrossprod`, x_csr_r, y_transposed, num_threads)
9074
}
9175

92-
initialize_biases_double <- function(m_csc_r, m_csr_r, user_bias, item_bias, lambda, dynamic_lambda, non_negative, calculate_global_bias = FALSE) {
93-
.Call(`_rsparse_initialize_biases_double`, m_csc_r, m_csr_r, user_bias, item_bias, lambda, dynamic_lambda, non_negative, calculate_global_bias)
76+
dense_csc_prod <- function(x_r, y_csc_r, num_threads = 1L) {
77+
.Call(`_rsparse_dense_csc_prod`, x_r, y_csc_r, num_threads)
9478
}
9579

96-
initialize_biases_float <- function(m_csc_r, m_csr_r, user_bias, item_bias, lambda, dynamic_lambda, non_negative, calculate_global_bias = FALSE) {
97-
.Call(`_rsparse_initialize_biases_float`, m_csc_r, m_csr_r, user_bias, item_bias, lambda, dynamic_lambda, non_negative, calculate_global_bias)
80+
top_product <- function(x, y, k, n_threads, not_recommend_r, exclude, glob_mean = 0.) {
81+
.Call(`_rsparse_top_product`, x, y, k, n_threads, not_recommend_r, exclude, glob_mean)
82+
}
83+
84+
c_nnls_double <- function(x, y, max_iter, rel_tol) {
85+
.Call(`_rsparse_c_nnls_double`, x, y, max_iter, rel_tol)
9886
}
9987

10088
rankmf_solver_double <- function(x_r, W, H, W2_grad, H2_grad, user_features_r, item_features_r, rank, n_updates, learning_rate = 0.01, gamma = 1, lambda_user = 0.0, lambda_item_positive = 0.0, lambda_item_negative = 0.0, n_threads = 1L, update_items = TRUE, loss = 0L, kernel = 0L, max_negative_samples = 50L, margin = 0.1, optimizer = 0L, report_progress = 10L) {
@@ -105,18 +93,6 @@ rankmf_solver_float <- function(x_r, W, H, W2_grad, H2_grad, user_features_r, it
10593
invisible(.Call(`_rsparse_rankmf_solver_float`, x_r, W, H, W2_grad, H2_grad, user_features_r, item_features_r, rank, n_updates, learning_rate, gamma, lambda_user, lambda_item_positive, lambda_item_negative, n_threads, update_items, loss, kernel, max_negative_samples, margin, optimizer, report_progress))
10694
}
10795

108-
top_product <- function(x, y, k, n_threads, not_recommend_r, exclude, glob_mean = 0.) {
109-
.Call(`_rsparse_top_product`, x, y, k, n_threads, not_recommend_r, exclude, glob_mean)
110-
}
111-
112-
arma_kmeans <- function(x, k, seed_mode, n_iter, verbose, result) {
113-
.Call(`_rsparse_arma_kmeans`, x, k, seed_mode, n_iter, verbose, result)
114-
}
115-
116-
c_nnls_double <- function(x, y, max_iter, rel_tol) {
117-
.Call(`_rsparse_c_nnls_double`, x, y, max_iter, rel_tol)
118-
}
119-
12096
omp_thread_count <- function() {
12197
.Call(`_rsparse_omp_thread_count`)
12298
}
@@ -133,3 +109,27 @@ deep_copy <- function(x) {
133109
.Call(`_rsparse_deep_copy`, x)
134110
}
135111

112+
als_explicit_double <- function(m_csc_r, X, Y, cnt_X, lambda, n_threads, solver, cg_steps, dynamic_lambda, with_biases, is_x_bias_last_row) {
113+
.Call(`_rsparse_als_explicit_double`, m_csc_r, X, Y, cnt_X, lambda, n_threads, solver, cg_steps, dynamic_lambda, with_biases, is_x_bias_last_row)
114+
}
115+
116+
als_explicit_float <- function(m_csc_r, X_, Y_, cnt_X_, lambda, n_threads, solver, cg_steps, dynamic_lambda, with_biases, is_x_bias_last_row) {
117+
.Call(`_rsparse_als_explicit_float`, m_csc_r, X_, Y_, cnt_X_, lambda, n_threads, solver, cg_steps, dynamic_lambda, with_biases, is_x_bias_last_row)
118+
}
119+
120+
als_implicit_double <- function(m_csc_r, X, Y, XtX, lambda, n_threads, solver, cg_steps, with_biases, is_x_bias_last_row) {
121+
.Call(`_rsparse_als_implicit_double`, m_csc_r, X, Y, XtX, lambda, n_threads, solver, cg_steps, with_biases, is_x_bias_last_row)
122+
}
123+
124+
als_implicit_float <- function(m_csc_r, X_, Y_, XtX_, lambda, n_threads, solver, cg_steps, with_biases, is_x_bias_last_row) {
125+
.Call(`_rsparse_als_implicit_float`, m_csc_r, X_, Y_, XtX_, lambda, n_threads, solver, cg_steps, with_biases, is_x_bias_last_row)
126+
}
127+
128+
initialize_biases_double <- function(m_csc_r, m_csr_r, user_bias, item_bias, lambda, dynamic_lambda, non_negative, calculate_global_bias = FALSE) {
129+
.Call(`_rsparse_initialize_biases_double`, m_csc_r, m_csr_r, user_bias, item_bias, lambda, dynamic_lambda, non_negative, calculate_global_bias)
130+
}
131+
132+
initialize_biases_float <- function(m_csc_r, m_csr_r, user_bias, item_bias, lambda, dynamic_lambda, non_negative, calculate_global_bias = FALSE) {
133+
.Call(`_rsparse_initialize_biases_float`, m_csc_r, m_csr_r, user_bias, item_bias, lambda, dynamic_lambda, non_negative, calculate_global_bias)
134+
}
135+

inst/include/mapped_csc.hpp

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#ifndef MAPPED_CSC_HPP
2+
#define MAPPED_CSC_HPP
3+
4+
#include <stddef.h>
5+
#include <armadillo>
6+
#include <cstdint>
7+
8+
template <typename T>
9+
class MappedCSC {
10+
public:
11+
MappedCSC();
12+
MappedCSC(arma::uword n_rows, arma::uword n_cols, size_t nnz, arma::uword* row_indices,
13+
arma::uword* col_ptrs, T* values)
14+
: n_rows(n_rows),
15+
n_cols(n_cols),
16+
nnz(nnz),
17+
row_indices(row_indices),
18+
col_ptrs(col_ptrs),
19+
values(values){};
20+
const arma::uword n_rows;
21+
const arma::uword n_cols;
22+
const size_t nnz;
23+
arma::uword* row_indices;
24+
arma::uword* col_ptrs;
25+
T* values;
26+
};
27+
28+
using dMappedCSC = MappedCSC<double>;
29+
using fMappedCSC = MappedCSC<float>;
30+
31+
#endif /* MAPPED_CSC_HPP */

inst/include/mapped_csr.hpp

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#ifndef MAPPED_CSR_HPP
2+
#define MAPPED_CSR_HPP
3+
4+
#include <stddef.h>
5+
#include <armadillo>
6+
#include <cstdint>
7+
8+
template <typename T>
9+
class MappedCSR {
10+
public:
11+
MappedCSR();
12+
MappedCSR(arma::uword n_rows, arma::uword n_cols, size_t nnz, arma::uword* col_indices,
13+
arma::uword* row_ptrs, T* values)
14+
: n_rows(n_rows),
15+
n_cols(n_cols),
16+
nnz(nnz),
17+
col_indices(col_indices),
18+
row_ptrs(row_ptrs),
19+
values(values){};
20+
const arma::uword n_rows;
21+
const arma::uword n_cols;
22+
const size_t nnz;
23+
arma::uword* col_indices;
24+
arma::uword* row_ptrs;
25+
T* values;
26+
std::pair<arma::uvec, arma::Col<T>> get_row(const arma::uword i) const {
27+
const arma::uword p1 = this->row_ptrs[i];
28+
const arma::uword p2 = this->row_ptrs[i + 1];
29+
const arma::uvec idx = arma::uvec(&this->col_indices[p1], p2 - p1, false, true);
30+
const arma::Col<T> values = arma::Col<T>(&this->values[p1], p2 - p1, false, true);
31+
return (std::pair<arma::uvec, arma::Col<T>>(idx, values));
32+
};
33+
};
34+
35+
using dMappedCSR = MappedCSR<double>;
36+
using fMappedCSR = MappedCSR<float>;
37+
38+
#endif /* MAPPED_CSR_HPP */

inst/include/nnls.hpp

+6-12
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@
33
#define EPS 1e-16
44

55
template <class T>
6-
arma::Col<T> scd_ls_update(const arma::Mat<T> &XtX,
7-
arma::Col<T> &mu,
8-
arma::uword max_iter,
9-
double rel_tol,
10-
const arma::Col<T> &initial) {
6+
arma::Col<T> scd_ls_update(const arma::Mat<T>& XtX, arma::Col<T>& mu,
7+
arma::uword max_iter, double rel_tol,
8+
const arma::Col<T>& initial) {
119
arma::Col<T> res = initial;
1210
T rel_diff, old_value, new_value, diff;
1311
const arma::Col<T> XtX_diag = XtX.diag();
@@ -16,7 +14,7 @@ arma::Col<T> scd_ls_update(const arma::Mat<T> &XtX,
1614
for (auto k = 0; k < XtX.n_cols; k++) {
1715
old_value = res(k);
1816
new_value = old_value - mu(k) / XtX_diag(k);
19-
if(new_value < 0) new_value = 0;
17+
if (new_value < 0) new_value = 0;
2018
diff = new_value - old_value;
2119
if (diff != 0) {
2220
res(k) = new_value;
@@ -31,12 +29,8 @@ arma::Col<T> scd_ls_update(const arma::Mat<T> &XtX,
3129
}
3230

3331
template <class T>
34-
arma::Col<T> c_nnls(const arma::Mat<T> &X,
35-
const arma::Col<T> &y,
36-
const arma::Col<T> &init,
37-
arma::uword max_iter,
38-
double rel_tol) {
39-
32+
arma::Col<T> c_nnls(const arma::Mat<T>& X, const arma::Col<T>& y,
33+
const arma::Col<T>& init, arma::uword max_iter, double rel_tol) {
4034
arma::Mat<T> Xt = X.t();
4135
arma::Mat<T> XtX = Xt * X;
4236
// for stability: avoid divided by 0

inst/include/wrmf.hpp

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <armadillo>
2+
#include "mapped_csc.hpp"
3+
#include "mapped_csr.hpp"
4+
5+
#ifdef _OPENMP
6+
#include <omp.h>
7+
#endif
8+
9+
#define GRAIN_SIZE 100
10+
11+
#define CHOLESKY 0
12+
#define CONJUGATE_GRADIENT 1
13+
#define SEQ_COORDINATE_WISE_NNLS 2
14+
15+
#define SCD_MAX_ITER 10000
16+
#define SCD_TOL 1e-4
17+
#define CG_TOL 1e-10

0 commit comments

Comments
 (0)