our_knp/examples/knp/core-library/impl/projection.cpp
int 8f43927575 Initial import: KNP experiments (examples, viz, cats-dogs, reports)
examples/ is based on KasperskyLab/knp at 55470ed6 with local changes
to the mnist-learn altai/blifat models, cuda-backend CMake and model_loader.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:17:15 +03:00

287 lines
9.2 KiB
C++

/**
* @file projection.cpp
* @brief Projection class implementation.
* @kaspersky_support Andrey V.
* @date 26.01.2023
* @license Apache 2.0
* @copyright © 2024 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 <knp/core/projection.h>
#include <spdlog/spdlog.h>
// Index functions.
template <class Index, class Connection>
void insert_to_index(Index &val, const Connection &connection)
{
val.insert(connection);
}
template <class Index, class Type>
auto find_by_type(const Index &val, size_t neuron_index)
{
return val.template get<Type>().equal_range(neuron_index);
}
template <class Index, class Type>
std::vector<size_t> find_indexes_by_type(const Index &val, size_t neuron_index)
{
auto range = find_by_type<Index, Type>(val, neuron_index);
std::vector<size_t> res;
std::transform(range.first, range.second, std::back_inserter(res), [](const auto &val) { return val.index_; });
return res;
}
/**
* @brief Remove a synapse with a given index.
* @param index index of a synapse.
* @return true if an element was found and erased.
*/
template <class Index, class Projection>
bool erase_synapse(Index &val, size_t index)
{
auto num_erased = val.template get<typename Projection::mi_synapse_index>().erase(index);
return num_erased > 0;
}
/**
* @brief Remove elements by their indexes in a single pass.
* @tparam T value type.
* @param data vector that will be modified by deletion.
* @param to_remove indexes of the elements to remove
* @warning Indexes must be sorted.
*/
template <class T, class IndexContainer>
void remove_by_index(std::vector<T> &data, const IndexContainer &to_remove)
{
if (to_remove.empty()) return;
size_t cor_counter = 0;
for (const auto rem_index : to_remove)
{
data.erase(std::next(data.begin(), rem_index - cor_counter++));
}
// Cool but incorrect (MSVC\14.40.33807\include\vector(56) : Assertion failed: can't dereference out of range vector
// iterator):
// size_t removed = 0;
// auto next_to_remove = to_remove.begin();
// size_t index = *next_to_remove;
// while (index + removed < data.size())
//{
// while (index + removed == *next_to_remove)
// {
// ++next_to_remove;
// ++removed;
// }
// data[index] = std::move(data[index + removed]);
// ++index;
//}
// data.resize(data.size() - removed);
}
namespace knp::core
{
using Connection = typename std::tuple<size_t, size_t, size_t>;
template <typename SynapseType>
Projection<SynapseType>::Projection(UID presynaptic_uid, UID postsynaptic_uid) //!OCLINT(Parameters used)
: presynaptic_uid_(presynaptic_uid), postsynaptic_uid_(postsynaptic_uid)
{
SPDLOG_DEBUG(
"Creating projection with UID = {}, presynaptic UID = {}, postsynaptic UID = {}...", std::string(get_uid()),
std::string(presynaptic_uid_), std::string(postsynaptic_uid_));
}
template <typename SynapseType>
Projection<SynapseType>::Projection(UID uid, UID presynaptic_uid, UID postsynaptic_uid) //!OCLINT(Parameters used)
: base_{uid}, presynaptic_uid_(presynaptic_uid), postsynaptic_uid_(postsynaptic_uid)
{
SPDLOG_DEBUG(
"Creating projection with UID = {}, presynaptic UID = {}, postsynaptic UID = {}...", std::string(get_uid()),
std::string(presynaptic_uid_), std::string(postsynaptic_uid_));
}
template <typename SynapseType>
Projection<SynapseType>::Projection(
UID presynaptic_uid, UID postsynaptic_uid, SynapseGenerator generator, //!OCLINT(Parameters used)
size_t num_iterations) //!OCLINT(Parameters used)
: presynaptic_uid_(presynaptic_uid), postsynaptic_uid_(postsynaptic_uid)
{
SPDLOG_DEBUG(
"Creating projection with UID = {}, presynaptic UID = {}, postsynaptic UID = {}, i = {}...",
std::string(get_uid()), std::string(presynaptic_uid_), std::string(postsynaptic_uid_), num_iterations);
for (size_t i = 0; i < num_iterations; ++i)
{
if (auto params = generator(i))
{
parameters_.emplace_back(std::move(params.value()));
}
}
reindex();
}
template <typename SynapseType>
Projection<SynapseType>::Projection(
UID uid, UID presynaptic_uid, UID postsynaptic_uid, SynapseGenerator generator, //!OCLINT(Parameters used)
size_t num_iterations) //!OCLINT(Parameters used)
: base_{uid}, presynaptic_uid_(presynaptic_uid), postsynaptic_uid_(postsynaptic_uid)
{
SPDLOG_DEBUG(
"Creating projection with UID = {}, presynaptic UID = {}, postsynaptic UID = {}, i = {}...",
std::string(get_uid()), std::string(presynaptic_uid_), std::string(postsynaptic_uid_), num_iterations);
for (size_t i = 0; i < num_iterations; ++i)
{
if (auto params = generator(i))
{
auto &&[p, id_from, id_to] = std::move(params.value());
parameters_.emplace_back(Synapse{std::move(p), id_from, id_to});
}
}
reindex();
}
template <typename SynapseType>
std::vector<size_t> knp::core::Projection<SynapseType>::find_synapses(
size_t neuron_id, Search search_criterion) const //!OCLINT(Parameters used)
{
reindex();
std::vector<size_t> res;
switch (search_criterion)
{
case Search::by_postsynaptic:
res = find_indexes_by_type<decltype(index_), core::Projection<SynapseType>::ByPostsynaptic>(
index_, neuron_id);
break;
case Search::by_presynaptic:
res =
find_indexes_by_type<decltype(index_), core::Projection<SynapseType>::ByPresynaptic>(index_, neuron_id);
break;
default:
return {};
}
return res;
}
template <typename SynapseType>
size_t knp::core::Projection<SynapseType>::add_synapses(
SynapseGenerator generator, size_t num_iterations) //!OCLINT(Parameters used)
{
const size_t starting_size = parameters_.size();
is_index_updated_ = false;
for (size_t i = 0; i < num_iterations; ++i)
{
if (auto data = generator(i))
{
parameters_.emplace_back(std::move(data.value()));
}
}
return parameters_.size() - starting_size;
}
template <typename SynapseType>
void Projection<SynapseType>::clear()
{
parameters_.clear();
index_.clear();
}
template <typename SynapseType>
void knp::core::Projection<SynapseType>::remove_synapse(size_t index) //!OCLINT
{
is_index_updated_ = false;
parameters_.erase(parameters_.begin() + index);
}
template <typename SynapseType>
size_t knp::core::Projection<SynapseType>::remove_synapse_if(std::function<bool(const Synapse &)> predicate) //!OCLINT
{
const size_t starting_size = parameters_.size();
is_index_updated_ = false;
parameters_.resize(std::remove_if(parameters_.begin(), parameters_.end(), predicate) - parameters_.begin());
return starting_size - parameters_.size();
}
template <typename SynapseType>
size_t knp::core::Projection<SynapseType>::remove_postsynaptic_neuron_synapses(size_t neuron_index) //!OCLINT
{
const size_t starting_size = parameters_.size();
bool was_index_updated = is_index_updated_;
// Basic exception safety.
is_index_updated_ = false;
auto synapses_to_remove = find_synapses(neuron_index, Search::by_postsynaptic);
std::sort(synapses_to_remove.begin(), synapses_to_remove.end());
remove_by_index(parameters_, synapses_to_remove);
if (was_index_updated)
for (auto &synapse : synapses_to_remove) index_.erase(synapse);
is_index_updated_ = was_index_updated;
return starting_size - parameters_.size();
}
template <typename SynapseType>
size_t knp::core::Projection<SynapseType>::remove_presynaptic_neuron_synapses(size_t neuron_index) //!OCLINT
{
// TODO: We now have a way to find them quickly, make use of it instead of disconnect_if.
return remove_synapse_if([neuron_index](const Synapse &synapse)
{ return std::get<knp::core::source_neuron_id>(synapse) == neuron_index; });
}
template <typename SynapseType>
void knp::core::Projection<SynapseType>::reindex() const
{
if (is_index_updated_)
{
return;
}
index_.clear();
for (size_t i = 0; i < parameters_.size(); ++i)
{
auto &synapse = parameters_[i];
insert_to_index(
index_,
Connection{
std::get<knp::core::source_neuron_id>(synapse), std::get<knp::core::target_neuron_id>(synapse), i});
}
is_index_updated_ = true;
}
#define INSTANCE_PROJECTIONS(n, template_for_instance, synapse_type) \
template class knp::core::Projection<knp::synapse_traits::synapse_type>;
BOOST_PP_SEQ_FOR_EACH(INSTANCE_PROJECTIONS, "", BOOST_PP_VARIADIC_TO_SEQ(ALL_SYNAPSES)) //!OCLINT
} // namespace knp::core