Skip to content

Commit

Permalink
soma_array mods [skip ci]
Browse files Browse the repository at this point in the history
  • Loading branch information
johnkerl committed Sep 17, 2024
1 parent a3c9a2a commit 411f03f
Show file tree
Hide file tree
Showing 4 changed files with 311 additions and 23 deletions.
176 changes: 176 additions & 0 deletions libtiledbsoma/src/soma/soma_array.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1141,6 +1141,182 @@ std::optional<TimestampRange> SOMAArray::timestamp() {
return timestamp_;
}

// Note that ArrowTable is simply our libtiledbsoma pairing of ArrowArray and
// ArrowSchema from nanoarrow.
//
// The domainish enum simply lets us re-use code which is common across
// core domain, core current domain, and core non-empty domain.
ArrowTable SOMAArray::_get_core_domainish(enum Domainish which_kind) {
int array_ndim = this->ndim();
auto dimensions = tiledb_schema()->domain().dimensions();

// Create the schema for the info we return
std::vector<std::string> names(array_ndim);
std::vector<tiledb_datatype_t> tiledb_datatypes(array_ndim);

for (int i = 0; i < (int)array_ndim; i++) {
const Dimension& core_dim = dimensions[i];
names[i] = core_dim.name();
tiledb_datatypes[i] = core_dim.type();
}

auto arrow_schema = ArrowAdapter::make_arrow_schema(
names, tiledb_datatypes);

// Create the data for the info we return
auto arrow_array = ArrowAdapter::make_arrow_array_parent(array_ndim);

for (int i = 0; i < array_ndim; i++) {
auto core_dim = dimensions[i];
auto core_type_code = core_dim.type();

ArrowArray* child = nullptr;

switch (core_type_code) {
case TILEDB_INT64:
child = ArrowAdapter::make_arrow_array_child(
_core_domainish_slot<int64_t>(core_dim.name(), which_kind));
break;
case TILEDB_UINT64:
child = ArrowAdapter::make_arrow_array_child(
_core_domainish_slot<uint64_t>(
core_dim.name(), which_kind));
break;
case TILEDB_INT32:
child = ArrowAdapter::make_arrow_array_child(
_core_domainish_slot<int32_t>(core_dim.name(), which_kind));
break;
case TILEDB_UINT32:
child = ArrowAdapter::make_arrow_array_child(
_core_domainish_slot<uint32_t>(
core_dim.name(), which_kind));
break;
case TILEDB_INT16:
child = ArrowAdapter::make_arrow_array_child(
_core_domainish_slot<int16_t>(core_dim.name(), which_kind));
break;
case TILEDB_UINT16:
child = ArrowAdapter::make_arrow_array_child(
_core_domainish_slot<uint16_t>(
core_dim.name(), which_kind));
break;
case TILEDB_INT8:
child = ArrowAdapter::make_arrow_array_child(
_core_domainish_slot<int8_t>(core_dim.name(), which_kind));
break;
case TILEDB_UINT8:
child = ArrowAdapter::make_arrow_array_child(
_core_domainish_slot<uint8_t>(core_dim.name(), which_kind));
break;

case TILEDB_FLOAT64:
child = ArrowAdapter::make_arrow_array_child(
_core_domainish_slot<double>(core_dim.name(), which_kind));
break;
case TILEDB_FLOAT32:
child = ArrowAdapter::make_arrow_array_child(
_core_domainish_slot<float>(core_dim.name(), which_kind));
break;

case TILEDB_STRING_ASCII:
case TILEDB_CHAR:
child = ArrowAdapter::make_arrow_array_child_string(
_core_domainish_slot_string(core_dim.name(), which_kind));
break;

default:
break;
}

if (child == nullptr) {
// throw TileDBSOMAError(fmt::format(
// "WIP {} {}",
// core_dim.name(),
// tiledb::impl::type_to_str(core_type_code)));
}
arrow_array->children[i] = child;
}

return ArrowTable(std::move(arrow_array), std::move(arrow_schema));
}

template <typename T>
std::pair<T, T> SOMAArray::_core_current_domain_slot(
const std::string& name) const {
CurrentDomain current_domain = _get_current_domain();
if (current_domain.is_empty()) {
throw TileDBSOMAError(
"_core_current_domain_slot: internal coding error");
}
if (current_domain.type() != TILEDB_NDRECTANGLE) {
throw TileDBSOMAError(
"_core_current_domain_slot: found non-rectangle type");
}
NDRectangle ndrect = current_domain.ndrectangle();

// Convert from two-element array (core API) to pair (tiledbsoma API)
std::array<T, 2> arr = ndrect.range<T>(name);
return std::pair<T, T>(arr[0], arr[1]);
}

