// Experiment-only cache for the fixed-topology CPU training backend. #pragma once #include #include #include #include #include #include namespace knp::backends::cpu::populations::impl::training::stdp { template struct ConnectionCacheEntry { using Projection = knp::core::Projection; using Params = knp::synapse_traits::synapse_parameters; std::vector projections; std::vector sizes; std::vector storage; std::vector> values; }; template auto& connection_cache() { using Key = std::pair; static thread_local std::map> cache; return cache; } template void clear_connection_cache() { connection_cache().clear(); } template const std::vector>>& get_all_connected_synapses( std::vector>>& projections, size_t neuron) { using Projection = knp::core::Projection; using Values = std::vector>>; static const Values empty; if (projections.empty()) return empty; auto key = std::make_pair(reinterpret_cast(&projections.front().get()), neuron); auto& entry = connection_cache()[key]; bool valid = entry.projections.size() == projections.size(); for (size_t i = 0; valid && i < projections.size(); ++i) { auto& p = projections[i].get(); const void* storage = p.size() ? static_cast(&p[0]) : nullptr; valid = entry.projections[i] == &p && entry.sizes[i] == p.size() && entry.storage[i] == storage; } if (!valid) { entry = ConnectionCacheEntry{}; for (auto& ref : projections) { auto& p = ref.get(); entry.projections.push_back(&p); entry.sizes.push_back(p.size()); entry.storage.push_back(p.size() ? static_cast(&p[0]) : nullptr); // Preserve the original index order, hence the floating-point update order. for (auto index : p.find_synapses(neuron, Projection::Search::by_postsynaptic)) entry.values.emplace_back(std::get(p[index])); } } return entry.values; } }