From a3819c2c492b4cda13fa7b4c76798161e04bc356 Mon Sep 17 00:00:00 2001 From: Daniel Nachbaur Date: Wed, 17 Aug 2016 17:05:55 +0200 Subject: [PATCH] Implement brain synapses specification --- CMakeLists.txt | 4 +- README.md | 18 +- brain/CMakeLists.txt | 11 + brain/circuit.cpp | 496 +-------------------- brain/circuit.h | 44 +- brain/detail/circuit.h | 535 +++++++++++++++++++++++ brain/detail/synapsesStream.h | 85 ++++ brain/enums.h | 51 +++ brain/synapse.cpp | 148 +++++++ brain/synapse.h | 125 ++++++ brain/synapses.cpp | 597 ++++++++++++++++++++++++++ brain/synapses.h | 222 ++++++++++ brain/synapsesIterator.cpp | 58 +++ brain/synapsesIterator.h | 51 +++ brain/synapsesStream.cpp | 74 ++++ brain/synapsesStream.h | 85 ++++ brain/types.h | 20 +- brion/enums.h | 3 +- brion/synapse.cpp | 4 +- brion/synapseSummary.cpp | 2 +- brion/synapseSummary.h | 2 +- doc/Changelog.md | 4 + tests/CMakeLists.txt | 12 +- tests/{ => brain}/circuit.cpp | 0 tests/brain/morphology.cpp | 370 ++++++++++++++++ tests/{ => brain}/spikeReportReaderWriter.cpp | 0 tests/brain/synapses.cpp | 265 ++++++++++++ tests/morphology.cpp | 249 +---------- 28 files changed, 2791 insertions(+), 744 deletions(-) create mode 100644 brain/detail/circuit.h create mode 100644 brain/detail/synapsesStream.h create mode 100644 brain/enums.h create mode 100644 brain/synapse.cpp create mode 100644 brain/synapse.h create mode 100644 brain/synapses.cpp create mode 100644 brain/synapses.h create mode 100644 brain/synapsesIterator.cpp create mode 100644 brain/synapsesIterator.h create mode 100644 brain/synapsesStream.cpp create mode 100644 brain/synapsesStream.h rename tests/{ => brain}/circuit.cpp (100%) create mode 100644 tests/brain/morphology.cpp rename tests/{ => brain}/spikeReportReaderWriter.cpp (100%) create mode 100644 tests/brain/synapses.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e905bd9..f7b5c4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,9 @@ common_find_package_post() list(APPEND BRION_DEPENDENT_LIBRARIES Boost Lunchbox Servus vmmlib) add_subdirectory(brion) -add_subdirectory(brain) +if(NOT COMMON_USE_CXX03) + add_subdirectory(brain) +endif() add_subdirectory(apps) add_subdirectory(tests) diff --git a/README.md b/README.md index 11b3021..38bcff1 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Mesh, Morphology, Synapse and Target files. Brion can be retrieved by cloning the [source code](https://github.com/BlueBrain/Brion.git). The [latest API documentation] -(http://bluebrain.github.io/Brion-1.7/index.html) can be found on +(http://bluebrain.github.io/Brion-1.9/index.html) can be found on [bluebrain.github.io](http://bluebrain.github.io). To keep track of the changes between releases check the [changelog](@ref Changelog). @@ -20,8 +20,8 @@ To keep track of the changes between releases check the [changelog](@ref Changel Brion provides two libraries Brion and Brain. The former is a collection of file readers and writers intended for low level access to the data model. The latter -is a set of higher level classes that wrap low level data objects with a more -user friendly API. +is a set of higher level classes that wrap low level data objects with a +use-case oriented API. ## IO library @@ -45,15 +45,17 @@ and writing files which store the Blue Brain data model. * Basic [data types](@ref brion/types.h) to work with the loaded data using [Boost](http://www.boost.org/doc/libs), [Lunchbox](http://eyescale.github.io/Lunchbox-1.14/index.html), - [vmmlib](http://eyescale.github.io/vmmlib-1.11/index.html). + [vmmlib](http://eyescale.github.io/vmmlib-1.12/index.html). ## High level library The higher level library is called Brain and it provides: -* brain::Circuit to facilitate loading information about cells and - morphologies in local and global circuit coordinates. -* brain::neuron::Morphology with higher level functions to deal with morphologies. +* brain::Circuit to facilitate loading information about cells, morphologies (in + local and global circuit coordinates) and synapses. +* brain::neuron::Morphology with higher level functions to deal with + morphologies. +* brain::Synapses and brain::Synapse for array and object access to synapses. # Building {#Building} @@ -62,7 +64,7 @@ system, including all Unix variants. Brion uses CMake to create a platform-specific build environment. The following platforms and build environments are tested: -* Linux: Ubuntu 14.04 and RHEL 6 (Makefile, i386, x64) +* Linux: Ubuntu 14.04 & 16.04 and RHEL 6 (Makefile, i386, x64) Building from source is as simple as: diff --git a/brain/CMakeLists.txt b/brain/CMakeLists.txt index e643a18..57e3417 100644 --- a/brain/CMakeLists.txt +++ b/brain/CMakeLists.txt @@ -6,6 +6,7 @@ set(BRAIN_PUBLIC_HEADERS circuit.h + enums.h neuron/morphology.h neuron/section.h neuron/soma.h @@ -13,11 +14,17 @@ set(BRAIN_PUBLIC_HEADERS spikeReportReader.h spikeReportWriter.h spikes.h + synapse.h + synapses.h + synapsesIterator.h + synapsesStream.h types.h ) set(BRAIN_HEADERS + detail/circuit.h detail/spikes.h + detail/synapsesStream.h neuron/morphologyImpl.h ) @@ -30,6 +37,10 @@ set(BRAIN_SOURCES spikeReportReader.cpp spikeReportWriter.cpp spikes.cpp + synapse.cpp + synapses.cpp + synapsesIterator.cpp + synapsesStream.cpp ) set(BRAIN_PUBLIC_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIRS}) diff --git a/brain/circuit.cpp b/brain/circuit.cpp index fc579ed..ad704cc 100644 --- a/brain/circuit.cpp +++ b/brain/circuit.cpp @@ -21,481 +21,13 @@ */ #include "circuit.h" -#include "neuron/morphology.h" +#include "detail/circuit.h" -#include -#include -#include -#include -#include - -#include -#include - -#ifdef BRAIN_USE_MVD3 -# include -# include -#endif - -#include -#include - -#ifdef BRION_USE_CXX11 -# include -# include -#else -# include -#endif - -namespace fs = boost::filesystem; -using boost::lexical_cast; +#include "synapsesStream.h" namespace brain { -namespace -{ -#ifdef BRAIN_USE_MVD3 -bool isSequence( const GIDSet& gids ) -{ - return ( *gids.rbegin() - *gids.begin() + 1 ) == gids.size(); -} - -::MVD3::Range getRange( const GIDSet& gids ) -{ - const size_t offset = ( *gids.begin( )); - const size_t count = *gids.rbegin() - offset + 1; - return ::MVD3::Range( offset - 1, count ); -} - -template< typename SrcArray, typename DstArray, typename AssignOp > -void assign( const ::MVD3::Range& range, const GIDSet& gids, - SrcArray& src, DstArray& dst, const AssignOp& assignOp ) -{ - if( isSequence( gids )) // OPT: no holes, no translation needed - { - std::transform( src.begin(), src.end(), dst.begin(), assignOp ); - return; - } - - typename DstArray::iterator dst_it = dst.begin(); - for( GIDSet::const_iterator i = gids.begin(); i != gids.end(); ++i ) - { - typename SrcArray::const_iterator src_it = src.begin(); - std::advance( src_it, *i - range.offset - 1 ); - *dst_it = assignOp( *src_it ); - ++dst_it; - } -} - -Vector3f toVector3f( - const ::MVD3::Positions::const_subarray< 1 >::type& subarray ) -{ - return Vector3f( subarray[0], subarray[1], subarray[2] ); -} - -Quaternionf toQuaternion( - const ::MVD3::Rotations::const_subarray< 1 >::type& subarray ) -{ - return Quaternionf( subarray[0], subarray[1], subarray[2], subarray[3] ); -} -#endif - -std::string toString( const std::string& in ) { return in; } -#ifdef BRION_USE_CXX03 -size_t toSize_t( const std::string& in ) - { return boost::lexical_cast< size_t >( in ); } -#else -size_t toSize_t( const std::string& in ) { return std::stoul( in ); } -#endif -size_t nop( const size_t& in ) { return in; } - -typedef boost::unordered_map< std::string, neuron::MorphologyPtr > Loaded; -} - -class Circuit::Impl -{ -public: - explicit Impl( const brion::BlueConfig& config ) - : _circuitSource( config.getCircuitSource( )) - , _morphologySource( config.getMorphologySource( )) - , _targetSources( config.getTargetSources( )) - , _cache( lunchbox::PersistentMap::createCache( )) -#ifdef BRION_USE_CXX11 - , _randomEngine( _randomDevice( )) -#endif - { - const char* seedEnv = getenv( "BRAIN_CIRCUIT_SEED" ); - if( seedEnv ) - { -#ifdef BRION_USE_CXX11 - _randomEngine.seed( std::stoul( seedEnv )); -#else - srand( atol( seedEnv )); -#endif - } - } - - virtual ~Impl() {} - - virtual size_t getNumNeurons() const = 0; - - const brion::URI& getCircuitSource() const - { - return _circuitSource; - } - - GIDSet getGIDs() const - { - brain::GIDSet gids; - brain::GIDSet::const_iterator hint = gids.begin(); - for( uint32_t i = 0; i < getNumNeurons(); ++i ) - hint = gids.insert( hint, i + 1 ); - return gids; - } - - GIDSet getGIDs( const std::string& target ) const - { - if( _targetParsers.empty( )) - { - BOOST_FOREACH( const URI& uri, _targetSources ) - _targetParsers.push_back( brion::Target( uri.getPath( ))); - } - return brion::Target::parse( _targetParsers, target ); - } - - GIDSet getRandomGIDs( const float fraction, - const std::string& target ) const - { - if( fraction < 0.f || fraction > 1.f ) - LBTHROW( std::runtime_error( "Fraction for getRandomGIDs() must be " - "in the range [0,1]" )); - - const GIDSet& gids = target.empty() ? getGIDs() : getGIDs( target ); - uint32_ts randomGids( gids.begin(), gids.end( )); -#ifdef BRION_USE_CXX1 - std::shuffle( randomGids.begin(), randomGids.end(), _randomEngine ); -#else - std::random_shuffle( randomGids.begin(), randomGids.end( )); -#endif - randomGids.resize( size_t( std::ceil( randomGids.size() * fraction ))); - return GIDSet( randomGids.begin(), randomGids.end( )); - } - - virtual Vector3fs getPositions( const GIDSet& gids ) const = 0; - virtual size_ts getMTypes( const GIDSet& gids ) const = 0; - virtual Strings getMorphologyNames() const = 0; - virtual size_ts getETypes( const GIDSet& gids ) const = 0; - virtual Strings getElectrophysiologyNames() const = 0; - virtual Quaternionfs getRotations( const GIDSet& gids ) const = 0; - virtual Strings getMorphologyNames( const GIDSet& gids ) const = 0; - - URI getMorphologyURI( const std::string& name ) const - { - URI uri; - uri.setPath( _morphologySource.getPath() + "/" + name + ".h5" ); - uri.setScheme( "file" ); - return uri; - } - - void saveToCache( const std::string& hash, - neuron::MorphologyPtr morphology ) - { - if( _cache ) - { - servus::Serializable::Data data = morphology->toBinary(); - _cache->insert( hash, data.ptr.get(), data.size ); - } - } - - Loaded loadFromCache( const std::set< std::string >& hashes LB_UNUSED ) - const - { - Loaded loaded; -#ifdef BRION_USE_CXX11 - if( _cache ) - { - LBDEBUG << "Using cache for morphology loading" << std::endl; - typedef std::future< std::pair< std::string, - neuron::MorphologyPtr > > Future; - std::vector< Future > futures; - - Strings keys( hashes.begin(), hashes.end( )); - futures.reserve( keys.size( )); - - _cache->takeValues( keys, [&futures] ( const std::string& key, - char* data, const size_t size ) - { - futures.push_back( std::async( std::launch::async, - [key, data, size] - { - neuron::MorphologyPtr morphology( - new neuron::Morphology( data, size )); - std::free( data ); - return std::make_pair( key, morphology ); - })); - }); - - for( auto& future : futures ) - loaded.insert( future.get( )); - - LBINFO << "Loaded " << loaded.size() << " out of " << hashes.size() - << " morphologies from cache" << std::endl; - } -#endif - return loaded; - } - -private: - const brion::URI _circuitSource; - const brion::URI _morphologySource; - const brion::URIs _targetSources; - mutable brion::Targets _targetParsers; - lunchbox::PersistentMapPtr _cache; - -#ifdef BRION_USE_CXX11 - std::random_device _randomDevice; - std::mt19937_64 _randomEngine; -#endif -}; - -class MVD2 : public Circuit::Impl -{ -public: - MVD2( const brion::BlueConfig& config ) - : Impl( config ) - , _circuit( config.getCircuitSource().getPath( )) - {} - - size_t getNumNeurons() const final - { - return _circuit.getNumNeurons(); - } - - Vector3fs getPositions( const GIDSet& gids ) const final - { - const brion::NeuronMatrix& data = _circuit.get( gids, - brion::NEURON_POSITION_X | brion::NEURON_POSITION_Y | - brion::NEURON_POSITION_Z ); - - Vector3fs positions( gids.size( )); -#pragma omp parallel for - for( size_t i = 0; i < gids.size(); ++i ) - { - try - { - positions[i] = - brion::Vector3f( lexical_cast< float >( data[i][0] ), - lexical_cast< float >( data[i][1] ), - lexical_cast< float >( data[i][2] )); - } - catch( const boost::bad_lexical_cast& ) - { - GIDSet::const_iterator gid = gids.begin(); - std::advance( gid, i ); - LBWARN << "Error parsing circuit position for gid " - << *gid << std::endl; - } - } - return positions; - } - - size_ts getMTypes( const GIDSet& gids ) const final - { - const brion::NeuronMatrix& matrix = _circuit.get( gids, - brion::NEURON_MTYPE ); - size_ts result( matrix.shape()[ 0 ]); - - brion::NeuronMatrix::const_array_view<1>::type view = - matrix[ boost::indices[brion::NeuronMatrix::index_range( )][ 0 ]]; - std::transform( view.begin(), view.end(), result.begin(), toSize_t ); - return result; - } - - Strings getMorphologyNames() const final - { - return _circuit.getTypes( brion::NEURONCLASS_MTYPE ); - } - - size_ts getETypes( const GIDSet& gids ) const final - { - const brion::NeuronMatrix& matrix = _circuit.get( gids, - brion::NEURON_ETYPE ); - size_ts result( matrix.shape()[ 0 ]); - - brion::NeuronMatrix::const_array_view<1>::type view = - matrix[ boost::indices[brion::NeuronMatrix::index_range( )][ 0 ]]; - std::transform( view.begin(), view.end(), result.begin(), toSize_t ); - return result; - } - - Strings getElectrophysiologyNames() const final - { - return _circuit.getTypes( brion::NEURONCLASS_ETYPE ); - } - - Quaternionfs getRotations( const GIDSet& gids ) const final - { - const float deg2rad = float( M_PI ) / 180.f; - const brion::NeuronMatrix& data = - _circuit.get( gids, brion::NEURON_ROTATION ); - Quaternionfs rotations( gids.size( )); - -#pragma omp parallel for - for( size_t i = 0; i < gids.size(); ++i ) - { - try - { - // transform rotation Y angle in degree into rotation quaternion - const float angle = lexical_cast( data[i][0] ) * deg2rad; - rotations[i] = Quaternionf( angle, Vector3f( 0, 1, 0 )); - } - catch( const boost::bad_lexical_cast& ) - { - GIDSet::const_iterator gid = gids.begin(); - std::advance( gid, i ); - LBWARN << "Error parsing circuit orientation for gid " - << *gid << std::endl; - } - } - return rotations; - } - - Strings getMorphologyNames( const GIDSet& gids ) const final - { - const brion::NeuronMatrix& matrix = - _circuit.get( gids, brion::NEURON_MORPHOLOGY_NAME ); - Strings result( matrix.shape()[ 0 ]); - - brion::NeuronMatrix::const_array_view<1>::type view = - matrix[ boost::indices[brion::NeuronMatrix::index_range( )][ 0 ]]; - std::transform( view.begin(), view.end(), result.begin(), toString ); - return result; - } - -private: - brion::Circuit _circuit; -}; - -#ifdef BRAIN_USE_MVD3 -class MVD3 : public Circuit::Impl -{ -public: - MVD3( const brion::BlueConfig& config ) - : Impl( config ) - , _circuit( config.getCircuitSource().getPath( )) - {} - - size_t getNumNeurons() const final - { - return _circuit.getNbNeuron(); - } - - Vector3fs getPositions( const GIDSet& gids ) const final - { - Vector3fs results( gids.size( )); - const ::MVD3::Range& range = getRange( gids ); - try - { - brion::detail::SilenceHDF5 silence; - const ::MVD3::Positions& positions = _circuit.getPositions( range ); - assign( range, gids, positions, results, toVector3f ); - return results; - } - catch( const HighFive::Exception& e ) - { - LBTHROW( std::runtime_error( "Exception in getPositions(): " + - std::string( e.what( )))); - } - } - - size_ts getMTypes( const GIDSet& gids ) const final - { - size_ts results( gids.size( )); - const ::MVD3::Range& range = getRange( gids ); - try - { - brion::detail::SilenceHDF5 silence; - const size_ts& mtypes = _circuit.getIndexMtypes( range ); - assign( range, gids, mtypes, results, nop ); - return results; - } - catch( const HighFive::Exception& e ) - { - LBTHROW( std::runtime_error( "Exception in getMTypes(): " + - std::string( e.what( )))); - } - } - - Strings getMorphologyNames() const final - { - return _circuit.listAllMtypes(); - } - - size_ts getETypes( const GIDSet& gids ) const final - { - size_ts results( gids.size( )); - const ::MVD3::Range& range = getRange( gids ); - try - { - brion::detail::SilenceHDF5 silence; - const size_ts& etypes = _circuit.getIndexEtypes( range ); - assign( range, gids, etypes, results, nop ); - return results; - } - catch( const HighFive::Exception& e ) - { - LBTHROW( std::runtime_error( "Exception in getETypes(): " + - std::string( e.what( )))); - } - } - - Strings getElectrophysiologyNames() const final - { - return _circuit.listAllEtypes(); - } - - Quaternionfs getRotations( const GIDSet& gids ) const final - { - Quaternionfs results( gids.size( )); - const ::MVD3::Range& range = getRange( gids ); - try - { - brion::detail::SilenceHDF5 silence; - const ::MVD3::Rotations& rotations = _circuit.getRotations( range ); - assign( range, gids, rotations, results, toQuaternion ); - return results; - } - catch( const HighFive::Exception& e ) - { - LBTHROW( std::runtime_error( "Exception in getRotations(): " + - std::string( e.what( )))); - } - } - - Strings getMorphologyNames( const GIDSet& gids ) const final - { - Strings results( gids.size( )); - const ::MVD3::Range& range = getRange( gids ); - try - { - brion::detail::SilenceHDF5 silence; - const Strings& morphos = _circuit.getMorphologies( range ); - assign( range, gids, morphos, results, toString ); - return results; - } - catch( const HighFive::Exception& e ) - { - LBTHROW( std::runtime_error( "Exception in getMorphologyNames(): " + - std::string( e.what( )))); - } - } - -private: - ::MVD3::MVD3File _circuit; -}; -#endif - Circuit::Impl* newImpl( const brion::BlueConfig& config ) { const std::string circuit = config.getCircuitSource().getPath(); @@ -520,7 +52,6 @@ Circuit::Circuit( const brion::BlueConfig& config ) Circuit::~Circuit() { - delete _impl; } GIDSet Circuit::getGIDs() const @@ -662,14 +193,33 @@ Matrix4fs Circuit::getTransforms( const GIDSet& gids ) const } +Quaternionfs Circuit::getRotations( const GIDSet& gids ) const +{ + return _impl->getRotations( gids ); +} + size_t Circuit::getNumNeurons() const { return _impl->getNumNeurons(); } -Quaternionfs Circuit::getRotations( const GIDSet& gids ) const +SynapsesStream Circuit::getAfferentSynapses( const GIDSet& gids, + const SynapsePrefetch prefetch ) const { - return _impl->getRotations( gids ); + return SynapsesStream( *this, gids, true, prefetch ); +} + +SynapsesStream Circuit::getEfferentSynapses( const GIDSet& gids, + const SynapsePrefetch prefetch ) const +{ + return SynapsesStream( *this, gids, false, prefetch ); +} + +SynapsesStream Circuit::getProjectedSynapses( const GIDSet& preGIDs, + const GIDSet& postGIDs, + const SynapsePrefetch prefetch ) const +{ + return SynapsesStream( *this, preGIDs, postGIDs, prefetch ); } } diff --git a/brain/circuit.h b/brain/circuit.h index 609bdab..33ee5cb 100644 --- a/brain/circuit.h +++ b/brain/circuit.h @@ -26,7 +26,7 @@ #include #include // return value -#include +#include namespace brain { @@ -36,7 +36,7 @@ namespace brain * This class provides convenience functions to access information about the * cells inside the circuit and their morphologies. */ -class Circuit : public boost::noncopyable +class Circuit { public: /** Coordinate system to use for circuit morphologies */ @@ -120,15 +120,53 @@ class Circuit : public boost::noncopyable /** @return The local to world transformations of the given cells. */ BRAIN_API Matrix4fs getTransforms( const GIDSet& gids ) const; + /** @return The local to world rotation of the given cells. */ BRAIN_API Quaternionfs getRotations( const GIDSet& gids ) const; + /** @return The number of neurons in the circuit. */ BRAIN_API size_t getNumNeurons() const; + /** + * Access all afferent synapses of the given GIDs. + * + * @param gids the gids to load afferent synapses for + * @param prefetch which synapse data to load on SynapsesStream.read() + * @return synapse data stream + */ + BRAIN_API SynapsesStream getAfferentSynapses( const GIDSet& gids, + SynapsePrefetch prefetch = SYNAPSEPREFETCH_ALL ) const; + + /** + * Access all efferent synapses of the given GIDs. + * + * @param gids the gids to load efferent synapses for + * @param prefetch which synapse data to load on SynapsesStream.read() + * @return synapse data stream + */ + BRAIN_API SynapsesStream getEfferentSynapses( const GIDSet& gids, + SynapsePrefetch prefetch = SYNAPSEPREFETCH_ALL ) const; + + /** + * Access all synapses along the projection from the pre- to the postGIDs. + * + * @param preGIDs the gids to load the efferent synapses for + * @param postGIDs the gids to load the afferent synapses for + * @param prefetch which synapse data to load on SynapsesStream.read() + * @return synapse data stream + */ + BRAIN_API SynapsesStream getProjectedSynapses( const GIDSet& preGIDs, + const GIDSet& postGIDs, + SynapsePrefetch prefetch = SYNAPSEPREFETCH_ALL ) const; + class Impl; //!< @internal, public for inheritance MVD2/3 impls private: - Impl* _impl; + Circuit( const Circuit& ) = delete; + Circuit& operator=( const Circuit& ) = delete; + + friend class Synapses; + std::unique_ptr< const Impl > _impl; }; } diff --git a/brain/detail/circuit.h b/brain/detail/circuit.h new file mode 100644 index 0000000..98bb3e4 --- /dev/null +++ b/brain/detail/circuit.h @@ -0,0 +1,535 @@ +/* Copyright (c) 2013-2016, EPFL/Blue Brain Project + * Juan Hernando + * Adrien.Devresse@epfl.ch + * Daniel.Nachbaur@epfl.ch + * Stefan.Eilemann@epfl.ch + * + * This file is part of Brion + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef BRAIN_USE_MVD3 +# include +# include +#endif + +#include +#include + +#include +#include + +namespace fs = boost::filesystem; +using boost::lexical_cast; + +namespace brain +{ + +const std::string summaryFilename( "/nrn_summary.h5" ); +const std::string afferentFilename( "/nrn.h5" ); +const std::string efferentFilename( "/nrn_efferent.h5" ); +const std::string afferentPositionsFilename( "/nrn_positions.h5" ); +const std::string efferentPositionsFilename( "/nrn_positions_efferent.h5" ); +const std::string extraFilename( "/nrn_extra.h5" ); + +namespace +{ +#ifdef BRAIN_USE_MVD3 +bool isSequence( const GIDSet& gids ) +{ + return ( *gids.rbegin() - *gids.begin() + 1 ) == gids.size(); +} + +::MVD3::Range getRange( const GIDSet& gids ) +{ + const size_t offset = ( *gids.begin( )); + const size_t count = *gids.rbegin() - offset + 1; + return ::MVD3::Range( offset - 1, count ); +} + +template< typename SrcArray, typename DstArray, typename AssignOp > +void assign( const ::MVD3::Range& range, const GIDSet& gids, + SrcArray& src, DstArray& dst, const AssignOp& assignOp ) +{ + if( isSequence( gids )) // OPT: no holes, no translation needed + { + std::transform( src.begin(), src.end(), dst.begin(), assignOp ); + return; + } + + typename DstArray::iterator dst_it = dst.begin(); + for( GIDSet::const_iterator i = gids.begin(); i != gids.end(); ++i ) + { + typename SrcArray::const_iterator src_it = src.begin(); + std::advance( src_it, *i - range.offset - 1 ); + *dst_it = assignOp( *src_it ); + ++dst_it; + } +} + +Vector3f toVector3f( + const ::MVD3::Positions::const_subarray< 1 >::type& subarray ) +{ + return Vector3f( subarray[0], subarray[1], subarray[2] ); +} + +Quaternionf toQuaternion( + const ::MVD3::Rotations::const_subarray< 1 >::type& subarray ) +{ + return Quaternionf( subarray[0], subarray[1], subarray[2], subarray[3] ); +} +#endif + +std::string toString( const std::string& in ) { return in; } +size_t toSize_t( const std::string& in ) { return std::stoul( in ); } +size_t nop( const size_t& in ) { return in; } + +typedef boost::unordered_map< std::string, neuron::MorphologyPtr > Loaded; +} + +class Circuit::Impl +{ +public: + explicit Impl( const brion::BlueConfig& config ) + : _circuitSource( config.getCircuitSource( )) + , _morphologySource( config.getMorphologySource( )) + , _synapseSource( config.getSynapseSource( )) + , _targetSources( config.getTargetSources( )) + , _cache( lunchbox::PersistentMap::createCache( )) + , _randomEngine( _randomDevice( )) + { + const char* seedEnv = getenv( "BRAIN_CIRCUIT_SEED" ); + if( seedEnv ) + _randomEngine.seed( std::stoul( seedEnv )); + } + + virtual ~Impl() {} + + virtual size_t getNumNeurons() const = 0; + + const brion::URI& getCircuitSource() const + { + return _circuitSource; + } + + GIDSet getGIDs() const + { + brain::GIDSet gids; + brain::GIDSet::const_iterator hint = gids.begin(); + for( uint32_t i = 0; i < getNumNeurons(); ++i ) + hint = gids.insert( hint, i + 1 ); + return gids; + } + + GIDSet getGIDs( const std::string& target ) const + { + if( _targetParsers.empty( )) + { + BOOST_FOREACH( const URI& uri, _targetSources ) + _targetParsers.push_back( brion::Target( uri.getPath( ))); + } + return brion::Target::parse( _targetParsers, target ); + } + + GIDSet getRandomGIDs( const float fraction, + const std::string& target ) const + { + if( fraction < 0.f || fraction > 1.f ) + LBTHROW( std::runtime_error( "Fraction for getRandomGIDs() must be " + "in the range [0,1]" )); + + const GIDSet& gids = target.empty() ? getGIDs() : getGIDs( target ); + uint32_ts randomGids( gids.begin(), gids.end( )); +#ifdef BRION_USE_CXX1 + std::shuffle( randomGids.begin(), randomGids.end(), _randomEngine ); +#else + std::random_shuffle( randomGids.begin(), randomGids.end( )); +#endif + randomGids.resize( size_t( std::ceil( randomGids.size() * fraction ))); + return GIDSet( randomGids.begin(), randomGids.end( )); + } + + virtual Vector3fs getPositions( const GIDSet& gids ) const = 0; + virtual size_ts getMTypes( const GIDSet& gids ) const = 0; + virtual Strings getMorphologyNames() const = 0; + virtual size_ts getETypes( const GIDSet& gids ) const = 0; + virtual Strings getElectrophysiologyNames() const = 0; + virtual Quaternionfs getRotations( const GIDSet& gids ) const = 0; + virtual Strings getMorphologyNames( const GIDSet& gids ) const = 0; + + URI getMorphologyURI( const std::string& name ) const + { + URI uri; + uri.setPath( _morphologySource.getPath() + "/" + name + ".h5" ); + uri.setScheme( "file" ); + return uri; + } + + const brion::SynapseSummary& getSynapseSummary() const + { + if( !_synapseSummary ) + _synapseSummary.reset( new brion::SynapseSummary( + _synapseSource.getPath() + summaryFilename )); + return *_synapseSummary; + } + + const brion::Synapse& getSynapseAttributes( const bool afferent ) const + { + const size_t i = afferent ? 0 : 1; + if( !_synapseAttributes[i] ) + _synapseAttributes[i].reset( + new brion::Synapse( _synapseSource.getPath() + (afferent ? + afferentFilename : efferentFilename ))); + return *_synapseAttributes[i]; + } + + const brion::Synapse* getSynapseExtra() const + { + if( !_synapseExtra ) + { + try + { + _synapseExtra.reset( new brion::Synapse( + _synapseSource.getPath() + extraFilename )); + } + catch( ... ) + { + return nullptr; + } + } + return _synapseExtra.get(); + } + + const brion::Synapse& getSynapsePositions( const bool afferent ) const + { + const size_t i = afferent ? 0 : 1; + if( !_synapsePositions[i] ) + _synapsePositions[i].reset( + new brion::Synapse( _synapseSource.getPath() + (afferent ? + afferentPositionsFilename : efferentPositionsFilename ))); + return *_synapsePositions[i]; + } + + void saveToCache( const std::string& hash, + neuron::MorphologyPtr morphology ) const + { + if( _cache ) + { + servus::Serializable::Data data = morphology->toBinary(); + _cache->insert( hash, data.ptr.get(), data.size ); + } + } + + Loaded loadFromCache( const std::set< std::string >& hashes LB_UNUSED ) + const + { + Loaded loaded; + if( _cache ) + { + LBDEBUG << "Using cache for morphology loading" << std::endl; + typedef std::future< std::pair< std::string, + neuron::MorphologyPtr > > Future; + std::vector< Future > futures; + + Strings keys( hashes.begin(), hashes.end( )); + futures.reserve( keys.size( )); + + _cache->takeValues( keys, [&futures] ( const std::string& key, + char* data, const size_t size ) + { + futures.push_back( std::async( std::launch::async, + [key, data, size] + { + neuron::MorphologyPtr morphology( + new neuron::Morphology( data, size )); + std::free( data ); + return std::make_pair( key, morphology ); + })); + }); + + for( auto& future : futures ) + loaded.insert( future.get( )); + + LBINFO << "Loaded " << loaded.size() << " out of " << hashes.size() + << " morphologies from cache" << std::endl; + } + return loaded; + } + + const brion::URI _circuitSource; + const brion::URI _morphologySource; + const brion::URI _synapseSource; + const brion::URIs _targetSources; + mutable brion::Targets _targetParsers; + mutable lunchbox::PersistentMapPtr _cache; + + mutable std::unique_ptr< brion::SynapseSummary > _synapseSummary; + mutable std::unique_ptr< brion::Synapse > _synapseAttributes[2]; + mutable std::unique_ptr< brion::Synapse > _synapseExtra; + mutable std::unique_ptr< brion::Synapse > _synapsePositions[2]; + + std::random_device _randomDevice; + std::mt19937_64 _randomEngine; +}; + +class MVD2 : public Circuit::Impl +{ +public: + MVD2( const brion::BlueConfig& config ) + : Impl( config ) + , _circuit( config.getCircuitSource().getPath( )) + {} + + size_t getNumNeurons() const final + { + return _circuit.getNumNeurons(); + } + + Vector3fs getPositions( const GIDSet& gids ) const final + { + const brion::NeuronMatrix& data = _circuit.get( gids, + brion::NEURON_POSITION_X | brion::NEURON_POSITION_Y | + brion::NEURON_POSITION_Z ); + + Vector3fs positions( gids.size( )); +#pragma omp parallel for + for( size_t i = 0; i < gids.size(); ++i ) + { + try + { + positions[i] = + brion::Vector3f( lexical_cast< float >( data[i][0] ), + lexical_cast< float >( data[i][1] ), + lexical_cast< float >( data[i][2] )); + } + catch( const boost::bad_lexical_cast& ) + { + GIDSet::const_iterator gid = gids.begin(); + std::advance( gid, i ); + LBWARN << "Error parsing circuit position for gid " + << *gid << std::endl; + } + } + return positions; + } + + size_ts getMTypes( const GIDSet& gids ) const final + { + const brion::NeuronMatrix& matrix = _circuit.get( gids, + brion::NEURON_MTYPE ); + size_ts result( matrix.shape()[ 0 ]); + + brion::NeuronMatrix::const_array_view<1>::type view = + matrix[ boost::indices[brion::NeuronMatrix::index_range( )][ 0 ]]; + std::transform( view.begin(), view.end(), result.begin(), toSize_t ); + return result; + } + + Strings getMorphologyNames() const final + { + return _circuit.getTypes( brion::NEURONCLASS_MTYPE ); + } + + size_ts getETypes( const GIDSet& gids ) const final + { + const brion::NeuronMatrix& matrix = _circuit.get( gids, + brion::NEURON_ETYPE ); + size_ts result( matrix.shape()[ 0 ]); + + brion::NeuronMatrix::const_array_view<1>::type view = + matrix[ boost::indices[brion::NeuronMatrix::index_range( )][ 0 ]]; + std::transform( view.begin(), view.end(), result.begin(), toSize_t ); + return result; + } + + Strings getElectrophysiologyNames() const final + { + return _circuit.getTypes( brion::NEURONCLASS_ETYPE ); + } + + Quaternionfs getRotations( const GIDSet& gids ) const final + { + const float deg2rad = float( M_PI ) / 180.f; + const brion::NeuronMatrix& data = + _circuit.get( gids, brion::NEURON_ROTATION ); + Quaternionfs rotations( gids.size( )); + +#pragma omp parallel for + for( size_t i = 0; i < gids.size(); ++i ) + { + try + { + // transform rotation Y angle in degree into rotation quaternion + const float angle = lexical_cast( data[i][0] ) * deg2rad; + rotations[i] = Quaternionf( angle, Vector3f( 0, 1, 0 )); + } + catch( const boost::bad_lexical_cast& ) + { + GIDSet::const_iterator gid = gids.begin(); + std::advance( gid, i ); + LBWARN << "Error parsing circuit orientation for gid " + << *gid << std::endl; + } + } + return rotations; + } + + Strings getMorphologyNames( const GIDSet& gids ) const final + { + const brion::NeuronMatrix& matrix = + _circuit.get( gids, brion::NEURON_MORPHOLOGY_NAME ); + Strings result( matrix.shape()[ 0 ]); + + brion::NeuronMatrix::const_array_view<1>::type view = + matrix[ boost::indices[brion::NeuronMatrix::index_range( )][ 0 ]]; + std::transform( view.begin(), view.end(), result.begin(), toString ); + return result; + } + +private: + brion::Circuit _circuit; +}; + +#ifdef BRAIN_USE_MVD3 +struct MVD3 : public Circuit::Impl +{ + MVD3( const brion::BlueConfig& config ) + : Impl( config ) + , _circuit( config.getCircuitSource().getPath( )) + {} + + size_t getNumNeurons() const final + { + return _circuit.getNbNeuron(); + } + + Vector3fs getPositions( const GIDSet& gids ) const final + { + Vector3fs results( gids.size( )); + const ::MVD3::Range& range = getRange( gids ); + try + { + brion::detail::SilenceHDF5 silence; + const ::MVD3::Positions& positions = _circuit.getPositions( range ); + assign( range, gids, positions, results, toVector3f ); + return results; + } + catch( const HighFive::Exception& e ) + { + LBTHROW( std::runtime_error( "Exception in getPositions(): " + + std::string( e.what( )))); + } + } + + size_ts getMTypes( const GIDSet& gids ) const final + { + size_ts results( gids.size( )); + const ::MVD3::Range& range = getRange( gids ); + try + { + brion::detail::SilenceHDF5 silence; + const size_ts& mtypes = _circuit.getIndexMtypes( range ); + assign( range, gids, mtypes, results, nop ); + return results; + } + catch( const HighFive::Exception& e ) + { + LBTHROW( std::runtime_error( "Exception in getMTypes(): " + + std::string( e.what( )))); + } + } + + Strings getMorphologyNames() const final + { + return _circuit.listAllMtypes(); + } + + size_ts getETypes( const GIDSet& gids ) const final + { + size_ts results( gids.size( )); + const ::MVD3::Range& range = getRange( gids ); + try + { + brion::detail::SilenceHDF5 silence; + const size_ts& etypes = _circuit.getIndexEtypes( range ); + assign( range, gids, etypes, results, nop ); + return results; + } + catch( const HighFive::Exception& e ) + { + LBTHROW( std::runtime_error( "Exception in getETypes(): " + + std::string( e.what( )))); + } + } + + Strings getElectrophysiologyNames() const final + { + return _circuit.listAllEtypes(); + } + + Quaternionfs getRotations( const GIDSet& gids ) const final + { + Quaternionfs results( gids.size( )); + const ::MVD3::Range& range = getRange( gids ); + try + { + brion::detail::SilenceHDF5 silence; + const ::MVD3::Rotations& rotations = _circuit.getRotations( range ); + assign( range, gids, rotations, results, toQuaternion ); + return results; + } + catch( const HighFive::Exception& e ) + { + LBTHROW( std::runtime_error( "Exception in getRotations(): " + + std::string( e.what( )))); + } + } + + Strings getMorphologyNames( const GIDSet& gids ) const final + { + Strings results( gids.size( )); + const ::MVD3::Range& range = getRange( gids ); + try + { + brion::detail::SilenceHDF5 silence; + const Strings& morphos = _circuit.getMorphologies( range ); + assign( range, gids, morphos, results, toString ); + return results; + } + catch( const HighFive::Exception& e ) + { + LBTHROW( std::runtime_error( "Exception in getMorphologyNames(): " + + std::string( e.what( )))); + } + } + +private: + ::MVD3::MVD3File _circuit; +}; +#endif + +} diff --git a/brain/detail/synapsesStream.h b/brain/detail/synapsesStream.h new file mode 100644 index 0000000..f8eaf5f --- /dev/null +++ b/brain/detail/synapsesStream.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2013-2016, EPFL/Blue Brain Project + * Daniel.Nachbaur@epfl.ch + * + * This file is part of Brion + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef BRAIN_DETAIL_SYNAPSESSTREAM +#define BRAIN_DETAIL_SYNAPSESSTREAM + +#include + +#include + +namespace brain +{ +namespace detail +{ + +struct SynapsesStream +{ + SynapsesStream( const Circuit& circuit, const GIDSet& gids, + const bool afferent, const SynapsePrefetch prefetch ) + : _circuit( circuit ) + , _afferent( afferent ) + , _gids( gids ) + , _prefetch( prefetch ) + , _it( _gids.begin( )) + { + } + + SynapsesStream( const Circuit& circuit, const GIDSet& preGIDs, + const GIDSet& postGIDs, const SynapsePrefetch prefetch ) + : _circuit( circuit ) + , _afferent( preGIDs.empty() || (postGIDs.size() < preGIDs.size( ))) + , _gids( _afferent ? postGIDs : preGIDs ) + , _filterGIDs( _afferent ? preGIDs : postGIDs ) + , _prefetch( prefetch ) + , _it( _gids.begin( )) + { + } + + const Circuit& _circuit; + const bool _afferent; + const GIDSet _gids; + const GIDSet _filterGIDs; + const SynapsePrefetch _prefetch; + GIDSet::const_iterator _it; + + size_t getRemaining() const + { + return size_t( std::abs( std::distance( _it, _gids.end( )))); + } + + std::future< Synapses > read( size_t count ) + { + count = std::min( count, getRemaining( )); + GIDSet::const_iterator start = _it; + std::advance( _it, count ); + GIDSet::const_iterator end = _it; + return std::async( std::launch::async, [&, start, end] + { + return Synapses( _circuit, GIDSet( start, end ), _filterGIDs, + _afferent, _prefetch ); + }); + } +}; + + +} +} + +#endif diff --git a/brain/enums.h b/brain/enums.h new file mode 100644 index 0000000..92f63fc --- /dev/null +++ b/brain/enums.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2013-2016, EPFL/Blue Brain Project + * Daniel.Nachbaur@epfl.ch + * + * This file is part of Brion + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** @file brain/enums.h Enumerations used in Brion. */ + +#ifndef BRAIN_ENUMS +#define BRAIN_ENUMS + +#include + +namespace brain +{ +namespace enums +{ + +using namespace brion::enums; + +/** + * Loading of data during SynapsesStream::read(), otherwise load happens + * on-demand. + */ +enum SynapsePrefetch +{ + SYNAPSEPREFETCH_NONE = 0, //!< only loads pre- and post GIDs + SYNAPSEPREFETCH_ATTRIBUTES = 1 << 0, //!< topological information (section, + //! segment, distance) and model + //! attributes + SYNAPSEPREFETCH_POSITIONS = 1 << 1, //!< pre/post surface/center positions + SYNAPSEPREFETCH_ALL = //!< all synapse data + SYNAPSEPREFETCH_ATTRIBUTES | SYNAPSEPREFETCH_POSITIONS +}; + +} +} +#endif diff --git a/brain/synapse.cpp b/brain/synapse.cpp new file mode 100644 index 0000000..02897a4 --- /dev/null +++ b/brain/synapse.cpp @@ -0,0 +1,148 @@ +/* Copyright (c) 2013-2016, EPFL/Blue Brain Project + * Daniel.Nachbaur@epfl.ch + * + * This file is part of Brion + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +#include "synapse.h" +#include "synapses.h" + +#include + +namespace brain +{ + +Synapse::Synapse( const Synapses& synapses, const size_t index ) + : _synapses( synapses ) + , _index( index ) +{ +} + +Synapse::~Synapse() +{ +} + +SynapseGID Synapse::getGID() const +{ + return std::make_pair( getPostsynapticGID(), + _synapses.index()[_index] ); +} + +uint32_t Synapse::getPresynapticGID() const +{ + return _synapses.preGID()[_index]; +} + +uint32_t Synapse::getPresynapticSectionID() const +{ + return _synapses.preSectionID()[_index]; +} + +uint32_t Synapse::getPresynapticSegmentID() const +{ + return _synapses.preSegmentID()[_index]; +} + +float Synapse::getPresynapticDistance() const +{ + return _synapses.preDistance()[_index]; +} + +Vector3f Synapse::getPresynapticSurfacePosition() const +{ + return Vector3f( _synapses.preSurfacePositionX()[_index], + _synapses.preSurfacePositionY()[_index], + _synapses.preSurfacePositionZ()[_index] ); +} + +Vector3f Synapse::getPresynapticCenterPosition() const +{ + return Vector3f( _synapses.preCenterPositionX()[_index], + _synapses.preCenterPositionY()[_index], + _synapses.preCenterPositionZ()[_index] ); +} + +uint32_t Synapse::getPostsynapticGID() const +{ + return _synapses.postGID()[_index]; +} + +uint32_t Synapse::getPostsynapticSectionID() const +{ + return _synapses.postSectionID()[_index]; +} + +uint32_t Synapse::getPostsynapticSegmentID() const +{ + return _synapses.postSegmentID()[_index]; +} + +float Synapse::getPostsynapticDistance() const +{ + return _synapses.postDistance()[_index]; +} + +Vector3f Synapse::getPostsynapticSurfacePosition() const +{ + return Vector3f( _synapses.postSurfacePositionX()[_index], + _synapses.postSurfacePositionY()[_index], + _synapses.postSurfacePositionZ()[_index] ); +} + +Vector3f Synapse::getPostsynapticCenterPosition() const +{ + return Vector3f( _synapses.postCenterPositionX()[_index], + _synapses.postCenterPositionY()[_index], + _synapses.postCenterPositionZ()[_index] ); +} + +float Synapse::getDelay() const +{ + return _synapses.delay()[_index]; +} + +float Synapse::getConductance() const +{ + return _synapses.conductance()[_index]; +} + +float Synapse::getUtilization() const +{ + return _synapses.utilization()[_index]; +} + +float Synapse::getDepression() const +{ + return _synapses.depression()[_index]; +} + +float Synapse::getFacilitation() const +{ + return _synapses.facilitation()[_index]; +} + +float Synapse::getDecay() const +{ + return _synapses.decay()[_index]; +} + +int Synapse::getEfficacy() const +{ + return _synapses.efficacy()[_index]; +} + +} diff --git a/brain/synapse.h b/brain/synapse.h new file mode 100644 index 0000000..455d21b --- /dev/null +++ b/brain/synapse.h @@ -0,0 +1,125 @@ +/* Copyright (c) 2013-2016, EPFL/Blue Brain Project + * Daniel.Nachbaur@epfl.ch + * + * This file is part of Brion + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef BRAIN_SYNAPSE +#define BRAIN_SYNAPSE + +#include +#include + +namespace brain +{ + +/** + * A proxy object returned by the Synapses container to access data for a + * particular synapse. + * + * The lifetime of this object is stricly bound to the synapses container it + * comes from. + */ +class Synapse +{ +public: + BRAIN_API ~Synapse(); + + /** + * @return the synapse GID containing GID of the post-synaptic cell and the + * index in the afferent contacts array. + * @throw std::runtime_error if index information not found in the synapse + * source of the circuit. + */ + BRAIN_API SynapseGID getGID() const; + + /** @name Presynaptic topological and geometrical attributes */ + //@{ + /** @return the GID of the presynaptic neuron. */ + BRAIN_API uint32_t getPresynapticGID() const; + + /** @return the section ID on the presynaptic neuron. */ + BRAIN_API uint32_t getPresynapticSectionID() const; + + /** @return the segment ID on the presynaptic neuron. */ + BRAIN_API uint32_t getPresynapticSegmentID() const; + + /** @return the distance in micrometer to the presynaptic neuron. */ + BRAIN_API float getPresynapticDistance() const; + + /** @return the presynaptic touch position on the surface of the segment. */ + BRAIN_API Vector3f getPresynapticSurfacePosition() const; + + /** @return the presynaptic touch position in the center of the segment. */ + BRAIN_API Vector3f getPresynapticCenterPosition() const; + //@} + + /** @name Presynaptic topological and geometrical attributes */ + //@{ + /** @return the GID of the postsynaptic neuron. */ + BRAIN_API uint32_t getPostsynapticGID() const; + + /** @return the section ID on the postsynaptic neuron. */ + BRAIN_API uint32_t getPostsynapticSectionID() const; + + /** @return the segment ID on the postsynaptic neuron. */ + BRAIN_API uint32_t getPostsynapticSegmentID() const; + + /** @return the distance in micrometer to the postsynaptic neuron. */ + BRAIN_API float getPostsynapticDistance() const; + + /** @return the postsynaptic touch position on the surface of the segment.*/ + BRAIN_API Vector3f getPostsynapticSurfacePosition() const; + + /** @return the postsynaptic touch position in the center of the segment. */ + BRAIN_API Vector3f getPostsynapticCenterPosition() const; + //@} + + /** @name Synaptic model attributes */ + //@{ + /** @return the axonal delay in milliseconds. */ + BRAIN_API float getDelay() const; + + /** @return the conductance in nanosiemens. */ + BRAIN_API float getConductance() const; + + /** @return the neuro-transmitter release probability. */ + BRAIN_API float getUtilization() const; + + /** @return the depression time constant in milliseconds. */ + BRAIN_API float getDepression() const; + + /** @return the facilitation time constant in milliseconds. */ + BRAIN_API float getFacilitation() const; + + /** @return the decay time constant in milliseconds. */ + BRAIN_API float getDecay() const; + + /** @return the absolute synaptic efficacy in millivolts. */ + BRAIN_API int getEfficacy() const; + //@} + +private: + friend class Synapses; + Synapse( const Synapses& synapses, size_t index ); + + const Synapses& _synapses; + const size_t _index; +}; + +} + +#endif diff --git a/brain/synapses.cpp b/brain/synapses.cpp new file mode 100644 index 0000000..3b0bd87 --- /dev/null +++ b/brain/synapses.cpp @@ -0,0 +1,597 @@ +/* Copyright (c) 2013-2016, EPFL/Blue Brain Project + * Daniel.Nachbaur@epfl.ch + * + * This file is part of Brion + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "synapses.h" + +#include "circuit.h" +#include "synapsesIterator.h" +#include "synapsesStream.h" +#include "synapse.h" + +#include "detail/circuit.h" +#include "detail/synapsesStream.h" + +#include +#include + +#include +#include + +namespace brain +{ + +namespace +{ +template void _allocate( T& data, const size_t size ) +{ + if( data ) + return; + + void* ptr; + if( posix_memalign( &ptr, 32, size * sizeof(typename T::element_type) )) + { + LBWARN << "Memory alignment failed. Trying normal allocation" + << std::endl; + ptr = calloc( size, sizeof(typename T::element_type)); + if( !ptr ) + LBTHROW( std::bad_alloc( )); + } + data.reset((typename T::element_type*) ptr ); + // cppcheck-suppress memleak +} +} + +struct Synapses::Impl +{ + Impl( const Circuit& circuit, const GIDSet& gids, const GIDSet& filterGIDs, + const bool afferent, const SynapsePrefetch prefetch ) + : _circuit( circuit ) + , _gids( prefetch != SYNAPSEPREFETCH_ALL ? gids : GIDSet( )) + , _filterGIDs( prefetch != SYNAPSEPREFETCH_ALL ? filterGIDs : GIDSet( )) + , _afferent( afferent ) + , _size( 0 ) + { + if( prefetch == SYNAPSEPREFETCH_NONE ) + { + _loadConnectivity( gids, filterGIDs ); + return; + } + + if( prefetch & SYNAPSEPREFETCH_ATTRIBUTES ) + _loadAttributes( gids, filterGIDs ); + if( prefetch & SYNAPSEPREFETCH_POSITIONS ) + _loadPositions( gids, filterGIDs ); + } + +#define FILTER( gid )\ + if( !filterGIDs.empty() && filterGIDs.find( gid ) == filterGIDs.end( ))\ + continue; + + void _loadConnectivity( const GIDSet& gids, + const GIDSet& filterGIDs ) const + { + const brion::SynapseSummary& synapseSummary = + _circuit._impl->getSynapseSummary(); + + uint32_ts pres, posts; + for( const auto gid : gids ) + { + const auto& summary = synapseSummary.read( gid ); + + for( size_t i = 0; i < summary.shape()[0]; ++i ) + { + const uint32_t peerGid = summary[i][0]; + FILTER( peerGid ); + + for( size_t j = 0; j < summary[i][_afferent ? 2 : 1]; ++j ) + { + pres.push_back( peerGid ); + posts.push_back( gid ); + } + } + } + + _size = pres.size(); + _allocate( _preGID, _size ); + _allocate( _postGID, _size ); + memcpy( _preGID.get(), pres.data(), _size * sizeof(uint32_t)); + memcpy( _postGID.get(), posts.data(), _size * sizeof(uint32_t)); + + if( !_afferent ) + _preGID.swap( _postGID ); + } + + void _loadAttributes( const GIDSet& gids, const GIDSet& filterGIDs ) const + { + if( _efficacy ) + return; + + const brion::Synapse& synapseAttributes = + _circuit._impl->getSynapseAttributes( _afferent ); + const brion::Synapse* synapseExtra = _circuit._impl->getSynapseExtra(); + + const bool haveExtra = _afferent && synapseExtra; + const bool haveSize = _size > 0; + _allocateAttributes( haveSize ? _size + : synapseAttributes.getNumSynapses( gids ), + haveExtra ); + + size_t i = 0; + for( const auto gid : gids ) + { + const auto& attr = synapseAttributes.read( gid, + brion::SYNAPSE_ALL_ATTRIBUTES ); + const auto extra = haveExtra ? synapseExtra->read( gid, 1 ) + : brion::SynapseMatrix(); + for( size_t j = 0; j < attr.shape()[0]; ++j ) + { + const uint32_t preGid = attr[j][0]; + FILTER( preGid ); + + if( !haveSize ) + { + _preGID.get()[i] = preGid; + _postGID.get()[i] = gid; + } + + if( haveExtra ) + _index.get()[i] = extra[j][0]; + + _delay.get()[i] = attr[j][1]; + _postSectionID.get()[i] = attr[j][2]; + _postSegmentID.get()[i] = attr[j][3]; + _postDistance.get()[i] = attr[j][4]; + _preSectionID.get()[i] = attr[j][5]; + _preSegmentID.get()[i] = attr[j][6]; + _preDistance.get()[i] = attr[j][7]; + _conductance.get()[i] = attr[j][8]; + _utilization.get()[i] = attr[j][9]; + _depression.get()[i] = attr[j][10]; + _facilitation.get()[i] = attr[j][11]; + _decay.get()[i] = attr[j][12]; + _efficacy.get()[i] = attr[j][17]; + ++i; + } + } + + if( !haveSize ) + { + if( !_afferent ) + _preGID.swap( _postGID ); + _size = i; + } + } + + void _loadPositions( const GIDSet& gids, const GIDSet& filterGIDs ) const + { + if( _preSurfacePositionX ) + return; + + const bool haveSize = _size > 0; + const brion::Synapse& synapsePositions = + _circuit._impl->getSynapsePositions( _afferent ); + _allocatePositions( haveSize ? _size + : synapsePositions.getNumSynapses( gids )); + + size_t i = 0; + for( const auto gid : gids ) + { + const auto& pos = synapsePositions.read( gid, + brion::SYNAPSE_POSITION ); + + for( size_t j = 0; j < pos.shape()[0]; ++j ) + { + const uint32_t preGid = pos[j][0]; + FILTER( preGid ); + + if( !haveSize ) + { + _preGID.get()[i] = preGid; + _postGID.get()[i] = gid; + } + + _preSurfacePositionX.get()[i] = pos[j][1]; + _preSurfacePositionY.get()[i] = pos[j][2]; + _preSurfacePositionZ.get()[i] = pos[j][3]; + _postSurfacePositionX.get()[i] = pos[j][4]; + _postSurfacePositionY.get()[i] = pos[j][5]; + _postSurfacePositionZ.get()[i] = pos[j][6]; + _preCenterPositionX.get()[i] = pos[j][7]; + _preCenterPositionY.get()[i] = pos[j][8]; + _preCenterPositionZ.get()[i] = pos[j][9]; + _postCenterPositionX.get()[i] = pos[j][10]; + _postCenterPositionY.get()[i] = pos[j][11]; + _postCenterPositionZ.get()[i] = pos[j][12]; + ++i; + } + } + + if( !haveSize ) + { + if( !_afferent ) + _preGID.swap( _postGID ); + _size = i; + } + } + + void _allocateAttributes( const size_t size, + const bool allocateIndex ) const + { + if( allocateIndex ) + _allocate( _index, size ); + + _allocate( _preGID, size ); + _allocate( _preSectionID, size ); + _allocate( _preSegmentID, size ); + _allocate( _preDistance, size ); + + _allocate( _postGID, size ); + _allocate( _postSectionID, size ); + _allocate( _postSegmentID, size ); + _allocate( _postDistance, size ); + + _allocate( _delay, size ); + _allocate( _conductance, size ); + _allocate( _utilization, size ); + _allocate( _depression, size ); + _allocate( _facilitation, size ); + _allocate( _decay, size ); + _allocate( _efficacy, size ); + } + + void _allocatePositions( const size_t size ) const + { + _allocate( _preGID, size ); + _allocate( _preSurfacePositionX, size ); + _allocate( _preSurfacePositionY, size ); + _allocate( _preSurfacePositionZ, size ); + _allocate( _preCenterPositionX, size ); + _allocate( _preCenterPositionY, size ); + _allocate( _preCenterPositionZ, size ); + + _allocate( _postGID, size ); + _allocate( _postSurfacePositionX, size ); + _allocate( _postSurfacePositionY, size ); + _allocate( _postSurfacePositionZ, size ); + _allocate( _postCenterPositionX, size ); + _allocate( _postCenterPositionY, size ); + _allocate( _postCenterPositionZ, size ); + } + + void _ensureAttributes() const + { + if( _hasAttributes( )) + return; + + lunchbox::ScopedWrite mutex( _lock ); + _loadAttributes( _gids, _filterGIDs ); + } + + void _ensurePositions() const + { + if( _hasPositions( )) + return; + + lunchbox::ScopedWrite mutex( _lock ); + _loadPositions( _gids, _filterGIDs ); + } + + bool _hasAttributes() const + { + lunchbox::ScopedRead mutex( _lock ); + return _efficacy.get() != nullptr; + } + + bool _hasPositions() const + { + lunchbox::ScopedRead mutex( _lock ); + return _preSurfacePositionX.get() != nullptr; + } + + const Circuit& _circuit; + const GIDSet _gids; + const GIDSet _filterGIDs; + const bool _afferent; + + template + struct FreeDeleter + { + void operator()( T* ptr ) { free( ptr ); } + }; + + typedef std::unique_ptr< uint32_t, FreeDeleter > UIntPtr; + typedef std::unique_ptr< int, FreeDeleter > IntPtr; + typedef std::unique_ptr< size_t, FreeDeleter > size_tPtr; + typedef std::unique_ptr< float, FreeDeleter > floatPtr; + + mutable size_t _size; + + mutable size_tPtr _index; + + mutable UIntPtr _preGID; + mutable UIntPtr _preSectionID; + mutable UIntPtr _preSegmentID; + mutable floatPtr _preDistance; + mutable floatPtr _preSurfacePositionX; + mutable floatPtr _preSurfacePositionY; + mutable floatPtr _preSurfacePositionZ; + mutable floatPtr _preCenterPositionX; + mutable floatPtr _preCenterPositionY; + mutable floatPtr _preCenterPositionZ; + + mutable UIntPtr _postGID; + mutable UIntPtr _postSectionID; + mutable UIntPtr _postSegmentID; + mutable floatPtr _postDistance; + mutable floatPtr _postSurfacePositionX; + mutable floatPtr _postSurfacePositionY; + mutable floatPtr _postSurfacePositionZ; + mutable floatPtr _postCenterPositionX; + mutable floatPtr _postCenterPositionY; + mutable floatPtr _postCenterPositionZ; + + mutable floatPtr _delay; + mutable floatPtr _conductance; + mutable floatPtr _utilization; + mutable floatPtr _depression; + mutable floatPtr _facilitation; + mutable floatPtr _decay; + mutable IntPtr _efficacy; + + mutable lunchbox::Lock _lock; +}; + +Synapses::Synapses( const Circuit& circuit, const GIDSet& preGIDs, + const GIDSet& postGIDs, const bool afferent, + const SynapsePrefetch prefetch ) + : _impl( new Impl( circuit, preGIDs, postGIDs, afferent, prefetch )) +{ +} + +Synapses::~Synapses() +{ +} + +Synapses::Synapses( const SynapsesStream& stream ) + : _impl( new Impl( stream._impl->_circuit, stream._impl->_gids, + stream._impl->_filterGIDs, stream._impl->_afferent, + stream._impl->_prefetch )) +{ +} + +Synapses::Synapses( const Synapses& rhs ) + : _impl( rhs._impl ) +{ +} + +Synapses::Synapses( Synapses&& rhs ) + : _impl( std::move( rhs._impl )) +{ +} + +Synapses& Synapses::operator=( const Synapses& rhs ) +{ + if( this != &rhs ) + _impl = rhs._impl; + return *this; +} + +Synapses& Synapses::operator=( Synapses&& rhs ) +{ + if( this != &rhs ) + _impl = std::move( rhs._impl ); + return *this; +} + +size_t Synapses::size() const +{ + lunchbox::ScopedRead mutex( _impl->_lock ); + return _impl->_size; +} + +bool Synapses::empty() const +{ + return size() == 0; +} + +Synapses::const_iterator Synapses::begin() const +{ + return const_iterator( *this, 0 ); +} + +Synapses::const_iterator Synapses::end() const +{ + return const_iterator( *this, size( )); +} + +Synapse Synapses::operator[]( const size_t index_ ) const +{ + return Synapse( *this, index_ ); +} + +const size_t* Synapses::index() const +{ + lunchbox::ScopedRead mutex( _impl->_lock ); + if( !_impl->_index ) + LBTHROW( std::runtime_error( "No synapse index file available" )); + return _impl->_index.get(); +} + +const uint32_t* Synapses::preGID() const +{ + return _impl->_preGID.get(); +} + +const uint32_t* Synapses::preSectionID() const +{ + _impl->_ensureAttributes(); + return _impl->_preSectionID.get(); +} + +const uint32_t* Synapses::preSegmentID() const +{ + _impl->_ensureAttributes(); + return _impl->_preSegmentID.get(); +} + +const float* Synapses::preDistance() const +{ + _impl->_ensureAttributes(); + return _impl->_preDistance.get(); +} + +const float* Synapses::preSurfacePositionX() const +{ + _impl->_ensurePositions(); + return _impl->_preSurfacePositionX.get(); +} + +const float* Synapses::preSurfacePositionY() const +{ + _impl->_ensurePositions(); + return _impl->_preSurfacePositionY.get(); +} + +const float* Synapses::preSurfacePositionZ() const +{ + _impl->_ensurePositions(); + return _impl->_preSurfacePositionZ.get(); +} + +const float* Synapses::preCenterPositionX() const +{ + _impl->_ensurePositions(); + return _impl->_preCenterPositionX.get(); +} + +const float* Synapses::preCenterPositionY() const +{ + _impl->_ensurePositions(); + return _impl->_preCenterPositionY.get(); +} + +const float* Synapses::preCenterPositionZ() const +{ + _impl->_ensurePositions(); + return _impl->_preCenterPositionZ.get(); +} + +const uint32_t* Synapses::postGID() const +{ + return _impl->_postGID.get(); +} + +const uint32_t* Synapses::postSectionID() const +{ + _impl->_ensureAttributes(); + return _impl->_postSectionID.get(); +} + +const uint32_t* Synapses::postSegmentID() const +{ + _impl->_ensureAttributes(); + return _impl->_postSegmentID.get(); +} + +const float* Synapses::postDistance() const +{ + _impl->_ensureAttributes(); + return _impl->_postDistance.get(); +} + +const float* Synapses::postSurfacePositionX() const +{ + _impl->_ensurePositions(); + return _impl->_postSurfacePositionX.get(); +} + +const float* Synapses::postSurfacePositionY() const +{ + _impl->_ensurePositions(); + return _impl->_postSurfacePositionY.get(); +} + +const float* Synapses::postSurfacePositionZ() const +{ + _impl->_ensurePositions(); + return _impl->_postSurfacePositionZ.get(); +} + +const float* Synapses::postCenterPositionX() const +{ + _impl->_ensurePositions(); + return _impl->_postCenterPositionX.get(); +} + +const float* Synapses::postCenterPositionY() const +{ + _impl->_ensurePositions(); + return _impl->_postCenterPositionY.get(); +} + +const float* Synapses::postCenterPositionZ() const +{ + _impl->_ensurePositions(); + return _impl->_postCenterPositionZ.get(); +} + +const float* Synapses::delay() const +{ + _impl->_ensureAttributes(); + return _impl->_delay.get(); +} + +const float* Synapses::conductance() const +{ + _impl->_ensureAttributes(); + return _impl->_conductance.get(); +} + +const float* Synapses::utilization() const +{ + _impl->_ensureAttributes(); + return _impl->_utilization.get(); +} + +const float* Synapses::depression() const +{ + _impl->_ensureAttributes(); + return _impl->_depression.get(); +} + +const float* Synapses::facilitation() const +{ + _impl->_ensureAttributes(); + return _impl->_facilitation.get(); +} + +const float* Synapses::decay() const +{ + _impl->_ensureAttributes(); + return _impl->_decay.get(); +} + +const int* Synapses::efficacy() const +{ + _impl->_ensureAttributes(); + return _impl->_efficacy.get(); +} + +} diff --git a/brain/synapses.h b/brain/synapses.h new file mode 100644 index 0000000..589cd28 --- /dev/null +++ b/brain/synapses.h @@ -0,0 +1,222 @@ +/* Copyright (c) 2013-2016, EPFL/Blue Brain Project + * Daniel.Nachbaur@epfl.ch + * + * This file is part of Brion + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef BRAIN_SYNAPSES +#define BRAIN_SYNAPSES + +#include +#include + +#include + +namespace brain +{ + +/** + * A container providing read-only access to Synapses retrieved by + * getXXXSynapses() functions from brain::Circuit. It provides per-object and + * per-array access on the various synapses attributes. Data which was not + * prefetched will be loaded on-demand. + * + * This class is thread-safe, moveable and copyable. + */ +class Synapses +{ +public: + /** Frees all memory on destruction of last copy. */ + BRAIN_API ~Synapses(); + + /** Conversion constructor for direct access from getXXXSynapses(). */ + BRAIN_API Synapses( const SynapsesStream& stream ); + + /** @name Copy semantics by data sharing. */ + //@{ + BRAIN_API Synapses( const Synapses& ); + BRAIN_API Synapses& operator=( const Synapses& ); + //@} + + /** @name Move semantics. */ + //@{ + BRAIN_API Synapses( Synapses&& rhs ); + BRAIN_API Synapses& operator=( Synapses&& rhs ); + //@} + + /** @return number of synapses available in this container. */ + BRAIN_API size_t size() const; + + /** @return size() == 0. */ + BRAIN_API bool empty() const; + + using const_iterator = SynapsesIterator; + + /** @return an iterator to the first synapse of this container. */ + BRAIN_API const_iterator begin() const; + + /** @return an iterator to the past-the-end synapse of this container. */ + BRAIN_API const_iterator end() const; + + /** return the Synapse at the index-th position. */ + BRAIN_API Synapse operator[]( size_t index ) const; + + /** + * @return the synapse GIDs containing GIDs of the post-synaptic cells and + * the indices in the afferent contacts array. + * @throw std::runtime_error if index information not found in the synapse + * source of the circuit. + */ + BRAIN_API const size_t* index() const; + + /** @name Presynaptic topological and geometrical attributes */ + //@{ + /** @return the GIDs of the presynaptic neurons. */ + BRAIN_API const uint32_t* preGID() const; + + /** @return the section IDs on the presynaptic neurons. */ + BRAIN_API const uint32_t* preSectionID() const; + + /** @return the segment IDs on the presynaptic neurons. */ + BRAIN_API const uint32_t* preSegmentID() const; + + /** @return the distances in micrometer to the presynaptic neurons. */ + BRAIN_API const float* preDistance() const; + + /** + * @return the presynaptic touch position x-coordinates on the surfaces of + * the segments. + */ + BRAIN_API const float* preSurfacePositionX() const; + + /** + * @return the presynaptic touch position y-coordinates on the surfaces of + * the segments. + */ + BRAIN_API const float* preSurfacePositionY() const; + + /** + * @return the presynaptic touch position z-coordinates on the surfaces of + * the segments. + */ + BRAIN_API const float* preSurfacePositionZ() const; + + /** + * @return the presynaptic touch position x-coordinates in the center of the + * segments. + */ + BRAIN_API const float* preCenterPositionX() const; + + /** + * @return the presynaptic touch position y-coordinates in the center of the + * segments. + */ + BRAIN_API const float* preCenterPositionY() const; + + /** + * @return the presynaptic touch position z-coordinates in the center of the + * segments. + */ + BRAIN_API const float* preCenterPositionZ() const; + //@} + + /** @name Presynaptic topological and geometrical attributes */ + //@{ + /** @return the GIDs of the postsynaptic neurons. */ + BRAIN_API const uint32_t* postGID() const; + + /** @return the section IDs on the postsynaptic neurons. */ + BRAIN_API const uint32_t* postSectionID() const; + + /** @return the segment IDs on the postsynaptic neurons. */ + BRAIN_API const uint32_t* postSegmentID() const; + + /** @return the distances in micrometer to the postsynaptic neurons. */ + BRAIN_API const float* postDistance() const; + + /** + * @return the postsynaptic touch position x-coordinates on the surfaces of + * the segments. + */ + BRAIN_API const float* postSurfacePositionX() const; + + /** + * @return the postsynaptic touch position x-coordinates on the surfaces of + * the segments. + */ + BRAIN_API const float* postSurfacePositionY() const; + + /** + * @return the postsynaptic touch position x-coordinates on the surfaces of + * the segments. + */ + BRAIN_API const float* postSurfacePositionZ() const; + + /** + * @return the postsynaptic touch position x-coordinates in the center of + * the segments. + */ + BRAIN_API const float* postCenterPositionX() const; + + /** + * @return the postsynaptic touch position y-coordinates in the center of + * the segments. + */ + BRAIN_API const float* postCenterPositionY() const; + + /** + * @return the postsynaptic touch position z-coordinates in the center of + * the segments. + */ + BRAIN_API const float* postCenterPositionZ() const; + //@} + + /** @name Synaptic model attributes */ + //@{ + /** @return the axonal delays in milliseconds. */ + BRAIN_API const float* delay() const; + + /** @return the conductances in nanosiemens. */ + BRAIN_API const float* conductance() const; + + /** @return the neuro-transmitter release probabilities. */ + BRAIN_API const float* utilization() const; + + /** @return the depression time constants in milliseconds. */ + BRAIN_API const float* depression() const; + + /** @return the facilitation time constants in milliseconds. */ + BRAIN_API const float* facilitation() const; + + /** @return the decay time constants in milliseconds. */ + BRAIN_API const float* decay() const; + + /** @return the absolute synaptic efficacies in millivolts. */ + BRAIN_API const int* efficacy() const; + //@} + +private: + friend struct detail::SynapsesStream; + Synapses( const Circuit& circuit, const GIDSet& gids, + const GIDSet& filterGIDs, bool afferent, SynapsePrefetch prefetch ); + + struct Impl; + std::shared_ptr< const Impl > _impl; +}; + +} + +#endif diff --git a/brain/synapsesIterator.cpp b/brain/synapsesIterator.cpp new file mode 100644 index 0000000..0368367 --- /dev/null +++ b/brain/synapsesIterator.cpp @@ -0,0 +1,58 @@ +/* Copyright (c) 2013-2016, EPFL/Blue Brain Project + * Daniel.Nachbaur@epfl.ch + * + * This file is part of Brion + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "synapsesIterator.h" + +#include "synapse.h" +#include "synapses.h" + +namespace brain +{ + +SynapsesIterator::SynapsesIterator( const Synapses& synapses, + const size_t index ) + : _synapses( synapses ) + , _index( index ) +{} + +SynapsesIterator::~SynapsesIterator() +{} + +bool SynapsesIterator::operator ==( const SynapsesIterator& rhs ) const +{ + return _index == rhs._index; +} + +bool SynapsesIterator::operator !=( const SynapsesIterator& rhs ) const +{ + return _index != rhs._index; +} + +SynapsesIterator& SynapsesIterator::operator ++() +{ + ++_index; + return *this; +} + +Synapse SynapsesIterator::operator*() const +{ + return _synapses[_index]; +} + +} diff --git a/brain/synapsesIterator.h b/brain/synapsesIterator.h new file mode 100644 index 0000000..b5b9678 --- /dev/null +++ b/brain/synapsesIterator.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2013-2016, EPFL/Blue Brain Project + * Daniel.Nachbaur@epfl.ch + * + * This file is part of Brion + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef BRAIN_SYNAPSES_ITERATOR +#define BRAIN_SYNAPSES_ITERATOR + +#include +#include + +namespace brain +{ + +/** A forward iterator for the Synapses container. */ +class SynapsesIterator : + public std::iterator< std::forward_iterator_tag, Synapse > +{ +public: + BRAIN_API SynapsesIterator( const Synapses& synapses, size_t index ); + BRAIN_API ~SynapsesIterator(); + + BRAIN_API bool operator ==( const SynapsesIterator& rhs ) const; + BRAIN_API bool operator !=( const SynapsesIterator& rhs ) const; + + BRAIN_API SynapsesIterator& operator++(); + + BRAIN_API Synapse operator*() const; + +private: + const Synapses& _synapses; + size_t _index; +}; + +} + +#endif diff --git a/brain/synapsesStream.cpp b/brain/synapsesStream.cpp new file mode 100644 index 0000000..f807c3a --- /dev/null +++ b/brain/synapsesStream.cpp @@ -0,0 +1,74 @@ +/* Copyright (c) 2013-2016, EPFL/Blue Brain Project + * Daniel.Nachbaur@epfl.ch + * + * This file is part of Brion + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "synapsesStream.h" + +#include "synapses.h" +#include "detail/synapsesStream.h" + +namespace brain +{ + +SynapsesStream::SynapsesStream( const Circuit& circuit, const GIDSet& gids, + const bool afferent, + const SynapsePrefetch prefetch ) + : _impl( new detail::SynapsesStream( circuit, gids, afferent, prefetch )) +{ +} + +SynapsesStream::SynapsesStream( const Circuit& circuit, const GIDSet& preGIDs, + const GIDSet& postGIDs, + const SynapsePrefetch prefetch ) + : _impl( new detail::SynapsesStream( circuit, preGIDs, postGIDs, prefetch )) +{ +} + +SynapsesStream::~SynapsesStream() +{ +} + +SynapsesStream::SynapsesStream( SynapsesStream&& rhs ) + : _impl( std::move( rhs._impl )) +{ +} + +SynapsesStream& SynapsesStream::operator=( SynapsesStream&& rhs ) +{ + if( this != &rhs ) + _impl = std::move( rhs._impl ); + return *this; +} + +bool SynapsesStream::eos() const +{ + return _impl->_it == _impl->_gids.end(); +} + +size_t SynapsesStream::getRemaining() const +{ + return _impl->getRemaining(); +} + +std::future< Synapses > SynapsesStream::read( const size_t count ) +{ + return _impl->read( count ); +} + +} diff --git a/brain/synapsesStream.h b/brain/synapsesStream.h new file mode 100644 index 0000000..337abdf --- /dev/null +++ b/brain/synapsesStream.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2013-2016, EPFL/Blue Brain Project + * Daniel.Nachbaur@epfl.ch + * + * This file is part of Brion + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library 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 this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef BRAIN_SYNAPSESSTREAM +#define BRAIN_SYNAPSESSTREAM + +#include +#include + +#include +#include + +namespace brain +{ + +/** + * A class which allows sequential and forward-only iterations through the + * synapses from the involved GIDs retrieved by getXXXSynapses() functions from + * brain::Circuit. + * + * This class is moveable, but non-copyable and not thread-safe. + */ +class SynapsesStream +{ +public: + BRAIN_API ~SynapsesStream(); + + /** @name Move semantics. */ + //@{ + BRAIN_API SynapsesStream( SynapsesStream&& rhs ); + BRAIN_API SynapsesStream& operator=( SynapsesStream&& rhs ); + //@} + + /** + * @return if the end of the stream was reached, i.e. any subsequent read() + * will return empty Synapses. + */ + BRAIN_API bool eos() const; + + /** @return the remaining count values for read(). */ + BRAIN_API size_t getRemaining() const; + + /** + * Starts an asynchronous read of the data for the next fraction of synapses + * of the requested GIDs. + * + * @param count the next fraction in the [0,getRemaining()] interval to read + * @return a future to the Synapses containing the requested fraction of + * synapses + */ + BRAIN_API std::future< Synapses > read( size_t count = 1 ); + +private: + SynapsesStream( const SynapsesStream& ) = delete; + SynapsesStream& operator=( const SynapsesStream& ) = delete; + + friend class Circuit; + SynapsesStream( const Circuit& circuit, const GIDSet& gids, + bool afferent, SynapsePrefetch prefetch ); + SynapsesStream( const Circuit& circuit, const GIDSet& preGIDs, + const GIDSet& postGIDs, SynapsePrefetch prefetch ); + + friend class Synapses; + std::unique_ptr< detail::SynapsesStream > _impl; +}; + +} + +#endif diff --git a/brain/types.h b/brain/types.h index 7b17827..dbc2df7 100644 --- a/brain/types.h +++ b/brain/types.h @@ -20,18 +20,23 @@ #ifndef BRAIN_TYPES #define BRAIN_TYPES +#include #include /** @namespace brain Algorithmic interface to Blue Brain data model */ namespace brain { -using namespace brion::enums; +using namespace brain::enums; class Circuit; class Spikes; class SpikeReportReader; class SpikeReportWriter; +class Synapse; +class Synapses; +class SynapsesIterator; +class SynapsesStream; using vmml::Matrix4f; using vmml::Quaternionf; @@ -56,5 +61,18 @@ typedef std::vector< Quaternionf > Quaternionfs; typedef boost::shared_ptr< SpikeReportReader > SpikeReportReaderPtr; typedef boost::shared_ptr< SpikeReportWriter > SpikeReportWriterPtr; + +/** + * The GID of a synapse is the a tuple of two numbers: + * - The GID of the post-synaptic cell. + * - The index of the synapse in the array of afferent contacts + * of the post-synaptic cell before pruning/filtering. + * GIDs are invariant regardless of how the structural touches are + * converted into functional synapses during circuit building. + */ +typedef std::pair< uint32_t, size_t > SynapseGID; + +namespace detail { struct SynapsesStream; } + } #endif diff --git a/brion/enums.h b/brion/enums.h index 58de379..f29e735 100644 --- a/brion/enums.h +++ b/brion/enums.h @@ -17,7 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -/** @file enums.h Enumerations used in Brion. */ +/** @file brion/enums.h Enumerations used in Brion. */ #ifndef BRION_ENUMS #define BRION_ENUMS @@ -26,7 +26,6 @@ namespace brion { -/** @namespace enums Enumeration definitions */ namespace enums { diff --git a/brion/synapse.cpp b/brion/synapse.cpp index a891805..681139f 100644 --- a/brion/synapse.cpp +++ b/brion/synapse.cpp @@ -315,7 +315,9 @@ class Synapse : public boost::noncopyable void _createIndex( const fs::path& dir, const std::string& filename ) { // extract the GID->file mapping from the merge_nrn.sh script - const fs::path merge_nrn = dir / "merge_nrn.sh"; + const bool afferent = filename.find( "efferent" ) == std::string::npos; + const fs::path merge_nrn = dir / (afferent ? "merge_nrn.sh" + : "merge_nrn_efferent.sh"); const std::ifstream mergeFile( merge_nrn.generic_string().c_str( )); if( !mergeFile.is_open( )) { diff --git a/brion/synapseSummary.cpp b/brion/synapseSummary.cpp index facb70f..675287d 100644 --- a/brion/synapseSummary.cpp +++ b/brion/synapseSummary.cpp @@ -162,7 +162,7 @@ SynapseSummary::~SynapseSummary() delete _impl; } -SynapseSummaryMatrix SynapseSummary::read( const uint32_t gid ) +SynapseSummaryMatrix SynapseSummary::read( const uint32_t gid ) const { return _impl->read( gid ); } diff --git a/brion/synapseSummary.h b/brion/synapseSummary.h index 2ca863c..56c6c2e 100644 --- a/brion/synapseSummary.h +++ b/brion/synapseSummary.h @@ -58,7 +58,7 @@ class SynapseSummary : public boost::noncopyable * synapses for each connected neuron * @version 1.0 */ - BRION_API SynapseSummaryMatrix read( const uint32_t gid ); + BRION_API SynapseSummaryMatrix read( const uint32_t gid ) const; //@} private: diff --git a/doc/Changelog.md b/doc/Changelog.md index c8108fe..02080e9 100644 --- a/doc/Changelog.md +++ b/doc/Changelog.md @@ -3,6 +3,10 @@ Changelog {#Changelog} # Release 1.9.0 (git master) +* [85](https://github.com/BlueBrain/Brion/pull/85): + Implement brain synapses specification +* [84](https://github.com/BlueBrain/Brion/pull/84): + Brain synapses specification * [83](https://github.com/BlueBrain/Brion/pull/83): Add brain::Circuit::getRandomGIDs() * [82](https://github.com/BlueBrain/Brion/pull/82): diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8ae17fc..603d2c7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,16 +9,16 @@ if(NOT BBPTESTDATA_FOUND) if(COMMON_ENABLE_COVERAGE) message(FATAL_ERROR "Coverage report generation requires test data") endif() - if(NOT TARGET ${PROJECT_NAME}-tests) - add_custom_target(${PROJECT_NAME}-tests) - set_target_properties(${PROJECT_NAME}-tests PROPERTIES - EXCLUDE_FROM_DEFAULT_BUILD ON FOLDER ${PROJECT_NAME}/tests) - endif() return() endif() configure_file(paths.h.in ${PROJECT_BINARY_DIR}/tests/paths.h) include_directories(${PROJECT_BINARY_DIR}/tests) -set(TEST_LIBRARIES ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} Brain Brion BBPTestData) +set(TEST_LIBRARIES ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} Brion BBPTestData) +if(NOT COMMON_USE_CXX03) + list(APPEND TEST_LIBRARIES Brain) +else() + file(GLOB EXCLUDE_FROM_TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} brain/*.cpp) +endif() include(CommonCTest) diff --git a/tests/circuit.cpp b/tests/brain/circuit.cpp similarity index 100% rename from tests/circuit.cpp rename to tests/brain/circuit.cpp diff --git a/tests/brain/morphology.cpp b/tests/brain/morphology.cpp new file mode 100644 index 0000000..bb0eb11 --- /dev/null +++ b/tests/brain/morphology.cpp @@ -0,0 +1,370 @@ +/* Copyright (c) 2013-2016, EPFL/Blue Brain Project + * Daniel Nachbaur + * Juan Hernando + * + * This file is part of Brion + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Eyescale Software GmbH nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#define BOOST_TEST_MODULE MorphologyBrain +#include +#include + +// typedef for brevity +typedef brion::Vector4f V4f; +typedef brion::Vector3f V3f; + +namespace +{ + +template< typename T > +struct VaArgsType +{ + typedef T type; +}; + +template<> +struct VaArgsType< brion::SectionType > +{ + typedef int type; +}; + +template< typename T > +void checkCloseArrays( const std::vector< T >& array1, + const std::vector< T >& array2 ) +{ + BOOST_CHECK_EQUAL( array1.size(), array2.size() ); + for( size_t i = 0; i != std::min( array1.size(), array2.size( )); ++i ) + BOOST_CHECK_CLOSE( array1[i], array2[i], 2e-5f); +} + +template< typename T > +void checkEqualArrays( const std::vector< T >& array, const size_t length, ... ) +{ + // Create the reference array + std::vector< T > ref; + va_list args; + va_start( args, length ); + for( size_t i = 0; i != length; ++i ) + ref.push_back( (T)va_arg( args, typename VaArgsType< T >::type )); + va_end( args ); + + BOOST_CHECK_EQUAL_COLLECTIONS( array.begin(), array.end(), + ref.begin(), ref.end( )); +} + +template< typename T > +void _checkCloseArrays( const std::vector< T >& array, + const size_t length, va_list args ) +{ + for( size_t i = 0; i != length; ++i ) + { + const T& v = ( T )va_arg( args, typename VaArgsType< T >::type ); + std::ostringstream os; + os << array[i] << " != " << v << " at " << i; + BOOST_CHECK_MESSAGE( array[i].equals( v ), os.str( )); + } +} + +template< typename T > +void checkCloseArrays( const std::vector< T >& array, + const size_t length, ... ) +{ + BOOST_CHECK_EQUAL( array.size(), length ); + va_list args; + va_start( args, length ); + _checkCloseArrays( array, length, args ); + va_end( args ); +} + +template< typename T, long unsigned int M > +void checkCloseArrays( const std::vector< vmml::vector< M, T > >& array1, + const std::vector< vmml::vector< M, T > >& array2 ) +{ + BOOST_CHECK_EQUAL( array1.size(), array2.size() ); + for( size_t i = 0; i != std::min( array1.size(), array2.size( )); ++i ) + BOOST_CHECK_SMALL(( array1[i] - array2[i] ).length( ), 0.00001f); +} + +template< typename T > +void checkCloseArraysUptoN( const std::vector< T >& array, + const size_t length, ... ) +{ + BOOST_CHECK( array.size() >= length ); + va_list args; + va_start( args, length ); + _checkCloseArrays( array, length, args ); + va_end( args ); +} + +brion::uint32_ts getSectionIDs( const brain::neuron::Sections& sections ) +{ + brion::uint32_ts result; + for( const brain::neuron::Section& section : sections ) + result.push_back( section.getID( )); + return result; +} + +const std::string TEST_MORPHOLOGY_FILENAME = + std::string( BRION_TESTDATA ) + "/h5/test_neuron.h5"; +const brion::URI TEST_MORPHOLOGY_URI = + brion::URI( "file://" + TEST_MORPHOLOGY_FILENAME ); + +void checkEqualMorphologies( const brain::neuron::Morphology& first, + const brion::Morphology& second ) +{ + BOOST_CHECK( *second.readPoints( brion::MORPHOLOGY_UNDEFINED ) == + first.getPoints( )); + BOOST_CHECK( *second.readSections( brion::MORPHOLOGY_UNDEFINED ) == + first.getSections( )); + BOOST_CHECK( *second.readSectionTypes() == first.getSectionTypes( )); + BOOST_CHECK( *second.readApicals() == first.getApicals( )); +} +} // namespace + +BOOST_AUTO_TEST_CASE( v2_morphology_constructors ) +{ + boost::shared_ptr< brion::Morphology > raw( + new brion::Morphology( TEST_MORPHOLOGY_FILENAME )); + + brain::neuron::Morphology morphology( TEST_MORPHOLOGY_URI ); + BOOST_CHECK_EQUAL( morphology.getTransformation(), + brain::Matrix4f( )); + checkEqualMorphologies( morphology, *raw ); + checkEqualMorphologies( brain::neuron::Morphology( *raw ), *raw ); + + BOOST_CHECK_THROW( brain::neuron::Morphology( brion::URI( "/mars" )), + std::runtime_error); +} + +BOOST_AUTO_TEST_CASE( get_section_ids ) +{ + brain::neuron::Morphology morphology( TEST_MORPHOLOGY_URI ); + + brion::SectionTypes types; + types.push_back( brion::SECTION_SOMA ); + checkEqualArrays( morphology.getSectionIDs( types ), 1, 0 ); + + types.push_back( brion::SECTION_DENDRITE ); + checkEqualArrays( morphology.getSectionIDs( types ), + 7, 0, 4, 5, 6, 7, 8, 9 ); + types.push_back( brion::SECTION_APICAL_DENDRITE ); + checkEqualArrays( morphology.getSectionIDs( types ), + 10, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12 ); + types.clear(); + types.push_back( brion::SECTION_AXON ); + types.push_back( brion::SECTION_DENDRITE ); + checkEqualArrays( morphology.getSectionIDs( types ), + 9, 1, 2, 3, 4, 5, 6, 7, 8, 9 ); +} + +BOOST_AUTO_TEST_CASE( get_sections ) +{ + brain::neuron::Morphology morphology( TEST_MORPHOLOGY_URI ); + + BOOST_CHECK_THROW( morphology.getSection( 0 ), std::runtime_error ); + + for( size_t i = 1; i < 13; ++i ) + BOOST_CHECK_EQUAL( morphology.getSection( i ).getID(), i ); + + brain::neuron::Section section = morphology.getSection( 1 ); + BOOST_CHECK( section == morphology.getSection( 1 )); + section = morphology.getSection( 2 ); + BOOST_CHECK( section != morphology.getSection( 1 )); + BOOST_CHECK( section == morphology.getSection( 2 )); + + for( size_t i = 1; i < 4; ++i ) + BOOST_CHECK_EQUAL( morphology.getSection( i ).getType(), + brion::SECTION_AXON ); + for( size_t i = 4; i < 10; ++i ) + BOOST_CHECK_EQUAL( morphology.getSection( i ).getType(), + brion::SECTION_DENDRITE ); + for( size_t i = 10; i < 13; ++i ) + BOOST_CHECK_EQUAL( morphology.getSection( i ).getType(), + brion::SECTION_APICAL_DENDRITE ); +} + +BOOST_AUTO_TEST_CASE( get_section_samples ) +{ + brain::neuron::Morphology morphology( TEST_MORPHOLOGY_URI ); + + brion::Vector4fs points; + for( size_t i = 0; i != 11; ++i) + { + float i2 = i * i; + points.push_back( + brion::Vector4f(0, -i2 / 20.0, i2 / 20.0, 0.5 + i2 /1000.0)); + } + checkCloseArrays( morphology.getSection( 1 ).getSamples(), points ); + + points.clear(); + for( size_t i = 0; i != 11; ++i) + { + float i2 = i * i; + points.push_back( + brion::Vector4f(i2 / 20.0, 0, i2 / 20.0, 0.5 + i2 /1000.0)); + } + checkCloseArrays( morphology.getSection( 4 ).getSamples(), points ); + + points.clear(); + for( size_t i = 0; i != 11; ++i) + { + float i2 = i * i; + points.push_back( + brion::Vector4f(-i2 / 20.0, 0, i2 / 20.0, 0.5 + i2 /1000.0)); + } + checkCloseArrays( morphology.getSection( 7 ).getSamples(), points ); + + points.clear(); + for( size_t i = 0; i != 11; ++i) + { + float i2 = i * i; + points.push_back( + brion::Vector4f(0, i2 / 20.0, i2 / 20.0, 0.5 + i2 /1000.0)); + } + checkCloseArrays( morphology.getSection( 10 ).getSamples(), points ); +} + +BOOST_AUTO_TEST_CASE( get_section_distances_to_soma ) +{ + brain::neuron::Morphology morphology( TEST_MORPHOLOGY_URI ); + + uint32_t sections[] = {1, 4, 7, 10}; + + for( size_t i = 0; i != 4; ++i) + { + uint32_t section = sections[i]; + BOOST_CHECK_EQUAL( + morphology.getSection( section ).getDistanceToSoma(), 0 ); + const float length = std::sqrt( 5 * 5 * 2 ); + BOOST_CHECK_CLOSE( + morphology.getSection( section ).getLength(), length, 1e-5 ); + + // The distance to the soma of the next section is equal to the length + // of its parent + BOOST_CHECK_CLOSE( + morphology.getSection( section + 1 ).getDistanceToSoma(), + length, 1e-5 ); + + brion::floats reference; + for( size_t j = 0; j != 11; ++j) + { + const float p = j*j / 20.0; + reference.push_back( std::sqrt( p * p * 2 )); + } + checkCloseArrays( + morphology.getSection( section ).getSampleDistancesToSoma( ), + reference ); + } +} + +BOOST_AUTO_TEST_CASE( get_soma_geometry ) +{ + brain::neuron::Morphology morphology( TEST_MORPHOLOGY_URI ); + + const brain::neuron::Soma soma = morphology.getSoma(); + checkEqualArrays( soma.getProfilePoints(), 4, + V4f( .1, 0, 0, .1 ), V4f( 0, .1, 0, .1 ), + V4f( -.1, 0, 0, .1 ), V4f( 0, -.1, 0, .1 )); + + BOOST_CHECK_CLOSE( soma.getMeanRadius(), 0.1, 1e-5 ); + BOOST_CHECK_EQUAL( soma.getCentroid(), V3f::ZERO ); + + brain::Matrix4f matrix; + matrix.setTranslation( V3f( 2, 0, 0 )); + brain::neuron::Morphology transformed( TEST_MORPHOLOGY_URI, matrix ); + BOOST_CHECK_MESSAGE( transformed.getSoma().getCentroid().equals(V3f( 2,0,0 )), + transformed.getSoma().getCentroid( )); + +} + +BOOST_AUTO_TEST_CASE( get_section_samples_by_positions ) +{ + brain::neuron::Morphology morphology( TEST_MORPHOLOGY_URI ); + + brion::floats points; + for( float p = 0.0; p <= 1.0; p += 0.2 ) + points.push_back( p ); + + checkCloseArrays( morphology.getSection( 1 ).getSamples( points ), 6, + V4f( 0, 0, 0, .5 ), V4f( 0, -1, 1, .52 ), V4f( 0, -2, 2, .54 ), + V4f( 0, -3, 3, .56 ), V4f( 0, -4, 4, .58 ), V4f( 0, -5, 5, .6 )); + + checkCloseArrays( morphology.getSection( 4 ).getSamples( points ), 6, + V4f( 0, 0, 0, .5 ), V4f( 1, 0, 1, .52 ), V4f( 2, 0, 2, .54 ), + V4f( 3, 0, 3, .56 ), V4f( 4, 0, 4, .58 ), V4f( 5, 0, 5, .6 )); + + checkCloseArrays( morphology.getSection( 7 ).getSamples( points ), 6, + V4f( 0, 0, 0, .5 ), V4f( -1, 0, 1, .52 ), V4f( -2, 0, 2, .54 ), + V4f( -3, 0, 3, .56 ), V4f( -4, 0, 4, .58 ), V4f( -5, 0, 5, .6 )); + + checkCloseArrays( morphology.getSection( 10 ).getSamples( points ), 6, + V4f( 0, 0, 0, .5 ), V4f( 0, 1, 1, .52 ), V4f( 0, 2, 2, .54 ), + V4f( 0, 3, 3, .56 ), V4f( 0, 4, 4, .58 ), V4f( 0, 5, 5, .6 )); +} + +BOOST_AUTO_TEST_CASE( morphology_hierarchy ) +{ + brain::neuron::Morphology morphology( TEST_MORPHOLOGY_URI ); + + BOOST_CHECK( !morphology.getSection( 1 ).hasParent( )); + BOOST_CHECK( !morphology.getSection( 4 ).hasParent( )); + BOOST_CHECK_EQUAL( morphology.getSection( 2 ).getParent().getID(), 1 ); + BOOST_CHECK_EQUAL( morphology.getSection( 3 ).getParent().getID(), 1 ); + BOOST_CHECK_EQUAL( morphology.getSection( 5 ).getParent().getID(), 4 ); + BOOST_CHECK_EQUAL( morphology.getSection( 6 ).getParent().getID(), 4 ); + + checkEqualArrays( getSectionIDs( morphology.getSoma().getChildren( )), + 4, 1, 4, 7, 10 ); + checkEqualArrays( getSectionIDs( morphology.getSection( 1 ).getChildren( )), + 2, 2, 3 ); + checkEqualArrays( getSectionIDs( morphology.getSection( 4 ).getChildren( )), + 2, 5, 6 ); + BOOST_CHECK( morphology.getSection( 5 ).getChildren().empty( )); +} + +BOOST_AUTO_TEST_CASE( transform_with_matrix ) +{ + brain::Matrix4f matrix; + matrix.rotate_z( M_PI * 0.5 ); + brain::neuron::Morphology rotated( TEST_MORPHOLOGY_URI, matrix ); + checkCloseArraysUptoN( rotated.getPoints(), 4, + V4f( .0, .1, .0, .1 ), V4f( -.1, .0, .0, .1 ), + V4f( .0, -.1, .0, .1 ), V4f( .1, .0, .0, .1 )); + + matrix = brain::Matrix4f(); + matrix.rotate_z( M_PI * 0.5 ); + matrix.setTranslation( V3f( 2, 0, 0 )); + brain::neuron::Morphology transformed( TEST_MORPHOLOGY_URI, matrix ); + BOOST_CHECK_EQUAL( transformed.getTransformation(), matrix ); + checkCloseArraysUptoN( transformed.getPoints(), 4, + V4f( 2., .1, .0, .1 ), V4f( 1.9, .0, .0, .1 ), + V4f( 2., -.1, .0, .1 ), V4f( 2.1, .0, .0, .1 )); +} diff --git a/tests/spikeReportReaderWriter.cpp b/tests/brain/spikeReportReaderWriter.cpp similarity index 100% rename from tests/spikeReportReaderWriter.cpp rename to tests/brain/spikeReportReaderWriter.cpp diff --git a/tests/brain/synapses.cpp b/tests/brain/synapses.cpp new file mode 100644 index 0000000..e2bb274 --- /dev/null +++ b/tests/brain/synapses.cpp @@ -0,0 +1,265 @@ +/* Copyright (c) 2013-2016, EPFL/Blue Brain Project + * Daniel Nachbaur + * + * This file is part of Brion + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Eyescale Software GmbH nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#define BOOST_TEST_MODULE Synapses +#include + +BOOST_AUTO_TEST_CASE( projection ) +{ + const brain::Circuit circuit( brion::URI( BBP_TEST_BLUECONFIG3 )); + const brain::Synapses& syn1 = + circuit.getProjectedSynapses( circuit.getGIDs( "Layer1" ), + circuit.getGIDs( "Layer2" ), + brain::SYNAPSEPREFETCH_NONE ); + const brain::Synapses& syn2 = + circuit.getProjectedSynapses( circuit.getGIDs( "Layer2" ), + circuit.getGIDs( "Layer1" ), + brain::SYNAPSEPREFETCH_NONE ); + BOOST_CHECK_NE( syn1.size(), syn2.size( )); + BOOST_CHECK_EQUAL( syn1.size(), 895 ); + BOOST_CHECK_EQUAL( syn2.size(), 353 ); + BOOST_CHECK_EQUAL( syn1[100].getPresynapticGID(), 3 ); + BOOST_CHECK_EQUAL( syn1[100].getPostsynapticGID(), 141 ); + BOOST_CHECK_EQUAL( syn2[100].getPresynapticGID(), 115 ); + BOOST_CHECK_EQUAL( syn2[100].getPostsynapticGID(), 7 ); +} + +BOOST_AUTO_TEST_CASE( projection_stream ) +{ + const brain::Circuit circuit( brion::URI( BBP_TEST_BLUECONFIG3 )); + brain::SynapsesStream stream = + circuit.getProjectedSynapses( circuit.getGIDs( "Layer2" ), + circuit.getGIDs( "Layer5" ), + brain::SYNAPSEPREFETCH_POSITIONS ); + const size_t remaining = 130; + BOOST_CHECK_EQUAL( stream.getRemaining(), remaining ); + std::future< brain::Synapses > future = stream.read(); + size_t i = 1; + size_t totalSize = 0; + vmml::AABBf bbox; + while( !stream.eos( )) + { + const brain::Synapses synapses = future.get(); + future = stream.read(); // fetch next + + ++i; + BOOST_CHECK_EQUAL( stream.getRemaining(), remaining - i ); + + const float* __restrict__ posx = synapses.preSurfacePositionX(); + const float* __restrict__ posy = synapses.preSurfacePositionY(); + const float* __restrict__ posz = synapses.preSurfacePositionZ(); + + for( size_t j = 0; j < synapses.size(); ++j ) + bbox.merge( vmml::Vector3f( posx[j], posy[j], posz[j] )); + totalSize += synapses.size(); + } + + BOOST_CHECK_CLOSE( bbox.getCenter()[0], 19.4931183f, 0.00001f ); + BOOST_CHECK_CLOSE( bbox.getCenter()[1], 1384.17578f, 0.00001f ); + BOOST_CHECK_CLOSE( bbox.getCenter()[2], 18.0030212f, 0.00001f ); + BOOST_CHECK_EQUAL( totalSize, 9520 ); +} + +BOOST_AUTO_TEST_CASE( afferent_synapses ) +{ + const brain::Circuit circuit( brion::URI( BBP_TEST_BLUECONFIG3 )); + const brain::Synapses& synapses = + circuit.getAfferentSynapses( circuit.getGIDs( "Layer1" )); + BOOST_CHECK( !synapses.empty( )); + BOOST_CHECK_EQUAL( synapses.size(), 1172 ); + BOOST_CHECK_EQUAL( synapses[0].getPresynapticGID(), 10 ); + BOOST_CHECK_CLOSE( synapses[1].getPostsynapticDistance(), 1.34995711f, + 0.00001f ); + BOOST_CHECK_CLOSE( synapses[2].getConductance(), 0.34758395f, 0.00001f ); + BOOST_CHECK_THROW( synapses[3].getGID(), std::runtime_error ); +} + +BOOST_AUTO_TEST_CASE( efferent_synapses ) +{ + const brain::Circuit circuit( brion::URI( BBP_TEST_BLUECONFIG3 )); + const brain::Synapses& synapses = + circuit.getEfferentSynapses( brion::GIDSet{10}); + BOOST_CHECK( !synapses.empty( )); + BOOST_CHECK_EQUAL( synapses.size(), 74 ); + BOOST_CHECK_EQUAL( synapses[0].getPostsynapticGID(), 1 ); + BOOST_CHECK_CLOSE( synapses[1].getPostsynapticDistance(), 1.34995711f, + 0.00001f ); + BOOST_CHECK_CLOSE( synapses[2].getConductance(), 0.34758395f, 0.00001f ); + BOOST_CHECK_THROW( synapses[3].getGID(), std::runtime_error ); +} + +BOOST_AUTO_TEST_CASE( retrograde_projection ) +{ + const brain::Circuit circuit( brion::URI( BBP_TEST_BLUECONFIG3 )); + const brion::GIDSet& preNeurons = circuit.getGIDs( "Layer1" ); + const brion::GIDSet postNeuron = { 1 }; + const brain::Synapses& synapses = + circuit.getProjectedSynapses( preNeurons, postNeuron ); + BOOST_CHECK( !synapses.empty( )); + BOOST_CHECK_EQUAL( synapses.size(), 5 ); + for( const auto& synapse : synapses ) + BOOST_CHECK_EQUAL( synapse.getPresynapticGID(), 10 ); +} + +BOOST_AUTO_TEST_CASE( lazy_loading_afferent ) +{ + const brain::Circuit circuit( brion::URI( BBP_TEST_BLUECONFIG3 )); + const brain::Synapses& synapses = + circuit.getAfferentSynapses( circuit.getGIDs( "Layer1" )); + const brain::Synapses& synapsesLazy = + circuit.getAfferentSynapses( circuit.getGIDs( "Layer1" ), + brain::SYNAPSEPREFETCH_NONE ); + BOOST_CHECK_EQUAL( synapses.size(), synapsesLazy.size() ); + BOOST_CHECK_EQUAL( synapses[0].getPresynapticGID(), + synapsesLazy[0].getPresynapticGID( )); + BOOST_CHECK_EQUAL( synapses[0].getPostsynapticDistance(), + synapsesLazy[0].getPostsynapticDistance( )); + BOOST_CHECK_EQUAL( synapses[0].getConductance(), + synapsesLazy[0].getConductance( )); + BOOST_CHECK_EQUAL( synapses[0].getPostsynapticCenterPosition(), + synapsesLazy[0].getPostsynapticCenterPosition( )); +} + +BOOST_AUTO_TEST_CASE( lazy_loading_efferent ) +{ + const brain::Circuit circuit( brion::URI( BBP_TEST_BLUECONFIG3 )); + const brain::Synapses& synapses = + circuit.getEfferentSynapses( circuit.getGIDs( "Layer1" )); + const brain::Synapses& synapsesLazy = + circuit.getEfferentSynapses( circuit.getGIDs( "Layer1" ), + brain::SYNAPSEPREFETCH_NONE ); + BOOST_CHECK_EQUAL( synapses.size(), synapsesLazy.size() ); + BOOST_CHECK_EQUAL( synapses[0].getPresynapticGID(), + synapsesLazy[0].getPresynapticGID( )); + BOOST_CHECK_EQUAL( synapses[0].getPostsynapticDistance(), + synapsesLazy[0].getPostsynapticDistance( )); + BOOST_CHECK_EQUAL( synapses[0].getConductance(), + synapsesLazy[0].getConductance( )); + BOOST_CHECK_EQUAL( synapses[0].getPostsynapticCenterPosition(), + synapsesLazy[0].getPostsynapticCenterPosition( )); +} + +BOOST_AUTO_TEST_CASE( lazy_loading_pathway ) +{ + const brain::Circuit circuit( brion::URI( BBP_TEST_BLUECONFIG3 )); + const brain::Synapses& synapses = + circuit.getProjectedSynapses( circuit.getGIDs( "Layer2" ), + circuit.getGIDs( "Layer4" )); + const brain::Synapses& synapsesLazy = + circuit.getProjectedSynapses( circuit.getGIDs( "Layer2" ), + circuit.getGIDs( "Layer4" ), + brain::SYNAPSEPREFETCH_NONE ); + BOOST_CHECK_EQUAL( synapses.size(), synapsesLazy.size() ); + BOOST_CHECK_EQUAL( synapses[0].getPresynapticGID(), + synapsesLazy[0].getPresynapticGID( )); + BOOST_CHECK_EQUAL( synapses[0].getPostsynapticDistance(), + synapsesLazy[0].getPostsynapticDistance( )); + BOOST_CHECK_EQUAL( synapses[0].getConductance(), + synapsesLazy[0].getConductance( )); + BOOST_CHECK_EQUAL( synapses[0].getPostsynapticCenterPosition(), + synapsesLazy[0].getPostsynapticCenterPosition( )); +} + +BOOST_AUTO_TEST_CASE( copy ) +{ + const brain::Circuit circuit( brion::URI( BBP_TEST_BLUECONFIG3 )); + const brain::Synapses& synapses = + circuit.getAfferentSynapses( circuit.getGIDs( "Layer1" ), + brain::SYNAPSEPREFETCH_NONE ); + + const brain::Synapses synapsesCopy = synapses; + + BOOST_CHECK_EQUAL( synapses.size(), synapsesCopy.size( )); + BOOST_CHECK_EQUAL( synapses[0].getPresynapticGID(), + synapsesCopy[0].getPresynapticGID( )); + + const brain::Synapse synapse = synapses[1]; + const brain::Synapse synapseCopy = synapse; + BOOST_CHECK_EQUAL( synapse.getPresynapticGID(), + synapseCopy.getPresynapticGID( )); + + BOOST_CHECK_EQUAL( synapse.getDepression(), + synapseCopy.getDepression( )); +} + +BOOST_AUTO_TEST_CASE( full_copy ) +{ + const brain::Circuit circuit( brion::URI( BBP_TEST_BLUECONFIG3 )); + const brain::Synapses& synapses = + circuit.getAfferentSynapses( circuit.getGIDs( "Layer1" ), + brain::SYNAPSEPREFETCH_ALL ); + + const brain::Synapses synapsesCopy = synapses; + + BOOST_CHECK_EQUAL( synapses.size(), synapsesCopy.size( )); + BOOST_CHECK_EQUAL( synapses[0].getPostsynapticSurfacePosition(), + synapsesCopy[0].getPostsynapticSurfacePosition( )); + BOOST_CHECK_EQUAL( synapses[10].getEfficacy(), + synapsesCopy[10].getEfficacy( )); +} + +BOOST_AUTO_TEST_CASE( check_all_synapse_attributes ) +{ + const brain::Circuit circuit( brion::URI( BBP_TEST_BLUECONFIG3 )); + const brain::Synapses& synapses = + circuit.getAfferentSynapses( brion::GIDSet{1}, + brain::SYNAPSEPREFETCH_ALL ); + BOOST_CHECK_EQUAL( synapses.size(), 77 ); + + const brain::Synapse& synapse = synapses[0]; + BOOST_CHECK_EQUAL( synapse.getConductance(), 0.572888553f ); + BOOST_CHECK_EQUAL( synapse.getDecay(), 10.208410263f ); + BOOST_CHECK_EQUAL( synapse.getDelay(), 0.583546519f ); + BOOST_CHECK_EQUAL( synapse.getDepression(), 1057 ); + BOOST_CHECK_EQUAL( synapse.getEfficacy(), 0 ); + BOOST_CHECK_EQUAL( synapse.getFacilitation(), 29 ); + BOOST_CHECK_THROW( synapse.getGID(), std::runtime_error ); + BOOST_CHECK_EQUAL( synapse.getPostsynapticCenterPosition(), + brion::Vector3f( 3.799289703f, 1947.041748047f, 9.237932205f )); + BOOST_CHECK_EQUAL( synapse.getPostsynapticDistance(), 0.924134851f ); + BOOST_CHECK_EQUAL( synapse.getPostsynapticGID(), 1 ); + BOOST_CHECK_EQUAL( synapse.getPostsynapticSectionID(), 70 ); + BOOST_CHECK_EQUAL( synapse.getPostsynapticSegmentID(), 13 ); + BOOST_CHECK_EQUAL( synapse.getPostsynapticSurfacePosition(), + brion::Vector3f( 3.603360415f, 1947.145141602f, 9.205502510f )); + BOOST_CHECK_EQUAL( synapse.getPresynapticCenterPosition(), + brion::Vector3f( 3.611587524f, 1947.084228516f, 9.198493958f )); + BOOST_CHECK_EQUAL( synapse.getPresynapticDistance(), 2.911511898f ); + BOOST_CHECK_EQUAL( synapse.getPresynapticGID(), 10 ); + BOOST_CHECK_EQUAL( synapse.getPresynapticSectionID(), 2 ); + BOOST_CHECK_EQUAL( synapse.getPresynapticSegmentID(), 15 ); + BOOST_CHECK_EQUAL( synapse.getPresynapticSurfacePosition(), + brion::Vector3f( 3.792815685f, 1947.050537109f, 9.214178085f )); + BOOST_CHECK_EQUAL( synapse.getUtilization(), 0.362769693f ); +} diff --git a/tests/morphology.cpp b/tests/morphology.cpp index 2f35259..c2eefd4 100644 --- a/tests/morphology.cpp +++ b/tests/morphology.cpp @@ -29,9 +29,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "paths.h" +#include #include -#include #define BOOST_TEST_MODULE Morphology #include @@ -40,11 +39,9 @@ #include #include -#include -// typdef for brevity +// typedef for brevity typedef brion::Vector4f V4f; -typedef brion::Vector3f V3f; typedef brion::Vector2i V2i; namespace @@ -466,14 +463,6 @@ void checkCloseArraysUptoN( const std::vector< T >& array, va_end( args ); } -brion::uint32_ts getSectionIDs( const brain::neuron::Sections& sections ) -{ - brion::uint32_ts result; - BOOST_FOREACH( const brain::neuron::Section& section, sections ) - result.push_back( section.getID( )); - return result; -} - BOOST_AUTO_TEST_CASE( swc_soma ) { boost::filesystem::path path( BRION_TESTDATA ); @@ -656,237 +645,3 @@ BOOST_AUTO_TEST_CASE( swc_neuron ) BOOST_CHECK_EQUAL( neuron.getCellFamily(), brion::FAMILY_NEURON ); BOOST_CHECK( neuron.readPerimeters()->empty( )); } - -namespace -{ -void checkEqualMorphologies( const brain::neuron::Morphology& first, - const brion::Morphology& second ) -{ - BOOST_CHECK( *second.readPoints( brion::MORPHOLOGY_UNDEFINED ) == - first.getPoints( )); - BOOST_CHECK( *second.readSections( brion::MORPHOLOGY_UNDEFINED ) == - first.getSections( )); - BOOST_CHECK( *second.readSectionTypes() == first.getSectionTypes( )); - BOOST_CHECK( *second.readApicals() == first.getApicals( )); -} -} - -BOOST_AUTO_TEST_CASE( v2_morphology_constructors ) -{ - boost::shared_ptr< brion::Morphology > raw( - new brion::Morphology( TEST_MORPHOLOGY_FILENAME )); - - brain::neuron::Morphology morphology( TEST_MORPHOLOGY_URI ); - BOOST_CHECK_EQUAL( morphology.getTransformation(), - brain::Matrix4f( )); - checkEqualMorphologies( morphology, *raw ); - checkEqualMorphologies( brain::neuron::Morphology( *raw ), *raw ); - - BOOST_CHECK_THROW( brain::neuron::Morphology( brion::URI( "/mars" )), - std::runtime_error); -} - -BOOST_AUTO_TEST_CASE( get_section_ids ) -{ - brain::neuron::Morphology morphology( TEST_MORPHOLOGY_URI ); - - brion::SectionTypes types; - types.push_back( brion::SECTION_SOMA ); - checkEqualArrays( morphology.getSectionIDs( types ), 1, 0 ); - - types.push_back( brion::SECTION_DENDRITE ); - checkEqualArrays( morphology.getSectionIDs( types ), - 7, 0, 4, 5, 6, 7, 8, 9 ); - types.push_back( brion::SECTION_APICAL_DENDRITE ); - checkEqualArrays( morphology.getSectionIDs( types ), - 10, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12 ); - types.clear(); - types.push_back( brion::SECTION_AXON ); - types.push_back( brion::SECTION_DENDRITE ); - checkEqualArrays( morphology.getSectionIDs( types ), - 9, 1, 2, 3, 4, 5, 6, 7, 8, 9 ); -} - -BOOST_AUTO_TEST_CASE( get_sections ) -{ - brain::neuron::Morphology morphology( TEST_MORPHOLOGY_URI ); - - BOOST_CHECK_THROW( morphology.getSection( 0 ), std::runtime_error ); - - for( size_t i = 1; i < 13; ++i ) - BOOST_CHECK_EQUAL( morphology.getSection( i ).getID(), i ); - - brain::neuron::Section section = morphology.getSection( 1 ); - BOOST_CHECK( section == morphology.getSection( 1 )); - section = morphology.getSection( 2 ); - BOOST_CHECK( section != morphology.getSection( 1 )); - BOOST_CHECK( section == morphology.getSection( 2 )); - - for( size_t i = 1; i < 4; ++i ) - BOOST_CHECK_EQUAL( morphology.getSection( i ).getType(), - brion::SECTION_AXON ); - for( size_t i = 4; i < 10; ++i ) - BOOST_CHECK_EQUAL( morphology.getSection( i ).getType(), - brion::SECTION_DENDRITE ); - for( size_t i = 10; i < 13; ++i ) - BOOST_CHECK_EQUAL( morphology.getSection( i ).getType(), - brion::SECTION_APICAL_DENDRITE ); -} - -BOOST_AUTO_TEST_CASE( get_section_samples ) -{ - brain::neuron::Morphology morphology( TEST_MORPHOLOGY_URI ); - - brion::Vector4fs points; - for( size_t i = 0; i != 11; ++i) - { - float i2 = i * i; - points.push_back( - brion::Vector4f(0, -i2 / 20.0, i2 / 20.0, 0.5 + i2 /1000.0)); - } - checkCloseArrays( morphology.getSection( 1 ).getSamples(), points ); - - points.clear(); - for( size_t i = 0; i != 11; ++i) - { - float i2 = i * i; - points.push_back( - brion::Vector4f(i2 / 20.0, 0, i2 / 20.0, 0.5 + i2 /1000.0)); - } - checkCloseArrays( morphology.getSection( 4 ).getSamples(), points ); - - points.clear(); - for( size_t i = 0; i != 11; ++i) - { - float i2 = i * i; - points.push_back( - brion::Vector4f(-i2 / 20.0, 0, i2 / 20.0, 0.5 + i2 /1000.0)); - } - checkCloseArrays( morphology.getSection( 7 ).getSamples(), points ); - - points.clear(); - for( size_t i = 0; i != 11; ++i) - { - float i2 = i * i; - points.push_back( - brion::Vector4f(0, i2 / 20.0, i2 / 20.0, 0.5 + i2 /1000.0)); - } - checkCloseArrays( morphology.getSection( 10 ).getSamples(), points ); -} - -BOOST_AUTO_TEST_CASE( get_section_distances_to_soma ) -{ - brain::neuron::Morphology morphology( TEST_MORPHOLOGY_URI ); - - uint32_t sections[] = {1, 4, 7, 10}; - - for( size_t i = 0; i != 4; ++i) - { - uint32_t section = sections[i]; - BOOST_CHECK_EQUAL( - morphology.getSection( section ).getDistanceToSoma(), 0 ); - const float length = std::sqrt( 5 * 5 * 2 ); - BOOST_CHECK_CLOSE( - morphology.getSection( section ).getLength(), length, 1e-5 ); - - // The distance to the soma of the next section is equal to the length - // of its parent - BOOST_CHECK_CLOSE( - morphology.getSection( section + 1 ).getDistanceToSoma(), - length, 1e-5 ); - - brion::floats reference; - for( size_t j = 0; j != 11; ++j) - { - const float p = j*j / 20.0; - reference.push_back( std::sqrt( p * p * 2 )); - } - checkCloseArrays( - morphology.getSection( section ).getSampleDistancesToSoma( ), - reference ); - } -} - -BOOST_AUTO_TEST_CASE( get_soma_geomery ) -{ - brain::neuron::Morphology morphology( TEST_MORPHOLOGY_URI ); - - const brain::neuron::Soma soma = morphology.getSoma(); - checkEqualArrays( soma.getProfilePoints(), 4, - V4f( .1, 0, 0, .1 ), V4f( 0, .1, 0, .1 ), - V4f( -.1, 0, 0, .1 ), V4f( 0, -.1, 0, .1 )); - - BOOST_CHECK_CLOSE( soma.getMeanRadius(), 0.1, 1e-5 ); - BOOST_CHECK_EQUAL( soma.getCentroid(), V3f::ZERO ); - - brain::Matrix4f matrix; - matrix.setTranslation( V3f( 2, 0, 0 )); - brain::neuron::Morphology transformed( TEST_MORPHOLOGY_URI, matrix ); - BOOST_CHECK_MESSAGE( transformed.getSoma().getCentroid().equals(V3f( 2,0,0 )), - transformed.getSoma().getCentroid( )); - -} - -BOOST_AUTO_TEST_CASE( get_section_samples_by_positions ) -{ - brain::neuron::Morphology morphology( TEST_MORPHOLOGY_URI ); - - brion::floats points; - for( float p = 0.0; p <= 1.0; p += 0.2 ) - points.push_back( p ); - - checkCloseArrays( morphology.getSection( 1 ).getSamples( points ), 6, - V4f( 0, 0, 0, .5 ), V4f( 0, -1, 1, .52 ), V4f( 0, -2, 2, .54 ), - V4f( 0, -3, 3, .56 ), V4f( 0, -4, 4, .58 ), V4f( 0, -5, 5, .6 )); - - checkCloseArrays( morphology.getSection( 4 ).getSamples( points ), 6, - V4f( 0, 0, 0, .5 ), V4f( 1, 0, 1, .52 ), V4f( 2, 0, 2, .54 ), - V4f( 3, 0, 3, .56 ), V4f( 4, 0, 4, .58 ), V4f( 5, 0, 5, .6 )); - - checkCloseArrays( morphology.getSection( 7 ).getSamples( points ), 6, - V4f( 0, 0, 0, .5 ), V4f( -1, 0, 1, .52 ), V4f( -2, 0, 2, .54 ), - V4f( -3, 0, 3, .56 ), V4f( -4, 0, 4, .58 ), V4f( -5, 0, 5, .6 )); - - checkCloseArrays( morphology.getSection( 10 ).getSamples( points ), 6, - V4f( 0, 0, 0, .5 ), V4f( 0, 1, 1, .52 ), V4f( 0, 2, 2, .54 ), - V4f( 0, 3, 3, .56 ), V4f( 0, 4, 4, .58 ), V4f( 0, 5, 5, .6 )); -} - -BOOST_AUTO_TEST_CASE( morphology_hierarchy ) -{ - brain::neuron::Morphology morphology( TEST_MORPHOLOGY_URI ); - - BOOST_CHECK( !morphology.getSection( 1 ).hasParent( )); - BOOST_CHECK( !morphology.getSection( 4 ).hasParent( )); - BOOST_CHECK_EQUAL( morphology.getSection( 2 ).getParent().getID(), 1 ); - BOOST_CHECK_EQUAL( morphology.getSection( 3 ).getParent().getID(), 1 ); - BOOST_CHECK_EQUAL( morphology.getSection( 5 ).getParent().getID(), 4 ); - BOOST_CHECK_EQUAL( morphology.getSection( 6 ).getParent().getID(), 4 ); - - checkEqualArrays( getSectionIDs( morphology.getSoma().getChildren( )), - 4, 1, 4, 7, 10 ); - checkEqualArrays( getSectionIDs( morphology.getSection( 1 ).getChildren( )), - 2, 2, 3 ); - checkEqualArrays( getSectionIDs( morphology.getSection( 4 ).getChildren( )), - 2, 5, 6 ); - BOOST_CHECK( morphology.getSection( 5 ).getChildren().empty( )); -} - -BOOST_AUTO_TEST_CASE( transform_with_matrix ) -{ - brain::Matrix4f matrix; - matrix.rotate_z( M_PI * 0.5 ); - brain::neuron::Morphology rotated( TEST_MORPHOLOGY_URI, matrix ); - checkCloseArraysUptoN( rotated.getPoints(), 4, - V4f( .0, .1, .0, .1 ), V4f( -.1, .0, .0, .1 ), - V4f( .0, -.1, .0, .1 ), V4f( .1, .0, .0, .1 )); - - matrix = brain::Matrix4f(); - matrix.rotate_z( M_PI * 0.5 ); - matrix.setTranslation( V3f( 2, 0, 0 )); - brain::neuron::Morphology transformed( TEST_MORPHOLOGY_URI, matrix ); - BOOST_CHECK_EQUAL( transformed.getTransformation(), matrix ); - checkCloseArraysUptoN( transformed.getPoints(), 4, - V4f( 2., .1, .0, .1 ), V4f( 1.9, .0, .0, .1 ), - V4f( 2., -.1, .0, .1 ), V4f( 2.1, .0, .0, .1 )); -}