template <typename T>
std::pair<T, T> SOMAArray::_core_domain_slot(const std::string& name) const {
if (std::is_same_v<T, std::string>) {
throw std::runtime_error(
"SOMAArray::_core_domain_slot: template-specialization "
"failure.");
}
return arr_->schema().domain().dimension(name).domain<T>();
}

template <>
std::pair<std::string, std::string> SOMAArray::_core_domain_slot(
const std::string&) const {
return std::pair("", "");
}

template <typename T>
std::pair<T, T> SOMAArray::_core_domainish_slot(
const std::string& name, enum Domainish which_kind) const {
if (std::is_same_v<T, std::string>) {
throw std::runtime_error(
"SOMAArray::_core_domain_slot: template-specialization "
"failure.");
}
switch (which_kind) {
case Domainish::kind_core_domain:
return _core_domain_slot<T>(name);
break;
case Domainish::kind_core_current_domain:
return _core_current_domain_slot<T>(name);
break;
case Domainish::kind_non_empty_domain:
return non_empty_domain_slot<T>(name);
break;
default:
throw std::runtime_error(
"internal coding error in SOMAArray::_core_domainish_slot");
}
}

std::pair<std::string, std::string> SOMAArray::_core_domainish_slot_string(
const std::string& name, enum Domainish which_kind) const {
switch (which_kind) {
case Domainish::kind_core_domain:
return _core_domain_slot<std::string>(name);
break;
case Domainish::kind_core_current_domain:
return _core_current_domain_slot<std::string>(name);
break;
case Domainish::kind_non_empty_domain:
return non_empty_domain_slot_var(name);
break;
default:
throw std::runtime_error(
"internal coding error in SOMAArray::_core_domainish_slot");
}
}

