our_knp/examples/knp/base-framework/impl/model_loader.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

182 lines
6.2 KiB
C++

/**
* @file model_loader.cpp
* @brief Model loader class implementation.
* @kaspersky_support Artiom N.
* @date 14.06.2024
* @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/framework/model_loader.h>
#include <spdlog/spdlog.h>
#include <exception>
namespace knp::framework
{
template <typename GenType>
void ModelLoader::init_channels(
knp::framework::Model &model, const std::unordered_multimap<core::UID, core::UID, core::uid_hash> &channels,
GenType channel_gen)
{
// Iterate over distinct channel UIDs: a hash bucket can hold several different channels.
for (auto iter = channels.begin(); iter != channels.end();)
{
const auto channel_uid = iter->first;
const auto range = channels.equal_range(channel_uid);
std::vector<core::UID> p_uids;
std::transform(
range.first, range.second, std::back_inserter(p_uids),
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wunused-lambda-capture"
#endif
[&channel_uid](const auto &bucket)
{
SPDLOG_TRACE(
"Inserting channel {} peer bucket UID = {}...", std::string(channel_uid),
std::string(bucket.second));
return bucket.second;
});
#ifdef __clang__
# pragma clang diagnostic pop
#endif
iter = range.second;
(this->*channel_gen)(model, channel_uid, p_uids);
}
}
void ModelLoader::gen_input_channel(
knp::framework::Model &model, const core::UID &channel_uid, const std::vector<core::UID> &p_uids)
{
try
{
in_channels_.emplace_back(channel_uid, backend_->get_message_bus().create_endpoint(), i_map_.at(channel_uid));
}
catch (const std::out_of_range &)
{
const std::string msg = "Incorrect input channel UID = " + std::string(channel_uid) + ".";
SPDLOG_ERROR("{}", msg);
throw std::logic_error(msg);
}
auto &network = model.get_network();
for (const auto &proj_uid : p_uids)
{
SPDLOG_TRACE(
"Input projection {} subscribing to channel {}...", std::string(proj_uid), std::string(channel_uid));
backend_->get_message_endpoint().subscribe<knp::core::messaging::SpikeMessage>(proj_uid, {channel_uid});
std::visit(
[](auto &proj)
{
SPDLOG_TRACE("Tagging input projection {}...", std::string(proj.get_uid()));
proj.get_tags()[core::tags::io_type_tag] = core::tags::IOType::input;
},
network.get_projection(proj_uid));
}
}
void ModelLoader::gen_output_channel(
knp::framework::Model &model, const core::UID &channel_uid, const std::vector<core::UID> &p_uids)
{
auto endpoint = backend_->get_message_bus().create_endpoint();
endpoint.subscribe<knp::core::messaging::SpikeMessage>(channel_uid, p_uids);
out_channels_.emplace_back(channel_uid, std::move(endpoint));
auto &network = model.get_network();
for (const auto &pop_uid : p_uids)
{
std::visit(
[](auto &pop)
{
SPDLOG_TRACE("Tagging output population {}...", std::string(pop.get_uid()));
pop.get_tags()[core::tags::io_type_tag] = core::tags::IOType::output;
},
network.get_population(pop_uid));
}
}
void ModelLoader::load(knp::framework::Model &model)
{
SPDLOG_DEBUG("Model loader initializing...");
// Create input.
SPDLOG_TRACE("Input channels initializing...");
init_channels(model, model.get_input_channels(), &ModelLoader::gen_input_channel);
// Create output.
SPDLOG_TRACE("Output channels initializing...");
init_channels(model, model.get_output_channels(), &ModelLoader::gen_output_channel);
const auto &network = model.get_network();
// Must be used after channel initialization, because the `init_channels()` function adds tags to
// the input projections and output populations.
backend_->load_all_populations(network.get_populations());
backend_->load_all_projections(network.get_projections());
}
const io::input::InputChannel &ModelLoader::get_input_channel(const core::UID &channel_uid) const
{
auto result = std::find_if(
in_channels_.cbegin(), in_channels_.cend(),
[&channel_uid](const auto &input_channel) { return input_channel.get_uid() == channel_uid; });
if (in_channels_.cend() == result) throw std::runtime_error("Wrong input channel UID.");
return *result;
}
io::input::InputChannel &ModelLoader::get_input_channel(const core::UID &channel_uid)
{
auto result = std::find_if(
in_channels_.begin(), in_channels_.end(),
[&channel_uid](const auto &input_channel) { return input_channel.get_uid() == channel_uid; });
if (in_channels_.end() == result) throw std::runtime_error("Wrong input channel UID.");
return *result;
}
io::output::OutputChannel &ModelLoader::get_output_channel(const core::UID &channel_uid)
{
auto result = std::find_if(
out_channels_.begin(), out_channels_.end(),
[&channel_uid](const auto &output_channel) { return output_channel.get_uid() == channel_uid; });
if (out_channels_.end() == result) throw std::runtime_error("Wrong output channel UID.");
return *result;
}
const io::output::OutputChannel &ModelLoader::get_output_channel(const core::UID &channel_uid) const
{
auto result = std::find_if(
out_channels_.cbegin(), out_channels_.cend(),
[&channel_uid](const auto &output_channel) { return output_channel.get_uid() == channel_uid; });
if (out_channels_.cend() == result) throw std::runtime_error("Wrong output channel UID.");
return *result;
}
} // namespace knp::framework