Package: libsonata
libsonataC++ / Python reader for SONATA circuit files:SONATA guideInstallationInstalling from PyPIpipinstalllibsonataInstalling as a Python package, directly from GitHubpipinstallgit+https://github.com/BlueBrain/libsonataBuilding the C++ librarygitclonegit@github.com:BlueBrain/libsonata.git--recursivecdlibsonatamkdirbuild&&cdbuildcmake-DCMAKE_BUILD_TYPE=Release-DEXTLIB_FROM_SUBMODULES=ON..make-jSincelibsonatauses backports forstd::optionalandstd::variantwhich
turn into their actual STL implementation once available, it’s recommended to compilelibsonatawith the same C++ standard as the project linking tolibsonata. This
is done by passing-DCMAKE_CXX_STANDARD={14,17}to thecmakecommand above.Usage (Python)NodesNodeStorage>>>importlibsonata>>>nodes=libsonata.NodeStorage('path/to/H5/file')# list populations>>>nodes.population_names# open population>>>population=nodes.open_population(<name>)NodePopulation# total number of nodes in the population>>>population.size# attribute names>>>population.attribute_names# get attribute value for single node, say 42>>>population.get_attribute('mtype',42)# ...or Selection of nodes (see below) => returns NumPy array with corresponding values>>>selection=libsonata.Selection(values=[1,5,9,42])# nodes 1, 5, 9, 42>>>mtypes=population.get_attribute('mtype',selection)>>>list(zip(selection.flatten(),mtypes))[(1, u'mtype_of_1'), (5, u'mtype_of_5'), (9, u'mtype_of_9'), (42, u'mtype_of_42')]SelectionList of element IDs (eithernode_id, oredge_id) where adjacent IDs are grouped for the sake of efficient HDF5 file access.
For instance,{1, 2, 3, 5}sequence becomes{[1, 4), [5, 6)}.Selectioncan be instantiated from:a sequence of scalar values (works for NumPy arrays as well)a sequence of pairs (interpreted as ranges above, works for N x 2 NumPy arrays as well)EdgePopulationconnectivity queries (see below) returnSelections as well.>>>selection=libsonata.Selection([1,2,3,5])>>>selection.ranges[(1, 4), (5, 6)]>>>selection=libsonata.Selection([(1,4),(5,6)])>>>selection.flatten()[1, 2, 3, 5]>>>selection.flat_size4>>>bool(selection)TrueNode Setslibsonata can work with the Node Set concept, as described here:SONATA guide: Node Sets FileThis allows the definition of names for groups of cells, and a way to query them.
libsonata also allows for extended expressions, such as Regular expressions,and floating point tests, as described here:SONATA extension: Node Sets# load a node set JSON file>>>node_sets=libsonata.NodeSets.from_file('node_sets.json')# list node sets>>>node_sets.names{'L6_UPC', 'Layer1', 'Layer2', 'Layer3', ....}

# get the selection of nodes that match in population>>>selection=node_sets.materialize('Layer1',population)# node sets can also be loaded from a JSON string>>>node_sets_manual=libsonata.NodeSets(json.dumps({"SLM_PPA_and_SP_PC":{"mtype":["SLM_PPA","SP_PC"]}}))>>>node_sets_manual.names{'SLM_PPA_and_SP_PC'}EdgesEdgeStoragePopulation handling forEdgeStorageis analogous toNodeStorage:>>>edges=libsonata.EdgeStorage('path/to/H5/file')# list populations>>>edges.population_names# open population>>>population=edges.open_population(<name>)EdgePopulation# total number of edges in the population>>>population.size# attribute names>>>population.attribute_names# get attribute value for single edge, say 123>>>population.get_attribute('delay',123)# ...or Selection of edges => returns NumPy array with corresponding values>>>selection=libsonata.Selection([1,5,9])>>>population.get_attribute('delay',selection)# returns delays for edges 1, 5, 9…with additional methods for querying connectivity, where the results are selections that can be applied like above# get source / target node ID for the 42nd edge:>>>population.source_node(42)>>>population.target_node(42)# query connectivity (result is Selection object)>>>selection_to_1=population.afferent_edges(1)# all edges with target node_id 1>>>population.target_nodes(selection_to_1)# since selection only contains edges# targeting node_id 1 the result will be a
                                             # numpy array of all 1's>>>selection_from_2=population.efferent_edges(2)# all edges sourced from node_id 2>>>selection=population.connecting_edges(2,1)# this selection is all edges from# node_id 2 to node_id 1

# ...or their vectorized analogues>>>selection=population.afferent_edges([1,2,3])>>>selection=population.efferent_edges([1,2,3])>>>selection=population.connecting_edges([1,2,3],[4,5,6])ReportsSpikeReader>>>importlibsonata>>>spikes=libsonata.SpikeReader('path/to/H5/file')# list populations>>>spikes.get_population_names()# open population>>>population=spikes['<name>']SpikePopulation# get all spikes [(node_id, timestep)]>>>population.get()[(5, 0.1), (2, 0.2), (3, 0.3), (2, 0.7), (3, 1.3)]

