Skip to content
This repository was archived by the owner on Feb 26, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/bbp/sonata/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,8 @@ class SONATA_API SonataError: public std::runtime_error
public:
explicit SonataError(const std::string& what);
};

#define THROW_IF_REACHED throw SonataError("Should never be reached");

} // namespace sonata
} // namespace bbp
4 changes: 2 additions & 2 deletions include/bbp/sonata/node_sets.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ class SONATA_API NodeSets
* \throw if content cannot be parsed
*/
NodeSets(const std::string& content);
NodeSets(NodeSets&&);
NodeSets(NodeSets&&) noexcept;
NodeSets(const NodeSets& other) = delete;
NodeSets& operator=(NodeSets&&);
NodeSets& operator=(NodeSets&&) noexcept;
~NodeSets();

/** Open a SONATA `node sets` file from a path */
Expand Down
8 changes: 7 additions & 1 deletion include/bbp/sonata/nodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,20 @@ class SONATA_API NodePopulation: public Population
* Note: This does not match dynamics_params datasets
*/
template <typename T>
Selection matchAttributeValues(const std::string& attribute, const T value) const;
Selection matchAttributeValues(const std::string& attribute, const T values) const;

/**
* Like matchAttributeValues, but for vectors of values to match
*/
template <typename T>
Selection matchAttributeValues(const std::string& attribute,
const std::vector<T>& values) const;


/**
* For named attribute, return a selection where the passed regular expression matches
*/
Selection regexMatch(const std::string& attribute, const std::string& re) const;
};

//--------------------------------------------------------------------------------------------------
Expand Down
4 changes: 4 additions & 0 deletions include/bbp/sonata/population.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "common.h"

#include <cstdint>
#include <functional>
#include <memory> // std::shared_ptr, std::unique_ptr
#include <set>
#include <string>
Expand Down Expand Up @@ -232,6 +233,9 @@ class SONATA_API Population
*/
std::string _dynamicsAttributeDataType(const std::string& name) const;

template <typename T>
Selection filterAttribute(const std::string& name, std::function<bool(const T)> pred) const;

protected:
Population(const std::string& h5FilePath,
const std::string& csvFilePath,
Expand Down
6 changes: 6 additions & 0 deletions python/generated/docstrings.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ Note: This does not match dynamics_params datasets)doc";

static const char *__doc_bbp_sonata_NodePopulation_matchAttributeValues_2 = R"doc(Like matchAttributeValues, but for vectors of values to match)doc";

static const char *__doc_bbp_sonata_NodePopulation_regexMatch =
R"doc(For named attribute, return a selection where the passed regular
expression matches)doc";

static const char *__doc_bbp_sonata_NodeSets = R"doc()doc";

static const char *__doc_bbp_sonata_NodeSets_NodeSets =
Expand Down Expand Up @@ -182,6 +186,8 @@ Parameter ``name``:
Throws:
if there is no such attribute for the population)doc";

static const char *__doc_bbp_sonata_Population_filterAttribute = R"doc()doc";

static const char *__doc_bbp_sonata_Population_getAttribute =
R"doc(Get attribute values for given {element} Selection

