Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 22 additions & 14 deletions .github/workflows/cmake-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ on:
jobs:
linux_build:
runs-on: ${{ matrix.os }}
env:
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}

strategy:
# Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable.
Expand Down Expand Up @@ -63,10 +65,17 @@ jobs:
shell: bash
run: |
echo "build-output-dir=build_${{ matrix.c_compiler }}" >> "$GITHUB_OUTPUT"
echo "KNP_IMAGE_VERSION=v2.0.${{ github.run_number }}" >> "$GITHUB_OUTPUT"
- name: Set environment
shell: bash
run: |
echo "KNP_IMAGE_VERSION=v2.0.${{ github.run_number }}" >> $GITHUB_ENV
echo "KNP_BUILD_IMAGE_NAME=${{ vars.DOCKERHUB_LOGIN }}/knp-build-image:latest" >> $GITHUB_ENV
dockerhub_login="${{ vars.DOCKERHUB_LOGIN }}"
if [ -z "$dockerhub_login" ]; then
dockerhub_login="kasperskydh"
fi
echo "DOCKERHUB_LOGIN=${dockerhub_login}" >> "$GITHUB_ENV"
echo "KNP_BUILD_IMAGE_NAME=${dockerhub_login}/knp-build-image:latest" >> "$GITHUB_ENV"
echo "KNP_SDK_IMAGE_NAME=${dockerhub_login}/knp-sdk-image" >> "$GITHUB_ENV"

# Install support for non-x86 emulation in Docker via QEMU.
# Platforms: linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x,
Expand All @@ -81,6 +90,7 @@ jobs:
run: |
echo "KNP_IMAGE_VERSION = ${{ steps.strings.outputs.KNP_IMAGE_VERSION }}"
echo "KNP_BUILD_IMAGE_NAME = ${{ env.KNP_BUILD_IMAGE_NAME }}"
echo "KNP_SDK_IMAGE_NAME = ${{ env.KNP_SDK_IMAGE_NAME }}"
- name: Configure
run: >
docker run --platform=linux/${{ matrix.arch }} --rm -v ${{ github.workspace }}:/KNP -w /KNP kasperskydh/knp-build-image cmake -B ${{ steps.strings.outputs.build-output-dir }}
Expand Down Expand Up @@ -118,32 +128,30 @@ jobs:

