diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..cc01c84 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,23 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.1] - 2024-02-12 + +### Added + +- Notes in Classifier class +- BoostAODE: Add note with used features in initialization with feature selection +- BoostAODE: Add note with the number of models +- BoostAODE: Add note with the number of features used to create models if not all features are used +- Test version number in TestBayesModels +- Add tests with feature_select and notes on BoostAODE + +### Fixed + +- Network predict test +- Network predict_proba test +- Network score test diff --git a/CMakeLists.txt b/CMakeLists.txt index 92c3294..8cc0b8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.20) project(BayesNet - VERSION 1.0.0 + VERSION 1.0.1 DESCRIPTION "Bayesian Network and basic classifiers Library." HOMEPAGE_URL "https://github.com/rmontanana/bayesnet" LANGUAGES CXX diff --git a/src/BayesNet/BoostAODE.cc b/src/BayesNet/BoostAODE.cc index e2083f0..efb95e6 100644 --- a/src/BayesNet/BoostAODE.cc +++ b/src/BayesNet/BoostAODE.cc @@ -201,6 +201,7 @@ namespace bayesnet { notes.push_back("Used features in train: " + std::to_string(featuresUsed.size()) + " of " + std::to_string(features.size())); status = WARNING; } + notes.push_back("Number of models: " + std::to_string(n_models)); } std::vector BoostAODE::graph(const std::string& title) const { diff --git a/src/BayesNet/Classifier.h b/src/BayesNet/Classifier.h index 3dfd18b..24657ca 100644 --- a/src/BayesNet/Classifier.h +++ b/src/BayesNet/Classifier.h @@ -37,7 +37,7 @@ namespace bayesnet { int getNumberOfStates() const override; torch::Tensor predict(torch::Tensor& X) override; status_t getStatus() const override { return status; } - std::string getVersion() override { return "0.2.0"; }; + std::string getVersion() override { return { project_version.begin(), project_version.end() }; }; std::vector predict(std::vector>& X) override; float score(torch::Tensor& X, torch::Tensor& y) override; float score(std::vector>& X, std::vector& y) override; diff --git a/tests/TestBayesModels.cc b/tests/TestBayesModels.cc index cfc91c4..3e97aab 100644 --- a/tests/TestBayesModels.cc +++ b/tests/TestBayesModels.cc @@ -16,6 +16,11 @@ #include "AODELd.h" #include "TestUtils.h" +TEST_CASE("Library check version", "[BayesNet]") +{ + auto clf = bayesnet::KDB(2); + REQUIRE(clf.getVersion() == "1.0.1"); +} TEST_CASE("Test Bayesian Classifiers score", "[BayesNet]") { map , float> scores = { @@ -139,7 +144,7 @@ TEST_CASE("Get num features & num edges", "[BayesNet]") REQUIRE(clf.getNumberOfNodes() == 5); REQUIRE(clf.getNumberOfEdges() == 8); } -TEST_CASE("BoostAODE feature_select CFS") +TEST_CASE("BoostAODE feature_select CFS", "[BayesNet]") { auto raw = RawDatasets("glass", true); auto clf = bayesnet::BoostAODE(); @@ -147,6 +152,25 @@ TEST_CASE("BoostAODE feature_select CFS") clf.fit(raw.Xv, raw.yv, raw.featuresv, raw.classNamev, raw.statesv); REQUIRE(clf.getNumberOfNodes() == 90); REQUIRE(clf.getNumberOfEdges() == 153); - REQUIRE(clf.getNotes().size() == 1); + REQUIRE(clf.getNotes().size() == 2); REQUIRE(clf.getNotes()[0] == "Used features in initialization: 6 of 9 with CFS"); -} \ No newline at end of file + REQUIRE(clf.getNotes()[1] == "Number of models: 9"); +} +TEST_CASE("BoostAODE test used features in train note", "[BayesNet]") +{ + auto raw = RawDatasets("diabetes", true); + auto clf = bayesnet::BoostAODE(); + clf.setHyperparameters({ + {"ascending",true}, + {"convergence", true}, + {"repeatSparent",true}, + {"select_features","CFS"} + }); + clf.fit(raw.Xv, raw.yv, raw.featuresv, raw.classNamev, raw.statesv); + REQUIRE(clf.getNumberOfNodes() == 72); + REQUIRE(clf.getNumberOfEdges() == 120); + REQUIRE(clf.getNotes().size() == 3); + REQUIRE(clf.getNotes()[0] == "Used features in initialization: 6 of 8 with CFS"); + REQUIRE(clf.getNotes()[1] == "Used features in train: 7 of 8"); + REQUIRE(clf.getNotes()[2] == "Number of models: 8"); +} diff --git a/tests/TestBayesNetwork.cc b/tests/TestBayesNetwork.cc index b232508..795ed13 100644 --- a/tests/TestBayesNetwork.cc +++ b/tests/TestBayesNetwork.cc @@ -25,6 +25,7 @@ TEST_CASE("Test Bayesian Network", "[BayesNet]") auto raw = RawDatasets("iris", true); auto net = bayesnet::Network(); + double threshold = 1e-4; SECTION("Test get features") { @@ -167,97 +168,44 @@ TEST_CASE("Test Bayesian Network", "[BayesNet]") REQUIRE(str[5] == "C [shape=circle] \n"); REQUIRE(str[6] == "}\n"); } - - - // SECTION("Test predict") - // { - // auto net = bayesnet::Network(); - // net.fit(raw.Xv, raw.yv, raw.weightsv, raw.featuresv, raw.classNamev, raw.statesv); - // std::vector> test = { {1, 2, 0, 1}, {0, 1, 2, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}, {2, 2, 2, 2} }; - // std::vector y_test = { 0, 1, 1, 0, 2 }; - // auto y_pred = net.predict(test); - // REQUIRE(y_pred == y_test); - // } - - // SECTION("Test predict_proba") - // { - // auto net = bayesnet::Network(); - // net.fit(raw.Xv, raw.yv, raw.weightsv, raw.featuresv, raw.classNamev, raw.statesv); - // std::vector> test = { {1, 2, 0, 1}, {0, 1, 2, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}, {2, 2, 2, 2} }; - // auto y_test = { 0, 1, 1, 0, 2 }; - // auto y_pred = net.predict(test); - // REQUIRE(y_pred == y_test); - // } -} - -// SECTION("Test score") -// { -// auto net = bayesnet::Network(); -// net.fit(Xd, y, weights, features, className, states); -// auto test = { {1, 2, 0, 1}, {0, 1, 2, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}, {2, 2, 2, 2} }; -// auto score = net.score(X, y); -// REQUIRE(score == Catch::Approx(); -// } - -// -// - -// SECTION("Test graph") -// { -// auto net = bayesnet::Network(); -// net.addNode("A"); -// net.addNode("B"); -// net.addNode("C"); -// net.addEdge("A", "B"); -// net.addEdge("A", "C"); -// auto str = net.graph("Test Graph"); -// REQUIRE(str.size() == 6); -// REQUIRE(str[0] == "digraph \"Test Graph\" {"); -// REQUIRE(str[1] == " A -> B;"); -// REQUIRE(str[2] == " A -> C;"); -// REQUIRE(str[3] == " B [shape=ellipse];"); -// REQUIRE(str[4] == " C [shape=ellipse];"); -// REQUIRE(str[5] == "}"); -// } - -// SECTION("Test initialize") -// { -// auto net = bayesnet::Network(); -// net.addNode("A"); -// net.addNode("B"); -// net.addNode("C"); -// net.addEdge("A", "B"); -// net.addEdge("A", "C"); -// net.initialize(); -// REQUIRE(net.getNodes().size() == 0); -// REQUIRE(net.getEdges().size() == 0); -// REQUIRE(net.getFeatures().size() == 0); -// REQUIRE(net.getClassNumStates() == 0); -// REQUIRE(net.getClassName().empty()); -// REQUIRE(net.getStates() == 0); -// REQUIRE(net.getSamples().numel() == 0); -// } - -// SECTION("Test dump_cpt") -// { -// auto net = bayesnet::Network(); -// net.addNode("A"); -// net.addNode("B"); -// net.addNode("C"); -// net.addEdge("A", "B"); -// net.addEdge("A", "C"); -// net.setClassName("C"); -// net.setStates({ {"A", {0, 1}}, {"B", {0, 1}}, {"C", {0, 1, 2}} }); -// net.fit({ {0, 0}, {0, 1}, {1, 0}, {1, 1} }, { 0, 1, 1, 2 }, {}, { "A", "B" }, "C", { {"A", {0, 1}}, {"B", {0, 1}}, {"C", {0, 1, 2}} }); -// net.dump_cpt(); -// // TODO: Check that the file was created and contains the expected data -// } - -// SECTION("Test version") -// { -// auto net = bayesnet::Network(); -// REQUIRE(net.version() == "0.2.0"); -// } -// } - -// } + SECTION("Test predict") + { + auto net = bayesnet::Network(); + buildModel(net, raw.featuresv, raw.classNamev); + net.fit(raw.Xv, raw.yv, raw.weightsv, raw.featuresv, raw.classNamev, raw.statesv); + std::vector> test = { {1, 2, 0, 1, 1}, {0, 1, 2, 0, 1}, {0, 0, 0, 0, 1}, {2, 2, 2, 2, 1} }; + std::vector y_test = { 2, 2, 0, 2, 1 }; + auto y_pred = net.predict(test); + REQUIRE(y_pred == y_test); + } + SECTION("Test predict_proba") + { + auto net = bayesnet::Network(); + buildModel(net, raw.featuresv, raw.classNamev); + net.fit(raw.Xv, raw.yv, raw.weightsv, raw.featuresv, raw.classNamev, raw.statesv); + std::vector> test = { {1, 2, 0, 1, 1}, {0, 1, 2, 0, 1}, {0, 0, 0, 0, 1}, {2, 2, 2, 2, 1} }; + std::vector> y_test = { + {0.450237, 0.0866621, 0.463101}, + {0.244443, 0.0925922, 0.662964}, + {0.913441, 0.0125857, 0.0739732}, + {0.450237, 0.0866621, 0.463101}, + {0.0135226, 0.971726, 0.0147519} + }; + auto y_pred = net.predict_proba(test); + REQUIRE(y_pred.size() == 5); + REQUIRE(y_pred[0].size() == 3); + for (int i = 0; i < y_pred.size(); ++i) { + for (int j = 0; j < y_pred[i].size(); ++j) { + REQUIRE(y_pred[i][j] == Catch::Approx(y_test[i][j]).margin(threshold)); + } + } + } + SECTION("Test score") + { + auto net = bayesnet::Network(); + buildModel(net, raw.featuresv, raw.classNamev); + net.fit(raw.Xv, raw.yv, raw.weightsv, raw.featuresv, raw.classNamev, raw.statesv); + auto score = net.score(raw.Xv, raw.yv); + REQUIRE(score == Catch::Approx(0.97333333).margin(threshold)); + } +} \ No newline at end of file