# get all spikes betwen tstart and tstop>>>population.get(tstart=0.2,tstop=1.0)[(2, 0.2), (3, 0.3), (2, 0.7)]

# get spikes attribute sorting (by_time, by_id, none)>>>population.sorting'by_time'

Pandas can be used to create a dataframe and get a better representation of the data>>>importpandasdata = population.get()
df = pandas.DataFrame(data=data, columns=['ids', 'times']).set_index('times')
print(df)
       ids
times
0.1      5
0.2      2
0.3      3
0.7      2
1.3      3SomaReportReader>>>somas=libsonata.SomaReportReader('path/to/H5/file')# list populations>>>somas.get_population_names()# open population>>>population_somas=somas['<name>']SomaReportPopulation# get times (tstart, tstop, dt)>>>population_somas.times(0.0, 1.0, 0.1)

# get unit attributes>>>population_somas.time_units'ms'>>>population_somas.data_units'mV'

# node_ids sorted?>>>population_somas.sortedTrue

# get a list of all node ids in the selected population>>>population_somas.get_node_ids()[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

# get the DataFrame of the node_id values for the timesteps between tstart and tstop>>>data_frame=population_somas.get(node_ids=[13,14],tstart=0.8,tstop=1.0)# get the data values>>>data_frame.data[[13.8, 14.8], [13.9, 14.9]]

# get the list of timesteps>>>data_frame.times[0.8, 0.9]

# get the list of node ids>>>data_frame.ids[13, 14]Once again, pandas can be used to create a dataframe using the data, ids and times lists>>>importpandasdf = pandas.DataFrame(data_frame.data, columns=data_frame.ids, index=data_frame.times)
print(df)
       13    14
0.8  13.8  14.8
0.9  13.9  14.9ElementReportReader>>>elements=libsonata.ElementReportReader('path/to/H5/file')# list populations>>>elements.get_population_names()# open population>>>population_elements=elements['<name>']ElementReportPopulation# get times (tstart, tstop, dt)>>>population_elements.times(0.0, 4.0, 0.2)>>>population_elements.get_node_ids()[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

# get the DataFrame of the node_id values for the timesteps between tstart and tstop>>>data_frame=population_elements.get(node_ids=[13,14],tstart=0.8,tstop=1.0)# get the data values (list of list of floats with data[time_index][element_index])>>>data_frame.data[[46.0, 46.1, 46.2, 46.3, 46.4, 46.5, 46.6, 46.7, 46.8, 46.9], [56.0, 56.1, 56.2, 56.3, 56.4, 56.5, 56.6, 56.7, 56.8, 56.9]]

# get the list of timesteps>>>data_frame.times[0.8, 1.0]

# get the list of (node id, element_id)>>>data_frame.ids[(13, 30), (13, 30), (13, 31), (13, 31), (13, 32), (14, 32), (14, 33), (14, 33), (14, 34), (14, 34)]The same way than with spikes and soma reports, pandas can be used to get a better representation of the data>>>importpandasdf = pandas.DataFrame(data_frame.data, columns=pandas.MultiIndex.from_tuples(data_frame.ids), index=data_frame.times)
print(df)
       13                            14
       30    30    31    31    32    32    33    33    34    34
0.8  46.0  46.1  46.2  46.3  46.4  46.5  46.6  46.7  46.8  46.9
1.0  56.0  56.1  56.2  56.3  56.4  56.5  56.6  56.7  56.8  56.9For big datasets, using numpy arrays could greatly improve the performance>>>importnumpynp_data = numpy.asarray(data_frame.data)
np_ids = numpy.asarray(data_frame.ids).T
np_times = numpy.asarray(data_frame.times)

df = pandas.DataFrame(np_data, columns=pandas.MultiIndex.from_arrays(np_ids), index=np_times)AcknowledgementsThe development of this software was supported by funding to the Blue Brain Project, a research center of the École polytechnique fédérale de Lausanne (EPFL), from the Swiss government’s ETH Board of the Swiss Federal Institutes of Technology.This research was supported by the EBRAINS research infrastructure, funded from the European Union’s Horizon 2020 Framework Programme for Research and Innovation under the Specific Grant Agreement No. 945539 (Human Brain Project SGA3).
This project/research has received funding from the European Union’s Horizon 2020 Framework Programme for Research and Innovation under the Specific Grant Agreement No. 785907 (Human Brain Project SGA2).Licenselibsonata is distributed under the terms of the GNU Lesser General Public License version 3,
unless noted otherwise, for example, for external dependencies.
Refer toCOPYING.LESSERandCOPYINGfiles for details.Copyright (c) 2018-2022 Blue Brain Project/EPFLlibsonata is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 3
as published by the Free Software Foundation.libsonata is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.You should have received a copy of the GNU Lesser General Public License
along with libsonata.  If not, see <https://www.gnu.org/licenses/>.