diff --git a/CMakeLists.txt b/CMakeLists.txt index c18c9ba..294f0bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,7 @@ add_subdirectory(src/BayesNet) add_subdirectory(src/Platform) add_subdirectory(sample) -file(GLOB BayesNet_HEADERS CONFIGURE_DEPENDS ${BayesNet_SOURCE_DIR}/src/BayesNet/*.h ${BayesNet_SOURCE_DIR}/BayesNet/*.hpp) +file(GLOB BayesNet_HEADERS CONFIGURE_DEPENDS ${BayesNet_SOURCE_DIR}/src/BayesNet/*.h ${BayesNet_SOURCE_DIR}/BayesNet/*.h) file(GLOB BayesNet_SOURCES CONFIGURE_DEPENDS ${BayesNet_SOURCE_DIR}/src/BayesNet/*.cc ${BayesNet_SOURCE_DIR}/src/BayesNet/*.cpp) file(GLOB Platform_SOURCES CONFIGURE_DEPENDS ${BayesNet_SOURCE_DIR}/src/Platform/*.cc ${BayesNet_SOURCE_DIR}/src/Platform/*.cpp) diff --git a/Makefile b/Makefile index 4e7b54f..9606adb 100644 --- a/Makefile +++ b/Makefile @@ -14,18 +14,21 @@ setup: ## Install dependencies for tests and coverage dest ?= ${HOME}/bin install: ## Copy binary files to bin folder @echo "Destination folder: $(dest)" - make build + make buildr @echo ">>> Copying files to $(dest)" - @cp build/src/Platform/b_main $(dest) - @cp build/src/Platform/b_list $(dest) - @cp build/src/Platform/b_manage $(dest) - @cp build/src/Platform/b_best $(dest) + @cp build_release/src/Platform/b_main $(dest) + @cp build_release/src/Platform/b_list $(dest) + @cp build_release/src/Platform/b_manage $(dest) + @cp build_release/src/Platform/b_best $(dest) dependency: ## Create a dependency graph diagram of the project (build/dependency.png) cd build && cmake .. --graphviz=dependency.dot && dot -Tpng dependency.dot -o dependency.png -build: ## Build the main and BayesNetSample - cmake --build build -t b_main -t BayesNetSample -t b_manage -t b_list -t b_best -j 32 +buildd: ## Build the debug targets + cmake --build build_debug -t b_main -t BayesNetSample -t b_manage -t b_list -t b_best -j 32 + +buildr: ## Build the release targets + cmake --build build_release -t b_main -t BayesNetSample -t b_manage -t b_list -t b_best -j 32 clean: ## Clean the debug info @echo ">>> Cleaning Debug BayesNet..."; @@ -37,36 +40,54 @@ clang-uml: ## Create uml class and sequence diagrams debug: ## Build a debug version of the project @echo ">>> Building Debug BayesNet..."; - @if [ -d ./build ]; then rm -rf ./build; fi - @mkdir build; - @cmake -S . -B build -D CMAKE_BUILD_TYPE=Debug -D ENABLE_TESTING=ON -D CODE_COVERAGE=ON; + @if [ -d ./build_debug ]; then rm -rf ./build_debug; fi + @mkdir build_debug; + @cmake -S . -B build_Debug -D CMAKE_BUILD_TYPE=Debug -D ENABLE_TESTING=ON -D CODE_COVERAGE=ON; @echo ">>> Done"; release: ## Build a Release version of the project @echo ">>> Building Release BayesNet..."; - @if [ -d ./build ]; then rm -rf ./build; fi - @mkdir build; - @cmake -S . -B build -D CMAKE_BUILD_TYPE=Release; + @if [ -d ./build_release ]; then rm -rf ./build_release; fi + @mkdir build_release; + @cmake -S . -B build_release -D CMAKE_BUILD_TYPE=Release; @echo ">>> Done"; opt = "" test: ## Run tests (opt="-s") to verbose output the tests, (opt="-c='Test Maximum Spanning Tree'") to run only that section - @echo ">>> Running tests..."; + @echo ">>> Running BayesNet & Platform tests..."; $(MAKE) clean - @cmake --build build --target unit_tests ; - @if [ -f build/tests/unit_tests ]; then cd build/tests ; ./unit_tests $(opt) ; fi ; - @echo ">>> Done"; + @cmake --build build_debug --target unit_tests_bayesnet --target unit_tests_platform ; + @if [ -f build_debug/tests/unit_tests_bayesnet ]; then cd build_debug/tests ; ./unit_tests_bayesnet $(opt) ; fi ; + @if [ -f build_debug/tests/unit_tests_platform ]; then cd build_debug/tests ; ./unit_tests_platform $(opt) ; fi ; + @echo ">>> Done"; + +opt = "" +testp: ## Run platform tests (opt="-s") to verbose output the tests, (opt="-c='Stratified Fold Test'") to run only that section + @echo ">>> Running Platform tests..."; + $(MAKE) clean + @cmake --build build_debug --target unit_tests_platform ; + @if [ -f build_debug/tests/unit_tests_platform ]; then cd build_debug/tests ; ./unit_tests_platform $(opt) ; fi ; + @echo ">>> Done"; + +opt = "" +testb: ## Run BayesNet tests (opt="-s") to verbose output the tests, (opt="-c='Test Maximum Spanning Tree'") to run only that section + @echo ">>> Running BayesNet tests..."; + $(MAKE) clean + @cmake --build build_debug --target unit_tests_bayesnet ; + @if [ -f build_debug/tests/unit_tests_bayesnet ]; then cd build_debug/tests ; ./unit_tests_bayesnet $(opt) ; fi ; + @echo ">>> Done"; coverage: ## Run tests and generate coverage report (build/index.html) @echo ">>> Building tests with coverage..."; $(MAKE) test - @cd build ; \ + @cd build_debug ; \ gcovr --config ../gcovr.cfg @echo ">>> Done"; define ClearTests = $(eval nfiles=$(find . -name "*.gcda" -print)) - @if [ -f build/tests/unit_tests ]; then rm -f build/tests/unit_tests ; fi ; + @if [ -f build_debug/tests/unit_tests_bayesnet ]; then rm -f build_debug/tests/unit_tests_bayesnet ; fi ; + @if [ -f build_debug/tests/unit_tests_platform ]; then rm -f build_debug/tests/unit_tests_platform ; fi ; @if test "${nfiles}" != "" ; then \ find . -name "*.gcda" -print0 | xargs -0 rm 2>/dev/null ;\ fi ; diff --git a/src/Platform/Folding.cc b/src/Platform/Folding.cc index d31f773..115e8cb 100644 --- a/src/Platform/Folding.cc +++ b/src/Platform/Folding.cc @@ -74,10 +74,11 @@ namespace platform { auto chosen = vector(k, false); while (remainder_samples_to_take > 0) { int fold = (rand() % static_cast(k)); + cout << "-candidate: " << fold << endl; if (chosen.at(fold)) { continue; } - chosen[k] = true; + chosen[fold] = true; cout << "One goes to fold " << fold << " that had " << stratified_indices[fold].size() << " elements before" << endl; auto it = next(class_indices[label].begin(), 1); stratified_indices[fold].push_back(*class_indices[label].begin()); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bb273e5..5a7e3ab 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,12 +1,17 @@ if(ENABLE_TESTING) - set(TEST_MAIN "unit_tests") + set(TEST_BAYESNET "unit_tests_bayesnet") + set(TEST_PLATFORM "unit_tests_platform") include_directories(${BayesNet_SOURCE_DIR}/src/BayesNet) include_directories(${BayesNet_SOURCE_DIR}/src/Platform) include_directories(${BayesNet_SOURCE_DIR}/lib/Files) include_directories(${BayesNet_SOURCE_DIR}/lib/mdlp) include_directories(${BayesNet_SOURCE_DIR}/lib/json/include) - set(TEST_SOURCES TestBayesModels.cc TestBayesNetwork.cc TestBayesMetrics.cc TestFolding.cc TestUtils.cc ${BayesNet_SOURCE_DIR}/src/Platform/Folding.cc ${BayesNet_SOURCES}) - add_executable(${TEST_MAIN} ${TEST_SOURCES}) - target_link_libraries(${TEST_MAIN} PUBLIC "${TORCH_LIBRARIES}" ArffFiles mdlp Catch2::Catch2WithMain) - add_test(NAME ${TEST_MAIN} COMMAND ${TEST_MAIN}) + set(TEST_SOURCES_BAYESNET TestBayesModels.cc TestBayesNetwork.cc TestBayesMetrics.cc TestUtils.cc ${BayesNet_SOURCE_DIR}/src/Platform/Folding.cc ${BayesNet_SOURCES}) + set(TEST_SOURCES_PLATFORM TestFolding.cc TestUtils.cc ${Platform_SOURCES}) + add_executable(${TEST_BAYESNET} ${TEST_SOURCES_BAYESNET}) + add_executable(${TEST_PLATFORM} ${TEST_SOURCES_PLATFORM}) + target_link_libraries(${TEST_BAYESNET} PUBLIC "${TORCH_LIBRARIES}" ArffFiles mdlp Catch2::Catch2WithMain) + target_link_libraries(${TEST_PLATFORM} PUBLIC "${TORCH_LIBRARIES}" ArffFiles mdlp Catch2::Catch2WithMain) + add_test(NAME ${TEST_BAYESNET} COMMAND ${TEST_BAYESNET}) + add_test(NAME ${TEST_PLATFORM} COMMAND ${TEST_PLATFORM}) endif(ENABLE_TESTING) diff --git a/tests/TestFolding.cc b/tests/TestFolding.cc index 783258d..259a0e7 100644 --- a/tests/TestFolding.cc +++ b/tests/TestFolding.cc @@ -4,14 +4,14 @@ #include "TestUtils.h" #include "Folding.h" -TEST_CASE("KFold Test", "[KFold]") +TEST_CASE("KFold Test", "[Platform][KFold]") { // Initialize a KFold object with k=5 and a seed of 19. string file_name = GENERATE("glass", "iris", "ecoli", "diabetes"); auto raw = RawDatasets(file_name, true); int nFolds = 5; - platform::KFold kfold(nFolds, raw.nSamples, 19);s - int number = raw.nSamples * (kfold.getNumberOfFolds() - 1) / kfold.getNumberOfFolds(); + platform::KFold kfold(nFolds, raw.nSamples, 19); + int number = raw.nSamples * (kfold.getNumberOfFolds() - 1) / kfold.getNumberOfFolds(); SECTION("Number of Folds") { @@ -38,61 +38,57 @@ map counts(vector y, vector indices) return result; } -TEST_CASE("StratifiedKFold Test", "[StratifiedKFold]") +TEST_CASE("StratifiedKFold Test", "[Platform][StratifiedKFold]") { - int nFolds = 3; // Initialize a StratifiedKFold object with k=3, using the y vector, and a seed of 17. string file_name = GENERATE("glass", "iris", "ecoli", "diabetes"); + int nFolds = GENERATE(3, 5, 10); auto raw = RawDatasets(file_name, true); platform::StratifiedKFold stratified_kfoldt(nFolds, raw.yt, 17); platform::StratifiedKFold stratified_kfoldv(nFolds, raw.yv, 17); - int number = raw.nSamples * (stratified_kfold.getNumberOfFolds() - 1) / stratified_kfold.getNumberOfFolds(); + int number = raw.nSamples * (stratified_kfoldt.getNumberOfFolds() - 1) / stratified_kfoldt.getNumberOfFolds(); - // SECTION("Number of Folds") - // { - // REQUIRE(stratified_kfold.getNumberOfFolds() == nFolds); - // } - SECTION("Fold Test") + SECTION("Stratified Number of Folds") + { + REQUIRE(stratified_kfoldt.getNumberOfFolds() == nFolds); + } + SECTION("Stratified Fold Test") { // Test each fold's size and contents. - auto counts = vector(raw.classNumStates, 0); + auto counts = map>(); + // Initialize the counts per Fold for (int i = 0; i < nFolds; ++i) { - auto [train_indicest, test_indicest] = stratified_kfoldt.getFold(i); - auto [train_indicesv, test_indicesv] = stratified_kfoldv.getFold(i); + counts[i] = vector(raw.classNumStates, 0); + } + // Check fold and compute counts of each fold + for (int fold = 0; fold < nFolds; ++fold) { + auto [train_indicest, test_indicest] = stratified_kfoldt.getFold(fold); + auto [train_indicesv, test_indicesv] = stratified_kfoldv.getFold(fold); REQUIRE(train_indicest == train_indicesv); REQUIRE(test_indicest == test_indicesv); - - bool result = train_indices.size() == number || train_indices.size() == number + 1; + bool result = train_indicest.size() == number || train_indicest.size() == number + 1; REQUIRE(result); - REQUIRE(train_indices.size() + test_indices.size() == raw.nSamples); - auto train_t = torch::tensor(train_indices); + REQUIRE(train_indicest.size() + test_indicest.size() == raw.nSamples); + auto train_t = torch::tensor(train_indicest); auto ytrain = raw.yt.index({ train_t }); cout << "dataset=" << file_name << endl; cout << "nSamples=" << raw.nSamples << endl;; cout << "number=" << number << endl; - cout << "train_indices.size()=" << train_indices.size() << endl; - cout << "test_indices.size()=" << test_indices.size() << endl; + cout << "train_indices.size()=" << train_indicest.size() << endl; + cout << "test_indices.size()=" << test_indicest.size() << endl; cout << "Class Name = " << raw.classNamet << endl; - cout << "Features = "; - for (const auto& item : raw.featurest) { - cout << item << ", "; - } - cout << endl; - cout << "Class States: "; - for (const auto& item : raw.statest.at(raw.classNamet)) { - cout << item << ", "; - } - cout << endl; // Check that the class labels have been equally assign to each fold - for (const auto& idx : train_indices) { - counts[ytrain[idx].item()]++; + for (const auto& idx : train_indicest) { + counts[fold][ytrain[idx].item()]++; + } + } + // Test the fold counting of every class + for (int fold = 0; fold < nFolds; ++fold) { + for (int j = 1; j < nFolds - 1; ++j) { + for (int k = 0; k < raw.classNumStates; ++k) { + REQUIRE(abs(counts.at(fold).at(k) - counts.at(fold).at(j)) <= 1); + } } - int j = 0; - for (const auto& item : counts) { - cout << "j=" << j++ << item << endl; - } - } - REQUIRE(1 == 1); } }