diff --git a/.github/workflows/cxx_unit_test.yaml b/.github/workflows/cxx_unit_test.yaml index 167f920..4b5ca61 100644 --- a/.github/workflows/cxx_unit_test.yaml +++ b/.github/workflows/cxx_unit_test.yaml @@ -6,11 +6,15 @@ on: pull_request: branches: [main] +concurrency: + cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref }} + jobs: unit-tests: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 container: - image: ubuntu:22.04 + image: ubuntu:24.04 steps: - name: Checkout source code @@ -27,12 +31,16 @@ jobs: id: install_packages run: | apt-get update - apt-get install -y cmake build-essential libboost-test-dev libfmt-dev wget unzip + apt-get install -y cmake build-essential libboost-test-dev libfmt-dev ninja-build wget unzip apt-get install -y python3-pip python3-numpy - name: Install python libraries id: install_torch - run: pip3 install 'torch>=2.2.0+cpu' --index-url https://download.pytorch.org/whl/cpu + run: > + pip3 install + 'torch>=2.2.0+cpu' + --break-system-packages + --index-url https://download.pytorch.org/whl/cpu - name: Find PyTorch cmake prefix path id: find_torch @@ -45,9 +53,10 @@ jobs: cmake -DCMAKE_PREFIX_PATH=${{steps.find_torch.outputs.cmake_prefix_path}} -B ${{github.workspace}}/build + -G Ninja - name: Build CXX library run: cmake --build ${{github.workspace}}/build - name: Run unit-tests - run: make -C ${{github.workspace}}/build test ARGS="--verbose" BOOST_TEST_LOG_LEVEL="test_suite" + run: ninja -C ${{github.workspace}}/build test diff --git a/.github/workflows/pre_commit.yaml b/.github/workflows/pre_commit.yaml index 3bbd60f..fcd8544 100644 --- a/.github/workflows/pre_commit.yaml +++ b/.github/workflows/pre_commit.yaml @@ -6,15 +6,33 @@ on: pull_request: branches: [main] +concurrency: + cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref }} + jobs: pre-commit: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - name: Install pre-commit - run: pip install pre-commit - - name: Run pre-commit - run: pre-commit run --all-files --show-diff-on-failure --color=always + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Install Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Configure apt to disable recommendations installation + id: configure_apt + run: | + echo 'APT::Install-Recommends "false";' | sudo tee /etc/apt/apt.conf + echo 'APT::Install-Suggests "false";' | sudo tee -a /etc/apt/apt.conf + + - name: Install third-party libraries and tools + id: install_packages + run: | + sudo apt-get install -y cmake build-essential libboost-test-dev libfmt-dev wget unzip + sudo apt-get install -y python3-numpy pre-commit + + - name: Run pre-commit + run: pre-commit run --all-files --show-diff-on-failure --color=always diff --git a/.github/workflows/python_build_wheel.yaml b/.github/workflows/python_build_wheel.yaml index 21cea56..98d5a30 100644 --- a/.github/workflows/python_build_wheel.yaml +++ b/.github/workflows/python_build_wheel.yaml @@ -6,6 +6,10 @@ on: - v* workflow_dispatch: +concurrency: + cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref }} + jobs: build_wheels: name: Build wheels on ${{ matrix.os }} diff --git a/.github/workflows/python_unit_test.yaml b/.github/workflows/python_unit_test.yaml index 1080942..54ec81a 100644 --- a/.github/workflows/python_unit_test.yaml +++ b/.github/workflows/python_unit_test.yaml @@ -6,6 +6,10 @@ on: pull_request: branches: [main] +concurrency: + cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref }} + jobs: unit-tests: strategy: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1aee585..a0ef9b2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,3 +30,20 @@ repos: - id: clang-format name: "Clang C++ formatting" types_or: [c++, c, cuda] +- repo: local + hooks: + - id: cppcheck + name: "C++ static analysis" + types_or: [c++, c, cuda] + entry: cppcheck -q -j8 --error-exitcode=1 + language: python + additional_dependencies: ["cppcheck==1.5.1"] + args: + - --check-level=exhaustive + - --language=c++ + - --enable=warning + - --library=boost + - -I include + - src + - test + - torch_geopooling diff --git a/include/torch_geopooling/quadtree.h b/include/torch_geopooling/quadtree.h deleted file mode 100644 index b1d4821..0000000 --- a/include/torch_geopooling/quadtree.h +++ /dev/null @@ -1,351 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -// SPDX-FileCopyrightText: 2025 Yakau Bubnou -// SPDX-FileType: SOURCE - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - -namespace torch_geopooling { - - -template -class quadtree_iterator; - - -template < - typename Coordinate, - typename T = long, - class Container = std::unordered_map, T>> -class quadtree { -public: - using key_type = std::pair; - - using mapped_type = T; - - using value_type = std::pair; - - using container_type = Container; - - using node_type = quad>; - - using node_iterator = quadtree_iterator; - - using exterior_type = quadrect; - - quadtree( - const exterior_type& exterior, - const Tile& tile = Tile::root, - std::optional options = std::nullopt - ) - : m_exterior(exterior), - m_tile(tile), - m_options(options.value_or(quadtree_options())), - m_nodes(nullptr) - {} - - quadtree( - const std::initializer_list xywh, - std::optional options = std::nullopt - ) - : quadtree(exterior_type(xywh), Tile::root, options) - {} - - Tile - tile() const - { - return m_tile; - } - - inline bool - is_terminal() const - { - return m_nodes == nullptr; - } - - const exterior_type& - exterior() const - { - return m_exterior; - } - - bool - contains(const key_type& point) const - { - return m_exterior.contains(point); - } - - node_iterator - begin() const - { - return node_iterator(*this); - } - - node_iterator - end() const - { - return node_iterator(); - } - - node_iterator - begin() - { - return node_iterator(*this); - } - - node_iterator - end() - { - return node_iterator(); - } - - std::size_t - depth() const - { - return m_tile.z(); - } - - std::size_t - total_depth() const - { - std::size_t depth = 0; - for (auto const& node : std::as_const(*this)) { - depth = std::max(depth, node.m_tile.z()); - } - return depth; - } - - void - insert(const value_type& value) - { - auto point = value.first; - if (m_options.has_precision()) { - point = round(point, m_options.precision()); - } - - assert_contains(point); - - auto& node = find(point); - node.m_data.insert(std::make_pair(point, value.second)); - node.subdivide(); - } - - void - insert(const key_type& point, const mapped_type& value) - { - insert(std::make_pair(point, value)); - } - - quadtree& - find(const key_type& point, std::optional max_depth = std::nullopt) - { - assert_contains(point); - - auto maximum_depth = max_depth.value_or(m_options.max_depth()); - if (((m_tile.z() >= maximum_depth) && maximum_depth >= 0) || m_nodes == nullptr) { - return *this; - } - - auto centroid = m_exterior.centroid(); - std::size_t x = 0, y = 0; - if (point.first > centroid.first) { - x += 1; - } - if (point.second > centroid.second) { - y += 1; - } - - auto& tree = m_nodes->at(x, y); - return tree.find(point, max_depth); - } - - quadtree& - find(const Tile& t) - { - std::size_t mid_width = 1 << t.z(); - mid_width >>= 1; - - std::size_t xmid = mid_width, ymid = mid_width; - - auto node = this; - while (!node->is_terminal() && t.z() > node->m_tile.z()) { - mid_width >>= 1; - - std::size_t x = 0, y = 0; - if (t.x() >= xmid) { - x += 1; - xmid += mid_width; - } else { - xmid -= mid_width; - } - if (t.y() >= ymid) { - y += 1; - ymid += mid_width; - } else { - ymid -= mid_width; - } - - node = &(node->m_nodes->at(x, y)); - } - - return *node; - } - - const quadtree& - find(const key_type& point, std::optional max_depth = std::nullopt) const - { - return find(point, max_depth); - } - - std::size_t - size() const - { - std::size_t num_elements = 0; - for (auto const& node : std::as_const(*this)) { - num_elements += node.m_data.size(); - } - return num_elements; - } - -private: - friend class quadtree_iterator; - - std::shared_ptr m_nodes; - container_type m_data; - exterior_type m_exterior; - - quadtree_options m_options; - Tile m_tile; - - void - assert_contains(const key_type& point) const - { - if (!contains(point)) { - throw value_error( - "quadtree: point ({}, {}) is outside of quad geometry", point.first, point.second - ); - } - } - - quadtree - make_subtree(quad& exteriors, std::size_t x, std::size_t y) const - { - return quadtree(exteriors.at(x, y), m_tile.child(x, y), m_options); - } - - void - subdivide() - { - if (m_nodes != nullptr || m_data.size() <= m_options.capacity() - || m_tile.z() >= m_options.max_depth()) { - return; - } - - // Symmetrically split the quad into the equal-area elements and move - // the data from the parent node to sub-nodes. At the end, clear the - // data from the parent. - quad exteriors = m_exterior.symmetric_split(); - - node_type nodes( - make_subtree(exteriors, 0, 0), make_subtree(exteriors, 0, 1), - make_subtree(exteriors, 1, 0), make_subtree(exteriors, 1, 1) - ); - - m_nodes = std::make_shared(std::move(nodes)); - - for (auto data : m_data) { - auto& node = find(data.first, -1); - if (node.tile() != tile()) { - node.insert(data); - } - } - - m_data.clear(); - } -}; - - -template -class quadtree_iterator { -public: - using iterator_category = std::forward_iterator_tag; - - using iterator = quadtree_iterator; - - using value_type = quadtree; - - using reference = value_type&; - - using pointer = value_type*; - - explicit quadtree_iterator(const value_type& tree) - : m_queue() - { - m_queue.push(tree); - } - - quadtree_iterator() - : m_queue() - {} - - ~quadtree_iterator() {} - - iterator& - operator++() - { - return next(); - } - - reference - operator*() - { - return m_queue.front(); - } - - pointer - operator->() - { - return m_queue.front(); - } - - bool - operator!=(const iterator& rhs) - { - return !(m_queue.empty() && rhs.m_queue.empty()); - } - -private: - std::queue m_queue; - - iterator& - next() - { - auto tree = m_queue.front(); - if (!tree.is_terminal()) { - for (auto const& node : *tree.m_nodes) { - m_queue.push(node); - } - } - - m_queue.pop(); - return *this; - } -}; - - -} // namespace torch_geopooling diff --git a/include/torch_geopooling/torch_geopooling.h b/include/torch_geopooling/torch_geopooling.h index a65dd60..c8c851c 100644 --- a/include/torch_geopooling/torch_geopooling.h +++ b/include/torch_geopooling/torch_geopooling.h @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include diff --git a/src/embedding.cc b/src/embedding.cc index 609bac1..bfbe437 100644 --- a/src/embedding.cc +++ b/src/embedding.cc @@ -1,17 +1,6 @@ -/// Copyright (C) 2024, Yakau Bubnou -/// -/// This program is free software: you can redistribute it and/or modify -/// it under the terms of the GNU General Public License as published by -/// the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2026 Yakau Bubnou +// SPDX-FileType: SOURCE #include #include diff --git a/src/embedding_op.h b/src/embedding_op.h index 437cd9a..859d7c4 100644 --- a/src/embedding_op.h +++ b/src/embedding_op.h @@ -1,18 +1,6 @@ -/// Copyright (C) 2024, Yakau Bubnou -/// -/// This program is free software: you can redistribute it and/or modify -/// it under the terms of the GNU General Public License as published by -/// the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . - +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2026 Yakau Bubnou +// SPDX-FileType: SOURCE #pragma once diff --git a/src/quadpool.cc b/src/quadpool.cc index 13c4d07..0377178 100644 --- a/src/quadpool.cc +++ b/src/quadpool.cc @@ -1,18 +1,6 @@ -/// Copyright (C) 2024, Yakau Bubnou -/// -/// This program is free software: you can redistribute it and/or modify -/// it under the terms of the GNU General Public License as published by -/// the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . - +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2026 Yakau Bubnou +// SPDX-FileType: SOURCE #include #include diff --git a/src/quadpool_op.h b/src/quadpool_op.h index db9d02f..f68f43d 100644 --- a/src/quadpool_op.h +++ b/src/quadpool_op.h @@ -1,18 +1,6 @@ -/// Copyright (C) 2024, Yakau Bubnou -/// -/// This program is free software: you can redistribute it and/or modify -/// it under the terms of the GNU General Public License as published by -/// the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . - +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2026 Yakau Bubnou +// SPDX-FileType: SOURCE #pragma once @@ -157,7 +145,7 @@ struct quadpool_op { m_training(training) {} - std::tuple + virtual std::tuple forward(tensor_reference tiles, tensor_reference values, tensor_reference input) { check_tiles(tiles); @@ -347,7 +335,7 @@ struct quadpool_stat_op : public quadpool_op { {} std::tuple - forward(tensor_reference tiles, tensor_reference values, tensor_reference input) + forward(tensor_reference tiles, tensor_reference values, tensor_reference input) override { auto result = base::forward(tiles, values, input); diff --git a/src/tile.cc b/src/tile.cc index 741b7f6..dfc7fff 100644 --- a/src/tile.cc +++ b/src/tile.cc @@ -1,17 +1,6 @@ -/// Copyright (C) 2024, Yakau Bubnou -/// -/// This program is free software: you can redistribute it and/or modify -/// it under the terms of the GNU General Public License as published by -/// the Free Software Foundation, either version 3 of the License, or -/// (at your option) any later version. -/// -/// This program is distributed in the hope that it will be useful, -/// but WITHOUT ANY WARRANTY; without even the implied warranty of -/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -/// GNU General Public License for more details. -/// -/// You should have received a copy of the GNU General Public License -/// along with this program. If not, see . +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2025 Yakau Bubnou +// SPDX-FileType: SOURCE #include #include diff --git a/test/quadtree_test.cc b/test/quadtree_test.cc deleted file mode 100644 index 63cf07c..0000000 --- a/test/quadtree_test.cc +++ /dev/null @@ -1,140 +0,0 @@ -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MODULE quadtree - -#include - -#include - - -using namespace torch_geopooling; - - -BOOST_AUTO_TEST_SUITE(TestQuadtree) - - -BOOST_AUTO_TEST_CASE(quadtree_is_terminal) -{ - quadtree tree({-180.0, -90.0, 360.0, 180.0}); - - BOOST_CHECK(tree.is_terminal()); -} - - -BOOST_AUTO_TEST_CASE(quadtree_contains) -{ - quadtree tree({0, 0, 10, 10}); - - BOOST_CHECK(tree.contains(std::pair(0, 0))); - BOOST_CHECK(tree.contains(std::pair(0, 10))); - BOOST_CHECK(tree.contains(std::pair(10, 0))); - BOOST_CHECK(tree.contains(std::pair(10, 10))); - - BOOST_CHECK(!tree.contains(std::pair(-1, -1))); - BOOST_CHECK(!tree.contains(std::pair(11, 11))); -} - - -BOOST_AUTO_TEST_CASE(quadtree_find_empty) -{ - quadtree tree({0, 0, 10, 10}); - - auto leaf = tree.find(std::pair(2, 2)); - - BOOST_CHECK(leaf.exterior().centroid() == std::pair(5, 5)); -} - - -BOOST_AUTO_TEST_CASE(quadtree_insert_and_find) -{ - quadtree tree({-10.0, -10.0, 20.0, 20.0}); - tree.insert(std::make_pair(0.0, 0.0), 0); - tree.insert(std::make_pair(1.0, 1.0), 1); - tree.insert(std::make_pair(1.5, 1.5), 2); - - auto node1 = tree.find(std::make_pair(1.5, 1.5), 3); - BOOST_CHECK_EQUAL(node1.depth(), 3); - BOOST_CHECK_EQUAL(node1.exterior(), quadrect({0.0, 0.0, 2.5, 2.5})); - - auto node2 = tree.find(std::make_pair(1.5, 1.5), 5); - BOOST_CHECK_EQUAL(node2.depth(), 4); - BOOST_CHECK_EQUAL(node2.exterior(), quadrect({1.25, 1.25, 1.25, 1.25})); -} - - -BOOST_AUTO_TEST_CASE(quadtree_insert_depth_1) -{ - quadtree tree({0, 0, 10, 10}); - - tree.insert(std::make_pair(1, 1), 0); - tree.insert(std::make_pair(1, 9), 1); - tree.insert(std::make_pair(9, 9), 2); - tree.insert(std::make_pair(9, 1), 3); - - BOOST_CHECK_EQUAL(tree.size(), 4); - BOOST_CHECK_EQUAL(tree.total_depth(), 1); -} - - -BOOST_AUTO_TEST_CASE(quadtree_insert_depth_3) -{ - quadtree tree({0.0, 0.0, 10.0, 10.0}); - - tree.insert(std::make_pair(1.0, 1.0), 0); - tree.insert(std::make_pair(1.7, 1.7), 1); - tree.insert(std::make_pair(1.0, 1.7), 2); - tree.insert(std::make_pair(1.7, 1.0), 3); - - BOOST_CHECK_EQUAL(tree.size(), 4); - BOOST_CHECK_EQUAL(tree.total_depth(), 3); - - auto node1 = tree.find(std::make_pair(1.0, 1.0)); - BOOST_CHECK_EQUAL(node1.depth(), 3); - BOOST_CHECK_EQUAL(node1.tile(), Tile(3, 0, 0)); - BOOST_CHECK_EQUAL(node1.exterior(), quadrect({0.0, 0.0, 1.25, 1.25})); - - auto node2 = tree.find(std::make_pair(1.7, 1.7)); - BOOST_CHECK_EQUAL(node2.depth(), 3); - BOOST_CHECK_EQUAL(node2.tile(), Tile(3, 1, 1)); - BOOST_CHECK_EQUAL(node2.exterior(), quadrect({1.25, 1.25, 1.25, 1.25})); - - auto node3 = tree.find(std::make_pair(1.0, 1.7)); - BOOST_CHECK_EQUAL(node3.depth(), 3); - BOOST_CHECK_EQUAL(node3.tile(), Tile(3, 0, 1)); - BOOST_CHECK_EQUAL(node3.exterior(), quadrect({0.0, 1.25, 1.25, 1.25})); - - auto node4 = tree.find(std::make_pair(1.7, 1.0)); - BOOST_CHECK_EQUAL(node4.depth(), 3); - BOOST_CHECK_EQUAL(node4.tile(), Tile(3, 1, 0)); - BOOST_CHECK_EQUAL(node4.exterior(), quadrect({1.25, 0.0, 1.25, 1.25})); -} - - -BOOST_AUTO_TEST_CASE(quadtree_find_by_tile) -{ - quadtree tree({0.0, 0.0, 10.0, 10.0}); - - tree.insert(std::make_pair(1.0, 1.0), 0); - tree.insert(std::make_pair(1.7, 1.7), 1); - tree.insert(std::make_pair(1.0, 1.7), 2); - tree.insert(std::make_pair(1.7, 1.0), 3); - tree.insert(std::make_pair(9.9, 9.9), 4); - tree.insert(std::make_pair(8.0, 8.0), 5); - - auto node1 = tree.find(Tile(0, 0, 0)); - BOOST_CHECK_EQUAL(node1.tile(), Tile(0, 0, 0)); - - auto node2 = tree.find(Tile(3, 0, 1)); - BOOST_CHECK_EQUAL(node2.tile(), Tile(3, 0, 1)); - - auto node3 = tree.find(Tile(3, 7, 7)); - BOOST_CHECK_EQUAL(node3.tile(), Tile(3, 7, 7)); - - auto node4 = tree.find(Tile(3, 6, 6)); - BOOST_CHECK_EQUAL(node4.tile(), Tile(3, 6, 6)); - - auto node5 = tree.find(Tile(3, 4, 4)); - BOOST_CHECK_EQUAL(node5.tile(), Tile(2, 2, 2)); -} - - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/testing.h b/test/testing.h index 3b2fc2c..63137e4 100644 --- a/test/testing.h +++ b/test/testing.h @@ -7,7 +7,7 @@ template std::function exception_contains_text(const std::string error_message) { - return [&](const Exception& error) -> bool { + return [=](const Exception& error) -> bool { return std::string(error.what()).find(error_message) != std::string::npos; }; }