Expand Down
9 changes: 9 additions & 0 deletions python/tests/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,15 @@ def test_NodeSet_toJSON(self):
"model_type": "point",
"node_id": [1, 2, 3, 5, 7, 9]
},
"power_number_test": {
"numeric_attribute_gt": { "$gt": 3 },
"numeric_attribute_lt": { "$lt": 3 },
"numeric_attribute_gte": { "$gte": 3 },
"numeric_attribute_lte": { "$lte": 3 }
},
"power_regex_test": {
"string_attr": { "$regex": "^[s][o]me value$" }
},
"combined": ["bio_layer45", "V1_point_prime"]
}'''
new = NodeSets(j).toJSON()
Expand Down
153 changes: 147 additions & 6 deletions src/node_sets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class NodeSets;
class NodeSetRule
{
public:
virtual ~NodeSetRule(){};
virtual ~NodeSetRule() = default;

virtual Selection materialize(const NodeSets&, const NodePopulation&) const = 0;
virtual std::string toJSON() const = 0;
Expand All @@ -51,7 +51,7 @@ class NodeSets
std::map<std::string, NodeSetRules> node_sets_;

public:
NodeSets(const std::string& content) {
explicit NodeSets(const std::string& content) {
json j = json::parse(content);
if (!j.is_object()) {
throw SonataError("Top level node_set must be an object");
Expand Down Expand Up @@ -176,13 +176,134 @@ class NodeSetBasicNodeIds: public NodeSetRule
Selection::Values values_;
};

class NodeSetBasicOperatorString: public NodeSetRule
{
public:
explicit NodeSetBasicOperatorString(const std::string& attribute,
const std::string& op,
const std::string& value)
: op_(string2op(op))
, attribute_(attribute)
, value_(value) {}

Selection materialize(const detail::NodeSets& /* unused */,
const NodePopulation& np) const final {
switch (op_) {
case Op::regex:
return np.regexMatch(attribute_, value_);
default: // LCOV_EXCL_LINE
THROW_IF_REACHED // LCOV_EXCL_LINE
}
}

std::string toJSON() const final {
return fmt::format(R"("{}": {{ "{}": "{}" }})", attribute_, op2string(op_), value_);
}

enum class Op {
regex = 1,
};

static Op string2op(const std::string& s) {
if (s == "$regex") {
return Op::regex;
}
throw SonataError(fmt::format("Operator '{}' not available for strings", s));
}

static std::string op2string(const Op op) {
switch (op) {
case Op::regex:
return "$regex";
default: // LCOV_EXCL_LINE
THROW_IF_REACHED // LCOV_EXCL_LINE
}
}

private:
Op op_;
std::string attribute_;
std::string value_;
};

class NodeSetBasicOperatorNumeric: public NodeSetRule
{
public:
explicit NodeSetBasicOperatorNumeric(const std::string& name,
const std::string& op,
double value)
: name_(name)
, value_(value)
, op_(string2op(op)) {}

Selection materialize(const detail::NodeSets& /* unused */,
const NodePopulation& np) const final {
switch (op_) {
case Op::gt:
return np.filterAttribute<double>(name_, [=](const double v) { return v > value_; });
case Op::lt:
return np.filterAttribute<double>(name_, [=](const double v) { return v < value_; });
case Op::gte:
return np.filterAttribute<double>(name_, [=](const double v) { return v >= value_; });
case Op::lte:
return np.filterAttribute<double>(name_, [=](const double v) { return v <= value_; });
default: // LCOV_EXCL_LINE
THROW_IF_REACHED // LCOV_EXCL_LINE
}
}

std::string toJSON() const final {
return fmt::format(R"("{}": {{ "{}": {} }})", name_, op2string(op_), value_);
}

enum class Op {
gt = 1,
lt = 2,
gte = 3,
lte = 4,
};

static Op string2op(const std::string& s) {
if (s == "$gt") {
return Op::gt;
} else if (s == "$lt") {
return Op::lt;
} else if (s == "$gte") {
return Op::gte;
} else if (s == "$lte") {
return Op::lte;
}
throw SonataError(fmt::format("Operator '{}' not available for numeric", s));
}

static std::string op2string(const Op op) {
switch (op) {
case Op::gt:
return "$gt";
case Op::lt:
return "$lt";
case Op::gte:
return "$gte";
case Op::lte:
return "$lte";
default: // LCOV_EXCL_LINE
THROW_IF_REACHED // LCOV_EXCL_LINE
}
}

private:
std::string name_;
double value_;
Op op_;
};

using CompoundTargets = std::vector<std::string>;
class NodeSetCompoundRule: public NodeSetRule
{
public:
NodeSetCompoundRule(std::string name, const CompoundTargets& targets)
NodeSetCompoundRule(std::string name, CompoundTargets targets)
: name_(std::move(name))
, targets_(targets) {}
, targets_(std::move(targets)) {}

Selection materialize(const detail::NodeSets& ns, const NodePopulation& np) const final {
Selection ret{{}};
Expand Down Expand Up @@ -287,6 +408,26 @@ NodeSetRules _dispatch_node(const json& contents) {
} else {
throw SonataError("Unknown array type");
}
} else if (el.value().is_object()) {
const auto& definition = el.value();
if (definition.size() != 1) {
throw SonataError(
fmt::format("Operator '{}' must have object with one key value pair",
attribute));
}
const auto& key = definition.begin().key();
const auto& value = definition.begin().value();
if (value.is_number()) {
ret.emplace_back(
new NodeSetBasicOperatorNumeric(attribute, key, value.get<double>()));
} else if (value.is_string()) {
ret.emplace_back(
new NodeSetBasicOperatorString(attribute, key, value.get<std::string>()));
} else {
throw SonataError("Unknown operator");
}
} else {
THROW_IF_REACHED // LCOV_EXCL_LINE
}
}
return ret;
Expand Down Expand Up @@ -354,8 +495,8 @@ void parse_compound(const json& j, std::map<std::string, NodeSetRules>& node_set
NodeSets::NodeSets(const std::string& content)
: impl_(new detail::NodeSets(content)) {}

NodeSets::NodeSets(NodeSets&&) = default;
NodeSets& NodeSets::operator=(NodeSets&&) = default;
NodeSets::NodeSets(NodeSets&&) noexcept = default;
NodeSets& NodeSets::operator=(NodeSets&&) noexcept = default;
NodeSets::~NodeSets() = default;

NodeSets NodeSets::fromFile(const std::string& path) {
Expand Down
Loading