diff --git a/include/bbp/sonata/node_sets.h b/include/bbp/sonata/node_sets.h index 11fb743a..26721fc5 100644 --- a/include/bbp/sonata/node_sets.h +++ b/include/bbp/sonata/node_sets.h @@ -48,6 +48,15 @@ class SONATA_API NodeSets */ std::set names() const; + /** + * Update this NodeSets to include the `other` nodeset + * + * Duplicate names are overriden with the values from `other` + * + * The duplicate names are returned. + */ + std::set update(const NodeSets& other) const; + /** * Return string version of node sets */ diff --git a/python/bindings.cpp b/python/bindings.cpp index 4dc0601e..81e5f512 100644 --- a/python/bindings.cpp +++ b/python/bindings.cpp @@ -515,6 +515,7 @@ PYBIND11_MODULE(_libsonata, m) { .def_static("from_file", [](py::object path) { return NodeSets::fromFile(py::str(path)); }) .def_property_readonly("names", &NodeSets::names, DOC_NODESETS(names)) .def("materialize", &NodeSets::materialize, DOC_NODESETS(materialize)) + .def("update", &NodeSets::update, "other"_a, DOC_NODESETS(update)) .def("toJSON", &NodeSets::toJSON, DOC_NODESETS(toJSON)); py::class_(m, diff --git a/python/generated/docstrings.h b/python/generated/docstrings.h index a8a26f50..310d08b3 100644 --- a/python/generated/docstrings.h +++ b/python/generated/docstrings.h @@ -282,6 +282,13 @@ static const char *__doc_bbp_sonata_NodeSets_operator_assign = R"doc()doc"; static const char *__doc_bbp_sonata_NodeSets_toJSON = R"doc(Return string version of node sets)doc"; +static const char *__doc_bbp_sonata_NodeSets_update = +R"doc(Update this NodeSets to include the `other` nodeset + +Duplicate names are overriden with the values from `other` + +The duplicate names are returned.)doc"; + static const char *__doc_bbp_sonata_Population = R"doc()doc"; static const char *__doc_bbp_sonata_PopulationStorage = R"doc(Collection of {PopulationClass}s stored in a H5 file and optional CSV.)doc"; @@ -676,8 +683,6 @@ static const char *__doc_bbp_sonata_SimulationConfig_InputAbsoluteShotNoise_ampC static const char *__doc_bbp_sonata_SimulationConfig_InputAbsoluteShotNoise_decayTime = R"doc(The decay time of the bi-exponential shots (ms))doc"; -static const char *__doc_bbp_sonata_SimulationConfig_InputAbsoluteShotNoise_reversal = R"doc(Reversal potential for conductance injection in mV. Default is 0)doc"; - static const char *__doc_bbp_sonata_SimulationConfig_InputAbsoluteShotNoise_dt = R"doc(Timestep of generated signal in ms. Default is 0.25 ms)doc"; static const char *__doc_bbp_sonata_SimulationConfig_InputAbsoluteShotNoise_mean = R"doc(Signal mean in nA (current_clamp) or uS (conductance).)doc"; @@ -686,6 +691,8 @@ static const char *__doc_bbp_sonata_SimulationConfig_InputAbsoluteShotNoise_rand R"doc(Override the random seed to introduce correlations between cells, default = None)doc"; +static const char *__doc_bbp_sonata_SimulationConfig_InputAbsoluteShotNoise_reversal = R"doc(Reversal potential for conductance injection in mV. Default is 0)doc"; + static const char *__doc_bbp_sonata_SimulationConfig_InputAbsoluteShotNoise_riseTime = R"doc(The rise time of the bi-exponential shots (ms))doc"; static const char *__doc_bbp_sonata_SimulationConfig_InputAbsoluteShotNoise_sigma = R"doc(signal std dev in nA (current_clamp) or uS (conductance).)doc"; @@ -824,8 +831,6 @@ static const char *__doc_bbp_sonata_SimulationConfig_InputRelativeShotNoise_ampC static const char *__doc_bbp_sonata_SimulationConfig_InputRelativeShotNoise_decayTime = R"doc(The decay time of the bi-exponential shots (ms))doc"; -static const char *__doc_bbp_sonata_SimulationConfig_InputRelativeShotNoise_reversal = R"doc(Reversal potential for conductance injection in mV. Default is 0)doc"; - static const char *__doc_bbp_sonata_SimulationConfig_InputRelativeShotNoise_dt = R"doc(Timestep of generated signal in ms. Default is 0.25 ms)doc"; static const char *__doc_bbp_sonata_SimulationConfig_InputRelativeShotNoise_meanPercent = @@ -836,6 +841,8 @@ static const char *__doc_bbp_sonata_SimulationConfig_InputRelativeShotNoise_rand R"doc(Override the random seed to introduce correlations between cells, default = None)doc"; +static const char *__doc_bbp_sonata_SimulationConfig_InputRelativeShotNoise_reversal = R"doc(Reversal potential for conductance injection in mV. Default is 0)doc"; + static const char *__doc_bbp_sonata_SimulationConfig_InputRelativeShotNoise_riseTime = R"doc(The rise time of the bi-exponential shots (ms))doc"; static const char *__doc_bbp_sonata_SimulationConfig_InputRelativeShotNoise_sdPercent = @@ -860,8 +867,6 @@ or uS^2 (conductance))doc"; static const char *__doc_bbp_sonata_SimulationConfig_InputShotNoise_decayTime = R"doc(The decay time of the bi-exponential shots (ms))doc"; -static const char *__doc_bbp_sonata_SimulationConfig_InputShotNoise_reversal = R"doc(Reversal potential for conductance injection in mV. Default is 0)doc"; - static const char *__doc_bbp_sonata_SimulationConfig_InputShotNoise_dt = R"doc(Timestep of generated signal in ms. Default is 0.25 ms)doc"; static const char *__doc_bbp_sonata_SimulationConfig_InputShotNoise_randomSeed = @@ -870,6 +875,8 @@ default = None)doc"; static const char *__doc_bbp_sonata_SimulationConfig_InputShotNoise_rate = R"doc(Rate of Poisson events (Hz))doc"; +static const char *__doc_bbp_sonata_SimulationConfig_InputShotNoise_reversal = R"doc(Reversal potential for conductance injection in mV. Default is 0)doc"; + static const char *__doc_bbp_sonata_SimulationConfig_InputShotNoise_riseTime = R"doc(The rise time of the bi-exponential shots (ms))doc"; static const char *__doc_bbp_sonata_SimulationConfig_InputSubthreshold = R"doc()doc"; @@ -966,10 +973,10 @@ static const char *__doc_bbp_sonata_SimulationConfig_Report_Type = R"doc()doc"; static const char *__doc_bbp_sonata_SimulationConfig_Report_Type_compartment = R"doc()doc"; -static const char *__doc_bbp_sonata_SimulationConfig_Report_Type_lfp = R"doc()doc"; - static const char *__doc_bbp_sonata_SimulationConfig_Report_Type_invalid = R"doc()doc"; +static const char *__doc_bbp_sonata_SimulationConfig_Report_Type_lfp = R"doc()doc"; + static const char *__doc_bbp_sonata_SimulationConfig_Report_Type_summation = R"doc()doc"; static const char *__doc_bbp_sonata_SimulationConfig_Report_Type_synapse = R"doc()doc"; @@ -1019,6 +1026,8 @@ static const char *__doc_bbp_sonata_SimulationConfig_Run_IntegrationMethod_nicho static const char *__doc_bbp_sonata_SimulationConfig_Run_dt = R"doc(Integration step duration in milliseconds)doc"; +static const char *__doc_bbp_sonata_SimulationConfig_Run_electrodesFile = R"doc(Filename that contains the weights for the LFP calculation.)doc"; + static const char *__doc_bbp_sonata_SimulationConfig_Run_integrationMethod = R"doc(Selects the NEURON/CoreNEURON integration method. This parameter sets the NEURON global variable h.secondorder. Default 0 ('euler'))doc"; @@ -1045,8 +1054,6 @@ is 0.)doc"; static const char *__doc_bbp_sonata_SimulationConfig_Run_tstop = R"doc(Biological simulation end time in milliseconds)doc"; -static const char *__doc_bbp_sonata_SimulationConfig_Run_electrodesFile = R"doc(Electrode weights file)doc"; - static const char *__doc_bbp_sonata_SimulationConfig_SimulationConfig = R"doc(Parses a SONATA JSON simulation configuration file. @@ -1170,19 +1177,27 @@ static const char *__doc_bbp_sonata_SpikeReader_Population_Sorting_by_time = R"d static const char *__doc_bbp_sonata_SpikeReader_Population_Sorting_none = R"doc()doc"; +static const char *__doc_bbp_sonata_SpikeReader_Population_createSpikes = R"doc(Create the spikes from the vectors of node_ids and timestamps)doc"; + static const char *__doc_bbp_sonata_SpikeReader_Population_filterNode = R"doc()doc"; static const char *__doc_bbp_sonata_SpikeReader_Population_filterTimestamp = R"doc()doc"; static const char *__doc_bbp_sonata_SpikeReader_Population_get = R"doc(Return spikes with all those node_ids between 'tstart' and 'tstop')doc"; +static const char *__doc_bbp_sonata_SpikeReader_Population_getArrays = +R"doc(Return the node_ids and timestamps vectors with all node_ids between +'tstart' and 'tstop')doc"; + +static const char *__doc_bbp_sonata_SpikeReader_Population_getRawArrays = R"doc(Return the raw node_ids and timestamps vectors)doc"; + static const char *__doc_bbp_sonata_SpikeReader_Population_getSorting = R"doc(Return the way data are sorted ('none', 'by_id', 'by_time'))doc"; static const char *__doc_bbp_sonata_SpikeReader_Population_getTimes = R"doc(Return (tstart, tstop) of the population)doc"; static const char *__doc_bbp_sonata_SpikeReader_Population_sorting = R"doc()doc"; -static const char *__doc_bbp_sonata_SpikeReader_Population_spikes = R"doc()doc"; +static const char *__doc_bbp_sonata_SpikeReader_Population_spike_times = R"doc()doc"; static const char *__doc_bbp_sonata_SpikeReader_Population_tstart = R"doc()doc"; @@ -1198,6 +1213,12 @@ static const char *__doc_bbp_sonata_SpikeReader_openPopulation = R"doc()doc"; static const char *__doc_bbp_sonata_SpikeReader_populations = R"doc()doc"; +static const char *__doc_bbp_sonata_SpikeTimes = R"doc()doc"; + +static const char *__doc_bbp_sonata_SpikeTimes_node_ids = R"doc()doc"; + +static const char *__doc_bbp_sonata_SpikeTimes_timestamps = R"doc()doc"; + static const char *__doc_bbp_sonata_detail_NodeSets = R"doc()doc"; static const char *__doc_bbp_sonata_fromValues = R"doc()doc"; diff --git a/python/tests/test_nodesets.py b/python/tests/test_nodesets.py index 5ac6b7b2..982fde3b 100644 --- a/python/tests/test_nodesets.py +++ b/python/tests/test_nodesets.py @@ -181,3 +181,20 @@ def test_library_datatype(self): j = json.dumps({"NodeSet0": { "E-mapping-good": [0, 1, ] }}) self.assertRaises(SonataError, NodeSets(j).materialize, "NodeSet0", self.population) + + def test_extend(self): + ns0 = NodeSets(json.dumps({"NodeSet0": { "E-mapping-good": 0 }})) + dup = ns0.update(ns0) + self.assertEqual(dup, {"NodeSet0"}) + self.assertEqual(ns0.names, {"NodeSet0"}) + + ns1 = NodeSets(json.dumps({"NodeSet1": { "E-mapping-good": 0 }})) + dup = ns0.update(ns1) + self.assertEqual(dup, set()) + self.assertEqual(ns0.names, {"NodeSet0", "NodeSet1"}) + + ns0 = NodeSets(json.dumps({"NodeSet0": { "E-mapping-good": 0 }})) + ns1 = NodeSets(json.dumps({"NodeSet0": { "E-mapping-good": 0 }})) + dup = ns0.update(ns1) + self.assertEqual(dup, {"NodeSet0"}) + self.assertEqual(ns0.names, {"NodeSet0"}) diff --git a/src/node_sets.cpp b/src/node_sets.cpp index ee9089a7..b08fcd7f 100644 --- a/src/node_sets.cpp +++ b/src/node_sets.cpp @@ -55,6 +55,7 @@ class NodeSetRule virtual bool is_compound() const { return false; } + virtual std::unique_ptr clone() const = 0; }; using NodeSetRulePtr = std::unique_ptr; @@ -101,6 +102,20 @@ class NodeSets return getMapKeys(node_sets_); } + std::set update(const NodeSets& other) { + if (&other == this) { + return names(); + } + std::set duplicates; + for (const auto& ns : other.node_sets_) { + if (node_sets_.count(ns.first) > 0) { + duplicates.insert(ns.first); + } + node_sets_[ns.first] = ns.second->clone(); + } + return duplicates; + } + std::string toJSON() const { std::string ret{"{\n"}; for (const auto& pair : node_sets_) { @@ -125,6 +140,10 @@ class NodeSetNullRule: public NodeSetRule std::string toJSON() const final { return {}; } + + std::unique_ptr clone() const final { + return std::make_unique(); + } }; // { 'region': ['region1', 'region2', ...] } @@ -132,8 +151,8 @@ template class NodeSetBasicRule: public NodeSetRule { public: - NodeSetBasicRule(std::string attribute, std::vector& values) - : attribute_(std::move(attribute)) + NodeSetBasicRule(const std::string attribute, const std::vector& values) + : attribute_(attribute) , values_(values) {} Selection materialize(const detail::NodeSets& /* unused */, @@ -152,6 +171,10 @@ class NodeSetBasicRule: public NodeSetRule return toString(attribute_, values_); } + std::unique_ptr clone() const final { + return std::make_unique>(attribute_, values_); + } + private: std::string attribute_; std::vector values_; @@ -161,7 +184,7 @@ class NodeSetBasicRule: public NodeSetRule class NodeSetBasicPopulation: public NodeSetRule { public: - explicit NodeSetBasicPopulation(std::vector& values) + explicit NodeSetBasicPopulation(const std::vector& values) : values_(values) {} Selection materialize(const detail::NodeSets& /* unused */, @@ -177,6 +200,10 @@ class NodeSetBasicPopulation: public NodeSetRule return toString("population", values_); } + std::unique_ptr clone() const final { + return std::make_unique(values_); + } + private: std::vector values_; }; @@ -185,7 +212,7 @@ class NodeSetBasicPopulation: public NodeSetRule class NodeSetBasicNodeIds: public NodeSetRule { public: - explicit NodeSetBasicNodeIds(Selection::Values&& values) + explicit NodeSetBasicNodeIds(const Selection::Values& values) : values_(values) {} Selection materialize(const detail::NodeSets& /* unused */, @@ -197,6 +224,10 @@ class NodeSetBasicNodeIds: public NodeSetRule return toString("node_ids", values_); } + std::unique_ptr clone() const final { + return std::make_unique(values_); + } + private: Selection::Values values_; }; @@ -230,6 +261,15 @@ class NodeSetBasicMultiClause: public NodeSetRule return ret; } + std::unique_ptr clone() const final { + std::vector clauses; + clauses.reserve(clauses_.size()); + for (const auto& clause : clauses_) { + clauses.push_back(clause->clone()); + } + return std::make_unique(std::move(clauses)); + } + private: std::vector clauses_; }; @@ -238,12 +278,12 @@ class NodeSetBasicMultiClause: public NodeSetRule class NodeSetBasicOperatorString: public NodeSetRule { public: - explicit NodeSetBasicOperatorString(std::string attribute, + explicit NodeSetBasicOperatorString(const std::string& attribute, const std::string& op, - std::string value) + const std::string& value) : op_(string2op(op)) - , attribute_(std::move(attribute)) - , value_(std::move(value)) {} + , attribute_(attribute) + , value_(value) {} Selection materialize(const detail::NodeSets& /* unused */, const NodePopulation& np) const final { @@ -279,6 +319,12 @@ class NodeSetBasicOperatorString: public NodeSetRule } } + std::unique_ptr clone() const final { + return std::make_unique(attribute_, + op2string(op_), + value_); + } + private: Op op_; std::string attribute_; @@ -349,6 +395,10 @@ class NodeSetBasicOperatorNumeric: public NodeSetRule } } + std::unique_ptr clone() const final { + return std::make_unique(name_, op2string(op_), value_); + } + private: std::string name_; double value_; @@ -382,6 +432,10 @@ class NodeSetCompoundRule: public NodeSetRule return targets_; } + std::unique_ptr clone() const final { + return std::make_unique(name_, targets_); + } + private: std::string name_; CompoundTargets targets_; @@ -488,8 +542,8 @@ NodeSetRulePtr _dispatch_node(const std::string& attribute, const json& value) { 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(); + const auto key = definition.begin().key(); + const auto value = definition.begin().value(); if (value.is_number()) { return std::make_unique(attribute, @@ -673,6 +727,10 @@ std::set NodeSets::names() const { return impl_->names(); } +std::set NodeSets::update(const NodeSets& other) const { + return impl_->update(*other.impl_); +} + std::string NodeSets::toJSON() const { return impl_->toJSON(); }