1 /**
2  * @file   unit-capi-sparse_neg.cc
3  *
4  * @section LICENSE
5  *
6  * The MIT License
7  *
8  * @copyright Copyright (c) 2017-2021 TileDB Inc.
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a copy
11  * of this software and associated documentation files (the "Software"), to deal
12  * in the Software without restriction, including without limitation the rights
13  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the Software is
15  * furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  *
28  * @section DESCRIPTION
29  *
30  * Tests of C API for sparse arrays with negative domains.
31  */
32 
33 #include "catch.hpp"
34 #include "test/src/helpers.h"
35 #include "test/src/vfs_helpers.h"
36 #ifdef _WIN32
37 #include "tiledb/sm/filesystem/win.h"
38 #else
39 #include "tiledb/sm/filesystem/posix.h"
40 #endif
41 #include "tiledb/sm/c_api/tiledb.h"
42 #include "tiledb/sm/misc/utils.h"
43 
44 #include <iostream>
45 #include <sstream>
46 #include <thread>
47 
48 using namespace tiledb::test;
49 
50 struct SparseNegFx2 {
51   // TileDB context
52   tiledb_ctx_t* ctx_;
53   tiledb_vfs_t* vfs_;
54 
55   // Vector of supported filsystems
56   const std::vector<std::unique_ptr<SupportedFs>> fs_vec_;
57 
58   // Functions
59   SparseNegFx2();
60   ~SparseNegFx2();
61   void create_temp_dir(const std::string& path);
62   void remove_temp_dir(const std::string& path);
63   void create_sparse_vector(const std::string& path);
64   void create_sparse_array(const std::string& path);
65   void write_sparse_vector(const std::string& path);
66   void write_sparse_array(const std::string& path);
67   void read_sparse_vector(const std::string& path);
68   void read_sparse_array_row(const std::string& path);
69   void read_sparse_array_col(const std::string& path);
70   static std::string random_name(const std::string& prefix);
71 };
72 
SparseNegFx2()73 SparseNegFx2::SparseNegFx2()
74     : fs_vec_(vfs_test_get_fs_vec()) {
75   // Initialize vfs test
76   REQUIRE(vfs_test_init(fs_vec_, &ctx_, &vfs_).ok());
77 }
78 
~SparseNegFx2()79 SparseNegFx2::~SparseNegFx2() {
80   // Close vfs test
81   REQUIRE(vfs_test_close(fs_vec_, ctx_, vfs_).ok());
82   tiledb_vfs_free(&vfs_);
83   tiledb_ctx_free(&ctx_);
84 }
85 
create_temp_dir(const std::string & path)86 void SparseNegFx2::create_temp_dir(const std::string& path) {
87   remove_temp_dir(path);
88   REQUIRE(tiledb_vfs_create_dir(ctx_, vfs_, path.c_str()) == TILEDB_OK);
89 }
90 
remove_temp_dir(const std::string & path)91 void SparseNegFx2::remove_temp_dir(const std::string& path) {
92   int is_dir = 0;
93   REQUIRE(tiledb_vfs_is_dir(ctx_, vfs_, path.c_str(), &is_dir) == TILEDB_OK);
94   if (is_dir)
95     REQUIRE(tiledb_vfs_remove_dir(ctx_, vfs_, path.c_str()) == TILEDB_OK);
96 }
97 
random_name(const std::string & prefix)98 std::string SparseNegFx2::random_name(const std::string& prefix) {
99   std::stringstream ss;
100   ss << prefix << "-" << std::this_thread::get_id() << "-"
101      << TILEDB_TIMESTAMP_NOW_MS;
102   return ss.str();
103 }
104 
create_sparse_vector(const std::string & path)105 void SparseNegFx2::create_sparse_vector(const std::string& path) {
106   int rc;
107   int64_t dim_domain[] = {-1, 2};
108   int64_t tile_extent = 2;
109 
110   // Create domain
111   tiledb_domain_t* domain;
112   rc = tiledb_domain_alloc(ctx_, &domain);
113   REQUIRE(rc == TILEDB_OK);
114   tiledb_dimension_t* dim;
115   rc = tiledb_dimension_alloc(
116       ctx_, "d0", TILEDB_INT64, dim_domain, &tile_extent, &dim);
117   REQUIRE(rc == TILEDB_OK);
118   rc = tiledb_domain_add_dimension(ctx_, domain, dim);
119   REQUIRE(rc == TILEDB_OK);
120 
121   // Create attribute
122   tiledb_attribute_t* attr;
123   rc = tiledb_attribute_alloc(ctx_, "a", TILEDB_INT32, &attr);
124   REQUIRE(rc == TILEDB_OK);
125 
126   // Create array schema
127   tiledb_array_schema_t* array_schema;
128   rc = tiledb_array_schema_alloc(ctx_, TILEDB_SPARSE, &array_schema);
129   REQUIRE(rc == TILEDB_OK);
130   rc = tiledb_array_schema_set_cell_order(ctx_, array_schema, TILEDB_ROW_MAJOR);
131   REQUIRE(rc == TILEDB_OK);
132   rc = tiledb_array_schema_set_tile_order(ctx_, array_schema, TILEDB_ROW_MAJOR);
133   REQUIRE(rc == TILEDB_OK);
134   rc = tiledb_array_schema_set_domain(ctx_, array_schema, domain);
135   REQUIRE(rc == TILEDB_OK);
136   rc = tiledb_array_schema_add_attribute(ctx_, array_schema, attr);
137   REQUIRE(rc == TILEDB_OK);
138 
139   rc = tiledb_array_schema_check(ctx_, array_schema);
140   REQUIRE(rc == TILEDB_OK);
141 
142   // Create array
143   rc = tiledb_array_create(ctx_, path.c_str(), array_schema);
144   REQUIRE(rc == TILEDB_OK);
145   tiledb_attribute_free(&attr);
146   tiledb_dimension_free(&dim);
147   tiledb_domain_free(&domain);
148   tiledb_array_schema_free(&array_schema);
149 }
150 
create_sparse_array(const std::string & path)151 void SparseNegFx2::create_sparse_array(const std::string& path) {
152   // Create dimensions
153   int64_t dim_domain[] = {-2, 1, -2, 1};
154   int64_t tile_extents[] = {2, 2};
155   tiledb_dimension_t* d1;
156   int rc = tiledb_dimension_alloc(
157       ctx_, "d1", TILEDB_INT64, &dim_domain[0], &tile_extents[0], &d1);
158   CHECK(rc == TILEDB_OK);
159   tiledb_dimension_t* d2;
160   rc = tiledb_dimension_alloc(
161       ctx_, "d2", TILEDB_INT64, &dim_domain[2], &tile_extents[1], &d2);
162   CHECK(rc == TILEDB_OK);
163 
164   // Create domain
165   tiledb_domain_t* domain;
166   rc = tiledb_domain_alloc(ctx_, &domain);
167   CHECK(rc == TILEDB_OK);
168   rc = tiledb_domain_add_dimension(ctx_, domain, d1);
169   CHECK(rc == TILEDB_OK);
170   rc = tiledb_domain_add_dimension(ctx_, domain, d2);
171   CHECK(rc == TILEDB_OK);
172 
173   // Create attributes
174   tiledb_attribute_t* a;
175   rc = tiledb_attribute_alloc(ctx_, "a", TILEDB_INT32, &a);
176   CHECK(rc == TILEDB_OK);
177   tiledb_filter_t* filter;
178   tiledb_filter_list_t* list;
179   rc = tiledb_filter_alloc(ctx_, TILEDB_FILTER_LZ4, &filter);
180   CHECK(rc == TILEDB_OK);
181   rc = tiledb_filter_list_alloc(ctx_, &list);
182   CHECK(rc == TILEDB_OK);
183   rc = tiledb_filter_list_add_filter(ctx_, list, filter);
184   CHECK(rc == TILEDB_OK);
185   rc = tiledb_attribute_set_filter_list(ctx_, a, list);
186   CHECK(rc == TILEDB_OK);
187 
188   // Create array schema
189   tiledb_array_schema_t* array_schema;
190   rc = tiledb_array_schema_alloc(ctx_, TILEDB_SPARSE, &array_schema);
191   CHECK(rc == TILEDB_OK);
192   rc = tiledb_array_schema_set_cell_order(ctx_, array_schema, TILEDB_ROW_MAJOR);
193   CHECK(rc == TILEDB_OK);
194   rc = tiledb_array_schema_set_tile_order(ctx_, array_schema, TILEDB_ROW_MAJOR);
195   CHECK(rc == TILEDB_OK);
196   rc = tiledb_array_schema_set_domain(ctx_, array_schema, domain);
197   CHECK(rc == TILEDB_OK);
198   rc = tiledb_array_schema_add_attribute(ctx_, array_schema, a);
199   CHECK(rc == TILEDB_OK);
200 
201   // Check array schema
202   rc = tiledb_array_schema_check(ctx_, array_schema);
203   CHECK(rc == TILEDB_OK);
204 
205   // Create array
206   rc = tiledb_array_create(ctx_, path.c_str(), array_schema);
207   CHECK(rc == TILEDB_OK);
208 
209   // Clean up
210   tiledb_filter_free(&filter);
211   tiledb_filter_list_free(&list);
212   tiledb_attribute_free(&a);
213   tiledb_dimension_free(&d1);
214   tiledb_dimension_free(&d2);
215   tiledb_domain_free(&domain);
216   tiledb_array_schema_free(&array_schema);
217 }
218 
write_sparse_vector(const std::string & path)219 void SparseNegFx2::write_sparse_vector(const std::string& path) {
220   // Open array
221   tiledb_array_t* array;
222   int rc = tiledb_array_alloc(ctx_, path.c_str(), &array);
223   CHECK(rc == TILEDB_OK);
224   rc = tiledb_array_open(ctx_, array, TILEDB_WRITE);
225   CHECK(rc == TILEDB_OK);
226 
227   int a[] = {0, 1};
228   uint64_t a_size = sizeof(a);
229   int64_t coords[] = {-1, 1};
230   uint64_t coords_size = sizeof(coords);
231   tiledb_query_t* query;
232   rc = tiledb_query_alloc(ctx_, array, TILEDB_WRITE, &query);
233   REQUIRE(rc == TILEDB_OK);
234   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a, &a_size);
235   REQUIRE(rc == TILEDB_OK);
236   rc = tiledb_query_set_data_buffer(ctx_, query, "d0", coords, &coords_size);
237   REQUIRE(rc == TILEDB_OK);
238   rc = tiledb_query_set_layout(ctx_, query, TILEDB_GLOBAL_ORDER);
239   REQUIRE(rc == TILEDB_OK);
240   rc = tiledb_query_submit(ctx_, query);
241   REQUIRE(rc == TILEDB_OK);
242   rc = tiledb_query_finalize(ctx_, query);
243   REQUIRE(rc == TILEDB_OK);
244 
245   // Close array
246   rc = tiledb_array_close(ctx_, array);
247   CHECK(rc == TILEDB_OK);
248 
249   // Clean up
250   tiledb_array_free(&array);
251   tiledb_query_free(&query);
252 }
253 
write_sparse_array(const std::string & path)254 void SparseNegFx2::write_sparse_array(const std::string& path) {
255   // Open array
256   tiledb_array_t* array;
257   int rc = tiledb_array_alloc(ctx_, path.c_str(), &array);
258   CHECK(rc == TILEDB_OK);
259   rc = tiledb_array_open(ctx_, array, TILEDB_WRITE);
260   CHECK(rc == TILEDB_OK);
261 
262   int a[] = {1, 2, 3, 4};
263   uint64_t a_size = sizeof(a);
264   int64_t coords_dim1[] = {-2, 1, -1, 1};
265   int64_t coords_dim2[] = {0, 1, -1, -1};
266   uint64_t coords_size = sizeof(coords_dim1);
267   tiledb_query_t* query;
268   rc = tiledb_query_alloc(ctx_, array, TILEDB_WRITE, &query);
269   REQUIRE(rc == TILEDB_OK);
270   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a, &a_size);
271   REQUIRE(rc == TILEDB_OK);
272   rc = tiledb_query_set_data_buffer(
273       ctx_, query, "d1", coords_dim1, &coords_size);
274   REQUIRE(rc == TILEDB_OK);
275   rc = tiledb_query_set_data_buffer(
276       ctx_, query, "d2", coords_dim2, &coords_size);
277   REQUIRE(rc == TILEDB_OK);
278   rc = tiledb_query_set_layout(ctx_, query, TILEDB_UNORDERED);
279   REQUIRE(rc == TILEDB_OK);
280   rc = tiledb_query_submit(ctx_, query);
281   REQUIRE(rc == TILEDB_OK);
282   rc = tiledb_query_finalize(ctx_, query);
283   REQUIRE(rc == TILEDB_OK);
284 
285   // Close array
286   rc = tiledb_array_close(ctx_, array);
287   CHECK(rc == TILEDB_OK);
288 
289   // Clean up
290   tiledb_array_free(&array);
291   tiledb_query_free(&query);
292 }
293 
read_sparse_vector(const std::string & path)294 void SparseNegFx2::read_sparse_vector(const std::string& path) {
295   // Open array
296   tiledb_array_t* array;
297   int rc = tiledb_array_alloc(ctx_, path.c_str(), &array);
298   CHECK(rc == TILEDB_OK);
299   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
300   CHECK(rc == TILEDB_OK);
301 
302   int a[2];
303   uint64_t a_size = sizeof(a);
304   int64_t coords[2];
305   uint64_t coords_size = sizeof(coords);
306   tiledb_query_t* query;
307   rc = tiledb_query_alloc(ctx_, array, TILEDB_READ, &query);
308   REQUIRE(rc == TILEDB_OK);
309 
310   // Set some subarray
311   int64_t s0[] = {-1, 2};
312   rc = tiledb_query_set_layout(ctx_, query, TILEDB_ROW_MAJOR);
313   REQUIRE(rc == TILEDB_OK);
314   rc = tiledb_query_add_range(ctx_, query, 0, &s0[0], &s0[1], nullptr);
315   REQUIRE(rc == TILEDB_OK);
316 
317   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a, &a_size);
318   REQUIRE(rc == TILEDB_OK);
319   rc = tiledb_query_set_data_buffer(ctx_, query, "d0", coords, &coords_size);
320   REQUIRE(rc == TILEDB_OK);
321   rc = tiledb_query_submit(ctx_, query);
322   REQUIRE(rc == TILEDB_OK);
323   rc = tiledb_query_finalize(ctx_, query);
324   REQUIRE(rc == TILEDB_OK);
325 
326   int a_c[] = {0, 1};
327   int64_t coords_c[] = {-1, 1};
328   CHECK(a_size == sizeof(a_c));
329   CHECK(coords_size == sizeof(coords_c));
330   CHECK(!memcmp(a, a_c, sizeof(a_c)));
331   CHECK(!memcmp(coords, coords_c, sizeof(coords_c)));
332 
333   // Close array
334   rc = tiledb_array_close(ctx_, array);
335   CHECK(rc == TILEDB_OK);
336 
337   // Clean up
338   tiledb_array_free(&array);
339   tiledb_query_free(&query);
340 }
341 
read_sparse_array_row(const std::string & path)342 void SparseNegFx2::read_sparse_array_row(const std::string& path) {
343   // Open array
344   tiledb_array_t* array;
345   int rc = tiledb_array_alloc(ctx_, path.c_str(), &array);
346   CHECK(rc == TILEDB_OK);
347   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
348   CHECK(rc == TILEDB_OK);
349 
350   int a[4];
351   uint64_t a_size = sizeof(a);
352   int64_t coords_dim1[4];
353   int64_t coords_dim2[4];
354   uint64_t coords_size = sizeof(coords_dim1);
355   tiledb_query_t* query;
356   rc = tiledb_query_alloc(ctx_, array, TILEDB_READ, &query);
357   REQUIRE(rc == TILEDB_OK);
358   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a, &a_size);
359   REQUIRE(rc == TILEDB_OK);
360   rc = tiledb_query_set_data_buffer(
361       ctx_, query, "d1", coords_dim1, &coords_size);
362   REQUIRE(rc == TILEDB_OK);
363   rc = tiledb_query_set_data_buffer(
364       ctx_, query, "d2", coords_dim2, &coords_size);
365   REQUIRE(rc == TILEDB_OK);
366 
367   // Create some subarray
368   int64_t s0[] = {-2, 1};
369   int64_t s1[] = {-2, 1};
370   rc = tiledb_query_set_layout(ctx_, query, TILEDB_ROW_MAJOR);
371   REQUIRE(rc == TILEDB_OK);
372   rc = tiledb_query_add_range(ctx_, query, 0, &s0[0], &s0[1], nullptr);
373   REQUIRE(rc == TILEDB_OK);
374   rc = tiledb_query_add_range(ctx_, query, 1, &s1[0], &s1[1], nullptr);
375   REQUIRE(rc == TILEDB_OK);
376 
377   rc = tiledb_query_submit(ctx_, query);
378   REQUIRE(rc == TILEDB_OK);
379   rc = tiledb_query_finalize(ctx_, query);
380   REQUIRE(rc == TILEDB_OK);
381 
382   int a_c[] = {1, 3, 4, 2};
383   int64_t coords_c_dim1[] = {-2, -1, 1, 1};
384   int64_t coords_c_dim2[] = {0, -1, -1, 1};
385   CHECK(a_size == sizeof(a_c));
386   CHECK(coords_size == sizeof(coords_c_dim1));
387   CHECK(!memcmp(a, a_c, sizeof(a_c)));
388   CHECK(!memcmp(coords_dim1, coords_c_dim1, sizeof(coords_c_dim1)));
389   CHECK(!memcmp(coords_dim2, coords_c_dim2, sizeof(coords_c_dim2)));
390 
391   // Close array
392   rc = tiledb_array_close(ctx_, array);
393   CHECK(rc == TILEDB_OK);
394 
395   // Clean up
396   tiledb_array_free(&array);
397   tiledb_query_free(&query);
398 }
399 
read_sparse_array_col(const std::string & path)400 void SparseNegFx2::read_sparse_array_col(const std::string& path) {
401   // Open array
402   tiledb_array_t* array;
403   int rc = tiledb_array_alloc(ctx_, path.c_str(), &array);
404   CHECK(rc == TILEDB_OK);
405   rc = tiledb_array_open(ctx_, array, TILEDB_READ);
406   CHECK(rc == TILEDB_OK);
407 
408   int a[4];
409   uint64_t a_size = sizeof(a);
410   int64_t coords_dim1[4];
411   int64_t coords_dim2[4];
412   uint64_t coords_size = sizeof(coords_dim1);
413   tiledb_query_t* query;
414   rc = tiledb_query_alloc(ctx_, array, TILEDB_READ, &query);
415   REQUIRE(rc == TILEDB_OK);
416   rc = tiledb_query_set_data_buffer(ctx_, query, "a", a, &a_size);
417   REQUIRE(rc == TILEDB_OK);
418   rc = tiledb_query_set_data_buffer(
419       ctx_, query, "d1", coords_dim1, &coords_size);
420   REQUIRE(rc == TILEDB_OK);
421   rc = tiledb_query_set_data_buffer(
422       ctx_, query, "d2", coords_dim2, &coords_size);
423   REQUIRE(rc == TILEDB_OK);
424 
425   // Set some subarray
426   int64_t s0[] = {-2, 1};
427   int64_t s1[] = {-2, 1};
428   rc = tiledb_query_set_layout(ctx_, query, TILEDB_COL_MAJOR);
429   REQUIRE(rc == TILEDB_OK);
430   rc = tiledb_query_add_range(ctx_, query, 0, &s0[0], &s0[1], nullptr);
431   REQUIRE(rc == TILEDB_OK);
432   rc = tiledb_query_add_range(ctx_, query, 1, &s1[0], &s0[1], nullptr);
433   REQUIRE(rc == TILEDB_OK);
434 
435   rc = tiledb_query_submit(ctx_, query);
436   REQUIRE(rc == TILEDB_OK);
437   rc = tiledb_query_finalize(ctx_, query);
438   REQUIRE(rc == TILEDB_OK);
439 
440   int a_c[] = {3, 4, 1, 2};
441   int64_t coords_c_dim1[] = {-1, 1, -2, 1};
442   int64_t coords_c_dim2[] = {-1, -1, 0, 1};
443   CHECK(a_size == sizeof(a_c));
444   CHECK(coords_size == sizeof(coords_c_dim1));
445   CHECK(!memcmp(a, a_c, sizeof(a_c)));
446   CHECK(!memcmp(coords_dim1, coords_c_dim1, sizeof(coords_c_dim1)));
447   CHECK(!memcmp(coords_dim2, coords_c_dim2, sizeof(coords_c_dim2)));
448 
449   // Close array
450   rc = tiledb_array_close(ctx_, array);
451   CHECK(rc == TILEDB_OK);
452 
453   // Clean up
454   tiledb_array_free(&array);
455   tiledb_query_free(&query);
456 }
457 
458 TEST_CASE_METHOD(
459     SparseNegFx2,
460     "C API: Test 1d sparse vector with negative domain 2",
461     "[capi][sparse-neg-2][sparse-neg-vector-2]") {
462   SupportedFsLocal local_fs;
463   std::string vector_name =
464       local_fs.file_prefix() + local_fs.temp_dir() + "sparse_neg_vector";
465   create_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
466 
467   create_sparse_vector(vector_name);
468   write_sparse_vector(vector_name);
469   read_sparse_vector(vector_name);
470 
471   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
472 }
473 
474 TEST_CASE_METHOD(
475     SparseNegFx2,
476     "C API: Test 2d sparse array with negative domain 2",
477     "[capi][sparse-neg-2][sparse-neg-array-2]") {
478   SupportedFsLocal local_fs;
479   std::string vector_name =
480       local_fs.file_prefix() + local_fs.temp_dir() + "sparse_neg_array";
481   create_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
482 
483   create_sparse_array(vector_name);
484   write_sparse_array(vector_name);
485   read_sparse_array_row(vector_name);
486   read_sparse_array_col(vector_name);
487 
488   remove_temp_dir(local_fs.file_prefix() + local_fs.temp_dir());
489 }
490