* Copyright (c) 2016-2026 Microsoft Corporation. All rights reserved.
* Copyright (c) 2016-2026 The LightGBM developers. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*/
#include <LightGBM/c_api.h>
#include <LightGBM/arrow.h>
#include <LightGBM/boosting.h>
#include <LightGBM/config.h>
#include <LightGBM/dataset.h>
#include <LightGBM/dataset_loader.h>
#include <LightGBM/metric.h>
#include <LightGBM/network.h>
#include <LightGBM/objective_function.h>
#include <LightGBM/prediction_early_stop.h>
#include <LightGBM/utils/byte_buffer.h>
#include <LightGBM/utils/common.h>
#include <LightGBM/utils/log.h>
#include <LightGBM/utils/openmp_wrapper.h>
#include <LightGBM/utils/random.h>
#include <LightGBM/utils/threading.h>
#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <functional>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "application/predictor.hpp"
#ifndef LGB_R_BUILD
#include "arrow/array.hpp"
#endif
#include <LightGBM/utils/yamc/alternate_shared_mutex.hpp>
#include <LightGBM/utils/yamc/yamc_shared_lock.hpp>
namespace LightGBM {
inline int LGBM_APIHandleException(const std::exception& ex) {
LGBM_SetLastError(ex.what());
return -1;
}
inline int LGBM_APIHandleException(const std::string& ex) {
LGBM_SetLastError(ex.c_str());
return -1;
}
#define API_BEGIN() try {
#define API_END() } \
catch(std::exception& ex) { return LGBM_APIHandleException(ex); } \
catch(std::string& ex) { return LGBM_APIHandleException(ex); } \
catch(...) { return LGBM_APIHandleException("unknown exception"); } \
return 0;
#define UNIQUE_LOCK(mtx) \
std::unique_lock<yamc::alternate::shared_mutex> lock(mtx);
#define SHARED_LOCK(mtx) \
yamc::shared_lock<yamc::alternate::shared_mutex> lock(&mtx);
const int PREDICTOR_TYPES = 4;
class SingleRowPredictorInner {
public:
PredictFunction predict_function;
int64_t num_pred_in_one_row;
SingleRowPredictorInner(int predict_type, Boosting* boosting, const Config& config, int start_iter, int num_iter) {
bool is_predict_leaf = false;
bool is_raw_score = false;
bool predict_contrib = false;
if (predict_type == C_API_PREDICT_LEAF_INDEX) {
is_predict_leaf = true;
} else if (predict_type == C_API_PREDICT_RAW_SCORE) {
is_raw_score = true;
} else if (predict_type == C_API_PREDICT_CONTRIB) {
predict_contrib = true;
}
early_stop_ = config.pred_early_stop;
early_stop_freq_ = config.pred_early_stop_freq;
early_stop_margin_ = config.pred_early_stop_margin;
iter_ = num_iter;
predictor_.reset(new Predictor(boosting, start_iter, iter_, is_raw_score, is_predict_leaf, predict_contrib,
early_stop_, early_stop_freq_, early_stop_margin_));
num_pred_in_one_row = boosting->NumPredictOneRow(start_iter, iter_, is_predict_leaf, predict_contrib);
predict_function = predictor_->GetPredictFunction();
num_total_model_ = boosting->NumberOfTotalModel();
}
~SingleRowPredictorInner() {}
bool IsPredictorEqual(const Config& config, int iter, Boosting* boosting) {
return early_stop_ == config.pred_early_stop &&
early_stop_freq_ == config.pred_early_stop_freq &&
early_stop_margin_ == config.pred_early_stop_margin &&
iter_ == iter &&
num_total_model_ == boosting->NumberOfTotalModel();
}
private:
std::unique_ptr<Predictor> predictor_;
bool early_stop_;
int early_stop_freq_;
double early_stop_margin_;
int iter_;
int num_total_model_;
};
* \brief Object to store resources meant for single-row Fast Predict methods.
*
* For legacy reasons this is called `FastConfig` in the public C API.
*
* Meant to be used by the *Fast* predict methods only.
* It stores the configuration and prediction resources for reuse across predictions.
*/
struct SingleRowPredictor {
public:
SingleRowPredictor(yamc::alternate::shared_mutex *booster_mutex,
const char *parameters,
const int data_type,
const int32_t num_cols,
int predict_type,
Boosting *boosting,
int start_iter,
int num_iter) : config(Config::Str2Map(parameters)), data_type(data_type), num_cols(num_cols), single_row_predictor_inner(predict_type, boosting, config, start_iter, num_iter), booster_mutex(booster_mutex) {
if (!config.predict_disable_shape_check && num_cols != boosting->MaxFeatureIdx() + 1) {
Log::Fatal("The number of features in data (%d) is not the same as it was in training data (%d).\n"\
"You can set ``predict_disable_shape_check=true`` to discard this error, but please be aware what you are doing.", num_cols, boosting->MaxFeatureIdx() + 1);
}
}
void Predict(std::function<std::vector<std::pair<int, double>>(int row_idx)> get_row_fun,
double* out_result, int64_t* out_len) const {
UNIQUE_LOCK(single_row_predictor_mutex)
yamc::shared_lock<yamc::alternate::shared_mutex> booster_shared_lock(booster_mutex);
auto one_row = get_row_fun(0);
single_row_predictor_inner.predict_function(one_row, out_result);
*out_len = single_row_predictor_inner.num_pred_in_one_row;
}
public:
Config config;
const int data_type;
const int32_t num_cols;
private:
SingleRowPredictorInner single_row_predictor_inner;
yamc::alternate::shared_mutex *booster_mutex;
mutable yamc::alternate::shared_mutex single_row_predictor_mutex;
};
class Booster {
public:
explicit Booster(const char* filename) {
boosting_.reset(Boosting::CreateBoosting("gbdt", filename, std::string("cpu"), 0));
}
Booster(const Dataset* train_data,
const char* parameters) {
auto param = Config::Str2Map(parameters);
config_.Set(param);
OMP_SET_NUM_THREADS(config_.num_threads);
if (config_.input_model.size() > 0) {
Log::Warning("Continued train from model is not supported for c_api,\n"
"please use continued train with input score");
}
boosting_.reset(Boosting::CreateBoosting(config_.boosting, nullptr, config_.device_type, config_.num_gpu));
train_data_ = train_data;
CreateObjectiveAndMetrics();
if (config_.tree_learner == std::string("feature")) {
Log::Fatal("Do not support feature parallel in c api");
}
if (Network::num_machines() == 1 && config_.tree_learner != std::string("serial")) {
Log::Warning("Only find one worker, will switch to serial tree learner");
config_.tree_learner = "serial";
}
boosting_->Init(&config_, train_data_, objective_fun_.get(),
Common::ConstPtrInVectorWrapper<Metric>(train_metric_));
}
void MergeFrom(const Booster* other) {
UNIQUE_LOCK(mutex_)
boosting_->MergeFrom(other->boosting_.get());
}
~Booster() {
}
void CreateObjectiveAndMetrics() {
objective_fun_.reset(ObjectiveFunction::CreateObjectiveFunction(config_.objective,
config_));
if (objective_fun_ == nullptr) {
Log::Info("Using self-defined objective function");
}
if (objective_fun_ != nullptr) {
objective_fun_->Init(train_data_->metadata(), train_data_->num_data());
}
train_metric_.clear();
for (auto metric_type : config_.metric) {
auto metric = std::unique_ptr<Metric>(
Metric::CreateMetric(metric_type, config_));
if (metric == nullptr) {
continue;
}
metric->Init(train_data_->metadata(), train_data_->num_data());
train_metric_.push_back(std::move(metric));
}
train_metric_.shrink_to_fit();
}
void ResetTrainingData(const Dataset* train_data) {
if (train_data != train_data_) {
UNIQUE_LOCK(mutex_)
train_data_ = train_data;
CreateObjectiveAndMetrics();
boosting_->ResetTrainingData(train_data_,
objective_fun_.get(), Common::ConstPtrInVectorWrapper<Metric>(train_metric_));
}
}
static void CheckDatasetResetConfig(
const Config& old_config,
const std::unordered_map<std::string, std::string>& new_param) {
Config new_config;
new_config.Set(new_param);
if (new_param.count("data_random_seed") &&
new_config.data_random_seed != old_config.data_random_seed) {
Log::Fatal("Cannot change data_random_seed after constructed Dataset handle.");
}
if (new_param.count("max_bin") &&
new_config.max_bin != old_config.max_bin) {
Log::Fatal("Cannot change max_bin after constructed Dataset handle.");
}
if (new_param.count("max_bin_by_feature") &&
new_config.max_bin_by_feature != old_config.max_bin_by_feature) {
Log::Fatal(
"Cannot change max_bin_by_feature after constructed Dataset handle.");
}
if (new_param.count("bin_construct_sample_cnt") &&
new_config.bin_construct_sample_cnt !=
old_config.bin_construct_sample_cnt) {
Log::Fatal(
"Cannot change bin_construct_sample_cnt after constructed Dataset "
"handle.");
}
if (new_param.count("min_data_in_bin") &&
new_config.min_data_in_bin != old_config.min_data_in_bin) {
Log::Fatal(
"Cannot change min_data_in_bin after constructed Dataset handle.");
}
if (new_param.count("use_missing") &&
new_config.use_missing != old_config.use_missing) {
Log::Fatal("Cannot change use_missing after constructed Dataset handle.");
}
if (new_param.count("zero_as_missing") &&
new_config.zero_as_missing != old_config.zero_as_missing) {
Log::Fatal(
"Cannot change zero_as_missing after constructed Dataset handle.");
}
if (new_param.count("categorical_feature") &&
new_config.categorical_feature != old_config.categorical_feature) {
Log::Fatal(
"Cannot change categorical_feature after constructed Dataset "
"handle.");
}
if (new_param.count("feature_pre_filter") &&
new_config.feature_pre_filter != old_config.feature_pre_filter) {
Log::Fatal(
"Cannot change feature_pre_filter after constructed Dataset handle.");
}
if (new_param.count("is_enable_sparse") &&
new_config.is_enable_sparse != old_config.is_enable_sparse) {
Log::Fatal(
"Cannot change is_enable_sparse after constructed Dataset handle.");
}
if (new_param.count("pre_partition") &&
new_config.pre_partition != old_config.pre_partition) {
Log::Fatal(
"Cannot change pre_partition after constructed Dataset handle.");
}
if (new_param.count("enable_bundle") &&
new_config.enable_bundle != old_config.enable_bundle) {
Log::Fatal(
"Cannot change enable_bundle after constructed Dataset handle.");
}
if (new_param.count("header") && new_config.header != old_config.header) {
Log::Fatal("Cannot change header after constructed Dataset handle.");
}
if (new_param.count("two_round") &&
new_config.two_round != old_config.two_round) {
Log::Fatal("Cannot change two_round after constructed Dataset handle.");
}
if (new_param.count("label_column") &&
new_config.label_column != old_config.label_column) {
Log::Fatal(
"Cannot change label_column after constructed Dataset handle.");
}
if (new_param.count("weight_column") &&
new_config.weight_column != old_config.weight_column) {
Log::Fatal(
"Cannot change weight_column after constructed Dataset handle.");
}
if (new_param.count("group_column") &&
new_config.group_column != old_config.group_column) {
Log::Fatal(
"Cannot change group_column after constructed Dataset handle.");
}
if (new_param.count("ignore_column") &&
new_config.ignore_column != old_config.ignore_column) {
Log::Fatal(
"Cannot change ignore_column after constructed Dataset handle.");
}
if (new_param.count("forcedbins_filename")) {
Log::Fatal("Cannot change forced bins after constructed Dataset handle.");
}
if (new_param.count("min_data_in_leaf") &&
new_config.min_data_in_leaf < old_config.min_data_in_leaf &&
old_config.feature_pre_filter) {
Log::Fatal(
"Reducing `min_data_in_leaf` with `feature_pre_filter=true` may "
"cause unexpected behaviour "
"for features that were pre-filtered by the larger "
"`min_data_in_leaf`.\n"
"You need to set `feature_pre_filter=false` to dynamically change "
"the `min_data_in_leaf`.");
}
if (new_param.count("linear_tree") && new_config.linear_tree != old_config.linear_tree) {
Log::Fatal("Cannot change linear_tree after constructed Dataset handle.");
}
if (new_param.count("precise_float_parser") &&
new_config.precise_float_parser != old_config.precise_float_parser) {
Log::Fatal("Cannot change precise_float_parser after constructed Dataset handle.");
}
}
void ResetConfig(const char* parameters) {
UNIQUE_LOCK(mutex_)
auto param = Config::Str2Map(parameters);
Config new_config;
new_config.Set(param);
if (param.count("num_class") && new_config.num_class != config_.num_class) {
Log::Fatal("Cannot change num_class during training");
}
if (param.count("boosting") && new_config.boosting != config_.boosting) {
Log::Fatal("Cannot change boosting during training");
}
if (param.count("metric") && new_config.metric != config_.metric) {
Log::Fatal("Cannot change metric during training");
}
CheckDatasetResetConfig(config_, param);
config_.Set(param);
OMP_SET_NUM_THREADS(config_.num_threads);
if (param.count("objective")) {
objective_fun_.reset(ObjectiveFunction::CreateObjectiveFunction(config_.objective,
config_));
if (objective_fun_ == nullptr) {
Log::Info("Using self-defined objective function");
}
if (objective_fun_ != nullptr) {
objective_fun_->Init(train_data_->metadata(), train_data_->num_data());
}
boosting_->ResetTrainingData(train_data_,
objective_fun_.get(), Common::ConstPtrInVectorWrapper<Metric>(train_metric_));
}
boosting_->ResetConfig(&config_);
}
void AddValidData(const Dataset* valid_data) {
UNIQUE_LOCK(mutex_)
valid_metrics_.emplace_back();
for (auto metric_type : config_.metric) {
auto metric = std::unique_ptr<Metric>(Metric::CreateMetric(metric_type, config_));
if (metric == nullptr) {
continue;
}
metric->Init(valid_data->metadata(), valid_data->num_data());
valid_metrics_.back().push_back(std::move(metric));
}
valid_metrics_.back().shrink_to_fit();
boosting_->AddValidDataset(valid_data,
Common::ConstPtrInVectorWrapper<Metric>(valid_metrics_.back()));
}
bool TrainOneIter() {
UNIQUE_LOCK(mutex_)
return boosting_->TrainOneIter(nullptr, nullptr);
}
void Refit(const int32_t* leaf_preds, int32_t nrow, int32_t ncol) {
UNIQUE_LOCK(mutex_)
boosting_->RefitTree(leaf_preds, nrow, ncol);
}
bool TrainOneIter(const score_t* gradients, const score_t* hessians) {
UNIQUE_LOCK(mutex_)
return boosting_->TrainOneIter(gradients, hessians);
}
void RollbackOneIter() {
UNIQUE_LOCK(mutex_)
boosting_->RollbackOneIter();
}
void SetSingleRowPredictorInner(int start_iteration, int num_iteration, int predict_type, const Config& config) {
UNIQUE_LOCK(mutex_)
if (single_row_predictor_[predict_type].get() == nullptr ||
!single_row_predictor_[predict_type]->IsPredictorEqual(config, num_iteration, boosting_.get())) {
single_row_predictor_[predict_type].reset(new SingleRowPredictorInner(predict_type, boosting_.get(),
config, start_iteration, num_iteration));
}
}
std::unique_ptr<SingleRowPredictor> InitSingleRowPredictor(int predict_type, int start_iteration, int num_iteration, int data_type, int32_t num_cols, const char *parameters) {
UNIQUE_LOCK(mutex_)
return std::unique_ptr<SingleRowPredictor>(new SingleRowPredictor(
&mutex_, parameters, data_type, num_cols, predict_type, boosting_.get(), start_iteration, num_iteration));
}
void PredictSingleRow(int predict_type, int ncol,
std::function<std::vector<std::pair<int, double>>(int row_idx)> get_row_fun,
const Config& config,
double* out_result, int64_t* out_len) const {
if (!config.predict_disable_shape_check && ncol != boosting_->MaxFeatureIdx() + 1) {
Log::Fatal("The number of features in data (%d) is not the same as it was in training data (%d).\n"\
"You can set ``predict_disable_shape_check=true`` to discard this error, but please be aware what you are doing.", ncol, boosting_->MaxFeatureIdx() + 1);
}
UNIQUE_LOCK(mutex_)
const auto& single_row_predictor = single_row_predictor_[predict_type];
auto one_row = get_row_fun(0);
auto pred_wrt_ptr = out_result;
single_row_predictor->predict_function(one_row, pred_wrt_ptr);
*out_len = single_row_predictor->num_pred_in_one_row;
}
std::shared_ptr<Predictor> CreatePredictor(int start_iteration, int num_iteration, int predict_type, int ncol, const Config& config) const {
if (!config.predict_disable_shape_check && ncol != boosting_->MaxFeatureIdx() + 1) {
Log::Fatal("The number of features in data (%d) is not the same as it was in training data (%d).\n" \
"You can set ``predict_disable_shape_check=true`` to discard this error, but please be aware what you are doing.", ncol, boosting_->MaxFeatureIdx() + 1);
}
bool is_predict_leaf = false;
bool is_raw_score = false;
bool predict_contrib = false;
if (predict_type == C_API_PREDICT_LEAF_INDEX) {
is_predict_leaf = true;
} else if (predict_type == C_API_PREDICT_RAW_SCORE) {
is_raw_score = true;
} else if (predict_type == C_API_PREDICT_CONTRIB) {
predict_contrib = true;
} else {
is_raw_score = false;
}
return std::make_shared<Predictor>(boosting_.get(), start_iteration, num_iteration, is_raw_score, is_predict_leaf, predict_contrib,
config.pred_early_stop, config.pred_early_stop_freq, config.pred_early_stop_margin);
}
void Predict(int start_iteration, int num_iteration, int predict_type, int nrow, int ncol,
std::function<std::vector<std::pair<int, double>>(int row_idx)> get_row_fun,
const Config& config,
double* out_result, int64_t* out_len) const {
SHARED_LOCK(mutex_);
auto predictor = CreatePredictor(start_iteration, num_iteration, predict_type, ncol, config);
bool is_predict_leaf = false;
bool predict_contrib = false;
if (predict_type == C_API_PREDICT_LEAF_INDEX) {
is_predict_leaf = true;
} else if (predict_type == C_API_PREDICT_CONTRIB) {
predict_contrib = true;
}
int64_t num_pred_in_one_row = boosting_->NumPredictOneRow(start_iteration, num_iteration, is_predict_leaf, predict_contrib);
auto pred_fun = predictor->GetPredictFunction();
OMP_INIT_EX();
#pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
for (int i = 0; i < nrow; ++i) {
OMP_LOOP_EX_BEGIN();
auto one_row = get_row_fun(i);
auto pred_wrt_ptr = out_result + static_cast<size_t>(num_pred_in_one_row) * i;
pred_fun(one_row, pred_wrt_ptr);
OMP_LOOP_EX_END();
}
OMP_THROW_EX();
*out_len = num_pred_in_one_row * nrow;
}
void PredictSparse(int start_iteration, int num_iteration, int predict_type, int64_t nrow, int ncol,
std::function<std::vector<std::pair<int, double>>(int64_t row_idx)> get_row_fun,
const Config& config, int64_t* out_elements_size,
std::vector<std::vector<std::unordered_map<int, double>>>* agg_ptr,
int32_t** out_indices, void** out_data, int data_type,
bool* is_data_float32_ptr, int num_matrices) const {
auto predictor = CreatePredictor(start_iteration, num_iteration, predict_type, ncol, config);
auto pred_sparse_fun = predictor->GetPredictSparseFunction();
std::vector<std::vector<std::unordered_map<int, double>>>& agg = *agg_ptr;
OMP_INIT_EX();
#pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
for (int64_t i = 0; i < nrow; ++i) {
OMP_LOOP_EX_BEGIN();
auto one_row = get_row_fun(i);
agg[i] = std::vector<std::unordered_map<int, double>>(num_matrices);
pred_sparse_fun(one_row, &agg[i]);
OMP_LOOP_EX_END();
}
OMP_THROW_EX();
int64_t elements_size = 0;
for (int64_t i = 0; i < static_cast<int64_t>(agg.size()); ++i) {
auto row_vector = agg[i];
for (int j = 0; j < static_cast<int>(row_vector.size()); ++j) {
elements_size += static_cast<int64_t>(row_vector[j].size());
}
}
*out_elements_size = elements_size;
*is_data_float32_ptr = false;
if (data_type == C_API_DTYPE_FLOAT32) {
*out_data = new float[elements_size];
*is_data_float32_ptr = true;
} else if (data_type == C_API_DTYPE_FLOAT64) {
*out_data = new double[elements_size];
} else {
Log::Fatal("Unknown data type in PredictSparse");
return;
}
*out_indices = new int32_t[elements_size];
}
void PredictSparseCSR(int start_iteration, int num_iteration, int predict_type, int64_t nrow, int ncol,
std::function<std::vector<std::pair<int, double>>(int64_t row_idx)> get_row_fun,
const Config& config,
int64_t* out_len, void** out_indptr, int indptr_type,
int32_t** out_indices, void** out_data, int data_type) const {
SHARED_LOCK(mutex_);
int num_matrices = boosting_->NumModelPerIteration();
bool is_indptr_int32 = false;
bool is_data_float32 = false;
int64_t indptr_size = (nrow + 1) * num_matrices;
if (indptr_type == C_API_DTYPE_INT32) {
*out_indptr = new int32_t[indptr_size];
is_indptr_int32 = true;
} else if (indptr_type == C_API_DTYPE_INT64) {
*out_indptr = new int64_t[indptr_size];
} else {
Log::Fatal("Unknown indptr type in PredictSparseCSR");
return;
}
std::vector<std::vector<std::unordered_map<int, double>>> agg(nrow);
int64_t elements_size = 0;
PredictSparse(start_iteration, num_iteration, predict_type, nrow, ncol, get_row_fun, config, &elements_size, &agg,
out_indices, out_data, data_type, &is_data_float32, num_matrices);
std::vector<int> row_sizes(num_matrices * nrow);
std::vector<int64_t> row_matrix_offsets(num_matrices * nrow);
std::vector<int64_t> matrix_offsets(num_matrices);
int64_t row_vector_cnt = 0;
for (int m = 0; m < num_matrices; ++m) {
for (int64_t i = 0; i < static_cast<int64_t>(agg.size()); ++i) {
auto row_vector = agg[i];
auto row_vector_size = row_vector[m].size();
row_sizes[row_vector_cnt] = static_cast<int>(row_vector_size);
if (i == 0) {
row_matrix_offsets[row_vector_cnt] = 0;
} else {
row_matrix_offsets[row_vector_cnt] = static_cast<int64_t>(row_sizes[row_vector_cnt - 1] + row_matrix_offsets[row_vector_cnt - 1]);
}
row_vector_cnt++;
}
if (m == 0) {
matrix_offsets[m] = 0;
}
if (m + 1 < num_matrices) {
matrix_offsets[m + 1] = static_cast<int64_t>(matrix_offsets[m] + row_matrix_offsets[row_vector_cnt - 1] + row_sizes[row_vector_cnt - 1]);
}
}
int64_t indptr_index = 0;
for (int m = 0; m < num_matrices; ++m) {
if (is_indptr_int32) {
(reinterpret_cast<int32_t*>(*out_indptr))[indptr_index] = 0;
} else {
(reinterpret_cast<int64_t*>(*out_indptr))[indptr_index] = 0;
}
indptr_index++;
int64_t matrix_start_index = m * static_cast<int64_t>(agg.size());
OMP_INIT_EX();
#pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
for (int64_t i = 0; i < static_cast<int64_t>(agg.size()); ++i) {
OMP_LOOP_EX_BEGIN();
auto row_vector = agg[i];
int64_t row_start_index = matrix_start_index + i;
int64_t element_index = row_matrix_offsets[row_start_index] + matrix_offsets[m];
int64_t indptr_loop_index = indptr_index + i;
for (auto it = row_vector[m].begin(); it != row_vector[m].end(); ++it) {
(*out_indices)[element_index] = it->first;
if (is_data_float32) {
(reinterpret_cast<float*>(*out_data))[element_index] = static_cast<float>(it->second);
} else {
(reinterpret_cast<double*>(*out_data))[element_index] = it->second;
}
element_index++;
}
int64_t indptr_value = row_matrix_offsets[row_start_index] + row_sizes[row_start_index];
if (is_indptr_int32) {
(reinterpret_cast<int32_t*>(*out_indptr))[indptr_loop_index] = static_cast<int32_t>(indptr_value);
} else {
(reinterpret_cast<int64_t*>(*out_indptr))[indptr_loop_index] = indptr_value;
}
OMP_LOOP_EX_END();
}
OMP_THROW_EX();
indptr_index += static_cast<int64_t>(agg.size());
}
out_len[0] = elements_size;
out_len[1] = indptr_size;
}
void PredictSparseCSC(int start_iteration, int num_iteration, int predict_type, int64_t nrow, int ncol,
std::function<std::vector<std::pair<int, double>>(int64_t row_idx)> get_row_fun,
const Config& config,
int64_t* out_len, void** out_col_ptr, int col_ptr_type,
int32_t** out_indices, void** out_data, int data_type) const {
SHARED_LOCK(mutex_);
int num_matrices = boosting_->NumModelPerIteration();
auto predictor = CreatePredictor(start_iteration, num_iteration, predict_type, ncol, config);
auto pred_sparse_fun = predictor->GetPredictSparseFunction();
bool is_col_ptr_int32 = false;
bool is_data_float32 = false;
int num_output_cols = ncol + 1;
int col_ptr_size = (num_output_cols + 1) * num_matrices;
if (col_ptr_type == C_API_DTYPE_INT32) {
*out_col_ptr = new int32_t[col_ptr_size];
is_col_ptr_int32 = true;
} else if (col_ptr_type == C_API_DTYPE_INT64) {
*out_col_ptr = new int64_t[col_ptr_size];
} else {
Log::Fatal("Unknown col_ptr type in PredictSparseCSC");
return;
}
std::vector<std::vector<std::unordered_map<int, double>>> agg(nrow);
int64_t elements_size = 0;
PredictSparse(start_iteration, num_iteration, predict_type, nrow, ncol, get_row_fun, config, &elements_size, &agg,
out_indices, out_data, data_type, &is_data_float32, num_matrices);
std::vector<std::vector<int64_t>> column_sizes(num_matrices);
for (int m = 0; m < num_matrices; ++m) {
column_sizes[m] = std::vector<int64_t>(num_output_cols, 0);
for (int64_t i = 0; i < static_cast<int64_t>(agg.size()); ++i) {
auto row_vector = agg[i];
for (auto it = row_vector[m].begin(); it != row_vector[m].end(); ++it) {
column_sizes[m][it->first] += 1;
}
}
}
std::vector<std::vector<int64_t>> column_counts(num_matrices);
std::vector<std::vector<int64_t>> column_start_indices(num_matrices);
std::vector<int64_t> matrix_start_indices(num_matrices, 0);
int col_ptr_index = 0;
for (int m = 0; m < num_matrices; ++m) {
int64_t col_ptr_value = 0;
column_start_indices[m] = std::vector<int64_t>(num_output_cols, 0);
column_counts[m] = std::vector<int64_t>(num_output_cols, 0);
if (is_col_ptr_int32) {
(reinterpret_cast<int32_t*>(*out_col_ptr))[col_ptr_index] = static_cast<int32_t>(col_ptr_value);
} else {
(reinterpret_cast<int64_t*>(*out_col_ptr))[col_ptr_index] = col_ptr_value;
}
col_ptr_index++;
for (int64_t i = 1; i < static_cast<int64_t>(column_sizes[m].size()); ++i) {
column_start_indices[m][i] = column_sizes[m][i - 1] + column_start_indices[m][i - 1];
if (is_col_ptr_int32) {
(reinterpret_cast<int32_t*>(*out_col_ptr))[col_ptr_index] = static_cast<int32_t>(column_start_indices[m][i]);
} else {
(reinterpret_cast<int64_t*>(*out_col_ptr))[col_ptr_index] = column_start_indices[m][i];
}
col_ptr_index++;
}
int64_t last_elem_index = static_cast<int64_t>(column_sizes[m].size()) - 1;
int64_t last_column_start_index = column_start_indices[m][last_elem_index];
int64_t last_column_size = column_sizes[m][last_elem_index];
if (is_col_ptr_int32) {
(reinterpret_cast<int32_t*>(*out_col_ptr))[col_ptr_index] = static_cast<int32_t>(last_column_start_index + last_column_size);
} else {
(reinterpret_cast<int64_t*>(*out_col_ptr))[col_ptr_index] = last_column_start_index + last_column_size;
}
if (m + 1 < num_matrices) {
matrix_start_indices[m + 1] = matrix_start_indices[m] + last_column_start_index + last_column_size;
}
col_ptr_index++;
}
OMP_INIT_EX();
#pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
for (int m = 0; m < num_matrices; ++m) {
OMP_LOOP_EX_BEGIN();
for (int64_t i = 0; i < static_cast<int64_t>(agg.size()); ++i) {
auto row_vector = agg[i];
for (auto it = row_vector[m].begin(); it != row_vector[m].end(); ++it) {
int64_t col_idx = it->first;
int64_t element_index = column_start_indices[m][col_idx] +
matrix_start_indices[m] +
column_counts[m][col_idx];
(*out_indices)[element_index] = static_cast<int32_t>(i);
column_counts[m][col_idx]++;
if (is_data_float32) {
(reinterpret_cast<float*>(*out_data))[element_index] = static_cast<float>(it->second);
} else {
(reinterpret_cast<double*>(*out_data))[element_index] = it->second;
}
}
}
OMP_LOOP_EX_END();
}
OMP_THROW_EX();
out_len[0] = elements_size;
out_len[1] = col_ptr_size;
}
void Predict(int start_iteration, int num_iteration, int predict_type, const char* data_filename,
int data_has_header, const Config& config,
const char* result_filename) const {
SHARED_LOCK(mutex_)
bool is_predict_leaf = false;
bool is_raw_score = false;
bool predict_contrib = false;
if (predict_type == C_API_PREDICT_LEAF_INDEX) {
is_predict_leaf = true;
} else if (predict_type == C_API_PREDICT_RAW_SCORE) {
is_raw_score = true;
} else if (predict_type == C_API_PREDICT_CONTRIB) {
predict_contrib = true;
} else {
is_raw_score = false;
}
Predictor predictor(boosting_.get(), start_iteration, num_iteration, is_raw_score, is_predict_leaf, predict_contrib,
config.pred_early_stop, config.pred_early_stop_freq, config.pred_early_stop_margin);
bool bool_data_has_header = data_has_header > 0 ? true : false;
predictor.Predict(data_filename, result_filename, bool_data_has_header, config.predict_disable_shape_check,
config.precise_float_parser);
}
void GetPredictAt(int data_idx, double* out_result, int64_t* out_len) const {
boosting_->GetPredictAt(data_idx, out_result, out_len);
}
void SaveModelToFile(int start_iteration, int num_iteration, int feature_importance_type, const char* filename) const {
boosting_->SaveModelToFile(start_iteration, num_iteration, feature_importance_type, filename);
}
void LoadModelFromString(const char* model_str) {
size_t len = std::strlen(model_str);
boosting_->LoadModelFromString(model_str, len);
}
std::string SaveModelToString(int start_iteration, int num_iteration,
int feature_importance_type) const {
return boosting_->SaveModelToString(start_iteration,
num_iteration, feature_importance_type);
}
std::string DumpModel(int start_iteration, int num_iteration,
int feature_importance_type) const {
return boosting_->DumpModel(start_iteration, num_iteration,
feature_importance_type);
}
std::vector<double> FeatureImportance(int num_iteration, int importance_type) const {
return boosting_->FeatureImportance(num_iteration, importance_type);
}
double UpperBoundValue() const {
SHARED_LOCK(mutex_)
return boosting_->GetUpperBoundValue();
}
double LowerBoundValue() const {
SHARED_LOCK(mutex_)
return boosting_->GetLowerBoundValue();
}
double GetLeafValue(int tree_idx, int leaf_idx) const {
SHARED_LOCK(mutex_)
return dynamic_cast<GBDTBase*>(boosting_.get())->GetLeafValue(tree_idx, leaf_idx);
}
void SetLeafValue(int tree_idx, int leaf_idx, double val) {
UNIQUE_LOCK(mutex_)
dynamic_cast<GBDTBase*>(boosting_.get())->SetLeafValue(tree_idx, leaf_idx, val);
}
void ShuffleModels(int start_iter, int end_iter) {
UNIQUE_LOCK(mutex_)
boosting_->ShuffleModels(start_iter, end_iter);
}
int GetEvalCounts() const {
SHARED_LOCK(mutex_)
int ret = 0;
for (const auto& metric : train_metric_) {
ret += static_cast<int>(metric->GetName().size());
}
return ret;
}
int GetEvalNames(char** out_strs, const int len, const size_t buffer_len, size_t *out_buffer_len) const {
SHARED_LOCK(mutex_)
*out_buffer_len = 0;
int idx = 0;
for (const auto& metric : train_metric_) {
for (const auto& name : metric->GetName()) {
if (idx < len) {
std::memcpy(out_strs[idx], name.c_str(), std::min(name.size() + 1, buffer_len));
out_strs[idx][buffer_len - 1] = '\0';
}
*out_buffer_len = std::max(name.size() + 1, *out_buffer_len);
++idx;
}
}
return idx;
}
int GetFeatureNames(char** out_strs, const int len, const size_t buffer_len, size_t *out_buffer_len) const {
SHARED_LOCK(mutex_)
*out_buffer_len = 0;
int idx = 0;
for (const auto& name : boosting_->FeatureNames()) {
if (idx < len) {
std::memcpy(out_strs[idx], name.c_str(), std::min(name.size() + 1, buffer_len));
out_strs[idx][buffer_len - 1] = '\0';
}
*out_buffer_len = std::max(name.size() + 1, *out_buffer_len);
++idx;
}
return idx;
}
const Boosting* GetBoosting() const { return boosting_.get(); }
private:
const Dataset* train_data_;
std::unique_ptr<Boosting> boosting_;
std::unique_ptr<SingleRowPredictorInner> single_row_predictor_[PREDICTOR_TYPES];
Config config_;
std::vector<std::unique_ptr<Metric>> train_metric_;
std::vector<std::vector<std::unique_ptr<Metric>>> valid_metrics_;
std::unique_ptr<ObjectiveFunction> objective_fun_;
mutable yamc::alternate::shared_mutex mutex_;
};
}
using LightGBM::AllgatherFunction;
#ifndef LGB_R_BUILD
using LightGBM::ArrowChunkedArray;
#endif
using LightGBM::Booster;
using LightGBM::Common::CheckElementsIntervalClosed;
using LightGBM::Common::RemoveQuotationSymbol;
using LightGBM::Common::Vector2Ptr;
using LightGBM::Common::VectorSize;
using LightGBM::Config;
using LightGBM::data_size_t;
using LightGBM::Dataset;
using LightGBM::DatasetLoader;
using LightGBM::kZeroThreshold;
using LightGBM::LGBM_APIHandleException;
using LightGBM::Log;
using LightGBM::Network;
using LightGBM::Random;
using LightGBM::ReduceScatterFunction;
using LightGBM::SingleRowPredictor;
std::function<std::vector<double>(int row_idx)>
RowFunctionFromDenseMatrix(const void* data, int num_row, int num_col, int data_type, int is_row_major);
std::function<std::vector<std::pair<int, double>>(int row_idx)>
RowPairFunctionFromDenseMatrix(const void* data, int num_row, int num_col, int data_type, int is_row_major);
std::function<std::vector<std::pair<int, double>>(int row_idx)>
RowPairFunctionFromDenseRows(const void** data, int num_col, int data_type);
template<typename T>
std::function<std::vector<std::pair<int, double>>(T idx)>
RowFunctionFromCSR(const void* indptr, int indptr_type, const int32_t* indices,
const void* data, int data_type, int64_t nindptr, int64_t nelem);
class CSC_RowIterator {
public:
CSC_RowIterator(const void* col_ptr, int col_ptr_type, const int32_t* indices,
const void* data, int data_type, int64_t ncol_ptr, int64_t nelem, int col_idx);
~CSC_RowIterator() {}
double Get(int idx);
std::pair<int, double> NextNonZero();
private:
int nonzero_idx_ = 0;
int cur_idx_ = -1;
double cur_val_ = 0.0f;
bool is_end_ = false;
std::function<std::pair<int, double>(int idx)> iter_fun_;
};
const char* LGBM_GetLastError() {
return LastErrorMsg();
}
int LGBM_DumpParamAliases(int64_t buffer_len,
int64_t* out_len,
char* out_str) {
API_BEGIN();
std::string aliases = Config::DumpAliases();
*out_len = static_cast<int64_t>(aliases.size()) + 1;
if (*out_len <= buffer_len) {
std::memcpy(out_str, aliases.c_str(), *out_len);
}
API_END();
}
int LGBM_RegisterLogCallback(void (*callback)(const char*)) {
API_BEGIN();
Log::ResetCallBack(callback);
API_END();
}
static inline int SampleCount(int32_t total_nrow, const Config& config) {
return static_cast<int>(total_nrow < config.bin_construct_sample_cnt ? total_nrow : config.bin_construct_sample_cnt);
}
static inline std::vector<int32_t> CreateSampleIndices(int32_t total_nrow, const Config& config) {
Random rand(config.data_random_seed);
int sample_cnt = SampleCount(total_nrow, config);
return rand.Sample(total_nrow, sample_cnt);
}
int LGBM_GetSampleCount(int32_t num_total_row,
const char* parameters,
int* out) {
API_BEGIN();
if (out == nullptr) {
Log::Fatal("LGBM_GetSampleCount output is nullptr");
}
auto param = Config::Str2Map(parameters);
Config config;
config.Set(param);
*out = SampleCount(num_total_row, config);
API_END();
}
int LGBM_SampleIndices(int32_t num_total_row,
const char* parameters,
void* out,
int32_t* out_len) {
API_BEGIN();
if (out == nullptr) {
Log::Fatal("LGBM_SampleIndices output is nullptr");
}
auto param = Config::Str2Map(parameters);
Config config;
config.Set(param);
auto sample_indices = CreateSampleIndices(num_total_row, config);
memcpy(out, sample_indices.data(), sizeof(int32_t) * sample_indices.size());
*out_len = static_cast<int32_t>(sample_indices.size());
API_END();
}
int LGBM_ByteBufferGetAt(ByteBufferHandle handle, int32_t index, uint8_t* out_val) {
API_BEGIN();
LightGBM::ByteBuffer* byteBuffer = reinterpret_cast<LightGBM::ByteBuffer*>(handle);
*out_val = byteBuffer->GetAt(index);
API_END();
}
int LGBM_ByteBufferFree(ByteBufferHandle handle) {
API_BEGIN();
delete reinterpret_cast<LightGBM::ByteBuffer*>(handle);
API_END();
}
int LGBM_DatasetCreateFromFile(const char* filename,
const char* parameters,
const DatasetHandle reference,
DatasetHandle* out) {
API_BEGIN();
auto param = Config::Str2Map(parameters);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
DatasetLoader loader(config, nullptr, 1, filename);
if (reference == nullptr) {
if (Network::num_machines() == 1) {
*out = loader.LoadFromFile(filename);
} else {
*out = loader.LoadFromFile(filename, Network::rank(), Network::num_machines());
}
} else {
*out = loader.LoadFromFileAlignWithOtherDataset(filename,
reinterpret_cast<const Dataset*>(reference));
}
API_END();
}
int LGBM_DatasetCreateFromSampledColumn(double** sample_data,
int** sample_indices,
int32_t ncol,
const int* num_per_col,
int32_t num_sample_row,
int32_t num_local_row,
int64_t num_dist_row,
const char* parameters,
DatasetHandle* out) {
API_BEGIN();
auto param = Config::Str2Map(parameters);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
DatasetLoader loader(config, nullptr, 1, nullptr);
*out = loader.ConstructFromSampleData(sample_data,
sample_indices,
ncol,
num_per_col,
num_sample_row,
static_cast<data_size_t>(num_local_row),
num_dist_row);
API_END();
}
int LGBM_DatasetCreateByReference(const DatasetHandle reference,
int64_t num_total_row,
DatasetHandle* out) {
API_BEGIN();
std::unique_ptr<Dataset> ret;
data_size_t nrows = static_cast<data_size_t>(num_total_row);
ret.reset(new Dataset(nrows));
const Dataset* reference_dataset = reinterpret_cast<const Dataset*>(reference);
ret->CreateValid(reference_dataset);
ret->InitByReference(nrows, reference_dataset);
*out = ret.release();
API_END();
}
int LGBM_DatasetCreateFromSerializedReference(const void* ref_buffer,
int32_t ref_buffer_size,
int64_t num_row,
int32_t num_classes,
const char* parameters,
DatasetHandle* out) {
API_BEGIN();
auto param = Config::Str2Map(parameters);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
DatasetLoader loader(config, nullptr, 1, nullptr);
*out = loader.LoadFromSerializedReference(static_cast<const char*>(ref_buffer),
static_cast<size_t>(ref_buffer_size),
static_cast<data_size_t>(num_row),
num_classes);
API_END();
}
int LGBM_DatasetInitStreaming(DatasetHandle dataset,
int32_t has_weights,
int32_t has_init_scores,
int32_t has_queries,
int32_t nclasses,
int32_t nthreads,
int32_t omp_max_threads) {
API_BEGIN();
auto p_dataset = reinterpret_cast<Dataset*>(dataset);
auto num_data = p_dataset->num_data();
p_dataset->InitStreaming(num_data, has_weights, has_init_scores, has_queries, nclasses, nthreads, omp_max_threads);
p_dataset->set_wait_for_manual_finish(true);
API_END();
}
int LGBM_DatasetPushRows(DatasetHandle dataset,
const void* data,
int data_type,
int32_t nrow,
int32_t ncol,
int32_t start_row) {
API_BEGIN();
auto p_dataset = reinterpret_cast<Dataset*>(dataset);
auto get_row_fun = RowFunctionFromDenseMatrix(data, nrow, ncol, data_type, 1);
if (p_dataset->has_raw()) {
p_dataset->ResizeRaw(p_dataset->num_numeric_features() + nrow);
}
OMP_INIT_EX();
#pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
for (int i = 0; i < nrow; ++i) {
OMP_LOOP_EX_BEGIN();
const int tid = omp_get_thread_num();
auto one_row = get_row_fun(i);
p_dataset->PushOneRow(tid, start_row + i, one_row);
OMP_LOOP_EX_END();
}
OMP_THROW_EX();
if (!p_dataset->wait_for_manual_finish() && (start_row + nrow == p_dataset->num_data())) {
p_dataset->FinishLoad();
}
API_END();
}
int LGBM_DatasetPushRowsWithMetadata(DatasetHandle dataset,
const void* data,
int data_type,
int32_t nrow,
int32_t ncol,
int32_t start_row,
const float* labels,
const float* weights,
const double* init_scores,
const int32_t* queries,
int32_t tid) {
API_BEGIN();
#ifdef LABEL_T_USE_DOUBLE
Log::Fatal("Don't support LABEL_T_USE_DOUBLE");
#endif
if (!data) {
Log::Fatal("data cannot be null.");
}
auto p_dataset = reinterpret_cast<Dataset*>(dataset);
auto get_row_fun = RowFunctionFromDenseMatrix(data, nrow, ncol, data_type, 1);
if (p_dataset->has_raw()) {
p_dataset->ResizeRaw(p_dataset->num_numeric_features() + nrow);
}
const int max_omp_threads = p_dataset->omp_max_threads() > 0 ? p_dataset->omp_max_threads() : OMP_NUM_THREADS();
OMP_INIT_EX();
#pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
for (int i = 0; i < nrow; ++i) {
OMP_LOOP_EX_BEGIN();
const int internal_tid = omp_get_thread_num() + (max_omp_threads * tid);
auto one_row = get_row_fun(i);
p_dataset->PushOneRow(internal_tid, start_row + i, one_row);
OMP_LOOP_EX_END();
}
OMP_THROW_EX();
p_dataset->InsertMetadataAt(start_row, nrow, labels, weights, init_scores, queries);
if (!p_dataset->wait_for_manual_finish() && (start_row + nrow == p_dataset->num_data())) {
p_dataset->FinishLoad();
}
API_END();
}
int LGBM_DatasetPushRowsByCSR(DatasetHandle dataset,
const void* indptr,
int indptr_type,
const int32_t* indices,
const void* data,
int data_type,
int64_t nindptr,
int64_t nelem,
int64_t,
int64_t start_row) {
API_BEGIN();
auto p_dataset = reinterpret_cast<Dataset*>(dataset);
auto get_row_fun = RowFunctionFromCSR<int>(indptr, indptr_type, indices, data, data_type, nindptr, nelem);
int32_t nrow = static_cast<int32_t>(nindptr - 1);
if (p_dataset->has_raw()) {
p_dataset->ResizeRaw(p_dataset->num_numeric_features() + nrow);
}
OMP_INIT_EX();
#pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
for (int i = 0; i < nrow; ++i) {
OMP_LOOP_EX_BEGIN();
const int tid = omp_get_thread_num();
auto one_row = get_row_fun(i);
p_dataset->PushOneRow(tid, static_cast<data_size_t>(start_row + i), one_row);
OMP_LOOP_EX_END();
}
OMP_THROW_EX();
if (!p_dataset->wait_for_manual_finish() && (start_row + nrow == static_cast<int64_t>(p_dataset->num_data()))) {
p_dataset->FinishLoad();
}
API_END();
}
int LGBM_DatasetPushRowsByCSRWithMetadata(DatasetHandle dataset,
const void* indptr,
int indptr_type,
const int32_t* indices,
const void* data,
int data_type,
int64_t nindptr,
int64_t nelem,
int64_t start_row,
const float* labels,
const float* weights,
const double* init_scores,
const int32_t* queries,
int32_t tid) {
API_BEGIN();
#ifdef LABEL_T_USE_DOUBLE
Log::Fatal("Don't support LABEL_T_USE_DOUBLE");
#endif
if (!data) {
Log::Fatal("data cannot be null.");
}
auto p_dataset = reinterpret_cast<Dataset*>(dataset);
auto get_row_fun = RowFunctionFromCSR<int>(indptr, indptr_type, indices, data, data_type, nindptr, nelem);
int32_t nrow = static_cast<int32_t>(nindptr - 1);
if (p_dataset->has_raw()) {
p_dataset->ResizeRaw(p_dataset->num_numeric_features() + nrow);
}
const int max_omp_threads = p_dataset->omp_max_threads() > 0 ? p_dataset->omp_max_threads() : OMP_NUM_THREADS();
OMP_INIT_EX();
#pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
for (int i = 0; i < nrow; ++i) {
OMP_LOOP_EX_BEGIN();
const int internal_tid = omp_get_thread_num() + (max_omp_threads * tid);
auto one_row = get_row_fun(i);
p_dataset->PushOneRow(internal_tid, static_cast<data_size_t>(start_row + i), one_row);
OMP_LOOP_EX_END();
}
OMP_THROW_EX();
p_dataset->InsertMetadataAt(static_cast<int32_t>(start_row), nrow, labels, weights, init_scores, queries);
if (!p_dataset->wait_for_manual_finish() && (start_row + nrow == static_cast<int64_t>(p_dataset->num_data()))) {
p_dataset->FinishLoad();
}
API_END();
}
int LGBM_DatasetSetWaitForManualFinish(DatasetHandle dataset, int wait) {
API_BEGIN();
auto p_dataset = reinterpret_cast<Dataset*>(dataset);
p_dataset->set_wait_for_manual_finish(wait);
API_END();
}
int LGBM_DatasetMarkFinished(DatasetHandle dataset) {
API_BEGIN();
auto p_dataset = reinterpret_cast<Dataset*>(dataset);
p_dataset->FinishLoad();
API_END();
}
int LGBM_DatasetCreateFromMat(const void* data,
int data_type,
int32_t nrow,
int32_t ncol,
int is_row_major,
const char* parameters,
const DatasetHandle reference,
DatasetHandle* out) {
return LGBM_DatasetCreateFromMats(1,
&data,
data_type,
&nrow,
ncol,
&is_row_major,
parameters,
reference,
out);
}
int LGBM_DatasetCreateFromMats(int32_t nmat,
const void** data,
int data_type,
int32_t* nrow,
int32_t ncol,
int* is_row_major,
const char* parameters,
const DatasetHandle reference,
DatasetHandle* out) {
API_BEGIN();
auto param = Config::Str2Map(parameters);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
std::unique_ptr<Dataset> ret;
int32_t total_nrow = 0;
for (int j = 0; j < nmat; ++j) {
total_nrow += nrow[j];
}
std::vector<std::function<std::vector<double>(int row_idx)>> get_row_fun;
for (int j = 0; j < nmat; ++j) {
get_row_fun.push_back(RowFunctionFromDenseMatrix(data[j], nrow[j], ncol, data_type, is_row_major[j]));
}
if (reference == nullptr) {
auto sample_indices = CreateSampleIndices(total_nrow, config);
int sample_cnt = static_cast<int>(sample_indices.size());
std::vector<std::vector<double>> sample_values(ncol);
std::vector<std::vector<int>> sample_idx(ncol);
int offset = 0;
int j = 0;
for (size_t i = 0; i < sample_indices.size(); ++i) {
auto idx = sample_indices[i];
while ((idx - offset) >= nrow[j]) {
offset += nrow[j];
++j;
}
auto row = get_row_fun[j](static_cast<int>(idx - offset));
for (size_t k = 0; k < row.size(); ++k) {
if (std::fabs(row[k]) > kZeroThreshold || std::isnan(row[k])) {
sample_values[k].emplace_back(row[k]);
sample_idx[k].emplace_back(static_cast<int>(i));
}
}
}
DatasetLoader loader(config, nullptr, 1, nullptr);
ret.reset(loader.ConstructFromSampleData(Vector2Ptr<double>(&sample_values).data(),
Vector2Ptr<int>(&sample_idx).data(),
ncol,
VectorSize<double>(sample_values).data(),
sample_cnt,
total_nrow,
total_nrow));
} else {
ret.reset(new Dataset(total_nrow));
ret->CreateValid(
reinterpret_cast<const Dataset*>(reference));
if (ret->has_raw()) {
ret->ResizeRaw(total_nrow);
}
}
int32_t start_row = 0;
for (int j = 0; j < nmat; ++j) {
OMP_INIT_EX();
#pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
for (int i = 0; i < nrow[j]; ++i) {
OMP_LOOP_EX_BEGIN();
const int tid = omp_get_thread_num();
auto one_row = get_row_fun[j](i);
ret->PushOneRow(tid, start_row + i, one_row);
OMP_LOOP_EX_END();
}
OMP_THROW_EX();
start_row += nrow[j];
}
ret->FinishLoad();
*out = ret.release();
API_END();
}
int LGBM_DatasetCreateFromCSR(const void* indptr,
int indptr_type,
const int32_t* indices,
const void* data,
int data_type,
int64_t nindptr,
int64_t nelem,
int64_t num_col,
const char* parameters,
const DatasetHandle reference,
DatasetHandle* out) {
API_BEGIN();
if (num_col <= 0) {
Log::Fatal("The number of columns should be greater than zero.");
} else if (num_col >= INT32_MAX) {
Log::Fatal("The number of columns should be smaller than INT32_MAX.");
}
auto param = Config::Str2Map(parameters);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
std::unique_ptr<Dataset> ret;
auto get_row_fun = RowFunctionFromCSR<int>(indptr, indptr_type, indices, data, data_type, nindptr, nelem);
int32_t nrow = static_cast<int32_t>(nindptr - 1);
if (reference == nullptr) {
auto sample_indices = CreateSampleIndices(nrow, config);
int sample_cnt = static_cast<int>(sample_indices.size());
std::vector<std::vector<double>> sample_values(num_col);
std::vector<std::vector<int>> sample_idx(num_col);
for (size_t i = 0; i < sample_indices.size(); ++i) {
auto idx = sample_indices[i];
auto row = get_row_fun(static_cast<int>(idx));
for (std::pair<int, double>& inner_data : row) {
CHECK_LT(inner_data.first, num_col);
if (std::fabs(inner_data.second) > kZeroThreshold || std::isnan(inner_data.second)) {
sample_values[inner_data.first].emplace_back(inner_data.second);
sample_idx[inner_data.first].emplace_back(static_cast<int>(i));
}
}
}
DatasetLoader loader(config, nullptr, 1, nullptr);
ret.reset(loader.ConstructFromSampleData(Vector2Ptr<double>(&sample_values).data(),
Vector2Ptr<int>(&sample_idx).data(),
static_cast<int>(num_col),
VectorSize<double>(sample_values).data(),
sample_cnt,
nrow,
nrow));
} else {
ret.reset(new Dataset(nrow));
ret->CreateValid(
reinterpret_cast<const Dataset*>(reference));
if (ret->has_raw()) {
ret->ResizeRaw(nrow);
}
}
OMP_INIT_EX();
#pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
for (int i = 0; i < static_cast<int>(nindptr - 1); ++i) {
OMP_LOOP_EX_BEGIN();
const int tid = omp_get_thread_num();
auto one_row = get_row_fun(i);
ret->PushOneRow(tid, i, one_row);
OMP_LOOP_EX_END();
}
OMP_THROW_EX();
ret->FinishLoad();
*out = ret.release();
API_END();
}
int LGBM_DatasetCreateFromCSRFunc(void* get_row_funptr,
int num_rows,
int64_t num_col,
const char* parameters,
const DatasetHandle reference,
DatasetHandle* out) {
API_BEGIN();
if (num_col <= 0) {
Log::Fatal("The number of columns should be greater than zero.");
} else if (num_col >= INT32_MAX) {
Log::Fatal("The number of columns should be smaller than INT32_MAX.");
}
auto get_row_fun = *static_cast<std::function<void(int idx, std::vector<std::pair<int, double>>&)>*>(get_row_funptr);
auto param = Config::Str2Map(parameters);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
std::unique_ptr<Dataset> ret;
int32_t nrow = num_rows;
if (reference == nullptr) {
auto sample_indices = CreateSampleIndices(nrow, config);
int sample_cnt = static_cast<int>(sample_indices.size());
std::vector<std::vector<double>> sample_values(num_col);
std::vector<std::vector<int>> sample_idx(num_col);
std::vector<std::pair<int, double>> buffer;
for (size_t i = 0; i < sample_indices.size(); ++i) {
auto idx = sample_indices[i];
get_row_fun(static_cast<int>(idx), buffer);
for (std::pair<int, double>& inner_data : buffer) {
CHECK_LT(inner_data.first, num_col);
if (std::fabs(inner_data.second) > kZeroThreshold || std::isnan(inner_data.second)) {
sample_values[inner_data.first].emplace_back(inner_data.second);
sample_idx[inner_data.first].emplace_back(static_cast<int>(i));
}
}
}
DatasetLoader loader(config, nullptr, 1, nullptr);
ret.reset(loader.ConstructFromSampleData(Vector2Ptr<double>(&sample_values).data(),
Vector2Ptr<int>(&sample_idx).data(),
static_cast<int>(num_col),
VectorSize<double>(sample_values).data(),
sample_cnt,
nrow,
nrow));
} else {
ret.reset(new Dataset(nrow));
ret->CreateValid(
reinterpret_cast<const Dataset*>(reference));
if (ret->has_raw()) {
ret->ResizeRaw(nrow);
}
}
OMP_INIT_EX();
std::vector<std::pair<int, double>> thread_buffer;
#pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static) private(thread_buffer)
for (int i = 0; i < num_rows; ++i) {
OMP_LOOP_EX_BEGIN();
{
const int tid = omp_get_thread_num();
get_row_fun(i, thread_buffer);
ret->PushOneRow(tid, i, thread_buffer);
}
OMP_LOOP_EX_END();
}
OMP_THROW_EX();
ret->FinishLoad();
*out = ret.release();
API_END();
}
int LGBM_DatasetCreateFromCSC(const void* col_ptr,
int col_ptr_type,
const int32_t* indices,
const void* data,
int data_type,
int64_t ncol_ptr,
int64_t nelem,
int64_t num_row,
const char* parameters,
const DatasetHandle reference,
DatasetHandle* out) {
API_BEGIN();
auto param = Config::Str2Map(parameters);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
std::unique_ptr<Dataset> ret;
int32_t nrow = static_cast<int32_t>(num_row);
if (reference == nullptr) {
auto sample_indices = CreateSampleIndices(nrow, config);
int sample_cnt = static_cast<int>(sample_indices.size());
std::vector<std::vector<double>> sample_values(ncol_ptr - 1);
std::vector<std::vector<int>> sample_idx(ncol_ptr - 1);
OMP_INIT_EX();
#pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
for (int i = 0; i < static_cast<int>(sample_values.size()); ++i) {
OMP_LOOP_EX_BEGIN();
CSC_RowIterator col_it(col_ptr, col_ptr_type, indices, data, data_type, ncol_ptr, nelem, i);
for (int j = 0; j < sample_cnt; j++) {
auto val = col_it.Get(sample_indices[j]);
if (std::fabs(val) > kZeroThreshold || std::isnan(val)) {
sample_values[i].emplace_back(val);
sample_idx[i].emplace_back(j);
}
}
OMP_LOOP_EX_END();
}
OMP_THROW_EX();
DatasetLoader loader(config, nullptr, 1, nullptr);
ret.reset(loader.ConstructFromSampleData(Vector2Ptr<double>(&sample_values).data(),
Vector2Ptr<int>(&sample_idx).data(),
static_cast<int>(sample_values.size()),
VectorSize<double>(sample_values).data(),
sample_cnt,
nrow,
nrow));
} else {
ret.reset(new Dataset(nrow));
ret->CreateValid(
reinterpret_cast<const Dataset*>(reference));
}
OMP_INIT_EX();
#pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
for (int i = 0; i < static_cast<int>(ncol_ptr - 1); ++i) {
OMP_LOOP_EX_BEGIN();
const int tid = omp_get_thread_num();
int feature_idx = ret->InnerFeatureIndex(i);
if (feature_idx < 0) {
continue;
}
int group = ret->Feature2Group(feature_idx);
int sub_feature = ret->Feature2SubFeature(feature_idx);
CSC_RowIterator col_it(col_ptr, col_ptr_type, indices, data, data_type, ncol_ptr, nelem, i);
auto bin_mapper = ret->FeatureBinMapper(feature_idx);
if (bin_mapper->GetDefaultBin() == bin_mapper->GetMostFreqBin()) {
int row_idx = 0;
while (row_idx < nrow) {
auto pair = col_it.NextNonZero();
row_idx = pair.first;
if (row_idx < 0) {
break;
}
ret->PushOneData(tid, row_idx, group, feature_idx, sub_feature, pair.second);
}
} else {
for (int row_idx = 0; row_idx < nrow; ++row_idx) {
auto val = col_it.Get(row_idx);
ret->PushOneData(tid, row_idx, group, feature_idx, sub_feature, val);
}
}
OMP_LOOP_EX_END();
}
OMP_THROW_EX();
ret->FinishLoad();
*out = ret.release();
API_END();
}
#ifndef LGB_R_BUILD
void DatasetCreateFromArrowChunkedArray(ArrowChunkedArray& chunked_array,
const char* parameters,
const DatasetHandle reference,
DatasetHandle* out) {
auto param = Config::Str2Map(parameters);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
std::unique_ptr<Dataset> ret;
auto chunked_array_view = chunked_array.view();
if (reference == nullptr) {
auto sample_indices = CreateSampleIndices(static_cast<int32_t>(chunked_array.get_length()), config);
auto sample_count = static_cast<int>(sample_indices.size());
std::vector<std::vector<double>> sample_values(chunked_array.get_num_fields());
std::vector<std::vector<int>> sample_idx(chunked_array.get_num_fields());
OMP_INIT_EX();
#pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
for (int64_t j = 0; j < chunked_array.get_num_fields(); ++j) {
OMP_LOOP_EX_BEGIN();
sample_values[j].reserve(sample_indices.size());
sample_idx[j].reserve(sample_indices.size());
chunked_array_view.field(j).visit<double>([&](auto&& visitor) {
int last_idx = 0;
int i = 0;
auto it = visitor.begin();
for (auto idx : sample_indices) {
std::advance(it, idx - last_idx);
auto v = *it;
if (std::fabs(v) > kZeroThreshold || std::isnan(v)) {
sample_values[j].emplace_back(v);
sample_idx[j].emplace_back(i);
}
last_idx = idx;
i++;
}
});
OMP_LOOP_EX_END();
}
OMP_THROW_EX();
DatasetLoader loader(config, nullptr, 1, nullptr);
ret.reset(loader.ConstructFromSampleData(Vector2Ptr<double>(&sample_values).data(),
Vector2Ptr<int>(&sample_idx).data(),
static_cast<int>(chunked_array.get_num_fields()),
VectorSize<double>(sample_values).data(),
sample_count,
static_cast<int>(chunked_array.get_length()),
static_cast<int>(chunked_array.get_length())));
} else {
ret.reset(new Dataset(static_cast<data_size_t>(chunked_array.get_length())));
ret->CreateValid(reinterpret_cast<const Dataset*>(reference));
if (ret->has_raw()) {
ret->ResizeRaw(static_cast<int>(chunked_array.get_length()));
}
}
OMP_INIT_EX();
#pragma omp parallel for num_threads(OMP_NUM_THREADS()) schedule(static)
for (int64_t j = 0; j < chunked_array.get_num_fields(); ++j) {
OMP_LOOP_EX_BEGIN();
const int tid = omp_get_thread_num();
data_size_t idx = 0;
chunked_array_view.field(j).visit<double>([&](auto&& visitor) {
for (auto it = visitor.begin(), end = visitor.end(); it != end; ++it) {
ret->PushOneValue(tid, idx++, j, *it);
}
});
OMP_LOOP_EX_END();
}
OMP_THROW_EX();
ret->FinishLoad();
*out = ret.release();
}
[[deprecated("Use LGBM_DatasetCreateFromArrowStream instead.")]]
int LGBM_DatasetCreateFromArrow(int64_t n_chunks,
ArrowArray* chunks,
ArrowSchema* schema,
const char* parameters,
const DatasetHandle reference,
DatasetHandle *out) {
API_BEGIN();
Log::Warning("LGBM_DatasetCreateFromArrow is deprecated. Please use LGBM_DatasetCreateFromArrowStream instead.");
ArrowChunkedArray chunked_array(n_chunks, chunks, schema);
DatasetCreateFromArrowChunkedArray(chunked_array, parameters, reference, out);
API_END();
}
int LGBM_DatasetCreateFromArrowStream(ArrowArrayStream* stream,
const char* parameters,
const DatasetHandle reference,
DatasetHandle *out) {
API_BEGIN();
ArrowChunkedArray chunked_array(stream);
DatasetCreateFromArrowChunkedArray(chunked_array, parameters, reference, out);
API_END();
}
#endif
int LGBM_DatasetGetSubset(
const DatasetHandle handle,
const int32_t* used_row_indices,
int32_t num_used_row_indices,
const char* parameters,
DatasetHandle* out) {
API_BEGIN();
auto param = Config::Str2Map(parameters);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
auto full_dataset = reinterpret_cast<const Dataset*>(handle);
CHECK_GT(num_used_row_indices, 0);
const int32_t lower = 0;
const int32_t upper = full_dataset->num_data() - 1;
CheckElementsIntervalClosed(used_row_indices, lower, upper, num_used_row_indices, "Used indices of subset");
if (!std::is_sorted(used_row_indices, used_row_indices + num_used_row_indices)) {
Log::Fatal("used_row_indices should be sorted in Subset");
}
auto ret = std::unique_ptr<Dataset>(new Dataset(num_used_row_indices));
ret->CopyFeatureMapperFrom(full_dataset);
ret->CopySubrow(full_dataset, used_row_indices, num_used_row_indices, true);
*out = ret.release();
API_END();
}
int LGBM_DatasetSetFeatureNames(
DatasetHandle handle,
const char** feature_names,
int num_feature_names) {
API_BEGIN();
auto dataset = reinterpret_cast<Dataset*>(handle);
std::vector<std::string> feature_names_str;
for (int i = 0; i < num_feature_names; ++i) {
feature_names_str.emplace_back(feature_names[i]);
}
dataset->set_feature_names(feature_names_str);
API_END();
}
int LGBM_DatasetGetFeatureNames(
DatasetHandle handle,
const int len,
int* num_feature_names,
const size_t buffer_len,
size_t* out_buffer_len,
char** feature_names) {
API_BEGIN();
*out_buffer_len = 0;
auto dataset = reinterpret_cast<Dataset*>(handle);
auto inside_feature_name = dataset->feature_names();
*num_feature_names = static_cast<int>(inside_feature_name.size());
for (int i = 0; i < *num_feature_names; ++i) {
if (i < len) {
std::memcpy(feature_names[i], inside_feature_name[i].c_str(), std::min(inside_feature_name[i].size() + 1, buffer_len));
feature_names[i][buffer_len - 1] = '\0';
}
*out_buffer_len = std::max(inside_feature_name[i].size() + 1, *out_buffer_len);
}
API_END();
}
#ifdef _MSC_VER
#pragma warning(disable : 4702)
#endif
int LGBM_DatasetFree(DatasetHandle handle) {
API_BEGIN();
delete reinterpret_cast<Dataset*>(handle);
API_END();
}
int LGBM_DatasetSaveBinary(DatasetHandle handle,
const char* filename) {
API_BEGIN();
auto dataset = reinterpret_cast<Dataset*>(handle);
dataset->SaveBinaryFile(filename);
API_END();
}
int LGBM_DatasetSerializeReferenceToBinary(DatasetHandle handle,
ByteBufferHandle* out,
int32_t* out_len) {
API_BEGIN();
auto dataset = reinterpret_cast<Dataset*>(handle);
std::unique_ptr<LightGBM::ByteBuffer> ret;
ret.reset(new LightGBM::ByteBuffer());
dataset->SerializeReference(ret.get());
*out_len = static_cast<int32_t>(ret->GetSize());
*out = ret.release();
API_END();
}
int LGBM_DatasetDumpText(DatasetHandle handle,
const char* filename) {
API_BEGIN();
auto dataset = reinterpret_cast<Dataset*>(handle);
dataset->DumpTextFile(filename);
API_END();
}
int LGBM_DatasetSetField(DatasetHandle handle,
const char* field_name,
const void* field_data,
int num_element,
int type) {
API_BEGIN();
auto dataset = reinterpret_cast<Dataset*>(handle);
bool is_success = false;
if (type == C_API_DTYPE_FLOAT32) {
is_success = dataset->SetFloatField(field_name, reinterpret_cast<const float*>(field_data), static_cast<int32_t>(num_element));
} else if (type == C_API_DTYPE_INT32) {
is_success = dataset->SetIntField(field_name, reinterpret_cast<const int*>(field_data), static_cast<int32_t>(num_element));
} else if (type == C_API_DTYPE_FLOAT64) {
is_success = dataset->SetDoubleField(field_name, reinterpret_cast<const double*>(field_data), static_cast<int32_t>(num_element));
}
if (!is_success) {
Log::Fatal("Input data type error or field not found");
}
API_END();
}
#ifndef LGB_R_BUILD
[[deprecated("Use LGBM_DatasetSetFieldFromArrowStream instead.")]]
int LGBM_DatasetSetFieldFromArrow(DatasetHandle handle,
const char* field_name,
int64_t n_chunks,
ArrowArray* chunks,
ArrowSchema* schema) {
API_BEGIN();
Log::Warning("LGBM_DatasetSetFieldFromArrow is deprecated. Please use LGBM_DatasetSetFieldFromArrowStream instead.");
auto dataset = reinterpret_cast<Dataset*>(handle);
auto is_success = dataset->SetFieldFromArrow(field_name, n_chunks, chunks, schema);
if (!is_success) {
Log::Fatal("Input field is not supported");
}
API_END();
}
int LGBM_DatasetSetFieldFromArrowStream(DatasetHandle handle,
const char* field_name,
ArrowArrayStream* stream) {
API_BEGIN();
auto dataset = reinterpret_cast<Dataset*>(handle);
auto is_success = dataset->SetFieldFromArrow(field_name, stream);
if (!is_success) {
Log::Fatal("Input field is not supported");
}
API_END();
}
#endif
int LGBM_DatasetGetField(DatasetHandle handle,
const char* field_name,
int* out_len,
const void** out_ptr,
int* out_type) {
API_BEGIN();
auto dataset = reinterpret_cast<Dataset*>(handle);
bool is_success = false;
if (dataset->GetFloatField(field_name, out_len, reinterpret_cast<const float**>(out_ptr))) {
*out_type = C_API_DTYPE_FLOAT32;
is_success = true;
} else if (dataset->GetIntField(field_name, out_len, reinterpret_cast<const int**>(out_ptr))) {
*out_type = C_API_DTYPE_INT32;
is_success = true;
} else if (dataset->GetDoubleField(field_name, out_len, reinterpret_cast<const double**>(out_ptr))) {
*out_type = C_API_DTYPE_FLOAT64;
is_success = true;
}
if (!is_success) {
Log::Fatal("Field not found");
}
if (*out_ptr == nullptr) {
*out_len = 0;
}
API_END();
}
int LGBM_DatasetUpdateParamChecking(const char* old_parameters, const char* new_parameters) {
API_BEGIN();
auto old_param = Config::Str2Map(old_parameters);
Config old_config;
old_config.Set(old_param);
auto new_param = Config::Str2Map(new_parameters);
Booster::CheckDatasetResetConfig(old_config, new_param);
API_END();
}
int LGBM_DatasetGetNumData(DatasetHandle handle,
int* out) {
API_BEGIN();
auto dataset = reinterpret_cast<Dataset*>(handle);
*out = dataset->num_data();
API_END();
}
int LGBM_DatasetGetNumFeature(DatasetHandle handle,
int* out) {
API_BEGIN();
auto dataset = reinterpret_cast<Dataset*>(handle);
*out = dataset->num_total_features();
API_END();
}
int LGBM_DatasetGetFeatureNumBin(DatasetHandle handle,
int feature,
int* out) {
API_BEGIN();
auto dataset = reinterpret_cast<Dataset*>(handle);
int num_features = dataset->num_total_features();
if (feature < 0 || feature >= num_features) {
Log::Fatal("Tried to retrieve number of bins for feature index %d, "
"but the valid feature indices are [0, %d].", feature, num_features - 1);
}
int inner_idx = dataset->InnerFeatureIndex(feature);
if (inner_idx >= 0) {
*out = dataset->FeatureNumBin(inner_idx);
} else {
*out = 0;
}
API_END();
}
int LGBM_DatasetAddFeaturesFrom(DatasetHandle target,
DatasetHandle source) {
API_BEGIN();
auto target_d = reinterpret_cast<Dataset*>(target);
auto source_d = reinterpret_cast<Dataset*>(source);
target_d->AddFeaturesFrom(source_d);
API_END();
}
int LGBM_BoosterCreate(const DatasetHandle train_data,
const char* parameters,
BoosterHandle* out) {
API_BEGIN();
const Dataset* p_train_data = reinterpret_cast<const Dataset*>(train_data);
auto ret = std::unique_ptr<Booster>(new Booster(p_train_data, parameters));
*out = ret.release();
API_END();
}
int LGBM_BoosterCreateFromModelfile(
const char* filename,
int* out_num_iterations,
BoosterHandle* out) {
API_BEGIN();
auto ret = std::unique_ptr<Booster>(new Booster(filename));
*out_num_iterations = ret->GetBoosting()->GetCurrentIteration();
*out = ret.release();
API_END();
}
int LGBM_BoosterLoadModelFromString(
const char* model_str,
int* out_num_iterations,
BoosterHandle* out) {
API_BEGIN();
auto ret = std::unique_ptr<Booster>(new Booster(nullptr));
ret->LoadModelFromString(model_str);
*out_num_iterations = ret->GetBoosting()->GetCurrentIteration();
*out = ret.release();
API_END();
}
int LGBM_BoosterGetLoadedParam(
BoosterHandle handle,
int64_t buffer_len,
int64_t* out_len,
char* out_str) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
std::string params = ref_booster->GetBoosting()->GetLoadedParam();
*out_len = static_cast<int64_t>(params.size()) + 1;
if (*out_len <= buffer_len) {
std::memcpy(out_str, params.c_str(), *out_len);
}
API_END();
}
#ifdef _MSC_VER
#pragma warning(disable : 4702)
#endif
int LGBM_BoosterFree(BoosterHandle handle) {
API_BEGIN();
delete reinterpret_cast<Booster*>(handle);
API_END();
}
int LGBM_BoosterShuffleModels(BoosterHandle handle, int start_iter, int end_iter) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
ref_booster->ShuffleModels(start_iter, end_iter);
API_END();
}
int LGBM_BoosterMerge(BoosterHandle handle,
BoosterHandle other_handle) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
Booster* ref_other_booster = reinterpret_cast<Booster*>(other_handle);
ref_booster->MergeFrom(ref_other_booster);
API_END();
}
int LGBM_BoosterAddValidData(BoosterHandle handle,
const DatasetHandle valid_data) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
const Dataset* p_dataset = reinterpret_cast<const Dataset*>(valid_data);
ref_booster->AddValidData(p_dataset);
API_END();
}
int LGBM_BoosterResetTrainingData(BoosterHandle handle,
const DatasetHandle train_data) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
const Dataset* p_dataset = reinterpret_cast<const Dataset*>(train_data);
ref_booster->ResetTrainingData(p_dataset);
API_END();
}
int LGBM_BoosterResetParameter(BoosterHandle handle, const char* parameters) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
ref_booster->ResetConfig(parameters);
API_END();
}
int LGBM_BoosterGetNumClasses(BoosterHandle handle, int* out_len) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
*out_len = ref_booster->GetBoosting()->NumberOfClasses();
API_END();
}
int LGBM_BoosterGetLinear(BoosterHandle handle, int* out) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
if (ref_booster->GetBoosting()->IsLinear()) {
*out = 1;
} else {
*out = 0;
}
API_END();
}
int LGBM_BoosterRefit(BoosterHandle handle, const int32_t* leaf_preds, int32_t nrow, int32_t ncol) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
ref_booster->Refit(leaf_preds, nrow, ncol);
API_END();
}
int LGBM_BoosterUpdateOneIter(BoosterHandle handle, int* produced_empty_tree) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
if (ref_booster->TrainOneIter()) {
*produced_empty_tree = 1;
} else {
*produced_empty_tree = 0;
}
API_END();
}
int LGBM_BoosterUpdateOneIterCustom(BoosterHandle handle,
const float* grad,
const float* hess,
int* produced_empty_tree) {
API_BEGIN();
#ifdef SCORE_T_USE_DOUBLE
(void) handle;
(void) grad;
(void) hess;
(void) produced_empty_tree;
Log::Fatal("Don't support custom loss function when SCORE_T_USE_DOUBLE is enabled");
#else
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
if (ref_booster->TrainOneIter(grad, hess)) {
*produced_empty_tree = 1;
} else {
*produced_empty_tree = 0;
}
#endif
API_END();
}
int LGBM_BoosterRollbackOneIter(BoosterHandle handle) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
ref_booster->RollbackOneIter();
API_END();
}
int LGBM_BoosterGetCurrentIteration(BoosterHandle handle, int* out_iteration) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
*out_iteration = ref_booster->GetBoosting()->GetCurrentIteration();
API_END();
}
int LGBM_BoosterNumModelPerIteration(BoosterHandle handle, int* out_tree_per_iteration) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
*out_tree_per_iteration = ref_booster->GetBoosting()->NumModelPerIteration();
API_END();
}
int LGBM_BoosterNumberOfTotalModel(BoosterHandle handle, int* out_models) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
*out_models = ref_booster->GetBoosting()->NumberOfTotalModel();
API_END();
}
int LGBM_BoosterGetEvalCounts(BoosterHandle handle, int* out_len) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
*out_len = ref_booster->GetEvalCounts();
API_END();
}
int LGBM_BoosterGetEvalNames(BoosterHandle handle,
const int len,
int* out_len,
const size_t buffer_len,
size_t* out_buffer_len,
char** out_strs) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
*out_len = ref_booster->GetEvalNames(out_strs, len, buffer_len, out_buffer_len);
API_END();
}
int LGBM_BoosterGetFeatureNames(BoosterHandle handle,
const int len,
int* out_len,
const size_t buffer_len,
size_t* out_buffer_len,
char** out_strs) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
*out_len = ref_booster->GetFeatureNames(out_strs, len, buffer_len, out_buffer_len);
API_END();
}
int LGBM_BoosterGetNumFeature(BoosterHandle handle, int* out_len) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
*out_len = ref_booster->GetBoosting()->MaxFeatureIdx() + 1;
API_END();
}
int LGBM_BoosterGetEval(BoosterHandle handle,
int data_idx,
int* out_len,
double* out_results) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
auto boosting = ref_booster->GetBoosting();
auto result_buf = boosting->GetEvalAt(data_idx);
*out_len = static_cast<int>(result_buf.size());
for (size_t i = 0; i < result_buf.size(); ++i) {
(out_results)[i] = static_cast<double>(result_buf[i]);
}
API_END();
}
int LGBM_BoosterGetNumPredict(BoosterHandle handle,
int data_idx,
int64_t* out_len) {
API_BEGIN();
auto boosting = reinterpret_cast<Booster*>(handle)->GetBoosting();
*out_len = boosting->GetNumPredictAt(data_idx);
API_END();
}
int LGBM_BoosterGetPredict(BoosterHandle handle,
int data_idx,
int64_t* out_len,
double* out_result) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
ref_booster->GetPredictAt(data_idx, out_result, out_len);
API_END();
}
int LGBM_BoosterPredictForFile(BoosterHandle handle,
const char* data_filename,
int data_has_header,
int predict_type,
int start_iteration,
int num_iteration,
const char* parameter,
const char* result_filename) {
API_BEGIN();
auto param = Config::Str2Map(parameter);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
ref_booster->Predict(start_iteration, num_iteration, predict_type, data_filename, data_has_header,
config, result_filename);
API_END();
}
int LGBM_BoosterCalcNumPredict(BoosterHandle handle,
int num_row,
int predict_type,
int start_iteration,
int num_iteration,
int64_t* out_len) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
*out_len = static_cast<int64_t>(num_row) * ref_booster->GetBoosting()->NumPredictOneRow(start_iteration,
num_iteration, predict_type == C_API_PREDICT_LEAF_INDEX, predict_type == C_API_PREDICT_CONTRIB);
API_END();
}
int LGBM_FastConfigFree(FastConfigHandle fastConfig) {
API_BEGIN();
delete reinterpret_cast<SingleRowPredictor*>(fastConfig);
API_END();
}
int LGBM_BoosterPredictForCSR(BoosterHandle handle,
const void* indptr,
int indptr_type,
const int32_t* indices,
const void* data,
int data_type,
int64_t nindptr,
int64_t nelem,
int64_t num_col,
int predict_type,
int start_iteration,
int num_iteration,
const char* parameter,
int64_t* out_len,
double* out_result) {
API_BEGIN();
if (num_col <= 0) {
Log::Fatal("The number of columns should be greater than zero.");
} else if (num_col >= INT32_MAX) {
Log::Fatal("The number of columns should be smaller than INT32_MAX.");
}
auto param = Config::Str2Map(parameter);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
auto get_row_fun = RowFunctionFromCSR<int>(indptr, indptr_type, indices, data, data_type, nindptr, nelem);
int nrow = static_cast<int>(nindptr - 1);
ref_booster->Predict(start_iteration, num_iteration, predict_type, nrow, static_cast<int>(num_col), get_row_fun,
config, out_result, out_len);
API_END();
}
int LGBM_BoosterPredictSparseOutput(BoosterHandle handle,
const void* indptr,
int indptr_type,
const int32_t* indices,
const void* data,
int data_type,
int64_t nindptr,
int64_t nelem,
int64_t num_col_or_row,
int predict_type,
int start_iteration,
int num_iteration,
const char* parameter,
int matrix_type,
int64_t* out_len,
void** out_indptr,
int32_t** out_indices,
void** out_data) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
auto param = Config::Str2Map(parameter);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
if (matrix_type == C_API_MATRIX_TYPE_CSR) {
if (num_col_or_row <= 0) {
Log::Fatal("The number of columns should be greater than zero.");
} else if (num_col_or_row >= INT32_MAX) {
Log::Fatal("The number of columns should be smaller than INT32_MAX.");
}
auto get_row_fun = RowFunctionFromCSR<int64_t>(indptr, indptr_type, indices, data, data_type, nindptr, nelem);
int64_t nrow = nindptr - 1;
ref_booster->PredictSparseCSR(start_iteration, num_iteration, predict_type, nrow, static_cast<int>(num_col_or_row), get_row_fun,
config, out_len, out_indptr, indptr_type, out_indices, out_data, data_type);
} else if (matrix_type == C_API_MATRIX_TYPE_CSC) {
int num_threads = OMP_NUM_THREADS();
int ncol = static_cast<int>(nindptr - 1);
std::vector<std::vector<CSC_RowIterator>> iterators(num_threads, std::vector<CSC_RowIterator>());
for (int i = 0; i < num_threads; ++i) {
for (int j = 0; j < ncol; ++j) {
iterators[i].emplace_back(indptr, indptr_type, indices, data, data_type, nindptr, nelem, j);
}
}
std::function<std::vector<std::pair<int, double>>(int64_t row_idx)> get_row_fun =
[&iterators, ncol](int64_t i) {
std::vector<std::pair<int, double>> one_row;
one_row.reserve(ncol);
const int tid = omp_get_thread_num();
for (int j = 0; j < ncol; ++j) {
auto val = iterators[tid][j].Get(static_cast<int>(i));
if (std::fabs(val) > kZeroThreshold || std::isnan(val)) {
one_row.emplace_back(j, val);
}
}
return one_row;
};
ref_booster->PredictSparseCSC(start_iteration, num_iteration, predict_type, num_col_or_row, ncol, get_row_fun, config,
out_len, out_indptr, indptr_type, out_indices, out_data, data_type);
} else {
Log::Fatal("Unknown matrix type in LGBM_BoosterPredictSparseOutput");
}
API_END();
}
int LGBM_BoosterFreePredictSparse(void* indptr, int32_t* indices, void* data, int indptr_type, int data_type) {
API_BEGIN();
if (indptr_type == C_API_DTYPE_INT32) {
delete[] reinterpret_cast<int32_t*>(indptr);
} else if (indptr_type == C_API_DTYPE_INT64) {
delete[] reinterpret_cast<int64_t*>(indptr);
} else {
Log::Fatal("Unknown indptr type in LGBM_BoosterFreePredictSparse");
}
delete[] indices;
if (data_type == C_API_DTYPE_FLOAT32) {
delete[] reinterpret_cast<float*>(data);
} else if (data_type == C_API_DTYPE_FLOAT64) {
delete[] reinterpret_cast<double*>(data);
} else {
Log::Fatal("Unknown data type in LGBM_BoosterFreePredictSparse");
}
API_END();
}
int LGBM_BoosterPredictForCSRSingleRow(BoosterHandle handle,
const void* indptr,
int indptr_type,
const int32_t* indices,
const void* data,
int data_type,
int64_t nindptr,
int64_t nelem,
int64_t num_col,
int predict_type,
int start_iteration,
int num_iteration,
const char* parameter,
int64_t* out_len,
double* out_result) {
API_BEGIN();
if (num_col <= 0) {
Log::Fatal("The number of columns should be greater than zero.");
} else if (num_col >= INT32_MAX) {
Log::Fatal("The number of columns should be smaller than INT32_MAX.");
}
auto param = Config::Str2Map(parameter);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
auto get_row_fun = RowFunctionFromCSR<int>(indptr, indptr_type, indices, data, data_type, nindptr, nelem);
ref_booster->SetSingleRowPredictorInner(start_iteration, num_iteration, predict_type, config);
ref_booster->PredictSingleRow(predict_type, static_cast<int32_t>(num_col), get_row_fun, config, out_result, out_len);
API_END();
}
int LGBM_BoosterPredictForCSRSingleRowFastInit(BoosterHandle handle,
const int predict_type,
const int start_iteration,
const int num_iteration,
const int data_type,
const int64_t num_col,
const char* parameter,
FastConfigHandle *out_fastConfig) {
API_BEGIN();
if (num_col <= 0) {
Log::Fatal("The number of columns should be greater than zero.");
} else if (num_col >= INT32_MAX) {
Log::Fatal("The number of columns should be smaller than INT32_MAX.");
}
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
std::unique_ptr<SingleRowPredictor> single_row_predictor =
ref_booster->InitSingleRowPredictor(start_iteration, num_iteration, predict_type, data_type, static_cast<int32_t>(num_col), parameter);
OMP_SET_NUM_THREADS(single_row_predictor->config.num_threads);
*out_fastConfig = single_row_predictor.release();
API_END();
}
int LGBM_BoosterPredictForCSRSingleRowFast(FastConfigHandle fastConfig_handle,
const void* indptr,
const int indptr_type,
const int32_t* indices,
const void* data,
const int64_t nindptr,
const int64_t nelem,
int64_t* out_len,
double* out_result) {
API_BEGIN();
SingleRowPredictor *single_row_predictor = reinterpret_cast<SingleRowPredictor*>(fastConfig_handle);
auto get_row_fun = RowFunctionFromCSR<int>(indptr, indptr_type, indices, data, single_row_predictor->data_type, nindptr, nelem);
single_row_predictor->Predict(get_row_fun, out_result, out_len);
API_END();
}
int LGBM_BoosterPredictForCSC(BoosterHandle handle,
const void* col_ptr,
int col_ptr_type,
const int32_t* indices,
const void* data,
int data_type,
int64_t ncol_ptr,
int64_t nelem,
int64_t num_row,
int predict_type,
int start_iteration,
int num_iteration,
const char* parameter,
int64_t* out_len,
double* out_result) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
auto param = Config::Str2Map(parameter);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
int num_threads = OMP_NUM_THREADS();
int ncol = static_cast<int>(ncol_ptr - 1);
std::vector<std::vector<CSC_RowIterator>> iterators(num_threads, std::vector<CSC_RowIterator>());
for (int i = 0; i < num_threads; ++i) {
for (int j = 0; j < ncol; ++j) {
iterators[i].emplace_back(col_ptr, col_ptr_type, indices, data, data_type, ncol_ptr, nelem, j);
}
}
std::function<std::vector<std::pair<int, double>>(int row_idx)> get_row_fun =
[&iterators, ncol](int i) {
std::vector<std::pair<int, double>> one_row;
one_row.reserve(ncol);
const int tid = omp_get_thread_num();
for (int j = 0; j < ncol; ++j) {
auto val = iterators[tid][j].Get(i);
if (std::fabs(val) > kZeroThreshold || std::isnan(val)) {
one_row.emplace_back(j, val);
}
}
return one_row;
};
ref_booster->Predict(start_iteration, num_iteration, predict_type, static_cast<int>(num_row), ncol, get_row_fun, config,
out_result, out_len);
API_END();
}
int LGBM_BoosterValidateFeatureNames(BoosterHandle handle,
const char** data_names,
int data_num_features) {
API_BEGIN();
int booster_num_features;
size_t out_buffer_len;
LGBM_BoosterGetFeatureNames(handle, 0, &booster_num_features, 0, &out_buffer_len, nullptr);
if (booster_num_features != data_num_features) {
Log::Fatal("Model was trained on %d features, but got %d input features to predict.", booster_num_features, data_num_features);
}
std::vector<std::vector<char>> tmp_names(booster_num_features, std::vector<char>(out_buffer_len));
std::vector<char*> booster_names = Vector2Ptr(&tmp_names);
LGBM_BoosterGetFeatureNames(handle, data_num_features, &booster_num_features, out_buffer_len, &out_buffer_len, booster_names.data());
for (int i = 0; i < booster_num_features; ++i) {
if (strcmp(data_names[i], booster_names[i]) != 0) {
Log::Fatal("Expected '%s' at position %d but found '%s'", booster_names[i], i, data_names[i]);
}
}
API_END();
}
int LGBM_BoosterPredictForMat(BoosterHandle handle,
const void* data,
int data_type,
int32_t nrow,
int32_t ncol,
int is_row_major,
int predict_type,
int start_iteration,
int num_iteration,
const char* parameter,
int64_t* out_len,
double* out_result) {
API_BEGIN();
auto param = Config::Str2Map(parameter);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
auto get_row_fun = RowPairFunctionFromDenseMatrix(data, nrow, ncol, data_type, is_row_major);
ref_booster->Predict(start_iteration, num_iteration, predict_type, nrow, ncol, get_row_fun,
config, out_result, out_len);
API_END();
}
int LGBM_BoosterPredictForMatSingleRow(BoosterHandle handle,
const void* data,
int data_type,
int32_t ncol,
int is_row_major,
int predict_type,
int start_iteration,
int num_iteration,
const char* parameter,
int64_t* out_len,
double* out_result) {
API_BEGIN();
auto param = Config::Str2Map(parameter);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
auto get_row_fun = RowPairFunctionFromDenseMatrix(data, 1, ncol, data_type, is_row_major);
ref_booster->SetSingleRowPredictorInner(start_iteration, num_iteration, predict_type, config);
ref_booster->PredictSingleRow(predict_type, ncol, get_row_fun, config, out_result, out_len);
API_END();
}
int LGBM_BoosterPredictForMatSingleRowFastInit(BoosterHandle handle,
const int predict_type,
const int start_iteration,
const int num_iteration,
const int data_type,
const int32_t ncol,
const char* parameter,
FastConfigHandle *out_fastConfig) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
std::unique_ptr<SingleRowPredictor> single_row_predictor =
ref_booster->InitSingleRowPredictor(predict_type, start_iteration, num_iteration, data_type, ncol, parameter);
OMP_SET_NUM_THREADS(single_row_predictor->config.num_threads);
*out_fastConfig = single_row_predictor.release();
API_END();
}
int LGBM_BoosterPredictForMatSingleRowFast(FastConfigHandle fastConfig_handle,
const void* data,
int64_t* out_len,
double* out_result) {
API_BEGIN();
SingleRowPredictor *single_row_predictor = reinterpret_cast<SingleRowPredictor*>(fastConfig_handle);
auto get_row_fun = RowPairFunctionFromDenseMatrix(data, 1, single_row_predictor->num_cols, single_row_predictor->data_type, 1);
single_row_predictor->Predict(get_row_fun, out_result, out_len);
API_END();
}
int LGBM_BoosterPredictForMats(BoosterHandle handle,
const void** data,
int data_type,
int32_t nrow,
int32_t ncol,
int predict_type,
int start_iteration,
int num_iteration,
const char* parameter,
int64_t* out_len,
double* out_result) {
API_BEGIN();
auto param = Config::Str2Map(parameter);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
auto get_row_fun = RowPairFunctionFromDenseRows(data, ncol, data_type);
ref_booster->Predict(start_iteration, num_iteration, predict_type, nrow, ncol, get_row_fun, config, out_result, out_len);
API_END();
}
#ifndef LGB_R_BUILD
void LGBM_BoosterPredictForArrowChunkedArray(BoosterHandle handle,
ArrowChunkedArray& chunked_array,
int predict_type,
int start_iteration,
int num_iteration,
const char* parameter,
int64_t* out_len,
double* out_result) {
auto param = Config::Str2Map(parameter);
Config config;
config.Set(param);
OMP_SET_NUM_THREADS(config.num_threads);
auto chunked_array_view = chunked_array.view();
std::vector<std::function<double(int64_t)>> accessors;
accessors.reserve(chunked_array.get_num_fields());
for (int64_t j = 0; j < chunked_array.get_num_fields(); ++j) {
chunked_array_view.field(j).visit<double>([&](auto&& visitor) {
accessors.emplace_back([visitor](int64_t i) { return visitor.begin()[i]; });
});
}
auto num_columns = chunked_array.get_num_fields();
auto row_fn = [num_columns, &accessors] (int row_idx) {
std::vector<std::pair<int, double>> result;
result.reserve(num_columns);
for (int64_t j = 0; j < num_columns; ++j) {
result.emplace_back(j, accessors[j](row_idx));
}
return result;
};
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
ref_booster->Predict(start_iteration,
num_iteration,
predict_type,
static_cast<int>(chunked_array.get_length()),
static_cast<int>(chunked_array.get_num_fields()),
row_fn,
config,
out_result,
out_len);
}
[[deprecated("Use LGBM_BoosterPredictForArrowStream instead.")]]
int LGBM_BoosterPredictForArrow(BoosterHandle handle,
int64_t n_chunks,
ArrowArray* chunks,
ArrowSchema* schema,
int predict_type,
int start_iteration,
int num_iteration,
const char* parameter,
int64_t* out_len,
double* out_result) {
API_BEGIN();
Log::Warning("LGBM_BoosterPredictForArrow is deprecated. Please use LGBM_BoosterPredictForArrowStream instead.");
ArrowChunkedArray chunked_array(n_chunks, chunks, schema);
LGBM_BoosterPredictForArrowChunkedArray(
handle, chunked_array, predict_type, start_iteration, num_iteration, parameter, out_len, out_result);
API_END();
}
int LGBM_BoosterPredictForArrowStream(BoosterHandle handle,
ArrowArrayStream* stream,
int predict_type,
int start_iteration,
int num_iteration,
const char* parameter,
int64_t* out_len,
double* out_result) {
API_BEGIN();
ArrowChunkedArray chunked_array(stream);
LGBM_BoosterPredictForArrowChunkedArray(
handle, chunked_array, predict_type, start_iteration, num_iteration, parameter, out_len, out_result);
API_END();
}
#endif
int LGBM_BoosterSaveModel(BoosterHandle handle,
int start_iteration,
int num_iteration,
int feature_importance_type,
const char* filename) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
ref_booster->SaveModelToFile(start_iteration, num_iteration,
feature_importance_type, filename);
API_END();
}
int LGBM_BoosterSaveModelToString(BoosterHandle handle,
int start_iteration,
int num_iteration,
int feature_importance_type,
int64_t buffer_len,
int64_t* out_len,
char* out_str) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
std::string model = ref_booster->SaveModelToString(
start_iteration, num_iteration, feature_importance_type);
*out_len = static_cast<int64_t>(model.size()) + 1;
if (*out_len <= buffer_len) {
std::memcpy(out_str, model.c_str(), *out_len);
}
API_END();
}
int LGBM_BoosterDumpModel(BoosterHandle handle,
int start_iteration,
int num_iteration,
int feature_importance_type,
int64_t buffer_len,
int64_t* out_len,
char* out_str) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
std::string model = ref_booster->DumpModel(start_iteration, num_iteration,
feature_importance_type);
*out_len = static_cast<int64_t>(model.size()) + 1;
if (*out_len <= buffer_len) {
std::memcpy(out_str, model.c_str(), *out_len);
}
API_END();
}
int LGBM_BoosterGetLeafValue(BoosterHandle handle,
int tree_idx,
int leaf_idx,
double* out_val) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
*out_val = static_cast<double>(ref_booster->GetLeafValue(tree_idx, leaf_idx));
API_END();
}
int LGBM_BoosterSetLeafValue(BoosterHandle handle,
int tree_idx,
int leaf_idx,
double val) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
ref_booster->SetLeafValue(tree_idx, leaf_idx, val);
API_END();
}
int LGBM_BoosterFeatureImportance(BoosterHandle handle,
int num_iteration,
int importance_type,
double* out_results) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
std::vector<double> feature_importances = ref_booster->FeatureImportance(num_iteration, importance_type);
for (size_t i = 0; i < feature_importances.size(); ++i) {
(out_results)[i] = feature_importances[i];
}
API_END();
}
int LGBM_BoosterGetUpperBoundValue(BoosterHandle handle,
double* out_results) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
double max_value = ref_booster->UpperBoundValue();
*out_results = max_value;
API_END();
}
int LGBM_BoosterGetLowerBoundValue(BoosterHandle handle,
double* out_results) {
API_BEGIN();
Booster* ref_booster = reinterpret_cast<Booster*>(handle);
double min_value = ref_booster->LowerBoundValue();
*out_results = min_value;
API_END();
}
int LGBM_NetworkInit(const char* machines,
int local_listen_port,
int listen_time_out,
int num_machines) {
API_BEGIN();
Config config;
config.machines = RemoveQuotationSymbol(std::string(machines));
config.local_listen_port = local_listen_port;
config.num_machines = num_machines;
config.time_out = listen_time_out;
if (num_machines > 1) {
Network::Init(config);
}
API_END();
}
int LGBM_NetworkFree() {
API_BEGIN();
Network::Dispose();
API_END();
}
int LGBM_NetworkInitWithFunctions(int num_machines, int rank,
void* reduce_scatter_ext_fun,
void* allgather_ext_fun) {
API_BEGIN();
if (num_machines > 1) {
Network::Init(num_machines, rank, (ReduceScatterFunction)reduce_scatter_ext_fun, (AllgatherFunction)allgather_ext_fun);
}
API_END();
}
int LGBM_SetMaxThreads(int num_threads) {
API_BEGIN();
if (num_threads <= 0) {
LGBM_MAX_NUM_THREADS = -1;
} else {
LGBM_MAX_NUM_THREADS = num_threads;
}
API_END();
}
int LGBM_GetMaxThreads(int* out) {
API_BEGIN();
*out = LGBM_MAX_NUM_THREADS;
API_END();
}
template<typename T>
std::function<std::vector<double>(int row_idx)>
RowFunctionFromDenseMatrix_helper(const void* data, int num_row, int num_col, int is_row_major) {
const T* data_ptr = reinterpret_cast<const T*>(data);
if (is_row_major) {
return [=] (int row_idx) {
std::vector<double> ret(num_col);
auto tmp_ptr = data_ptr + static_cast<size_t>(num_col) * row_idx;
for (int i = 0; i < num_col; ++i) {
ret[i] = static_cast<double>(*(tmp_ptr + i));
}
return ret;
};
} else {
return [=] (int row_idx) {
std::vector<double> ret(num_col);
for (int i = 0; i < num_col; ++i) {
ret[i] = static_cast<double>(*(data_ptr + static_cast<size_t>(num_row) * i + row_idx));
}
return ret;
};
}
}
std::function<std::vector<double>(int row_idx)>
RowFunctionFromDenseMatrix(const void* data, int num_row, int num_col, int data_type, int is_row_major) {
if (data_type == C_API_DTYPE_FLOAT32) {
return RowFunctionFromDenseMatrix_helper<float>(data, num_row, num_col, is_row_major);
} else if (data_type == C_API_DTYPE_FLOAT64) {
return RowFunctionFromDenseMatrix_helper<double>(data, num_row, num_col, is_row_major);
}
Log::Fatal("Unknown data type in RowFunctionFromDenseMatrix");
return nullptr;
}
std::function<std::vector<std::pair<int, double>>(int row_idx)>
RowPairFunctionFromDenseMatrix(const void* data, int num_row, int num_col, int data_type, int is_row_major) {
auto inner_function = RowFunctionFromDenseMatrix(data, num_row, num_col, data_type, is_row_major);
if (inner_function != nullptr) {
return [inner_function] (int row_idx) {
auto raw_values = inner_function(row_idx);
std::vector<std::pair<int, double>> ret;
ret.reserve(raw_values.size());
for (int i = 0; i < static_cast<int>(raw_values.size()); ++i) {
if (std::fabs(raw_values[i]) > kZeroThreshold || std::isnan(raw_values[i])) {
ret.emplace_back(i, raw_values[i]);
}
}
return ret;
};
}
return nullptr;
}
std::function<std::vector<std::pair<int, double>>(int row_idx)>
RowPairFunctionFromDenseRows(const void** data, int num_col, int data_type) {
return [=](int row_idx) {
auto inner_function = RowFunctionFromDenseMatrix(data[row_idx], 1, num_col, data_type, true);
auto raw_values = inner_function(0);
std::vector<std::pair<int, double>> ret;
ret.reserve(raw_values.size());
for (int i = 0; i < static_cast<int>(raw_values.size()); ++i) {
if (std::fabs(raw_values[i]) > kZeroThreshold || std::isnan(raw_values[i])) {
ret.emplace_back(i, raw_values[i]);
}
}
return ret;
};
}
template<typename T, typename T1, typename T2>
std::function<std::vector<std::pair<int, double>>(T idx)>
RowFunctionFromCSR_helper(const void* indptr, const int32_t* indices, const void* data) {
const T1* data_ptr = reinterpret_cast<const T1*>(data);
const T2* ptr_indptr = reinterpret_cast<const T2*>(indptr);
return [=] (T idx) {
std::vector<std::pair<int, double>> ret;
int64_t start = ptr_indptr[idx];
int64_t end = ptr_indptr[idx + 1];
if (end - start > 0) {
ret.reserve(end - start);
}
for (int64_t i = start; i < end; ++i) {
ret.emplace_back(indices[i], data_ptr[i]);
}
return ret;
};
}
template<typename T>
std::function<std::vector<std::pair<int, double>>(T idx)>
RowFunctionFromCSR(const void* indptr, int indptr_type, const int32_t* indices, const void* data, int data_type, int64_t , int64_t ) {
if (data_type == C_API_DTYPE_FLOAT32) {
if (indptr_type == C_API_DTYPE_INT32) {
return RowFunctionFromCSR_helper<T, float, int32_t>(indptr, indices, data);
} else if (indptr_type == C_API_DTYPE_INT64) {
return RowFunctionFromCSR_helper<T, float, int64_t>(indptr, indices, data);
}
} else if (data_type == C_API_DTYPE_FLOAT64) {
if (indptr_type == C_API_DTYPE_INT32) {
return RowFunctionFromCSR_helper<T, double, int32_t>(indptr, indices, data);
} else if (indptr_type == C_API_DTYPE_INT64) {
return RowFunctionFromCSR_helper<T, double, int64_t>(indptr, indices, data);
}
}
Log::Fatal("Unknown data type in RowFunctionFromCSR");
return nullptr;
}
template <typename T1, typename T2>
std::function<std::pair<int, double>(int idx)> IterateFunctionFromCSC_helper(const void* col_ptr, const int32_t* indices, const void* data, int col_idx) {
const T1* data_ptr = reinterpret_cast<const T1*>(data);
const T2* ptr_col_ptr = reinterpret_cast<const T2*>(col_ptr);
int64_t start = ptr_col_ptr[col_idx];
int64_t end = ptr_col_ptr[col_idx + 1];
return [=] (int offset) {
int64_t i = static_cast<int64_t>(start + offset);
if (i >= end) {
return std::make_pair(-1, 0.0);
}
int idx = static_cast<int>(indices[i]);
double val = static_cast<double>(data_ptr[i]);
return std::make_pair(idx, val);
};
}
std::function<std::pair<int, double>(int idx)>
IterateFunctionFromCSC(const void* col_ptr, int col_ptr_type, const int32_t* indices, const void* data, int data_type, int64_t ncol_ptr, int64_t , int col_idx) {
CHECK(col_idx < ncol_ptr && col_idx >= 0);
if (data_type == C_API_DTYPE_FLOAT32) {
if (col_ptr_type == C_API_DTYPE_INT32) {
return IterateFunctionFromCSC_helper<float, int32_t>(col_ptr, indices, data, col_idx);
} else if (col_ptr_type == C_API_DTYPE_INT64) {
return IterateFunctionFromCSC_helper<float, int64_t>(col_ptr, indices, data, col_idx);
}
} else if (data_type == C_API_DTYPE_FLOAT64) {
if (col_ptr_type == C_API_DTYPE_INT32) {
return IterateFunctionFromCSC_helper<double, int32_t>(col_ptr, indices, data, col_idx);
} else if (col_ptr_type == C_API_DTYPE_INT64) {
return IterateFunctionFromCSC_helper<double, int64_t>(col_ptr, indices, data, col_idx);
}
}
Log::Fatal("Unknown data type in CSC matrix");
return nullptr;
}
CSC_RowIterator::CSC_RowIterator(const void* col_ptr, int col_ptr_type, const int32_t* indices,
const void* data, int data_type, int64_t ncol_ptr, int64_t nelem, int col_idx) {
iter_fun_ = IterateFunctionFromCSC(col_ptr, col_ptr_type, indices, data, data_type, ncol_ptr, nelem, col_idx);
}
double CSC_RowIterator::Get(int idx) {
while (idx > cur_idx_ && !is_end_) {
auto ret = iter_fun_(nonzero_idx_);
if (ret.first < 0) {
is_end_ = true;
break;
}
cur_idx_ = ret.first;
cur_val_ = ret.second;
++nonzero_idx_;
}
if (idx == cur_idx_) {
return cur_val_;
} else {
return 0.0f;
}
}
std::pair<int, double> CSC_RowIterator::NextNonZero() {
if (!is_end_) {
auto ret = iter_fun_(nonzero_idx_);
++nonzero_idx_;
if (ret.first < 0) {
is_end_ = true;
}
return ret;
} else {
return std::make_pair(-1, 0.0);
}
}