diff --git a/knp/backends/cpu/cpu-library/include/knp/backends/cpu-library/impl/populations/lif/lif_dispatcher.h b/knp/backends/cpu/cpu-library/include/knp/backends/cpu-library/impl/populations/lif/lif_dispatcher.h new file mode 100644 index 00000000..b85cb59f --- /dev/null +++ b/knp/backends/cpu/cpu-library/include/knp/backends/cpu-library/impl/populations/lif/lif_dispatcher.h @@ -0,0 +1,100 @@ +/** + * @file lif_dispatcher.h + * @brief Specification of population interface for lif neuron population. + * @kaspersky_support Postnikov D. + * @date 08.12.2025 + * @license Apache 2.0 + * @copyright © 2025 AO Kaspersky Lab + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include + +#include "lif_impl.h" + + +namespace knp::backends::cpu::populations::impl +{ + +/** + * @brief Calculate pre impact state of single neuron. + * @param neuron Neuron. + */ +inline void calculate_pre_impact_single_neuron_state_dispatch( + knp::neuron_traits::neuron_parameters &neuron) +{ + lif::calculate_pre_impact_single_neuron_state_impl(neuron); +} + + +/** + * @brief Impact neuron. + * @param neuron Neuron. + * @param impact Impact message. + * @param is_forcing Is impact forced. + */ +inline void impact_neuron_dispatch( + knp::neuron_traits::neuron_parameters &neuron, + const knp::core::messaging::SynapticImpact &impact, bool is_forcing) +{ + lif::impact_neuron_impl(neuron, impact, is_forcing); +} + + +/** + * @brief Calculate post impact state of single neuron. + * @param neuron Neuron. + * @return Should neuron produce spike or should not. + */ +inline bool calculate_post_impact_single_neuron_state_dispatch( + knp::neuron_traits::neuron_parameters &neuron) +{ + return lif::calculate_post_impact_single_neuron_state_impl(neuron); +} + + +/** + * @brief Train population. + * @param population Population. + * @param projections Connected projections. + * @param message Spiking neurons in population at current step. + * @param step Step. + */ +inline void train_population_dispatch( + knp::core::Population &population, + std::vector>> &projections, + const knp::core::messaging::SpikeMessage &message, knp::core::Step step) +{ +} + + +/** + * @brief Train population. + * @param population Population. + * @param projections Connected projections. + * @param message Spiking neurons in population at current step. + * @param step Step. + */ +inline void train_population_dispatch( + knp::core::Population &population, + std::vector>> + &projections, + const knp::core::messaging::SpikeMessage &message, knp::core::Step step) +{ +} + +} //namespace knp::backends::cpu::populations::impl diff --git a/knp/backends/cpu/cpu-library/include/knp/backends/cpu-library/impl/populations/lif/lif_impl.h b/knp/backends/cpu/cpu-library/include/knp/backends/cpu-library/impl/populations/lif/lif_impl.h new file mode 100644 index 00000000..09f61f56 --- /dev/null +++ b/knp/backends/cpu/cpu-library/include/knp/backends/cpu-library/impl/populations/lif/lif_impl.h @@ -0,0 +1,88 @@ +/** + * @file lif_impl.h + * @brief Implementation of lif neuron population. + * @kaspersky_support Postnikov D. + * @date 12.05.2026 + * @license Apache 2.0 + * @copyright © 2026 AO Kaspersky Lab + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include + +#include + + +namespace knp::backends::cpu::populations::impl::lif +{ + +inline void calculate_pre_impact_single_neuron_state_impl( + knp::neuron_traits::neuron_parameters &neuron) +{ + if (0 == neuron.refract_counter_) + { + neuron.potential_ *= neuron.leak_coefficient_; + } +} + + +inline void impact_neuron_impl( + knp::neuron_traits::neuron_parameters &neuron, + const knp::core::messaging::SynapticImpact &impact, bool is_forcing) +{ + switch (impact.synapse_type_) + { + case knp::synapse_traits::OutputType::EXCITATORY: + if (0 == neuron.refract_counter_) + { + neuron.potential_ += impact.impact_value_; + } + break; + case knp::synapse_traits::OutputType::INHIBITORY_CURRENT: + if (0 == neuron.refract_counter_) + { + neuron.potential_ -= impact.impact_value_; + } + break; + + default: + SPDLOG_ERROR("Unhandled synapse type."); + throw std::runtime_error("Unhandled synapse type."); + } +} + + +inline bool calculate_post_impact_single_neuron_state_impl( + knp::neuron_traits::neuron_parameters &neuron) +{ + if (0 == neuron.refract_counter_) + { + if (neuron.potential_ > neuron.activation_threshold_) + { + neuron.potential_ = neuron.potential_reset_value_; + neuron.refract_counter_ = neuron.refract_period_; + return true; + } + } + else + { + --neuron.refract_counter_; + } + return false; +} + +} //namespace knp::backends::cpu::populations::impl::lif diff --git a/knp/backends/cpu/cpu-library/include/knp/backends/cpu-library/impl/populations/population_dispatcher.h b/knp/backends/cpu/cpu-library/include/knp/backends/cpu-library/impl/populations/population_dispatcher.h index 3bdb449f..5ba21f85 100644 --- a/knp/backends/cpu/cpu-library/include/knp/backends/cpu-library/impl/populations/population_dispatcher.h +++ b/knp/backends/cpu/cpu-library/include/knp/backends/cpu-library/impl/populations/population_dispatcher.h @@ -29,6 +29,7 @@ #include "altai/altai_dispatcher.h" #include "blifat/blifat_dispatcher.h" +#include "lif/lif_dispatcher.h" namespace knp::backends::cpu::populations::impl diff --git a/knp/backends/cpu/cpu-multi-threaded-backend/include/knp/backends/cpu-multi-threaded/backend.h b/knp/backends/cpu/cpu-multi-threaded-backend/include/knp/backends/cpu-multi-threaded/backend.h index efc41692..68ed0003 100644 --- a/knp/backends/cpu/cpu-multi-threaded-backend/include/knp/backends/cpu-multi-threaded/backend.h +++ b/knp/backends/cpu/cpu-multi-threaded-backend/include/knp/backends/cpu-multi-threaded/backend.h @@ -77,8 +77,9 @@ class KNP_DECLSPEC MultiThreadedCPUBackend : public knp::core::Backend /** * @brief List of neuron types supported by the multi-threaded CPU backend. */ - using SupportedNeurons = - boost::mp11::mp_list; + using SupportedNeurons = boost::mp11::mp_list< + knp::neuron_traits::BLIFATNeuron, knp::neuron_traits::SynapticResourceSTDPBLIFATNeuron, + knp::neuron_traits::LIFNeuron>; /** * @brief List of synapse types supported by the multi-threaded CPU backend. @@ -97,25 +98,25 @@ class KNP_DECLSPEC MultiThreadedCPUBackend : public knp::core::Backend using SupportedProjections = boost::mp11::mp_transform; /** * @brief Population variant that contains any population type specified in `SupportedPopulations`. - * + * * @details `PopulationVariants` takes the value of `std::variant`, where * `PopulationType_[1..n]` is the population type specified in `SupportedPopulations`. \n * For example, if `SupportedPopulations` contains BLIFATNeuron and IzhikevichNeuron types, * then `PopulationVariants = std::variant`. \n * `PopulationVariants` retains the same order of message types as defined in `SupportedPopulations`. - * + * * @see ALL_NEURONS. */ using PopulationVariants = boost::mp11::mp_rename; /** * @brief Projection variant that contains any projection type specified in `SupportedProjections`. - * + * * @details `ProjectionVariants` takes the value of `std::variant`, where * `ProjectionType_[1..n]` is the projection type specified in `SupportedProjections`. \n * For example, if `SupportedProjections` contains DeltaSynapse and AdditiveSTDPSynapse types, * then `ProjectionVariants = std::variant`. \n * `ProjectionVariants` retains the same order of message types as defined in `SupportedProjections`. - * + * * @see ALL_SYNAPSES. */ using ProjectionVariants = boost::mp11::mp_rename; @@ -149,7 +150,7 @@ class KNP_DECLSPEC MultiThreadedCPUBackend : public knp::core::Backend /** * @brief Get a set of iterators for projections and populations. - * + * * @return `DataRanges` structure containing iterators. */ [[nodiscard]] DataRanges get_network_data() const override; @@ -168,11 +169,11 @@ class KNP_DECLSPEC MultiThreadedCPUBackend : public knp::core::Backend public: /** * @brief Default constructor for multi-threaded CPU backend. - * + * * @param thread_count number of threads. * @param population_part_size number of synapses that are calculated in a single thread. * @param projection_part_size number of neurons that are calculated in a single thread. - * + * * @note If `thread_count` equals `0`, then the number of threads is calculated automatically. */ explicit MultiThreadedCPUBackend( @@ -180,7 +181,7 @@ class KNP_DECLSPEC MultiThreadedCPUBackend : public knp::core::Backend size_t projection_part_size = default_projection_part_size); /** * @brief Destructor for multi-threaded CPU backend. - * + * * @note All threads are stopped and joined on destruction by an internal thread pool object. */ ~MultiThreadedCPUBackend() override = default; @@ -188,7 +189,7 @@ class KNP_DECLSPEC MultiThreadedCPUBackend : public knp::core::Backend public: /** * @brief Create an object of the multi-threaded CPU backend. - * + * * @return shared pointer to backend object. */ static std::shared_ptr create(); @@ -196,31 +197,31 @@ class KNP_DECLSPEC MultiThreadedCPUBackend : public knp::core::Backend public: /** * @brief Define if plasticity is supported. - * + * * @return `true` if plasticity is supported, `false` if plasticity is not supported. */ [[nodiscard]] bool plasticity_supported() const override { return true; } /** * @brief Get type names of supported neurons. - * + * * @return vector of supported neuron type names. */ [[nodiscard]] std::vector get_supported_neurons() const override; /** * @brief Get type names of supported synapses. - * + * * @return vector of supported synapse type names. */ [[nodiscard]] std::vector get_supported_synapses() const override; /** * @brief Get indexes of supported projections. - * + * * @return vector of supported type indexes. */ [[nodiscard]] std::vector get_supported_projection_indexes() const override; /** * @brief Get indexes of supported populations. - * + * * @return vector of supported type indexes. */ [[nodiscard]] std::vector get_supported_population_indexes() const override; @@ -228,32 +229,32 @@ class KNP_DECLSPEC MultiThreadedCPUBackend : public knp::core::Backend public: /** * @brief Load populations to the backend. - * + * * @param populations vector of populations to load. */ void load_populations(const std::vector &populations); /** * @brief Load projections to the backend. - * + * * @param projections vector of projections to load. */ void load_projections(const std::vector &projections); /** * @brief Add projections to backend. - * + * * @param projections projections to add. - * + * * @throw exception if the `projections` parameter contains unsupported projection types. */ void load_all_projections(const std::vector &projections) override; /** * @brief Add populations to backend. - * + * * @param populations populations to add. - * + * * @throw exception if the `populations` parameter contains unsupported population types. */ void load_all_populations(const std::vector &populations) override; @@ -261,49 +262,49 @@ class KNP_DECLSPEC MultiThreadedCPUBackend : public knp::core::Backend public: /** * @brief Get an iterator pointing to the first element of the population loaded to backend. - * + * * @return population iterator. */ [[nodiscard]] PopulationIterator begin_populations(); /** * @brief Get an iterator pointing to the first element of the population loaded to backend. - * + * * @return constant population iterator. */ [[nodiscard]] PopulationConstIterator begin_populations() const; /** * @brief Get an iterator pointing to the last element of the population. - * + * * @return iterator. */ [[nodiscard]] PopulationIterator end_populations(); /** * @brief Get a constant iterator pointing to the last element of the population. - * + * * @return iterator. */ [[nodiscard]] PopulationConstIterator end_populations() const; /** * @brief Get an iterator pointing to the first element of the projection loaded to backend. - * + * * @return projection iterator. */ [[nodiscard]] ProjectionIterator begin_projections(); /** * @brief Get an iterator pointing to the first element of the projection loaded to backend. - * + * * @return constant projection iterator. */ [[nodiscard]] ProjectionConstIterator begin_projections() const; /** * @brief Get an iterator pointing to the last element of the projection. - * + * * @return iterator. */ [[nodiscard]] ProjectionIterator end_projections(); /** * @brief Get a constant iterator pointing to the last element of the projection. - * + * * @return iterator. */ [[nodiscard]] ProjectionConstIterator end_projections() const; @@ -311,14 +312,14 @@ class KNP_DECLSPEC MultiThreadedCPUBackend : public knp::core::Backend public: /** * @brief Remove projections with given UIDs from the backend. - * + * * @param uids UIDs of projections to remove. */ void remove_projections(const std::vector &uids) override {} /** * @brief Remove populations with given UIDs from the backend. - * + * * @param uids UIDs of populations to remove. */ void remove_populations(const std::vector &uids) override {} @@ -326,9 +327,9 @@ class KNP_DECLSPEC MultiThreadedCPUBackend : public knp::core::Backend public: /** * @brief Get a list of devices supported by the backend. - * + * * @return list of devices. - * + * * @see Device. */ [[nodiscard]] std::vector> get_devices() const override; diff --git a/knp/backends/cpu/cpu-single-threaded-backend/impl/backend.cpp b/knp/backends/cpu/cpu-single-threaded-backend/impl/backend.cpp index 218019e4..40c8f12f 100644 --- a/knp/backends/cpu/cpu-single-threaded-backend/impl/backend.cpp +++ b/knp/backends/cpu/cpu-single-threaded-backend/impl/backend.cpp @@ -252,6 +252,14 @@ std::optional SingleThreadedCPUBackend::calculate } +std::optional SingleThreadedCPUBackend::calculate_population( + core::Population &population) +{ + SPDLOG_TRACE("Calculate LIF population {}.", std::string(population.get_uid())); + return knp::backends::cpu::calculate_any_population(population, get_message_endpoint(), get_step()); +} + + void SingleThreadedCPUBackend::calculate_projection( knp::core::Projection &projection, SynapticMessageQueue &message_queue) { diff --git a/knp/backends/cpu/cpu-single-threaded-backend/include/knp/backends/cpu-single-threaded/backend.h b/knp/backends/cpu/cpu-single-threaded-backend/include/knp/backends/cpu-single-threaded/backend.h index a7fc9cf3..03d2f849 100644 --- a/knp/backends/cpu/cpu-single-threaded-backend/include/knp/backends/cpu-single-threaded/backend.h +++ b/knp/backends/cpu/cpu-single-threaded-backend/include/knp/backends/cpu-single-threaded/backend.h @@ -57,7 +57,8 @@ class KNP_DECLSPEC SingleThreadedCPUBackend : public knp::core::Backend */ using SupportedNeurons = boost::mp11::mp_list< knp::neuron_traits::BLIFATNeuron, knp::neuron_traits::SynapticResourceSTDPBLIFATNeuron, - knp::neuron_traits::AltAILIF, knp::neuron_traits::SynapticResourceSTDPAltAILIFNeuron>; + knp::neuron_traits::AltAILIF, knp::neuron_traits::SynapticResourceSTDPAltAILIFNeuron, + knp::neuron_traits::LIFNeuron>; /** * @brief List of synapse types supported by the single-threaded CPU backend. @@ -78,26 +79,26 @@ class KNP_DECLSPEC SingleThreadedCPUBackend : public knp::core::Backend /** * @brief Population variant that contains any population type specified in `SupportedPopulations`. - * + * * @details `PopulationVariants` takes the value of `std::variant`, where * `PopulationType_[1..n]` is the population type specified in `SupportedPopulations`. \n * For example, if `SupportedPopulations` contains BLIFATNeuron and IzhikevichNeuron types, * then `PopulationVariants = std::variant`. \n * `PopulationVariants` retains the same order of message types as defined in `SupportedPopulations`. - * + * * @see ALL_NEURONS. */ using PopulationVariants = boost::mp11::mp_rename; /** * @brief Projection variant that contains any projection type specified in `SupportedProjections`. - * + * * @details `ProjectionVariants` takes the value of `std::variant`, where * `ProjectionType_[1..n]` is the projection type specified in `SupportedProjections`. \n * For example, if `SupportedProjections` contains DeltaSynapse and AdditiveSTDPSynapse types, * then `ProjectionVariants = std::variant`. \n * `ProjectionVariants` retains the same order of message types as defined in `SupportedProjections`. - * + * * @see ALL_SYNAPSES. */ using ProjectionVariants = boost::mp11::mp_rename; @@ -152,7 +153,7 @@ class KNP_DECLSPEC SingleThreadedCPUBackend : public knp::core::Backend public: /** * @brief Create an object of the single-threaded CPU backend. - * + * * @return shared pointer to backend object. */ static std::shared_ptr create(); @@ -160,31 +161,31 @@ class KNP_DECLSPEC SingleThreadedCPUBackend : public knp::core::Backend public: /** * @brief Define if plasticity is supported. - * + * * @return `true` if plasticity is supported, `false` if plasticity is not supported. */ [[nodiscard]] bool plasticity_supported() const override { return true; } /** * @brief Get type names of supported neurons. - * + * * @return vector of supported neuron type names. */ [[nodiscard]] std::vector get_supported_neurons() const override; /** * @brief Get type names of supported synapses. - * + * * @return vector of supported synapse type names. */ [[nodiscard]] std::vector get_supported_synapses() const override; /** * @brief Get indexes of supported projections. - * + * * @return type indexes. */ [[nodiscard]] std::vector get_supported_projection_indexes() const override; /** * @brief Get indexes of supported populations. - * + * * @return type indexes. */ [[nodiscard]] std::vector get_supported_population_indexes() const override; @@ -192,32 +193,32 @@ class KNP_DECLSPEC SingleThreadedCPUBackend : public knp::core::Backend public: /** * @brief Load populations to the backend. - * + * * @param populations vector of populations to load. */ void load_populations(const std::vector &populations); /** * @brief Load projections to the backend. - * + * * @param projections vector of projections to load. */ void load_projections(const std::vector &projections); /** * @brief Add projections to backend. - * + * * @param projections projections to add. - * + * * @throw exception if the `projections` parameter contains unsupported projection types. */ void load_all_projections(const std::vector &projections) override; /** * @brief Add populations to backend. - * + * * @param populations populations to add. - * + * * @throw exception if the `populations` parameter contains unsupported population types. */ void load_all_populations(const std::vector &populations) override; @@ -225,26 +226,26 @@ class KNP_DECLSPEC SingleThreadedCPUBackend : public knp::core::Backend public: /** * @brief Get an iterator pointing to the first element of the population loaded to backend. - * + * * @return population iterator. */ PopulationIterator begin_populations(); /** * @brief Get an iterator pointing to the first element of the population loaded to backend. - * + * * @return constant population iterator. */ PopulationConstIterator begin_populations() const; /** * @brief Get an iterator pointing to the last element of the population. - * + * * @return iterator. */ PopulationIterator end_populations(); /** * @brief Get a constant iterator pointing to the last element of the population. - * + * * @return iterator. */ PopulationConstIterator end_populations() const; @@ -254,25 +255,25 @@ class KNP_DECLSPEC SingleThreadedCPUBackend : public knp::core::Backend */ /** * @brief Get an iterator pointing to the first element of the projection loaded to backend. - * + * * @return projection iterator. */ ProjectionIterator begin_projections(); /** * @brief Get an iterator pointing to the first element of the projection loaded to backend. - * + * * @return constant projection iterator. */ ProjectionConstIterator begin_projections() const; /** * @brief Get an iterator pointing to the last element of the projection. - * + * * @return iterator. */ ProjectionIterator end_projections(); /** * @brief Get a constant iterator pointing to the last element of the projection. - * + * * @return iterator. */ ProjectionConstIterator end_projections() const; @@ -280,14 +281,14 @@ class KNP_DECLSPEC SingleThreadedCPUBackend : public knp::core::Backend public: /** * @brief Remove projections with given UIDs from the backend. - * + * * @param uids UIDs of projections to remove. */ void remove_projections(const std::vector &uids) override {} /** * @brief Remove populations with given UIDs from the backend. - * + * * @param uids UIDs of populations to remove. */ void remove_populations(const std::vector &uids) override {} @@ -295,9 +296,9 @@ class KNP_DECLSPEC SingleThreadedCPUBackend : public knp::core::Backend public: /** * @brief Get a list of devices supported by the backend. - * + * * @return list of devices. - * + * * @see Device. */ [[nodiscard]] std::vector> get_devices() const override; @@ -332,7 +333,7 @@ class KNP_DECLSPEC SingleThreadedCPUBackend : public knp::core::Backend /** * @brief Get a set of iterators for projections and populations. - * + * * @return `DataRanges` structure containing iterators. */ [[nodiscard]] DataRanges get_network_data() const override; @@ -350,11 +351,11 @@ class KNP_DECLSPEC SingleThreadedCPUBackend : public knp::core::Backend /** * @brief Calculate a population of BLIFAT neurons. - * + * * @param population population to calculate. - * + * * @return spike message with indexes of spiking neurons; empty if the population does not emit a spike. - * + * * @note The population state is modified during the calculation. */ std::optional calculate_population( @@ -362,11 +363,11 @@ class KNP_DECLSPEC SingleThreadedCPUBackend : public knp::core::Backend /** * @brief Calculate a population of `SynapticResourceSTDPBLIFATNeuron` neurons. - * + * * @param population population to calculate. - * + * * @return spike message with indexes of spiking neurons; empty if the population does not emit a spike. - * + * * @note The population state is modified during the calculation. */ std::optional calculate_population( @@ -374,11 +375,11 @@ class KNP_DECLSPEC SingleThreadedCPUBackend : public knp::core::Backend /** * @brief Calculate a population of 'AltAILIF' neurons. - * + * * @param population population to calculate. - * + * * @return spike message with indexes of spiking neurons; empty if the population does not emit a spike. - * + * * @note The population state is modified during the calculation. */ std::optional calculate_population( @@ -387,33 +388,43 @@ class KNP_DECLSPEC SingleThreadedCPUBackend : public knp::core::Backend /** * @brief Calculate a population of 'SynapticResourceSTDPAltAILIFNeuron' neurons. - * + * * @param population population to calculate. - * + * * @return spike message with indexes of spiking neurons; empty if the population does not emit a spike. - * + * * @note The population state is modified during the calculation. */ std::optional calculate_population( knp::core::Population &population); + /** + * @brief Calculate population of LIF neurons. + * @note Population state will be changed during calculation. + * @param population population to calculate. + * @return spike message with indexes of spiked neurons if population is emitting one. + */ + std::optional calculate_population( + knp::core::Population &population); + + /** * @brief Calculate a projection of delta synapses. - * + * * @param projection projection to calculate. * @param message_queue message queue to send to projection for calculation. - * + * * @note The projection is modified during the calculation. */ void calculate_projection( knp::core::Projection &projection, SynapticMessageQueue &message_queue); /** * @brief Calculate a projection of `AdditiveSTDPDeltaSynapse` synapses. - * + * * @param projection projection to calculate. * @param message_queue message queue to send to projection for calculation. - * + * * @note The projection is modified during the calculation. */ void calculate_projection( @@ -421,10 +432,10 @@ class KNP_DECLSPEC SingleThreadedCPUBackend : public knp::core::Backend SynapticMessageQueue &message_queue); /** * @brief Calculate a projection of `SynapticResourceSTDPDeltaSynapse` synapses. - * + * * @param projection projection to calculate. * @param message_queue message queue to send to projection for calculation. - * + * * @note The projection is modified during the calculation. */ void calculate_projection( diff --git a/knp/base-framework/CMakeLists.txt b/knp/base-framework/CMakeLists.txt index c7dbc87e..5fede91e 100644 --- a/knp/base-framework/CMakeLists.txt +++ b/knp/base-framework/CMakeLists.txt @@ -66,6 +66,7 @@ knp_add_library("${PROJECT_NAME}-core" impl/sonata/save_network.cpp impl/sonata/load_network.cpp impl/sonata/csv_content.cpp + impl/sonata/types/lif_neuron.cpp impl/sonata/types/blifat_neuron.cpp impl/sonata/types/delta_synapse.cpp impl/sonata/types/resource_blifat_neuron.cpp diff --git a/knp/base-framework/impl/sonata/load_network.cpp b/knp/base-framework/impl/sonata/load_network.cpp index b0ac757d..cb21d6d1 100644 --- a/knp/base-framework/impl/sonata/load_network.cpp +++ b/knp/base-framework/impl/sonata/load_network.cpp @@ -109,6 +109,11 @@ std::vector load_populations(const fs::path &pop_h5 result.emplace_back(load_population(group, proj_name)); else if (neuron_type == get_neuron_type_id()) result.emplace_back(load_population(group, proj_name)); + else if (neuron_type == get_neuron_type_id()) + result.emplace_back(load_population(group, proj_name)); + else if (neuron_type == get_neuron_type_id()) + result.emplace_back(load_population(group, proj_name)); + // TODO: Add other supported types or better use a template. } return result; diff --git a/knp/base-framework/impl/sonata/types/lif_neuron.cpp b/knp/base-framework/impl/sonata/types/lif_neuron.cpp new file mode 100644 index 00000000..0f9ea84b --- /dev/null +++ b/knp/base-framework/impl/sonata/types/lif_neuron.cpp @@ -0,0 +1,115 @@ +/** + * @file lif_neuron.cpp + * @brief AltaiLIF neuron procedures. + * @kaspersky_support An. Vartenkov + * @date 15.05.2024 + * @license Apache 2.0 + * @copyright © 2024-2025 AO Kaspersky Lab + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include + +#include "../csv_content.h" +#include "../highfive.h" +#include "../load_network.h" +#include "../save_network.h" +#include "saving_initialization.h" +#include "type_id_defines.h" + + +namespace knp::framework::sonata +{ + +template <> +std::string get_neuron_type_name() +{ + return "knp:LIFNeuron"; +} + + +void save_static(const core::Population &population, HighFive::Group &group) +{ + SPDLOG_TRACE("Saving LIF neurons static parameters."); + PUT_NEURON_TO_DATASET(population, activation_threshold_, group); + PUT_NEURON_TO_DATASET(population, leak_coefficient_, group); + PUT_NEURON_TO_DATASET(population, refract_period_, group); +} + + +void save_dynamic(const core::Population &population, HighFive::Group &group0) +{ + SPDLOG_TRACE("Saving LIF neurons dynamic parameters."); + auto dyn_group = group0.createGroup(dynamic_subgroup_name); + PUT_NEURON_TO_DATASET(population, potential_, dyn_group); + PUT_NEURON_TO_DATASET(population, refract_counter_, dyn_group); +} + + +template <> +void add_population_to_h5>( + HighFive::File &file_h5, const core::Population &population) +{ + SPDLOG_DEBUG("Saving LIF population to sonata..."); + auto group0 = initialize_adding_population(population, file_h5); + save_static(population, group0); + save_dynamic(population, group0); +} + + +void load_static_parameters( + const HighFive::Group &group0, std::vector> &target) +{ + SPDLOG_TRACE("Loading static LIF parameters."); + const size_t group_size = target.size(); + LOAD_NEURONS_PARAMETER(target, neuron_traits::LIFNeuron, activation_threshold_, group0, group_size); + LOAD_NEURONS_PARAMETER(target, neuron_traits::LIFNeuron, leak_coefficient_, group0, group_size); + LOAD_NEURONS_PARAMETER(target, neuron_traits::LIFNeuron, refract_period_, group0, group_size); +} + + +void load_dynamic_parameters( + const HighFive::Group &group0, std::vector> &target) +{ + SPDLOG_TRACE("Loading LIF neurons dynamic parameters."); + const size_t group_size = target.size(); + auto dyn_group = group0.getGroup(dynamic_subgroup_name); + LOAD_NEURONS_PARAMETER(target, neuron_traits::LIFNeuron, potential_, dyn_group, group_size); + LOAD_NEURONS_PARAMETER(target, neuron_traits::LIFNeuron, refract_counter_, dyn_group, group_size); +} + + +template <> +core::Population load_population( + const HighFive::Group &nodes_group, const std::string &population_name) +{ + SPDLOG_DEBUG("Loading nodes..."); + auto group0 = nodes_group.getGroup(population_name).getGroup("0"); + const size_t group_size = nodes_group.getGroup(population_name).getDataSet("node_id").getDimensions().at(0); + + // TODO: Load default neuron from JSON file. + std::vector> target(group_size); + load_static_parameters(group0, target); + load_dynamic_parameters(group0, target); + const knp::core::UID uid{boost::lexical_cast(population_name)}; + core::Population out_population( + uid, [&target](size_t index) { return target[index]; }, group_size); + return out_population; +} +} // namespace knp::framework::sonata diff --git a/knp/neuron-traits-library/include/knp/neuron-traits/all_traits.h b/knp/neuron-traits-library/include/knp/neuron-traits/all_traits.h index 4f870f4f..86585e2b 100644 --- a/knp/neuron-traits-library/include/knp/neuron-traits/all_traits.h +++ b/knp/neuron-traits-library/include/knp/neuron-traits/all_traits.h @@ -27,6 +27,7 @@ #include "altai_lif.h" #include "blifat.h" +#include "lif.h" #include "stdp_synaptic_resource_rule.h" #include "stdp_type_traits.h" @@ -38,7 +39,8 @@ namespace knp::neuron_traits /** * @brief Comma-separated list of neuron tags. */ -#define ALL_NEURONS BLIFATNeuron, SynapticResourceSTDPBLIFATNeuron, AltAILIF, SynapticResourceSTDPAltAILIFNeuron +#define ALL_NEURONS \ + BLIFATNeuron, SynapticResourceSTDPBLIFATNeuron, AltAILIF, SynapticResourceSTDPAltAILIFNeuron, LIFNeuron /** diff --git a/knp/neuron-traits-library/include/knp/neuron-traits/lif.h b/knp/neuron-traits-library/include/knp/neuron-traits/lif.h new file mode 100644 index 00000000..76bf2676 --- /dev/null +++ b/knp/neuron-traits-library/include/knp/neuron-traits/lif.h @@ -0,0 +1,118 @@ +/** + * @file lif.h + * @brief LIF neuron type traits. + * @kaspersky_support David P. + * @date 29.04.2026 + * @license Apache 2.0 + * @copyright © 2026 AO Kaspersky Lab + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "type_traits.h" + + +/** + * @brief Namespace for neuron traits. + */ +namespace knp::neuron_traits +{ + +/** + * @brief LIF neuron. + * @note Use as a template parameter only. + */ +struct LIFNeuron; + + +/** + * @brief Structure for LIF neuron default values. + */ +template <> +struct default_values +{ + /** + * @brief Default value for potential. + */ + constexpr static float potential_ = 0; + + /** + * @brief Default value for potential reset. + */ + constexpr static float potential_reset_value_ = 0; + + /** + * @brief Default value for activation threshold. + */ + constexpr static float activation_threshold_ = 1; + + /** + * @brief Default value for leak coefficient. + */ + constexpr static float leak_coefficient_ = 1; + + /** + * @brief Default value for refract counter. + */ + constexpr static uint32_t refract_counter_ = 0; + + /** + * @brief Default value for refract period. + */ + constexpr static uint32_t refract_period_ = 0; +}; + + +/** + * @brief Structure for LIF neuron parameters. + */ +template <> +struct neuron_parameters +{ + /** + * @brief If neuron's potential exceeds activation_threshold, spike is produced, and potential is reset. + */ + float potential_ = default_values::potential_; + + /** + * @brief When neuron is activated, potential is reset to this value. + */ + float potential_reset_value_ = default_values::potential_reset_value_; + + /** + * @brief Threshold for neuron activation. + */ + float activation_threshold_ = default_values::activation_threshold_; + + /** + * @brief Multiplier of potential on each pre-impact step. + */ + float leak_coefficient_ = default_values::leak_coefficient_; + + /** + * @brief Refract counter. On neuron activation, counter is set to refract_period and decremented on each step. + * Incoming impacts are ignored if refract_counter > 0. + */ + uint32_t refract_counter_ = default_values::refract_counter_; + + /** + * @brief Refract period for refract counter. + */ + uint32_t refract_period_ = default_values::refract_period_; +}; + +} // namespace knp::neuron_traits diff --git a/knp/tests/framework/lif_test.cpp b/knp/tests/framework/lif_test.cpp new file mode 100644 index 00000000..d9f8bc76 --- /dev/null +++ b/knp/tests/framework/lif_test.cpp @@ -0,0 +1,178 @@ +/** + * @file lif_test.cpp + * @brief LIF neuron test. + * @kaspersky_support David P. + * @date 05.05.2026 + * @license Apache 2.0 + * @copyright © 2026 AO Kaspersky Lab + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include +#include +#include +#include + +#include + +#include + + +using Synapse = knp::synapse_traits::DeltaSynapse; + + +namespace +{ + +class TestingBackendST : public knp::backends::single_threaded_cpu::SingleThreadedCPUBackend +{ +public: + TestingBackendST() = default; + void _init() override { knp::backends::single_threaded_cpu::SingleThreadedCPUBackend::_init(); } +}; + +using Population = knp::core::Population; +using Projection = knp::core::Projection; + + +struct NeuronLog +{ + std::vector potential_; + std::vector spikes_; +}; + + +NeuronLog run_lif_neuron( + const knp::neuron_traits::neuron_parameters &neuron, size_t steps, + const std::vector &impacts = {}) +{ + const knp::core::UID pop_uid, in_uid, out_uid; + knp::core::Population population{pop_uid, [&neuron](size_t) { return neuron; }, 1}; + TestingBackendST backend; + backend.subscribe(pop_uid, {in_uid}); + auto endpoint = backend.get_message_bus().create_endpoint(); + endpoint.subscribe(out_uid, {pop_uid}); + + backend.load_populations({population}); + backend._init(); + auto &pop = *backend.begin_populations(); + NeuronLog result; + const auto &neuron_ref = std::get>(pop)[0]; + for (size_t step = 0; step < steps; ++step) + { + const knp::core::messaging::MessageHeader header{in_uid, step}; + if (step < impacts.size()) + { + knp::core::messaging::SynapticImpact impact{ + 0, impacts[step], knp::synapse_traits::OutputType::EXCITATORY, 0, 0}; + const knp::core::messaging::SynapticImpactMessage msg{ + header, knp::core::UID{false}, pop_uid, true, {impact}}; + endpoint.send_message(msg); + } + result.potential_.push_back(neuron_ref.potential_); + backend._step(); + endpoint.receive_all_messages(); + auto out_msgs = endpoint.unload_messages(out_uid); + if (!out_msgs.empty() && !out_msgs[0].neuron_indexes_.empty()) + { + if (std::find(out_msgs[0].neuron_indexes_.begin(), out_msgs[0].neuron_indexes_.end(), 0) != + out_msgs[0].neuron_indexes_.end()) + result.spikes_.push_back(step); + } + } + return result; +} + +} //namespace + + +TEST(LIFNeuron, NeuronPotentialLeakRev) +{ + constexpr int starting_potential = 100; + constexpr float leak_coefficient = 0.25F; + constexpr size_t steps_amount = 10; + auto base_neuron = knp::neuron_traits::neuron_parameters{}; + base_neuron.activation_threshold_ = starting_potential + 1; // We don't want activations in this test. + base_neuron.leak_coefficient_ = leak_coefficient; + base_neuron.potential_ = starting_potential; + + auto results = run_lif_neuron(base_neuron, steps_amount); + + std::vector expected_results; + expected_results.reserve(steps_amount); + expected_results.push_back(starting_potential); + for (size_t power = 0; power < steps_amount - 1; ++power) + { + expected_results.push_back(expected_results[power] * leak_coefficient); + } + + ASSERT_EQ(results.potential_, expected_results); + ASSERT_TRUE(results.spikes_.empty()); +} + + +TEST(LIFNeuron, Threshold) +{ + constexpr float leak_coefficient = 0.5F; + constexpr float activation_threshold = 1.F; + constexpr float potential = 3.F; + constexpr size_t steps_amount = 3; + auto base_neuron = knp::neuron_traits::neuron_parameters{}; + base_neuron.leak_coefficient_ = leak_coefficient; + base_neuron.activation_threshold_ = activation_threshold; + base_neuron.potential_ = potential; + auto result = run_lif_neuron(base_neuron, steps_amount); + const std::vector expected_potential{potential, 0, 0}; + const std::vector expected_spikes{0}; + ASSERT_EQ(result.potential_, expected_potential); + ASSERT_EQ(result.spikes_, expected_spikes); +} + + +TEST(LIFNeuron, ImpactsSpikes) +{ + constexpr float leak_coefficient = 0.5F; + constexpr float threshold = 6.F; + constexpr size_t steps_amount = 6; + auto base_neuron = knp::neuron_traits::neuron_parameters{}; + base_neuron.leak_coefficient_ = leak_coefficient; + base_neuron.activation_threshold_ = threshold; + const std::vector impacts{5.F, 5.F, 2.F, 3.F, 8.F}; + auto result = run_lif_neuron(base_neuron, steps_amount, impacts); + const std::vector expected_potential{0.F, 5.F, 0.F, 2.F, 4.F, 0.F}; + const std::vector expected_spikes{1, 4}; + ASSERT_EQ(result.potential_, expected_potential); + ASSERT_EQ(result.spikes_, expected_spikes); +} + + +TEST(LIFNeuron, RefractPeriod) +{ + constexpr float leak_coefficient = 0.5F; + constexpr float threshold = 6.F; + constexpr size_t steps_amount = 7; + auto base_neuron = knp::neuron_traits::neuron_parameters{}; + base_neuron.leak_coefficient_ = leak_coefficient; + base_neuron.activation_threshold_ = threshold; + base_neuron.refract_period_ = 2; + const std::vector impacts{5.F, 5.F, 10.F, 10.F, 5.F, 5.F}; + auto result = run_lif_neuron(base_neuron, steps_amount, impacts); + const std::vector expected_potential{0.F, 5.F, 0.F, 0.F, 0.F, 5.F, 0.F}; + const std::vector expected_spikes{1, 5}; + ASSERT_EQ(result.potential_, expected_potential); + ASSERT_EQ(result.spikes_, expected_spikes); +} diff --git a/knp/tests/framework/save_load_test.cpp b/knp/tests/framework/save_load_test.cpp index c9bc0271..7b566bf1 100644 --- a/knp/tests/framework/save_load_test.cpp +++ b/knp/tests/framework/save_load_test.cpp @@ -20,17 +20,25 @@ */ #include +#include #include #include #include +#include + +namespace +{ + +template knp::framework::Network make_simple_network() { namespace kt = knp::testing; // Create a single-neuron neural network: input -> input_projection -> population <=> loop_projection. - kt::BLIFATPopulation population{kt::neuron_generator, 1}; + knp::core::Population population{ + knp::framework::population::neurons_generators::make_default(), 1}; knp::core::Projection loop_projection = kt::DeltaProjection{population.get_uid(), population.get_uid(), kt::synapse_generator, 1}; knp::core::Projection input_projection = @@ -43,33 +51,6 @@ knp::framework::Network make_simple_network() } -class SaveLoadNetworkSuite : public ::testing::Test -{ -protected: - void TearDown() override - { - std::filesystem::remove_all(path_to_network_ / "network"); - std::filesystem::remove(path_to_network_ / "config.json"); - } - - std::filesystem::path path_to_network_; -}; - - -TEST_F(SaveLoadNetworkSuite, SaveTest) -{ - auto network = make_simple_network(); - path_to_network_ = "."; - knp::framework::sonata::save_network(network, path_to_network_); - ASSERT_TRUE(std::filesystem::is_directory(path_to_network_ / "network")); - ASSERT_TRUE(std::filesystem::is_regular_file(path_to_network_ / "network/network_config.json")); - ASSERT_TRUE(std::filesystem::is_regular_file(path_to_network_ / "network/populations.h5")); - ASSERT_TRUE(std::filesystem::is_regular_file(path_to_network_ / "network/projections.h5")); - ASSERT_TRUE(std::filesystem::is_regular_file(path_to_network_ / "network/neurons.csv")); - ASSERT_TRUE(std::filesystem::is_regular_file(path_to_network_ / "network/synapses.csv")); -} - - template knp::core::UID get_uid(const Entity &entity) { @@ -121,12 +102,51 @@ bool are_networks_similar(const knp::framework::Network ¤t, const knp::fra return true; } +} //namespace + -TEST_F(SaveLoadNetworkSuite, SaveLoadTest) +class SaveLoadNetworkSuite : public ::testing::Test { +protected: + void TearDown() override + { + std::filesystem::remove_all(path_to_network_ / "network"); + std::filesystem::remove(path_to_network_ / "config.json"); + } + + std::filesystem::path path_to_network_; + + template + void save_load_test() + { + path_to_network_ = "."; + auto network = make_simple_network(); + knp::framework::sonata::save_network(network, path_to_network_); + auto network_loaded = knp::framework::sonata::load_network(path_to_network_); + ASSERT_TRUE(are_networks_similar(network, network_loaded)); + } +}; + + +TEST_F(SaveLoadNetworkSuite, SaveTest) +{ + auto network = make_simple_network(); path_to_network_ = "."; - auto network = make_simple_network(); knp::framework::sonata::save_network(network, path_to_network_); - auto network_loaded = knp::framework::sonata::load_network(path_to_network_); - ASSERT_TRUE(are_networks_similar(network, network_loaded)); + ASSERT_TRUE(std::filesystem::is_directory(path_to_network_ / "network")); + ASSERT_TRUE(std::filesystem::is_regular_file(path_to_network_ / "network/network_config.json")); + ASSERT_TRUE(std::filesystem::is_regular_file(path_to_network_ / "network/populations.h5")); + ASSERT_TRUE(std::filesystem::is_regular_file(path_to_network_ / "network/projections.h5")); + ASSERT_TRUE(std::filesystem::is_regular_file(path_to_network_ / "network/neurons.csv")); + ASSERT_TRUE(std::filesystem::is_regular_file(path_to_network_ / "network/synapses.csv")); +} + + +#define NEURON_TESTS(n, _, neuron_type) save_load_test(); + + +TEST_F(SaveLoadNetworkSuite, SaveLoadNeuronTypes) +{ + // Testing all neuron types. + BOOST_PP_SEQ_FOR_EACH(NEURON_TESTS, , BOOST_PP_VARIADIC_TO_SEQ(ALL_NEURONS)) }