- name: Copy SDK image files to build directory
if: matrix.c_compiler == 'gcc'
run:
run: |
sudo cp -a ${{ github.workspace }}/docker/sdk-image/** ${{ steps.strings.outputs.build-output-dir }}/_packages/

- name: Login to Docker Hub
if: matrix.c_compiler == 'gcc' && env.DOCKERHUB_TOKEN != ''
uses: docker/login-action@v3
with:
username: ${{ vars.DOCKERHUB_LOGIN }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
username: ${{ env.DOCKERHUB_LOGIN }}
password: ${{ env.DOCKERHUB_TOKEN }}

- name: Create SDK Docker image
if: matrix.c_compiler == 'gcc'
uses: docker/build-push-action@v7
if: matrix.c_compiler == 'gcc' && env.DOCKERHUB_TOKEN != ''
uses: docker/build-push-action@v6
timeout-minutes: 180
continue-on-error: false
with:
platforms: linux/${{ matrix.arch }}
context: ${{ steps.strings.outputs.build-output-dir }}/_packages/
load: true
push: true
build-args: |
KNP_IMAGE_VERSION=latest
KNP_VERSION=2.0.0

tags: |
${{ vars.DOCKERHUB_LOGIN }}/knp-sdk-image:${{ env.KNP_IMAGE_VERSION }}
${{ vars.DOCKERHUB_LOGIN }}/knp-sdk-image:latest
timeout-minutes: 180
continue-on-error: false
${{ env.KNP_SDK_IMAGE_NAME }}:${{ steps.strings.outputs.KNP_IMAGE_VERSION }}
${{ env.KNP_SDK_IMAGE_NAME }}:latest

- name: Upload deb packages
if: matrix.c_compiler == 'gcc'
Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/docker-build-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ jobs:
${{ vars.DOCKERHUB_LOGIN }}/knp-base-image:${{ env.KNP_IMAGE_VERSION }}
${{ vars.DOCKERHUB_LOGIN }}/knp-base-image:latest
build-args: KNP_IMAGE_VERSION=${{ env.KNP_IMAGE_VERSION }}
timeout-minutes: 180
continue-on-error: false
- name: Build and push the Docker image for Kaspersky Neuromorphic Platform build
uses: docker/build-push-action@v7
with:
Expand All @@ -65,5 +63,3 @@ jobs:
tags: |
${{ vars.DOCKERHUB_LOGIN }}/knp-build-image:${{ env.KNP_IMAGE_VERSION }}
${{ vars.DOCKERHUB_LOGIN }}/knp-build-image:latest
timeout-minutes: 180
continue-on-error: false
9 changes: 1 addition & 8 deletions docker/sdk-image/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
# limitations under the License.

ARG KNP_IMAGE_VERSION=2.0.0
ARG KNP_VERSION=2.0.0
ARG TARGETPLATFORM

FROM kasperskydh/knp-base-image:${KNP_IMAGE_VERSION}
Expand All @@ -40,10 +39,4 @@ RUN \
COPY NOTICE.txt .
COPY *.deb /knp/

RUN DEBIAN_FRONTEND=noninteractive dpkg -i \
/knp/knp-cpp-framework_2.0.0_amd64.deb /knp/knp-cpp-framework-dev_2.0.0_amd64.deb \
/knp/knp-documentation_2.0.0_all.deb knp-examples_2.0.0_amd64.deb \
/knp/knp-cpu-single-threaded-backend_2.0.0_amd64.deb /knp/knp-cpu-single-threaded-backend-dev_2.0.0_amd64.deb \
/knp/knp-cpu-multi-threaded-backend_2.0.0_amd64.deb /knp/knp-cpu-multi-threaded-backend-dev_2.0.0_amd64.deb \
/knp/knp-examples_2.0.0_amd64.deb \
/knp/knp-python3-framework_2.0.0_amd64.deb
RUN DEBIAN_FRONTEND=noninteractive dpkg -i /knp/*.deb
4 changes: 2 additions & 2 deletions examples/mnist-client/inference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ auto make_observer_function(std::vector<InferenceResult> &result)


// Read image dataset from a binary file and trasnform it into a vector of boolean frames.
std::vector<std::vector<bool>> read_spike_frames(const std::string &path_to_data)
std::vector<std::vector<bool>> read_spike_frames(const std::filesystem::path &path_to_data)
{
// Image-to-spikes conversion parameters.
constexpr int intensity_levels = 10;
Expand Down Expand Up @@ -127,7 +127,7 @@ std::vector<InferenceResult> do_inference(

// The largest projection is the input image projection. These are numbers for a specific MNIST model.
constexpr size_t img_input_size = 117600;
auto is_input = [](const knp::core::AllProjectionsVariant &proj)
auto is_input = [img_input_size](const knp::core::AllProjectionsVariant &proj)
{ return std::visit([](const auto &p) { return p.size(); }, proj) == img_input_size; };

std::vector<knp::core::UID> input_image_projection_uids = find_projections(model.get_network(), is_input);
Expand Down
19 changes: 16 additions & 3 deletions knp/base-framework/impl/backend_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,32 @@
#include <spdlog/spdlog.h>

#include <functional>
#include <unordered_map>

#include <boost/dll/import.hpp>


namespace knp::framework
{
namespace
{
auto &get_backend_creators()
{
// Keep imported backend libraries loaded for the process lifetime.
// Python and C++ backend objects can outlive a particular BackendLoader instance.
static auto *creators =
new std::unordered_map<std::string, std::function<BackendLoader::BackendCreateFunction>>();
return *creators;
}
} // namespace

std::function<BackendLoader::BackendCreateFunction> BackendLoader::make_creator(
const std::filesystem::path &backend_path)
{
auto creator_iter = creators_.find(backend_path.string());
auto &creators = get_backend_creators();
auto creator_iter = creators.find(backend_path.string());

if (creator_iter != creators_.end())
if (creator_iter != creators.end())
{
return creator_iter->second;
}
Expand All @@ -48,7 +61,7 @@ std::function<BackendLoader::BackendCreateFunction> BackendLoader::make_creator(

SPDLOG_DEBUG("Created backend creator.");

creators_[backend_path.string()] = creator;
creators[backend_path.string()] = creator;

return creator;
}
Expand Down
5 changes: 1 addition & 4 deletions knp/base-framework/include/knp/framework/backend_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
#include <knp/core/impexp.h>

#include <filesystem>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>


/**
Expand Down Expand Up @@ -77,9 +77,6 @@ class KNP_DECLSPEC BackendLoader
*/
std::function<BackendCreateFunction> make_creator(const std::filesystem::path &backend_path);

