This repository was archived by the owner on Feb 26, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathbind_immutable.cpp
More file actions
487 lines (451 loc) · 23.8 KB
/
bind_immutable.cpp
File metadata and controls
487 lines (451 loc) · 23.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
#include "bind_immutable.h"
#include <pybind11/iostream.h> // py::add_ostream_redirect
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <morphio/dendritic_spine.h>
#include <morphio/endoplasmic_reticulum.h>
#include <morphio/enums.h>
#include <morphio/glial_cell.h>
#include <morphio/mut/dendritic_spine.h>
#include <morphio/mut/endoplasmic_reticulum.h>
#include <morphio/mut/glial_cell.h>
#include <morphio/mut/mitochondria.h>
//#include <morphio/mut/dendritic_spine.h>
#include <morphio/mut/morphology.h>
#include <morphio/soma.h>
#include <morphio/types.h>
#include <memory> // std::make_unique
#include "bind_enums.h"
#include "bindings_utils.h"
namespace py = pybind11;
void bind_immutable_module(py::module& m) {
using namespace py::literals;
// http://pybind11.readthedocs.io/en/stable/advanced/pycpp/utilities.html?highlight=iostream#capturing-standard-output-from-ostream
py::add_ostream_redirect(m, "ostream_redirect");
py::class_<morphio::Morphology>(m, "Morphology")
.def(py::init<const std::string&, unsigned int>(),
"filename"_a,
"options"_a = morphio::enums::Option::NO_MODIFIER)
.def(py::init<morphio::mut::Morphology&>())
.def(py::init([](py::object arg, unsigned int options) {
return std::make_unique<morphio::Morphology>(py::str(arg), options);
}),
"filename"_a,
"options"_a = morphio::enums::Option::NO_MODIFIER,
"Additional Ctor that accepts as filename any python object that implements __repr__ "
"or __str__")
.def("as_mutable",
[](const morphio::Morphology* morph) { return morphio::mut::Morphology(*morph); })
// Cell sub-parts accessors
.def_property_readonly("soma", &morphio::Morphology::soma, "Returns the soma object")
.def_property_readonly("mitochondria",
&morphio::Morphology::mitochondria,
"Returns the soma object")
.def_property_readonly("annotations",
&morphio::Morphology::annotations,
"Returns a list of annotations")
.def_property_readonly("markers",
&morphio::Morphology::markers,
"Returns the list of NeuroLucida markers")
.def_property_readonly("endoplasmic_reticulum",
&morphio::Morphology::endoplasmicReticulum,
"Returns the endoplasmic reticulum object")
.def_property_readonly("root_sections",
&morphio::Morphology::rootSections,
"Returns a list of all root sections "
"(sections whose parent ID are -1)")
.def_property_readonly("sections",
&morphio::Morphology::sections,
"Returns a vector containing all sections objects\n\n"
"Notes:\n"
"- Soma is not included\n"
"- First section ID is 1 (0 is reserved for the soma)\n"
"- To select sections by ID use: Morphology::section(id)")
.def("section",
&morphio::Morphology::section,
"Returns the Section with the given id\n"
"throw RawDataError if the id is out of range",
"section_id"_a)
// Property accessors
.def_property_readonly(
"points",
[](const morphio::Morphology& morpho) {
return internal_vector_as_readonly_array(morpho.points(), morpho);
},
"Returns a list with all points from all sections (soma points are not included)\n"
"Note: points belonging to the n'th section are located at indices:\n"
"[Morphology.sectionOffsets(n), Morphology.sectionOffsets(n+1)[")
.def_property_readonly(
"n_points",
[](const morphio::Morphology& obj) { return obj.points().size(); },
"Returns the number of points from all sections (soma points are not included)")
.def_property_readonly(
"diameters",
[](const morphio::Morphology& morpho) {
return internal_vector_as_readonly_array(morpho.diameters(), morpho);
},
"Returns a list with all diameters from all sections (soma points are not included)\n"
"Note: diameters belonging to the n'th section are located at indices:\n"
"[Morphology.sectionOffsets(n), Morphology.sectionOffsets(n+1)[")
.def_property_readonly(
"perimeters",
[](const morphio::Morphology& morpho) {
return internal_vector_as_readonly_array(morpho.perimeters(), morpho);
},
"Returns a list with all perimeters from all sections (soma points are not included)\n"
"Note: perimeters belonging to the n'th section are located at indices:\n"
"[Morphology.sectionOffsets(n), Morphology.sectionOffsets(n+1)[")
.def_property_readonly(
"section_offsets",
[](const morphio::Morphology& morpho) { return as_pyarray(morpho.sectionOffsets()); },
"Returns a list with N+1 offsets to access data of a specific section in the points\n"
"and diameters arrays (size N).\n"
"\n"
"Example: accessing diameters of n'th section will be located in the diameters\n"
"array: diameters[section_offsets[n]: section_offsets[n + 1]]\n"
"\n"
"Note: for convenience, the last element of this array is equal to the length of the\n"
"points/diameters arrays so that the above example works also for the last section.")
.def_property_readonly(
"section_types",
[](const morphio::Morphology& morph) {
return internal_vector_as_readonly_array(morph.sectionTypes(), morph);
},
"Returns a vector with the section type of every section")
.def_property_readonly("connectivity",
&morphio::Morphology::connectivity,
"Return the graph connectivity of the morphology "
"where each section is seen as a node\nNote: -1 is the soma node")
.def_property_readonly("soma_type", &morphio::Morphology::somaType, "Returns the soma type")
.def_property_readonly("cell_family",
&morphio::Morphology::cellFamily,
"Returns the cell family (neuron or glia)")
.def_property_readonly("version", &morphio::Morphology::version, "Returns the version")
// Iterators
.def(
"iter",
[](morphio::Morphology* morpho, IterType type) {
switch (type) {
case IterType::DEPTH_FIRST:
return py::make_iterator(morpho->depth_begin(), morpho->depth_end());
case IterType::BREADTH_FIRST:
return py::make_iterator(morpho->breadth_begin(), morpho->breadth_end());
case IterType::UPSTREAM:
default:
throw morphio::MorphioError(
"Only iteration types depth_first and breadth_first are supported");
}
},
py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */,
"Section iterator that runs successively on every neurite\n"
"iter_type controls the order of iteration on sections of a given neurite. 2 values "
"can be passed:\n"
"- morphio.IterType.depth_first (default)\n"
"- morphio.IterType.breadth_first (default)\n"
"iter_type"_a = IterType::DEPTH_FIRST);
py::class_<morphio::GlialCell, morphio::Morphology>(m, "GlialCell")
.def(py::init<const std::string&>())
.def(py::init(
[](py::object arg) { return std::make_unique<morphio::GlialCell>(py::str(arg)); }),
"filename"_a,
"Additional Ctor that accepts as filename any python object that implements __repr__ "
"or __str__");
py::class_<morphio::Mitochondria>(
m,
"Mitochondria",
"The entry-point class to access mitochondrial data\n"
"By design, it is the equivalent of the Morphology class but at the mitochondrial level\n"
"As the Morphology class, it implements a section accessor and a root section accessor\n"
"returning views on the Properties object for the queried mitochondrial section")
.def("section",
&morphio::Mitochondria::section,
"Returns the mithochondrial section with the given ID",
"section_id"_a)
.def_property_readonly("sections",
&morphio::Mitochondria::sections,
"Returns a list of all mitochondrial sections")
.def_property_readonly(
"root_sections",
&morphio::Mitochondria::rootSections,
"Returns a list of all root sections (section whose parent ID is -1)");
py::class_<morphio::EndoplasmicReticulum>(
m,
"EndoplasmicReticulum",
"The entry-point class to access endoplasmic reticulum data\n"
"Spec "
"https://bbpteam.epfl.ch/documentation/projects/Morphology%20Documentation/latest/"
"h5v1.html")
.def_property_readonly("section_indices",
&morphio::EndoplasmicReticulum::sectionIndices,
"Returns the list of neuronal section indices")
.def_property_readonly("volumes",
&morphio::EndoplasmicReticulum::volumes,
"Returns the list of neuronal section indices")
.def_property_readonly("surface_areas",
&morphio::EndoplasmicReticulum::surfaceAreas,
"Returns the surface areas for each neuronal section")
.def_property_readonly("filament_counts",
&morphio::EndoplasmicReticulum::filamentCounts,
"Returns the number of filaments for each neuronal section");
py::class_<morphio::Soma>(m, "Soma")
.def(py::init<const morphio::Soma&>())
.def_property_readonly(
"points",
[](morphio::Soma* soma) { return span_array_to_ndarray(soma->points()); },
"Returns the coordinates (x,y,z) of all soma point")
.def_property_readonly(
"diameters",
[](morphio::Soma* soma) { return span_to_ndarray(soma->diameters()); },
"Returns the diameters of all soma points")
.def_property_readonly(
"center",
[](morphio::Soma* soma) { return py::array(3, soma->center().data()); },
"Returns the center of gravity of the soma points")
.def_property_readonly("max_distance",
&morphio::Soma::maxDistance,
"Return the maximum distance between the center of gravity "
"and any of the soma points")
.def_property_readonly("type", &morphio::Soma::type, "Returns the soma type")
.def_property_readonly("surface",
&morphio::Soma::surface,
"Returns the soma surface\n\n"
"Note: the soma surface computation depends on the soma type");
py::class_<morphio::Section>(m, "Section")
.def("__str__",
[](const morphio::Section& section) {
std::stringstream ss;
ss << section;
return ss.str();
})
// Topology-related member functions
.def_property_readonly("parent",
&morphio::Section::parent,
"Returns the parent section of this section\n"
"throw MissingParentError is the section doesn't have a parent")
.def_property_readonly("is_root",
&morphio::Section::isRoot,
"Returns true if this section is a root section (parent ID == -1)")
.def_property_readonly("children",
&morphio::Section::children,
"Returns a list of children sections")
// Property-related accessors
.def_property_readonly(
"id",
&morphio::Section::id,
"Returns the section ID\n"
"The section ID can be used to query sections via Morphology::section(uint32_t id)")
.def_property_readonly("type",
&morphio::Section::type,
"Returns the morphological type of this section "
"(dendrite, axon, ...)")
.def_property_readonly(
"points",
[](const morphio::Section& self) {
return internal_vector_as_readonly_array(self.points(), self);
},
"Returns list of section's point coordinates")
.def_property_readonly(
"n_points",
[](const morphio::Section& section) { return section.points().size(); },
"Returns the number of points in section")
.def_property_readonly(
"diameters",
[](const morphio::Section& self) {
return internal_vector_as_readonly_array(self.diameters(), self);
},
"Returns list of section's point diameters")
.def_property_readonly(
"perimeters",
[](const morphio::Section& self) {
return internal_vector_as_readonly_array(self.perimeters(), self);
},
"Returns list of section's point perimeters")
.def("is_heterogeneous",
&morphio::Section::is_heterogeneous,
"Returns true if the tree downtream (downstream = true) or upstream (downstream = "
"false)\n"
"has the same type as the current section.",
py::arg("downstream") = true)
// Iterators
.def(
"iter",
[](morphio::Section* section, IterType type) {
switch (type) {
case IterType::DEPTH_FIRST:
return py::make_iterator(section->depth_begin(), section->depth_end());
case IterType::BREADTH_FIRST:
return py::make_iterator(section->breadth_begin(), section->breadth_end());
case IterType::UPSTREAM:
return py::make_iterator(section->upstream_begin(), section->upstream_end());
default:
throw morphio::MorphioError(
"Only iteration types depth_first, breadth_first and "
"upstream are supported");
}
},
py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */,
"Section iterator\n"
"\n"
"iter_type controls the iteration order. 3 values can be passed:\n"
"- morphio.IterType.depth_first (default)\n"
"- morphio.IterType.breadth_first\n"
"- morphio.IterType.upstream\n",
"iter_type"_a = IterType::DEPTH_FIRST);
py::class_<morphio::MitoSection>(m, "MitoSection")
// Topology-related member functions
.def_property_readonly("parent",
&morphio::MitoSection::parent,
"Returns the parent mitochondrial section of this section\n"
"throw MissingParentError is the section doesn't have a parent")
.def_property_readonly("is_root",
&morphio::MitoSection::isRoot,
"Returns true if this section is a root section (parent ID == -1)")
.def_property_readonly("children",
&morphio::MitoSection::children,
"Returns a list of children mitochondrial sections")
// Property-related accesors
.def_property_readonly(
"id",
&morphio::MitoSection::id,
"Returns the section ID\n"
"The section ID can be used to query sections via Mitochondria::section(uint32_t id)")
.def_property_readonly(
"neurite_section_ids",
[](morphio::MitoSection* section) {
return span_to_ndarray(section->neuriteSectionIds());
},
"Returns list of neuronal section IDs associated to each point "
"of this mitochondrial section")
.def_property_readonly(
"diameters",
[](morphio::MitoSection* section) { return span_to_ndarray(section->diameters()); },
"Returns list of section's point diameters")
.def_property_readonly(
"relative_path_lengths",
[](morphio::MitoSection* section) {
return span_to_ndarray(section->relativePathLengths());
},
"Returns list of relative distances between the start of the "
"neuronal section and each point of the mitochondrial section\n\n"
"Note: - a relative distance of 0 means the mitochondrial point is at the "
"beginning of the neuronal section\n"
" - a relative distance of 1 means the mitochondrial point is at the "
"end of the neuronal section\n")
// Iterators
.def(
"iter",
[](morphio::MitoSection* section, IterType type) {
switch (type) {
case IterType::DEPTH_FIRST:
return py::make_iterator(section->depth_begin(), section->depth_end());
case IterType::BREADTH_FIRST:
return py::make_iterator(section->breadth_begin(), section->breadth_end());
case IterType::UPSTREAM:
return py::make_iterator(section->upstream_begin(), section->upstream_end());
default:
throw morphio::MorphioError(
"Only iteration types depth_first, breadth_first and "
"upstream are supported");
}
},
py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */,
"Depth first iterator starting at a given section id\n"
"\n"
"If id == -1, the iteration will be successively performed starting\n"
"at each root section",
"iter_type"_a = IterType::DEPTH_FIRST);
py::class_<morphio::DendriticSpine, morphio::Morphology>(m, "DendriticSpine")
.def(py::init([](py::object arg) {
return std::make_unique<morphio::DendriticSpine>(py::str(arg));
}),
"filename"_a)
.def_property_readonly("root_sections",
&morphio::DendriticSpine::rootSections,
"Returns a list of all root sections "
"(sections whose parent ID are -1)")
.def_property_readonly("sections",
&morphio::DendriticSpine::sections,
"Returns a vector containing all sections objects\n\n"
"Notes:\n"
"- Soma is not included\n"
"- First section ID is 1 (0 is reserved for the soma)\n"
"- To select sections by ID use: DendriticSpine::section(id)")
.def("section",
&morphio::DendriticSpine::section,
"Returns the Section with the given id\n"
"throw RawDataError if the id is out of range",
"section_id"_a)
// Property accessors
.def_property_readonly(
"points",
[](const morphio::DendriticSpine& morph) {
return internal_vector_as_readonly_array(morph.points(), morph);
},
"Returns a list with all points from all sections\n"
"Note: points belonging to the n'th section are located at indices:\n"
"[DendriticSpine.sectionOffsets(n), DendriticSpine.sectionOffsets(n+1)[")
.def_property_readonly(
"diameters",
[](const morphio::DendriticSpine& morph) {
return internal_vector_as_readonly_array(morph.diameters(), morph);
},
"Returns a list with all diameters from all sections\n"
"Note: diameters belonging to the n'th section are located at indices:\n"
"[DendriticSpine.sectionOffsets(n), DendriticSpine.sectionOffsets(n+1)[")
.def_property_readonly(
"section_offsets",
[](const morphio::DendriticSpine& morpho) {
return as_pyarray(morpho.sectionOffsets());
},
"Returns a list with offsets to access data of a specific section in the points\n"
"and diameters arrays.\n"
"\n"
"Example: accessing diameters of n'th section will be located in the DIAMETERS\n"
"array from DIAMETERS[sectionOffsets(n)] to DIAMETERS[sectionOffsets(n+1)-1]\n"
"\n"
"Note: for convenience, the last point of this array is the points array size\n"
"so that the above example works also for the last section.")
.def_property_readonly(
"section_types",
[](const morphio::DendriticSpine& morph) {
return internal_vector_as_readonly_array(morph.diameters(), morph);
},
"Returns a vector with the section type of every section")
.def_property_readonly("connectivity",
&morphio::DendriticSpine::connectivity,
"Return the graph connectivity of the DendriticSpine "
"where each section is seen as a node\nNote: -1 is the soma node")
.def_property_readonly("cell_family",
&morphio::DendriticSpine::cellFamily,
"Returns the cell family (neuron or glia)")
.def_property_readonly("post_synaptic_density",
&morphio::DendriticSpine::postSynapticDensity,
"Returns the post synaptic density values")
.def_property_readonly("version", &morphio::DendriticSpine::version, "Returns the version")
// Iterators
.def(
"iter",
[](morphio::DendriticSpine* morpho, IterType type) {
switch (type) {
case IterType::DEPTH_FIRST:
return py::make_iterator(morpho->depth_begin(), morpho->depth_end());
case IterType::BREADTH_FIRST:
return py::make_iterator(morpho->breadth_begin(), morpho->breadth_end());
case IterType::UPSTREAM:
default:
throw morphio::MorphioError(
"Only iteration types depth_first and breadth_first are supported");
}
},
py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */,
"Section iterator that runs successively on every neurite\n"
"iter_type controls the order of iteration on sections of a given neurite. 2 values "
"can be passed:\n"
"- morphio.IterType.depth_first (default)\n"
"- morphio.IterType.breadth_first (default)\n",
"iter_type"_a = IterType::DEPTH_FIRST)
.def(
"write",
[](morphio::mut::DendriticSpine* morph, py::object arg) { morph->write(py::str(arg)); },
"filename"_a);
}