1 /*!
2  * Copyright 2019-2020 XGBoost contributors
3  */
4 #include <gtest/gtest.h>
5 #include <xgboost/version_config.h>
6 #include <xgboost/c_api.h>
7 #include <xgboost/data.h>
8 #include <xgboost/learner.h>
9 
10 #include "../helpers.h"
11 #include "../../../src/common/io.h"
12 
13 #include "../../../src/c_api/c_api_error.h"
14 
TEST(CAPI,XGDMatrixCreateFromMatDT)15 TEST(CAPI, XGDMatrixCreateFromMatDT) {
16   std::vector<int> col0 = {0, -1, 3};
17   std::vector<float> col1 = {-4.0f, 2.0f, 0.0f};
18   const char *col0_type = "int32";
19   const char *col1_type = "float32";
20   std::vector<void *> data = {col0.data(), col1.data()};
21   std::vector<const char *> types = {col0_type, col1_type};
22   DMatrixHandle handle;
23   XGDMatrixCreateFromDT(data.data(), types.data(), 3, 2, &handle,
24                         0);
25   std::shared_ptr<xgboost::DMatrix> *dmat =
26       static_cast<std::shared_ptr<xgboost::DMatrix> *>(handle);
27   xgboost::MetaInfo &info = (*dmat)->Info();
28   ASSERT_EQ(info.num_col_, 2ul);
29   ASSERT_EQ(info.num_row_, 3ul);
30   ASSERT_EQ(info.num_nonzero_, 6ul);
31 
32   for (const auto &batch : (*dmat)->GetBatches<xgboost::SparsePage>()) {
33     auto page = batch.GetView();
34     ASSERT_EQ(page[0][0].fvalue, 0.0f);
35     ASSERT_EQ(page[0][1].fvalue, -4.0f);
36     ASSERT_EQ(page[2][0].fvalue, 3.0f);
37     ASSERT_EQ(page[2][1].fvalue, 0.0f);
38   }
39 
40   delete dmat;
41 }
42 
TEST(CAPI,XGDMatrixCreateFromMatOmp)43 TEST(CAPI, XGDMatrixCreateFromMatOmp) {
44   std::vector<bst_ulong> num_rows = {100, 11374, 15000};
45   for (auto row : num_rows) {
46     bst_ulong num_cols = 50;
47     int num_missing = 5;
48     DMatrixHandle handle;
49     std::vector<float> data(num_cols * row, 1.5);
50     for (int i = 0; i < num_missing; i++) {
51       data[i] = std::numeric_limits<float>::quiet_NaN();
52     }
53 
54     XGDMatrixCreateFromMat_omp(data.data(), row, num_cols,
55                                std::numeric_limits<float>::quiet_NaN(), &handle,
56                                0);
57 
58     std::shared_ptr<xgboost::DMatrix> *dmat =
59         static_cast<std::shared_ptr<xgboost::DMatrix> *>(handle);
60     xgboost::MetaInfo &info = (*dmat)->Info();
61     ASSERT_EQ(info.num_col_, num_cols);
62     ASSERT_EQ(info.num_row_, row);
63     ASSERT_EQ(info.num_nonzero_, num_cols * row - num_missing);
64 
65     for (const auto &batch : (*dmat)->GetBatches<xgboost::SparsePage>()) {
66       auto page = batch.GetView();
67       for (size_t i = 0; i < batch.Size(); i++) {
68         auto inst = page[i];
69         for (auto e : inst) {
70           ASSERT_EQ(e.fvalue, 1.5);
71         }
72       }
73     }
74     delete dmat;
75   }
76 }
77 
78 namespace xgboost {
79 
TEST(CAPI,Version)80 TEST(CAPI, Version) {
81   int patch {0};
82   XGBoostVersion(NULL, NULL, &patch);  // NOLINT
83   ASSERT_EQ(patch, XGBOOST_VER_PATCH);
84 }
85 
TEST(CAPI,ConfigIO)86 TEST(CAPI, ConfigIO) {
87   size_t constexpr kRows = 10;
88   auto p_dmat = RandomDataGenerator(kRows, 10, 0).GenerateDMatrix();
89   std::vector<std::shared_ptr<DMatrix>> mat {p_dmat};
90   std::vector<bst_float> labels(kRows);
91   for (size_t i = 0; i < labels.size(); ++i) {
92     labels[i] = i;
93   }
94   p_dmat->Info().labels_.HostVector() = labels;
95 
96   std::shared_ptr<Learner> learner { Learner::Create(mat) };
97 
98   BoosterHandle handle = learner.get();
99   learner->UpdateOneIter(0, p_dmat);
100 
101   char const* out[1];
102   bst_ulong len {0};
103   XGBoosterSaveJsonConfig(handle, &len, out);
104 
105   std::string config_str_0 { out[0] };
106   auto config_0 = Json::Load({config_str_0.c_str(), config_str_0.size()});
107   XGBoosterLoadJsonConfig(handle, out[0]);
108 
109   bst_ulong len_1 {0};
110   std::string config_str_1 { out[0] };
111   XGBoosterSaveJsonConfig(handle, &len_1, out);
112   auto config_1 = Json::Load({config_str_1.c_str(), config_str_1.size()});
113 
114   ASSERT_EQ(config_0, config_1);
115 }
116 
TEST(CAPI,JsonModelIO)117 TEST(CAPI, JsonModelIO) {
118   size_t constexpr kRows = 10;
119   size_t constexpr kCols = 10;
120   dmlc::TemporaryDirectory tempdir;
121 
122   auto p_dmat = RandomDataGenerator(kRows, kCols, 0).GenerateDMatrix();
123   std::vector<std::shared_ptr<DMatrix>> mat {p_dmat};
124   std::vector<bst_float> labels(kRows);
125   for (size_t i = 0; i < labels.size(); ++i) {
126     labels[i] = i;
127   }
128   p_dmat->Info().labels_.HostVector() = labels;
129 
130   std::shared_ptr<Learner> learner { Learner::Create(mat) };
131 
132   learner->UpdateOneIter(0, p_dmat);
133   BoosterHandle handle = learner.get();
134 
135   std::string modelfile_0 = tempdir.path + "/model_0.json";
136   XGBoosterSaveModel(handle, modelfile_0.c_str());
137   XGBoosterLoadModel(handle, modelfile_0.c_str());
138 
139   bst_ulong num_feature {0};
140   ASSERT_EQ(XGBoosterGetNumFeature(handle, &num_feature), 0);
141   ASSERT_EQ(num_feature, kCols);
142 
143   std::string modelfile_1 = tempdir.path + "/model_1.json";
144   XGBoosterSaveModel(handle, modelfile_1.c_str());
145 
146   auto model_str_0 = common::LoadSequentialFile(modelfile_0);
147   auto model_str_1 = common::LoadSequentialFile(modelfile_1);
148 
149   ASSERT_EQ(model_str_0.front(), '{');
150   ASSERT_EQ(model_str_0, model_str_1);
151 }
152 
TEST(CAPI,CatchDMLCError)153 TEST(CAPI, CatchDMLCError) {
154   DMatrixHandle out;
155   ASSERT_EQ(XGDMatrixCreateFromFile("foo", 0, &out), -1);
156   EXPECT_THROW({ dmlc::Stream::Create("foo", "r"); },  dmlc::Error);
157 }
158 
TEST(CAPI,DMatrixSetFeatureName)159 TEST(CAPI, DMatrixSetFeatureName) {
160   size_t constexpr kRows = 10;
161   bst_feature_t constexpr kCols = 2;
162 
163   DMatrixHandle handle;
164   std::vector<float> data(kCols * kRows, 1.5);
165 
166   XGDMatrixCreateFromMat_omp(data.data(), kRows, kCols,
167                              std::numeric_limits<float>::quiet_NaN(), &handle,
168                              0);
169   std::vector<std::string> feature_names;
170   for (bst_feature_t i = 0; i < kCols; ++i) {
171     feature_names.emplace_back(std::to_string(i));
172   }
173   std::vector<char const*> c_feature_names;
174   c_feature_names.resize(feature_names.size());
175   std::transform(feature_names.cbegin(), feature_names.cend(),
176                  c_feature_names.begin(),
177                  [](auto const &str) { return str.c_str(); });
178   XGDMatrixSetStrFeatureInfo(handle, u8"feature_name", c_feature_names.data(),
179                              c_feature_names.size());
180   bst_ulong out_len = 0;
181   char const **c_out_features;
182   XGDMatrixGetStrFeatureInfo(handle, u8"feature_name", &out_len,
183                              &c_out_features);
184 
185   CHECK_EQ(out_len, kCols);
186   std::vector<std::string> out_features;
187   for (bst_ulong i = 0; i < out_len; ++i) {
188     ASSERT_EQ(std::to_string(i), c_out_features[i]);
189   }
190 
191   char const* feat_types [] {"i", "q"};
192   static_assert(sizeof(feat_types)/ sizeof(feat_types[0]) == kCols, "");
193   XGDMatrixSetStrFeatureInfo(handle, "feature_type", feat_types, kCols);
194   char const **c_out_types;
195   XGDMatrixGetStrFeatureInfo(handle, u8"feature_type", &out_len,
196                              &c_out_types);
197   for (bst_ulong i = 0; i < out_len; ++i) {
198     ASSERT_STREQ(feat_types[i], c_out_types[i]);
199   }
200 
201   XGDMatrixFree(handle);
202 }
203 
TestExceptionCatching()204 int TestExceptionCatching() {
205   API_BEGIN();
206   throw std::bad_alloc();
207   API_END();
208 }
209 
TEST(CAPI,Exception)210 TEST(CAPI, Exception) {
211   ASSERT_NO_THROW({TestExceptionCatching();});
212   ASSERT_EQ(TestExceptionCatching(), -1);
213   auto error = XGBGetLastError();
214   // Not null
215   ASSERT_TRUE(error);
216 }
217 
TEST(CAPI,XGBGlobalConfig)218 TEST(CAPI, XGBGlobalConfig) {
219   int ret;
220   {
221     const char *config_str = R"json(
222     {
223       "verbosity": 0,
224       "use_rmm": false
225     }
226   )json";
227     ret = XGBSetGlobalConfig(config_str);
228     ASSERT_EQ(ret, 0);
229     const char *updated_config_cstr;
230     ret = XGBGetGlobalConfig(&updated_config_cstr);
231     ASSERT_EQ(ret, 0);
232 
233     std::string updated_config_str{updated_config_cstr};
234     auto updated_config =
235         Json::Load({updated_config_str.data(), updated_config_str.size()});
236     ASSERT_EQ(get<Integer>(updated_config["verbosity"]), 0);
237     ASSERT_EQ(get<Boolean>(updated_config["use_rmm"]), false);
238   }
239   {
240     const char *config_str = R"json(
241     {
242       "use_rmm": true
243     }
244   )json";
245     ret = XGBSetGlobalConfig(config_str);
246     ASSERT_EQ(ret, 0);
247     const char *updated_config_cstr;
248     ret = XGBGetGlobalConfig(&updated_config_cstr);
249     ASSERT_EQ(ret, 0);
250 
251     std::string updated_config_str{updated_config_cstr};
252     auto updated_config =
253         Json::Load({updated_config_str.data(), updated_config_str.size()});
254     ASSERT_EQ(get<Boolean>(updated_config["use_rmm"]), true);
255   }
256   {
257     const char *config_str = R"json(
258     {
259       "foo": 0
260     }
261   )json";
262     ret = XGBSetGlobalConfig(config_str);
263     ASSERT_EQ(ret , -1);
264     auto err = std::string{XGBGetLastError()};
265     ASSERT_NE(err.find("foo"), std::string::npos);
266   }
267   {
268     const char *config_str = R"json(
269     {
270       "foo": 0,
271       "verbosity": 0
272     }
273   )json";
274     ret = XGBSetGlobalConfig(config_str);
275     ASSERT_EQ(ret , -1);
276     auto err = std::string{XGBGetLastError()};
277     ASSERT_NE(err.find("foo"), std::string::npos);
278     ASSERT_EQ(err.find("verbosity"), std::string::npos);
279   }
280 }
281 }  // namespace xgboost
282