private:
// std::filesystem::path doesn't work on any compilers.
std::unordered_map<std::string, std::function<BackendCreateFunction>> creators_;
};

} // namespace knp::framework
22 changes: 19 additions & 3 deletions knp/python-framework/impl/core/cpp/uid_utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#pragma once

#include <algorithm>
#include <vector>

#include "common.h"
Expand All @@ -46,13 +47,28 @@ struct uid_from_python

static void construct(PyObject* obj, py::converter::rvalue_from_python_stage1_data* data)
{
py::object bytes_object = py::object(py::handle<>(py::borrowed(obj))).attr("bytes");
char* bytes = nullptr;
Py_ssize_t bytes_size = 0;
if (PyBytes_AsStringAndSize(bytes_object.ptr(), &bytes, &bytes_size) == -1)
{
py::throw_error_already_set();
}

constexpr auto expected_size = boost::uuids::uuid::static_size();
if (bytes_size != static_cast<Py_ssize_t>(expected_size))
{
const auto error_message = "UUID byte array must contain exactly " + std::to_string(expected_size) +
" bytes, got " + std::to_string(bytes_size) + ".";
PyErr_SetString(PyExc_ValueError, error_message.c_str());
py::throw_error_already_set();
}

auto storage =
reinterpret_cast<py::converter::rvalue_from_python_storage<boost::uuids::uuid>*>(data)->storage.bytes;
std::vector<uint8_t> ba =
py::extract<std::vector<uint8_t>>(py::object(py::handle<>(py::borrowed(obj))).attr("bytes"));
boost::uuids::uuid* res = new (storage) boost::uuids::uuid;

memcpy(res->data, &ba.front(), ba.size());
std::copy_n(reinterpret_cast<const uint8_t*>(bytes), expected_size, res->begin());
data->convertible = storage;
}
};
Expand Down
44 changes: 44 additions & 0 deletions knp/python-framework/tests/unit/core/test_uid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""
@file test_uid.py
@brief UID tests.

@kaspersky_support OpenAI Codex.
@license Apache 2.0 License.
@copyright © 2026 AO Kaspersky Lab
@date 08.04.2026.

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.
"""

import uuid

import pytest

from knp.core import UID


class BrokenUUID(uuid.UUID):
@property
def bytes(self) -> bytes:
return b"\x00" * 15


def test_uid_rejects_uuid_with_invalid_bytes_size() -> None:
with pytest.raises(ValueError, match="exactly 16 bytes"):
UID(BrokenUUID(bytes=b"\x00" * 16))


def test_uid_accepts_uuid() -> None:
uuid_value = uuid.uuid4()

assert str(UID(uuid_value)) == str(uuid_value)
Loading