Complete implementation with tests
This commit is contained in:
@@ -31,9 +31,9 @@ TEST_CASE("Test Bayesian Classifiers score & version", "[Models]")
|
||||
{{"diabetes", "SPODE"}, 0.802083},
|
||||
{{"diabetes", "TAN"}, 0.821615},
|
||||
{{"diabetes", "AODELd"}, 0.8125f},
|
||||
{{"diabetes", "KDBLd"}, 0.80208f},
|
||||
{{"diabetes", "KDBLd"}, 0.804688f},
|
||||
{{"diabetes", "SPODELd"}, 0.7890625f},
|
||||
{{"diabetes", "TANLd"}, 0.803385437f},
|
||||
{{"diabetes", "TANLd"}, 0.8125f},
|
||||
{{"diabetes", "BoostAODE"}, 0.83984f},
|
||||
// Ecoli
|
||||
{{"ecoli", "AODE"}, 0.889881},
|
||||
@@ -42,9 +42,9 @@ TEST_CASE("Test Bayesian Classifiers score & version", "[Models]")
|
||||
{{"ecoli", "SPODE"}, 0.880952},
|
||||
{{"ecoli", "TAN"}, 0.892857},
|
||||
{{"ecoli", "AODELd"}, 0.875f},
|
||||
{{"ecoli", "KDBLd"}, 0.880952358f},
|
||||
{{"ecoli", "KDBLd"}, 0.872024f},
|
||||
{{"ecoli", "SPODELd"}, 0.839285731f},
|
||||
{{"ecoli", "TANLd"}, 0.848214269f},
|
||||
{{"ecoli", "TANLd"}, 0.869047642f},
|
||||
{{"ecoli", "BoostAODE"}, 0.89583f},
|
||||
// Glass
|
||||
{{"glass", "AODE"}, 0.79439},
|
||||
@@ -53,9 +53,9 @@ TEST_CASE("Test Bayesian Classifiers score & version", "[Models]")
|
||||
{{"glass", "SPODE"}, 0.775701},
|
||||
{{"glass", "TAN"}, 0.827103},
|
||||
{{"glass", "AODELd"}, 0.799065411f},
|
||||
{{"glass", "KDBLd"}, 0.82710278f},
|
||||
{{"glass", "KDBLd"}, 0.864485979f},
|
||||
{{"glass", "SPODELd"}, 0.780373812f},
|
||||
{{"glass", "TANLd"}, 0.869158864f},
|
||||
{{"glass", "TANLd"}, 0.831775725f},
|
||||
{{"glass", "BoostAODE"}, 0.84579f},
|
||||
// Iris
|
||||
{{"iris", "AODE"}, 0.973333},
|
||||
@@ -68,29 +68,29 @@ TEST_CASE("Test Bayesian Classifiers score & version", "[Models]")
|
||||
{{"iris", "SPODELd"}, 0.96f},
|
||||
{{"iris", "TANLd"}, 0.97333f},
|
||||
{{"iris", "BoostAODE"}, 0.98f} };
|
||||
std::map<std::string, bayesnet::BaseClassifier*> models{ {"AODE", new bayesnet::AODE()},
|
||||
{"AODELd", new bayesnet::AODELd()},
|
||||
{"BoostAODE", new bayesnet::BoostAODE()},
|
||||
{"KDB", new bayesnet::KDB(2)},
|
||||
{"KDBLd", new bayesnet::KDBLd(2)},
|
||||
{"XSPODE", new bayesnet::XSpode(1)},
|
||||
{"SPODE", new bayesnet::SPODE(1)},
|
||||
{"SPODELd", new bayesnet::SPODELd(1)},
|
||||
{"TAN", new bayesnet::TAN()},
|
||||
{"TANLd", new bayesnet::TANLd()} };
|
||||
std::map<std::string, std::unique_ptr<bayesnet::BaseClassifier>> models;
|
||||
models["AODE"] = std::make_unique<bayesnet::AODE>();
|
||||
models["AODELd"] = std::make_unique<bayesnet::AODELd>();
|
||||
models["BoostAODE"] = std::make_unique<bayesnet::BoostAODE>();
|
||||
models["KDB"] = std::make_unique<bayesnet::KDB>(2);
|
||||
models["KDBLd"] = std::make_unique<bayesnet::KDBLd>(2);
|
||||
models["XSPODE"] = std::make_unique<bayesnet::XSpode>(1);
|
||||
models["SPODE"] = std::make_unique<bayesnet::SPODE>(1);
|
||||
models["SPODELd"] = std::make_unique<bayesnet::SPODELd>(1);
|
||||
models["TAN"] = std::make_unique<bayesnet::TAN>();
|
||||
models["TANLd"] = std::make_unique<bayesnet::TANLd>();
|
||||
std::string name = GENERATE("AODE", "AODELd", "KDB", "KDBLd", "SPODE", "XSPODE", "SPODELd", "TAN", "TANLd");
|
||||
auto clf = models[name];
|
||||
auto clf = std::move(models[name]);
|
||||
|
||||
SECTION("Test " + name + " classifier")
|
||||
{
|
||||
for (const std::string& file_name : { "glass", "iris", "ecoli", "diabetes" }) {
|
||||
auto clf = models[name];
|
||||
auto discretize = name.substr(name.length() - 2) != "Ld";
|
||||
auto raw = RawDatasets(file_name, discretize);
|
||||
clf->fit(raw.Xt, raw.yt, raw.features, raw.className, raw.states, raw.smoothing);
|
||||
auto score = clf->score(raw.Xt, raw.yt);
|
||||
// std::cout << "Classifier: " << name << " File: " << file_name << " Score: " << score << " expected = " <<
|
||||
// scores[{file_name, name}] << std::endl;
|
||||
// scores[{file_name, name}] << std::endl;
|
||||
INFO("Classifier: " << name << " File: " << file_name);
|
||||
REQUIRE(score == Catch::Approx(scores[{file_name, name}]).epsilon(raw.epsilon));
|
||||
REQUIRE(clf->getStatus() == bayesnet::NORMAL);
|
||||
@@ -101,7 +101,6 @@ TEST_CASE("Test Bayesian Classifiers score & version", "[Models]")
|
||||
INFO("Checking version of " << name << " classifier");
|
||||
REQUIRE(clf->getVersion() == ACTUAL_VERSION);
|
||||
}
|
||||
delete clf;
|
||||
}
|
||||
TEST_CASE("Models features & Graph", "[Models]")
|
||||
{
|
||||
@@ -133,7 +132,7 @@ TEST_CASE("Models features & Graph", "[Models]")
|
||||
clf.fit(raw.Xt, raw.yt, raw.features, raw.className, raw.states, raw.smoothing);
|
||||
REQUIRE(clf.getNumberOfNodes() == 5);
|
||||
REQUIRE(clf.getNumberOfEdges() == 7);
|
||||
REQUIRE(clf.getNumberOfStates() == 27);
|
||||
REQUIRE(clf.getNumberOfStates() == 26);
|
||||
REQUIRE(clf.getClassNumStates() == 3);
|
||||
REQUIRE(clf.show() == std::vector<std::string>{"class -> sepallength, sepalwidth, petallength, petalwidth, ",
|
||||
"petallength -> sepallength, ", "petalwidth -> ",
|
||||
@@ -149,7 +148,6 @@ TEST_CASE("Get num features & num edges", "[Models]")
|
||||
REQUIRE(clf.getNumberOfNodes() == 5);
|
||||
REQUIRE(clf.getNumberOfEdges() == 8);
|
||||
}
|
||||
|
||||
TEST_CASE("Model predict_proba", "[Models]")
|
||||
{
|
||||
std::string model = GENERATE("TAN", "SPODE", "BoostAODEproba", "BoostAODEvoting", "TANLd", "SPODELd", "KDBLd");
|
||||
@@ -180,15 +178,15 @@ TEST_CASE("Model predict_proba", "[Models]")
|
||||
{0.0284828, 0.770524, 0.200993},
|
||||
{0.0213182, 0.857189, 0.121493},
|
||||
{0.00868436, 0.949494, 0.0418215} });
|
||||
auto res_prob_tanld = std::vector<std::vector<double>>({ {0.000544493, 0.995796, 0.00365992 },
|
||||
{0.000908092, 0.997268, 0.00182429 },
|
||||
{0.000908092, 0.997268, 0.00182429 },
|
||||
{0.000908092, 0.997268, 0.00182429 },
|
||||
{0.00228423, 0.994645, 0.00307078 },
|
||||
{0.00120539, 0.0666788, 0.932116 },
|
||||
{0.00361847, 0.979203, 0.017179 },
|
||||
{0.00483293, 0.985326, 0.00984064 },
|
||||
{0.000595606, 0.9977, 0.00170441 } });
|
||||
auto res_prob_tanld = std::vector<std::vector<double>>({ {0.000597557, 0.9957, 0.00370254},
|
||||
{0.000731377, 0.997914, 0.0013544},
|
||||
{0.000731377, 0.997914, 0.0013544},
|
||||
{0.000731377, 0.997914, 0.0013544},
|
||||
{0.000838614, 0.998122, 0.00103923},
|
||||
{0.00130852, 0.0659492, 0.932742},
|
||||
{0.00365946, 0.979412, 0.0169281},
|
||||
{0.00435035, 0.986248, 0.00940212},
|
||||
{0.000583815, 0.997746, 0.00167066} });
|
||||
auto res_prob_spodeld = std::vector<std::vector<double>>({ {0.000908024, 0.993742, 0.00535024 },
|
||||
{0.00187726, 0.99167, 0.00645308 },
|
||||
{0.00187726, 0.99167, 0.00645308 },
|
||||
@@ -216,29 +214,33 @@ TEST_CASE("Model predict_proba", "[Models]")
|
||||
{"TANLd", res_prob_tanld},
|
||||
{"SPODELd", res_prob_spodeld},
|
||||
{"KDBLd", res_prob_kdbld} };
|
||||
std::map<std::string, bayesnet::BaseClassifier*> models{ {"TAN", new bayesnet::TAN()},
|
||||
{"SPODE", new bayesnet::SPODE(0)},
|
||||
{"BoostAODEproba", new bayesnet::BoostAODE(false)},
|
||||
{"BoostAODEvoting", new bayesnet::BoostAODE(true)},
|
||||
{"TANLd", new bayesnet::TANLd()},
|
||||
{"SPODELd", new bayesnet::SPODELd(0)},
|
||||
{"KDBLd", new bayesnet::KDBLd(2)} };
|
||||
|
||||
std::map<std::string, std::unique_ptr<bayesnet::BaseClassifier>> models;
|
||||
models["TAN"] = std::make_unique<bayesnet::TAN>();
|
||||
models["SPODE"] = std::make_unique<bayesnet::SPODE>(0);
|
||||
models["BoostAODEproba"] = std::make_unique<bayesnet::BoostAODE>(false);
|
||||
models["BoostAODEvoting"] = std::make_unique<bayesnet::BoostAODE>(true);
|
||||
models["TANLd"] = std::make_unique<bayesnet::TANLd>();
|
||||
models["SPODELd"] = std::make_unique<bayesnet::SPODELd>(0);
|
||||
models["KDBLd"] = std::make_unique<bayesnet::KDBLd>(2);
|
||||
|
||||
int init_index = 78;
|
||||
|
||||
SECTION("Test " + model + " predict_proba")
|
||||
{
|
||||
INFO("Testing " << model << " predict_proba");
|
||||
auto ld_model = model.substr(model.length() - 2) == "Ld";
|
||||
auto discretize = !ld_model;
|
||||
auto raw = RawDatasets("iris", discretize);
|
||||
auto clf = models[model];
|
||||
clf->fit(raw.Xt, raw.yt, raw.features, raw.className, raw.states, raw.smoothing);
|
||||
auto yt_pred_proba = clf->predict_proba(raw.Xt);
|
||||
auto yt_pred = clf->predict(raw.Xt);
|
||||
auto& clf = *models[model];
|
||||
clf.fit(raw.Xt, raw.yt, raw.features, raw.className, raw.states, raw.smoothing);
|
||||
auto yt_pred_proba = clf.predict_proba(raw.Xt);
|
||||
auto yt_pred = clf.predict(raw.Xt);
|
||||
std::vector<int> y_pred;
|
||||
std::vector<std::vector<double>> y_pred_proba;
|
||||
if (!ld_model) {
|
||||
y_pred = clf->predict(raw.Xv);
|
||||
y_pred_proba = clf->predict_proba(raw.Xv);
|
||||
y_pred = clf.predict(raw.Xv);
|
||||
y_pred_proba = clf.predict_proba(raw.Xv);
|
||||
REQUIRE(y_pred.size() == y_pred_proba.size());
|
||||
REQUIRE(y_pred.size() == yt_pred.size(0));
|
||||
REQUIRE(y_pred.size() == yt_pred_proba.size(0));
|
||||
@@ -267,18 +269,20 @@ TEST_CASE("Model predict_proba", "[Models]")
|
||||
} else {
|
||||
// Check predict_proba values for vectors and tensors
|
||||
auto predictedClasses = yt_pred_proba.argmax(1);
|
||||
// std::cout << model << std::endl;
|
||||
for (int i = 0; i < 9; i++) {
|
||||
REQUIRE(predictedClasses[i].item<int>() == yt_pred[i].item<int>());
|
||||
// std::cout << "{";
|
||||
for (int j = 0; j < 3; j++) {
|
||||
// std::cout << yt_pred_proba[i + init_index][j].item<double>() << ", ";
|
||||
REQUIRE(res_prob[model][i][j] ==
|
||||
Catch::Approx(yt_pred_proba[i + init_index][j].item<double>()).epsilon(raw.epsilon));
|
||||
}
|
||||
// std::cout << "\b\b}," << std::endl;
|
||||
}
|
||||
}
|
||||
delete clf;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("AODE voting-proba", "[Models]")
|
||||
{
|
||||
auto raw = RawDatasets("glass", true);
|
||||
@@ -324,11 +328,15 @@ TEST_CASE("KDB with hyperparameters", "[Models]")
|
||||
REQUIRE(score == Catch::Approx(0.827103).epsilon(raw.epsilon));
|
||||
REQUIRE(scoret == Catch::Approx(0.761682).epsilon(raw.epsilon));
|
||||
}
|
||||
TEST_CASE("Incorrect type of data for SPODELd", "[Models]")
|
||||
TEST_CASE("Incorrect type of data for Ld models", "[Models]")
|
||||
{
|
||||
auto raw = RawDatasets("iris", true);
|
||||
auto clf = bayesnet::SPODELd(0);
|
||||
REQUIRE_THROWS_AS(clf.fit(raw.dataset, raw.features, raw.className, raw.states, raw.smoothing), std::runtime_error);
|
||||
auto clfs = bayesnet::SPODELd(0);
|
||||
REQUIRE_THROWS_AS(clfs.fit(raw.dataset, raw.features, raw.className, raw.states, raw.smoothing), std::runtime_error);
|
||||
auto clft = bayesnet::TANLd();
|
||||
REQUIRE_THROWS_AS(clft.fit(raw.dataset, raw.features, raw.className, raw.states, raw.smoothing), std::runtime_error);
|
||||
auto clfk = bayesnet::KDBLd(0);
|
||||
REQUIRE_THROWS_AS(clfk.fit(raw.dataset, raw.features, raw.className, raw.states, raw.smoothing), std::runtime_error);
|
||||
}
|
||||
TEST_CASE("Predict, predict_proba & score without fitting", "[Models]")
|
||||
{
|
||||
@@ -428,3 +436,49 @@ TEST_CASE("Check KDB loop detection", "[Models]")
|
||||
REQUIRE_NOTHROW(clf.test_add_m_edges(features, 0, S, weights));
|
||||
REQUIRE_NOTHROW(clf.test_add_m_edges(features, 1, S, weights));
|
||||
}
|
||||
TEST_CASE("Local discretization hyperparameters", "[Models]")
|
||||
{
|
||||
auto raw = RawDatasets("iris", false);
|
||||
auto clfs = bayesnet::SPODELd(0);
|
||||
clfs.setHyperparameters({
|
||||
{"max_iterations", 7},
|
||||
{"verbose_convergence", true},
|
||||
});
|
||||
REQUIRE_NOTHROW(clfs.fit(raw.Xt, raw.yt, raw.features, raw.className, raw.states, raw.smoothing));
|
||||
REQUIRE(clfs.getStatus() == bayesnet::NORMAL);
|
||||
auto clfk = bayesnet::KDBLd(0);
|
||||
clfk.setHyperparameters({
|
||||
{"k", 3},
|
||||
{"theta", 1e-4},
|
||||
});
|
||||
REQUIRE_NOTHROW(clfk.fit(raw.Xt, raw.yt, raw.features, raw.className, raw.states, raw.smoothing));
|
||||
REQUIRE(clfk.getStatus() == bayesnet::NORMAL);
|
||||
auto clfa = bayesnet::AODELd();
|
||||
clfa.setHyperparameters({
|
||||
{"ld_proposed_cuts", 9},
|
||||
{"ld_algorithm", "BINQ"},
|
||||
});
|
||||
REQUIRE_NOTHROW(clfa.fit(raw.Xt, raw.yt, raw.features, raw.className, raw.states, raw.smoothing));
|
||||
REQUIRE(clfa.getStatus() == bayesnet::NORMAL);
|
||||
auto clft = bayesnet::TANLd();
|
||||
clft.setHyperparameters({
|
||||
{"ld_proposed_cuts", 7},
|
||||
{"mdlp_max_depth", 5},
|
||||
{"mdlp_min_length", 3},
|
||||
{"ld_algorithm", "MDLP"},
|
||||
});
|
||||
REQUIRE_NOTHROW(clft.fit(raw.Xt, raw.yt, raw.features, raw.className, raw.states, raw.smoothing));
|
||||
REQUIRE(clft.getStatus() == bayesnet::NORMAL);
|
||||
clft.setHyperparameters({
|
||||
{"ld_proposed_cuts", 9},
|
||||
{"ld_algorithm", "BINQ"},
|
||||
});
|
||||
REQUIRE_NOTHROW(clft.fit(raw.Xt, raw.yt, raw.features, raw.className, raw.states, raw.smoothing));
|
||||
REQUIRE(clft.getStatus() == bayesnet::NORMAL);
|
||||
clft.setHyperparameters({
|
||||
{"ld_proposed_cuts", 5},
|
||||
{"ld_algorithm", "BINU"},
|
||||
});
|
||||
REQUIRE_NOTHROW(clft.fit(raw.Xt, raw.yt, raw.features, raw.className, raw.states, raw.smoothing));
|
||||
REQUIRE(clft.getStatus() == bayesnet::NORMAL);
|
||||
}
|
||||
|
@@ -345,12 +345,12 @@ TEST_CASE("Test Bayesian Network", "[Network]")
|
||||
auto net1 = bayesnet::Network();
|
||||
buildModel(net1, raw.features, raw.className);
|
||||
net1.fit(raw.Xv, raw.yv, raw.weightsv, raw.features, raw.className, raw.states, raw.smoothing);
|
||||
|
||||
|
||||
// Create empty network and assign
|
||||
auto net2 = bayesnet::Network();
|
||||
net2.addNode("TempNode"); // Add something to make sure it gets cleared
|
||||
net2 = net1;
|
||||
|
||||
|
||||
// Verify they are equal
|
||||
REQUIRE(net1.getFeatures() == net2.getFeatures());
|
||||
REQUIRE(net1.getEdges() == net2.getEdges());
|
||||
@@ -361,10 +361,10 @@ TEST_CASE("Test Bayesian Network", "[Network]")
|
||||
REQUIRE(net1.getSamples().size(0) == net2.getSamples().size(0));
|
||||
REQUIRE(net1.getSamples().size(1) == net2.getSamples().size(1));
|
||||
REQUIRE(net1.getNodes().size() == net2.getNodes().size());
|
||||
|
||||
|
||||
// Verify topology equality
|
||||
REQUIRE(net1 == net2);
|
||||
|
||||
|
||||
// Verify they are separate objects by modifying one
|
||||
net2.initialize();
|
||||
net2.addNode("OnlyInNet2");
|
||||
@@ -376,46 +376,47 @@ TEST_CASE("Test Bayesian Network", "[Network]")
|
||||
INFO("Test self assignment");
|
||||
buildModel(net, raw.features, raw.className);
|
||||
net.fit(raw.Xv, raw.yv, raw.weightsv, raw.features, raw.className, raw.states, raw.smoothing);
|
||||
|
||||
|
||||
int original_edges = net.getNumEdges();
|
||||
int original_nodes = net.getNodes().size();
|
||||
|
||||
|
||||
// Self assignment should not corrupt the network
|
||||
net = net;
|
||||
|
||||
auto all_features = raw.features;
|
||||
all_features.push_back(raw.className);
|
||||
REQUIRE(net.getNumEdges() == original_edges);
|
||||
REQUIRE(net.getNodes().size() == original_nodes);
|
||||
REQUIRE(net.getFeatures() == raw.features);
|
||||
REQUIRE(net.getFeatures() == all_features);
|
||||
REQUIRE(net.getClassName() == raw.className);
|
||||
}
|
||||
SECTION("Test operator== topology comparison")
|
||||
{
|
||||
INFO("Test operator== topology comparison");
|
||||
|
||||
|
||||
// Test 1: Two identical networks
|
||||
auto net1 = bayesnet::Network();
|
||||
auto net2 = bayesnet::Network();
|
||||
|
||||
|
||||
net1.addNode("A");
|
||||
net1.addNode("B");
|
||||
net1.addNode("C");
|
||||
net1.addEdge("A", "B");
|
||||
net1.addEdge("B", "C");
|
||||
|
||||
|
||||
net2.addNode("A");
|
||||
net2.addNode("B");
|
||||
net2.addNode("C");
|
||||
net2.addEdge("A", "B");
|
||||
net2.addEdge("B", "C");
|
||||
|
||||
|
||||
REQUIRE(net1 == net2);
|
||||
|
||||
|
||||
// Test 2: Different nodes
|
||||
auto net3 = bayesnet::Network();
|
||||
net3.addNode("A");
|
||||
net3.addNode("D"); // Different node
|
||||
REQUIRE_FALSE(net1 == net3);
|
||||
|
||||
|
||||
// Test 3: Same nodes, different edges
|
||||
auto net4 = bayesnet::Network();
|
||||
net4.addNode("A");
|
||||
@@ -424,12 +425,12 @@ TEST_CASE("Test Bayesian Network", "[Network]")
|
||||
net4.addEdge("A", "C"); // Different topology
|
||||
net4.addEdge("B", "C");
|
||||
REQUIRE_FALSE(net1 == net4);
|
||||
|
||||
|
||||
// Test 4: Empty networks
|
||||
auto net5 = bayesnet::Network();
|
||||
auto net6 = bayesnet::Network();
|
||||
REQUIRE(net5 == net6);
|
||||
|
||||
|
||||
// Test 5: Same topology, different edge order
|
||||
auto net7 = bayesnet::Network();
|
||||
net7.addNode("A");
|
||||
@@ -442,35 +443,36 @@ TEST_CASE("Test Bayesian Network", "[Network]")
|
||||
SECTION("Test RAII compliance with smart pointers")
|
||||
{
|
||||
INFO("Test RAII compliance with smart pointers");
|
||||
|
||||
|
||||
std::unique_ptr<bayesnet::Network> net1 = std::make_unique<bayesnet::Network>();
|
||||
buildModel(*net1, raw.features, raw.className);
|
||||
net1->fit(raw.Xv, raw.yv, raw.weightsv, raw.features, raw.className, raw.states, raw.smoothing);
|
||||
|
||||
|
||||
// Test that copy constructor works with smart pointers
|
||||
std::unique_ptr<bayesnet::Network> net2 = std::make_unique<bayesnet::Network>(*net1);
|
||||
|
||||
|
||||
REQUIRE(*net1 == *net2);
|
||||
REQUIRE(net1->getNumEdges() == net2->getNumEdges());
|
||||
REQUIRE(net1->getNodes().size() == net2->getNodes().size());
|
||||
|
||||
|
||||
// Destroy original
|
||||
net1.reset();
|
||||
|
||||
|
||||
// Test predictions still work
|
||||
std::vector<std::vector<int>> test = { {1}, {2}, {0}, {1} };
|
||||
REQUIRE_NOTHROW(net2->predict(test));
|
||||
|
||||
// net2 should still be valid and functional
|
||||
net2->initialize();
|
||||
REQUIRE_NOTHROW(net2->addNode("NewNode"));
|
||||
REQUIRE(net2->getNodes().count("NewNode") == 1);
|
||||
|
||||
// Test predictions still work
|
||||
std::vector<std::vector<int>> test = { {1, 2, 0, 1, 1} };
|
||||
REQUIRE_NOTHROW(net2->predict(test));
|
||||
}
|
||||
SECTION("Test complex topology copy")
|
||||
{
|
||||
INFO("Test complex topology copy");
|
||||
|
||||
|
||||
auto original = bayesnet::Network();
|
||||
|
||||
|
||||
// Create a more complex network
|
||||
original.addNode("Root");
|
||||
original.addNode("Child1");
|
||||
@@ -478,45 +480,45 @@ TEST_CASE("Test Bayesian Network", "[Network]")
|
||||
original.addNode("Grandchild1");
|
||||
original.addNode("Grandchild2");
|
||||
original.addNode("Grandchild3");
|
||||
|
||||
|
||||
original.addEdge("Root", "Child1");
|
||||
original.addEdge("Root", "Child2");
|
||||
original.addEdge("Child1", "Grandchild1");
|
||||
original.addEdge("Child1", "Grandchild2");
|
||||
original.addEdge("Child2", "Grandchild3");
|
||||
|
||||
|
||||
// Copy it
|
||||
auto copy = original;
|
||||
|
||||
|
||||
// Verify topology is identical
|
||||
REQUIRE(original == copy);
|
||||
REQUIRE(original.getNodes().size() == copy.getNodes().size());
|
||||
REQUIRE(original.getNumEdges() == copy.getNumEdges());
|
||||
|
||||
|
||||
// Verify edges are properly reconstructed
|
||||
auto originalEdges = original.getEdges();
|
||||
auto copyEdges = copy.getEdges();
|
||||
REQUIRE(originalEdges.size() == copyEdges.size());
|
||||
|
||||
|
||||
// Verify node relationships are properly copied
|
||||
for (const auto& nodePair : original.getNodes()) {
|
||||
const std::string& nodeName = nodePair.first;
|
||||
auto* originalNode = nodePair.second.get();
|
||||
auto* copyNode = copy.getNodes().at(nodeName).get();
|
||||
|
||||
|
||||
REQUIRE(originalNode->getParents().size() == copyNode->getParents().size());
|
||||
REQUIRE(originalNode->getChildren().size() == copyNode->getChildren().size());
|
||||
|
||||
|
||||
// Verify parent names match
|
||||
for (size_t i = 0; i < originalNode->getParents().size(); ++i) {
|
||||
REQUIRE(originalNode->getParents()[i]->getName() ==
|
||||
copyNode->getParents()[i]->getName());
|
||||
REQUIRE(originalNode->getParents()[i]->getName() ==
|
||||
copyNode->getParents()[i]->getName());
|
||||
}
|
||||
|
||||
|
||||
// Verify child names match
|
||||
for (size_t i = 0; i < originalNode->getChildren().size(); ++i) {
|
||||
REQUIRE(originalNode->getChildren()[i]->getName() ==
|
||||
copyNode->getChildren()[i]->getName());
|
||||
REQUIRE(originalNode->getChildren()[i]->getName() ==
|
||||
copyNode->getChildren()[i]->getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -158,4 +158,47 @@ TEST_CASE("TEST MinFill method", "[Node]")
|
||||
REQUIRE(node_2.minFill() == 6);
|
||||
REQUIRE(node_3.minFill() == 3);
|
||||
REQUIRE(node_4.minFill() == 1);
|
||||
}
|
||||
TEST_CASE("Test operator =", "[Node]")
|
||||
{
|
||||
// Generate a test to test the operator = of the Node class
|
||||
// Create a node with 3 parents and 2 children
|
||||
auto node = bayesnet::Node("N1");
|
||||
auto parent_1 = bayesnet::Node("P1");
|
||||
parent_1.setNumStates(3);
|
||||
auto child_1 = bayesnet::Node("H1");
|
||||
child_1.setNumStates(2);
|
||||
node.addParent(&parent_1);
|
||||
node.addChild(&child_1);
|
||||
// Create a cpt in the node using computeCPT
|
||||
auto dataset = torch::tensor({ {1, 0, 0, 1}, {0, 1, 2, 1}, {0, 1, 1, 0} });
|
||||
auto states = std::vector<int>({ 2, 3, 3 });
|
||||
auto features = std::vector<std::string>{ "N1", "P1", "H1" };
|
||||
auto className = std::string("Class");
|
||||
auto weights = torch::tensor({ 1.0, 1.0, 1.0, 1.0 }, torch::kDouble);
|
||||
node.setNumStates(2);
|
||||
node.computeCPT(dataset, features, 0.0, weights);
|
||||
// Get the cpt of the node
|
||||
auto cpt = node.getCPT();
|
||||
// Check that the cpt is not empty
|
||||
REQUIRE(cpt.numel() > 0);
|
||||
// Check that the cpt has the correct dimensions
|
||||
auto dimensions = cpt.sizes();
|
||||
REQUIRE(dimensions.size() == 2);
|
||||
REQUIRE(dimensions[0] == 2); // Number of states of the node
|
||||
REQUIRE(dimensions[1] == 3); // Number of states of the first parent
|
||||
// Create a copy of the node
|
||||
auto node_copy = node;
|
||||
// Check that the copy has not any parents or children
|
||||
auto parents = node_copy.getParents();
|
||||
auto children = node_copy.getChildren();
|
||||
REQUIRE(parents.size() == 0);
|
||||
REQUIRE(children.size() == 0);
|
||||
// Check that the copy has the same name
|
||||
REQUIRE(node_copy.getName() == "N1");
|
||||
// Check that the copy has the same cpt
|
||||
auto cpt_copy = node_copy.getCPT();
|
||||
REQUIRE(cpt_copy.equal(cpt));
|
||||
// Check that the copy has the same number of states
|
||||
REQUIRE(node_copy.getNumStates() == node.getNumStates());
|
||||
}
|
Reference in New Issue
Block a user