diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 138b0bd..591db18 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -15,6 +15,7 @@ jobs: - changed-files - checks - clang-tidy + - conda-cpp-build-cpu - conda-cpp-build - conda-cpp-tests - conda-cpp-checks @@ -169,6 +170,23 @@ jobs: arch: "amd64" container_image: "rapidsai/ci-conda:26.06-latest" script: "ci/run_clang_tidy.sh" + conda-cpp-build-cpu: + needs: [checks, changed-files] + permissions: + actions: read + contents: read + id-token: write + packages: read + pull-requests: read + secrets: inherit # zizmor: ignore[secrets-inherit] + uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@release/26.06 + if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_cpp + with: + build_type: pull-request + node_type: cpu16 + arch: "amd64" + container_image: "rapidsai/ci-conda:26.06-latest" + script: "env NVFOREST_EXTRA_CMAKE_ARGS=-DNVFOREST_ENABLE_GPU=OFF ci/build_cpp.sh" conda-cpp-build: needs: checks permissions: diff --git a/conda/recipes/libnvforest/recipe.yaml b/conda/recipes/libnvforest/recipe.yaml index 1994538..3495392 100644 --- a/conda/recipes/libnvforest/recipe.yaml +++ b/conda/recipes/libnvforest/recipe.yaml @@ -40,6 +40,7 @@ cache: CMAKE_CXX_COMPILER_LAUNCHER: ${{ env.get("CMAKE_CXX_COMPILER_LAUNCHER") }} CMAKE_GENERATOR: ${{ env.get("CMAKE_GENERATOR") }} NVCC_APPEND_FLAGS: ${{ env.get("NVCC_APPEND_FLAGS", default="") }} + NVFOREST_EXTRA_CMAKE_ARGS: ${{ env.get("NVFOREST_EXTRA_CMAKE_ARGS", default="") }} PARALLEL_LEVEL: ${{ env.get("PARALLEL_LEVEL", default="8") }} RAPIDS_ARTIFACTS_DIR: ${{ env.get("RAPIDS_ARTIFACTS_DIR", default="") }} SCCACHE_BUCKET: ${{ env.get("SCCACHE_BUCKET", default="") }} diff --git a/cpp/include/nvforest/exceptions.hpp b/cpp/include/nvforest/exceptions.hpp index d9e3cb7..3074934 100644 --- a/cpp/include/nvforest/exceptions.hpp +++ b/cpp/include/nvforest/exceptions.hpp @@ -22,11 +22,11 @@ struct unusable_model_exception : std::exception { /** Exception indicating model import failed */ struct model_import_error : std::exception { model_import_error() : model_import_error("Error while importing model") {} - model_import_error(char const* msg) : msg_{msg} {} - virtual char const* what() const noexcept { return msg_; } + model_import_error(std::string msg) : msg_{msg} {} + virtual char const* what() const noexcept { return msg_.c_str(); } private: - char const* msg_; + std::string msg_; }; /** diff --git a/cpp/include/nvforest/forest_model.hpp b/cpp/include/nvforest/forest_model.hpp index 783b517..9c07f13 100644 --- a/cpp/include/nvforest/forest_model.hpp +++ b/cpp/include/nvforest/forest_model.hpp @@ -11,6 +11,10 @@ #include #include +#ifdef NVFOREST_ENABLE_GPU +#include +#endif + #include #include #include @@ -289,7 +293,11 @@ struct forest_model { int current_device_id; if (out_mem_type == raft_proto::device_type::gpu || in_mem_type == raft_proto::device_type::gpu) { +#ifdef NVFOREST_ENABLE_GPU raft_proto::cuda_check(cudaGetDevice(¤t_device_id)); +#else + throw raft_proto::gpu_unsupported("Tried to use GPU memory in CPU-only build"); +#endif } else { current_device_id = -1; } diff --git a/cpp/include/nvforest/treelite_importer.hpp b/cpp/include/nvforest/treelite_importer.hpp index 4e1b380..ccbcc13 100644 --- a/cpp/include/nvforest/treelite_importer.hpp +++ b/cpp/include/nvforest/treelite_importer.hpp @@ -21,6 +21,7 @@ #include #include +#include #include namespace nvforest { @@ -351,24 +352,34 @@ struct treelite_importer { *processed_tl_model.get(), align_bytes, use_double_precision, dev_type, device, stream); } - ASSERT(tl_model.num_target == 1, "nvForest does not support multi-target model"); + if (tl_model.num_target != 1) { + throw model_import_error("nvForest does not support multi-target model"); + } // Check tree annotation (assignment) if (tl_model.task_type == treelite::TaskType::kMultiClf) { + if (tl_model.num_class.Empty() || tl_model.num_class[0] <= 0) { + throw model_import_error("MultiClf model must define positive num_class"); + } // Must be either vector leaf or grove-per-class if (tl_model.leaf_vector_shape[1] > 1) { // vector-leaf - ASSERT(tl_model.leaf_vector_shape[1] == int(tl_model.num_class[0]), - "Vector leaf must be equal to num_class = %d", - tl_model.num_class[0]); + if (tl_model.leaf_vector_shape[1] != int(tl_model.num_class[0])) { + throw model_import_error("Vector leaf must be equal to num_class = " + + std::to_string(tl_model.num_class[0])); + } auto tree_count = num_trees(tl_model); for (decltype(tree_count) tree_id = 0; tree_id < tree_count; ++tree_id) { - ASSERT(tl_model.class_id[tree_id] == -1, "Tree %d has invalid class assignment", tree_id); + if (tl_model.class_id[tree_id] != -1) { + throw model_import_error("Tree " + std::to_string(tree_id) + + " has invalid class assignment"); + } } } else { // grove-per-class auto tree_count = num_trees(tl_model); for (decltype(tree_count) tree_id = 0; tree_id < tree_count; ++tree_id) { - ASSERT(tl_model.class_id[tree_id] == int(tree_id % tl_model.num_class[0]), - "Tree %d has invalid class assignment", - tree_id); + if (tl_model.class_id[tree_id] != int(tree_id % tl_model.num_class[0])) { + throw model_import_error("Tree " + std::to_string(tree_id) + + " has invalid class assignment"); + } } } } diff --git a/cpp/tests/treelite_importer.cpp b/cpp/tests/treelite_importer.cpp index 8f946e4..f29baad 100644 --- a/cpp/tests/treelite_importer.cpp +++ b/cpp/tests/treelite_importer.cpp @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -337,7 +338,12 @@ TEST(TreeliteImporter, DegenerateTree) auto fil_model = import_from_treelite_model(*tl_model, tree_layout::breadth_first); ASSERT_FALSE(fil_model.has_vector_leaves()); - auto handle = raft::handle_t{}; +#ifdef NVFOREST_ENABLE_GPU + auto raft_handle = raft::handle_t{}; + auto handle = raft_proto::handle_t{raft_handle}; +#else + auto handle = raft_proto::handle_t{}; +#endif auto X = std::vector{0.0}; auto preds = std::vector(1, 0.0); auto expected_preds = std::vector{1.0}; @@ -358,7 +364,12 @@ TEST(TreeliteImporter, DegenerateTreeWithVectorLeaf) auto fil_model = import_from_treelite_model(*tl_model, tree_layout::breadth_first); ASSERT_TRUE(fil_model.has_vector_leaves()); - auto handle = raft::handle_t{}; +#ifdef NVFOREST_ENABLE_GPU + auto raft_handle = raft::handle_t{}; + auto handle = raft_proto::handle_t{raft_handle}; +#else + auto handle = raft_proto::handle_t{}; +#endif auto X = std::vector{0.0}; auto preds = std::vector(2, 0.0); auto expected_preds = std::vector{0.5, 0.5};