Repository: BlueBrain/libsonata (language: python)

Issue:
don't make many small reads via HighFive/hdf5
* use HighFive::ElementSet
* about twice as fast on my simple benchmark of reversing all ids: 
     sel = libsonata.Selection(list(reversed(sel_all.flatten()))) compared to reading everything as one large chunk

I don't know if hdf5 is coalescing reads - we may have to do that manually.

Note: This is still slow
don't make many small reads via HighFive/hdf5
* use HighFive::ElementSet
* about twice as fast on my simple benchmark of reversing all ids: 
     sel = libsonata.Selection(list(reversed(sel_all.flatten()))) compared to reading everything as one large chunk

I don't know if hdf5 is coalescing reads - we may have to do that manually.

Note: This is still slow


Discussion / hints:
For [c5ce666](https://github.com/BlueBrain/libsonata/pull/226/commits/c5ce666e26dc94f9d36c2582e4306b55585a0271):
The speedup (from master) for all reversed ids is 24.3x, and for a random subset of ids (1/4 of all ids): the speedup is 17.5.
For [c5ce666](https://github.com/BlueBrain/libsonata/pull/226/commits/c5ce666e26dc94f9d36c2582e4306b55585a0271):
The speedup (from master) for all reversed ids is 24.3x, and for a random subset of ids (1/4 of all ids): the speedup is 17.5.

Fix patch:
diff --git a/src/population.hpp b/src/population.hpp
index fbed5c44..f55541e3 100644
--- a/src/population.hpp
+++ b/src/population.hpp
@@ -89,7 +89,9 @@ std::vector<T> _readSelection(const HighFive::DataSet& dset, const Selection& se
 
 template <typename T, typename std::enable_if<std::is_pod<T>::value>::type* = nullptr>
 std::vector<T> _readSelection(const HighFive::DataSet& dset, const Selection& selection) {
-    if (selection.ranges().size() == 1) {
+    if (selection.ranges().empty()) {
+        return {};
+    } else if (selection.ranges().size() == 1) {
         return _readChunk<T>(dset, selection.ranges().front());
     }
 


Test patch:
diff --git a/python/tests/test.py b/python/tests/test.py
index 885a3d0f..7a92dc3c 100644
--- a/python/tests/test.py
+++ b/python/tests/test.py
@@ -163,6 +163,14 @@ def test_enumeration_names(self):
         )
 
     def test_enumeration_values(self):
+        self.assertEqual(
+            self.test_obj.get_attribute(
+                "E-mapping-good",
+                Selection([])
+            ).tolist(),
+            []
+        )
+
         self.assertEqual(
             self.test_obj.get_attribute(
                 "E-mapping-good",
@@ -240,10 +248,12 @@ def test_target(self):
     def test_source_nodes(self):
         self.assertEqual(self.test_obj.source_node(1), 1)
         self.assertEqual(self.test_obj.source_nodes(Selection([0, 1, 2, 4])).tolist(), [1, 1, 2, 3])
+        self.assertEqual(self.test_obj.source_nodes(Selection([])).tolist(), [])
 
     def test_target_nodes(self):
         self.assertEqual(self.test_obj.target_node(1), 2)
         self.assertEqual(self.test_obj.target_nodes(Selection([0, 1, 2, 4])).tolist(), [1, 2, 1, 0])
+        self.assertEqual(self.test_obj.target_nodes(Selection([])).tolist(), [])
 
     def test_afferent_edges(self):
         self.assertEqual(self.test_obj.afferent_edges([1, 2]).ranges, [(0, 4), (5, 6)])
diff --git a/tests/test_nodes.cpp b/tests/test_nodes.cpp
index 9b129eb0..62a4ca47 100644
--- a/tests/test_nodes.cpp
+++ b/tests/test_nodes.cpp
@@ -38,6 +38,8 @@ TEST_CASE("NodePopulation", "[base]") {
     REQUIRE(population.enumerationNames() ==
             std::set<std::string>{"E-mapping-good", "E-mapping-bad"});
 
+    CHECK(population.getAttribute<double>("attr-X", Selection({})) ==
+          std::vector<double>{});
     CHECK(population.getAttribute<double>("attr-X", Selection({{0, 1}, {5, 6}})) ==
           std::vector<double>{11.0, 16.0});
     CHECK(population.getAttribute<float>("attr-X", Selection({{0, 1}})) ==