23 #include <tbb/parallel_for.h>
24 #include <mlpack/methods/dbscan/dbscan.hpp>
25 #include <mlpack/methods/kmeans/kmeans.hpp>
29 using MatrixT = arma::Mat<double>;
31 inline arma::Mat<double> create_input_matrix(
32 const std::vector<const float*>& input_features,
33 const int64_t num_rows) {
34 const int64_t num_features = input_features.size();
35 arma::Mat<double> features_matrix(num_rows, num_features);
36 for (int64_t c = 0; c < num_features; ++c) {
37 double* matrix_col_ptr = features_matrix.colptr(c);
38 const float* input_features_col = input_features[c];
40 [&](
const tbb::blocked_range<int64_t>& r) {
41 const int64_t start_idx = r.begin();
42 const int64_t end_idx = r.end();
43 for (int64_t r = start_idx; r < end_idx; ++r) {
44 matrix_col_ptr[r] = input_features_col[r];
48 return features_matrix;
51 inline arma::Mat<double> create_input_matrix(
52 const std::vector<const double*>& input_features,
53 const int64_t num_rows) {
54 const int64_t num_features = input_features.size();
55 arma::Mat<double> features_matrix(num_rows, num_features);
56 for (int64_t c = 0; c < num_features; ++c) {
57 memcpy(features_matrix.colptr(c), input_features[c],
sizeof(double) * num_rows);
59 return features_matrix;
62 inline void rewrite_cluster_id_nulls(
const arma::Row<size_t>& cluster_assignments,
63 int32_t* output_clusters,
64 const int64_t num_rows) {
66 [&](
const tbb::blocked_range<int64_t>& r) {
67 const int64_t start_idx = r.begin();
68 const int64_t end_idx = r.end();
69 for (int64_t r = start_idx; r < end_idx; ++r) {
70 output_clusters[r] = cluster_assignments[r] == SIZE_MAX
72 : cluster_assignments[r];
77 template <
typename InitStrategy>
78 void run_kmeans(
const MatrixT& input_features_matrix_transposed,
79 arma::Row<size_t>& cluster_assignments,
80 const int32_t num_clusters) {
81 mlpack::kmeans::KMeans<mlpack::metric::EuclideanDistance,
83 mlpack::kmeans::MaxVarianceNewCluster,
84 mlpack::kmeans::NaiveKMeans,
87 kmeans.Cluster(input_features_matrix_transposed,
88 static_cast<size_t>(num_clusters),
93 NEVER_INLINE HOST int32_t mlpack_kmeans_impl(
const std::vector<const T*>& input_features,
94 int32_t* output_clusters,
95 const int64_t num_rows,
96 const int32_t num_clusters,
97 const int32_t num_iterations,
100 const auto input_features_matrix = create_input_matrix(input_features, num_rows);
101 const MatrixT input_features_matrix_transposed = input_features_matrix.t();
102 arma::Row<size_t> cluster_assignments;
106 run_kmeans<mlpack::kmeans::SampleInitialization>(
107 input_features_matrix_transposed, cluster_assignments, num_clusters);
119 throw std::runtime_error(
120 "MLPack KMeans initialization not implemented for given strategy.");
125 rewrite_cluster_id_nulls(cluster_assignments, output_clusters, num_rows);
126 }
catch (std::exception& e) {
127 throw std::runtime_error(e.what());
132 template <
typename T>
133 NEVER_INLINE HOST int32_t mlpack_dbscan_impl(
const std::vector<const T*>& input_features,
134 int32_t* output_clusters,
135 const int64_t num_rows,
136 const double epsilon,
137 const int32_t min_observations) {
139 const auto input_features_matrix = create_input_matrix(input_features, num_rows);
140 const MatrixT input_features_matrix_transposed = input_features_matrix.t();
142 mlpack::dbscan::DBSCAN<> dbscan(epsilon, min_observations);
143 arma::Row<size_t> cluster_assignments;
144 dbscan.Cluster(input_features_matrix_transposed, cluster_assignments);
146 rewrite_cluster_id_nulls(cluster_assignments, output_clusters, num_rows);
147 }
catch (std::exception& e) {
148 throw std::runtime_error(e.what());
153 template <
typename T>
155 mlpack_linear_reg_fit_impl(
const T* input_labels,
156 const std::vector<const T*>& input_features,
157 int64_t* output_coef_idxs,
158 double* output_coefs,
159 const int64_t num_rows) {
163 arma::Mat<T>
X(num_rows, input_features.size() + 1);
165 X.unsafe_col(0).fill(1);
167 const int64_t num_features = input_features.size();
168 for (int64_t feature_idx = 0; feature_idx < num_features; ++feature_idx) {
170 X.colptr(feature_idx + 1), input_features[feature_idx],
sizeof(
T) * num_rows);
172 const arma::Mat<T> Xt =
X.t();
173 const arma::Mat<T> XtX = Xt *
X;
174 const arma::Col<T>
Y(input_labels, num_rows);
175 const arma::Col<T> B_est = arma::solve(XtX, Xt *
Y);
176 for (int64_t coef_idx = 0; coef_idx < num_features + 1; ++coef_idx) {
177 output_coef_idxs[coef_idx] = coef_idx;
178 output_coefs[coef_idx] = B_est[coef_idx];
180 return num_features + 1;
181 }
catch (std::exception& e) {
182 throw std::runtime_error(e.what());
186 template <
typename T>
188 mlpack_linear_reg_predict_impl(
const std::shared_ptr<LinearRegressionModel>& model,
189 const std::vector<const T*>& input_features,
190 T* output_predictions,
191 const int64_t num_rows) {
194 if (model->getNumFeatures() !=
static_cast<int64_t
>(input_features.size())) {
195 throw std::runtime_error(
196 "Number of model coefficients does not match number of input features.");
200 const int64_t num_features = input_features.size();
201 const int64_t num_coefs = num_features + 1;
202 arma::Mat<T>
X(num_rows, num_coefs);
203 X.unsafe_col(0).fill(1);
204 for (int64_t feature_idx = 0; feature_idx < num_features; ++feature_idx) {
206 X.colptr(feature_idx + 1), input_features[feature_idx],
sizeof(
T) * num_rows);
208 std::vector<T> casted_coefs(num_coefs);
209 const auto& coefs = model->getCoefs();
210 for (int64_t coef_idx = 0; coef_idx < num_coefs; ++coef_idx) {
211 casted_coefs[coef_idx] = coefs[coef_idx];
213 const arma::Col<T> B(casted_coefs.data(), num_coefs);
214 const arma::Col<T> Y_hat = X * B;
215 memcpy(output_predictions, Y_hat.colptr(0),
sizeof(
T) * num_rows);
217 }
catch (std::exception& e) {
218 throw std::runtime_error(e.what());
222 #endif // #ifdef HAVE_TBB
223 #endif // #ifdef HAVE_MLPACK
224 #endif // #ifdef __CUDACC__
void parallel_for(const blocked_range< Int > &range, const Body &body, const Partitioner &p=Partitioner())