diff options
Diffstat (limited to 'core')
97 files changed, 7763 insertions, 0 deletions
diff --git a/core/all_spikes.cpp b/core/all_spikes.cpp new file mode 100644 index 0000000..5307e8b --- /dev/null +++ b/core/all_spikes.cpp @@ -0,0 +1,49 @@ +#include <assert.h> +#include <errno.h> +#include <iostream> +#include <stdlib.h> + +#include "everything_else.hpp" +#include "index.hpp" +#include "index_spike.hpp" +#include "index_randomspike.hpp" + +#include "mempool.hpp" + +using namespace std; + +int main(int argc, char **argv) { + // read cmd line params + assert(argc == 3); + + errno = 0; + char *tail; + Time start(strtod( argv[1], &tail)); assert(*tail == 0); + Time stop( strtod( argv[2], &tail)); assert(*tail == 0); + assert(errno == 0); + + assertSimDir(); + + { + // go to first spike with time >= start + // Index<RandomSpike> idx; + // Ptr<RandomSpike>::ptr_t cur(idx.first(start)); + Index<Spike> idx; + Ptr<Spike>::ptr_t cur(idx.first(start)); + + // no spike found? + if (cur == idx.nil()) + return 0; + + // output + cout << "# time\tneuron" << endl; + while (cur <= idx.last() && idx.time(cur) <= stop) { + if (idx.src(cur) < numActualNeurons) // don't plot pseudo neurons + // cout << idx.eventTime(cur) << "\t" << idx.src(cur) << endl; + cout << idx.time(cur) << "\t" << idx.src(cur) << endl; + ++cur; + } + } + + return 0; +} diff --git a/core/array.hpp b/core/array.hpp new file mode 100644 index 0000000..e882f92 --- /dev/null +++ b/core/array.hpp @@ -0,0 +1,53 @@ +#ifndef IuE4Lmq81ZDeAq0u7S79e56kZZQ +#define IuE4Lmq81ZDeAq0u7S79e56kZZQ + +#include <inttypes.h> +#include <assert.h> +#include <boost/static_assert.hpp> + +#include "bit_access.hpp" +#include "template_helpers.hpp" + +// size - number of elements +// width - size of each elements (in bit!) +template<typename T, uint32_t size, size_t width = 8 * sizeof(T)> +class Array { +public: + typedef uint32_t ptr_t; + + // reader and writer + inline T get(ptr_t id); + inline void set(ptr_t id, T value); + + // allow accessing raw memory (this also encodes the correct size of the object) + uint8_t mem[ size * ((width + 7) / 8) ]; + + // garantuee that all sizes = 2^n + STATIC_ASSERT_IS_POWER_OF_TWO(width); + STATIC_ASSERT_IS_POWER_OF_TWO(size); + // garantue that we do not need bit-level access of the underlying container + BOOST_STATIC_ASSERT((uint64_t) width * size >= 8); +}; + +template<typename T, uint32_t size, size_t width> +inline T Array<T, size, width>::get(ptr_t id) { + assert(id < size); + if (width < 8) { + uint8_t res = bit_extract(mem, (uint8_t) width, id); + return FORCE_CONV(T, res); + }else{ + return *((T*) (((char*) mem) + (width / 8) * id)); // TO CHECK: improve cast? + } +} + +template<typename T, uint32_t size, size_t width> +inline void Array<T, size, width>::set(ptr_t id, T value) { + assert(id < size); + if (width < 8) { + bit_insert(mem, (uint8_t) width, id, value); + }else{ + *((T*) (((char*) mem) + (width / 8) * id)) = value; + } +} + +#endif // IuE4Lmq81ZDeAq0u7S79e56kZZQ diff --git a/core/bit_access.hpp b/core/bit_access.hpp new file mode 100644 index 0000000..566254e --- /dev/null +++ b/core/bit_access.hpp @@ -0,0 +1,38 @@ +#ifndef vjxaugYoBtZu5XZVahcafy1l5XU +#define vjxaugYoBtZu5XZVahcafy1l5XU + +#include <inttypes.h> +#include <assert.h> + +#include "template_helpers.hpp" + +inline uint8_t bit_extract(void *base, uint8_t width, uint64_t id) { + assert(width < 8); + + uint8_t off, mask, block, result, *addr; + + addr = ((uint8_t*) base) + (id * width / 8); + off = (id % (8 / width)) * width; + mask = (~((~0) << width)) << off; + block = * addr; + result = (block & mask) >> off; + + return result; +} + +template<class T> +inline void bit_insert(void *base, uint8_t width, uint64_t id, T rawValue) { + assert(width < 8); + + uint8_t off, mask, block, result, *addr; + uint8_t value(FORCE_CONV(uint8_t, rawValue)); + + addr = ((uint8_t*) base) + (id * width / 8); + off = (id % (8 / width)) * width; + mask = (~((~0) << width)) << off; + block = * addr; + result = (block & (~mask)) | ((value << off) & mask); // mask also value, in case it is larger than allowed + *addr = result; +} + +#endif // vjxaugYoBtZu5XZVahcafy1l5XU diff --git a/core/bootstrap.cpp b/core/bootstrap.cpp new file mode 100644 index 0000000..092d0f2 --- /dev/null +++ b/core/bootstrap.cpp @@ -0,0 +1,106 @@ +#include <boost/mpl/list.hpp> +#include <boost/mpl/pair.hpp> +#include <iostream> +#include <inttypes.h> + +#include "index.hpp" +#include "index_spike.hpp" +#include "pla_get.hpp" +#include "property_composition.hpp" +#include "pla_init_default.hpp" +#include "pla_set.hpp" +#include "sim_loop.hpp" + +#include "model.hpp" + +using namespace boost; + +const Time startTime(0.1); + +struct FakeSim { + FakeSim() + : indices(), + queues() + { + using boost::make_tuple; + } + + Ptr<GlobalMsg> addGlobalMsg(RNG::seed_t seed) { + const Time gmStartTime(ModelConsts::TrainerInitialDelay); + // create an discrete quant instance + Ptr<GlobalMsg> res(indices.get<Index<GlobalMsg>>().add + (Time(0), gmStartTime, Ptr<Global>())); + // and a corresponding event occuring at time 0.1, stored in start + // interval (t=0) + queues.insert<GlobalMsg>(Time(0), gmStartTime, Event<GlobalMsg>(res)); + pc.cast(PLA_Set<NextTrainer>(res, TrainerT(seed))); + return res; + } + + Ptr<Spike> addSpike(Ptr<Neuron> src) { + Time eventTime = startTime + TopologicalTimeOffset<Neuron, Spike>()(topo, src); + Ptr<Spike> res(indices.get<Index<Spike>>().add(startTime, eventTime, src)); + queues.insert<Spike>(Time(0), eventTime, Event<Spike>(startTime, src, res)); + return res; + } + + Ptr<RandomSpike> addRandomSpike(Ptr<Neuron> src, RNG::seed_t seed) { + Time eventTime = startTime + RNG::expo(seed, 1 / ModelConsts::RandomFreq); + Ptr<RandomSpike> res(indices.get<Index<RandomSpike>>().add + (startTime, eventTime, src)); + queues.insert<RandomSpike>(Time(0), eventTime, + Event<RandomSpike>(eventTime, src, res)); + return res; + } + + // data structs + typedef SimLoop<Lists::all> sim_t; + sim_t::indices_t indices; + sim_t::queues_t queues; + sim_t::pc_t pc; + Topology topo; +}; + +using namespace std; + +int main(int argc, char **argv) { + using boost::mpl::pair; + using boost::mpl::list; + FakeSim data; + + // read cmd line arg + if (argc > 2) { + cout << "Usage: " << argv[0] << " [random-seed=0]"; + } + char *tail; + RNG::seed_t rng(0); + if (argc == 2) { + rng = RNG::seed_t(strtoll(argv[1], &tail, 10)); + assert(*tail == 0); + } + + // init values (except weight) + data.pc.cast(PLA_Init_Default()); + + // init random seed (different one for every neuron) + for (Ptr<Neuron> i : Ptr<Global>().childs()) { + data.pc.cast(PLA_Set<RandomSeed>(Time(0), i, RNG::split(rng))); + rng = RNG::next(rng, 40); + } + + // TODO: add a global event to start trainer + PLA_Get<NextTrainer> get(Ptr<GlobalMsg>(0)); + data.addGlobalMsg(RNG::split(rng)); + rng = RNG::next(rng, 40); + + // add a random noise source for every neuron + for (Ptr<Neuron> i : Ptr<Global>().childs()) + if (i() < ModelConsts::NumExcitatory) { // exclude special neurons + data.addRandomSpike(i, RNG::split(rng)); + rng = RNG::next(rng, 40); + } + + // TODO: add discrete properties to the created events + + return 0; +} diff --git a/core/checkpoint.hpp b/core/checkpoint.hpp new file mode 100644 index 0000000..44457b9 --- /dev/null +++ b/core/checkpoint.hpp @@ -0,0 +1,165 @@ +#ifndef a4jYavu7NKcD1XX5TXFDvm6LUn8 +#define a4jYavu7NKcD1XX5TXFDvm6LUn8 + +#include <boost/static_assert.hpp> + +#include "array.hpp" +#include "everything_else.hpp" +#include "simlimits.hpp" +#include "string_helpers.hpp" +#include "template_helpers.hpp" +#include "time.hpp" +#include "vector.hpp" + +template<typename T, uint32_t size> +class Checkpoint { +public: + typedef uint32_t ptr_t; + + Checkpoint() = delete; + Checkpoint(const string name, const T initialValue); + + // read access + inline Time getTime (const Time t, const ptr_t i) __attribute__ ((pure)); // DANGER + inline T getValue(const Time t, const ptr_t i); + inline Time getTime (const Time t); // only with size == 1 + inline T getValue(const Time t); // only with size == 1 + + // write access + inline void set(const Time t, const ptr_t i, const T val); + inline void set(const Time t, const T val); // only with size == 1 + + void sync() const; + + // page turn-over: stores all checkpoints and open a new checkpoint + // table for the next interval + void addCheckpoints(const Time newTime); + + // time managment + static const ptr_t binTime(const Time t) { return t.ceilDiv(interval()); } + static constexpr Time interval() { return checkpointInterval; } + + Time timeLimit, // current interval's time limits (infimum, maximum) + timeInf; + ptr_t currentInterval; + + // file backings; they store for each interval, for each element the ... + Vector< Array< T, size > > content; // ... last known content + Vector< Array< Time, size > > times; // ... associated time +}; + +template<typename T, uint32_t size> +inline void Checkpoint<T, size>::set(const Time t, const ptr_t i, const T val) { + if (unlikely(t > timeLimit)) addCheckpoints(t); + times (currentInterval).set(i, t); + content(currentInterval).set(i, val); +} + +template<typename T, uint32_t size> +inline void Checkpoint<T, size>::set(const Time t, const T val) { + BOOST_STATIC_ASSERT(size == 1); + set(t, 0, val); +} + +template<typename T, uint32_t size> +void Checkpoint<T, size>::sync() const { + content.sync(); + times.sync(); +} + +template<typename T, uint32_t size> +inline Time Checkpoint<T, size>::getTime(const Time t, const ptr_t i) { + if (unlikely(t > timeLimit)) addCheckpoints(t); + ptr_t ival = likely(t > timeInf) ? currentInterval : binTime(t); + return times(ival).get(i); +} + +template<typename T, uint32_t size> +inline Time Checkpoint<T, size>::getTime(const Time t) { + BOOST_STATIC_ASSERT(size == 1); + return getTime(t, 0); +} + +template<typename T, uint32_t size> +inline T Checkpoint<T, size>::getValue(const Time t, const ptr_t i) { + if (unlikely(t > timeLimit)) addCheckpoints(t); + ptr_t ival = likely(t > timeInf) ? currentInterval : binTime(t); + return content(ival).get(i); +} + +template<typename T, uint32_t size> +inline T Checkpoint<T, size>::getValue(const Time t) { + BOOST_STATIC_ASSERT(size == 1); + return getValue(t, 0); +} + +template<typename T, uint32_t size> +Checkpoint<T, size>::Checkpoint(const string name, const T initialValue) : + timeLimit(0), + timeInf(Time(0) - interval()), + currentInterval(0), + content("checkpoint_content_" + name, maxCheckpoints), + times( "checkpoint_times_" + name, maxCheckpoints) +{ + assert(content.barrierWrite() == times.barrierWrite()); + + // check if we are the first to use this checkpoint and have to initialize it + if (content.barrierWrite() == 0) { + content.barrierWrite() = 1; + times. barrierWrite() = 1; + content.barrierRead() = 0; + times .barrierRead() = 0; + + for (ptr_t i=0; i<size; i++) { + content(0).set(i, initialValue); + times(0).set(i, 0); + } + } + + currentInterval = content.barrierWrite() - 1; + timeLimit = Time(currentInterval) * interval(); + timeInf = Time(currentInterval - 1) * interval(); +} + +template<typename T, uint32_t size> +void Checkpoint<T, size>::addCheckpoints(const Time t) { + assert(content.barrierWrite() == times.barrierWrite()); // is a pre- and postcondition + // check if the write barrier has been updated externally + assert(currentInterval == content.barrierWrite() - 1); + + // now create new checkpoint(s) + // + // usually the loop below is executed exactly once; only in the + // rare case of having no event in an interval it may be + // executed several times; if this happens more often one should + // increase the interval length significantly to avoid wasting + // space + while (t > timeLimit) { + // copy content and associated times to the new checkpoint + // TODO: store chekcpoint number in object, use as cache + currentInterval = content.barrierWrite(); // next checkpoint's number + for (uint32_t i=0; i<size; i++) { + // one could simply memcpy() instead of below, but this way it is intuitive (?) + content(currentInterval).set(i, content(currentInterval - 1).get(i)); + times (currentInterval).set(i, times (currentInterval - 1).get(i)); + } + + // increase barriers and time limit + content.barrierWrite()++; + times. barrierWrite()++; + content.barrierRead() = content.barrierWrite() - 1; + times. barrierRead() = times. barrierWrite() - 1; + timeLimit = interval() * currentInterval; + timeInf = interval() * (currentInterval - 1); + } + + assert(content.barrierWrite() == times.barrierWrite()); + + // memory advise that we do not need the past + if (currentInterval) { + content.advise(0, currentInterval, MADV_DONTNEED); + times. advise(0, currentInterval, MADV_DONTNEED); + } +} + +#endif // a4jYavu7NKcD1XX5TXFDvm6LUn8 diff --git a/core/coarse_replay.cpp b/core/coarse_replay.cpp new file mode 100644 index 0000000..24412cc --- /dev/null +++ b/core/coarse_replay.cpp @@ -0,0 +1,89 @@ +#include <assert.h> +#include <errno.h> +#include <iostream> +#include <stdlib.h> + +#include <boost/type_traits/is_same.hpp> + +#include "everything_else.hpp" +#include "id_list.hpp" +#include "time.hpp" +#include "template_helpers.hpp" +#include "pla_getbyname.hpp" +#include "property_composition.hpp" + +#include "model.hpp" + +#include "mempool.hpp" + +using namespace std; + +template<typename Prop, typename QuantClass = typename Prop::quant::class_t> +struct CoarseReplay; + +// define base case with virtual op() to runtime dispatch call by property name +template<> +struct CoarseReplay<BaseCase, Void> { + virtual void operator() (Time start, Time stop, IdList<uint64_t> rawIds) { + assert(false); + } +}; + +template<typename Prop> +struct CoarseReplay<Prop, ContinuousPropertyClass> : CoarseReplay<BaseCase, Void> { + virtual void operator() (Time start, Time stop, + IdList<uint64_t> rawIds) { + IdList<typename Ptr<typename Prop::quant>::ptr_t> ids(rawIds); + PropertyInstance<Prop, true> pi; + assert(stop <= pi.data.timeLimit); + assert(start >= 0); + + // print header + cout << "#"; + for (auto i : ids) cout << "\tval(" << (uint64_t) i << ")"; + for (auto i : ids) cout << "\ttime(" << (uint64_t) i << ")"; + cout << endl; + + // print data + for (Time t = start; t <= stop; t = t + pi.data.interval()) { + cout << t(); + for (auto i : ids) cout << "\t" << pi.data.getValue(t, i); + for (auto i : ids) cout << "\t" << pi.data.getTime (t, i); + cout << endl; + } + } +}; + +template<typename Prop> +struct CoarseReplay<Prop, DiscretePropertyClass> : CoarseReplay<BaseCase, Void> { + virtual void operator() (Time start, Time stop, uint64_t raw_addr) { + cerr << Prop::name << " is a discrete property" << endl; + exit(-1); + } +}; + + +int main(int argc, char **argv) { + // read cmd line params + assert(argc == 5); + assertSimDir(); + + { + PropertyComposition<Lists::all> pc; + PLA_GetByName<CoarseReplay> pla(argv[1]); + CoarseReplay<BaseCase, Void> *replay(pc.call(pla)); + assert(replay != NULL); + + errno = 0; + char *tail; + IdList<uint64_t> ids(argv[2]); + Time start( strtod( argv[3], &tail)); assert(*tail == 0); + Time stop( strtod( argv[4], &tail)); assert(*tail == 0); + assert(errno == 0); + + // output + (*replay)(start, stop, ids); + } + + return 0; +} diff --git a/core/context.hpp b/core/context.hpp new file mode 100644 index 0000000..dd4ed7e --- /dev/null +++ b/core/context.hpp @@ -0,0 +1,123 @@ +#ifndef sOuC39DJVwHiStoyuZdritpuC5M +#define sOuC39DJVwHiStoyuZdritpuC5M + +#include <assert.h> + +#include <boost/type_traits/is_same.hpp> +#include <boost/mpl/set.hpp> +#include <boost/mpl/if.hpp> +#include <boost/mpl/insert.hpp> +#include <boost/tuple/tuple.hpp> + +#include "pointers.hpp" +#include "time.hpp" +#include "type_set.hpp" + +/* methods common to all context's + + getptr<Quant>() -> return an instance id + + */ + +namespace ContextImpl { + + using namespace boost; + using namespace boost::mpl; + + // common context (internal) + template<typename... Quants> + struct CCtx : public TypeSet<Ptr<Quants>...> { + CCtx(Ptr<Quants>... ids) : TypeSet<Ptr<Quants>...>(std::move(ids)...) {} + + template<typename Quant> + typename Quant::instance_ptr_t getptr() { + return this->template get<Ptr<Quant>>(); + } + }; + + /// SingleContext: stores a single quant w/o any relation + template<typename Quant> + struct SingleContext : public CCtx<Quant> { + SingleContext(Ptr<Quant> id) : CCtx<Quant>(id) {} + }; + + /// ContinuousContext: resembles the global <- neuron <- synapse hierarchy + template<typename Quant> + struct ContinuousContext; + + template<> + struct ContinuousContext<Global> : CCtx<Global> { + ContinuousContext() + : CCtx<Global>(Ptr<Global>()) {} + explicit ContinuousContext(Ptr<Global> p) + : CCtx<Global>(p) {} + }; + + template<> + struct ContinuousContext<Neuron> : CCtx<Neuron, Global> { + explicit ContinuousContext(Ptr<Neuron> p) + : CCtx<Neuron, Global>(p, Ptr<Global>()) {} + }; + + template<> + struct ContinuousContext<Synapse> + : CCtx<Synapse, Neuron, Global> { + explicit ContinuousContext(Ptr<Synapse> p) + : CCtx<Synapse, Neuron, Global>(p, p.extractNeuron(), Ptr<Global>()) {} + }; + + // common context with one discrete ptr (internal) + template<typename DQ, typename CQ> + struct DCtx { + DCtx(Ptr<DQ> did, Ptr<CQ> cid) + : id(did), sub(cid) {} + + template<typename Quant> + typename Quant::instance_ptr_t getptr() { + if (boost::is_same<Quant, DQ>::value) { + return FORCE_CONV(typename Quant::instance_ptr_t, id); + }else{ + return sub.getptr<Quant>(); + } + } + + friend std::ostream& operator<< (std::ostream &os, DCtx val) { + return os << val.id << ":" << val.sub; + } + + Ptr<DQ> id; + ContinuousContext<CQ> sub; + }; + + + /// DelieverContext: when a (discrete) event is delievered to a + /// continuous quantity + template<typename DQ, typename CQ> + struct DelieverContext : DCtx<DQ, CQ> { + DelieverContext(Ptr<DQ> did, Ptr<CQ> cid) + : DCtx<DQ, CQ>(did, cid) {} + }; + + // HACK: allow Neuron to access Synapse on SpikeArrival; this only + // works because SA is send with a zero delay, so synapse is already + // evolved to current time + template<> + struct DelieverContext<SpikeArrival, Neuron> : DCtx<SpikeArrival, Synapse> { + DelieverContext(boost::tuple<Ptr<SpikeArrival>, Ptr<Synapse>> t, Ptr<Neuron>) + : DCtx<SpikeArrival, Synapse>(t.get<0>(), t.get<1>()) {} + }; + + /// EmitContent: CQ emits event of class DQ + template<typename CQ, typename DQ> + struct EmitContext : DCtx<DQ, CQ> { + EmitContext(Ptr<CQ> cid, Ptr<DQ> did) + : DCtx<DQ, CQ>(did, cid) {} + }; +} + +using ContextImpl::SingleContext; +using ContextImpl::ContinuousContext; +using ContextImpl::DelieverContext; +using ContextImpl::EmitContext; + +#endif // sOuC39DJVwHiStoyuZdritpuC5M diff --git a/core/continuous_property.hpp b/core/continuous_property.hpp new file mode 100644 index 0000000..bed37cf --- /dev/null +++ b/core/continuous_property.hpp @@ -0,0 +1,93 @@ +#ifndef H3OuNUuw3KK0wmZozXqMmrvzz3M +#define H3OuNUuw3KK0wmZozXqMmrvzz3M + +#include <inttypes.h> +#include <boost/mpl/bool.hpp> + +#include "context.hpp" +#include "index_global.hpp" +#include "index_spike.hpp" +#include "index_spike_arrival.hpp" +#include "pointers.hpp" +#include "quant_types.hpp" +#include "time.hpp" + +//////////////////////////////////////////////////////////////////////////////// +// event related actions +//////////////////////////////////////////////////////////////////////////////// + +// evolve in time - default case: value remains the same +template<typename PropComp, typename Prop, typename Quant, typename Context> +struct ContinuousProperty_Evolve { + static inline typename Prop::type eval + (PropComp &, Context context, Time t, Time dt); +}; + +// apply an incoming event - default case: do not send anything +template<typename PropComp, typename Prop, typename Quant, typename EventQuant, + typename Context, typename IntentMap, typename Transaction> +struct ContinuousProperty_Apply { + static inline void eval(PropComp &, Context, Time, IntentMap &, Transaction &) {} +}; + +// apply an incoming event: do we change the prop val at all? +template<typename Prop, typename SrcQ> +struct ContinuousProperty_ApplyChangesProp : boost::mpl::bool_<false> {}; + +// generate an outgoing event - default case: generate nothing +template<typename Prop, typename Quant, typename EventQuant> +struct ContinuousProperty_Generate { + template<typename PropComp, typename Context, typename MQ, typename MI> + static inline void eval(PropComp &, Context, Time, typename Prop::type &, + MI &, MQ &) {} + static const bool changesValue = false; +}; + +//////////////////////////////////////////////////////////////////////////////// +// definition helpers +//////////////////////////////////////////////////////////////////////////////// + +#define GEN_CP(Quant, nameT, nameS, ValueType, InitialVal) \ +struct nameT { \ + typedef ValueType type; \ + typedef Quant quant; \ + typedef Quant::instance_ptr_t instance_ptr_t; \ + \ + static const ValueType initialValue; \ + static const uint32_t size = sizeof(ValueType); \ + static const char* const name; \ +}; \ + \ +const char* const nameT::name = nameS; \ +const ValueType nameT::initialValue{InitialVal}; + +#define GEN_CPL_EVOLVE(nameT) \ +template<typename PropComp, typename Context> \ +struct ContinuousProperty_Evolve<PropComp, nameT, nameT::quant, Context> { \ + inline static nameT::type eval(PropComp &pc, Context context, Time t, Time td) + +#define GEN_CP_EVOLVE(nameT, ReturnVal) \ +GEN_CPL_EVOLVE(nameT) { \ + return ReturnVal; \ + } \ +}; + +#define GEN_CP_APPLY(Property, EventQuant, ChangeProp) \ +template<> \ +struct ContinuousProperty_ApplyChangesProp<Property, EventQuant> \ + : boost::mpl::bool_<ChangeProp> {}; \ + \ +template<typename PropComp, typename Quant, typename Context, typename IntentMap, typename Transaction> \ +struct ContinuousProperty_Apply<PropComp, Property, Quant, EventQuant, Context, IntentMap, Transaction> { \ + typedef boost::mpl::bool_<ChangeProp> changeProp_t; \ + static inline void eval(PropComp &pc, Context context, Time t, IntentMap &intent, Transaction &transaction) + +#define GEN_CP_GENERATE(CQ, DQ, Prop) \ +template<> \ +struct ContinuousProperty_Generate<Prop, CQ, DQ> { \ + static const bool changesValue = true; \ + template<typename PropComp, typename Context, typename MQ, typename MI> \ + static inline void eval(PropComp &pc, Context context, Time t, \ + Prop::type &value, MI &indices, MQ &queues) + +#endif // H3OuNUuw3KK0wmZozXqMmrvzz3M diff --git a/core/continuous_property_impl.hpp b/core/continuous_property_impl.hpp new file mode 100644 index 0000000..13695cf --- /dev/null +++ b/core/continuous_property_impl.hpp @@ -0,0 +1,13 @@ +#ifndef LEt1PyaObN7YkjcGFenqweegIyk +#define LEt1PyaObN7YkjcGFenqweegIyk + +#include "property_access.hpp" + +template<typename PropComp, typename Prop, typename Quant, typename Context> +inline typename Prop::type +ContinuousProperty_Evolve<PropComp, Prop, Quant, Context> +::eval(PropComp &pc, Context context, Time t, Time dt) { + return Property_Get<Prop>::eval(pc, context, t); +} + +#endif // LEt1PyaObN7YkjcGFenqweegIyk diff --git a/core/convert_topology.cpp b/core/convert_topology.cpp new file mode 100644 index 0000000..e348c80 --- /dev/null +++ b/core/convert_topology.cpp @@ -0,0 +1,161 @@ +/* format + "src dst delay weight\n" + multiple src dst combinations are possible (?) + */ +#include <string.h> +#include <iostream> +#include <map> + +#include <boost/tuple/tuple.hpp> +#include <boost/mpl/pair.hpp> +#include <boost/mpl/list.hpp> + +#include "pla_set.hpp" +#include "pointers.hpp" +#include "property_composition.hpp" +#include "simlimits.hpp" +#include "time.hpp" +#include "topology.hpp" + +#include "model.hpp" + +#include "mempool.hpp" + +using namespace std; + +typedef Ptr<Neuron>::ptr_t np_t; +typedef Ptr<Synapse>::ptr_t sp_t; +typedef Ptr<Synapse>::offset_t op_t; + +struct Connection { + np_t src, dst; + Weight::type weight; + Time::type delay; +}; + +map<np_t, multimap<Time, Connection>*> cons; + +PropertyComposition<boost::mpl::list< + boost::mpl::pair<Weight, boost::mpl::bool_<true>>, + boost::mpl::pair<TargetSumWeight, boost::mpl::bool_<true>>, + boost::mpl::pair<SumWeight, boost::mpl::bool_<true>> +>> pc; + + +void init() { + // check that pc time is 0.0 + assert(pc.properties.data.data.timeLimit == 0.0); + for (np_t i=0; i<maxNeurons; i++) + cons[i] = new multimap<Time, Connection>(); +} + +void read() { + cin >> skipws; + while (!cin.eof()) { + Connection c; + + // read from stream + cin >> c.src >> c.dst >> c.delay >> c.weight; + assert(!cin.fail()); + cin >> ws; + + // first sanity check + assert(c.src < maxNeurons); + assert(c.dst < maxNeurons); + assert(c.delay > 0.0); + assert(c.weight != 0.0); + + // store (to sort) + cons[c.src]->insert(make_pair(c.delay, c)); + } +} + +void addPseudo() { + const Ptr<Neuron>::ptr_t half = maxNeurons/2; + assert(numActualNeurons <= half); + for (Ptr<Neuron>::ptr_t i=0; i<half; i++) { + Connection c; + c.src = i + half; + c.dst = i; + c.delay = Time::epsilon()(); + c.weight = ModelConsts::TrainerInput; + cons[c.src]->insert(make_pair(c.delay, c)); + } +} + +void setWeight(const sp_t synapse, Weight::type weight) { + PLA_Set<Weight> pla{Time{0}, Ptr<Synapse>{synapse}, weight}; + pc.call(pla); +} + +void writeTopology() { + // fill topology tables + Array<Time, maxNeurons * maxSynapsesPerNeuron> &delay + = *(new Array<Time, maxNeurons * maxSynapsesPerNeuron>()); + Array<Ptr<Synapse>::ptr_t, maxNeurons * maxSynapsesPerNeuron> &target + = *(new Array<Ptr<Synapse>::ptr_t, maxNeurons * maxSynapsesPerNeuron>()); + + op_t currentSynapse[maxNeurons]; + memset(currentSynapse, 0, sizeof(currentSynapse)); + + for (np_t i=0; i<maxNeurons; i++) { + op_t j=0; + multimap<Time, Connection> &l = *(cons[i]); + assert(l.size() < maxSynapsesPerNeuron); // last synapse required for nil + + for (multimap<Time, Connection>::iterator k = l.begin(); k != l.end(); k++) { + sp_t t = i * maxSynapsesPerNeuron + j; + Connection &c = (*k).second; + Time::type dt = c.delay; + sp_t s = c.dst * maxSynapsesPerNeuron + currentSynapse[c.dst]++; + + delay.set (t, dt); + target.set(t, s); + setWeight(s, c.weight); + j++; + } + + // fill unused synapses + for (; j<maxSynapsesPerNeuron; j++) { + sp_t t = i * maxSynapsesPerNeuron + j; + target.set(t, Topology::nil()()); + delay.set(t, Time(-666)); // HINT: this should raise an decreasing time error + } + } + + // write topology via special ctor + { Topology(delay, target); } + + // init unused synapse weight to inf to provoke errors + for (np_t i=0; i<maxNeurons; i++) { + while (currentSynapse[i] < maxSynapsesPerNeuron) { + setWeight(i * maxSynapsesPerNeuron + currentSynapse[i], + std::numeric_limits<Weight::type>::infinity()); + currentSynapse[i]++; + } + } +} + +void writeWeightSums() { + Topology t; + PropertyInstance<Weight, true> weights; + for (auto neuron : Ptr<Global>().childs()) { + SumWeight::type weightSum = 0; + for (auto synapse : neuron.childs()) { + Weight::type weight = weights.data.getValue(Time{0}, synapse()); + if (weight > 0 and weight < std::numeric_limits<Weight::type>::infinity()) + weightSum += weight; + } + PLA_Set<TargetSumWeight> s1{Time{0}, neuron, weightSum}; + PLA_Set<SumWeight> s2{Time{0}, neuron, weightSum}; + pc.call(s1); pc.call(s2); + } +} + +int main() { + init(); + read(); + addPseudo(); + writeTopology(); + writeWeightSums(); +} diff --git a/core/discrete_property.hpp b/core/discrete_property.hpp new file mode 100644 index 0000000..741049f --- /dev/null +++ b/core/discrete_property.hpp @@ -0,0 +1,39 @@ +#ifndef iNnFGNqFLlBaWFgGGH0sLM3mh40 +#define iNnFGNqFLlBaWFgGGH0sLM3mh40 + +#include "pointers.hpp" +#include "string_helpers.hpp" +#include "template_helpers.hpp" + +/// Macros + +#define GEN_DP(Quant, nameT, nameS, ValueType) \ +struct nameT { \ + typedef ValueType type; \ + typedef Quant quant; \ + typedef Quant::instance_ptr_t instance_ptr_t; \ + static const uint32_t size = sizeof(ValueType); \ + static const char* const name; \ +}; \ + \ +const char* const nameT::name = nameS; + +#define GEN_DP_DELAY(DQuant, CQuant, Rule) \ +template<> \ +struct DiscreteProperty_ComputeDelay<DQuant, CQuant> { \ + template<typename PC, typename Context, typename Transaction> \ + inline static Time eval(PC &pc, Context context, \ + Transaction transaction, Time t) { \ + return Rule; \ + } \ +}; + +/// default impls + +template<typename DQuant, typename CQuant> +struct DiscreteProperty_ComputeDelay { + template<typename PC, typename Context, typename Transaction> + inline static Time eval(PC &, Context, Transaction, Time) DO_NOT_CALL +}; + +#endif // iNnFGNqFLlBaWFgGGH0sLM3mh40 diff --git a/core/ephermal_checkpoint.hpp b/core/ephermal_checkpoint.hpp new file mode 100644 index 0000000..1ca59e4 --- /dev/null +++ b/core/ephermal_checkpoint.hpp @@ -0,0 +1,83 @@ +// like Checkpoint, but memory-backed instead of file-backed and only +// storing the last time/value-paiir -> NO ACTUAL CHECKPOINTS +#ifndef kR1Z42gIzRShmbO3sKvSk0HIEo +#define kR1Z42gIzRShmbO3sKvSk0HIEo + +#include <boost/static_assert.hpp> + +#include "array.hpp" +#include "string_helpers.hpp" +#include "simlimits.hpp" +#include "template_helpers.hpp" +#include "time.hpp" + +template<typename T, uint32_t size> +class EphermalCheckpoint { +public: + typedef uint32_t ptr_t; + + EphermalCheckpoint(string name, const T initialValue); + + // read access + inline Time getTime (const Time t, const ptr_t i); + inline T getValue(const Time t, const ptr_t i); + inline Time getTime (const Time t); // only with size == 1 + inline T getValue(const Time t); // only with size == 1 + + // write access + inline void set(const Time t, const ptr_t i, const T val); + inline void set(const Time t, const T val); // only with size == 1 + + void sync() const {}; + + // memory backed data; they store for each element the ... + Array<T, size> content; // ... last known content + Array<Time, size> times; // ... associated time + +private: + EphermalCheckpoint(); +}; + +template<typename T, uint32_t size> +inline void EphermalCheckpoint<T, size>::set(const Time t, const ptr_t i, const T val) { + times.set(i, t); + content.set(i, val); +} + +template<typename T, uint32_t size> +inline void EphermalCheckpoint<T, size>::set(const Time t, const T val) { + BOOST_STATIC_ASSERT(size == 1); + setValue(t, 0, val); +} + +template<typename T, uint32_t size> +inline Time EphermalCheckpoint<T, size>::getTime(const Time t, const ptr_t i) { + return times.get(i); +} + +template<typename T, uint32_t size> +inline Time EphermalCheckpoint<T, size>::getTime(const Time t) { + BOOST_STATIC_ASSERT(size == 1); + return getTime(t, 0); +} + +template<typename T, uint32_t size> +inline T EphermalCheckpoint<T, size>::getValue(const Time t, const ptr_t i) { + return content.get(i); +} + +template<typename T, uint32_t size> +inline T EphermalCheckpoint<T, size>::getValue(const Time t) { + BOOST_STATIC_ASSERT(size == 1); + return getValue(t, 0); +} + +template<typename T, uint32_t size> +EphermalCheckpoint<T, size>::EphermalCheckpoint(string name, const T initialValue) { + for (ptr_t i=0; i<size; i++) { + content.set(i, initialValue); + times.set(i, 0); + } +} + +#endif // kR1Z42gIzRShmbO3sKvSk0HIEo diff --git a/core/event.hpp b/core/event.hpp new file mode 100644 index 0000000..0044c15 --- /dev/null +++ b/core/event.hpp @@ -0,0 +1,107 @@ +#ifndef lolpAJAYZev7fEXugmdiNlqUhWA +#define lolpAJAYZev7fEXugmdiNlqUhWA + +#include <boost/tuple/tuple.hpp> + +#include "pointers.hpp" +#include "index_global.hpp" +#include "index_spike.hpp" +#include "template_helpers.hpp" +#include "topology.hpp" + +template<typename UnknownType> +struct Event { + DO_NOT_INSTANCE(UnknownType); +}; + +template<> +struct Event<GlobalMsg> { + typedef GlobalMsg quant_t; + typedef Ptr<GlobalMsg> msg_ptr_t; + typedef Ptr<Global> ptr_t; + + // regular ctors + Event(msg_ptr_t inst) : inst(inst) {} + Event(Time ignored1, ptr_t ignored2, msg_ptr_t inst) : inst(inst) {} + + // ctor for illegal event sources + GEN_ANYCAST_CTOR_3(Event) : inst(0) { DO_NOT_CALL } + + inline ptr_t dst(Topology &ignored) { return ptr_t(); } + inline msg_ptr_t instance() { return inst; } + + msg_ptr_t inst; +}; + +template<> +struct Event<RandomSpike> { + typedef RandomSpike quant_t; + typedef Ptr<RandomSpike> msg_ptr_t; + typedef Ptr<Neuron> ptr_t; + + Event(Time, ptr_t neuron, msg_ptr_t inst) : neuron(neuron), inst(inst) {} + GEN_ANYCAST_CTOR_3(Event) : neuron(0), inst(0) { DO_NOT_CALL } + + inline ptr_t dst(Topology &) { return neuron; } + inline msg_ptr_t instance() { return inst; } + + ptr_t neuron; + msg_ptr_t inst; +}; + +template<> +struct Event<Spike> { + typedef Spike quant_t; + typedef Ptr<Neuron> neuron_ptr_t; + typedef Ptr<Spike> spike_ptr_t; + typedef uint16_t offset_t; + + // legal ctors + Event(Time baseTime, neuron_ptr_t src, spike_ptr_t spike) + : baseTime(baseTime), + src(src), + offset(0), + spike(spike) + {} + Event(Time baseTime, neuron_ptr_t src, spike_ptr_t spike, offset_t offset) + : baseTime(baseTime), + src(src), + offset(offset), + spike(spike) + {} + + // ctor for illegal event sources + GEN_ANYCAST_CTOR_3(Event) : baseTime(0),src(0),offset(0),spike(0) { DO_NOT_CALL } + // Event(Time baseTime, neuron_ptr_t src, uint16_t offset) + // : baseTime(baseTime), src(src), offset(offset) {} + + inline Ptr<Synapse> dst(Topology &topology) { + return topology.synapse(src, offset); + } + inline spike_ptr_t instance() { return spike; } + + Time baseTime; // time of outgoing spike; add topology.time(src, + // offset) to get spike arrival time + neuron_ptr_t src; + offset_t offset; // HINT: offset in topolgy, _not_ a synapse offset! + spike_ptr_t spike; +}; + +template<> +struct Event<SpikeArrival> { + typedef SpikeArrival quant_t; + + Event(Ptr<Synapse> dst, Ptr<SpikeArrival> sa) + : sdst(dst), sa(sa) {} + inline Ptr<Neuron> dst(Topology &ignored) { return dst(); } + inline Ptr<Neuron> dst() { return sdst.extractNeuron(); } + // HACK for DelieverContext ... what a shame + inline boost::tuple<Ptr<SpikeArrival>, Ptr<Synapse>> instance() { + return boost::make_tuple(sa, sdst); + } + + Ptr<Synapse> sdst; + Ptr<SpikeArrival> sa; +}; + +#endif // lolpAJAYZev7fEXugmdiNlqUhWA diff --git a/core/everything_else.hpp b/core/everything_else.hpp new file mode 100644 index 0000000..8b18cd5 --- /dev/null +++ b/core/everything_else.hpp @@ -0,0 +1,14 @@ +#ifndef oaRGw4aWx0YufhAmmpm1h6BILQU +#define oaRGw4aWx0YufhAmmpm1h6BILQU + +#include <assert.h> +#include <unistd.h> + +void assertSimDir() { + assert(access("topology_initialized", F_OK) == 0); +} + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +#endif // oaRGw4aWx0YufhAmmpm1h6BILQU diff --git a/core/evolve.hpp b/core/evolve.hpp new file mode 100644 index 0000000..628c170 --- /dev/null +++ b/core/evolve.hpp @@ -0,0 +1,35 @@ +#ifndef X66AJaG3Qy6m9CIBTEKhiCYWag0 +#define X66AJaG3Qy6m9CIBTEKhiCYWag0 + +#include "pla_evolve.hpp" + + + +namespace EvolveImpl { + + template<typename Quant> + struct Evolve { + template<class PC> + void operator() (PC &pc, const Time ct, const Ptr<Quant> id) { + typedef typename QuantChild<Quant>::type Child; + // first evolve childs + for (Ptr<Child> i : id.childs()) + Evolve<Child>()(pc, ct, i); + // then self + pc.cast(PLA_Evolve<Quant>{ContinuousContext<Quant>{id}, ct}); + } + }; + + template<> + struct Evolve<Synapse> { + template<class PC> + void operator() (PC &pc, const Time ct, const Ptr<Synapse> id) { + // no childs -> evolve only self + pc.cast(PLA_Evolve<Synapse>{ContinuousContext<Synapse>{id}, ct}); + } + }; +} + +using EvolveImpl::Evolve; + +#endif // X66AJaG3Qy6m9CIBTEKhiCYWag0 diff --git a/core/filter.hpp b/core/filter.hpp new file mode 100644 index 0000000..89ebbf6 --- /dev/null +++ b/core/filter.hpp @@ -0,0 +1,379 @@ +#ifndef YFUuCq9eKDuTmNenHAFfkJG3V +#define YFUuCq9eKDuTmNenHAFfkJG3V + +/* a Filter is used soley within MultiQueue. It has to have the same + interface as a Heap<PriorityQueue>. MultiQueue uses Heap, as well + as PriorityQueue features. To simulate this, a filter overloads + operator(Time) to return itself and implements all required methods + of Heap and PriorityQueue in one class */ + +#include <queue> +#include <map> +#include <set> +#include <list> +#include <boost/type_traits/is_same.hpp> +#include <boost/tuple/tuple.hpp> +//#include <boost/tuple/tuple_comparison.hpp> + +#include "index_global.hpp" +#include "index_spike.hpp" +#include "index_randomspike.hpp" +#include "index_spike_arrival.hpp" +#include "time.hpp" +#include "topology.hpp" +#include "id_list.hpp" + +#include "model.hpp" // for QuantDestinationQuant + +namespace FilterImpl { + +using namespace boost; + +// "forward decl" to make iter comparable +typedef Ptr<Synapse>::offset_t FSA_offset_t; +typedef std::list<tuple<Time, // 0. delay + Ptr<Synapse>, // 1. dst + FSA_offset_t // 2. offset in topo table + >> FSA_Recv_List; +typedef std::map<Ptr<Neuron>, FSA_Recv_List> FSA_recv_t; + +// an operator only to be used for std::priority_queue, which returns +// the largest element, not the smallest +template<typename T> +struct queue_greater_equal { + bool operator() (const T &a, const T &b) { + return a.get<0>() > b.get<0>() + || (a.get<0>() == b.get<0>() + && queue_greater_equal<typename T::tail_type>()(a.get_tail(), + b.get_tail())); + } +}; + +#define MKCMP(T) \ + template<> \ + struct queue_greater_equal<T> { \ + bool operator() (const T&, const T&) { \ + return true; \ + } \ + } +MKCMP(tuples::null_type); +MKCMP(FSA_recv_t::mapped_type::iterator); +typedef boost::tuples::cons<std::_List_iterator<boost::tuples::tuple<Time, Ptr<Synapse>, unsigned char> >, boost::tuples::null_type> ttttt; +MKCMP(ttttt); +#undef MKCMP + +template<typename Quant> struct Filter; + +template< + typename Quant, + typename QueuePayload, + typename Queue = std::priority_queue<QueuePayload, + std::vector<QueuePayload>, + queue_greater_equal<QueuePayload>>> +struct CommonFilter { + typedef QueuePayload queue_payload_t; + typedef Queue queue_t; + typedef IdList<Ptr<Neuron>::ptr_t> id_list_t; + + explicit CommonFilter(id_list_t &ids, const Time time) + : currentTime(time), + ids(ids), + queue(), + index() + {} + + Filter<Quant> & operator() (Time t) { + // helper function to emulate Heap<PriorityQueue> behaviour + assert(t >= currentTime); + currentTime = t; + return *static_cast<Filter<Quant>*>(this); + } + + Time minVal() const { + assert(!isEmpty()); + return queue.top().get<0>(); // by convention first element of + // QueuePayload is Time + } + + bool isEmpty() const { + return queue.empty(); + } + + Time currentTime; + id_list_t &ids; + Queue queue; + Index<Quant> index; + + struct Payload { + Payload() : src(0), dst(0) {} // fake init + + Ptr<Quant> src; + Ptr<typename QuantDestinationQuant<Quant>::type> dst; + } payload; + + const Payload & minPayload() const { + return payload; + } + +}; + +template<> +struct Filter<GlobalMsg> + : CommonFilter<GlobalMsg, + tuple<Time, Ptr<GlobalMsg>::ptr_t>> +{ + Filter(Time time) + : CommonFilter(id_list, time), + cur(0), // later + last(index.last()) + { + // late init of CommonFilter + payload.dst = Ptr<Global>(); + + // look for first relevant checkpoint + Time tMin(std::min(currentTime, index.maxEventTime.timeLimit)); + for (; tMin > 0 && index.maxEventTime.getTime(tMin) >= currentTime; + tMin -= index.maxEventTime.interval()); + cur = Ptr<GlobalMsg>(index.first(tMin)); + + // init queue + if (cur() != index.nil()) + refillQueue(); + } + + void removeMin() { + assert(currentTime >= minVal()); // assure currentTime is updated + queue.pop(); + refillQueue(); + if (!isEmpty()) + payload.src = Ptr<GlobalMsg>(queue.top().get<1>()); + } + + void refillQueue() { + // add all events until the next event to be inserted is created + // after the queues min event arrives, but at least until currentTime + Time until = queue.empty() ? currentTime : minVal(); + for (; cur() <= last() && index.time(cur()) <= until; ++cur) { + // discard messages for the past + if (index.eventTime(cur()) >= currentTime) { + queue.push(queue_payload_t(index.eventTime(cur()), cur())); + until = minVal(); + } + } + // add at most one message iff. required to make progress + if (queue.empty() && cur() <= last() && index.time(cur()) > until) { + queue.push(queue_payload_t(index.eventTime(cur()), cur())); + ++cur; + } + } + + Ptr<GlobalMsg> cur, last; + static id_list_t id_list; +}; +Filter<GlobalMsg>::id_list_t Filter<GlobalMsg>::id_list((char*) "0"); + +template<> +struct Filter<RandomSpike> + : CommonFilter<RandomSpike, + tuple<Time, Ptr<RandomSpike>::ptr_t>> { + Filter(id_list_t &ids, const Time time) + : CommonFilter(ids, time) + { + // find first spike for each given neuron, following spikes are + // simple (using index.next) + for (auto neuron : ids) { + // find first event via first relevent checkpoints + Time tMin(std::min(currentTime, index.maxEventTime.timeLimit)); + for (; tMin > 0 && index.maxEventTime.getTime(tMin, neuron) >= currentTime; + tMin -= index.maxEventTime.interval()); + auto next = Ptr<RandomSpike>(index.first(tMin, Ptr<Neuron>(neuron)))(); + // skip events before ct + while (next != index.nil() && index.time(next) < currentTime) + next = index.next(next); + // add event (if) + if (next != index.nil()) + queue.push(queue_payload_t(index.eventTime(next), next)); + } + updatePayload(); + } + + void removeMin() { + assert(!isEmpty()); + queue.pop(); + auto next = index.next(payload.src()); + if (next != index.nil()) + queue.push(queue_payload_t(index.eventTime(next), next)); + updatePayload(); + } + + void updatePayload() { + if (!isEmpty()) { + payload.src = Ptr<RandomSpike>(queue.top().get<1>()); + payload.dst = Ptr<Neuron>(index.src(payload.src())); + } + } + +}; + +template<> +struct Filter<SpikeArrival> + : CommonFilter<SpikeArrival, + boost::tuple<Time, Ptr<Spike>, FSA_recv_t::mapped_type::iterator>> { + typedef FSA_offset_t offset_t; + + Filter(id_list_t &ids, const Time time) + : CommonFilter(ids, time) { + // extract topology info + Topology topo; + std::set<Ptr<Neuron>> used; + std::map<Ptr<Neuron>, Time> maxDelay; + for (auto neuron : Ptr<Neuron>::all()) { + for (offset_t off(0); off < maxSynapsesPerNeuron; off++) { + Ptr<Synapse> syn(topo.synapse(neuron, off)); + if (syn == topo.nil()) break; + if (ids.count(syn.extractNeuron()())) { + Time delay = topo.time(neuron, off); + recv[neuron].push_back(make_tuple(delay, syn, off)); + maxDelay[neuron] = std::max(delay, maxDelay[neuron]); + used.insert(neuron); + } + } + } + + // insert pending spikes + for (auto neuron : used) { + Ptr<Spike> spike(index.first(std::min(currentTime - maxDelay[neuron], + index.prevCache.timeLimit), + neuron)); + if (spike() != index.nil()) { + Time baseTime = index.time(spike()); + queue.push(queue_payload_t(baseTime + recv[neuron].front().get<0>(), + spike, + recv[neuron].begin())); + } + } + updatePayload(); + + // remove all arrivals of spikes prior to time + while (!isEmpty() && minVal() < time) + removeMin(); + } + + void updatePayload() { + if (!isEmpty()) { + const queue_payload_t &top(queue.top()); + const auto &recvElem(*(top.get<2>())); + payload.dst = recvElem.get<1>().extractNeuron(); + payload.src.get<0>() = Ptr<SpikeArrival>(top.get<1>(), recvElem.get<2>()); + payload.src.get<1>() = recvElem.get<1>(); + } + } + + void removeMin() { + assert(!isEmpty()); + // reinsert next spike from current min (if it exists) + const queue_payload_t top = queue.top(); // copy, to allow popping early + Ptr<Neuron> neuron(index.src(top.get<1>()())); + FSA_Recv_List &recvList = recv[neuron]; + auto re_iter = top.get<2>(); + + // remove old element + queue.pop(); + + // was this the first element in the rev list? + if (re_iter == recvList.begin()) { + // insert next spike from same neuron + Ptr<Spike> next(index.next(top.get<1>()())); + if (next() != index.nil()) { + auto re_iter(recvList.begin()); + queue_payload_t payload(index.time(next()) + re_iter->get<0>(), + next, + re_iter); + queue.push(payload); + } + } + + // is there a next element in the recv list? + if (++re_iter != recvList.end()) { + // add SA of remaining list + Time baseTime = index.time(top.get<1>()()); + queue_payload_t payload(baseTime + re_iter->get<0>(), + top.get<1>(), + re_iter); + queue.push(payload); + } + + // update payload from new min + updatePayload(); + } + + FSA_recv_t recv; + Index<Spike> index; + + struct Payload { + Payload() : src(Ptr<SpikeArrival>(0), Ptr<Synapse>(0)), dst(0) {} // fake init + + boost::tuple<Ptr<SpikeArrival>, Ptr<Synapse> > src; + Ptr<Neuron> dst; + } payload; + + Payload & minPayload() { + return payload; + } +}; + +template<> +struct Filter<Spike> : Filter<SpikeArrival> { + typedef Ptr<Synapse>::offset_t offset_t; + + Filter(id_list_t &res, const Time time) + : Filter<SpikeArrival>(res, time) { + updatePayload(); + } + + void removeMin() { + Filter<SpikeArrival>::removeMin(); + updatePayload(); + } + + void updatePayload() { + // we copy from parent unconditionally, assuming valid values + // remain in payload even when queue is empty + payload.src = Filter<SpikeArrival>::payload.src.get<0>().extractSpike(); + payload.dst = Filter<SpikeArrival>::payload.src.get<1>(); + } + + Filter<Spike> & operator() (Time t) { + // helper function to emulate Heap<PriorityQueue> behaviour + if (t >= currentTime) + assert(t >= currentTime); + currentTime = t; + return *this; + } + + typedef CommonFilter<Spike, int>::Payload Payload; // ignore "int" + Payload payload; + + Payload & minPayload() { + return payload; + } +}; + +} // namespace FilterImpl + +using FilterImpl::Filter; + +/// arguments for MultiQueue + +template<typename Quant> +struct FilterPayload : Filter<Quant>::Payload { + typedef Quant quant_type; +}; + +template<typename Val, typename Payload> +struct FilterContainer { + BOOST_STATIC_ASSERT((boost::is_same<Val, Time>::value)); + typedef Filter<typename Payload::quant_type> type; +}; + +#endif // YFUuCq9eKDuTmNenHAFfkJG3V diff --git a/core/heap.hpp b/core/heap.hpp new file mode 100644 index 0000000..40c0a55 --- /dev/null +++ b/core/heap.hpp @@ -0,0 +1,157 @@ +#ifndef lxZLmAQ0qUwCFC9Ehehzy92Ldko +#define lxZLmAQ0qUwCFC9Ehehzy92Ldko + +/* warn: heap is sloppy with the write barrier of it's content + mmap()ed data store; the stated barrier is always equal or less + than the actually used amount of space; it is only correct after + sync() calls (which happen implicitely at interval turnover */ + +#include <inttypes.h> +#include <boost/tuple/tuple.hpp> + +#include "checkpoint.hpp" +#include "everything_else.hpp" +#include "scalar.hpp" +#include "string_helpers.hpp" +#include "time.hpp" +#include "vector.hpp" + +template<typename Payload> +struct Heap { + // constructor requires a 3-tuple: + // 1. name (used for file name creation) + // 2. average size of contained object + // 3. default payload value (if a new heap is created) + Heap(const string name, const uint64_t avgLength); + Heap(); + + // read & write access + inline Payload & operator() (Time t) __attribute__ ((pure)); + void sync(); + + // intern methods + void addCheckpoints(Time t); + + // intern data + typedef Vector<char>::ptr_t ptr_t; + ptr_t interval; // current interval (number) + Time timeLimit; // current intervals time limit (inclusive) + Time currentTime; // current intervals time (same as times(interval) + // but allows compiler to perform better + // optimizations) + + + // file backings + Vector<Time> times; // times of the payload + Vector<char> content; // the payloads (in temporally ascending order) + Vector<ptr_t> index; + +private: + inline Payload & get(ptr_t id) __attribute__ ((pure)); + void init(); +}; + +template<typename Payload> +Heap<Payload>::Heap(const string name, const uint64_t avgLength) : + times( "heap_times_" + name, maxCheckpoints), + content("heap_content_" + name, maxCheckpoints * avgLength), + index( "heap_index_" + name, maxCheckpoints) +{ + init(); +} + +template<typename Payload> +Heap<Payload>::Heap() : + times( "heap_times_" + std::string(Payload::payload_t::quant_t::name), + maxCheckpoints), + content("heap_content_" + std::string(Payload::payload_t::quant_t::name), + maxCheckpoints * AverageQueueSize<typename Payload::payload_t::quant_t>::value * Payload::elementSize), + index( "heap_index_" + std::string(Payload::payload_t::quant_t::name), + maxCheckpoints) +{ + init(); +} + +template<typename Payload> +void Heap<Payload>::init() { + // do we create the heap (and backing files)? + if (index.barrierWrite() == 0) { + // yes -> extra initialisation + interval = 0; + timeLimit = 0; + currentTime = 0; + times(0) = 0; + times.barrierWrite() = 1; + // new(&(content(0))) Payload(boost::get<2>(arg)); // init with given arg + new(&(content(0))) Payload(); // init with empty ctor + content.barrierWrite() = get(0).getSize(); + index(0) = 0; + index.barrierWrite() = 1; + }else{ + // no -> restore previous setup + interval = index.barrierWrite() - 1; + timeLimit = Time(checkpointInterval) * interval; // hint interval #0 ends with time 0 + currentTime = times(interval); + } +} + + +template<typename Payload> +inline Payload& Heap<Payload>::operator() (Time t) { + if (t > currentTime) { + if (unlikely(t > timeLimit)) addCheckpoints(t); + currentTime = t; + times(interval) = t; + } + return get(interval); +} + +template<typename Payload> +void Heap<Payload>::sync() { + // update length of the current heap + // WARN: for synchronization these values actually should never + // shrink, but the embedded container might violate this + content.barrierWrite() = index(interval) + get(interval).getSize(); + + // sync all mmap containers + content.sync(); + times.sync(); + index.sync(); +} + +// see checkpoint.hpp for a more commented analogon of this method +template<typename Payload> +void Heap<Payload>::addCheckpoints(Time t) { + assert(index.barrierWrite() == times.barrierWrite()); // is a pre- and postcondition + + while (t > timeLimit) { + content.barrierWrite() = index(interval) + get(interval).getSize(); // update current payload size + interval++; + index(interval) = content.barrierWrite(); + times(interval) = times(interval - 1); + + // invoke copy constructor via placement new to copy the payload + new(&get(interval)) Payload(get(interval-1)); + + index.barrierWrite()++; + times.barrierWrite()++; + times.barrierRead() = times.barrierWrite() - 1; + index.barrierRead() = index.barrierWrite() - 1; + content.barrierRead() = content.barrierWrite(); + + timeLimit = checkpointInterval * interval; + } + + currentTime = t; + times(interval) = t; + + assert(index.barrierWrite() == times.barrierWrite()); + + content.advise(0, content.barrierRead(), MADV_DONTNEED); +} + +template<typename Payload> +inline Payload& Heap<Payload>::get (ptr_t id) { + return *((Payload*) (&(content(index(id))))); +} +#endif // lxZLmAQ0qUwCFC9Ehehzy92Ldko diff --git a/core/id_list.hpp b/core/id_list.hpp new file mode 100644 index 0000000..99ff5a2 --- /dev/null +++ b/core/id_list.hpp @@ -0,0 +1,47 @@ +#ifndef iUCKfY0lAetXcXu8Ykh6wMB3KhQ +#define iUCKfY0lAetXcXu8Ykh6wMB3KhQ + +#include <set> + +#include <boost/xpressive/xpressive_static.hpp> +#include <boost/xpressive/regex_actions.hpp> + +namespace IdListImpl { + +using namespace boost::xpressive; + +struct push_impl { + typedef void result_type; + template<typename Set, class Value> + void operator()(Set &s, Value const &from, Value const &until) const { + for (Value i = from; i <= until; ++i) + s.insert(i); + } +}; +function<push_impl>::type const pushRange = {{}}; + + +template<typename ID> +struct IdList : std::set<ID> { + explicit IdList(char *s = (char*) "") { + cregex number = (s1= +_d) + [insert(boost::xpressive::ref(*this), as<ID>(s1))]; + cregex range = ((s1= +_d) >> as_xpr('-') >> (s2= +_d)) + [pushRange(boost::xpressive::ref(*this), as<ID>(s1), as<ID>(s2))]; + cregex list = *((range | number) >> *( as_xpr(',') >> (range | number))); + assert(regex_match(s, list)); + } + + template<typename OtherID> + IdList(IdList<OtherID> &src) { + for (auto i : src) { + assert(i <= std::numeric_limits<ID>::max()); + this->insert(i); + } + } +}; + +} // IdListImpl + +using IdListImpl::IdList; +#endif // iUCKfY0lAetXcXu8Ykh6wMB3KhQ diff --git a/core/index.hpp b/core/index.hpp new file mode 100644 index 0000000..d110586 --- /dev/null +++ b/core/index.hpp @@ -0,0 +1,148 @@ +#ifndef hVYFXl0fhRtHkuIHnrxmZOhDCeI +#define hVYFXl0fhRtHkuIHnrxmZOhDCeI + +#include <assert.h> + +#include <string> + +#include "time.hpp" +#include "checkpoint.hpp" +#include "simlimits.hpp" +#include "template_helpers.hpp" +#include "vector.hpp" + +// TODO: update r/w-barriers in vector + +using std::string; + +template<typename Quant> +struct Index; + +template<typename Ptr> +struct CommonIndex { + CommonIndex() = delete; + CommonIndex(const string name, Ptr max); + + // type and const defs + typedef Ptr ptr_t; + static constexpr ptr_t nil() { + BOOST_STATIC_ASSERT(( (ptr_t) -1 > 0 )); + return -1; + } + + // read operations + // HINT: time refers to creation time, not delievery time; the + // later is handled by Filter<> + inline ptr_t last(); // returns the latest known event + ptr_t last(Time t); // ... before (or at) time t + inline ptr_t first(); // ... after (or at) ... + ptr_t first(Time t); + + ptr_t binarySearch(Time t); + + virtual void sync() { + time.barrierRead() = time.barrierWrite(); + time.sync(); + } + + // vector storing timing + Vector<Time> time; + + // writing + inline ptr_t add(Time t); +}; + +template<typename Ptr> +inline typename CommonIndex<Ptr>::ptr_t CommonIndex<Ptr>::last() { + // get ptr from any barrier (all should be the same!) + ptr_t id = time.barrierWrite.get(); + if (id==0) + return nil(); + else + return id - 1; +} + +template<typename Ptr> +inline typename CommonIndex<Ptr>::ptr_t CommonIndex<Ptr>::first() { + // get ptr from any barrier (all should be the same!) + ptr_t id = time.barrierWrite.get(); + if (id==0) + return nil(); + else + return 0; +} + +template<typename Ptr> +CommonIndex<Ptr>::CommonIndex(const string name, ptr_t max) : + time(name, max) +{} + +template<typename Ptr> +typename CommonIndex<Ptr>::ptr_t CommonIndex<Ptr>::last(Time t) { + ptr_t cur = binarySearch(t); + if ((cur == nil()) or (time(cur) > t)) + return nil(); + + // there may be several elements with time = t; go to the last one + while ((cur < last()) && (time(cur + 1) == t)) + cur++; + + return cur; +} + +template<typename Ptr> +typename CommonIndex<Ptr>::ptr_t CommonIndex<Ptr>::first(Time t) { + ptr_t cur = binarySearch(t); + + if (cur == nil()) + return nil(); + if ((time(cur) < t) && (cur < last())) + cur = cur + 1; + if (time(cur) < t) + return nil(); + + + // there may be several elements with time = t; go to the first one + while ((cur > first()) && (time(cur - 1) == t)) + cur--; + return cur; +} + +template<typename Ptr> +typename CommonIndex<Ptr>::ptr_t CommonIndex<Ptr>::binarySearch(Time t) { + // TODO (optimize): use a constant frequency approximation to + // initialize min, max and pivot (w/ proper fallback) + + // get the limits of the heap + ptr_t max = last(); + if (max == nil()) return nil(); // no elements, yet + ptr_t min = first(); + + // check if t \in [min, max] + if (t > time(max)) return max; + if (t < time(min)) return min; + + // binary search in the heap + // invariant: min <= target <= max + while ((time(min) != time(max))) { + ptr_t pivot = (min + max + 1) / 2; + if (t >= time(pivot)) { + min = pivot; + }else{ + max = pivot - 1; + } + } + + return max; // min would be ok, too +} + +template<typename Ptr> +inline typename CommonIndex<Ptr>::ptr_t CommonIndex<Ptr>::add(Time aTime) { + ptr_t id = last() + 1; // get position to write + time.set(id, aTime); // add new record + time.barrierWrite.set(id + 1); // increase write barrier + + return id; + } + +#endif // hVYFXl0fhRtHkuIHnrxmZOhDCeI diff --git a/core/index_global.hpp b/core/index_global.hpp new file mode 100644 index 0000000..138c167 --- /dev/null +++ b/core/index_global.hpp @@ -0,0 +1,48 @@ +#ifndef Hiq32h5eXlWfeiJLSv6W9vFXUmc +#define Hiq32h5eXlWfeiJLSv6W9vFXUmc + +#include <boost/tuple/tuple.hpp> + +#include "simlimits.hpp" +#include "index.hpp" +#include "quant_types.hpp" + +template <> +struct Index<GlobalMsg> : CommonIndex<Ptr<GlobalMsg>::ptr_t> { + typedef boost::tuple<> con_arg_t; + explicit Index(con_arg_t = con_arg_t()) + : CommonIndex("index_globalmsg_time", maxSpikes), + eventTime("index_globalmsg_eventtime", maxSpikes), + maxEventTime("index_globalmsg_maxeventtime", Time::beforeAll()) + {} + + // writing + template<typename SrcQuant> + inline ptr_t add(Time time, Time eventTime, SrcQuant src); + + virtual void sync() { + CommonIndex::sync(); + eventTime.barrierRead() = eventTime.barrierWrite() = time.barrierWrite(); + eventTime.sync(); + maxEventTime.sync(); + } + + // data + Vector<Time> eventTime; + Checkpoint<Time, 1> maxEventTime; +}; + +template<typename IllegalImpl> +inline Index<GlobalMsg>::ptr_t Index<GlobalMsg>::add(Time, Time, IllegalImpl) { + DO_NOT_CALL; +} + +template<> +inline Index<GlobalMsg>::ptr_t Index<GlobalMsg>::add(Time t_in, Time t_out, Global::instance_ptr_t) { + ptr_t ptr(CommonIndex::add(t_in)); + eventTime.set(ptr, t_out); + maxEventTime.set(t_in, std::max(t_out, maxEventTime.getValue(t_in))); + return ptr; +} + +#endif // Hiq32h5eXlWfeiJLSv6W9vFXUmc diff --git a/core/index_randomspike.hpp b/core/index_randomspike.hpp new file mode 100644 index 0000000..a40a99b --- /dev/null +++ b/core/index_randomspike.hpp @@ -0,0 +1,52 @@ +#ifndef zpkaBa96qijSBV6tlRldSMrZ3N0 +#define zpkaBa96qijSBV6tlRldSMrZ3N0 + +#include "index_spike_base.hpp" + +template<> +struct Index<RandomSpike> : CommonSpikeIndex<Ptr<RandomSpike>::ptr_t> { + typedef boost::tuple<> con_arg_t; + Index() : + CommonSpikeIndex("randomspike"), + eventTime("index_randomspike_eventtime", maxSpikes), + maxEventTime("index_randomspike_maxeventtime", Time::beforeAll()) + {} + Index(const con_arg_t &) : + CommonSpikeIndex("randomspike"), + eventTime("index_randomspike_eventtime", maxSpikes), + maxEventTime("index_randomspike_maxeventtime", Time::beforeAll()) + {} + + // using Index<SpikeBaseCase>::first; + + // write + template<typename SrcQuant> + inline ptr_t add(Time time, Time eventTime, SrcQuant src); + + virtual void sync() { + CommonSpikeIndex::sync(); + eventTime.barrierRead() = eventTime.barrierWrite() = time.barrierWrite(); + eventTime.sync(); + maxEventTime.sync(); + } + + // data + Vector<Time> eventTime; + Checkpoint<Time, maxNeurons> maxEventTime; +}; + +template<typename IllegalImpl> +Index<RandomSpike>::ptr_t Index<RandomSpike>:: +add(Time, Time, IllegalImpl) DO_NOT_CALL + +template<> +Index<RandomSpike>::ptr_t Index<RandomSpike>:: +add(Time t_in, Time t_out, neuron_ptr_t aSrc) { + ptr_t ptr(CommonSpikeIndex::add(t_in, t_out, aSrc)); + eventTime.set(ptr, t_out); + maxEventTime.set(t_in, aSrc(), + std::max(t_out, maxEventTime.getValue(t_in, aSrc()))); + return ptr; +} + +#endif // zpkaBa96qijSBV6tlRldSMrZ3N0 diff --git a/core/index_spike.hpp b/core/index_spike.hpp new file mode 100644 index 0000000..6ec49f1 --- /dev/null +++ b/core/index_spike.hpp @@ -0,0 +1,14 @@ +#ifndef oMw7speKIiOKW6S5XaDR2wH4rZM +#define oMw7speKIiOKW6S5XaDR2wH4rZM + +#include "index_spike_base.hpp" + +template<> +struct Index<Spike> : CommonSpikeIndex<Ptr<Spike>::ptr_t> { + typedef boost::tuple<> con_arg_t; + + Index() : CommonSpikeIndex("spike") {} + explicit Index(const con_arg_t &) : CommonSpikeIndex("spike") {} +}; + +#endif // oMw7speKIiOKW6S5XaDR2wH4rZM diff --git a/core/index_spike_arrival.hpp b/core/index_spike_arrival.hpp new file mode 100644 index 0000000..c5bff9f --- /dev/null +++ b/core/index_spike_arrival.hpp @@ -0,0 +1,32 @@ +#ifndef B0LYLjBmmWhE4fufCZdMp6k8weE +#define B0LYLjBmmWhE4fufCZdMp6k8weE + +#include <boost/static_assert.hpp> +#include <boost/tuple/tuple.hpp> + +#include "index_spike.hpp" +#include "simlimits.hpp" + +// fake class used to simplify sim_loop; look at Filter<SpikeArrival> +template<> +class Index<SpikeArrival> { +public: + typedef Ptr<SpikeArrival>::ptr_t ptr_t; + typedef Index<Spike>::neuron_ptr_t neuron_ptr_t; + typedef Ptr<Synapse>::offset_t local_synapse_ptr_t; + typedef boost::tuple<> con_arg_t; + + static constexpr ptr_t nil() { + BOOST_STATIC_ASSERT(( (ptr_t) -1 > 0 )); + return -1; + } + + // NOP ctor for TypeMap + Index(con_arg_t = con_arg_t()) {} + + // check for correct # of synapses + BOOST_STATIC_ASSERT(maxSynapsesPerNeuron <= 256); + STATIC_ASSERT_IS_POWER_OF_TWO(maxSynapsesPerNeuron); +}; + +#endif // B0LYLjBmmWhE4fufCZdMp6k8weE diff --git a/core/index_spike_base.hpp b/core/index_spike_base.hpp new file mode 100644 index 0000000..3ec08c3 --- /dev/null +++ b/core/index_spike_base.hpp @@ -0,0 +1,151 @@ +#ifndef x9xBGzwmK2Ew29hQ89VsJgDlY3M +#define x9xBGzwmK2Ew29hQ89VsJgDlY3M + +#include <boost/tuple/tuple.hpp> + +#include "checkpoint.hpp" +#include "index.hpp" +#include "quant_types.hpp" + +template<typename Ptr> +struct CommonSpikeIndex : CommonIndex<Ptr> { + // type- and constdefs + typedef Neuron::instance_ptr_t neuron_ptr_t; + + // lift stuff from CommonIndex + typedef typename CommonIndex<Ptr>::ptr_t ptr_t; + using CommonIndex<Ptr>::nil; + using CommonIndex<Ptr>::first; + using CommonIndex<Ptr>::last; + using CommonIndex<Ptr>::time; + // using CommonIndex<Ptr>::nil; + + CommonSpikeIndex(string qname); + + // read operations + ptr_t last(Time t, neuron_ptr_t n); // ... of neuron n + ptr_t first(Time t, neuron_ptr_t n); + + // write operations + template<typename SrcQuant> + inline ptr_t add(Time time, Time eventTime /* ignored */, SrcQuant src); + + virtual void sync(); + + // vectors storing topological information + Vector<neuron_ptr_t::ptr_t> src; + Vector<ptr_t> prev, next; // address of the previous and next spike + // of the same sender neuron + + // checkpoint to store the last known spike of every neuron (to + // implement adding a spike in a fast way) + Checkpoint<ptr_t, maxNeurons> prevCache; +}; + +template<typename Ptr> +CommonSpikeIndex<Ptr>::CommonSpikeIndex(string qname) : + CommonIndex<Ptr>("index_" + qname + "_time", maxSpikes), + src("index_" + qname + "_src", maxSpikes), + prev("index_" + qname + "_prev", maxSpikes), + next("index_" + qname + "_next", maxSpikes), + prevCache("index_" + qname + "_checkpoint_prev", this->nil()) +{ } + +template<typename Ptr> +typename CommonSpikeIndex<Ptr>::ptr_t CommonSpikeIndex<Ptr>::last(Time t, neuron_ptr_t n) { + // TODO (optimize): improve the alg to use prevCache as seed for + // proper min/max values + + // get the last element before (at) time t + ptr_t id = last(t); + if (id == nil()) return nil(); // no spikes at all + + // get the last known spike of neuron + Time lookup = fmin(prevCache.timeLimit(), t()); + ptr_t lst = prevCache.getValue(lookup, n()); + if (lst == nil()) return nil(); // n didn't fire at all + if (lst < id) return lst; // no l' with time(lst) < time(lst') <= t may exist + + // do a linear search until a spike of n is found + while (src(id) != n()) { + id--; + } + + return id; +} + +template<typename Ptr> +typename CommonSpikeIndex<Ptr>::ptr_t CommonSpikeIndex<Ptr>::first(Time t, neuron_ptr_t n) { + // TODO (optimize): improve the alg to use prevCache as seed for + // proper min/max values + + // get the first element after (at) time t + ptr_t id = first(t); + if (id == nil()) return nil(); // no spikes at all + + // get the first known spike of neuron + ptr_t lst; + Time lookup = (t + prevCache.interval())(); + do { + lookup = fmin(prevCache.timeLimit(), lookup()); + lst = prevCache.getValue(lookup, n()); + lookup += prevCache.interval(); + } while (lst == nil() && lookup < prevCache.timeLimit()); + if ((lst < id) || (lst == nil())) return nil(); // no spike of n after t exists + + // do a linear search until a spike of n is found + while (src(id) != n()) { + id++; + } + + return id; +} + +template<typename Ptr> template<typename AddPtr> +typename CommonSpikeIndex<Ptr>::ptr_t CommonSpikeIndex<Ptr>::add(Time aTime, Time, + AddPtr rawSrc) { + // cast to neuron ptr (only correct type for call) + if (!boost::is_same<AddPtr, neuron_ptr_t>::value) DO_NOT_CALL; + neuron_ptr_t &aSrc(FORCE_CONV(neuron_ptr_t, rawSrc)); + + // check if all barriers are equal + assert(time.barrierWrite() == src.barrierWrite()); + assert(time.barrierWrite() == prev.barrierWrite()); + assert(time.barrierWrite() == next.barrierWrite()); + + // get position to write + ptr_t id = last(); + if (last() == nil()) { + id = 0; + }else{ + id += 1; + } + + // add new record + time(id) = aTime; + src(id) = aSrc(); + prev(id) = prevCache.getValue(aTime, aSrc()); + if (prevCache.getValue(aTime, aSrc()) != nil()) + next(prevCache.getValue(aTime, aSrc())) = id; + next(id) = nil(); + prevCache.set(aTime, aSrc(), id); + + // increase all write barriers + time.barrierWrite() = id + 1; + src.barrierWrite() = id + 1; + prev.barrierWrite() = id + 1; + next.barrierWrite() = id + 1; + + return id; +} + +template<typename Ptr> +void CommonSpikeIndex<Ptr>::sync() { + CommonIndex<Ptr>::sync(); + src.sync(); + prev.sync(); + next.sync(); + prevCache.sync(); +} + +#endif // x9xBGzwmK2Ew29hQ89V diff --git a/core/list_synapses.cpp b/core/list_synapses.cpp new file mode 100644 index 0000000..6132b56 --- /dev/null +++ b/core/list_synapses.cpp @@ -0,0 +1,22 @@ +#include <iostream> + +#include "everything_else.hpp" +#include "id_list.hpp" +#include "topology.hpp" + +int main(int argc, char **argv) { + assert(argc == 3); + assertSimDir(); + { + Topology t; + IdList<Ptr<Neuron>::ptr_t> src(argv[1]), dst(argv[2]); + + for (auto i : src) { + int j=0; + Ptr<Synapse> s(-1); + while ((s = t.synapse(Ptr<Neuron>(i), j++)) != t.nil()) + if (dst.count(s.extractNeuron()())) + std::cout << s() << "\t" << i << " -> " << s.extractNeuron() << "\n"; + } + } +} diff --git a/core/mempool.hpp b/core/mempool.hpp new file mode 100644 index 0000000..a5ac07b --- /dev/null +++ b/core/mempool.hpp @@ -0,0 +1,156 @@ +#ifndef qEzyOXbTTD6KlhKDUsawdWmx6c0 +#define qEzyOXbTTD6KlhKDUsawdWmx6c0 + +#include <assert.h> +#include <fcntl.h> +#include <malloc.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string> + +#include <boost/utility.hpp> + +using std::string; + +struct MemPool : boost::noncopyable { + explicit MemPool(size_t size); // anonymous in-memory-pool + MemPool(size_t size, int fd); // mmap()ed memory pool + MemPool(size_t size, const string filename); // mmap()ed memory pool + MemPool(MemPool &&src); // move ctor + MemPool& operator=(MemPool&&); + MemPool& operator=(MemPool&) = delete; + MemPool& operator=(MemPool) = delete; + + ~MemPool(); + + inline void* operator() () const; // return memory region; TODO: pure attr + inline size_t getSize() const; + void sync() const; // call msync() + void advise(size_t start, size_t len, int adv); + +protected: + void *base; + size_t size; + bool isMMap; + + void doMMap(int fd, size_t size); + int openMMappableFile(const string filename, size_t size); + +private: + static char* global_offset; + MemPool() = delete; + MemPool(const MemPool&) = delete; +}; + +char* MemPool::global_offset = (char*) (void*) 0x2fffff000000; + +inline void* MemPool::operator() () const { + return base; +} + +inline size_t MemPool::getSize() const { + return size; +} + +MemPool::MemPool(size_t size) : size(size), isMMap(false) { + if (size == 0) { + base = NULL; + }else{ + base = malloc(size); + assert(base != NULL); + } +} + +MemPool::MemPool(size_t size, int fd) : size(size),isMMap(true) { + if (size == 0) { + base = NULL; + }else{ + doMMap(fd, size); + } +} + +MemPool::MemPool(size_t size, const string filename) : size(size), isMMap(true) { + if (size == 0) { + base = NULL; + }else{ + doMMap(openMMappableFile(filename, size), size); + } +} + +MemPool::MemPool(MemPool &&src) : base(src.base), size(src.size), isMMap(src.isMMap) { + // std::cout << "Mempool " << base << " moved via copy" << std::endl; + src.base = NULL; + src.size = 0; +} + +MemPool& MemPool::operator= (MemPool &&src) { + // std::cout << "Mempool " << base << " moved via assignment" << std::endl; + base = src.base; + size = src.size; + isMMap = src.isMMap; + src.base = NULL; + src.size = 0; + return *this; +} + +MemPool::~MemPool() { + // std::cout << "Mempool " << base << " terminated"; + if (size == 0 or base == NULL) { + // std::cout << " from move" << std::endl; + return; + }else{ + // std::cout << std::endl; + } + if (isMMap) { + sync(); + munmap(base, size); + }else{ + free(base); + } +} + +void MemPool::sync() const { + if (isMMap) + msync(base, size, MS_SYNC); +} + +void MemPool::advise(size_t start, size_t len, int adv) { + // adjust region to page boundary (defensive) + if (start & 4095) { + len -= 4096 - (start & 4095); + len &= ~4095; + start &= 4095; + start +=1; + } + // detect underflow and to large len + if (len < size) { + assert(madvise((char*) base + start, len, adv) == 0); + } +} + +void MemPool::doMMap(int fd, size_t size) { + base = (void*) mmap((void*) global_offset, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NONBLOCK, fd, 0); + assert(base != MAP_FAILED); + assert(base == (void*) global_offset); + global_offset += ((size / 4096 + 1) * 4096) + 1024 * 4096; +} + +int MemPool::openMMappableFile(const string filename, size_t size) { + int fd = open(filename.c_str(), O_RDWR | O_CREAT | O_LARGEFILE | O_NOATIME, 0644); + assert(fd != -1); + + // check if the file size is correct + struct stat st; + fstat(fd, &st); + if ((size_t) st.st_size < size) { + // write one byte at the end of the file to make it big enough for the later mmap call (mmap does not increase file size) + assert(lseek64(fd, size-1, SEEK_SET) != (off_t) -1); + char c(0); + assert(write(fd, &c, 1) == 1); + } + + return fd; +} + +#endif // qEzyOXbTTD6KlhKDUsawdWmx6c0 diff --git a/core/model.hpp b/core/model.hpp new file mode 100644 index 0000000..c95bf46 --- /dev/null +++ b/core/model.hpp @@ -0,0 +1,23 @@ +#include <math.h> + +#include <boost/mpl/bool.hpp> +#include <boost/mpl/list.hpp> +#include <boost/mpl/pair.hpp> + +#include "continuous_property.hpp" +#include "continuous_property_impl.hpp" +#include "discrete_property.hpp" +#include "property_composition.hpp" +#include "rng.hpp" +#include "trainer.hpp" + +#ifndef RASIMU_MODEL +#define RASIMU_MODEL + +/// the actual model +#include "properties.hpp" + +#include "trainer_impl.hpp" + +#endif + diff --git a/core/multi_queue.hpp b/core/multi_queue.hpp new file mode 100644 index 0000000..b77785b --- /dev/null +++ b/core/multi_queue.hpp @@ -0,0 +1,187 @@ +#ifndef JszL9wNT11XcdnV6wZR3NcSJB0 +#define JszL9wNT11XcdnV6wZR3NcSJB0 + +#include <assert.h> + +#include <boost/mpl/list.hpp> +#include <boost/mpl/pair.hpp> +#include <boost/mpl/size.hpp> +#include <boost/mpl/if.hpp> +#include <boost/mpl/front.hpp> +#include <boost/mpl/empty.hpp> +#include <boost/mpl/push_front.hpp> +#include <boost/mpl/pop_front.hpp> +#include <boost/mpl/back.hpp> +#include <boost/mpl/transform.hpp> +#include <boost/static_assert.hpp> +#include <boost/type_traits/is_same.hpp> +#include <boost/tuple/tuple.hpp> + +#include "heap.hpp" +#include "priority_queue.hpp" +#include "quant_types.hpp" +#include "template_helpers.hpp" +#include "type_set.hpp" + +namespace MultiQueueImpl { + + using namespace boost; + using namespace boost::mpl; + using boost::mpl::empty; + using boost::mpl::size; + + const unsigned char numRID = + 1 + RuntimeID<back<DiscreteQuantorList>::type>::value; + + template<typename Val, typename Payload> + struct DefaultContainer { + typedef Heap<PriorityQueue<Val, Payload>> type; + }; + + // the actual class (the only one lifted) + template <typename Val, // keys to be sorted + template<typename X> class Payload, // values belongig to above keys + typename Members, // list of all data sources + template<typename _Val, typename _Payload> class Container + = DefaultContainer> // queue impl + struct MultiQueue { + // type constructions + typedef typename size<Members>::type numMembers; + typedef typename transform<Members, + Container<Val, Payload<_>> + >::type MemberList; + // typedef typename CalcSetType<Val, Container, Payload, Members>::type MemberList; + typedef typename UnpackTypeList<TypeSet, MemberList>::type set_t; + + // get time and type of min element + Val min() { + return minVal[1]; + } + + uint8_t minType() { + return minRID[1]; + } + + // insert event + template<typename Quant> + inline void insert(Val ct, Val val, Payload<Quant> payload) { + assert(ct() <= val()); + BOOST_STATIC_ASSERT(( numRID > RuntimeID<Quant>::value )); + get<Quant>()(ct).insert(val, payload); + + uint pos = RIDpos[RuntimeID<Quant>::value]; + minVal[pos] = get<Quant>()(ct).minVal(); + + // correct order; val can only decrease -> bubble down + for (uint i=pos-1; maybeSwap(i); i--); + } + + // remove min event; ugly part: caller has to supply correct quant + // and position + template<typename Quant> + inline void removeMin(Val ct, uint pos=1) { + assert(RuntimeID<Quant>::value == minRID[pos]); + + get<Quant>()(ct).removeMin(); + minVal[pos] = get<Quant>()(ct).isEmpty() + ? Val::never() + : get<Quant>()(ct).minVal(); + + // correct order; val can only increase -> bubble up + for (int i=pos; maybeSwap(i); i++); + } + + // like removeMin, but the quant does not need to be the current + // global min and no position has to be specified + template<typename Quant> + inline void removeLocalMin(Val ct) { + removeMin<Quant>(ct, RIDpos[RuntimeID<Quant>::value]); + } + + // constructor + template<typename... Arg> + MultiQueue(Arg... arg) : typeSet(std::move(arg)...) { + init(); + } + + MultiQueue() : typeSet() { + init(); + } + + void init() { + // set fake entries (to avoid boundary condition during sorting) + minVal[0] = Val::beforeAll(); + minRID[0] = -1; + // (and prefill minVal/minRID in case of missing values in loadInitial()) + for (int i=1; i<=numMembers::value + 1; i++) { + minVal[i] = Val::never(); + minRID[i] = i - 1; + } + + // init queue head caches + loadInitial<Members>(); + + // blind in-place bubble sort + for (int i=0; i<numMembers::value; i++) + for (int j=1; j<numMembers::value; j++) + maybeSwap(j); + } + + template<typename RemMembers> + void loadInitial(uint pos=1) { + typedef typename front<RemMembers>::type head_t; + typedef empty<typename pop_front<RemMembers>::type> isLast_t; + typedef typename if_<isLast_t, + RemMembers, + typename pop_front<RemMembers>::type + >::type tail_t; + + // read current time from heaps + Val ct = get<head_t>().currentTime; + + // init queue head caches + minRID[pos] = RuntimeID<head_t>::value; + if (get<head_t>()(ct).isEmpty()) { + minVal[pos] = Val::never(); + }else{ + minVal[pos] = get<head_t>()(ct).minVal(); + } + RIDpos[RuntimeID<head_t>::value] = pos; + + // template recursion + if (!is_same<RemMembers, tail_t>::value) + loadInitial<tail_t>(pos+1); + } + + // access underlying queues directly + template <typename Quant> + inline typename Container<Val, Payload<Quant>>::type & get() { + return typeSet.get<typename Container<Val, Payload<Quant>>::type>(); + } + + // swap if i and i+1 are in wrong order; return if swapped + inline bool maybeSwap(uint i) { + // check if order is violated + if (minVal[i] < minVal[i+1]) return false; + if ((minVal[i] == minVal[i+1]) && (minRID[i] <= minRID[i+1])) return false; // sort by RID if val is equal + + // swap + std::swap(minVal[i], minVal[i+1]); + std::swap(minRID[i], minRID[i+1]); + RIDpos[minRID[i]] = i; + RIDpos[minRID[i + 1]] = i + 1; + return true; + } + + // data + set_t typeSet; + + Val minVal[numMembers::value + 2]; + uint minRID[numMembers::value + 2]; + uint RIDpos[numRID]; + }; +} + +using MultiQueueImpl::MultiQueue; + +#endif // JszL9wNT11XcdnV6wZR3NcSJB0 diff --git a/core/perftools.hpp b/core/perftools.hpp new file mode 100644 index 0000000..91cf56e --- /dev/null +++ b/core/perftools.hpp @@ -0,0 +1,40 @@ +#ifndef U6JSwQqqOviv7kQT0JaHQQr1VDI +#define U6JSwQqqOviv7kQT0JaHQQr1VDI + +#include <sys/time.h> +#include <stdio.h> + +////// THROUGHPUT + +void print_throughput(double time, double num, char * title) { + fprintf(stderr, "%s: %lfs\t%lf/s\n", title, time, num / time); +} + +void print_throughput2(double time, double num, long base) { + fprintf(stderr, "%ld:\t%lfs\t%lf/s\n", base, time, num / time); +} + +////// TIMER + +class Timer { +public: + Timer(); + + double diff(); + + timeval start; +}; + +Timer::Timer() { + gettimeofday(&start,NULL); +} + +double Timer::diff() { + timeval stop; + + gettimeofday(&stop,NULL); + + return (stop.tv_sec + stop.tv_usec/1000000.0) - (start.tv_sec + start.tv_usec/1000000.0); +} + +#endif // U6JSwQqqOviv7kQT0JaHQQr1VDI diff --git a/core/pla_apply.hpp b/core/pla_apply.hpp new file mode 100644 index 0000000..f9ab3f6 --- /dev/null +++ b/core/pla_apply.hpp @@ -0,0 +1,276 @@ +#ifndef QwJZ8toZywNt6ETv1SpnUMCaNRM +#define QwJZ8toZywNt6ETv1SpnUMCaNRM + +#include <assert.h> + +#include <boost/mpl/and.hpp> +#include <boost/mpl/bool.hpp> +#include <boost/mpl/copy.hpp> +#include <boost/mpl/copy_if.hpp> +#include <boost/mpl/front.hpp> +#include <boost/mpl/has_key.hpp> +#include <boost/mpl/insert.hpp> +#include <boost/mpl/inserter.hpp> +#include <boost/mpl/list.hpp> +#include <boost/mpl/transform.hpp> +#include <boost/type_traits/is_same.hpp> + +#include "context.hpp" +#include "continuous_property.hpp" +#include "pla_set.hpp" +#include "quant_types.hpp" +#include "template_helpers.hpp" +#include "time.hpp" +#include "type_map.hpp" + +#include "model.hpp" // to specialize QuantMayEmit and + // QuantHasVarDelay before instanciation + +namespace PLA_ApplyImpl { + + using namespace boost::mpl; + using boost::is_same; + + /// event intent + template< + typename EmittingQuant, + typename EmittedQuant, + typename Enable = typename QuantMayEmit<EmittingQuant, EmittedQuant>::result, + typename EnableTime = typename QuantHasVarDelay<EmittedQuant>::result> + struct Intent; + + template<typename _Q1, typename _Q2, typename _B> + struct Intent<_Q1, _Q2, bool_<false>, _B> { + inline void combine(bool) {} + inline bool getResult() const { return false; } + inline void setDelay(Time t) const { DO_NOT_CALL } + inline Time getDelay() const { return Time(0); } + }; + + // intent without variable delay + template<typename SrcCQ, typename DstDQ> + struct Intent<SrcCQ, DstDQ, bool_<true>, bool_<false>> + : Intent<SrcCQ, DstDQ, bool_<false>, bool_<false>> { + inline Intent() : + result(QuantEmitDefault<SrcCQ, DstDQ>::value) + {} + + inline void combine(bool val) { + if (QuantEmitDefault<SrcCQ, DstDQ>::value) { + // default == true -> combine via and + result = result and val; + }else{ + // default == false -> combine via or + result = result or val; + } + } + inline bool getResult() const { return result; } + + bool result; + }; + + // intent with variable delay + template<typename SrcCQ, typename DstDQ> + struct Intent<SrcCQ, DstDQ, bool_<true>, bool_<true>> + : Intent<SrcCQ, DstDQ, bool_<true>, bool_<false>> { + inline Intent() : + delay(Time::never()) + {} + + inline void setDelay(Time t) { delay = t; } + inline Time getDelay() const { + assert(delay != Time::never()); // assure setDelay has been called + return delay; + } + + bool result; + Time delay; + }; + + /// event transaction: store changed values (and apply them later); + /// it is a PLA itself, but is not exported + template<typename SrcDQ, + typename DstCQ, + typename PC, + typename Context> + struct Transaction { + /// 1st use: storage + // filter props to store + struct filter_f { + template<typename Pair> + struct apply { + typedef typename Pair::first prop; + typedef typename and_< + typename is_same<typename prop::quant, DstCQ>::type, + typename ContinuousProperty_ApplyChangesProp<prop, SrcDQ>::type + >::type type; + }; + }; + + // transform property list item into (property, property::type) + struct transform_f { + template<typename Item> + struct apply { + typedef typename Item::first prop; + typedef pair<prop, typename prop::type> type; + }; + }; + + // compute map of props to store (filter and transform) + typedef typename transform< + typename copy_if< + typename PC::properties_t, + filter_f + >::type, + transform_f, + inserter<map<>, insert<_1, _2> > + >::type map_t; + + // instance vars + typename if_<empty<map_t>, + TypeMap<Void>, + TypeMap<map_t> + >::type instance; + + // access + template<typename Q> + void set(typename at<map_t, Q>::type val) { + instance.template get<Q>() = val; + } + + /// 2nd use: PLA + typedef Void result_t; + template<typename ContextProp, bool _aaa> struct local_state_t { + typedef ContextProp property_t; + }; + + Transaction(Context context, Time time) : context(context), time(time) {} + + Context context; + Time time; + result_t result; + + template<typename ContextData, typename LocalState> + inline void pre(PC &pc, ContextData &data, LocalState &state) { + typedef typename LocalState::property_t prop_t; + typedef typename prop_t::quant quant_t; + if (has_key<map_t, prop_t>::type::value) { + pc.cast(PLA_Set<prop_t> + (time, Ptr<quant_t>(context.template getptr<quant_t>()), + instance.template get<prop_t, typename prop_t::type>())); + } + } + + template<typename _Data, typename LocalState> + inline void post(PC &pc, _Data &data, LocalState &state) { + } + + + }; + + /// apply action + template< + typename SrcQuant, // discrete + typename DstQuant, // continuous + typename PropComp, + typename Context = DelieverContext<SrcQuant, DstQuant> + > + struct PLA_Apply { + // map->intent transformation + template<typename T> + struct intent_from_type { + typedef pair<T, Intent<DstQuant, T>> type; + }; + + // action types & consts + typedef Void result_t; + template<typename ContextProp, bool _doWriteResults> struct local_state_t { + typedef ContextProp property_t; + }; + typedef Transaction<SrcQuant, DstQuant, PropComp, Context> transaction_t; + typedef TypeMap< + typename transform< + DiscreteQuantorList, + intent_from_type<_1>, + inserter<map<>, insert<_1, _2> > + >::type + > intentmap_t; + + // action state & constructor + Context context; + intentmap_t intent; + Time time; + result_t result; + transaction_t transaction; + + inline PLA_Apply(Time time, Context context) + : context(context), intent(), time(time), transaction(context, time) {} + + // action methods + template<typename PC, typename ContextData, typename LocalState> + inline void pre(PC &pc, ContextData &data, LocalState &) { + // call apply definition for every destination property + if (boost::is_same<typename LocalState::property_t::quant, DstQuant>::value) { + ContinuousProperty_Apply< + PC, typename LocalState::property_t, + DstQuant, SrcQuant, Context, + intentmap_t, transaction_t> + ::eval(pc, context, time, intent, transaction); + } + } + + template<typename _PC, typename _Data, typename LocalState> + inline void post(_PC &pc, _Data &data, LocalState &state) { + } + + // after-use queries + inline bool generateAny() { + // TODO (beauty, optimize): compile time fold over discrete + // quantor list, removing queries of quants we do never emit + return generate<GlobalMsg>() + or generate<Spike>() + or generate<RandomSpike>() + or generate<SpikeArrival>(); + } + + template<typename Q> + inline bool generate() { + return intent.template get<Q>().getResult(); + } + + template<typename Q> + inline Time getDelay() { + return intent.template get<Q>().getDelay(); + } + + template<typename PC> + void computeDelay(PC &pc) { + // TODO (beauty, optimize): compile time fold over discrete + // quantor list +#define GEN_CD(Q) \ + if (QuantHasVarDelay<Q>::result::value \ + && QuantMayEmit<DstQuant, Q>::result::value) \ + intent.template get<Q>().setDelay \ + (DiscreteProperty_ComputeDelay<Q, DstQuant> \ + ::eval(pc, context, transaction, time)); + + GEN_CD(GlobalMsg) + GEN_CD(Spike) + GEN_CD(RandomSpike) + GEN_CD(SpikeArrival) +#undef GEN_CD + } + + template<typename PC> + void commit(PC &pc) { + pc.call(transaction); + } + + private: + PLA_Apply(); + }; +} + +using PLA_ApplyImpl::PLA_Apply; + +#endif // QwJZ8toZywNt6ETv1SpnUMCaNRM diff --git a/core/pla_debug_name.hpp b/core/pla_debug_name.hpp new file mode 100644 index 0000000..1b1f89e --- /dev/null +++ b/core/pla_debug_name.hpp @@ -0,0 +1,39 @@ +#ifndef lULoAciwLXvtwzfgpfl9LrvpNj0 +#define lULoAciwLXvtwzfgpfl9LrvpNj0 + +#include <string> + +#include <boost/type_traits/is_same.hpp> + +#include "property_instance.hpp" +#include "template_helpers.hpp" + +// PLA template +struct PLA_Debug_Name { + // action types & consts + typedef std::string result_t; + template<typename _ContextProp, bool doWriteResults> struct local_state_t { + typedef _ContextProp prop; + static const bool write = doWriteResults; + }; + + // action state + result_t result; + + PLA_Debug_Name() : result("Property List Content:") { } + + // action methods + template<typename _PropList, typename _ContextData, typename LocalState> + inline void pre(_PropList &pc, _ContextData &data, LocalState &state) { + result = result + "\n" + + LocalState::prop::quant::name + "::" + + LocalState::prop::name + + " (" + LocalState::prop::quant::class_t::name + "): " + + (LocalState::write ? "writing" : "reading"); + } + + template<typename _PropComp, typename _Data, typename _LocalState> + inline void post(_PropComp &pc, _Data &data, _LocalState &state) { } +}; + +#endif // lULoAciwLXvtwzfgpfl9LrvpNj0 diff --git a/core/pla_debug_space.hpp b/core/pla_debug_space.hpp new file mode 100644 index 0000000..361f3d5 --- /dev/null +++ b/core/pla_debug_space.hpp @@ -0,0 +1,172 @@ +#ifndef iAN34Y9CxgHgVUyhhdYpI8Ynza8 +#define iAN34Y9CxgHgVUyhhdYpI8Ynza8 + +#include <iostream> +#include <sstream> +#include <string> + +#include <boost/type_traits/is_same.hpp> + +#include "checkpoint.hpp" +#include "index_spike.hpp" +#include "property_instance.hpp" +#include "simlimits.hpp" +#include "template_helpers.hpp" + +namespace PLA_Debug_Space_Impl { + +template <class Quant> +struct PLA_Debug_Space_Quant2Num; + +// PLA template +struct PLA_Debug_Space { + // action types & consts + typedef Void result_t; + template<typename _ContextProp, bool doWriteResults> struct local_state_t { + typedef _ContextProp prop; + static const bool write = doWriteResults; + }; + + // action state + result_t result; + + // tmp type const; to do: change to quant::runtimeID + static const int cGlobal = 0; + static const int cNeuron = 1; + static const int cSynapse = 2; + static const int cGlobalMsg = 3; + static const int cSpike = 4; + static const int cSpikeArrival = 5; + static const int propCount = 6; + + // result storage + // * set by action methods + int num[propCount], + numRead[propCount], + size[propCount], + sizeRead[propCount]; + + // * set by generateReport() + int totalNum, + totalNumRead, + totalSize; + + // methods + PLA_Debug_Space(); + std::string generateReport(); + + // action methods + template<typename _PropList, typename _ContextData, typename LocalState> + inline void pre(_PropList &pc, _ContextData &data, LocalState &state) { + int id = PLA_Debug_Space_Quant2Num<typename LocalState::prop::quant>::eval(); + num[id] += 1; + size[id] += LocalState::prop::size; + + if (!LocalState::write) { + numRead[id] += 1; + sizeRead[id] += LocalState::prop::size; + } + } + + template<typename PropComp, typename Data, typename _LocalState> + inline void post(PropComp &pc, Data &data, _LocalState &state) { } +}; + +PLA_Debug_Space::PLA_Debug_Space() { + for (int i=0; i<propCount; i++) { + num[i] = 0; + numRead[i] = 0; + size[i] = 0; + sizeRead[i] = 0; + } +} + +using namespace std; + +string PLA_Debug_Space::generateReport() { + ostringstream &result = *(new ostringstream()); + + double chkpt_delta = checkpointInterval; + + result + << "Simulation\n" + << "==========\n\n" + <<"neurons:\t" << maxNeurons << "\n" + << "synapses:\t" << maxSynapsesPerNeuron * maxNeurons << "\t(" << (int) maxSynapsesPerNeuron << " per neuron)\n" + << "\n" + << "max. # spikes:\t\t" << maxSpikes << "\n" + << "max. # global messages:\t" << maxGlobalMsg << "\n" + << "max. # checkpojts:\t" << maxCheckpoints << "\n" + << "checkpoint every\t" << chkpt_delta << " s\n" + << "\n" + << "max. run-time:\n" + << "\t" << min( chkpt_delta * maxCheckpoints, (double) maxSpikes / maxNeurons / 1.0 ) << " s \t@ 1 Hz\n" + << "\t" << min( chkpt_delta * maxCheckpoints, (double) maxSpikes / maxNeurons / 10.0 ) << " s \t@ 10 Hz\n" + << "\t" << min( chkpt_delta * maxCheckpoints, (double) maxSpikes / maxNeurons / 100.0 ) << " s \t@ 100 Hz\n" + << "\n\n"; + + totalNum = 0; + totalSize = 0; + for (int i=0; i<propCount; i++) { + totalNum += num[i]; + totalSize += size[i]; + } + + result + << "Properties\n" + << "==========\n\n" + << "| Quantor\t| # of \t| size (per\t| size (all \t|\n" + << "| \t| props\t| instance)\t| instances)\t|\n" + << "+---------------+-------+---------------+---------------+\n" + + << "| Global \t| " << num[cGlobal] << "\t| " << size[cGlobal] << "\t\t| " << size[cGlobal] << "\t\t|\n" + << "| Neuron \t| " << num[cNeuron] << "\t| " << size[cNeuron] << "\t\t| " << maxNeurons * size[cNeuron] << "\t\t|\n" + << "| Synapse \t| " << num[cSynapse] << "\t| " << size[cSynapse] << "\t\t| " << maxNeurons * maxSynapsesPerNeuron * size[cSynapse] << "\t|\n" + << "| GlobalMsg \t| " << num[cGlobalMsg] << "\t| " << size[cGlobalMsg] << "\t\t| - \t|\n" + << "| Spike \t| " << num[cSpike] << "\t| " << size[cSpike] << "\t\t| - \t|\n" + << "| SpikeArrival \t| " << num[cSpikeArrival] << "\t| " << size[cSpikeArrival] << "\t\t| - \t|\n" + << "+---------------+-------+---------------+---------------+\n" + + << "| Total \t| " << totalNum << "\t| " << totalSize << "\t\t| - \t|\n" + << "\n\n"; + + double pt, ps; // per time / spike space requirement + pt = size[cGlobal] + sizeof(Time) * num[cGlobal] + + (size[cNeuron] + sizeof(Index<Spike>::ptr_t) + sizeof(Time) * (num[cNeuron] + 1)) * maxNeurons // addend 2 and 4 account for Index<Spike>::prevCache + + (size[cSynapse] + sizeof(Time) * num[cSynapse]) * maxNeurons * maxSynapsesPerNeuron; + pt /= chkpt_delta; + + ps = maxNeurons * (2*sizeof(Index<Spike>::ptr_t) + sizeof(Index<Spike>::neuron_ptr_t) + size[cNeuron] + maxSynapsesPerNeuron * size[cSpikeArrival]); + + result + << "Space Requirements\n" + << "==================\n\n" + << "These are strict minimum numbers. Global messages, additional managment information (in-app and in-kernel) and many small vars have not been taken into account.\n\n" + << "\t: 1 s\t\t| 1 h\t\t| 1 d\t\t|\n" + << "--------+---------------+---------------+---------------+\n" + << " 1 Hz\t: " << SISuffixify(1 * ( 1 * ps + pt), 1024) << "B\t| " << SISuffixify(3600 * ( 1 * ps + pt), 1024) << "B\t| " << SISuffixify(86400 * ( 1 * ps + pt), 1024) << "B\t|\n" + << " 10 Hz\t: " << SISuffixify(1 * ( 10 * ps + pt), 1024) << "B\t| " << SISuffixify(3600 * ( 10 * ps + pt), 1024) << "B\t| " << SISuffixify(86400 * ( 10 * ps + pt), 1024) << "B\t|\n" + << " 100 Hz\t: " << SISuffixify(1 * (100 * ps + pt), 1024) << "B\t| " << SISuffixify(3600 * (100 * ps + pt), 1024) << "B\t| " << SISuffixify(86400 * (100 * ps + pt), 1024) << "B\t|\n" + << ""; + + + return result.str(); +} + +// PLA_Debug_Space_Quant2Num implementation +// TODO: overhaul + +template <> struct PLA_Debug_Space_Quant2Num<Global> { static int eval() { return PLA_Debug_Space::cGlobal; } }; +template <> struct PLA_Debug_Space_Quant2Num<Neuron> { static int eval() { return PLA_Debug_Space::cNeuron; } }; +template <> struct PLA_Debug_Space_Quant2Num<Synapse> { static int eval() { return PLA_Debug_Space::cSynapse; } }; +template <> struct PLA_Debug_Space_Quant2Num<GlobalMsg> { static int eval() { return PLA_Debug_Space::cGlobalMsg; } }; +template <> struct PLA_Debug_Space_Quant2Num<Spike> { static int eval() { return PLA_Debug_Space::cSpike; } }; +template <> struct PLA_Debug_Space_Quant2Num<SpikeArrival> { static int eval() { return PLA_Debug_Space::cSpikeArrival; } }; + +} // NS + +using PLA_Debug_Space_Impl::PLA_Debug_Space; + + + +#endif // iAN34Y9CxgHgVUyhhdYpI8Ynza8 diff --git a/core/pla_evolve.hpp b/core/pla_evolve.hpp new file mode 100644 index 0000000..032d9d4 --- /dev/null +++ b/core/pla_evolve.hpp @@ -0,0 +1,93 @@ +#ifndef FznMZXtH9pg7npfVO7D248udzM8 +#define FznMZXtH9pg7npfVO7D248udzM8 + +#include <assert.h> + +#include <boost/type_traits/is_same.hpp> + +#include "context.hpp" +#include "continuous_property.hpp" +#include "template_helpers.hpp" +#include "pla_set.hpp" +#include "time.hpp" + +// local state +// general case: no action (and zero storage) +template<typename Prop, typename Quant1, typename Quant2> +struct PLA_Evolve_LocalState { + template <typename PropComp, typename Context, typename LocalData> + inline void evolve(PropComp &pc, Context context, LocalData &data, Time newTime) { + DO_NOT_CALL; + } + + template <typename PropComp, typename Context> + inline void commit(PropComp &pc, Context context, Time time) { + DO_NOT_CALL; + } +}; + +// matching case: get/set the value to local storage +template<typename Prop, typename Quant> +struct PLA_Evolve_LocalState<Prop, Quant, Quant> { + typename Prop::type val; + + template <typename PropComp, typename Context, typename LocalData> + inline void evolve(PropComp &pc, Context context, LocalData &data, Time newTime) { + // calc time difference (= time amount we evolve) + Time oldTime = data.data.getTime(newTime, context.template getptr<Quant>()()); + Time dt = newTime - oldTime; + assert(dt >= 0); + + // calc new val and store locally + val = ContinuousProperty_Evolve<PropComp, Prop, Quant, Context>:: + eval(pc, context, oldTime, dt); + } + + template <typename PropComp, typename Context> + inline void commit(PropComp &pc, Context context, Time time) { + Ptr<Quant> dst(context.template getptr<Quant>()); + PLA_Set<Prop> pla_set(time, dst, val); + pc.call(pla_set); + } +}; + +// evolve action +template<typename Quant, typename Context = ContinuousContext<Quant> > +struct PLA_Evolve { + // action types & consts + typedef Void result_t; + template<typename ContextProp, bool _doWriteResults> + struct local_state_t + : PLA_Evolve_LocalState<ContextProp, Quant, typename ContextProp::quant> + { + typedef ContextProp prop; + }; + + // action state & constructor + typedef typename Quant::instance_ptr_t instance_ptr_t; + Context context; + Time newTime; + result_t result; + + PLA_Evolve(Context context, Time newTime) : context(context), newTime(newTime) {} + + // action methods + template<typename PropComp, typename LocalData, typename LocalState> + inline void pre(PropComp &pc, LocalData &data, LocalState &state) { + // if we have the correct quantity ... + if (boost::is_same<Quant, typename LocalState::prop::quant>::value) + state.evolve(pc, context, data, newTime); // ... evolve it + } + + template<typename _PropComp, typename _LocalData, typename LocalState> + inline void post(_PropComp &pc, _LocalData &data, LocalState &state) { + // if we have the correct quantity ... + if (boost::is_same<Quant, typename LocalState::prop::quant>::value) + state.commit(pc, context, newTime); // ... store new value and time + } + +private: + PLA_Evolve(); +}; + +#endif // FznMZXtH9pg7npfVO7D248udzM8 diff --git a/core/pla_generate.hpp b/core/pla_generate.hpp new file mode 100644 index 0000000..7ae5169 --- /dev/null +++ b/core/pla_generate.hpp @@ -0,0 +1,112 @@ +#ifndef Vaz5kkzRinGGCeloVow8grqZI1c +#define Vaz5kkzRinGGCeloVow8grqZI1c + +// HINT: simple schema, based on the fact the this PLA changes only +// props it does not depend on (read from) + +#include <assert.h> + +#include <boost/type_traits/is_same.hpp> +#include <boost/tuple/tuple.hpp> + +#include "context.hpp" +#include "template_helpers.hpp" +#include "time.hpp" + +namespace PLA_Generate_Impl { + + using namespace boost; + using namespace boost::mpl; + + // local state + // general case: no action (and zero storage) + template<typename Prop, typename CQ, typename DQ, bool quantMatch /* == false */> + struct LocalState { + template<typename PropComp, typename Context, typename MI, typename MQ> + inline void process(PropComp &, Context, Time, MI &, MQ &) {} + + template <typename PropComp, typename Context> + inline void commit(PropComp &, Context, Time) {} + }; + + // matching case: get/set the value to local storage + template<typename Prop, typename CQ, typename DQ> + struct LocalState<Prop, CQ, DQ, true /* == quantMatch */> { + typename Prop::type val; + + template<typename PropComp, typename Context, typename MI, typename MQ> + inline void process(PropComp &pc, Context context, Time time, + MI &indices, MQ &queues) { + // calc new val and store locally + // TODO: check generation of discrete properties + ContinuousProperty_Generate<Prop, CQ, DQ>:: + eval(pc, context, time, val, indices, queues); + } + + template <typename PropComp, typename Context> + inline void commit(PropComp &pc, Context context, Time time) { + // HINT: caller has to check if we should commit + typedef typename Prop::quant Quant; + Ptr<Quant> dst(context.template getptr<Quant>()); + PLA_Set<Prop> pla_set(time, dst, val); // TODO: omit time if Prop is discrete + pc.call(pla_set); + } + }; + + // generate action + template<typename CQ, typename DQ, typename MI, typename MQ> + struct PLA_Generate { + // action types & consts + typedef Void result_t; + template<typename ContextProp, bool doWriteResults> + struct local_state_t : LocalState<ContextProp, CQ, DQ, + or_<is_same<CQ, typename ContextProp::quant>, + is_same<DQ, typename ContextProp::quant> + >::value> + { + typedef ContextProp prop; + static const bool write = doWriteResults; + }; + typedef EmitContext<CQ, DQ> context_t; + + // action state & constructor + context_t context; + Time time; + result_t result; + + MI &indices; + MQ &queues; + + PLA_Generate(Time time, context_t context, MI &indices, MQ &queues) + : context(context), time(time), indices(indices), queues(queues) {} + + // action methods + template<typename PropComp, typename LocalData, typename LocalState> + inline void pre(PropComp &pc, LocalData &, LocalState &state) { + // if we have the correct quantity (and ought to write to it) + typedef typename LocalState::prop Prop; + typedef typename Prop::quant Quant; + if ( (is_same<CQ, Quant>::value and ContinuousProperty_Generate + <Prop, CQ, DQ>::changesValue) + or (is_same<DQ, typename LocalState::prop::quant>::value and state.write)) + state.process(pc, context, time, indices, queues); + } + + template<typename PropComp, typename _LocalData, typename LocalState> + inline void post(PropComp &pc, _LocalData &data, LocalState &state) { + typedef typename LocalState::prop Prop; + typedef typename Prop::quant Quant; + if ( (is_same<CQ, Quant>::value and ContinuousProperty_Generate + <Prop, CQ, DQ>::changesValue) + or (is_same<DQ, typename LocalState::prop::quant>::value and state.write)) + state.commit(pc, context, time); // ... store new value and time + } + + private: + PLA_Generate(); + }; +} + +using PLA_Generate_Impl::PLA_Generate; + +#endif // Vaz5kkzRinGGCeloVow8grqZI1c diff --git a/core/pla_get.hpp b/core/pla_get.hpp new file mode 100644 index 0000000..ba7d2a0 --- /dev/null +++ b/core/pla_get.hpp @@ -0,0 +1,92 @@ +#ifndef j9bZiR9W7dKz17gTUOtW1xODHdA +#define j9bZiR9W7dKz17gTUOtW1xODHdA + +#include <boost/mpl/if.hpp> +#include <boost/type_traits/is_same.hpp> + +#include "context.hpp" +#include "continuous_property.hpp" +#include "property_instance.hpp" +#include "property_list.hpp" +#include "quant_types.hpp" +#include "template_helpers.hpp" + +// worker template forward decl +template<typename Prop, typename CurrentProp, typename Data, typename QuantClass> +struct PLA_Get_Impl; + +// PLA template +template<typename Prop, typename Context = SingleContext<typename Prop::quant>> +struct PLA_Get { + // action consts + typedef typename Prop::type result_t; + template<typename ContextProp, bool _doWriteResult> struct local_state_t { + typedef ContextProp prop; + }; + + // action parameters + typedef typename Prop::instance_ptr_t instance_t; + typedef typename boost::mpl::if_<boost::is_same<ContinuousPropertyClass, typename Prop::quant::class_t>, + Time, + Void>::type time_t; + Context ctx; + time_t time; + result_t result; + + PLA_Get(time_t time, Context ctx) : ctx(ctx), time(time) {} + PLA_Get(Context ctx) : ctx(ctx) { + BOOST_STATIC_ASSERT((boost::is_same<time_t, Void>::value)); + } + + // action methods + template<typename _PropComp, typename _ContextData, typename _LocalState> + inline void pre(_PropComp &pc, _ContextData &data, _LocalState &state) { } + + + template<typename PC, typename ContextData, typename LocalState> + inline void post(PC &pc, ContextData &data, LocalState &state) { + BOOST_MPL_ASSERT(( PL_Contains<PL<typename PC::properties_t>, Prop> )); + PLA_Get_Impl<Prop, typename LocalState::prop, ContextData, typename LocalState::prop::quant::class_t> + ::eval(pc, data, ctx, time, result); + } +}; + +// worker template implementation +// * do nothing at the wrong type +template<typename Prop, typename WrongProp, typename Data, typename QuantClass> +struct PLA_Get_Impl { + template<typename PC, typename Context = SingleContext<typename Prop::quant>> + static inline void eval(PC &, Data &, Context, + typename PLA_Get<Prop>::time_t, typename Prop::type &) { } +}; + +// * store pointer to the data structure at the correct type +template<typename Prop, typename Data> +struct PLA_Get_Impl<Prop, Prop, Data, ContinuousPropertyClass> { + template<typename PC, typename Context = SingleContext<typename Prop::quant>> + static inline void eval(PC &pc, Data &data, Context ctx, + typename PLA_Get<Prop>::time_t time, + typename Prop::type &fold_state) { + Time oldTime = data.data.getTime(time, ctx.template getptr<typename Prop::quant>()()); + assert(oldTime <= time); // we can not jump backward + if (oldTime < time) { + Time dt = time - oldTime; + fold_state = ContinuousProperty_Evolve<PC, Prop, typename Prop::quant, Context> + ::eval(pc, ctx, oldTime, dt); + }else{ + fold_state = data.data.getValue(time, ctx.template getptr<typename Prop::quant>()()); + } + } +}; + +template<typename Prop, typename Data> +struct PLA_Get_Impl<Prop, Prop, Data, DiscretePropertyClass> { + template<typename PC, typename Context = SingleContext<typename Prop::quant>> + static inline void eval(PC &, Data &data, Context ctx, + typename PLA_Get<Prop>::time_t, + typename Prop::type &fold_state) { + fold_state = data.data(ctx.template getptr<typename Prop::quant>()()); + } +}; + +#endif // j9bZiR9W7dKz17gTUOtW1xODHdA diff --git a/core/pla_getbyname.hpp b/core/pla_getbyname.hpp new file mode 100644 index 0000000..30f2db0 --- /dev/null +++ b/core/pla_getbyname.hpp @@ -0,0 +1,37 @@ +#ifndef fwzTcntkbxdRjyBO90weDhBZ4Nc +#define fwzTcntkbxdRjyBO90weDhBZ4Nc + +#include <string.h> + +#include "template_helpers.hpp" + +// PLA template +template<template<typename Prop, typename QuantClass> class Payload, + typename BaseCase = Payload<BaseCase, Void>> +struct PLA_GetByName { + // action types & consts + typedef BaseCase* result_t; + template<typename ContextProp, bool doWriteResults> struct local_state_t { + typedef ContextProp prop; + }; + + // action state + char *name; + result_t result; + + PLA_GetByName(char *name) : name(name), result(NULL) {} + + // action methods + template<typename _PropList, typename _ContextData, typename LocalState> + inline void pre(_PropList &pc, _ContextData &data, LocalState &state) { + typedef typename LocalState::prop Prop; + if (strcmp(Prop::name, name) == 0) { + result = new Payload<Prop, typename Prop::quant::class_t>(); + } + } + + template<typename _PropComp, typename _Data, typename _LocalState> + inline void post(_PropComp &, _Data &, _LocalState &) { } +}; + +#endif // fwzTcntkbxdRjyBO90weDhBZ4Nc diff --git a/core/pla_init_default.hpp b/core/pla_init_default.hpp new file mode 100644 index 0000000..a0e0808 --- /dev/null +++ b/core/pla_init_default.hpp @@ -0,0 +1,65 @@ +#ifndef CbSv0r4ZjDkLZr4WCEmSXjjwPA0 +#define CbSv0r4ZjDkLZr4WCEmSXjjwPA0 + +#include <assert.h> + +#include <boost/static_assert.hpp> +#include <boost/type_traits/is_same.hpp> + +#include "template_helpers.hpp" +#include "time.hpp" +#include "quant_types.hpp" + +#include "model.hpp" // for Weight + +namespace PLA_Init_Default_Impl { + +template<typename Prop, typename QC = typename Prop::quant::class_t /* == discrete */> +struct Action { + template<typename Data> void operator() (Data &data) {} +}; + +template<typename Prop> +struct Action<Prop, ContinuousPropertyClass> { + template<typename Data> + void operator() (Data &data) { + if (boost::is_same<Weight, Prop>::value || + boost::is_same<SumWeight, Prop>::value || + boost::is_same<TargetSumWeight, Prop>::value) + return; // do not set weight .. it is done in convert_topology + for (uint64_t i{0}; + i < Prop::quant::instance_ptr_t::numInstances(); + i++) { + data.data.set(0, i, Prop::initialValue); + } + } +}; + +// init default action +struct PLA_Init_Default { + // action types & consts + typedef Void result_t; + template<typename ContextProp, bool doWriteResults> struct local_state_t { + typedef ContextProp prop; + static const bool write = doWriteResults; + }; + + // action state + result_t result; + + // action methods + template<typename _PropComp, typename ContextData, typename LocalState> + inline void pre(_PropComp &pc, ContextData &data, LocalState &state) { + Action<typename LocalState::prop>()(data); + } + + template<typename _PropComp, typename _Data, typename _LocalState> + inline void post(_PropComp &pc, _Data &data, _LocalState &state) { } +}; + +} // ns + +using PLA_Init_Default_Impl::PLA_Init_Default; + + +#endif // CbSv0r4ZjDkLZr4WCEmSXjjwPA0 diff --git a/core/pla_initbycopy.hpp b/core/pla_initbycopy.hpp new file mode 100644 index 0000000..7fe6334 --- /dev/null +++ b/core/pla_initbycopy.hpp @@ -0,0 +1,70 @@ +#ifndef IsIx5LlrtRM4hjuaHq11FAgirk +#define IsIx5LlrtRM4hjuaHq11FAgirk + +#include <assert.h> + +#include <boost/static_assert.hpp> +#include <boost/type_traits/is_same.hpp> +#include <boost/mpl/if.hpp> + +#include "template_helpers.hpp" +#include "time.hpp" +#include "quant_types.hpp" + +#include "model.hpp" // for Weight + +namespace PLA_InitByCopy_Impl { + +using namespace boost; +using namespace boost::mpl; + +template<typename Prop, typename QuantClass = typename Prop::quant::class_t> +struct Action { + template<typename Data> + void operator() (Data &, Time) {} // NOOP on discrete quants +}; + +template<typename Prop> +struct Action<Prop, ContinuousPropertyClass> { + typedef typename Prop::quant Quant; + + template<typename Data> + void operator() (Data &data, Time time) { + PropertyInstance<Prop, true> src; + typedef typename if_<is_same<Quant, Global>, + uint8_t, + typename Prop::instance_ptr_t::ptr_t + >::type ptr_t; + assert(time <= src.data.timeLimit); // we do not want to cause addCheckpoint() + for (ptr_t i = 0; i < Prop::instance_ptr_t::numInstances(); i++) + data.data.set(src.data.getTime(time, i), i, src.data.getValue(time, i)); + } +}; + +struct PLA_InitByCopy { + // action types & consts + typedef Void result_t; + template<typename ContextProp, bool _doWriteResults> struct local_state_t + : Action<ContextProp> {}; + + // action state + result_t result; + Time time; + + PLA_InitByCopy(Time time) : time(time) {} + + // action methods + template<typename _PC, typename ContextData, typename LocalState> + inline void pre(_PC &, ContextData &data, LocalState &action) { + action(data, time); + } + + template<typename _PC, typename _Data, typename _LocalState> + inline void post(_PC &, _Data &, _LocalState &) { } +}; + +} // namespace PLA_InitByCopy_Impl + +using PLA_InitByCopy_Impl::PLA_InitByCopy; + +#endif // IsIx5LlrtRM4hjuaHq11FAgirk diff --git a/core/pla_set.hpp b/core/pla_set.hpp new file mode 100644 index 0000000..1c99cc9 --- /dev/null +++ b/core/pla_set.hpp @@ -0,0 +1,79 @@ +#ifndef LVOy9pHh2bcXzO1tZ5oxb80UXy8 +#define LVOy9pHh2bcXzO1tZ5oxb80UXy8 + +#include <boost/mpl/if.hpp> +#include <boost/type_traits/is_same.hpp> + +#include "property_instance.hpp" +#include "property_list.hpp" +#include "quant_types.hpp" +#include "template_helpers.hpp" + +// worker template forward decl +template<typename Prop, typename CurrentProp, typename Data, typename QuantClass, bool isWrite> +struct PLA_Set_Impl; + +// PLA template +template<typename Prop> +struct PLA_Set { + // action consts + typedef Void result_t; + template<typename ContextProp, bool _doWriteResult> struct local_state_t { + typedef ContextProp prop; + static const bool write = _doWriteResult; + }; + + // action parameters + typedef typename Prop::instance_ptr_t instance_t; + typedef typename boost::mpl::if_<boost::is_same<ContinuousPropertyClass, typename Prop::quant::class_t>, + Time, + FakeTime>::type time_t; + typedef typename Prop::type value_t; + instance_t instance; + time_t time; + value_t value; + result_t result; + + inline PLA_Set(instance_t instance, value_t value) + : instance(instance), value(value) { + // ctor only allowed for discrete properties + BOOST_STATIC_ASSERT((boost::is_same<DiscretePropertyClass, + typename Prop::quant::class_t>::value)); + } + inline PLA_Set(time_t time, instance_t instance, value_t value) : instance(instance), time(time), value(value) { } + + // action methods + template<typename _PropComp, typename _ContextData, typename _LocalState> + inline void pre(_PropComp &pc, _ContextData &data, _LocalState &state) { } + + template<typename PropComp, typename ContextData, typename LocalState> + inline void post(PropComp &pl, ContextData &data, LocalState &state) { + BOOST_MPL_ASSERT(( PL_Contains<PL<typename PropComp::properties_t>, Prop> )); + PLA_Set_Impl<Prop, typename LocalState::prop, ContextData, typename LocalState::prop::quant::class_t, LocalState::write> + ::eval(data, instance, time, value); + } +}; + +// worker template implementation +// * do nothing at the wrong type +template<typename Prop, typename WrongProp, typename Data, typename QuantClass, bool isWrite> +struct PLA_Set_Impl { + static inline void eval(Data &data, typename PLA_Set<Prop>::instance_t instance, typename PLA_Set<Prop>::time_t time, typename Prop::type &value) { } +}; + +// * store pointer to the data structure at the correct type +template<typename Prop, typename Data, bool _isWrite> +struct PLA_Set_Impl<Prop, Prop, Data, ContinuousPropertyClass, _isWrite> { + static inline void eval(Data &data, typename PLA_Set<Prop>::instance_t instance, typename PLA_Set<Prop>::time_t time, typename Prop::type &value) { + data.data.set(time, instance(), value); + } +}; + +template<typename Prop, typename Data, bool _isWrite> +struct PLA_Set_Impl<Prop, Prop, Data, DiscretePropertyClass, _isWrite> { + static inline void eval(Data &data, typename PLA_Set<Prop>::instance_t instance, typename PLA_Set<Prop>::time_t time, typename Prop::type &value) { + data.data.set(instance(), value); + } +}; + +#endif // LVOy9pHh2bcXzO1tZ5oxb80UXy8 diff --git a/core/pla_sync.hpp b/core/pla_sync.hpp new file mode 100644 index 0000000..7114cb4 --- /dev/null +++ b/core/pla_sync.hpp @@ -0,0 +1,47 @@ +#ifndef aVCkmoPnFvf2TxOWrvfVb5qPbZM +#define aVCkmoPnFvf2TxOWrvfVb5qPbZM + +#include <boost/mpl/if.hpp> +#include <boost/type_traits/is_same.hpp> + +#include "property_instance.hpp" +#include "property_list.hpp" +#include "quant_types.hpp" +#include "template_helpers.hpp" + +// worker template forward decl +template<typename Data, bool isWrite /* == false */> +struct PLA_Sync_Impl { + static void eval(Data &) {} +}; + +template<typename Data> +struct PLA_Sync_Impl<Data, true> { + static void eval(Data &data) { + data.data.sync(); + } +}; + +// PLA template +struct PLA_Sync { + // action consts + typedef Void result_t; + template<typename ContextProp, bool doWriteResult> struct local_state_t { + typedef ContextProp prop; + static const bool write = doWriteResult; + }; + + result_t result; + + // action parameters + template<typename _PC, typename _ContextData, typename _LocalState> + inline void pre(_PC &, _ContextData &, _LocalState &) {} + + + template<typename _PC, typename ContextData, typename LocalState> + inline void post(_PC &, ContextData &data, LocalState &) { + PLA_Sync_Impl<ContextData, LocalState::write>::eval(data); + } +}; + +#endif // aVCkmoPnFvf2TxOWrvfVb5qPbZM diff --git a/core/pointers.hpp b/core/pointers.hpp new file mode 100644 index 0000000..cae5f4b --- /dev/null +++ b/core/pointers.hpp @@ -0,0 +1,213 @@ +#ifndef wOYVH4QhyaNIiefhP4sh9Osro2k +#define wOYVH4QhyaNIiefhP4sh9Osro2k + +#include <assert.h> + +#include <iostream> + +#include <boost/integer.hpp> +#include <boost/static_assert.hpp> + +#include "simlimits.hpp" +#include "template_helpers.hpp" + +// HINT: the template Ptr<> is defined in quant_types; it is an empty, +// forbidden basecase anyway +#include "quant_types.hpp" + +namespace PtrImpl { + +using boost::is_same; + +template<typename Quant, typename IterPtr> +struct Iter { + Iter(IterPtr ptr) : ptr(ptr) {} + Ptr<Quant> operator* () const { return Ptr<Quant>(ptr); } +#define REL(op) bool operator op (Iter i) { return ptr op i.ptr; } + REL(!=) + REL(==) +#undef REL + Iter& operator++ () { ptr++; return *this; } + Iter& operator-- () { ptr--; return *this; } + IterPtr ptr; +}; + +template<typename Quant, typename IterPtr> +struct Range { + typedef Iter<Quant, IterPtr> iter_t; + // init range with raw begin/end ptr or the first and last element + // to visit (DIFFERENT SEMANTICS!) + Range(IterPtr p1, IterPtr p2) : p1(p1), p2(p2) {} + Range(Ptr<Quant> p1, Ptr<Quant> p2) : p1(p1.raw), p2(p2.raw + 1) {} + iter_t begin() const { return iter_t(p1); } + iter_t end() const { return iter_t(p2); } + IterPtr p1, p2; +}; + +// we discriminate between storage and iter pointer to allow a zero +// bit storage pointer while maintaining a conforming iteration +// strategy +template<typename Quant, uint64_t instanceCount, + typename StoragePtr = typename boost::uint_value_t<instanceCount - 1>::least, + typename IterPtr = typename boost::uint_value_t<instanceCount >::least> +struct Common { + typedef StoragePtr ptr_t; + typedef Range<Quant, IterPtr> range_t; + + Common() = delete; + //Common(const StoragePtr raw) : raw(raw) {} + explicit Common(const IterPtr ptr) : raw(ptr) {} + const StoragePtr operator() () const { return raw; } + Ptr<Quant>& operator++ () { raw++; return FORCE_CONV(Ptr<Quant>, *this); } +#define REL(op) bool operator op (Common const &i) const { return raw op i.raw; } + REL(!=) + REL(==) + REL(<) REL(<=) + REL(>) REL(>=) +#undef REL + + static range_t all() { + BOOST_STATIC_ASSERT((is_same<typename Quant::class_t, + ContinuousPropertyClass>::value)); + return range_t(0, instanceCount); + } + + // return instanceCount; different semantic for continuous and + // discrete quants, so we use two identical function + static constexpr IterPtr numInstances() { + BOOST_STATIC_ASSERT((is_same<typename Quant::class_t, + ContinuousPropertyClass>::value)); + return instanceCount; + } + + static constexpr IterPtr maxInstances() { + BOOST_STATIC_ASSERT((is_same<typename Quant::class_t, + DiscretePropertyClass>::value)); + return instanceCount; + } + + StoragePtr raw; +}; + +// we define the Common<>-instances later used to maintain +// one-source-of-truth albeit circular dependencies +typedef Common<Synapse, maxNeurons * maxSynapsesPerNeuron> CommonSynapse; +typedef Common<Neuron, maxNeurons> CommonNeuron; + +} // NS + +//////////////////////////////////////////////////////////////////////////////// +// CONTINOUS PROPERTIES +//////////////////////////////////////////////////////////////////////////////// + +#define PTR_DEFAULT_CTOR \ + explicit Ptr(const ptr_t raw) : Common(raw) {} + +template<> +struct Ptr<Global> : PtrImpl::Common<Global, 1> { + Ptr() : Common(0) {} + explicit Ptr(const uint64_t raw) : Common(0) { assert(raw == 0); } +// #define REL(op) bool operator op (Ptr &i) { return raw op i.raw; } +// REL(!=) +// REL(==) +// REL(<) REL(<=) +// REL(>) REL(>=) +// #undef REL + + + inline PtrImpl::CommonNeuron::range_t childs() const; +}; + +template<> +struct Ptr<Neuron> : PtrImpl::CommonNeuron { + PTR_DEFAULT_CTOR + + + inline PtrImpl::CommonSynapse::range_t childs() const; +}; + +template<> +struct Ptr<Synapse> : PtrImpl::CommonSynapse { + typedef boost::uint_value_t<maxSynapsesPerNeuron>::least offset_t; + + PTR_DEFAULT_CTOR + Ptr(const Ptr<Neuron> neuron, const offset_t synapse) + : Common(neuron() * maxSynapsesPerNeuron + synapse) {} + + const Ptr<Neuron> extractNeuron() const { + return Ptr<Neuron>(raw / maxSynapsesPerNeuron); + } + const offset_t extractSynapseOffset() const { + return (raw % maxSynapsesPerNeuron); + } +}; + +// we have to declare childs()-functions after childs' Ptr<> + +inline PtrImpl::CommonNeuron::range_t Ptr<Global>::childs() const { + return PtrImpl::CommonNeuron::range_t{ + Ptr<Neuron>{0}, + Ptr<Neuron>{maxNeurons - 1}}; +} + +inline PtrImpl::CommonSynapse::range_t Ptr<Neuron>::childs() const { + return PtrImpl::CommonSynapse::range_t{ + Ptr<Synapse>{*this, 0}, + Ptr<Synapse>{*this, maxSynapsesPerNeuron - 1}}; +} + +//////////////////////////////////////////////////////////////////////////////// +// DISCRETE PROPERTIES +//////////////////////////////////////////////////////////////////////////////// + +template<> +struct Ptr<GlobalMsg> : PtrImpl::Common<GlobalMsg, maxGlobalMsg> { + PTR_DEFAULT_CTOR +}; + +template<> +struct Ptr<Spike> : PtrImpl::Common<Spike, maxSpikes> { + PTR_DEFAULT_CTOR +}; + +template<> +struct Ptr<RandomSpike> : PtrImpl::Common<RandomSpike, maxRandomSpikes> { + PTR_DEFAULT_CTOR +}; + +template<> +struct Ptr<SpikeArrival> : PtrImpl::Common<SpikeArrival, + maxSpikes * maxSynapsesPerNeuron> { + typedef boost::uint_value_t<maxSynapsesPerNeuron>::least offset_t; + + PTR_DEFAULT_CTOR + // HINT: offset refers to the topology table of the neuron + // corresponding to the given spike, _not_ to the synpase offset of + // the receiving neuron + Ptr(const Ptr<Spike> spike, const offset_t offset) + : Common(spike() * maxSynapsesPerNeuron + offset) {} + const Ptr<Spike> extractSpike() const { + return Ptr<Spike>(raw / maxSynapsesPerNeuron); + } +}; + +// Stream I/O + +namespace std { +#define GEN(T) \ + ostream& operator<< (ostream &os, Ptr<T> val) { return os << (uint64_t) val(); } + +GEN(Global) +GEN(Neuron) +GEN(Synapse) +GEN(GlobalMsg) +GEN(Spike) +GEN(RandomSpike) +GEN(SpikeArrival) + +#undef GEN +} + + + +#endif // wOYVH4QhyaNIiefhP4sh9Osro2k diff --git a/core/priority_queue.hpp b/core/priority_queue.hpp new file mode 100644 index 0000000..efcdc7e --- /dev/null +++ b/core/priority_queue.hpp @@ -0,0 +1,146 @@ +#ifndef RIoeyayKWeHMNQr5V3eTPolQ4fQ +#define RIoeyayKWeHMNQr5V3eTPolQ4fQ + +#include <assert.h> +#include <boost/swap.hpp> +#include <inttypes.h> +#include <string.h> + +template<typename Val, typename Payload> +struct PriorityQueue { + // types + typedef Payload payload_t; + + // creation + PriorityQueue(); + PriorityQueue(const PriorityQueue &); + + // access + inline Val& minVal(); + inline Payload& minPayload(); + inline void insert(const Val val, const Payload payload); + inline void removeMin(); + + inline bool isEmpty() const; + size_t getSize() const; // returns size in bytes, _not_ # of elements + + // intern + typedef uint32_t Id; + inline void swap(const Id id1, const Id id2); + void heapify(const Id id); + + // data + Id length; + struct Element { + Val val; + Payload payload; + } heap[0]; + static const size_t elementSize = sizeof(Element); + +}; + +template<typename Val, typename Payload> +PriorityQueue<Val, Payload>::PriorityQueue() { + length = 0; +} + +template<typename Val, typename Payload> +PriorityQueue<Val, Payload>::PriorityQueue(const PriorityQueue &src) { + length = src.length; + memcpy(heap, src.heap, length * sizeof(Element)); +} + + +template<typename Val, typename Payload> +inline Val& PriorityQueue<Val, Payload>::minVal() { + assert(!isEmpty()); + return heap[0].val; +} + +template<typename Val, typename Payload> +inline Payload& PriorityQueue<Val, Payload>::minPayload() { + assert(!isEmpty()); + return heap[0].payload; +} + +template<typename Val, typename Payload> +inline void PriorityQueue<Val, Payload>::insert(const Val val, const Payload payload) { + heap[length].val = val; + heap[length].payload = payload; + length++; + heapify(length-1); +} + +template<typename Val, typename Payload> +void PriorityQueue<Val, Payload>::removeMin() { + length--; + swap(0, length); + heapify(0); +} + +template<typename Val, typename Payload> +inline bool PriorityQueue<Val, Payload>::isEmpty() const { + return (length==0); +} + +template<typename Val, typename Payload> +size_t PriorityQueue<Val, Payload>::getSize() const { + return ((char*) &(heap[length])) - (char*) this; +} + +template<typename Val, typename Payload> +inline void PriorityQueue<Val, Payload>::swap(const Id id1, const Id id2) { + boost::swap(heap[id1], heap[id2]); +} + +template<typename Val, typename Payload> +void PriorityQueue<Val, Payload>::heapify(const Id id) { + Id prev(id), + next((id-1)/2); + + // upward + while (prev != 0 // this is not the root of the tree + && heap[prev].val // and our element can still + < heap[next].val // bubble further up ... + ) { + swap(prev, next); + prev = next; + next = ((next-1)/2); + } + + // downward + while (true) { + next = 2*prev + 1; + + // no child? + if (next >= length) + return; + + // one child? + if (next == length - 1) { + if (heap[next].val < heap[prev].val) { + swap(prev, next); + continue; + } + return; + } + + // two childs! + if ((heap[next].val < heap[next+1].val) && (heap[next].val < heap[prev].val)) { + swap(next, prev); + prev = next; + continue; + } + + if ((heap[next+1].val <= heap[next].val) && (heap[next+1].val < heap[prev].val)) { + swap(next+1, prev); + prev = next+1; + continue; + } + + // we are bigger than both childs + return; + } +} + +#endif // RIoeyayKWeHMNQr5V3eTPolQ4fQ diff --git a/core/property_abbrevations_begin.hpp b/core/property_abbrevations_begin.hpp new file mode 100644 index 0000000..4aaa409 --- /dev/null +++ b/core/property_abbrevations_begin.hpp @@ -0,0 +1,18 @@ +#ifndef YCro2esMYpQbHpc3SnRqHRp4QZI +#define YCro2esMYpQbHpc3SnRqHRp4QZI + +// get a continuous property by name +// * has to have the same quantor type and will be the same instance +// * can only be used within the evolve function of a continuous property +#define _CP(Prop) (Property_Get<Prop>::eval(pc, context, t)) + +// get a discrete property by name +// * can only be used in the correct (...?) apply function +#define _DP(Prop) (Property_Get<Prop>::eval(pc, context)) + +// get a property from a transaction +#define _TP(Prop) (transaction.instance.template get<Prop, typename Prop::type>()) + +#else +#error previous use of property_abbrevation_begin not closed +#endif // YCro2esMYpQbHpc3SnRqHRp4QZI diff --git a/core/property_abbrevations_end.hpp b/core/property_abbrevations_end.hpp new file mode 100644 index 0000000..3df28c3 --- /dev/null +++ b/core/property_abbrevations_end.hpp @@ -0,0 +1,9 @@ +#ifdef YCro2esMYpQbHpc3SnRqHRp4QZI +#undef YCro2esMYpQbHpc3SnRqHRp4QZI + +#undef _DP +#undef _CP + +#else +#error there is no property_abbrevation_begin to close +#endif diff --git a/core/property_access.hpp b/core/property_access.hpp new file mode 100644 index 0000000..19016e9 --- /dev/null +++ b/core/property_access.hpp @@ -0,0 +1,32 @@ +#ifndef VIp1Hrkjsl2bQTTqbsZf9mqNsWY +#define VIp1Hrkjsl2bQTTqbsZf9mqNsWY + +#include "pla_get.hpp" +#include "context.hpp" + +template <typename Prop> +struct Property_Get { + template<typename PropComp, typename Context> + static inline typename Prop::type eval(PropComp &pc, Context context) __attribute__ ((pure)); + + template<typename PropComp, typename Context> + static inline typename Prop::type eval(PropComp &pc, Context context, Time t) __attribute__ ((pure)); +}; + +template <typename Prop> +template<typename PropComp, typename Context> +inline typename Prop::type Property_Get<Prop>::eval(PropComp &pc, Context context) { + PLA_Get<Prop, Context> action(context); + return pc.call(action); +} + +template <typename Prop> +template<typename PropComp, typename Context> +inline typename Prop::type Property_Get<Prop>::eval(PropComp &pc, Context context, Time t) { + PLA_Get<Prop, Context> action(t, context); + return pc.call(action); +} + + + +#endif // VIp1Hrkjsl2bQTTqbsZf9mqNsWY diff --git a/core/property_composition.hpp b/core/property_composition.hpp new file mode 100644 index 0000000..39b6984 --- /dev/null +++ b/core/property_composition.hpp @@ -0,0 +1,35 @@ +#ifndef XDTdAXIBNQWHy4pABnW9tvtXRks +#define XDTdAXIBNQWHy4pABnW9tvtXRks + +#include <boost/mpl/bool.hpp> +#include <boost/utility.hpp> + +#include "pla_get.hpp" +#include "property_list.hpp" + +template <typename PropList> +struct PropertyComposition : boost::noncopyable { + // your endless journey has come to an end. down here lies the + // ACTUAL INSTANCE of the entire simulation! + typedef PropList properties_t; + PL<properties_t> properties; + + /// data access + // call: return value of action is of interest + template<typename Action> + inline typename Action::result_t call(Action &action) { + return properties.call(*this, action); + } + + // cast: fire and forget + template<typename Action> + inline void cast(Action &&action) { + properties.call(*this, action); + } + + void sync() { + properties.sync(); + } +}; + +#endif // XDTdAXIBNQWHy4pABnW9tvtXRks diff --git a/core/property_instance.hpp b/core/property_instance.hpp new file mode 100644 index 0000000..74a1a98 --- /dev/null +++ b/core/property_instance.hpp @@ -0,0 +1,65 @@ +#ifndef m6EAApyWyIDlhjYO7FswFNLRrB0 +#define m6EAApyWyIDlhjYO7FswFNLRrB0 + +#include <boost/static_assert.hpp> + +#include "checkpoint.hpp" +#include "ephermal_checkpoint.hpp" +#include "pointers.hpp" +#include "quant_types.hpp" +#include "string_helpers.hpp" + +namespace PropertyInstanceImpl { + +template < + typename Prop, + bool doWriteResults, + typename Quant = typename Prop::quant, + typename QuantClass = typename Prop::quant::class_t + > struct PropertyInstance_Container; + +template <typename Prop> +struct PropertyInstance_Container< + Prop, true, typename Prop::quant, ContinuousPropertyClass> { + PropertyInstance_Container() : + data(string(Prop::quant::name) + "_" + string(Prop::name), Prop::initialValue) + {} + typedef Checkpoint<typename Prop::type, + Prop::quant::instance_ptr_t::numInstances()> type; + type data; +}; + +template <typename Prop> +struct PropertyInstance_Container< + Prop, false, typename Prop::quant, ContinuousPropertyClass> { + PropertyInstance_Container() : + data(string(Prop::quant::name) + "_" + string(Prop::name), Prop::initialValue) + {} + typedef EphermalCheckpoint<typename Prop::type, + Prop::quant::instance_ptr_t::numInstances()> type; + type data; +}; + +template <typename Prop, bool doWriteResults> +struct PropertyInstance_Container< + Prop, doWriteResults, typename Prop::quant, DiscretePropertyClass> { + PropertyInstance_Container() : + data(string(Prop::quant::name) + "_" + string(Prop::name), + Prop::instance_ptr_t::maxInstances()) + {} + typedef Vector< + typename Prop::type, + 8 * Prop::size, // size in [bits] + boost::mpl::bool_<doWriteResults> + > type; + type data; +}; + +} // namespace PropertyInstanceImpl + +template <typename Prop, bool doWriteResults> +struct PropertyInstance : + PropertyInstanceImpl::PropertyInstance_Container<Prop, doWriteResults> {}; + + +#endif // m6EAApyWyIDlhjYO7FswFNLRrB0 diff --git a/core/property_list.hpp b/core/property_list.hpp new file mode 100644 index 0000000..3fd5ee9 --- /dev/null +++ b/core/property_list.hpp @@ -0,0 +1,94 @@ +#ifndef RAA4DT8yVWXBMqy1rxYiM12MKFA +#define RAA4DT8yVWXBMqy1rxYiM12MKFA + +#include <boost/mpl/assert.hpp> +#include <boost/mpl/empty.hpp> +#include <boost/mpl/front.hpp> +#include <boost/mpl/list.hpp> +#include <boost/mpl/logical.hpp> +#include <boost/mpl/not.hpp> +#include <boost/mpl/pop_front.hpp> +#include <boost/static_assert.hpp> +#include <boost/type_traits/is_same.hpp> + +#include "property_instance.hpp" +#include "template_helpers.hpp" + +namespace PLImpl { + + using namespace boost::mpl; + + // forward decls + template <typename Haystack, typename Needle> struct PL_Contains; + + + /// PL - Property List + + // template <typename Head, bool doWriteResults, typename Tail> + template <typename List> + struct PL { + // compile time data structure (types) + typedef typename front<List>::type head_t; + typedef typename pop_front<List>::type tail_t; + + typedef typename head_t::first prop_t; + typedef typename head_t::second doWriteResult_t; + typedef typename if_<empty<tail_t>, + PL<Void>, + PL<tail_t> + >::type next_t; + + typedef PropertyInstance<prop_t, doWriteResult_t::value > data_t; + + // access methods + // template params: + // - PropComp: the list of all properties + // - Action: the action class to call + template<typename PropComp, typename Action> + inline typename Action::result_t call(PropComp &pc, Action &action) { + typename Action::template local_state_t<prop_t, doWriteResult_t::value> state; + action.pre(pc, data, state); + tail.call<PropComp, Action>(pc, action); + action.post(pc, data, state); + return action.result; + } + + // data members + data_t data; + next_t tail; + + // integrity checks: + // 1. each property may be used only once + BOOST_MPL_ASSERT(( not_< PL_Contains< next_t, prop_t > > )); + // TODO (beauty): dependencies (once they are implemented anywhere) + }; + + // the empty tail + template<> + struct PL<Void> { + typedef Void prop_t; + typedef PL<Void> tail_t; + typedef Void data_t; + + template<typename PropComp, typename Action> + inline typename Action::result_t call(PropComp &pc, Action &action) { + return typename Action::result_t(); + } + }; + + /// PL Template Helpers + template <typename Haystack, typename Needle> + struct PL_Contains : boost::mpl::or_< + boost::is_same<typename Haystack::prop_t, Needle>, + PL_Contains<typename Haystack::next_t, Needle> + > { }; + + template <typename Needle> + struct PL_Contains<PL<Void>, Needle> : boost::mpl::false_ { }; + +}; + +using PLImpl::PL; +using PLImpl::PL_Contains; + +#endif // RAA4DT8yVWXBMqy1rxYiM12MKFA diff --git a/core/quant_types.hpp b/core/quant_types.hpp new file mode 100644 index 0000000..8278f30 --- /dev/null +++ b/core/quant_types.hpp @@ -0,0 +1,181 @@ +#ifndef sKmf7ztuNbBT3ua3iNX4k5rtsg0 +#define sKmf7ztuNbBT3ua3iNX4k5rtsg0 + +#include <boost/mpl/begin.hpp> +#include <boost/mpl/bool.hpp> +#include <boost/mpl/distance.hpp> +#include <boost/mpl/find.hpp> +#include <boost/mpl/joint_view.hpp> +#include <boost/mpl/list.hpp> +#include <boost/mpl/vector.hpp> +#include <boost/type_traits/is_same.hpp> + +#include "template_helpers.hpp" +#include "simlimits.hpp" + +/// definition of Ptr<> template; template specialization +/// in pointers.hpp (included at the end of this file) +template<typename UnknownType> +// struct Ptr { +// DO_NOT_INSTANCE(UnknownType); +// }; +struct Ptr; + +/// definition of the different quantor classes + +struct DiscretePropertyClass { + static const char* const name; +}; +const char* const DiscretePropertyClass::name = "discrete_property"; + +struct ContinuousPropertyClass { + static const char* const name; +}; +const char* const ContinuousPropertyClass::name = "continuous_property"; + +template<typename Wrong> +struct AverageQueueSize { + DO_NOT_INSTANCE(Wrong) +}; + +/// definition of the different quantor types + +#define GEN_QUANT_PROP(nameT, classT, nameS) \ +class nameT { \ +public: \ + typedef Ptr<nameT> instance_ptr_t; \ + typedef classT##PropertyClass class_t; \ + static const char* const name; \ +}; \ +const char* const nameT::name = nameS; + +// EQS: estimated average queue size of related event +#define GEN_DQ_PROP(nameT, classT, nameS, EQS) \ + GEN_QUANT_PROP(nameT, classT, nameS) \ + \ + template<> \ + struct AverageQueueSize<nameT> { \ + static const uint64_t value = EQS; \ + }; + +// discrete properties +GEN_DQ_PROP(GlobalMsg, Discrete, "global_msg", 10); +GEN_DQ_PROP(Spike, Discrete, "spike", maxNeurons); +GEN_DQ_PROP(RandomSpike, Discrete, "rand_spike", maxNeurons); +GEN_DQ_PROP(SpikeArrival, Discrete, "spike_arrival", 1); + +// continuous properties +GEN_QUANT_PROP(Global, Continuous, "global"); +GEN_QUANT_PROP(Neuron, Continuous, "neuron"); +GEN_QUANT_PROP(Synapse, Continuous, "synapse"); + +#undef GEN_QUANT_PROP +#undef GEN_DQ_PROP + +/// quantor type lists +namespace QuantTypeImpl { + using namespace boost::mpl; + + typedef vector< + Global, Neuron, Synapse + > ContinuousQuantorList; + + // discrete properties are sorted by likelyhood of their occuring + // events; also Spike has to have a lower RuntimeID than + // SpikeArrival for simulation correctness during replay! + typedef vector< + Spike, SpikeArrival, RandomSpike, GlobalMsg + > DiscreteQuantorList; + + typedef joint_view< + DiscreteQuantorList, ContinuousQuantorList + > QuantorList; + + /// runtime IDs + + template<typename T> + struct RuntimeID { + static const uint8_t value = distance< + begin<QuantorList>::type, + typename find<QuantorList, T>::type + >::value; + }; +} + +using QuantTypeImpl::ContinuousQuantorList; +using QuantTypeImpl::DiscreteQuantorList; +using QuantTypeImpl::QuantorList; +using QuantTypeImpl::RuntimeID; + +/// parent-child relations + +template<typename Quant> struct QuantChild; + +#define GEN_QC(P,C) \ + template<> struct QuantChild<P> { \ + typedef C type; \ + }; + +GEN_QC(Global, Neuron); +GEN_QC(Neuron, Synapse); +GEN_QC(Spike, SpikeArrival); // useless, potentially dangerous + +#undef GEN_QC + +/// definition of receivers of discrete properties +/// HINT: in case of multiple receivers the lowest quant in the +/// context hierarchy is called + +template<typename NoMatch> +struct QuantDestinationQuant { + DO_NOT_INSTANCE(NoMatch); +}; + +#define GEN_QDQ(Src,Dst) \ +template<> \ +struct QuantDestinationQuant<Src> { \ + typedef Dst type; \ +}; + +/// definition of the events a cont. quant can create; defaults to no +/// events and has to be extended by model description via template +/// specialization + +template<typename ContQ, typename DiscQ> +struct QuantMayEmit { + typedef boost::mpl::false_ result; + bool operator() () { return false; } +}; + +template<typename ContQ, typename DiscQ> +struct QuantEmitDefault : boost::mpl::bool_<false> {}; + +#define GEN_QUANT_EMIT(CQ, DQ, Default) \ + template<> \ + struct QuantMayEmit<CQ, DQ> { \ + typedef boost::mpl::true_ result; \ + bool operator() () { return true; } \ + }; \ + \ + template<> \ + struct QuantEmitDefault<CQ, DQ> : boost::mpl::bool_<Default> {}; + +/// definition if the event linked to a discrete property has a (per +/// event) variable delay; defaults false + +template<typename DiscQ> +struct QuantHasVarDelay { + typedef boost::mpl::false_ result; +}; + +#define GEN_QUANT_HASVARDELAY(DQ) \ + template<> \ + struct QuantHasVarDelay<DQ> { \ + typedef boost::mpl::true_ result; \ +}; + +// we include pointers here, because they rely on the quant classes to +// be defined +#include "pointers.hpp" + +#endif // sKmf7ztuNbBT3ua3iNX4k5rtsg0 diff --git a/core/replay.cpp b/core/replay.cpp new file mode 100644 index 0000000..73f0c44 --- /dev/null +++ b/core/replay.cpp @@ -0,0 +1,99 @@ +#include <assert.h> +#include <errno.h> +#include <iostream> +#include <stdlib.h> + +#include <boost/type_traits/is_same.hpp> + +#include "everything_else.hpp" +#include "pla_getbyname.hpp" +#include "sim_replay.hpp" +#include "system_helpers.hpp" +#include "template_helpers.hpp" +#include "time.hpp" + +#include "model.hpp" + +using namespace std; + +template<typename Prop, typename QuantClass = typename Prop::quant::class_t> +struct Replay; + +template<> +struct Replay<BaseCase, Void> { + virtual void operator() (TimeSpan span, IdList<uint64_t> &ids) { + assert(false); + } +}; + +template<typename Prop> +struct Replay<Prop, ContinuousPropertyClass> : Replay<BaseCase, Void> { + typedef typename Prop::quant Quant; + + virtual void operator() (TimeSpan span, IdList<uint64_t> &raw_ids) { + IdList<typename Quant::instance_ptr_t::ptr_t> ids(raw_ids); + for (auto i : ids) + assert(i < Ptr<Quant>::numInstances()); + SimReplay<Lists::all_ro, Quant> simReplay{ids, span.begin()()}; + + // header + cout << "# " << Quant::name << "\n# time"; + for (auto i : ids) + cout << "\t" << (uint64_t) i; + cout << endl; + + // body + for (TimeSpan::iterator t=span.begin(); t.hasNext(); ++t) { + cout << t()(); + for (auto i : ids) + cout << "\t" << simReplay.template get<Prop>(Ptr<Quant>(i), t()); + cout << endl; + } + } +}; + +template<typename Prop> +struct Replay<Prop, DiscretePropertyClass> : Replay<BaseCase, Void> { + virtual void operator() (TimeSpan span, IdList<uint64_t> &raw_ids) { + cerr << Prop::name << " is a discrete property" << endl; + exit(-1); + } +}; + +int realmain(int argc, char **argv) __attribute__((noinline)); +int realmain(int argc, char **argv) { + if (argc != 6) { + cout << "Usage: " << argv[0] << " property instance start stop delta" << endl; + return -1; + } + + garantueeStackSize(64 * 1024 * 1024); + + // read cmd line params + Replay<BaseCase, Void> *replay(NULL); + { + PropertyComposition<Lists::all_ro> pc; + PLA_GetByName<Replay> pla(argv[1]); + replay = pc.call(pla); + assert(replay != NULL); + } + errno = 0; + char *tail; + IdList<uint64_t> ids(argv[2]); + Time start(strtod(argv[3], &tail)); assert(*tail == 0); + Time stop( strtod(argv[4], &tail)); assert(*tail == 0); + Time delta(strtod(argv[5], &tail)); assert(*tail == 0); + assert(errno == 0); + TimeSpan span(start, stop, delta); + + // replay simulation + (*replay)(span, ids); + + return 0; +} + +int main(int argc, char **argv) { + garantueeStackSize(64 * 1024 * 1024); + assertSimDir(); + return realmain(argc, argv); +} diff --git a/core/report_names.cpp b/core/report_names.cpp new file mode 100644 index 0000000..0243daf --- /dev/null +++ b/core/report_names.cpp @@ -0,0 +1,15 @@ +#include <iostream> + +#include "pla_debug_name.hpp" +#include "property_composition.hpp" + +#include "mempool.hpp" + +#include "model.hpp" + +int main() { + PropertyComposition<Lists::all> allProperties; + PLA_Debug_Name action_debug; + allProperties.call(action_debug); + std::cout << action_debug.result << std::endl; +} diff --git a/core/report_runtimeid.cpp b/core/report_runtimeid.cpp new file mode 100644 index 0000000..0e06afd --- /dev/null +++ b/core/report_runtimeid.cpp @@ -0,0 +1,19 @@ +#include <iostream> + +#include <boost/mpl/for_each.hpp> + +#include "quant_types.hpp" + +using namespace std; + +struct printer { + template<typename T> + void operator() (T x) { + cout << T::name << ":\t" << (int) RuntimeID<T>::value << endl; + } +}; + +int main() +{ + boost::mpl::for_each<QuantorList>(printer()); +} diff --git a/core/report_spacereq.cpp b/core/report_spacereq.cpp new file mode 100644 index 0000000..f9df0c2 --- /dev/null +++ b/core/report_spacereq.cpp @@ -0,0 +1,15 @@ +#include <iostream> + +#include "pla_debug_space.hpp" +#include "property_composition.hpp" + +#include "mempool.hpp" + +#include "model.hpp" + +int main() { + Lists::all allProperties; + PLA_Debug_Space action_debug; + allProperties.call(action_debug); + std::cout << action_debug.generateReport() << std::endl; +} diff --git a/core/rng.hpp b/core/rng.hpp new file mode 100644 index 0000000..a8d88f6 --- /dev/null +++ b/core/rng.hpp @@ -0,0 +1,83 @@ +#ifndef eyNJdhz0jqVQc4zddlEi67woThg +#define eyNJdhz0jqVQc4zddlEi67woThg + +// TODO: use proper PRNG + +#include <stdlib.h> +#include <inttypes.h> +#include <math.h> +#include <boost/random.hpp> +#include <boost/random/uniform_int.hpp> +#include <boost/random/exponential_distribution.hpp> +#include <boost/random/variate_generator.hpp> +#include <boost/static_assert.hpp> + +#include "template_helpers.hpp" + +#include <boost/random/ranlux.hpp> +#include <boost/random/additive_combine.hpp> + +namespace RNG { + typedef boost::ecuyer1988 generator; + struct seed_t { + generator rng; + + seed_t() : rng(0) {} + template<typename T> + explicit seed_t(T x) : rng(x + 17 * x + 257 * x) {} + + seed_t & operator= (seed_t x) { + rng = x.rng; + return *this; + } + template<typename T> + seed_t & operator= (T x) { + rng.seed(x); + return *this; + } + }; + + seed_t next(seed_t last, int amount=1) { + boost::uniform_int<uint64_t> dist; + for (int i=0; i<amount; i++) + dist(last.rng); + return last; + } + + seed_t split(seed_t old) { + boost::uniform_int<uint64_t> dist(0, -1); + uint64_t s = 0; + for (int i=0; i<37; i++) { + s ^= dist(old.rng); + s ^= ~(s << 1); + } + // std::cout << "new seed: " << s << std::endl; + return seed_t(s); + } + + uint32_t integer(seed_t s, uint32_t num) { + boost::uniform_int<uint32_t> gen(0, num-1); + return gen(s.rng); + } + + // returns val \in [0,1) + double equi(seed_t s) { + boost::uniform_01<double> dist; + return dist(s.rng); + } + + double expo(seed_t s, double mean) { + typedef boost::exponential_distribution<double> dist; + boost::variate_generator<generator &, dist> gen(s.rng, dist(1.0 / mean)); + double res = gen(); + return res; + } +} + +namespace std { + std::ostream& operator<< (std::ostream &os, RNG::seed_t s) { + return os << "rand48(" << s.rng << ")"; + } +} + +#endif // eyNJdhz0jqVQc4zddlEi67woThg diff --git a/core/scalar.hpp b/core/scalar.hpp new file mode 100644 index 0000000..cbab5e6 --- /dev/null +++ b/core/scalar.hpp @@ -0,0 +1,58 @@ +#ifndef LOp9uF4R8c3Nq3YZQrpOlN8rCLE +#define LOp9uF4R8c3Nq3YZQrpOlN8rCLE + +#include <inttypes.h> +#include <boost/static_assert.hpp> +#include <string> + +#include "mempool.hpp" + +using std::string; + +template<typename T, long long width = 8 * sizeof(T)> +struct Scalar { + explicit Scalar(const string name); + + inline T & operator() () const; + inline T get() const; + inline void set(const T value) const; + void sync() const; + + // associated memory container + MemPool mem; + + BOOST_STATIC_ASSERT((width > 0)); + +private: + // Scalar(); +}; + +// Implementation +template<typename T, long long width> +Scalar<T, width>::Scalar(const string name) : mem((width + 7) / 8, name) { +} + +template<typename T, long long width> +inline T & Scalar<T, width>::operator() () const { + return *((T*) mem()); +} + + +template<typename T, long long width> +inline T Scalar<T, width>::get() const { + return (*this)(); +} + +template<typename T, long long width> +inline void Scalar<T, width>::set(const T value) const { + // only a single value is in a scalar so we do not require bit-level + // access + (*this)() = value; +} + +template<typename T, long long width> +void Scalar<T, width>::sync() const { + mem.sync(); +} + +#endif // LOp9uF4R8c3Nq3YZQrpOlN8rCLE diff --git a/core/signal_handler.hpp b/core/signal_handler.hpp new file mode 100644 index 0000000..87a1353 --- /dev/null +++ b/core/signal_handler.hpp @@ -0,0 +1,34 @@ +#ifndef WvCIARcg6KvWbOCcoxmM1lc74OI +#define WvCIARcg6KvWbOCcoxmM1lc74OI + +#include <assert.h> +#include <signal.h> + +volatile bool terminationRequested = false; + +void sigIntHandler(int sig) { + terminationRequested = true; +} + +void installSigIntHandler() { + struct sigaction action; + action.sa_handler = sigIntHandler; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + assert(sigaction(SIGINT, &action, NULL) == 0); + +} + +void sigIntExit() { + // restore old SIG INT handler ... + struct sigaction action; + action.sa_handler = SIG_DFL; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + assert(sigaction(SIGINT, &action, NULL) == 0); + + // ... and use it for suicide + kill(getpid(), SIGINT); +} + +#endif // WvCIARcg6KvWbOCcoxmM1lc74OI= diff --git a/core/sim_causality.hpp b/core/sim_causality.hpp new file mode 100644 index 0000000..239a070 --- /dev/null +++ b/core/sim_causality.hpp @@ -0,0 +1,177 @@ +#ifndef GyWue2EL4TyiAsNv0XegfFL4jk +#define GyWue2EL4TyiAsNv0XegfFL4jk + +#include <map> + +#include "sim_replay.hpp" + +// discrete property Egal +struct Egal { + typedef uint8_t type; + typedef SpikeArrival quant; + typedef SpikeArrival::instance_ptr_t instance_ptr_t; + static const uint32_t size = 2; + static const char* const name; +}; +const char* const Egal::name = "Egal"; + +namespace SimCausalityImpl { + +using namespace boost::mpl; +using namespace boost; +namespace MC = ModelConsts; + +template<typename PropList> struct SimCausality; + +template<typename ReplayQuant, typename Quant> +struct MaybeTrackEvent { + template<typename Sim> + void operator() (Sim &sim) { + // we handle every event but only track those affecting neurons + auto &sg = static_cast<SimCausality<typename Sim::PropComp::properties_t>&>(sim); + if (is_same<Quant, SpikeArrival>::value) { + sg.handleSA(); + }else if (is_same<Quant, RandomSpike>::value) { + sg.handleRand(); + }else{ + sim.template handleEvent<Quant>(); + } + } + + typedef MaybeTrackEvent<ReplayQuant, Quant> impl; +}; + +template<typename PropList> +struct SimCausality : public SimReplay<PropList, Neuron, MaybeTrackEvent> { + typedef SimReplay<PropList, Neuron, MaybeTrackEvent> Super; + using Super::queues; + using Super::pc; + using Super::ct; + using Super::handleEvent; + + SimCausality(Time start) + : Super(IdList<uint16_t>((char*) "0-999"), start), + totalMarks{}, + trueMarks{}, + inactiveNeurons(1000), + hasFired{} + { + if (start == Time(0)) { + // special case: at t=0 all neurons are have zero membrane + // voltage and can be used for analysis before firing the first + // time + inactiveNeurons = 0; + for (int i=0; i<1000; i++) + hasFired[i] = true; + } + } + + bool run(const Time until, uint64_t maxEvents) __attribute__((noinline)) { + return Super::run(until, maxEvents); + } + + void handleRand() { + Ptr<Neuron> neuron(queues.get<RandomSpike>().minPayload().dst); + Ptr<SpikeArrival> fakeSA(Index<SpikeArrival>::nil()); + handleDiff(neuron, fakeSA, MC::RandomSpikeWeight, + &SimCausality<PropList>::template handleEvent<RandomSpike>); + } + + void handleSA() { + auto &event(queues.get<SpikeArrival>().minPayload()); + Ptr<SpikeArrival> sa (event.src.get<0>()); + Ptr<Synapse> syn (event.src.get<1>()); + Ptr<Neuron> neuron(event.dst); + PLA_Get<Weight> getWeight (ct, syn); + Weight::type weight(pc.call(getWeight)); + handleDiff(neuron, sa, weight, + &SimCausality<PropList>::template handleEvent<SpikeArrival>); + } + + void handleDiff(Ptr<Neuron> neuron, Ptr<SpikeArrival> sa, Voltage::type diff, + bool (SimCausality<PropList>::*handleEvent)()) { + PLA_Get<Voltage> getVoltage (ct, neuron); + PLA_Get<IPCoeff1> getIPCoeff1(ct, neuron); + Voltage::type preEventVoltage(pc.call(getVoltage)); + IPCoeff1::type ipCoeff1 (pc.call(getIPCoeff1)); + bool evokedSpike = (this->*handleEvent)(); + auto &openExc(this->openExc[neuron()]); + auto &openInh(this->openInh[neuron()]); + + if (!hasFired[neuron()]) { + if (evokedSpike) { + hasFired[neuron()] = true; + inactiveNeurons--; + if (!inactiveNeurons) + lastNeuronActivation = ct; + } + return; + } + + if (evokedSpike) { + // check exc. list against voltage diff + Time wntGap = ct + wntNorm(diff + preEventVoltage + - MC::FireThreshold - ipCoeff1); + for (auto i = openExc.lower_bound(wntGap); + i != openExc.end();) { + mark(i->second, 0); + openExc.erase(i++); + } + + // mark own spike + mark(sa, 0); + + // mark remainig inh/exc spikes as q>0, clear buffers + for (auto i : openExc) mark(i.second, 1); openExc.clear(); + for (auto i : openInh) mark(i.second, 1); openInh.clear(); + }else{ + if (diff < 0) { + // add to inh. list + Time wnt = ct + wntNorm(- diff); + openInh.insert(std::make_pair(wnt, sa)); + }else{ + // add to exc. list + Time wntSelf = ct + wntNorm(diff); + openExc.insert(std::make_pair(wntSelf, sa)); + + // check inh. list against voltage gap + PLA_Get<Voltage> pla_get(ct, neuron); + Voltage::type voltage = pc.call(pla_get); + Time wntGap = ct + wntNorm(MC::FireThreshold + ipCoeff1 - voltage); + for (auto i = openInh.lower_bound(wntGap); + i != openInh.end();) { + mark(i->second, 0); + openInh.erase(i++); + } + } + } + } + + void mark(Ptr<SpikeArrival> ptr, bool m) { + bool isSA = ptr() != Index<SpikeArrival>::nil(); + totalMarks[isSA]++; + trueMarks[isSA] += !m; + if (isSA) + egalPC.cast(PLA_Set<Egal>(ptr, 1+m)); // unset values are encoded as zero + } + + Time wntNorm(Voltage::type diff) { + return MC::Tau_Voltage * log(diff); + } + + PropertyComposition<boost::mpl::list< + boost::mpl::pair<Egal, boost::mpl::bool_<true>> + >> egalPC; + uint64_t totalMarks[2], + trueMarks[2]; + uint16_t inactiveNeurons; + Time lastNeuronActivation; + bool hasFired[maxNeurons]; + std::multimap<Time, Ptr<SpikeArrival>> openInh[maxNeurons], openExc[maxNeurons]; +}; + +} // NS + +using SimCausalityImpl::SimCausality; + +#endif // GyWue2EL4TyiAsNv0XegfFL4jk diff --git a/core/sim_loop.hpp b/core/sim_loop.hpp new file mode 100644 index 0000000..0ef748d --- /dev/null +++ b/core/sim_loop.hpp @@ -0,0 +1,218 @@ +#ifndef Lh5wJREkwqXIDpptCEqbkLO3ed4 +#define Lh5wJREkwqXIDpptCEqbkLO3ed4 + +#include <boost/tuple/tuple.hpp> +#include <boost/mpl/joint_view.hpp> + +#include "context.hpp" +#include "event.hpp" +#include "evolve.hpp" +#include "index.hpp" +#include "index_global.hpp" +#include "index_randomspike.hpp" +#include "index_spike.hpp" +#include "index_spike_arrival.hpp" +#include "multi_queue.hpp" +#include "pla_apply.hpp" +#include "pla_evolve.hpp" +#include "pla_generate.hpp" +#include "pla_sync.hpp" +#include "time.hpp" +#include "topology.hpp" +#include "type_set.hpp" + +namespace SimLoopImpl { + +using namespace boost; +using namespace boost::mpl; + +template<typename PropList> +struct SimLoop { + typedef PropertyComposition<PropList> pc_t; + + SimLoop() + : indices(), + queues() + {} + + bool run(const Time until, uint64_t maxEvents) __attribute__((noinline)) { + Time old(queues.min()); + while (queues.min() <= until && maxEvents) { + assert(old <= queues.min()); + old = queues.min(); + switch (queues.minType()) { +#define HT(T) case RuntimeID<T>::value: handleEvent<T>(); break; + HT(SpikeArrival); + HT(Spike); + HT(RandomSpike); + HT(GlobalMsg); +#undef HT + default: assert(false); + } + maxEvents--; + } + return maxEvents; + } + + template<typename Quant> + inline void handleEvent() { + typedef typename QuantDestinationQuant<Quant>::type DstQuant; + Time ct(queues.min()); + Event<Quant> &event = queues.get<Quant>()(ct).minPayload(); + Ptr<DstQuant> dst(event.dst(topology)); + + // deliever the event and check if to create new events + DelieverContext<Quant, DstQuant> delieverContext(event.instance(), dst); + PLA_Apply<Quant, DstQuant, pc_t> apply(ct, delieverContext); + pc.call(apply); + + // if we create any events + if (apply.generateAny()) { + // evolve all entities which depend on our pre-event values + Evolve<DstQuant>()(pc, ct, dst); + + // compute delay values (they require access to the pre- and + // post-event values and can thus only be computed here) + apply.computeDelay(pc); + } + + // commit data calculated during apply + // this is necessary because childs are only eventually evolved, + // but require the pre-event values of the instance we are + // committing to now + apply.commit(pc); + + // generate events and compute discrete properties +#define MGE(DQ) \ + if (apply.template generate<DQ>()) \ + generateEvent<DstQuant, DQ, Quant> (ct, dst, apply); + + MGE(GlobalMsg); + MGE(Spike); + MGE(RandomSpike); +#undef MGE + if (apply.template generate<SpikeArrival>()) + generateSAEvent(ct, event, apply); + + if (boost::is_same<Quant, Spike>::value) { + // reinsert spike to get to the next neuron; also handles + // removing current min element from the queue (because of a + // race condition) + reinsertSpike(ct, FORCE_CONV(Event<Spike>, event)); + }else{ + queues.removeLocalMin<Quant>(ct); + } + } + + template<typename ContQuant, typename EventQuant, typename SrcEvQuant> + inline void generateEvent(Time ct, + typename ContQuant::instance_ptr_t src, + PLA_Apply<SrcEvQuant, ContQuant, pc_t> &apply) { + // generate new discrete quant instance + Time eventTime = ct + apply.template getDelay<EventQuant>(); + Ptr<EventQuant> ptrNE(indices.get<Index<EventQuant>>().add(ct, eventTime, src)); + EmitContext<ContQuant, EventQuant> emitContext(src, ptrNE); + PLA_Generate<ContQuant, EventQuant, indices_t, queues_t> + pla_gen(ct, emitContext, indices, queues); + pc.call(pla_gen); + + // generate new event + if (likely((not TopologicalEventDiscard<ContQuant, EventQuant>()(topology, src)))) + queues.insert + (ct, + eventTime + TopologicalTimeOffset<ContQuant, EventQuant>()(topology, src), + Event<EventQuant>(eventTime, src, ptrNE)); + } + + template<typename EventT, typename ContQuant, typename SrcEvQuant> + inline void generateSAEvent(Time ct, + EventT &rawEvent, + PLA_Apply<SrcEvQuant, ContQuant, pc_t> &apply) { + // garantuee that we are called by spike event and cast alike + assert((boost::is_same<EventT, Event<Spike>>::value)); + assert((boost::is_same<ContQuant, Synapse>::value)); + Event<Spike> &event(FORCE_CONV(Event<Spike>, rawEvent)); + Ptr<Synapse> src(event.dst(topology)); + + // calculate the SA-ptr + Ptr<SpikeArrival> ptrNE(event.instance(), event.offset); + + // generate all SA properties (except voltage difference) + EmitContext<Synapse, SpikeArrival> emitContext(src, ptrNE); + PLA_Generate<Synapse, SpikeArrival, indices_t, queues_t> + pla_gen(ct, emitContext, indices, queues); + pc.call(pla_gen); + + // generate event and add to queues + queues.insert(ct, ct, Event<SpikeArrival>(src, ptrNE)); + } + + inline void reinsertSpike(Time ct, Event<Spike> &event) { + // TODO (optimize): reuse the current event + Event<Spike>::offset_t offset(event.offset + 1); + + if (unlikely((topology.synapse(event.src, offset)() == Topology::nil()()))) { + queues.removeLocalMin<Spike>(ct); + }else{ + // create new event (depends on old) + Event<Spike> newEvent + (event.baseTime, + event.src, + event.spike, + offset); + + // delete old event; this must happen before insertion of the + // new event to avoid race in case of equal times + queues.removeLocalMin<Spike>(ct); + + // insert new event + queues.insert + (ct, + newEvent.baseTime + topology.time(newEvent.src, offset), + newEvent); + } + } + + void sync() { + // TODO: sync all or remove method + pc.cast(PLA_Sync{}); + indices.get<Index<GlobalMsg>>().sync(); + queues.get<GlobalMsg>().sync(); + } + + Time currentTime() { + return queues.min(); + } + + /// persistant data storage (holds every mmap'ed data structure) + + // properties + pc_t pc; + Topology topology; + + // indices + template<typename T> struct index_from_type { + typedef Index<T> type; + }; + typedef typename boost::mpl::transform< + DiscreteQuantorList, + index_from_type<boost::mpl::_> + >::type indices_list_t; + typedef typename UnpackTypeList<TypeSet, indices_list_t>::type indices_t; + indices_t indices; + + // event queues + typedef MultiQueue< + Time, Event, DiscreteQuantorList + > queues_t; + queues_t queues; + + // TODO (beauty): add checks for proper r/w status in pc_t (all + // writable) +}; + +} // Namespace SimLoopImpl + +using SimLoopImpl::SimLoop; + +#endif // Lh5wJREkwqXIDpptCEqbkLO3ed4 diff --git a/core/sim_replay.hpp b/core/sim_replay.hpp new file mode 100644 index 0000000..c34e216 --- /dev/null +++ b/core/sim_replay.hpp @@ -0,0 +1,268 @@ +#ifndef BjxF7IwCJEpR8eQGIzMk04pwFg +#define BjxF7IwCJEpR8eQGIzMk04pwFg + +#include <algorithm> +#include <boost/tuple/tuple.hpp> +#include <boost/mpl/set.hpp> +#include <boost/mpl/contains.hpp> + +#include "context.hpp" +#include "event.hpp" +#include "evolve.hpp" +#include "index.hpp" +#include "index_global.hpp" +#include "index_spike.hpp" +#include "index_spike_arrival.hpp" +#include "filter.hpp" +#include "multi_queue.hpp" +#include "pla_apply.hpp" +#include "pla_evolve.hpp" +#include "pla_generate.hpp" +#include "pla_initbycopy.hpp" +#include "pla_sync.hpp" +#include "time.hpp" +#include "topology.hpp" +#include "type_set.hpp" + +#include "model.hpp" + +namespace SimReplayImpl { + +using namespace boost; +using namespace boost::mpl; + +// TODO: remove (is redundant code) +template <typename Quant> struct CalcDPtr { + typedef Ptr<Quant> type; +}; +template <> struct CalcDPtr<SpikeArrival> { + typedef boost::tuple<Ptr<SpikeArrival>, Ptr<Synapse>> type; +}; + +/// factory defining which discrete quants we have to consider + +template<typename ReplayQuant> +struct MultiQueueFactory; + +template<> +struct MultiQueueFactory<Global> { + typedef list<GlobalMsg> dependencies; + typedef MultiQueue<Time, FilterPayload, dependencies, FilterContainer> type; + typedef IdList<Ptr<Global>::ptr_t> id_list_t; + static type instance(id_list_t &, const Time time) { + return type(time); + } +}; + +template<> +struct MultiQueueFactory<Neuron> { + typedef list<GlobalMsg, Spike, RandomSpike, SpikeArrival> dependencies; + typedef MultiQueue<Time, FilterPayload, dependencies, FilterContainer> type; + typedef IdList<Ptr<Neuron>::ptr_t> id_list_t; + static type instance(id_list_t ids, const Time time) { + return type(Filter<GlobalMsg> (time), + Filter<Spike> (ids, time), + Filter<RandomSpike> (ids, time), + Filter<SpikeArrival>(ids, time)); + } +}; + +template<> +struct MultiQueueFactory<Synapse> : MultiQueueFactory<Neuron> { + typedef IdList<Ptr<Synapse>::ptr_t> id_list_t; + static type instance(id_list_t ids_synapse, Time time) { + MultiQueueFactory<Neuron>::id_list_t ids_neuron; + for (auto syn : ids_synapse) + ids_neuron.insert(Ptr<Synapse>(syn).extractNeuron()()); + return MultiQueueFactory<Neuron>::instance(ids_neuron, time); + } +}; + +/// template brainfuck to prevent instancing unnecessary handleEvents<>() + +template<typename ReplayQuant, typename Quant> +struct MaybeHandleEvent { + template<typename Q> struct HandleEvent { + template<typename Sim> void operator() (Sim &sim) { + sim.template handleEvent<Q>(); + } + }; + + struct IgnoreEvent { + template<typename Sim> void operator() (Sim &sim) {} + }; + + typedef typename contains< + typename MultiQueueFactory<ReplayQuant>::dependencies, + Quant>::type isRelevant; + typedef typename if_<isRelevant, + HandleEvent<Quant>, + IgnoreEvent + >::type impl; +}; + + +/// extract neurons from arbitrary id list +template<typename Quant> struct ExtractNeuronIDs; + +template<> +struct ExtractNeuronIDs<Global> { + IdList<Ptr<Neuron>::ptr_t> operator() (IdList<Ptr<Global>::ptr_t> &) { + return IdList<Ptr<Neuron>::ptr_t>(); + } +}; + +template<> +struct ExtractNeuronIDs<Neuron> { + IdList<Ptr<Neuron>::ptr_t> operator() (IdList<Ptr<Neuron>::ptr_t> &src) { + return src; + } +}; + +template<> +struct ExtractNeuronIDs<Synapse> { + IdList<Ptr<Neuron>::ptr_t> operator() (IdList<Ptr<Synapse>::ptr_t> &src) { + IdList<Ptr<Neuron>::ptr_t> res; + for (auto id : src) + res.insert(Ptr<Synapse>(id).extractNeuron()()); + return res; + } +}; + +/// the precious replay + +template<typename PropList, typename ReplayQuant, + template<class, class> class EventHandler = MaybeHandleEvent> +struct SimReplay { + typedef PropertyComposition<PropList> PropComp; + typedef Checkpoint<Void, 1> Cp; + typedef IdList<typename Ptr<ReplayQuant>::ptr_t> id_list_t; + typedef IdList<typename Ptr<Neuron>::ptr_t> neuron_list_t; + + SimReplay(id_list_t ids, Time start) + // we need the previous checkpoint interval + : ct(Cp::interval() * (std::max((const uint32_t) 1, Cp::binTime(start)) - 1)), + ids(ids), + neurons(ExtractNeuronIDs<ReplayQuant>()(ids)), + queues(std::move(MultiQueueFactory<ReplayQuant>::instance(ids, ct))) + { + // init values of our quant + pc.cast(PLA_InitByCopy{ct}); + + // forward precisely to the request time + run(start, -1); + } + + template<typename Prop> + typename Prop::type get(Ptr<ReplayQuant> id, Time t) { + // TODO (maybe): allow to read all calculated values, not just + // those of the ReplayQuant (e.g. the synapses properties of a + // simulated neuron) + assert(t >= ct); + assert(ids.count(id())); + run(t, -1); + typedef ContinuousContext<ReplayQuant> Ctx; + PLA_Get<Prop, Ctx> pla_get(t, Ctx(id)); + return pc.call(pla_get); + } + + bool run(const Time until, uint64_t maxEvents) { + assert(until != Time::never()); + while (queues.min() <= until && maxEvents) { + ct = queues.min(); + switch (queues.minType()) { +#define HT(T) case RuntimeID<T>::value: typename EventHandler<ReplayQuant, T>::impl()(*this); break; + HT(SpikeArrival); + HT(Spike); + HT(RandomSpike); + HT(GlobalMsg); +#undef HT + default: assert(false); + } + --maxEvents; + } + return maxEvents; + } + + template<typename Quant> + inline bool handleEvent() { + typedef typename QuantDestinationQuant<Quant>::type DstQuant; + typename CalcDPtr<Quant>::type src(queues.get<Quant>().minPayload().src); + Ptr<DstQuant> dst(queues.get<Quant>().minPayload().dst); + + // deliever the event and check if to create new events + DelieverContext<Quant, DstQuant> delieverContext{src, dst}; + PLA_Apply<Quant, DstQuant, PropComp> apply(ct, delieverContext); + pc.call(apply); + + // create events + if (apply.generateAny()) { + // determine how much we have to simulate: + if (is_same<ReplayQuant, Global>::value) { + // - global replay don't needs any childs + pc.cast(PLA_Evolve<Global>{ContinuousContext<Global>{Ptr<Global>{}}, ct}); + }else{ + if (is_same<DstQuant, Global>::value) { + // - neuron/synapse replay needs global and selected neurons + for (auto id : neurons) + Evolve<Neuron>()(pc, ct, Ptr<Neuron>{id}); + pc.cast(PLA_Evolve<Global>{ContinuousContext<Global>{Ptr<Global>{}}, ct}); + }else{ + // - neuron/synapse evolve happens as usual + Evolve<DstQuant>()(pc, ct, dst); + } + } + } + // commit data calculated during apply + // this is necessary because childs are only eventually evolved, + // but require the pre-event values of the instance we are + // committing to now + apply.commit(pc); + + // call the continuous property related post-event triggers to + // update state +#define HGE(Q) \ + if (apply.template generate<Q>()) \ + handleEventGeneration<DstQuant, Q, Quant>(ct, dst, apply); + + HGE(GlobalMsg); + HGE(Spike); + HGE(RandomSpike); + HGE(SpikeArrival); +#undef HGE + + queues.template removeMin<Quant>(ct); + + return apply.template generate<Spike>(); + } + + template<typename ContQuant, typename EventQuant, typename SrcEvQuant> + inline void handleEventGeneration(Time ct, Ptr<ContQuant> src, + PLA_Apply<SrcEvQuant, ContQuant, PropComp> &) + { + Void nix; + pc.cast(PLA_Generate<ContQuant, EventQuant, Void, Void> + (ct, EmitContext<ContQuant, EventQuant>(src, Ptr<EventQuant>(-1)), + nix, nix)); + } + + Time currentTime() { + return ct; + } + + /// ephermal state + Time ct; + id_list_t ids; + neuron_list_t neurons; + typename MultiQueueFactory<ReplayQuant>::type queues; + PropComp pc; + + /// persistant data storage (holds every mmap'ed data structure) + //Topology topology; +}; + +} // Namespace ReplayImpl + +using SimReplayImpl::SimReplay; + +#endif // BjxF7IwCJEpR8eQGIzMk04pwFg diff --git a/core/simlimits.hpp b/core/simlimits.hpp new file mode 100644 index 0000000..6680bcd --- /dev/null +++ b/core/simlimits.hpp @@ -0,0 +1,18 @@ +#ifndef q9BbYVk1ebyTzHeB6vbWj8BXsIA +#define q9BbYVk1ebyTzHeB6vbWj8BXsIA + +#include <inttypes.h> + +const uint64_t maxNeurons = 2048; // 2^11 +const uint64_t maxSpikes = 1073741824; // 2^30 +const uint64_t maxRandomSpikes = 1073741824; // 2^30 +const uint64_t maxGlobalMsg = 1073741824; // 2^30 +const uint8_t maxSynapsesPerNeuron = 128; // 2^7 + +const uint64_t maxCheckpoints = 1024; // 2^10 +//const uint64_t maxCheckpoints = 1048576; // 2^20 +const double checkpointInterval = 10.0; + +const uint64_t numActualNeurons = 1000; // all above are abused + +#endif // q9BbYVk1ebyTzHeB6vbWj8BXsIA diff --git a/core/simulate.cpp b/core/simulate.cpp new file mode 100644 index 0000000..7205308 --- /dev/null +++ b/core/simulate.cpp @@ -0,0 +1,77 @@ +#include <boost/mpl/list.hpp> +#include <boost/mpl/pair.hpp> +#include <iostream> + +#include "everything_else.hpp" +#include "perftools.hpp" +#include "signal_handler.hpp" +#include "sim_loop.hpp" + +#include "mempool.hpp" + +#include "model.hpp" + +using namespace std; + +int main(int argc, char **argv) { + // init sim env + const uint64_t eventsPerChunk = 100000; + uint64_t totalEventsProcessed(0); + installSigIntHandler(); + assertSimDir(); + + // parse command line args + assert(argc == 2); + Time until(atof(argv[1])); + + { + SimLoop<Lists::all> sim; + + Time startTime(sim.currentTime()), oldTime(startTime); + cout << "simulating from " << oldTime() + << " to " << until() << endl; + + // run for requested time + Timer timerAll; + while (oldTime < until && !terminationRequested) { + uint64_t eventsProcessed = eventsPerChunk - sim.run(until, eventsPerChunk); + totalEventsProcessed += eventsProcessed; + Time newTime(sim.currentTime()), + dt(newTime - oldTime); + cout << "\r\033[K" + << newTime() << "\t" + << eventsProcessed / dt() << " Hz(v)\t" + << totalEventsProcessed / timerAll.diff() << " Hz(p)\t" + << totalEventsProcessed << " events\t"; + if (newTime < until) + cout << "ETA " << ceil( (until() - newTime()) + / (newTime() - startTime()) + * timerAll.diff()); + cout << flush; + oldTime = newTime; + } + + if (terminationRequested) { + cout << " ... received SIGINT, terminating" << endl; + }else{ + cout << "\r\033[K"; + } + + // print summary + cout << "simulated " << totalEventsProcessed << " events " + << "in " << timerAll.diff() << " s " + << "(" << totalEventsProcessed / timerAll.diff() << " Hz(p))" + << endl; + + // sync to disk + cout << "syncing ... " << flush; + Timer timerSync; + sim.sync(); + cout << "done (" << timerSync.diff() << "s)" << endl; + + if (terminationRequested) + sigIntExit(); + } + + return 0; +} diff --git a/core/spike_in.cpp b/core/spike_in.cpp new file mode 100644 index 0000000..73a5649 --- /dev/null +++ b/core/spike_in.cpp @@ -0,0 +1,64 @@ +#include <assert.h> +#include <errno.h> +#include <iostream> +#include <stdlib.h> + +#include "everything_else.hpp" +#include "filter.hpp" +#include "index.hpp" +#include "index_spike.hpp" +#include "multi_queue.hpp" + +using std::cout; +using std::endl; +using boost::make_tuple; + +int main(int argc, char **argv) { + // read cmd line params + if (argc != 4) { + cout << "Usage: " << argv[0] << " instance start until" << endl; + return -1; + } + errno = 0; + char *tail; + IdList<Ptr<Neuron>::ptr_t> ids(argv[1]); + const Time start(strtod(argv[2], &tail)); assert(*tail == 0); + const Time until(strtod(argv[3], &tail)); assert(*tail == 0); + assert(errno == 0); + for (auto id : ids) + assert(id < Ptr<Neuron>::numInstances()); + + assertSimDir(); + + { + MultiQueue< + Time, FilterPayload, + boost::mpl::list<SpikeArrival, RandomSpike>, + FilterContainer> queues(Filter<SpikeArrival>(ids, start), + Filter<RandomSpike> (ids, start)); + + // output + Time ct(start); + cout << "# time\tsrc\tdst" << endl; + while ((ct = queues.min()) <= until) { + cout << ct() << '\t'; + switch (queues.minType()) { + case RuntimeID<SpikeArrival>::value: + cout << queues.get<SpikeArrival>().index.src + (queues.get<SpikeArrival>().minPayload().src.get<0>().extractSpike()()) + << '\t' << queues.get<SpikeArrival>().minPayload().dst(); + queues.removeMin<SpikeArrival>(ct); + break; + case RuntimeID<RandomSpike>::value: + cout << "R\t" << queues.get<RandomSpike>().minPayload().dst(); + queues.removeMin<RandomSpike>(ct); + break; + default: + assert(false); + } + cout << endl; + } +} + + return 0; +} diff --git a/core/spike_out.cpp b/core/spike_out.cpp new file mode 100644 index 0000000..2626353 --- /dev/null +++ b/core/spike_out.cpp @@ -0,0 +1,46 @@ +#include <assert.h> +#include <errno.h> +#include <iostream> +#include <stdlib.h> + +#include "everything_else.hpp" +#include "index.hpp" +#include "index_spike.hpp" + +#include "mempool.hpp" + +using namespace std; + +int main(int argc, char **argv) { + // read cmd line params + assert(argc == 4); + errno = 0; + char *tail; + Ptr<Neuron> addr(strtoul(argv[1], &tail, 10)); assert(*tail == 0); + Time start(strtod( argv[2], &tail)); assert(*tail == 0); + Time stop(strtod( argv[3], &tail)); assert(*tail == 0); + assert(errno == 0); + assert(addr() < Ptr<Neuron>::numInstances()); + + assertSimDir(); + + { + // go to first spike with time >= start + Index<Spike> idx; + Ptr<Spike>::ptr_t cur(idx.first(start, addr)); + + // no spike found? + if (cur == idx.nil()) + return 0; + + // output + cout << "# time of outgoing spikes from neuron " << addr() + << " between " << start() << " and " << stop() << endl; + while (cur != idx.nil() && idx.time(cur) <= stop) { + cout << idx.time(cur) << endl; + cur = idx.next(cur); + } + } + + return 0; +} diff --git a/core/string_helpers.hpp b/core/string_helpers.hpp new file mode 100644 index 0000000..a35448b --- /dev/null +++ b/core/string_helpers.hpp @@ -0,0 +1,36 @@ +#ifndef M6lbZdsknZWfvEr3i3hoi7EZO8M +#define M6lbZdsknZWfvEr3i3hoi7EZO8M + +#include <math.h> +#include <sstream> +#include <string> + +template<int> struct SISuffix { }; + +const char microSISuffixName[6] = { 'm', '~', 'n', 'p', 'f', 'a' }; +const char macroSISuffixName[7] = { 'K', 'M', 'G', 'T', 'P', 'E', 'Y' }; + +int calcSISuffixId(double val, int base=1000) { + if (val >= base) return 1 + calcSISuffixId(val / base); + if (val < 1) return -1 + calcSISuffixId(val * base); + return 0; +} + +std::string SISuffixify(double val, int base=1000) { + int id = calcSISuffixId(val, base); + assert(id >= -6); + assert(id <= 7); + + std::ostringstream result; + + result << val / pow(base, id) << " "; + if (id < 0) + result << microSISuffixName[-id - 1]; + if (id > 0) + result << macroSISuffixName[ id - 1]; + + return result.str(); +} + + +#endif // M6lbZdsknZWfvEr3i3hoi7EZO8M diff --git a/core/system_helpers.hpp b/core/system_helpers.hpp new file mode 100644 index 0000000..75e0d1d --- /dev/null +++ b/core/system_helpers.hpp @@ -0,0 +1,15 @@ +#ifndef AWHaeL8KnNSHrAOreOTeQZXxBG8 +#define AWHaeL8KnNSHrAOreOTeQZXxBG8 + +#include <sys/resource.h> + +void garantueeStackSize(rlim_t size) { + struct rlimit rl; + assert(getrlimit(RLIMIT_STACK, &rl) == 0); + if (rl.rlim_cur < size) { + rl.rlim_cur = size; + assert(setrlimit(RLIMIT_STACK, &rl) == 0); + } +} + +#endif // AWHaeL8KnNSHrAOreOTeQZXxBG8 diff --git a/core/template_helpers.hpp b/core/template_helpers.hpp new file mode 100644 index 0000000..01a27b5 --- /dev/null +++ b/core/template_helpers.hpp @@ -0,0 +1,121 @@ +#ifndef LY1b9novVl8oDseRTw8dH5lzpNo +#define LY1b9novVl8oDseRTw8dH5lzpNo + +#include <boost/static_assert.hpp> +#include <boost/type_traits/is_empty.hpp> +#include <boost/type_traits/is_same.hpp> +#include <boost/type_traits/is_fundamental.hpp> +#include <boost/mpl/unpack_args.hpp> +#include <boost/mpl/list.hpp> +#include <boost/mpl/vector.hpp> +#include <boost/mpl/deref.hpp> +#include <boost/mpl/next.hpp> +#include <boost/mpl/begin.hpp> +#include <boost/mpl/end.hpp> +#include <boost/mpl/front.hpp> +#include <boost/mpl/pop_front.hpp> + + +#define STATIC_ASSERT_IS_POWER_OF_TWO(x) BOOST_STATIC_ASSERT( !(x & (x-1)) ); + +template<typename T, long long numerator, long long denominator = 1> +struct StaticVal { + inline static const T value() { return T(numerator) / T(denominator); } +}; + +// forcing a type conversion of non-ptr types +#define FORCE_CONV(Type, Val) (*(reinterpret_cast<Type *>(& Val))) + +// generate a constructor which fails when called +// TODO (beauty): catch error after optimization, before runtime +// (e.g. by linking to a forbidden symbol) +#define GEN_ANYCAST_CTOR_3(name) \ + template<class T1, class T2, class T3> name(T1 t1, T2 t2, T3 t3) + +// a void you may return +struct Void { + typedef Void type; // for metaprogramming brainfucks + typedef Void con_arg_t; +}; + +// just to beautify some code +struct BaseCase {}; + +// prevent instanciation of default or unimplemented cases +#define DO_NOT_INSTANCE(T) BOOST_STATIC_ASSERT((false && boost::is_same<T,T>::value)); + +// forward decl of a function which will never exist (allows showing +// errors at link time: later than DO_NOT_INSTANCE but before +// assert(false) +void oOoOo_LINK_TIME_ASSERTION_FAILED_oOoOo() __attribute__ ((noreturn)); +#define DO_NOT_CALL {oOoOo_LINK_TIME_ASSERTION_FAILED_oOoOo();} + +namespace UnpackTypeListImpl { + +using namespace boost::mpl; + +template<template<typename... T> class Tgt, + typename Iter, typename EndIter, + typename... NewList> +struct Action { + typedef typename Action<Tgt, + typename next<Iter>::type, EndIter, + NewList..., + typename deref<Iter>::type + >::type type; +}; + +template<template<typename... T> class Tgt, typename EndIter, typename... List> +struct Action<Tgt, EndIter, EndIter, List...> { + typedef Tgt<List...> type; +}; + +} // NS + +// unpack a boost::mpl sequence into a variadic type list +template<template<typename... T> class Tgt, + typename Seq, + typename... NewList> +struct UnpackTypeList { + typedef typename UnpackTypeListImpl + ::Action<Tgt, + typename boost::mpl::begin<Seq>::type, + typename boost::mpl::end<Seq>::type, + NewList...>::type type; +}; + +// create a unique class from a type, given a variadic garbage type +// list; if the type is an empty struct it is not stored but created +// upon every request +// TODO: access functions (right now the stored values are only +// accessed using outraging pointer casts) +template<typename T, bool isEmpty, bool isFund, typename... Garbage> struct MakeUniqueImpl; + +template<typename T, typename... Garbage> +struct MakeUnique { + typedef MakeUniqueImpl<T, boost::is_empty<T>::value, + boost::is_fundamental<T>::value, Garbage...> type; +}; + +template<typename T, typename... Garbage> +struct MakeUniqueImpl<T, true, false, Garbage...> { + MakeUniqueImpl(const T &v) {} + MakeUniqueImpl(T &&v) {} + MakeUniqueImpl() = default; +}; + +template<typename T, typename... Garbage> +struct MakeUniqueImpl<T, false, true, Garbage...> { + MakeUniqueImpl(const T &v) : v(v) {} + MakeUniqueImpl(T &&v) : v(v) {} + MakeUniqueImpl() = default; + T v; +}; + +template<typename T, typename... Garbage> +struct MakeUniqueImpl<T, false, false, Garbage...> : protected T { + MakeUniqueImpl(T&& a) : T(std::move(a)) {} + MakeUniqueImpl() = default; +}; + +#endif // LY1b9novVl8oDseRTw8dH5lzpNo diff --git a/core/test_.cpp b/core/test_.cpp new file mode 100644 index 0000000..9c14384 --- /dev/null +++ b/core/test_.cpp @@ -0,0 +1,45 @@ +#include <set> +#include <boost/static_assert.hpp> +#include <boost/mpl/and.hpp> +#include <boost/mpl/assert.hpp> +#include <boost/mpl/at.hpp> +#include <boost/mpl/empty.hpp> +#include <boost/mpl/erase_key.hpp> +#include <boost/mpl/front.hpp> +#include <boost/mpl/if.hpp> +#include <boost/mpl/map.hpp> +#include <boost/mpl/pair.hpp> +#include <boost/mpl/transform.hpp> +#include <boost/mpl/list.hpp> +#include <boost/tuple/tuple.hpp> +#include <boost/type_traits/is_same.hpp> +#include <boost/random.hpp> +#include <boost/random/exponential_distribution.hpp> +#include <boost/random/variate_generator.hpp> +#include <boost/utility.hpp> +#include <iostream> +#include <assert.h> +#include <string> + + + +// #include "type_set.hpp" +// #include "simlimits.hpp" +// #include "time.hpp" +// #include "pointers.hpp" +// #include "mempool.hpp" +// #include "scalar.hpp" +// #include "multi_queue.hpp" +// #include "filter.hpp" + + +using namespace boost::mpl; +using namespace boost; +using namespace std; + + +int main() { + bool b(1); + cout << b << endl; + return 0; +} diff --git a/core/test_checkpoint.cpp b/core/test_checkpoint.cpp new file mode 100644 index 0000000..2417a32 --- /dev/null +++ b/core/test_checkpoint.cpp @@ -0,0 +1,52 @@ +#include <iostream> + +#include "checkpoint.hpp" +#include "ephermal_checkpoint.hpp" + +#include "vector.hpp" +#include "scalar.hpp" +#include "mempool.hpp" + +using namespace std; + +int main() { + Checkpoint<int, 16> cont("cont", 0), add("moppelkotze", -10); + EphermalCheckpoint<int, 16> eph("ignored", -10); + + // check that all initial values are set + for (int i=0; i<16; i++) { + if (add.getValue(0, i) != -10) { + cerr << "initial value of " << i << "wrong (" << add.getValue(0, i) << " != -10)" << endl; + return -1; + } + if (add.getValue(0, i) != -10) { + cerr << "(ephermal) initial value of " << i << "wrong (" << add.getValue(0, i) << " != -10)" << endl; + return -1; + } + } + + // check takeover to the next generation + add.getValue(1, 0); // readout already triggers (yes, this also happens below) + for (int i=0; i<16; i++) + if (add.getValue(1, i) != -10) { + cerr << "checkout generation takover of " << i << "failed (" << add.getValue(0, i) << " != -10)" << endl; + return -1; + } + + // set and get value + add.set(6, 5, 42); + + // check value continuation over time + // WARN: tests also the behaviour that only the most recent value per timeslot is stored + for (int i=1; i<1000; i++) + if (add.getValue(i, 5) != 42) { + cerr << "new value takover failed (time: " << i << ", element 5); value 42 != " << add.getValue(0, i) << endl; + return -1; + } + + + add.sync(); + + // success + return 0; +} diff --git a/core/test_filter.cpp b/core/test_filter.cpp new file mode 100644 index 0000000..3df91fe --- /dev/null +++ b/core/test_filter.cpp @@ -0,0 +1,77 @@ +#include <iostream> + +#include "index_global.hpp" +#include "filter.hpp" + +using namespace std; + +int main() { + const int testSize = 10000; + + // create index + { + Index<GlobalMsg> idx; + for (int i=0; i<testSize; i++) + idx.add(Time(i), Time(i + 100.0 * drand48()), Ptr<Global>()); + } + { + Index<RandomSpike> idx; + for (int i=0; i<testSize; i++) + idx.add(Time(i), Time(i + 7.9 * drand48()), Ptr<Neuron>(i % 8)); + } + + // test index + { + Time ct(0.0); + Filter<GlobalMsg> filter(ct); + for (int i=0; i<testSize; i++) { + // cout << "OUT#" << i << " " << filter(ct).minPayload().src() << " (" << filter(ct).minVal()() << ")" << endl; + assert(!filter(ct).isEmpty()); + assert(ct <= filter(ct).minVal()); + assert(filter(ct).minVal() == filter(ct).index.eventTime(filter(ct).minPayload().src())); + ct = filter(ct).minVal(); + filter(ct).removeMin(); + } + assert(filter.isEmpty()); + } + + { + Time ct(0.0); + IdList<Ptr<Neuron>::ptr_t> lll((char*) "0,1,2,3,4,5,6,7"); + Filter<RandomSpike> filter(lll, ct); + for (int i=0; i<testSize; i++) { + // cout << "OUT#" << i << " " << filter(ct).minPayload().src() << " (" << filter(ct).minVal()() << ")" << endl; + assert(!filter(ct).isEmpty()); + assert(ct <= filter(ct).minVal()); + assert(filter(ct).minPayload().dst() == filter(ct).minPayload().src() % 8); + ct = filter(ct).minVal(); + filter(ct).removeMin(); + } + assert(filter.isEmpty()); + } + + { + Index<Spike> idx; + for (int i=0; i<testSize; i++) + idx.add(Time(1+i), Time(1+i), Ptr<Neuron>((i+100) % maxNeurons)); + } + { + Time ct(testSize / 2.0); + IdList<Ptr<Neuron>::ptr_t> lll((char*) "1,2,3,4,5,6,7,8,9,55"); + Filter<SpikeArrival> filter(lll, ct); + + int i=0; + while (!filter.isEmpty()) { + cout << "OUT#" << i << " " + << filter(ct).minPayload().src.get<0>().extractSpike()() << ", " + << filter(ct).minPayload().src.get<1>()() << " (" + << filter(ct).minVal()() << ")" << endl; + assert(ct <= filter(ct).minVal()); + ct = filter(ct).minVal(); + filter(ct).removeMin(); + i++; + } + } + return 0; +} + diff --git a/core/test_heap.cpp b/core/test_heap.cpp new file mode 100644 index 0000000..1ae79c2 --- /dev/null +++ b/core/test_heap.cpp @@ -0,0 +1,43 @@ +#include <iostream> +#include <stdlib.h> + + +template<typename Ignored> +struct AverageQueueSize { + static const uint64_t value = 0; // cause a crash if used +}; + +#include "priority_queue.hpp" +#include "heap.hpp" +#include "mempool.hpp" + +using namespace std; + +int main() { + int count = 1000; + typedef Heap<PriorityQueue<Time, double> > heap_t; + heap_t heap("test", count * 2 * sizeof(double)); + + // add many random values + for (int i=0; i<count; i++) { + heap(0).insert(Time(i), i); + } + + // retrieve them while continuing the passage of time, perhaps + // provoking an error ;-) + Time cur = 0; + for (int i=0; i<count; i++) { + Time n = heap(cur).minVal(); + if (n < cur) return -1; + cur = n; + heap(cur).removeMin(); + } + + // recap them again + for (int i=1; i<count; i++) { + if (!heap(Time(i)).isEmpty() && (heap(Time(i-1)).minPayload() > heap(Time(i)).minPayload())) return -1; + } + + // got until here -> success + return 0; +} diff --git a/core/test_index.cpp b/core/test_index.cpp new file mode 100644 index 0000000..7550cdf --- /dev/null +++ b/core/test_index.cpp @@ -0,0 +1,41 @@ +#include <iostream> + +#include "index_global.hpp" +#include "index_spike.hpp" + +using namespace std; + +int main() { + Index<GlobalMsg> g; + Index<Spike> s; + + // add/check some global events + for (uint i=0; i<1000; i++) { + Index<GlobalMsg>::ptr_t r; + r = g.add(Time(i), Time(i), Ptr<Global>()); + if (r != i) { + cerr << "discontinuous global event allocation (event: " << i << ", ptr: " << r << ")" << endl; + return -1; + } + } + + // add some spike events + for (uint i=0; i<1000; i++) { + s.add(i, i, Ptr<Neuron>(i)); + } + + for (uint i=0; i<1000; i++) { + if (s.last(i) != i) { + cerr << "last(" << i << ") = " << s.last(i) << " != " << i << endl; + return -1; + } + if (s.last(10000, Ptr<Neuron>(i)) != i) { + cerr << "last(inf, " << i << ") = " << s.last(i) << " != " << i << endl; + return -1; + } + } + + // and check if we found the corrent one + + return 0; // success +} diff --git a/core/test_index_speed.cpp b/core/test_index_speed.cpp new file mode 100644 index 0000000..dfcca24 --- /dev/null +++ b/core/test_index_speed.cpp @@ -0,0 +1,39 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "index.hpp" +#include "index_global.hpp" +#include "index_spike.hpp" +#include "perftools.hpp" +#include "simlimits.hpp" + +int main() { + Timer *timer; + Index<GlobalMsg> g; + Index<Spike> s; + + + timer = new Timer(); + const int gcount = 10000000; + for (int i=0; i<gcount; i++) + g.add(i); + print_throughput(timer->diff(), gcount, (char*) "global add\t"); + + timer = new Timer(); + for (int i=0; i<gcount; i++) + g.last(i); + print_throughput(timer->diff(), gcount,(char*) "global search\t"); + + timer = new Timer(); + const int scount = 10000000; + for (int i=0; i<scount; i++) + s.add(((double) i / 10 / maxNeurons), Ptr<Neuron>(i%maxNeurons)); + print_throughput(timer->diff(), scount,(char*) "spike add\t"); + + timer = new Timer(); + for (int i=0; i<scount; i++) + s.last(((double) scount / 10 / maxNeurons), Ptr<Neuron>(i%maxNeurons)); + print_throughput(timer->diff(), scount,(char*) "spike search\t"); + + return 0; // user has to control the files +} diff --git a/core/test_mmap.cpp b/core/test_mmap.cpp new file mode 100644 index 0000000..9166521 --- /dev/null +++ b/core/test_mmap.cpp @@ -0,0 +1,52 @@ +#include <malloc.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +// how many mmap areas to create +#define mapMax 100 +// how large should they be +#define mapSize ((size_t)1024 * 1024 * 1024 * 100) + +int fd[mapMax]; +char name[mapMax][100]; + +void quit(int ret) { + for (int i=0; i<mapMax; i++) { + // close(fd[i]); + unlink(name[i]); + } + exit(ret); +} + +int main() { + memset(name[0], 0, sizeof(name)); + + fprintf(stderr, "Allocating\t%f GB\t=\t%d\tx\t%f GB\n", ((double) mapMax * mapSize) / 1024.0 / 1024 / 1024, mapMax, (double) mapSize / 1024.0 / 1024 / 1024 ); + + // create tmp files + for (int i=0; i<mapMax; i++) { + tmpnam(name[i]); + fd[i] = open( name[i], O_CREAT | O_EXCL | O_RDWR, S_IRWXU ); + if (fd[i] == -1) { + fprintf(stderr, "opening tmp file #%d failed\n", i); + quit(-1); + } + } + + // mmap them + for (int i=0; i<mapMax; i++) { + if (MAP_FAILED == mmap((void*) NULL, mapSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NONBLOCK, fd[i], 0)) { + fprintf(stderr, "mmap #%d failed (errno: %d, errstr: %s)\n", i, errno, strerror(errno)); + quit(-1); + } + } + + quit(0); +} diff --git a/core/test_mmap2.cpp b/core/test_mmap2.cpp new file mode 100644 index 0000000..29312a7 --- /dev/null +++ b/core/test_mmap2.cpp @@ -0,0 +1,26 @@ +#include <malloc.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <iostream> + +#include "mempool.hpp" + +int main() { + MemPool ram(4096); + MemPool file(4096, "myfunnymmapfile"); + MemPool file2(4096, "myfunnymmapfile2"); + + ram.sync(); + file.sync(); + + char *s = (char*) "Ei der Daus!"; + memcpy(ram(), s, strlen(s)); + memcpy(file(), ram(), 4096); +} diff --git a/core/test_movector.cpp b/core/test_movector.cpp new file mode 100644 index 0000000..6ca0b21 --- /dev/null +++ b/core/test_movector.cpp @@ -0,0 +1,21 @@ +#include "index_global.hpp" +#include "index_spike.hpp" +#include "heap.hpp" +#include "priority_queue.hpp" +#include "type_set.hpp" + +struct Foo {}; + +int main() { + MemPool m1(1), m2(std::move(m1)); + Scalar<int> s1("s"), s2(std::move(s1)); + Vector<Time> v1("v", 1), v2(std::move(v2)); + Checkpoint<int, 16> c1("c", 0), c2(std::move(c1)); + Heap<PriorityQueue<int, int>> h1("h", 10), h2(std::move(h1)); + CommonIndex<int> ci1("ci", 1), ci2(std::move(ci1)); + CommonSpikeIndex<Ptr<Spike>::ptr_t> cis1("cis"), cis2(std::move(cis1)); + Index<Spike> is1{}, is2(std::move(is1)); + Index<GlobalMsg> i1{}, i2(std::move(i1)); + TypeSet<int, double, Foo> t1{}, t2(std::move(t1)); + return 0; +} diff --git a/core/test_multi_queue.cpp b/core/test_multi_queue.cpp new file mode 100644 index 0000000..e9ff9b3 --- /dev/null +++ b/core/test_multi_queue.cpp @@ -0,0 +1,164 @@ +#include <assert.h> +#include <iostream> +#include <stdlib.h> + +#include <boost/mpl/vector.hpp> + +#include "time.hpp" +#include "template_helpers.hpp" + +// fake RuntimeID & AverageQueueLength wich is otherwise in "quant_types.hpp" +template<typename T> +struct RuntimeID { + static const uint8_t value = T::runtimeId; +}; + +template<typename T> +struct AverageQueueSize { + static const uint64_t value = T::aql; +}; + +template<typename T> struct Foo { + Foo(int i) : val(i) {} + int operator() () { return val; } + int val; + typedef T quant_t; +}; + +struct A { + static const unsigned char runtimeId = 0; + static const uint64_t aql = 1000; + static const char* const name; +}; +const char* const A::name = "A"; +struct B { + static const unsigned char runtimeId = 1; + static const uint64_t aql = 2000; + static const char* const name; +}; +const char* const B::name = "B"; +struct C { + static const unsigned char runtimeId = 2; + static const uint64_t aql = 3000; + static const char* const name; +}; +const char* const C::name = "C"; + +typedef boost::mpl::vector<A, B, C> DiscreteQuantorList; + +// prevent quant_types to be included +#define sKmf7ztuNbBT3ua3iNX4k5rtsg0 + +#include "multi_queue.hpp" + +#include "mempool.hpp" + +using namespace std; + +template<typename T> +void test(int line, int testid, T ex, T got) { + if (ex != got) { + cout << "line " << line + << ", test " << testid + << ", expected " << ex + << ", got " << got << endl; + exit(-1); + } +} + +int main() { + using boost::make_tuple; + typedef MultiQueue<Time, Foo, + boost::mpl::list< A, B, C > + > mq_t; + + mq_t mq{}; + Time ct(0); + + // macro helpers + //#define PMQ {for(int i=0; i<MultiQueueImpl::numRID;i++)cout<<mq.minVal[i] << " "; cout << endl;} +#define PMQ +#define I(v,q) { mq.insert<q>(ct,Time(v),10*v); PMQ } +#define T(v,q,command) { \ + test(__LINE__, 1, mq.minType(), RuntimeID<q>::value); \ + test(__LINE__, 2, mq.min(), Time(v)); \ + test(__LINE__, 3, mq.get<q>()(ct).minVal(), Time(v)); \ + test(__LINE__, 4, mq.get<q>()(ct).minPayload()(), 10*v); \ + mq.command<q>(ct); \ + PMQ \ + } +#define Tg(v,q) T(v,q,removeMin) +#define Tl(v,q) T(v,q,removeLocalMin) + + // test extractMin + PMQ + I(9,C); + I(7,C); + I(3,B); + I(3,B); + I(3,A); + I(2,C); + I(2,A); + I(2,B); + I(1,A); + + Tg(1,A); + Tg(2,A); + Tg(2,B); + Tg(2,C); + Tg(3,A); + Tg(3,B); + Tg(3,B); + Tg(7,C); + Tg(9,C); + + // test extractLocalMin + PMQ + I(9,C); + I(7,C); + I(3,B); + I(3,B); + I(3,A); + I(2,C); + I(2,A); + I(2,B); + I(1,A); + + Tl(1,A); + Tl(2,A); + Tl(2,B); + Tl(2,C); + Tl(3,A); + Tl(3,B); + Tl(3,B); + Tl(7,C); + Tl(9,C); + + // test extractLocalMin + PMQ + I(9,C); + I(7,C); + I(3,B); + I(3,B); + I(3,A); + I(2,C); + I(2,A); + I(2,B); + I(1,A); + + mq.removeLocalMin<B>(ct); + Tl(1,A); + Tl(2,A); + //Tl(2,B); + mq.removeLocalMin<A>(ct); + Tl(2,C); + // Tl(3,A); + Tl(3,B); + Tl(3,B); + Tl(7,C); + Tl(9,C); + + + // got until here -> success + return 0; +} diff --git a/core/test_pla_apply.cpp b/core/test_pla_apply.cpp new file mode 100644 index 0000000..7e18c93 --- /dev/null +++ b/core/test_pla_apply.cpp @@ -0,0 +1,56 @@ +#include <boost/mpl/list.hpp> +#include <boost/mpl/pair.hpp> +#include <boost/tuple/tuple.hpp> +#include <iostream> + +#include "index.hpp" +#include "index_spike.hpp" +#include "pla_apply.hpp" +#include "property_composition.hpp" +#include "property_access.hpp" +#include "context.hpp" + +#include "model.hpp" // for Voltage, Weight + + +int main() { + using boost::mpl::pair; + using boost::mpl::list; + using boost::mpl::bool_; + using boost::make_tuple; + + // define a fake sim env + typedef PropertyComposition<Lists::all> pc_t; + pc_t pc; + + // set weight to 3.0 + pc.cast(PLA_Set<Weight>{0.0, Ptr<Synapse>{0}, 3.0}); + + // create and call an apply PLA + DelieverContext<SpikeArrival, Neuron> + ctx(make_tuple(Ptr<SpikeArrival>{0}, Ptr<Synapse>{0}), + Ptr<Neuron>(0)); + PLA_Apply<SpikeArrival, Neuron, pc_t> apply(Time(0), ctx); + + assert(Property_Get<Voltage>::eval(pc, ctx, Time(0)) == 0.0); + pc.call(apply); + assert(Property_Get<Voltage>::eval(pc, ctx, Time(0)) == 0.0); + + + // check for desired event generation pattern + assert(!apply.generate<SpikeArrival>()); + assert(!apply.generate<GlobalMsg>()); + assert(apply.generate<Spike>()); + assert(apply.generateAny()); + + // commit and test for proper change + assert(Property_Get<Voltage>::eval(pc, ctx, Time(0)) == 0.0); + apply.commit(pc); + assert(Property_Get<Voltage>::eval(pc, ctx, Time(0)) == 3.0); + + // additional compile time checks of helper classes + BOOST_MPL_ASSERT((ContinuousProperty_ApplyChangesProp<Voltage, SpikeArrival>)); + BOOST_MPL_ASSERT_NOT((ContinuousProperty_ApplyChangesProp<Weight, SpikeArrival>)); + + return 0; +} diff --git a/core/test_pla_evolve.cpp b/core/test_pla_evolve.cpp new file mode 100644 index 0000000..4187c9a --- /dev/null +++ b/core/test_pla_evolve.cpp @@ -0,0 +1,88 @@ +#include <iostream> +#include <boost/mpl/list.hpp> +#include <boost/mpl/pair.hpp> +#include <boost/mpl/bool.hpp> + + +#include "context.hpp" +#include "continuous_property.hpp" +#include "pla_evolve.hpp" +#include "pla_get.hpp" +#include "pla_init_default.hpp" +#include "property_composition.hpp" + +#include "mempool.hpp" + +// create some extra properties + +#include "property_abbrevations_begin.hpp" + +GEN_CP(Neuron, ConstProp, "const_prop", int, 42); +GEN_CP_EVOLVE(ConstProp, _CP(ConstProp)); + +GEN_CP(Neuron, ArithProp, "arith_prop", int, 0); +GEN_CP_EVOLVE(ArithProp, (_CP(ArithProp) + 1)); + +GEN_CP(Neuron, GeomProp, "geom_prop", int, 1); +GEN_CP_EVOLVE(GeomProp, (2*_CP(GeomProp))); + +GEN_CP(Neuron, TimeProp, "time_prop", Time, 0.0); +GEN_CP_EVOLVE(TimeProp, _CP(TimeProp) + td); + +GEN_CP(Neuron, Alter1, "alter1", bool, true); +GEN_CP(Neuron, Alter2, "alter2", bool, false); +GEN_CP_EVOLVE(Alter1, _CP(Alter2)); +GEN_CP_EVOLVE(Alter2, _CP(Alter1)); + +#include "property_abbrevations_end.hpp" + +using namespace std; + +int main() { + using boost::mpl::pair; + using boost::mpl::list; + using boost::mpl::bool_; + + // below is bullshit + PropertyComposition<list< + pair<ConstProp, bool_<true>>, + pair<ArithProp, bool_<true>>, + pair<GeomProp, bool_<true>>, + pair<TimeProp, bool_<true>>, + pair<Alter1, bool_<true>>, + pair<Alter2, bool_<true>> + >> pc; + + // init with default values + //PLA_Init_Default<ArithProp> action_init; + // pc.call(action_init); + + // create an instance ptr (we only consider a single instance) + Ptr<Neuron> inst{0}; + ContinuousContext<Neuron> context{inst}; + + // evolve and check result using specific gets + for (int i=1; i<20; i++) { + Time t{double{i}}; + PLA_Evolve<Neuron> evolve(context, t); + + // evolve + pc.call(evolve); + + // check + #define CHECK(PN, COND) { \ + PLA_Get<PN> action{t,inst}; \ + PN::type res(pc.call(action)); \ + assert(res == (COND)); \ + } + + CHECK(ConstProp, 42); + CHECK(ArithProp, i); + CHECK(GeomProp, (1 << i)); + CHECK(TimeProp, t); + + PLA_Get<Alter1> ag_a1 (t, inst); + PLA_Get<Alter2> ag_a2 (t, inst); + assert(pc.call(ag_a1) ^ pc.call(ag_a2)); + } +} diff --git a/core/test_pla_getset.cpp b/core/test_pla_getset.cpp new file mode 100644 index 0000000..08065f2 --- /dev/null +++ b/core/test_pla_getset.cpp @@ -0,0 +1,44 @@ +#include <boost/mpl/pair.hpp> +#include <iostream> + +#include "context.hpp" +#include "continuous_property.hpp" +#include "pla_get.hpp" +#include "pla_set.hpp" +#include "property_composition.hpp" +#include "pointers.hpp" +#include "time.hpp" + +#include "mempool.hpp" + +// create a test property +#define RASIMU_MODEL +#include "model.hpp" +#include "property_abbrevations_begin.hpp" +GEN_CP(Neuron, TestProp, "test_prop", int, 42); +GEN_CP_EVOLVE(TestProp, _CP(TestProp)); + +using namespace std; + +int main() { + using boost::mpl::pair; + using boost::mpl::list; + using boost::mpl::bool_; + + // below is bullshit + PropertyComposition<list<boost::mpl::pair<TestProp, bool_<true>>>> pc; + + for (int j=0; j<100; j++) { + for (Ptr<Neuron>::ptr_t i=0; i<100; i++) { + PLA_Set<TestProp> set{Time{double{j}}, Ptr<Neuron>{i}, 42 * i + j}; + pc.call(set); + } + for (int i=0; i<100; i++) { + PLA_Get<TestProp> get{Time{double{j}}, Ptr<Neuron>(i)}; + assert(pc.call(get) == (42 * i + j)); + } + } + + // success + return 0; +} diff --git a/core/test_prioque.cpp b/core/test_prioque.cpp new file mode 100644 index 0000000..9457cd8 --- /dev/null +++ b/core/test_prioque.cpp @@ -0,0 +1,40 @@ +#include <assert.h> +#include <iostream> +#include <stdlib.h> + +#include "priority_queue.hpp" + +using namespace std; + +int main() { + // PQ is an open ended data structure -> alloc an arbitrary mem amount + PriorityQueue<unsigned int, unsigned int> *pq = new(malloc(1024 * 1024)) PriorityQueue<unsigned int, unsigned int>(); + + // macro helpers +#define I(v) { uint s=pq->length; pq->insert(v,10*v); if (s+1 != pq->length) return -1; } +#define T(v) { if (pq->minVal() != v || pq->minPayload() != 10*v) return -1; pq->removeMin(); } +#define DP { for (uint i=0; i<pq->length; i++) { cout << pq->heap[i].val << ", "; } cout << "-" << endl; } + // add and remove some values + I(3); + I(2); + I(1); + T(1); + T(2); + T(3); + + // add many random values + srand(42); + for (int i=0; i<100000; i++) { + // cout << "."; + pq->insert(rand(), 0); + } + unsigned int prev = 0; + for (int i=0; i<100000; i++) { + if (pq->minVal() < prev) return -1; + prev = pq->minVal(); + pq->removeMin(); + } + + // got until here -> success + return 0; +} diff --git a/core/test_prioque_speed.cpp b/core/test_prioque_speed.cpp new file mode 100644 index 0000000..adb6d63 --- /dev/null +++ b/core/test_prioque_speed.cpp @@ -0,0 +1,38 @@ +#include <iostream> +#include <stdlib.h> + +#include "perftools.hpp" +#include "priority_queue.hpp" + +using namespace std; + +int main() { + // PQ is an open ended data structure -> alloc an arbitrary mem amount + PriorityQueue<double, int> *pq; + + Timer *timer = new Timer(); + int trans = 1000000; + int maxBase = 1024 * 1024; + void *mem = malloc(20 * 1024 * 1024); + + for (int i=0; i<trans; i++) { + drand48(); + } + print_throughput(timer->diff(), trans, (char*) "raw PRNG"); + + for (int base=1; base<=maxBase; base*=2) { + // setup queue + pq = new(mem) PriorityQueue<double, int>(); + for (int i=0; i<base; i++) pq->insert(drand48(),0); + + // test queue + timer = new Timer(); + for (int i=0; i<trans; i++) { + pq->insert(i, 0); + pq->removeMin(); + } + print_throughput2(timer->diff(), trans, base); + } + + return 0; +} diff --git a/core/test_propcomp.cpp b/core/test_propcomp.cpp new file mode 100644 index 0000000..c5522f1 --- /dev/null +++ b/core/test_propcomp.cpp @@ -0,0 +1,33 @@ +#include <boost/mpl/list.hpp> +#include <boost/mpl/pair.hpp> +#include <boost/mpl/bool.hpp> +#include <iostream> + +#include "continuous_property.hpp" +#include "pla_debug_name.hpp" +#include "pla_get.hpp" +#include "property_composition.hpp" + +#include "model.hpp" // for Voltage + +#include "mempool.hpp" + +// create some extra properties +GEN_CP(Neuron, CrazyVal, "crazy_val", double, 0.0); +GEN_CP(Global, CoolVal, "cool_val", double, 0.0); +GEN_CP(Synapse, ExtraordinaryVal, "extraordinary_val", double, 0.0); + +int main() { + using boost::mpl::pair; + using boost::mpl::list; + using boost::mpl::bool_; + + // below is bullshit + PropertyComposition<list< + pair<Voltage, bool_<false> >, + pair<ExtraordinaryVal, bool_<false> > + > > test1; + + PLA_Debug_Name action_debug; + std::cout << test1.call(action_debug) << std::endl; +} diff --git a/core/test_propcomp_cpp-speed.cpp b/core/test_propcomp_cpp-speed.cpp new file mode 100644 index 0000000..ab9e4ba --- /dev/null +++ b/core/test_propcomp_cpp-speed.cpp @@ -0,0 +1,48 @@ +#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS +#define BOOST_MPL_LIMIT_LIST_SIZE 50 + +#include <iostream> + +#include <boost/mpl/pair.hpp> +#include <boost/mpl/list.hpp> +#include <boost/mpl/bool.hpp> + +#include "continuous_property.hpp" +#include "pla_debug_name.hpp" +#include "property_composition.hpp" + +#include "mempool.hpp" + +// create some extra properties +#define A1(t, s) GEN_CP(Global, t, s, int, 0); +#define A2(t, s) A1(t ## 0, s "0") A1(t ## 1, s "1") +#define A4(t, s) A2(t ## 0, s "0") A2(t ## 1, s "1") +#define A8(t, s) A4(t ## 0, s "0") A4(t ## 1, s "1") +#define A16(t, s) A8(t ## 0, s "0") A8(t ## 1, s "1") +#define A32(t, s) A16(t ## 0, s "0") A16(t ## 1, s "1") +#define A64(t, s) A32(t ## 0, s "0") A32(t ## 1, s "1") +#define A128(t, s) A64(t ## 0, s "0") A64(t ## 1, s "1") + +#define B1(t) boost::mpl::pair<t, boost::mpl::bool_<false>> +#define B2(t) B1(t ## 0), B1(t ## 1) +#define B4(t) B2(t ## 0), B2(t ## 1) +#define B8(t) B4(t ## 0), B4(t ## 1) +#define B16(t) B8(t ## 0), B8(t ## 1) +#define B32(t) B16(t ## 0), B16(t ## 1) +#define B64(t) B32(t ## 0), B32(t ## 1) +#define B128(t) B64(t ## 0), B64(t ## 1) + +#define B48(t) B32(t ## 0), B16(t ## 1 ## 1) + +A64(Many, "many") + +int main() { + // below is bullshit + PropertyComposition< + boost::mpl::list< + B48(Many) // work around boost::mpl::list limit of 50 elements + >> test1; + + PLA_Debug_Name action_debug; + std::cout << *test1.call(action_debug) << std::endl; +} diff --git a/core/test_rng.cpp b/core/test_rng.cpp new file mode 100644 index 0000000..4ee419c --- /dev/null +++ b/core/test_rng.cpp @@ -0,0 +1,76 @@ +#include <assert.h> +#include <iostream> + +#include "rng.hpp" + +using namespace RNG; +//using namespace std; + +int main() { + seed_t src(0), + s( split(src)), + s1(split(next(src))); + + std::cout << "RNG size: " << sizeof(generator) << std::endl; + // for (int j=0; j<10; j++) { + // s = j; + // for (int i=0; i<10; i++) { + // std::cout << s << ", "; + // s = next(s); + // } + // std::cout << std::endl; + // } + + // determinism? + assert(next(s).rng == next(s).rng); + + // respect seed + assert(next(s).rng != next(s1).rng); + + // respect equipropable distribution + { + const int times(1000); + double mean(0); + for (int i=0; i<times; i++) { + double res = equi(s); + // std::cout << res << std::endl; + + s = next(s); + assert(0 <= res); + assert(res < 1); + mean += res; + } + mean /= times; + assert(0.45 < mean && mean < 0.55); + } + + // respect exponential distribution + { + const int times(1000); + double mean(0); + for (int i=0; i<times; i++) { + double res = expo(s, 66); + // std::cout << res << " " << s.rng << std::endl; + s = next(s); + assert(0 <= res); + mean += res; + } + mean /= times; + assert(60 < mean && mean < 75); + } + + // print 1000 parallel streams + // for (int i=0; i<1000; i++) { + // seed_t s = split(src); + // src = next(src, 40); + + // double d = 0; + // while (d < 100) { + // std::cerr << d << "\t" << i << std::endl; + // d += expo(s, 1.0); + // s = next(s); + // } + // } + + return 0; +} diff --git a/core/test_scalar.cpp b/core/test_scalar.cpp new file mode 100644 index 0000000..3c57266 --- /dev/null +++ b/core/test_scalar.cpp @@ -0,0 +1,23 @@ +#include "stdio.h" + +#include "scalar.hpp" +#include "mempool.hpp" + +int main() { + Scalar<int> eins("eins"), zwei("zwei"); + Scalar<uint8_t, 1> bit1("bit"); + + eins() = 1; + zwei() = 2 * eins(); + bit1.set(1); + + int result = bit1() * eins() * zwei(); + + fprintf(stderr, "bit mul result = %d", result); + + if (result == 2) { + return 0; + }else{ + return -1; + } +} diff --git a/core/test_sim_loop.cpp b/core/test_sim_loop.cpp new file mode 100644 index 0000000..c17f796 --- /dev/null +++ b/core/test_sim_loop.cpp @@ -0,0 +1,28 @@ +#include <boost/mpl/list.hpp> +#include <boost/mpl/pair.hpp> +#include <iostream> + +#include "sim_loop.hpp" + +#include "model.hpp" + +using namespace std; + +int main() { + using boost::mpl::pair; + using boost::mpl::list; + + SimLoop<Lists::all> sim; + + // run, exceeding time limit + assert(sim.run(0.05, 1000000)); + cout << "left sim.run at " << sim.queues.min()() + << " (" << (int) sim.queues.minType() << ")" << endl; + + // run, execeeding event limit + assert(sim.run(1000, 1) == 0); + cout << "left sim.run at " << sim.queues.min()() + << " (" << (int) sim.queues.minType() << ")" << endl; + + return 0; +} diff --git a/core/test_typemap.cpp b/core/test_typemap.cpp new file mode 100644 index 0000000..12254e5 --- /dev/null +++ b/core/test_typemap.cpp @@ -0,0 +1,25 @@ +#include <assert.h> + +#include <boost/mpl/map.hpp> +#include "type_map.hpp" + +using namespace boost::mpl; + +class Foo {}; + +int main() { + TypeMap<map< + pair<int, char>, + pair<double, int>, + pair<char, Foo> + > > mapp; + + mapp.get<int>() = 5; + mapp.get<double>() = mapp.get<int>() + 1; + mapp.get<char>() = Foo(); + + assert(mapp.get<int>() == 5); + assert(mapp.get<double>() == 6); + + return 0; +} diff --git a/core/test_vector.cpp b/core/test_vector.cpp new file mode 100644 index 0000000..eb79bd6 --- /dev/null +++ b/core/test_vector.cpp @@ -0,0 +1,62 @@ +#include <stdio.h> + +#include "vector.hpp" + +#include "mempool.hpp" + +int main() { + Vector<uint8_t, 1> eins("eins", 64); + Vector<uint8_t, 2> zwei("zwei", 16); + Vector<uint8_t, 4> vier("vier", 8); + Vector<uint8_t, 2> del("del", 10); + Vector<char, 1> type("type",1); + + Vector<uint8_t, 2> m("test", 32); + Vector<uint8_t, 1> c("test", 64); + /* + for (int i=0; i<m.size; i+=2) + m.set(i,1); + + for (int i=0; i<c.size; i++) { + fprintf(stderr,"%u", c.get(i)); + if (i%8==7) fprintf(stderr, " "); + } + fprintf(stderr,"\n"); + */ + + + // set a 1 at every position + eins.set(0,1); + eins.set(9,1); + eins.set(18,1); + eins.set(27,1); + eins.set(36,1); + eins.set(45,1); + eins.set(54,1); + eins.set(63,1); + + // set a 11 at every position + zwei.set(0,3); + zwei.set(5,3); + zwei.set(10,3); + zwei.set(15,3); + + // set a 111 at every position + vier.set(0,31); + vier.set(3,31); + + // fiill with one and delete some + for (uint i=0; i<del.size; i++) + del.set(i, 3); + del.set(2, 1); + del.set(5, 2); + del.set(7, 0); + + // print the result + for (uint i=0; i<eins.size; i++) fprintf(stderr,"%u", eins.get(i)); fprintf(stderr,"\n"); + for (uint i=0; i<zwei.size; i++) fprintf(stderr,"%u", zwei.get(i)); fprintf(stderr,"\n"); + for (uint i=0; i<vier.size; i++) fprintf(stderr,"%2u", vier.get(i)); fprintf(stderr,"\n"); + for (uint i=0; i<del.size; i++) fprintf(stderr,"%u", del.get(i)); fprintf(stderr,"\n"); + + return 0; // user has to control the files +} diff --git a/core/test_vector_speed.cpp b/core/test_vector_speed.cpp new file mode 100644 index 0000000..257e8f3 --- /dev/null +++ b/core/test_vector_speed.cpp @@ -0,0 +1,28 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "scalar.hpp" +#include "vector.hpp" +#include "mempool.hpp" +#include "perftools.hpp" + + +int main() { + Timer *timer; + Vector<uint8_t, 1> dense("dense", (uint64_t) 1024 * 1024 * 1024 * 800); + Vector<uint8_t, 1> sparse("sparse", (uint64_t) 1024 * 1024 * 1024 * 800); + + timer = new Timer(); + const int dcount = 100000000; + for (int i=0; i<dcount; i++) + dense.set(i,i%2); + print_throughput(timer->diff(), dcount,(char*) "dense"); + + timer = new Timer(); + const int scount = 100000; + for (int i=0; i<scount; i++) + dense.set(rand(),i%2); + print_throughput(timer->diff(), scount,(char*) "sparse"); + + return 0; // user has to control the files +} diff --git a/core/time.hpp b/core/time.hpp new file mode 100644 index 0000000..827772b --- /dev/null +++ b/core/time.hpp @@ -0,0 +1,98 @@ +#ifndef yg0goL5EwkwiF99Qqt5ozzKAq9U +#define yg0goL5EwkwiF99Qqt5ozzKAq9U + +#include <assert.h> +#include <math.h> +#include <iostream> +#include <limits> + +struct Time { + typedef double type; + type time; + + Time() {} + Time(type time) : time(time) {} + +#define Op(op) \ + Time operator op (const Time & arg) const { return Time(time op arg.time); } \ + Time& operator op##= (const Time & arg) { time op##= arg.time; return *this; } + Op(+); + Op(-); + Op(*); + Op(/); + // Op(%); +#undef Op + // WARN: wrong for time/t < epsilon + inline unsigned long ceilDiv(const Time & t) const { return ceil(time / t.time); } + + +#define CmpOp(op) inline bool operator op (const Time & arg) const \ + { return time op arg.time; } + CmpOp(==); + CmpOp(!=); + CmpOp(<=); + CmpOp(>=); + CmpOp(<); + CmpOp(>); +#undef CmpOp + + void operator= (type t) { time = t; } + + inline type operator() () const { return time; } + + // used as a hack in creating global events; see event_intent.hpp + static inline Time never() { + return Time(std::numeric_limits<type>::infinity()); + } + + static inline Time beforeAll() { + return Time(- std::numeric_limits<type>::infinity()); + } + + // a small value, large enough that t + epsilon > t will always hold + // during sane simulations + static inline Time epsilon() { + return Time(0.000001); // leaves approx. 10 digits for time + } +}; + +namespace std { + ostream& operator<< (ostream &os, Time val) { + return os << val.time; + } +} + + +struct TimeSpan { + TimeSpan(Time start, Time end, Time delta) : start(start), end(end), delta(delta) { + assert(start <= end); + assert(delta > 0); + }; + Time start, end, delta; + + struct iterator { + const TimeSpan &timeSpan; + Time current; + + iterator(TimeSpan &parent) : timeSpan(parent), current(timeSpan.start) {} + + bool hasNext() { return current < timeSpan.end; } + Time & operator() () { return current; } + iterator & operator++() { current = fmin(current.time + timeSpan.delta.time, timeSpan.end.time); return *this; } + }; + + iterator begin() { return iterator(*this); } +}; + +struct FakeTime { + FakeTime() {} + FakeTime(const Time) {}; +}; + +namespace std { + ostream& operator<< (ostream &os, FakeTime val) { + return os << "(FakeTime)"; + } +} + +#endif // yg0goL5EwkwiF99Qqt5ozzKAq9U diff --git a/core/topology.hpp b/core/topology.hpp new file mode 100644 index 0000000..f614739 --- /dev/null +++ b/core/topology.hpp @@ -0,0 +1,88 @@ +#ifndef Orz6Wl1nWMpziVYSyArLK0EzFpA +#define Orz6Wl1nWMpziVYSyArLK0EzFpA + +#include "array.hpp" +#include "pointers.hpp" +#include "scalar.hpp" +#include "simlimits.hpp" +#include "time.hpp" + +struct Topology { + // ctor for reading topo + Topology() + : delay("topology_delay"), + target("topology_target"), + isInitialized("topology_initialized") + { assert(isInitialized()); } + + // ctor for writing topo + Topology(Array<Time, maxNeurons * maxSynapsesPerNeuron> &aDelay, + Array<Ptr<Synapse>::ptr_t, maxNeurons * maxSynapsesPerNeuron> &aTarget) + : delay("topology_delay"), + target("topology_target"), + isInitialized("topology_initialized") { + assert(not isInitialized()); + + isInitialized() = true; + target() = aTarget; + delay() = aDelay; + } + + + // tools + Ptr<Synapse> synapse(Ptr<Neuron> neuron, uint16_t offset) { + return Ptr<Synapse>(target().get(neuron() * maxSynapsesPerNeuron + offset)); + } + + Time time(Ptr<Neuron> neuron, uint16_t offset) { + return delay().get(neuron() * maxSynapsesPerNeuron + offset); + } + + // consts + inline static Ptr<Synapse> nil() { return Ptr<Synapse>(-1); } + + // storage + Scalar<Array<Time, maxNeurons * maxSynapsesPerNeuron>> delay; + Scalar<Array<Ptr<Synapse>::ptr_t, maxNeurons * maxSynapsesPerNeuron>> target; + Scalar<bool> isInitialized; +}; + +/// a helper to generate the offset between the baseTime of an event +/// and it's arrival time (in priority queue); is zero except for the +/// case of sending a spike from a neuron + +// default case: zero offset +template<typename ContQuant, typename EventQuant> +struct TopologicalTimeOffset { + inline Time operator() (Topology &topology, typename ContQuant::instance_ptr_t src) { + return Time(0.0); + } +}; + +// neuron emit spike case: lookup offset in topology +template<> +struct TopologicalTimeOffset<Neuron, Spike> { + inline Time operator() (Topology &topology, Neuron::instance_ptr_t src) { + return topology.time(src, 0); + } +}; + +/// a helper to catch the case of a neuron with empty fanout + +// default case: emit event +template<typename ContQuant, typename EventQuant> +struct TopologicalEventDiscard { + inline bool operator() (Topology &topology, typename ContQuant::instance_ptr_t src) { + return false; + } +}; + +// neuron emit spike case: lookup offset in topology +template<> +struct TopologicalEventDiscard<Neuron, Spike> { + inline bool operator() (Topology &topology, Neuron::instance_ptr_t src) { + return (topology.synapse(src, 0)() == Topology::nil()()); + } +}; + +#endif // Orz6Wl1nWMpziVYSyArLK0EzFpA diff --git a/core/track_causality.cpp b/core/track_causality.cpp new file mode 100644 index 0000000..652fa26 --- /dev/null +++ b/core/track_causality.cpp @@ -0,0 +1,72 @@ +#include "perftools.hpp" +#include "sim_causality.hpp" +#include "system_helpers.hpp" + +void realMain(Time start, Time until) { + SimCausality<Lists::all_ro> s(start); + uint64_t totalEventsProcessed(0), eventsProcessed(-1); + const uint64_t eventsPerChunk{10000}; + Timer timerAll; + uint64_t trueMarks[2] = {}, totalMarks[2] = {}; + std::cout << "miau" << std::endl; + while (s.ct < until && until > s.queues.min()) { + eventsProcessed = eventsPerChunk - s.run(until, eventsPerChunk); + totalEventsProcessed += eventsProcessed; + std::cout << "\r\033[K" + << s.ct << "\t" + << totalEventsProcessed / timerAll.diff() << " Hz(p)\t" + << "ETA " << ceil( (until() - s.ct()) + / (s.ct() - start()) + * timerAll.diff()); + std::cout << std::flush; + + + std::cerr << "MARKSTAT\t" << s.ct << "\t" + << s.trueMarks[1] - trueMarks[1] << "\t" << s.totalMarks[1] - totalMarks[1] << "\t" + << s.trueMarks[0] - trueMarks[0] << "\t" << s.totalMarks[0] - totalMarks[0] << "\t" + << "\n"; + trueMarks[1] = s.trueMarks[1]; + trueMarks[0] = s.trueMarks[0]; + totalMarks[0] = s.totalMarks[0]; + totalMarks[1] = s.totalMarks[1]; + } + s.run(until, -1); + + if (s.inactiveNeurons) { + std::cout << s.inactiveNeurons + << " neurons remained inactive during replay time:\n"; + bool first(0); + for (int i=0; i<1000; i++) { + if (!s.hasFired[i]) { + if (first) { + first = false; + }else{ + std::cout << ", "; + } + std::cout << i; + } + } + std::cout << std::endl; + }else{ + std::cout << "Last neuron went active at " << s.lastNeuronActivation() << "\n"; + } + std::cout << s.trueMarks [1] << " of " + << s.totalMarks[1] << " spike arrivals are 0-egal (" + << 100 * double(s.trueMarks[1]) / s.totalMarks[1] << " %)\n" + << s.trueMarks [0] << " of " + << s.totalMarks[0] << " random spikes are 0-egal" + << 100 * double(s.trueMarks[0]) / s.totalMarks[0] << " %)\n"; +} + +int main(int argc, char **argv) { + assert(argc == 3); + char *tail; + Time start(strtod( argv[1], &tail)); assert(*tail == 0); + Time stop( strtod( argv[2], &tail)); assert(*tail == 0); + + assertSimDir(); + garantueeStackSize(64 * 1024 * 1024); + realMain(start, stop); + + return 0; +} diff --git a/core/trainer.hpp b/core/trainer.hpp new file mode 100644 index 0000000..7f0d38c --- /dev/null +++ b/core/trainer.hpp @@ -0,0 +1,52 @@ +#ifndef jB1MwurMlfU1OJE1w1IE29RFf9I +#define jB1MwurMlfU1OJE1w1IE29RFf9I + +#include "event.hpp" +#include "index.hpp" +#include "pointers.hpp" +#include "quant_types.hpp" +#include "rng.hpp" +#include "time.hpp" + +namespace TrainerImpl { + +struct TrainerT; + +template <typename PC, typename MI, typename MQ> +struct Update; + +struct TrainerT { + explicit TrainerT(RNG::seed_t seed = RNG::seed_t(0)); + + template<typename PC, typename MI, typename MQ> + TrainerT update(PC &pc, MI &indices, MQ &queues, Time t) const { + return Update<PC, MI, MQ>::eval(*this, pc, indices, queues, t); + } + + RNG::seed_t rng; + Time::type delay; + double reward, + performance; + uint64_t generation, + input, output; + uint32_t state, + resetCounter; +}; + +} // NS + +using TrainerImpl::TrainerT; + +namespace std { + ostream& operator<< (ostream &os, TrainerT val) { + return os + << "Trainer(" + << val.generation << ", " + << val.state << ", " + << val.input << ", " + << val.output + << ")"; + } +} + +#endif // jB1MwurMlfU1OJE1w1IE29RFf9I diff --git a/core/trainer_impl.hpp b/core/trainer_impl.hpp new file mode 100644 index 0000000..d685d66 --- /dev/null +++ b/core/trainer_impl.hpp @@ -0,0 +1,162 @@ +#ifndef pAHg29dA1QrONep0XYIUhA0GC08 +#define pAHg29dA1QrONep0XYIUhA0GC08 + +#include <iostream> + +#include "trainer.hpp" +#include "model.hpp" + +#include "index.hpp" +#include "index_global.hpp" +#include "index_randomspike.hpp" +#include "index_spike.hpp" +#include "index_spike_arrival.hpp" + +namespace TrainerImpl { + +namespace MC = ModelConsts; + +const Time::type delays[6] = + { Time::epsilon()(), + MC::TrainerInputWindow + MC::TrainerReadoutDelay, + MC::TrainerReadoutWindow, + Time::epsilon()(), + Time::epsilon()(), + MC::TrainerInterTrialDelay }; + +const Time::type randDelays[6] = + { 0, + MC::TrainerReadoutRandDelay, + 0, 0, 0, + MC::TrainerInterTrialRandDelay }; + +TrainerT::TrainerT(RNG::seed_t seed) : + rng(seed), + delay(delays[0]), + reward(0), + performance(1.0 / MC::TrainerNumSymbols), + generation(0), + input(0), output(2), + state(0), + resetCounter(1) {} + +template <typename PC, typename MI, typename MQ> +struct Update { + static TrainerT eval(const TrainerT &old, PC &pc, + MI &indices, MQ &queues, Time t) { + TrainerT res = old; + res.generation++; + res.state++; + res.reward = 0; + + auto sendSpikes = [&](int mult, double window, int fanIn, double freq) -> void { + Time ct = t + Time::epsilon(); + while (ct < t + window) { + // determine dst + Ptr<Neuron> dst{uint16_t(RNG::integer(res.rng, fanIn) + * mult + % MC::NumExcitatory)}; + Ptr<Neuron> src(dst() + ((Ptr<Neuron>::ptr_t) maxNeurons/2)); + Ptr<RandomSpike> ptrNE(indices.template get<Index<RandomSpike>>().add(t, ct, src)); + queues.insert(t, ct, Event<RandomSpike>(ct, src, ptrNE)); + res.rng = RNG::next(res.rng); + + // determine next spike + ct += RNG::expo(res.rng, 1.0 / freq / fanIn); + res.rng = RNG::next(res.rng); + } + }; + + switch (res.state) { + case 1: // pre: let the network settle + case 7: { // pre: give last reward + // select and give input + res.input = RNG::integer(res.rng, MC::TrainerNumSymbols); + res.rng = RNG::next(res.rng); + res.state = 1; + const int mult_in[10] = // some arbitrarily selected prime number except for + {1, 997, 1013, 1021, 1033, 1049, 1061, 1069, 1091, 1097}; // symbol one + BOOST_STATIC_ASSERT((MC::TrainerNumSymbols <= 10)); + std::cerr << "INPUT " << t << std::endl; + sendSpikes(mult_in[res.input], + MC::TrainerInputWindow, MC::FanIn, MC::TrainerInputFreq); + } break; + + case 2: + res.resetCounter = 0; + break; + + case 4: case 6: // wait + break; + + case 3: { + // send readout signal + std::cerr << "READOUT SIGNAL " << t << std::endl; + if (MC::TrainerReadoutFreq > 0) + sendSpikes(967, // arbitrary prime + MC::TrainerReadoutWindow, MC::FanIn, MC::TrainerReadoutFreq); + } break; + + + case 5: { + // evaluate which symbol won, reward (TODO), wait for a longer time + const int mult_out[10] = // some arbitrarily selected prime number except for + {1, 937, 919, 907, 883, 877, 859, 853, 829, 823}; // symbol one + BOOST_STATIC_ASSERT((MC::TrainerNumSymbols <= 10)); + uint32_t freq[MC::TrainerNumSymbols + 1] = {}; + uint8_t maxIdx = MC::TrainerNumSymbols, + sndIdx = MC::TrainerNumSymbols; + uint16_t overlap = uint16_t(MC::FanIn / MC::NumExcitatory * MC::FanOut); + std::cerr << "READOUT"; + for (int o=0; o<MC::TrainerNumSymbols; o++) { + for (Ptr<Neuron>::ptr_t i = MC::FanIn - overlap; + i < MC::FanIn + MC::FanIn; + i++) { + Ptr<Neuron> src(uint16_t(mult_out[o] * i % MC::NumExcitatory)); + PLA_Get<SpikeCounter, ContinuousContext<Neuron> > + pla_get(t, ContinuousContext<Neuron>(src)); + freq[o] += pc.call(pla_get); + } + if (freq[o] > freq[maxIdx]) { + sndIdx = maxIdx; + maxIdx = o; + }else if (freq[o] > freq[sndIdx]) { + sndIdx = o; + } + std::cerr << " " << freq[o]; + } + bool correct = maxIdx == res.input; + res.output = maxIdx; + res.reward = (correct ? MC::TrainerReward : (-MC::TrainerPunish)) + * ((1.0 + MC::TrainerWinAdv) * freq[sndIdx] < freq[maxIdx]); + res.performance *= 0.9; + res.performance += correct * 0.1; + res.resetCounter = 1; + std::cerr << "\nWINNER @ " << t << " = " << uint16_t(maxIdx) + << " correct = " << uint16_t(res.input) + << " reward = " << res.reward + << "\n"; + } break; + + default: + assert(false); + } + + res.delay = delays[res.state] + + randDelays[res.state] * RNG::equi(res.rng); + res.rng = RNG::next(res.rng); + + return res; + } +}; + +/// dummy for sim_replay, must not be called +template <typename PC> +struct Update<PC, Void, Void> { + static TrainerT eval(const TrainerT&, PC &, Void&, Void&, Time) DO_NOT_CALL; +}; + +} // NS + + +#endif // pAHg29dA1QrONep0XYIUhA0GC08 diff --git a/core/type_map.hpp b/core/type_map.hpp new file mode 100644 index 0000000..e5cedfe --- /dev/null +++ b/core/type_map.hpp @@ -0,0 +1,83 @@ +#ifndef P12JZuE3eei6EPJyqPT0m7uqIrA +#define P12JZuE3eei6EPJyqPT0m7uqIrA + +#include <assert.h> + +#include <boost/mpl/assert.hpp> +#include <boost/mpl/at.hpp> +#include <boost/mpl/empty.hpp> +#include <boost/mpl/erase_key.hpp> +#include <boost/mpl/front.hpp> +#include <boost/mpl/has_key.hpp> +#include <boost/mpl/if.hpp> +#include <boost/mpl/map.hpp> +#include <boost/mpl/pair.hpp> +#include <boost/type_traits/is_same.hpp> + +#include "template_helpers.hpp" + +// HINT: in contrast to to typeset wo do not allow the elements to +// have a (non-default) ctor + +namespace TypeMapImpl { + + using namespace boost::mpl; + using namespace boost; + using boost::mpl::empty; + + template<typename Content> + struct TypeMap; + template<> + struct TypeMap<Void> { + // empty constructor and it's param + TypeMap() {} + + // get()-dummy never to be called (runtime) + template<typename Key, typename Value = Void> + inline Value & get() DO_NOT_CALL + }; + + template<typename Content> + struct TypeMap { + // type constructions + typedef typename front<Content>::type head_t; + typedef typename head_t::first key_t; + typedef typename head_t::second payload_t; + + typedef typename erase_key<Content, key_t>::type tail_t; + typedef typename if_<empty<tail_t>, + TypeMap<Void>, + TypeMap<tail_t> + >::type next_t; + + + template<typename Key> struct get_t + : if_<has_key<Content, Key>, + typename at<Content, Key>::type, + Void> + {}; + + // getter + template<typename Key, typename Value = typename get_t<Key>::type> + inline Value & get() { + if (is_same<Key, key_t>::value) { + return FORCE_CONV(Value, data); + }else{ + return FORCE_CONV(Value, tail.get<Key>()); + } + } + + // constructor + //TypeMap() {} + + // instantiation of data + payload_t data; + next_t tail; + }; + +} + +// lift to global NS +using TypeMapImpl::TypeMap; + +#endif // P12JZuE3eei6EPJyqPT0m7uqIrA diff --git a/core/type_set.hpp b/core/type_set.hpp new file mode 100644 index 0000000..206edd0 --- /dev/null +++ b/core/type_set.hpp @@ -0,0 +1,91 @@ +#ifndef WSAdISl7wGLmX6tBH6AAVivibiM +#define WSAdISl7wGLmX6tBH6AAVivibiM + +#include <utility> + +#include <boost/mpl/if.hpp> +#include <boost/mpl/or.hpp> +#include <boost/utility.hpp> + +#include "template_helpers.hpp" + +namespace TypeSetImpl { + +using namespace boost::mpl; +using boost::is_same; + +class Found {}; + +template <typename... Tail> +struct TypeSet; + +template <typename Head, typename... Tail> +struct TypeSet<Head, Tail...> : protected MakeUnique<Head, Tail...>::type { + typedef typename MakeUnique<Head, Tail...>::type head_t; + TypeSet(Head&& head, Tail&&... tail) + : head_t(std::move(head)), + tail(std::move(tail)...) + {} + TypeSet() = default; + + template<typename Target> + inline Target & get() { + typedef is_same<Target, Head> found; + typedef typename if_<found, Found, Target>::type nextTarget; + if (found::value) { + return FORCE_CONV(Target, *this); + }else{ + return FORCE_CONV(Target, tail.get<nextTarget>()); + } + } + + friend std::ostream& operator<< (std::ostream &os, TypeSet val) { + return os << val.template get<Head>() << "," << val.tail; + } + + TypeSet<Tail...> tail; +}; + +template <typename End> +struct TypeSet<End> : protected MakeUnique<End>::type { + typedef typename MakeUnique<End>::type head_t; + TypeSet(End&& end) : head_t(std::move(end)) {} + TypeSet() = default; + + template<typename Target> + inline Target & get() { + if (not or_<is_same<Target, Found>, is_same<Target, End>>::value) + DO_NOT_CALL; + return FORCE_CONV(Target, *this); + } + + friend std::ostream& operator<< (std::ostream &os, TypeSet val) { + return os << val.template get<End>(); + } +}; + +} // NS + +using TypeSetImpl::TypeSet; + +// EXAMPLE +/* +#include <iostream> +#include "index_global.hpp" +using namespace boost::mpl; +int main() { + TypeSetImpl::TypeSet<Index<GlobalMsg>> a(Index<GlobalMsg>{}); + + UnpackTypeList<TypeSetImpl::TypeSet, boost::mpl::list<int, double, bool>::type> + ::type t(556, 3.1, true), t2; + t.get<double>() = t.get<int>() * 1.5; + + std::cout << t.get<int>() << " " + << t.get<double>() << " " + << t.get<bool>() << " " + << std::endl; + std::cout << sizeof(TypeSetImpl::TypeSet<Void, Void, Void, Void, Void, Void>) << std::endl; + return 0; +} */ + +#endif // WSAdISl7wGLmX6tBH6AAVivibiM diff --git a/core/vector.hpp b/core/vector.hpp new file mode 100644 index 0000000..b6c7ccb --- /dev/null +++ b/core/vector.hpp @@ -0,0 +1,106 @@ +#ifndef ZnnjWYk20gszrSSZENMjL1hM9gI +#define ZnnjWYk20gszrSSZENMjL1hM9gI + +#include <inttypes.h> +#include <iostream> +#include <assert.h> +#include <boost/static_assert.hpp> +#include <boost/mpl/if.hpp> +#include <string> + +#include "bit_access.hpp" +#include "mempool.hpp" +#include "scalar.hpp" +#include "string_helpers.hpp" +#include "template_helpers.hpp" + + + +namespace VectorImpl { + +using std::string; +using namespace boost::mpl; + +template< + typename RawPayload, + uint32_t width = 8 * sizeof(RawPayload), + typename Writable = true_> // true_ or false_ +class Vector { +public: + typedef uint64_t ptr_t; + typedef typename if_<Writable, + RawPayload, + const RawPayload>::type Payload; + + Vector(const string name, const ptr_t size) : + mem((size * width + 7) / 8, name), + barrierRead("barrier_read_" + name), + barrierWrite("barrier_write_" + name), + size(size) + {} + // Vector(Vector&&) = default; + + // direct access + Payload & operator() (const ptr_t id) const { + BOOST_STATIC_ASSERT((width >= 8)); + assert(id < size); + return *((RawPayload*) (((char*) mem()) + (width / 8) * id)); + } + + // reader and writer + Payload get(const ptr_t id) const { + assert(id < size); + if (width < 8) { + return (uint8_t) bit_extract(mem(), width, id); + }else{ + return *((RawPayload*) (((char*) mem()) + (width / 8) * id)); + } + } + + void set(const ptr_t id, const RawPayload value) const { + if (!Writable::value) DO_NOT_CALL; + assert(id < size); + if (width < 8) { + bit_insert(mem(), (uint8_t) width, id, value); + }else{ + *((RawPayload*) (((char*) mem()) + (width / 8) * id)) = value; + } + + } + + void sync() const { + if (Writable::value) { + mem.sync(); + barrierRead.sync(); + barrierWrite.sync(); + } + } + + void advise(const ptr_t base, const ptr_t length, int adv) { + mem.advise(base * (width / 8), length * (width / 8), adv); + } + + // associated memory container + MemPool mem; + + // associated r/w-barrier + // WARN: these barriers are not checked/updated in Vector but only + // by iterators/ranges using them; Vector checks only against + // the given size and nothing more + Scalar<ptr_t> barrierRead, barrierWrite; + + // maximal number of elements + const ptr_t size; + + // garantuee that size = 2^n + STATIC_ASSERT_IS_POWER_OF_TWO(width); + +private: + Vector(); +}; + +} // namespace VectorImpl + +using VectorImpl::Vector; + +#endif // ZnnjWYk20gszrSSZENMjL1hM9gI |