uint64_t SOMAArray::nnz() {
// Verify array is sparse
if (mq_->schema()->array_type() != TILEDB_SPARSE) {
Expand Down
140 changes: 120 additions & 20 deletions libtiledbsoma/src/soma/soma_array.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@
namespace tiledbsoma {
using namespace tiledb;

// This enables some code deduplication between core domain, core current
// domain, and core non-empty domain.
enum class Domainish {
kind_core_domain = 0,
kind_core_current_domain = 1,
kind_non_empty_domain = 2
};

class SOMAArray : public SOMAObject {
public:
//===================================================================
Expand Down Expand Up @@ -703,7 +711,7 @@ class SOMAArray : public SOMAObject {
* non-empty domains of the array fragments.
*/
template <typename T>
std::pair<T, T> non_empty_domain_slot(const std::string& name) {
std::pair<T, T> non_empty_domain_slot(const std::string& name) const {
try {
return arr_->non_empty_domain<T>(name);
} catch (const std::exception& e) {
Expand All @@ -717,7 +725,7 @@ class SOMAArray : public SOMAObject {
* Applicable only to var-sized dimensions.
*/
std::pair<std::string, std::string> non_empty_domain_slot_var(
const std::string& name) {
const std::string& name) const {
try {
return arr_->non_empty_domain_var(name);
} catch (const std::exception& e) {
Expand Down Expand Up @@ -775,6 +783,59 @@ class SOMAArray : public SOMAObject {
return _core_domain_slot<T>(name);
}

std::pair<std::string, std::string> _core_domain_slot_string(
const std::string& name) const;

/**
* Returns the SOMA domain in its entirety, as an Arrow table for return to
* Python/R.
*
* o For arrays with core current-domain support:
* - soma domain is core current domain
* - soma maxdomain is core domain
* o For arrays without core current-domain support:
* - soma domain is core domain
* - soma maxdomain is core domain
* - core current domain is not accessed at the soma level
*
* @tparam T Domain datatype
* @return Pair of [lower, upper] inclusive bounds.
*/
ArrowTable get_soma_domain() {
if (has_current_domain()) {
return _get_core_current_domain();
} else {
return _get_core_domain();
}
}

/**
* Returns the SOMA maxdomain in its entirety, as an Arrow table for return
* to Python/R.
*
* o For arrays with core current-domain support:
* - soma domain is core current domain
* - soma maxdomain is core domain
* o For arrays without core current-domain support:
* - soma domain is core domain
* - soma maxdomain is core domain
* - core current domain is not accessed at the soma level
*
* @tparam T Domain datatype
* @return Pair of [lower, upper] inclusive bounds.
*/
ArrowTable get_soma_maxdomain() {
return _get_core_domain();
}

/**
* Returns the core non-empty domain in its entirety, as an Arrow
* table for return to Python/R.
*/
ArrowTable get_non_empty_domain() {
return _get_core_domainish(Domainish::kind_non_empty_domain);
}

/**
* @brief Get the total number of unique cells in the array.
*
Expand Down Expand Up @@ -910,25 +971,65 @@ class SOMAArray : public SOMAObject {
* @return Pair of [lower, upper] inclusive bounds.
*/
template <typename T>
std::pair<T, T> _core_current_domain_slot(const std::string& name) const {
CurrentDomain current_domain = _get_current_domain();
if (current_domain.is_empty()) {
throw TileDBSOMAError(
"_core_current_domain_slot: internal coding error");
}
if (current_domain.type() != TILEDB_NDRECTANGLE) {
throw TileDBSOMAError(
"_core_current_domain_slot: found non-rectangle type");
}
NDRectangle ndrect = current_domain.ndrectangle();
std::pair<T, T> _core_current_domain_slot(const std::string& name) const;

/**
* Returns the core domain at the given dimension.
*
* o For arrays with core current-domain support:
* - soma domain is core current domain
* - soma maxdomain is core domain
* o For arrays without core current-domain support:
* - soma domain is core domain
* - soma maxdomain is core domain
* - core current domain is not accessed at the soma level
*
* @tparam T Domain datatype
* @return Pair of [lower, upper] inclusive bounds.
*/
template <typename T>
std::pair<T, T> _core_domain_slot(const std::string& name) const;

/**
* This enables some code deduplication between core domain, core current
* domain, and core non-empty domain.
*/
ArrowTable _get_core_domainish(enum Domainish which_kind);

/**
* This enables some code deduplication between core domain, core current
* domain, and core non-empty domain.
*/
template <typename T>
std::pair<T, T> _core_domainish_slot(
const std::string& name, enum Domainish which_kind) const;

// Convert from two-element array (core API) to pair (tiledbsoma API)
std::array<T, 2> arr = ndrect.range<T>(name);
return std::pair<T, T>(arr[0], arr[1]);
std::pair<std::string, std::string> _core_domainish_slot_string(
const std::string& name, enum Domainish which_kind) const;

/**
* Returns the core domain in its entirety, as an Arrow table
* for return to Python/R.
*
* o For arrays with core current-domain support:
* - soma domain is core current domain
* - soma maxdomain is core domain
* o For arrays without core current-domain support:
* - soma domain is core domain
* - soma maxdomain is core domain
* - core current domain is not accessed at the soma level
*
* @tparam T Domain datatype
* @return Pair of [lower, upper] inclusive bounds.
*/
ArrowTable _get_core_domain() {
return _get_core_domainish(Domainish::kind_core_domain);
}

/**
* Returns the core current domain at the given dimension.
* Returns the core current domain in its entirety, as an Arrow table
* for return to Python/R.
*
*
* o For arrays with core current-domain support:
* - soma domain is core current domain
Expand All @@ -941,9 +1042,8 @@ class SOMAArray : public SOMAObject {
* @tparam T Domain datatype
* @return Pair of [lower, upper] inclusive bounds.
*/
template <typename T>
std::pair<T, T> _core_domain_slot(const std::string& name) const {
return arr_->schema().domain().dimension(name).domain<T>();
ArrowTable _get_core_current_domain() {
return _get_core_domainish(Domainish::kind_core_current_domain);
}

/**
Expand Down
14 changes: 13 additions & 1 deletion libtiledbsoma/src/utils/arrow_adapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,20 @@ class ArrowAdapter {
return make_arrow_array_child<T>(v);
}

static ArrowArray* make_arrow_array_child_string(
const std::pair<std::string, std::string>& pair) {
std::vector<std::string> v({pair.first, pair.second});
return make_arrow_array_child_string(v);
}

template <typename T>
static ArrowArray* make_arrow_array_child(const std::vector<T>& v) {
if (std::is_same_v<T, std::string>) {
throw std::runtime_error(
"ArrowAdapter::make_arrow_array_child: template-specialization "
"failure.");
}

// Use new here, not malloc, to match ArrowAdapter::release_array
auto arrow_array = new ArrowArray;

Expand Down Expand Up @@ -331,7 +343,7 @@ class ArrowAdapter {
// using Arrow's validity buffers? Or do we use ("", "") as TileDB-Py does?
//
// We choose the latter.
static ArrowArray* make_arrow_array_child(
static ArrowArray* make_arrow_array_child_string(
const std::vector<std::string>& v) {
// Use new here, not malloc, to match ArrowAdapter::release_array
auto arrow_array = new ArrowArray;
Expand Down
Loading

0 comments on commit 411f03f

Please sign in to comment.