our_knp/examples/knp/backends/gpu/cuda-backend/impl/projection.cuh
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

180 lines
6.2 KiB
Text

/**
* @file projection.cuh
* @brief GPU projection implementation.
* @kaspersky_support Artiom N.
* @date 24.02.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 <tuple>
#include <utility>
#include <knp/core/projection.h>
#include <knp/synapse-traits/all_traits.h>
#include "cuda_lib/vector.cuh"
#include "cuda_lib/value_index.cuh"
#include "cuda_bus/synaptic_impact_message.cuh"
#include "uid.cuh"
/**
* @brief Namespace for CUDA backend.
*/
namespace knp::backends::gpu::cuda
{
/**
* @brief The CUDAProjection class is a definition of a CUDA synapses.
*/
template <typename SynapseType>
struct CUDAProjection
{
/**
* @brief Type of the projection synapses.
*/
using ProjectionSynapseType = SynapseType;
/**
* @brief Projection of synapses with the specified synapse type.
*/
using ProjectionType = CUDAProjection<SynapseType>;
/**
* @brief Parameters of the specified synapse type.
*/
using SynapseParameters = typename synapse_traits::synapse_parameters<SynapseType>;
/**
* @brief Synapse description structure that contains synapse parameters and indexes of the associated neurons.
* @note make sure this is the same as in core projection.
*/
using Synapse = ::cuda::std::tuple<SynapseParameters, device_lib::LongIndex, device_lib::LongIndex>;
__host__ __device__ CUDAProjection()
#if !defined(__CUDA_ARCH__)
: is_locked_(true)
#endif
{}
/**
* @brief Constructor.
* @param projection source projection.
*/
__host__ explicit CUDAProjection(const knp::core::Projection<SynapseType> &projection)
: uid_(to_gpu_uid(projection.get_uid())),
presynaptic_uid_(to_gpu_uid(projection.get_presynaptic())),
postsynaptic_uid_(to_gpu_uid(projection.get_postsynaptic())),
is_locked_(projection.is_locked())
{
constexpr int data_index = core::SynapseElementAccess::synapse_data;
constexpr int source_id_index = core::SynapseElementAccess::source_neuron_id;
constexpr int target_id_index = core::SynapseElementAccess::target_neuron_id;
for (auto &synapse : projection)
{
Synapse out_synapse{std::get<data_index>(synapse), std::get<source_id_index>(synapse),
std::get<target_id_index>(synapse)};
synapses_.push_back(out_synapse);
SPDLOG_TRACE("Synapse: weight {} delay {}", ::cuda::std::get<data_index>(out_synapse).weight_,
::cuda::std::get<data_index>(out_synapse).delay_);
}
index_ = device_lib::build_index<SynapseType>(projection);
}
__host__ __device__ void lock_weights() { is_locked_ = true; }
__host__ __device__ void unlock_weights() { is_locked_ = false; }
__host__ __device__ void actualize()
{
synapses_.actualize();
impact_indexes_.actualize();
sending_steps_.actualize();
index_.actualize();
}
/**
* @brief Add new impacts to the impact queue.
* @param new_impacts_indexes indexes of the synapses that will emit impacts.
* @param new_sending_steps the target step when the impact will be emitted.
* @note pre-sort new_impacts_indexes by new_sending_steps.
*/
__host__ void add_impacts(const device_lib::CUDAVector<device_lib::LongIndex> &new_impacts_indexes,
const device_lib::CUDAVector<device_lib::LongIndex> &new_sending_steps)
{
assert(new_impacts_indexes.size() == new_sending_steps.size());
if (new_impacts_indexes.size() == 0) return;
const auto size = new_impacts_indexes.size();
const auto out_size = new_impacts_indexes.size() + impact_indexes_.size();
device_lib::LongIndex *res_steps;
device_lib::LongIndex *res_impacts;
cudaMalloc(&res_steps, sizeof(device_lib::LongIndex) * out_size);
cudaMalloc(&res_impacts, sizeof(device_lib::LongIndex) * out_size);
thrust::merge_by_key(thrust::device, new_sending_steps.data(), new_sending_steps.data() + size,
sending_steps_.data(), sending_steps_.data() + sending_steps_.size(),
new_impacts_indexes.data(), impact_indexes_.data(), res_steps, res_impacts);
impact_indexes_ = device_lib::CUDAVector<device_lib::LongIndex>{res_impacts, out_size};
sending_steps_ = device_lib::CUDAVector<device_lib::LongIndex>{res_steps, out_size};
}
/**
* @brief Create an impact message.
* @param current_step current step.
* @return Synaptic impact message that would be sent.
*/
__host__ void form_message(device_lib::LongIndex current_step);
/**
* @brief UID.
*/
cuda::UID uid_;
/**
* @brief UID of the population that sends spikes to the projection (presynaptic population)
*/
cuda::UID presynaptic_uid_;
/**
* @brief UID of the population that receives synapse responses from this projection (postsynaptic population).
*/
cuda::UID postsynaptic_uid_;
/**
* @brief Return `false` if the weight change for synapses is not locked.
*/
bool is_locked_;
/**
* @brief Synapse index for quick neuron to synapse search.
*/
device_lib::ValueIndex index_;
/**
* @brief Container of synapse parameters.
*/
cuda::device_lib::CUDAVector<Synapse> synapses_;
/**
* @brief Incoming impacts for the projection.
*/
device_lib::CUDAVector<device_lib::LongIndex> impact_indexes_;
device_lib::CUDAVector<device_lib::LongIndex> sending_steps_;
/**
* @brief Message buffer.
*/
SynapticImpactMessage message_buf_;
};
} // namespace knp::backends::gpu::cuda