From 1192e5f098625c373cfda22611bc23307e96f69f Mon Sep 17 00:00:00 2001 From: Stefan Eilemann Date: Mon, 18 Apr 2016 17:44:53 +0200 Subject: [PATCH] Move old SDK/Monsteer spike report here pending refactoring --- CMakeLists.txt | 4 +- brain/CMakeLists.txt | 13 +- brain/detail/spikes.h | 73 +++++++++ brain/python/CMakeLists.txt | 34 ++++ brain/python/brain.cpp | 36 +++++ brain/python/spikeReportReader.cpp | 57 +++++++ brain/python/spikeReportReader.h | 26 ++++ brain/python/spikeReportWriter.cpp | 50 ++++++ brain/python/spikeReportWriter.h | 26 ++++ brain/python/spikes.cpp | 49 ++++++ brain/python/spikes.h | 25 +++ brain/spikeReportReader.cpp | 148 ++++++++++++++++++ brain/spikeReportReader.h | 129 ++++++++++++++++ brain/spikeReportWriter.cpp | 67 ++++++++ brain/spikeReportWriter.h | 88 +++++++++++ brain/spikes.cpp | 134 ++++++++++++++++ brain/spikes.h | 167 ++++++++++++++++++++ brain/types.h | 6 + tests/spikeReportReaderWriter.cpp | 307 +++++++++++++++++++++++++++++++++++++ 19 files changed, 1436 insertions(+), 3 deletions(-) create mode 100644 brain/detail/spikes.h create mode 100644 brain/python/CMakeLists.txt create mode 100644 brain/python/brain.cpp create mode 100644 brain/python/spikeReportReader.cpp create mode 100644 brain/python/spikeReportReader.h create mode 100644 brain/python/spikeReportWriter.cpp create mode 100644 brain/python/spikeReportWriter.h create mode 100644 brain/python/spikes.cpp create mode 100644 brain/python/spikes.h create mode 100644 brain/spikeReportReader.cpp create mode 100644 brain/spikeReportReader.h create mode 100644 brain/spikeReportWriter.cpp create mode 100644 brain/spikeReportWriter.h create mode 100644 brain/spikes.cpp create mode 100644 brain/spikes.h create mode 100644 tests/spikeReportReaderWriter.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d6ea86f..d1d34c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2015, EPFL/Blue Brain Project +# Copyright (c) 2013-2016, EPFL/Blue Brain Project # Daniel Nachbaur # # This file is part of Brion @@ -31,6 +31,8 @@ common_find_package(HDF5 REQUIRED COMPONENTS C CXX) common_find_package(Lunchbox REQUIRED) common_find_package(OpenMP) common_find_package(Servus REQUIRED) +common_find_package(PythonInterp) +common_find_package(PythonLibs) common_find_package(vmmlib REQUIRED) common_find_package_post() diff --git a/brain/CMakeLists.txt b/brain/CMakeLists.txt index b07f1d4..af047db 100644 --- a/brain/CMakeLists.txt +++ b/brain/CMakeLists.txt @@ -6,14 +6,18 @@ set(BRAIN_PUBLIC_HEADERS circuit.h - types.h neuron/morphology.h neuron/section.h neuron/soma.h neuron/types.h + spikeReportReader.h + spikeReportWriter.h + spikes.h + types.h ) set(BRAIN_HEADERS + detail/spikes.h neuron/morphologyImpl.h ) @@ -23,7 +27,10 @@ set(BRAIN_SOURCES neuron/morphologyImpl.cpp neuron/section.cpp neuron/soma.cpp - ) + spikeReportReader.cpp + spikeReportWriter.cpp + spikes.cpp +) set(BRAIN_PUBLIC_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIRS}) set(BRAIN_LINK_LIBRARIES PUBLIC Brion vmmlib) @@ -37,3 +44,5 @@ common_library(Brain) if(TARGET MVDTool) add_definitions(-DBRAIN_USE_MVD3) endif() + +add_subdirectory(python) diff --git a/brain/detail/spikes.h b/brain/detail/spikes.h new file mode 100644 index 0000000..6bdb659 --- /dev/null +++ b/brain/detail/spikes.h @@ -0,0 +1,73 @@ + +/* Copyright (c) 2006-2015, Juan Hernando + * + * 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_SPIKES_H +#define BRAIN_DETAIL_SPIKES_H + +namespace brain +{ +namespace detail +{ + +class Spikes +{ +public: + /** Create a default Spikes private implementation. */ + Spikes() + : _startTime(0) + , _endTime(0) + , _size(0) + {} + + /** Create a Spikes private implementation. */ + Spikes( const brion::Spikes::const_iterator& begin, + const brion::Spikes::const_iterator& end, + const float startTime, + const float endTime, + const size_t size ) + : _begin( begin ) + , _end( end ) + , _startTime( startTime ) + , _endTime( endTime ) + , _size( size ) + {} + + brion::Spikes::const_iterator _begin; + brion::Spikes::const_iterator _end; + float _startTime; + float _endTime; + size_t _size; +}; + +class Spikes_const_iterator +{ +public: + /** Create a default / invalid iterator */ + Spikes_const_iterator() {} + + /** Constructor */ + explicit Spikes_const_iterator( const brion::Spikes::const_iterator& it ) + : _it( it ) + {} + + brion::Spikes::const_iterator _it; +}; + +} +} +#endif diff --git a/brain/python/CMakeLists.txt b/brain/python/CMakeLists.txt new file mode 100644 index 0000000..6b0a078 --- /dev/null +++ b/brain/python/CMakeLists.txt @@ -0,0 +1,34 @@ +# +# Copyright (c) 2011-2015, ahmet.bilgili@epfl.ch +# +# This file is part of Brion +# + +if( NOT Boost_PYTHON${USE_BOOST_PYTHON_VERSION}_LIBRARY OR + NOT PYTHONLIBS_FOUND OR NOT PYTHON_EXECUTABLE ) + + message(STATUS "No Boost.Python or Python found. Disabling python bindings") + return() +endif() + +set(BRAIN_PYTHON_SOURCE_FILES + brain.cpp + spikeReportWriter.cpp + spikeReportReader.cpp + spikes.cpp) + +include_directories(${PYTHON_INCLUDE_DIRS}) +add_library(brain_python MODULE ${BRAIN_PYTHON_SOURCE_FILES}) +add_dependencies(brain_python Brain) + +target_link_libraries(brain_python + Brain ${PYTHON_LIBRARIES} ${Boost_PYTHON${USE_BOOST_PYTHON_VERSION}_LIBRARY}) + +set_target_properties(brain_python PROPERTIES + OUTPUT_NAME "_brain" PREFIX "" + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/brain) + +set_property(GLOBAL APPEND PROPERTY BRAIN_ALL_DEP_TARGETS brain_python) + +install(TARGETS brain_python + LIBRARY DESTINATION ${PYTHON_LIBRARY_SUFFIX}/brain) diff --git a/brain/python/brain.cpp b/brain/python/brain.cpp new file mode 100644 index 0000000..a211789 --- /dev/null +++ b/brain/python/brain.cpp @@ -0,0 +1,36 @@ + +/* Copyright (c) 2006-2016, Ahmet Bilgili + * Juan Hernando + * + * 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. + */ + +#ifdef _MSC_VER +#pragma warning(disable:4127) +#endif + +#include + +#include "spikeReportReader.h" +#include "spikeReportWriter.h" +#include "spikes.h" + +BOOST_PYTHON_MODULE(_brain) +{ + export_SpikeReportReader(); + export_SpikeReportWriter(); + export_Spikes(); +} diff --git a/brain/python/spikeReportReader.cpp b/brain/python/spikeReportReader.cpp new file mode 100644 index 0000000..9dbfd2b --- /dev/null +++ b/brain/python/spikeReportReader.cpp @@ -0,0 +1,57 @@ + +/* Copyright (c) 2006-2016, Ahmet Bilgili + * Juan Hernando + * + * 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 + +using namespace brain; +using namespace boost::python; + +namespace +{ + +SpikeReportReaderPtr initURI( const std::string& uri ) +{ + return SpikeReportReaderPtr( new SpikeReportReader( brion::URI( uri ))); +} + +} + +void export_SpikeReportReader() +{ + +class_< SpikeReportReader, boost::noncopyable >( + "SpikeReportReader", no_init ) + .def( "__init__", make_constructor( initURI )) + .def( "close", &SpikeReportReader::close ) + .def( "getStartTime", &SpikeReportReader::getStartTime ) + .def( "getEndTime", &SpikeReportReader::getEndTime ) + .def( "getSpikes", ( Spikes (SpikeReportReader::* )( )) + &SpikeReportReader::getSpikes ) + .def( "getSpikes", + ( Spikes (SpikeReportReader::* )( float, float )) + &SpikeReportReader::getSpikes ) + .def( "hasEnded", &SpikeReportReader::hasEnded ) + .def( "isStream", &SpikeReportReader::isStream ); + +} diff --git a/brain/python/spikeReportReader.h b/brain/python/spikeReportReader.h new file mode 100644 index 0000000..4ed6efb --- /dev/null +++ b/brain/python/spikeReportReader.h @@ -0,0 +1,26 @@ + +/* Copyright (c) 2006-2015, Ahmet Bilgili + * Juan Hernando + * + * 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_BINDING_SPIKEREPORTREADER_H +#define BRAIN_BINDING_SPIKEREPORTREADER_H + +void export_SpikeReportReader(); + +#endif diff --git a/brain/python/spikeReportWriter.cpp b/brain/python/spikeReportWriter.cpp new file mode 100644 index 0000000..6b3e576 --- /dev/null +++ b/brain/python/spikeReportWriter.cpp @@ -0,0 +1,50 @@ + +/* Copyright (c) 2006-2016, Ahmet Bilgili + * Juan Hernando + * + * 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 + +using namespace boost::python; +using namespace brain; + +namespace +{ + +SpikeReportWriterPtr initURI( const std::string& uri ) +{ + return SpikeReportWriterPtr( new SpikeReportWriter( brion::URI( uri ))); +} + +} + +void export_SpikeReportWriter() +{ + +class_< SpikeReportWriter, boost::noncopyable >( + "SpikeReportWriter", no_init ) + .def( "__init__", make_constructor( initURI )) + .def( "close", &SpikeReportWriter::close ) + .def( "writeSpikes", &SpikeReportWriter::writeSpikes ) +; + +} diff --git a/brain/python/spikeReportWriter.h b/brain/python/spikeReportWriter.h new file mode 100644 index 0000000..09dd44f --- /dev/null +++ b/brain/python/spikeReportWriter.h @@ -0,0 +1,26 @@ + +/* Copyright (c) 2006-2016, Ahmet Bilgili + * Juan Hernando + * + * 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_BINDING_SPIKEREPORTWRITER_H +#define BRAIN_BINDING_SPIKEREPORTWRITER_H + +void export_SpikeReportWriter(); + +#endif diff --git a/brain/python/spikes.cpp b/brain/python/spikes.cpp new file mode 100644 index 0000000..683ccdf --- /dev/null +++ b/brain/python/spikes.cpp @@ -0,0 +1,49 @@ + +/* Copyright (c) 2006-2016, Juan Hernando + * + * 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 + +using namespace boost::python; +using namespace brain; + +namespace +{ +struct Conversion +{ + static PyObject* convert(const std::pair< float, unsigned int >& pair) + { + tuple t = make_tuple(pair.first, pair.second); + Py_INCREF(t.ptr()); + return t.ptr(); + } +}; +} + +void export_Spikes() +{ + +to_python_converter< brion::Spike, Conversion >(); + +class_< Spikes >( "Spikes", no_init ) + .def( "__iter__", range( &Spikes::begin, &Spikes::end )) + .def( "__len__", &Spikes::size ) +; + +} diff --git a/brain/python/spikes.h b/brain/python/spikes.h new file mode 100644 index 0000000..5e4c7ab --- /dev/null +++ b/brain/python/spikes.h @@ -0,0 +1,25 @@ + +/* Copyright (c) 2006-2016, Juan Hernando + * + * 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_BINDING_SPIKES_H +#define BRAIN_BINDING_SPIKES_H + +void export_Spikes(); + +#endif diff --git a/brain/spikeReportReader.cpp b/brain/spikeReportReader.cpp new file mode 100644 index 0000000..06dd8c0 --- /dev/null +++ b/brain/spikeReportReader.cpp @@ -0,0 +1,148 @@ + +/* Copyright (c) 2006-2015, Raphael Dumusc + * + * 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 "spikeReportReader.h" +#include "spikes.h" +#include "detail/spikes.h" + +#include + +#include // for nextafterf and INFINITY + +namespace brain +{ + +/** + * @internal + * + * Private subclass of Spikes. + * + * Spikes has to be subclassed here because of the protected constructor. + * @see getSpikes() + */ +class BrionSpikes : public Spikes +{ +public: + BrionSpikes( brion::Spikes::const_iterator beginIt, + brion::Spikes::const_iterator endIt, + const float startTime, + const float endTime, + size_t count ) + : Spikes( new detail::Spikes( beginIt, endIt, + startTime, endTime, count )) + {} +}; + +class SpikeReportReader::_Impl +{ +public: + explicit _Impl( const brion::URI& uri ) + : _report( uri, brion::MODE_READ ) + {} + + brion::SpikeReport _report; +}; + +SpikeReportReader::SpikeReportReader( const brion::URI& uri ) + : _impl( new _Impl( uri )) +{ +} + +SpikeReportReader::~SpikeReportReader() +{ + delete _impl; +} + +Spikes SpikeReportReader::getSpikes() +{ + brion::SpikeReport& report = _impl->_report; + if( report.getReadMode() == brion::SpikeReport::STREAM ) + { + if( report.getNextSpikeTime() == brion::UNDEFINED_TIMESTAMP ) + { + // If the stream has reached the end (or an error ocurred) all + // the spikes are fetched. + report.waitUntil( brion::UNDEFINED_TIMESTAMP ); + } + else + { + // Otherwise fetch all the spikes with time < last spike received. + // Spikes with time == last time are left in the cache on + // purpose. This ensures that the snapshot for [start, last time) + // is complete. + const float time = report.getLatestSpikeTime(); + // Ensuring that we don't block if no spikes have been received. + if( time != brion::UNDEFINED_TIMESTAMP ) + report.waitUntil( nextafterf( time, -INFINITY )); + } + } + + return BrionSpikes( report.getSpikes().begin(), report.getSpikes().end(), + getStartTime(), getEndTime(), + report.getSpikes().size( )); +} + +Spikes SpikeReportReader::getSpikes( const float startTime, const float endTime ) +{ + const brion::Spikes& spikes = _impl->_report.getSpikes(); + + if( endTime <= startTime ) + return BrionSpikes( spikes.end(), spikes.end(), startTime, endTime, 0 ); + + // Receive spikes if needed + if( _impl->_report.getReadMode() == brion::SpikeReport::STREAM ) + { + // The interval is open on the right + _impl->_report.waitUntil( nextafterf( endTime, -INFINITY )); + } + + const brion::Spikes::const_iterator start = spikes.lower_bound(startTime); + const brion::Spikes::const_iterator end = spikes.lower_bound(endTime); + const size_t size = std::distance( start, end ); + + return BrionSpikes( start, end, startTime, endTime, size ); +} + +bool SpikeReportReader::hasEnded() const +{ + return _impl->_report.getReadMode() == brion::SpikeReport::STATIC || + _impl->_report.getNextSpikeTime() == brion::UNDEFINED_TIMESTAMP; +} + +bool SpikeReportReader::isStream() const +{ + return _impl->_report.getReadMode() == brion::SpikeReport::STREAM; +} + +float SpikeReportReader::getStartTime() const +{ + return _impl->_report.getStartTime(); +} + +float SpikeReportReader::getEndTime() const +{ + return _impl->_report.getEndTime(); +} + +void SpikeReportReader::close() +{ + _impl->_report.close(); +} + +} diff --git a/brain/spikeReportReader.h b/brain/spikeReportReader.h new file mode 100644 index 0000000..93069e5 --- /dev/null +++ b/brain/spikeReportReader.h @@ -0,0 +1,129 @@ + +/* Copyright (c) 2006-2015, Raphael Dumusc + * Juan Hernando + * + * 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_SPIKEREPORTREADER_H +#define BRAIN_SPIKEREPORTREADER_H + +#include "spikes.h" + +#include + +namespace brain +{ +/** + * Reader for Spike data. + * + * Following RAII, all readers are ready for use after the creation and will + * ensure release of resources upon destruction. + * + * This class is not thread-safe except where noted. + */ +class SpikeReportReader : public boost::noncopyable +{ +public: + /** + * Construct a new reader opening a spike data source. + * @param uri URI to spike report (can contain a wildcard to specify several + * files). + * @version 0.2 + * @throw std::runtime_error if source is invalid. + */ + explicit SpikeReportReader( const brion::URI& uri ); + + /** + * Destructor. + * @version 0.2 + */ + ~SpikeReportReader(); + + /** + * Get all available spikes from this reader. + * + * For stream reports this method returns a coherent snapshot of all the + * data that has been received so far without blocking (some spikes may + * be left in the receive buffer to ensure coherency). The actual time + * window can be verified at the Spikes objects. + * + * @version 0.2 + */ + Spikes getSpikes(); + + /** + * Get all spikes inside a time window. + * + * For stream reports this method will wait until the first spike + * with a time larger or equal to end arrives. The time interval is + * open on the right, so assuming that spikes arrive in order, this + * method will return a full snapshot of the spikes between [start, end). + * + * @version 0.2 + */ + Spikes getSpikes( const float start, const float end ); + + /** + * @return true if any of the versions of getSpikes() reaches the end + * of the stream, if the report is static or if closed has been + called. + * @version 0.2 + */ + bool hasEnded() const; + + /** + * @return true iff spikes are coming from a stream source. + * @version 0.2 + */ + bool isStream() const; + + /** + * @return The start time of the report window in milliseconds or + * UNDEFINED_TIMESTAMP if the report is stream-based and no spike + * has been received yet. + * @version 0.2 + */ + float getStartTime() const; + + /** + * @return The end time of the report window in milliseconds or + * UNDEFINED_TIMESTAMP if the report is stream-based and no spike + * has been received yet. + * @version 0.2 + */ + float getEndTime() const; + + /** + * Close the data source. + * + * Any thread blocked in getSpikes will return immediately, possibly + * returning an empty container. + * This method may be called concurrently to both getSpikes() functions + * and hasEnded(). + * + * @version 0.2 + */ + void close(); + +private: + class _Impl; + _Impl* _impl; +}; + +} +#endif diff --git a/brain/spikeReportWriter.cpp b/brain/spikeReportWriter.cpp new file mode 100644 index 0000000..af4521d --- /dev/null +++ b/brain/spikeReportWriter.cpp @@ -0,0 +1,67 @@ + +/* Copyright (c) 2006-2015, Juan Hernando + * Ahmet Bilgili + * + * 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 "spikeReportWriter.h" +#include "spikes.h" + +#include + +namespace brain +{ + +class SpikeReportWriter::_Impl +{ +public: + _Impl( const brion::URI& uri, const int accessMode ) + : _report( uri, accessMode ) + {} + + brion::SpikeReport _report; +}; + +SpikeReportWriter::SpikeReportWriter( const brion::URI& uri, + const int accessMode ) + : _impl( new _Impl( uri, accessMode )) +{ +} + +SpikeReportWriter::~SpikeReportWriter() +{ + delete _impl; +} + +void SpikeReportWriter::writeSpikes( const Spikes &spikes ) +{ + brion::Spikes brionSpikes; + brionSpikes.insert( spikes.begin(), spikes.end() ); + _impl->_report.writeSpikes( brionSpikes ); +} + +const lunchbox::URI& SpikeReportWriter::getURI() const +{ + return _impl->_report.getURI(); +} + +void SpikeReportWriter::close() +{ + _impl->_report.close(); +} + +} diff --git a/brain/spikeReportWriter.h b/brain/spikeReportWriter.h new file mode 100644 index 0000000..187a850 --- /dev/null +++ b/brain/spikeReportWriter.h @@ -0,0 +1,88 @@ + +/* Copyright (c) 2006-2015, Juan Hernando + * Ahmet Bilgili + * + * 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_SPIKEREPORTWRITER_H +#define BRAIN_SPIKEREPORTWRITER_H + +#include +#include +#include + +namespace brain +{ + +/** + * Writer for spike data. + * + * Following RAII, a writer is ready for use after the creation and will + * ensure release of resources upon destruction. + * + * @version 0.2 + */ +class SpikeReportWriter : public boost::noncopyable +{ +public: + /** + * Construct a new writer for the given URI. + * @param uri URI to spike report + * @param accessMode Access mode + * @version 0.2 + */ + SpikeReportWriter( const brion::URI& uri, + const int accessMode = brion::MODE_WRITE ); + + /** + * Destructor. + * @version 0.2 + */ + ~SpikeReportWriter(); + + /** + * Writes the spike times and cell GIDs. + * + * @param spikes Spikes to write. + * @version 0.2 + */ + void writeSpikes( const Spikes& spikes ); + + /** + * Get the URI where the writer is publishing. It could be same as the one + * used as input for the construction, or a different one (more + * complete) once the publisher is bound to it. + * + * @return the fully-qualified URI that the writer uses to publish spikes + * @version 0.3 + */ + const lunchbox::URI& getURI() const; + + /** + * Closes the report. ( It is implicitly called on destruction ). + * + * @version 0.2 + */ + void close(); + +private: + class _Impl; + _Impl* _impl; +}; + +} +#endif diff --git a/brain/spikes.cpp b/brain/spikes.cpp new file mode 100644 index 0000000..0c0ebf0 --- /dev/null +++ b/brain/spikes.cpp @@ -0,0 +1,134 @@ + +/* Copyright (c) 2006-2015, Raphael Dumusc + * Juan Hernando + * + * 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 "spikes.h" +#include "detail/spikes.h" + +namespace brain +{ + +Spikes::Spikes() + : _impl( new detail::Spikes( )) +{} + +Spikes::Spikes( detail::Spikes* impl ) + : _impl( impl ) +{} + +Spikes::Spikes(const Spikes& rhs) + : _impl( new detail::Spikes( *rhs._impl )) +{} + +Spikes::~Spikes() +{ + delete _impl; +} + +Spikes& Spikes::operator=(const Spikes& rhs) +{ + if (this == &rhs) + return *this; + + delete _impl; + _impl = new detail::Spikes( *rhs._impl ); + + return *this; +} + +Spikes::const_iterator Spikes::begin() const +{ + return const_iterator( new detail::Spikes_const_iterator( _impl->_begin )); +} + +Spikes::const_iterator Spikes::end() const +{ + return const_iterator( new detail::Spikes_const_iterator( _impl->_end )); +} + +float Spikes::getStartTime() const +{ + return _impl->_startTime; +} + +float Spikes::getEndTime() const +{ + return _impl->_endTime; +} + +size_t Spikes::size() const +{ + return _impl->_size; +} + +bool Spikes::empty() const +{ + return _impl->_size == 0; +} + +Spikes::const_iterator::const_iterator() + : _impl( new detail::Spikes_const_iterator ) +{} + +Spikes::const_iterator::const_iterator(const Spikes::const_iterator& rhs) + : _impl( new detail::Spikes_const_iterator( *rhs._impl )) +{} + +Spikes::const_iterator& Spikes::const_iterator::operator= ( + const Spikes::const_iterator& rhs) +{ + if (this == &rhs) + return *this; + + delete _impl; + _impl = new detail::Spikes_const_iterator( *rhs._impl ); + + return *this; +} + +Spikes::const_iterator::~const_iterator() +{ + delete _impl; +} + +Spikes::const_iterator::const_iterator( detail::Spikes_const_iterator* impl ) + : _impl( impl ) +{} + +brion::Spike Spikes::const_iterator::dereference() const +{ + return *(_impl->_it); +} + +bool Spikes::const_iterator::equal( const Spikes::const_iterator& other ) const +{ + return _impl->_it == other._impl->_it; +} + +void Spikes::const_iterator::increment() +{ + ++(_impl->_it); +} + +void Spikes::const_iterator::decrement() +{ + --(_impl->_it); +} + +} diff --git a/brain/spikes.h b/brain/spikes.h new file mode 100644 index 0000000..0b4f7f3 --- /dev/null +++ b/brain/spikes.h @@ -0,0 +1,167 @@ + +/* Copyright (c) 2006-2015, Raphael Dumusc + * Juan Hernando + * + * 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_SPIKES_H +#define BRAIN_SPIKES_H + +#include + +#include + +namespace brain +{ + +namespace detail +{ + class Spikes; + class Spikes_const_iterator; +} + +/** + * An iterable list of spikes over a time window, sorted by time. + * + * It is meant to be subclassed to provide different implementations for + * different sources of data (file or stream). + * By hiding the internal data structures, this object ensures that no + * unnecessary copies of spikes data takes place regardless of the source type. + */ +class Spikes +{ +public: + /* Forward declaration. */ + class const_iterator; + + /** + * Create an empty Spikes object. + * @version 0.2 + */ + Spikes(); + + /** + * Copy constructor. + * @version 0.2 + */ + Spikes( const Spikes& rhs ); + + /** + * Destructor + * @version 0.2 + */ + ~Spikes(); + + /** + * Assignment operator. + * @version 0.2 + */ + Spikes& operator= (const Spikes& rhs); + + /** + * Returns the first element of the container for const linear access. + * @version 0.2 + */ + const_iterator begin() const; + + /** + * Returns the const iterator that indicates the end of the container. + * @version 0.2 + */ + const_iterator end() const; + + /** + * Get the start of the time window in milliseconds. + * + * This time may be smaller or equal than the smallest spike time + * in the container. + * @version 0.2 + */ + float getStartTime() const; + + /** + * Get the end of the time window in milliseconds. + * + * This time may be greater or equal than the highest spike time + * in the container + * @version 0.2 + */ + float getEndTime() const; + + /** + * Get the number of elements in this container. + * @version 0.2 + */ + size_t size() const; + + /** + * Check if the container is empty. + * @version 0.2 + */ + bool empty() const; + +protected: + /** + * Protected constructor. + * Allows different implementations for different source types. + * @param impl The private implementation (for ABI compatiblity). + * @version 0.2 + */ + explicit Spikes( detail::Spikes* impl ); + +private: + detail::Spikes* _impl; +}; + +/** + * Iterator for the Spikes container. + */ +class Spikes::const_iterator : public boost::iterator_facade< + const_iterator, + brion::Spike, + std::bidirectional_iterator_tag, + brion::Spike +> +{ + friend class boost::iterator_core_access; + friend class Spikes; + +public: + /** Create an undefined / invalid iterator. */ + const_iterator(); + + /** Copy constructor. */ + const_iterator( const const_iterator& rhs ); + + /** Assignment operator. */ + const_iterator& operator= (const const_iterator& rhs); + + /** Destructor. */ + ~const_iterator(); + +private: + detail::Spikes_const_iterator* _impl; + + explicit const_iterator( detail::Spikes_const_iterator* impl ); + brion::Spike dereference() const; + bool equal( const const_iterator &other ) const; + void increment(); + void decrement(); +}; + +} +#endif diff --git a/brain/types.h b/brain/types.h index 658d436..97e52f6 100644 --- a/brain/types.h +++ b/brain/types.h @@ -29,6 +29,9 @@ namespace brain using namespace brion::enums; class Circuit; +class Spikes; +class SpikeReportReader; +class SpikeReportWriter; using vmml::Matrix4f; using vmml::Quaternionf; @@ -49,5 +52,8 @@ using brion::uint32_ts; typedef std::vector< Matrix4f > Matrix4fs; typedef std::vector< Quaternionf > Quaternionfs; typedef std::vector< URI > URIs; + +typedef boost::shared_ptr< SpikeReportReader > SpikeReportReaderPtr; +typedef boost::shared_ptr< SpikeReportWriter > SpikeReportWriterPtr; } #endif diff --git a/tests/spikeReportReaderWriter.cpp b/tests/spikeReportReaderWriter.cpp new file mode 100644 index 0000000..6df4dbe --- /dev/null +++ b/tests/spikeReportReaderWriter.cpp @@ -0,0 +1,307 @@ + +/* Copyright (c) 2006-2015, Juan Hernando + * + * 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 + +#define BOOST_TEST_MODULE SpikeReportReader +#include +#include +#include +#include + +#define BLURON_SPIKE_REPORT_FILE "local/simulations/may17_2011/Control/out.dat" + +#define BLURON_SPIKES_START_TIME 0.15f +#define BLURON_SPIKES_END_TIME 9.975f +#define BLURON_SPIKES_COUNT 274 + +#define BLURON_FIRST_SPIKE_TIME BLURON_SPIKES_START_TIME +#define BLURON_FIRST_SPIKE_GID 290 +#define BLURON_LAST_SPIKE_TIME BLURON_SPIKES_END_TIME +#define BLURON_LAST_SPIKE_GID 353 + +#define NEST_SPIKE_REPORT_FILE "NESTSpikeData/spike_detector-65537-00.gdf" + +#define NEST_SPIKES_START_TIME 1.8f +#define NEST_SPIKES_END_TIME 98.8f +#define NEST_SPIKES_COUNT 96256 + +#define NEST_FIRST_SPIKE_TIME NEST_SPIKES_START_TIME +#define NEST_FIRST_SPIKE_GID 33872 + +#define NEST_LAST_SPIKE_TIME NEST_SPIKES_END_TIME +#define NEST_LAST_SPIKE_GID 47760 + +struct TmpFile +{ + const std::string name; + + explicit TmpFile( const std::string& suffix = std::string( )) + : name("/tmp/" + lunchbox::make_UUID().getString() + suffix) + { + } + + ~TmpFile() + { + if( boost::filesystem::exists( name )) + boost::filesystem::remove( name ); + } +}; + +namespace std +{ + std::ostream& operator<<( std::ostream &os, const brion::Spike& spike ) + { + return os << spike.first << ", " << spike.second; + } +} + +BOOST_AUTO_TEST_CASE( test_invalid_report ) +{ + BOOST_CHECK_THROW( brain::SpikeReportReader report0( brion::URI( "./bla" )), + std::runtime_error ); + + boost::filesystem::path path( BBP_TESTDATA ); + path /= "local/README"; + BOOST_CHECK_THROW( brain::SpikeReportReader( brion::URI( path.string( ))), + std::runtime_error ); +} + +BOOST_AUTO_TEST_CASE( test_simple_load_static ) +{ + boost::filesystem::path path( BBP_TESTDATA ); + path /= BLURON_SPIKE_REPORT_FILE; + + brain::SpikeReportReader reader( brion::URI( path.string( ))); +} + +BOOST_AUTO_TEST_CASE( test_default_constructor ) +{ + brain::Spikes spikes; + BOOST_CHECK( spikes.empty() ); + BOOST_CHECK_EQUAL( spikes.size(), 0 ); + BOOST_CHECK( spikes.begin() == spikes.end( )); + BOOST_CHECK_EQUAL( spikes.getStartTime(), 0.0f ); + BOOST_CHECK_EQUAL( spikes.getEndTime(), 0.0f ); +} + +BOOST_AUTO_TEST_CASE( test_simple_read ) +{ + boost::filesystem::path path( BBP_TESTDATA ); + path /= BLURON_SPIKE_REPORT_FILE; + + brain::SpikeReportReader reader( brion::URI( path.string( ))); + const brain::Spikes& spikes = reader.getSpikes(); + + BOOST_REQUIRE_EQUAL( spikes.size(), BLURON_SPIKES_COUNT ); + + BOOST_CHECK_EQUAL( reader.getStartTime(), BLURON_SPIKES_START_TIME ); + BOOST_CHECK_EQUAL( reader.getEndTime(), BLURON_SPIKES_END_TIME ); + + BOOST_CHECK_EQUAL( spikes.begin()->first, BLURON_SPIKES_START_TIME ); + BOOST_CHECK_EQUAL( spikes.begin()->second, BLURON_FIRST_SPIKE_GID ); + + BOOST_CHECK_EQUAL( ( --spikes.end( ))->first, BLURON_LAST_SPIKE_TIME ); + BOOST_CHECK_EQUAL( ( --spikes.end( ))->second, BLURON_LAST_SPIKE_GID ); +} + + +BOOST_AUTO_TEST_CASE( test_closed_window ) +{ + boost::filesystem::path path( BBP_TESTDATA ); + path /= BLURON_SPIKE_REPORT_FILE; + brain::SpikeReportReader reader( brion::URI( path.string( ))); + + const brain::Spikes& spikes = reader.getSpikes( 2.5f, 2.5f ); + BOOST_CHECK( spikes.empty() ); + BOOST_CHECK_EQUAL( spikes.size(), 0 ); + BOOST_CHECK( spikes.begin() == spikes.end( )); +} + +BOOST_AUTO_TEST_CASE( test_out_of_window ) +{ + boost::filesystem::path path( BBP_TESTDATA ); + path /= BLURON_SPIKE_REPORT_FILE; + brain::SpikeReportReader reader( brion::URI( path.string( ))); + const brain::Spikes& spikes = reader.getSpikes(); + + const float start = spikes.getEndTime() + 1; + + const brain::Spikes& window = reader.getSpikes(start, start + 1 ); + BOOST_CHECK_EQUAL( window.getStartTime(), start ); + BOOST_CHECK_EQUAL( window.getEndTime(), start + 1 ); + BOOST_CHECK_EQUAL( window.size(), 0 ); + BOOST_CHECK( window.empty( )); +} + +BOOST_AUTO_TEST_CASE( test_simple_stream_read ) +{ + boost::filesystem::path path( BBP_TESTDATA ); + path /= NEST_SPIKE_REPORT_FILE; + + brain::SpikeReportReader reader( brion::URI( path.string( ))); + while( !reader.hasEnded( )) + reader.getSpikes(); + + const brain::Spikes& spikes = reader.getSpikes(); + + BOOST_REQUIRE_EQUAL( spikes.size(), NEST_SPIKES_COUNT ); + + BOOST_CHECK_EQUAL( reader.getStartTime(), NEST_SPIKES_START_TIME ); + BOOST_CHECK_EQUAL( reader.getEndTime(), NEST_SPIKES_END_TIME ); + + BOOST_CHECK_EQUAL( spikes.begin()->first, NEST_FIRST_SPIKE_TIME ); + BOOST_CHECK_EQUAL( spikes.begin()->second, NEST_FIRST_SPIKE_GID ); + + BOOST_CHECK_EQUAL( ( --spikes.end( ))->first, NEST_LAST_SPIKE_TIME ); + BOOST_CHECK_EQUAL( ( --spikes.end( ))->second, NEST_LAST_SPIKE_GID ); +} + +BOOST_AUTO_TEST_CASE( test_moving_window ) +{ + boost::filesystem::path path( BBP_TESTDATA ); + path /= NEST_SPIKE_REPORT_FILE; + + brain::SpikeReportReader reader( brion::URI( path.string( ))); + float start = 0; + while( !reader.hasEnded( )) + { + const brain::Spikes& spikes = reader.getSpikes(start, start + 1); + if( !spikes.empty( )) + { + BOOST_CHECK( spikes.begin()->first >= start ); + BOOST_CHECK( ( --spikes.end( ))->first >= start ); + } + start += 1; + } + + const brain::Spikes& spikes = reader.getSpikes(); + + BOOST_REQUIRE_EQUAL( spikes.size(), NEST_SPIKES_COUNT ); + + BOOST_CHECK_EQUAL( reader.getStartTime(), NEST_SPIKES_START_TIME ); + BOOST_CHECK_EQUAL( reader.getEndTime(), NEST_SPIKES_END_TIME ); + + BOOST_CHECK_EQUAL( spikes.begin()->first, NEST_FIRST_SPIKE_TIME ); + BOOST_CHECK_EQUAL( spikes.begin()->second, NEST_FIRST_SPIKE_GID ); + + BOOST_CHECK_EQUAL( ( --spikes.end( ))->first, NEST_LAST_SPIKE_TIME ); + BOOST_CHECK_EQUAL( ( --spikes.end( ))->second, NEST_LAST_SPIKE_GID ); +} + +BOOST_AUTO_TEST_CASE( TestSpikes_nest_spikes_read_write ) +{ + boost::filesystem::path path( BBP_TESTDATA ); + path /= BLURON_SPIKE_REPORT_FILE; + + brain::SpikeReportReader reader( brion::URI( path.string( ))); + const brain::Spikes& readSpikes = reader.getSpikes(); + + TmpFile file( ".gdf" ); + + brain::SpikeReportWriter writer( brion::URI( file.name )); + writer.writeSpikes( readSpikes ); + writer.close(); + + brain::SpikeReportReader reReader( brion::URI( file.name )); + const brain::Spikes& reReadSpikes = reReader.getSpikes(); + + BOOST_CHECK_EQUAL_COLLECTIONS( + readSpikes.begin(), readSpikes.end(), + reReadSpikes.begin(), reReadSpikes.end( )); +} + +BOOST_AUTO_TEST_CASE( TestSpikes_bluron_spikes_read_write ) +{ + boost::filesystem::path path( BBP_TESTDATA ); + path /= BLURON_SPIKE_REPORT_FILE; + + brain::SpikeReportReader reader( brion::URI( path.string( ))); + const brain::Spikes& readSpikes = reader.getSpikes(); + + TmpFile file( ".dat" ); + + brain::SpikeReportWriter writer( brion::URI( file.name )); + writer.writeSpikes( readSpikes ); + writer.close(); + + brain::SpikeReportReader reReader( brion::URI( file.name )); + const brain::Spikes& reReadSpikes = reReader.getSpikes(); + + BOOST_CHECK_EQUAL_COLLECTIONS( readSpikes.begin(), readSpikes.end(), + reReadSpikes.begin(), reReadSpikes.end( )); +} + +BOOST_AUTO_TEST_CASE( const_iterator_assignment_operator ) +{ + boost::filesystem::path path( BBP_TESTDATA ); + path /= BLURON_SPIKE_REPORT_FILE; + + brain::SpikeReportReader reader( brion::URI( path.string( ))); + + brain::Spikes spikes = reader.getSpikes(); + + brain::Spikes::const_iterator it; + BOOST_CHECK( it != spikes.begin() ); + + it = spikes.begin(); + brain::Spikes::const_iterator it2 = it; + + BOOST_CHECK( it == it2 ); + BOOST_CHECK_EQUAL( *it, *it2 ); +} + +BOOST_AUTO_TEST_CASE( const_iterator_copy_constructor ) +{ + boost::filesystem::path path( BBP_TESTDATA ); + path /= BLURON_SPIKE_REPORT_FILE; + + brain::SpikeReportReader reader( brion::URI( path.string( ))); + + brain::Spikes spikes = reader.getSpikes(); + + brain::Spikes::const_iterator it = spikes.begin(); + brain::Spikes::const_iterator it2( it ); + + BOOST_CHECK( it == it2 ); + BOOST_CHECK_EQUAL( *it, *it2 ); +} + +BOOST_AUTO_TEST_CASE( const_iterator_increment_and_decrement ) +{ + boost::filesystem::path path( BBP_TESTDATA ); + path /= BLURON_SPIKE_REPORT_FILE; + + brain::SpikeReportReader reader( brion::URI( path.string( ))); + + brain::Spikes spikes = reader.getSpikes(); + + brain::Spikes::const_iterator it = spikes.begin(); + BOOST_CHECK( ++it != spikes.begin() ); + BOOST_CHECK( --it == spikes.begin() ); +}